Commit 5ef9ce4
authored
* feat(watch): application/watcher.ts skeleton — pure debouncer + path filter + chokidar backend (Tracer 1 of 5)
Per docs/plans/watch-mode.md the engine is split into pure helpers + an injectable backend so tests don't need real fs watches:
- shouldIndexPath(relPath, excludeDirNames) — pure predicate. Same indexed extensions as the indexer (.ts/.tsx/.mts/.cts/.js/.jsx/.mjs/.cjs/.css) plus project-local recipes (<root>/.codemap/recipes/<id>.{sql,md}). Hand-rolled path-segment scan (no .split('/') alloc per call — watcher fires on every unrelated edit).
- createDebouncer(onFlush, delayMs) — sliding-window timer; trigger() resets, flushNow() forces, reset() clears without firing. Coalesces a burst (git checkout, npm install) into one onChange call.
- WatchBackend interface — production wires chokidar v5 (atomic + awaitWriteFinish for chunked-write detection); tests inject a fake backend that drives events deterministically (no flake in CI containers).
- runWatchLoop({root, excludeDirNames, onChange, debounceMs?, backend?}) — wires the three together. Returns {stop} that drains the debouncer + closes the backend (so SIGINT loses no events).
- DEFAULT_DEBOUNCE_MS = 250 (long enough to coalesce one editor save burst, short enough that agents don't perceive lag).
18 unit tests cover: extension whitelist, exclude-dir-names exact match (not substring — 'distill' is fine even when 'dist' is excluded), recipe paths, .codemap/codemap.db rejected, debouncer sliding window + flushNow + reset, backend dispatch, abs→rel POSIX conversion, dedup within burst, stop flushes pending.
chokidar v5 added (1 dep, 82 KB) per docs/plans/watch-mode.md decision. Tracer 2 wires cmd-watch.ts; Tracer 3 wires --watch into serve / mcp.
* feat(watch): cmd-watch.ts CLI verb + main/bootstrap wiring (Tracer 2 of 5)
Adds the standalone 'codemap watch' command. Wires the engine from Tracer 1:
- parseWatchRest() with --debounce <ms> + --quiet flag (12 unit tests cover space/equals forms, validation, defaults, composition, error paths).
- printWatchCmdHelp() — explains the --debounce trade-off (lower = snappier, higher = fewer cycles during git checkout / npm install) and points at 'codemap serve --watch' / 'codemap mcp --watch' as the killer combo (Tracer 3).
- runWatchCmd() — bootstraps codemap (initCodemap + configureResolver), starts runWatchLoop with onChange = runCodemapIndex({mode: 'files', files: [...paths]}), awaits SIGINT/SIGTERM, drains pending edits before close.
- main.ts: dispatch on rest[0] === 'watch'; bootstrap.ts: validateIndexModeArgs accepts 'watch'; printCliUsage lists 'Watch mode' between HTTP server and Targeted reads.
Per-batch stderr line: 'codemap watch: reindex N file(s) in Mms' unless --quiet.
Smoke verified: 'bun src/index.ts watch' boots, logs the bind line, drains cleanly on SIGTERM. Tracer 3 wires --watch into serve / mcp.
* feat(watch): --watch flag on serve + mcp + shared createReindexOnChange helper (Tracer 3 of 5)
Killer combo: codemap mcp --watch / codemap serve --watch boots the transport AND a co-process file watcher in one process. Removes the 'is the index stale?' friction agents hit today (per docs/plans/watch-mode.md § Agent-experience win).
Factored helper to keep cmd-watch / mcp-server / http-server identical:
- application/watcher.ts: createReindexOnChange({quiet, label?}) — opens DB, runs targeted reindex on the changed paths, logs 'reindex N file(s) in Mms' to stderr unless quiet, catches errors so a transient parse failure doesn't kill the loop. Caller passes a label so 'codemap mcp' / 'codemap serve' / 'codemap watch' lines are distinguishable in interleaved logs.
- cmd-watch.ts now uses createReindexOnChange (DRY with the embedders).
CLI surface:
- cmd-mcp.ts: new --watch flag + --debounce <ms> override (default 250). Help text + parser tests + propagation through runMcpCmd → runMcpServer.
- cmd-serve.ts: same flags. Parser tests + 'serve: ... (watch: on)' marker on the bind line.
- CODEMAP_WATCH=1 / 'true' env shortcut for IDE / CI launches that can't easily edit the agent host's tool spawn command (per the plan's sketched API).
Embedder lifecycle:
- runMcpServer: starts watcher AFTER server.connect(transport); on shutdown awaits stopWatch() (drains pending reindex) before resolving.
- runHttpServer: starts watcher AFTER listen succeeds; on SIGINT/SIGTERM awaits stopWatch() then closes the listener.
146 tests pass (cmd-watch + cmd-mcp + cmd-serve + watcher + mcp-server + http-server). No new code paths in the existing engines — just the boot-time wiring.
* feat(watch): handleAudit skips incremental-index prelude when watcher is active (Tracer 4 of 5)
Closes the wasted-I/O loop the plan called out: today MCP audit's default behavior is to run an incremental-index prelude (so 'head' reflects the on-disk source) — but with mcp --watch / serve --watch the watcher already keeps the index fresh, so the prelude is pure overhead.
- application/watcher.ts: module-level watchActive flag toggled by runWatchLoop start/stop. isWatchActive() exposed for handleAudit; _resetWatchStateForTests + _markWatchActiveForTests for test seam.
- application/tool-handlers.ts handleAudit: shouldRunPrelude = !args.no_index && !(isWatchActive() && args.no_index !== false). Hoisted to function scope so the inner finally can also pass it as the readonly hint to closeDb (avoids a wasted checkpoint pass).
- Explicit no_index: false still forces the prelude even when watch is on (escape hatch for 'force re-index right now').
- 1 new watcher test + 1 new MCP-server integration test (audit succeeds with no_index unset when watcher is marked active — would have failed if the prelude tried to run on the test's freshly-created DB without git history).
62 watcher + MCP tests pass.
* docs: sync README + architecture + glossary + roadmap + agents (Rule 10) + delete plan + changeset (Tracer 5 of 5)
- README.md 'Daily commands' stripe: extended with codemap mcp --watch / serve --watch / watch standalone / CODEMAP_WATCH=1 examples.
- docs/architecture.md: new 'Watch wiring' paragraph after MCP / HTTP wiring; covers chokidar selection, debounce + filter, audit prelude optimization. application/ table extended with watcher.ts.
- docs/glossary.md: new 'codemap watch / watch mode' entry under ## C (alphabetically before 'codemap mcp' / 'codemap serve' since 'watch' < 'serve' but the entry naming convention puts it after the existing CLI verbs).
- docs/roadmap.md: 'Watch mode for dev' line removed (shipped per Rule 2).
- .agents/rules/codemap.md + templates/agents/rules/codemap.md (Rule 10): new 'Watch mode (live reindex)' table row + --watch / --debounce flags appended to the mcp + serve rows.
- .agents/skills/codemap/SKILL.md + templates/agents/skills/codemap/SKILL.md: --watch / --debounce + CODEMAP_WATCH semantics on the MCP + HTTP server bullets; new 'Watch mode' bullet covering standalone vs combined shape choice and the audit prelude optimization.
- .changeset/codemap-watch.md: minor changeset (new top-level CLI verb + new --watch flag on mcp + serve).
- docs/plans/watch-mode.md: deleted on ship per docs-governance Rule 3.
- src/{application/{watcher,mcp-server,http-server}.ts, cli/cmd-{mcp,watch}.ts}: replaced dangling cross-refs to the deleted plan with cross-refs to architecture.md § Watch wiring.
* fix(watch): drain in-flight + prime gating + onError clears flag + 6 robustness fixes (CodeRabbit on #47)
9 of 10 CodeRabbit threads, all verified ✅ correct. The 10th (#4 — anchor) is ⚠️ partial: their suggested anchor (#watch-wiring) doesn't exist; #cli-usage is correct (precedent for every wiring paragraph). Pushing back with evidence in the reply.
**Major correctness fixes:**
- (#5, heavy) handleAudit treated 'watch active' as 'index definitely fresh' — but the watcher only sees NEW events, not historical drift. On boot before catch-up, audit could read a months-stale index. New onPrime opt on runWatchLoop runs an incremental catch-up BEFORE flipping watchActive=true. Embedders pass createPrimeIndex({label}) — same pattern as createReindexOnChange. Without onPrime, flag flips immediately (test-friendly default).
- (#7, heavy) stop() didn't drain async reindex work — fire-and-forget meant a stop() could resolve while onChange was mid-DB-write. Now: serialize onChange via inFlight chain, await it on stop. Also await primingDone so we don't tear down a DB connection out from under the prime catch-up.
- (#8) Backend onError left watchActive=true → handleAudit kept skipping prelude even when chokidar died. Now clears the flag.
- (#1) http-server: watcher boot throw after listen() leaked the listener. try/catch closes server on failure.
- (#2 + #10) http-server + cmd-watch: stopWatch().then(closeServer) never fired closeServer if stopWatch rejected — process hang on SIGTERM. Now .catch(log).finally(...) so progress is guaranteed.
- (#3) mcp-server.test: _markWatchActiveForTests ran outside the try guard — a thrown makeClient() would leak the singleton flag into sibling tests. Hoisted into try/finally.
**Minor:**
- (#6) shouldIndexPath built recipe prefix with platform sep, but relPath is POSIX-normalized. Windows recipe edits got skipped. Fixed to literal '.codemap/recipes/'.
- (#9) printWatchCmdHelp lacked JSDoc; added.
**Push-back:**
- (#4) CodeRabbit suggested changing #cli-usage anchor to #watch-wiring. Verified: there's no '## Watch wiring' heading — the wiring sections are bold-prefix paragraphs under '## CLI usage'. Their fix would 404. Keeping #cli-usage matches the precedent for MCP / HTTP / SARIF / serve wiring paragraphs.
5 new watcher tests cover the prime-gating race, onError flag clear, in-flight drain on stop (both auto-fire and flushNow paths). 152 tests pass total.
* docs(watch): outside-diff + 2 nitpicks from CodeRabbit follow-up review on #47
All 3 verified ✅ correct (the inline 10 actionable were already addressed in 207c05d):
- (outside-diff, mcp-server.ts:165) audit tool description still claimed prelude always runs first; updated to document watch-mode default ('default true-equivalent without watch, default false-equivalent with --watch active') and the 'pass no_index: false to force a re-index even when watch is active' escape hatch.
- (nitpick, glossary.md:395) widened wording from 'codemap mcp audit' to 'audit tool ... on both transports' since the same skip applies to 'codemap serve --watch' POST /tool/audit.
- (nitpick, cmd-serve.test.ts:14 + cmd-mcp.test.ts:11) replaced hardcoded debounceMs: 250 with imported DEFAULT_DEBOUNCE_MS so future default changes don't silently break tests.
1 parent 1534d1c commit 5ef9ce4
26 files changed
Lines changed: 1593 additions & 210 deletions
File tree
- .agents
- rules
- skills/codemap
- .changeset
- docs
- plans
- src
- application
- cli
- templates/agents
- rules
- skills/codemap
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
58 | 58 | | |
59 | | - | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
60 | 62 | | |
61 | 63 | | |
62 | 64 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
122 | 128 | | |
123 | 129 | | |
124 | 130 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments