@lhi/tdd-audit 1.16.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,236 @@
1
+ # Configuration Reference
2
+
3
+ All `tdd-audit` behaviour is controlled by `.tdd-audit.json` at your repo root. CLI flags override file config; file config overrides built-in defaults.
4
+
5
+ Generate a starter file:
6
+ ```bash
7
+ npx @lhi/tdd-audit@latest init
8
+ npx @lhi/tdd-audit@latest init --provider anthropic
9
+ ```
10
+
11
+ ---
12
+
13
+ ## Full schema
14
+
15
+ ```jsonc
16
+ {
17
+ // ── Core ──────────────────────────────────────────────────────────────────
18
+ "output": "text", // "text" | "json" | "sarif" | "report"
19
+ "severityThreshold": "LOW", // minimum severity to include: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
20
+ "ignore": [], // path prefixes to skip: ["node_modules", "dist"]
21
+ "port": 3000, // port for `tdd-audit serve`
22
+ "serverApiKey": null, // required on REST API calls; falls back to TDD_AUDIT_API_KEY env var
23
+
24
+ // ── AI provider ───────────────────────────────────────────────────────────
25
+ "provider": "anthropic", // "anthropic" | "openai" | "gemini" | "ollama"
26
+ "model": "claude-opus-4-6",
27
+ "apiKeyEnv": "ANTHROPIC_API_KEY", // env var to read the key from
28
+ "baseUrl": null, // override for OpenAI-compatible providers
29
+
30
+ // ── Branding ──────────────────────────────────────────────────────────────
31
+ // For wrapper/rebranded distributions. See docs/extensibility.md.
32
+ "org": "Daily Caller",
33
+ "project": "my-project",
34
+ "badge_label": "dc-audit", // replaces "tdd-audit" in the shields.io badge
35
+ "tdd_site": "https://security.example.com", // replaces npm link in badge + SARIF
36
+ "security_name": "Alice Smith", // name of the security contact — stamped into SECURITY.md, compliance reports, and webhook payloads
37
+ "security_email": "security@example.com", // email for the vulnerability reporting address in SECURITY.md and payloads
38
+
39
+ // ── Policy as code ────────────────────────────────────────────────────────
40
+ // Override the default severity for any named pattern.
41
+ // Useful when a pattern's default severity doesn't match your org's risk model.
42
+ "severity_overrides": {
43
+ "CORS Wildcard": "CRITICAL", // default: MEDIUM
44
+ "Sensitive Log": "HIGH", // default: MEDIUM
45
+ "Cleartext Traffic": "HIGH" // default: MEDIUM
46
+ },
47
+
48
+ // ── Notifications ─────────────────────────────────────────────────────────
49
+ // Fired when a scan completes. Both can be set simultaneously.
50
+ "webhook_url": "https://hooks.example.com/tdd-audit",
51
+ // POST body: { project, timestamp, summary: { critical, high, medium, low }, findings: [...] }
52
+
53
+ "slack_webhook": "https://hooks.slack.com/services/...",
54
+ "slack_channel": "#security", // optional override; uses webhook default if absent
55
+
56
+ // ── Workflow integration ───────────────────────────────────────────────────
57
+ "open_pr": true, // open a GitHub PR per finding instead of committing directly
58
+ "github_token": null, // falls back to GITHUB_TOKEN env var
59
+ "github_repo": null, // "owner/repo" — auto-detected from git remote if null
60
+
61
+ // ── CI / scheduled modes ──────────────────────────────────────────────────
62
+ "pr_mode": false, // lightweight static scan only — fast, designed for PR gates
63
+ "org_scan": null, // "my-github-org" — scan all repos in the org
64
+ "schedule": null, // cron expression for external schedulers: "0 2 * * *"
65
+
66
+ // ── Output additions ──────────────────────────────────────────────────────
67
+ "sbom": false, // generate CycloneDX SBOM alongside the audit
68
+ "report": false, // generate human-readable compliance report (markdown)
69
+ "watch": false, // re-scan affected files on save (watch mode)
70
+
71
+ // ── Secret rotation ───────────────────────────────────────────────────────
72
+ "rotate_secrets": false, // when a hardcoded key is detected, prompt to rotate via provider API
73
+
74
+ // ── Extensibility ─────────────────────────────────────────────────────────
75
+ // See docs/extensibility.md for full documentation.
76
+ "pattern_repos": [],
77
+ "extra_skill_dirs": [],
78
+ "extra_repos": [],
79
+ "mcp_services": [],
80
+ "extra_domains": []
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## CLI flag equivalents
87
+
88
+ | Config key | CLI flag |
89
+ |---|---|
90
+ | `output: "json"` | `--json` or `--format json` |
91
+ | `output: "sarif"` | `--format sarif` |
92
+ | `output: "report"` | `--format report` |
93
+ | `pr_mode: true` | `--pr` |
94
+ | `org_scan: "myorg"` | `--org myorg` |
95
+ | `open_pr: true` | `--open-pr` |
96
+ | `sbom: true` | `--sbom` |
97
+ | `watch: true` | `--watch` |
98
+ | `report: true` | `--report` |
99
+ | `rotate_secrets: true` | `--rotate-secrets` |
100
+ | `provider: "openai"` | `--provider openai` |
101
+ | `model: "gpt-4o"` | `--model gpt-4o` |
102
+ | `severityThreshold: "HIGH"` | `--threshold HIGH` |
103
+
104
+ CLI flags always win over file config.
105
+
106
+ ---
107
+
108
+ ## Policy as code
109
+
110
+ `severity_overrides` lets you redefine what severity means for your org without forking the scanner. The key is the exact pattern name from the [vulnerability patterns reference](./vulnerability-patterns.md).
111
+
112
+ ```json
113
+ "severity_overrides": {
114
+ "CORS Wildcard": "CRITICAL"
115
+ }
116
+ ```
117
+
118
+ This is applied before findings are reported, before notifications fire, and before PR gates are evaluated. A pattern overridden to `CRITICAL` will block a PR merge just like any built-in CRITICAL.
119
+
120
+ ---
121
+
122
+ ## Notifications
123
+
124
+ Both `webhook_url` and `slack_webhook` fire after every scan (CLI, `--ai`, and `POST /scan`).
125
+
126
+ **Webhook payload:**
127
+ ```json
128
+ {
129
+ "project": "my-project",
130
+ "org": "My Org",
131
+ "timestamp": "2026-03-26T12:00:00Z",
132
+ "duration_ms": 4200,
133
+ "summary": { "critical": 1, "high": 3, "medium": 2, "low": 0 },
134
+ "findings": [ ... ]
135
+ }
136
+ ```
137
+
138
+ **Slack message format:**
139
+ ```
140
+ 🔴 tdd-audit — my-project
141
+ 1 critical · 3 high · 2 medium
142
+ Run /caller-audit to remediate.
143
+ ```
144
+
145
+ ---
146
+
147
+ ## PR mode (`--pr`)
148
+
149
+ Designed for CI PR gates. Runs only the static scanner — no AI agents, no RAG, no fixes. Completes in under a second.
150
+
151
+ ```yaml
152
+ - run: npx @lhi/tdd-audit@latest --pr --threshold HIGH
153
+ ```
154
+
155
+ Exits non-zero if any finding meets or exceeds `severityThreshold`. Wire into your branch protection rules to block merges automatically.
156
+
157
+ Use `severity_overrides` to tune what counts as a blocker for your stack.
158
+
159
+ ---
160
+
161
+ ## Auto-fix PR (`--open-pr`)
162
+
163
+ Instead of committing fixes directly to the working branch, opens a GitHub PR per finding. Each PR contains:
164
+ - The exploit test (Red)
165
+ - The patch (Green)
166
+ - A description linking to the vulnerability pattern
167
+
168
+ Requires `GITHUB_TOKEN` in the environment or `github_token` in config.
169
+
170
+ ---
171
+
172
+ ## Watch mode (`--watch`)
173
+
174
+ ```bash
175
+ npx @lhi/tdd-audit@latest --watch
176
+ ```
177
+
178
+ Re-runs the static scanner on any modified file on save. Reports new findings immediately in the terminal. Does not run agents or apply fixes — use `/caller-audit` for that.
179
+
180
+ ---
181
+
182
+ ## SBOM (`--sbom`)
183
+
184
+ Generates a [CycloneDX](https://cyclonedx.org/) Software Bill of Materials in JSON format alongside the audit report. Output to `sbom.json` in the project root.
185
+
186
+ Required for US federal contracts (EO 14028), increasingly required in enterprise vendor questionnaires.
187
+
188
+ ---
189
+
190
+ ## Compliance report (`--format report` / `--report`)
191
+
192
+ Generates a markdown report suitable for attaching to a SOC 2 audit, ISO 27001 evidence package, or vendor security questionnaire. Includes:
193
+
194
+ - Findings summary table (severity, location, status)
195
+ - Fix evidence (exploit test name, patch commit, suite result)
196
+ - Coverage gate result
197
+ - Hardening controls applied
198
+ - SBOM reference (if generated)
199
+ - Timestamp and auditor (org/project from config)
200
+
201
+ ---
202
+
203
+ ## Org scan (`--org`)
204
+
205
+ ```bash
206
+ npx @lhi/tdd-audit@latest --org my-github-org --format report
207
+ ```
208
+
209
+ Discovers all repos in a GitHub org, runs `--pr` mode on each, and produces a cross-org summary:
210
+
211
+ ```
212
+ my-github-org security posture — 2026-03-26
213
+
214
+ ✅ repo-a 0 critical · 0 high
215
+ ⚠️ repo-b 0 critical · 2 high
216
+ 🔴 repo-c 1 critical · 4 high
217
+
218
+ 3 repos scanned · 1 critical · 6 high total
219
+ ```
220
+
221
+ Fires `webhook_url` and `slack_webhook` with the aggregate payload if configured.
222
+
223
+ ---
224
+
225
+ ## Secret rotation (`--rotate-secrets`)
226
+
227
+ When a hardcoded API key is detected, prompts to rotate it via the provider's API. Supported providers:
228
+
229
+ | Secret type | Rotation method |
230
+ |---|---|
231
+ | OpenAI key (`sk-...`) | OpenAI API — revoke + generate new key |
232
+ | Anthropic key (`sk-ant-...`) | Anthropic console link + clipboard |
233
+ | GitHub token | GitHub API — revoke token |
234
+ | Supabase service key | Supabase API — regenerate |
235
+
236
+ After rotation, updates the relevant `.env` file and removes the hardcoded value from source. The old key is invalidated whether you commit the change or not.
package/docs/rest-api.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # REST API
2
2
 
3
- `tdd-audit serve` turns the scanner into an authenticated HTTP API built on **Fastify**. Use it to integrate vulnerability scanning and AI remediation into dashboards, CI pipelines, bots, or any tooling that speaks JSON.
3
+ `tdd-audit serve` exposes an authenticated HTTP API built on **Fastify**. Use it to integrate AI-powered security audits into dashboards, CI pipelines, bots, or any tooling that speaks JSON.
4
4
 
5
5
  ---
6
6
 
@@ -59,7 +59,7 @@ By default the rate limiter keys on the **socket IP**, not `X-Forwarded-For`, to
59
59
 
60
60
  ### Path validation
61
61
 
62
- `POST /scan` and `POST /audit` validate that the requested path is inside the server's working directory. The check is normalised with a trailing path separator to prevent sibling-directory prefix bypasses (e.g. `/app-evil` cannot escape via `/app`). Paths outside cwd return `400`.
62
+ `POST /audit` and `POST /audit/ai` validate that the requested path is inside the server's working directory. The check is normalised with a trailing path separator to prevent sibling-directory prefix bypasses (e.g. `/app-evil` cannot escape via `/app`). Paths outside cwd return `400`.
63
63
 
64
64
  ### Security headers
65
65
 
@@ -80,60 +80,117 @@ X-Frame-Options: DENY
80
80
  No auth required. Returns server status and version.
81
81
 
82
82
  ```json
