auditor-lambda 0.2.1 → 0.2.2

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.
Files changed (61) hide show
  1. package/audit-code-wrapper-lib.mjs +229 -289
  2. package/dist/adapters/eslint.js +4 -2
  3. package/dist/adapters/npmAudit.js +1 -1
  4. package/dist/cli.js +82 -45
  5. package/dist/extractors/bucketing.js +14 -35
  6. package/dist/extractors/disposition.js +8 -9
  7. package/dist/extractors/fileInventory.js +0 -2
  8. package/dist/extractors/flows.js +14 -23
  9. package/dist/extractors/pathPatterns.d.ts +19 -0
  10. package/dist/extractors/pathPatterns.js +87 -0
  11. package/dist/extractors/surfaces.js +2 -7
  12. package/dist/io/artifacts.d.ts +23 -1
  13. package/dist/io/artifacts.js +4 -3
  14. package/dist/io/runArtifacts.js +1 -1
  15. package/dist/orchestrator/advance.js +53 -71
  16. package/dist/orchestrator/flowCoverage.js +1 -2
  17. package/dist/orchestrator/internalExecutors.js +4 -6
  18. package/dist/orchestrator/planning.js +12 -20
  19. package/dist/orchestrator/resultIngestion.js +3 -2
  20. package/dist/orchestrator/runtimeValidation.js +5 -0
  21. package/dist/orchestrator/syntaxResolutionExecutor.js +10 -2
  22. package/dist/orchestrator/taskBuilder.js +15 -28
  23. package/dist/prompts/renderWorkerPrompt.js +2 -1
  24. package/dist/providers/claudeCodeProvider.js +1 -2
  25. package/dist/providers/constants.d.ts +1 -0
  26. package/dist/providers/constants.js +1 -0
  27. package/dist/providers/index.js +7 -3
  28. package/dist/providers/opencodeProvider.js +1 -6
  29. package/dist/providers/spawnLoggedCommand.js +4 -0
  30. package/dist/providers/types.d.ts +0 -1
  31. package/dist/supervisor/operatorHandoff.d.ts +2 -0
  32. package/dist/supervisor/operatorHandoff.js +21 -9
  33. package/dist/supervisor/runLedger.js +7 -8
  34. package/dist/supervisor/sessionConfig.js +1 -0
  35. package/dist/types/flowCoverage.d.ts +1 -1
  36. package/dist/types/runLedger.d.ts +1 -1
  37. package/dist/types/runtimeValidation.d.ts +2 -1
  38. package/dist/types/sessionConfig.d.ts +0 -6
  39. package/dist/types/surfaces.d.ts +2 -1
  40. package/dist/types/workerSession.d.ts +2 -0
  41. package/dist/types.d.ts +0 -1
  42. package/dist/validation/sessionConfig.js +1 -15
  43. package/package.json +1 -1
  44. package/schemas/audit-code-v1alpha1.schema.json +4 -0
  45. package/schemas/audit_result.schema.json +9 -3
  46. package/schemas/audit_state.schema.json +2 -2
  47. package/schemas/audit_task.schema.json +14 -3
  48. package/schemas/blind_spot_register.schema.json +13 -3
  49. package/schemas/coverage_matrix.schema.json +16 -4
  50. package/schemas/critical_flows.schema.json +6 -3
  51. package/schemas/external_analyzer_results.schema.json +10 -4
  52. package/schemas/finding.schema.json +31 -3
  53. package/schemas/flow_coverage.schema.json +12 -3
  54. package/schemas/graph_bundle.schema.json +12 -6
  55. package/schemas/merged_findings.schema.json +7 -2
  56. package/schemas/risk_register.schema.json +5 -1
  57. package/schemas/root_cause_clusters.schema.json +2 -1
  58. package/schemas/runtime_validation_tasks.schema.json +4 -1
  59. package/schemas/surface_manifest.schema.json +4 -1
  60. package/schemas/unit_manifest.schema.json +10 -3
  61. package/skills/audit-code/audit-code.prompt.md +0 -2
@@ -1,42 +1,26 @@
1
- import {
2
- access,
3
- mkdir,
4
- open,
5
- readFile,
6
- readdir,
7
- stat,
8
- unlink,
9
- writeFile,
10
- } from "node:fs/promises";
11
- import { constants } from "node:fs";
12
- import { spawn } from "node:child_process";
13
- import { dirname, join, relative, resolve } from "node:path";
14
- import { fileURLToPath } from "node:url";
1
+ import { access, mkdir, open, readFile, readdir, stat, unlink, writeFile } from 'node:fs/promises';
2
+ import { constants } from 'node:fs';
3
+ import { spawn } from 'node:child_process';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
15
6
 
16
7
  const repoRoot = dirname(fileURLToPath(import.meta.url));
17
- const distEntry = join(repoRoot, "dist", "index.js");
18
- const packageJsonPath = join(repoRoot, "package.json");
19
- const promptAssetPath = join(
20
- repoRoot,
21
- "skills",
22
- "audit-code",
23
- "audit-code.prompt.md",
24
- );
25
- const skillAssetPath = join(repoRoot, "skills", "audit-code", "SKILL.md");
26
- const tsconfigPath = join(repoRoot, "tsconfig.json");
27
- const sourceRoot = join(repoRoot, "src");
28
- const buildLockPath = join(repoRoot, ".audit-code-build.lock");
8
+ const distEntry = join(repoRoot, 'dist', 'index.js');
9
+ const packageJsonPath = join(repoRoot, 'package.json');
10
+ const promptAssetPath = join(repoRoot, 'skills', 'audit-code', 'audit-code.prompt.md');
11
+ const skillAssetPath = join(repoRoot, 'skills', 'audit-code', 'SKILL.md');
12
+ const tsconfigPath = join(repoRoot, 'tsconfig.json');
13
+ const sourceRoot = join(repoRoot, 'src');
14
+ const buildLockPath = join(repoRoot, '.audit-code-build.lock');
29
15
  const BUILD_LOCK_MAX_AGE_MS = 5 * 60 * 1000;
30
16
  const BUILD_LOCK_WAIT_TIMEOUT_MS = 2 * 60 * 1000;
31
17
  const BUILD_LOCK_WAIT_INTERVAL_MS = 200;
