3aff237Thanks @SutuSebastian! - Refresh runtime and toolchain dependencies (better-sqlite3, oxc-parser, oxfmt, oxlint, and related dev tooling) to latest compatible releases. Pin transitive hono and js-yaml to patched versions so dependency audits report no known vulnerabilities.
-
#178
36106ffThanks @SutuSebastian! - Add structural duplicate detection:symbols.body_hashat index time (canonical function body AST) and bundledduplicatesrecipe. Schema version 39 — existing indexes rebuild on nextcodemaprun. Function-shaped symbols only; trivial one-line bodies skipped. Triage collisions withsnippet— shared control-flow skeletons can false-positive. -
#179
8595173Thanks @SutuSebastian! - Add churn × complexity hotspot ranking:file_churnrefreshed on every index from git history, withcodemap ingest-churn, MCP/HTTPingest_churn, and configchurn.filefor non-git repos. Schema version 40 — existing indexes rebuild on nextcodemaprun. Newchurn-complexity-hotspotsrecipe ranks files or symbols (by_symbol) by change frequency × complexity with normalized 0–100 scores andchurn_trend. Outcome aliashotspotsstill maps to fan-in. -
#173
d4982c4Thanks @SutuSebastian! - Add SonarSource cognitive complexity onsymbols(same function-shaped coverage as cyclomatic, including class methods). Schema version 38 — existing indexes rebuild on nextcodemaprun. New recipehigh-cognitive-complexity;high-complexity-untestedrows includecognitive_complexity.
-
#177
bb8bae9Thanks @SutuSebastian! - Onaudit --base <ref>(CLI / MCP / HTTP), eachaddedrow carriesattribution: introduced | inherited(branch-new vs pre-existing at merge base).--summaryaddsadded_introduced/added_inheritedper delta. -
#172
cf7df2fThanks @SutuSebastian! - Addquery --format codeclimate(GitLab Code Quality JSON) andquery --format badge(markdown issue-count line orcodemap-badge/v1JSON via--badge-style json). Available on CLI, MCP, and HTTP query tools. -
#183
b509e4cThanks @SutuSebastian! - Add hash-stablemap_idandcodebase_maprouting hints tocontextresponses (CLI, MCP, HTTP). MCP initialize instructions now includemap_idand top hub paths. Opt out with--no-codebase-maporinclude_codebase_map: false; omitted whencompact. -
#176
16687a0Thanks @SutuSebastian! - Addcoverage-confirmed-deadrecipe: static dead exports withconfidence(highwhen ingested 0% coverage,mediumwhen unmeasured), plusreasonandcaller_count. -
#174
a11242eThanks @SutuSebastian! - Addreasonandevidence_jsoncolumns on high-judgment recipe rows (boundary-violations,deprecated-symbols,unimported-exports) so agents can cite detection path beforeapplyor manual edits. -
#175
af86d10Thanks @SutuSebastian! - Addhigh-crap-scorerecipe: CRAP ranking with measured coverage when ingested, or graph-estimated 85/40/0% tiers from test reachability otherwise.Extend
unimported-exportswithunresolved_import_blind_spotreason andevidence_json(unresolved import hop) so dead-export / high-CRAP triage does not over-trust the graph past alias blind spots. -
#181
aae172fThanks @SutuSebastian! - Scopecodemap impactand MCP/HTTPimpacthomonym symbols:--in/indisambiguates by defining file (same prefix/exact rules ascodemap show --in); unscoped homonyms union per-defining-file call graphs instead of merging by name only. Mismatched scope returns emptymatcheswithskipped_scope. -
#170
e74410cThanks @SutuSebastian! - MCPtools/listand HTTPGET /toolsexpose advisoryreadOnlyHint,destructiveHint, andidempotentHintper tool so clients can gate auto-approval. Apply tools carrydestructiveHint; read-only query tools carryreadOnlyHint. -
#180
a5caca8Thanks @SutuSebastian! - Harden read surfaces:codemap query --format …blocks index mutations via the same read-only guard as--json;codemap serverequires--tokenwhen--hostis not loopback (any127.0.0.0/8address counts as loopback, so--tokenstays optional on127.0.0.2and similar);codemap validate(and MCP/HTTPvalidate) can returnrejectedrows with optionalreason(path escapes project root|path escapes via symlink|path resolves outside project root) — outputpathkeys are always project-relative POSIX paths. -
#182
3568c5fThanks @SutuSebastian! -createCodemap()and the CLI now reject invalid project config at load time. A secondcreateCodemap()with a different project root in the same process throws (audit--baseworktree reindex is exempt). -
#184
fb73e0dThanks @SutuSebastian! -showandsnippetnow use fast equality lookup for exactnameand lonename:Tokenqueries (no wildcards); substring, multi-field, and FTS paths stay on the broader slow tier. CLI help, MCP tool descriptions, and bundled agent guidance document the two tiers.
-
#169
1632bddThanks @SutuSebastian! - Refresh runtime and toolchain dependencies (oxc-parser, oxc-resolver, oxfmt, oxlint, @clack/prompts, tinyglobby, and related dev tooling) to latest compatible releases. -
#166
4bfb067Thanks @SutuSebastian! - Addcodemap renameCLI alias for homonym-safe renames viaapply rename-preview(--define-in,--in-file,--kind). -
#167
231d6ccThanks @SutuSebastian! - Add MCP/HTTP transport parity for coverage ingest and query baselines: newingest_coveragetool (CLI twincodemap ingest-coverage --json) and optionalbaselineparam onquery/query_recipe(same diff envelope ascodemap query --baseline). Tool count 19 → 20.
-
#164
8c7064aThanks @SutuSebastian! - Add optional heuristic call edges (calls.provenance) for JSX parent→child composition. Schema rebuild to v37. Enable via.codemap/configsynthesis.heuristicCalls: true(default off). Bundled recipecalls-including-heuristic;call-pathexcludes heuristics by default. -
#162
6e50d77Thanks @SutuSebastian! - Add two-phase call resolution (unresolved_callsstaging,calls.callee_symbol_id/callee_resolution_kind). Schema version 36 — existing indexes rebuild on nextcodemaprun.
7b0406aThanks @SutuSebastian! - Ship the apply substrate: recipe-drivencodemap applyfor stale imports, deprecated migrations, import-source moves, JSX prop renames, and scopedrename-preview(define_inhomonyms). Add--rows,--diff-input, fixpoint--until-empty, optional--commit,auto_fixable/--force, andapply.autoApplyRecipesallowlist. MCP/HTTP twins:apply,apply_rows,apply_diff_input.
- #160
b1e3d58Thanks @SutuSebastian! - Add CLI twins for MCP query composers and resources so shell-only consumers get the same JSON payloads:codemap query batch,trace,explore,node,file,schema,symbols, andcontext --include-snippets.
- #158
e8ea640Thanks @SutuSebastian! - Addcodemap agents init --targetsand--link-modefor non-interactive IDE wiring. Combine with--mcpto write MCP config only for selected integrations (e.g. Cursor + Copilot without Continue/Cline). Mutually exclusive with--interactive.
- #156
ae4a143Thanks @SutuSebastian! -codemap agents init --mcpnow includes--root ${workspaceFolder}in the VS Code / Copilot MCP config (.vscode/mcp.json), same as Cursor. Re-runcodemap agents init --mcpto upgrade an existing.vscode/mcp.jsonfrom older init output (or--interactiveand select Copilot only).
-
#152
150c6c6Thanks @SutuSebastian! - Adaptive snippet budgets fortrace,explore, andnodescale with indexed file count whenbudget_charsis omitted (explicit override unchanged).explorealso applies adaptive row limits internally (no transport override). -
#155
450ca2eThanks @SutuSebastian! -codemap agents initis safer on re-run: IDE mirrors sync bundled template paths only;--forceoverwrites IDE mirrors only when they carrycodemap-init:managedor match the legacy mirror heuristic (pre-marker bundled copies); invalid MCP JSON shapes are rejected instead of reset (even with--force). -
#149
41a4184Thanks @SutuSebastian! - Addindex_freshnessmetadata oncontext, MCP tool responses, HTTP headers, and boot stderr warnings so agents can detect commit drift, pending watcher sync, and disk-ahead-of-index states before trusting structural queries. -
#154
1bcca3aThanks @SutuSebastian! -codemap agents init --mcpwrites PM-aware MCP spawn commands (e.g.npx codemap,pnpm exec codemap,yarn exec codemap,bunx codemap, or dlx@stainless-code/codemap@latest) instead of assuming globalcodemapon PATH. -
#153
048278bThanks @SutuSebastian! - MCP session lifecycle hygiene: stdio disconnect detection (stdin EOF, stdout EPIPE, parent-PID poll) with graceful watcher shutdown on client exit; HTTPserve --watchrefcount-gates the watcher per request (5s release grace,/healthexcluded). No MCP idle shutdown. -
#151
e717652Thanks @SutuSebastian! - Richcontextbootstrap:start_herewith intent-ranked recipe cards, inline index summary, budget-capped hub leaders with export signatures (adaptive caps by repo size), debug-biased marker samples, and optionalinclude_snippetson MCP/HTTPcontext(no-op whencompact: true). Legacyhubskeeps the bundledfan-inrecipe default limit; preferstart_here.hub_leadersfor signatures.
- #146
7afc9ebThanks @SutuSebastian! - Fixcodemap --full(and other worker-pool parses) appearing to hang ~120s after stats print — clear parse timeout timers when workers respond instead of leaving orphanedsetTimeouthandles on the event loop.
-
#129
6648addThanks @SutuSebastian! - Add cross-process index lock withcodemap unlock, and append parse failures to<state-dir>/errors.log. -
#130
f5374b8Thanks @SutuSebastian! - Add per-file parse timeouts with worker recycle during full and incremental indexing; failures log to errors.log and appear in the index summary.
-
#132
8a1de53Thanks @SutuSebastian! - Addaffected-testsrecipe andcodemap affectedCLI for reverse-dependency test selection from changed files. -
#135
c4b4b36Thanks @SutuSebastian! - Addcodemap agents init --mcp— project-level MCP config for Cursor, Claude Code, VS Code, Continue, Cline, Amazon Q (workspace.amazonq/default.json+ legacy.amazonq/mcp.json), and Gemini CLI (Windsurf when that integration is selected), with idempotent JSON merge and Claudemcp__codemap__*permissions. -
#138
829075cThanks @SutuSebastian! - Add field-qualified symbol discovery oncodemap show --queryand MCP/HTTPshow/snippetvia aqueryargument (kind:,name:,path:,in:+ optional free text). Includes parameterized SQL engine, optional FTS join,--print-sqlMoat-A transparency, and{matches, disambiguation?, warning?}envelope parity across CLI, MCP, and HTTP. -
#124
04c4aa7Thanks @SutuSebastian! - Honor--state-dirfor project recipes; populatefunction_params.owner_kind; register MCPfiles/symbolsresource templates with optional?in=filter; watcher fixes for custom state dirs. -
#133
386ffa6Thanks @SutuSebastian! - Add MCP/HTTPaffectedtool — same preprocessor ascodemap affected, composes theaffected-testsrecipe. RespectsCODEMAP_MCP_TOOLSallowlist. -
#126
ed4ca6bThanks @SutuSebastian! - Add MCP initialize server instructions (tool-selection playbook) andCODEMAP_MCP_TOOLSenv for subset tool registration. -
ee9f6b2Thanks @SutuSebastian! - Bumpoxc-parserto ^0.133.0 and@typescript/native-previewto the latest 7.0 dev build. -
9fa1e9dThanks @SutuSebastian! - Pin runtime and dev dependencies to exact versions inpackage.json(drop carets) and refreshbun.lock. Dev tooling:oxfmt0.52,oxlint1.67,@typescript/native-preview20260526. Oxlint 1.67 drops a redundant escape inshow-search-mode.tsglob regex (no behavior change). -
#131
a8cf25bThanks @SutuSebastian! - Addcall-pathandsymbol-neighborhoodquery recipes for cycle-safe call-graph tracing and bidirectional symbol exploration. -
#143
e475d20Thanks @SutuSebastian! - Fix type heritage resolve edge cases: default-import bases, re-export barrel incremental scope, homonym dedupe in ancestor walks, and(expression)complex extends handling. -
#142
a085ec6Thanks @SutuSebastian! - Addtype_heritagesubstrate and rewiretype-ancestors/type-descendantsrecipes to JOIN indexed heritage edges instead of parsingsymbols.signature. -
#141
bd5a1faThanks @SutuSebastian! - Addtype-ancestorsandtype-descendantsquery recipes for extends/implements hierarchy walks (backed by thetype_heritagesubstrate after #142). -
#127
4318cc4Thanks @SutuSebastian! - Add WSL watch policy (auto-disable on/mnt/*mounts) and opt-in git hooks for background incremental index when the watcher is off.
-
#107
f24f8b6Thanks @SutuSebastian! - Substrate tiers 1–6 remainder (excludes C.9 /files.is_entry). Schema bumpSCHEMA_VERSION27 → 34 — first run after upgrade auto-rebuilds.codemap/index.dbvia the existing version-mismatch path.Tier 1 — call + import precision
calls.{args_count,is_method_call,is_constructor_call,is_optional_chain}; constructor vs call dedup key fixsymbols.{return_type,is_async,is_generator}- Side-effect
import_specifiersrows (kind='side-effect') +import_idFK toimports
Tier 2 — bindings
bindings.resolution_kind='re-exported'when resolution walks a re-export chain
Tier 3 — JSX
- New tables
jsx_elements/jsx_attributes; extractor with per-file parent linking post-pass
Tier 5 — behavioral
- New tables
async_calls,try_catch,decorators,jsdoc_tags; context stack forin_loop/in_try
Tier 6 — module graph (no entry points)
dynamic_importstable + extractor- Post-pass
files.is_barreland parse-timefiles.has_side_effects
Recipes + goldens:
find-call-sites(extended),find-async-functions,find-dynamic-imports,find-barrel-files,find-side-effect-files,find-re-exported-bindings,find-side-effect-imports,find-jsx-usages,find-await-in-loop,find-swallowed-errors,find-decorator-usage,find-throws-jsdoc, plus golden coverage forfan-out-sample/fan-out-sample-jsonand substrate regression tests.Read-only CLI hardening:
printQueryResult(ad-hoccodemap query "<SQL>") now setsPRAGMA query_only = 1, closing the last gap vsqueryRows/executeQuery(#107).Out of scope: C.9 plugin layer (
files.is_entry, reachability-from-entry); tiers 7–13.Migration: No in-place DDL — rebuild on schema mismatch preserves user-data tables (
coverage,query_baselines,recipe_recency). Re-runcodemap --full(or any index) after upgrade.
-
#118
665c19aThanks @SutuSebastian! - Reject apply targets that are symlinks so phase-2 rename cannot replace a link with a regular file. -
#117
5ee5f2eThanks @SutuSebastian! - Fail benchmark reindex runs when the spawned indexer exits non-zero instead of recording misleading timings. -
#109
02a628fThanks @SutuSebastian! - Validatecodemap --filesoperands before indexing starts. -
#110
7767a97Thanks @SutuSebastian! - ValidateVERSIONoutput indetect-pmbefore writing GitHub Actions outputs. -
#112
ec31949Thanks @SutuSebastian! - Harden apply and diff-json path containment against traversal outside the project root. -
#123
54ad25aThanks @SutuSebastian! - Fix high-severity bugs (describe.each parent stack, git porcelain -z paths, CLI symlink entry, pr-comment TTY stdin), medium bugs (changed-since -z paths, perf baseline RUNS guard, qualified typeof, decorator args_text, for-of binding refs, HTTP body drain, benchmark regex validation, jsx INSERT RETURNING id, sqlite stmt cache on close), and low bugs (CLI parse guards, incremental delete transaction, apply summary.files, extension case, pnpm # paths, coverage db.transaction, impact walk delimiter, worker errors, pointer dedup, benchmark readAll visibility). -
#114
ae54ce0Thanks @SutuSebastian! - Fix diff preview deletions, config empty-array overrides, resolver path containment, impact call-site selection, and FTS cleanup on file delete. -
#122
4e191baThanks @SutuSebastian! - Run fullcheck(not build-only) inprepublishOnlyso npm publish cannot skip tests or typecheck. -
#113
126066dThanks @SutuSebastian! - Reject opaqueOrigin: nullincodemap serveCSRF checks. -
#120
17dcbd1Thanks @SutuSebastian! - Reject unexpected arguments oncodemap skillandcodemap ruleinstead of silently printing bundled content. -
#119
cf0532bThanks @SutuSebastian! - Print symbol signature incodemap snippetterminal output, matchingcodemap showand the documented contract. -
#116
a444c40Thanks @SutuSebastian! - Fix V8 coverage ingest so innermost-wins applies across all FunctionCoverage entries in a script, not per-function iteration order. -
#115
eb18750Thanks @SutuSebastian! - Fix watcher priming race, parse-worker stat errors, incremental rename cleanup, and several indexer extractor gaps (scopes, references, tests.each, process.env, CSS imports). -
#121
f5d013cThanks @SutuSebastian! - Reject malformedCODEMAP_PARSE_WORKERSvalues (e.g.2abc,1.5) instead of silently truncating withparseInt.
-
#96
cc8daedThanks @SutuSebastian! - Performance: full-rebuild wall down ~21% on small trees and ~18% on a real-world ~2k-file external corpus. Headline contributor:collectFilesswitched to a singletinyglobbycall withignorepatterns (collect_ms -93%, file set bit-identical viafollowSymbolicLinks: false). Bindings/cycles/re-exports phase now keeps the bulk-INSERT PRAGMA-OFF window open through full rebuild (bindings_ms -33% on the 2k corpus). Plusquery_batchsingle connection, incremental double-read kill, sharedcountLineshelper,stmtCacheplaceholder memo, SQLitebusy_timeout, adapterMaplookup, byte-order sort, FTS5 batched delete, andgetAllFileHasheshoist.Instrumentation:
IndexPerformanceReportnow surfacesbindings_ms,module_cycles_ms,re_export_chains_ms(previously rolled intototal_mswith no breakdown). SetCODEMAP_PERFORMANCE_JSON=<path>to dump the report as JSON post-run (no new CLI flag added).Knobs:
CODEMAP_PARSE_WORKERS=N(clamped[1, 32]) overrides the defaultmax(2, min(cpus, 6))worker count.bun run check:perf-baseline+ a non-blocking CI job (📈 Perf baseline (self-index)) guard against per-phase regressions vsfixtures/benchmark/perf-baseline.json;CODEMAP_PERF_RUNS/CODEMAP_PERF_REGRESSION_PCT/CODEMAP_PERF_NOISE_FLOOR_MStune the checker.Behavior change (correctness hardening):
queryRows(the implementation behindCodemap.query(),codemap applyrecipe SQL execution, andbun run test:golden) now setsPRAGMA query_only = 1to mirrorexecuteQuery's read-only enforcement. DML / DDL slipping through these paths now errors at SQLite instead of mutating the database. All these call sites are contractually read-only; this turns a contract into an enforceable boundary. Anyone who relied on undocumented mutation throughCodemap.query("DELETE FROM ...")would now get an error — but that was always API abuse. (Ad-hoc CLI SQL viaprintQueryResultwas a separate path; it gained the same guard in 0.8.0 — see that release entry.)Full design context:
docs/plans/perf-triangulation-rollout.md(synthesis + execution rollout of 5 independent perf/architecture audits authored 2026-05-17).
-
#93
d92b917Thanks @SutuSebastian! - Fix: project recipes (<root>/.codemap/recipes/<id>.sql) are now visible via the CLI.parseQueryRestvalidates--recipe <id>/--recipes-json/--print-sql <id>BEFORErunQueryCmdcallsbootstrapCodemap, so the recipe registry hitgetProjectRoot()pre-init, the throw was silently caught, and the loader fell back to bundled-only. The MCP and HTTP transports always bootstrap before reaching the loader, so project recipes worked there throughout — only the CLI path was affected.Fix is surgical:
- New
setQueryRecipesProjectRoot(root)API inapplication/query-recipes.ts— caller-supplied root takes precedence over the runtime config (which isn't initialised yet during argv parse). cli/main.tscalls it once right afterparseBootstrapArgsreturnsroot, so every subsequent verb (parser-side and post-bootstrap) sees the same value.
Single source of truth: the override is the same root
bootstrapCodemapresolves to — no parallel walk-up heuristic, no new env var, no second resolution path. The registry cache invalidates when the override changes.Adds regression tests (
query-recipes.pre-bootstrap.test.ts) exercising the override-only path (noinitCodemap) plus a dogfood project recipe (.codemap/recipes/src-deprecated.sql) that scopes the bundleddeprecated-symbolsaudit tosrc/only — useful for codemap's own deprecation lifecycle, and a permanent regression case against this bug recurring.Consumers with project recipes authored on 0.6.x–0.7.2 didn't need to wait — recipes worked via MCP / HTTP throughout. After upgrading, the CLI auto-picks them up.
- New
-
#91
82bca4bThanks @SutuSebastian! - Slim the auto-generated<state-dir>/.gitignoreheader for consumer clarity:- Drop the internal function-name reference (
ensureStateGitignore) — consumers can't look it up. - Drop the "Rule 9 analogue" / "bump alongside any new cache" line — it was guidance for codemap contributors, leaking into every consumer's checkout.
- Reframe "blacklist" / parenthetical mention of tracked files in plainer language.
Existing two-line header (
# codemap-managed — edits will be overwritten by ensureStateGitignore./# Blacklist of generated artifacts...) becomes:# Managed by codemap — overwritten on next run. # Generated artifacts only; user-authored config (config.*, recipes/) stays tracked.One-time rewrite on consumer side. The reconciler matches the canonical body via exact string comparison, so every consumer's next
codemaprun rewrites<state-dir>/.gitignoreto the new shape (no entries change — only the comment lines). Harmless; the blacklist entries (index.db,index.db-shm,index.db-wal,audit-cache/) are unchanged. - Drop the internal function-name reference (
-
#89
6e53458Thanks @SutuSebastian! -codemap audit --base <ref>now materialises the sha-keyed cache viagit archive | tar -xinstead ofgit worktree add. The cache entry at.codemap/audit-cache/<sha>/is a plain extracted tree with no.gitpointer file and no registered git worktree, so:git clean -xdfsweeps it without needing-ff(which used to also nuke unrelated nested repos).- Plain
rm -rfworks — no more dangling registrations under<repo>/.git/worktrees/that neededgit worktree prune.
All other invariants preserved: cache path layout, hit detection (
<sha>/.codemap/index.dbexists), atomic populate (per-pid temp + POSIXrename), LRU eviction (5 entries / 500 MiB), error code names (worktree-add-failedetc. kept for API stability — external consumers discriminate oncode, not the underlying primitive).Also: the cache reindex now stamps
meta.last_indexed_commitwith the resolved sha directly instead of shelling out togit rev-parse HEADinside the cache dir — silences afatal: not a git repositorystderr line that older revisions leaked.Migration — existing consumers with
.codemap/audit-cache/<sha>/worktrees from earlier versions can rungit worktree pruneonce after upgrade to clear dangling registrations. The registrations are inert when the path is gone, so skipping the prune is harmless.
-
904e4a5Thanks @SutuSebastian! - Two skill / docs clarifications that ship in the live-served skill (codemap skill/codemap://skill/GET /resources/<encoded-skill-uri>):-
parent_namevsscope_local_id(#86).parent_nameis the nearest named enclosing scope — it walks past anonymous arrows / IIFEs / callbacks, soparent_name IS NULLmatches both true module-scope symbols and symbols inside top-level anonymous IIFEs. For a strict "module-scope only" filter usescope_local_id = 0.docs/architecture.mdsymbols.parent_namecolumn doc +40-query-patterns.mdmutability example updated accordingly. -
imports.sourcevsimports.resolved_path(#87). The single most common cause of emptyimportsresult sets on alias-using codebases (TSpaths, Webpack / Vite aliases, Node subpath imports#internal/…, monorepo workspaces) is picking the wrong column. The skill now explicitly teaches: filtersourcefor "via alias / package name", filterresolved_pathfor "via on-disk path", andWHERE resolved_path IS NULLfor "external packages only".
No schema change, no CLI / API change. Patch bump; existing
.codemap/index.dbunaffected. -
-
#84
e003218Thanks @SutuSebastian! -symbols.kinddistinguishesconst/let/varinstead of collapsing all three into'const'. Schema bumpSCHEMA_VERSION26 → 27 — first run after upgrade auto-rebuilds.codemap/index.dbvia the existing version-mismatch path; consumer queries see the new values immediately.What changes:
let x = 1now emitskind = 'let'+signature = 'let x'.var y = 2now emitskind = 'var'+signature = 'var y'.const z = 3unchanged (kind = 'const'+signature = 'const z').- Destructuring patterns inherit the declaration keyword:
let { a, b } = obj→ bothaandbarekind = 'let'. for (let x of arr) { ... }body bindings inherit the keyword (kind = 'let').- Arrow / function init still wins over the keyword:
const handler = () => 1andlet handler = () => 1both emitkind = 'function'.
Breaking — but pre-v1 the breakage IS the fix. Any query that filtered
WHERE kind = 'const'to mean "all variable bindings" was silently over-matching everyletandvar. Post-upgrade the filter is precise; queries that wanted the over-match should widen toWHERE kind IN ('const', 'let', 'var'). Affected paths in this PR: the40-query-patterns.mdconst-values example (now demonstrates the precise filter + adds two new patterns that depend on it — "lets that should be const" and "consts that get illegally written"), andfind-write-sites.mdprose where the recipe's documented JOIN trick now works as described.Mutability filters that finally work:
-- bindings declared `let` but never reassigned — candidates to tighten to `const` SELECT s.name, s.file_path, s.line_start FROM symbols s WHERE s.kind = 'let' AND NOT EXISTS ( SELECT 1 FROM "references" r WHERE r.name = s.name AND r.file_path = s.file_path AND r.is_write = 1 AND r.line_start > s.line_start ); -- `const`s that get reassigned anyway (TypeScript usually catches it; queryable here for completeness) SELECT s.name, s.file_path, s.line_start FROM symbols s JOIN "references" r ON r.name = s.name AND r.file_path = s.file_path WHERE s.kind = 'const' AND r.is_write = 1 AND r.line_start > s.line_start;
find-symbol-by-kindrecipe params updated to enumerate the new values explicitly.file_metricskeyword counts populate too —let_count/const_count/var_countwere previously hardcoded to0(// let/const/var distinction not yet trackedper the in-source TODO). They now reflect the actual per-file counts.docs/architecture.md§file_metricsdrops the "Reserved (parser-keyword variant TBD)" notes.
-
#82
8e8aae0Thanks @SutuSebastian! - FixresolveBundledRecipesDir()path resolution — the 40 bundled recipes were unreachable at runtime in 0.6.0's published artifact. The resolver had one extra..segment relative to where the bundler emits the dist chunk;bunx codemap query --recipes-jsonreturned[]andbunx codemap query --recipe <id>rejected every bundled id withunknown recipe.The fix derives the bundled-recipes path off
resolveAgentsTemplateDir()(same pattern used byresolveAgentContentDir()) so a single resolver handles both source-mode (bun src/index.ts) and dist-mode (node dist/index.mjs) without environment-specific branching — every chunk lands flat indist/regardless of the source file's nested depth.Discovered by a downstream consumer immediately after
bun install @stainless-code/codemap@0.6.0. Regression guard: newsrc/application/query-recipes.dist.test.tsassertsexistsSync(resolveBundledRecipesDir())+ catalog populates; CI gains anode dist/index.mjs query --recipes-jsonsmoke step that exits non-zero on an empty catalog.
-
#80
7c3ba71Thanks @SutuSebastian! -codemap skill/codemap rule— live-served agent content. Consumer-disk.agents/skills/codemap/SKILL.mdand.agents/rules/codemap.mdare now thin pointer files (~16-22 lines); the full content is served live by the installed binary, sobun update @stainless-code/codemapautomatically refreshes what agents see without re-runningagents init.Three transports, one engine:
- CLI:
codemap skill/codemap rule - MCP: resources
codemap://skill/codemap://rule - HTTP:
GET /resources/{encoded-uri}againstcodemap serve
All three resolve through the same
assembleAgentContent(kind)function. MCP and HTTP share a lazy per-process cache.Section assembler with
*.gen.mdrenderers:templates/agent-content/<kind>/*.mdfiles concatenate in lexical name order. Files ending in.gen.mdroute throughRENDERERSinsrc/application/agent-content.ts— today,20-recipes.gen.mdregenerates the recipe catalog fromlistQueryRecipeCatalog()on every fetch, and30-schema.gen.mdregenerates table DDL fromcreateTables(). Adding a recipe undertemplates/recipes/or a column insrc/db.tsnow surfaces in the served skill automatically with zero template edits.Pointer protocol + staleness detection: every consumer-disk pointer carries
<!-- codemap-pointer-version: N -->. On startup, codemap scans the consumer's.agents/{skills/codemap/SKILL,rules/codemap}.md; if the stamp is< EXPECTED_POINTER_VERSION(or absent on a fat legacy file > 50 lines), a one-line stderr nag prints with the fix command (codemap agents init --force). Warning is stderr-only socodemap skill > file.mdstays clean.Rule trimmed to priming surface: the always-on rule shrank from 248 → 102 lines (~70% token reduction per turn) — STOP banner + trigger patterns table + top-11 quick reference queries + pointer to the skill for full reference. CLI command table / MCP narrative / audit + apply detail moved into the skill where they belong (on-demand, not every-turn).
Migration: existing consumers re-run
codemap agents init --forceto swap their fat.agents/files for the new pointer templates (the staleness nag prompts them on first invocation). - CLI:
-
#78
84f9b97Thanks @SutuSebastian! -codemap apply <recipe-id>— substrate-shaped fix executor over the existing--format diff-jsonrow contract. The recipe SQL describes the transformation ({file_path, line_start, before_pattern, after_pattern}rows); codemap is the executor. Floor "No fix engine" preserved — codemap doesn't synthesise edits, it only executes the hunks the recipe row described.Three transports, one engine:
- CLI:
codemap apply <recipe-id> [--params k=v[,k=v]] [--dry-run] [--yes] [--json] - MCP tool:
apply(registered alongsideimpact/show/snippet) - HTTP:
POST /tool/apply
All three dispatch the same pure
applyDiffPayloadengine inapplication/apply-engine.ts.Decisions worth knowing (Q1–Q10 locked in
docs/plans/codemap-apply.md, lifted intodocs/architecture.md § Apply wiringon this PR):- Apply-by-default,
--dry-runopts into preview. Verb-name semantics +git apply/terraform applyprecedent. - Per-recipe-run all-or-nothing (Q2 (c)). Phase 1 validates every row first; any conflict aborts phase 2 entirely before any file is touched. Cross-file invariants matter —
rename-previewproduces a definition row + N import rows, and partial application leaves the project syntactically broken. - Scan-and-collect conflicts (Q3 (b)). Phase 1 walks every row and collects all conflicts in one pass — better remediation UX than fail-fast.
- TTY prompt +
--yesgate (Q6 (a)). Interactive contexts (TTY) get aProceed? [y/N]prompt with default-N; non-interactive contexts (CI / agents / MCP / HTTP) require--yes(oryes: true) explicitly.--dry-run+--yesmutually exclusive. - Substring match per row, single-line (Q8 (a)). Mirrors
buildDiffJson's contract verbatim —actual.includes(before_pattern)+actual.replace(before, after)with$-pre-escape perString.prototype.replace's GetSubstitution rule. Exemplar:templates/recipes/rename-preview.sqlemitsbefore_pattern = old_name(the bare identifier). Whenbefore_patternappears more than once on the line (e.g.const foo = foo();), only the leftmost is replaced — same shape--format diffpreviews; recipe authors normalise their SQL if they need a different occurrence. - Path-containment guard. Every
file_pathis rejected with apath escapes project rootconflict if it's absolute or ifpath.resolve(projectRoot, file_path)lands outside the project root. Defends the CLI + MCP + HTTP write paths against malicious or malformed recipe rows. - Overlap detection. Two rows targeting the same
(file_path, line_start)are rejected with aduplicate edit on same lineconflict during phase 1. Without it, the second row's substring assertion would fail mid-phase-2 (after earlier files in alphabetical order had already been renamed) — that would leave the project in a partial-write state and violate Q2 (c). - Atomic per-file writes via temp + rename. Sibling
<file>.codemap-apply-<rand>.tmpthenrenameSync— POSIX-atomic so concurrent readers see either pre-rename or post-rename content, never a torn write. - Q7 idempotency (conflict-only path). Re-running on already-applied code reports
line content driftedwithactual_at_lineshowing the post-rename content; user re-runscodemapto refresh the index → next run produces 0 rows → vacuous clean apply. - Single envelope shape across modes (Q5).
{mode, applied, files, conflicts, summary}— same shape fordry-runandapply; consumers pattern-match onmode+applied. - No SARIF / annotations. Apply is a write action, not a findings list.
Boundary discipline (Q10): only
cli/cmd-apply.ts+application/tool-handlers.tsmay import the apply engine — re-runnable kit atdocs/architecture.md § Boundary verification — apply write path.Plan: PR #77 (merged). Implementation: this PR.
- CLI:
-
#79
ec91bdfThanks @SutuSebastian! -codemap-richer-index— substrate extraction across 12 tiers. Schema bump (SCHEMA_VERSION10 → 26) — first run after upgrade rebuilds.codemap/index.dbfrom source.10 new substrate tables:
import_specifiers,scopes,references,bindings,function_params,file_metrics,re_export_chains,module_cycles,runtime_markers,test_suites.Column additions:
symbols.{name_column_start, name_column_end, scope_local_id, body_line_count, param_count, nesting_depth}·calls.{line_start, column_start, column_end}·exports.{is_re_export, line_start, line_end, column_start, column_end}·markers.{column_start, column_end}.12 new recipes:
find-references·find-symbol-references·find-write-sites·find-by-param-type·large-functions·deeply-nested-functions·circular-imports·barrel-chains·find-leftover-console·env-var-audit·find-skipped-tests·tests-by-file.Architecture: modular extractor pattern (R.17) splits
parser.tsinto per-tier extractors undersrc/extractors/with a sharedExtractContext. Targeted reindex stays sub-100ms; full reindex includes bindings resolution + Tarjan SCC + re-export chain materialisation.Reference precision:
referencestable emits every identifier USE with column-precise positions;kind='member'rows distinguish non-computed property access from bindings. Native JSX tags + JSXAttribute names + long-hand object-literal keys are suppressed.TSQualifiedName(e.g.React.ReactNode) splits into namespace head (kind='type') + member tail (kind='member'). Bindings resolver (full-rebuild only) walks same-file scope → imports → globals → unresolved with deduped TypeScript / DOM / Node / ES global sets. Re-export chains followed up to 10 hops with cycle detection.Dependency bumps:
oxc-parser0.127 → 0.130 ·zod4.3 → 4.4 (dedupe override added so the MCP SDK keeps a single$ZodTypeidentity) ·tsdown0.21 → 0.22 (declaredunrunas devDep to unblock CI build under Node's tsdown binstub).Docs sync:
docs/architecture.md§ Schema reflects every new table + column;docs/glossary.mdgains 10 new entries;docs/golden-queries.md+fixtures/golden/regenerated. Templates (templates/agents/) updated with the new schema overview + trigger patterns.
-
#35
119db38Thanks @SutuSebastian! - feat(mcp):codemap mcp— Model Context Protocol server (agent-transports v1)Adds the
codemap mcptop-level command — boots an MCP server over stdio so agent hosts (Claude Code, Cursor, Codex, generic MCP clients) call codemap as JSON-RPC tools instead of shelling out per query. Eliminates the bash round-trip on every agent invocation.Surface (one tool per CLI verb plus
query_batch, all snake_case):query,query_batch,query_recipe,audit,save_baseline,list_baselines,drop_baseline,context,validate- Resources:
codemap://recipes,codemap://recipes/{id},codemap://schema,codemap://skill(lazy-cached)
query_batchis MCP-only — N statements in one round-trip with batch-wide-defaults + per-statement-overrides (items arestring | {sql, summary?, changed_since?, group_by?}). Per-statement errors are isolated.save_baselineships as one polymorphic tool ({name, sql? | recipe?}with runtime exclusivity check) mirroring the CLI's single--save-baseline=<name>verb.Output shape is verbatim from each tool's CLI counterpart's
--jsonenvelope (no re-mapping). Bootstrap once at server boot; tool handlers reuse existing engine entry-points (executeQuery,runAudit, etc.) — no duplicate business logic.New dep:
@modelcontextprotocol/sdk.HTTP API (
codemap serve) stays in roadmap backlog; design points (tool taxonomy + output shape) are reserved indocs/architecture.md § MCP wiringso HTTP inherits them when a concrete consumer asks. -
#74
7889fedThanks @SutuSebastian! -codemap audit --format <text|json|sarif>— emit a SARIF 2.1.0 doc directly from the audit envelope, no JSON→SARIF transform step needed. One rule per delta key (codemap.audit.files-added,codemap.audit.dependencies-added,codemap.audit.deprecated-added); one result peraddedrow; severity =warning(audit deltas are more actionable than per-recipenote). Locations auto-detected via the samefile_path/path/to_path/from_pathpriority list thatquery --format sarifuses; line ranges (line_start/line_end) populate the SARIFregion. Pure output-formatter addition on top of the existing audit envelope; no schema impact.--jsonstays as the shortcut for--format json(backward-compatible).--json+--format <other>rejected as a contradiction.--summaryis a no-op with--format sarif(SARIF results are per-row, not counts) and surfaces a stderr warning.removedrows are intentionally excluded from SARIF output — SARIF surfaces findings to act on, not cleanups. Location-only rows (e.g. files-added has onlypath) get a "new files: src/foo.ts" message instead of the generic "(no message)" fallback.This is the first half of Slice 1 from the GitHub Marketplace Action plan — independently useful for any CI consumer running
codemap auditwho wants Code Scanning surface without a translation layer; required for the upcoming Marketplace Action's headline default command. -
#72
2c3045dThanks @SutuSebastian! - feat(boundaries): config-driven architecture-boundary rules +boundary-violationsrecipeAdds the smallest substrate for first-class architecture boundary checks. Schema bump 8 → 9.
Configure
import { defineConfig } from "@stainless-code/codemap"; export default defineConfig({ boundaries: [ { name: "ui-cant-touch-server", from_glob: "src/ui/**", to_glob: "src/server/**", }, ], });
actiondefaults to"deny"(the only shape v1 surfaces);"allow"reserves the slot for future whitelist semantics.Substrate
- New config field
boundaries: BoundaryRule[]on the Zod user-config schema (src/config.ts); validated at config-load time. - New table
boundary_rules(name PK, from_glob, to_glob, action CHECK IN ('deny','allow'))(STRICT, WITHOUT ROWID) — fully derived from config, dropped on--full/SCHEMA_VERSIONrebuilds and re-filled by the next index pass. - New helper
reconcileBoundaryRules(db, rules)insrc/db.ts; called fromrunCodemapIndexaftercreateSchemaso the table tracks config exactly. - New runtime accessor
getBoundaryRules().
Recipe
templates/recipes/boundary-violations.{sql,md}joinsdependencies×boundary_rulesvia SQLiteGLOBand surfaces violating import edges as locatable rows.--format sarifand--format annotationslight up automatically (the recipe aliasesdependencies.from_pathtofile_path). Use as a CI gate:codemap query --recipe boundary-violations --format sarif > findings.sarifLockstep
docs/architecture.md§ Schema gains aboundary_rulessubsection.docs/glossary.mdaddsboundaries/boundary_rules/boundary-violationsentry.docs/roadmap.md § Backlogremoves the now-shipped item per Rule 2.templates/agents/rules/codemap.md,.agents/rules/codemap.md,templates/agents/skills/codemap/SKILL.md,.agents/skills/codemap/SKILL.md, andREADME.mdall document the new shape.
Tests
src/application/boundary-rules.test.tscovers schema creation, idempotent reconciliation, CHECK constraint, and the recipe SQL against a synthetic dependency graph.src/config.test.tscovers Zod validation including default-action filling and unknown-action rejection. - New config field
-
#52
fe5a355Thanks @SutuSebastian! -codemap audit --base <ref>— ad-hoc structural-drift audit against any git committish (origin/main,HEAD~5,<sha>, tag, …). Closes the highest-frequency post-watch agent loop: "what changed structurally between this branch andorigin/main?". Replaces today's 3-step--baselinedance (switch branches, reindex, save baselines, switch back) with one verb.Three transports, one engine:
- CLI:
codemap audit --base <ref> [--<delta>-baseline <name>] [--summary] [--json] [--no-index] - MCP tool:
auditwith newbase?: stringarg - HTTP:
POST /tool/audit(auto-wired via the existing dispatcher)
All three dispatch the same pure
runAuditFromRefengine inapplication/audit-engine.ts.How it works:
git rev-parse --verify "<ref>^{commit}"resolves<ref>to a sha (clean error on non-git or unresolvable ref).- Cache lookup at
<projectRoot>/.codemap/audit-cache/<sha>/.codemap.db. Hit → sub-100ms; miss → continue. - Atomic populate —
git worktree addto a per-pid temp dir +runCodemapIndex({mode: "full"})against the worktree's.codemap.db+ POSIXrenameclaims the final<sha>/slot. Concurrent CI matrix runs against the same sha race-safely without lock files (loser's rename fails with EEXIST → falls through to cache hit). - Run each delta's canonical SQL on the cached DB vs the live DB;
diffRows(existing helper) computes{added, removed}. - Compose
AuditEnvelopewith per-deltabase.source: "ref"(new value) +base.ref(user-supplied string) +base.sha(resolved).
Decisions worth knowing:
AuditBaseis now a discriminated union — existing{source: "baseline", name, sha, indexed_at}rows untouched; new{source: "ref", ref, sha, indexed_at}arm. Consumers narrowing onbase.sourcekeep compiling.- Mutually exclusive with
--baseline <prefix>. Parser + handler both guard. Per-delta--<key>-baselineoverrides compose orthogonally with both, so--base origin/main --files-baseline pre-refactor-filesis valid (mixed sources). - Eviction: hardcoded LRU 5 entries / 500 MiB;
git worktree remove --force+rm -rffor each victim. Orphan.tmp.*dirs older than 10 min get swept on the next cycle. No config knobs in v1; defer to v1.x+ if real consumers ask. - Hard error on non-git projects. No graceful fallback — there's no meaningful "ref" without git. The other audit modes (
--baseline,--<delta>-baseline) still work without git. - Env hygiene. All git spawns in
audit-worktree.tsstrip inheritedGIT_*env vars so a containing git operation (e.g. running codemap from a husky hook) doesn't route worktree calls at the wrong index.
Auto-
.gitignore:codemap agents initnow adds.codemap/audit-cache/alongside.codemap.*so cached worktrees never get committed..codemap/recipes/stays git-tracked. - CLI:
-
#54
1313fc2Thanks @SutuSebastian! -.codemap/directory consolidation + self-healing files. Every codemap-managed path lives under a single configurable state directory (default.codemap/, override via--state-dir <path>orCODEMAP_STATE_DIR). Cleans up the dual-pattern surface (<root>/.codemap.db+<root>/.codemap/<thing>/) that's been growing with every cache PR; collapses the user.gitignorepatching surface to zero.New layout:
<root>/ └── .codemap/ ← override via --state-dir / CODEMAP_STATE_DIR ├── .gitignore ← codemap-managed (self-healing); tracked ├── config.{ts,js,json} ← was <root>/codemap.config.*; tracked ├── recipes/ ← user-authored SQL; tracked (existing) ├── index.db ← was .codemap.db ├── index.db-shm ← was .codemap.db-shm ├── index.db-wal ← was .codemap.db-wal └── audit-cache/ ← was .codemap/audit-cache/ (existing)Self-healing files (D11):
<state-dir>/.gitignoreand<state-dir>/config.jsonare owned by idempotentensure*reconcilers (src/application/state-dir.ts,src/application/state-config.ts) that run on every codemap boot — read → validate → reconcile → write only on drift. The setup logic IS the migration: future codemap versions add new generated artifacts toSTATE_GITIGNORE_BODY(or extend the Zod schema), and every consumer's project repairs itself on the nextcodemapinvocation. No more per-feature.gitignorepatching inagents-init.ts.Pre-v1 — no migration shim:
<root>/.codemap.db→<state-dir>/index.db(rename basename)<root>/codemap.config.{ts,json}→<state-dir>/config.{ts,js,json}(move file)- Existing dev clones:
rm .codemap.db .codemap.db-shm .codemap.db-walonce and re-index; movecodemap.config.*into.codemap/(or set--config <old-path>to keep using the legacy location explicitly).
New flags + env:
--state-dir <path>— override the state directory (resolves relative to project root).CODEMAP_STATE_DIR— same, env-var form.
Internal refactor: new
src/cli/bootstrap-codemap.tsextracts theloadUserConfig + resolveCodemapConfig + initCodemap + configureResolverdance from 9 cmd-* files into one helper that also runs the self-healing reconcilers. Adding a new self-healing file is now a one-line addition there.Inspired by flowbite-react's
.flowbite-react/.gitignore+setup-*pattern; expressed in codemap's own conventions (ensure*reconcilers, Zod schema asz.infersource of truth, pure{before, after, written}return shapes for testability). -
#50
90092aeThanks @SutuSebastian! -codemap impact <target>— symbol/file blast-radius walker. Replaces hand-composedWITH RECURSIVEqueries that agents struggle to write reliably with a single verb that walks the calls / dependencies / imports graphs (callers, callees, dependents, dependencies). Depth- and limit-bounded, cycle-detected.Three transports, one engine:
- CLI:
codemap impact <target> [--direction up|down|both] [--depth N] [--via dependencies|calls|imports|all] [--limit N] [--summary] [--json] - MCP tool:
impact(registered alongsideshow/snippet) - HTTP:
POST /tool/impact
All three dispatch the same pure
findImpactengine inapplication/impact-engine.tsper the post-PR #41 layering — adding tools never duplicates business logic.Decisions worth knowing:
- Target auto-resolution. Contains
/or matchesfiles.path→ file target; otherwise symbol (case-sensitive, exact). Symbol targets walkcalls; file targets walkdependencies+imports(resolved_pathonly). Mismatched explicit--viachoices land inskipped_backends(no error — agent sees why their selection yielded fewer rows than expected). - Cycle detection. SQLite has no native cycle predicate; we materialise a comma-bounded path string per row and
instrit to break re-entry. Bounded depth +--limit(default 500) keep cyclic graphs cheap regardless.--depth 0walks unbounded but stays cycle-detected and limit-capped. - Termination classification.
summary.terminated_by:limit>depth>exhausted. CI gates can branch on it. --summaryshape. Trims thematchesarray but preservessummary.nodes— thejq '.summary.nodes'consumption pattern still works.- No SARIF / annotations. Impact rows are graph traversals, not findings — wrong shape for those formats.
Engine sketch: one
WITH RECURSIVEquery per (direction, backend) combo, JS-side merge + dedup by(direction, kind, name?, file_path)keeping the shallowest depth, thensummary.by_kind+terminated_byclassification. - CLI:
-
#44
4ec51d8Thanks @SutuSebastian! -codemap serve— HTTP server exposing the same tool taxonomy ascodemap mcpoverPOST /tool/{name}. For non-MCP consumers (CI scripts, simplecurl, IDE plugins that don't speak MCP).Default bind
127.0.0.1:7878(loopback only — refuse0.0.0.0unless explicitly opted in via--host 0.0.0.0). Optional--token <secret>requiresAuthorization: Bearer <secret>on every request;GET /healthis auth-exempt so liveness probes work without leaking the token. Barenode:http(no Express / Fastify dep) — runs on Bun + Node.Routes:
POST /tool/{name}— every MCP tool (query, query_recipe, query_batch, audit, context, validate, show, snippet, save_baseline, list_baselines, drop_baseline). Body{<args>}; response = samecodemap query --jsonenvelope (NOT MCP's{content: [...]}wrapper).format: "sarif"payloads ship asapplication/sarif+json;format: "annotations"astext/plain.GET /resources/{encoded-uri}— mirror of MCP resources (codemap://recipes,codemap://recipes/{id},codemap://schema,codemap://skill).GET /health— liveness (auth-exempt);GET /tools/GET /resources— catalogs.- Errors:
{"error": "..."}with HTTP status 400 / 401 / 404 / 500. - Every response carries
X-Codemap-Version: <semver>so consumers can pin / detect upgrades.
Internals: Tool bodies (
application/tool-handlers.ts) and resource fetchers (application/resource-handlers.ts) are pure transport-agnostic — same handlerscodemap mcpdispatches. No engine duplication;mcp-server.tsandhttp-server.tsboth wrap the sameToolResultdiscriminated union.Security: CSRF + DNS-rebinding guard rejects requests with
Sec-Fetch-Site: cross-site/same-site(modern-browser CSRF), anyOriginheader that isn'tnull(older-browser CSRF), andHostheader mismatch on loopback bind (DNS rebinding) — runs on every request including auth-exempt/health. Defends against a malicious local webpagefetch-ing the API while the developer is browsing. Non-browser clients (curl, MCP hosts, CI scripts) don't send those headers and pass through. SIGINT / SIGTERM → graceful drain. 1 MiB request-body cap (DoS protection). SQLite reader concurrency handles parallel requests;PRAGMA query_only = 1set per connection. -
#47
5ef9ce4Thanks @SutuSebastian! -codemap watch— long-running process that re-indexes changed files in real time so every CLI / MCP / HTTP query reads live data without a per-query reindex prelude. Eliminates the single biggest source of agent-side friction: "is the index stale right now?"Three shapes:
- Standalone:
codemap watch [--debounce 250] [--quiet]— foreground process; logsreindex N file(s) in Mmsper batch unless--quiet. SIGINT / SIGTERM drains pending edits. - MCP killer combo:
codemap mcp --watch [--debounce <ms>]— boots stdio MCP server + watcher in one process. Long Cursor / Claude Code sessions never hit a stale index; agents stop having to remember to reindex between edit + query. - HTTP killer combo:
codemap serve --watch [--debounce <ms>]— same shape for non-MCP consumers (CI scripts, IDE plugins, simplecurl).
Audit prelude optimization: when watch is active,
mcp audit's default incremental-index prelude becomes a no-op (the watcher already keeps the index fresh — saves the per-request reindex cost). Explicitno_index: falsestill forces the prelude.Env shortcut:
CODEMAP_WATCH=1(or"true") implies--watchformcp/serve— useful for IDE / CI launches that can't easily edit the spawn command.Backend: chokidar v5 (selected via 6-watcher audit in PR #46). Pure JS — runs identically on Bun + Node, no per-runtime branching, no native compile matrix on top of
bun:sqlite/better-sqlite3. Cross-platform (macOS / Linux / Windows / WSL). Atomic-write + chunked-write detection out of the box. 1 dep (readdirp), 82 KB.Filtering: Only paths the indexer cares about trigger a reindex (TS / TSX / JS / JSX / CSS + project-local recipes under
<root>/.codemap/recipes/).node_modules/.git/dist/ configuredexcludeDirNamesare skipped. - Standalone:
-
#57
b5679a6Thanks @SutuSebastian! -codemap ingest-coverage <path>— static coverage ingestion. Reads Istanbul JSON (coverage-final.json) or LCOV (lcov.info) into a newcoveragetable joinable tosymbols, so structural queries can compose coverage filters in pure SQL — no runtime tracer, no paid coverage stack.Both formats land in v1 (Istanbul + LCOV) so every test runner is a first-class consumer on day one —
vitest --coverage,jest --coverage,c8,nyc(Istanbul JSON), andbun test --coverage(LCOV) all work without waiting on a follow-up release.Bundled recipes (auto-discovered, no opt-in needed):
untested-and-dead— exported functions with no callers AND zero coverage; the killer recipe combining structural and runtime evidence axes.files-by-coverage— files ranked ascending by statement coverage.worst-covered-exports— top-20 worst-covered exported functions.
Each recipe ships a frontmatter
actionsblock so agents see per-row follow-up hints in--jsonoutput.Schema:
- New
coveragetable with natural-key PK(file_path, name, line_start)— intentionally not a FK tosymbols.idso coverage rows survive thesymbolsdrop-recreate cycle on every--fullreindex. idx_coverage_file_namecovers the typical join shape and theGROUP BY file_pathscan used by thefiles-by-coveragerecipe.- Three new
metakeys (coverage_last_ingested_at/_path/_format) record ingest freshness. SCHEMA_VERSION5 → 6 — auto-rebuilds on nextcodemaprun; the new table is empty until firstingest-coverageinvocation. Subsequent bumps preserve coverage data via thedropAll()exclusion.
CLI:
codemap ingest-coverage coverage/coverage-final.json # Istanbul (auto-detected) codemap ingest-coverage coverage/lcov.info # LCOV (auto-detected) codemap ingest-coverage coverage --json # directory probe (errors if both files present) codemap query --json --recipe untested-and-dead # the killer query
No
--sourceflag — format is auto-detected from extension. No MCP / HTTP transport in v1 — coverage exposes as a SQL column, composable with every existing recipe and ad-hoc query through the existingquery/query_recipetools (no parallel surface).Plan: PR #56 (merged). Implementation: this PR.
-
#71
fc6790bThanks @SutuSebastian! - feat(query): add--format diffand--format diff-jsonAdds transport-agnostic diff formatters for query row sets shaped as:
SELECT 'src/file.ts' AS file_path, 42 AS line_start, 'oldName' AS before_pattern, 'newName' AS after_pattern
--format diffemits plain unified diff text, ready forgit apply --check.--format diff-jsonemits{files, warnings, summary}for agents that need structured hunks.- Source files are read at format time. If a file is missing or the indexed line no longer contains
before_pattern, the formatter marks itmissing/staleindiff-jsonand emits# WARNING:comments at the top of plain diff output. - Same formatter support is exposed through MCP / HTTP
format: "diff" | "diff-json"onqueryandquery_recipe.
This is read-only preview infrastructure — codemap never writes files.
-
#74
7889fedThanks @SutuSebastian! - GitHub Marketplace Action — Slices 1b-4 ofdocs/plans/github-marketplace-action.md. v1.0 readiness;action.ymlis now installable via- uses: stainless-code/codemap@v1once the corresponding tag is published.--ciaggregate flag (Slice 1b) onquery+audit. Aliases--format sarif+process.exitCode = 1on findings/additions + suppresses no-locatable-rows stderr warning. Mutually exclusive with--jsonand--format <other>. Parser rejects contradictions with helpful errors.action.yml+scripts/detect-pm.mjs(Slice 2). Composite Action wrapping the codemap CLI. ~16 declarative inputs across 3 categories (where to run / what to run / what to do with output); Q1 resolution. Default α command onpull_requestevents:audit --base ${{ github.base_ref }} --ci; no-op on other events unless an explicitcommand:input is passed. Package-manager autodetection delegates topackage-manager-detector(antfu/userquin, MIT, 0 transitive deps); CLI invocation resolution via the library's'execute-local'/'execute'intents.codemap pr-comment(Slice 3). New CLI verb that renders a markdown PR-summary comment from a codemap-audit-JSON envelope or a SARIF doc. Auto-detects input shape;--shape audit|sarifoverrides. Reads from a file or stdin (-).--jsonenvelope emits{ markdown, findings_count, kind }for action.yml steps. Closes the SARIF→Code-Scanning gap for: private repos without GHAS, repos that haven't enabled Code Scanning, aggregate audit deltas without a single file:line anchor, trend / delta narratives, and bot-context seeding (review bots read PR conversation, not workflow artifacts). v1.0 ships the (b) summary-comment shape per Q4 resolution; (c) inline-review comments deferred to v1.x.Dogfood (Slice 4). New
action-smokejob in.github/workflows/ci.ymlrunsuses: ./on every PR withcommand: --versionto validate the composite-step flow + npm-pulled codemap binary. Non-blocking until v1.0.0 ships (at which point the smoke gates the build).Engine + CLI separation discipline preserved:
pr-comment-engine.tsis pure;cmd-pr-comment.tswraps it. Tests cover the engine (12 cases) and the CLI parser (4 audit + 4 query tests for--ci).Lockstep agent updates (per
docs/README.mdRule 10):.agents/rules/codemap.md+templates/agents/rules/codemap.mdgain rows for--ciandpr-commentso installed agents and this clone's session view stay in lockstep.Slice 5 (Marketplace publish + listing metadata) is post-merge — gated on a v1.0.0 tag.
-
31479a5Thanks @SutuSebastian! - feat(mcp/serve): default-ON watcher forcodemap mcpandcodemap serveStale-index friction is empirically the most-frequent agent UX issue under
codemap mcp(driving the watch-mode planning in PR #46) and the most-frequent CI/IDE-plugin friction undercodemap serve. Both modes are inherently long-running, so the chokidar co-process pays for itself immediately. Decision originally resolved 2026-05 (research note§ 6 Q1); this PR ships it.New defaults.
codemap mcp— watcher boots automatically; tools always read a live index.codemap serve— same.- One-shot CLI defaults preserved:
codemap query/codemap show/codemap snippet/ etc. still spawn no watcher.
Opt out.
--no-watchflag (new) — explicit opt-out for ephemeral-index workflows, fire-and-forget CI scripts, etc.CODEMAP_WATCH=0/CODEMAP_WATCH="false"— env-shortcut mirroring--no-watchfor IDE / CI launches that can't easily edit the spawn command.
Backwards-compat preserved.
--watchflag still parses and is honored (no-op since it matches the new default; kept so existing scripts and launch commands don't break).CODEMAP_WATCH=1/CODEMAP_WATCH="true"still parses (redundant after the flip, kept for backwards-compat).--no-watchwins over--watchwhen both passed (last-write semantics).
Tradeoffs accepted.
- Slightly slower mcp/serve startup (~chokidar boot cost, validated tiny on Bun + Node by PR #46's 6-watcher audit).
- Spawns a second process — visible to users running
htop/Activity Monitor. Worth it for the live-index correctness gain.
Tests: 12 new tests across
cmd-mcp.test.tsandcmd-serve.test.tscover default-ON behavior,--no-watchopt-out, env opt-out (CODEMAP_WATCH=0/"false"), env opt-in still honored (CODEMAP_WATCH=1), and--no-watchwins over--watch.Lockstep updates:
templates/agents/rules/codemap.md,templates/agents/skills/codemap/SKILL.md,.agents/rules/codemap.md,.agents/skills/codemap/SKILL.md, andREADME.mdall updated to reflect the new defaults + opt-out shape perdocs/README.mdRule 10. -
#71
fc6790bThanks @SutuSebastian! - feat(recipes): parametrised recipe support +find-symbol-by-kindRecipes may now declare
paramsin sibling<id>.mdfrontmatter and consume values through positional?placeholders in SQL. Values validate before SQL binding and supportstring,number, andbooleantypes.CLI
codemap query --recipe <id> --params key=value[,key=value]--paramsmay be repeated; duplicate keys use last-write semantics.- Values may contain
=(split on first equals). Values containing literal commas should use repeated--params. - Param validation is strict: missing required, unknown, and malformed values return
{error}.
MCP / HTTP
query_recipeacceptsparams: {key: value}.- HTTP
POST /tool/query_recipeuses the same shape.
Catalog
--recipes-json,codemap://recipes, andcodemap://recipes/{id}expose theparamsdeclaration for each parametrised recipe.
Example bundled recipe
find-symbol-by-kinddemonstrates the new path:codemap query --json --recipe find-symbol-by-kind --params kind=function,name_pattern=%Query%
No schema bump. Runtime remains read-only via
PRAGMA query_only=1; params are bound through SQLite placeholders, not string interpolation. -
#30
a309d52Thanks @SutuSebastian! -codemap query --save-baseline/--baseline— snapshot a query result set and diff against it later. Stored in the newquery_baselinestable inside.codemap.db(no parallel JSON files).--baselineslists saved snapshots,--drop-baseline <name>deletes one. Diff identity is per-rowJSON.stringifyequality;--summarycollapses to{added: N, removed: N}. Recipeactionsattach to theaddedrows when running under--baseline. Baselines survive--fulland SCHEMA rebuilds.SCHEMA_VERSIONbumps from 4 to 5. -
#37
5110b1aThanks @SutuSebastian! - feat(recipes): recipes-as-content registry — bundled .md siblings + project-local recipesTwo complementary capabilities:
-
Bundled recipes get richer descriptions. Every bundled recipe in
templates/recipes/is now a<id>.sqlfile paired with an optional<id>.mddescription body (replaces the inline TypeScript map insrc/cli/query-recipes.ts). Per-rowactionstemplates live in YAML frontmatter on the.mdinstead of code. Same surface for end users (--recipe <id>/--recipes-json/codemap://recipes); single storage shape across bundled + project recipes. -
Project-local recipes — drop
<id>.{sql,md}files into<projectRoot>/.codemap/recipes/to ship team-internal SQL as first- class recipes. Auto-discovered via--recipe <id>, surfaced in--recipes-jsonand thecodemap://recipesMCP resource alongside bundled. Project recipes win on id collision; the catalog entry carriesshadows: trueon overrides so agents reading the catalog at session start see when a recipe behaves differently from the documented bundled version (per-execution response shape stays unchanged — uniformity contract preserved).
Catalog entries (
--recipes-jsonoutput,codemap://recipespayload) gain three additive fields:body(full Markdown body),source("bundled" | "project"), andshadows?(true on project entries that override a bundled id). Existing consumers that destructure{id, description, sql, actions?}keep working.Validation: load-time lexical scan rejects DML / DDL keywords (
INSERT/UPDATE/DELETE/DROP/CREATE/ALTER/ATTACH/DETACH/REPLACE/TRUNCATE/VACUUM/PRAGMA) in recipe SQL with recipe-aware error messages — defence in depth alongside the runtimePRAGMA query_only=1backstop inquery-engine.tsshipped in the previous release.Implementation: pure transport-agnostic loader in
src/application/recipes-loader.ts; thin shim insrc/cli/query-recipes.tspreserves backwards-compat exports (QUERY_RECIPES,getQueryRecipeSql, etc.). Hand-rolled YAML frontmatter parser scoped to theactionsshape (nojs-yamldependency)..codemap.dbis gitignored as before;.codemap/recipes/is NOT (verified viagit check-ignore) — recipes are git-tracked source code authored for human review. -
-
#71
fc6790bThanks @SutuSebastian! - feat(recipes): add read-onlyrename-previewrecipeAdds a conservative
rename-previewbundled recipe that composes the new parametrised recipe infrastructure with the new diff formatters:codemap query --recipe rename-preview \ --params old=usePermissions,new=useAccess,kind=function \ --format diff
The v1 recipe emits rows shaped for
--format diff/diff-jsonand covers:- symbol definition lines from
symbols - direct named import specifier lines from
imports.specifierswhenimports.resolved_pathpoints at the target symbol file
It intentionally does not cover call sites, re-export alias chains, string literals, comments, dynamic dispatch, or template-literal property access yet. Those require more precise source-location substrate (for calls / exports) or non-structural search. The recipe
.mddocuments the caveats clearly and repeats the key product-floor rule: codemap never writes files; this is a preview for review /git apply --check.Parameters:
old(required string)new(required string)kind(optional string)in_file(optional string path prefix)include_tests(optional boolean, default true)include_re_exports(optional boolean, default true; reserved until export locations are indexed)
- symbol definition lines from
-
#43
4061ac3Thanks @SutuSebastian! -codemap query --format <text|json|sarif|annotations>— pipe any recipe row-set into GitHub Code Scanning (SARIF 2.1.0) or surface findings inline on PRs (GH Actions::notice file=…,line=…::msg). Pure output-formatter additions on top of the existing JSON pipeline; no schema impact.Auto-detects file-path columns (
file_path/path/to_path/from_pathpriority) andline_start(+ optionalline_end) for SARIF region. Aggregate recipes without locations (index-summary,markers-by-kind) emitresults: []+ a stderr warning. Rule id iscodemap.<recipe-id>for--recipe,codemap.adhocfor ad-hoc SQL. Defaultresult.levelis"note"; per-recipe overrides via<id>.mdfrontmatter (sarifLevel,sarifMessage,sarifRuleId) deferred to v1.x.--formatoverrides--jsonwhen both passed;--jsonstays as the alias for--format json. Incompatible with--summary/--group-by/ baseline (different output shapes — sarif/annotations only support flat row lists).MCP
queryandquery_recipetools accept the sameformat: "sarif" | "annotations"argument;query_batchdeferred to v1.x. -
#75
ba01d81Thanks @SutuSebastian! -suppressionssubstrate — opt-in recipe-suppression markers parsed from source comments. The markers parser now recognises// codemap-ignore-next-line <recipe-id>and// codemap-ignore-file <recipe-id>(also#,--,<!--,/*leaders for non-JS files) and writes them to a newsuppressions(file_path, line_number, recipe_id)table. Two scopes encoded byline_number: positive = next-line (the directive sits one line above;line_numberpoints at the suppressed line),0= file scope.Recipe authors opt in via
LEFT JOIN suppressions s ON s.file_path = … AND s.recipe_id = '<id>' AND (s.line_number = 0 OR s.line_number = <row's line>) WHERE s.id IS NULL. Ad-hoc SQL is unaffected. Bundled recipes that opt in today:untested-and-dead(line + file) andunimported-exports(file only —exportshas noline_numbercolumn, so per-line suppression isn't expressible there).Stays consistent with the "no opinionated rule engine" Floor — no severity, no suppression-by-default, no universal-honor model. The suppression is consumer-chosen substrate: recipe authors choose whether to honor it; consumers can override per recipe by writing project-local SQL that ignores suppressions or filters differently. The leader regex requires the directive to start a line (modulo whitespace) so directives never match inside string literals — both this clone's tests and recipe
.mdexamples use the directive text in prose without polluting the index.Schema bumps to 10 —
--fullrebuild auto-runs on next index pass.dropAll()includessuppressions(index-data table, not user data). Surfaced in agent rules + skills + glossary + architecture schema docs per Rule 10. -
#39
7460b46Thanks @SutuSebastian! - feat(show + snippet): targeted-read CLI verbs + MCP toolsTwo sibling verbs that close the "agent wants to read this thing" loop without composing SQL:
codemap show <name>— returns metadata (file_path:line_start-line_end+signature+kind) for the symbol(s) matching the exact name (case-sensitive).codemap snippet <name>— same lookup; each match also carriessource(file lines from disk),stale(true when content_hash drifted since indexing),missing(true when file is gone).
Both share the same flag set (
--kind <k>filter,--in <path>file scope — directory prefix or exact file, normalized via the existingtoProjectRelativehelper for cross-platform consistency).Output is the agent-friendly
{matches, disambiguation?}envelope on both CLI--jsonand MCP responses (uniformity contract per the MCP plan). Single match →{matches: [{...}]}; multi-match addsdisambiguation: {n, by_kind, files, hint}— structured aids so the agent narrows without scanning every row. Forward-extensible (futurenearest_to_cursor/most_recently_modified/caller_countfields land as additive keys).MCP tools
showandsnippetregister parallel to the CLI verbs and auto-inherit the same envelope shape.Stale-file behavior on snippet:
sourceis always returned when the file exists;stale: trueis metadata the agent reads. No refusal, no auto-reindex side-effects — read tool stays read-only.Architecturally: pure transport-agnostic engine in
src/application/show-engine.ts(mirrors the cmd-_ ↔ _-engine seam from PRs #33 / #35 / #37); thin CLI verbs insrc/cli/cmd-show.tssrc/cli/cmd-snippet.ts. ReusesfindSymbolsByName,hashContent(fromsrc/hash.ts),toProjectRelative(now exported fromcmd-validate.ts), andfiles.content_hash— same primitives the existingvalidatecommand already uses for stale detection. No schema change.
Test coverage: 19 engine tests (lookup variants, line slicing, stale detection, missing files), 13 cmd-show parser/envelope tests, 11 cmd-snippet parser/envelope/stale tests, 8 in-process MCP integration tests via
@modelcontextprotocol/sdk'sInMemoryTransport. -
#75
ba01d81Thanks @SutuSebastian! -codemap ingest-coverage --runtime <dir>— V8 runtime coverage parser. Reads aNODE_V8_COVERAGE=...-style directory (one or morecoverage-<pid>-<ts>-<seq>.jsonfiles) and dispatches to the existingupsertCoverageRowscore through a newingestV8parser. Each script's byte-offset ranges are converted to per-line hit counts via innermost-wins range walking (smaller, more specific ranges override the function-as-a-whole count — matches V8's documented semantics). Skips non-file://URLs (Node internals,evalmachine.<anonymous>); merges duplicate-URL scripts across dumps so multi-process test runs don't inflatetotal_statements.Format auto-detection is unchanged for files (
.json→ istanbul,.info→ lcov, directory of either → probe both with explicit-error on ambiguity);--runtimeis the explicit opt-in for V8 directories. Thecoveragetable schema doesn't move — V8 rows write through the same(file_path, name, line_start, hit_statements, total_statements, coverage_pct)projection, so every existing JOIN (untested-and-dead,files-by-coverage,worst-covered-exports) works unchanged.Useful for "delete cold code with stronger evidence" agent flows: production-style traces from real test runs feed the same recipes that consume Istanbul/LCOV today. Local-only — SaaS aggregation explicitly out of scope (different product class). The parser stays in-process; no aggregation server, no upload primitive. New
format: "v8"arm on the result envelope; existing"istanbul" | "lcov"consumers don't break.Engine module:
application/coverage-engine.ts(addedingestV8,V8ScriptCoverage,V8FunctionCoverage,V8CoveragePayloadexports). CLI module:cli/cmd-ingest-coverage.ts(added--runtimeflag,resolveV8Directoryhelper that reads every top-level*.jsonin the directory and merges theirresultarrays). Pure additive —--jsonoutput gains"format": "v8"as a possible value.
-
#33
114303fThanks @SutuSebastian! -codemap audit(B.5 v1) — structural-drift command emitting{head, deltas}where eachdeltas[<key>]carries{base, added, removed}. Three v1 deltas:files,dependencies,deprecated. Two snapshot-source shapes —--baseline <prefix>(auto-resolves<prefix>-files/<prefix>-dependencies/<prefix>-deprecatedinquery_baselines) and--<delta>-baseline <name>(explicit per-delta override; composes with--baseline). Reuses B.6 baselines; no schema bump.--summarycollapses to per-delta counts;--no-indexskips the auto-incremental-index prelude. v1 ships noverdict/ threshold config — consumers compose--json+jqfor CI exit codes (v1.x slice).--base <ref>(worktree+reindex snapshot) defers to v1.x. -
#70
db2f27aThanks @SutuSebastian! - feat(complexity): cyclomatic complexity column onsymbols+ bundled recipe (research note § 1.4 ship-pick (c))Adds per-function cyclomatic complexity computed during AST walking. Schema bump
SCHEMA_VERSION7 → 8 — first reindex after upgrade triggers a full rebuild via the existing version-mismatch path.What lands:
- New
complexity REALcolumn onsymbols. Computed via McCabe formula (1 + decision points) for function-shaped symbols (top-levelfunctiondeclarations + arrow-function consts).NULLfor non-functions (interfaces, types, enums, plain consts) and class methods (v1 limitation; documented in the recipe.md). - Decision points counted:
if,while,do…while,for,for…in,for…of,case X:arms (notdefault:fall-through),&&/||/??short-circuit operators,?:ternary,catchclauses. - New bundled recipe
high-complexity-untested— function-shaped symbols with complexity ≥ 10 AND measured coverage < 50%. Combines structural + runtime evidence axes; surfaces refactor-priority candidates that single-axis recipes (untested-and-dead,worst-covered-exports) miss because they're "called but undertested."
Implementation:
- Parser visitor (
src/parser.ts) maintains acomplexityStackkeyed by symbol index. On function entry, pushes counter at 1 + symbol index. Branching-node visitors increment the top counter. On function exit, pops and writes complexity into the symbol row already pushed during entry. - Nested function declarations get their own stack entries — inner branches don't count toward the outer function. (Standard McCabe — each function counted independently.)
Pre-v1 patch per
.agents/lessons.md"changesets bump policy": schema-bumping changes are minor in semver but pre-v1 we default to patch unless the bump forces a.codemap.dbrebuild. This one does (column added; auto-detected bycreateSchema()mismatch path) — every consumer's first run after upgrade re-indexes from scratch.Agent rule + skill lockstep updated per
docs/README.mdRule 10 — bothtemplates/agents/and.agents/codemap rule + skill mention thecomplexitycolumn, the new recipe, and the cyclomatic-complexity definition.Out of scope:
- Class method complexity —
MethodDefinitionvisitor currently doesn't push to the complexity stack. Documented inhigh-complexity-untested.mdv1 limitation; refactor opportunity for class-heavy projects. - Per-class / per-file rollups —
complexityis per-symbol; project-local recipes canSUM/AVGit as needed.
- New
-
#69
560390bThanks @SutuSebastian! - feat(fts5+mermaid): opt-in FTS5 virtual table + Mermaid output formatterImplements the FTS5+Mermaid plan (
docs/plans/fts5-mermaid.md) — two non-goal flips in one PR.FTS5 (opt-in, default OFF):
- New
source_ftsvirtual table —(file_path UNINDEXED, content)columns,tokenize='porter unicode61'. Always created; populated only when toggle is on. - Toggle via
codemap.config.tsfts5: trueOR--with-ftsCLI flag at index time. CLI overrides config (logs stderr line on override). - Indexer tees file content into
source_ftsin same transaction asfilesrow insert (atomic). Worker → main serialization cost is zero on default-OFF path. - Toggle-change auto-detect via
meta.fts5_enabled— flippingfts5: false → trueauto-upgrades incremental → full rebuild sosource_ftsis consistently populated. - DB-size telemetry on first FTS5 populate:
[fts5] source_fts populated: <N> files / <X> KB. - Bundled demo recipe
text-in-deprecated-functions—@deprecatedfunctions in files containingTODO/FIXME/HACKmarkers AND coverage<50%. Demonstrates FTS5 ⨯symbols⨯coverageJOIN composability that ripgrep can't match.
Mermaid output formatter:
- New
--format mermaidoutput mode. Renders{from, to, label?, kind?}row-shape asflowchart LR. - Bounded-input contract (50-edge ceiling,
MERMAID_MAX_EDGES): unbounded inputs reject with a scope-suggestion error naming the recipe + count + scoping knobs (LIMIT/--via/WHERE). Auto-truncation deliberately out of scope (would be a verdict masquerading as an output mode). - Available across CLI, MCP
query/query_recipetools, HTTPPOST /tool/query(text/plain content type).
Schema bump:
SCHEMA_VERSION6 → 7. First reindex after upgrade triggers a full rebuild via the existing version-mismatch path; existing.codemap/index.dbis preserved (only schema-managed tables get dropped + recreated).Pre-v1 patch per
.agents/lessons.md"changesets bump policy" — additive feature, default-OFF for FTS5, behaviour-preserving for existing users (--with-ftsis opt-in; Mermaid is a new output mode).Agent rule + skill lockstep updated per
docs/README.mdRule 10 — bothtemplates/agents/and.agents/mention--with-fts,--format mermaid, the new bundled recipe, and the bounded-input contract. - New
-
#41
0134944Thanks @SutuSebastian! - Internal refactor — liftcli/*envelope builders + path helpers intoapplication/*engines soapplication/mcp-server.tsno longer reaches sideways intocli/. Affected modules:audit-engine(addedresolveAuditBaselines), newcontext-engine(buildContextEnvelope,classifyIntent,ContextEnvelope), newvalidate-engine(computeValidateRows,toProjectRelative),show-engine(addedbuildShowResult,buildSnippetResult,ShowResult,SnippetResult,SnippetMatch),query-recipesmoved fromcli/toapplication/. CLI verbs stay shells (parse / help / run / render). No behavior change, no public API change —cli/cmd-*andapplication/*are internal modules; the published surface (api.ts, thecodemapbinary, the MCP server) is untouched. -
#67
3e03db7Thanks @SutuSebastian! - feat(mcp): addcodemap://files/{path}+codemap://symbols/{name}resources (research note § 1.8)Two new MCP / HTTP resources for direct agent reads — saves the recipe-compose round-trip when the agent just wants "everything about this file" or "where is this symbol?".
codemap://files/{path}— per-file roll-up. Returns{path, language, line_count, symbols, imports, exports, coverage}.imports.specifiersparsed inline (callers don't have to JSON.parse).coverageis{measured_symbols, avg_coverage_pct, per_symbol}when coverage was ingested, elsenull. URI-encode the path.codemap://symbols/{name}— symbol lookup by exact name. Returns{matches, disambiguation?}envelope (same shape as theshowverb per PR #39). Optional?in=<path-prefix>query parameter mirrorsshow --in <path>(directory prefix or exact file).
Both reuse existing infrastructure (no schema bump):
codemap://files/queries the existing tables;codemap://symbols/reusesfindSymbolsByName+buildShowResultfromapplication/show-engine.ts.Caching policy: catalog-style resources (
recipes,schema,skill) lazy-cache as before. Data-shaped resources (files/,symbols/) read live every call — no caching, since the index can change between requests under--watch.Both available over MCP
read_resourceand HTTPGET /resources/{encoded-uri}via the existing dispatcher (no new transport plumbing).Agent rule + skill lockstep updated per
docs/README.mdRule 10 — bothtemplates/agents/and.agents/codemap rule + skill mention the new resource templates + caching policy. -
8da7f3dThanks @SutuSebastian! - docs(cli):mcp --helpandserve --helpnow list every shipped tool + resourceStale help text in
src/cli/cmd-mcp.tsandsrc/cli/cmd-serve.tslisted the original v1 tool / resource taxonomy. Updated to match what's registered today (verified againstsrc/application/mcp-server.ts):mcp --helpTools section now includesshow,snippet,impact(was missing all three).mcp --helpResources section now distinguishes lazy-cached catalog resources (recipes,recipes/{id},schema,skill) from live read-per-call resources (files/{path},symbols/{name}) — was listing only the original four.serve --helpRoutes section now includesPOST /tool/impact(was missing) and lists every mirrored MCP resource explicitly underGET /resources/{encoded-uri}(was a...ellipsis).
No behavior change — purely a documentation accuracy fix. Bundled agent rule + skill (
templates/agents/and.agents/) already enumerate the six resources correctly. -
#75
ba01d81Thanks @SutuSebastian! - Outcome-shaped CLI aliases — five thin top-level verbs that wrapquery --recipe <id>:codemap dead-code→query --recipe untested-and-deadcodemap deprecated→query --recipe deprecated-symbolscodemap boundaries→query --recipe boundary-violationscodemap hotspots→query --recipe fan-incodemap coverage-gaps→query --recipe worst-covered-exports
Every
queryflag passes through (--json,--format sarif|annotations|mermaid|diff|diff-json,--ci,--summary,--changed-since <ref>,--group-by owner|directory|package,--params key=value,--save-baseline,--baseline). Runcodemap <alias> --helpfor the wrapped recipe id.Closes the verb-obviousness gap —
codemap dead-codeis more discoverable thancodemap query --recipe untested-and-dead. Capped at five to avoid alias-sprawl perroadmap.md; promote a sixth only when the recipe becomes a headline outcome.Mapping lives in
src/cli/aliases.ts(OUTCOME_ALIASES); rewrite happens before dispatch insrc/cli/main.ts. Pure CLI surface; no schema, no engine, no new substrate. Moat-A clean — the alias is a one-linequery --recipe <id>rewrite, not a new primitive; the recipe IS the SQL. -
#76
dfbf4e1Thanks @SutuSebastian! - Recipe-recency tracking — every successful--recipecall now writes to a newrecipe_recency(recipe_id PK, last_run_at, run_count)table.--recipes-jsonand the matchingcodemap://recipes/codemap://recipes/{id}MCP resources gain inlinelast_run_at: number | null+run_count: numberfields per entry, so agent hosts can rank live recipes ahead of historic ones viajq 'sort_by(.last_run_at // 0) | reverse'. Default ON; opt-out via.codemap/configrecipeRecency: false(short-circuits before any DB write — no rows ever land).Two write sites both call
tryRecordRecipeRun(the failure-isolated wrapper aroundrecordRecipeRun) fromapplication/recipe-recency.ts:handleQueryRecipeinapplication/tool-handlers.ts(covers MCP + HTTP — both flow through it) andrunQueryCmdincli/cmd-query.ts(CLI —finallyblock keys off a localrecipeQuerySucceededflag, NOTprocess.exitCode, so--ci's deliberate exit-1-on-findings is recognised as success). Counts only successful runs; recency-write failures are swallowed with a stderr[recency] write failed: <reason>warning so they NEVER block the recipe response. The 90-day rolling window is enforced eagerly on the write path (single indexedDELETEinsiderecordRecipeRunbefore the upsert); reads filter at SELECT time (WHERE last_run_at >= cutoff) and never mutate the DB so the catalog stays side-effect free for--recipes-jsonand the MCPcodemap://recipesresources.The MCP/HTTP catalog cache was dropped — caching the JSON.stringify result alongside recency would freeze
last_run_atat first-read forever per long-runningcodemap mcp/codemap servelifetime. The underlyinglistQueryRecipeCatalog()is itself module-cached upstream, so the extra cost is one DB-read + one JSON.stringify per call. Schema / skill resources stay cached.Local-only — no upload primitive ever ships. The Floor exists to resist accumulation pressure. Sibling to
query_baselines/coverage: intentionally absent fromdropAll()so--fullandSCHEMA_VERSIONrebuilds preserve user-activity history. NoSCHEMA_VERSIONbump — the new table is purely additive and lands on existing DBs viaCREATE TABLE IF NOT EXISTSon next boot.Schema docs:
architecture.md§recipe_recency. Term entry:glossary.md. Bundled agent rule + skill (templates/agents/) + dev-side mirror (.agents/) updated in lockstep per Rule 10. -
#65
1b7a5c7Thanks @SutuSebastian! - feat(recipes): ship two new bundled recipes from research note § 1components-touching-deprecated(research note § 1.1) — UNION of two paths surfacing components that touch@deprecatedsymbols: hook path (components.hooks_usedJSON overlap) + call path (calls.caller_name = component,callee_nameis@deprecated). Hook-only variants ship false negatives — recipe spells out the explicit UNION. Action templatereview-deprecation-impact.refactor-risk-ranking(research note § 1.4) — per-file ranking by(fan_in + 1) × (100 - avg_coverage_pct). Three correctness fixes vs the naïve formula: orphans (fan_in = 0) score on coverage alone via+1; NULLcoverage_pcttreated as 0% viaCOALESCE(otherwise the row drops fromORDER BY); files with no exports excluded (no public-API surface to refactor externally). Output is per-file (not per-symbol) — empirical test showed per-symbol ranking ties on file-level fan_in. Per-symbol viacallsis a documented tuning axis for project-local override. Action templatereview-refactor-impact.
Both recipes use only existing substrate (
components,calls,symbols,dependencies,coverage,files) — no schema bump. Bundled recipe content follows the existing recipe-as-content registry pattern (PR #37); project-local overrides live at<projectRoot>/.codemap/recipes/<id>.{sql,md}.Agent rule + skill lockstep updated per
docs/README.mdRule 10 — bothtemplates/agents/(ships to npm viacodemap agents init) and.agents/(this clone's mirror) gain trigger-pattern entries, quick-reference rows, and recipe-id list updates. -
#66
f121d84Thanks @SutuSebastian! - feat(recipes): shipunimported-exportsrecipe (research note § 1.2)Surfaces exports that have no detectable import. Useful as a starting candidate list for "what's unused?" — explicitly NOT a "safe to delete" list.
V1 limitations documented in the recipe
.md:- Re-export chains not followed — false positives if A re-exports
barfrom B and consumers importbarfrom A. Tracked under research note § 1.2; future recipe with recursive CTE walkingre_export_sourcewill close the gap. - Unresolved imports ignored — when
imports.resolved_path IS NULL(codemap's resolver couldn't resolve atsconfig.jsonpath alias or external package), those rows don't count toward "used" matching. - Default exports skipped — common framework entry points (Next.js
page.tsx, Storybook stories,vite.config.ts) skipped to reduce noise. Override in project-local recipe if you want to include them.
Action template
review-for-deletion(auto_fixable: false) — agents flag for manual verification before deletion.Agent rule + skill lockstep updated per
docs/README.mdRule 10 — bothtemplates/agents/and.agents/codemap rule + skill gain trigger-pattern row, quick-reference row, and recipe-id list update. - Re-export chains not followed — false positives if A re-exports
-
#75
ba01d81Thanks @SutuSebastian! -unused-type-membersrecipe — field-level enumeration oftype_memberswhose owning type has no detectable importer in the project. Sister recipe tounimported-exports: same upstream signal at the type level, but JOINed againsttype_membersso each row carries the field's name, type annotation, optionality, and readonly flag. Useful when planning a deletion of an interface and you need the full field inventory before drafting the codemod.Strictly advisory. Codemap doesn't track property access, so the recipe inherits all of
unimported-exports's false-positive classes plus the per-field opaqueness of indexed access (T['field']),keyof T, mapped types, type spreads, destructuring, and re-export chains. Output is a STARTING POINT for human review, never a "safe to delete" list. Bundled.mddocuments every caveat and includes tuning axes for project-local overrides.unused-type-membersjoins the standard recipe taxonomy and ships intemplates/recipes/unused-type-members.{sql,md}. Reachable ascodemap query --recipe unused-type-members(or via the--format sarif/--format annotations/--ciaggregate flags); rule id iscodemap.unused-type-members. Golden-query expectation lives atfixtures/golden/minimal/unused-type-members.json. Pure recipe addition — no schema impact, no engine change.
- #28
91598bcThanks @SutuSebastian! -symbols.visibilitycolumn — JSDoc visibility tag (@public/@private/@internal/@alpha/@beta) extracted at parse time and stored as a real column. Replaces theLIKE '%@beta%'regex in thevisibility-tagsrecipe.SCHEMA_VERSIONbumps from 3 to 4 —.codemap.dbrebuilds automatically on next index. HelperextractVisibility(doc)exported fromparser.ts. New partial indexidx_symbols_visibilitycoversWHERE visibility IS NOT NULLqueries.
-
#29
03fbddfThanks @SutuSebastian! - Update bundledtemplates/agents/rule and skill to cover the recent CLI surface —codemap query --summary/--changed-since <ref>/--group-by owner|directory|package, per-row recipeactions, and the newsymbols.visibilitycolumn. The dev-side.agents/mirror is updated in lockstep so this clone stays self-consistent. -
#26
c32f052Thanks @SutuSebastian! -codemap queryTier A flags —--summary,--changed-since <ref>,--group-by owner|directory|package, plus per-rowactionstemplates on bundled recipes. All output filters; the SQL still executes against the index. Ad-hoc SQL and thecm.query()programmatic API stay unchanged.
- #23
ebd4c34Thanks @SutuSebastian! - Agent-friendly CLI surface plus a schema v3 bump that tightensNOT NULLinvariants. Existing.codemap.dbfiles auto-rebuild on first open.- New:
codemap validate [--json] [paths...]— diffs the on-disk SHA-256 of indexed files againstfiles.content_hashand prints stale / missing / unindexed rows. Lets agents skip re-reads they don't need; exits1on any drift (git-status semantics) - New:
codemap context [--compact] [--for "<intent>"]— emits a stable JSON envelope (project metadata, top hubs, recent markers, recipe catalog) for any agent or editor that wants the index in one cheap shot.--forruns lightweight intent classification (refactor / debug / test / feature / explore / other) and returns matched recipe ids plus a hint - New:
codemap --performanceflag — prints a per-phase timing breakdown (collect / parse / insert / index_create) and the top-10 slowest files by parse time during full rebuilds, for triaging giant or pathological inputs - New:
-rshort alias forcodemap query --recipe+ cleaner organizedcodemap query --help(sectioned flags, dynamic recipe-id padding, examples for both forms) - New recipes:
deprecated-symbols(@deprecatedJSDoc tag scan),visibility-tags(@internal/@private/@alpha/@beta),files-hashes(powersvalidate),barrel-files(top files by export count) - Friendlier no-
.codemap.dberror:no such table: <X>now rewrites to an actionable hint pointing atcodemap/codemap --full, on both the JSON and human paths - Public type surface: new
IndexPerformanceReport;IndexRunStats.performance?field; per-field JSDoc coverage onIndexResult,IndexRunStats,ResolvedCodemapConfig, alldb.tsrow interfaces (FileRow,SymbolRow,ImportRow,ExportRow,ComponentRow,DependencyRow,MarkerRow,CssVariableRow,CssClassRow,CssKeyframeRow,CallRow,TypeMemberRow), andParsedFile - Documentation: README now leads with a "What you get" Grep/Read vs Codemap capability table and a "Daily commands" stripe;
docs/why-codemap.mdadds a "What Codemap is not" anti-pitch section and a scenario-keyed token-savings table (single lookup → 50-turn session) replacing the earlier hand-wave - Stricter lint baseline: enabled
prefer-const,consistent-type-specifier-style,consistent-type-definitions,no-confusing-non-null-assertion,no-unnecessary-{boolean-literal-compare,template-expression,type-assertion},prefer-{includes,nullish-coalescing,optional-chain}, andunicorn/switch-case-braces - Schema v3 — tighter
NOT NULLinvariants: every column whoseRow-interface type was non-nullable is nowNOT NULLin the SQLite DDL (files.size/line_count/language/last_modified/indexed_at,symbols.line_start/line_end/signature/is_exported/is_default_export,imports.specifiers/is_type_only/line_number,exports.kind/is_default,components.hooks_used/is_default_export,markers.line_number/content,css_variables.scope/line_number,css_classes.is_module/line_number,css_keyframes.line_number,type_members.is_optional/is_readonly). Existing v2 databases auto-rebuild viacreateSchema()'s version-mismatch detector — no manual action needed
- New:
5f65c33Thanks @SutuSebastian! - Use vendor-neutral.mdextension for agent rules in templates; Cursor integration remaps to.mdcat wiring timecodemap agents initnow writes.mdrule files to.agents/rules/(plain Markdown with YAML frontmatter)- Cursor target automatically renames rules to
.mdc(required for frontmatter parsing); all other targets (Windsurf, Continue, Cline, Amazon Q) keep.md SKILL.mdnow includesnameanddescriptionfrontmatter per the Agent Skills spec
7f663beThanks @SutuSebastian! - Sync bundled agent rule template with schema v2: addtype_members,calls, anddoc_commenttrigger patterns and query rows. Add golden scenarios and fixture coverage for both new tables.
- #19
53b2c52Thanks @SutuSebastian! - Richer symbol metadata: generics, return types, JSDoc, type members, const values, symbol nesting, call graph- Signatures now include generic type parameters, return type annotations, and heritage clauses (extends/implements)
- New
doc_commentcolumn on symbols extracts leading JSDoc comments - New
type_memberstable indexes properties and methods of interfaces and object-literal types - New
valuecolumn on symbols captures const literal values (strings, numbers, booleans, null) - New
parent_namecolumn on symbols tracks scope nesting; class methods/properties/getters extracted as individual symbols - New
callstable tracks function-scoped call edges withcaller_scopefor qualified disambiguation (deduped per file) - Enum members extracted into
memberscolumn as JSON - Performance: cached scope strings, hoisted hot-path regex, batch deletes, reduced redundant I/O, BATCH_SIZE 100→500
- SCHEMA_VERSION bumped to 2
- #17
e962326Thanks @SutuSebastian! - Fix incremental detection reporting unchanged files as "changed" on every run when the working tree has uncommitted modifications.getChangedFilesnow compares content hashes against the index before including candidates, so only truly modified files enter the indexing pipeline.
-
#15
f2362f9Thanks @SutuSebastian! - Fix three HIGH-severity bugs found via cross-audit triangulation, plus performance and docs improvements.Bug fixes
- Add missing
onerrorhandler on Bun Worker — prevents silent promise hang when a parse worker crashes - Require JSX return or hook usage for component detection — eliminates false positives (e.g.
FormatCurrency()in.tsxfiles no longer indexed as a component) - Include previously-indexed files in incremental and
--filesmodes — custom-extension files indexed during--fullno longer silently go stale
Performance
- Batch CSS imports instead of inserting one-at-a-time (both full-rebuild and incremental paths)
- Add
Map<string, Statement>cache forbetter-sqlite3run()/query()— avoids ~2,000+ redundantprepare()calls on large projects - Hoist
inner.query()inwrap()to prepare once per call instead of per.get()/.all() - Skip
PRAGMA optimizeoncloseDbfor read-only query paths
Docs
- Fix Wyhash → SHA-256 in architecture.md and SKILL.md (3 locations)
- Correct
symbols.kindvalues (variable→const,type_alias→type) andexports.kindvalues - Clarify
Database.query()caching is Bun-only; Node statement cache via wrapper - Update architecture.md: component heuristic, statement cache,
closeDbreadonly, incremental/--filescustom extensions - Update benchmark.md and golden-queries.md for enriched fixture
Testing
- Enrich
fixtures/minimal/to cover all 10 indexed tables (CSS module,@keyframes,@import, non-component PascalCase export, FIXME marker) - Add 7 new golden scenarios (exports, css_variables, css_classes, css_keyframes, css_imports, markers-all-kinds, components-no-false-positives)
Cleanup
- Remove unused
analyzeDependencies: truefrom CSS parser - Deduplicate
fetchTableStats(was duplicated acrossindex-engine.tsandrun-index.ts) - Remove dead
eslint-disable-next-linedirectives (oxlint doesn't enforce those rules) - Fix
SCHEMA_VERSIONcomment (said "2", value is1)
- Add missing
- #13
13a2c14Thanks @SutuSebastian! - Replacefast-globwithtinyglobbyfor Node include globs. Smaller dependency footprint;expandDirectories: falsekeeps matching aligned with the previous behavior.
ca4b47aThanks @SutuSebastian! - Shipped agent rules and skills now lead withcodemap query --json(optional table output when--jsonis omitted). Addbun run benchmark:queryto compareconsole.tablevs JSON stdout size, plus integration tests for--jsonvs default output when.codemap.dbis present. README anddocs/(includingbenchmark.md§ Query stdout) updated to match.
- #10
9d37bd5Thanks @SutuSebastian! - Agent templates (templates/agents/)- Align
codemap.mdcandskills/codemap/SKILL.mdwith the currentcodemap query --jsoncontract (bootstrap / DB / SQL failures,process.exitCode). - SKILL:
QUERY_RECIPES-aligned fan-out SQL examples and bundled-recipe determinism note.
- Align
-
#8
889ed5bThanks @SutuSebastian! - Query CLI-
codemap query --json: print a JSON array of result rows to stdout (and{"error":"…"}on SQL errors) for agents and automation. Document that the query subcommand does not cap rows — use SQLLIMITfor bounded results. Update bundled agent rule and skill with--jsonpreference, verbatim structural answers, and generic SQL recipes (fan-out + sample targets). -
codemap query --recipe <id>for bundled read-only SQL so agents can run common structural queries without embedding SQL on the command line.--jsonworks with recipes the same way as ad-hoc SQL. Bundled ids include dependencyfan-out/fan-out-sample/fan-out-sample-json(JSON1json_group_array) /fan-in, indexindex-summary,files-largest, Reactcomponents-by-hooks(comma-based hook count, no JSON1), andmarkers-by-kind. The benchmark suite uses thefan-outrecipe SQL for an indexed-path scenario; docs clarify that recipes add no extra query cost vs pasting the same SQL. -
Recipe discovery (no index / DB):
codemap query --recipes-jsonprints all bundled recipes (id,description,sql) as JSON.codemap query --print-sql <id>prints one recipe’s SQL.listQueryRecipeCatalog()insrc/cli/query-recipes.tsis the single derived view ofQUERY_RECIPESfor the JSON output.
Golden tests
bun run test:golden: indexfixtures/minimal, run scenarios fromfixtures/golden/scenarios.json, and compare query JSON tofixtures/golden/minimal/. Usebun scripts/query-golden.ts --updateafter intentional fixture or schema changes. Documented in benchmark.md and CONTRIBUTING.
Query robustness
- With
--json,{"error":"…"}is printed for invalid SQL, database open failures, andcodemap querybootstrap failures (config / resolver setup), not only bad SQL. The CLI setsprocess.exitCodeinstead ofprocess.exitso piped stdout is not cut off mid-stream.
Benchmark &
CODEMAP_BENCHMARK_CONFIG- Each
indexedSqlin custom scenario JSON is validated as a single read-onlySELECT(orWITH…SELECT) — DDL/DML andRETURNINGare rejected before execution. - Config file paths are resolved from
process.cwd()(see benchmark.md).traditional.regexstrings are developer-controlled (local JSON);filesmode compiles the regex once per scenario. - Overlapping globs in the traditional path are deduplicated so Files read / Bytes read count each path once.
- The default components in
shop/scenario uses aLIKEfilter aligned with the traditional globs undercomponents/shop/(*.tsx and *.jsx, matchingcomponentsrows from the parser) and avoids unrelated paths such asworkshop.
Recipes (determinism)
- Bundled recipe SQL adds stable secondary
ORDER BYcolumns (and orders innerLIMITsamples) so--recipe/--jsonoutput does not vary on aggregate ties.
External QA
bun run qa:external:--max-filesand--max-symbolsmust be positive integers (invalid values throw before indexing).
-
- #6
ad29694Thanks @SutuSebastian! - Align shipped agent templates with the published CLI (codemap,npx @stainless-code/codemap, …). Keep this repository’s.agents/rule and skill dev-oriented (bun src/index.ts). Remove the redundantagents-first-conventiontemplate. Document the dev vstemplates/agents/split intemplates/agents/README.mdanddocs/agents.md.
-
#4
0a9d829Thanks @SutuSebastian! -codemap agents init: For Git repos, ensure.codemap.*is in.gitignore(create the file or append the line once).--forceremoves only template file paths (same relpaths under.agents/rules/and.agents/skills/astemplates/agents) before merging; other files under.agents/,rules/, orskills/are kept.--interactive/-i— pick IDE integrations (Cursor, GitHub Copilot, Windsurf, Continue, Cline, Amazon Q,CLAUDE.md,AGENTS.md,GEMINI.md) and symlink vs copy for rule mirrors; requires a TTY. Unknown positional arguments (e.g.interactivewithout--interactive) are rejected. Depends on@clack/prompts.Docs:
docs/agents.md;docs/README.mdindex updated. Root.gitignoreuses a single.codemap.*line.
- #1
b366c53Thanks @SutuSebastian! - Consolidate docs (index hub, packaging/Releases, benchmark vs external root), point.changeset/READMEat packaging, and addclean/check-updatesnpm scripts.
- Initial release (0.1.0): structural SQLite index, CLI (
codemap,query), programmatic API, Zod-validatedcodemap.config, Bun and Node support.