Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rename-alias-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stainless-code/codemap": patch
---

Add `codemap rename` CLI alias for homonym-safe renames via `apply rename-preview` (`--define-in`, `--in-file`, `--kind`).
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ codemap affected --changed-since origin/main --json # committed de
codemap apply rename-preview --params old=usePermissions,new=useAccess,kind=function --dry-run
codemap apply rename-preview --params old=usePermissions,new=useAccess,kind=function --yes # TTY prompts without --yes
# Homonym-safe: add define_in=src/path/to/definition.ts (scopes target; in_file only filters output rows)
# Alias: codemap rename helper worker --define-in src/path/to/definition.ts --dry-run
codemap apply migrate-import-source --params old_source=legacy,new_source=@app/core --dry-run
codemap apply stale-imports --params in_file=src/widget --dry-run # preview; writes need --force --yes
codemap apply migrate-jsx-prop --params old_name=data-id,new_name=data-testid,component_name=ProductCard --dry-run --force
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,13 @@ Three **mutually exclusive** CLI entry shapes; all converge on `applyDiffPayload

**Bundled diff-shape recipes** (emit the row contract; inspect with `codemap query --recipe <id> --format diff-json`): `rename-preview` (includes member/namespaced JSX via `jsx_elements`), `migrate-import-source`, `replace-marker-kind` (`auto_fixable: true`); `stale-imports`, `migrate-deprecated`, `deprecated-usages`, `add-jsdoc-deprecated`, `migrate-jsx-prop` (`auto_fixable: false` — writes need `--force` unless allowlisted). Pair read `deprecated-symbols` with `migrate-deprecated` + `deprecated-usages`; `find-jsx-usages` with `migrate-jsx-prop`. Golden map: [`testing-coverage.md`](./testing-coverage.md).

**Homonym-safe rename:** optional `define_in=<definition file_path>` on `rename-preview` anchors `target_symbols` and binding-resolved call/JSX sites (distinct from `in_file`, which only filters output row paths). Bare `old`/`new` still unions every same-named symbol.
**Homonym-safe rename:** optional `define_in=<definition file_path>` on `rename-preview` anchors `target_symbols` and binding-resolved call/JSX sites (distinct from `in_file`, which only filters output row paths). Bare `old`/`new` still unions every same-named symbol. CLI shorthand: `codemap rename <old> <new> [--define-in <file_path>] [--in-file <prefix>] [--kind <k>]` → `apply rename-preview` (thin alias — same recipe + policy gates; see `codemap rename --help`).

**Policy** (`src/application/apply-policy.ts`, recipe mode only): non-`auto_fixable` recipes reject writes unless `--force` / MCP `force: true`. `apply.autoApplyRecipes` in user config is an allowlist of recipe ids that may run without TTY `--yes` on non-interactive CLI (MCP/HTTP still require `yes: true` for writes). `--rows` / `apply_rows` / `--diff-input` bypass both gates — separate trust boundary for agent-supplied hunks.

**Discover → preview → apply** (agent loop): `query_recipe` / `query --recipe <id> --format diff-json` (or audit baseline `added` rows) → `apply` with `dry_run: true` → `apply` with `yes: true` (+ `force: true` when required). Per-row `actions[].command` on `--json` query output renders a copy-paste shell line (`renderRecipeActionCommands`).

**Non-goals on the apply path** (Moat A preserved): no curated write verbs (`codemap rename`, …); no severity / verdict engine on rows; no JS execution at apply time; no Path A AST apply engine; no cross-file transactional rollback. Rejected alternatives + revisit triggers: [synthesis §7](./research/codemap-richer-index-synthesis-2026-05.md#7-rejected-items-with-trigger-conditions) (`organize-imports`, Path A AST apply, trust tiers, …).
**Non-goals on the apply path** (Moat A preserved): no curated write verbs with new semantics (`codemap fix deprecated`, …); **`codemap rename`** is a thin alias to `apply rename-preview` (same recipe + policy gates as outcome aliases → `query --recipe`). No severity / verdict engine on rows; no JS execution at apply time; no Path A AST apply engine; no cross-file transactional rollback. Rejected alternatives + revisit triggers: [synthesis §7](./research/codemap-richer-index-synthesis-2026-05.md#7-rejected-items-with-trigger-conditions) (`organize-imports`, Path A AST apply, trust tiers, …).

**Show / snippet wiring:** **`src/cli/show-snippet-args.ts`** (shared argv parser) + **`src/cli/show-snippet-render.ts`** (shared terminal/JSON error helpers) + **`src/cli/cmd-show.ts`** + **`src/cli/cmd-snippet.ts`** — sibling CLI verbs sharing the same parser shape (`<name>` or **`--query '<field:value …>'`** + **`--with-fts`** + `--kind` + `--in <path>` + `--json`; show adds **`--print-sql`**) and the pure engines **`src/application/show-engine.ts`** (exact lookup + envelope builders), **`src/application/search-query-parser.ts`** + **`src/application/search-engine.ts`** (field-qualified search → parameterized SQL on `symbols`, optional `source_fts` join), and **`src/application/show-search-mode.ts`** (shared parse/normalize + FTS resolution + **`executeShowLookup`** + **`formatShowSearchSqlForQuery`** for CLI/MCP/HTTP). Exact lookup: `findSymbolsByName({db, name, kind?, inPath?})`. Query lookup: `searchSymbols({db, parsed, withFts?})`. Snippet FS read: `readSymbolSource({match, projectRoot, indexedContentHash?})` + `getIndexedContentHash(db, filePath)`. **`buildShowResult`** + **`buildSnippetResult`** envelope builders — same engines the MCP show/snippet tools call. Both verbs return the same `{matches, disambiguation?, warning?}` envelope — single match → `{matches: [{...}]}`; multi-match adds `{n, by_kind, files, hint}`; optional **`warning`** when FTS was requested but `source_fts` is empty. Snippet matches add `source` / `stale` / `missing` fields (additive — no shape divergence). **`--in <path>`** and **`path:`** inside **`--query`** normalize through `toProjectRelative(projectRoot, p)` (from **`src/application/validate-engine.ts`**). Stale-file behavior on `snippet`: `hashContent` (from **`src/hash.ts`**) compares on-disk content against `files.content_hash`; mismatch sets `stale: true` but source IS still returned. MCP tools `show` and `snippet` register parallel to the CLI surface (see [§ MCP wiring](#cli-usage)).

Expand Down
4 changes: 2 additions & 2 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Third apply transport for **caller-supplied unified diff text** — parses git-s

### `codemap apply` / apply tool

Substrate-shaped fix executor — reads the same row contract `--format diff-json` emits and applies hunks to disk. Recipe SQL is the synthesis surface; codemap is the executor (Moat-A — verdict-shape "should we fix this?" stays on the recipe author). **Three CLI input modes** (mutually exclusive): (1) **recipe** — `codemap apply <recipe-id> [--params k=v[,k=v]]`; MCP/HTTP `apply` `{recipe, params?, dry_run?, yes?, force?, until_empty?, max_passes?, commit_message?}`. (2) **rows** — `codemap apply --rows -|<file.json>`; MCP/HTTP [`apply_rows`](#apply_rows-mcp-tool--cli-mode). (3) **diff** — `codemap apply --diff-input <unified-diff>`; MCP/HTTP [`apply_diff_input`](#apply_diff_input-mcp-tool--cli-mode). Shared flags: `--dry-run` (phase-1 only), `--yes` (skip TTY prompt; required for non-TTY writes), `--json`. **Recipe-only flags:** `--force` / MCP `force` (bypass `auto_fixable` + allowlist); `--until-empty` / `until_empty` + `--max-passes` / `max_passes` (fixpoint on recipe `apply` only). **Git commit:** `--commit` / `commit_message` on recipe `apply` and `apply_diff_input`. **diff-json preview:** each hunk includes `ambiguity_count` (extra `before_pattern` matches on the line; apply rewrites first match only). **Policy (recipe mode only):** recipes with `auto_fixable: false` in `<id>.md` frontmatter reject writes unless `--force` / MCP `force: true`; `apply.autoApplyRecipes` in [user config](./architecture.md#user-config) allowlists recipe ids for non-interactive CLI without `--yes` (MCP/HTTP writes still need `yes: true`). Bundled diff-shape recipe ids: `rename-preview`, `migrate-import-source`, `replace-marker-kind` (`auto_fixable: true`); `stale-imports`, `migrate-deprecated`, `deprecated-usages`, `add-jsdoc-deprecated`, `migrate-jsx-prop` (force-gated unless allowlisted). **`rename-preview` homonyms:** pass `define_in=<symbols.file_path>` to scope the rename to one definition; omit to union all homonyms (use `find-symbol-references` to pick the anchor first). **`--commit` / `commit_message`:** recipe `apply` and `apply_diff_input` only (not `apply_rows`); with `--until-empty`, commit only when `terminated_by` is `empty`. **Phase 1** validates every row via `actual.includes(before_pattern)` (substring match); seven conflict reasons (`file missing` / `line out of range` / `line content drifted` / `path escapes project root` / `path is a symlink` / `duplicate edit on same line`). **Phase 2** (gated on `!dryRun && zero conflicts`) writes via sibling temp + `renameSync` per file; **all-or-nothing** across files on conflicts (no cross-file rollback on crash mid-phase-2). **Q6 gate** — TTY without `--yes` prompts `Proceed? [y/N]`; MCP/HTTP have no prompt path. Result envelope: `{mode, applied, files, conflicts, summary}` (+ optional `passes`, `terminated_by`). Re-apply on stale disk → `line content drifted`; re-index then vacuous zero-row pass (Q7). Engine: `application/apply-engine.ts` (`applyDiffPayload`); orchestration: `application/apply-run.ts`. Full transport matrix: [`architecture.md` § Apply — input modes](./architecture.md#apply--input-modes-transport-and-policy). Boundary kit: [§ Boundary verification — apply write path](./architecture.md#boundary-verification--apply-write-path).
Substrate-shaped fix executor — reads the same row contract `--format diff-json` emits and applies hunks to disk. Recipe SQL is the synthesis surface; codemap is the executor (Moat-A — verdict-shape "should we fix this?" stays on the recipe author). **Three CLI input modes** (mutually exclusive): (1) **recipe** — `codemap apply <recipe-id> [--params k=v[,k=v]]`; MCP/HTTP `apply` `{recipe, params?, dry_run?, yes?, force?, until_empty?, max_passes?, commit_message?}`. (2) **rows** — `codemap apply --rows -|<file.json>`; MCP/HTTP [`apply_rows`](#apply_rows-mcp-tool--cli-mode). (3) **diff** — `codemap apply --diff-input <unified-diff>`; MCP/HTTP [`apply_diff_input`](#apply_diff_input-mcp-tool--cli-mode). Shared flags: `--dry-run` (phase-1 only), `--yes` (skip TTY prompt; required for non-TTY writes), `--json`. **Recipe-only flags:** `--force` / MCP `force` (bypass `auto_fixable` + allowlist); `--until-empty` / `until_empty` + `--max-passes` / `max_passes` (fixpoint on recipe `apply` only). **Git commit:** `--commit` / `commit_message` on recipe `apply` and `apply_diff_input`. **diff-json preview:** each hunk includes `ambiguity_count` (extra `before_pattern` matches on the line; apply rewrites first match only). **Policy (recipe mode only):** recipes with `auto_fixable: false` in `<id>.md` frontmatter reject writes unless `--force` / MCP `force: true`; `apply.autoApplyRecipes` in [user config](./architecture.md#user-config) allowlists recipe ids for non-interactive CLI without `--yes` (MCP/HTTP writes still need `yes: true`). Bundled diff-shape recipe ids: `rename-preview`, `migrate-import-source`, `replace-marker-kind` (`auto_fixable: true`); `stale-imports`, `migrate-deprecated`, `deprecated-usages`, `add-jsdoc-deprecated`, `migrate-jsx-prop` (force-gated unless allowlisted). **`rename-preview` homonyms:** pass `define_in=<symbols.file_path>` to scope the rename to one definition; omit to union all homonyms (use `find-symbol-references` to pick the anchor first). CLI shorthand: `codemap rename <old> <new> [--define-in <file_path>] [--in-file <prefix>] [--kind <k>]` → `apply rename-preview` (thin alias — same recipe + policy gates). **`--commit` / `commit_message`:** recipe `apply` and `apply_diff_input` only (not `apply_rows`); with `--until-empty`, commit only when `terminated_by` is `empty`. **Phase 1** validates every row via `actual.includes(before_pattern)` (substring match); seven conflict reasons (`file missing` / `line out of range` / `line content drifted` / `path escapes project root` / `path is a symlink` / `duplicate edit on same line`). **Phase 2** (gated on `!dryRun && zero conflicts`) writes via sibling temp + `renameSync` per file; **all-or-nothing** across files on conflicts (no cross-file rollback on crash mid-phase-2). **Q6 gate** — TTY without `--yes` prompts `Proceed? [y/N]`; MCP/HTTP have no prompt path. Result envelope: `{mode, applied, files, conflicts, summary}` (+ optional `passes`, `terminated_by`). Re-apply on stale disk → `line content drifted`; re-index then vacuous zero-row pass (Q7). Engine: `application/apply-engine.ts` (`applyDiffPayload`); orchestration: `application/apply-run.ts`. Full transport matrix: [`architecture.md` § Apply — input modes](./architecture.md#apply--input-modes-transport-and-policy). Boundary kit: [§ Boundary verification — apply write path](./architecture.md#boundary-verification--apply-write-path).

### audit

Expand Down Expand Up @@ -393,7 +393,7 @@ Key-value metadata table. Holds `schema_version`, `last_indexed_commit`, `indexe

