auditor-lambda 0.8.0 → 0.9.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.
Files changed (98) hide show
  1. package/audit-code-wrapper-lib.mjs +149 -129
  2. package/dist/adapters/normalizeExternal.js +6 -3
  3. package/dist/cli/args.d.ts +0 -1
  4. package/dist/cli/args.js +0 -6
  5. package/dist/cli/dispatch.js +3 -2
  6. package/dist/cli/lineIndex.js +4 -1
  7. package/dist/cli/mergeAndIngestCommand.d.ts +1 -0
  8. package/dist/cli/mergeAndIngestCommand.js +219 -0
  9. package/dist/cli/nextStepCommand.js +5 -1
  10. package/dist/cli/runToCompletion.d.ts +9 -0
  11. package/dist/cli/runToCompletion.js +655 -480
  12. package/dist/cli/statusCommand.d.ts +1 -0
  13. package/dist/cli/statusCommand.js +113 -0
  14. package/dist/cli/submitPacketCommand.d.ts +1 -0
  15. package/dist/cli/submitPacketCommand.js +155 -0
  16. package/dist/cli/workerResult.d.ts +1 -1
  17. package/dist/cli/workerRunCommand.d.ts +1 -0
  18. package/dist/cli/workerRunCommand.js +88 -0
  19. package/dist/cli.js +14 -563
  20. package/dist/extractors/analyzers/sql.js +4 -1
  21. package/dist/extractors/analyzers/treeSitter.js +29 -15
  22. package/dist/extractors/analyzers/typescript.js +10 -8
  23. package/dist/extractors/designAssessment.js +43 -24
  24. package/dist/extractors/graph.js +139 -73
  25. package/dist/extractors/pathPatterns.js +17 -5
  26. package/dist/io/runArtifactTypes.d.ts +18 -0
  27. package/dist/io/runArtifactTypes.js +1 -0
  28. package/dist/io/runArtifacts.d.ts +2 -18
  29. package/dist/io/runArtifacts.js +14 -3
  30. package/dist/mcp/server.js +9 -0
  31. package/dist/orchestrator/advance.js +37 -22
  32. package/dist/orchestrator/artifactFreshness.js +2 -2
  33. package/dist/orchestrator/autoFixExecutor.d.ts +1 -1
  34. package/dist/orchestrator/autoFixExecutor.js +16 -8
  35. package/dist/orchestrator/dependencyMap.d.ts +1 -1
  36. package/dist/orchestrator/dependencyMap.js +7 -1
  37. package/dist/orchestrator/fileAnchors.js +14 -3
  38. package/dist/orchestrator/flowCoverage.js +1 -0
  39. package/dist/orchestrator/flowRequeue.js +4 -1
  40. package/dist/orchestrator/{internalExecutors.d.ts → ingestionExecutors.d.ts} +0 -6
  41. package/dist/orchestrator/ingestionExecutors.js +237 -0
  42. package/dist/orchestrator/intakeExecutors.d.ts +3 -0
  43. package/dist/orchestrator/intakeExecutors.js +25 -0
  44. package/dist/orchestrator/planningExecutors.d.ts +4 -0
  45. package/dist/orchestrator/planningExecutors.js +95 -0
  46. package/dist/orchestrator/runtimeCommand.js +7 -15
  47. package/dist/orchestrator/selectiveDeepening/conflict.d.ts +8 -0
  48. package/dist/orchestrator/selectiveDeepening/conflict.js +71 -0
  49. package/dist/orchestrator/selectiveDeepening/findingFollowup.d.ts +10 -0
  50. package/dist/orchestrator/selectiveDeepening/findingFollowup.js +52 -0
  51. package/dist/orchestrator/selectiveDeepening/highRiskClean.d.ts +7 -0
  52. package/dist/orchestrator/selectiveDeepening/highRiskClean.js +44 -0
  53. package/dist/orchestrator/selectiveDeepening/index.d.ts +18 -0
  54. package/dist/orchestrator/selectiveDeepening/index.js +128 -0
  55. package/dist/orchestrator/selectiveDeepening/lensVerification.d.ts +12 -0
  56. package/dist/orchestrator/selectiveDeepening/lensVerification.js +242 -0
  57. package/dist/orchestrator/selectiveDeepening/runtimeValidation.d.ts +13 -0
  58. package/dist/orchestrator/selectiveDeepening/runtimeValidation.js +57 -0
  59. package/dist/orchestrator/selectiveDeepening/shared.d.ts +45 -0
  60. package/dist/orchestrator/selectiveDeepening/shared.js +128 -0
  61. package/dist/orchestrator/selectiveDeepening/stewardFollowup.d.ts +6 -0
  62. package/dist/orchestrator/selectiveDeepening/stewardFollowup.js +72 -0
  63. package/dist/orchestrator/selectiveDeepening.d.ts +2 -20
  64. package/dist/orchestrator/selectiveDeepening.js +6 -760
  65. package/dist/orchestrator/staleness.js +3 -3
  66. package/dist/orchestrator/structureExecutors.d.ts +5 -0
  67. package/dist/orchestrator/structureExecutors.js +94 -0
  68. package/dist/orchestrator/taskBuilder.d.ts +2 -2
  69. package/dist/orchestrator/taskBuilder.js +101 -82
  70. package/dist/providers/index.d.ts +7 -0
  71. package/dist/providers/index.js +14 -95
  72. package/dist/quota/discoveredLimits.d.ts +1 -0
  73. package/dist/quota/discoveredLimits.js +7 -1
  74. package/dist/quota/index.d.ts +0 -2
  75. package/dist/quota/index.js +1 -2
  76. package/dist/reporting/workBlocks.js +7 -4
  77. package/dist/types/reviewPlanning.d.ts +23 -16
  78. package/dist/validation/auditResults.js +97 -95
  79. package/dist/validation/sessionConfig.d.ts +2 -2
  80. package/dist/validation/sessionConfig.js +14 -7
  81. package/package.json +3 -2
  82. package/schemas/audit_findings.schema.json +3 -3
  83. package/schemas/critical_flows.schema.json +3 -2
  84. package/schemas/dispatch_quota.schema.json +1 -1
  85. package/schemas/graph_bundle.schema.json +1 -1
  86. package/schemas/review_packets.schema.json +1 -1
  87. package/schemas/step_contract.schema.json +80 -0
  88. package/scripts/postinstall.mjs +19 -2
  89. package/skills/audit-code/opencode-command-template.txt +3 -3
  90. package/dist/orchestrator/internalExecutors.js +0 -424
  91. package/dist/providers/localSubprocessProvider.d.ts +0 -9
  92. package/dist/providers/localSubprocessProvider.js +0 -18
  93. package/dist/providers/subprocessTemplateProvider.d.ts +0 -8
  94. package/dist/providers/subprocessTemplateProvider.js +0 -59
  95. package/dist/providers/vscodeTaskProvider.d.ts +0 -7
  96. package/dist/providers/vscodeTaskProvider.js +0 -14
  97. package/dist/quota/probe.d.ts +0 -10
  98. package/dist/quota/probe.js +0 -18
