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,638 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Swarm/execution-related 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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.validateAdapterSecrets = validateAdapterSecrets;
|
|
45
|
+
exports.executeSwarm = executeSwarm;
|
|
46
|
+
exports.writeCIOutputs = writeCIOutputs;
|
|
47
|
+
exports.handleBootstrapCommand = handleBootstrapCommand;
|
|
48
|
+
exports.handleSwarmCommand = handleSwarmCommand;
|
|
49
|
+
exports.handleQuickCommand = handleQuickCommand;
|
|
50
|
+
exports.handleRunCommand = handleRunCommand;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const config_loader_1 = require("../config-loader");
|
|
54
|
+
const plan_generator_1 = require("../plan-generator");
|
|
55
|
+
const plan_files_1 = require("../plan-files");
|
|
56
|
+
const quick_fix_mode_1 = __importDefault(require("../quick-fix-mode"));
|
|
57
|
+
const swarm_orchestrator_1 = require("../swarm-orchestrator");
|
|
58
|
+
const adapters_1 = require("../adapters");
|
|
59
|
+
const cost_prompt_1 = require("./cost-prompt");
|
|
60
|
+
const flags_1 = require("./flags");
|
|
61
|
+
const usage_1 = require("./usage");
|
|
62
|
+
const spinner_1 = require("../spinner");
|
|
63
|
+
const logger_1 = require("../logger");
|
|
64
|
+
const logger = (0, logger_1.getLogger)('cli:swarm');
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Internal constants
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Checked before execution so users get clear guidance instead of cryptic
|
|
69
|
+
// auth failures deep in the agent subprocess.
|
|
70
|
+
// Copilot authenticates via `gh auth login` (filesystem credentials),
|
|
71
|
+
// so it has no hard env var requirement. GITHUB_TOKEN is only used
|
|
72
|
+
// in CI where Actions provides it automatically.
|
|
73
|
+
// Claude Code supports both API key auth and subscription auth (via `claude login`).
|
|
74
|
+
// Codex supports both OPENAI_API_KEY env var and ~/.codex/auth.json (via `codex login`).
|
|
75
|
+
const ADAPTER_REQUIRED_KEYS = {
|
|
76
|
+
codex: ['OPENAI_API_KEY'],
|
|
77
|
+
};
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// Internal helpers
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
function validateAdapterSecrets(tool) {
|
|
82
|
+
const required = ADAPTER_REQUIRED_KEYS[tool];
|
|
83
|
+
if (!required)
|
|
84
|
+
return;
|
|
85
|
+
// Codex can auth via ~/.codex/auth.json (written by `codex login`) instead of
|
|
86
|
+
// OPENAI_API_KEY. If the auth file exists, treat the key requirement as satisfied.
|
|
87
|
+
const codexAuthFile = path.join(process.env.HOME || '/tmp', '.codex', 'auth.json');
|
|
88
|
+
const codexLoggedIn = tool === 'codex' && fs.existsSync(codexAuthFile);
|
|
89
|
+
const missing = required.filter(k => !process.env[k] && !codexLoggedIn);
|
|
90
|
+
if (missing.length === 0)
|
|
91
|
+
return;
|
|
92
|
+
const lines = [
|
|
93
|
+
`Missing required secrets for --tool ${tool}: ${missing.join(', ')}`,
|
|
94
|
+
'',
|
|
95
|
+
'Set them in your environment or GitHub Secrets:',
|
|
96
|
+
...missing.map(k => ` ${k}=<your-key>`),
|
|
97
|
+
'',
|
|
98
|
+
'In a GitHub Actions workflow, pass them via the env: block:',
|
|
99
|
+
...missing.map(k => ` ${k}: \${{ secrets.${k} }}`),
|
|
100
|
+
];
|
|
101
|
+
throw new Error(lines.join('\n'));
|
|
102
|
+
}
|
|
103
|
+
async function executeSwarm(planFilename, options) {
|
|
104
|
+
const selectedTool = options?.cliAgent || 'copilot';
|
|
105
|
+
validateAdapterSecrets(selectedTool);
|
|
106
|
+
let plan = (0, plan_files_1.loadPlanFile)(planFilename);
|
|
107
|
+
// In pretty mode (demo commands) the caller has already printed its own
|
|
108
|
+
// banner with the goal + step count ā don't duplicate.
|
|
109
|
+
if (!(0, logger_1.isPrettyMode)()) {
|
|
110
|
+
logger.info('š Swarm Orchestrator - Parallel Execution\n');
|
|
111
|
+
logger.info(`Goal: ${plan.goal}`);
|
|
112
|
+
logger.info(`Total Steps: ${plan.steps.length}\n`);
|
|
113
|
+
}
|
|
114
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
115
|
+
const agents = configLoader.loadAllAgents();
|
|
116
|
+
const agentMap = configLoader.buildAgentMap();
|
|
117
|
+
// PM agent review (optional, activated with --pm)
|
|
118
|
+
if (options?.pm) {
|
|
119
|
+
const { PMAgent } = await Promise.resolve().then(() => __importStar(require('../pm-agent')));
|
|
120
|
+
const pmAgent = new PMAgent(agents);
|
|
121
|
+
logger.info('š PM Agent: Reviewing plan...');
|
|
122
|
+
const pmResult = pmAgent.reviewPlan(plan);
|
|
123
|
+
if (pmResult.reviewNotes.length > 0) {
|
|
124
|
+
logger.info(' Review notes:');
|
|
125
|
+
pmResult.reviewNotes.forEach((note) => logger.info(` - ${note}`));
|
|
126
|
+
}
|
|
127
|
+
if (pmResult.changesApplied.length > 0) {
|
|
128
|
+
logger.info(' Changes applied:');
|
|
129
|
+
pmResult.changesApplied.forEach((change) => logger.info(` - ${change}`));
|
|
130
|
+
}
|
|
131
|
+
if (pmResult.reviewNotes.length === 0 && pmResult.changesApplied.length === 0) {
|
|
132
|
+
logger.info(' Plan approved with no issues.');
|
|
133
|
+
}
|
|
134
|
+
plan = pmResult.revisedPlan;
|
|
135
|
+
logger.info('');
|
|
136
|
+
}
|
|
137
|
+
// Cost estimation (always runs pre-execution)
|
|
138
|
+
const { CostEstimator } = await Promise.resolve().then(() => __importStar(require('../cost-estimator')));
|
|
139
|
+
const costEstimator = new CostEstimator();
|
|
140
|
+
const modelName = options?.model || (0, adapters_1.defaultModelForAdapter)(options?.cliAgent);
|
|
141
|
+
const costEstimate = costEstimator.estimate(plan, {
|
|
142
|
+
modelName,
|
|
143
|
+
fleetMode: !!options?.useInnerFleet,
|
|
144
|
+
qualityGatesEnabled: !options?.noQualityGates,
|
|
145
|
+
});
|
|
146
|
+
const retryPct = Math.round((costEstimate.perStep[0]?.retryProbability ?? 0.15) * 100);
|
|
147
|
+
logger.info(`\nš° Cost Estimate: ${costEstimate.lowEstimate}-${costEstimate.totalPremiumRequests} premium requests`);
|
|
148
|
+
logger.info(` ${plan.steps.length} steps | ${modelName} (${costEstimate.modelMultiplier}x) | ${retryPct}% retry buffer`);
|
|
149
|
+
if (costEstimate.remediationBuffer > 0) {
|
|
150
|
+
logger.info(` includes ${costEstimate.remediationBuffer} remediation buffer (quality gates enabled)`);
|
|
151
|
+
}
|
|
152
|
+
if (options?.useInnerFleet) {
|
|
153
|
+
logger.info(` /fleet mode: subagent multiplier applied`);
|
|
154
|
+
}
|
|
155
|
+
logger.info('');
|
|
156
|
+
if (options?.costEstimateOnly) {
|
|
157
|
+
logger.info(`Cost Estimate for: ${plan.goal}`);
|
|
158
|
+
logger.info(`Steps: ${plan.steps.length} | Model: ${modelName} (${costEstimate.modelMultiplier}x multiplier)`);
|
|
159
|
+
logger.info(`Estimated premium requests: ${costEstimate.lowEstimate}-${costEstimate.highEstimate}`);
|
|
160
|
+
logger.info(`Retry buffer (${retryPct}% historical failure rate): +${costEstimate.retryBuffer}`);
|
|
161
|
+
logger.info(`Total estimate: ${costEstimate.lowEstimate}-${costEstimate.totalPremiumRequests} premium requests`);
|
|
162
|
+
if (costEstimate.remainingAllowance !== undefined) {
|
|
163
|
+
logger.info(`At $0.04/overage: $${costEstimate.overageCostUSD.toFixed(2)} if over allowance`);
|
|
164
|
+
}
|
|
165
|
+
logger.info('');
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
if (options?.maxPremiumRequests !== undefined && costEstimate.totalPremiumRequests > options.maxPremiumRequests) {
|
|
169
|
+
logger.error(`Aborting: estimated ${costEstimate.totalPremiumRequests} premium requests exceeds budget of ${options.maxPremiumRequests}`);
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
// Pre-flight auth check: verify credentials before asking the user to confirm cost.
|
|
173
|
+
// This prevents the confusing UX of confirming a spend and then immediately failing on auth.
|
|
174
|
+
if (selectedTool === 'copilot') {
|
|
175
|
+
try {
|
|
176
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
177
|
+
execSync('gh auth status', { stdio: 'pipe', timeout: 10_000 });
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
logger.error('ā GitHub authentication check failed.');
|
|
181
|
+
logger.error(' Run `gh auth login` or `copilot /login` to re-authenticate.');
|
|
182
|
+
logger.error(' If using a token, ensure GH_TOKEN / GITHUB_TOKEN is set and valid.');
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Gate: require explicit user confirmation before spending tokens
|
|
187
|
+
const confirmed = await (0, cost_prompt_1.confirmCostPrompt)(costEstimate.lowEstimate, costEstimate.totalPremiumRequests, modelName, !!options?.yes);
|
|
188
|
+
if (!confirmed) {
|
|
189
|
+
logger.info('Cancelled.');
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
// Resolve the target repo directory: plan metadata > CLI flag > first step repo > plan file location > cwd
|
|
193
|
+
// When the plan file sits under <project>/plans/, infer <project> as the target.
|
|
194
|
+
let inferredFromPlan;
|
|
195
|
+
if (path.isAbsolute(planFilename)) {
|
|
196
|
+
const planDir = path.dirname(planFilename);
|
|
197
|
+
if (path.basename(planDir) === 'plans') {
|
|
198
|
+
inferredFromPlan = path.dirname(planDir);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const targetDir = plan.metadata?.targetDir
|
|
202
|
+
|| options?.targetDir
|
|
203
|
+
|| plan.steps.find(s => s.repo)?.repo
|
|
204
|
+
|| inferredFromPlan
|
|
205
|
+
|| undefined;
|
|
206
|
+
if (targetDir) {
|
|
207
|
+
logger.info(`š Target directory: ${targetDir}`);
|
|
208
|
+
}
|
|
209
|
+
// targetMode: structural discriminator for whether this run is against an
|
|
210
|
+
// external repo (bootstrap / swarm against a target dir) vs the
|
|
211
|
+
// orchestrator's own cwd (self-improvement). Drives quality-gate scoping:
|
|
212
|
+
// orchestrator-internal gates (duplicateBlocks, accessibility, etc.) skip
|
|
213
|
+
// in target mode; universal gates (hardcodedConfig, testFileProtection)
|
|
214
|
+
// always fire. See SELF_IMPROVEMENT_GATE_KEYS and #27 Phase-4a smoke4.
|
|
215
|
+
const targetMode = Boolean(targetDir);
|
|
216
|
+
const orchestrator = new swarm_orchestrator_1.SwarmOrchestrator(targetDir, targetMode);
|
|
217
|
+
const runId = `swarm-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
218
|
+
const baseDir = targetDir || process.cwd();
|
|
219
|
+
const runDir = path.join(baseDir, 'runs', runId);
|
|
220
|
+
if ((0, logger_1.isPrettyMode)()) {
|
|
221
|
+
// Demo: only the run dir is useful, and dim so it doesn't compete.
|
|
222
|
+
logger.info(` Run: ${runDir}\n`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
logger.info(`Run ID: ${runId}`);
|
|
226
|
+
logger.info(`Run Directory: ${runDir}\n`);
|
|
227
|
+
}
|
|
228
|
+
const swarmOptions = {};
|
|
229
|
+
if (options?.model)
|
|
230
|
+
swarmOptions.model = options.model;
|
|
231
|
+
if (options?.confirmDeploy)
|
|
232
|
+
swarmOptions.confirmDeploy = true;
|
|
233
|
+
if (options?.noQualityGates)
|
|
234
|
+
swarmOptions.qualityGates = false;
|
|
235
|
+
if (options?.strictIsolation)
|
|
236
|
+
swarmOptions.strictIsolation = true;
|
|
237
|
+
if (options?.lean)
|
|
238
|
+
swarmOptions.lean = true;
|
|
239
|
+
if (options?.useInnerFleet)
|
|
240
|
+
swarmOptions.useInnerFleet = true;
|
|
241
|
+
if (options?.prMode)
|
|
242
|
+
swarmOptions.prMode = options.prMode;
|
|
243
|
+
if (options?.hooksEnabled !== undefined)
|
|
244
|
+
swarmOptions.hooksEnabled = options.hooksEnabled;
|
|
245
|
+
if (options?.owaspReport)
|
|
246
|
+
swarmOptions.owaspReport = true;
|
|
247
|
+
if (options?.cliAgent)
|
|
248
|
+
swarmOptions.cliAgent = options.cliAgent;
|
|
249
|
+
const context = await orchestrator.executeSwarm(plan, agentMap, runDir, swarmOptions);
|
|
250
|
+
const completed = context.results.filter(r => r.status === 'completed').length;
|
|
251
|
+
const failed = context.results.filter(r => r.status === 'failed').length;
|
|
252
|
+
const totalSteps = context.results.length;
|
|
253
|
+
const actualPremiumRequests = context.stepCostRecords?.reduce((sum, record) => sum + record.actualPremiumRequests, 0) ?? 0;
|
|
254
|
+
const estimateDeltaRatio = costEstimate.totalPremiumRequests > 0
|
|
255
|
+
? (actualPremiumRequests - costEstimate.totalPremiumRequests) / costEstimate.totalPremiumRequests
|
|
256
|
+
: 0;
|
|
257
|
+
const gateResults = context.finalGateResults || [];
|
|
258
|
+
const gatesPassed = gateResults.filter(g => g.status === 'pass').length;
|
|
259
|
+
const gatesFailed = gateResults.filter(g => g.status === 'fail').length;
|
|
260
|
+
const remediationSteps = Math.max(0, totalSteps - plan.steps.length);
|
|
261
|
+
logger.info('\n' + 'ā'.repeat(60));
|
|
262
|
+
logger.info(' SWARM EXECUTION COMPLETE');
|
|
263
|
+
logger.info('ā'.repeat(60));
|
|
264
|
+
logger.info(`\n ā
Completed: ${completed}/${totalSteps}`);
|
|
265
|
+
if (failed > 0) {
|
|
266
|
+
logger.info(` ā Failed: ${failed}/${totalSteps}`);
|
|
267
|
+
logger.info('\n Failed steps:');
|
|
268
|
+
context.results
|
|
269
|
+
.filter(r => r.status === 'failed')
|
|
270
|
+
.forEach(r => {
|
|
271
|
+
logger.info(` - Step ${r.stepNumber} (${r.agentName}): ${r.error}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const starts = context.results.filter(r => r.startTime).map(r => new Date(r.startTime).getTime());
|
|
275
|
+
const ends = context.results.filter(r => r.endTime).map(r => new Date(r.endTime).getTime());
|
|
276
|
+
if (starts.length > 0 && ends.length > 0) {
|
|
277
|
+
const totalSec = Math.round((Math.max(...ends) - Math.min(...starts)) / 1000);
|
|
278
|
+
const min = Math.floor(totalSec / 60);
|
|
279
|
+
const sec = totalSec % 60;
|
|
280
|
+
logger.info(` ā± Total time: ${min > 0 ? `${min}m ${sec}s` : `${sec}s`}`);
|
|
281
|
+
}
|
|
282
|
+
logger.info(`\n š° Actual cost: ${actualPremiumRequests} premium requests`);
|
|
283
|
+
logger.info(` estimate: ${costEstimate.lowEstimate}-${costEstimate.totalPremiumRequests} | ${modelName} (${costEstimate.modelMultiplier}x) | ${totalSteps} steps${remediationSteps > 0 ? ` (${plan.steps.length} planned + ${remediationSteps} remediation)` : ''} | ${retryPct}% retry buffer`);
|
|
284
|
+
if (estimateDeltaRatio > 0.2) {
|
|
285
|
+
logger.info(` ā exceeded estimate by ${Math.round(estimateDeltaRatio * 100)}%`);
|
|
286
|
+
}
|
|
287
|
+
logger.info(`\n š Artifacts: ${runDir}`);
|
|
288
|
+
logger.info(` š Reports: ${runDir}/verification/`);
|
|
289
|
+
if (gateResults.length > 0) {
|
|
290
|
+
logger.info(` š§Ŗ Quality gates: ${gatesPassed} passed, ${gatesFailed} failed`);
|
|
291
|
+
}
|
|
292
|
+
// Show PR URLs if --pr mode was active
|
|
293
|
+
if (context.prUrls && context.prUrls.size > 0) {
|
|
294
|
+
logger.info('\n Pull Requests:');
|
|
295
|
+
for (const [stepNum, url] of context.prUrls) {
|
|
296
|
+
logger.info(` Step ${stepNum}: ${url}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
logger.info('ā'.repeat(60));
|
|
300
|
+
if (failed > 0) {
|
|
301
|
+
logger.info(`\nInspect failed run: swarm report ${runId}`);
|
|
302
|
+
}
|
|
303
|
+
else if (completed === totalSteps) {
|
|
304
|
+
logger.info('\nš All steps completed successfully!');
|
|
305
|
+
logger.info(' Review the git log to see the natural commit history:');
|
|
306
|
+
logger.info(' git log --oneline -20\n');
|
|
307
|
+
}
|
|
308
|
+
// CI output: write structured result when running inside GitHub Actions
|
|
309
|
+
const allPassed = context.results.every(r => r.verificationResult?.passed) && gatesFailed === 0;
|
|
310
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
311
|
+
writeCIOutputs(context, plan, allPassed);
|
|
312
|
+
}
|
|
313
|
+
return allPassed ? 0 : 1;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Write CI-specific output files when running inside GitHub Actions.
|
|
317
|
+
* These feed into action.yml outputs via GITHUB_OUTPUT.
|
|
318
|
+
*/
|
|
319
|
+
function writeCIOutputs(context, plan, allPassed) {
|
|
320
|
+
const starts = context.results.filter(r => r.startTime).map(r => new Date(r.startTime).getTime());
|
|
321
|
+
const ends = context.results.filter(r => r.endTime).map(r => new Date(r.endTime).getTime());
|
|
322
|
+
const totalDurationMs = (starts.length > 0 && ends.length > 0)
|
|
323
|
+
? Math.max(...ends) - Math.min(...starts)
|
|
324
|
+
: 0;
|
|
325
|
+
const resultJson = {
|
|
326
|
+
allPassed,
|
|
327
|
+
totalSteps: context.results.length,
|
|
328
|
+
completed: context.results.filter(r => r.status === 'completed').length,
|
|
329
|
+
failed: context.results.filter(r => r.status === 'failed').length,
|
|
330
|
+
totalDurationMs,
|
|
331
|
+
steps: context.results.map(r => ({
|
|
332
|
+
stepNumber: r.stepNumber,
|
|
333
|
+
agentName: r.agentName,
|
|
334
|
+
status: r.status,
|
|
335
|
+
passed: r.verificationResult?.passed ?? false,
|
|
336
|
+
retryCount: r.retryCount ?? 0,
|
|
337
|
+
})),
|
|
338
|
+
};
|
|
339
|
+
fs.writeFileSync('/tmp/swarm-result.json', JSON.stringify(resultJson), 'utf8');
|
|
340
|
+
fs.writeFileSync('/tmp/swarm-plan.json', JSON.stringify(plan, null, 2), 'utf8');
|
|
341
|
+
// Write first PR URL if present
|
|
342
|
+
if (context.prUrls && context.prUrls.size > 0) {
|
|
343
|
+
const firstUrl = context.prUrls.values().next().value;
|
|
344
|
+
if (firstUrl) {
|
|
345
|
+
fs.writeFileSync('/tmp/swarm-pr-url.txt', firstUrl, 'utf8');
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
// Exported command handlers
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
async function handleBootstrapCommand(args) {
|
|
353
|
+
if (args.length < 3) {
|
|
354
|
+
logger.error('Usage: swarm bootstrap <repo-path> [<repo-path2> ...] "Goal description"');
|
|
355
|
+
return 1;
|
|
356
|
+
}
|
|
357
|
+
// Extract positional args, skipping flags and their values
|
|
358
|
+
const flagsWithValues = new Set([
|
|
359
|
+
'--tool', '--model', '--resume', '--pr', '--target', '--dir',
|
|
360
|
+
'--quality-gates-config', '--quality-gates-out', '--max-premium-requests',
|
|
361
|
+
]);
|
|
362
|
+
const positional = [];
|
|
363
|
+
const raw = args.slice(1);
|
|
364
|
+
for (let i = 0; i < raw.length; i++) {
|
|
365
|
+
if (flagsWithValues.has(raw[i])) {
|
|
366
|
+
i++; // skip the flag's value
|
|
367
|
+
}
|
|
368
|
+
else if (!raw[i].startsWith('--')) {
|
|
369
|
+
positional.push(raw[i]);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (positional.length < 2) {
|
|
373
|
+
logger.error('Usage: swarm bootstrap <repo-path> [<repo-path2> ...] "Goal description"');
|
|
374
|
+
return 1;
|
|
375
|
+
}
|
|
376
|
+
const repoPaths = positional.slice(0, -1);
|
|
377
|
+
const goal = positional[positional.length - 1];
|
|
378
|
+
for (const repoPath of repoPaths) {
|
|
379
|
+
if (!fs.existsSync(repoPath)) {
|
|
380
|
+
logger.error(`Repository path does not exist: ${repoPath}`);
|
|
381
|
+
return 1;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
logger.info('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
385
|
+
logger.info('ā BOOTSTRAP MODE - Multi-Repo Analysis & Planning ā');
|
|
386
|
+
logger.info('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
|
|
387
|
+
const BootstrapOrchestrator = require('../bootstrap-orchestrator').default;
|
|
388
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
389
|
+
const goalSlug = goal.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 30);
|
|
390
|
+
const runId = `bootstrap-${timestamp}-${goalSlug}`;
|
|
391
|
+
const runDir = path.join(process.cwd(), 'runs', runId);
|
|
392
|
+
try {
|
|
393
|
+
const orchestrator = new BootstrapOrchestrator();
|
|
394
|
+
const bsSpinner = new spinner_1.Spinner('Analyzing repos...', { style: 'dots', prefix: ' ' });
|
|
395
|
+
bsSpinner.start();
|
|
396
|
+
let bootstrapResult;
|
|
397
|
+
try {
|
|
398
|
+
bootstrapResult = await orchestrator.bootstrap(repoPaths, goal, runDir);
|
|
399
|
+
}
|
|
400
|
+
finally {
|
|
401
|
+
bsSpinner.stop();
|
|
402
|
+
}
|
|
403
|
+
const { evidencePath, plan } = bootstrapResult;
|
|
404
|
+
const planPath = (0, plan_files_1.savePlanFile)(plan, runId);
|
|
405
|
+
logger.info('š Bootstrap Results:');
|
|
406
|
+
logger.info(` Evidence: ${evidencePath}`);
|
|
407
|
+
logger.info(` Plan: ${planPath}`);
|
|
408
|
+
logger.info(` Run ID: ${runId}`);
|
|
409
|
+
logger.info('');
|
|
410
|
+
logger.info('Next steps:');
|
|
411
|
+
logger.info(` 1. Review the evidence: cat ${evidencePath}`);
|
|
412
|
+
logger.info(` 2. Execute the plan: swarm swarm ${planPath}`);
|
|
413
|
+
return 0;
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
logger.error('Bootstrap failed:', error instanceof Error ? error.message : String(error));
|
|
417
|
+
return 1;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function handleSwarmCommand(args) {
|
|
421
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
422
|
+
logger.info(`
|
|
423
|
+
Usage: swarm swarm <planfile> [flags]
|
|
424
|
+
|
|
425
|
+
Execute a plan in parallel swarm mode. Runs steps concurrently based on
|
|
426
|
+
their dependency graph, verifying each step with evidence-based checks.
|
|
427
|
+
|
|
428
|
+
Arguments:
|
|
429
|
+
<planfile> Path to a plan JSON file (from \`swarm plan\` or \`swarm run\`)
|
|
430
|
+
|
|
431
|
+
Flags:
|
|
432
|
+
--model <name> Model to use (e.g., claude-opus-4.5, o3)
|
|
433
|
+
--pm Run PM agent plan review before execution
|
|
434
|
+
--strict-isolation Force per-task branch isolation
|
|
435
|
+
--lean Enable Delta Context Engine (reuse KB patterns)
|
|
436
|
+
--useInnerFleet Prefix all prompts with /fleet
|
|
437
|
+
--cost-estimate-only Print cost estimate and exit without executing
|
|
438
|
+
--max-premium-requests <n> Abort if estimated cost exceeds budget
|
|
439
|
+
--pr <auto|review> Create PRs instead of direct merge
|
|
440
|
+
--target <dir> Run in specified directory instead of cwd
|
|
441
|
+
--resume <id> Resume a paused or failed session
|
|
442
|
+
--hooks / --no-hooks Enable/disable per-step hook injection
|
|
443
|
+
--owasp-report Generate OWASP ASI compliance report
|
|
444
|
+
--tool <name> Agent tool: copilot, claude-code, claude-code-teams
|
|
445
|
+
|
|
446
|
+
Examples:
|
|
447
|
+
swarm swarm plan.json
|
|
448
|
+
swarm swarm plan.json --model claude-opus-4.5 --pm
|
|
449
|
+
swarm swarm plan.json --cost-estimate-only
|
|
450
|
+
swarm swarm plan.json --pr auto
|
|
451
|
+
`);
|
|
452
|
+
return 0;
|
|
453
|
+
}
|
|
454
|
+
if (args.length < 2 || !args[1]) {
|
|
455
|
+
logger.error('Error: Plan filename required\n');
|
|
456
|
+
(0, usage_1.showUsage)();
|
|
457
|
+
return 1;
|
|
458
|
+
}
|
|
459
|
+
const planFilename = args[1];
|
|
460
|
+
try {
|
|
461
|
+
const options = (0, flags_1.parseSwarmFlags)(args);
|
|
462
|
+
return await executeSwarm(planFilename, options);
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
logger.error('Error executing swarm:', error instanceof Error ? error.message : error);
|
|
466
|
+
return 1;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async function handleQuickCommand(args) {
|
|
470
|
+
if (args.length < 2) {
|
|
471
|
+
logger.error('Error: Quick-fix mode requires a task');
|
|
472
|
+
logger.info('Usage: swarm quick "task description"');
|
|
473
|
+
logger.info('Example: swarm quick "fix typo in README"');
|
|
474
|
+
return 1;
|
|
475
|
+
}
|
|
476
|
+
const task = args[1];
|
|
477
|
+
const flags = {
|
|
478
|
+
model: args.includes('--model') ? args[args.indexOf('--model') + 1] : undefined,
|
|
479
|
+
agent: args.includes('--agent') ? args[args.indexOf('--agent') + 1] : undefined,
|
|
480
|
+
tool: args.includes('--tool') ? args[args.indexOf('--tool') + 1] : undefined,
|
|
481
|
+
skipVerify: args.includes('--skip-verify'),
|
|
482
|
+
yes: args.includes('--yes') || args.includes('-y')
|
|
483
|
+
};
|
|
484
|
+
const quickFix = new quick_fix_mode_1.default();
|
|
485
|
+
logger.info('ā” Quick-Fix Mode\n');
|
|
486
|
+
const quickModel = flags.model || (0, adapters_1.defaultModelForAdapter)(flags.tool);
|
|
487
|
+
const { CostEstimator } = require('../cost-estimator');
|
|
488
|
+
const quickCostEstimator = new CostEstimator();
|
|
489
|
+
const quickEstimate = quickCostEstimator.estimate({
|
|
490
|
+
goal: task,
|
|
491
|
+
createdAt: new Date().toISOString(),
|
|
492
|
+
steps: [{
|
|
493
|
+
stepNumber: 1,
|
|
494
|
+
agentName: flags.agent || 'backend_master',
|
|
495
|
+
task,
|
|
496
|
+
dependencies: [],
|
|
497
|
+
expectedOutputs: []
|
|
498
|
+
}]
|
|
499
|
+
}, { modelName: quickModel });
|
|
500
|
+
logger.info(`š° Cost Estimate: ${quickEstimate.lowEstimate}-${quickEstimate.totalPremiumRequests} premium requests`);
|
|
501
|
+
logger.info(` 1 step | ${quickModel} (${quickEstimate.modelMultiplier}x)\n`);
|
|
502
|
+
// Gate: require explicit user confirmation before spending tokens
|
|
503
|
+
const quickConfirmed = await (0, cost_prompt_1.confirmCostPrompt)(quickEstimate.lowEstimate, quickEstimate.totalPremiumRequests, quickModel, flags.yes);
|
|
504
|
+
if (!quickConfirmed) {
|
|
505
|
+
logger.info('Cancelled.');
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
const quickOpts = {
|
|
509
|
+
skipVerification: flags.skipVerify
|
|
510
|
+
};
|
|
511
|
+
if (flags.model)
|
|
512
|
+
quickOpts.model = flags.model;
|
|
513
|
+
if (flags.agent)
|
|
514
|
+
quickOpts.agent = flags.agent;
|
|
515
|
+
const result = await quickFix.execute(task, quickOpts);
|
|
516
|
+
if (!result.wasQuickFixEligible) {
|
|
517
|
+
logger.info(result.output);
|
|
518
|
+
return 1;
|
|
519
|
+
}
|
|
520
|
+
if (result.success) {
|
|
521
|
+
logger.info(`\nā
Quick-fix completed in ${(result.duration / 1000).toFixed(1)}s`);
|
|
522
|
+
logger.info(` Agent: ${result.agentUsed}`);
|
|
523
|
+
if (result.verificationPassed !== undefined) {
|
|
524
|
+
logger.info(` Verification: ${result.verificationPassed ? 'ā Passed' : 'ā Failed'}`);
|
|
525
|
+
}
|
|
526
|
+
return 0;
|
|
527
|
+
}
|
|
528
|
+
logger.error(`\nā Quick-fix failed (${(result.duration / 1000).toFixed(1)}s)`);
|
|
529
|
+
logger.error(` Agent: ${result.agentUsed}`);
|
|
530
|
+
if (result.reason) {
|
|
531
|
+
logger.error(` Reason: ${result.reason}`);
|
|
532
|
+
}
|
|
533
|
+
return 1;
|
|
534
|
+
}
|
|
535
|
+
async function handleRunCommand(args) {
|
|
536
|
+
// Flags that consume the next token as a value (must be skipped during goal extraction)
|
|
537
|
+
const valuedFlags = new Set([
|
|
538
|
+
'--model', '--resume', '--quality-gates-config', '--quality-gates-out',
|
|
539
|
+
'--pr', '--target', '--dir', '--tool', '--max-premium-requests',
|
|
540
|
+
'--sarif', '--goal', '--base-commit', '--agent-guidance',
|
|
541
|
+
]);
|
|
542
|
+
// Extract positional tokens: skip the command name (args[0]) and any flag + value pairs
|
|
543
|
+
const positional = [];
|
|
544
|
+
for (let i = 1; i < args.length; i++) {
|
|
545
|
+
if (args[i].startsWith('--') || args[i] === '-y') {
|
|
546
|
+
if (valuedFlags.has(args[i]) && i + 1 < args.length)
|
|
547
|
+
i++; // skip the value
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
positional.push(args[i]);
|
|
551
|
+
}
|
|
552
|
+
// If --goal is explicit, extract its value directly
|
|
553
|
+
const goalIndex = args.indexOf('--goal');
|
|
554
|
+
let goal = '';
|
|
555
|
+
if (goalIndex !== -1) {
|
|
556
|
+
const afterGoal = args.slice(goalIndex + 1);
|
|
557
|
+
const tokens = [];
|
|
558
|
+
for (const tok of afterGoal) {
|
|
559
|
+
if (tok.startsWith('--'))
|
|
560
|
+
break;
|
|
561
|
+
tokens.push(tok);
|
|
562
|
+
}
|
|
563
|
+
goal = tokens.join(' ');
|
|
564
|
+
}
|
|
565
|
+
// Detect plan file: if the first positional arg resolves to an existing file, execute it directly
|
|
566
|
+
const firstPositional = positional[0];
|
|
567
|
+
if (!goal && firstPositional) {
|
|
568
|
+
let isPlanFile = false;
|
|
569
|
+
const resolved = path.resolve(process.cwd(), firstPositional);
|
|
570
|
+
// Check as a direct path (absolute or relative from cwd)
|
|
571
|
+
if (fs.existsSync(resolved)) {
|
|
572
|
+
try {
|
|
573
|
+
JSON.parse(fs.readFileSync(resolved, 'utf8'));
|
|
574
|
+
isPlanFile = true;
|
|
575
|
+
}
|
|
576
|
+
catch {
|
|
577
|
+
// Exists but not valid JSON; not a plan file
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// Check under plans/ directory for bare filenames
|
|
581
|
+
if (!isPlanFile) {
|
|
582
|
+
try {
|
|
583
|
+
(0, plan_files_1.loadPlanFile)(firstPositional);
|
|
584
|
+
isPlanFile = true;
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
// Not found in plans/ either; not a plan file
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (isPlanFile) {
|
|
591
|
+
logger.info('š Swarm Orchestrator - Execute Plan\n');
|
|
592
|
+
try {
|
|
593
|
+
const options = (0, flags_1.parseSwarmFlags)(args);
|
|
594
|
+
return await executeSwarm(firstPositional, options);
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
logger.error('Error:', error instanceof Error ? error.message : error);
|
|
598
|
+
return 1;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// No plan file detected; use positional tokens as goal (or --goal value)
|
|
603
|
+
if (!goal) {
|
|
604
|
+
goal = positional.join(' ');
|
|
605
|
+
}
|
|
606
|
+
if (!goal) {
|
|
607
|
+
logger.error('Error: goal required\nUsage: swarm run --goal "Build a REST API"\n swarm run <planfile> [flags]');
|
|
608
|
+
return 1;
|
|
609
|
+
}
|
|
610
|
+
logger.info('š Swarm Orchestrator - Plan & Execute\n');
|
|
611
|
+
logger.info(`Goal: ${goal}\n`);
|
|
612
|
+
// --agent-guidance: text that the planner prepends to every step's task
|
|
613
|
+
// but does NOT feed into goal classification. Used by benchmark harnesses
|
|
614
|
+
// (e.g. run_swebench.py's "do not modify test files" constraint) so the
|
|
615
|
+
// guidance doesn't poison the classifier into picking the wrong agent.
|
|
616
|
+
// See issue #27 Fix 1.
|
|
617
|
+
const guidanceIndex = args.indexOf('--agent-guidance');
|
|
618
|
+
let agentGuidance = '';
|
|
619
|
+
if (guidanceIndex !== -1 && guidanceIndex + 1 < args.length) {
|
|
620
|
+
agentGuidance = args[guidanceIndex + 1];
|
|
621
|
+
}
|
|
622
|
+
const configLoader = new config_loader_1.ConfigLoader();
|
|
623
|
+
const agents = configLoader.loadAllAgents();
|
|
624
|
+
const generator = new plan_generator_1.PlanGenerator(agents);
|
|
625
|
+
const plan = generator.createPlan(goal, undefined, {
|
|
626
|
+
...(agentGuidance ? { agentGuidance } : {}),
|
|
627
|
+
});
|
|
628
|
+
const planFilename = (0, plan_files_1.savePlanFile)(plan);
|
|
629
|
+
logger.info(`Plan saved: ${planFilename} (${plan.steps.length} steps)\n`);
|
|
630
|
+
try {
|
|
631
|
+
const options = (0, flags_1.parseSwarmFlags)(args);
|
|
632
|
+
return await executeSwarm(path.basename(planFilename), options);
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
logger.error('Error:', error instanceof Error ? error.message : error);
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
}
|