### outcome aliases (`dead-code` / `deprecated` / `boundaries` / `hotspots` / `coverage-gaps`)

Top-level CLI verbs that thin-wrap `query --recipe <id>`: `dead-code` → `untested-and-dead`, `deprecated` → `deprecated-symbols`, `boundaries` → `boundary-violations`, `hotspots` → `fan-in`, `coverage-gaps` → `worst-covered-exports`. Every `query` flag passes through (`--json`, `--format`, `--ci`, `--summary`, `--changed-since`, `--group-by`, `--params`, `--save-baseline`, `--baseline`). Mapping lives in `src/cli/aliases.ts` (`OUTCOME_ALIASES`). Capped at 5 to avoid alias-sprawl — promote a sixth only when the recipe becomes a headline outcome. Moat-A clean: the alias is a one-line rewrite, not a new primitive; the recipe IS the SQL.
Top-level CLI verbs that thin-wrap `query --recipe <id>`: `dead-code` → `untested-and-dead`, `deprecated` → `deprecated-symbols`, `boundaries` → `boundary-violations`, `hotspots` → `fan-in`, `coverage-gaps` → `worst-covered-exports`. Every `query` flag passes through (`--json`, `--format`, `--ci`, `--summary`, `--changed-since`, `--group-by`, `--params`, `--save-baseline`, `--baseline`). Mapping lives in `src/cli/aliases.ts` (`OUTCOME_ALIASES`). Capped at 5 to avoid alias-sprawl — promote a sixth only when the recipe becomes a headline outcome. Moat-A clean: the alias is a one-line rewrite, not a new primitive; the recipe IS the SQL. **Write alias (distinct):** `codemap rename` thin-wraps `apply rename-preview` (not `query --recipe`) — mapping in `src/cli/rename-alias.ts`; same Moat-A rule (no new write semantics).

### oxc-parser

Expand Down
Loading
Loading