auditor-lambda 0.3.30 → 0.3.33

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 (36) hide show
  1. package/README.md +2 -1
  2. package/audit-code-wrapper-lib.mjs +208 -198
  3. package/dist/cli.d.ts +5 -0
  4. package/dist/cli.js +65 -101
  5. package/dist/extractors/risk.js +6 -4
  6. package/dist/io/artifacts.d.ts +2 -0
  7. package/dist/io/artifacts.js +1 -0
  8. package/dist/io/toolingManifest.d.ts +1 -0
  9. package/dist/io/toolingManifest.js +1 -1
  10. package/dist/mcp/server.d.ts +71 -0
  11. package/dist/mcp/server.js +261 -222
  12. package/dist/orchestrator/artifactFreshness.d.ts +4 -0
  13. package/dist/orchestrator/artifactFreshness.js +45 -0
  14. package/dist/orchestrator/artifactMetadata.js +2 -51
  15. package/dist/orchestrator/dependencyMap.js +14 -0
  16. package/dist/orchestrator/internalExecutors.js +8 -0
  17. package/dist/orchestrator/staleness.js +2 -46
  18. package/dist/orchestrator/state.js +1 -1
  19. package/dist/orchestrator/syntaxResolutionExecutor.js +121 -13
  20. package/dist/orchestrator/unitBuilder.js +2 -1
  21. package/dist/providers/spawnLoggedCommand.js +71 -18
  22. package/dist/providers/types.d.ts +5 -0
  23. package/dist/quota/scheduler.js +10 -2
  24. package/dist/quota/state.js +6 -2
  25. package/dist/supervisor/operatorHandoff.js +1 -1
  26. package/dist/types/externalAnalyzer.d.ts +10 -0
  27. package/dist/types/sessionConfig.d.ts +1 -0
  28. package/dist/types/workerSession.js +1 -2
  29. package/dist/validation/artifacts.js +36 -0
  30. package/dist/validation/sessionConfig.js +4 -0
  31. package/package.json +1 -1
  32. package/schemas/audit_task.schema.json +2 -2
  33. package/schemas/risk_register.schema.json +1 -1
  34. package/schemas/unit_manifest.schema.json +2 -1
  35. package/scripts/postinstall.mjs +10 -41
  36. package/skills/audit-code/audit-code.prompt.md +5 -0
package/README.md CHANGED
@@ -138,7 +138,8 @@ audit-code next-step
138
138
 
139
139
  This writes `.audit-artifacts/steps/current-step.json` and
140
140
  `.audit-artifacts/steps/current-prompt.md`; hosts should follow only the
141
- returned step prompt.
141
+ returned step prompt. MCP tools are compatibility adapters over this same
142
+ `next-step` contract rather than a separate orchestration path.
142
143
 
143
144
  For an operator-side artifact consistency check:
144
145
 
