Skip to content

Commit cf7df2f

Browse files
feat(query): CodeClimate and badge CI output formats (#172)
* feat(query): add CodeClimate and badge CI output formats. GitLab Code Quality ingestion and README/CI issue-count summaries are output modes on existing recipe rows — flat minor severity and codemap-badge/v1 per plan F.7/F.8. * chore(changeset): use patch bump for pre-v1 additive formats. * fix(ci-formats): address PR review — docs, tests, warning parity. Hoist noLocatableFindingsWarning for CLI/MCP/HTTP; extend consumer surfaces (README, agent-content, MCP descriptions); add HTTP/MCP/formatter tests; reject badge_style without format=badge on all transports. * docs(ci-formats): close lifecycle gaps from PR review cycle 2. Sync architecture/glossary ToolResult and HTTP Content-Type; extend agent-content and alias help; remove shipped roadmap item; mark plan shipped pending merge. * fix(codeclimate): always emit location.lines.begin for GitLab. File-level recipe rows (e.g. boundary-violations) default begin to 1 when line_start is absent; clarify README format comment. * fix(codeclimate): include row message in fingerprint hash. Distinct same-file boundary violations no longer collide under GitLab dedup; document codeclimate on boundary-violations recipe. * docs(codeclimate): sync fingerprint and lines.begin prose. * docs(ci-formats): delete shipped plan per Rule 3. Lift lives in architecture.md, glossary.md, README, and agent-content; roadmap item already removed. * docs(mcp): align query_recipe format description with query * fix(ci-formats): align fingerprint line with emitted begin + test gaps Fingerprint hashes use the same line as location.lines.begin (default 1). Add MCP/CLI/handler regression tests for warnings, combo guards, and happy paths.
1 parent e74410c commit cf7df2f

21 files changed

Lines changed: 1141 additions & 167 deletions

.changeset/ci-output-formats.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@stainless-code/codemap": patch
3+
---
4+
5+
Add `query --format codeclimate` (GitLab Code Quality JSON) and `query --format badge` (markdown issue-count line or `codemap-badge/v1` JSON via `--badge-style json`). Available on CLI, MCP, and HTTP query tools.

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,23 @@ codemap audit --base v1.0.0 --files-baseline pre-release-files # mix --base wit
131131
# non-git projects get a clean `codemap audit: --base requires a git repository.` error.
132132
# Recipes that define per-row action templates append "actions" hints (kebab-case verb +
133133
# description) in --json output; ad-hoc SQL never carries actions. Inspect via --recipes-json.
134-
# --format <text|json|sarif|annotations|mermaid|diff|diff-json> — pipe results into GitHub Code Scanning
135-
# (SARIF 2.1.0), surface findings inline on PRs (GH Actions ::notice file=…,line=…::msg), or
136-
# render edge-shaped recipes as Mermaid `flowchart LR`, or preview edits as unified diffs. All
134+
# --format <text|json|sarif|annotations|mermaid|diff|diff-json|codeclimate|badge> — SARIF for GitHub
135+
# Code Scanning; annotations for GH Actions ::notice lines; codeclimate for GitLab Code Quality;
136+
# badge for issue-count summaries; mermaid/diff for graph and edit previews. All
137137
# formatted outputs require a flat row list
138-
# (no --summary / --group-by / baseline). SARIF / annotations auto-detect file_path /
139-
# path / to_path / from_path; rule.id is codemap.<recipe-id> (or codemap.adhoc). Mermaid
138+
# (no --summary / --group-by / baseline). SARIF / annotations / codeclimate / badge
139+
# auto-detect file_path / path / to_path / from_path; rule.id is codemap.<recipe-id>
140+
# (or codemap.adhoc). codeclimate/badge skip aggregate-only rows. Mermaid
140141
# requires {from, to, label?, kind?} rows and rejects unbounded inputs (>50 edges) with a
141142
# scope-suggestion error — alias columns via SELECT col AS "from", col2 AS "to".
142143
codemap query --recipe deprecated-symbols --format sarif > findings.sarif
143144
codemap query --recipe deprecated-symbols --ci # CI shortcut: --format sarif + non-zero exit + quiet
144145
codemap query --recipe deprecated-symbols --format annotations # one ::notice per row
146+
# GitLab Code Quality artifact (locatable rows only; flat minor severity):
147+
codemap query --recipe boundary-violations --format codeclimate > gl-code-quality-report.json
148+
# Badge summary for README paste or CI (counts locatable rows only):
149+
codemap query --recipe boundary-violations --format badge
150+
codemap query --recipe boundary-violations --format badge --badge-style json | jq -e '.status == "pass"'
145151
# Render any audit/SARIF output as a markdown PR-summary comment (for repos without
146152
# Code Scanning / aggregate audit deltas / bot-context seeding):
147153
codemap audit --base origin/main --json | codemap pr-comment - | gh pr comment <PR> -F -

docs/architecture.md

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

docs/glossary.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,19 @@ Long-running process that subscribes to filesystem changes via [chokidar v5](htt
544544

545545
### `codemap serve` / HTTP server
546546

547-
Long-running HTTP server exposing the same tool taxonomy as `codemap mcp` over `POST /tool/{name}` for non-MCP consumers (CI scripts, simple `curl`, IDE plugins that don't speak MCP). Default bind **`127.0.0.1:7878`** (loopback only — refuse `0.0.0.0` unless explicitly opted in via `--host 0.0.0.0`); optional `--token <secret>` requires `Authorization: Bearer <secret>` on every request. HTTP returns each tool's native JSON payload directly (NOT MCP's `{content: [...]}` wrapper); `query` / `query_recipe` match `codemap query --json` row arrays unless `summary` / `group_by` reshape the envelope, or `baseline` returns a diff envelope (incompatible with non-`json` `format` / `group_by`; save/list/drop remain separate tools); parity twins (`query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context`, `ingest-coverage`) always emit JSON on CLI without `--json`; other tools match their CLI `--json` payloads when that flag is set; `format: "sarif"` payloads ship as `application/sarif+json`, `format: "annotations"` / `"mermaid"` / `"diff"` as `text/plain; charset=utf-8`, `format: "diff-json"` as `application/json; charset=utf-8`. Routes: `POST /tool/{name}` (every MCP tool), `GET /resources/{encoded-uri}` (resource handler for `codemap://recipes`, `codemap://recipes/{id}`, `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://files/{path}`, and `codemap://symbols/{name}`), `GET /health` (auth-exempt liveness probe — does not start the watcher), `GET /tools` / `GET /resources` (catalogs). With `--watch`, chokidar is refcount-gated per request and stops 5s after the last client (`HTTP_WATCH_RELEASE_GRACE_MS`) — distinct from MCP idle shutdown; the HTTP process keeps listening. Pure transport — same `tool-handlers.ts` / `resource-handlers.ts` MCP uses; no engine duplication. Errors → `{"error": "..."}` with HTTP status 400 / 401 / 403 / 404 / 500. SIGINT / SIGTERM → graceful drain. Every response carries `X-Codemap-Version: <semver>`. **CSRF + DNS-rebinding guard:** every request (including auth-exempt `/health`) is evaluated against `Sec-Fetch-Site` / `Origin` / `Host` when present — modern browsers send `Sec-Fetch-Site` and `Origin` on cross-origin fetches (header presence varies by request type, browser, and privacy settings), so the guard rejects browser-driven cross-origin requests like a malicious local webpage `fetch`-ing `http://127.0.0.1:7878/tool/save_baseline` to mutate `.codemap/index.db`. `Host` mismatch on a loopback bind blocks DNS rebinding (an attacker resolving `evil.com` to `127.0.0.1` post-load). Non-browser clients (curl, fetch from Node, MCP hosts, CI scripts) typically omit these headers and pass through. Implementation: `src/cli/cmd-serve.ts` (CLI shell) + `src/application/http-server.ts` (transport). See [`architecture.md` § HTTP wiring](./architecture.md#cli-usage).
547+
Long-running HTTP server exposing the same tool taxonomy as `codemap mcp` over `POST /tool/{name}` for non-MCP consumers (CI scripts, simple `curl`, IDE plugins that don't speak MCP). Default bind **`127.0.0.1:7878`** (loopback only — refuse `0.0.0.0` unless explicitly opted in via `--host 0.0.0.0`); optional `--token <secret>` requires `Authorization: Bearer <secret>` on every request. HTTP returns each tool's native JSON payload directly (NOT MCP's `{content: [...]}` wrapper); `query` / `query_recipe` match `codemap query --json` row arrays unless `summary` / `group_by` reshape the envelope, or `baseline` returns a diff envelope (incompatible with non-`json` `format` / `group_by`; save/list/drop remain separate tools); parity twins (`query batch`, `trace`, `explore`, `node`, `file`, `schema`, `symbols`, `context`, `ingest-coverage`) always emit JSON on CLI without `--json`; other tools match their CLI `--json` payloads when that flag is set; `format: "sarif"` payloads ship as `application/sarif+json`, `format: "annotations"` / `"mermaid"` / `"diff"` / `"badge"` (markdown) as `text/plain; charset=utf-8`, `format: "diff-json"` / `"codeclimate"` / `"badge"` + `badge_style: "json"` as `application/json; charset=utf-8`. Routes: `POST /tool/{name}` (every MCP tool), `GET /resources/{encoded-uri}` (resource handler for `codemap://recipes`, `codemap://recipes/{id}`, `codemap://schema`, `codemap://skill`, `codemap://rule`, `codemap://mcp-instructions`, `codemap://files/{path}`, and `codemap://symbols/{name}`), `GET /health` (auth-exempt liveness probe — does not start the watcher), `GET /tools` / `GET /resources` (catalogs). With `--watch`, chokidar is refcount-gated per request and stops 5s after the last client (`HTTP_WATCH_RELEASE_GRACE_MS`) — distinct from MCP idle shutdown; the HTTP process keeps listening. Pure transport — same `tool-handlers.ts` / `resource-handlers.ts` MCP uses; no engine duplication. Errors → `{"error": "..."}` with HTTP status 400 / 401 / 403 / 404 / 500. SIGINT / SIGTERM → graceful drain. Every response carries `X-Codemap-Version: <semver>`. **CSRF + DNS-rebinding guard:** every request (including auth-exempt `/health`) is evaluated against `Sec-Fetch-Site` / `Origin` / `Host` when present — modern browsers send `Sec-Fetch-Site` and `Origin` on cross-origin fetches (header presence varies by request type, browser, and privacy settings), so the guard rejects browser-driven cross-origin requests like a malicious local webpage `fetch`-ing `http://127.0.0.1:7878/tool/save_baseline` to mutate `.codemap/index.db`. `Host` mismatch on a loopback bind blocks DNS rebinding (an attacker resolving `evil.com` to `127.0.0.1` post-load). Non-browser clients (curl, fetch from Node, MCP hosts, CI scripts) typically omit these headers and pass through. Implementation: `src/cli/cmd-serve.ts` (CLI shell) + `src/application/http-server.ts` (transport). See [`architecture.md` § HTTP wiring](./architecture.md#cli-usage).
548+
549+
### Code Climate format (`codeclimate`)
550+
551+
GitLab [Code Quality](https://docs.gitlab.com/ee/ci/testing/code_quality.html) JSON array emitted by `codemap query --format codeclimate` (MCP/HTTP: `format: "codeclimate"`). One object per locatable row (`file_path` / `path` / `to_path` / `from_path` + optional `line_start`); flat `severity: "minor"` in v1; `location.lines.begin` defaults to `1` without `line_start`; stable SHA-256 fingerprints (recipe + path + line + check name + row message) for GitLab dedup. Aggregate recipes emit `[]` + stderr warning. Incompatible with `--summary` / `--group-by` / baseline.
552+
553+
### Badge format (`badge`)
554+
555+
Issue-count presentation derived from locatable-row count only — not a triage primitive. CLI: `codemap query --format badge` prints `codemap: N issues` / `codemap: clean`; `--badge-style json` emits `codemap-badge/v1` (`{schema, label, message, count, status}`). MCP/HTTP: `format: "badge"` + optional `badge_style`. Agents should triage via JSON query rows or `--summary`, not badge stdout.
556+
557+
### `codemap-badge/v1`
558+
559+
JSON schema for structured badge output (`badge_style: "json"`). Fields: `schema`, `label` (`"codemap"`), `message`, `count`, `status` (`pass` when `count === 0`). Intended for CI gates (`jq -e '.status == "pass"'`).
548560

549561
### SARIF
550562

docs/plans/ci-output-formats.md

Lines changed: 0 additions & 102 deletions
This file was deleted.

docs/roadmap.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ Predicate-as-API only — enrich row shape and audit deltas; no standalone pass/
9191
- [ ] **Tiered lookup fast paths**`show` / exact-name recipe paths hit covering indexes first; document latency expectations in MCP tool descriptions. FTS and broad scans remain explicit fallbacks. Effort: S–M.
9292
- [ ] **Graph-estimated CRAP recipe** — bundled `high-crap-score`: CRAP = `CC² × (1 - coverage/100)³ + CC` using `symbols.complexity`; **measured** `coverage` when ingested, else **graph-estimated** tiers (85% / 40% / 0% from test-file reachability over `dependencies` / `calls` / `test_suites`). Rows expose `coverage_source: measured | estimated`. Complements `high-complexity-untested` when no coverage file exists. Plan: [`plans/graph-estimated-crap.md`](./plans/graph-estimated-crap.md). Effort: M.
9393
- [ ] **Coverage-confirmed dead recipe** — bundled `coverage-confirmed-dead`: JOIN static dead-code predicate (uncalled exports, suppression-aware) with ingested `coverage` — rows carry `confidence: high` when callers = 0 and `coverage_pct = 0`, `medium` when coverage not ingested. Predicate columns only, no verdict primitive ([Moat A](./roadmap.md#moats-load-bearing)). Plan: [`plans/coverage-deletion-confidence.md`](./plans/coverage-deletion-confidence.md). Effort: L–M.
94-
- [ ] **CI output formats (CodeClimate + badge)**`query --format codeclimate` (GitLab Code Quality JSON array + stable fingerprints) and `query --format badge` (compact issue-count line for README/CI summary). Same location contract as SARIF/annotations. Plan: [`plans/ci-output-formats.md`](./plans/ci-output-formats.md). Effort: S–M.
9594

9695
### Distribution & evaluation depth
9796

0 commit comments

Comments
 (0)