swarm-orchestrator 7.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +199 -0
  3. package/agents/reviewer.agent.md +50 -0
  4. package/agents/worker.agent.md +41 -0
  5. package/config/default-agents.yaml +56 -0
  6. package/config/pm-agent.yaml +36 -0
  7. package/config/quality-gates.yaml +88 -0
  8. package/config/repair-agent.yaml +34 -0
  9. package/config/semgrep-rules/complexity-mismatch.yaml +6 -0
  10. package/config/semgrep-rules/exception-swallowing.yaml +13 -0
  11. package/config/semgrep-rules/hardcoded-answer.yaml +6 -0
  12. package/config/semgrep-rules/mock-mutation.yaml +6 -0
  13. package/config/semgrep-rules/test-modification.yaml +6 -0
  14. package/config/user-agents.yaml +23 -0
  15. package/dist/src/adapters/adapter-factory.js +29 -0
  16. package/dist/src/adapters/agent-adapter.js +32 -0
  17. package/dist/src/adapters/claude-code-adapter.js +126 -0
  18. package/dist/src/adapters/claude-code-teams.js +119 -0
  19. package/dist/src/adapters/codex-adapter.js +118 -0
  20. package/dist/src/adapters/copilot-adapter.js +224 -0
  21. package/dist/src/adapters/fatal-error-classifier.js +88 -0
  22. package/dist/src/adapters/index.js +44 -0
  23. package/dist/src/adapters/persistent-session.js +204 -0
  24. package/dist/src/adapters/process-supervisor.js +199 -0
  25. package/dist/src/agents-exporter.js +398 -0
  26. package/dist/src/analytics-log.js +161 -0
  27. package/dist/src/baseline-scanner.js +65 -0
  28. package/dist/src/bootstrap-evidence.js +88 -0
  29. package/dist/src/bootstrap-orchestrator.js +205 -0
  30. package/dist/src/bootstrap-types.js +5 -0
  31. package/dist/src/branch-merger.js +427 -0
  32. package/dist/src/cli/attest-handlers.js +44 -0
  33. package/dist/src/cli/cost-prompt.js +51 -0
  34. package/dist/src/cli/demo-handlers.js +274 -0
  35. package/dist/src/cli/flags.js +152 -0
  36. package/dist/src/cli/index.js +57 -0
  37. package/dist/src/cli/live-status.js +186 -0
  38. package/dist/src/cli/misc-handlers.js +242 -0
  39. package/dist/src/cli/plan-handlers.js +350 -0
  40. package/dist/src/cli/share-handlers.js +163 -0
  41. package/dist/src/cli/status-handlers.js +457 -0
  42. package/dist/src/cli/swarm-handlers.js +638 -0
  43. package/dist/src/cli/usage.js +72 -0
  44. package/dist/src/cli.js +217 -0
  45. package/dist/src/commit-pattern-detector.js +143 -0
  46. package/dist/src/commit-quality-analyzer.js +38 -0
  47. package/dist/src/config-loader.js +339 -0
  48. package/dist/src/context-broker.js +358 -0
  49. package/dist/src/copilot-cli-wrapper.js +258 -0
  50. package/dist/src/cost-estimator.js +241 -0
  51. package/dist/src/defaults.js +18 -0
  52. package/dist/src/demo-mode.js +180 -0
  53. package/dist/src/deployment-handler.js +78 -0
  54. package/dist/src/deployment-manager.js +228 -0
  55. package/dist/src/execution-queue.js +196 -0
  56. package/dist/src/external-tool-manager.js +184 -0
  57. package/dist/src/gate-prompt-builder.js +117 -0
  58. package/dist/src/gate-remediation.js +173 -0
  59. package/dist/src/github-issues-ingester.js +152 -0
  60. package/dist/src/github-mcp-integrator.js +164 -0
  61. package/dist/src/hook-generator.js +229 -0
  62. package/dist/src/index.js +48 -0
  63. package/dist/src/knowledge-base.js +300 -0
  64. package/dist/src/logger.js +111 -0
  65. package/dist/src/meta-analyzer.js +208 -0
  66. package/dist/src/metrics-collector.js +209 -0
  67. package/dist/src/metrics-types.js +5 -0
  68. package/dist/src/multi-repo-coordinator.js +179 -0
  69. package/dist/src/orchestrator/async-meta-analysis.js +84 -0
  70. package/dist/src/orchestrator/fatal-run-error.js +43 -0
  71. package/dist/src/orchestrator/final-gates-remediation.js +258 -0
  72. package/dist/src/orchestrator/git-state-utils.js +119 -0
  73. package/dist/src/orchestrator/pause-controller.js +55 -0
  74. package/dist/src/orchestrator/replan-runner.js +258 -0
  75. package/dist/src/orchestrator/step-executor.js +391 -0
  76. package/dist/src/orchestrator/wave-scheduler-loop.js +204 -0
  77. package/dist/src/owasp-mapper.js +153 -0
  78. package/dist/src/owasp-report-renderer.js +39 -0
  79. package/dist/src/plan-files.js +142 -0
  80. package/dist/src/plan-generator.js +1207 -0
  81. package/dist/src/pm-agent.js +296 -0
  82. package/dist/src/post-run-reporter.js +203 -0
  83. package/dist/src/pr-automation.js +103 -0
  84. package/dist/src/pr-manager.js +318 -0
  85. package/dist/src/presenter/index.js +209 -0
  86. package/dist/src/prompt-builder.js +173 -0
  87. package/dist/src/quality-gates/config-loader.js +196 -0
  88. package/dist/src/quality-gates/default-config.js +243 -0
  89. package/dist/src/quality-gates/file-utils.js +152 -0
  90. package/dist/src/quality-gates/gate-runner.js +171 -0
  91. package/dist/src/quality-gates/gates/accessibility.js +355 -0
  92. package/dist/src/quality-gates/gates/duplicate-blocks.js +144 -0
  93. package/dist/src/quality-gates/gates/hardcoded-config.js +52 -0
  94. package/dist/src/quality-gates/gates/readme-claims.js +62 -0
  95. package/dist/src/quality-gates/gates/runtime-checks.js +265 -0
  96. package/dist/src/quality-gates/gates/scaffold-defaults.js +108 -0
  97. package/dist/src/quality-gates/gates/test-coverage.js +240 -0
  98. package/dist/src/quality-gates/gates/test-file-protection.js +85 -0
  99. package/dist/src/quality-gates/gates/test-isolation.js +74 -0
  100. package/dist/src/quality-gates/index.js +17 -0
  101. package/dist/src/quality-gates/registry.js +205 -0
  102. package/dist/src/quality-gates/types.js +2 -0
  103. package/dist/src/quick-fix-mode.js +302 -0
  104. package/dist/src/recipe-loader.js +122 -0
  105. package/dist/src/repair-agent.js +396 -0
  106. package/dist/src/repo-analyzer.js +342 -0
  107. package/dist/src/report-generator.js +146 -0
  108. package/dist/src/report-renderer.js +90 -0
  109. package/dist/src/requirement-filter.js +52 -0
  110. package/dist/src/sarif-formatter.js +140 -0
  111. package/dist/src/scheduling/dependency-analyzer.js +178 -0
  112. package/dist/src/scheduling/work-stealing-queue.js +123 -0
  113. package/dist/src/secret-redactor.js +90 -0
  114. package/dist/src/session-executor.js +587 -0
  115. package/dist/src/session-manager.js +221 -0
  116. package/dist/src/share/transcript-verification.js +128 -0
  117. package/dist/src/share-parser.js +477 -0
  118. package/dist/src/spinner.js +146 -0
  119. package/dist/src/step-runner.js +365 -0
  120. package/dist/src/swarm-orchestrator.js +686 -0
  121. package/dist/src/task-classifier.js +134 -0
  122. package/dist/src/test-command-discovery.js +123 -0
  123. package/dist/src/text-similarity.js +24 -0
  124. package/dist/src/tier-maps.js +158 -0
  125. package/dist/src/types.js +5 -0
  126. package/dist/src/url-shortener.js +135 -0
  127. package/dist/src/verification/attestation.js +166 -0
  128. package/dist/src/verification/cheat-detector.js +240 -0
  129. package/dist/src/verification/command-runner.js +70 -0
  130. package/dist/src/verification/composite-score.js +157 -0
  131. package/dist/src/verification/cosign-attestation.js +137 -0
  132. package/dist/src/verification/diff-analysis.js +89 -0
  133. package/dist/src/verification/differential-gate.js +173 -0
  134. package/dist/src/verification/index.js +41 -0
  135. package/dist/src/verification/mutation-gate.js +242 -0
  136. package/dist/src/verification/property-gate.js +201 -0
  137. package/dist/src/verification/test-synthesizer.js +243 -0
  138. package/dist/src/verifier/outcome-checks.js +396 -0
  139. package/dist/src/verifier/transcript-checks.js +207 -0
  140. package/dist/src/verifier/verification-reporters.js +148 -0
  141. package/dist/src/verifier-engine.js +491 -0
  142. package/dist/src/wave-resizer.js +131 -0
  143. package/dist/src/wave-scheduler.js +44 -0
  144. package/dist/src/worktree-manager.js +368 -0
  145. package/dist/src/worktree-reserved-paths.js +97 -0
  146. package/package.json +80 -0
  147. package/plugin/agents/.gitkeep +0 -0
  148. package/plugin/agents/backend_master.agent.md +72 -0
  149. package/plugin/agents/devops_pro.agent.md +45 -0
  150. package/plugin/agents/frontend_expert.agent.md +59 -0
  151. package/plugin/agents/integrator_finalizer.agent.md +44 -0
  152. package/plugin/agents/meta_reviewer.agent.md +44 -0
  153. package/plugin/agents/security_auditor.agent.md +43 -0
  154. package/plugin/agents/tester_elite.agent.md +43 -0
  155. package/plugin/hooks/hooks.json +19 -0
  156. package/plugin/hooks/post-tool-use.json +9 -0
  157. package/plugin/hooks/pre-tool-use.json +9 -0
  158. package/plugin/plugin.json +47 -0
  159. package/plugin/skills/swarm-gates/SKILL.md +53 -0
  160. package/plugin/skills/swarm-orchestrate/SKILL.md +34 -0
  161. package/plugin/skills/swarm-verify/SKILL.md +44 -0
  162. package/templates/README.md +35 -0
  163. package/templates/cli-tool.json +31 -0
  164. package/templates/fullstack.json +52 -0
  165. package/templates/library.json +38 -0
  166. package/templates/react-app.json +45 -0
  167. package/templates/recipes/add-api-docs.json +35 -0
  168. package/templates/recipes/add-auth.json +47 -0
  169. package/templates/recipes/add-ci.json +39 -0
  170. package/templates/recipes/add-tests.json +39 -0
  171. package/templates/recipes/migrate-to-ts.json +42 -0
  172. package/templates/recipes/refactor-modularize.json +44 -0
  173. package/templates/recipes/security-audit.json +35 -0
  174. package/templates/rest-api.json +38 -0
@@ -0,0 +1,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;