83
- { "status": "ok", "version": "1.13.0" }
83
+ { "status": "ok", "version": "1.17.0" }
84
84
  ```
85
85
 
86
86
  ---
87
87
 
88
- ### `POST /scan`
88
+ ### `POST /audit/ai`
89
89
 
90
- Scan a local path and return structured findings synchronously.
90
+ **The primary endpoint.** Runs a full agentic LLM audit using tool calls (`read_file`, `list_files`, `search_in_files`, `write_file`). Returns immediately with a `jobId`; poll `GET /jobs/:id` or stream `GET /jobs/:id/stream` for results.
91
+
92
+ Provider and API key can be supplied in the request body or pre-configured in `.tdd-audit.json`.
91
93
 
92
94
  **Request**
93
95
  ```json
94
96
  {
95
- "path": ".",
96
- "format": "json"
97
+ "path": ".",
98
+ "provider": "anthropic",
99
+ "apiKey": "sk-ant-...",
100
+ "model": "claude-opus-4-6",
101
+ "baseUrl": null,
102
+ "depth": "tier-2"
97
103
  }
98
104
  ```
99
105
 
100
- | Field | Type | Default | Description |
106
+ | Field | Required | Default | Description |
101
107
  |---|---|---|---|
102
- | `path` | string | cwd | Absolute or relative path to scan. Must be inside server cwd. |
103
- | `format` | `"json"` \| `"sarif"` | `"json"` | Output format |
108
+ | `path` | no | cwd | Path to audit. Must be inside server cwd. |
109
+ | `provider` | yes* | cfg | `anthropic` \| `openai` \| `gemini` \| `ollama` |
110
+ | `apiKey` | yes* | cfg | Provider API key |
111
+ | `model` | no | provider default | Model override |
112
+ | `baseUrl` | no | — | Base URL for OpenAI-compatible services |
113
+ | `depth` | no | `tier-1` | Output depth tier (see below) |
114
+ | `scanOnly` | no | — | Override depth-derived scan mode |
115
+ | `allowWrites` | no | `false` | Override depth-derived write permission |
116
+ | `findings` | no | — | Pre-identified findings array (triggers targeted-apply mode when `depth=tier-4`) |
117
+
118
+ *Required unless configured in `.tdd-audit.json`.
119
+
120
+ **Response — 202 Accepted**
121
+
122
+ ```
123
+ HTTP/1.1 202 Accepted
124
+ Location: /jobs/job_1_...
125
+ Retry-After: 5
126
+ ```
127
+ ```json
128
+ { "jobId": "job_1_1711363200000" }
129
+ ```
130
+
131
+ **Job result envelope** (available via `GET /jobs/:id` after completion)
104
132
 
105
- **Response — JSON**
106
133
  ```json
