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
1 change: 1 addition & 0 deletions .agents/lessons.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Each entry is a single bullet: `- **<topic>** — <lesson>`. Newest entries at t
- **agent rule + skill maintenance** — when shipping a CLI flag, recipe id, recipe `actions` template, schema column, or any agent-queryable surface, update **both** copies of the codemap rule + skill in the **same PR** per [docs/README.md Rule 10](../docs/README.md): `templates/agents/rules/codemap.md` + `templates/agents/skills/codemap/SKILL.md` (ships to npm) **and** `.agents/rules/codemap.md` + `.agents/skills/codemap/SKILL.md` (this clone's mirror). Drift between the two pairs should be CLI-prefix-only (`codemap` vs `bun src/index.ts`). Forgetting this leaves installed agents with a stale view of the CLI — that's how `--summary` / `--changed-since` / `--group-by` / `actions` / `symbols.visibility` shipped without any `templates/agents/` mention until PR #29 retro-fixed it.
- **backticks inside SQL or help-text template literals** — never put a literal backtick inside a `` `...` `` template-literal string. `db.ts` SQL DDL strings (multi-line CREATE TABLE templates) and `printQueryCmdHelp()` (multi-line help text) are both `` `...` `` template literals; an inner backtick — typically a Markdown-style code-fence around a flag like `` `--full` `` — terminates the literal early and the parser blows up several lines later with cryptic "expected `,` or `)`" errors. **Use plain prose in those strings** (`--full` not `` `--full` ``), or escape (`` \` ``) if you really need the character. Hit twice (B.7 + B.6 PR #30); the lesson is general — applies to any TS template literal that gets pasted prose later, not just SQL / help text.
- **STOP-before-Grep applies to symbol lookups too** — `Grep` for symbol names like `printQueryResult`, `getCurrentCommit`, `dropAll` violates the [`codemap` rule](rules/codemap.md). The codemap query `SELECT file_path, line_start FROM symbols WHERE name = '<X>'` answers it faster and without scanning. Reach for `Grep` only when the question is content-shaped (regex over file bodies, finding pattern usages inside function bodies, etc.) — not when it's "where is X defined / who calls X / what does file Y export." This was a PR #30 self-correction.
- **PR / issue / comment bodies always go through a temp file** — never pass markdown bodies via shell heredoc to `gh pr create --body "$(cat <<'EOF'…)"` / `gh pr edit --body …` / `gh pr comment --body …` / `gh issue create --body …` / `gh api` `--field body=…`. Backticks inside the heredoc (every code span and code fence) get shell-escaped to `\`` and render literally on GitHub — every recipe id, file path, flag, SQL fragment, and code fence in the rendered body comes out as `\`coverage\``instead of`coverage`. Pattern: write the body to a temp file (`Write`to`/tmp/pr-<n>-body.md`), pass `--body-file /tmp/pr-<n>-body.md`, then delete the temp file. Cost is one extra tool call; saves redoing every PR body that has more than a few backticks. Hit on PR #57 — final body was a wall of `\`` artifacts until rewritten via temp file.
9 changes: 8 additions & 1 deletion .agents/rules/codemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ alwaysApply: true

> **STOP.** Before you call Grep, Glob, SemanticSearch, or Read to answer a **structural** question about this repository — query the Codemap SQLite index first. This is not optional when the question matches a trigger pattern below.

A local database (default **`.codemap/index.db`**) indexes structure: symbols, imports, exports, components, dependencies, markers, CSS variables, CSS classes, CSS keyframes. The `.codemap/` directory holds every codemap-managed file (`index.db` + WAL/SHM, `audit-cache/`, project `recipes/`, `config.{ts,js,json}`, self-managed `.gitignore`); override the dir with `--state-dir <path>` or `CODEMAP_STATE_DIR`. The `.codemap/.gitignore` is **codemap-managed and reconciled on every boot** (`ensureStateGitignore`) — bumping its canonical body in a PR auto-applies on every consumer's next run.
A local database (default **`.codemap/index.db`**) indexes structure: symbols, imports, exports, components, dependencies, markers, CSS variables, CSS classes, CSS keyframes, and (after `bun src/index.ts ingest-coverage <path>`) static coverage from Istanbul JSON or LCOV. The `.codemap/` directory holds every codemap-managed file (`index.db` + WAL/SHM, `audit-cache/`, project `recipes/`, `config.{ts,js,json}`, self-managed `.gitignore`); override the dir with `--state-dir <path>` or `CODEMAP_STATE_DIR`. The `.codemap/.gitignore` is **codemap-managed and reconciled on every boot** (`ensureStateGitignore`) — bumping its canonical body in a PR auto-applies on every consumer's next run.

**This file** is for **developing Codemap** in this clone. **End users** of the published package get the agent rule from **`templates/agents/`** (via **`codemap agents init`**). **Generic defaults:** SQL and triggers stay project-agnostic — **edit** this rule for repo-specific paths and queries.

Expand All @@ -32,6 +32,7 @@ A local database (default **`.codemap/index.db`**) indexes structure: symbols, i
| Targeted read (metadata) | — | `bun src/index.ts show <name> [--kind <k>] [--in <path>] [--json]` — file:line + signature |
| Targeted read (source text) | — | `bun src/index.ts snippet <name> [--kind <k>] [--in <path>] [--json]` — same lookup + source from disk + stale flag |
| Impact (blast-radius walker) | — | `bun src/index.ts impact <target> [--direction up\|down\|both] [--depth N] [--via <b>] [--limit N] [--summary] [--json]` — replaces hand-composed `WITH RECURSIVE` queries |
| Coverage ingest | — | `bun src/index.ts ingest-coverage <path> [--json]` — Istanbul (`coverage-final.json`) or LCOV (`lcov.info`); format auto-detected. Joinable to `symbols` for "untested AND dead" queries. |
| SARIF / GH annotations | — | `bun src/index.ts query --recipe deprecated-symbols --format sarif` · `… --format annotations` |

**Recipe `actions`:** with **`--json`**, recipes that define an `actions` template append it to every row (kebab-case verb + description — e.g. `fan-out` → `review-coupling`). Under `--baseline`, actions attach to the **`added`** rows only. Inspect via **`--recipes-json`**. Ad-hoc SQL never carries actions.
Expand Down Expand Up @@ -109,6 +110,10 @@ If the question looks like any of these → use the index:
| "Is symbol X deprecated?" / "What does X do?" | `symbols` (`doc_comment`) |
| "What's `@internal` / `@beta` / `@alpha` / `@private`?" | `symbols.visibility` (parsed JSDoc tag — not regex) |
| "Who calls X?" / "What does X call?" | `calls` |
| "Is symbol X tested?" / "What's the coverage of file Y?" | `coverage` (after `ingest-coverage`) |
| "What's structurally dead AND untested?" | `--recipe untested-and-dead` |
| "Rank files by test coverage" | `--recipe files-by-coverage` |
| "Worst-covered exported functions" | `--recipe worst-covered-exports` |

## When Grep / Read IS appropriate

Expand Down Expand Up @@ -156,6 +161,8 @@ bun src/index.ts query --json "<SQL>"
| Who calls X? | `SELECT DISTINCT caller_name, file_path FROM calls WHERE callee_name = '...'` |
| What does X call? | `SELECT DISTINCT callee_name FROM calls WHERE caller_name = '...'` |
| Call hotspots | `SELECT callee_name, COUNT(*) as fan_in FROM calls GROUP BY callee_name ORDER BY fan_in DESC LIMIT 10` |
| Symbol coverage | `SELECT name, hit_statements, total_statements, coverage_pct FROM coverage WHERE file_path = '...'` |
| Untested + dead exports | `bun src/index.ts query --json --recipe untested-and-dead` |

**Use `DISTINCT`** on dependency and import queries — a file importing multiple specifiers from the same module produces duplicate rows.

Expand Down
15 changes: 15 additions & 0 deletions .agents/skills/codemap/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,21 @@ User-facing baselines saved by `codemap query --save-baseline`, replayed by `cod
| git_ref | TEXT | `git rev-parse HEAD` at save time, or NULL when not a git working tree |
| created_at | INTEGER | `Date.now()` at save time (epoch ms) |

### `coverage` — Statement coverage (user data, ingested via `codemap ingest-coverage`)

Static coverage from Istanbul JSON or LCOV. Joinable to `symbols` for "what's untested?" queries. **Survives `--full` and SCHEMA bumps** — intentionally absent from `dropAll()`. Empty until first ingest.

| Column | Type | Description |
| ---------------- | ------- | -------------------------------------------------------------------------------------------------------- |
| file_path | TEXT PK | Project-relative path; matches `symbols.file_path`. Forward-slashed (Windows paths normalised on ingest) |
| name | TEXT PK | Symbol name (matches `symbols.name`). Same `(file_path, name, line_start)` is unique by construction |
| line_start | INT PK | Symbol's starting line (matches `symbols.line_start`). Disambiguates re-declared names |
| coverage_pct | REAL | Percentage 0.0–100.0; `NULL` when `total_statements = 0` (zero-statement scope; not the same as 0%) |
| hit_statements | INTEGER | Count of statements with non-zero hit count after innermost-wins projection |
| total_statements | INTEGER | Count of statements that projected onto this symbol |

Three meta keys (`coverage_last_ingested_at` / `_path` / `_format`) record freshness — single ingest at a time, format is meta-level.

## Query patterns

### Basic lookups
Expand Down
36 changes: 36 additions & 0 deletions .changeset/coverage-ingestion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
"@stainless-code/codemap": minor
---

`codemap ingest-coverage <path>` — static coverage ingestion. Reads Istanbul JSON (`coverage-final.json`) or LCOV (`lcov.info`) into a new `coverage` table joinable to `symbols`, 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), and `bun 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 `actions` block so agents see per-row follow-up hints in `--json` output.

**Schema:**

- New `coverage` table with natural-key PK `(file_path, name, line_start)` — intentionally not a FK to `symbols.id` so coverage rows survive the `symbols` drop-recreate cycle on every `--full` reindex.
- `idx_coverage_file_name` covers the typical join shape and the `GROUP BY file_path` scan used by the `files-by-coverage` recipe.
- Three new `meta` keys (`coverage_last_ingested_at` / `_path` / `_format`) record ingest freshness.
- `SCHEMA_VERSION` 5 → 6 — auto-rebuilds on next `codemap` run; the new table is empty until first `ingest-coverage` invocation. Subsequent bumps preserve coverage data via the `dropAll()` exclusion.

**CLI:**

```bash
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 `--source` flag — 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 existing `query` / `query_recipe` tools (no parallel surface).

Plan: PR #56 (merged). Implementation: this PR.
Loading
Loading