@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,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Helpers — interact with GitHub via the `gh` CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { writeFileSync, unlinkSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { exec } from './shell.js';
|
|
8
|
+
import { log } from './logger.js';
|
|
9
|
+
/** Max PR body length. GitHub supports 65536 but we leave room for metadata. */
|
|
10
|
+
const MAX_PR_BODY_CHARS = 60_000;
|
|
11
|
+
/**
|
|
12
|
+
* List open milestones for a repository.
|
|
13
|
+
*/
|
|
14
|
+
export function listMilestones(repo) {
|
|
15
|
+
const result = exec(`gh api "repos/${repo}/milestones?state=open&sort=due_on&direction=asc" --jq '[.[] | {number, title, description, openIssues: .open_issues, closedIssues: .closed_issues, dueOn: .due_on, state}]'`);
|
|
16
|
+
if (result.exitCode !== 0) {
|
|
17
|
+
log.warn(`Failed to list milestones: ${result.stderr}`);
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(result.stdout);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
log.warn('Failed to parse milestones JSON');
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch issues to process. When a project board is configured, reads from
|
|
30
|
+
* the board in display order (the order you set by dragging), filtered to
|
|
31
|
+
* "Todo" status. Falls back to label-based polling when no project is set.
|
|
32
|
+
*
|
|
33
|
+
* When a milestone is specified, only issues in that milestone are returned.
|
|
34
|
+
*/
|
|
35
|
+
export function pollIssues(repo, label, limit = 10, options) {
|
|
36
|
+
const project = options?.project;
|
|
37
|
+
const repoOwner = options?.repoOwner ?? repo.split('/')[0];
|
|
38
|
+
const milestone = options?.milestone;
|
|
39
|
+
// If project board is configured, use it for ordering
|
|
40
|
+
if (project && project > 0) {
|
|
41
|
+
return pollIssuesByProject(repoOwner, project, limit, { repo, milestone });
|
|
42
|
+
}
|
|
43
|
+
// Fallback: poll by label
|
|
44
|
+
return pollIssuesByLabel(repo, label, limit, milestone);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Poll from GitHub Project board — items come in the board's display order.
|
|
48
|
+
* Filters to "Todo" status only. When a milestone is specified, cross-references
|
|
49
|
+
* with the GitHub API to only include issues in that milestone.
|
|
50
|
+
*/
|
|
51
|
+
function pollIssuesByProject(owner, project, limit, options) {
|
|
52
|
+
const result = exec(`gh project item-list ${project} --owner "${owner}" --format json --limit 100`);
|
|
53
|
+
if (result.exitCode !== 0) {
|
|
54
|
+
log.warn(`Failed to poll project board: ${result.stderr}`);
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const data = JSON.parse(result.stdout);
|
|
59
|
+
let items = data.items
|
|
60
|
+
.filter((item) => item.status === 'Todo' && item.content?.type === 'Issue');
|
|
61
|
+
// Filter by milestone if specified
|
|
62
|
+
if (options?.milestone && options?.repo) {
|
|
63
|
+
const milestoneIssues = getMilestoneIssueNumbers(options.repo, options.milestone);
|
|
64
|
+
if (milestoneIssues) {
|
|
65
|
+
items = items.filter((item) => milestoneIssues.has(item.content.number));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return items
|
|
69
|
+
.slice(0, limit)
|
|
70
|
+
.map((item) => ({
|
|
71
|
+
number: item.content.number,
|
|
72
|
+
title: item.content.title,
|
|
73
|
+
body: item.content.body ?? '',
|
|
74
|
+
labels: (item.labels ?? []).map((l) => l.name),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
log.warn('Failed to parse project board JSON');
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the set of open issue numbers belonging to a milestone.
|
|
84
|
+
*/
|
|
85
|
+
function getMilestoneIssueNumbers(repo, milestone) {
|
|
86
|
+
const result = exec(`gh issue list --repo "${repo}" --milestone "${milestone}" --state open --json number --limit 100`);
|
|
87
|
+
if (result.exitCode !== 0)
|
|
88
|
+
return null;
|
|
89
|
+
try {
|
|
90
|
+
const issues = JSON.parse(result.stdout);
|
|
91
|
+
return new Set(issues.map((i) => i.number));
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Fallback: poll issues by label when no project board is configured.
|
|
99
|
+
* Optionally filters by milestone.
|
|
100
|
+
*/
|
|
101
|
+
function pollIssuesByLabel(repo, label, limit, milestone) {
|
|
102
|
+
const milestoneFlag = milestone ? ` --milestone "${milestone}"` : '';
|
|
103
|
+
const result = exec(`gh issue list --repo "${repo}" --label "${label}" --state open${milestoneFlag} --json number,title,body,labels --limit ${limit}`);
|
|
104
|
+
if (result.exitCode !== 0) {
|
|
105
|
+
log.warn(`Failed to poll issues: ${result.stderr}`);
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const raw = JSON.parse(result.stdout);
|
|
110
|
+
return raw
|
|
111
|
+
.map((issue) => ({
|
|
112
|
+
number: issue.number,
|
|
113
|
+
title: issue.title,
|
|
114
|
+
body: issue.body ?? '',
|
|
115
|
+
labels: (issue.labels ?? []).map((l) => l.name),
|
|
116
|
+
}))
|
|
117
|
+
.sort((a, b) => a.number - b.number)
|
|
118
|
+
.slice(0, limit);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
log.warn('Failed to parse issues JSON');
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Add/remove labels on an issue.
|
|
127
|
+
*/
|
|
128
|
+
export function labelIssue(repo, issueNum, addLabel, removeLabel) {
|
|
129
|
+
const args = [`gh issue edit ${issueNum} --repo "${repo}" --add-label "${addLabel}"`];
|
|
130
|
+
if (removeLabel) {
|
|
131
|
+
args[0] += ` --remove-label "${removeLabel}"`;
|
|
132
|
+
}
|
|
133
|
+
const result = exec(args[0]);
|
|
134
|
+
if (result.exitCode !== 0) {
|
|
135
|
+
log.warn(`Failed to update labels on issue #${issueNum}: ${result.stderr}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Comment on an issue.
|
|
140
|
+
*/
|
|
141
|
+
export function commentIssue(repo, issueNum, body) {
|
|
142
|
+
const result = exec(`gh issue comment ${issueNum} --repo "${repo}" --body ${JSON.stringify(body)}`);
|
|
143
|
+
if (result.exitCode !== 0) {
|
|
144
|
+
log.warn(`Failed to comment on issue #${issueNum}: ${result.stderr}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Create a PR, or update an existing one if a PR already exists for the branch.
|
|
149
|
+
* Returns the PR URL.
|
|
150
|
+
*/
|
|
151
|
+
export function createPR(options) {
|
|
152
|
+
const { repo, base, head, title, body, cwd } = options;
|
|
153
|
+
// Push the branch first
|
|
154
|
+
const pushResult = exec(`git push -u origin "${head}"`, { cwd });
|
|
155
|
+
if (pushResult.exitCode !== 0) {
|
|
156
|
+
// Try force push if branch exists from previous attempt
|
|
157
|
+
log.warn('Push failed, trying force push...');
|
|
158
|
+
const forceResult = exec(`git push -u origin "${head}" --force`, { cwd });
|
|
159
|
+
if (forceResult.exitCode !== 0) {
|
|
160
|
+
throw new Error(`Failed to push branch ${head}: ${forceResult.stderr}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Write body to a temp file to avoid shell argument length/escaping issues
|
|
164
|
+
const truncatedBody = truncateBody(body);
|
|
165
|
+
const bodyFile = join(tmpdir(), `alpha-loop-pr-body-${Date.now()}`);
|
|
166
|
+
writeFileSync(bodyFile, truncatedBody, 'utf-8');
|
|
167
|
+
try {
|
|
168
|
+
// Check if PR already exists for this branch
|
|
169
|
+
const existingResult = exec(`gh pr list --repo "${repo}" --head "${head}" --json number,url --limit 1`);
|
|
170
|
+
if (existingResult.exitCode === 0 && existingResult.stdout) {
|
|
171
|
+
try {
|
|
172
|
+
const existing = JSON.parse(existingResult.stdout);
|
|
173
|
+
if (existing.length > 0) {
|
|
174
|
+
const prUrl = existing[0].url;
|
|
175
|
+
log.info(`PR already exists: ${prUrl}, updating...`);
|
|
176
|
+
exec(`gh pr edit ${existing[0].number} --repo "${repo}" --body-file "${bodyFile}"`);
|
|
177
|
+
return prUrl;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Fall through to create
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Create new PR using --body-file to avoid shell escaping issues
|
|
185
|
+
const createResult = exec(`gh pr create --repo "${repo}" --base "${base}" --head "${head}" --title ${JSON.stringify(title)} --body-file "${bodyFile}"`);
|
|
186
|
+
if (createResult.exitCode !== 0) {
|
|
187
|
+
throw new Error(`Failed to create PR: ${createResult.stderr}`);
|
|
188
|
+
}
|
|
189
|
+
return createResult.stdout.trim();
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
try {
|
|
193
|
+
unlinkSync(bodyFile);
|
|
194
|
+
}
|
|
195
|
+
catch { /* cleanup best-effort */ }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Merge a PR by branch name.
|
|
200
|
+
*/
|
|
201
|
+
export function mergePR(repo, head, method = 'squash') {
|
|
202
|
+
// Find the PR number by branch
|
|
203
|
+
const listResult = exec(`gh pr list --repo "${repo}" --head "${head}" --json number --limit 1`);
|
|
204
|
+
if (listResult.exitCode !== 0 || !listResult.stdout) {
|
|
205
|
+
log.warn(`No PR found to merge for branch ${head}`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
let prNum;
|
|
209
|
+
try {
|
|
210
|
+
const prs = JSON.parse(listResult.stdout);
|
|
211
|
+
if (prs.length === 0) {
|
|
212
|
+
log.warn(`No PR found to merge for branch ${head}`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
prNum = prs[0].number;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
log.warn('Failed to parse PR list');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const mergeFlag = method === 'squash' ? '--squash' : '--merge';
|
|
222
|
+
const result = exec(`gh pr merge ${prNum} --repo "${repo}" ${mergeFlag} --delete-branch`);
|
|
223
|
+
if (result.exitCode !== 0) {
|
|
224
|
+
throw new Error(`Failed to merge PR #${prNum}: ${result.stderr}`);
|
|
225
|
+
}
|
|
226
|
+
log.info(`PR #${prNum} merged`);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Update project board status for an issue.
|
|
230
|
+
* This is a multi-step operation using gh project commands.
|
|
231
|
+
*/
|
|
232
|
+
export function updateProjectStatus(repo, projectNum, owner, issueNum, status) {
|
|
233
|
+
// Find the item ID for this issue in the project
|
|
234
|
+
const itemResult = exec(`gh project item-list ${projectNum} --owner "${owner}" --format json --limit 100`);
|
|
235
|
+
if (itemResult.exitCode !== 0) {
|
|
236
|
+
log.warn(`Could not list project items: ${itemResult.stderr}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
let itemId;
|
|
240
|
+
try {
|
|
241
|
+
const data = JSON.parse(itemResult.stdout);
|
|
242
|
+
const item = data.items.find((i) => i.content?.number === issueNum);
|
|
243
|
+
itemId = item?.id;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
log.warn('Failed to parse project items');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!itemId) {
|
|
250
|
+
log.warn(`Could not find project item for issue #${issueNum}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// Get the Status field ID and option ID
|
|
254
|
+
const fieldResult = exec(`gh project field-list ${projectNum} --owner "${owner}" --format json`);
|
|
255
|
+
if (fieldResult.exitCode !== 0) {
|
|
256
|
+
log.warn(`Could not list project fields: ${fieldResult.stderr}`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
let fieldId;
|
|
260
|
+
let optionId;
|
|
261
|
+
try {
|
|
262
|
+
const data = JSON.parse(fieldResult.stdout);
|
|
263
|
+
const statusField = data.fields.find((f) => f.name === 'Status');
|
|
264
|
+
if (statusField) {
|
|
265
|
+
fieldId = statusField.id;
|
|
266
|
+
const option = statusField.options?.find((o) => o.name === status);
|
|
267
|
+
optionId = option?.id;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
log.warn('Failed to parse project fields');
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (!fieldId || !optionId) {
|
|
275
|
+
log.warn(`Could not resolve project field/option for status '${status}'`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
// Get project ID
|
|
279
|
+
const projectResult = exec(`gh project view ${projectNum} --owner "${owner}" --format json`);
|
|
280
|
+
if (projectResult.exitCode !== 0) {
|
|
281
|
+
log.warn(`Could not view project: ${projectResult.stderr}`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
let projectId;
|
|
285
|
+
try {
|
|
286
|
+
const data = JSON.parse(projectResult.stdout);
|
|
287
|
+
projectId = data.id;
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
log.warn('Failed to parse project data');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (!projectId) {
|
|
294
|
+
log.warn('Could not get project ID');
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
// Update the item
|
|
298
|
+
const editResult = exec(`gh project item-edit --project-id "${projectId}" --id "${itemId}" --field-id "${fieldId}" --single-select-option-id "${optionId}"`);
|
|
299
|
+
if (editResult.exitCode !== 0) {
|
|
300
|
+
log.warn(`Failed to update project status for #${issueNum}: ${editResult.stderr}`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
log.info(`Project board: #${issueNum} -> ${status}`);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Truncate PR body at 30k chars to stay within GitHub limits.
|
|
307
|
+
*/
|
|
308
|
+
function truncateBody(body) {
|
|
309
|
+
if (body.length <= MAX_PR_BODY_CHARS)
|
|
310
|
+
return body;
|
|
311
|
+
return body.slice(0, MAX_PR_BODY_CHARS) + '\n\n... (body truncated, see full log)';
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/lib/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Config } from './config.js';
|
|
2
|
+
export type ExtractLearningsOptions = {
|
|
3
|
+
issueNum: number;
|
|
4
|
+
title: string;
|
|
5
|
+
status: string;
|
|
6
|
+
retries: number;
|
|
7
|
+
duration: number;
|
|
8
|
+
diff: string;
|
|
9
|
+
testOutput: string;
|
|
10
|
+
reviewOutput: string;
|
|
11
|
+
verifyOutput: string;
|
|
12
|
+
body: string;
|
|
13
|
+
config: Config;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Extract learnings from a completed run.
|
|
17
|
+
* Invokes an agent with the learn prompt and saves the output.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractLearnings(options: ExtractLearningsOptions): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Count learning files in the learnings directory.
|
|
22
|
+
*/
|
|
23
|
+
export declare function countLearnings(learningsDir: string): number;
|
|
24
|
+
/**
|
|
25
|
+
* Get learning context for injection into implementation prompts.
|
|
26
|
+
* Reads the last N learning files and extracts key sections.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getLearningContext(learningsDir: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate a session summary that aggregates learnings across all processed issues.
|
|
31
|
+
* Produces a markdown summary with patterns, anti-patterns, and recommendations.
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateSessionSummary(options: {
|
|
34
|
+
sessionName: string;
|
|
35
|
+
results: Array<{
|
|
36
|
+
issueNum: number;
|
|
37
|
+
title: string;
|
|
38
|
+
status: string;
|
|
39
|
+
duration: number;
|
|
40
|
+
}>;
|
|
41
|
+
learningsDir: string;
|
|
42
|
+
config: Config;
|
|
43
|
+
}): Promise<string | null>;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learning Extractor — extract and aggregate learnings from completed runs.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { log } from './logger.js';
|
|
7
|
+
import { formatTimestamp } from './shell.js';
|
|
8
|
+
import { spawnAgent } from './agent.js';
|
|
9
|
+
import { buildLearnPrompt } from './prompts.js';
|
|
10
|
+
/**
|
|
11
|
+
* Extract learnings from a completed run.
|
|
12
|
+
* Invokes an agent with the learn prompt and saves the output.
|
|
13
|
+
*/
|
|
14
|
+
export async function extractLearnings(options) {
|
|
15
|
+
const { config } = options;
|
|
16
|
+
if (config.skipLearn) {
|
|
17
|
+
log.info('Skipping learning extraction (skipLearn=true)');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
log.step('Extracting learnings from run...');
|
|
21
|
+
const learningsDir = join(process.cwd(), '.alpha-loop', 'learnings');
|
|
22
|
+
mkdirSync(learningsDir, { recursive: true });
|
|
23
|
+
const timestamp = formatTimestamp(new Date());
|
|
24
|
+
const learningFile = join(learningsDir, `issue-${options.issueNum}-${timestamp}.md`);
|
|
25
|
+
const prompt = buildLearnPrompt({
|
|
26
|
+
issueNum: options.issueNum,
|
|
27
|
+
title: options.title,
|
|
28
|
+
status: options.status,
|
|
29
|
+
retries: options.retries,
|
|
30
|
+
duration: options.duration,
|
|
31
|
+
diff: options.diff,
|
|
32
|
+
testOutput: options.testOutput,
|
|
33
|
+
reviewOutput: options.reviewOutput,
|
|
34
|
+
verifyOutput: options.verifyOutput,
|
|
35
|
+
body: options.body,
|
|
36
|
+
});
|
|
37
|
+
if (config.dryRun) {
|
|
38
|
+
log.dry(`Would extract learnings to ${learningFile}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const result = await spawnAgent({
|
|
42
|
+
agent: 'claude',
|
|
43
|
+
model: config.reviewModel,
|
|
44
|
+
prompt,
|
|
45
|
+
cwd: process.cwd(),
|
|
46
|
+
logFile: undefined,
|
|
47
|
+
});
|
|
48
|
+
if (result.exitCode !== 0 || !result.output.trim()) {
|
|
49
|
+
log.warn(`Learning extraction failed (exit ${result.exitCode}, output ${result.output.length} chars), skipping`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const output = result.output.trim();
|
|
53
|
+
// Validate output has frontmatter, wrap if not
|
|
54
|
+
if (output.startsWith('---')) {
|
|
55
|
+
writeFileSync(learningFile, output + '\n');
|
|
56
|
+
log.success(`Learning saved to ${learningFile}`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const today = new Date().toISOString().split('T')[0];
|
|
60
|
+
const wrapped = `---
|
|
61
|
+
issue: ${options.issueNum}
|
|
62
|
+
status: ${options.status}
|
|
63
|
+
retries: ${options.retries}
|
|
64
|
+
duration: ${options.duration}
|
|
65
|
+
date: ${today}
|
|
66
|
+
---
|
|
67
|
+
${output}`;
|
|
68
|
+
writeFileSync(learningFile, wrapped + '\n');
|
|
69
|
+
log.success(`Learning saved to ${learningFile} (added frontmatter)`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Count learning files in the learnings directory.
|
|
74
|
+
*/
|
|
75
|
+
export function countLearnings(learningsDir) {
|
|
76
|
+
if (!existsSync(learningsDir))
|
|
77
|
+
return 0;
|
|
78
|
+
return readdirSync(learningsDir)
|
|
79
|
+
.filter((f) => f.startsWith('issue-') && f.endsWith('.md'))
|
|
80
|
+
.length;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get learning context for injection into implementation prompts.
|
|
84
|
+
* Reads the last N learning files and extracts key sections.
|
|
85
|
+
*/
|
|
86
|
+
export function getLearningContext(learningsDir) {
|
|
87
|
+
if (!existsSync(learningsDir))
|
|
88
|
+
return '';
|
|
89
|
+
const files = readdirSync(learningsDir)
|
|
90
|
+
.filter((f) => f.startsWith('issue-') && f.endsWith('.md'))
|
|
91
|
+
.sort()
|
|
92
|
+
.reverse()
|
|
93
|
+
.slice(0, 5);
|
|
94
|
+
if (files.length === 0)
|
|
95
|
+
return '';
|
|
96
|
+
const sections = ['## Learnings from Previous Runs', ''];
|
|
97
|
+
for (const file of files) {
|
|
98
|
+
const content = readFileSync(join(learningsDir, file), 'utf-8');
|
|
99
|
+
// Extract issue number and status from frontmatter
|
|
100
|
+
const issueMatch = content.match(/^issue:\s*(.+)$/m);
|
|
101
|
+
const statusMatch = content.match(/^status:\s*(.+)$/m);
|
|
102
|
+
const issue = issueMatch?.[1] ?? 'unknown';
|
|
103
|
+
const status = statusMatch?.[1] ?? 'unknown';
|
|
104
|
+
sections.push(`### Run #${issue} (${status})`);
|
|
105
|
+
// Extract What Worked section
|
|
106
|
+
const workedMatch = content.match(/## What Worked\n([\s\S]*?)(?=\n## |$)/);
|
|
107
|
+
if (workedMatch)
|
|
108
|
+
sections.push(workedMatch[1].trim());
|
|
109
|
+
// Extract What Failed section
|
|
110
|
+
const failedMatch = content.match(/## What Failed\n([\s\S]*?)(?=\n## |$)/);
|
|
111
|
+
if (failedMatch)
|
|
112
|
+
sections.push(failedMatch[1].trim());
|
|
113
|
+
sections.push('');
|
|
114
|
+
}
|
|
115
|
+
// Extract anti-patterns from recent learnings
|
|
116
|
+
const antiPatterns = [];
|
|
117
|
+
const recentFiles = readdirSync(learningsDir)
|
|
118
|
+
.filter((f) => f.startsWith('issue-') && f.endsWith('.md'))
|
|
119
|
+
.sort()
|
|
120
|
+
.reverse()
|
|
121
|
+
.slice(0, 10);
|
|
122
|
+
for (const file of recentFiles) {
|
|
123
|
+
const content = readFileSync(join(learningsDir, file), 'utf-8');
|
|
124
|
+
const apMatch = content.match(/## Anti-Patterns\n([\s\S]*?)(?=\n## |$)/);
|
|
125
|
+
if (apMatch)
|
|
126
|
+
antiPatterns.push(apMatch[1].trim());
|
|
127
|
+
}
|
|
128
|
+
if (antiPatterns.length > 0) {
|
|
129
|
+
sections.push('', '## Known Anti-Patterns to Avoid');
|
|
130
|
+
sections.push(antiPatterns.join('\n'));
|
|
131
|
+
}
|
|
132
|
+
return sections.join('\n');
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Generate a session summary that aggregates learnings across all processed issues.
|
|
136
|
+
* Produces a markdown summary with patterns, anti-patterns, and recommendations.
|
|
137
|
+
*/
|
|
138
|
+
export async function generateSessionSummary(options) {
|
|
139
|
+
const { sessionName, results, learningsDir, config } = options;
|
|
140
|
+
if (config.skipLearn || config.dryRun || results.length === 0)
|
|
141
|
+
return null;
|
|
142
|
+
log.step('Generating session summary...');
|
|
143
|
+
// Collect all learnings from this session
|
|
144
|
+
const learningContents = [];
|
|
145
|
+
for (const result of results) {
|
|
146
|
+
const files = readdirSync(learningsDir)
|
|
147
|
+
.filter((f) => f.startsWith(`issue-${result.issueNum}-`) && f.endsWith('.md'));
|
|
148
|
+
for (const file of files) {
|
|
149
|
+
learningContents.push(readFileSync(join(learningsDir, file), 'utf-8'));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (learningContents.length === 0)
|
|
153
|
+
return null;
|
|
154
|
+
const successCount = results.filter((r) => r.status === 'success').length;
|
|
155
|
+
const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
|
|
156
|
+
const prompt = `Analyze these learnings from a development session and produce a concise session summary with actionable recommendations.
|
|
157
|
+
|
|
158
|
+
## Session: ${sessionName}
|
|
159
|
+
- Issues processed: ${results.length} (${successCount} succeeded, ${results.length - successCount} failed)
|
|
160
|
+
- Total duration: ${Math.round(totalDuration / 60)} minutes
|
|
161
|
+
|
|
162
|
+
## Individual Learnings
|
|
163
|
+
|
|
164
|
+
${learningContents.join('\n\n---\n\n')}
|
|
165
|
+
|
|
166
|
+
Output ONLY this markdown structure:
|
|
167
|
+
|
|
168
|
+
# Session Summary: ${sessionName}
|
|
169
|
+
|
|
170
|
+
## Overview
|
|
171
|
+
- (2-3 sentences summarizing the session)
|
|
172
|
+
|
|
173
|
+
## Recurring Patterns
|
|
174
|
+
- (patterns that appeared across multiple issues — these should be reinforced)
|
|
175
|
+
|
|
176
|
+
## Recurring Anti-Patterns
|
|
177
|
+
- (problems that kept happening — these need fixing)
|
|
178
|
+
|
|
179
|
+
## Recommendations
|
|
180
|
+
- (specific, actionable improvements for the agent prompts, project config, or workflow)
|
|
181
|
+
- (e.g., "Update the implement prompt to always check for X before Y")
|
|
182
|
+
- (e.g., "Add a pre-check for port conflicts before starting verification")
|
|
183
|
+
|
|
184
|
+
## Metrics
|
|
185
|
+
| Metric | Value |
|
|
186
|
+
|--------|-------|
|
|
187
|
+
| Issues processed | ${results.length} |
|
|
188
|
+
| Success rate | ${Math.round((successCount / results.length) * 100)}% |
|
|
189
|
+
| Avg duration | ${Math.round(totalDuration / results.length)}s |
|
|
190
|
+
| Total duration | ${Math.round(totalDuration / 60)} min |`;
|
|
191
|
+
const agentResult = await spawnAgent({
|
|
192
|
+
agent: 'claude',
|
|
193
|
+
model: config.reviewModel,
|
|
194
|
+
prompt,
|
|
195
|
+
cwd: process.cwd(),
|
|
196
|
+
logFile: undefined,
|
|
197
|
+
});
|
|
198
|
+
if (agentResult.exitCode !== 0 || !agentResult.output.trim()) {
|
|
199
|
+
log.warn('Session summary generation failed');
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const summaryFile = join(learningsDir, `session-summary-${sessionName.replace(/\//g, '-')}.md`);
|
|
203
|
+
writeFileSync(summaryFile, agentResult.output.trim() + '\n');
|
|
204
|
+
log.success(`Session summary saved: ${summaryFile}`);
|
|
205
|
+
return agentResult.output.trim();
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=learning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning.js","sourceRoot":"","sources":["../../src/lib/learning.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const RED = '\x1b[0;31m';
|
|
2
|
+
const GREEN = '\x1b[0;32m';
|
|
3
|
+
const YELLOW = '\x1b[1;33m';
|
|
4
|
+
const BLUE = '\x1b[0;34m';
|
|
5
|
+
const CYAN = '\x1b[0;36m';
|
|
6
|
+
const GRAY = '\x1b[0;90m';
|
|
7
|
+
const BOLD = '\x1b[1m';
|
|
8
|
+
const NC = '\x1b[0m';
|
|
9
|
+
function timestamp() {
|
|
10
|
+
const now = new Date();
|
|
11
|
+
const h = String(now.getHours()).padStart(2, '0');
|
|
12
|
+
const m = String(now.getMinutes()).padStart(2, '0');
|
|
13
|
+
const s = String(now.getSeconds()).padStart(2, '0');
|
|
14
|
+
return `${h}:${m}:${s}`;
|
|
15
|
+
}
|
|
16
|
+
function fmt(label, color, msg) {
|
|
17
|
+
console.error(`${color}[${label}]${NC} ${timestamp()} ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
export const log = {
|
|
20
|
+
info: (msg) => fmt('INFO', BLUE, msg),
|
|
21
|
+
success: (msg) => fmt('OK', GREEN, msg),
|
|
22
|
+
warn: (msg) => fmt('WARN', YELLOW, msg),
|
|
23
|
+
error: (msg) => fmt('ERROR', RED, msg),
|
|
24
|
+
step: (msg) => fmt('STEP', CYAN, msg),
|
|
25
|
+
dry: (msg) => fmt('DRY', YELLOW, msg),
|
|
26
|
+
debug: (msg) => fmt('DEBUG', GRAY, msg),
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +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;CACtD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Config } from './config.js';
|
|
2
|
+
import type { SessionContext } from './session.js';
|
|
3
|
+
export type PipelineResult = {
|
|
4
|
+
issueNum: number;
|
|
5
|
+
title: string;
|
|
6
|
+
status: 'success' | 'failure';
|
|
7
|
+
prUrl?: string;
|
|
8
|
+
testsPassing: boolean;
|
|
9
|
+
verifyPassing: boolean;
|
|
10
|
+
duration: number;
|
|
11
|
+
filesChanged: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Process a single issue through the full pipeline.
|
|
15
|
+
* Steps: status → worktree → plan → implement → test+retry → verify+retry →
|
|
16
|
+
* review → PR → learnings → update → auto-merge → cleanup
|
|
17
|
+
*/
|
|
18
|
+
export declare function processIssue(issueNum: number, title: string, body: string, config: Config, session: SessionContext): Promise<PipelineResult>;
|