@@ -1268,14 +1268,11 @@ async function createStoredZipBuffer(sourceDir) {
1268
1268
  return Buffer.concat([...localParts, centralDirectory, endOfCentralDirectory]);
1269
1269
  }
1270
1270
 
1271
- async function buildClaudeDesktopBundle(root, results) {
1272
- const bundleRoot = join(root, '.audit-code', 'install', 'claude-desktop', 'bundle');
1273
- const serverRoot = join(bundleRoot, 'server');
1274
- const manifestPath = join(bundleRoot, 'manifest.json');
1275
- const serverEntrypointPath = join(serverRoot, 'index.js');
1276
- const dxtPath = join(root, '.audit-code', 'install', 'claude-desktop', 'auditor-lambda.dxt');
1277
- const mcpbPath = join(root, '.audit-code', 'install', 'claude-desktop', 'auditor-lambda.mcpb');
1278
-
1271
+ // Copy the auditor's dist + schemas + skills + wrapper entrypoints (and, best
1272
+ // effort, the resolved @audit-tools/shared dist) into the Claude Desktop bundle
1273
+ // tree. Returns whether the bundle directory already existed (for the
1274
+ // created/updated result marker).
1275
+ async function copyClaudeDesktopBundleFiles(bundleRoot, serverRoot) {
1279
1276
  const bundleExisted = await fileExists(bundleRoot);
1280
1277
  await mkdir(serverRoot, { recursive: true });
1281
1278
  await cp(distEntry.replace(/dist[\\/]index\.js$/, 'dist'), join(bundleRoot, 'dist'), { recursive: true, force: true });
@@ -1300,9 +1297,13 @@ async function buildClaudeDesktopBundle(root, results) {
1300
1297
  // @audit-tools/shared not resolvable — bundle will use runtime resolution
1301
1298
  }
1302
1299
 
1303
- results.push({ path: bundleRoot, mode: bundleExisted ? 'updated' : 'created' });
1300
+ return { bundleExisted };
1301
+ }
1304
1302
 
1305
- const serverEntry = [
1303
+ // Source for the bundle's server/index.js shim: it spawns the bundled
1304
+ // audit-code.mjs in MCP mode against the user-configured repo root.
1305
+ function renderClaudeDesktopServerEntry() {
1306
+ return [
1306
1307
  "import { spawn } from 'node:child_process';",
1307
1308
  "import { dirname, join } from 'node:path';",
1308
1309
  "import { fileURLToPath } from 'node:url';",
@@ -1328,10 +1329,12 @@ async function buildClaudeDesktopBundle(root, results) {
1328
1329
  '});',
1329
1330
  '',
1330
1331
  ].join('\n');
1332
+ }
1331
1333
 
1332
- results.push(await writeGeneratedMarkdown(serverEntrypointPath, serverEntry));
1333
-
1334
- const manifest = {
1334
+ // The DXT/MCPB manifest.json describing the bundled node MCP server and its
1335
+ // Claude Desktop user-config inputs.
1336
+ function buildClaudeDesktopManifest() {
1337
+ return {
1335
1338
  manifest_version: '0.3',
1336
1339
  name: 'auditor-lambda',
1337
1340
  display_name: 'Auditor Lambda',
@@ -1386,8 +1389,26 @@ async function buildClaudeDesktopBundle(root, results) {
1386
1389
  },
1387
1390
  },
1388
1391
  };
1392
+ }
1393
+
1394
+ // Orchestrates the Claude Desktop bundle: copy files, write the server shim and
1395
+ // manifest, then zip into the .dxt / .mcpb archives. Pushes each generated file
1396
+ // into `results`.
1397
+ async function buildClaudeDesktopBundle(root, results) {
1398
+ const bundleRoot = join(root, '.audit-code', 'install', 'claude-desktop', 'bundle');
1399
+ const serverRoot = join(bundleRoot, 'server');
1400
+ const manifestPath = join(bundleRoot, 'manifest.json');
1401
+ const serverEntrypointPath = join(serverRoot, 'index.js');
1402
+ const dxtPath = join(root, '.audit-code', 'install', 'claude-desktop', 'auditor-lambda.dxt');
1403
+ const mcpbPath = join(root, '.audit-code', 'install', 'claude-desktop', 'auditor-lambda.mcpb');
1389
1404
 
1390
- results.push(await writeGeneratedJson(manifestPath, manifest));
1405
+ const { bundleExisted } = await copyClaudeDesktopBundleFiles(bundleRoot, serverRoot);
1406
+ results.push({ path: bundleRoot, mode: bundleExisted ? 'updated' : 'created' });
1407
+
1408
+ results.push(
1409
+ await writeGeneratedMarkdown(serverEntrypointPath, renderClaudeDesktopServerEntry()),
1410
+ );
1411
+ results.push(await writeGeneratedJson(manifestPath, buildClaudeDesktopManifest()));
1391
1412
 
1392
1413
  const archive = await createStoredZipBuffer(bundleRoot);
1393
1414
  results.push(await writeGeneratedBinary(dxtPath, archive));
@@ -2509,21 +2530,15 @@ async function ensureBootstrap(argv) {
2509
2530
  return payload;
2510
2531
  }
2511
2532
 
2512
- async function installBootstrap(argv, options = {}) {
2513
- const host = (getFlag(argv, '--host') ?? DEFAULT_INSTALL_HOST).toLowerCase();
2514
- const root = resolve(getFlag(argv, '--root') ?? '.');
2515
- await assertDirectoryExists(root, 'Target repository root');
2516
- const profile = getInstallProfile(host);
2517
- const promptSource = await readFile(promptAssetPath, 'utf8');
2518
- const skillSource = (await readFile(skillAssetPath, 'utf8')).replace(/\r\n/g, '\n');
2519
- const { body: promptBody } = splitFrontmatter(promptSource);
2533
+ // Compute the full asset-path map for an install profile. Each per-host path is
2534
+ // gated on its profile flag (null when the host is not part of this profile).
2535
+ function buildInstallAssetPaths(root, profile) {
2520
2536
  const installedPromptPath = join(root, '.audit-code', 'install', INSTALLED_PROMPT_FILENAME);
2521
- const legacyInstalledPromptPath = join(root, '.audit-code', 'install', 'audit-code.prompt.md');
2522
2537
  const installedSkillPath = join(root, '.audit-code', 'install', 'SKILL.md');
2523
2538
  const installGuidePath = join(root, '.audit-code', 'install', INSTALL_GUIDE_FILENAME);
2524
2539
  const installManifestPath = join(root, '.audit-code', 'install', INSTALL_MANIFEST_FILENAME);
2525
2540
  const mcpLauncherPath = join(root, '.audit-code', 'install', MCP_LAUNCHER_FILENAME);
2526
- const assetPaths = {
2541
+ return {
2527
2542
  installedPromptPath,
2528
2543
  installedSkillPath,
2529
2544
  installGuidePath,
@@ -2579,144 +2594,149 @@ async function installBootstrap(argv, options = {}) {
2579
2594
  ? join(root, '.agent', 'skills', 'audit-code', 'SKILL.md')
2580
2595
  : null,
2581
2596
  };
2597
+ }
2582
2598
 
2599
+ // Always-written core assets (installed prompt + skill, shared MCP launcher,
2600
+ // AGENTS/copilot compatibility directive blocks) plus legacy-surface cleanup.
2601
+ async function writeCoreInstallAssets(root, assetPaths, promptSource, skillSource) {
2583
2602
  const results = [];
2603
+ const legacyInstalledPromptPath = join(root, '.audit-code', 'install', 'audit-code.prompt.md');
2584
2604
  if (await fileExists(legacyInstalledPromptPath)) {
2585
2605
  await unlink(legacyInstalledPromptPath).catch(() => {});
2586
2606
  }
2607
+ results.push(await writeGeneratedMarkdown(assetPaths.installedPromptPath, promptSource));
2608
+ results.push(await writeGeneratedMarkdown(assetPaths.installedSkillPath, skillSource));
2587
2609
  results.push(
2588
- await writeGeneratedMarkdown(
2589
- installedPromptPath,
2590
- promptSource,
2591
- ),
2592
- );
2593
- results.push(await writeGeneratedMarkdown(installedSkillPath, skillSource));
2594
-
2595
- results.push(
2596
- await writeGeneratedMarkdown(mcpLauncherPath, renderSharedMcpLauncher(repoRoot)),
2610
+ await writeGeneratedMarkdown(assetPaths.mcpLauncherPath, renderSharedMcpLauncher(repoRoot)),
2597
2611
  );
2598
2612
 
2599
2613
  const compatibilityBlockTargets = [
2600
2614
  assetPaths.agentsInstructionsPath,
2601
2615
  assetPaths.copilotInstructionsPath,
2602
2616
  ].filter(Boolean);
2603
-
2604
2617
  for (const targetPath of compatibilityBlockTargets) {
2605
2618
  results.push(
2606
2619
  await writeManagedMarkdown(
2607
2620
  targetPath,
2608
2621
  buildInstallDirective(
2609
- relative(dirname(targetPath), installedPromptPath) || `./.audit-code/install/${INSTALLED_PROMPT_FILENAME}`,
2622
+ relative(dirname(targetPath), assetPaths.installedPromptPath) || `./.audit-code/install/${INSTALLED_PROMPT_FILENAME}`,
2610
2623
  ),
2611
2624
  ),
2612
2625
  );
2613
2626
  }
2614
2627
 
2615
2628
  results.push(...await removeLegacyAuditCodeSurfaceFiles(root));
2629
+ return results;
2630
+ }
2616
2631
 
2617
- if (profile.writeCodex) {
2618
- results.push(
2619
- await writeGeneratedMarkdown(
2620
- assetPaths.codexSkillPath,
2621
- skillSource,
2622
- ),
2623
- );
2624
- results.push(
2625
- await writeGeneratedMarkdown(
2626
- assetPaths.codexPromptPath,
2627
- promptSource,
2628
- ),
2629
- );
2630
- results.push(
2631
- await writeGeneratedMarkdown(
2632
- assetPaths.codexMcpSetupPath,
2633
- renderCodexMcpSetupGuide(root),
2634
- ),
2635
- );
2636
- results.push(
2637
- await writeGeneratedMarkdown(
2638
- assetPaths.codexAutomationRecipePath,
2639
- renderCodexAutomationRecipe(),
2632
+ async function writeCodexAssets(assetPaths, promptSource, skillSource, root) {
2633
+ return [
2634
+ await writeGeneratedMarkdown(assetPaths.codexSkillPath, skillSource),
2635
+ await writeGeneratedMarkdown(assetPaths.codexPromptPath, promptSource),
2636
+ await writeGeneratedMarkdown(assetPaths.codexMcpSetupPath, renderCodexMcpSetupGuide(root)),
2637
+ await writeGeneratedMarkdown(assetPaths.codexAutomationRecipePath, renderCodexAutomationRecipe()),
2638
+ ];
2639
+ }
2640
+
2641
+ // Builds the Claude Desktop DXT/MCPB bundle (mutating assetPaths with the
2642
+ // produced bundle paths) and writes the project template + remote connector.
2643
+ async function writeClaudeDesktopAssets(assetPaths, root) {
2644
+ const results = [];
2645
+ const claudeDesktopBundle = await buildClaudeDesktopBundle(root, results);
2646
+ assetPaths.claudeDesktopDxtPath = claudeDesktopBundle.dxtPath;
2647
+ assetPaths.claudeDesktopMcpbPath = claudeDesktopBundle.mcpbPath;
2648
+ results.push(
2649
+ await writeGeneratedMarkdown(
2650
+ assetPaths.claudeDesktopProjectTemplatePath,
2651
+ renderClaudeDesktopProjectTemplate(),
2652
+ ),
2653
+ );
2654
+ results.push(
2655
+ await writeGeneratedJson(
2656
+ assetPaths.claudeDesktopRemoteConnectorPath,
2657
+ JSON.parse(renderClaudeDesktopRemoteConnectorTemplate()),
2658
+ ),
2659
+ );
2660
+ return results;
2661
+ }
2662
+
2663
+ async function writeOpenCodeAssets(assetPaths, root) {
2664
+ return [
2665
+ await writeMergedGeneratedJson(
2666
+ assetPaths.opencodeConfigPath,
2667
+ 'OpenCode project config',
2668
+ (existing) => buildMergedOpenCodeProjectConfig(existing, root),
2669
+ ),
2670
+ ];
2671
+ }
2672
+
2673
+ async function writeVSCodeAssets(assetPaths, promptBody) {
2674
+ return [
2675
+ await writeGeneratedMarkdown(
2676
+ assetPaths.vscodePromptPath,
2677
+ renderPromptFile(
2678
+ {
2679
+ name: 'audit-code',
2680
+ description: 'Autonomous local loop code auditing',
2681
+ agent: 'auditor',
2682
+ },
2683
+ promptBody,
2640
2684
  ),
2641
- );
2642
- }
2685
+ ),
2686
+ await writeGeneratedMarkdown(assetPaths.vscodeAgentPath, renderVSCodeAgentFile()),
2687
+ await writeMergedGeneratedJson(
2688
+ assetPaths.vscodeMcpConfigPath,
2689
+ 'VS Code MCP config',
2690
+ buildMergedVSCodeMcpConfig,
2691
+ ),
2692
+ ];
2693
+ }
2694
+
2695
+ async function writeAntigravityAssets(assetPaths, promptBody, skillSource, root) {
2696
+ return [
2697
+ await writeGeneratedMarkdown(
2698
+ assetPaths.antigravityPlanningGuidePath,
2699
+ renderAntigravityPlanningGuide(root),
2700
+ ),
2701
+ await writeGeneratedMarkdown(assetPaths.geminiCommandPath, renderGeminiCommandToml(promptBody)),
2702
+ await writeGeneratedMarkdown(assetPaths.antigravitySkillPath, skillSource),
2703
+ ];
2704
+ }
2705
+
2706
+ async function installBootstrap(argv, options = {}) {
2707
+ const host = (getFlag(argv, '--host') ?? DEFAULT_INSTALL_HOST).toLowerCase();
2708
+ const root = resolve(getFlag(argv, '--root') ?? '.');
2709
+ await assertDirectoryExists(root, 'Target repository root');
2710
+ const profile = getInstallProfile(host);
2711
+ const promptSource = await readFile(promptAssetPath, 'utf8');
2712
+ const skillSource = (await readFile(skillAssetPath, 'utf8')).replace(/\r\n/g, '\n');
2713
+ const { body: promptBody } = splitFrontmatter(promptSource);
2714
+ const assetPaths = buildInstallAssetPaths(root, profile);
2715
+ const {
2716
+ installedPromptPath,
2717
+ installedSkillPath,
2718
+ installGuidePath,
2719
+ installManifestPath,
2720
+ mcpLauncherPath,
2721
+ } = assetPaths;
2643
2722
 
2723
+ const results = [];
2724
+ results.push(...await writeCoreInstallAssets(root, assetPaths, promptSource, skillSource));
2725
+
2726
+ if (profile.writeCodex) {
2727
+ results.push(...await writeCodexAssets(assetPaths, promptSource, skillSource, root));
2728
+ }
2644
2729
  if (profile.writeClaudeDesktop) {
2645
- const claudeDesktopBundle = await buildClaudeDesktopBundle(root, results);
2646
- assetPaths.claudeDesktopDxtPath = claudeDesktopBundle.dxtPath;
2647
- assetPaths.claudeDesktopMcpbPath = claudeDesktopBundle.mcpbPath;
2648
- results.push(
2649
- await writeGeneratedMarkdown(
2650
- assetPaths.claudeDesktopProjectTemplatePath,
2651
- renderClaudeDesktopProjectTemplate(),
2652
- ),
2653
- );
2654
- results.push(
2655
- await writeGeneratedJson(
2656
- assetPaths.claudeDesktopRemoteConnectorPath,
2657
- JSON.parse(renderClaudeDesktopRemoteConnectorTemplate()),
2658
- ),
2659
- );
2730
+ results.push(...await writeClaudeDesktopAssets(assetPaths, root));
2660
2731
  }
2661
-
2662
2732
  if (profile.writeOpenCode) {
2663
- results.push(
2664
- await writeMergedGeneratedJson(
2665
- assetPaths.opencodeConfigPath,
2666
- 'OpenCode project config',
2667
- (existing) => buildMergedOpenCodeProjectConfig(existing, root),
2668
- ),
2669
- );
2733
+ results.push(...await writeOpenCodeAssets(assetPaths, root));
2670
2734
  }
2671
-
2672
2735
  if (profile.writeVSCode) {
2673
- results.push(
2674
- await writeGeneratedMarkdown(
2675
- assetPaths.vscodePromptPath,
2676
- renderPromptFile(
2677
- {
2678
- name: 'audit-code',
2679
- description: 'Autonomous local loop code auditing',
2680
- agent: 'auditor',
2681
- },
2682
- promptBody,
2683
- ),
2684
- ),
2685
- );
2686
- results.push(
2687
- await writeGeneratedMarkdown(
2688
- assetPaths.vscodeAgentPath,
2689
- renderVSCodeAgentFile(),
2690
- ),
2691
- );
2692
- results.push(
2693
- await writeMergedGeneratedJson(
2694
- assetPaths.vscodeMcpConfigPath,
2695
- 'VS Code MCP config',
2696
- buildMergedVSCodeMcpConfig,
2697
- ),
2698
- );
2736
+ results.push(...await writeVSCodeAssets(assetPaths, promptBody));
2699
2737
  }
2700
-
2701
2738
  if (profile.writeAntigravity) {
2702
- results.push(
2703
- await writeGeneratedMarkdown(
2704
- assetPaths.antigravityPlanningGuidePath,
2705
- renderAntigravityPlanningGuide(root),
2706
- ),
2707
- );
2708
- results.push(
2709
- await writeGeneratedMarkdown(
2710
- assetPaths.geminiCommandPath,
2711
- renderGeminiCommandToml(promptBody),
2712
- ),
2713
- );
2714
- results.push(
2715
- await writeGeneratedMarkdown(
2716
- assetPaths.antigravitySkillPath,
2717
- skillSource,
2718
- ),
2719
- );
2739
+ results.push(...await writeAntigravityAssets(assetPaths, promptBody, skillSource, root));
2720
2740
  }
2721
2741
 
2722
2742
  const hostGuidance = buildHostCatalog({
@@ -1,10 +1,13 @@
1
1
  export function normalizeGenericExternalResults(tool, items) {
2
+ const valid = items.filter((item) => item.path && item.summary);
3
+ const dropped = items.length - valid.length;
4
+ if (dropped > 0) {
5
+ process.stderr.write(`[audit-code] normalizeExternal: dropped ${dropped}/${items.length} ${tool} finding(s) missing path or summary\n`);
6
+ }
2
7
  return {
3
8
  tool,
4
9
  generated_at: new Date().toISOString(),
5
- results: items
6
- .filter((item) => item.path && item.summary)
7
- .map((item, index) => ({
10
+ results: valid.map((item, index) => ({
8
11
  id: item.id ?? `${tool}-${index + 1}`,
9
12
  category: item.category ?? "unknown",
10
13
  severity: item.severity ?? "unknown",
@@ -51,7 +51,6 @@ export declare function getTimeoutMs(argv: string[], sessionConfig: SessionConfi
51
51
  export declare function getExplicitProvider(argv: string[]): string | undefined;
52
52
  export declare function getHostModel(argv: string[]): string | null;
53
53
  export declare function getHostMaxActiveSubagents(argv: string[]): number | null;
54
- export declare function getQuotaProbeMode(argv: string[], sessionConfig: SessionConfig): "auto" | "never" | "force";
55
54
  export declare function resolveRunProviderName(argv: string[], sessionConfig: SessionConfig): string;
56
55
  export declare function chunkArray<T>(arr: T[], size: number): T[][];
57
56
  export declare function getUiMode(argv: string[], fallback?: UiMode): UiMode;
package/dist/cli/args.js CHANGED
@@ -185,12 +185,6 @@ export function getHostModel(argv) {
185
185
  export function getHostMaxActiveSubagents(argv) {
186
186
  return parsePositiveIntegerFlag(argv, "--host-max-active-subagents") ?? null;
187
187
  }
188
- export function getQuotaProbeMode(argv, sessionConfig) {
189
- const raw = getFlag(argv, "--quota-probe") ?? sessionConfig.quota?.probe ?? "auto";
190
- if (raw === "auto" || raw === "never" || raw === "force")
191
- return raw;
192
- return "auto";
193
- }
194
188
  export function resolveRunProviderName(argv, sessionConfig) {
195
189
  return resolveFreshSessionProviderName(getExplicitProvider(argv), sessionConfig);
196
190
  }
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { existsSync } from "node:fs";
3
3
  import { isAbsolute, join, relative, resolve } from "node:path";
4
- import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
4
+ import { isFileMissingError, readJsonFile, writeJsonFile, DEFAULT_EMPIRICAL_HALF_LIFE_HOURS, } from "@audit-tools/shared";
5
5
  import { buildQuotaSource } from "@audit-tools/shared/quota/compositeQuotaSource";
6
6
  import { loadArtifactBundle } from "../io/artifacts.js";
7
7
  import { orderTasksForPacketReview, buildReviewPackets, sizeIndexFromManifest, } from "../orchestrator/reviewPackets.js";
@@ -480,7 +480,8 @@ export async function prepareDispatchArtifacts(params) {
480
480
  ?? null;
481
481
  const dispatchCachedLimits = await lookupDiscoveredLimits(quotaProviderKey).catch(() => null);
482
482
  const discoveredLimits = mergeDiscoveredLimits(providerLimits, dispatchCachedLimits);
483
- const halfLifeHours = sessionConfig.quota?.empirical_half_life_hours ?? 24;
483
+ const halfLifeHours = sessionConfig.quota?.empirical_half_life_hours ??
484
+ DEFAULT_EMPIRICAL_HALF_LIFE_HOURS;
484
485
  const quotaSource = buildQuotaSource({ halfLifeHours });
485
486
  const quotaSourceSnapshot = await quotaSource.queryCurrentUsage(quotaProviderKey).catch(() => null);
486
487
  const waveSchedule = scheduleWave({
@@ -3,9 +3,12 @@ import { countLines } from "./args.js";
3
3
  // Line-count helpers extracted from cli.ts. Pure functions over the repo
4
4
  // manifest / task file paths — used to annotate audit tasks with per-file line
5
5
  // counts and to build line indexes for prompt rendering.
6
+ // How many files to read concurrently when counting lines, bounding open file
7
+ // descriptors so a large repo manifest does not exhaust the fd limit.
8
+ const LINE_COUNT_BATCH_SIZE = 25;
6
9
  export async function buildLineIndex(root, repoManifest) {
7
10
  const entries = [];
8
- const batchSize = 25;
11
+ const batchSize = LINE_COUNT_BATCH_SIZE;
9
12
  for (let i = 0; i < repoManifest.files.length; i += batchSize) {
10
13
  const batch = repoManifest.files.slice(i, i + batchSize);
11
14
  const results = await Promise.all(batch.map(async (file) => {
@@ -0,0 +1 @@
1
+ export declare function cmdMergeAndIngest(argv: string[]): Promise<void>;
@@ -0,0 +1,219 @@
1
+ import { readFile, readdir } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { isFileMissingError, readJsonFile, writeJsonFile } from "@audit-tools/shared";
4
+ import { validateAuditResults } from "../validation/auditResults.js";
5
+ import { runAuditStep } from "./auditStep.js";
6
+ import { DISPATCH_RESULT_MAP_FILENAME, ACTIVE_DISPATCH_FILENAME, loadDispatchResultMap, entriesByTaskId, buildPendingAuditTasks, } from "./dispatch.js";
7
+ import { addFileLineCountHints } from "./lineIndex.js";
8
+ import { isCanonicalResultFilename, getArtifactsDir, getFlag } from "./args.js";
9
+ import { buildWorkerResult } from "./workerResult.js";
10
+ export async function cmdMergeAndIngest(argv) {
11
+ const runId = getFlag(argv, "--run-id");
12
+ if (!runId)
13
+ throw new Error("merge-and-ingest requires --run-id <run_id>");
14
+ const artifactsDir = getArtifactsDir(argv);
15
+ const runDir = join(artifactsDir, "runs", runId);
16
+ const taskResultsDir = join(runDir, "task-results");
17
+ const auditResultsPath = join(runDir, "audit-results.json");
18
+ const taskPath = join(runDir, "task.json");
19
+ const tasksPath = join(runDir, "pending-audit-tasks.json");
20
+ const workerTask = await readJsonFile(taskPath);
21
+ const resultMap = await loadDispatchResultMap(runDir);
22
+ if (!resultMap) {
23
+ throw new Error(`No ${DISPATCH_RESULT_MAP_FILENAME} found for run ${runId}; run prepare-dispatch first.`);
24
+ }
25
+ let allTasks = [];
26
+ try {
27
+ allTasks = await readJsonFile(tasksPath);
28
+ }
29
+ catch { /* may not exist */ }
30
+ const entryByTaskId = entriesByTaskId(resultMap.entries);
31
+ if (entryByTaskId.size !== resultMap.entries.length) {
32
+ throw new Error(`Dispatch result map for run ${runId} contains duplicate task entries.`);
33
+ }
34
+ const expectedPaths = new Set(resultMap.entries.map((entry) => resolve(entry.result_path)));
35
+ let files;
36
+ try {
37
+ files = (await readdir(taskResultsDir)).filter(f => f.endsWith(".json")).sort();
38
+ }
39
+ catch {
40
+ files = [];
41
+ }
42
+ const passing = [];
43
+ const failing = [];
44
+ const seenTaskIds = new Set();
45
+ let spuriousFileCount = 0;
46
+ const fallbackByTaskId = new Map();
47
+ for (const filename of files) {
48
+ const filePath = resolve(join(taskResultsDir, filename));
49
+ if (expectedPaths.has(filePath))
50
+ continue;
51
+ // Not part of this round's plan. Still read it so a current task can be
52
+ // recovered by task_id (e.g. a subagent wrote a valid result under a
53
+ // non-assigned name).
54
+ try {
55
+ const raw = await readFile(filePath, "utf8");
56
+ const parsed = JSON.parse(raw);
57
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
58
+ const tid = typeof parsed.task_id === "string"
59
+ ? String(parsed.task_id) : undefined;
60
+ if (tid && !fallbackByTaskId.has(tid)) {
61
+ fallbackByTaskId.set(tid, parsed);
62
+ }
63
+ }
64
+ }
65
+ catch { /* not parseable — skip */ }
66
+ // Only genuinely stray files are "spurious". Canonical per-task result files
67
+ // (<stem>_<digest>.json) left by prior deepening rounds in the same
68
+ // task-results/ dir are legitimate and must not inflate the count or bury
69
+ // the real stray-file signal (3 -> 191 over a run before this fix).
70
+ if (!isCanonicalResultFilename(filename)) {
71
+ spuriousFileCount++;
72
+ process.stderr.write(`[merge-and-ingest] Warning: unexpected file in task-results/: ${filename}\n`);
73
+ }
74
+ }
75
+ for (const task of allTasks) {
76
+ const entry = entryByTaskId.get(task.task_id);
77
+ if (!entry) {
78
+ failing.push({
79
+ task_id: task.task_id,
80
+ errors: ["Missing dispatch result-map entry for assigned task."],
81
+ });
82
+ continue;
83
+ }
84
+ const filePath = entry.result_path;
85
+ let obj;
86
+ try {
87
+ obj = JSON.parse(await readFile(filePath, "utf8"));
88
+ }
89
+ catch (e) {
90
+ if (isFileMissingError(e)) {
91
+ const fallback = fallbackByTaskId.get(task.task_id);
92
+ if (fallback) {
93
+ process.stderr.write(`[merge-and-ingest] Recovered result for '${task.task_id}' from unexpected file (matched by task_id)\n`);
94
+ obj = fallback;
95
+ }
96
+ else {
97
+ failing.push({
98
+ task_id: task.task_id,
99
+ errors: ["Missing audit result for assigned task."],
100
+ });
101
+ continue;
102
+ }
103
+ }
104
+ else {
105
+ failing.push({ task_id: task.task_id, errors: [`Invalid JSON: ${e.message}`] });
106
+ continue;
107
+ }
108
+ }
109
+ const record = obj && typeof obj === "object" && !Array.isArray(obj)
110
+ ? obj
111
+ : undefined;
112
+ const taskId = typeof record?.task_id === "string"
113
+ ? String(record.task_id) : undefined;
114
+ const resultErrors = [];
115
+ if (taskId) {
116
+ if (seenTaskIds.has(taskId)) {
117
+ resultErrors.push(`Duplicate audit result for assigned task '${taskId}'.`);
118
+ }
119
+ else {
120
+ seenTaskIds.add(taskId);
121
+ }
122
+ if (taskId !== task.task_id) {
123
+ resultErrors.push(`Result file is assigned to '${task.task_id}' but contains task_id '${taskId}'.`);
124
+ }
125
+ }
126
+ const issues = validateAuditResults([obj], [task], { lineIndex: task.file_line_counts ?? {} });
127
+ resultErrors.push(...issues
128
+ .filter(i => i.severity === "error")
129
+ .map(i => i.message));
130
+ if (resultErrors.length === 0) {
131
+ passing.push(obj);
132
+ }
133
+ else {
134
+ failing.push({ task_id: taskId ?? task.task_id, errors: resultErrors });
135
+ }
136
+ }
137
+ await writeJsonFile(auditResultsPath, passing);
138
+ const failedTasksPath = join(runDir, "failed-tasks.json");
139
+ if (failing.length > 0) {
140
+ await writeJsonFile(failedTasksPath, failing);
141
+ }
142
+ if (passing.length === 0 && failing.length > 0) {
143
+ throw new Error(`All ${failing.length} assigned task result(s) were missing or invalid; blocked before ingestion. See ${failedTasksPath}`);
144
+ }
145
+ const findingCount = passing.reduce((sum, result) => sum + result.findings.length, 0);
146
+ let result = null;
147
+ if (passing.length > 0) {
148
+ result = await runAuditStep({
149
+ root: workerTask.repo_root,
150
+ artifactsDir,
151
+ preferredExecutor: "result_ingestion_executor",
152
+ auditResultsPath,
153
+ });
154
+ const updatedPendingTasks = await addFileLineCountHints(workerTask.repo_root, buildPendingAuditTasks(result.updated_bundle));
155
+ await writeJsonFile(tasksPath, updatedPendingTasks);
156
+ }
157
+ const activeDispatchPath = join(artifactsDir, ACTIVE_DISPATCH_FILENAME);
158
+ try {
159
+ const dispatch = await readJsonFile(activeDispatchPath);
160
+ if (dispatch.run_id === runId) {
161
+ dispatch.status = failing.length > 0 ? "active" : "merged";
162
+ await writeJsonFile(activeDispatchPath, dispatch);
163
+ }
164
+ }
165
+ catch { /* no active dispatch file — skip */ }
166
+ let retryDispatchPath = null;
167
+ if (failing.length > 0) {
168
+ const failedTaskIds = new Set(failing.map((f) => f.task_id));
169
+ const failedPacketIds = [
170
+ ...new Set(resultMap.entries
171
+ .filter((e) => failedTaskIds.has(e.task_id))
172
+ .map((e) => e.packet_id)),
173
+ ];
174
+ const retryDispatch = {
175
+ run_id: runId,
176
+ retry_packet_ids: failedPacketIds,
177
+ failed_task_count: failing.length,
178
+ accepted_task_count: passing.length,
179
+ };
180
+ retryDispatchPath = join(runDir, "retry-dispatch.json");
181
+ await writeJsonFile(retryDispatchPath, retryDispatch);
182
+ process.stderr.write(`[merge-and-ingest] ${passing.length} accepted, ${failing.length} failed. ` +
183
+ `Retry packets: ${failedPacketIds.join(", ")}\n`);
184
+ }
185
+ const status = failing.length > 0
186
+ ? "partial"
187
+ : (result?.progress_made ? "completed" : "no_progress");
188
+ const workerResult = buildWorkerResult({
189
+ runId,
190
+ obligationId: workerTask.obligation_id,
191
+ status: failing.length > 0 ? "no_progress" : (result?.progress_made ? "completed" : "no_progress"),
192
+ progressMade: result?.progress_made ?? false,
193
+ selectedExecutor: result?.selected_executor ?? null,
194
+ artifactsWritten: result?.artifacts_written ?? [],
195
+ summary: result?.progress_summary ?? `${failing.length} task(s) failed`,
196
+ nextLikelyStep: result?.next_likely_step ?? null,
197
+ errors: [],
198
+ });
199
+ await writeJsonFile(workerTask.result_path, workerResult);
200
+ console.log(JSON.stringify({
201
+ run_id: runId,
202
+ status,
203
+ accepted_count: passing.length,
204
+ rejected_count: failing.length,
205
+ spurious_file_count: spuriousFileCount,
206
+ finding_count: findingCount,
207
+ audit_results_path: auditResultsPath,
208
+ ...(retryDispatchPath ? { retry_dispatch_path: retryDispatchPath } : {}),
209
+ ...(result ? {
210
+ selected_executor: workerResult.selected_executor,
211
+ progress_made: workerResult.progress_made,
212
+ progress_summary: workerResult.summary,
213
+ next_likely_step: workerResult.next_likely_step,
214
+ } : {}),
215
+ }, null, 2));
216
+ if (failing.length > 0) {
217
+ process.exitCode = 2;
218
+ }
219
+ }
@@ -71,7 +71,11 @@ async function runDeterministicForNextStep(params) {
71
71
  if (taskFiles.size > 0) {
72
72
  const integrity = await checkFileIntegrity(params.root, bundle.repo_manifest, [...taskFiles]);
73
73
  if (!integrity.is_clean) {
74
- console.log(`File integrity check: ${integrity.changed_files.length} changed, ${integrity.missing_files.length} missing re-running intake.`);
74
+ // Route this diagnostic OFF stdout: cmdNextStep emits the step
75
+ // contract as the sole stdout payload via console.log(JSON.stringify),
76
+ // so a console.log here would corrupt the JSON-on-stdout contract.
77
+ process.stderr.write(`[audit-code] nextStep: integrity check — ${integrity.changed_files.length} changed, ` +
78
+ `${integrity.missing_files.length} missing, ${integrity.io_errors.length} io-error(s); re-running intake.\n`);
75
79
  await advanceAudit(bundle, { root: params.root, preferredExecutor: "intake_executor", opentoken: params.opentoken });
76
80
  continue;
77
81
  }