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.
- package/LICENSE +15 -0
- package/README.md +199 -0
- package/agents/reviewer.agent.md +50 -0
- package/agents/worker.agent.md +41 -0
- package/config/default-agents.yaml +56 -0
- package/config/pm-agent.yaml +36 -0
- package/config/quality-gates.yaml +88 -0
- package/config/repair-agent.yaml +34 -0
- package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
- package/config/semgrep-rules/exception-swallowing.yaml +13 -0
- package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
- package/config/semgrep-rules/mock-mutation.yaml +6 -0
- package/config/semgrep-rules/test-modification.yaml +6 -0
- package/config/user-agents.yaml +23 -0
- package/dist/src/adapters/adapter-factory.js +29 -0
- package/dist/src/adapters/agent-adapter.js +32 -0
- package/dist/src/adapters/claude-code-adapter.js +126 -0
- package/dist/src/adapters/claude-code-teams.js +119 -0
- package/dist/src/adapters/codex-adapter.js +118 -0
- package/dist/src/adapters/copilot-adapter.js +224 -0
- package/dist/src/adapters/fatal-error-classifier.js +88 -0
- package/dist/src/adapters/index.js +44 -0
- package/dist/src/adapters/persistent-session.js +204 -0
- package/dist/src/adapters/process-supervisor.js +199 -0
- package/dist/src/agents-exporter.js +398 -0
- package/dist/src/analytics-log.js +161 -0
- package/dist/src/baseline-scanner.js +65 -0
- package/dist/src/bootstrap-evidence.js +88 -0
- package/dist/src/bootstrap-orchestrator.js +205 -0
- package/dist/src/bootstrap-types.js +5 -0
- package/dist/src/branch-merger.js +427 -0
- package/dist/src/cli/attest-handlers.js +44 -0
- package/dist/src/cli/cost-prompt.js +51 -0
- package/dist/src/cli/demo-handlers.js +274 -0
- package/dist/src/cli/flags.js +152 -0
- package/dist/src/cli/index.js +57 -0
- package/dist/src/cli/live-status.js +186 -0
- package/dist/src/cli/misc-handlers.js +242 -0
- package/dist/src/cli/plan-handlers.js +350 -0
- package/dist/src/cli/share-handlers.js +163 -0
- package/dist/src/cli/status-handlers.js +457 -0
- package/dist/src/cli/swarm-handlers.js +638 -0
- package/dist/src/cli/usage.js +72 -0
- package/dist/src/cli.js +217 -0
- package/dist/src/commit-pattern-detector.js +143 -0
- package/dist/src/commit-quality-analyzer.js +38 -0
- package/dist/src/config-loader.js +339 -0
- package/dist/src/context-broker.js +358 -0
- package/dist/src/copilot-cli-wrapper.js +258 -0
- package/dist/src/cost-estimator.js +241 -0
- package/dist/src/defaults.js +18 -0
- package/dist/src/demo-mode.js +180 -0
- package/dist/src/deployment-handler.js +78 -0
- package/dist/src/deployment-manager.js +228 -0
- package/dist/src/execution-queue.js +196 -0
- package/dist/src/external-tool-manager.js +184 -0
- package/dist/src/gate-prompt-builder.js +117 -0
- package/dist/src/gate-remediation.js +173 -0
- package/dist/src/github-issues-ingester.js +152 -0
- package/dist/src/github-mcp-integrator.js +164 -0
- package/dist/src/hook-generator.js +229 -0
- package/dist/src/index.js +48 -0
- package/dist/src/knowledge-base.js +300 -0
- package/dist/src/logger.js +111 -0
- package/dist/src/meta-analyzer.js +208 -0
- package/dist/src/metrics-collector.js +209 -0
- package/dist/src/metrics-types.js +5 -0
- package/dist/src/multi-repo-coordinator.js +179 -0
- package/dist/src/orchestrator/async-meta-analysis.js +84 -0
- package/dist/src/orchestrator/fatal-run-error.js +43 -0
- package/dist/src/orchestrator/final-gates-remediation.js +258 -0
- package/dist/src/orchestrator/git-state-utils.js +119 -0
- package/dist/src/orchestrator/pause-controller.js +55 -0
- package/dist/src/orchestrator/replan-runner.js +258 -0
- package/dist/src/orchestrator/step-executor.js +391 -0
- package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
- package/dist/src/owasp-mapper.js +153 -0
- package/dist/src/owasp-report-renderer.js +39 -0
- package/dist/src/plan-files.js +142 -0
- package/dist/src/plan-generator.js +1207 -0
- package/dist/src/pm-agent.js +296 -0
- package/dist/src/post-run-reporter.js +203 -0
- package/dist/src/pr-automation.js +103 -0
- package/dist/src/pr-manager.js +318 -0
- package/dist/src/presenter/index.js +209 -0
- package/dist/src/prompt-builder.js +173 -0
- package/dist/src/quality-gates/config-loader.js +196 -0
- package/dist/src/quality-gates/default-config.js +243 -0
- package/dist/src/quality-gates/file-utils.js +152 -0
- package/dist/src/quality-gates/gate-runner.js +171 -0
- package/dist/src/quality-gates/gates/accessibility.js +355 -0
- package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
- package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
- package/dist/src/quality-gates/gates/readme-claims.js +62 -0
- package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
- package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
- package/dist/src/quality-gates/gates/test-coverage.js +240 -0
- package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
- package/dist/src/quality-gates/gates/test-isolation.js +74 -0
- package/dist/src/quality-gates/index.js +17 -0
- package/dist/src/quality-gates/registry.js +205 -0
- package/dist/src/quality-gates/types.js +2 -0
- package/dist/src/quick-fix-mode.js +302 -0
- package/dist/src/recipe-loader.js +122 -0
- package/dist/src/repair-agent.js +396 -0
- package/dist/src/repo-analyzer.js +342 -0
- package/dist/src/report-generator.js +146 -0
- package/dist/src/report-renderer.js +90 -0
- package/dist/src/requirement-filter.js +52 -0
- package/dist/src/sarif-formatter.js +140 -0
- package/dist/src/scheduling/dependency-analyzer.js +178 -0
- package/dist/src/scheduling/work-stealing-queue.js +123 -0
- package/dist/src/secret-redactor.js +90 -0
- package/dist/src/session-executor.js +587 -0
- package/dist/src/session-manager.js +221 -0
- package/dist/src/share/transcript-verification.js +128 -0
- package/dist/src/share-parser.js +477 -0
- package/dist/src/spinner.js +146 -0
- package/dist/src/step-runner.js +365 -0
- package/dist/src/swarm-orchestrator.js +686 -0
- package/dist/src/task-classifier.js +134 -0
- package/dist/src/test-command-discovery.js +123 -0
- package/dist/src/text-similarity.js +24 -0
- package/dist/src/tier-maps.js +158 -0
- package/dist/src/types.js +5 -0
- package/dist/src/url-shortener.js +135 -0
- package/dist/src/verification/attestation.js +166 -0
- package/dist/src/verification/cheat-detector.js +240 -0
- package/dist/src/verification/command-runner.js +70 -0
- package/dist/src/verification/composite-score.js +157 -0
- package/dist/src/verification/cosign-attestation.js +137 -0
- package/dist/src/verification/diff-analysis.js +89 -0
- package/dist/src/verification/differential-gate.js +173 -0
- package/dist/src/verification/index.js +41 -0
- package/dist/src/verification/mutation-gate.js +242 -0
- package/dist/src/verification/property-gate.js +201 -0
- package/dist/src/verification/test-synthesizer.js +243 -0
- package/dist/src/verifier/outcome-checks.js +396 -0
- package/dist/src/verifier/transcript-checks.js +207 -0
- package/dist/src/verifier/verification-reporters.js +148 -0
- package/dist/src/verifier-engine.js +491 -0
- package/dist/src/wave-resizer.js +131 -0
- package/dist/src/wave-scheduler.js +44 -0
- package/dist/src/worktree-manager.js +368 -0
- package/dist/src/worktree-reserved-paths.js +97 -0
- package/package.json +80 -0
- package/plugin/agents/.gitkeep +0 -0
- package/plugin/agents/backend_master.agent.md +72 -0
- package/plugin/agents/devops_pro.agent.md +45 -0
- package/plugin/agents/frontend_expert.agent.md +59 -0
- package/plugin/agents/integrator_finalizer.agent.md +44 -0
- package/plugin/agents/meta_reviewer.agent.md +44 -0
- package/plugin/agents/security_auditor.agent.md +43 -0
- package/plugin/agents/tester_elite.agent.md +43 -0
- package/plugin/hooks/hooks.json +19 -0
- package/plugin/hooks/post-tool-use.json +9 -0
- package/plugin/hooks/pre-tool-use.json +9 -0
- package/plugin/plugin.json +47 -0
- package/plugin/skills/swarm-gates/SKILL.md +53 -0
- package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
- package/plugin/skills/swarm-verify/SKILL.md +44 -0
- package/templates/README.md +35 -0
- package/templates/cli-tool.json +31 -0
- package/templates/fullstack.json +52 -0
- package/templates/library.json +38 -0
- package/templates/react-app.json +45 -0
- package/templates/recipes/add-api-docs.json +35 -0
- package/templates/recipes/add-auth.json +47 -0
- package/templates/recipes/add-ci.json +39 -0
- package/templates/recipes/add-tests.json +39 -0
- package/templates/recipes/migrate-to-ts.json +42 -0
- package/templates/recipes/refactor-modularize.json +44 -0
- package/templates/recipes/security-audit.json +35 -0
- 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
|
+
}
|