@@ -580,16 +580,17 @@ function replaceBackslashes(value) {
580
580
  function renderVSCodeAgentFile() {
581
581
  return [
582
582
  '---',
583
- 'description: Plan and orchestrate /audit-code with the installed auditor MCP server before making code changes.',
583
+ 'description: Plan and orchestrate /audit-code through the next-step machine before making code changes.',
584
584
  '---',
585
585
  '',
586
586
  '# Auditor Agent',
587
587
  '',
588
- 'Use the installed auditor MCP server as the primary integration surface for the audit workflow.',
588
+ 'Use `audit-code next-step` as the primary integration surface for the audit workflow. The installed auditor MCP server is a compatibility adapter over the same step contract.',
589
589
  '',
590
590
  'When the user asks to run or continue `/audit-code`:',
591
591
  '',
592
- '- call the `start_audit`, `get_status`, and `continue_audit` MCP tools instead of reconstructing backend state manually',
592
+ '- run `audit-code next-step` directly when shell access is available',
593
+ '- if MCP is the only available integration, call `start_audit`, `get_status`, and `continue_audit`; those tools return the same one-step contract',
593
594
  '- read `audit-code://handoff/current` and `audit-code://artifacts/current` when the audit blocks or you need current context',
594
595
  '- prefer imported audit results and runtime updates over ad hoc manual state edits',
595
596
  '- treat the deterministic audit report as the final source of truth once the audit completes',
@@ -615,7 +616,7 @@ function renderCodexMcpSetupGuide(root) {
615
616
  `args = ["${replaceBackslashes(toRepoRelativePath(root, join(root, '.audit-code', 'install', MCP_LAUNCHER_FILENAME)))}"]`,
616
617
  '```',
617
618
  '',
618
- 'Once the server is registered, ask Codex to use the `auditor` MCP tools to start or continue the audit.',
619
+ 'Prefer `audit-code next-step` directly. Use the registered `auditor` MCP tools only when shell access is unavailable; they return the same one-step contract.',
619
620
  '',
620
621
  ].join('\n');
621
622
  }
@@ -626,9 +627,9 @@ function renderCodexAutomationRecipe() {
626
627
  '',
627
628
  'Suggested recurring task:',
628
629
  '',
629
- '- Prompt: Re-run the autonomous audit workflow for this repository. Use the installed auditor MCP tools, summarize only new or regressed findings, and stop once the deterministic report is current.',
630
+ '- Prompt: Re-run the autonomous audit workflow for this repository with `audit-code next-step`, summarize only new or regressed findings, and stop once the deterministic report is current.',
630
631
  '- Cadence: daily on active branches or before release cut-offs',
631
- '- Inputs: repository root plus the installed `auditor` MCP server',
632
+ '- Inputs: repository root; the installed `auditor` MCP server is optional compatibility plumbing',
632
633
  '',
633
634
  'Use this recipe as a starting point for a Codex automation once the local workflow is stable in your environment.',
634
635
  '',
@@ -706,16 +707,17 @@ function renderOpenCodePermissionConfig() {
706
707
  const OPENCODE_MCP_COMMAND_TEMPLATE = [
707
708
  '# audit-code',
708
709
  '',
709
- 'Use the auditor MCP tools as the primary interface to the audit workflow.',
710
+ 'Use `audit-code next-step` as the primary interface to the audit workflow.',
710
711
  '',
711
- '1. Call `auditor_start_audit` to initialize and receive the first step.',
712
- '2. Read `prompt_content` in the response and follow it.',
713
- '3. When a step completes (not blocked), call `auditor_continue_audit` to advance.',
714
- '4. Stop when the step instructions say to stop.',
712
+ '1. Run `audit-code next-step` directly when shell access is available.',
713
+ '2. If MCP is your only available interface, call `auditor_start_audit` or `auditor_continue_audit`; both return the same one-step contract.',
714
+ '3. Read `prompt_content` in the response and follow it.',
715
+ '4. When a step completes (not blocked), run `audit-code next-step` again or call `auditor_continue_audit` as the compatibility adapter.',
716
+ '5. Stop when the step instructions say to stop.',
715
717
  '',
716
- 'Do not run shell commands. Use only `auditor_*` MCP tools and the `task` tool for subagent dispatch.',
718
+ 'Use the `task` tool or equivalent for subagent dispatch when the step tells you to fan out review work.',
717
719
  '',
718
- 'If `auditor_start_audit` is not listed in your available tools, stop immediately and tell the user the auditor MCP server is not connected. Do not read local files as a fallback.',
720
+ 'If neither shell access nor `auditor_start_audit` is available, stop and report that no next-step interface is connected.',
719
721
  ].join('\n');
720
722
 
721
723
  function renderOpenCodeProjectConfig(_root) {
@@ -939,7 +941,7 @@ function renderClaudeDesktopProjectTemplate() {
939
941
  'Suggested project instructions:',
940
942
  '',
941
943
  '- Treat `/audit-code` as the canonical autonomous audit workflow for this repository.',
942
- '- Prefer the installed auditor MCP tools over recreating state manually.',
944
+ '- Prefer `audit-code next-step`; use the installed auditor MCP tools only as a compatibility adapter over the same step contract.',
943
945
  '- Read the operator handoff and artifact resources before asking for more context.',
944
946
  '- Present the final deterministic audit report as work blocks first.',
945
947
  '',
@@ -951,7 +953,7 @@ function renderClaudeDesktopProjectTemplate() {
951
953
  '',
952
954
  'Starter prompt:',
953
955
  '',
954
- '> Start `/audit-code` for this repository using the installed auditor MCP tools. Continue until the audit is complete or blocked for operator input, and summarize the current handoff status before you stop.',
956
+ '> Start `/audit-code` for this repository using `audit-code next-step`. Continue until the audit is complete or blocked for operator input, and summarize the current handoff status before you stop.',
955
957
  '',
956
958
  ].join('\n');
957
959
  }
@@ -981,7 +983,7 @@ function renderAntigravityPlanningGuide(root) {
981
983
  '',
982
984
  '1. Open Antigravity in Planning mode.',
983
985
  '2. Load the repo-local prompt asset or the AGENTS instructions before starting the audit conversation.',
984
- '3. Ask Antigravity to use the installed auditor MCP server for structured state inspection and continuation.',
986
+ '3. Ask Antigravity to use `audit-code next-step` directly. The installed auditor MCP server is available only as compatibility plumbing when direct shell access is unavailable.',
985
987
  '4. Review Antigravity artifacts before accepting major code changes or imported evidence.',
986
988
  '',
987
989
  'Recommended repo-local paths:',
@@ -1266,9 +1268,9 @@ async function buildClaudeDesktopBundle(root, results) {
1266
1268
  name: 'auditor-lambda',
1267
1269
  display_name: 'Auditor Lambda',
1268
1270
  version: packageVersion,
1269
- description: 'Local MCP bundle for the /audit-code autonomous audit workflow.',
1271
+ description: 'Compatibility MCP bundle for the /audit-code autonomous audit workflow.',
1270
1272
  long_description:
1271
- 'Runs the auditor-lambda MCP server locally so Claude Desktop can start, inspect, and continue repository audits with structured tools, resources, and prompts.',
1273
+ 'Runs a local compatibility MCP adapter whose start and continue tools return the same audit-code next-step contract as the direct CLI loop.',
1272
1274
  author: {
1273
1275
  name: 'auditor-lambda',
1274
1276
  url: 'https://github.com/OhOkThisIsFine/auditor-lambda',
@@ -1371,6 +1373,18 @@ const INSTALL_HOST_DEFINITIONS = {
1371
1373
  profile: {
1372
1374
  writeAgents: true,
1373
1375
  },
1376
+ async verify({ checks, assetPaths, collectVerifyCheck: collect }) {
1377
+ await collect(checks, 'codex_global_surface', async () => {
1378
+ const content = await readFile(assetPaths.agentsInstructionsPath, 'utf8');
1379
+ if (!content.includes('/audit-code')) {
1380
+ throw new Error(`AGENTS instructions do not reference /audit-code: ${assetPaths.agentsInstructionsPath}`);
1381
+ }
1382
+ return {
1383
+ summary: 'Codex uses the global skill surface with AGENTS fallback instructions.',
1384
+ path: assetPaths.agentsInstructionsPath,
1385
+ };
1386
+ });
1387
+ },
1374
1388
  },
1375
1389
  'claude-desktop': {
1376
1390
  host: 'claude-desktop',
@@ -1378,7 +1392,7 @@ const INSTALL_HOST_DEFINITIONS = {
1378
1392
  support_level: 'supported',
1379
1393
  setup_kind: 'local-mcp-bundle',
1380
1394
  summary:
1381
- 'Install the generated local MCP bundle in Claude Desktop, then use the project template and prompt asset as supporting context.',
1395
+ 'Install the generated local MCP compatibility bundle in Claude Desktop, then use the project template and prompt asset as supporting context.',
1382
1396
  primary_path_key: 'claudeDesktopDxtPath',
1383
1397
  supporting_path_keys: [
1384
1398
  'claudeDesktopMcpbPath',
@@ -1388,12 +1402,91 @@ const INSTALL_HOST_DEFINITIONS = {
1388
1402
  ],
1389
1403
  steps: [
1390
1404
  'Open Claude Desktop Settings and install the generated `.dxt` bundle.',
1391
- 'Configure the repository root when prompted so the bundle can launch the local auditor MCP server.',
1405
+ 'Configure the repository root when prompted so the bundle can launch the local auditor MCP adapter.',
1392
1406
  'Use the project template and prompt asset to kick off `/audit-code` in conversation.',
1393
1407
  ],
1394
1408
  profile: {
1395
1409
  writeClaudeDesktop: true,
1396
1410
  },
1411
+ async verify({ checks, root, assetPaths, collectVerifyCheck: collect }) {
1412
+ const bundleManifestPath = join(
1413
+ root,
1414
+ '.audit-code',
1415
+ 'install',
1416
+ 'claude-desktop',
1417
+ 'bundle',
1418
+ 'manifest.json',
1419
+ );
1420
+ const bundleServerPath = join(
1421
+ root,
1422
+ '.audit-code',
1423
+ 'install',
1424
+ 'claude-desktop',
1425
+ 'bundle',
1426
+ 'server',
1427
+ 'index.js',
1428
+ );
1429
+
1430
+ await collect(checks, 'claude_bundle_manifest', async () => {
1431
+ const manifest = await readJson(bundleManifestPath, 'Claude Desktop bundle manifest');
1432
+ if (manifest?.server?.entry_point !== 'server/index.js') {
1433
+ throw new Error(`Claude Desktop bundle manifest has an unexpected entry_point: ${manifest?.server?.entry_point ?? 'missing'}.`);
1434
+ }
1435
+ return {
1436
+ summary: 'Claude Desktop bundle manifest parsed successfully.',
1437
+ path: bundleManifestPath,
1438
+ };
1439
+ });
1440
+ await collect(checks, 'claude_connector_template', async () => {
1441
+ const connector = await readJson(
1442
+ assetPaths.claudeDesktopRemoteConnectorPath,
1443
+ 'Claude Desktop remote connector template',
1444
+ );
1445
+ if (connector?.transport !== 'remote-mcp') {
1446
+ throw new Error(`Claude Desktop remote connector transport must be "remote-mcp", got ${connector?.transport ?? 'missing'}.`);
1447
+ }
1448
+ return {
1449
+ summary: 'Claude Desktop remote connector template parsed successfully.',
1450
+ path: assetPaths.claudeDesktopRemoteConnectorPath,
1451
+ };
1452
+ });
1453
+ await collect(checks, 'claude_dxt_archive', async () => ({
1454
+ summary: 'Claude Desktop .dxt bundle is present.',
1455
+ path: assetPaths.claudeDesktopDxtPath,
1456
+ size_bytes: await verifyZipFile(
1457
+ assetPaths.claudeDesktopDxtPath,
1458
+ 'Claude Desktop .dxt bundle',
1459
+ ),
1460
+ }));
1461
+ await collect(checks, 'claude_mcpb_archive', async () => ({
1462
+ summary: 'Claude Desktop .mcpb bundle is present.',
1463
+ path: assetPaths.claudeDesktopMcpbPath,
1464
+ size_bytes: await verifyZipFile(
1465
+ assetPaths.claudeDesktopMcpbPath,
1466
+ 'Claude Desktop .mcpb bundle',
1467
+ ),
1468
+ }));
1469
+ await collect(checks, 'claude_bundle_mcp', async () => {
1470
+ const probe = await probeMcpServer({
1471
+ label: 'Claude Desktop bundle server',
1472
+ command: process.execPath,
1473
+ args: [bundleServerPath],
1474
+ cwd: root,
1475
+ env: {
1476
+ AUDIT_CODE_REPO_ROOT: root,
1477
+ AUDIT_CODE_ARTIFACTS_DIR: join(root, '.audit-artifacts'),
1478
+ },
1479
+ });
1480
+ const toolNames = (probe.tools?.tools ?? []).map((tool) => tool.name);
1481
+ if (!toolNames.includes('start_audit')) {
1482
+ throw new Error('Claude Desktop bundle server did not expose the start_audit tool.');
1483
+ }
1484
+ return {
1485
+ summary: 'Claude Desktop bundle completed an MCP handshake.',
1486
+ tool_count: toolNames.length,
1487
+ };
1488
+ });
1489
+ },
1397
1490
  },
1398
1491
  opencode: {
1399
1492
  host: 'opencode',
@@ -1401,7 +1494,7 @@ const INSTALL_HOST_DEFINITIONS = {
1401
1494
  support_level: 'supported',
1402
1495
  setup_kind: 'global-command+project-mcp',
1403
1496
  summary:
1404
- 'Use the global OpenCode `/audit-code` command installed by npm plus generated project MCP and permission wiring.',
1497
+ 'Use the global OpenCode `/audit-code` command installed by npm plus generated project permissions; MCP is compatibility-only.',
1405
1498
  primary_path_key: 'opencodeConfigPath',
1406
1499
  supporting_path_keys: [
1407
1500
  'agentsInstructionsPath',
@@ -1410,12 +1503,29 @@ const INSTALL_HOST_DEFINITIONS = {
1410
1503
  steps: [
1411
1504
  'Open this repository in OpenCode.',
1412
1505
  'Use the global `/audit-code` command installed by `npm install -g auditor-lambda`.',
1413
- 'Let OpenCode load the generated `opencode.json` for the auditor MCP server and project permissions only.',
1506
+ 'Let OpenCode load the generated `opencode.json` for project permissions; the global command drives `audit-code next-step` directly.',
1414
1507
  ],
1415
1508
  profile: {
1416
1509
  writeOpenCode: true,
1417
1510
  writeAgents: true,
1418
1511
  },
1512
+ async verify({ checks, assetPaths, collectVerifyCheck: collect }) {
1513
+ await collect(checks, 'opencode_config', async () => {
1514
+ const config = await readJson(assetPaths.opencodeConfigPath, 'OpenCode project config');
1515
+ if (config?.command?.['audit-code']) {
1516
+ throw new Error('OpenCode project config must not define command["audit-code"]; the slash command is global npm-installed state. Run "audit-code install --host opencode" to remove the stale local command.');
1517
+ }
1518
+ if (config?.mcp?.auditor) {
1519
+ throw new Error('OpenCode project config must not define mcp.auditor; the MCP server is supplied by the global npm-installed config. Run "audit-code install --host opencode" to remove the stale project-level MCP entry.');
1520
+ }
1521
+ assertOpenCodeAuditPermissionConfig(config?.permission, 'permission');
1522
+ assertOpenCodeAuditPermissionConfig(config?.agent?.auditor?.permission, 'agent.auditor.permission');
1523
+ return {
1524
+ summary: 'OpenCode project config has audit permissions; /audit-code is supplied by the global npm-installed config.',
1525
+ path: assetPaths.opencodeConfigPath,
1526
+ };
1527
+ });
1528
+ },
1419
1529
  },
1420
1530
  vscode: {
1421
1531
  host: 'vscode',
@@ -1423,7 +1533,7 @@ const INSTALL_HOST_DEFINITIONS = {
1423
1533
  support_level: 'supported',
1424
1534
  setup_kind: 'prompt+agent+mcp',
1425
1535
  summary:
1426
- 'Use the generated prompt file, custom agent, and workspace MCP configuration for the cleanest VS Code integration.',
1536
+ 'Use the generated prompt file and custom agent for next-step-first VS Code integration; workspace MCP is compatibility-only.',
1427
1537
  primary_path_key: 'vscodePromptPath',
1428
1538
  supporting_path_keys: [
1429
1539
  'vscodeAgentPath',
@@ -1432,13 +1542,46 @@ const INSTALL_HOST_DEFINITIONS = {
1432
1542
  ],
1433
1543
  steps: [
1434
1544
  'Open this repository in VS Code with Copilot.',
1435
- 'Allow VS Code to discover the workspace MCP server from `.vscode/mcp.json`.',
1436
- 'Invoke `/audit-code` in chat and use the custom auditor agent when you want the dedicated orchestration persona.',
1545
+ 'Invoke `/audit-code` from the generated prompt or chat so the workflow calls `audit-code next-step` directly.',
1546
+ 'Use the workspace MCP adapter only when direct shell access is unavailable.',
1437
1547
  ],
1438
1548
  profile: {
1439
1549
  writeVSCode: true,
1440
1550
  writeCopilotInstructions: true,
1441
1551
  },
1552
+ async verify({ checks, assetPaths, collectVerifyCheck: collect }) {
1553
+ await collect(checks, 'vscode_prompt', async () => {
1554
+ const content = await readFile(assetPaths.vscodePromptPath, 'utf8');
1555
+ if (!content.includes('name: audit-code')) {
1556
+ throw new Error(`VS Code prompt file is missing the expected frontmatter name: ${assetPaths.vscodePromptPath}`);
1557
+ }
1558
+ const { body: promptBody } = splitFrontmatter(content);
1559
+ const { body: sourceBody } = splitFrontmatter(await readFile(promptAssetPath, 'utf8'));
1560
+ if (promptBody !== sourceBody.trimStart()) {
1561
+ throw new Error(
1562
+ `VS Code prompt body is out of sync with the source prompt. Run "audit-code install --host vscode" or "audit-code install".`,
1563
+ );
1564
+ }
1565
+ return {
1566
+ summary: 'VS Code prompt file is present and uses the source prompt body.',
1567
+ path: assetPaths.vscodePromptPath,
1568
+ };
1569
+ });
1570
+ await collect(checks, 'vscode_mcp_config', async () => {
1571
+ const config = await readJson(assetPaths.vscodeMcpConfigPath, 'VS Code MCP config');
1572
+ const args = config?.servers?.auditor?.args;
1573
+ if (config?.servers?.auditor?.command !== 'node') {
1574
+ throw new Error(`VS Code MCP config must use node as the command, got ${config?.servers?.auditor?.command ?? 'missing'}.`);
1575
+ }
1576
+ if (!Array.isArray(args) || args[0] !== '${workspaceFolder}/.audit-code/install/run-mcp-server.mjs') {
1577
+ throw new Error(`VS Code MCP config must point at \${workspaceFolder}/.audit-code/install/${MCP_LAUNCHER_FILENAME}.`);
1578
+ }
1579
+ return {
1580
+ summary: 'VS Code MCP config parsed successfully.',
1581
+ path: assetPaths.vscodeMcpConfigPath,
1582
+ };
1583
+ });
1584
+ },
1442
1585
  },
1443
1586
  antigravity: {
1444
1587
  host: 'antigravity',
@@ -1446,7 +1589,7 @@ const INSTALL_HOST_DEFINITIONS = {
1446
1589
  support_level: 'supported',
1447
1590
  setup_kind: 'agent-skill+gemini-command+planning-guide+mcp-ready',
1448
1591
  summary:
1449
- 'Uses the project-scoped .agent/skills/audit-code/SKILL.md skill, the .gemini/commands/audit-code.toml slash command, the planning guide, and AGENTS instructions. The shared MCP launcher is available for structured tool calls.',
1592
+ 'Uses the project-scoped .agent/skills/audit-code/SKILL.md skill, the .gemini/commands/audit-code.toml slash command, the planning guide, and AGENTS instructions. The shared MCP launcher is compatibility-only.',
1450
1593
  primary_path_key: 'antigravitySkillPath',
1451
1594
  supporting_path_keys: [
1452
1595
  'geminiCommandPath',
@@ -1459,12 +1602,34 @@ const INSTALL_HOST_DEFINITIONS = {
1459
1602
  'Open this repository in Antigravity.',
1460
1603
  'The audit-code skill is automatically discovered from .agent/skills/audit-code/SKILL.md.',
1461
1604
  'The /audit-code slash command is also available from .gemini/commands/audit-code.toml.',
1462
- 'Use the shared auditor MCP server when Antigravity needs structured audit state instead of free-form shell guesses.',
1605
+ 'Use `audit-code next-step` directly; use the shared auditor MCP launcher only when direct shell access is unavailable.',
1463
1606
  ],
1464
1607
  profile: {
1465
1608
  writeAntigravity: true,
1466
1609
  writeAgents: true,
1467
1610
  },
1611
+ async verify({ checks, assetPaths, collectVerifyCheck: collect }) {
1612
+ await collect(checks, 'antigravity_skill', async () => {
1613
+ const content = await readFile(assetPaths.antigravitySkillPath, 'utf8');
1614
+ if (!content.includes('name: audit-code')) {
1615
+ throw new Error('Antigravity skill SKILL.md must contain "name: audit-code" in frontmatter.');
1616
+ }
1617
+ return {
1618
+ summary: 'Antigravity .agent/skills/audit-code/SKILL.md is present and valid.',
1619
+ path: assetPaths.antigravitySkillPath,
1620
+ };
1621
+ });
1622
+ await collect(checks, 'antigravity_guide', async () => {
1623
+ const content = await readFile(assetPaths.antigravityPlanningGuidePath, 'utf8');
1624
+ if (!content.includes(MCP_LAUNCHER_FILENAME) || !content.includes(INSTALLED_PROMPT_FILENAME)) {
1625
+ throw new Error(`Antigravity guide must reference both ${MCP_LAUNCHER_FILENAME} and ${INSTALLED_PROMPT_FILENAME}.`);
1626
+ }
1627
+ return {
1628
+ summary: 'Antigravity planning guide references the repo-local prompt asset and MCP launcher.',
1629
+ path: assetPaths.antigravityPlanningGuidePath,
1630
+ };
1631
+ });
1632
+ },
1468
1633
  },
1469
1634
  };
1470
1635
 
@@ -1505,6 +1670,13 @@ function getInstallProfile(host) {
1505
1670
  return profile;
1506
1671
  }
1507
1672
 
1673
+ export {
1674
+ INSTALL_HOST_ORDER as _INSTALL_HOST_ORDER,
1675
+ INSTALL_HOST_DEFINITIONS as _INSTALL_HOST_DEFINITIONS,
1676
+ getInstallHostKeys as _getInstallHostKeys,
1677
+ getInstallProfile as _getInstallProfile,
1678
+ };
1679
+
1508
1680
  function buildHostCatalog({ root, host, assets }) {
1509
1681
  return getInstallHostKeys(host)
1510
1682
  .map((hostKey) => {
@@ -2028,177 +2200,15 @@ async function verifyInstalledBootstrap(argv) {
2028
2200
  primary_path: hostEntry.primary_path,
2029
2201
  }));
2030
2202
 
2031
- switch (hostKey) {
2032
- case 'codex':
2033
- await collectVerifyCheck(checks, 'codex_global_surface', async () => {
2034
- const content = await readFile(assetPaths.agentsInstructionsPath, 'utf8');
2035
- if (!content.includes('/audit-code')) {
2036
- throw new Error(`AGENTS instructions do not reference /audit-code: ${assetPaths.agentsInstructionsPath}`);
2037
- }
2038
- return {
2039
- summary: 'Codex uses the global skill surface with AGENTS fallback instructions.',
2040
- path: assetPaths.agentsInstructionsPath,
2041
- };
2042
- });
2043
- break;
2044
- case 'claude-desktop': {
2045
- const bundleManifestPath = join(
2046
- root,
2047
- '.audit-code',
2048
- 'install',
2049
- 'claude-desktop',
2050
- 'bundle',
2051
- 'manifest.json',
2052
- );
2053
- const bundleServerPath = join(
2054
- root,
2055
- '.audit-code',
2056
- 'install',
2057
- 'claude-desktop',
2058
- 'bundle',
2059
- 'server',
2060
- 'index.js',
2061
- );
2062
-
2063
- await collectVerifyCheck(checks, 'claude_bundle_manifest', async () => {
2064
- const manifest = await readJson(bundleManifestPath, 'Claude Desktop bundle manifest');
2065
- if (manifest?.server?.entry_point !== 'server/index.js') {
2066
- throw new Error(`Claude Desktop bundle manifest has an unexpected entry_point: ${manifest?.server?.entry_point ?? 'missing'}.`);
2067
- }
2068
- return {
2069
- summary: 'Claude Desktop bundle manifest parsed successfully.',
2070
- path: bundleManifestPath,
2071
- };
2072
- });
2073
- await collectVerifyCheck(checks, 'claude_connector_template', async () => {
2074
- const connector = await readJson(
2075
- assetPaths.claudeDesktopRemoteConnectorPath,
2076
- 'Claude Desktop remote connector template',
2077
- );
2078
- if (connector?.transport !== 'remote-mcp') {
2079
- throw new Error(`Claude Desktop remote connector transport must be "remote-mcp", got ${connector?.transport ?? 'missing'}.`);
2080
- }
2081
- return {
2082
- summary: 'Claude Desktop remote connector template parsed successfully.',
2083
- path: assetPaths.claudeDesktopRemoteConnectorPath,
2084
- };
2085
- });
2086
- await collectVerifyCheck(checks, 'claude_dxt_archive', async () => ({
2087
- summary: 'Claude Desktop .dxt bundle is present.',
2088
- path: assetPaths.claudeDesktopDxtPath,
2089
- size_bytes: await verifyZipFile(
2090
- assetPaths.claudeDesktopDxtPath,
2091
- 'Claude Desktop .dxt bundle',
2092
- ),
2093
- }));
2094
- await collectVerifyCheck(checks, 'claude_mcpb_archive', async () => ({
2095
- summary: 'Claude Desktop .mcpb bundle is present.',
2096
- path: assetPaths.claudeDesktopMcpbPath,
2097
- size_bytes: await verifyZipFile(
2098
- assetPaths.claudeDesktopMcpbPath,
2099
- 'Claude Desktop .mcpb bundle',
2100
- ),
2101
- }));
2102
- await collectVerifyCheck(checks, 'claude_bundle_mcp', async () => {
2103
- const probe = await probeMcpServer({
2104
- label: 'Claude Desktop bundle server',
2105
- command: process.execPath,
2106
- args: [bundleServerPath],
2107
- cwd: root,
2108
- env: {
2109
- AUDIT_CODE_REPO_ROOT: root,
2110
- AUDIT_CODE_ARTIFACTS_DIR: join(root, '.audit-artifacts'),
2111
- },
2112
- });
2113
- const toolNames = (probe.tools?.tools ?? []).map((tool) => tool.name);
2114
- if (!toolNames.includes('start_audit')) {
2115
- throw new Error('Claude Desktop bundle server did not expose the start_audit tool.');
2116
- }
2117
- return {
2118
- summary: 'Claude Desktop bundle completed an MCP handshake.',
2119
- tool_count: toolNames.length,
2120
- };
2121
- });
2122
- break;
2123
- }
2124
- case 'opencode':
2125
- await collectVerifyCheck(checks, 'opencode_config', async () => {
2126
- const config = await readJson(assetPaths.opencodeConfigPath, 'OpenCode project config');
2127
- if (config?.command?.['audit-code']) {
2128
- throw new Error('OpenCode project config must not define command["audit-code"]; the slash command is global npm-installed state. Run "audit-code install --host opencode" to remove the stale local command.');
2129
- }
2130
- if (config?.mcp?.auditor) {
2131
- throw new Error('OpenCode project config must not define mcp.auditor; the MCP server is supplied by the global npm-installed config. Run "audit-code install --host opencode" to remove the stale project-level MCP entry.');
2132
- }
2133
- assertOpenCodeAuditPermissionConfig(config?.permission, 'permission');
2134
- assertOpenCodeAuditPermissionConfig(config?.agent?.auditor?.permission, 'agent.auditor.permission');
2135
- return {
2136
- summary: 'OpenCode project config has audit permissions; MCP server and /audit-code command are supplied by the global npm-installed config.',
2137
- path: assetPaths.opencodeConfigPath,
2138
- };
2139
- });
2140
- break;
2141
- case 'vscode':
2142
- await collectVerifyCheck(checks, 'vscode_prompt', async () => {
2143
- const content = await readFile(assetPaths.vscodePromptPath, 'utf8');
2144
- if (!content.includes('name: audit-code')) {
2145
- throw new Error(`VS Code prompt file is missing the expected frontmatter name: ${assetPaths.vscodePromptPath}`);
2146
- }
2147
- const { body: promptBody } = splitFrontmatter(content);
2148
- const { body: sourceBody } = splitFrontmatter(await readFile(promptAssetPath, 'utf8'));
2149
- if (promptBody !== sourceBody.trimStart()) {
2150
- throw new Error(
2151
- `VS Code prompt body is out of sync with the source prompt. Run "audit-code install --host vscode" or "audit-code install".`,
2152
- );
2153
- }
2154
- return {
2155
- summary: 'VS Code prompt file is present and uses the source prompt body.',
2156
- path: assetPaths.vscodePromptPath,
2157
- };
2158
- });
2159
- await collectVerifyCheck(checks, 'vscode_mcp_config', async () => {
2160
- const config = await readJson(assetPaths.vscodeMcpConfigPath, 'VS Code MCP config');
2161
- const args = config?.servers?.auditor?.args;
2162
- if (config?.servers?.auditor?.command !== 'node') {
2163
- throw new Error(`VS Code MCP config must use node as the command, got ${config?.servers?.auditor?.command ?? 'missing'}.`);
2164
- }
2165
- if (!Array.isArray(args) || args[0] !== '${workspaceFolder}/.audit-code/install/run-mcp-server.mjs') {
2166
- throw new Error(`VS Code MCP config must point at \${workspaceFolder}/.audit-code/install/${MCP_LAUNCHER_FILENAME}.`);
2167
- }
2168
- return {
2169
- summary: 'VS Code MCP config parsed successfully.',
2170
- path: assetPaths.vscodeMcpConfigPath,
2171
- };
2172
- });
2173
- break;
2174
- case 'antigravity':
2175
- await collectVerifyCheck(checks, 'antigravity_skill', async () => {
2176
- const content = await readFile(assetPaths.antigravitySkillPath, 'utf8');
2177
- if (!content.includes('name: audit-code')) {
2178
- throw new Error('Antigravity skill SKILL.md must contain "name: audit-code" in frontmatter.');
2179
- }
2180
- return {
2181
- summary: 'Antigravity .agent/skills/audit-code/SKILL.md is present and valid.',
2182
- path: assetPaths.antigravitySkillPath,
2183
- };
2184
- });
2185
- await collectVerifyCheck(checks, 'antigravity_guide', async () => {
2186
- const content = await readFile(assetPaths.antigravityPlanningGuidePath, 'utf8');
2187
- if (!content.includes(MCP_LAUNCHER_FILENAME) || !content.includes(INSTALLED_PROMPT_FILENAME)) {
2188
- throw new Error(`Antigravity guide must reference both ${MCP_LAUNCHER_FILENAME} and ${INSTALLED_PROMPT_FILENAME}.`);
2189
- }
2190
- return {
2191
- summary: 'Antigravity planning guide references the repo-local prompt asset and MCP launcher.',
2192
- path: assetPaths.antigravityPlanningGuidePath,
2193
- };
2194
- });
2195
- break;
2196
- default:
2197
- checks.push({
2198
- id: 'host_handler',
2199
- status: 'error',
2200
- summary: `No verification handler is implemented for host "${hostKey}".`,
2201
- });
2203
+ const hostDefinition = INSTALL_HOST_DEFINITIONS[hostKey];
2204
+ if (hostDefinition?.verify) {
2205
+ await hostDefinition.verify({ checks, root, assetPaths, collectVerifyCheck });
2206
+ } else {
2207
+ checks.push({
2208
+ id: 'host_handler',
2209
+ status: 'error',
2210
+ summary: `No verification handler is implemented for host "${hostKey}".`,
2211
+ });
2202
2212
  }
2203
2213
 
2204
2214
  hostResults.push({
package/dist/cli.d.ts CHANGED
@@ -2,6 +2,11 @@ import type { SessionConfig } from "./types/sessionConfig.js";
2
2
  type UiMode = "visible" | "headless";
3
3
  declare function getFlag(argv: string[], name: string, fallback?: string): string | undefined;
4
4
  declare function hasFlag(argv: string[], name: string): boolean;
5
+ export declare function resolveHostDispatchCapability(options: {
6
+ explicit?: boolean;
7
+ sessionConfig: SessionConfig;
8
+ env?: NodeJS.ProcessEnv;
9
+ }): boolean;
5
10
  declare function getArtifactsDir(argv: string[]): string;
6
11
  declare function getRootDir(argv: string[]): string;
7
12
  declare function warnIfNotGitRepo(root: string): void;