@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,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Verification — start the app, use playwright-cli to test it like a real user.
|
|
3
|
+
*
|
|
4
|
+
* This is NOT running E2E test suites. This is an AI agent using playwright-cli
|
|
5
|
+
* to interactively verify that the implemented feature actually works by:
|
|
6
|
+
* 1. Starting the dev server
|
|
7
|
+
* 2. Opening the app in a browser via playwright-cli
|
|
8
|
+
* 3. Navigating, clicking, typing, taking screenshots
|
|
9
|
+
* 4. Reporting PASS or FAIL
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, mkdirSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { exec } from './shell.js';
|
|
14
|
+
import { log } from './logger.js';
|
|
15
|
+
import { spawnAgent } from './agent.js';
|
|
16
|
+
/** Max seconds to wait for app to become ready. */
|
|
17
|
+
const APP_READY_TIMEOUT_S = 60;
|
|
18
|
+
/**
|
|
19
|
+
* Run live verification using playwright-cli.
|
|
20
|
+
* Starts the app, sends agent to test it, returns result.
|
|
21
|
+
*/
|
|
22
|
+
export async function runVerify(options) {
|
|
23
|
+
const { worktree, logFile, issueNum, title, body, config, sessionDir } = options;
|
|
24
|
+
if (config.skipVerify) {
|
|
25
|
+
log.info('Verification skipped (skipVerify=true)');
|
|
26
|
+
return { passed: true, output: 'Verification skipped' };
|
|
27
|
+
}
|
|
28
|
+
if (config.dryRun) {
|
|
29
|
+
log.dry('Would run live verification');
|
|
30
|
+
return { passed: true, output: 'Verification skipped (dry run)' };
|
|
31
|
+
}
|
|
32
|
+
log.step(`Running live verification for issue #${issueNum}`);
|
|
33
|
+
// Check if playwright-cli is available
|
|
34
|
+
const whichResult = exec('which playwright-cli');
|
|
35
|
+
if (whichResult.exitCode !== 0) {
|
|
36
|
+
log.warn('playwright-cli not installed. Install with: npm install -g @anthropic-ai/claude-code');
|
|
37
|
+
log.info('Skipping live verification (no playwright-cli)');
|
|
38
|
+
return { passed: true, output: 'Verification skipped (playwright-cli not installed)' };
|
|
39
|
+
}
|
|
40
|
+
// Detect how to start the app
|
|
41
|
+
let devCmd = config.devCommand;
|
|
42
|
+
if (!devCmd) {
|
|
43
|
+
const pkgJsonPath = join(worktree, 'package.json');
|
|
44
|
+
if (existsSync(pkgJsonPath)) {
|
|
45
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
46
|
+
const scripts = pkg.scripts ?? {};
|
|
47
|
+
for (const name of ['dev', 'start', 'preview']) {
|
|
48
|
+
if (scripts[name]) {
|
|
49
|
+
devCmd = `pnpm ${name}`;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (!devCmd) {
|
|
56
|
+
log.info('No dev/start/preview command found, skipping verification');
|
|
57
|
+
return { passed: true, output: 'Verification skipped (no start command)' };
|
|
58
|
+
}
|
|
59
|
+
const port = config.port ?? 3000;
|
|
60
|
+
// Start the app in the background
|
|
61
|
+
log.info(`Starting app with '${devCmd}' on port ${port}...`);
|
|
62
|
+
const { spawn } = await import('node:child_process');
|
|
63
|
+
const appProcess = spawn('sh', ['-c', `PORT=${port} ${devCmd}`], {
|
|
64
|
+
cwd: worktree,
|
|
65
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
66
|
+
detached: true,
|
|
67
|
+
});
|
|
68
|
+
const appPid = appProcess.pid;
|
|
69
|
+
// Wait for app to be ready
|
|
70
|
+
let ready = false;
|
|
71
|
+
for (let i = 0; i < APP_READY_TIMEOUT_S; i++) {
|
|
72
|
+
const check = exec(`curl -s -o /dev/null http://localhost:${port}`);
|
|
73
|
+
if (check.exitCode === 0) {
|
|
74
|
+
ready = true;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
// Check if process is still alive
|
|
78
|
+
try {
|
|
79
|
+
process.kill(appPid, 0);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
log.error('App process exited before becoming ready');
|
|
83
|
+
return { passed: false, output: 'App failed to start' };
|
|
84
|
+
}
|
|
85
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
86
|
+
}
|
|
87
|
+
if (!ready) {
|
|
88
|
+
log.error(`App did not become ready on port ${port} within ${APP_READY_TIMEOUT_S}s`);
|
|
89
|
+
killProcess(appPid);
|
|
90
|
+
return { passed: false, output: `App failed to start on port ${port}` };
|
|
91
|
+
}
|
|
92
|
+
log.success(`App is ready on port ${port}`);
|
|
93
|
+
// Load saved auth state if it exists
|
|
94
|
+
const authStateDir = join(process.cwd(), '.alpha-loop', 'auth');
|
|
95
|
+
if (existsSync(join(authStateDir, 'state.json'))) {
|
|
96
|
+
log.info('Loading saved auth state...');
|
|
97
|
+
exec(`playwright-cli state-load "${authStateDir}/state.json"`);
|
|
98
|
+
}
|
|
99
|
+
// Save screenshots to session directory
|
|
100
|
+
const screenshotDir = join(sessionDir, 'screenshots', `issue-${issueNum}`);
|
|
101
|
+
mkdirSync(screenshotDir, { recursive: true });
|
|
102
|
+
// Get the diff to understand what changed
|
|
103
|
+
const diffStat = exec(`git diff --stat "origin/${config.baseBranch}...HEAD"`, { cwd: worktree });
|
|
104
|
+
// Get vision context
|
|
105
|
+
const visionFile = join(process.cwd(), '.alpha-loop', 'vision.md');
|
|
106
|
+
const visionContext = existsSync(visionFile) ? readFileSync(visionFile, 'utf-8') : '';
|
|
107
|
+
// Build the verification prompt
|
|
108
|
+
const verifyPrompt = `You are a QA tester verifying that issue #${issueNum} was implemented correctly.
|
|
109
|
+
|
|
110
|
+
## Issue: ${title}
|
|
111
|
+
|
|
112
|
+
${body}
|
|
113
|
+
|
|
114
|
+
## What Changed
|
|
115
|
+
${diffStat.stdout || 'No diff available'}
|
|
116
|
+
|
|
117
|
+
${visionContext ? `## Product Vision\n${visionContext}\n` : ''}
|
|
118
|
+
## Your Task
|
|
119
|
+
|
|
120
|
+
The app is running at http://localhost:${port}. Use the playwright-cli to test it.
|
|
121
|
+
|
|
122
|
+
### Playwright CLI Commands Available
|
|
123
|
+
- \`playwright-cli open http://localhost:${port}\` — Open the app in a browser
|
|
124
|
+
- \`playwright-cli goto <url>\` — Navigate to a page
|
|
125
|
+
- \`playwright-cli snapshot\` — Get a snapshot of the current page with element refs
|
|
126
|
+
- \`playwright-cli click <ref>\` — Click an element (use ref from snapshot, e.g. \`e15\`)
|
|
127
|
+
- \`playwright-cli type <text>\` — Type text into the focused element
|
|
128
|
+
- \`playwright-cli screenshot --path <file>\` — Take a screenshot and save to file
|
|
129
|
+
- \`playwright-cli fill <ref> <text>\` — Fill a form field
|
|
130
|
+
- \`playwright-cli select <ref> <value>\` — Select a dropdown option
|
|
131
|
+
- \`playwright-cli wait <selector>\` — Wait for an element to appear
|
|
132
|
+
- \`playwright-cli console\` — Check browser console for errors
|
|
133
|
+
- \`playwright-cli network\` — Check network requests/responses
|
|
134
|
+
|
|
135
|
+
### Testing Steps
|
|
136
|
+
|
|
137
|
+
1. Open the app: \`playwright-cli open http://localhost:${port}\`
|
|
138
|
+
2. Take a snapshot to see the page structure: \`playwright-cli snapshot\`
|
|
139
|
+
3. Navigate to the feature that was changed
|
|
140
|
+
4. Test the ACTUAL user flow described in the issue:
|
|
141
|
+
- Can you do what the issue says should work?
|
|
142
|
+
- Does the UI render correctly?
|
|
143
|
+
- Do form submissions work end-to-end?
|
|
144
|
+
- Check console for errors: \`playwright-cli console\`
|
|
145
|
+
- Check network for failed requests: \`playwright-cli network\`
|
|
146
|
+
5. Take screenshots at key states (save to the screenshot directory below)
|
|
147
|
+
6. Check for functional gaps:
|
|
148
|
+
- Is the backend wired to the frontend?
|
|
149
|
+
- Are there UI elements that don't respond?
|
|
150
|
+
- Does data persist after submission?
|
|
151
|
+
|
|
152
|
+
### Auth / Login
|
|
153
|
+
${existsSync(authStateDir) ? 'Auth state is pre-loaded. If you need to log in, use the credentials from the environment or .env file.' : 'No auth state saved.'}
|
|
154
|
+
|
|
155
|
+
## Report
|
|
156
|
+
|
|
157
|
+
After testing, output a verification report:
|
|
158
|
+
|
|
159
|
+
### Status: PASS or FAIL
|
|
160
|
+
|
|
161
|
+
### What Was Tested
|
|
162
|
+
- (list each action you took with playwright-cli)
|
|
163
|
+
|
|
164
|
+
### What Worked
|
|
165
|
+
- (list what functioned correctly)
|
|
166
|
+
|
|
167
|
+
### What Failed
|
|
168
|
+
- (list what didn't work, with details and screenshots)
|
|
169
|
+
|
|
170
|
+
### Console Errors
|
|
171
|
+
- (any browser console errors found)
|
|
172
|
+
|
|
173
|
+
### Network Issues
|
|
174
|
+
- (any failed API calls or missing endpoints)
|
|
175
|
+
|
|
176
|
+
### Gaps Found
|
|
177
|
+
- (any disconnects between frontend and backend, missing pieces, etc.)
|
|
178
|
+
|
|
179
|
+
### Screenshots
|
|
180
|
+
Save screenshots to this directory: ${screenshotDir}
|
|
181
|
+
Use descriptive filenames:
|
|
182
|
+
- \`playwright-cli screenshot --path "${screenshotDir}/01-initial-load.png"\`
|
|
183
|
+
- \`playwright-cli screenshot --path "${screenshotDir}/02-after-action.png"\`
|
|
184
|
+
- \`playwright-cli screenshot --path "${screenshotDir}/03-final-state.png"\`
|
|
185
|
+
|
|
186
|
+
IMPORTANT: Use playwright-cli commands to actually interact with the app.
|
|
187
|
+
Navigate, click, type, submit forms. Verify the feature works as a real user would use it.`;
|
|
188
|
+
log.info(`Verification agent: claude + playwright-cli | Testing live at http://localhost:${port}`);
|
|
189
|
+
// Run the verification agent
|
|
190
|
+
const agentResult = await spawnAgent({
|
|
191
|
+
agent: 'claude',
|
|
192
|
+
model: config.model,
|
|
193
|
+
prompt: verifyPrompt,
|
|
194
|
+
cwd: worktree,
|
|
195
|
+
logFile,
|
|
196
|
+
});
|
|
197
|
+
const verifyOutput = agentResult.output;
|
|
198
|
+
// Kill the app process
|
|
199
|
+
log.info('Shutting down app...');
|
|
200
|
+
killProcess(appPid);
|
|
201
|
+
// Close playwright-cli browser sessions
|
|
202
|
+
exec('playwright-cli close-all');
|
|
203
|
+
// Check if verification passed based on agent output
|
|
204
|
+
if (/Status:.*FAIL/i.test(verifyOutput)) {
|
|
205
|
+
log.error('Live verification FAILED');
|
|
206
|
+
return { passed: false, output: verifyOutput };
|
|
207
|
+
}
|
|
208
|
+
else if (/Status:.*PASS/i.test(verifyOutput)) {
|
|
209
|
+
log.success('Live verification PASSED');
|
|
210
|
+
return { passed: true, output: verifyOutput };
|
|
211
|
+
}
|
|
212
|
+
else if (agentResult.exitCode === 0) {
|
|
213
|
+
log.success('Verification completed (agent exit 0)');
|
|
214
|
+
return { passed: true, output: verifyOutput };
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
log.warn(`Verification unclear (agent exit ${agentResult.exitCode})`);
|
|
218
|
+
return { passed: false, output: verifyOutput };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function killProcess(pid) {
|
|
222
|
+
try {
|
|
223
|
+
// Kill process group (negative pid)
|
|
224
|
+
process.kill(-pid, 'SIGTERM');
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
try {
|
|
228
|
+
process.kill(pid, 'SIGTERM');
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// Process already dead
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/lib/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get vision context for prompt injection.
|
|
3
|
+
* Returns the contents of .alpha-loop/vision.md, or null if it doesn't exist.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getVisionContext(projectDir?: string): string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Check if vision has been set up.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasVision(projectDir?: string): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const VISION_FILE = join('.alpha-loop', 'vision.md');
|
|
4
|
+
/**
|
|
5
|
+
* Get vision context for prompt injection.
|
|
6
|
+
* Returns the contents of .alpha-loop/vision.md, or null if it doesn't exist.
|
|
7
|
+
*/
|
|
8
|
+
export function getVisionContext(projectDir = '.') {
|
|
9
|
+
const filePath = join(projectDir, VISION_FILE);
|
|
10
|
+
if (!existsSync(filePath)) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
return readFileSync(filePath, 'utf-8');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if vision has been set up.
|
|
17
|
+
*/
|
|
18
|
+
export function hasVision(projectDir = '.') {
|
|
19
|
+
return existsSync(join(projectDir, VISION_FILE));
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=vision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vision.js","sourceRoot":"","sources":["../../src/lib/vision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,aAAqB,GAAG;IACvD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,aAAqB,GAAG;IAChD,OAAO,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type WorktreeResult = {
|
|
2
|
+
path: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
};
|
|
5
|
+
export type SetupWorktreeOptions = {
|
|
6
|
+
issueNum: number;
|
|
7
|
+
projectDir: string;
|
|
8
|
+
baseBranch: string;
|
|
9
|
+
sessionBranch?: string;
|
|
10
|
+
autoMerge?: boolean;
|
|
11
|
+
skipInstall?: boolean;
|
|
12
|
+
dryRun?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type CleanupWorktreeOptions = {
|
|
15
|
+
issueNum: number;
|
|
16
|
+
projectDir: string;
|
|
17
|
+
autoCleanup?: boolean;
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Create an isolated git worktree for processing an issue.
|
|
22
|
+
* When autoMerge is enabled and a session branch exists,
|
|
23
|
+
* branches from session branch so changes stack across issues.
|
|
24
|
+
*/
|
|
25
|
+
export declare function setupWorktree(options: SetupWorktreeOptions): Promise<WorktreeResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Remove a worktree. Keeps the branch (needed for PR).
|
|
28
|
+
*/
|
|
29
|
+
export declare function cleanupWorktree(options: CleanupWorktreeOptions): Promise<void>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree Manager — create and clean up isolated git worktrees.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, mkdirSync, symlinkSync, readlinkSync, unlinkSync, readFileSync, writeFileSync, appendFileSync } from 'node:fs';
|
|
5
|
+
import { join, resolve, basename } from 'node:path';
|
|
6
|
+
import { exec } from './shell.js';
|
|
7
|
+
import { log } from './logger.js';
|
|
8
|
+
const ENV_FILES = ['.env', '.env.local', '.env.development', '.env.development.local'];
|
|
9
|
+
/**
|
|
10
|
+
* Create an isolated git worktree for processing an issue.
|
|
11
|
+
* When autoMerge is enabled and a session branch exists,
|
|
12
|
+
* branches from session branch so changes stack across issues.
|
|
13
|
+
*/
|
|
14
|
+
export async function setupWorktree(options) {
|
|
15
|
+
const { issueNum, projectDir, baseBranch, sessionBranch, autoMerge, skipInstall, dryRun } = options;
|
|
16
|
+
const branch = `agent/issue-${issueNum}`;
|
|
17
|
+
const worktreesDir = resolve(projectDir, '.worktrees');
|
|
18
|
+
mkdirSync(worktreesDir, { recursive: true });
|
|
19
|
+
const worktreePath = resolve(worktreesDir, `issue-${issueNum}`);
|
|
20
|
+
log.info(`Creating worktree at ${worktreePath} (branch: ${branch})`);
|
|
21
|
+
if (dryRun) {
|
|
22
|
+
log.dry(`Would create worktree: ${worktreePath}`);
|
|
23
|
+
return { path: worktreePath, branch };
|
|
24
|
+
}
|
|
25
|
+
// Clean up existing worktree if present
|
|
26
|
+
if (existsSync(worktreePath)) {
|
|
27
|
+
log.warn(`Worktree already exists at ${worktreePath}, removing...`);
|
|
28
|
+
exec(`git worktree remove "${worktreePath}" --force`, { cwd: projectDir });
|
|
29
|
+
}
|
|
30
|
+
// Delete local branch from previous runs (may exist even without worktree)
|
|
31
|
+
exec(`git branch -D "${branch}"`, { cwd: projectDir });
|
|
32
|
+
// Delete remote branch from previous failed runs
|
|
33
|
+
exec(`git push origin --delete "${branch}"`, { cwd: projectDir });
|
|
34
|
+
// Determine source branch: use session branch when auto-merging and it exists
|
|
35
|
+
let fromBranch = baseBranch;
|
|
36
|
+
if (autoMerge && sessionBranch && sessionBranch !== baseBranch) {
|
|
37
|
+
const remoteCheck = exec(`git rev-parse --verify "origin/${sessionBranch}"`, { cwd: projectDir });
|
|
38
|
+
const localCheck = exec(`git rev-parse --verify "${sessionBranch}"`, { cwd: projectDir });
|
|
39
|
+
if (remoteCheck.exitCode === 0 || localCheck.exitCode === 0) {
|
|
40
|
+
fromBranch = sessionBranch;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Fetch latest
|
|
44
|
+
exec('git fetch origin', { cwd: projectDir });
|
|
45
|
+
// Create worktree from the appropriate branch (try origin/ first, fall back to local)
|
|
46
|
+
log.info(`Branching worktree from: ${fromBranch}`);
|
|
47
|
+
const remoteResult = exec(`git worktree add "${worktreePath}" -b "${branch}" "origin/${fromBranch}"`, { cwd: projectDir });
|
|
48
|
+
if (remoteResult.exitCode !== 0) {
|
|
49
|
+
const localResult = exec(`git worktree add "${worktreePath}" -b "${branch}" "${fromBranch}"`, { cwd: projectDir });
|
|
50
|
+
if (localResult.exitCode !== 0) {
|
|
51
|
+
throw new Error(`Failed to create worktree from ${fromBranch}: ${localResult.stderr}`);
|
|
52
|
+
}
|
|
53
|
+
log.info(`Created worktree from local ${fromBranch}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
log.info(`Created worktree from origin/${fromBranch}`);
|
|
57
|
+
}
|
|
58
|
+
// Symlink env files from main repo to worktree (gitignored files don't exist in worktrees)
|
|
59
|
+
for (const envFile of ENV_FILES) {
|
|
60
|
+
const src = join(projectDir, envFile);
|
|
61
|
+
const dest = join(worktreePath, envFile);
|
|
62
|
+
if (existsSync(src)) {
|
|
63
|
+
// Remove existing file/symlink if present
|
|
64
|
+
if (existsSync(dest)) {
|
|
65
|
+
try {
|
|
66
|
+
unlinkSync(dest);
|
|
67
|
+
}
|
|
68
|
+
catch { /* ignore */ }
|
|
69
|
+
}
|
|
70
|
+
symlinkSync(src, dest);
|
|
71
|
+
log.info(`Symlinked ${envFile} to worktree`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Set COMPOSE_PROJECT_NAME so Docker doesn't use "issue-N" as project name
|
|
75
|
+
ensureComposeProjectName(worktreePath, projectDir);
|
|
76
|
+
// Install dependencies unless skipped
|
|
77
|
+
if (!skipInstall) {
|
|
78
|
+
log.info('Installing dependencies in worktree...');
|
|
79
|
+
const installResult = exec('pnpm install --frozen-lockfile', { cwd: worktreePath });
|
|
80
|
+
if (installResult.exitCode !== 0) {
|
|
81
|
+
// Fall back to regular install
|
|
82
|
+
const fallback = exec('pnpm install', { cwd: worktreePath });
|
|
83
|
+
if (fallback.exitCode !== 0) {
|
|
84
|
+
log.warn('pnpm install had issues, continuing anyway...');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
log.info(`Worktree ready at ${worktreePath}`);
|
|
89
|
+
return { path: worktreePath, branch };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove a worktree. Keeps the branch (needed for PR).
|
|
93
|
+
*/
|
|
94
|
+
export async function cleanupWorktree(options) {
|
|
95
|
+
const { issueNum, projectDir, autoCleanup = true, dryRun } = options;
|
|
96
|
+
const worktreePath = resolve(projectDir, '.worktrees', `issue-${issueNum}`);
|
|
97
|
+
if (!autoCleanup) {
|
|
98
|
+
log.info('Skipping worktree cleanup (autoCleanup=false)');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (dryRun) {
|
|
102
|
+
log.dry(`Would clean up worktree: ${worktreePath}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (existsSync(worktreePath)) {
|
|
106
|
+
log.info(`Removing worktree: ${worktreePath}`);
|
|
107
|
+
const result = exec(`git worktree remove "${worktreePath}" --force`, { cwd: projectDir });
|
|
108
|
+
if (result.exitCode !== 0) {
|
|
109
|
+
log.warn('Could not remove worktree cleanly, forcing...');
|
|
110
|
+
exec(`rm -rf "${worktreePath}"`, { cwd: projectDir });
|
|
111
|
+
exec('git worktree prune', { cwd: projectDir });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
log.info('Worktree cleaned up');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Ensure COMPOSE_PROJECT_NAME is set in the worktree's .env file
|
|
118
|
+
* so Docker Compose doesn't use the worktree directory name (e.g., "issue-2")
|
|
119
|
+
* as the project name for containers.
|
|
120
|
+
*/
|
|
121
|
+
function ensureComposeProjectName(worktreePath, projectDir) {
|
|
122
|
+
const repoName = basename(projectDir);
|
|
123
|
+
const envPath = join(worktreePath, '.env');
|
|
124
|
+
// Check if .env already has COMPOSE_PROJECT_NAME
|
|
125
|
+
if (existsSync(envPath)) {
|
|
126
|
+
// If it's a symlink, we can't modify it — check if the source has the var
|
|
127
|
+
try {
|
|
128
|
+
readlinkSync(envPath);
|
|
129
|
+
// It's a symlink — check if the source .env already has COMPOSE_PROJECT_NAME
|
|
130
|
+
const content = readFileSync(envPath, 'utf-8');
|
|
131
|
+
if (content.includes('COMPOSE_PROJECT_NAME'))
|
|
132
|
+
return;
|
|
133
|
+
// Source doesn't have it — create a .env.compose override instead
|
|
134
|
+
const composePath = join(worktreePath, '.env.compose');
|
|
135
|
+
writeFileSync(composePath, `COMPOSE_PROJECT_NAME=${repoName}\n`);
|
|
136
|
+
log.info(`Set COMPOSE_PROJECT_NAME=${repoName} in .env.compose`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Not a symlink — safe to check/append
|
|
141
|
+
const content = readFileSync(envPath, 'utf-8') ?? '';
|
|
142
|
+
if (content.includes('COMPOSE_PROJECT_NAME'))
|
|
143
|
+
return;
|
|
144
|
+
appendFileSync(envPath, `\nCOMPOSE_PROJECT_NAME=${repoName}\n`);
|
|
145
|
+
log.info(`Added COMPOSE_PROJECT_NAME=${repoName} to .env`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// No .env at all — create one with just COMPOSE_PROJECT_NAME
|
|
150
|
+
writeFileSync(envPath, `COMPOSE_PROJECT_NAME=${repoName}\n`);
|
|
151
|
+
log.info(`Created .env with COMPOSE_PROJECT_NAME=${repoName}`);
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree.js","sourceRoot":"","sources":["../../src/lib/worktree.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bradtaylorsf/alpha-loop",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Agent-agnostic automated development loop: Plan → Build → Test → Review → Ship",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@9.0.0",
|
|
7
|
+
"bin": {
|
|
8
|
+
"alpha-loop": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"templates",
|
|
13
|
+
"agents",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"postbuild": "chmod +x dist/cli.js",
|
|
20
|
+
"dev": "tsx src/cli.ts",
|
|
21
|
+
"test": "jest --runInBand",
|
|
22
|
+
"test:unit": "jest --runInBand",
|
|
23
|
+
"test:watch": "jest --watch",
|
|
24
|
+
"prepublishOnly": "pnpm build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"ai",
|
|
28
|
+
"agent",
|
|
29
|
+
"automation",
|
|
30
|
+
"development",
|
|
31
|
+
"loop",
|
|
32
|
+
"claude",
|
|
33
|
+
"codex",
|
|
34
|
+
"github"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
},
|
|
39
|
+
"author": "Bradley Taylor",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/bradtaylorsf/alpha-loop.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/bradtaylorsf/alpha-loop#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/bradtaylorsf/alpha-loop/issues"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"commander": "^14.0.3",
|
|
51
|
+
"yaml": "^2.8.3",
|
|
52
|
+
"zod": "^4.3.6"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/jest": "^29.5.11",
|
|
56
|
+
"@types/node": "^20.9.0",
|
|
57
|
+
"jest": "^29.7.0",
|
|
58
|
+
"ts-jest": "^29.1.1",
|
|
59
|
+
"ts-node": "^10.9.2",
|
|
60
|
+
"tsx": "^4.6.0",
|
|
61
|
+
"typescript": "^5.2.2"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implementer
|
|
3
|
+
description: Implements GitHub issues by writing code, tests, and committing. The primary coding agent in the loop.
|
|
4
|
+
tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
|
+
model: opus
|
|
6
|
+
skills: api-patterns, api-contracts, testing-patterns, jest-mock-patterns, implementation-planning, git-workflow, sqlite-patterns, security-analysis
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Implementer Agent
|
|
10
|
+
|
|
11
|
+
You implement GitHub issues autonomously. You receive an issue description with acceptance criteria, and you produce working, tested, committed code.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
1. **Read** the issue requirements and acceptance criteria carefully
|
|
16
|
+
2. **Explore** the codebase to understand existing patterns (check CLAUDE.md first)
|
|
17
|
+
3. **Plan** your approach -- which files to create/modify, in what order
|
|
18
|
+
4. **Implement** the changes following existing conventions
|
|
19
|
+
5. **Write tests** for all new functionality (unit tests at minimum)
|
|
20
|
+
6. **Run tests** (`pnpm test`) and fix any failures
|
|
21
|
+
7. **Commit** with a conventional commit message referencing the issue
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Follow CLAUDE.md guidelines strictly
|
|
26
|
+
- Match existing code patterns and conventions
|
|
27
|
+
- Write TypeScript with strict types (no `any`)
|
|
28
|
+
- Use pnpm (never npm or yarn)
|
|
29
|
+
- Write tests before or alongside implementation
|
|
30
|
+
- Run `pnpm test` before committing
|
|
31
|
+
- One logical commit per issue
|
|
32
|
+
- Do NOT modify unrelated files
|
|
33
|
+
- Do NOT add features beyond the issue scope
|
|
34
|
+
- Install dependencies as needed (`pnpm add` / `pnpm add -D`)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reviewer
|
|
3
|
+
description: Reviews code changes, fixes issues found, and produces a review summary. Runs after implementation.
|
|
4
|
+
tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
|
+
model: opus
|
|
6
|
+
skills: code-review, security-analysis, testing-patterns, test-robustness, api-patterns
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Reviewer Agent
|
|
10
|
+
|
|
11
|
+
You review code changes for a completed GitHub issue. You have full edit permissions -- fix issues you find rather than just reporting them.
|
|
12
|
+
|
|
13
|
+
## Process
|
|
14
|
+
|
|
15
|
+
1. **Read** the original issue requirements
|
|
16
|
+
2. **Review** the diff (`git diff origin/master...HEAD`)
|
|
17
|
+
3. **Check** against the code-review skill checklist
|
|
18
|
+
4. **Fix** any CRITICAL or WARNING issues directly
|
|
19
|
+
5. **Run tests** after fixes to verify nothing broke
|
|
20
|
+
6. **Commit** fixes with: `fix: address review findings for #{issue}`
|
|
21
|
+
7. **Report** a brief summary of what you found and fixed
|
|
22
|
+
|
|
23
|
+
## What to Fix Directly
|
|
24
|
+
|
|
25
|
+
- Security vulnerabilities
|
|
26
|
+
- Missing error handling
|
|
27
|
+
- Missing tests for new code paths
|
|
28
|
+
- TypeScript `any` types
|
|
29
|
+
- Console.log left in code
|
|
30
|
+
- Code that doesn't match project conventions
|
|
31
|
+
|
|
32
|
+
## What to Report (Not Fix)
|
|
33
|
+
|
|
34
|
+
- Architectural suggestions that would require significant refactoring
|
|
35
|
+
- Performance optimizations that aren't urgent
|
|
36
|
+
- Style preferences that aren't in the project conventions
|
|
37
|
+
|
|
38
|
+
## Output
|
|
39
|
+
|
|
40
|
+
End your response with a review summary:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
### Review Summary
|
|
44
|
+
**Status**: PASS | FAIL
|
|
45
|
+
**Issues found**: N
|
|
46
|
+
**Issues fixed**: N
|
|
47
|
+
**Issues deferred**: N
|
|
48
|
+
```
|