Releases: stainless-code/codemap
v0.8.0
Minor Changes
-
#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.
Patch Changes
-
#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 index --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.
v0.7.5
Patch Changes
-
#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,bun run test:golden, and thecmd-queryprint/grouped paths) 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. Existing tests pass unchanged. Anyone who relied on undocumented mutation throughCodemap.query("DELETE FROM ...")would now get an error — but that was always API abuse.Full design context:
docs/plans/perf-triangulation-rollout.md(synthesis + execution rollout of 5 independent perf/architecture audits authored 2026-05-17).
v0.7.4
Patch Changes
-
#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
v0.7.3
Patch Changes
-
#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 (
v0.7.2
Patch Changes
-
#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.
v0.7.1
Patch Changes
-
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. -
v0.7.0
Minor Changes
-
#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.
v0.6.1
Patch Changes
-
#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.
v0.6.0
Minor Changes
-
#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.
v0.5.0
Minor Changes
-
#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 are
string | {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
--json
envelope (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 config...