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,686 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.SwarmOrchestrator = void 0;
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const adapters_1 = require("./adapters");
|
|
44
|
+
const config_loader_1 = require("./config-loader");
|
|
45
|
+
const context_broker_1 = __importDefault(require("./context-broker"));
|
|
46
|
+
const execution_queue_1 = require("./execution-queue");
|
|
47
|
+
const knowledge_base_1 = require("./knowledge-base");
|
|
48
|
+
const meta_analyzer_1 = require("./meta-analyzer");
|
|
49
|
+
const metrics_collector_1 = __importDefault(require("./metrics-collector"));
|
|
50
|
+
const post_run_reporter_1 = require("./post-run-reporter");
|
|
51
|
+
const quality_gates_1 = require("./quality-gates");
|
|
52
|
+
const session_executor_1 = __importDefault(require("./session-executor"));
|
|
53
|
+
const share_parser_1 = __importDefault(require("./share-parser"));
|
|
54
|
+
const verifier_engine_1 = __importDefault(require("./verifier-engine"));
|
|
55
|
+
const wave_resizer_1 = require("./wave-resizer");
|
|
56
|
+
const cost_estimator_1 = require("./cost-estimator");
|
|
57
|
+
const pr_manager_1 = __importDefault(require("./pr-manager"));
|
|
58
|
+
const worktree_manager_1 = require("./worktree-manager");
|
|
59
|
+
const branch_merger_1 = require("./branch-merger");
|
|
60
|
+
const baseline_scanner_1 = require("./baseline-scanner");
|
|
61
|
+
const task_classifier_1 = require("./task-classifier");
|
|
62
|
+
const tier_maps_1 = require("./tier-maps");
|
|
63
|
+
const requirement_filter_1 = require("./requirement-filter");
|
|
64
|
+
const logger_1 = require("./logger");
|
|
65
|
+
const prompt_builder_1 = require("./prompt-builder");
|
|
66
|
+
const wave_scheduler_1 = require("./wave-scheduler");
|
|
67
|
+
const deployment_handler_1 = require("./deployment-handler");
|
|
68
|
+
const commit_quality_analyzer_1 = require("./commit-quality-analyzer");
|
|
69
|
+
const pause_controller_1 = require("./orchestrator/pause-controller");
|
|
70
|
+
const git_state_utils_1 = require("./orchestrator/git-state-utils");
|
|
71
|
+
const async_meta_analysis_1 = require("./orchestrator/async-meta-analysis");
|
|
72
|
+
const final_gates_remediation_1 = require("./orchestrator/final-gates-remediation");
|
|
73
|
+
const replan_runner_1 = require("./orchestrator/replan-runner");
|
|
74
|
+
const step_executor_1 = require("./orchestrator/step-executor");
|
|
75
|
+
const wave_scheduler_loop_1 = require("./orchestrator/wave-scheduler-loop");
|
|
76
|
+
const fatal_run_error_1 = require("./orchestrator/fatal-run-error");
|
|
77
|
+
const logger = (0, logger_1.getLogger)('orchestrator');
|
|
78
|
+
/**
|
|
79
|
+
* Swarm Orchestrator - coordinates parallel execution of independent Copilot CLI sessions
|
|
80
|
+
* Manages concurrent sessions, per-agent branches, and automatic merging
|
|
81
|
+
*/
|
|
82
|
+
class SwarmOrchestrator {
|
|
83
|
+
sessionExecutor;
|
|
84
|
+
shareParser;
|
|
85
|
+
verifier;
|
|
86
|
+
workingDir;
|
|
87
|
+
worktreeManager;
|
|
88
|
+
branchMerger;
|
|
89
|
+
pauseController = new pause_controller_1.PauseController();
|
|
90
|
+
/**
|
|
91
|
+
* `targetMode` is true when the orchestrator is operating on an external
|
|
92
|
+
* target repo (e.g. `swarm bootstrap ./external-repo`, the SWE-bench
|
|
93
|
+
* harness). False when operating on its own codebase (self-improvement
|
|
94
|
+
* runs). Structural signal, not keyword-based: the caller passes a
|
|
95
|
+
* truthy `workingDir` that is not the orchestrator's own cwd, and the
|
|
96
|
+
* CLI dispatcher sets `targetMode = true` at that boundary.
|
|
97
|
+
*
|
|
98
|
+
* Drives two behaviors:
|
|
99
|
+
* 1. Orchestrator-internal quality gates (scaffoldDefaults,
|
|
100
|
+
* duplicateBlocks, readmeClaims, testIsolation, runtimeChecks,
|
|
101
|
+
* accessibility, testCoverage) are skipped when targetMode is
|
|
102
|
+
* true — those enforce conventions on the orchestrator's own
|
|
103
|
+
* generated code, not on arbitrary target repos. See #27 Phase-4a
|
|
104
|
+
* smoke4 for the failure mode this prevents.
|
|
105
|
+
* 2. Verifier per-step outcome checks (git_diff, file_existence,
|
|
106
|
+
* build_exec, test_exec) are NOT affected by targetMode. Those
|
|
107
|
+
* are the orchestrator's verification contract for detecting
|
|
108
|
+
* agent lies about their own work, and apply universally.
|
|
109
|
+
*/
|
|
110
|
+
targetMode;
|
|
111
|
+
constructor(workingDir, targetMode = false) {
|
|
112
|
+
this.workingDir = workingDir || process.cwd();
|
|
113
|
+
this.targetMode = targetMode;
|
|
114
|
+
this.sessionExecutor = new session_executor_1.default(this.workingDir);
|
|
115
|
+
this.shareParser = new share_parser_1.default();
|
|
116
|
+
this.verifier = new verifier_engine_1.default(this.workingDir);
|
|
117
|
+
this.worktreeManager = new worktree_manager_1.WorktreeManager(this.workingDir);
|
|
118
|
+
this.branchMerger = new branch_merger_1.BranchMerger(this.workingDir, this.worktreeManager);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Look up an agent by name, falling back to normalized (snake_case) matching.
|
|
122
|
+
* Handles plans using lowercase ('worker', 'reviewer') against YAML agents.
|
|
123
|
+
* Public to satisfy `RemediationHost`; callers outside the class should still
|
|
124
|
+
* treat it as an internal helper.
|
|
125
|
+
*/
|
|
126
|
+
resolveAgent(agents, name) {
|
|
127
|
+
const exact = agents.get(name);
|
|
128
|
+
if (exact)
|
|
129
|
+
return exact;
|
|
130
|
+
const normalized = config_loader_1.ConfigLoader.normalizeAgentName(name);
|
|
131
|
+
for (const [key, agent] of agents) {
|
|
132
|
+
if (config_loader_1.ConfigLoader.normalizeAgentName(key) === normalized)
|
|
133
|
+
return agent;
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Request pause of current execution
|
|
139
|
+
*/
|
|
140
|
+
requestPause() {
|
|
141
|
+
this.pauseController.requestPause();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Request resume of paused execution
|
|
145
|
+
*/
|
|
146
|
+
requestResume() {
|
|
147
|
+
this.pauseController.requestResume();
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if pause is requested
|
|
151
|
+
*/
|
|
152
|
+
isPauseRequested() {
|
|
153
|
+
return this.pauseController.isPauseRequested();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Initialize swarm execution context
|
|
157
|
+
*/
|
|
158
|
+
initializeSwarmExecution(plan, runDir, maxConcurrency) {
|
|
159
|
+
const executionId = this.generateExecutionId();
|
|
160
|
+
const contextBroker = new context_broker_1.default(runDir);
|
|
161
|
+
const metricsCollector = new metrics_collector_1.default(executionId, plan.goal);
|
|
162
|
+
// ensure repo has at least one commit (required for branch creation)
|
|
163
|
+
this.ensureInitialCommit();
|
|
164
|
+
// resolve the integration branch: prefer origin/HEAD's symbolic ref (so repos
|
|
165
|
+
// whose default is `master` or `trunk` work), fall back to the currently
|
|
166
|
+
// checked-out branch, then fall back to 'main'. See worktree-manager
|
|
167
|
+
// .resolveDefaultBranch() for the rationale.
|
|
168
|
+
const mainBranch = this.worktreeManager.resolveDefaultBranch();
|
|
169
|
+
let baseCommitSha;
|
|
170
|
+
try {
|
|
171
|
+
baseCommitSha = (0, child_process_1.execSync)('git rev-parse HEAD', {
|
|
172
|
+
cwd: this.workingDir, encoding: 'utf8', stdio: 'pipe'
|
|
173
|
+
}).trim();
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// fresh repo with no commits yet
|
|
177
|
+
}
|
|
178
|
+
// initialize scalability components
|
|
179
|
+
const concurrencyLimit = maxConcurrency || 3;
|
|
180
|
+
const executionQueue = new execution_queue_1.ExecutionQueue(concurrencyLimit);
|
|
181
|
+
const waveResizer = new wave_resizer_1.WaveResizer();
|
|
182
|
+
const adaptiveConcurrency = new wave_resizer_1.AdaptiveConcurrencyManager(concurrencyLimit, 10);
|
|
183
|
+
// initialize adaptive intelligence components
|
|
184
|
+
// save knowledge base in run dir, not target repo (avoids git checkout conflicts)
|
|
185
|
+
const knowledgeBase = new knowledge_base_1.KnowledgeBaseManager(runDir);
|
|
186
|
+
const metaAnalyzer = new meta_analyzer_1.MetaAnalyzer();
|
|
187
|
+
const context = {
|
|
188
|
+
plan,
|
|
189
|
+
runDir,
|
|
190
|
+
executionId,
|
|
191
|
+
startTime: new Date().toISOString(),
|
|
192
|
+
results: plan.steps.map(step => ({
|
|
193
|
+
stepNumber: step.stepNumber,
|
|
194
|
+
agentName: step.agentName,
|
|
195
|
+
status: 'pending'
|
|
196
|
+
})),
|
|
197
|
+
contextBroker,
|
|
198
|
+
mainBranch,
|
|
199
|
+
...(baseCommitSha ? { baseCommitSha } : {}),
|
|
200
|
+
metricsCollector,
|
|
201
|
+
executionQueue,
|
|
202
|
+
queueStats: executionQueue.getStats(),
|
|
203
|
+
waveResizer,
|
|
204
|
+
adaptiveConcurrency,
|
|
205
|
+
knowledgeBase,
|
|
206
|
+
metaAnalyzer,
|
|
207
|
+
waveAnalyses: []
|
|
208
|
+
};
|
|
209
|
+
return context;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Execute plan with parallel swarm - independent steps run concurrently
|
|
213
|
+
*/
|
|
214
|
+
async executeSwarm(plan, agents, runDir, options) {
|
|
215
|
+
const context = this.initializeSwarmExecution(plan, runDir, options?.maxConcurrency);
|
|
216
|
+
// Sanitize git state before starting. Previous crashed runs may have left
|
|
217
|
+
// unmerged files, staged changes, or an in-progress merge that would block
|
|
218
|
+
// branch creation, worktree setup, and post-step merge operations.
|
|
219
|
+
this.sanitizeGitState();
|
|
220
|
+
context.agents = agents;
|
|
221
|
+
context.qualityGatesTriggered = {
|
|
222
|
+
duplicateRefactorAdded: false,
|
|
223
|
+
readmeTruthAdded: false,
|
|
224
|
+
scaffoldFixAdded: false,
|
|
225
|
+
configFixAdded: false,
|
|
226
|
+
accessibilityFixAdded: false,
|
|
227
|
+
testCoverageFixAdded: false
|
|
228
|
+
};
|
|
229
|
+
// Initialize PR manager when --pr mode is set
|
|
230
|
+
if (options?.prMode) {
|
|
231
|
+
const prManager = new pr_manager_1.default(this.workingDir);
|
|
232
|
+
if (!prManager.isGhAvailable()) {
|
|
233
|
+
logger.error(' --pr requires gh CLI installed and authenticated. Run "gh auth login" first.');
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
context.prManager = prManager;
|
|
237
|
+
context.prUrls = new Map();
|
|
238
|
+
}
|
|
239
|
+
// Execution-header scaffolding (exec id, main branch, concurrency) is
|
|
240
|
+
// diagnostic detail; emit at debug level so --verbose surfaces it on
|
|
241
|
+
// stderr while default user output stays uncluttered.
|
|
242
|
+
logger.debug(`exec id ${context.executionId}`);
|
|
243
|
+
logger.debug(`main ${context.mainBranch}`);
|
|
244
|
+
logger.debug(`concurrency ${options?.maxConcurrency || 'unlimited'}`);
|
|
245
|
+
if (options?.confirmDeploy) {
|
|
246
|
+
logger.info(` deployment enabled (--confirm-deploy)`);
|
|
247
|
+
}
|
|
248
|
+
// Group steps by repo for multi-repo orchestration
|
|
249
|
+
const repoGroups = new Map();
|
|
250
|
+
for (const step of plan.steps) {
|
|
251
|
+
const repo = step.repo ?? process.cwd();
|
|
252
|
+
if (!repoGroups.has(repo))
|
|
253
|
+
repoGroups.set(repo, []);
|
|
254
|
+
repoGroups.get(repo).push(step);
|
|
255
|
+
}
|
|
256
|
+
if (repoGroups.size > 1) {
|
|
257
|
+
const presenter = require('./presenter');
|
|
258
|
+
const p = presenter.getPresenter();
|
|
259
|
+
p.print(` ${p.dim('multi-repo')} ${repoGroups.size} repos`);
|
|
260
|
+
for (const [repo, steps] of repoGroups) {
|
|
261
|
+
p.print(` ${path.basename(repo)} ${p.dim(`${steps.length} step${steps.length === 1 ? '' : 's'}`)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// build dependency graph
|
|
265
|
+
const dependencyGraph = this.buildDependencyGraph(plan);
|
|
266
|
+
// identify waves of parallel execution
|
|
267
|
+
const executionWaves = this.identifyExecutionWaves(dependencyGraph);
|
|
268
|
+
context.totalWaves = executionWaves.length;
|
|
269
|
+
if (executionWaves.length > 1) {
|
|
270
|
+
const presenter = require('./presenter');
|
|
271
|
+
const p = presenter.getPresenter();
|
|
272
|
+
p.print(` ${p.dim('plan')} ${executionWaves.length} waves`);
|
|
273
|
+
p.blank();
|
|
274
|
+
}
|
|
275
|
+
// Pre-execution cost estimation
|
|
276
|
+
const costEstimator = new cost_estimator_1.CostEstimator(context.knowledgeBase);
|
|
277
|
+
const modelName = options?.model || (0, adapters_1.defaultModelForAdapter)(options?.cliAgent);
|
|
278
|
+
context.costEstimator = costEstimator;
|
|
279
|
+
context.costEstimate = costEstimator.estimate(plan, {
|
|
280
|
+
modelName,
|
|
281
|
+
fleetMode: !!options?.useInnerFleet,
|
|
282
|
+
qualityGatesEnabled: options?.qualityGates !== false,
|
|
283
|
+
});
|
|
284
|
+
context.stepCostRecords = [];
|
|
285
|
+
// Capture existing file inventory before agents run, so preservation rules
|
|
286
|
+
// can be injected into shared instructions and per-step prompts.
|
|
287
|
+
context.baselineSnapshot = (0, baseline_scanner_1.scanBaseline)(this.workingDir);
|
|
288
|
+
// Classify the goal and filter requirements by tier so agents only see
|
|
289
|
+
// what matters for this task type (backend, frontend, CLI, etc.)
|
|
290
|
+
const taskClassifier = new task_classifier_1.TaskClassifier();
|
|
291
|
+
const taskClassification = taskClassifier.classify(plan.goal || '');
|
|
292
|
+
const requirementFilter = new requirement_filter_1.RequirementFilter(tier_maps_1.TIER_MAPS);
|
|
293
|
+
const filtered = requirementFilter.filter(taskClassification);
|
|
294
|
+
context.filteredRequirements = filtered;
|
|
295
|
+
const tierInjection = requirementFilter.toPromptInjection(filtered);
|
|
296
|
+
// Write common prompt instructions to repo root so Copilot CLI picks them up natively.
|
|
297
|
+
// Per-step prompts only carry task-specific content; shared boilerplate lives here.
|
|
298
|
+
this.writeSharedInstructions(context.baselineSnapshot, tierInjection);
|
|
299
|
+
// Cache quality gates config once for the entire run
|
|
300
|
+
const gatesConfig = (0, quality_gates_1.load_quality_gates_config)(this.workingDir, options?.qualityGatesConfigPath);
|
|
301
|
+
// Scheduler loop: greedy as-soon-as-ready scheduling, lean-mode KB
|
|
302
|
+
// injection, optional /fleet dispatch, adaptive concurrency.
|
|
303
|
+
// See src/orchestrator/wave-scheduler-loop.ts
|
|
304
|
+
// for the INVARIANT on context.plan re-read (executeReplan may swap
|
|
305
|
+
// it mid-run; scheduler re-reads context.plan.steps every iteration).
|
|
306
|
+
try {
|
|
307
|
+
await (0, wave_scheduler_loop_1.runWaveLoop)(this, plan, agents, context, options);
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
if ((0, fatal_run_error_1.isFatalRunError)(err)) {
|
|
311
|
+
// Account-level wall (usage-limit, auth, extended rate-limit) reached.
|
|
312
|
+
// Skip the failed-step replan, the quality-gate pipeline, and the
|
|
313
|
+
// post-run reporter — none of those can recover from "no agent
|
|
314
|
+
// calls will succeed". Persist a sentinel for the SWE-bench harness
|
|
315
|
+
// and rethrow so the CLI exits non-zero with the fatal kind.
|
|
316
|
+
const sentinel = {
|
|
317
|
+
kind: err.fatalKind,
|
|
318
|
+
message: err.message,
|
|
319
|
+
evidence: err.evidence,
|
|
320
|
+
stepNumber: err.stepNumber ?? null,
|
|
321
|
+
agentName: err.agentName ?? null,
|
|
322
|
+
executionId: context.executionId,
|
|
323
|
+
timestamp: new Date().toISOString(),
|
|
324
|
+
};
|
|
325
|
+
try {
|
|
326
|
+
fs.writeFileSync(path.join(runDir, 'fatal-run-error.json'), JSON.stringify(sentinel, null, 2), 'utf8');
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Sentinel-write failure is non-fatal; the rethrown error still
|
|
330
|
+
// surfaces the abort to the CLI exit handler.
|
|
331
|
+
}
|
|
332
|
+
logger.error(`\n aborting: agent CLI reported unrecoverable ${err.fatalKind} error`);
|
|
333
|
+
logger.error(` evidence: ${err.evidence}`);
|
|
334
|
+
logger.error(` no replan or quality gate can recover from an account-level wall.`);
|
|
335
|
+
throw err;
|
|
336
|
+
}
|
|
337
|
+
throw err;
|
|
338
|
+
}
|
|
339
|
+
// Per-step execution summary. The wave-loop already prints the per-step
|
|
340
|
+
// verified/failed lines via LiveStatus; this block is intentionally
|
|
341
|
+
// suppressed to keep the post-run section quiet, leaving the final
|
|
342
|
+
// headline (printed by the CLI handler) as the user-visible signal.
|
|
343
|
+
const failedResults = context.results.filter(r => r.status === 'failed');
|
|
344
|
+
if (context.unmergedBranches && context.unmergedBranches.length > 0) {
|
|
345
|
+
logger.warn(`\n ${context.unmergedBranches.length} branch${context.unmergedBranches.length === 1 ? '' : 'es'} could not merge (work preserved):`);
|
|
346
|
+
for (const um of context.unmergedBranches) {
|
|
347
|
+
logger.warn(` step-${um.stepNumber} ${um.agentName} on ${um.branchName}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Remove any remaining worktrees (failed or unmerged steps) so their
|
|
351
|
+
// leftover test files don't pollute recursive test runners during gates.
|
|
352
|
+
// mergeWaveBranches already cleaned merged steps; this catches the rest.
|
|
353
|
+
await this.cleanupRemainingWorktrees(context);
|
|
354
|
+
// Install dependencies if any agent added new ones to package.json.
|
|
355
|
+
// Without this, quality gates' `npm test` would fail on MODULE_NOT_FOUND
|
|
356
|
+
// for newly-added packages like express or cors.
|
|
357
|
+
await this.installDependenciesIfNeeded();
|
|
358
|
+
// Re-queue failed steps before quality gates so their objectives aren't silently
|
|
359
|
+
// dropped. A step that fails verification doesn't disappear — its required work
|
|
360
|
+
// is still in scope. Build a targeted repair task per failed step, include the
|
|
361
|
+
// original task text and the verification failure context, and run a replan pass
|
|
362
|
+
// so the repair executes on the current merged state before gates run against it.
|
|
363
|
+
const retriableFailures = context.results.filter(r => r.status === 'failed' && (r.retryCount ?? 0) < 3);
|
|
364
|
+
if (retriableFailures.length > 0) {
|
|
365
|
+
const completedStepNumbers = context.results
|
|
366
|
+
.filter(r => r.status === 'completed')
|
|
367
|
+
.map(r => r.stepNumber);
|
|
368
|
+
const maxCompletedStep = completedStepNumbers.length > 0
|
|
369
|
+
? Math.max(...completedStepNumbers)
|
|
370
|
+
: 0;
|
|
371
|
+
const failedStepRetries = [];
|
|
372
|
+
for (const failed of retriableFailures) {
|
|
373
|
+
const originalStep = context.plan.steps.find(s => s.stepNumber === failed.stepNumber);
|
|
374
|
+
if (!originalStep)
|
|
375
|
+
continue;
|
|
376
|
+
const failureDetail = failed.verificationResult?.failureContext ?? failed.error ?? 'Verification failed';
|
|
377
|
+
const retryTask = [
|
|
378
|
+
`The previous attempt by ${originalStep.agentName} failed verification.`,
|
|
379
|
+
`Failure reason:\n${failureDetail}`,
|
|
380
|
+
``,
|
|
381
|
+
`Original task:`,
|
|
382
|
+
originalStep.task,
|
|
383
|
+
].join('\n');
|
|
384
|
+
failedStepRetries.push({
|
|
385
|
+
agent: originalStep.agentName,
|
|
386
|
+
task: retryTask,
|
|
387
|
+
...(maxCompletedStep > 0 ? { afterStep: maxCompletedStep } : {}),
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
if (failedStepRetries.length > 0) {
|
|
391
|
+
logger.info(`\n re-queuing ${failedStepRetries.length} failed step${failedStepRetries.length === 1 ? '' : 's'}`);
|
|
392
|
+
await this.executeReplan(context, { retrySteps: [], addSteps: failedStepRetries }, agents, options);
|
|
393
|
+
// Re-run dep install in case the repair step added packages.
|
|
394
|
+
await this.installDependenciesIfNeeded();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// final quality gates run on the merged state (hard gate)
|
|
398
|
+
// this happens before auto-PR so we don't create a PR for a failing run
|
|
399
|
+
const gatesEnabled = options?.qualityGates !== false;
|
|
400
|
+
if (gatesEnabled) {
|
|
401
|
+
const pipelineResult = await (0, final_gates_remediation_1.runFinalGatesPipeline)(this, context, runDir, agents, gatesConfig, options);
|
|
402
|
+
if (!pipelineResult.passed && gatesConfig.failOnIssues) {
|
|
403
|
+
if (pipelineResult.remediationAttempted && failedResults.length === 0) {
|
|
404
|
+
// All plan steps passed but remediation couldn't fully resolve
|
|
405
|
+
// pre-existing quality gaps. Downgrade to a warning so the run
|
|
406
|
+
// isn't marked failed for issues outside the plan's scope.
|
|
407
|
+
const remaining = pipelineResult.finalGateResults
|
|
408
|
+
.filter(r => r.status === 'fail')
|
|
409
|
+
.map(r => `${r.id} (${r.issues.length} issues)`);
|
|
410
|
+
logger.warn(` quality gates still have issues after remediation: ${remaining.join(', ')}`);
|
|
411
|
+
logger.warn(' treating as warning since all plan steps passed.');
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
logger.error(' quality gates failed. See report in:', pipelineResult.gatesOut);
|
|
415
|
+
throw new Error('Quality gates failed');
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// Final merge pass: sweep any branches the wave loop did not already
|
|
420
|
+
// merge. Header is suppressed because the wave loop's per-branch
|
|
421
|
+
// "merged step-N <agent>" lines (and any "(already merged)" follow-ups
|
|
422
|
+
// below) carry the signal on their own.
|
|
423
|
+
// Clean up any stale locks before final merge (e.g. from cancelled runs)
|
|
424
|
+
context.contextBroker.forceReleaseStaleLocks();
|
|
425
|
+
await this.mergeAllBranches(context);
|
|
426
|
+
// Metrics, cost attribution, session state, OWASP, KB recordRun, auto-PR.
|
|
427
|
+
// All of this lives in src/post-run-reporter.ts; the inline block was a
|
|
428
|
+
// pre-existing duplicate. See docs/post-run-diff-report.md for the
|
|
429
|
+
// behavioral diff that preceded this swap.
|
|
430
|
+
const postRunContext = context;
|
|
431
|
+
const postRunOptions = {};
|
|
432
|
+
if (options?.model !== undefined)
|
|
433
|
+
postRunOptions.model = options.model;
|
|
434
|
+
if (options?.cliAgent !== undefined)
|
|
435
|
+
postRunOptions.cliAgent = options.cliAgent;
|
|
436
|
+
if (options?.owaspReport !== undefined)
|
|
437
|
+
postRunOptions.owaspReport = options.owaspReport;
|
|
438
|
+
if (options?.strictIsolation !== undefined)
|
|
439
|
+
postRunOptions.strictIsolation = options.strictIsolation;
|
|
440
|
+
if (options?.enableExternal !== undefined)
|
|
441
|
+
postRunOptions.enableExternal = options.enableExternal;
|
|
442
|
+
if (options?.dryRun !== undefined)
|
|
443
|
+
postRunOptions.dryRun = options.dryRun;
|
|
444
|
+
if (options?.autoPR !== undefined)
|
|
445
|
+
postRunOptions.autoPR = options.autoPR;
|
|
446
|
+
await (0, post_run_reporter_1.runPostExecution)(this.workingDir, runDir, postRunContext, plan, postRunOptions);
|
|
447
|
+
return context;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Shared auto-remediation logic for quality gate failures -
|
|
451
|
+
* delegates to final-gates-remediation module. Kept as a thin private
|
|
452
|
+
* method so tests that invoke it via `(orch as any).buildRemediationStep`
|
|
453
|
+
* continue to work unchanged.
|
|
454
|
+
*/
|
|
455
|
+
buildRemediationStep(gateResult, configEnabled, triggeredFlag, context, agents, taskDescription, warningMessage, afterStep, fallbackAgent) {
|
|
456
|
+
return (0, final_gates_remediation_1.buildRemediationStepForDelegate)(this, gateResult, configEnabled, triggeredFlag, context, agents, taskDescription, warningMessage, afterStep, fallbackAgent);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* execute replan - delegates to replan-runner module. Public to satisfy
|
|
460
|
+
* `RemediationHost` and `ReplanHost`; callers outside the class should
|
|
461
|
+
* still treat it as an internal helper.
|
|
462
|
+
*
|
|
463
|
+
* WARNING: mutates context.plan by assignment. See replan-runner.ts
|
|
464
|
+
* for the full invariant comment.
|
|
465
|
+
*/
|
|
466
|
+
async executeReplan(context, replanPayload, agents, options) {
|
|
467
|
+
return (0, replan_runner_1.executeReplan)(this, context, replanPayload, agents, options);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Execute a single step within the swarm - delegates to step-executor module.
|
|
471
|
+
* Public to satisfy `ReplanHost` and `StepExecutorHost`; callers outside
|
|
472
|
+
* the class should still treat it as an internal helper.
|
|
473
|
+
*/
|
|
474
|
+
async executeStepInSwarm(step, agent, context, options) {
|
|
475
|
+
return (0, step_executor_1.executeStepInSwarm)(this, step, agent, context, options);
|
|
476
|
+
}
|
|
477
|
+
/** Build prompt for swarm step execution. Delegates to prompt-builder module. */
|
|
478
|
+
buildSwarmPrompt(step, agent, context, dependencyContext) {
|
|
479
|
+
// Pass the orchestrator's working dir so prompt-builder can read the
|
|
480
|
+
// target project's package.json to discover the real test gate.
|
|
481
|
+
return (0, prompt_builder_1.buildSwarmPrompt)(step, agent, { ...context, targetProjectRoot: this.workingDir }, dependencyContext);
|
|
482
|
+
}
|
|
483
|
+
/** Build dependency graph from plan. Delegates to wave-scheduler module. */
|
|
484
|
+
buildDependencyGraph(plan) {
|
|
485
|
+
return (0, wave_scheduler_1.buildDependencyGraph)(plan);
|
|
486
|
+
}
|
|
487
|
+
/** Identify waves of parallel execution (topological sort by levels). Delegates to wave-scheduler module. */
|
|
488
|
+
identifyExecutionWaves(graph) {
|
|
489
|
+
return (0, wave_scheduler_1.identifyExecutionWaves)(graph);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Create a git worktree for an agent - delegates to WorktreeManager.
|
|
493
|
+
* Public to satisfy `StepExecutorHost`.
|
|
494
|
+
*/
|
|
495
|
+
async createAgentWorktree(branchName, fromBranch, runDir, stepNumber, repoDir) {
|
|
496
|
+
return this.worktreeManager.createAgentWorktree(branchName, fromBranch, runDir, stepNumber, repoDir);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Remove a git worktree after merge - delegates to WorktreeManager.
|
|
500
|
+
*/
|
|
501
|
+
async removeAgentWorktree(worktreePath) {
|
|
502
|
+
return this.worktreeManager.removeAgentWorktree(worktreePath);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Remove all remaining worktree directories under runs/<id>/worktrees/.
|
|
506
|
+
* mergeWaveBranches cleans up merged steps, but failed or unmerged steps
|
|
507
|
+
* leave worktrees containing test files that recursive test runners
|
|
508
|
+
* (node --test, Jest, etc.) discover during quality gates.
|
|
509
|
+
*/
|
|
510
|
+
async cleanupRemainingWorktrees(context) {
|
|
511
|
+
const worktreesDir = path.join(context.runDir, 'worktrees');
|
|
512
|
+
if (!fs.existsSync(worktreesDir))
|
|
513
|
+
return;
|
|
514
|
+
const entries = fs.readdirSync(worktreesDir, { withFileTypes: true });
|
|
515
|
+
const remainingDirs = entries.filter(e => e.isDirectory());
|
|
516
|
+
if (remainingDirs.length === 0)
|
|
517
|
+
return;
|
|
518
|
+
logger.info(`\n🧹 Cleaning up ${remainingDirs.length} remaining worktree(s) before quality gates...`);
|
|
519
|
+
for (const dir of remainingDirs) {
|
|
520
|
+
const worktreePath = path.join(worktreesDir, dir.name);
|
|
521
|
+
try {
|
|
522
|
+
await this.worktreeManager.removeAgentWorktree(worktreePath);
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
// Force-remove if git worktree remove fails (e.g., already detached)
|
|
526
|
+
try {
|
|
527
|
+
fs.rmSync(worktreePath, { recursive: true, force: true });
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
// Best-effort cleanup; log but don't block execution
|
|
531
|
+
logger.warn(` could not remove worktree ${dir.name}: ${err.message}`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Create a new git branch for an agent - delegates to WorktreeManager.
|
|
538
|
+
* Public to satisfy `ReplanHost`.
|
|
539
|
+
*/
|
|
540
|
+
async createAgentBranch(branchName, fromBranch) {
|
|
541
|
+
return this.worktreeManager.createAgentBranch(branchName, fromBranch);
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Merge completed branches to main. Delegates to BranchMerger with
|
|
545
|
+
* the appropriate context and tracks unmerged branches on the context.
|
|
546
|
+
* Public to satisfy `ReplanHost`.
|
|
547
|
+
*/
|
|
548
|
+
async mergeWaveBranches(completedResults, context, options) {
|
|
549
|
+
const mergeCtx = {
|
|
550
|
+
mainBranch: context.mainBranch,
|
|
551
|
+
contextBroker: context.contextBroker,
|
|
552
|
+
prManager: context.prManager,
|
|
553
|
+
};
|
|
554
|
+
const unmerged = await this.branchMerger.mergeWaveBranches(completedResults, mergeCtx, options?.prMode, context.prUrls, context.stepCostRecords, context.plan);
|
|
555
|
+
if (unmerged.length > 0) {
|
|
556
|
+
if (!context.unmergedBranches)
|
|
557
|
+
context.unmergedBranches = [];
|
|
558
|
+
context.unmergedBranches.push(...unmerged);
|
|
559
|
+
}
|
|
560
|
+
// Remove worktrees for merged steps so their leftover test files
|
|
561
|
+
// don't confuse recursive test runners (node --test, Jest, etc.)
|
|
562
|
+
const worktreesDir = path.join(context.runDir, 'worktrees');
|
|
563
|
+
for (const result of completedResults) {
|
|
564
|
+
if (result.branchName) {
|
|
565
|
+
const worktreePath = path.join(worktreesDir, `step-${result.stepNumber}`);
|
|
566
|
+
await this.worktreeManager.removeAgentWorktree(worktreePath);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Merge all agent branches back to main and clean up worktrees
|
|
572
|
+
*/
|
|
573
|
+
/**
|
|
574
|
+
* Merge all agent branches back to main - delegates to BranchMerger.
|
|
575
|
+
*/
|
|
576
|
+
async mergeAllBranches(context) {
|
|
577
|
+
const mergeCtx = {
|
|
578
|
+
mainBranch: context.mainBranch,
|
|
579
|
+
contextBroker: context.contextBroker,
|
|
580
|
+
prManager: context.prManager,
|
|
581
|
+
};
|
|
582
|
+
const unmerged = await this.branchMerger.mergeAllBranches(context.results, context.runDir, mergeCtx);
|
|
583
|
+
if (unmerged.length > 0) {
|
|
584
|
+
if (!context.unmergedBranches)
|
|
585
|
+
context.unmergedBranches = [];
|
|
586
|
+
context.unmergedBranches.push(...unmerged);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Detect whether agents introduced new dependencies and install them - delegates to git-state-utils.
|
|
591
|
+
*/
|
|
592
|
+
async installDependenciesIfNeeded() {
|
|
593
|
+
return (0, git_state_utils_1.installDependenciesIfNeeded)(this.workingDir);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Switch to a git branch - delegates to WorktreeManager.
|
|
597
|
+
* Public to satisfy `ReplanHost`.
|
|
598
|
+
*/
|
|
599
|
+
async switchBranch(branchName) {
|
|
600
|
+
return this.worktreeManager.switchBranch(branchName);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Merge a branch with conflict detection
|
|
604
|
+
*/
|
|
605
|
+
/**
|
|
606
|
+
* Wait for resume signal - delegates to PauseController.
|
|
607
|
+
*/
|
|
608
|
+
async waitForResume() {
|
|
609
|
+
return this.pauseController.waitForResume();
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Rebase a branch onto main and retry the merge - delegates to BranchMerger.
|
|
613
|
+
*/
|
|
614
|
+
tryRebaseAndMerge(branchName, context) {
|
|
615
|
+
const mergeCtx = {
|
|
616
|
+
mainBranch: context.mainBranch,
|
|
617
|
+
contextBroker: context.contextBroker,
|
|
618
|
+
prManager: context.prManager,
|
|
619
|
+
};
|
|
620
|
+
return this.branchMerger.tryRebaseAndMerge(branchName, mergeCtx);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Merge a single branch via git merge --no-ff - delegates to BranchMerger.
|
|
624
|
+
*/
|
|
625
|
+
async mergeBranch(branchName, context) {
|
|
626
|
+
const mergeCtx = {
|
|
627
|
+
mainBranch: context.mainBranch,
|
|
628
|
+
contextBroker: context.contextBroker,
|
|
629
|
+
prManager: context.prManager,
|
|
630
|
+
};
|
|
631
|
+
return this.branchMerger.mergeBranch(branchName, mergeCtx);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Get current git branch - delegates to WorktreeManager.
|
|
635
|
+
*/
|
|
636
|
+
getCurrentBranch() {
|
|
637
|
+
return this.worktreeManager.getCurrentBranch();
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Ensure repo has at least one commit - delegates to WorktreeManager.
|
|
641
|
+
*/
|
|
642
|
+
ensureInitialCommit() {
|
|
643
|
+
this.worktreeManager.ensureInitialCommit();
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Clean up leftover git state from crashed runs - delegates to git-state-utils.
|
|
647
|
+
*/
|
|
648
|
+
sanitizeGitState() {
|
|
649
|
+
(0, git_state_utils_1.sanitizeGitState)(this.workingDir);
|
|
650
|
+
}
|
|
651
|
+
generateExecutionId() {
|
|
652
|
+
return `swarm-${new Date().toISOString().replace(/[:.]/g, '-')}`;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
/** Analyze commit quality and flag anti-patterns. Delegates to commit-quality-analyzer module. */
|
|
656
|
+
async analyzeCommitQuality(commits, stepNumber, agentName, _context) {
|
|
657
|
+
return (0, commit_quality_analyzer_1.analyzeCommitQuality)(commits, stepNumber, agentName);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Write .copilot-instructions.md to the repo root with boilerplate every agent needs.
|
|
661
|
+
* Copilot CLI picks this up automatically, so per-step prompts stay minimal.
|
|
662
|
+
*/
|
|
663
|
+
writeSharedInstructions(baseline, tierInjection) {
|
|
664
|
+
(0, prompt_builder_1.writeSharedInstructions)(this.workingDir, baseline, tierInjection);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Run meta-analysis off the critical path - delegates to async-meta-analysis module.
|
|
668
|
+
*/
|
|
669
|
+
runAsyncMetaAnalysis(context, plan, runDir, completedSteps) {
|
|
670
|
+
(0, async_meta_analysis_1.runAsyncMetaAnalysis)(context, plan, runDir, completedSteps);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* execute optional deployment for devops_pro when --confirm-deploy is set
|
|
674
|
+
* captures preview URL and stores in context
|
|
675
|
+
*/
|
|
676
|
+
async executeOptionalDeployment(step, agent, context, options) {
|
|
677
|
+
await (0, deployment_handler_1.executeOptionalDeployment)(this.workingDir, step, agent, {
|
|
678
|
+
runDir: context.runDir,
|
|
679
|
+
executionId: context.executionId,
|
|
680
|
+
results: context.results,
|
|
681
|
+
deployments: context.deployments,
|
|
682
|
+
}, options);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
exports.SwarmOrchestrator = SwarmOrchestrator;
|
|
686
|
+
exports.default = SwarmOrchestrator;
|