Repository navigation
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 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 in
src/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://recipes
payload) 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 in
query-engine.tsshipped in the previous release.Implementation: pure transport-agnostic loader in
src/application/recipes-loader.ts; thin shim in
src/cli/query-recipes.tspreserves backwards-compat exports
(QUERY_RECIPES,getQueryRecipeSql, etc.). Hand-rolled YAML
frontmatter parser scoped to theactionsshape (nojs-yaml
dependency)..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 carries
source(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 existing
toProjectRelativehelper 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 adds
disambiguation: {n, by_kind, files, hint}— structured aids so the
agent narrows without scanning every row. Forward-extensible (future
nearest_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 from
cmd-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.
Patch Changes
-
#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.