swarm-orchestrator 7.0.0-alpha.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 (174) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +199 -0
  3. package/agents/reviewer.agent.md +50 -0
  4. package/agents/worker.agent.md +41 -0
  5. package/config/default-agents.yaml +56 -0
  6. package/config/pm-agent.yaml +36 -0
  7. package/config/quality-gates.yaml +88 -0
  8. package/config/repair-agent.yaml +34 -0
  9. package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
  10. package/config/semgrep-rules/exception-swallowing.yaml +13 -0
  11. package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
  12. package/config/semgrep-rules/mock-mutation.yaml +6 -0
  13. package/config/semgrep-rules/test-modification.yaml +6 -0
  14. package/config/user-agents.yaml +23 -0
  15. package/dist/src/adapters/adapter-factory.js +29 -0
  16. package/dist/src/adapters/agent-adapter.js +32 -0
  17. package/dist/src/adapters/claude-code-adapter.js +126 -0
  18. package/dist/src/adapters/claude-code-teams.js +119 -0
  19. package/dist/src/adapters/codex-adapter.js +118 -0
  20. package/dist/src/adapters/copilot-adapter.js +224 -0
  21. package/dist/src/adapters/fatal-error-classifier.js +88 -0
  22. package/dist/src/adapters/index.js +44 -0
  23. package/dist/src/adapters/persistent-session.js +204 -0
  24. package/dist/src/adapters/process-supervisor.js +199 -0
  25. package/dist/src/agents-exporter.js +398 -0
  26. package/dist/src/analytics-log.js +161 -0
  27. package/dist/src/baseline-scanner.js +65 -0
  28. package/dist/src/bootstrap-evidence.js +88 -0
  29. package/dist/src/bootstrap-orchestrator.js +205 -0
  30. package/dist/src/bootstrap-types.js +5 -0
  31. package/dist/src/branch-merger.js +427 -0
  32. package/dist/src/cli/attest-handlers.js +44 -0
  33. package/dist/src/cli/cost-prompt.js +51 -0
  34. package/dist/src/cli/demo-handlers.js +274 -0
  35. package/dist/src/cli/flags.js +152 -0
  36. package/dist/src/cli/index.js +57 -0
  37. package/dist/src/cli/live-status.js +186 -0
  38. package/dist/src/cli/misc-handlers.js +242 -0
  39. package/dist/src/cli/plan-handlers.js +350 -0
  40. package/dist/src/cli/share-handlers.js +163 -0
  41. package/dist/src/cli/status-handlers.js +457 -0
  42. package/dist/src/cli/swarm-handlers.js +638 -0
  43. package/dist/src/cli/usage.js +72 -0
  44. package/dist/src/cli.js +217 -0
  45. package/dist/src/commit-pattern-detector.js +143 -0
  46. package/dist/src/commit-quality-analyzer.js +38 -0
  47. package/dist/src/config-loader.js +339 -0
  48. package/dist/src/context-broker.js +358 -0
  49. package/dist/src/copilot-cli-wrapper.js +258 -0
  50. package/dist/src/cost-estimator.js +241 -0
  51. package/dist/src/defaults.js +18 -0
  52. package/dist/src/demo-mode.js +180 -0
  53. package/dist/src/deployment-handler.js +78 -0
  54. package/dist/src/deployment-manager.js +228 -0
  55. package/dist/src/execution-queue.js +196 -0
  56. package/dist/src/external-tool-manager.js +184 -0
  57. package/dist/src/gate-prompt-builder.js +117 -0
  58. package/dist/src/gate-remediation.js +173 -0
  59. package/dist/src/github-issues-ingester.js +152 -0
  60. package/dist/src/github-mcp-integrator.js +164 -0
  61. package/dist/src/hook-generator.js +229 -0
  62. package/dist/src/index.js +48 -0
  63. package/dist/src/knowledge-base.js +300 -0
  64. package/dist/src/logger.js +111 -0
  65. package/dist/src/meta-analyzer.js +208 -0
  66. package/dist/src/metrics-collector.js +209 -0
  67. package/dist/src/metrics-types.js +5 -0
  68. package/dist/src/multi-repo-coordinator.js +179 -0
  69. package/dist/src/orchestrator/async-meta-analysis.js +84 -0
  70. package/dist/src/orchestrator/fatal-run-error.js +43 -0
  71. package/dist/src/orchestrator/final-gates-remediation.js +258 -0
  72. package/dist/src/orchestrator/git-state-utils.js +119 -0
  73. package/dist/src/orchestrator/pause-controller.js +55 -0
  74. package/dist/src/orchestrator/replan-runner.js +258 -0
  75. package/dist/src/orchestrator/step-executor.js +391 -0
  76. package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
  77. package/dist/src/owasp-mapper.js +153 -0
  78. package/dist/src/owasp-report-renderer.js +39 -0
  79. package/dist/src/plan-files.js +142 -0
  80. package/dist/src/plan-generator.js +1207 -0
  81. package/dist/src/pm-agent.js +296 -0
  82. package/dist/src/post-run-reporter.js +203 -0
  83. package/dist/src/pr-automation.js +103 -0
  84. package/dist/src/pr-manager.js +318 -0
  85. package/dist/src/presenter/index.js +209 -0
  86. package/dist/src/prompt-builder.js +173 -0
  87. package/dist/src/quality-gates/config-loader.js +196 -0
  88. package/dist/src/quality-gates/default-config.js +243 -0
  89. package/dist/src/quality-gates/file-utils.js +152 -0
  90. package/dist/src/quality-gates/gate-runner.js +171 -0
  91. package/dist/src/quality-gates/gates/accessibility.js +355 -0
  92. package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
  93. package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
  94. package/dist/src/quality-gates/gates/readme-claims.js +62 -0
  95. package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
  96. package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
  97. package/dist/src/quality-gates/gates/test-coverage.js +240 -0
  98. package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
  99. package/dist/src/quality-gates/gates/test-isolation.js +74 -0
  100. package/dist/src/quality-gates/index.js +17 -0
  101. package/dist/src/quality-gates/registry.js +205 -0
  102. package/dist/src/quality-gates/types.js +2 -0
  103. package/dist/src/quick-fix-mode.js +302 -0
  104. package/dist/src/recipe-loader.js +122 -0
  105. package/dist/src/repair-agent.js +396 -0
  106. package/dist/src/repo-analyzer.js +342 -0
  107. package/dist/src/report-generator.js +146 -0
  108. package/dist/src/report-renderer.js +90 -0
  109. package/dist/src/requirement-filter.js +52 -0
  110. package/dist/src/sarif-formatter.js +140 -0
  111. package/dist/src/scheduling/dependency-analyzer.js +178 -0
  112. package/dist/src/scheduling/work-stealing-queue.js +123 -0
  113. package/dist/src/secret-redactor.js +90 -0
  114. package/dist/src/session-executor.js +587 -0
  115. package/dist/src/session-manager.js +221 -0
  116. package/dist/src/share/transcript-verification.js +128 -0
  117. package/dist/src/share-parser.js +477 -0
  118. package/dist/src/spinner.js +146 -0
  119. package/dist/src/step-runner.js +365 -0
  120. package/dist/src/swarm-orchestrator.js +686 -0
  121. package/dist/src/task-classifier.js +134 -0
  122. package/dist/src/test-command-discovery.js +123 -0
  123. package/dist/src/text-similarity.js +24 -0
  124. package/dist/src/tier-maps.js +158 -0
  125. package/dist/src/types.js +5 -0
  126. package/dist/src/url-shortener.js +135 -0
  127. package/dist/src/verification/attestation.js +166 -0
  128. package/dist/src/verification/cheat-detector.js +240 -0
  129. package/dist/src/verification/command-runner.js +70 -0
  130. package/dist/src/verification/composite-score.js +157 -0
  131. package/dist/src/verification/cosign-attestation.js +137 -0
  132. package/dist/src/verification/diff-analysis.js +89 -0
  133. package/dist/src/verification/differential-gate.js +173 -0
  134. package/dist/src/verification/index.js +41 -0
  135. package/dist/src/verification/mutation-gate.js +242 -0
  136. package/dist/src/verification/property-gate.js +201 -0
  137. package/dist/src/verification/test-synthesizer.js +243 -0
  138. package/dist/src/verifier/outcome-checks.js +396 -0
  139. package/dist/src/verifier/transcript-checks.js +207 -0
  140. package/dist/src/verifier/verification-reporters.js +148 -0
  141. package/dist/src/verifier-engine.js +491 -0
  142. package/dist/src/wave-resizer.js +131 -0
  143. package/dist/src/wave-scheduler.js +44 -0
  144. package/dist/src/worktree-manager.js +368 -0
  145. package/dist/src/worktree-reserved-paths.js +97 -0
  146. package/package.json +80 -0
  147. package/plugin/agents/.gitkeep +0 -0
  148. package/plugin/agents/backend_master.agent.md +72 -0
  149. package/plugin/agents/devops_pro.agent.md +45 -0
  150. package/plugin/agents/frontend_expert.agent.md +59 -0
  151. package/plugin/agents/integrator_finalizer.agent.md +44 -0
  152. package/plugin/agents/meta_reviewer.agent.md +44 -0
  153. package/plugin/agents/security_auditor.agent.md +43 -0
  154. package/plugin/agents/tester_elite.agent.md +43 -0
  155. package/plugin/hooks/hooks.json +19 -0
  156. package/plugin/hooks/post-tool-use.json +9 -0
  157. package/plugin/hooks/pre-tool-use.json +9 -0
  158. package/plugin/plugin.json +47 -0
  159. package/plugin/skills/swarm-gates/SKILL.md +53 -0
  160. package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
  161. package/plugin/skills/swarm-verify/SKILL.md +44 -0
  162. package/templates/README.md +35 -0
  163. package/templates/cli-tool.json +31 -0
  164. package/templates/fullstack.json +52 -0
  165. package/templates/library.json +38 -0
  166. package/templates/react-app.json +45 -0
  167. package/templates/recipes/add-api-docs.json +35 -0
  168. package/templates/recipes/add-auth.json +47 -0
  169. package/templates/recipes/add-ci.json +39 -0
  170. package/templates/recipes/add-tests.json +39 -0
  171. package/templates/recipes/migrate-to-ts.json +42 -0
  172. package/templates/recipes/refactor-modularize.json +44 -0
  173. package/templates/recipes/security-audit.json +35 -0
  174. package/templates/rest-api.json +38 -0
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ /**
3
+ * Share-related CLI command handlers extracted from cli-handlers.ts.
4
+ * Handles /share transcript import and prior-context display.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.handleShareCommand = handleShareCommand;
8
+ const session_manager_1 = require("../session-manager");
9
+ const usage_1 = require("./usage");
10
+ const logger_1 = require("../logger");
11
+ const logger = (0, logger_1.getLogger)('cli:share');
12
+ // ---------------------------------------------------------------------------
13
+ // Internal helpers
14
+ // ---------------------------------------------------------------------------
15
+ function importShare(runId, stepNumber, agentName, transcriptPath) {
16
+ logger.info('Swarm Orchestrator - Import /share Transcript\n');
17
+ const step = parseInt(stepNumber, 10);
18
+ if (isNaN(step)) {
19
+ logger.error('Error: Step number must be a number');
20
+ return 1;
21
+ }
22
+ const manager = new session_manager_1.SessionManager();
23
+ try {
24
+ const stepShare = manager.importShare(runId, step, agentName, transcriptPath);
25
+ logger.info(`✓ Imported /share transcript for step ${step}`);
26
+ logger.info(` Agent: ${agentName}`);
27
+ logger.info(` Run: ${runId}`);
28
+ logger.info(` Saved to: ${stepShare.transcriptPath}\n`);
29
+ logger.info('Extracted Index:');
30
+ logger.info('===============');
31
+ if (stepShare.index.changedFiles.length > 0) {
32
+ logger.info(`\nChanged Files (${stepShare.index.changedFiles.length}):`);
33
+ stepShare.index.changedFiles.forEach(file => logger.info(` - ${file}`));
34
+ }
35
+ if (stepShare.index.commandsExecuted.length > 0) {
36
+ logger.info(`\nCommands Executed (${stepShare.index.commandsExecuted.length}):`);
37
+ stepShare.index.commandsExecuted.forEach(cmd => logger.info(` $ ${cmd}`));
38
+ }
39
+ if (stepShare.index.testsRun.length > 0) {
40
+ logger.info(`\nTests Run:`);
41
+ stepShare.index.testsRun.forEach(test => {
42
+ const status = test.verified ? '✓' : '✗';
43
+ logger.info(` ${status} ${test.command}`);
44
+ if (!test.verified && test.reason) {
45
+ logger.info(` reason: ${test.reason}`);
46
+ }
47
+ });
48
+ }
49
+ if (stepShare.index.prLinks.length > 0) {
50
+ logger.info(`\nPR Links:`);
51
+ stepShare.index.prLinks.forEach(link => logger.info(` - ${link}`));
52
+ }
53
+ if (stepShare.index.claims.length > 0) {
54
+ logger.info(`\nClaims Verification:`);
55
+ stepShare.index.claims.forEach(claim => {
56
+ const status = claim.verified ? '✓' : '⚠';
57
+ logger.info(` ${status} ${claim.claim.substring(0, 80)}`);
58
+ if (claim.evidence) {
59
+ logger.info(` evidence: ${claim.evidence}`);
60
+ }
61
+ });
62
+ }
63
+ const unverified = stepShare.index.claims.filter(c => !c.verified);
64
+ if (unverified.length > 0) {
65
+ logger.warn(`\n⚠ WARNING: ${unverified.length} unverified claims detected`);
66
+ logger.warn('this step may require manual review before proceeding\n');
67
+ }
68
+ else {
69
+ logger.info('\n✓ All claims verified\n');
70
+ }
71
+ return 0;
72
+ }
73
+ catch (error) {
74
+ logger.error('Error importing share:', error instanceof Error ? error.message : error);
75
+ return 1;
76
+ }
77
+ }
78
+ function showShareContext(runId, stepNumber) {
79
+ logger.info('Swarm Orchestrator - Prior Context\n');
80
+ const step = parseInt(stepNumber, 10);
81
+ if (isNaN(step)) {
82
+ logger.error('Error: Step number must be a number');
83
+ return 1;
84
+ }
85
+ const manager = new session_manager_1.SessionManager();
86
+ try {
87
+ const summary = manager.generateContextSummary(runId, step);
88
+ logger.info(`Prior context for step ${step} in run ${runId}:\n`);
89
+ logger.info(summary);
90
+ logger.info('');
91
+ const allUnverified = manager.getUnverifiedClaims(runId);
92
+ if (allUnverified.length > 0) {
93
+ logger.warn('⚠ UNVERIFIED CLAIMS IN PRIOR STEPS:');
94
+ allUnverified.forEach(item => {
95
+ logger.warn(` Step ${item.step} (${item.agent}): ${item.claims.length} unverified claims`);
96
+ });
97
+ logger.info('');
98
+ }
99
+ return 0;
100
+ }
101
+ catch (error) {
102
+ logger.error('Error loading context:', error instanceof Error ? error.message : error);
103
+ return 1;
104
+ }
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // Exported command handler
108
+ // ---------------------------------------------------------------------------
109
+ async function handleShareCommand(args) {
110
+ const subcommand = args[1];
111
+ if (!subcommand) {
112
+ logger.error('Error: share subcommand required (import or context)\n');
113
+ (0, usage_1.showUsage)();
114
+ return 1;
115
+ }
116
+ if (subcommand === 'import') {
117
+ if (args.length < 6) {
118
+ logger.error('Error: share import requires: <runid> <step> <agent> <path>\n');
119
+ (0, usage_1.showUsage)();
120
+ return 1;
121
+ }
122
+ const runId = args[2];
123
+ const stepNumber = args[3];
124
+ const agentName = args[4];
125
+ const transcriptPath = args[5];
126
+ if (!runId || !stepNumber || !agentName || !transcriptPath) {
127
+ logger.error('Error: all arguments required\n');
128
+ (0, usage_1.showUsage)();
129
+ return 1;
130
+ }
131
+ try {
132
+ return importShare(runId, stepNumber, agentName, transcriptPath);
133
+ }
134
+ catch (error) {
135
+ logger.error('Error:', error instanceof Error ? error.message : error);
136
+ return 1;
137
+ }
138
+ }
139
+ if (subcommand === 'context') {
140
+ if (args.length < 4) {
141
+ logger.error('Error: share context requires: <runid> <step>\n');
142
+ (0, usage_1.showUsage)();
143
+ return 1;
144
+ }
145
+ const runId = args[2];
146
+ const stepNumber = args[3];
147
+ if (!runId || !stepNumber) {
148
+ logger.error('Error: all arguments required\n');
149
+ (0, usage_1.showUsage)();
150
+ return 1;
151
+ }
152
+ try {
153
+ return showShareContext(runId, stepNumber);
154
+ }
155
+ catch (error) {
156
+ logger.error('Error:', error instanceof Error ? error.message : error);
157
+ return 1;
158
+ }
159
+ }
160
+ logger.error(`Unknown share subcommand: ${subcommand}\n`);
161
+ (0, usage_1.showUsage)();
162
+ return 1;
163
+ }
@@ -0,0 +1,457 @@
1
+ "use strict";
2
+ /**
3
+ * Status / reporting CLI command handlers extracted from cli-handlers.ts.
4
+ * Each handler validates its arguments, performs the work, and returns an
5
+ * exit code (0 = success, 1 = failure). No handler calls process.exit.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.showStatus = showStatus;
42
+ exports.handleStatusCommand = handleStatusCommand;
43
+ exports.handleGatesCommand = handleGatesCommand;
44
+ exports.handleAuditCommand = handleAuditCommand;
45
+ exports.handleMetricsCommand = handleMetricsCommand;
46
+ exports.handleReportCommand = handleReportCommand;
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const step_runner_1 = require("../step-runner");
50
+ const sarif_formatter_1 = require("../sarif-formatter");
51
+ const flags_1 = require("./flags");
52
+ const usage_1 = require("./usage");
53
+ const logger_1 = require("../logger");
54
+ const logger = (0, logger_1.getLogger)('cli:status');
55
+ // ---------------------------------------------------------------------------
56
+ // Internal helpers (not exported; called by the handle* functions)
57
+ // ---------------------------------------------------------------------------
58
+ function showStatus(executionId, outputFormat = 'text') {
59
+ if (outputFormat !== 'json') {
60
+ logger.info('Swarm Orchestrator - Execution Status\n');
61
+ }
62
+ const runner = new step_runner_1.StepRunner();
63
+ try {
64
+ const context = runner.loadExecutionContext(executionId);
65
+ if (outputFormat === 'json') {
66
+ const nextStep = context.stepResults.find(r => r.status === 'pending') || null;
67
+ (0, logger_1.writeStructuredOutput)({
68
+ mode: 'sequential',
69
+ executionId,
70
+ status: context.stepResults.every(r => r.status === 'completed')
71
+ ? 'completed'
72
+ : context.stepResults.some(r => r.status === 'failed')
73
+ ? 'failed'
74
+ : 'in_progress',
75
+ nextStep,
76
+ context,
77
+ });
78
+ }
79
+ else {
80
+ const summary = runner.generateSummary(context);
81
+ logger.info(summary);
82
+ const nextStep = context.stepResults.find(r => r.status === 'pending');
83
+ if (nextStep) {
84
+ logger.info(`\nNext step: ${nextStep.stepNumber} (${nextStep.agentName})`);
85
+ logger.info('Run execution command again to see instructions for this step.');
86
+ }
87
+ else {
88
+ const allCompleted = context.stepResults.every(r => r.status === 'completed');
89
+ if (allCompleted) {
90
+ logger.info('\n✓ All steps completed!');
91
+ }
92
+ else {
93
+ logger.info('\n⚠ Some steps failed or were skipped');
94
+ }
95
+ }
96
+ }
97
+ return 0;
98
+ }
99
+ catch {
100
+ // Not a sequential execution context; fall through to swarm session state
101
+ }
102
+ const MetricsCollectorClass = require('../metrics-collector').default;
103
+ const collector = new MetricsCollectorClass(executionId, '');
104
+ const state = collector.loadSession(executionId);
105
+ if (state) {
106
+ const completed = state.lastCompletedStep;
107
+ const total = state.graph.steps.length;
108
+ if (outputFormat === 'json') {
109
+ (0, logger_1.writeStructuredOutput)({
110
+ mode: 'swarm',
111
+ executionId,
112
+ sessionId: state.sessionId,
113
+ status: state.status,
114
+ completed,
115
+ total,
116
+ branches: Object.keys(state.branchMap).length,
117
+ transcripts: Object.keys(state.transcripts).length,
118
+ gateResults: state.gateResults,
119
+ metrics: state.metrics,
120
+ });
121
+ }
122
+ else {
123
+ logger.info(` Session: ${state.sessionId}`);
124
+ logger.info(` Status: ${state.status}`);
125
+ logger.info(` Progress: ${completed}/${total} steps completed`);
126
+ logger.info(` Branches: ${Object.keys(state.branchMap).length}`);
127
+ logger.info(` Transcripts:${Object.keys(state.transcripts).length}`);
128
+ if (state.status === 'completed') {
129
+ logger.info('\n✓ All steps completed!');
130
+ }
131
+ else if (state.status === 'failed') {
132
+ logger.info('\n⚠ Execution failed. Use --resume to retry.');
133
+ }
134
+ else {
135
+ logger.info(`\n⏳ ${state.status} at step ${completed + 1}`);
136
+ }
137
+ }
138
+ return 0;
139
+ }
140
+ logger.error(`Execution not found: ${executionId}`);
141
+ logger.error('Checked both proof/ (sequential) and runs/ (swarm) directories.');
142
+ return 1;
143
+ }
144
+ // ---------------------------------------------------------------------------
145
+ // Command handlers (exported for testing and called by main dispatch)
146
+ // ---------------------------------------------------------------------------
147
+ async function handleStatusCommand(args) {
148
+ const executionId = (0, flags_1.extractPositionalArgs)(args.slice(1), {
149
+ booleanFlags: ['--verbose', '--json'],
150
+ valueFlags: ['--output'],
151
+ })[0];
152
+ if (!executionId) {
153
+ logger.error('Error: Execution ID required\n');
154
+ (0, usage_1.showUsage)();
155
+ return 1;
156
+ }
157
+ try {
158
+ return showStatus(executionId, (0, flags_1.parseOutputFormat)(args));
159
+ }
160
+ catch (error) {
161
+ logger.error('Error showing status:', error instanceof Error ? error.message : error);
162
+ return 1;
163
+ }
164
+ }
165
+ async function handleGatesCommand(args) {
166
+ const { load_quality_gates_config, run_quality_gates } = require('../quality-gates');
167
+ const outputFormat = (0, flags_1.parseOutputFormat)(args);
168
+ const positional = (0, flags_1.extractPositionalArgs)(args, {
169
+ booleanFlags: ['--verbose', '--json'],
170
+ valueFlags: ['--output', '--quality-gates-config', '--quality-gates-out', '--base-commit', '--sarif'],
171
+ })[0];
172
+ const projectRoot = positional ? path.resolve(process.cwd(), positional) : process.cwd();
173
+ const configIndex = args.indexOf('--quality-gates-config');
174
+ const configPath = configIndex !== -1 && args[configIndex + 1] ? args[configIndex + 1] : undefined;
175
+ const outIndex = args.indexOf('--quality-gates-out');
176
+ const outDir = outIndex !== -1 && args[outIndex + 1]
177
+ ? path.resolve(process.cwd(), args[outIndex + 1])
178
+ : undefined;
179
+ // When a base commit is provided, compute baseline files so gates only flag
180
+ // agent-created or agent-modified files, not pre-existing project code.
181
+ const baseCommitIdx = args.indexOf('--base-commit');
182
+ const baseCommitSha = baseCommitIdx !== -1 && args[baseCommitIdx + 1] ? args[baseCommitIdx + 1] : undefined;
183
+ let baselineFiles;
184
+ if (baseCommitSha) {
185
+ try {
186
+ const { execSync } = require('child_process');
187
+ const fileList = execSync(`git ls-tree -r --name-only ${baseCommitSha}`, {
188
+ cwd: projectRoot,
189
+ encoding: 'utf8',
190
+ stdio: ['pipe', 'pipe', 'pipe'],
191
+ });
192
+ baselineFiles = new Set(fileList.trim().split('\n').filter(Boolean));
193
+ }
194
+ catch (err) {
195
+ const msg = err instanceof Error ? err.message : String(err);
196
+ logger.error(`Failed to read baseline files from commit ${baseCommitSha}: ${msg}`);
197
+ return 1;
198
+ }
199
+ }
200
+ const sarifIndex = args.indexOf('--sarif');
201
+ const sarifPath = sarifIndex !== -1 && args[sarifIndex + 1] ? args[sarifIndex + 1] : undefined;
202
+ if (outputFormat === 'json' && sarifPath === '-') {
203
+ logger.error('Cannot combine --output json with --sarif -');
204
+ return 1;
205
+ }
206
+ const config = load_quality_gates_config(projectRoot, configPath);
207
+ const result = await run_quality_gates(projectRoot, config, outDir, baselineFiles, baseCommitSha);
208
+ if (outputFormat === 'json') {
209
+ (0, logger_1.writeStructuredOutput)({
210
+ projectRoot,
211
+ configPath: configPath || null,
212
+ outDir: outDir || null,
213
+ baseCommitSha: baseCommitSha || null,
214
+ ...result,
215
+ });
216
+ }
217
+ else {
218
+ // When SARIF goes to stdout, route status text to stderr to keep stdout clean
219
+ const log = sarifPath === '-' ? logger.error : logger.info;
220
+ const icon = result.passed ? '✅' : '❌';
221
+ log(`${icon} quality gates ${result.passed ? 'passed' : 'failed'} (${result.totalDurationMs}ms)`);
222
+ for (const gate of result.results) {
223
+ const g = gate.status === 'pass' ? '✅' : gate.status === 'skip' ? '⏭️' : '❌';
224
+ log(` ${g} ${gate.id}: ${gate.issues.length} issue(s)`);
225
+ if (gate.issues.length > 0) {
226
+ for (const issue of gate.issues) {
227
+ const loc = issue.filePath ? ` (${issue.filePath}${issue.line ? ':' + issue.line : ''})` : '';
228
+ log(` ${issue.message}${loc}`);
229
+ if (issue.excerpt) {
230
+ const trimmed = issue.excerpt.split('\n').slice(0, 5).join('\n');
231
+ log(` ${trimmed}`);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ if (sarifPath) {
238
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf8'));
239
+ const sarifOutput = (0, sarif_formatter_1.formatSarif)(result, pkg.version);
240
+ if (sarifPath === '-') {
241
+ process.stdout.write(sarifOutput + '\n');
242
+ }
243
+ else {
244
+ const resolved = path.resolve(process.cwd(), sarifPath);
245
+ const dir = path.dirname(resolved);
246
+ if (!fs.existsSync(dir)) {
247
+ logger.error(`SARIF output directory does not exist: ${dir}`);
248
+ return 1;
249
+ }
250
+ fs.writeFileSync(resolved, sarifOutput, 'utf8');
251
+ logger.info(`SARIF report written to ${resolved}`);
252
+ }
253
+ }
254
+ return result.passed ? 0 : 1;
255
+ }
256
+ async function handleAuditCommand(args) {
257
+ const sessionId = (0, flags_1.extractPositionalArgs)(args.slice(1), {
258
+ booleanFlags: ['--verbose', '--json'],
259
+ valueFlags: ['--output'],
260
+ })[0];
261
+ if (!sessionId) {
262
+ logger.error('Error: session ID required\nUsage: swarm audit <session-id>');
263
+ return 1;
264
+ }
265
+ const MetricsCollectorClass = require('../metrics-collector').default;
266
+ const collector = new MetricsCollectorClass(sessionId, '');
267
+ const state = collector.loadSession(sessionId);
268
+ if (!state) {
269
+ logger.error(`Session not found: ${sessionId}`);
270
+ return 1;
271
+ }
272
+ logger.info(collector.generateAuditReport(state));
273
+ return 0;
274
+ }
275
+ async function handleMetricsCommand(args) {
276
+ const sessionId = (0, flags_1.extractPositionalArgs)(args.slice(1), {
277
+ booleanFlags: ['--verbose', '--json'],
278
+ valueFlags: ['--output'],
279
+ })[0];
280
+ if (!sessionId) {
281
+ logger.error('Error: session ID required\nUsage: swarm metrics <session-id>');
282
+ return 1;
283
+ }
284
+ const MetricsCollectorClass = require('../metrics-collector').default;
285
+ const collector = new MetricsCollectorClass(sessionId, '');
286
+ const state = collector.loadSession(sessionId);
287
+ if (!state) {
288
+ logger.error(`Session not found: ${sessionId}`);
289
+ return 1;
290
+ }
291
+ const steps = state.graph.steps.length;
292
+ const completed = state.lastCompletedStep;
293
+ const branches = Object.keys(state.branchMap).length;
294
+ const transcripts = Object.keys(state.transcripts).length;
295
+ const gatesPassed = state.gateResults.filter(g => g.status === 'pass').length;
296
+ const gatesFailed = state.gateResults.filter(g => g.status === 'fail').length;
297
+ const gatesSkipped = state.gateResults.filter(g => g.status === 'skip').length;
298
+ const premiumReqs = readActualPremiumRequests(sessionId, state);
299
+ const totalMs = Number(state.metrics['totalTimeMs'] ?? 0);
300
+ if ((0, flags_1.parseOutputFormat)(args) === 'json') {
301
+ (0, logger_1.writeStructuredOutput)({
302
+ sessionId: state.sessionId,
303
+ status: state.status,
304
+ steps, completed, branches, transcripts,
305
+ gatesPassed, gatesFailed, gatesSkipped, premiumReqs,
306
+ totalTimeMs: totalMs
307
+ });
308
+ }
309
+ else {
310
+ logger.info(`\n Session Metrics: ${state.sessionId}\n ${'─'.repeat(50)}`);
311
+ logger.info(` Status: ${state.status}`);
312
+ logger.info(` Steps: ${completed}/${steps} completed`);
313
+ logger.info(` Branches: ${branches}`);
314
+ logger.info(` Transcripts: ${transcripts}`);
315
+ const gateLine = gatesSkipped > 0
316
+ ? `${gatesPassed} passed, ${gatesFailed} failed, ${gatesSkipped} skipped`
317
+ : `${gatesPassed} passed, ${gatesFailed} failed`;
318
+ logger.info(` Gates: ${gateLine}`);
319
+ logger.info(` Premium requests:${premiumReqs}`);
320
+ if (totalMs > 0) {
321
+ const sec = Math.round(totalMs / 1000);
322
+ logger.info(` Wall time: ${sec}s`);
323
+ }
324
+ logger.info(` ${'─'.repeat(50)}\n`);
325
+ }
326
+ return 0;
327
+ }
328
+ /**
329
+ * Pull the actual premium-request count for a session from
330
+ * `cost-attribution.json`, which the post-run reporter writes from
331
+ * `CostAttributor`. Falls back to `state.metrics.premiumRequests` and then 0.
332
+ *
333
+ * The session-state metrics map is populated incrementally during the run and
334
+ * has historically been missing this field; cost-attribution is the
335
+ * authoritative post-run record.
336
+ */
337
+ function readActualPremiumRequests(sessionId, state) {
338
+ const fs = require('fs');
339
+ const path = require('path');
340
+ const candidate = path.join(process.cwd(), 'runs', sessionId, 'cost-attribution.json');
341
+ if (fs.existsSync(candidate)) {
342
+ try {
343
+ const parsed = JSON.parse(fs.readFileSync(candidate, 'utf8'));
344
+ if (typeof parsed.totalActualPremiumRequests === 'number') {
345
+ return parsed.totalActualPremiumRequests;
346
+ }
347
+ }
348
+ catch {
349
+ // fall through to metrics-map fallback
350
+ }
351
+ }
352
+ return Number(state.metrics['premiumRequests'] ?? 0);
353
+ }
354
+ async function handleReportCommand(args) {
355
+ if (args.includes('--help') || args.includes('-h')) {
356
+ logger.info(`
357
+ Usage: swarm report <run-id> [flags]
358
+
359
+ Generate a structured run report from existing run artifacts.
360
+
361
+ Arguments:
362
+ <run-id> The run directory name (e.g., swarm-2026-04-08T05-23-52-947Z)
363
+
364
+ Flags:
365
+ --format <md|json> Output format (default: both)
366
+ --stdout Print to terminal instead of writing files
367
+ --latest Use the most recent run directory
368
+
369
+ Examples:
370
+ swarm report swarm-2026-04-08T05-23-52-947Z
371
+ swarm report --latest --format md --stdout
372
+ swarm report --latest
373
+ `);
374
+ return 0;
375
+ }
376
+ const useLatest = args.includes('--latest');
377
+ const toStdout = args.includes('--stdout');
378
+ const formatIdx = args.indexOf('--format');
379
+ let format = 'both';
380
+ if (formatIdx !== -1 && args[formatIdx + 1]) {
381
+ const val = args[formatIdx + 1];
382
+ if (val !== 'md' && val !== 'json') {
383
+ logger.error(`--format requires "md" or "json", got "${val}"`);
384
+ return 1;
385
+ }
386
+ format = val;
387
+ }
388
+ let runDir;
389
+ if (useLatest) {
390
+ const runsRoot = path.join(process.cwd(), 'runs');
391
+ if (!fs.existsSync(runsRoot)) {
392
+ logger.error(`No runs/ directory found in ${process.cwd()}`);
393
+ return 1;
394
+ }
395
+ const entries = fs.readdirSync(runsRoot, { withFileTypes: true })
396
+ .filter(e => e.isDirectory())
397
+ .map(e => e.name)
398
+ .sort()
399
+ .reverse();
400
+ if (entries.length === 0) {
401
+ logger.error('No run directories found in runs/');
402
+ return 1;
403
+ }
404
+ runDir = path.join(runsRoot, entries[0]);
405
+ }
406
+ else {
407
+ const runId = args.find(a => !a.startsWith('--') && a !== 'report');
408
+ if (!runId) {
409
+ logger.error('Error: run-id required (or use --latest)');
410
+ logger.error('Usage: swarm report <run-id>');
411
+ return 1;
412
+ }
413
+ runDir = path.join(process.cwd(), 'runs', runId);
414
+ if (!fs.existsSync(runDir)) {
415
+ // Try as absolute path
416
+ runDir = runId;
417
+ }
418
+ }
419
+ if (!fs.existsSync(runDir)) {
420
+ logger.error(`Run directory not found: ${runDir}`);
421
+ return 1;
422
+ }
423
+ const { ReportGenerator } = await Promise.resolve().then(() => __importStar(require('../report-generator')));
424
+ const { ReportRenderer } = await Promise.resolve().then(() => __importStar(require('../report-renderer')));
425
+ let report;
426
+ try {
427
+ const generator = new ReportGenerator();
428
+ report = generator.generate(runDir);
429
+ }
430
+ catch (err) {
431
+ logger.error(`Failed to generate report: ${err instanceof Error ? err.message : err}`);
432
+ return 1;
433
+ }
434
+ const md = ReportRenderer.toMarkdown(report);
435
+ const json = ReportRenderer.toJson(report);
436
+ if (toStdout) {
437
+ if (format === 'md' || format === 'both')
438
+ logger.info(md);
439
+ if (format === 'json' || format === 'both')
440
+ logger.info(json);
441
+ }
442
+ else {
443
+ if (format === 'md' || format === 'both') {
444
+ const mdPath = path.join(runDir, 'report.md');
445
+ fs.writeFileSync(mdPath, md);
446
+ logger.info(`Report written: ${mdPath}`);
447
+ }
448
+ if (format === 'json' || format === 'both') {
449
+ const jsonPath = path.join(runDir, 'report.json');
450
+ fs.writeFileSync(jsonPath, json);
451
+ logger.info(`Report written: ${jsonPath}`);
452
+ }
453
+ }
454
+ const summary = ReportRenderer.toSummaryLine(report);
455
+ logger.info(summary);
456
+ return 0;
457
+ }