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,184 @@
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.ExternalToolManager = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
40
+ const logger_1 = require("./logger");
41
+ const logger = (0, logger_1.getLogger)('external-tool-manager');
42
+ /**
43
+ * External Tool Manager - handles external CLI tool execution with safety gating
44
+ * All external commands require explicit user confirmation via enableExternal flag
45
+ */
46
+ class ExternalToolManager {
47
+ config;
48
+ executionLog = [];
49
+ toolAvailability;
50
+ constructor(config) {
51
+ this.config = config;
52
+ }
53
+ /**
54
+ * Detect which external tools are available
55
+ */
56
+ async detectAvailableTools() {
57
+ if (this.toolAvailability) {
58
+ return this.toolAvailability;
59
+ }
60
+ const availability = {
61
+ gh: await this.isToolAvailable('gh'),
62
+ vercel: await this.isToolAvailable('vercel'),
63
+ netlify: await this.isToolAvailable('netlify')
64
+ };
65
+ this.toolAvailability = availability;
66
+ return availability;
67
+ }
68
+ /**
69
+ * Check if a specific tool is available
70
+ */
71
+ async isToolAvailable(toolName) {
72
+ return new Promise((resolve) => {
73
+ const proc = (0, child_process_1.spawn)('which', [toolName]);
74
+ proc.on('close', (code) => {
75
+ resolve(code === 0);
76
+ });
77
+ });
78
+ }
79
+ /**
80
+ * Execute external command with safety gating
81
+ */
82
+ async executeCommand(command, args, options = {}) {
83
+ const execution = {
84
+ command,
85
+ args: this.sanitizeArgs(args), // Remove any potential secrets
86
+ workingDir: options.workingDir || process.cwd(),
87
+ timestamp: new Date().toISOString()
88
+ };
89
+ // Check tool availability if required
90
+ if (options.requireTool) {
91
+ const tools = await this.detectAvailableTools();
92
+ if (!tools[options.requireTool]) {
93
+ execution.exitCode = -1;
94
+ execution.error = `Tool '${options.requireTool}' not available`;
95
+ this.logExecution(execution);
96
+ return execution;
97
+ }
98
+ }
99
+ // Dry run mode: log but don't execute
100
+ if (this.config.dryRun) {
101
+ logger.info(`[DRY RUN] Would execute: ${command} ${args.join(' ')}`);
102
+ logger.info(` Working dir: ${execution.workingDir}`);
103
+ execution.exitCode = 0;
104
+ execution.output = '[DRY RUN - not executed]';
105
+ this.logExecution(execution);
106
+ return execution;
107
+ }
108
+ // Safety gate: require explicit external permission
109
+ if (!this.config.enableExternal) {
110
+ execution.exitCode = -1;
111
+ execution.error = 'External tool execution disabled. Use --enable-external flag.';
112
+ this.logExecution(execution);
113
+ return execution;
114
+ }
115
+ // Execute command
116
+ const startTime = Date.now();
117
+ return new Promise((resolve) => {
118
+ const proc = (0, child_process_1.spawn)(command, args, {
119
+ cwd: execution.workingDir,
120
+ stdio: ['pipe', 'pipe', 'pipe']
121
+ });
122
+ let stdout = '';
123
+ let stderr = '';
124
+ proc.stdout.on('data', (data) => {
125
+ stdout += data.toString();
126
+ });
127
+ proc.stderr.on('data', (data) => {
128
+ stderr += data.toString();
129
+ });
130
+ proc.on('close', (code) => {
131
+ execution.exitCode = code || 0;
132
+ execution.output = stdout;
133
+ if (stderr)
134
+ execution.error = stderr;
135
+ execution.duration = Date.now() - startTime;
136
+ this.logExecution(execution);
137
+ resolve(execution);
138
+ });
139
+ proc.on('error', (err) => {
140
+ execution.exitCode = -1;
141
+ execution.error = err.message;
142
+ execution.duration = Date.now() - startTime;
143
+ this.logExecution(execution);
144
+ resolve(execution);
145
+ });
146
+ });
147
+ }
148
+ /**
149
+ * Sanitize command arguments to remove secrets
150
+ */
151
+ sanitizeArgs(args) {
152
+ return args.map(arg => {
153
+ // Hide tokens and keys
154
+ if (arg.includes('token=') || arg.includes('key=') || arg.includes('secret=')) {
155
+ const parts = arg.split('=');
156
+ return `${parts[0]}=[REDACTED]`;
157
+ }
158
+ return arg;
159
+ });
160
+ }
161
+ /**
162
+ * Log command execution
163
+ */
164
+ logExecution(execution) {
165
+ this.executionLog.push(execution);
166
+ // Write to log file if specified
167
+ if (this.config.logFile) {
168
+ const logDir = path.dirname(this.config.logFile);
169
+ if (!fs.existsSync(logDir)) {
170
+ fs.mkdirSync(logDir, { recursive: true });
171
+ }
172
+ const logEntry = JSON.stringify(execution, null, 2) + '\n';
173
+ fs.appendFileSync(this.config.logFile, logEntry);
174
+ }
175
+ }
176
+ /**
177
+ * Get execution log
178
+ */
179
+ getExecutionLog() {
180
+ return [...this.executionLog];
181
+ }
182
+ }
183
+ exports.ExternalToolManager = ExternalToolManager;
184
+ exports.default = ExternalToolManager;
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyStep = classifyStep;
4
+ exports.buildGateClauses = buildGateClauses;
5
+ exports.getGateRequirements = getGateRequirements;
6
+ exports.requiresTestStep = requiresTestStep;
7
+ /** Agent names that indicate frontend-facing work. */
8
+ const FRONTEND_AGENTS = new Set();
9
+ /** Agent names that indicate test-writing work. */
10
+ const TEST_AGENTS = new Set();
11
+ /** Agent names that indicate documentation/review work. */
12
+ const DOC_AGENTS = new Set(['reviewer']);
13
+ /**
14
+ * Classify a step by its agent name to determine which gate requirements apply.
15
+ * @param agentName - The agent assigned to the step
16
+ * @returns The step category
17
+ */
18
+ function classifyStep(agentName) {
19
+ if (TEST_AGENTS.has(agentName))
20
+ return 'test';
21
+ if (FRONTEND_AGENTS.has(agentName))
22
+ return 'frontend';
23
+ if (DOC_AGENTS.has(agentName))
24
+ return 'documentation';
25
+ return 'code-generation';
26
+ }
27
+ /**
28
+ * Build gate-aware prompt clauses from resolved quality gate configuration.
29
+ * Returns an array of clauses, each with its applicable step categories.
30
+ * Only produces clauses for gates that are enabled.
31
+ * @param config - Resolved quality gates configuration
32
+ * @returns Array of gate-derived prompt clauses
33
+ */
34
+ function buildGateClauses(config) {
35
+ if (!config.enabled)
36
+ return [];
37
+ const clauses = [];
38
+ const gates = config.gates;
39
+ if (gates.scaffoldDefaults.enabled) {
40
+ clauses.push({
41
+ appliesTo: ['code-generation', 'test', 'frontend', 'documentation', 'all'],
42
+ text: 'Remove all TODO comments, placeholder text, and default scaffold values before completing.',
43
+ });
44
+ }
45
+ if (gates.duplicateBlocks.enabled) {
46
+ clauses.push({
47
+ appliesTo: ['code-generation', 'frontend'],
48
+ text: 'Avoid duplicating code blocks. Extract shared logic into utility functions.',
49
+ });
50
+ }
51
+ if (gates.hardcodedConfig.enabled) {
52
+ clauses.push({
53
+ appliesTo: ['code-generation', 'frontend'],
54
+ text: 'Use environment variables for configuration values. No hardcoded URLs, ports, credentials, or API keys.',
55
+ });
56
+ }
57
+ if (gates.testCoverage.enabled) {
58
+ clauses.push({
59
+ appliesTo: ['test'],
60
+ text: `Achieve thorough test coverage. Test every exported function and major code path.`,
61
+ });
62
+ }
63
+ if (gates.testIsolation.enabled) {
64
+ clauses.push({
65
+ appliesTo: ['test'],
66
+ text: 'Each test must be independently runnable. No shared mutable state between tests.',
67
+ });
68
+ }
69
+ if (gates.accessibility.enabled) {
70
+ clauses.push({
71
+ appliesTo: ['frontend'],
72
+ text: 'Include ARIA attributes, keyboard navigation, and semantic HTML.',
73
+ });
74
+ }
75
+ if (gates.readmeClaims.enabled) {
76
+ clauses.push({
77
+ appliesTo: ['documentation'],
78
+ text: 'README claims must match actual implemented functionality.',
79
+ });
80
+ }
81
+ if (gates.runtimeChecks.enabled) {
82
+ clauses.push({
83
+ appliesTo: ['code-generation', 'test', 'frontend', 'documentation', 'all'],
84
+ text: 'Code must build and pass all tests before completion.',
85
+ });
86
+ }
87
+ return clauses;
88
+ }
89
+ /**
90
+ * Get requirement lines to append to a step's prompt based on gate config.
91
+ * Filters clauses to those applicable to the step's category, then formats
92
+ * them as a bulleted list. Returns empty string if no clauses apply.
93
+ * @param config - Resolved quality gates configuration
94
+ * @param agentName - Agent assigned to the step (used for classification)
95
+ * @returns Formatted requirement text to append to the step prompt, or empty string
96
+ */
97
+ function getGateRequirements(config, agentName) {
98
+ const clauses = buildGateClauses(config);
99
+ if (clauses.length === 0)
100
+ return '';
101
+ const category = classifyStep(agentName);
102
+ const applicable = clauses.filter(c => c.appliesTo.includes(category) || c.appliesTo.includes('all'));
103
+ if (applicable.length === 0)
104
+ return '';
105
+ const lines = applicable.map(c => `- ${c.text}`).join('\n');
106
+ return `\n\nQuality gate requirements:\n${lines}`;
107
+ }
108
+ /**
109
+ * Check whether the gate config indicates a need for an explicit test step.
110
+ * Returns true when testCoverage is enabled, which signals the planner
111
+ * should verify a worker test step exists in the plan.
112
+ * @param config - Resolved quality gates configuration
113
+ * @returns Whether a test step should be ensured
114
+ */
115
+ function requiresTestStep(config) {
116
+ return config.enabled && config.gates.testCoverage.enabled;
117
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildRemediationStep = buildRemediationStep;
4
+ exports.runQualityGateRemediation = runQualityGateRemediation;
5
+ const logger_1 = require("./logger");
6
+ const quality_gates_1 = require("./quality-gates");
7
+ const logger = (0, logger_1.getLogger)('gate-remediation');
8
+ // ---------------------------------------------------------------------------
9
+ // buildRemediationStep
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Inspect a single gate result and, if it failed and auto-fix is enabled
13
+ * (and hasn't already been attempted), return a remediation step descriptor.
14
+ * Returns `null` when no action is needed.
15
+ */
16
+ function buildRemediationStep(gateResult, configEnabled, triggeredFlag, triggeredFlags, agents, resolveAgent, taskDescription, warningMessage, afterStep, fallbackAgent) {
17
+ if (!gateResult || gateResult.status !== 'fail')
18
+ return null;
19
+ if (!configEnabled)
20
+ return null;
21
+ if (triggeredFlags[triggeredFlag])
22
+ return null;
23
+ const preferredAgent = resolveAgent(agents, 'integrator_finalizer')
24
+ ? 'integrator_finalizer'
25
+ : fallbackAgent;
26
+ logger.warn(warningMessage);
27
+ triggeredFlags[triggeredFlag] = true;
28
+ // Append specific findings so the remediation agent knows exactly what to fix
29
+ let taskWithFindings = taskDescription;
30
+ if (gateResult.issues && gateResult.issues.length > 0) {
31
+ const findings = gateResult.issues.map(issue => {
32
+ let finding = `- ${issue.message}`;
33
+ if (issue.filePath)
34
+ finding += ` (${issue.filePath})`;
35
+ if (issue.hint)
36
+ finding += ` -- hint: ${issue.hint}`;
37
+ return finding;
38
+ }).join('\n');
39
+ taskWithFindings += `\n\nSpecific findings from the gate:\n${findings}`;
40
+ }
41
+ return { agent: preferredAgent, task: taskWithFindings, afterStep };
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // runQualityGateRemediation
45
+ // ---------------------------------------------------------------------------
46
+ /**
47
+ * Run quality gates, check if auto-fix is possible, build remediation steps,
48
+ * consolidate them into a single step if multiple are needed, call
49
+ * `executeReplan`, re-run gates, and if still failing do a second attempt
50
+ * with specific findings.
51
+ *
52
+ * Returns whether the final gate run passed and the latest gate results.
53
+ */
54
+ async function runQualityGateRemediation(params) {
55
+ const { workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds, context, agents, options, resolveAgent, executeReplan, } = params;
56
+ // Initial quality-gate run
57
+ let gatesResult = await (0, quality_gates_1.run_quality_gates)(workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds);
58
+ context.finalGateResults = gatesResult.results;
59
+ if (!gatesResult.passed && gatesConfig.failOnIssues) {
60
+ const failedIds = new Set(gatesResult.results.filter(r => r.status === 'fail').map(r => r.id));
61
+ let remediationAttempted = false;
62
+ const triggeredFlags = context.qualityGatesTriggered || {};
63
+ const canAutoFix = !!context.qualityGatesTriggered && ((failedIds.has('duplicate-blocks') && gatesConfig.autoAddRefactorStepOnDuplicateBlocks && !triggeredFlags.duplicateRefactorAdded) ||
64
+ (failedIds.has('readme-claims') && gatesConfig.autoAddReadmeTruthStepOnReadmeClaims && !triggeredFlags.readmeTruthAdded) ||
65
+ (failedIds.has('scaffold-defaults') && gatesConfig.autoAddScaffoldFixStepOnScaffoldDefaults && !triggeredFlags.scaffoldFixAdded) ||
66
+ (failedIds.has('hardcoded-config') && gatesConfig.autoAddConfigFixStepOnHardcodedConfig && !triggeredFlags.configFixAdded) ||
67
+ (failedIds.has('accessibility') && gatesConfig.autoAddAccessibilityFixStepOnAccessibility && !triggeredFlags.accessibilityFixAdded) ||
68
+ (failedIds.has('test-coverage') && gatesConfig.autoAddTestCoverageStepOnTestCoverage && !triggeredFlags.testCoverageFixAdded));
69
+ if (canAutoFix) {
70
+ // Remediation steps should depend only on steps that actually completed.
71
+ // Using the max step number can create dependencies on failed/blocked steps,
72
+ // causing the remediation to wait forever then timeout.
73
+ const completedStepNumbers = context.results
74
+ .filter(r => r.status === 'completed')
75
+ .map(r => r.stepNumber);
76
+ const maxCompletedStep = completedStepNumbers.length > 0
77
+ ? Math.max(...completedStepNumbers)
78
+ : 0; // no dependency if nothing completed
79
+ const lastAgent = context.plan.steps[context.plan.steps.length - 1]?.agentName || 'integrator_finalizer';
80
+ const addSteps = [];
81
+ const dupStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'duplicate-blocks'), gatesConfig.autoAddRefactorStepOnDuplicateBlocks, 'duplicateRefactorAdded', triggeredFlags, agents, resolveAgent, '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);
82
+ if (dupStep)
83
+ addSteps.push(dupStep);
84
+ const readmeStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'readme-claims'), gatesConfig.autoAddReadmeTruthStepOnReadmeClaims, 'readmeTruthAdded', triggeredFlags, agents, resolveAgent, '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);
85
+ if (readmeStep)
86
+ addSteps.push(readmeStep);
87
+ const scaffoldStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'scaffold-defaults'), gatesConfig.autoAddScaffoldFixStepOnScaffoldDefaults, 'scaffoldFixAdded', triggeredFlags, agents, resolveAgent, '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);
88
+ if (scaffoldStep)
89
+ addSteps.push(scaffoldStep);
90
+ const configStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'hardcoded-config'), gatesConfig.autoAddConfigFixStepOnHardcodedConfig, 'configFixAdded', triggeredFlags, agents, resolveAgent, '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);
91
+ if (configStep)
92
+ addSteps.push(configStep);
93
+ const a11yStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'accessibility'), gatesConfig.autoAddAccessibilityFixStepOnAccessibility, 'accessibilityFixAdded', triggeredFlags, agents, resolveAgent, '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);
94
+ if (a11yStep)
95
+ addSteps.push(a11yStep);
96
+ const testCovStep = buildRemediationStep(gatesResult.results.find(r => r.id === 'test-coverage'), gatesConfig.autoAddTestCoverageStepOnTestCoverage, 'testCoverageFixAdded', triggeredFlags, agents, resolveAgent, '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);
97
+ if (testCovStep)
98
+ addSteps.push(testCovStep);
99
+ // Consolidate multiple gate failures into a single remediation step
100
+ // to avoid burning one premium request per gate failure.
101
+ if (addSteps.length > 1) {
102
+ const combinedTask = addSteps.map(s => s.task).join('\n\nALSO: ');
103
+ const singleStep = {
104
+ agent: addSteps[0].agent,
105
+ task: combinedTask,
106
+ };
107
+ if (addSteps[0].afterStep !== undefined) {
108
+ singleStep.afterStep = addSteps[0].afterStep;
109
+ }
110
+ addSteps.length = 0;
111
+ addSteps.push(singleStep);
112
+ logger.warn(`⚠️ Final quality gates failed (${failedIds.size} gates); scheduling single consolidated remediation step...`);
113
+ }
114
+ else if (addSteps.length === 1) {
115
+ logger.warn('⚠️ Final quality gates failed; attempting one remediation pass...');
116
+ }
117
+ if (addSteps.length > 0) {
118
+ remediationAttempted = true;
119
+ await executeReplan(context, { retrySteps: [], addSteps }, agents, options);
120
+ gatesResult = await (0, quality_gates_1.run_quality_gates)(workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds);
121
+ context.finalGateResults = gatesResult.results;
122
+ // Second remediation attempt if gates still fail after first fix
123
+ if (!gatesResult.passed && gatesConfig.failOnIssues) {
124
+ const stillFailed = gatesResult.results.filter(r => r.status === 'fail');
125
+ if (stillFailed.length > 0) {
126
+ const findings = stillFailed.flatMap(gate => gate.issues.map(issue => {
127
+ let desc = `[${gate.id}] ${issue.message}`;
128
+ if (issue.filePath)
129
+ desc += ` (${issue.filePath})`;
130
+ if (issue.hint)
131
+ desc += ` -- hint: ${issue.hint}`;
132
+ return desc;
133
+ })).join('\n');
134
+ const retryTask = [
135
+ 'The previous remediation attempt did not fully resolve the quality gate failures.',
136
+ 'The following issues remain. Fix each one specifically:',
137
+ '',
138
+ findings,
139
+ '',
140
+ 'Verify your fixes by checking that the specific issues listed above are resolved.',
141
+ 'Run tests to ensure nothing is broken.',
142
+ ].join('\n');
143
+ const retryAgent = resolveAgent(agents, 'integrator_finalizer')
144
+ ? 'integrator_finalizer'
145
+ : lastAgent;
146
+ const retryMaxStep = Math.max(...context.plan.steps.map(s => s.stepNumber));
147
+ logger.warn('⚠️ First remediation did not resolve all gate failures. Running second attempt with specific findings...');
148
+ await executeReplan(context, {
149
+ retrySteps: [],
150
+ addSteps: [{ agent: retryAgent, task: retryTask, afterStep: retryMaxStep }],
151
+ }, agents, options);
152
+ gatesResult = await (0, quality_gates_1.run_quality_gates)(workingDir, gatesConfig, gatesOut, baselineFiles, baseCommit, skippedReqIds);
153
+ context.finalGateResults = gatesResult.results;
154
+ }
155
+ }
156
+ }
157
+ }
158
+ if (!gatesResult.passed) {
159
+ if (remediationAttempted) {
160
+ // Remediation was attempted but couldn't fully resolve all issues.
161
+ // Downgrade to a warning so callers can decide how to handle.
162
+ const remaining = gatesResult.results
163
+ .filter(r => r.status === 'fail')
164
+ .map(r => `${r.id} (${r.issues.length} issues)`);
165
+ logger.warn(`⚠️ Quality gates still have issues after remediation: ${remaining.join(', ')}`);
166
+ }
167
+ else {
168
+ logger.error('❌ Quality gates failed. See report in:', gatesOut);
169
+ }
170
+ }
171
+ }
172
+ return { passed: gatesResult.passed, results: gatesResult.results };
173
+ }
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitHubIssuesIngester = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const logger_1 = require("./logger");
6
+ const logger = (0, logger_1.getLogger)('github-issues-ingester');
7
+ /**
8
+ * GitHub Issues Ingester - fetch open issues via gh CLI
9
+ * Only used if gh CLI is available and repo has .git directory
10
+ */
11
+ class GitHubIssuesIngester {
12
+ /**
13
+ * Check if gh CLI is available
14
+ */
15
+ async isGhCliAvailable() {
16
+ return new Promise((resolve) => {
17
+ const proc = (0, child_process_1.spawn)('which', ['gh']);
18
+ proc.on('close', (code) => {
19
+ resolve(code === 0);
20
+ });
21
+ });
22
+ }
23
+ /**
24
+ * Fetch open issues for a repository
25
+ */
26
+ async fetchIssues(repoPath) {
27
+ const ghAvailable = await this.isGhCliAvailable();
28
+ if (!ghAvailable) {
29
+ logger.warn('gh CLI not available, skipping issue ingestion');
30
+ return [];
31
+ }
32
+ return new Promise((resolve, _reject) => {
33
+ const proc = (0, child_process_1.spawn)('gh', ['issue', 'list', '--json', 'number,title,url,labels,createdAt', '--limit', '50'], {
34
+ cwd: repoPath,
35
+ stdio: ['pipe', 'pipe', 'pipe']
36
+ });
37
+ let stdout = '';
38
+ let stderr = '';
39
+ proc.stdout.on('data', (data) => {
40
+ stdout += data.toString();
41
+ });
42
+ proc.stderr.on('data', (data) => {
43
+ stderr += data.toString();
44
+ });
45
+ proc.on('close', (code) => {
46
+ if (code !== 0) {
47
+ logger.warn(`gh issue list failed: ${stderr}`);
48
+ resolve([]);
49
+ return;
50
+ }
51
+ try {
52
+ const rawIssues = JSON.parse(stdout);
53
+ const issues = rawIssues.map((issue) => ({
54
+ number: issue.number,
55
+ title: issue.title,
56
+ url: issue.url,
57
+ labels: issue.labels?.map((l) => typeof l === 'string' ? l : l.name || '') || [],
58
+ repoName: this.extractRepoName(issue.url),
59
+ createdAt: issue.createdAt
60
+ }));
61
+ resolve(issues);
62
+ }
63
+ catch (error) {
64
+ logger.warn(`Failed to parse gh issue output: ${error}`);
65
+ resolve([]);
66
+ }
67
+ });
68
+ });
69
+ }
70
+ /**
71
+ * Extract repo name from GitHub URL
72
+ */
73
+ extractRepoName(url) {
74
+ const match = url.match(/github\.com\/([^/]+\/[^/]+)/);
75
+ return match ? match[1] : 'unknown';
76
+ }
77
+ /**
78
+ * Link issues to plan context based on keywords
79
+ */
80
+ linkIssuesToTasks(issues, goal) {
81
+ // Simple keyword matching - find issues relevant to goal
82
+ const goalKeywords = goal.toLowerCase().split(/\s+/).filter(w => w.length > 3);
83
+ return issues.filter(issue => {
84
+ const searchText = `${issue.title} ${issue.labels.join(' ')}`.toLowerCase();
85
+ return goalKeywords.some(keyword => searchText.includes(keyword));
86
+ });
87
+ }
88
+ /**
89
+ * Close an issue with a comment
90
+ */
91
+ async closeIssue(issueNumber, comment, repoPath) {
92
+ const ghAvailable = await this.isGhCliAvailable();
93
+ if (!ghAvailable) {
94
+ return { success: false, error: 'gh CLI not available' };
95
+ }
96
+ return new Promise((resolve) => {
97
+ // Add comment first
98
+ const commentProc = (0, child_process_1.spawn)('gh', ['issue', 'comment', issueNumber.toString(), '--body', comment], {
99
+ cwd: repoPath,
100
+ stdio: ['pipe', 'pipe', 'pipe']
101
+ });
102
+ let commentError = '';
103
+ commentProc.stderr.on('data', (data) => {
104
+ commentError += data.toString();
105
+ });
106
+ commentProc.on('close', (commentCode) => {
107
+ if (commentCode !== 0) {
108
+ resolve({ success: false, error: `Failed to add comment: ${commentError}` });
109
+ return;
110
+ }
111
+ // Then close the issue
112
+ const closeProc = (0, child_process_1.spawn)('gh', ['issue', 'close', issueNumber.toString()], {
113
+ cwd: repoPath,
114
+ stdio: ['pipe', 'pipe', 'pipe']
115
+ });
116
+ let closeError = '';
117
+ closeProc.stderr.on('data', (data) => {
118
+ closeError += data.toString();
119
+ });
120
+ closeProc.on('close', (closeCode) => {
121
+ if (closeCode !== 0) {
122
+ resolve({ success: false, error: `Failed to close issue: ${closeError}` });
123
+ return;
124
+ }
125
+ resolve({ success: true });
126
+ });
127
+ });
128
+ });
129
+ }
130
+ /**
131
+ * Close multiple issues resolved by a step
132
+ */
133
+ async closeResolvedIssues(issueNumbers, stepNumber, agentName, repoPath) {
134
+ const comment = `✅ Resolved by swarm orchestrator - Step ${stepNumber} (${agentName})`;
135
+ let closed = 0;
136
+ let failed = 0;
137
+ for (const issueNumber of issueNumbers) {
138
+ const result = await this.closeIssue(issueNumber, comment, repoPath);
139
+ if (result.success) {
140
+ closed++;
141
+ logger.info(` ✓ Closed issue #${issueNumber}`);
142
+ }
143
+ else {
144
+ failed++;
145
+ logger.warn(` ✗ Failed to close issue #${issueNumber}: ${result.error}`);
146
+ }
147
+ }
148
+ return { closed, failed };
149
+ }
150
+ }
151
+ exports.GitHubIssuesIngester = GitHubIssuesIngester;
152
+ exports.default = GitHubIssuesIngester;