@bradtaylorsf/alpha-loop 1.8.0 → 1.9.1
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/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.js +6 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/plan.d.ts +1 -0
- package/dist/commands/plan.js +298 -195
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/resume.js +6 -5
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/roadmap.js +2 -3
- package/dist/commands/roadmap.js.map +1 -1
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +33 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/triage.js +12 -16
- package/dist/commands/triage.js.map +1 -1
- package/dist/commands/vision.js +4 -3
- package/dist/commands/vision.js.map +1 -1
- package/dist/lib/github.d.ts +16 -3
- package/dist/lib/github.js +151 -75
- package/dist/lib/github.js.map +1 -1
- package/dist/lib/learning.d.ts +13 -1
- package/dist/lib/learning.js +90 -17
- package/dist/lib/learning.js.map +1 -1
- package/dist/lib/logger.d.ts +1 -0
- package/dist/lib/logger.js +2 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/pipeline.js +116 -44
- package/dist/lib/pipeline.js.map +1 -1
- package/dist/lib/planning.d.ts +5 -0
- package/dist/lib/planning.js +14 -0
- package/dist/lib/planning.js.map +1 -1
- package/dist/lib/prompts.d.ts +5 -0
- package/dist/lib/prompts.js +18 -5
- package/dist/lib/prompts.js.map +1 -1
- package/dist/lib/rate-limit.d.ts +55 -0
- package/dist/lib/rate-limit.js +188 -0
- package/dist/lib/rate-limit.js.map +1 -0
- package/dist/lib/session.js +2 -1
- package/dist/lib/session.js.map +1 -1
- package/dist/lib/validation.d.ts +69 -0
- package/dist/lib/validation.js +280 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/worktree.d.ts +16 -0
- package/dist/lib/worktree.js +99 -31
- package/dist/lib/worktree.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/reviewer.md +7 -0
package/dist/lib/learning.js
CHANGED
|
@@ -7,9 +7,57 @@ import { log } from './logger.js';
|
|
|
7
7
|
import { formatTimestamp } from './shell.js';
|
|
8
8
|
import { spawnAgent } from './agent.js';
|
|
9
9
|
import { buildLearnPrompt } from './prompts.js';
|
|
10
|
+
/** Expected sections in learning output. */
|
|
11
|
+
const LEARNING_SECTIONS = [
|
|
12
|
+
'## What Worked',
|
|
13
|
+
'## What Failed',
|
|
14
|
+
'## Patterns',
|
|
15
|
+
'## Anti-Patterns',
|
|
16
|
+
'## Suggested Skill Updates',
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Parse raw agent output and extract only the expected learning sections.
|
|
20
|
+
* Discards prompt echoes, session logs, tool calls, and other noise.
|
|
21
|
+
*/
|
|
22
|
+
export function parseLearningOutput(raw) {
|
|
23
|
+
// Extract frontmatter if present
|
|
24
|
+
let frontmatter = null;
|
|
25
|
+
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---/);
|
|
26
|
+
if (fmMatch) {
|
|
27
|
+
frontmatter = fmMatch[1];
|
|
28
|
+
}
|
|
29
|
+
// Extract each expected section
|
|
30
|
+
const extracted = [];
|
|
31
|
+
for (const header of LEARNING_SECTIONS) {
|
|
32
|
+
const pattern = new RegExp(`${header.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\n([\\s\\S]*?)(?=\\n## |$)`);
|
|
33
|
+
const match = raw.match(pattern);
|
|
34
|
+
if (match) {
|
|
35
|
+
const content = match[1].trim();
|
|
36
|
+
if (content) {
|
|
37
|
+
extracted.push(`${header}\n${content}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { frontmatter, sections: extracted.join('\n\n') };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build trace pointers for a learning file's frontmatter.
|
|
45
|
+
*/
|
|
46
|
+
function buildTracePointers(sessionName, issueNum) {
|
|
47
|
+
const base = `.alpha-loop/sessions/${sessionName}`;
|
|
48
|
+
return [
|
|
49
|
+
'traces:',
|
|
50
|
+
` plan: ${base}/traces/prompts/plan-issue-${issueNum}.md`,
|
|
51
|
+
` implement: ${base}/logs/issue-${issueNum}-implement.log`,
|
|
52
|
+
` review: ${base}/traces/outputs/review-issue-${issueNum}.log`,
|
|
53
|
+
` diff: ${base}/diffs/issue-${issueNum}.diff`,
|
|
54
|
+
].join('\n');
|
|
55
|
+
}
|
|
10
56
|
/**
|
|
11
57
|
* Extract learnings from a completed run.
|
|
12
|
-
* Invokes an agent with the learn prompt
|
|
58
|
+
* Invokes an agent with the learn prompt, parses the output to extract
|
|
59
|
+
* only expected sections, and saves a concise learning file with trace pointers.
|
|
60
|
+
* Raw agent output is saved separately to the traces directory.
|
|
13
61
|
*/
|
|
14
62
|
export async function extractLearnings(options) {
|
|
15
63
|
const { config } = options;
|
|
@@ -49,25 +97,48 @@ export async function extractLearnings(options) {
|
|
|
49
97
|
log.warn(`Learning extraction failed (exit ${result.exitCode}, output ${result.output.length} chars), skipping`);
|
|
50
98
|
return;
|
|
51
99
|
}
|
|
52
|
-
const
|
|
53
|
-
//
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
100
|
+
const rawOutput = result.output.trim();
|
|
101
|
+
// Save raw agent output to traces directory if session info is available
|
|
102
|
+
if (options.sessionLogsDir) {
|
|
103
|
+
const rawDir = join(options.sessionLogsDir, 'learnings');
|
|
104
|
+
mkdirSync(rawDir, { recursive: true });
|
|
105
|
+
writeFileSync(join(rawDir, `issue-${options.issueNum}-raw.md`), rawOutput + '\n');
|
|
106
|
+
log.info(`Raw learning output saved to traces`);
|
|
107
|
+
}
|
|
108
|
+
// Parse the output to extract only expected sections
|
|
109
|
+
const { frontmatter: parsedFm, sections } = parseLearningOutput(rawOutput);
|
|
110
|
+
const today = new Date().toISOString().split('T')[0];
|
|
111
|
+
// Build frontmatter with trace pointers — always ensure key fields exist
|
|
112
|
+
const requiredFields = {
|
|
113
|
+
issue: String(options.issueNum),
|
|
114
|
+
status: options.status,
|
|
115
|
+
retries: String(options.retries),
|
|
116
|
+
duration: String(options.duration),
|
|
117
|
+
date: today,
|
|
118
|
+
};
|
|
119
|
+
const fmLines = [];
|
|
120
|
+
if (parsedFm) {
|
|
121
|
+
fmLines.push(parsedFm);
|
|
122
|
+
// Inject any missing required fields
|
|
123
|
+
for (const [key, value] of Object.entries(requiredFields)) {
|
|
124
|
+
if (!new RegExp(`^${key}:`, 'm').test(parsedFm)) {
|
|
125
|
+
fmLines.push(`${key}: ${value}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
57
128
|
}
|
|
58
129
|
else {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
${output}`;
|
|
68
|
-
writeFileSync(learningFile, wrapped + '\n');
|
|
69
|
-
log.success(`Learning saved to ${learningFile} (added frontmatter)`);
|
|
130
|
+
for (const [key, value] of Object.entries(requiredFields)) {
|
|
131
|
+
fmLines.push(`${key}: ${value}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Add trace pointers if session info available
|
|
135
|
+
if (options.sessionName) {
|
|
136
|
+
fmLines.push(buildTracePointers(options.sessionName, options.issueNum));
|
|
70
137
|
}
|
|
138
|
+
const conciseContent = sections || rawOutput;
|
|
139
|
+
const finalOutput = `---\n${fmLines.join('\n')}\n---\n\n${conciseContent}`;
|
|
140
|
+
writeFileSync(learningFile, finalOutput + '\n');
|
|
141
|
+
log.success(`Learning saved to ${learningFile}`);
|
|
71
142
|
}
|
|
72
143
|
/**
|
|
73
144
|
* Count learning files in the learnings directory.
|
|
@@ -142,6 +213,8 @@ export async function generateSessionSummary(options) {
|
|
|
142
213
|
log.step('Generating session summary...');
|
|
143
214
|
// Collect all learnings from this session
|
|
144
215
|
const learningContents = [];
|
|
216
|
+
if (!existsSync(learningsDir))
|
|
217
|
+
return null;
|
|
145
218
|
for (const result of results) {
|
|
146
219
|
const files = readdirSync(learningsDir)
|
|
147
220
|
.filter((f) => f.startsWith(`issue-${result.issueNum}-`) && f.endsWith('.md'));
|
package/dist/lib/learning.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learning.js","sourceRoot":"","sources":["../../src/lib/learning.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"learning.js","sourceRoot":"","sources":["../../src/lib/learning.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/logger.d.ts
CHANGED
package/dist/lib/logger.js
CHANGED
|
@@ -2,6 +2,7 @@ const RED = '\x1b[0;31m';
|
|
|
2
2
|
const GREEN = '\x1b[0;32m';
|
|
3
3
|
const YELLOW = '\x1b[1;33m';
|
|
4
4
|
const BLUE = '\x1b[0;34m';
|
|
5
|
+
const MAGENTA = '\x1b[0;35m';
|
|
5
6
|
const CYAN = '\x1b[0;36m';
|
|
6
7
|
const GRAY = '\x1b[0;90m';
|
|
7
8
|
const BOLD = '\x1b[1m';
|
|
@@ -24,5 +25,6 @@ export const log = {
|
|
|
24
25
|
step: (msg) => fmt('STEP', CYAN, msg),
|
|
25
26
|
dry: (msg) => fmt('DRY', YELLOW, msg),
|
|
26
27
|
debug: (msg) => fmt('DEBUG', GRAY, msg),
|
|
28
|
+
rate: (msg) => fmt('RATE', MAGENTA, msg),
|
|
27
29
|
};
|
|
28
30
|
//# sourceMappingURL=logger.js.map
|
package/dist/lib/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA,MAAM,GAAG,GAAG,YAAY,CAAC;AACzB,MAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,MAAM,MAAM,GAAG,YAAY,CAAC;AAC5B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,EAAE,GAAG,SAAS,CAAC;AAErB,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW;IACpD,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;IACnD,OAAO,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;IACrD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC;IACrD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC;IACpD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;IACnD,GAAG,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;IACnD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA,MAAM,GAAG,GAAG,YAAY,CAAC;AACzB,MAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,MAAM,MAAM,GAAG,YAAY,CAAC;AAC5B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,IAAI,GAAG,YAAY,CAAC;AAC1B,MAAM,IAAI,GAAG,SAAS,CAAC;AACvB,MAAM,EAAE,GAAG,SAAS,CAAC;AAErB,SAAS,SAAS;IAChB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW;IACpD,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,IAAI,EAAE,KAAK,SAAS,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;IACnD,OAAO,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;IACrD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC;IACrD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC;IACpD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC;IACnD,GAAG,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC;IACnD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC;IACrD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC;CACvD,CAAC"}
|
package/dist/lib/pipeline.js
CHANGED
|
@@ -223,6 +223,7 @@ export async function processIssue(issueNum, title, body, config, session) {
|
|
|
223
223
|
log.step('Step 2: Setting up worktree');
|
|
224
224
|
let worktreePath;
|
|
225
225
|
let worktreeBranch;
|
|
226
|
+
let worktreeResumed = false;
|
|
226
227
|
try {
|
|
227
228
|
const wt = await setupWorktree({
|
|
228
229
|
issueNum,
|
|
@@ -236,12 +237,13 @@ export async function processIssue(issueNum, title, body, config, session) {
|
|
|
236
237
|
});
|
|
237
238
|
worktreePath = wt.path;
|
|
238
239
|
worktreeBranch = wt.branch;
|
|
240
|
+
worktreeResumed = wt.resumed;
|
|
239
241
|
}
|
|
240
242
|
catch (err) {
|
|
241
243
|
log.error(`Failed to set up worktree for issue #${issueNum}: ${err}`);
|
|
242
244
|
if (!config.dryRun) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
// Re-queue instead of marking failed — this is a setup issue, not an implementation failure
|
|
246
|
+
requeueIssue(config, issueNum);
|
|
245
247
|
}
|
|
246
248
|
return failureResult(issueNum, title, startTime);
|
|
247
249
|
}
|
|
@@ -332,13 +334,19 @@ Rules:
|
|
|
332
334
|
}
|
|
333
335
|
// --- Step 4: Implement ---
|
|
334
336
|
log.step('Step 4: Implementing');
|
|
337
|
+
// Build resume context if worktree was recovered from a previous session
|
|
338
|
+
const resumeNote = worktreeResumed
|
|
339
|
+
? `**IMPORTANT: This worktree contains work from a previous session that was interrupted.**
|
|
340
|
+
Run \`git log --oneline -10\` to see what has already been done.
|
|
341
|
+
Do NOT redo work that is already committed. Build on top of existing progress.\n\n`
|
|
342
|
+
: '';
|
|
335
343
|
if (!config.dryRun) {
|
|
336
344
|
// Load vision and project context
|
|
337
345
|
const visionContext = loadFileIfExists(join(projectDir, '.alpha-loop', 'vision.md'));
|
|
338
346
|
const projectContext = loadFileIfExists(join(projectDir, '.alpha-loop', 'context.md'));
|
|
339
347
|
const previousResult = getPreviousResult(session);
|
|
340
348
|
const learningContext = getLearningContext(join(projectDir, '.alpha-loop', 'learnings'));
|
|
341
|
-
const implementPrompt = buildImplementPrompt({
|
|
349
|
+
const implementPrompt = resumeNote + buildImplementPrompt({
|
|
342
350
|
issueNum,
|
|
343
351
|
title,
|
|
344
352
|
body,
|
|
@@ -363,16 +371,22 @@ Rules:
|
|
|
363
371
|
traceOutput(session.name, issueNum, 'implement', implResult.output);
|
|
364
372
|
stepCosts.push(buildStepCost('implement', issueNum, implResult, config));
|
|
365
373
|
if (implResult.exitCode !== 0) {
|
|
374
|
+
// Auto-commit any uncommitted work before deciding on cleanup
|
|
375
|
+
const dirtyCheck = exec('git status --porcelain', { cwd: worktreePath });
|
|
376
|
+
if (dirtyCheck.stdout.trim()) {
|
|
377
|
+
exec('git add -A', { cwd: worktreePath });
|
|
378
|
+
exec(`git commit -m "wip: partial implementation of #${issueNum} (agent timed out or failed)"`, { cwd: worktreePath });
|
|
379
|
+
}
|
|
366
380
|
if (isTransientError(implResult.output)) {
|
|
367
381
|
log.warn(`Agent hit a transient error during implementation for #${issueNum} — re-queuing`);
|
|
368
382
|
requeueIssue(config, issueNum);
|
|
369
|
-
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup });
|
|
383
|
+
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup, preserveIfCommits: true });
|
|
370
384
|
return failureResult(issueNum, title, startTime, 'transient');
|
|
371
385
|
}
|
|
372
386
|
log.error(`Implementation failed for issue #${issueNum}`);
|
|
373
387
|
labelIssue(config.repo, issueNum, 'failed', 'in-progress');
|
|
374
388
|
commentIssue(config.repo, issueNum, 'Agent loop failed during implementation. See logs for details.');
|
|
375
|
-
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup });
|
|
389
|
+
await cleanupWorktree({ issueNum, projectDir, autoCleanup: config.autoCleanup, preserveIfCommits: true });
|
|
376
390
|
return failureResult(issueNum, title, startTime, 'permanent');
|
|
377
391
|
}
|
|
378
392
|
// Auto-commit if agent didn't
|
|
@@ -728,6 +742,8 @@ Rules:
|
|
|
728
742
|
verifyOutput,
|
|
729
743
|
body,
|
|
730
744
|
config,
|
|
745
|
+
sessionLogsDir: session.logsDir,
|
|
746
|
+
sessionName: session.name,
|
|
731
747
|
});
|
|
732
748
|
// --- Step 9b: Write full traces (Meta-Harness style) ---
|
|
733
749
|
stepsCompleted.push('learn');
|
|
@@ -799,20 +815,27 @@ Rules:
|
|
|
799
815
|
log.dry('Would update issue status to in-review');
|
|
800
816
|
}
|
|
801
817
|
// --- Step 11: Auto-merge ---
|
|
818
|
+
let mergeSucceeded = false;
|
|
802
819
|
if (config.autoMerge && !config.dryRun && prUrl) {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
mergePR(config.repo, worktreeBranch);
|
|
806
|
-
// Update local repo to include merged changes
|
|
807
|
-
exec('git fetch origin', { cwd: projectDir });
|
|
808
|
-
const currentBranch = exec('git rev-parse --abbrev-ref HEAD', { cwd: projectDir }).stdout;
|
|
809
|
-
if (currentBranch !== session.branch) {
|
|
810
|
-
exec(`git checkout "${session.branch}"`, { cwd: projectDir });
|
|
811
|
-
}
|
|
812
|
-
exec(`git pull origin "${session.branch}"`, { cwd: projectDir });
|
|
820
|
+
if (!testsPassing) {
|
|
821
|
+
log.warn('Skipping auto-merge: tests are not passing');
|
|
813
822
|
}
|
|
814
|
-
|
|
815
|
-
log.
|
|
823
|
+
else {
|
|
824
|
+
log.step('Step 11: Auto-merging PR');
|
|
825
|
+
try {
|
|
826
|
+
mergePR(config.repo, worktreeBranch);
|
|
827
|
+
mergeSucceeded = true;
|
|
828
|
+
// Update local repo to include merged changes
|
|
829
|
+
exec('git fetch origin', { cwd: projectDir });
|
|
830
|
+
const currentBranch = exec('git rev-parse --abbrev-ref HEAD', { cwd: projectDir }).stdout;
|
|
831
|
+
if (currentBranch !== session.branch) {
|
|
832
|
+
exec(`git checkout "${session.branch}"`, { cwd: projectDir });
|
|
833
|
+
}
|
|
834
|
+
exec(`git pull origin "${session.branch}"`, { cwd: projectDir });
|
|
835
|
+
}
|
|
836
|
+
catch (err) {
|
|
837
|
+
log.warn(`Auto-merge failed: ${err}`);
|
|
838
|
+
}
|
|
816
839
|
}
|
|
817
840
|
}
|
|
818
841
|
else if (config.dryRun) {
|
|
@@ -820,12 +843,24 @@ Rules:
|
|
|
820
843
|
}
|
|
821
844
|
// --- Step 12: Cleanup ---
|
|
822
845
|
log.step('Step 12: Cleanup');
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
846
|
+
if (!mergeSucceeded && config.autoMerge && prUrl) {
|
|
847
|
+
// Merge was expected but didn't happen (tests failing or merge failed) — preserve worktree for recovery
|
|
848
|
+
await cleanupWorktree({
|
|
849
|
+
issueNum,
|
|
850
|
+
projectDir,
|
|
851
|
+
autoCleanup: config.autoCleanup,
|
|
852
|
+
preserveIfCommits: true,
|
|
853
|
+
dryRun: config.dryRun,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
await cleanupWorktree({
|
|
858
|
+
issueNum,
|
|
859
|
+
projectDir,
|
|
860
|
+
autoCleanup: config.autoCleanup,
|
|
861
|
+
dryRun: config.dryRun,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
829
864
|
const result = {
|
|
830
865
|
issueNum,
|
|
831
866
|
title,
|
|
@@ -874,6 +909,7 @@ export async function processBatch(issues, config, session) {
|
|
|
874
909
|
const batchId = issueNums.join('-');
|
|
875
910
|
let worktreePath;
|
|
876
911
|
let worktreeBranch;
|
|
912
|
+
let worktreeResumed = false;
|
|
877
913
|
try {
|
|
878
914
|
const wt = await setupWorktree({
|
|
879
915
|
issueNum: issues[0].number, // Use first issue number for branch naming
|
|
@@ -887,9 +923,13 @@ export async function processBatch(issues, config, session) {
|
|
|
887
923
|
});
|
|
888
924
|
worktreePath = wt.path;
|
|
889
925
|
worktreeBranch = wt.branch;
|
|
926
|
+
worktreeResumed = wt.resumed;
|
|
890
927
|
}
|
|
891
928
|
catch (err) {
|
|
892
929
|
log.error(`Failed to set up worktree for batch: ${err}`);
|
|
930
|
+
// Re-queue issues back to ready state so they aren't stuck as "In Progress"
|
|
931
|
+
for (const issue of issues)
|
|
932
|
+
requeueIssue(config, issue.number);
|
|
893
933
|
return issues.map((i) => failureResult(i.number, i.title, startTime, 'permanent'));
|
|
894
934
|
}
|
|
895
935
|
// --- Step 3: Fetch comments for all issues ---
|
|
@@ -917,9 +957,15 @@ export async function processBatch(issues, config, session) {
|
|
|
917
957
|
// --- Step 4: Batch plan ---
|
|
918
958
|
log.step('Batch Step 3: Planning all issues');
|
|
919
959
|
const plans = new Map();
|
|
960
|
+
// Build resume context if worktree was recovered from a previous session
|
|
961
|
+
const resumeNote = worktreeResumed
|
|
962
|
+
? `**IMPORTANT: This worktree contains work from a previous session that was interrupted.**
|
|
963
|
+
Run \`git log --oneline -10\` to see what has already been done.
|
|
964
|
+
Do NOT redo work that is already committed. Build on top of existing progress.\n\n`
|
|
965
|
+
: '';
|
|
920
966
|
if (!config.dryRun) {
|
|
921
967
|
try {
|
|
922
|
-
const planPrompt = buildBatchPlanPrompt({ issues: batchIssues });
|
|
968
|
+
const planPrompt = resumeNote + buildBatchPlanPrompt({ issues: batchIssues });
|
|
923
969
|
tracePrompt(session.name, issues[0].number, 'batch-plan', planPrompt);
|
|
924
970
|
const planResult = await spawnAgent({
|
|
925
971
|
agent: config.agent,
|
|
@@ -966,7 +1012,7 @@ export async function processBatch(issues, config, session) {
|
|
|
966
1012
|
const plan = plans.get(i.number) ?? DEFAULT_PLAN;
|
|
967
1013
|
return `### Plan for #${i.number}: ${i.title}\n${plan.implementation || '(no plan)'}`;
|
|
968
1014
|
}).join('\n\n');
|
|
969
|
-
const implementPrompt = buildBatchImplementPrompt({
|
|
1015
|
+
const implementPrompt = resumeNote + buildBatchImplementPrompt({
|
|
970
1016
|
issues: batchIssues,
|
|
971
1017
|
planContent: allPlans,
|
|
972
1018
|
visionContext: visionContext ?? undefined,
|
|
@@ -985,11 +1031,18 @@ export async function processBatch(issues, config, session) {
|
|
|
985
1031
|
traceOutput(session.name, issues[0].number, 'batch-implement', implResult.output);
|
|
986
1032
|
stepCosts.push(buildStepCost('implement', issues[0].number, implResult, config));
|
|
987
1033
|
if (implResult.exitCode !== 0) {
|
|
1034
|
+
// Auto-commit any uncommitted work before deciding on cleanup
|
|
1035
|
+
const dirtyCheck = exec('git status --porcelain', { cwd: worktreePath });
|
|
1036
|
+
if (dirtyCheck.stdout.trim()) {
|
|
1037
|
+
exec('git add -A', { cwd: worktreePath });
|
|
1038
|
+
const issueRefs = issues.map((i) => `#${i.number}`).join(', ');
|
|
1039
|
+
exec(`git commit -m "wip: partial batch implementation of ${issueRefs} (agent timed out or failed)"`, { cwd: worktreePath });
|
|
1040
|
+
}
|
|
988
1041
|
if (isTransientError(implResult.output)) {
|
|
989
1042
|
log.warn('Agent hit a transient error during batch implementation — re-queuing');
|
|
990
1043
|
for (const issue of issues)
|
|
991
1044
|
requeueIssue(config, issue.number);
|
|
992
|
-
await cleanupWorktree({ issueNum: issues[0].number, projectDir, autoCleanup: config.autoCleanup });
|
|
1045
|
+
await cleanupWorktree({ issueNum: issues[0].number, projectDir, autoCleanup: config.autoCleanup, preserveIfCommits: true });
|
|
993
1046
|
return issues.map((i) => failureResult(i.number, i.title, startTime, 'transient'));
|
|
994
1047
|
}
|
|
995
1048
|
log.error('Batch implementation failed');
|
|
@@ -997,7 +1050,7 @@ export async function processBatch(issues, config, session) {
|
|
|
997
1050
|
labelIssue(config.repo, issue.number, 'failed', 'in-progress');
|
|
998
1051
|
commentIssue(config.repo, issue.number, 'Agent loop failed during batch implementation. See logs.');
|
|
999
1052
|
}
|
|
1000
|
-
await cleanupWorktree({ issueNum: issues[0].number, projectDir, autoCleanup: config.autoCleanup });
|
|
1053
|
+
await cleanupWorktree({ issueNum: issues[0].number, projectDir, autoCleanup: config.autoCleanup, preserveIfCommits: true });
|
|
1001
1054
|
return issues.map((i) => failureResult(i.number, i.title, startTime, 'permanent'));
|
|
1002
1055
|
}
|
|
1003
1056
|
// Auto-commit if agent didn't
|
|
@@ -1268,29 +1321,48 @@ export async function processBatch(issues, config, session) {
|
|
|
1268
1321
|
}
|
|
1269
1322
|
}
|
|
1270
1323
|
// --- Step 10: Auto-merge ---
|
|
1324
|
+
let mergeSucceeded = false;
|
|
1271
1325
|
if (config.autoMerge && !config.dryRun && prUrl) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
mergePR(config.repo, worktreeBranch);
|
|
1275
|
-
exec('git fetch origin', { cwd: projectDir });
|
|
1276
|
-
const currentBranch = exec('git rev-parse --abbrev-ref HEAD', { cwd: projectDir }).stdout;
|
|
1277
|
-
if (currentBranch !== session.branch) {
|
|
1278
|
-
exec(`git checkout "${session.branch}"`, { cwd: projectDir });
|
|
1279
|
-
}
|
|
1280
|
-
exec(`git pull origin "${session.branch}"`, { cwd: projectDir });
|
|
1326
|
+
if (!testsPassing) {
|
|
1327
|
+
log.warn('Skipping auto-merge: tests are not passing');
|
|
1281
1328
|
}
|
|
1282
|
-
|
|
1283
|
-
log.
|
|
1329
|
+
else {
|
|
1330
|
+
log.step('Batch Step 9: Auto-merging PR');
|
|
1331
|
+
try {
|
|
1332
|
+
mergePR(config.repo, worktreeBranch);
|
|
1333
|
+
mergeSucceeded = true;
|
|
1334
|
+
exec('git fetch origin', { cwd: projectDir });
|
|
1335
|
+
const currentBranch = exec('git rev-parse --abbrev-ref HEAD', { cwd: projectDir }).stdout;
|
|
1336
|
+
if (currentBranch !== session.branch) {
|
|
1337
|
+
exec(`git checkout "${session.branch}"`, { cwd: projectDir });
|
|
1338
|
+
}
|
|
1339
|
+
exec(`git pull origin "${session.branch}"`, { cwd: projectDir });
|
|
1340
|
+
}
|
|
1341
|
+
catch (err) {
|
|
1342
|
+
log.warn(`Auto-merge failed: ${err}`);
|
|
1343
|
+
}
|
|
1284
1344
|
}
|
|
1285
1345
|
}
|
|
1286
1346
|
// --- Step 11: Cleanup ---
|
|
1287
1347
|
log.step('Batch Step 10: Cleanup');
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1348
|
+
if (!mergeSucceeded && config.autoMerge && prUrl) {
|
|
1349
|
+
// Merge was expected but didn't happen (tests failing or merge failed) — preserve worktree for recovery
|
|
1350
|
+
await cleanupWorktree({
|
|
1351
|
+
issueNum: issues[0].number,
|
|
1352
|
+
projectDir,
|
|
1353
|
+
autoCleanup: config.autoCleanup,
|
|
1354
|
+
preserveIfCommits: true,
|
|
1355
|
+
dryRun: config.dryRun,
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
await cleanupWorktree({
|
|
1360
|
+
issueNum: issues[0].number,
|
|
1361
|
+
projectDir,
|
|
1362
|
+
autoCleanup: config.autoCleanup,
|
|
1363
|
+
dryRun: config.dryRun,
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1294
1366
|
const successCount = results.filter((r) => r.status === 'success').length;
|
|
1295
1367
|
log.success(`Batch complete: ${successCount}/${issues.length} issues succeeded in ${duration}s`);
|
|
1296
1368
|
if (prUrl)
|