32
- const INSTALL_MARKER_START = "<!-- audit-code:begin -->";
33
- const INSTALL_MARKER_END = "<!-- audit-code:end -->";
34
- const INSTALL_GUIDE_FILENAME = "GETTING-STARTED.md";
35
- const DEFAULT_INSTALL_HOST = "all";
36
- const INSTALLED_PROMPT_FILENAME = "audit-code.import.md";
37
- const packageVersion = JSON.parse(
38
- await readFile(packageJsonPath, "utf8"),
39
- ).version;
18
+ const INSTALL_MARKER_START = '<!-- audit-code:begin -->';
19
+ const INSTALL_MARKER_END = '<!-- audit-code:end -->';
20
+ const INSTALL_GUIDE_FILENAME = 'GETTING-STARTED.md';
21
+ const DEFAULT_INSTALL_HOST = 'all';
22
+ const INSTALLED_PROMPT_FILENAME = 'audit-code.import.md';
23
+ const packageVersion = JSON.parse(await readFile(packageJsonPath, 'utf8')).version;
40
24
 
41
25
  function hasFlag(argv, name) {
42
26
  return argv.includes(name);
@@ -63,7 +47,7 @@ function requireFlagValue(argv, name) {
63
47
  }
64
48
 
65
49
  function npmExecutable() {
66
- return process.platform === "win32" ? "npm.cmd" : "npm";
50
+ return process.platform === 'win32' ? 'npm.cmd' : 'npm';
67
51
  }
68
52
 
69
53
  function nodeExecutable() {
@@ -77,13 +61,13 @@ function quoteForCmd(arg) {
77
61
  }
78
62
 
79
63
  function resolveSpawn(command, args) {
80
- if (!(process.platform === "win32" && /\.(cmd|bat)$/i.test(command))) {
64
+ if (!(process.platform === 'win32' && /\.(cmd|bat)$/i.test(command))) {
81
65
  return { command, args };
82
66
  }
83
67
 
84
68
  return {
85
- command: process.env.ComSpec ?? "cmd.exe",
86
- args: ["/d", "/s", "/c", [command, ...args].map(quoteForCmd).join(" ")],
69
+ command: process.env.ComSpec ?? 'cmd.exe',
70
+ args: ['/d', '/s', '/c', [command, ...args].map(quoteForCmd).join(' ')],
87
71
  };
88
72
  }
89
73
 
@@ -92,35 +76,29 @@ function run(command, args, options = {}) {
92
76
  const resolved = resolveSpawn(command, args);
93
77
  const child = spawn(resolved.command, resolved.args, {
94
78
  cwd: repoRoot,
95
- stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit",
96
- env: process.env,
79
+ stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit',
80
+ env: process.env
97
81
  });
98
82
 
99
- let stdout = "";
100
- let stderr = "";
83
+ let stdout = '';
84
+ let stderr = '';
101
85
 
102
86
  if (options.capture) {
103
- child.stdout?.on("data", (chunk) => {
87
+ child.stdout?.on('data', (chunk) => {
104
88
  stdout += String(chunk);
105
89
  });
106
- child.stderr?.on("data", (chunk) => {
90
+ child.stderr?.on('data', (chunk) => {
107
91
  stderr += String(chunk);
108
92
  });
109
93
  }
110
94
 
111
- child.on("error", rejectPromise);
112
- child.on("exit", (code) => {
95
+ child.on('error', rejectPromise);
96
+ child.on('exit', (code) => {
113
97
  if (code === 0) {
114
98
  resolvePromise({ stdout, stderr });
115
99
  return;
116
100
  }
117
- rejectPromise(
118
- new Error(
119
- options.capture
120
- ? stderr || `Command failed with exit code ${code}.`
121
- : `Command failed with exit code ${code}.`,
122
- ),
123
- );
101
+ rejectPromise(new Error(options.capture ? stderr || `Command failed with exit code ${code}.` : `Command failed with exit code ${code}.`));
124
102
  });
125
103
  });
126
104
  }
@@ -163,7 +141,7 @@ async function shouldBuildDist() {
163
141
  if (!(await fileExists(distEntry))) {
164
142
  if (!(await fileExists(sourceRoot)) || !(await fileExists(tsconfigPath))) {
165
143
  throw new Error(
166
- "Bundled dist is missing and source files are unavailable for rebuild.",
144
+ 'Bundled dist is missing and source files are unavailable for rebuild.',
167
145
  );
168
146
  }
169
147
  return true;
@@ -208,16 +186,11 @@ async function waitForPeerBuild() {
208
186
  async function acquireBuildLock() {
209
187
  while (true) {
210
188
  try {
211
- const handle = await open(buildLockPath, "wx");
212
- await handle.writeFile(
213
- JSON.stringify({
214
- pid: process.pid,
215
- acquired_at: new Date().toISOString(),
216
- }),
217
- );
189
+ const handle = await open(buildLockPath, 'wx');
190
+ await handle.writeFile(JSON.stringify({ pid: process.pid, acquired_at: new Date().toISOString() }));
218
191
  return handle;
219
192
  } catch (error) {
220
- if (error && error.code === "EEXIST") {
193
+ if (error && error.code === 'EEXIST') {
221
194
  try {
222
195
  const lockStats = await stat(buildLockPath);
223
196
  if (Date.now() - lockStats.mtimeMs > BUILD_LOCK_MAX_AGE_MS) {
@@ -253,7 +226,7 @@ async function ensureBuilt() {
253
226
  if (!(await shouldBuildDist())) {
254
227
  return;
255
228
  }
256
- await run(npmExecutable(), ["run", "build"]);
229
+ await run(npmExecutable(), ['run', 'build']);
257
230
  } finally {
258
231
  await releaseBuildLock(lockHandle);
259
232
  }
@@ -262,33 +235,33 @@ async function ensureBuilt() {
262
235
  function printHelp({ usageName, preferredEntrypoint }) {
263
236
  const lines = [
264
237
  `Usage: node ${usageName} [--single-step] [--root PATH] [--artifacts-dir PATH] [--results FILE] [--updates FILE] [--external-analyzer-results FILE]`,
265
- "",
266
- "Helper commands:",
267
- "- prompt-path prints the absolute path to the canonical /audit-code prompt asset",
268
- "- install bootstraps /audit-code into supported repo-local host surfaces",
269
- "- install-host --host copilot keeps the narrower Copilot-focused install path available",
270
- "- validate checks the current artifact bundle plus session-config/provider readiness and exits non-zero when issues exist",
271
- "",
272
- "Primary usage:",
273
- "- from the repository root, run the wrapper with no arguments",
274
- "- default behavior advances the audit automatically until it completes or no further automatic progress is possible",
275
- "- each wrapper response refreshes operator-handoff.json and operator-handoff.md under the artifacts directory",
276
- "- use --single-step only for debugging or bounded-step testing",
277
- "",
278
- "Defaults:",
279
- "- --root .",
280
- "- --artifacts-dir <root>/.audit-artifacts",
281
- "",
282
- "Completion signals:",
283
- "- done: audit_state.status is complete",
284
- "- blocked/no further automatic progress: progress_made is false and next_likely_step is null",
238
+ '',
239
+ 'Helper commands:',
240
+ '- prompt-path prints the absolute path to the canonical /audit-code prompt asset',
241
+ '- install bootstraps /audit-code into supported repo-local host surfaces',
242
+ '- install-host --host copilot keeps the narrower Copilot-focused install path available',
243
+ '- validate checks the current artifact bundle plus session-config/provider readiness and exits non-zero when issues exist',
244
+ '',
245
+ 'Primary usage:',
246
+ '- from the repository root, run the wrapper with no arguments',
247
+ '- default behavior advances the audit automatically until it completes or no further automatic progress is possible',
248
+ '- each wrapper response refreshes operator-handoff.json and operator-handoff.md under the artifacts directory',
249
+ '- use --single-step only for debugging or bounded-step testing',
250
+ '',
251
+ 'Defaults:',
252
+ '- --root .',
253
+ '- --artifacts-dir <root>/.audit-artifacts',
254
+ '',
255
+ 'Completion signals:',
256
+ '- done: audit_state.status is complete',
257
+ '- blocked/no further automatic progress: progress_made is false and next_likely_step is null'
285
258
  ];
286
259
 
287
260
  if (preferredEntrypoint && preferredEntrypoint !== usageName) {
288
- lines.push("", `Preferred entrypoint: node ${preferredEntrypoint}`);
261
+ lines.push('', `Preferred entrypoint: node ${preferredEntrypoint}`);
289
262
  }
290
263
 
291
- console.log(lines.join("\n"));
264
+ console.log(lines.join('\n'));
292
265
  }
293
266
 
294
267
  async function printPromptPath() {
@@ -300,7 +273,7 @@ async function printPromptPath() {
300
273
  }
301
274
 
302
275
  function normalizeNewlines(value) {
303
- return value.replace(/\r\n/g, "\n");
276
+ return value.replace(/\r\n/g, '\n');
304
277
  }
305
278
 
306
279
  function splitFrontmatter(markdown) {
@@ -322,22 +295,22 @@ function renderFrontmatter(fields) {
322
295
  return false;
323
296
  }
324
297
 
325
- if (typeof value === "string") {
298
+ if (typeof value === 'string') {
326
299
  return value.length > 0;
327
300
  }
328
301
 
329
302
  return true;
330
303
  });
331
304
  if (entries.length === 0) {
332
- return "";
305
+ return '';
333
306
  }
334
307
 
335
308
  return [
336
- "---",
309
+ '---',
337
310
  ...entries.map(([key, value]) => `${key}: ${value}`),
338
- "---",
339
- "",
340
- ].join("\n");
311
+ '---',
312
+ '',
313
+ ].join('\n');
341
314
  }
342
315
 
343
316
  function renderPromptFile(fields, body) {
@@ -345,19 +318,19 @@ function renderPromptFile(fields, body) {
345
318
  }
346
319
 
347
320
  function toRepoRelativePath(root, targetPath) {
348
- const value = relative(root, targetPath).replace(/\\/g, "/");
349
- return value.length > 0 ? value : ".";
321
+ const value = relative(root, targetPath).replace(/\\/g, '/');
322
+ return value.length > 0 ? value : '.';
350
323
  }
351
324
 
352
325
  function buildInstallDirective(relativePromptPath) {
353
326
  return [
354
327
  INSTALL_MARKER_START,
355
- "## /audit-code",
356
- "When the user enters `/audit-code`, treat it as this repository's autonomous audit workflow.",
357
- `If your host does not automatically register the installed slash command file, load and follow [the repo-local audit directive](${relativePromptPath.replace(/\\/g, "/")}).`,
358
- "Normal usage should stay conversation-first and avoid manual `--root`, provider flags, or model-selection arguments.",
328
+ '## /audit-code',
329
+ 'When the user enters `/audit-code`, treat it as this repository\'s autonomous audit workflow.',
330
+ `If your host does not automatically register the installed slash command file, load and follow [the repo-local audit directive](${relativePromptPath.replace(/\\/g, '/')}).`,
331
+ 'Normal usage should stay conversation-first and avoid manual `--root`, provider flags, or model-selection arguments.',
359
332
  INSTALL_MARKER_END,
360
- ].join("\n");
333
+ ].join('\n');
361
334
  }
362
335
 
363
336
  function buildInstallHostGuidance({
@@ -369,90 +342,90 @@ function buildInstallHostGuidance({
369
342
 
370
343
  if (slashCommandSurfaces.vscode_prompt) {
371
344
  guidance.push({
372
- host: "vscode",
373
- label: "VS Code",
374
- support_level: "supported",
375
- setup_kind: "repo-local-slash-command",
345
+ host: 'vscode',
346
+ label: 'VS Code',
347
+ support_level: 'supported',
348
+ setup_kind: 'repo-local-slash-command',
376
349
  summary:
377
- "Use the generated VS Code / Copilot prompt surface, then invoke `/audit-code` in chat.",
350
+ 'Use the generated VS Code / Copilot prompt surface, then invoke `/audit-code` in chat.',
378
351
  primary_path: slashCommandSurfaces.vscode_prompt,
379
352
  supporting_paths: [
380
353
  instructionSurfaces.copilot_instructions,
381
354
  instructionSurfaces.agents,
382
355
  ].filter(Boolean),
383
356
  steps: [
384
- "Open this repository in VS Code or GitHub Copilot Chat.",
385
- "Invoke `/audit-code` in chat.",
386
- "Use the integrated terminal and run `audit-code` only when you intentionally need the repo-local backend fallback.",
357
+ 'Open this repository in VS Code or GitHub Copilot Chat.',
358
+ 'Invoke `/audit-code` in chat.',
359
+ 'Use the integrated terminal and run `audit-code` only when you intentionally need the repo-local backend fallback.',
387
360
  ],
388
361
  });
389
362
  }
390
363
 
391
364
  if (slashCommandSurfaces.opencode_command) {
392
365
  guidance.push({
393
- host: "opencode",
394
- label: "OpenCode",
395
- support_level: "supported",
396
- setup_kind: "repo-local-slash-command",
366
+ host: 'opencode',
367
+ label: 'OpenCode',
368
+ support_level: 'supported',
369
+ setup_kind: 'repo-local-slash-command',
397
370
  summary:
398
- "Use the generated OpenCode command surface so `/audit-code` is available without extra provider flags.",
371
+ 'Use the generated OpenCode command surface so `/audit-code` is available without extra provider flags.',
399
372
  primary_path: slashCommandSurfaces.opencode_command,
400
373
  supporting_paths: [instructionSurfaces.agents].filter(Boolean),
401
374
  steps: [
402
- "Open this repository in OpenCode.",
403
- "Invoke `/audit-code` from the OpenCode command surface.",
404
- "Use the repo-local backend wrapper only when you intentionally need the fallback automation path.",
375
+ 'Open this repository in OpenCode.',
376
+ 'Invoke `/audit-code` from the OpenCode command surface.',
377
+ 'Use the repo-local backend wrapper only when you intentionally need the fallback automation path.',
405
378
  ],
406
379
  });
407
380
  }
408
381
 
409
382
  if (slashCommandSurfaces.claude_code_command) {
410
383
  guidance.push({
411
- host: "claude-code",
412
- label: "Claude Code",
413
- support_level: "supported",
414
- setup_kind: "repo-local-slash-command",
384
+ host: 'claude-code',
385
+ label: 'Claude Code',
386
+ support_level: 'supported',
387
+ setup_kind: 'repo-local-slash-command',
415
388
  summary:
416
- "Use the generated Claude Code command surface so `/audit-code` is available inside the repository without extra provider wiring.",
389
+ 'Use the generated Claude Code command surface so `/audit-code` is available inside the repository without extra provider wiring.',
417
390
  primary_path: slashCommandSurfaces.claude_code_command,
418
391
  supporting_paths: [instructionSurfaces.claude].filter(Boolean),
419
392
  steps: [
420
- "Open this repository in Claude Code.",
421
- "Invoke `/audit-code` from the Claude Code project command surface.",
422
- "Use the terminal fallback and run `audit-code` only when you intentionally need the repo-local backend wrapper.",
393
+ 'Open this repository in Claude Code.',
394
+ 'Invoke `/audit-code` from the Claude Code project command surface.',
395
+ 'Use the terminal fallback and run `audit-code` only when you intentionally need the repo-local backend wrapper.',
423
396
  ],
424
397
  });
425
398
  }
426
399
 
427
400
  guidance.push({
428
- host: "claude-desktop",
429
- label: "Claude Desktop",
430
- support_level: "manual-import",
431
- setup_kind: "prompt-import",
401
+ host: 'claude-desktop',
402
+ label: 'Claude Desktop',
403
+ support_level: 'manual-import',
404
+ setup_kind: 'prompt-import',
432
405
  summary:
433
- "No verified project-local slash-command surface is shipped for Claude Desktop, so use the installed prompt asset as the primary path.",
406
+ 'No verified project-local slash-command surface is shipped for Claude Desktop, so use the installed prompt asset as the primary path.',
434
407
  primary_path: installedPromptPath,
435
408
  supporting_paths: [instructionSurfaces.claude].filter(Boolean),
436
409
  steps: [
437
- "Import the installed prompt asset into Claude Desktop's prompt or instruction surface.",
438
- "Invoke `/audit-code` conversationally inside Claude Desktop after the prompt is available.",
439
- "If you intentionally need the repo-local backend fallback instead, run `audit-code` from the repository root.",
410
+ 'Import the installed prompt asset into Claude Desktop\'s prompt or instruction surface.',
411
+ 'Invoke `/audit-code` conversationally inside Claude Desktop after the prompt is available.',
412
+ 'If you intentionally need the repo-local backend fallback instead, run `audit-code` from the repository root.',
440
413
  ],
441
414
  });
442
415
 
443
416
  guidance.push({
444
- host: "antigravity",
445
- label: "Antigravity",
446
- support_level: "manual-import",
447
- setup_kind: "prompt-import-or-terminal",
417
+ host: 'antigravity',
418
+ label: 'Antigravity',
419
+ support_level: 'manual-import',
420
+ setup_kind: 'prompt-import-or-terminal',
448
421
  summary:
449
- "No verified repo-local slash-command surface is shipped for Antigravity, so start from the installed prompt asset or an Antigravity-managed terminal.",
422
+ 'No verified repo-local slash-command surface is shipped for Antigravity, so start from the installed prompt asset or an Antigravity-managed terminal.',
450
423
  primary_path: installedPromptPath,
451
424
  supporting_paths: [instructionSurfaces.agents].filter(Boolean),
452
425
  steps: [
453
- "Import the installed prompt asset into Antigravity's prompt or instruction surface when that surface is available.",
454
- "Invoke `/audit-code` conversationally inside Antigravity.",
455
- "If you prefer the backend fallback, run `audit-code` from an Antigravity-managed terminal with `local-subprocess` first.",
426
+ 'Import the installed prompt asset into Antigravity\'s prompt or instruction surface when that surface is available.',
427
+ 'Invoke `/audit-code` conversationally inside Antigravity.',
428
+ 'If you prefer the backend fallback, run `audit-code` from an Antigravity-managed terminal with `local-subprocess` first.',
456
429
  ],
457
430
  });
458
431
 
@@ -462,30 +435,30 @@ function buildInstallHostGuidance({
462
435
  function renderInstallHostSection(root, guide) {
463
436
  const lines = [
464
437
  `## ${guide.label}`,
465
- "",
438
+ '',
466
439
  `Support level: ${guide.support_level}`,
467
440
  `Setup kind: ${guide.setup_kind}`,
468
- "",
441
+ '',
469
442
  guide.summary,
470
- "",
471
- "Primary repo-local path:",
443
+ '',
444
+ 'Primary repo-local path:',
472
445
  `- \`${toRepoRelativePath(root, guide.primary_path)}\``,
473
446
  ];
474
447
 
475
448
  if (guide.supporting_paths.length > 0) {
476
- lines.push("", "Supporting repo-local paths:");
449
+ lines.push('', 'Supporting repo-local paths:');
477
450
  for (const targetPath of guide.supporting_paths) {
478
451
  lines.push(`- \`${toRepoRelativePath(root, targetPath)}\``);
479
452
  }
480
453
  }
481
454
 
482
- lines.push("", "Recommended steps:");
455
+ lines.push('', 'Recommended steps:');
483
456
  for (const step of guide.steps) {
484
457
  lines.push(`- ${step}`);
485
458
  }
486
- lines.push("");
459
+ lines.push('');
487
460
 
488
- return lines.join("\n");
461
+ return lines.join('\n');
489
462
  }
490
463
 
491
464
  function renderInstallGuide({
@@ -501,67 +474,67 @@ function renderInstallGuide({
501
474
  const slashCommandPaths = Object.values(slashCommandSurfaces).filter(Boolean);
502
475
  const instructionPaths = Object.values(instructionSurfaces).filter(Boolean);
503
476
  const lines = [
504
- "# audit-code bootstrap guide",
505
- "",
506
- "The canonical product route is `/audit-code` in conversation.",
507
- "",
508
- "Canonical installed assets:",
477
+ '# audit-code bootstrap guide',
478
+ '',
479
+ 'The canonical product route is `/audit-code` in conversation.',
480
+ '',
481
+ 'Canonical installed assets:',
509
482
  `- prompt asset: \`${toRepoRelativePath(root, installedPromptPath)}\``,
510
483
  `- skill asset: \`${toRepoRelativePath(root, installedSkillPath)}\``,
511
484
  ];
512
485
 
513
486
  if (slashCommandPaths.length > 0) {
514
- lines.push("", "Repo-local slash-command surfaces:");
487
+ lines.push('', 'Repo-local slash-command surfaces:');
515
488
  for (const targetPath of slashCommandPaths) {
516
489
  lines.push(`- \`${toRepoRelativePath(root, targetPath)}\``);
517
490
  }
518
491
  }
519
492
 
520
493
  if (instructionPaths.length > 0) {
521
- lines.push("", "Compatibility instruction surfaces:");
494
+ lines.push('', 'Compatibility instruction surfaces:');
522
495
  for (const targetPath of instructionPaths) {
523
496
  lines.push(`- \`${toRepoRelativePath(root, targetPath)}\``);
524
497
  }
525
498
  }
526
499
 
527
- lines.push("", "Host-specific quick starts:");
500
+ lines.push('', 'Host-specific quick starts:');
528
501
  for (const guide of hostGuidance) {
529
502
  lines.push(`- ${guide.label}: ${guide.summary}`);
530
503
  }
531
504
 
532
505
  for (const guide of hostGuidance) {
533
- lines.push("", renderInstallHostSection(root, guide).trimEnd());
506
+ lines.push('', renderInstallHostSection(root, guide).trimEnd());
534
507
  }
535
508
 
536
509
  lines.push(
537
- "",
538
- "Backend fallback:",
539
- "- from the repository root, run `audit-code` only when you intentionally need the repo-local backend wrapper",
510
+ '',
511
+ 'Backend fallback:',
512
+ '- from the repository root, run `audit-code` only when you intentionally need the repo-local backend wrapper',
540
513
  );
541
514
 
542
515
  if (unsupportedHosts.length > 0) {
543
- lines.push("", "Hosts still requiring extra handling today:");
516
+ lines.push('', 'Hosts still requiring extra handling today:');
544
517
  for (const item of unsupportedHosts) {
545
518
  lines.push(`- ${item.host}: ${item.reason}`);
546
519
  }
547
520
  }
548
521
 
549
- if (host !== "all") {
522
+ if (host !== 'all') {
550
523
  lines.push(
551
- "",
524
+ '',
552
525
  `This install was scoped to \`${host}\`, so some repo-local surfaces may be intentionally omitted.`,
553
526
  );
554
527
  }
555
528
 
556
- lines.push("");
557
- return lines.join("\n");
529
+ lines.push('');
530
+ return lines.join('\n');
558
531
  }
559
532
 
560
533
  function upsertManagedBlock(existingContent, blockContent) {
561
534
  const normalized = normalizeNewlines(existingContent);
562
535
  const blockPattern = new RegExp(
563
536
  `${INSTALL_MARKER_START}[\\s\\S]*?${INSTALL_MARKER_END}`,
564
- "u",
537
+ 'u',
565
538
  );
566
539
 
567
540
  if (blockPattern.test(normalized)) {
@@ -572,34 +545,34 @@ function upsertManagedBlock(existingContent, blockContent) {
572
545
  return `${blockContent}\n`;
573
546
  }
574
547
 
575
- return `${normalized.replace(/\s+$/u, "")}\n\n${blockContent}\n`;
548
+ return `${normalized.replace(/\s+$/u, '')}\n\n${blockContent}\n`;
576
549
  }
577
550
 
578
551
  async function writeManagedMarkdown(targetPath, blockContent) {
579
552
  const existed = await fileExists(targetPath);
580
- const existingContent = existed ? await readFile(targetPath, "utf8") : "";
553
+ const existingContent = existed ? await readFile(targetPath, 'utf8') : '';
581
554
  const nextContent = upsertManagedBlock(existingContent, blockContent);
582
555
  await mkdir(dirname(targetPath), { recursive: true });
583
- await writeFile(targetPath, nextContent, "utf8");
556
+ await writeFile(targetPath, nextContent, 'utf8');
584
557
  return {
585
558
  path: targetPath,
586
- mode: existed ? "updated" : "created",
559
+ mode: existed ? 'updated' : 'created',
587
560
  };
588
561
  }
589
562
 
590
563
  async function writeGeneratedMarkdown(targetPath, content) {
591
564
  const existed = await fileExists(targetPath);
592
565
  await mkdir(dirname(targetPath), { recursive: true });
593
- await writeFile(targetPath, content, "utf8");
566
+ await writeFile(targetPath, content, 'utf8');
594
567
  return {
595
568
  path: targetPath,
596
- mode: existed ? "updated" : "created",
569
+ mode: existed ? 'updated' : 'created',
597
570
  };
598
571
  }
599
572
 
600
573
  function getInstallProfile(host) {
601
574
  switch (host) {
602
- case "all":
575
+ case 'all':
603
576
  return {
604
577
  writeVSCodePrompt: true,
605
578
  writeCopilotInstructions: true,
@@ -609,7 +582,7 @@ function getInstallProfile(host) {
609
582
  writeClaudeCommand: true,
610
583
  writeCompatibilitySkills: true,
611
584
  };
612
- case "copilot":
585
+ case 'copilot':
613
586
  return {
614
587
  writeVSCodePrompt: true,
615
588
  writeCopilotInstructions: true,
@@ -619,7 +592,7 @@ function getInstallProfile(host) {
619
592
  writeClaudeCommand: false,
620
593
  writeCompatibilitySkills: false,
621
594
  };
622
- case "vscode":
595
+ case 'vscode':
623
596
  return {
624
597
  writeVSCodePrompt: true,
625
598
  writeCopilotInstructions: true,
@@ -629,7 +602,7 @@ function getInstallProfile(host) {
629
602
  writeClaudeCommand: false,
630
603
  writeCompatibilitySkills: false,
631
604
  };
632
- case "opencode":
605
+ case 'opencode':
633
606
  return {
634
607
  writeVSCodePrompt: false,
635
608
  writeCopilotInstructions: false,
@@ -639,7 +612,7 @@ function getInstallProfile(host) {
639
612
  writeClaudeCommand: false,
640
613
  writeCompatibilitySkills: true,
641
614
  };
642
- case "claude-code":
615
+ case 'claude-code':
643
616
  return {
644
617
  writeVSCodePrompt: false,
645
618
  writeCopilotInstructions: false,
@@ -670,65 +643,49 @@ async function assertDirectoryExists(path, description) {
670
643
  }
671
644
 
672
645
  async function installBootstrap(argv) {
673
- const host = (getFlag(argv, "--host") ?? DEFAULT_INSTALL_HOST).toLowerCase();
674
- const root = resolve(getFlag(argv, "--root") ?? ".");
675
- await assertDirectoryExists(root, "Target repository root");
646
+ const host = (getFlag(argv, '--host') ?? DEFAULT_INSTALL_HOST).toLowerCase();
647
+ const root = resolve(getFlag(argv, '--root') ?? '.');
648
+ await assertDirectoryExists(root, 'Target repository root');
676
649
  const profile = getInstallProfile(host);
677
- const promptSource = await readFile(promptAssetPath, "utf8");
678
- const skillSource = await readFile(skillAssetPath, "utf8");
650
+ const promptSource = await readFile(promptAssetPath, 'utf8');
651
+ const skillSource = (await readFile(skillAssetPath, 'utf8')).replace(/\r\n/g, '\n');
679
652
  const { body: promptBody } = splitFrontmatter(promptSource);
680
- const installedPromptPath = join(
681
- root,
682
- ".audit-code",
683
- "install",
684
- INSTALLED_PROMPT_FILENAME,
685
- );
686
- const legacyInstalledPromptPath = join(
687
- root,
688
- ".audit-code",
689
- "install",
690
- "audit-code.prompt.md",
691
- );
692
- const installedSkillPath = join(root, ".audit-code", "install", "SKILL.md");
693
- const installGuidePath = join(
694
- root,
695
- ".audit-code",
696
- "install",
697
- INSTALL_GUIDE_FILENAME,
698
- );
653
+ const installedPromptPath = join(root, '.audit-code', 'install', INSTALLED_PROMPT_FILENAME);
654
+ const legacyInstalledPromptPath = join(root, '.audit-code', 'install', 'audit-code.prompt.md');
655
+ const installedSkillPath = join(root, '.audit-code', 'install', 'SKILL.md');
656
+ const installGuidePath = join(root, '.audit-code', 'install', INSTALL_GUIDE_FILENAME);
699
657
  const slashCommandSurfaces = {
700
658
  vscode_prompt: profile.writeVSCodePrompt
701
- ? join(root, ".github", "prompts", "audit-code.prompt.md")
659
+ ? join(root, '.github', 'prompts', 'audit-code.prompt.md')
702
660
  : null,
703
661
  opencode_command: profile.writeOpenCodeCommand
704
- ? join(root, ".opencode", "commands", "audit-code.md")
662
+ ? join(root, '.opencode', 'commands', 'audit-code.md')
705
663
  : null,
706
664
  claude_code_command: profile.writeClaudeCommand
707
- ? join(root, ".claude", "commands", "audit-code.md")
665
+ ? join(root, '.claude', 'commands', 'audit-code.md')
708
666
  : null,
709
667
  };
710
668
  const instructionSurfaces = {
711
669
  copilot_instructions: profile.writeCopilotInstructions
712
- ? join(root, ".github", "copilot-instructions.md")
670
+ ? join(root, '.github', 'copilot-instructions.md')
713
671
  : null,
714
- agents: profile.writeAgents ? join(root, "AGENTS.md") : null,
715
- claude: profile.writeClaudeMemory ? join(root, "CLAUDE.md") : null,
672
+ agents: profile.writeAgents ? join(root, 'AGENTS.md') : null,
673
+ claude: profile.writeClaudeMemory ? join(root, 'CLAUDE.md') : null,
716
674
  };
717
- const unsupportedHosts =
718
- host === "all"
719
- ? [
720
- {
721
- host: "claude-desktop",
722
- reason:
723
- "No verified project-local slash-command installation surface is currently shipped for Claude Desktop.",
724
- },
725
- {
726
- host: "antigravity",
727
- reason:
728
- "No verified repo-local slash-command installation surface is currently shipped for Antigravity.",
729
- },
730
- ]
731
- : [];
675
+ const unsupportedHosts = host === 'all'
676
+ ? [
677
+ {
678
+ host: 'claude-desktop',
679
+ reason:
680
+ 'No verified project-local slash-command installation surface is currently shipped for Claude Desktop.',
681
+ },
682
+ {
683
+ host: 'antigravity',
684
+ reason:
685
+ 'No verified repo-local slash-command installation surface is currently shipped for Antigravity.',
686
+ },
687
+ ]
688
+ : [];
732
689
  const hostGuidance = buildInstallHostGuidance({
733
690
  installedPromptPath,
734
691
  slashCommandSurfaces,
@@ -739,18 +696,23 @@ async function installBootstrap(argv) {
739
696
  if (await fileExists(legacyInstalledPromptPath)) {
740
697
  await unlink(legacyInstalledPromptPath).catch(() => {});
741
698
  }
742
- results.push(await writeGeneratedMarkdown(installedPromptPath, promptSource));
699
+ results.push(
700
+ await writeGeneratedMarkdown(
701
+ installedPromptPath,
702
+ promptSource,
703
+ ),
704
+ );
743
705
  results.push(await writeGeneratedMarkdown(installedSkillPath, skillSource));
744
706
 
745
707
  if (profile.writeVSCodePrompt) {
746
708
  results.push(
747
709
  await writeGeneratedMarkdown(
748
- join(root, ".github", "prompts", "audit-code.prompt.md"),
710
+ join(root, '.github', 'prompts', 'audit-code.prompt.md'),
749
711
  renderPromptFile(
750
712
  {
751
- name: "audit-code",
752
- description: "Autonomous local loop code auditing",
753
- agent: "agent",
713
+ name: 'audit-code',
714
+ description: 'Autonomous local loop code auditing',
715
+ agent: 'agent',
754
716
  },
755
717
  promptBody,
756
718
  ),
@@ -761,11 +723,11 @@ async function installBootstrap(argv) {
761
723
  if (profile.writeOpenCodeCommand) {
762
724
  results.push(
763
725
  await writeGeneratedMarkdown(
764
- join(root, ".opencode", "commands", "audit-code.md"),
726
+ join(root, '.opencode', 'commands', 'audit-code.md'),
765
727
  renderPromptFile(
766
728
  {
767
- description: "Autonomous local loop code auditing",
768
- agent: "build",
729
+ description: 'Autonomous local loop code auditing',
730
+ agent: 'build',
769
731
  subtask: false,
770
732
  },
771
733
  promptBody,
@@ -777,10 +739,10 @@ async function installBootstrap(argv) {
777
739
  if (profile.writeClaudeCommand) {
778
740
  results.push(
779
741
  await writeGeneratedMarkdown(
780
- join(root, ".claude", "commands", "audit-code.md"),
742
+ join(root, '.claude', 'commands', 'audit-code.md'),
781
743
  renderPromptFile(
782
744
  {
783
- description: "Autonomous local loop code auditing",
745
+ description: 'Autonomous local loop code auditing',
784
746
  },
785
747
  promptBody,
786
748
  ),
@@ -788,16 +750,14 @@ async function installBootstrap(argv) {
788
750
  );
789
751
  }
790
752
 
791
- const compatibilityBlockTargets =
792
- Object.values(instructionSurfaces).filter(Boolean);
753
+ const compatibilityBlockTargets = Object.values(instructionSurfaces).filter(Boolean);
793
754
 
794
755
  for (const targetPath of compatibilityBlockTargets) {
795
756
  results.push(
796
757
  await writeManagedMarkdown(
797
758
  targetPath,
798
759
  buildInstallDirective(
799
- relative(dirname(targetPath), installedPromptPath) ||
800
- `./.audit-code/install/${INSTALLED_PROMPT_FILENAME}`,
760
+ relative(dirname(targetPath), installedPromptPath) || `./.audit-code/install/${INSTALLED_PROMPT_FILENAME}`,
801
761
  ),
802
762
  ),
803
763
  );
@@ -805,18 +765,18 @@ async function installBootstrap(argv) {
805
765
 
806
766
  if (profile.writeCompatibilitySkills) {
807
767
  const skillTargets = [
808
- join(root, ".opencode", "skills", "audit-code"),
809
- join(root, ".claude", "skills", "audit-code"),
810
- join(root, ".agents", "skills", "audit-code"),
768
+ join(root, '.opencode', 'skills', 'audit-code'),
769
+ join(root, '.claude', 'skills', 'audit-code'),
770
+ join(root, '.agents', 'skills', 'audit-code'),
811
771
  ];
812
772
 
813
773
  for (const targetDir of skillTargets) {
814
774
  results.push(
815
- await writeGeneratedMarkdown(join(targetDir, "SKILL.md"), skillSource),
775
+ await writeGeneratedMarkdown(join(targetDir, 'SKILL.md'), skillSource),
816
776
  );
817
777
  results.push(
818
778
  await writeGeneratedMarkdown(
819
- join(targetDir, "audit-code.prompt.md"),
779
+ join(targetDir, 'audit-code.prompt.md'),
820
780
  promptSource,
821
781
  ),
822
782
  );
@@ -839,24 +799,16 @@ async function installBootstrap(argv) {
839
799
  ),
840
800
  );
841
801
 
842
- const sessionConfigPath = join(
843
- root,
844
- ".audit-artifacts",
845
- "session-config.json",
846
- );
802
+ const sessionConfigPath = join(root, '.audit-artifacts', 'session-config.json');
847
803
  let sessionConfigWritten = false;
848
804
  if (!(await fileExists(sessionConfigPath))) {
849
805
  const insideClaudeCode = Boolean(process.env.CLAUDECODE);
850
806
  const defaultConfig = insideClaudeCode
851
- ? { provider: "local-subprocess" }
852
- : { provider: "auto" };
807
+ ? { provider: 'local-subprocess' }
808
+ : { provider: 'auto' };
853
809
  await mkdir(dirname(sessionConfigPath), { recursive: true });
854
- await writeFile(
855
- sessionConfigPath,
856
- JSON.stringify(defaultConfig, null, 2) + "\n",
857
- "utf8",
858
- );
859
- results.push({ path: sessionConfigPath, mode: "created" });
810
+ await writeFile(sessionConfigPath, JSON.stringify(defaultConfig, null, 2) + '\n', 'utf8');
811
+ results.push({ path: sessionConfigPath, mode: 'created' });
860
812
  sessionConfigWritten = true;
861
813
  }
862
814
 
@@ -875,9 +827,9 @@ async function installBootstrap(argv) {
875
827
  host_guidance: hostGuidance,
876
828
  unsupported_hosts: unsupportedHosts,
877
829
  next_steps: [
878
- "Open the repository in your preferred host and follow the matching host_guidance entry.",
830
+ 'Open the repository in your preferred host and follow the matching host_guidance entry.',
879
831
  `Open ${installGuidePath} for repo-local quick-start steps for VS Code, OpenCode, Claude Code, Claude Desktop, and Antigravity.`,
880
- "If a host does not auto-discover slash commands, use the installed prompt asset or the listed compatibility instruction surfaces.",
832
+ 'If a host does not auto-discover slash commands, use the installed prompt asset or the listed compatibility instruction surfaces.',
881
833
  ],
882
834
  },
883
835
  null,
@@ -887,9 +839,9 @@ async function installBootstrap(argv) {
887
839
  }
888
840
 
889
841
  async function installHostPrompt(argv) {
890
- const host = requireFlagValue(argv, "--host").toLowerCase();
842
+ const host = requireFlagValue(argv, '--host').toLowerCase();
891
843
 
892
- if (host !== "copilot") {
844
+ if (host !== 'copilot') {
893
845
  throw new Error(
894
846
  `install-host currently supports only "copilot". Use "install --host ${host}" for the broader bootstrap flow.`,
895
847
  );
@@ -898,20 +850,13 @@ async function installHostPrompt(argv) {
898
850
  await installBootstrap(argv);
899
851
  }
900
852
 
901
- async function runDistCommand(
902
- commandName,
903
- argv,
904
- { ensureArtifactsDir = false } = {},
905
- ) {
853
+ async function runDistCommand(commandName, argv, { ensureArtifactsDir = false } = {}) {
906
854
  const commandArgs = [...argv];
907
- const rootValue = resolve(getFlag(commandArgs, "--root") ?? ".");
908
- const artifactsDir = resolve(
909
- getFlag(commandArgs, "--artifacts-dir") ??
910
- join(rootValue, ".audit-artifacts"),
911
- );
855
+ const rootValue = resolve(getFlag(commandArgs, '--root') ?? '.');
856
+ const artifactsDir = resolve(getFlag(commandArgs, '--artifacts-dir') ?? join(rootValue, '.audit-artifacts'));
912
857
 
913
- setDefaultFlag(commandArgs, "--root", rootValue);
914
- setDefaultFlag(commandArgs, "--artifacts-dir", artifactsDir);
858
+ setDefaultFlag(commandArgs, '--root', rootValue);
859
+ setDefaultFlag(commandArgs, '--artifacts-dir', artifactsDir);
915
860
 
916
861
  if (ensureArtifactsDir) {
917
862
  await mkdir(artifactsDir, { recursive: true });
@@ -926,58 +871,53 @@ export async function runAuditCodeWrapper({
926
871
  argv = process.argv.slice(2),
927
872
  ensureArtifactsDir = true,
928
873
  preferredEntrypoint,
929
- defaultSingleStep = false,
874
+ defaultSingleStep = false
930
875
  }) {
931
- if (hasFlag(argv, "--help") || hasFlag(argv, "-h")) {
876
+ if (hasFlag(argv, '--help') || hasFlag(argv, '-h')) {
932
877
  printHelp({ usageName, preferredEntrypoint });
933
878
  return;
934
879
  }
935
880
 
936
- if (hasFlag(argv, "--version") || hasFlag(argv, "-v")) {
881
+ if (hasFlag(argv, '--version') || hasFlag(argv, '-v')) {
937
882
  console.log(packageVersion);
938
883
  return;
939
884
  }
940
885
 
941
- if (argv[0] === "prompt-path") {
886
+ if (argv[0] === 'prompt-path') {
942
887
  await printPromptPath();
943
888
  return;
944
889
  }
945
890
 
946
- if (argv[0] === "install") {
891
+ if (argv[0] === 'install') {
947
892
  await installBootstrap(argv.slice(1));
948
893
  return;
949
894
  }
950
895
 
951
- if (argv[0] === "install-host") {
896
+ if (argv[0] === 'install-host') {
952
897
  await installHostPrompt(argv.slice(1));
953
898
  return;
954
899
  }
955
900
 
956
- if (argv[0] === "validate") {
957
- await runDistCommand("validate", argv.slice(1));
901
+ if (argv[0] === 'validate') {
902
+ await runDistCommand('validate', argv.slice(1));
958
903
  return;
959
904
  }
960
905
 
961
906
  const wrapperArgs = [...argv];
962
- if (defaultSingleStep && !hasFlag(wrapperArgs, "--single-step")) {
963
- wrapperArgs.push("--single-step");
907
+ if (defaultSingleStep && !hasFlag(wrapperArgs, '--single-step')) {
908
+ wrapperArgs.push('--single-step');
964
909
  }
965
- const rootValue = resolve(getFlag(wrapperArgs, "--root") ?? ".");
966
- const artifactsDir = resolve(
967
- getFlag(wrapperArgs, "--artifacts-dir") ??
968
- join(rootValue, ".audit-artifacts"),
969
- );
910
+ const rootValue = resolve(getFlag(wrapperArgs, '--root') ?? '.');
911
+ const artifactsDir = resolve(getFlag(wrapperArgs, '--artifacts-dir') ?? join(rootValue, '.audit-artifacts'));
970
912
 
971
- setDefaultFlag(wrapperArgs, "--root", rootValue);
972
- setDefaultFlag(wrapperArgs, "--artifacts-dir", artifactsDir);
913
+ setDefaultFlag(wrapperArgs, '--root', rootValue);
914
+ setDefaultFlag(wrapperArgs, '--artifacts-dir', artifactsDir);
973
915
 
974
916
  if (ensureArtifactsDir) {
975
917
  await mkdir(artifactsDir, { recursive: true });
976
918
  }
977
919
 
978
920
  await ensureBuilt();
979
- const command = hasFlag(wrapperArgs, "--single-step")
980
- ? "advance-audit"
981
- : "run-to-completion";
921
+ const command = hasFlag(wrapperArgs, '--single-step') ? 'advance-audit' : 'run-to-completion';
982
922
  await run(nodeExecutable(), [distEntry, command, ...wrapperArgs]);
983
923
  }