a73x

cmd/filewatch-mcp/main.go

Ref:   Size: 2.1 KiB

package main

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() {
	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)
	}
}