1package cli
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "os/signal"
8 "syscall"
9
10 "arche/internal/repo"
11 "arche/internal/watcher"
12 "arche/internal/wc"
13
14 "github.com/spf13/cobra"
15)
16
17var watchCmd = &cobra.Command{
18 Use: "watch",
19 Short: "Watch working tree for changes to accelerate snap and status",
20 Long: `arche watch starts a filesystem event watcher that tracks which files
21have changed since the last snapshot. When the watcher is active, arche snap
22and arche status skip re-statting and re-hashing unmodified files, measurably
23improving performance on large working trees.
24
25The watcher seeds the dirty set with any files already modified before it
26starts, so the first arche snap after launch is still fully correct.
27
28Only one watcher per repository. Stop the watcher with Ctrl-C or SIGTERM.`,
29 RunE: runWatch,
30}
31
32func runWatch(cmd *cobra.Command, args []string) error {
33 wd, err := os.Getwd()
34 if err != nil {
35 return err
36 }
37 root, err := repo.FindRoot(wd)
38 if err != nil || root == "" {
39 return fmt.Errorf("not inside an arche repository")
40 }
41 r, err := repo.Open(root)
42 if err != nil {
43 return err
44 }
45 arch := r.ArcheDir()
46 workRoot := r.Root
47
48 if watcher.IsActive(arch) {
49 r.Close()
50 return fmt.Errorf("watcher is already running for this repository")
51 }
52
53 w := wc.New(r)
54 statuses, err := w.Status()
55 if err != nil {
56 r.Close()
57 return fmt.Errorf("status: %w", err)
58 }
59 for _, s := range statuses {
60 if markErr := r.Store.MarkWCacheDirty(s.Path); markErr != nil {
61 fmt.Fprintf(os.Stderr, "arche watch: seed dirty %q: %v\n", s.Path, markErr)
62 }
63 }
64
65 fmt.Fprintf(os.Stderr, "arche watch: watching %s (%d modified files at startup)\n", workRoot, len(statuses))
66 fmt.Fprintln(os.Stderr, "arche watch: tracking file events — stop with Ctrl-C")
67
68 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
69 defer stop()
70
71 defer r.Close()
72 if err := watcher.Run(ctx, workRoot, arch, r.Store); err != nil {
73 return err
74 }
75 fmt.Fprintln(os.Stderr, "arche watch: stopped")
76 return nil
77}