107
134
  {
108
- "version": "1.13.0",
109
- "summary": { "CRITICAL": 1, "HIGH": 3, "MEDIUM": 1, "LOW": 0 },
110
- "findings": [ ... ],
135
+ "version": "1.16.0",
136
+ "provider": "anthropic",
137
+ "model": "claude-opus-4-6",
138
+ "depth": "tier-2",
139
+ "mode": "scan-only",
140
+ "stack": "Node.js / Express",
141
+ "summary": { "CRITICAL": 1, "HIGH": 2, "MEDIUM": 0, "LOW": 1 },
142
+ "patchesApplied": 0,
143
+ "findings": [ ... ],
111
144
  "likelyFalsePositives": [ ... ],
112
- "exempted": [],
113
- "scannedAt": "2026-03-25T12:00:00.000Z",
114
- "duration": 42
145
+ "remediation": [],
146
+ "scannedAt": "2026-03-26T12:00:00.000Z"
115
147
  }
116
148
  ```
117
149
 
118
- **Response SARIF**
150
+ `patchesApplied` is the billable unit for `tier-4`: the count of `remediation` entries where `status === "fixed"`.
151
+
152
+ ---
153
+
154
+ ### Depth tiers
155
+
156
+ | Tier | Mode | Finding fields added | `allowWrites` |
157
+ |---|---|---|---|
158
+ | `tier-1` | scan-only | `name`, `severity`, `file`, `line`, `snippet` | no |
159
+ | `tier-2` | scan-only | + `risk`, `effort`, `cwe`, `owasp`, `references` | no |
160
+ | `tier-3` | full audit | + `patch` (copy-ready), `testSnippet` | no |
161
+ | `tier-4` | full audit | LLM calls `write_file`; `remediation` array tracks each patch | yes |
162
+
163
+ ---
164
+
165
+ ### Targeted apply (tier-4 + findings)
119
166
 
120
- Returns a SARIF 2.1.0 object ready to upload to GitHub code scanning.
167
+ When `depth=tier-4` and a `findings` array is supplied, the LLM skips the scan phase entirely and applies only the patches you specify. Use this to apply a single finding from a previous tier-3 report:
121
168
 
122
- **Errors**
169
+ ```json
170
+ {
171
+ "provider": "anthropic",
172
+ "apiKey": "sk-ant-...",
173
+ "depth": "tier-4",
174
+ "findings": [
175
+ {
176
+ "name": "SQL Injection",
177
+ "file": "src/db.js",
178
+ "line": 42,
179
+ "patch": "const stmt = db.prepare('SELECT * FROM users WHERE id = ?');\nstmt.run(id);"
180
+ }
181
+ ]
182
+ }
183
+ ```
123
184
 
124
- | Status | Reason |
125
- |---|---|
126
- | 400 | Path traversal attempt, oversized body (> 512 KB), or invalid JSON |
127
- | 401 | Missing or invalid API key |
128
- | 429 | Rate limit exceeded |
185
+ The job mode will be reported as `targeted-apply/tier-4(1)`. Only the listed findings are touched — no full re-scan.
129
186
 
130
187
  ---
131
188
 
132
189
  ### `POST /remediate`
133
190
 
134
- Queue an AI-powered remediation job for a **provided findings list**. Returns immediately with a `jobId`; poll `GET /jobs/:id` (or stream `GET /jobs/:id/stream`) for results.
191
+ Queue an AI-powered remediation job for a **provided findings list**. Returns immediately with a `jobId`.
135
192
 
136
- Use `POST /audit` instead if you want the server to run the scan itself.
193
+ Use `POST /audit/ai` for a fully agentic audit instead.
137
194
 
138
195
  **Request**
139
196
  ```json
