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,258 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildRemediationStepForDelegate = buildRemediationStepForDelegate;
37
+ exports.runFinalGatesPipeline = runFinalGatesPipeline;
38
+ const path = __importStar(require("path"));
39
+ const quality_gates_1 = require("../quality-gates");
40
+ const registry_1 = require("../quality-gates/registry");
41
+ const logger_1 = require("../logger");
42
+ const logger = (0, logger_1.getLogger)('orchestrator');
43
+ /**
44
+ * Shared auto-remediation logic for quality gate failures. Returns a
45
+ * remediation step descriptor if the gate failed and auto-fix is both
46
+ * enabled and not already triggered; otherwise returns null. Includes
47
+ * specific gate findings in the task so the fix agent knows exactly
48
+ * what to address.
49
+ *
50
+ * Mutates `context.qualityGatesTriggered[triggeredFlag] = true` when a
51
+ * step is produced, to prevent re-adding the same remediation on a
52
+ * subsequent gate-failure pass.
53
+ *
54
+ * @param host - orchestrator surface (used for agent resolution)
55
+ * @param gateResult - the gate result being remediated; may be undefined if the gate didn't run
56
+ * @param configEnabled - whether the auto-remediation is enabled in the gate config
57
+ * @param triggeredFlag - key on `context.qualityGatesTriggered` to flip when this remediation fires
58
+ * @param context - mutable remediation context
59
+ * @param agents - available agent map for `resolveAgent`
60
+ * @param taskDescription - base task text for the remediation step
61
+ * @param warningMessage - log line emitted when the remediation is scheduled
62
+ * @param afterStep - dependency step number for the new remediation step
63
+ * @param fallbackAgent - agent to use when `integrator_finalizer` is not available
64
+ * @returns remediation step descriptor, or null when no remediation should fire
65
+ */
66
+ function buildRemediationStep(host, gateResult, configEnabled, triggeredFlag, context, agents, taskDescription, warningMessage, afterStep, fallbackAgent) {
67
+ if (!gateResult || gateResult.status !== 'fail')
68
+ return null;
69
+ if (!configEnabled)
70
+ return null;
71
+ if (!context.qualityGatesTriggered || context.qualityGatesTriggered[triggeredFlag])
72
+ return null;
73
+ const preferredAgent = host.resolveAgent(agents, 'integrator_finalizer')
74
+ ? 'integrator_finalizer'
75
+ : fallbackAgent;
76
+ logger.warn(warningMessage);
77
+ context.qualityGatesTriggered[triggeredFlag] = true;
78
+ // Append specific findings so the remediation agent knows exactly what to fix
79
+ let taskWithFindings = taskDescription;
80
+ if (gateResult.issues && gateResult.issues.length > 0) {
81
+ const findings = gateResult.issues.map(issue => {
82
+ let finding = `- ${issue.message}`;
83
+ if (issue.filePath)
84
+ finding += ` (${issue.filePath})`;
85
+ if (issue.hint)
86
+ finding += ` -- hint: ${issue.hint}`;
87
+ return finding;
88
+ }).join('\n');
89
+ taskWithFindings += `\n\nSpecific findings from the gate:\n${findings}`;
90
+ }
91
+ return { agent: preferredAgent, task: taskWithFindings, afterStep };
92
+ }
93
+ /**
94
+ * Exposes `buildRemediationStep` for the thin private delegate on
95
+ * `SwarmOrchestrator`. Tests access the class method via
96
+ * `(orch as any).buildRemediationStep(...)` so this wrapper preserves
97
+ * the exact call surface while keeping the module's internal helper
98
+ * unexported.
99
+ */
100
+ function buildRemediationStepForDelegate(host, gateResult, configEnabled, triggeredFlag, context, agents, taskDescription, warningMessage, afterStep, fallbackAgent) {
101
+ return buildRemediationStep(host, gateResult, configEnabled, triggeredFlag, context, agents, taskDescription, warningMessage, afterStep, fallbackAgent);
102
+ }
103
+ /**
104
+ * Run the final quality gates pass and, if the gates fail with
105
+ * auto-fixable findings, schedule remediation replan(s) until either
106
+ * the gates pass or the auto-fix budget is exhausted.
107
+ *
108
+ * Behavior mirrors the inline block previously at
109
+ * swarm-orchestrator.ts:700-907 exactly. Preserves the
110
+ * gate-replan-gate-replan-gate ordering. Mutates
111
+ * `context.finalGateResults` after each gate run and
112
+ * `context.qualityGatesTriggered` when remediation fires.
113
+ *
114
+ * @param host - orchestrator surface providing workingDir, targetMode,
115
+ * resolveAgent, and executeReplan
116
+ * @param context - mutable remediation context
117
+ * @param runDir - run directory used to resolve the gate output path
118
+ * @param agents - fallback agent map (used when `context.agents` is not set)
119
+ * @param gatesConfig - cached quality gates config
120
+ * @param options - orchestrator options (qualityGates, qualityGatesOutDir, plus pass-through for replan)
121
+ * @returns pipeline outcome including the final gate results, whether
122
+ * any remediation was attempted, and the gate output directory
123
+ */
124
+ async function runFinalGatesPipeline(host, context, runDir, agents, gatesConfig, options) {
125
+ // Live status presents this transition as a single in-place line; the
126
+ // logger entry is the forensic record visible with --verbose.
127
+ logger.debug('running final quality gates');
128
+ const presenter = require('../presenter');
129
+ presenter.getPresenter().gateRunningStart();
130
+ const gatesOut = options?.qualityGatesOutDir
131
+ ? path.isAbsolute(options.qualityGatesOutDir)
132
+ ? options.qualityGatesOutDir
133
+ : path.join(runDir, options.qualityGatesOutDir)
134
+ : path.join(runDir, 'quality-gates');
135
+ // Reuse cached gatesConfig from top of executeSwarm.
136
+ // Pass baseline so gates only flag issues on agent-created files,
137
+ // not pre-existing project code the agents weren't asked to change.
138
+ const baselineFiles = context.baselineSnapshot
139
+ ? new Set(context.baselineSnapshot.allFiles)
140
+ : undefined;
141
+ const baseCommit = context.baselineSnapshot?.headCommit || undefined;
142
+ const skippedReqIds = context.filteredRequirements
143
+ ? new Set(context.filteredRequirements.skipped.map(r => r.id))
144
+ : undefined;
145
+ // targetMode: skip orchestrator-internal self-improvement gates when
146
+ // we're operating on an external repo. Universal gates
147
+ // (hardcodedConfig, testFileProtection) continue to fire. See
148
+ // registry.ts for the classification rationale.
149
+ const skippedGateKeys = host.targetMode ? registry_1.SELF_IMPROVEMENT_GATE_KEYS : undefined;
150
+ let gatesResult = await (0, quality_gates_1.run_quality_gates)(host.workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds, skippedGateKeys);
151
+ context.finalGateResults = gatesResult.results;
152
+ let remediationAttempted = false;
153
+ if (!gatesResult.passed && gatesConfig.failOnIssues) {
154
+ const failedIds = new Set(gatesResult.results.filter(r => r.status === 'fail').map(r => r.id));
155
+ const agentMap = context.agents || agents;
156
+ const canAutoFix = !!context.qualityGatesTriggered && ((failedIds.has('duplicate-blocks') && gatesConfig.autoAddRefactorStepOnDuplicateBlocks && !context.qualityGatesTriggered.duplicateRefactorAdded) ||
157
+ (failedIds.has('readme-claims') && gatesConfig.autoAddReadmeTruthStepOnReadmeClaims && !context.qualityGatesTriggered.readmeTruthAdded) ||
158
+ (failedIds.has('scaffold-defaults') && gatesConfig.autoAddScaffoldFixStepOnScaffoldDefaults && !context.qualityGatesTriggered.scaffoldFixAdded) ||
159
+ (failedIds.has('hardcoded-config') && gatesConfig.autoAddConfigFixStepOnHardcodedConfig && !context.qualityGatesTriggered.configFixAdded) ||
160
+ (failedIds.has('accessibility') && gatesConfig.autoAddAccessibilityFixStepOnAccessibility && !context.qualityGatesTriggered.accessibilityFixAdded) ||
161
+ (failedIds.has('test-coverage') && gatesConfig.autoAddTestCoverageStepOnTestCoverage && !context.qualityGatesTriggered.testCoverageFixAdded));
162
+ if (canAutoFix) {
163
+ // Remediation steps should depend only on steps that actually completed.
164
+ // Using the max step number can create dependencies on failed/blocked steps,
165
+ // causing the remediation to wait forever then timeout.
166
+ const completedStepNumbers = context.results
167
+ .filter(r => r.status === 'completed')
168
+ .map(r => r.stepNumber);
169
+ const maxCompletedStep = completedStepNumbers.length > 0
170
+ ? Math.max(...completedStepNumbers)
171
+ : 0; // no dependency if nothing completed
172
+ const lastAgent = context.plan.steps[context.plan.steps.length - 1]?.agentName || 'integrator_finalizer';
173
+ const addSteps = [];
174
+ const dupStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'duplicate-blocks'), gatesConfig.autoAddRefactorStepOnDuplicateBlocks, 'duplicateRefactorAdded', context, agentMap, 'Quality gates flagged repeated code blocks. Extract shared utilities/hooks/middleware and refactor duplicates away. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: duplicate blocks detected; scheduling refactor', maxCompletedStep, lastAgent);
175
+ if (dupStep)
176
+ addSteps.push(dupStep);
177
+ const readmeStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'readme-claims'), gatesConfig.autoAddReadmeTruthStepOnReadmeClaims, 'readmeTruthAdded', context, agentMap, 'Quality gates flagged README claims that are not backed by code. Either implement the missing features or downgrade/remove the claims. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: README claims mismatch; scheduling truth step', maxCompletedStep, lastAgent);
178
+ if (readmeStep)
179
+ addSteps.push(readmeStep);
180
+ const scaffoldStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'scaffold-defaults'), gatesConfig.autoAddScaffoldFixStepOnScaffoldDefaults, 'scaffoldFixAdded', context, agentMap, 'Quality gates flagged scaffold defaults. Remove placeholder assets and generic scaffold README sections, and ensure HTML title/app metadata are meaningful. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: scaffold defaults detected; scheduling cleanup', maxCompletedStep, lastAgent);
181
+ if (scaffoldStep)
182
+ addSteps.push(scaffoldStep);
183
+ const configStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'hardcoded-config'), gatesConfig.autoAddConfigFixStepOnHardcodedConfig, 'configFixAdded', context, agentMap, 'Quality gates flagged hardcoded config values. Move API base URLs, ports, retry counts, timeouts, and environment-specific values into env/typed config. For Vite proxy targets, prefer import.meta.env with a safe default. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: hardcoded config detected; scheduling cleanup', maxCompletedStep, lastAgent);
184
+ if (configStep)
185
+ addSteps.push(configStep);
186
+ const a11yStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'accessibility'), gatesConfig.autoAddAccessibilityFixStepOnAccessibility, 'accessibilityFixAdded', context, agentMap, 'Quality gates flagged accessibility issues. Fix all items from the gate report: skip-to-content link, heading hierarchy, aria-labels, focus-visible styles, meta description + theme-color tags, responsive CSS (viewport meta, media queries or relative units), CSS custom properties on :root with prefers-color-scheme:dark override, semantic HTML landmarks (main, nav, header), img alt attributes. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: accessibility issues detected; scheduling fix', maxCompletedStep, lastAgent);
187
+ if (a11yStep)
188
+ addSteps.push(a11yStep);
189
+ const testCovStep = buildRemediationStep(host, gatesResult.results.find(r => r.id === 'test-coverage'), gatesConfig.autoAddTestCoverageStepOnTestCoverage, 'testCoverageFixAdded', context, agentMap, 'Quality gates flagged missing test coverage. Add tests for uncovered source files, ensure each test file contains real assertions, and add component-level tests for React projects. Use the gate report as the source of truth. Re-run tests and ensure quality gates pass.', '⚠️ Final quality gates: test coverage gaps detected; scheduling fix', maxCompletedStep, lastAgent);
190
+ if (testCovStep)
191
+ addSteps.push(testCovStep);
192
+ // Consolidate multiple gate failures into a single remediation step
193
+ // to avoid burning one premium request per gate failure.
194
+ if (addSteps.length > 1) {
195
+ const combinedTask = addSteps.map(s => s.task).join('\n\nALSO: ');
196
+ const singleStep = {
197
+ agent: addSteps[0].agent,
198
+ task: combinedTask,
199
+ };
200
+ if (addSteps[0].afterStep !== undefined) {
201
+ singleStep.afterStep = addSteps[0].afterStep;
202
+ }
203
+ addSteps.length = 0;
204
+ addSteps.push(singleStep);
205
+ logger.warn(`⚠️ Final quality gates failed (${failedIds.size} gates); scheduling single consolidated remediation step...`);
206
+ }
207
+ else if (addSteps.length === 1) {
208
+ logger.warn('⚠️ Final quality gates failed; attempting one remediation pass...');
209
+ }
210
+ if (addSteps.length > 0) {
211
+ remediationAttempted = true;
212
+ await host.executeReplan(context, { retrySteps: [], addSteps }, agentMap, options);
213
+ gatesResult = await (0, quality_gates_1.run_quality_gates)(host.workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds, skippedGateKeys);
214
+ context.finalGateResults = gatesResult.results;
215
+ // Second remediation attempt if gates still fail after first fix
216
+ if (!gatesResult.passed && gatesConfig.failOnIssues) {
217
+ const stillFailed = gatesResult.results.filter(r => r.status === 'fail');
218
+ if (stillFailed.length > 0) {
219
+ const findings = stillFailed.flatMap(gate => gate.issues.map(issue => {
220
+ let desc = `[${gate.id}] ${issue.message}`;
221
+ if (issue.filePath)
222
+ desc += ` (${issue.filePath})`;
223
+ if (issue.hint)
224
+ desc += ` -- hint: ${issue.hint}`;
225
+ return desc;
226
+ })).join('\n');
227
+ const retryTask = [
228
+ 'The previous remediation attempt did not fully resolve the quality gate failures.',
229
+ 'The following issues remain. Fix each one specifically:',
230
+ '',
231
+ findings,
232
+ '',
233
+ 'Verify your fixes by checking that the specific issues listed above are resolved.',
234
+ 'Run tests to ensure nothing is broken.',
235
+ ].join('\n');
236
+ const retryAgent = host.resolveAgent(agentMap, 'integrator_finalizer')
237
+ ? 'integrator_finalizer'
238
+ : lastAgent;
239
+ const retryMaxStep = Math.max(...context.plan.steps.map(s => s.stepNumber));
240
+ logger.warn('⚠️ First remediation did not resolve all gate failures. Running second attempt with specific findings...');
241
+ await host.executeReplan(context, {
242
+ retrySteps: [],
243
+ addSteps: [{ agent: retryAgent, task: retryTask, afterStep: retryMaxStep }]
244
+ }, agentMap, options);
245
+ gatesResult = await (0, quality_gates_1.run_quality_gates)(host.workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds, skippedGateKeys);
246
+ context.finalGateResults = gatesResult.results;
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+ return {
253
+ finalGateResults: gatesResult.results,
254
+ remediationAttempted,
255
+ passed: gatesResult.passed,
256
+ gatesOut,
257
+ };
258
+ }
@@ -0,0 +1,119 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.sanitizeGitState = sanitizeGitState;
37
+ exports.installDependenciesIfNeeded = installDependenciesIfNeeded;
38
+ const child_process_1 = require("child_process");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const logger_1 = require("../logger");
42
+ const logger = (0, logger_1.getLogger)('orchestrator');
43
+ /**
44
+ * Clean up leftover git state from crashed runs: abort pending merges,
45
+ * reset staged/unmerged index entries, and restore working tree files.
46
+ * Prevents cascading failures when binary files (e.g. .pyc, .db) from
47
+ * a previous merge conflict block branch creation or verification commits.
48
+ *
49
+ * @param workingDir - repository root where the git operations run
50
+ */
51
+ function sanitizeGitState(workingDir) {
52
+ const opts = { cwd: workingDir, stdio: 'pipe', encoding: 'utf8' };
53
+ try {
54
+ (0, child_process_1.execSync)('git merge --abort', opts);
55
+ logger.info(' [cleanup] Aborted in-progress merge from previous run');
56
+ }
57
+ catch { /* no merge in progress; expected */ }
58
+ // Check for unmerged or staged entries that would block new operations
59
+ try {
60
+ const status = (0, child_process_1.execSync)('git status --porcelain', opts).trim();
61
+ const hasUnmerged = status.split('\n').some(line => line.startsWith('U') || line.startsWith('AA') || line.startsWith('DD'));
62
+ if (hasUnmerged) {
63
+ (0, child_process_1.execSync)('git reset HEAD', opts);
64
+ (0, child_process_1.execSync)('git checkout -- .', opts);
65
+ logger.info(' [cleanup] Reset unmerged files from previous crashed run');
66
+ }
67
+ }
68
+ catch { /* status check failed; not critical */ }
69
+ // Prune stale worktrees left by previous crashes
70
+ try {
71
+ (0, child_process_1.execSync)('git worktree prune', opts);
72
+ }
73
+ catch { /* prune failed; not critical */ }
74
+ }
75
+ /**
76
+ * Detect whether agents introduced new dependencies and install them.
77
+ * Runs after all branches are merged but before quality gates so that
78
+ * `npm test` has access to any newly-added packages.
79
+ *
80
+ * @param workingDir - repository root with package.json and node_modules
81
+ */
82
+ async function installDependenciesIfNeeded(workingDir) {
83
+ const pkgPath = path.join(workingDir, 'package.json');
84
+ const nodeModulesPath = path.join(workingDir, 'node_modules');
85
+ if (!fs.existsSync(pkgPath))
86
+ return;
87
+ try {
88
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
89
+ const allDeps = {
90
+ ...pkg.dependencies,
91
+ ...pkg.devDependencies,
92
+ };
93
+ if (Object.keys(allDeps).length === 0)
94
+ return;
95
+ // Check if any declared dependency is missing from node_modules
96
+ const missing = Object.keys(allDeps).filter(dep => {
97
+ return !fs.existsSync(path.join(nodeModulesPath, dep));
98
+ });
99
+ if (missing.length === 0)
100
+ return;
101
+ logger.info(`\n📦 Installing ${missing.length} new dependenc${missing.length === 1 ? 'y' : 'ies'}: ${missing.join(', ')}`);
102
+ // Use the right package manager for the project
103
+ const installCmd = fs.existsSync(path.join(workingDir, 'yarn.lock'))
104
+ ? 'yarn install --frozen-lockfile 2>/dev/null || yarn install'
105
+ : fs.existsSync(path.join(workingDir, 'pnpm-lock.yaml'))
106
+ ? 'pnpm install --no-frozen-lockfile'
107
+ : 'npm install --loglevel=error';
108
+ (0, child_process_1.execSync)(installCmd, {
109
+ cwd: workingDir,
110
+ stdio: ['pipe', 'pipe', 'pipe'],
111
+ timeout: 120_000,
112
+ });
113
+ logger.info(' ✅ Dependencies installed');
114
+ }
115
+ catch (err) {
116
+ const msg = err instanceof Error ? err.message : String(err);
117
+ logger.warn(` ⚠️ Dependency install failed (quality gates may report test failures): ${msg}`);
118
+ }
119
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PauseController = void 0;
4
+ /**
5
+ * Owns the pause/resume state that `SwarmOrchestrator` uses to suspend
6
+ * the scheduling loop mid-execution. Extracted from swarm-orchestrator.ts
7
+ * so the orchestrator class only forwards to an isolated controller.
8
+ *
9
+ * Two flags coordinate the handshake:
10
+ * - `pauseRequested` signals the scheduler to stop launching new steps
11
+ * the next time the loop checks.
12
+ * - `resumeRequested` is set by `requestResume` and consumed by
13
+ * `waitForResume` to release the pause.
14
+ */
15
+ class PauseController {
16
+ pauseRequested = false;
17
+ resumeRequested = false;
18
+ /**
19
+ * Request pause of current execution. The scheduler observes the flag
20
+ * at the top of its loop and suspends launching further steps.
21
+ */
22
+ requestPause() {
23
+ this.pauseRequested = true;
24
+ }
25
+ /**
26
+ * Request resume of paused execution. Clears the pause flag and signals
27
+ * `waitForResume` to return.
28
+ */
29
+ requestResume() {
30
+ this.resumeRequested = true;
31
+ this.pauseRequested = false;
32
+ }
33
+ /**
34
+ * @returns `true` when a pause has been requested and not yet resumed.
35
+ */
36
+ isPauseRequested() {
37
+ return this.pauseRequested;
38
+ }
39
+ /**
40
+ * Block until `requestResume` fires or the pause is otherwise cleared.
41
+ * Polls every 500ms, matching the original inline implementation.
42
+ */
43
+ async waitForResume() {
44
+ return new Promise((resolve) => {
45
+ const checkInterval = setInterval(() => {
46
+ if (this.resumeRequested || !this.pauseRequested) {
47
+ clearInterval(checkInterval);
48
+ this.resumeRequested = false;
49
+ resolve();
50
+ }
51
+ }, 500); // Check every 500ms
52
+ });
53
+ }
54
+ }
55
+ exports.PauseController = PauseController;