23114045
feat: wire watcher → scanner → mcp → rewriter in main
a73x 2026-04-29 05:59
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff --git a/cmd/filewatch-mcp/main.go b/cmd/filewatch-mcp/main.go index ba241f0..f22dbc9 100644 --- a/cmd/filewatch-mcp/main.go +++ b/cmd/filewatch-mcp/main.go @@ -1,7 +1,101 @@ package main import "fmt" import ( "context" "flag" "log" "os" "os/signal" "strconv" "syscall" "time" "github.com/google/uuid" "github.com/xanderle/claudealong/internal/ignore" "github.com/xanderle/claudealong/internal/mcp" "github.com/xanderle/claudealong/internal/rewriter" "github.com/xanderle/claudealong/internal/scanner" "github.com/xanderle/claudealong/internal/watcher" ) func main() { fmt.Println("filewatch-mcp") root := flag.String("root", ".", "project root to watch") debounce := flag.Duration("debounce", 500*time.Millisecond, "per-file debounce window") flag.Parse() log.SetOutput(os.Stderr) log.SetFlags(log.Ltime | log.Lshortfile) ig, err := ignore.New(*root) if err != nil { log.Fatalf("ignore: %v", err) } w, err := watcher.New(*root, ig, *debounce) if err != nil { log.Fatalf("watcher: %v", err) } defer w.Close() srv := mcp.New() ctx, cancel := context.WithCancel(context.Background()) defer cancel() sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigCh; cancel() }() if err := w.Start(ctx); err != nil { log.Fatalf("watcher start: %v", err) } go consume(ctx, w, srv) if err := srv.Run(ctx); err != nil { log.Fatalf("mcp run: %v", err) } } func consume(ctx context.Context, w *watcher.Watcher, srv *mcp.Server) { for { select { case <-ctx.Done(): return case ev, ok := <-w.Events(): if !ok { return } handle(ctx, ev.Path, srv) } } } func handle(ctx context.Context, path string, srv *mcp.Server) { markers, err := scanner.Scan(path) if err != nil { log.Printf("scan %s: %v", path, err) return } stamps := make(map[int]string, len(markers)) for _, m := range markers { if m.Tagged { continue } id := "fw-" + uuid.NewString()[:8] stamps[m.Line] = id if err := srv.SendChannel(ctx, mcp.ChannelMeta{ Source: "filewatch", File: path, Line: strconv.Itoa(m.Line), ReplyTo: id, }, m.Text); err != nil { log.Printf("send: %v", err) } } if len(stamps) == 0 { return } if err := rewriter.StampUUIDs(path, stamps); err != nil { log.Printf("stamp %s: %v", path, err) } } diff --git a/go.mod b/go.mod index 47a7d14..c0aeb67 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.26.2 require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect golang.org/x/sys v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index ced19ef..e3d1022 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=