@bradtaylorsf/alpha-loop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/agents/implementer.md +30 -0
- package/agents/reviewer.md +29 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +57 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +89 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/history.d.ts +8 -0
- package/dist/commands/history.js +185 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +241 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/run.d.ts +15 -0
- package/dist/commands/run.js +321 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/scan.d.ts +1 -0
- package/dist/commands/scan.js +50 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/sync.d.ts +20 -0
- package/dist/commands/sync.js +149 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/vision.d.ts +1 -0
- package/dist/commands/vision.js +194 -0
- package/dist/commands/vision.js.map +1 -0
- package/dist/engine/agents.d.ts +41 -0
- package/dist/engine/agents.js +90 -0
- package/dist/engine/agents.js.map +1 -0
- package/dist/engine/config.d.ts +71 -0
- package/dist/engine/config.js +73 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/prerequisites.d.ts +34 -0
- package/dist/engine/prerequisites.js +90 -0
- package/dist/engine/prerequisites.js.map +1 -0
- package/dist/lib/agent.d.ts +25 -0
- package/dist/lib/agent.js +97 -0
- package/dist/lib/agent.js.map +1 -0
- package/dist/lib/config.d.ts +35 -0
- package/dist/lib/config.js +179 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/context.d.ts +17 -0
- package/dist/lib/context.js +96 -0
- package/dist/lib/context.js.map +1 -0
- package/dist/lib/github.d.ts +61 -0
- package/dist/lib/github.js +313 -0
- package/dist/lib/github.js.map +1 -0
- package/dist/lib/learning.d.ts +43 -0
- package/dist/lib/learning.js +207 -0
- package/dist/lib/learning.js.map +1 -0
- package/dist/lib/logger.d.ts +9 -0
- package/dist/lib/logger.js +28 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/pipeline.d.ts +18 -0
- package/dist/lib/pipeline.js +456 -0
- package/dist/lib/pipeline.js.map +1 -0
- package/dist/lib/preflight.d.ts +33 -0
- package/dist/lib/preflight.js +123 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/prerequisites.d.ts +12 -0
- package/dist/lib/prerequisites.js +54 -0
- package/dist/lib/prerequisites.js.map +1 -0
- package/dist/lib/prompts.d.ts +44 -0
- package/dist/lib/prompts.js +102 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/session.d.ts +28 -0
- package/dist/lib/session.js +173 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/shell.d.ts +32 -0
- package/dist/lib/shell.js +95 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/testing.d.ts +10 -0
- package/dist/lib/testing.js +51 -0
- package/dist/lib/testing.js.map +1 -0
- package/dist/lib/verify.d.ts +18 -0
- package/dist/lib/verify.js +235 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/vision.d.ts +9 -0
- package/dist/lib/vision.js +21 -0
- package/dist/lib/vision.js.map +1 -0
- package/dist/lib/worktree.d.ts +29 -0
- package/dist/lib/worktree.js +153 -0
- package/dist/lib/worktree.js.map +1 -0
- package/package.json +63 -0
- package/templates/agents/implementer.md +34 -0
- package/templates/agents/reviewer.md +48 -0
- package/templates/skills/code-review/SKILL.md +58 -0
- package/templates/skills/git-workflow/SKILL.md +53 -0
- package/templates/skills/implementation-planning/SKILL.md +64 -0
- package/templates/skills/security-analysis/SKILL.md +560 -0
- package/templates/skills/security-analysis/scripts/security-scanner.sh +227 -0
- package/templates/skills/test-robustness/SKILL.md +897 -0
- package/templates/skills/testing-patterns/SKILL.md +75 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Issue Pipeline — the 12-step orchestration for a single issue.
|
|
3
|
+
*/
|
|
4
|
+
import { mkdirSync, readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { log } from './logger.js';
|
|
7
|
+
import { exec } from './shell.js';
|
|
8
|
+
import { spawnAgent } from './agent.js';
|
|
9
|
+
import { setupWorktree, cleanupWorktree } from './worktree.js';
|
|
10
|
+
import { labelIssue, commentIssue, createPR, mergePR, updateProjectStatus, } from './github.js';
|
|
11
|
+
import { buildImplementPrompt, buildReviewPrompt } from './prompts.js';
|
|
12
|
+
import { runTests } from './testing.js';
|
|
13
|
+
import { runVerify } from './verify.js';
|
|
14
|
+
import { extractLearnings, getLearningContext } from './learning.js';
|
|
15
|
+
import { saveResult, getPreviousResult } from './session.js';
|
|
16
|
+
/** Max diff size to include in learning analysis. */
|
|
17
|
+
const MAX_DIFF_CHARS = 10_000;
|
|
18
|
+
/**
|
|
19
|
+
* Process a single issue through the full pipeline.
|
|
20
|
+
* Steps: status → worktree → plan → implement → test+retry → verify+retry →
|
|
21
|
+
* review → PR → learnings → update → auto-merge → cleanup
|
|
22
|
+
*/
|
|
23
|
+
export async function processIssue(issueNum, title, body, config, session) {
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
const projectDir = process.cwd();
|
|
26
|
+
// Setup logging
|
|
27
|
+
mkdirSync(session.logsDir, { recursive: true });
|
|
28
|
+
const logFile = join(session.logsDir, `issue-${issueNum}.log`);
|
|
29
|
+
log.step(`Processing Issue #${issueNum}: ${title}`);
|
|
30
|
+
// --- Step 1: Update status ---
|
|
31
|
+
log.step('Step 1: Updating issue status');
|
|
32
|
+
if (!config.dryRun) {
|
|
33
|
+
updateProjectStatus(config.repo, config.project, config.repoOwner, issueNum, 'In progress');
|
|
34
|
+
labelIssue(config.repo, issueNum, 'in-progress', config.labelReady);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
log.dry('Would update issue status to in-progress');
|
|
38
|
+
}
|
|
39
|
+
// --- Step 2: Setup worktree ---
|
|
40
|
+
log.step('Step 2: Setting up worktree');
|
|
41
|
+
let worktreePath;
|
|
42
|
+
let worktreeBranch;
|
|
43
|
+
try {
|
|
44
|
+
const wt = await setupWorktree({
|
|
45
|
+
issueNum,
|
|
46
|
+
projectDir,
|
|
47
|
+
baseBranch: config.baseBranch,
|
|
48
|
+
sessionBranch: session.branch,
|
|
49
|
+
autoMerge: config.autoMerge,
|
|
50
|
+
skipInstall: config.skipInstall,
|
|
51
|
+
dryRun: config.dryRun,
|
|
52
|
+
});
|
|
53
|
+
worktreePath = wt.path;
|
|
54
|
+
worktreeBranch = wt.branch;
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
log.error(`Failed to set up worktree for issue #${issueNum}: ${err}`);
|
|
58
|
+
if (!config.dryRun) {
|
|
59
|
+
labelIssue(config.repo, issueNum, 'failed', 'in-progress');
|
|
60
|
+
commentIssue(config.repo, issueNum, 'Agent loop failed: could not create worktree. Check logs.');
|
|
61
|
+
}
|
|
62
|
+
return failureResult(issueNum, title, startTime);
|
|
63
|
+
}
|
|
64
|
+
// --- Step 3: Plan (optional, non-fatal) ---
|
|
65
|
+
log.step('Step 3: Planning');
|
|
66
|
+
let implBody = body;
|
|
67
|
+
if (!config.dryRun) {
|
|
68
|
+
try {
|
|
69
|
+
const planResult = await spawnAgent({
|
|
70
|
+
agent: 'claude',
|
|
71
|
+
model: config.model,
|
|
72
|
+
prompt: `Analyze this GitHub issue and enrich it with implementation details.\n\nIssue #${issueNum}: ${title}\n\n${body}\n\nOutput the enriched issue body with acceptance criteria, implementation notes, and any edge cases to handle.`,
|
|
73
|
+
cwd: worktreePath,
|
|
74
|
+
logFile: join(session.logsDir, `issue-${issueNum}-plan.log`),
|
|
75
|
+
verbose: config.verbose,
|
|
76
|
+
});
|
|
77
|
+
if (planResult.exitCode === 0 && planResult.output.trim()) {
|
|
78
|
+
implBody = body + '\n\n## Agent Planning Notes\n\n' + planResult.output.trim();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
log.warn('Planning stage failed, proceeding with original issue description');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
log.dry('Would run planning agent');
|
|
87
|
+
}
|
|
88
|
+
// --- Step 4: Implement ---
|
|
89
|
+
log.step('Step 4: Implementing');
|
|
90
|
+
if (!config.dryRun) {
|
|
91
|
+
// Load vision and project context
|
|
92
|
+
const visionContext = loadFileIfExists(join(projectDir, '.alpha-loop', 'vision.md'));
|
|
93
|
+
const projectContext = loadFileIfExists(join(projectDir, '.alpha-loop', 'context.md'));
|
|
94
|
+
const previousResult = getPreviousResult(session);
|
|
95
|
+
const learningContext = getLearningContext(join(projectDir, '.alpha-loop', 'learnings'));
|
|
96
|
+
const implementPrompt = buildImplementPrompt({
|
|
97
|
+
issueNum,
|
|
98
|
+
title,
|
|
99
|
+
body: implBody,
|
|
100
|
+
visionContext: visionContext ?? undefined,
|
|
101
|
+
projectContext: projectContext ?? undefined,
|
|
102
|
+
previousResult: previousResult ?? undefined,
|
|
103
|
+
learningContext: learningContext || undefined,
|
|
104
|
+
});
|
|
105
|
+
const implResult = await spawnAgent({
|
|
106
|
+
agent: 'claude',
|
|
107
|
+
model: config.model,
|
|
108
|
+
prompt: implementPrompt,
|
|
109
|
+
cwd: worktreePath,
|
|
110
|
+
logFile: join(session.logsDir, `issue-${issueNum}-implement.log`),
|
|
111
|
+
verbose: config.verbose,
|
|
112
|
+
});
|
|
113
|
+
if (implResult.exitCode !== 0) {
|
|
114
|
+
log.error(`Implementation failed for issue #${issueNum}`);
|
|
115
|
+
labelIssue(config.repo, issueNum, 'failed', 'in-progress');
|
|
116
|
+
commentIssue(config.repo, issueNum, 'Agent loop failed during implementation. See logs for details.');
|
|
117
|
+
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup });
|
|
118
|
+
return failureResult(issueNum, title, startTime);
|
|
119
|
+
}
|
|
120
|
+
// Auto-commit if agent didn't
|
|
121
|
+
const statusResult = exec('git status --porcelain', { cwd: worktreePath });
|
|
122
|
+
if (statusResult.stdout.trim()) {
|
|
123
|
+
exec('git add -A', { cwd: worktreePath });
|
|
124
|
+
exec(`git commit -m "feat: implement issue #${issueNum} - ${title}"`, { cwd: worktreePath });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
log.dry('Would run implementation agent');
|
|
129
|
+
}
|
|
130
|
+
// --- Step 5: Test + retry loop ---
|
|
131
|
+
log.step('Step 5: Running tests');
|
|
132
|
+
let testOutput = '';
|
|
133
|
+
let testsPassing = false;
|
|
134
|
+
for (let attempt = 1; attempt <= config.maxTestRetries; attempt++) {
|
|
135
|
+
log.info(`Test attempt ${attempt} of ${config.maxTestRetries}`);
|
|
136
|
+
const testResult = runTests(worktreePath, config, logFile);
|
|
137
|
+
testOutput = testResult.output;
|
|
138
|
+
if (testResult.passed) {
|
|
139
|
+
testsPassing = true;
|
|
140
|
+
log.success(`All tests passed on attempt ${attempt}`);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
if (attempt < config.maxTestRetries) {
|
|
144
|
+
log.warn(`Tests failed on attempt ${attempt}, invoking agent to fix...`);
|
|
145
|
+
if (!config.dryRun) {
|
|
146
|
+
const fixPrompt = `Tests are failing for issue #${issueNum} (attempt ${attempt} of ${config.maxTestRetries}). Fix the failing tests.\n\nTest output:\n${testOutput}\n\nInstructions:\n1. Read the failing test output carefully and identify the ROOT CAUSE\n2. Fix the implementation code or the tests\n3. Run the tests again to verify\n4. Commit your fixes with a DESCRIPTIVE message that explains WHAT you fixed and WHY it failed.\n Format: fix(#${issueNum}): <what you changed> — <why it was failing>\n Example: fix(#${issueNum}): use port 5435 for postgres — default 5432 conflicts with host service\n DO NOT use generic messages like "fix: resolve test failures"`;
|
|
147
|
+
await spawnAgent({
|
|
148
|
+
agent: 'claude',
|
|
149
|
+
model: config.model,
|
|
150
|
+
prompt: fixPrompt,
|
|
151
|
+
cwd: worktreePath,
|
|
152
|
+
logFile: join(session.logsDir, `issue-${issueNum}-fix-${attempt}.log`),
|
|
153
|
+
verbose: config.verbose,
|
|
154
|
+
});
|
|
155
|
+
// Auto-commit fixes
|
|
156
|
+
const fixStatus = exec('git status --porcelain', { cwd: worktreePath });
|
|
157
|
+
if (fixStatus.stdout.trim()) {
|
|
158
|
+
exec('git add -A', { cwd: worktreePath });
|
|
159
|
+
exec(`git commit -m "fix(#${issueNum}): resolve test failures (attempt ${attempt})"`, { cwd: worktreePath });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
log.warn(`Tests still failing after ${config.maxTestRetries} attempts`);
|
|
165
|
+
testOutput = `TESTS FAILED after ${config.maxTestRetries} fix attempts. Latest output:\n${testOutput}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// --- Step 6: Live verification with playwright-cli ---
|
|
169
|
+
log.step('Step 6: Live verification');
|
|
170
|
+
let verifyOutput = '';
|
|
171
|
+
let verifyPassing = false;
|
|
172
|
+
for (let attempt = 1; attempt <= config.maxTestRetries; attempt++) {
|
|
173
|
+
log.info(`Verification attempt ${attempt} of ${config.maxTestRetries}`);
|
|
174
|
+
const verifyResult = await runVerify({
|
|
175
|
+
worktree: worktreePath,
|
|
176
|
+
logFile,
|
|
177
|
+
issueNum,
|
|
178
|
+
title,
|
|
179
|
+
body,
|
|
180
|
+
config,
|
|
181
|
+
sessionDir: session.resultsDir,
|
|
182
|
+
});
|
|
183
|
+
verifyOutput = verifyResult.output;
|
|
184
|
+
if (verifyResult.passed) {
|
|
185
|
+
verifyPassing = true;
|
|
186
|
+
log.success(`Verification passed on attempt ${attempt}`);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
if (attempt < config.maxTestRetries) {
|
|
190
|
+
log.warn(`Verification failed on attempt ${attempt}, invoking agent to fix...`);
|
|
191
|
+
const verifyFixPrompt = `Build verification failed after implementing issue #${issueNum} (attempt ${attempt} of ${config.maxTestRetries}).\nThe app was started and tested with playwright-cli, but verification failed.\n\nVerification output:\n${verifyOutput}\n\nInstructions:\n1. Read the verification output above and identify the ROOT CAUSE of each failure\n2. Fix the implementation code so the feature works correctly\n3. Run the test command to make sure unit tests still pass\n4. Commit your fixes with a DESCRIPTIVE message that explains WHAT you fixed and WHY it failed.\n Format: fix(#${issueNum}): <what you changed> — <why verification failed>\n Example: fix(#${issueNum}): add ENCRYPTION_KEY to langfuse config — service requires 32+ char secret\n DO NOT use generic messages like "fix: resolve verification failures"`;
|
|
192
|
+
await spawnAgent({
|
|
193
|
+
agent: 'claude',
|
|
194
|
+
model: config.model,
|
|
195
|
+
prompt: verifyFixPrompt,
|
|
196
|
+
cwd: worktreePath,
|
|
197
|
+
logFile: join(session.logsDir, `issue-${issueNum}-verify-fix-${attempt}.log`),
|
|
198
|
+
verbose: config.verbose,
|
|
199
|
+
});
|
|
200
|
+
// Auto-commit fixes
|
|
201
|
+
const fixStatus = exec('git status --porcelain', { cwd: worktreePath });
|
|
202
|
+
if (fixStatus.stdout.trim()) {
|
|
203
|
+
exec('git add -A', { cwd: worktreePath });
|
|
204
|
+
exec(`git commit -m "fix(#${issueNum}): resolve verification failures (attempt ${attempt})"`, { cwd: worktreePath });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
log.warn(`Verification still failing after ${config.maxTestRetries} attempts`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// --- Step 7: Review ---
|
|
212
|
+
log.step('Step 7: Code review');
|
|
213
|
+
let reviewOutput = '';
|
|
214
|
+
if (config.skipReview) {
|
|
215
|
+
log.info('Code review skipped');
|
|
216
|
+
}
|
|
217
|
+
else if (config.dryRun) {
|
|
218
|
+
log.dry('Would run code review');
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
try {
|
|
222
|
+
const reviewPrompt = buildReviewPrompt({
|
|
223
|
+
issueNum,
|
|
224
|
+
title,
|
|
225
|
+
body,
|
|
226
|
+
baseBranch: config.baseBranch,
|
|
227
|
+
visionContext: loadFileIfExists(join(projectDir, '.alpha-loop', 'vision.md')) ?? undefined,
|
|
228
|
+
});
|
|
229
|
+
const reviewResult = await spawnAgent({
|
|
230
|
+
agent: 'claude',
|
|
231
|
+
model: config.reviewModel,
|
|
232
|
+
prompt: reviewPrompt,
|
|
233
|
+
cwd: worktreePath,
|
|
234
|
+
logFile: join(session.logsDir, `issue-${issueNum}-review.log`),
|
|
235
|
+
verbose: config.verbose,
|
|
236
|
+
});
|
|
237
|
+
reviewOutput = reviewResult.output;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
log.warn('Code review failed, continuing without review');
|
|
241
|
+
reviewOutput = 'Code review could not be completed';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// --- Step 8: Create PR ---
|
|
245
|
+
log.step('Step 8: Creating PR');
|
|
246
|
+
let prUrl;
|
|
247
|
+
if (!config.dryRun) {
|
|
248
|
+
const prBase = config.autoMerge ? session.branch : config.baseBranch;
|
|
249
|
+
const prBody = buildPRBody(issueNum, title, reviewOutput, testOutput, testsPassing, verifyPassing, body);
|
|
250
|
+
try {
|
|
251
|
+
prUrl = createPR({
|
|
252
|
+
repo: config.repo,
|
|
253
|
+
base: prBase,
|
|
254
|
+
head: worktreeBranch,
|
|
255
|
+
title: `feat: ${title} (closes #${issueNum})`,
|
|
256
|
+
body: prBody,
|
|
257
|
+
cwd: worktreePath,
|
|
258
|
+
});
|
|
259
|
+
log.success(`PR created: ${prUrl}`);
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
log.error(`Failed to create PR for issue #${issueNum}: ${err}`);
|
|
263
|
+
labelIssue(config.repo, issueNum, 'failed', 'in-progress');
|
|
264
|
+
commentIssue(config.repo, issueNum, `Agent loop failed: could not create PR. Branch: ${worktreeBranch}`);
|
|
265
|
+
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup });
|
|
266
|
+
return failureResult(issueNum, title, startTime);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
log.dry('Would create PR');
|
|
271
|
+
}
|
|
272
|
+
// --- Step 9: Extract learnings ---
|
|
273
|
+
log.step('Step 9: Extracting learnings');
|
|
274
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
275
|
+
// Get diff for learning analysis
|
|
276
|
+
let runDiff = '';
|
|
277
|
+
if (!config.dryRun) {
|
|
278
|
+
const diffResult = exec(`git diff "origin/${config.baseBranch}...HEAD"`, { cwd: worktreePath });
|
|
279
|
+
runDiff = diffResult.stdout.slice(0, MAX_DIFF_CHARS);
|
|
280
|
+
}
|
|
281
|
+
await extractLearnings({
|
|
282
|
+
issueNum,
|
|
283
|
+
title,
|
|
284
|
+
status: testsPassing ? 'success' : 'failure',
|
|
285
|
+
retries: config.maxTestRetries,
|
|
286
|
+
duration,
|
|
287
|
+
diff: runDiff,
|
|
288
|
+
testOutput,
|
|
289
|
+
reviewOutput,
|
|
290
|
+
verifyOutput,
|
|
291
|
+
body,
|
|
292
|
+
config,
|
|
293
|
+
});
|
|
294
|
+
// --- Step 10: Update issue status ---
|
|
295
|
+
log.step('Step 10: Updating issue status');
|
|
296
|
+
if (!config.dryRun) {
|
|
297
|
+
const testsStatus = testsPassing ? 'PASSING' : 'FAILING';
|
|
298
|
+
updateProjectStatus(config.repo, config.project, config.repoOwner, issueNum, 'Done');
|
|
299
|
+
labelIssue(config.repo, issueNum, 'in-review', 'in-progress');
|
|
300
|
+
commentIssue(config.repo, issueNum, `Automated implementation complete.\n\n**PR**: ${prUrl ?? 'N/A'}\n**Tests**: ${testsStatus}\n**Review**: Attached to PR body.\n\n---\n*Processed by alpha-loop in ${duration}s*`);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
log.dry('Would update issue status to in-review');
|
|
304
|
+
}
|
|
305
|
+
// --- Step 11: Auto-merge ---
|
|
306
|
+
if (config.autoMerge && !config.dryRun && prUrl) {
|
|
307
|
+
log.step('Step 11: Auto-merging PR');
|
|
308
|
+
try {
|
|
309
|
+
mergePR(config.repo, worktreeBranch);
|
|
310
|
+
// Update local repo to include merged changes
|
|
311
|
+
exec('git fetch origin', { cwd: projectDir });
|
|
312
|
+
const currentBranch = exec('git rev-parse --abbrev-ref HEAD', { cwd: projectDir }).stdout;
|
|
313
|
+
if (currentBranch !== session.branch) {
|
|
314
|
+
exec(`git checkout "${session.branch}"`, { cwd: projectDir });
|
|
315
|
+
}
|
|
316
|
+
exec(`git pull origin "${session.branch}"`, { cwd: projectDir });
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
log.warn(`Auto-merge failed: ${err}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else if (config.dryRun) {
|
|
323
|
+
log.dry('Would auto-merge PR');
|
|
324
|
+
}
|
|
325
|
+
// --- Step 12: Cleanup ---
|
|
326
|
+
log.step('Step 12: Cleanup');
|
|
327
|
+
await cleanupWorktree({
|
|
328
|
+
issueNum,
|
|
329
|
+
projectDir,
|
|
330
|
+
autoCleanup: config.autoCleanup,
|
|
331
|
+
dryRun: config.dryRun,
|
|
332
|
+
});
|
|
333
|
+
// Count files changed
|
|
334
|
+
let filesChanged = 0;
|
|
335
|
+
if (runDiff) {
|
|
336
|
+
filesChanged = (runDiff.match(/^diff --git/gm) ?? []).length;
|
|
337
|
+
}
|
|
338
|
+
const result = {
|
|
339
|
+
issueNum,
|
|
340
|
+
title,
|
|
341
|
+
status: testsPassing ? 'success' : 'failure',
|
|
342
|
+
prUrl,
|
|
343
|
+
testsPassing,
|
|
344
|
+
verifyPassing,
|
|
345
|
+
duration,
|
|
346
|
+
filesChanged,
|
|
347
|
+
};
|
|
348
|
+
// Save result to session
|
|
349
|
+
saveResult(session, result);
|
|
350
|
+
log.success(`Issue #${issueNum} processed in ${duration}s`);
|
|
351
|
+
if (prUrl)
|
|
352
|
+
log.info(`PR: ${prUrl}`);
|
|
353
|
+
return result;
|
|
354
|
+
}
|
|
355
|
+
function failureResult(issueNum, title, startTime) {
|
|
356
|
+
return {
|
|
357
|
+
issueNum,
|
|
358
|
+
title,
|
|
359
|
+
status: 'failure',
|
|
360
|
+
testsPassing: false,
|
|
361
|
+
verifyPassing: false,
|
|
362
|
+
duration: Math.round((Date.now() - startTime) / 1000),
|
|
363
|
+
filesChanged: 0,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function loadFileIfExists(filePath) {
|
|
367
|
+
if (!existsSync(filePath))
|
|
368
|
+
return null;
|
|
369
|
+
try {
|
|
370
|
+
return readFileSync(filePath, 'utf-8');
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Extract just the review summary from the full agent output.
|
|
378
|
+
* Looks for the structured report section the reviewer agent produces.
|
|
379
|
+
*/
|
|
380
|
+
function extractReviewSummary(reviewOutput) {
|
|
381
|
+
if (!reviewOutput)
|
|
382
|
+
return 'No review available';
|
|
383
|
+
// Look for the structured review report (reviewer agent outputs this format)
|
|
384
|
+
const patterns = [
|
|
385
|
+
/### Review Summary[\s\S]*$/m,
|
|
386
|
+
/### Findings Fixed[\s\S]*$/m,
|
|
387
|
+
/## Review Report[\s\S]*$/m,
|
|
388
|
+
/\*\*Verdict:.*$/m,
|
|
389
|
+
];
|
|
390
|
+
for (const pattern of patterns) {
|
|
391
|
+
const match = reviewOutput.match(pattern);
|
|
392
|
+
if (match)
|
|
393
|
+
return match[0].trim();
|
|
394
|
+
}
|
|
395
|
+
// Fallback: take the last 500 chars which usually has the summary
|
|
396
|
+
const lines = reviewOutput.trim().split('\n');
|
|
397
|
+
const lastLines = lines.slice(-20).join('\n');
|
|
398
|
+
if (lastLines.length > 0)
|
|
399
|
+
return lastLines;
|
|
400
|
+
return 'Review completed — see logs for details';
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Extract a one-line test summary from raw test output.
|
|
404
|
+
* e.g., "30 passed, 0 failed" from Jest/Vitest output.
|
|
405
|
+
*/
|
|
406
|
+
function extractTestSummary(testOutput) {
|
|
407
|
+
if (!testOutput)
|
|
408
|
+
return '';
|
|
409
|
+
// Jest: "Tests: 30 passed, 30 total"
|
|
410
|
+
const jestMatch = testOutput.match(/Tests:\s+(.+total)/);
|
|
411
|
+
if (jestMatch)
|
|
412
|
+
return jestMatch[1].trim();
|
|
413
|
+
// Vitest: "Tests 30 passed (30)"
|
|
414
|
+
const vitestMatch = testOutput.match(/Tests\s+(.+\(\d+\))/);
|
|
415
|
+
if (vitestMatch)
|
|
416
|
+
return vitestMatch[1].trim();
|
|
417
|
+
// Fallback: count "passed" and "failed" lines
|
|
418
|
+
const passed = (testOutput.match(/passed/gi) || []).length;
|
|
419
|
+
const failed = (testOutput.match(/failed/gi) || []).length;
|
|
420
|
+
if (passed > 0 || failed > 0)
|
|
421
|
+
return `${passed} passed, ${failed} failed`;
|
|
422
|
+
return '';
|
|
423
|
+
}
|
|
424
|
+
function buildPRBody(issueNum, title, reviewOutput, testOutput, testsPassing, verifyPassing, body) {
|
|
425
|
+
const testSummary = extractTestSummary(testOutput);
|
|
426
|
+
const reviewSummary = extractReviewSummary(reviewOutput);
|
|
427
|
+
const lines = [
|
|
428
|
+
`Closes #${issueNum}`,
|
|
429
|
+
'',
|
|
430
|
+
`## Summary`,
|
|
431
|
+
'',
|
|
432
|
+
`Automated implementation of **${title}**`,
|
|
433
|
+
'',
|
|
434
|
+
'## Test Results',
|
|
435
|
+
'',
|
|
436
|
+
`| Check | Status |`,
|
|
437
|
+
`|-------|--------|`,
|
|
438
|
+
`| Unit tests | ${testsPassing ? 'PASS' : 'FAIL'} |`,
|
|
439
|
+
`| Verification | ${verifyPassing ? 'PASS' : 'FAIL'} |`,
|
|
440
|
+
];
|
|
441
|
+
if (testSummary) {
|
|
442
|
+
lines.push(`| Details | ${testSummary} |`);
|
|
443
|
+
}
|
|
444
|
+
lines.push('');
|
|
445
|
+
// Code review — just the summary, not the full agent output
|
|
446
|
+
lines.push('## Code Review', '', reviewSummary, '');
|
|
447
|
+
// What to test — from issue body or generic
|
|
448
|
+
const whatToTestMatch = body.match(/## Test Requirements[\s\S]*?(?=\n## |$)/);
|
|
449
|
+
if (whatToTestMatch) {
|
|
450
|
+
lines.push(whatToTestMatch[0].trim(), '');
|
|
451
|
+
}
|
|
452
|
+
// Session log reference
|
|
453
|
+
lines.push('---', `*Automated by [alpha-loop](https://github.com/bradtaylorsf/alpha-loop) · Full logs in \`.alpha-loop/sessions/\`*`);
|
|
454
|
+
return lines.join('\n');
|
|
455
|
+
}
|
|
456
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/lib/pipeline.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAI7D,qDAAqD;AACrD,MAAM,cAAc,GAAG,MAAM,CAAC;AAa9B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAgB,EAChB,KAAa,EACb,IAAY,EACZ,MAAc,EACd,OAAuB;IAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAEjC,gBAAgB;IAChB,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,CAAC;IAE/D,GAAG,CAAC,IAAI,CAAC,qBAAqB,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;IAEpD,gCAAgC;IAChC,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC5F,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;IACtD,CAAC;IAED,iCAAiC;IACjC,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IACxC,IAAI,YAAoB,CAAC;IACzB,IAAI,cAAsB,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC;YAC7B,QAAQ;YACR,UAAU;YACV,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,aAAa,EAAE,OAAO,CAAC,MAAM;YAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAC;QACH,YAAY,GAAG,EAAE,CAAC,IAAI,CAAC;QACvB,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,KAAK,CAAC,wCAAwC,QAAQ,KAAK,GAAG,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC3D,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,2DAA2D,CAAC,CAAC;QACnG,CAAC;QACD,OAAO,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;IAED,6CAA6C;IAC7C,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7B,IAAI,QAAQ,GAAG,IAAI,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC;gBAClC,KAAK,EAAE,QAAQ;gBACf,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,kFAAkF,QAAQ,KAAK,KAAK,OAAO,IAAI,kHAAkH;gBACzO,GAAG,EAAE,YAAY;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,WAAW,CAAC;gBAC5D,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC,CAAC;YACH,IAAI,UAAU,CAAC,QAAQ,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC1D,QAAQ,GAAG,IAAI,GAAG,iCAAiC,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IACtC,CAAC;IAED,4BAA4B;IAC5B,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,kCAAkC;QAClC,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;QACrF,MAAM,cAAc,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;QACvF,MAAM,cAAc,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAC;QAEzF,MAAM,eAAe,GAAG,oBAAoB,CAAC;YAC3C,QAAQ;YACR,KAAK;YACL,IAAI,EAAE,QAAQ;YACd,aAAa,EAAE,aAAa,IAAI,SAAS;YACzC,cAAc,EAAE,cAAc,IAAI,SAAS;YAC3C,cAAc,EAAE,cAAc,IAAI,SAAS;YAC3C,eAAe,EAAE,eAAe,IAAI,SAAS;SAC9C,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC;YAClC,KAAK,EAAE,QAAQ;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,eAAe;YACvB,GAAG,EAAE,YAAY;YAEjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,gBAAgB,CAAC;YACjE,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC,CAAC;QAEH,IAAI,UAAU,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,KAAK,CAAC,oCAAoC,QAAQ,EAAE,CAAC,CAAC;YAC1D,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC3D,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,gEAAgE,CAAC,CAAC;YACtG,MAAM,eAAe,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;YACjF,OAAO,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACnD,CAAC;QAED,8BAA8B;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,wBAAwB,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;QAC3E,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC/B,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,yCAAyC,QAAQ,MAAM,KAAK,GAAG,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC5C,CAAC;IAED,oCAAoC;IACpC,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAClC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,cAAc,EAAE,OAAO,EAAE,EAAE,CAAC;QAClE,GAAG,CAAC,IAAI,CAAC,gBAAgB,OAAO,OAAO,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAEhE,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3D,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;QAE/B,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;YACtB,YAAY,GAAG,IAAI,CAAC;YACpB,GAAG,CAAC,OAAO,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;YACtD,MAAM;QACR,CAAC;QAED,IAAI,OAAO,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC;YACpC,GAAG,CAAC,IAAI,CAAC,2BAA2B,OAAO,4BAA4B,CAAC,CAAC;YACzE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM,SAAS,GAAG,gCAAgC,QAAQ,aAAa,OAAO,OAAO,MAAM,CAAC,cAAc,8CAA8C,UAAU,6RAA6R,QAAQ,kEAAkE,QAAQ,4IAA4I,CAAC;gBAE9pB,MAAM,UAAU,CAAC;oBACf,KAAK,EAAE,QAAQ;oBACf,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,MAAM,EAAE,SAAS;oBACjB,GAAG,EAAE,YAAY;oBAEjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,QAAQ,OAAO,MAAM,CAAC;oBACtE,OAAO,EAAE,MAAM,CAAC,OAAO;iBACxB,CAAC,CAAC;gBAEH,oBAAoB;gBACpB,MAAM,SAAS,GAAG,IAAI,CAAC,wBAAwB,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;gBACxE,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;oBAC5B,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;oBAC1C,IAAI,CAAC,uBAAuB,QAAQ,qCAAqC,OAAO,IAAI,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC/G,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,6BAA6B,MAAM,CAAC,cAAc,WAAW,CAAC,CAAC;YACxE,UAAU,GAAG,sBAAsB,MAAM,CAAC,cAAc,kCAAkC,UAAU,EAAE,CAAC;QACzG,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,GAAG,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACtC,IAAI,YAAY,GAAG,EAAE,CAAC;IACtB,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,cAAc,EAAE,OAAO,EAAE,EAAE,CAAC;QAClE,GAAG,CAAC,IAAI,CAAC,wBAAwB,OAAO,OAAO,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAExE,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC;YACnC,QAAQ,EAAE,YAAY;YACtB,OAAO;YACP,QAAQ;YACR,KAAK;YACL,IAAI;YACJ,MAAM;YACN,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC,CAAC;QACH,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;QAEnC,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;YACxB,aAAa,GAAG,IAAI,CAAC;YACrB,GAAG,CAAC,OAAO,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAC;YACzD,MAAM;QACR,CAAC;QAED,IAAI,OAAO,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC;YACpC,GAAG,CAAC,IAAI,CAAC,kCAAkC,OAAO,4BAA4B,CAAC,CAAC;YAChF,MAAM,eAAe,GAAG,uDAAuD,QAAQ,aAAa,OAAO,OAAO,MAAM,CAAC,cAAc,6GAA6G,YAAY,qVAAqV,QAAQ,uEAAuE,QAAQ,uJAAuJ,CAAC;YAEp0B,MAAM,UAAU,CAAC;gBACf,KAAK,EAAE,QAAQ;gBACf,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,eAAe;gBACvB,GAAG,EAAE,YAAY;gBAEjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,eAAe,OAAO,MAAM,CAAC;gBAC7E,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC,CAAC;YAEH,oBAAoB;YACpB,MAAM,SAAS,GAAG,IAAI,CAAC,wBAAwB,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;YACxE,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5B,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;gBAC1C,IAAI,CAAC,uBAAuB,QAAQ,6CAA6C,OAAO,IAAI,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;YACvH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,oCAAoC,MAAM,CAAC,cAAc,WAAW,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAChC,IAAI,YAAY,GAAG,EAAE,CAAC;IAEtB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAClC,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IACnC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,iBAAiB,CAAC;gBACrC,QAAQ;gBACR,KAAK;gBACL,IAAI;gBACJ,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,aAAa,EAAE,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,IAAI,SAAS;aAC3F,CAAC,CAAC;YAEH,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC;gBACpC,KAAK,EAAE,QAAQ;gBACf,KAAK,EAAE,MAAM,CAAC,WAAW;gBACzB,MAAM,EAAE,YAAY;gBACpB,GAAG,EAAE,YAAY;gBAEjB,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,QAAQ,aAAa,CAAC;gBAC9D,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC,CAAC;YAEH,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAC1D,YAAY,GAAG,oCAAoC,CAAC;QACtD,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAChC,IAAI,KAAyB,CAAC;IAE9B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QACrE,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QAEzG,IAAI,CAAC;YACH,KAAK,GAAG,QAAQ,CAAC;gBACf,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,cAAc;gBACpB,KAAK,EAAE,SAAS,KAAK,aAAa,QAAQ,GAAG;gBAC7C,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,YAAY;aAClB,CAAC,CAAC;YACH,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,KAAK,CAAC,kCAAkC,QAAQ,KAAK,GAAG,EAAE,CAAC,CAAC;YAChE,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC3D,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,mDAAmD,cAAc,EAAE,CAAC,CAAC;YACzG,MAAM,eAAe,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;YACjF,OAAO,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC7B,CAAC;IAED,oCAAoC;IACpC,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;IAE7D,iCAAiC;IACjC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,MAAM,CAAC,UAAU,UAAU,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;QAChG,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,gBAAgB,CAAC;QACrB,QAAQ;QACR,KAAK;QACL,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAC5C,OAAO,EAAE,MAAM,CAAC,cAAc;QAC9B,QAAQ;QACR,IAAI,EAAE,OAAO;QACb,UAAU;QACV,YAAY;QACZ,YAAY;QACZ,IAAI;QACJ,MAAM;KACP,CAAC,CAAC;IAEH,uCAAuC;IACvC,GAAG,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACrF,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;QAC9D,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAChC,iDAAiD,KAAK,IAAI,KAAK,gBAAgB,WAAW,0EAA0E,QAAQ,IAAI,CACjL,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACpD,CAAC;IAED,8BAA8B;IAC9B,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAChD,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;YAErC,8CAA8C;YAC9C,IAAI,CAAC,kBAAkB,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;YAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,iCAAiC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;YAC1F,IAAI,aAAa,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,iBAAiB,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;YAChE,CAAC;YACD,IAAI,CAAC,oBAAoB,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACjC,CAAC;IAED,2BAA2B;IAC3B,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7B,MAAM,eAAe,CAAC;QACpB,QAAQ;QACR,UAAU;QACV,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,sBAAsB;IACtB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,OAAO,EAAE,CAAC;QACZ,YAAY,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAC/D,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,QAAQ;QACR,KAAK;QACL,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QAC5C,KAAK;QACL,YAAY;QACZ,aAAa;QACb,QAAQ;QACR,YAAY;KACb,CAAC;IAEF,yBAAyB;IACzB,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5B,GAAG,CAAC,OAAO,CAAC,UAAU,QAAQ,iBAAiB,QAAQ,GAAG,CAAC,CAAC;IAC5D,IAAI,KAAK;QAAE,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;IAEpC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,KAAa,EAAE,SAAiB;IACvE,OAAO;QACL,QAAQ;QACR,KAAK;QACL,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK;QACpB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;QACrD,YAAY,EAAE,CAAC;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB;IACxC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,YAAoB;IAChD,IAAI,CAAC,YAAY;QAAE,OAAO,qBAAqB,CAAC;IAEhD,6EAA6E;IAC7E,MAAM,QAAQ,GAAG;QACf,6BAA6B;QAC7B,6BAA6B;QAC7B,2BAA2B;QAC3B,kBAAkB;KACnB,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED,kEAAkE;IAClE,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,OAAO,yCAAyC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,UAAkB;IAC5C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAC;IAE3B,sCAAsC;IACtC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACzD,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE1C,kCAAkC;IAClC,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC5D,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE9C,8CAA8C;IAC9C,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAC3D,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAC3D,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,MAAM,YAAY,MAAM,SAAS,CAAC;IAE1E,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,WAAW,CAClB,QAAgB,EAChB,KAAa,EACb,YAAoB,EACpB,UAAkB,EAClB,YAAqB,EACrB,aAAsB,EACtB,IAAY;IAEZ,MAAM,WAAW,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAEzD,MAAM,KAAK,GAAa;QACtB,WAAW,QAAQ,EAAE;QACrB,EAAE;QACF,YAAY;QACZ,EAAE;QACF,iCAAiC,KAAK,IAAI;QAC1C,EAAE;QACF,iBAAiB;QACjB,EAAE;QACF,oBAAoB;QACpB,oBAAoB;QACpB,kBAAkB,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI;QACpD,oBAAoB,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI;KACxD,CAAC;IAEF,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,4DAA4D;IAC5D,KAAK,CAAC,IAAI,CACR,gBAAgB,EAChB,EAAE,EACF,aAAa,EACb,EAAE,CACH,CAAC;IAEF,4CAA4C;IAC5C,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC9E,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,wBAAwB;IACxB,KAAK,CAAC,IAAI,CACR,KAAK,EACL,kHAAkH,CACnH,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ExecResult } from './shell.js';
|
|
2
|
+
export interface PreflightConfig {
|
|
3
|
+
testCommand: string;
|
|
4
|
+
skipPreflight?: boolean;
|
|
5
|
+
skipTests?: boolean;
|
|
6
|
+
dryRun?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PreflightResult {
|
|
9
|
+
passed: boolean;
|
|
10
|
+
preExistingFailures: string[];
|
|
11
|
+
ignoreFile?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Run preflight test validation.
|
|
15
|
+
* If tests fail, records pre-existing failures so they can be ignored during issue processing.
|
|
16
|
+
* Returns list of pre-existing failures to ignore.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runPreflight(config: PreflightConfig, executor?: (cmd: string) => Promise<ExecResult>): Promise<PreflightResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Parse test output for failing test names.
|
|
21
|
+
* Supports Jest (● suite › test) and Vitest (❌) formats.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseFailingTests(output: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Check if configured ports are already in use on the host.
|
|
26
|
+
* Warns about conflicts so the user can fix them before the agent wastes retries.
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkPortConflicts(port: number): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Run port conflict checks for the dev server port.
|
|
31
|
+
* Returns list of conflicts found. Empty = no conflicts.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runPortCheck(port: number): string[];
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { exec, execAsync } from './shell.js';
|
|
5
|
+
import { log } from './logger.js';
|
|
6
|
+
const SKIP_RESULT = { passed: true, preExistingFailures: [] };
|
|
7
|
+
/**
|
|
8
|
+
* Run preflight test validation.
|
|
9
|
+
* If tests fail, records pre-existing failures so they can be ignored during issue processing.
|
|
10
|
+
* Returns list of pre-existing failures to ignore.
|
|
11
|
+
*/
|
|
12
|
+
export async function runPreflight(config, executor = execAsync) {
|
|
13
|
+
if (config.skipPreflight || config.skipTests) {
|
|
14
|
+
return SKIP_RESULT;
|
|
15
|
+
}
|
|
16
|
+
if (config.dryRun) {
|
|
17
|
+
return SKIP_RESULT;
|
|
18
|
+
}
|
|
19
|
+
const result = await executor(config.testCommand);
|
|
20
|
+
// Tests passed
|
|
21
|
+
if (result.exitCode === 0) {
|
|
22
|
+
return { passed: true, preExistingFailures: [] };
|
|
23
|
+
}
|
|
24
|
+
// Tests failed — parse output for failing test names
|
|
25
|
+
const output = result.stdout + '\n' + result.stderr;
|
|
26
|
+
const failures = parseFailingTests(output);
|
|
27
|
+
// Save ignore file
|
|
28
|
+
const ignoreFile = saveIgnoreFile(failures, output);
|
|
29
|
+
return {
|
|
30
|
+
passed: false,
|
|
31
|
+
preExistingFailures: failures,
|
|
32
|
+
ignoreFile,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse test output for failing test names.
|
|
37
|
+
* Supports Jest (● suite › test) and Vitest (❌) formats.
|
|
38
|
+
*/
|
|
39
|
+
export function parseFailingTests(output) {
|
|
40
|
+
const lines = output.split('\n');
|
|
41
|
+
const failures = [];
|
|
42
|
+
// Jest format: lines starting with ● (with optional leading whitespace)
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
const trimmed = line.trim();
|
|
45
|
+
if (trimmed.startsWith('●')) {
|
|
46
|
+
failures.push(trimmed);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Vitest format: lines starting with ❌
|
|
50
|
+
if (failures.length === 0) {
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const trimmed = line.trim();
|
|
53
|
+
if (trimmed.startsWith('❌')) {
|
|
54
|
+
failures.push(trimmed);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Fallback: FAIL lines for file-level info
|
|
59
|
+
if (failures.length === 0) {
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
const trimmed = line.trim();
|
|
62
|
+
if (trimmed.startsWith('FAIL ')) {
|
|
63
|
+
failures.push(trimmed);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return failures;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if configured ports are already in use on the host.
|
|
71
|
+
* Warns about conflicts so the user can fix them before the agent wastes retries.
|
|
72
|
+
*/
|
|
73
|
+
export function checkPortConflicts(port) {
|
|
74
|
+
const conflicts = [];
|
|
75
|
+
const result = exec(`lsof -i :${port} -P -n -sTCP:LISTEN`, { cwd: process.cwd() });
|
|
76
|
+
if (result.exitCode === 0 && result.stdout.trim()) {
|
|
77
|
+
const lines = result.stdout.trim().split('\n').slice(1); // skip header
|
|
78
|
+
for (const line of lines) {
|
|
79
|
+
const parts = line.trim().split(/\s+/);
|
|
80
|
+
const processName = parts[0] ?? 'unknown';
|
|
81
|
+
const pid = parts[1] ?? '?';
|
|
82
|
+
conflicts.push(`Port ${port} in use by ${processName} (PID ${pid})`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return conflicts;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Run port conflict checks for the dev server port.
|
|
89
|
+
* Returns list of conflicts found. Empty = no conflicts.
|
|
90
|
+
*/
|
|
91
|
+
export function runPortCheck(port) {
|
|
92
|
+
if (!port)
|
|
93
|
+
return [];
|
|
94
|
+
const conflicts = checkPortConflicts(port);
|
|
95
|
+
if (conflicts.length > 0) {
|
|
96
|
+
log.warn('Port conflicts detected:');
|
|
97
|
+
for (const c of conflicts) {
|
|
98
|
+
log.warn(` ${c}`);
|
|
99
|
+
}
|
|
100
|
+
log.warn('The verification step may fail if these ports are not freed.');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
log.success(`Port ${port} is available`);
|
|
104
|
+
}
|
|
105
|
+
return conflicts;
|
|
106
|
+
}
|
|
107
|
+
function saveIgnoreFile(failures, rawOutput) {
|
|
108
|
+
const filePath = join(tmpdir(), `preflight-ignore-${Date.now()}`);
|
|
109
|
+
const content = [...failures];
|
|
110
|
+
// Also extract FAIL file paths as fallback
|
|
111
|
+
const failLines = rawOutput.split('\n')
|
|
112
|
+
.map((l) => l.trim())
|
|
113
|
+
.filter((l) => l.startsWith('FAIL '))
|
|
114
|
+
.map((l) => l.replace(/^FAIL\s+/, ''));
|
|
115
|
+
for (const f of failLines) {
|
|
116
|
+
if (!content.includes(f)) {
|
|
117
|
+
content.push(f);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
writeFileSync(filePath, content.join('\n') + '\n', 'utf-8');
|
|
121
|
+
return filePath;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/lib/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PrerequisiteConfig {
|
|
2
|
+
agent: string;
|
|
3
|
+
}
|
|
4
|
+
export declare class PrerequisiteError extends Error {
|
|
5
|
+
readonly errors: string[];
|
|
6
|
+
constructor(errors: string[]);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Verify required tools are installed: gh, git, and the configured agent CLI.
|
|
10
|
+
* Throws PrerequisiteError with all failures if any checks fail.
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkPrerequisites(config: PrerequisiteConfig): Promise<void>;
|