Commit 4061ac3
authored
feat(query): --format sarif | annotations (B.8 — pipe rows into GitHub Code Scanning + PR annotations) (#43)
* feat(query): --format <text|json|sarif|annotations> flag parser (Tracer 1 of 6)
Adds the parser slice + tests for the new --format flag. No formatter wired yet — sarif/annotations values parse and propagate but render via the existing text/json paths until Tracers 2 + 3 land the actual formatters.
Per docs/plans/sarif-formatter.md § D9, --format overrides --json when both passed; --json alone implies --format json (back-compat); default = text. Plan doc committed alongside (created on commit, deleted on ship per docs-governance Rule 3).
* feat(query): formatSarif end-to-end + parser combo guards (Tracer 2 of 6)
- New application/output-formatters.ts: pure transport-agnostic formatter; SARIF 2.1.0 doc with auto-detected location columns (file_path / path / to_path / from_path priority) and optional region.startLine + .endLine. Recipes without locations emit results: [] + stderr warning (per plan § D6 + § D8).
- Wired into runQueryCmd: --format sarif short-circuits to printFormattedQuery before printQueryResult; recipe description / body pulled from getQueryRecipeCatalogEntry to populate rule.shortDescription / fullDescription.
- Parser combo guard: --format sarif|annotations cannot be combined with --summary / --group-by / --save-baseline / --baseline (different output shapes — sarif/annotations require flat rows). Tested.
- 22 unit tests on the formatter cover location detection, message construction, region emission, ad-hoc rule id (codemap.adhoc), recipe-body fullDescription.
- Annotations branch is a stub that prints "not yet implemented" + returns 1 — Tracer 3 lands the actual formatter.
Verified end-to-end: bun src/index.ts query --recipe deprecated-symbols --format sarif emits a valid SARIF 2.1.0 doc with one result for the @deprecated fixture symbol.
* feat(query): formatAnnotations end-to-end (Tracer 3 of 6)
GitHub Actions ::notice file=…,line=…::msg per row. One line per locatable row; rows without a location column are silently skipped (caller decides whether to print a stderr warning); empty input → empty string. Newlines in the message are collapsed to single spaces because the GH parser stops at the first newline.
Default level 'notice'; 'warning' / 'error' overrides supported for future per-recipe severity (sarifLevel frontmatter, deferred to v1.x).
Verified end-to-end: bun src/index.ts query --recipe deprecated-symbols --format annotations emits the expected ::notice line for the @deprecated fixture symbol.
* feat(mcp): format=sarif|annotations on query + query_recipe tools (Tracer 5 of 6)
Adds the same --format CLI surface to the MCP query / query_recipe tools so agents can request a formatted text payload directly without piping through codemap query.
- New formatEnum on the inputSchema (json | sarif | annotations); 'text' is omitted because terminal-table output is useless to an agent.
- formatToolIncompatibility mirrors the CLI parser's incompatibility check (sarif/annotations + summary/group_by → error).
- formattedQueryResult shared helper between query and query_recipe — query_recipe passes recipeId so the SARIF rule.id derives to codemap.<recipe>; query (ad-hoc) leaves it undefined → codemap.adhoc.
- 4 new MCP server tests cover SARIF on a recipe, annotations on a recipe, sarif+summary rejection, and sarif on ad-hoc SQL.
query_batch deliberately not wired — annotation/sarif on a heterogeneous batch is awkward (every statement could ask for a different format) and no real consumer has asked. Defer to v1.x.
* docs: sync README + glossary + architecture + agents (Rule 10) + delete plan + changeset (Tracer 6 of 6)
- README.md "Daily commands" stripe: add --format <text|json|sarif|annotations> example pair (sarif > findings.sarif and annotations).
- docs/glossary.md: new 'SARIF' and 'GH annotations' entries (per Rule 9 — new domain nouns).
- docs/architecture.md: new 'Output formatters' wiring paragraph above 'Validate wiring'; covers location auto-detection, rule-id taxonomy, MCP integration, deferred-to-v1.x overrides.
- .agents/rules/codemap.md + templates/agents/rules/codemap.md (Rule 10): new 'SARIF / GH annotations' row in the CLI table.
- .agents/skills/codemap/SKILL.md + templates/agents/skills/codemap/SKILL.md: query / query_recipe tool descriptions extended with format arg semantics; query_batch deferral noted.
- .changeset/sarif-formatter.md: minor changeset (new flag).
- docs/plans/sarif-formatter.md: deleted on ship per docs-governance Rule 3.
- src/{cli/cmd-query.ts, application/output-formatters.ts}: replaced dangling cross-refs to the deleted plan with cross-refs to architecture.md § Output formatters.
* fix(query): respect resolved --format + escape annotation fields + doc --format in CLI help (CodeRabbit on #43)
Four CodeRabbit threads, all verified ✅ correct:
1. **(major) Text/JSON path was ignoring --format.** runQueryCmd resolved opts.format in the parser then passed opts.json (raw) to printQueryResult, breaking my own design D9: --format json (no --json) printed a terminal table; --json --format text printed JSON. Fixed by computing effectiveFormat once at the top of runQueryCmd and threading the resulting isJson boolean through every branch (saveBaseline / baselineDiff / groupedQuery / printQueryResult / emitErrorMaybeJson). Two new parser tests lock the precedence (--format text wins over --json; --format json with no flag still emits JSON).
2. **(major) formatAnnotations emitted unescaped fields.** Per actions/toolkit (https://github.com/actions/toolkit/blob/master/packages/core/src/command.ts), property values must escape % \r \n : , and message payloads must escape % \r \n; otherwise file paths with : (Windows drive letters) or , break the annotation, and messages with % get parsed as malformed escape sequences. Added escapeAnnotationData + escapeAnnotationProperty helpers (exported + 5 unit tests covering order-of-operations, empty strings, idempotence). Whitespace collapse still runs first so messages stay single-line by GH spec; the CR/LF escape paths are exercised by property values.
3. **(minor) printQueryCmdHelp didn't advertise --format.** Added the full enum + precedence + sarif/annotations incompatibility note. Both "missing SQL or recipe" usage strings updated to mention --format <fmt>.
4. **(minor) docs/glossary.md alphabetical order.** GH annotations was under ## S (next to SARIF since they shipped together); moved to a new ## G section between F and H per glossary's per-letter structure. Cross-references unchanged.
Smoke verified post-fix: `codemap query --format json` emits JSON; `--json --format text` emits text.
37 new + updated unit tests pass; bun run check green.
* test(query): lock --format sarif|annotations combo-guard rejection (CodeRabbit nitpick on #43)
7 new parser tests covering every incompatible combo CodeRabbit flagged: --format sarif|annotations × --summary | --group-by | --baseline | --save-baseline (both =name and default-name forms) on ad-hoc SQL + on recipes. Plus 2 negative tests confirming --format text/json compose freely with summary/group-by (text/json don't trip the guard).
The guard logic existed since Tracer 2 but only had end-to-end coverage via the SARIF/annotations smoke + the MCP-side mirror tests; these direct parse-time tests prevent regressions when the parser grows new flags.1 parent e47d1f5 commit 4061ac3
15 files changed
Lines changed: 1237 additions & 19 deletions
File tree
- .agents
- rules
- skills/codemap
- .changeset
- docs
- src
- application
- cli
- templates/agents
- rules
- skills/codemap
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
62 | | - | |
63 | | - | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
108 | 114 | | |
109 | 115 | | |
110 | 116 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
| 122 | + | |
| 123 | + | |
122 | 124 | | |
123 | 125 | | |
124 | 126 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
189 | 189 | | |
190 | 190 | | |
191 | 191 | | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
192 | 200 | | |
193 | 201 | | |
194 | 202 | | |
| |||
376 | 384 | | |
377 | 385 | | |
378 | 386 | | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
379 | 391 | | |
380 | 392 | | |
381 | 393 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
113 | 131 | | |
114 | 132 | | |
115 | 133 | | |
| |||
291 | 309 | | |
292 | 310 | | |
293 | 311 | | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
294 | 383 | | |
295 | 384 | | |
296 | 385 | | |
| |||
0 commit comments