@@ -142,34 +199,28 @@ Use `POST /audit` instead if you want the server to run the scan itself.
142
199
  "provider": "anthropic",
143
200
  "apiKey": "sk-ant-...",
144
201
  "model": "claude-opus-4-6",
145
- "baseUrl": null,
146
- "severity": "HIGH"
202
+ "baseUrl": null
147
203
  }
148
204
  ```
149
205
 
150
206
  | Field | Required | Description |
151
207
  |---|---|---|
152
- | `findings` | yes | Array of finding objects from `POST /scan` |
208
+ | `findings` | yes | Array of finding objects |
153
209
  | `provider` | yes | `anthropic` \| `openai` \| `gemini` \| `ollama` |
154
210
  | `apiKey` | yes | Provider API key |
155
- | `model` | no | Defaults per provider (see [AI Remediation](ai-remediation.md)) |
156
- | `baseUrl` | no | Override base URL for any OpenAI-compatible service |
157
- | `severity` | no | Minimum severity to fix. Default: `LOW` (fix all) |
211
+ | `model` | no | Defaults per provider |
212
+ | `baseUrl` | no | Override base URL for OpenAI-compatible services |
158
213
 
159
214
  **Response — 202 Accepted**
160
215
  ```json
161
216
  { "jobId": "job_1_1711363200000" }
162
217
  ```
163
218
 
164
- Job lifecycle: `pending → running → done | error`
165
-
166
219
  ---
167
220
 
168
221
  ### `POST /audit`
169
222
 
170
- Full automated pipeline: **scan + AI remediation in one shot**. No interaction needed. Returns immediately with a `jobId`.
171
-
172
- If no `provider`/`apiKey` are supplied, the server runs the scan only (no remediation) and the job transitions to `done` with just the `findings` array.
223
+ Static scan + AI remediation pipeline in one shot. Runs `quickScan` then passes findings to the remediator. If no `provider`/`apiKey` are supplied, runs the scan only.
173
224
 
174
225
  **Request**
175
226
  ```json
@@ -185,7 +236,7 @@ If no `provider`/`apiKey` are supplied, the server runs the scan only (no remedi
185
236
 
186
237
  | Field | Required | Description |
187
238
  |---|---|---|
188
- | `path` | no | Path to scan. Defaults to cwd. Must be inside server cwd. |
239
+ | `path` | no | Path to scan. Defaults to cwd. |
189
240
  | `provider` | no | If supplied with `apiKey`, AI remediation runs after the scan |
190
241
  | `apiKey` | no | Provider API key |
191
242
  | `model` | no | Defaults per provider |
@@ -196,69 +247,21 @@ If no `provider`/`apiKey` are supplied, the server runs the scan only (no remedi
196
247
 
197
248
  ```
198
249
  HTTP/1.1 202 Accepted
199
- Location: /jobs/job_1_1711363200000
250
+ Location: /jobs/job_1_...
200
251
  Retry-After: 2
201
252
  ```
202
- ```json
203
- { "jobId": "job_1_1711363200000" }
204
- ```
205
253
 
206
254
  Job lifecycle: `pending → scanning → scanned → remediating → done | error`
207
255
 
208
- Poll `GET /jobs/:id` or stream `GET /jobs/:id/stream` for progress.
209
-
210
- **Job object during remediation**
211
- ```json
212
- {
213
- "id": "job_1_...",
214
- "status": "remediating",
215
- "total": 8,
216
- "completed": 3,
217
- "current": "SQL Injection"
218
- }
219
- ```
220
-
221
- **Job object when done**
222
- ```json
223
- {
224
- "id": "job_1_...",
225
- "status": "done",
226
- "createdAt": "...",
227
- "startedAt": "...",
228
- "completedAt": "...",
229
- "findings": [ ... ],
230
- "results": [
231
- {
232
- "finding": { ... },
233
- "status": "remediated",
234
- "exploitTest": { "filename": "__tests__/security/xss.test.js", "content": "..." },
235
- "patch": { "filename": "src/app.js", "diff": "..." },
236
- "refactorChecks": ["npm test", "npm run test:security"]
237
- }
238
- ]
239
- }
240
- ```
241
-
242
256
  ---
243
257
 
244
258
  ### `GET /jobs/:id`
245
259
 
246
- Poll for job status. Works for jobs created by both `POST /remediate` and `POST /audit`.
260
+ Poll for job status. Works for jobs created by `POST /audit/ai`, `POST /remediate`, and `POST /audit`.
247
261
 
248
- **Response — pending / scanning**
262
+ **Response — pending / running**
249
263
  ```json
250
- { "id": "job_1_...", "status": "scanning", "createdAt": "..." }
251
- ```
252
-
253
- **Response — remediating (with progress)**
254
- ```json
255
- {
256
- "id": "job_1_...",
257
- "status": "remediating",
258
- "total": 8,
259
- "completed": 3,
260
- "current": "SQL Injection"
261
- }
264
+ { "id": "job_1_...", "status": "running", "depth": "tier-2", "createdAt": "..." }
262
265
  ```
263
266
 
264
267
  **Response — done**
@@ -269,7 +272,7 @@ Poll for job status. Works for jobs created by both `POST /remediate` and `POST
269
272
  "createdAt": "...",
270
273
  "startedAt": "...",
271
274
  "completedAt": "...",
272
- "results": [ ... ]
275
+ "result": { ... }
273
276
  }
274
277
  ```
275
278
 
@@ -293,18 +296,14 @@ curl -N http://localhost:3000/jobs/job_1_.../stream \
293
296
 
294
297
  **Event format**
295
298
  ```
296
- data: {"id":"job_1_...","status":"scanning","createdAt":"..."}
297
-
298
- data: {"id":"job_1_...","status":"scanned","findings":[...]}
299
-
300
- data: {"id":"job_1_...","status":"remediating","total":8,"completed":1,"current":"SQL Injection"}
299
+ data: {"id":"job_1_...","status":"running","depth":"tier-2","log":"🔍 Exploring..."}
301
300
 
302
- data: {"id":"job_1_...","status":"done","completedAt":"...","results":[...]}
301
+ data: {"id":"job_1_...","status":"done","completedAt":"...","result":{...}}
303
302
  ```
304
303
 
305
- The connection is closed automatically after the terminal state (`done` / `error`). If you connect to an already-completed job, the server pushes the current state and closes immediately.
304
+ The connection closes automatically after the terminal state. Connecting to an already-completed job pushes the current state and closes immediately.
306
305
 
307
- **Node.js example using EventSource**
306
+ **Node.js example**
308
307
  ```javascript
309
308
  const es = new EventSource(
310
309
  'http://localhost:3000/jobs/job_1_.../stream',
@@ -312,7 +311,7 @@ const es = new EventSource(
312
311
  );
313
312
  es.onmessage = (e) => {
314
313
  const job = JSON.parse(e.data);
315
- if (job.status === 'done') { console.log(job.results); es.close(); }
314
+ if (job.status === 'done') { console.log(job.result); es.close(); }
316
315
  if (job.status === 'error') { console.error(job.error); es.close(); }
317
316
  };
318
317
  ```
@@ -321,28 +320,19 @@ es.onmessage = (e) => {
321
320
 
322
321
  ## Full workflow examples
323
322
 
324
- ### curl — scan only
323
+ ### curl — tier-2 audit with polling
325
324
 
326
325
  ```bash
327
326
  npx @lhi/tdd-audit serve --port 3000 --api-key mysecret &
328
327
 
329
- curl -s -X POST http://localhost:3000/scan \
330
- -H "Authorization: Bearer mysecret" \
331
- -H "Content-Type: application/json" \
332
- -d '{"path": "."}' | jq '.summary'
333
- ```
334
-
335
- ### curl — full pipeline with polling
336
-
337
- ```bash
338
- # Kick off audit
339
- JOB=$(curl -s -X POST http://localhost:3000/audit \
328
+ # Start the audit
329
+ JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
340
330
  -H "Authorization: Bearer mysecret" \
341
331
  -H "Content-Type: application/json" \
342
332
  -d '{
343
- "path": ".",
344
333
  "provider": "anthropic",
345
- "apiKey": "sk-ant-..."
334
+ "apiKey": "sk-ant-...",
335
+ "depth": "tier-2"
346
336
  }' | jq -r '.jobId')
347
337
 
348
338
  # Poll until done
@@ -351,30 +341,53 @@ while true; do
351
341
  -H "Authorization: Bearer mysecret" | jq -r '.status')
352
342
  echo "Status: $STATUS"
353
343
  [ "$STATUS" = "done" ] || [ "$STATUS" = "error" ] && break
354
- sleep 2
344
+ sleep 3
355
345
  done
346
+
347
+ # Print findings summary
348
+ curl -s http://localhost:3000/jobs/$JOB \
349
+ -H "Authorization: Bearer mysecret" | jq '.result.summary'
356
350
  ```
357
351
 
358
- ### curl — SARIF output for GitHub code scanning
352
+ ### curl — tier-3 report then targeted apply
359
353
 
360
354
  ```bash
361
- curl -s -X POST http://localhost:3000/scan \
355
+ # Step 1: get copy-ready patches (no writes)
356
+ JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
362
357
  -H "Authorization: Bearer mysecret" \
363
358
  -H "Content-Type: application/json" \
364
- -d '{"path": ".", "format": "sarif"}' > results.sarif
359
+ -d '{"provider":"anthropic","apiKey":"sk-ant-...","depth":"tier-3"}' \
360
+ | jq -r '.jobId')
361
+
362
+ # (wait for done)
363
+
364
+ # Step 2: apply one specific patch
365
+ FINDING=$(curl -s http://localhost:3000/jobs/$JOB \
366
+ -H "Authorization: Bearer mysecret" \
367
+ | jq '.result.findings[0]')
368
+
369
+ curl -s -X POST http://localhost:3000/audit/ai \
370
+ -H "Authorization: Bearer mysecret" \
371
+ -H "Content-Type: application/json" \
372
+ -d "{
373
+ \"provider\": \"anthropic\",
374
+ \"apiKey\": \"sk-ant-...\",
375
+ \"depth\": \"tier-4\",
376
+ \"findings\": [$FINDING]
377
+ }" | jq '.jobId'
365
378
  ```
366
379
 
367
- ### Node.jsscan
380
+ ### curlSARIF output for GitHub code scanning
368
381
 
369
- ```javascript
370
- const res = await fetch('http://localhost:3000/scan', {
371
- method: 'POST',
372
- headers: {
373
- 'Authorization': 'Bearer mysecret',
374
- 'Content-Type': 'application/json',
375
- },
376
- body: JSON.stringify({ path: '/path/to/project' }),
377
- });
378
- const { findings, summary } = await res.json();
379
- console.log(`CRITICAL: ${summary.CRITICAL} HIGH: ${summary.HIGH}`);
382
+ ```bash
383
+ JOB=$(curl -s -X POST http://localhost:3000/audit/ai \
384
+ -H "Authorization: Bearer mysecret" \
385
+ -H "Content-Type: application/json" \
386
+ -d '{"provider":"anthropic","apiKey":"sk-ant-...","depth":"tier-2","format":"sarif"}' \
387
+ | jq -r '.jobId')
388
+
389
+ # (wait for done)
390
+
391
+ curl -s http://localhost:3000/jobs/$JOB \
392
+ -H "Authorization: Bearer mysecret" | jq '.result' > results.sarif
380
393
  ```