@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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +294 -0
  3. package/agents/implementer.md +30 -0
  4. package/agents/reviewer.md +29 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +57 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/auth.d.ts +1 -0
  9. package/dist/commands/auth.js +89 -0
  10. package/dist/commands/auth.js.map +1 -0
  11. package/dist/commands/history.d.ts +8 -0
  12. package/dist/commands/history.js +185 -0
  13. package/dist/commands/history.js.map +1 -0
  14. package/dist/commands/init.d.ts +1 -0
  15. package/dist/commands/init.js +241 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/run.d.ts +15 -0
  18. package/dist/commands/run.js +321 -0
  19. package/dist/commands/run.js.map +1 -0
  20. package/dist/commands/scan.d.ts +1 -0
  21. package/dist/commands/scan.js +50 -0
  22. package/dist/commands/scan.js.map +1 -0
  23. package/dist/commands/sync.d.ts +20 -0
  24. package/dist/commands/sync.js +149 -0
  25. package/dist/commands/sync.js.map +1 -0
  26. package/dist/commands/vision.d.ts +1 -0
  27. package/dist/commands/vision.js +194 -0
  28. package/dist/commands/vision.js.map +1 -0
  29. package/dist/engine/agents.d.ts +41 -0
  30. package/dist/engine/agents.js +90 -0
  31. package/dist/engine/agents.js.map +1 -0
  32. package/dist/engine/config.d.ts +71 -0
  33. package/dist/engine/config.js +73 -0
  34. package/dist/engine/config.js.map +1 -0
  35. package/dist/engine/prerequisites.d.ts +34 -0
  36. package/dist/engine/prerequisites.js +90 -0
  37. package/dist/engine/prerequisites.js.map +1 -0
  38. package/dist/lib/agent.d.ts +25 -0
  39. package/dist/lib/agent.js +97 -0
  40. package/dist/lib/agent.js.map +1 -0
  41. package/dist/lib/config.d.ts +35 -0
  42. package/dist/lib/config.js +179 -0
  43. package/dist/lib/config.js.map +1 -0
  44. package/dist/lib/context.d.ts +17 -0
  45. package/dist/lib/context.js +96 -0
  46. package/dist/lib/context.js.map +1 -0
  47. package/dist/lib/github.d.ts +61 -0
  48. package/dist/lib/github.js +313 -0
  49. package/dist/lib/github.js.map +1 -0
  50. package/dist/lib/learning.d.ts +43 -0
  51. package/dist/lib/learning.js +207 -0
  52. package/dist/lib/learning.js.map +1 -0
  53. package/dist/lib/logger.d.ts +9 -0
  54. package/dist/lib/logger.js +28 -0
  55. package/dist/lib/logger.js.map +1 -0
  56. package/dist/lib/pipeline.d.ts +18 -0
  57. package/dist/lib/pipeline.js +456 -0
  58. package/dist/lib/pipeline.js.map +1 -0
  59. package/dist/lib/preflight.d.ts +33 -0
  60. package/dist/lib/preflight.js +123 -0
  61. package/dist/lib/preflight.js.map +1 -0
  62. package/dist/lib/prerequisites.d.ts +12 -0
  63. package/dist/lib/prerequisites.js +54 -0
  64. package/dist/lib/prerequisites.js.map +1 -0
  65. package/dist/lib/prompts.d.ts +44 -0
  66. package/dist/lib/prompts.js +102 -0
  67. package/dist/lib/prompts.js.map +1 -0
  68. package/dist/lib/session.d.ts +28 -0
  69. package/dist/lib/session.js +173 -0
  70. package/dist/lib/session.js.map +1 -0
  71. package/dist/lib/shell.d.ts +32 -0
  72. package/dist/lib/shell.js +95 -0
  73. package/dist/lib/shell.js.map +1 -0
  74. package/dist/lib/testing.d.ts +10 -0
  75. package/dist/lib/testing.js +51 -0
  76. package/dist/lib/testing.js.map +1 -0
  77. package/dist/lib/verify.d.ts +18 -0
  78. package/dist/lib/verify.js +235 -0
  79. package/dist/lib/verify.js.map +1 -0
  80. package/dist/lib/vision.d.ts +9 -0
  81. package/dist/lib/vision.js +21 -0
  82. package/dist/lib/vision.js.map +1 -0
  83. package/dist/lib/worktree.d.ts +29 -0
  84. package/dist/lib/worktree.js +153 -0
  85. package/dist/lib/worktree.js.map +1 -0
  86. package/package.json +63 -0
  87. package/templates/agents/implementer.md +34 -0
  88. package/templates/agents/reviewer.md +48 -0
  89. package/templates/skills/code-review/SKILL.md +58 -0
  90. package/templates/skills/git-workflow/SKILL.md +53 -0
  91. package/templates/skills/implementation-planning/SKILL.md +64 -0
  92. package/templates/skills/security-analysis/SKILL.md +560 -0
  93. package/templates/skills/security-analysis/scripts/security-scanner.sh +227 -0
  94. package/templates/skills/test-robustness/SKILL.md +897 -0
  95. package/templates/skills/testing-patterns/SKILL.md +75 -0
@@ -0,0 +1,54 @@
1
+ import { commandExists, execAsync } from './shell.js';
2
+ const AGENT_INSTALL_URLS = {
3
+ claude: 'https://claude.ai/code',
4
+ codex: 'https://github.com/openai/codex',
5
+ opencode: 'https://github.com/sst/opencode',
6
+ };
7
+ export class PrerequisiteError extends Error {
8
+ errors;
9
+ constructor(errors) {
10
+ super(errors.join('\n'));
11
+ this.errors = errors;
12
+ this.name = 'PrerequisiteError';
13
+ }
14
+ }
15
+ /**
16
+ * Verify required tools are installed: gh, git, and the configured agent CLI.
17
+ * Throws PrerequisiteError with all failures if any checks fail.
18
+ */
19
+ export async function checkPrerequisites(config) {
20
+ const errors = [];
21
+ // Check git
22
+ if (!commandExists('git')) {
23
+ errors.push('git not found. Please install git.');
24
+ }
25
+ else {
26
+ // Check we're in a git repo
27
+ const gitCheck = await execAsync('git rev-parse --git-dir');
28
+ if (gitCheck.exitCode !== 0) {
29
+ errors.push('Not in a git repository.');
30
+ }
31
+ }
32
+ // Check gh CLI
33
+ if (!commandExists('gh')) {
34
+ errors.push('gh CLI not found. Install: https://cli.github.com/');
35
+ }
36
+ else {
37
+ // Check gh auth
38
+ const authCheck = await execAsync('gh auth status');
39
+ if (authCheck.exitCode !== 0) {
40
+ errors.push('gh not authenticated. Run: gh auth login');
41
+ }
42
+ }
43
+ // Check agent CLI
44
+ const agent = config.agent;
45
+ if (!commandExists(agent)) {
46
+ const installUrl = AGENT_INSTALL_URLS[agent] ?? '';
47
+ const installHint = installUrl ? ` Install: ${installUrl}` : '';
48
+ errors.push(`${agent} CLI not found.${installHint}`);
49
+ }
50
+ if (errors.length > 0) {
51
+ throw new PrerequisiteError(errors);
52
+ }
53
+ }
54
+ //# sourceMappingURL=prerequisites.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerequisites.js","sourceRoot":"","sources":["../../src/lib/prerequisites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMtD,MAAM,kBAAkB,GAA2B;IACjD,MAAM,EAAE,wBAAwB;IAChC,KAAK,EAAE,iCAAiC;IACxC,QAAQ,EAAE,iCAAiC;CAC5C,CAAC;AAEF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACd;IAA5B,YAA4B,MAAgB;QAC1C,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QADC,WAAM,GAAN,MAAM,CAAU;QAE1C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAA0B;IACjE,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,YAAY;IACZ,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAC5D,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,eAAe;IACf,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;IACpE,CAAC;SAAM,CAAC;QACN,gBAAgB;QAChB,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACpD,IAAI,SAAS,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,kBAAkB,WAAW,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;AACH,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Prompt Builder — construct prompts for AI agents.
3
+ * Pure string template functions with no side effects.
4
+ */
5
+ export type ImplementPromptOptions = {
6
+ issueNum: number;
7
+ title: string;
8
+ body: string;
9
+ visionContext?: string;
10
+ projectContext?: string;
11
+ previousResult?: string;
12
+ learningContext?: string;
13
+ };
14
+ export type ReviewPromptOptions = {
15
+ issueNum: number;
16
+ title: string;
17
+ body: string;
18
+ baseBranch: string;
19
+ visionContext?: string;
20
+ };
21
+ export type LearnPromptOptions = {
22
+ issueNum: number;
23
+ title: string;
24
+ status: string;
25
+ retries: number;
26
+ duration: number;
27
+ diff: string;
28
+ testOutput: string;
29
+ reviewOutput: string;
30
+ verifyOutput: string;
31
+ body?: string;
32
+ };
33
+ /**
34
+ * Build the implementation prompt for an AI agent.
35
+ */
36
+ export declare function buildImplementPrompt(options: ImplementPromptOptions): string;
37
+ /**
38
+ * Build the code review prompt for an AI agent.
39
+ */
40
+ export declare function buildReviewPrompt(options: ReviewPromptOptions): string;
41
+ /**
42
+ * Build the learning extraction prompt.
43
+ */
44
+ export declare function buildLearnPrompt(options: LearnPromptOptions): string;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Prompt Builder — construct prompts for AI agents.
3
+ * Pure string template functions with no side effects.
4
+ */
5
+ /**
6
+ * Build the implementation prompt for an AI agent.
7
+ */
8
+ export function buildImplementPrompt(options) {
9
+ const { issueNum, title, body, visionContext, projectContext, previousResult, learningContext } = options;
10
+ const sections = [
11
+ `Implement GitHub issue #${issueNum}: ${title}`,
12
+ '',
13
+ body,
14
+ ];
15
+ if (visionContext) {
16
+ sections.push('', '', '## Product Vision', visionContext);
17
+ }
18
+ if (projectContext) {
19
+ sections.push('', '', '## Technical Context', projectContext);
20
+ }
21
+ if (previousResult) {
22
+ sections.push('', '', previousResult);
23
+ }
24
+ if (learningContext) {
25
+ sections.push('', '', learningContext);
26
+ }
27
+ sections.push('', '## Before You Start', '1. Read the product vision and technical context above', '2. Make decisions that align with the target users and current priority', '3. Understand how your changes connect to existing code', "4. If you're creating new files, make sure they're wired into the appropriate entry points", '', '## After Implementing', '1. Write tests for your changes', '2. Run the test command to verify', `3. Commit with: git commit -m "feat: ${title} (closes #${issueNum})"`);
28
+ return sections.join('\n');
29
+ }
30
+ /**
31
+ * Build the code review prompt for an AI agent.
32
+ */
33
+ export function buildReviewPrompt(options) {
34
+ const { issueNum, title, body, baseBranch, visionContext } = options;
35
+ const sections = [
36
+ `Review the code changes for issue #${issueNum}: ${title}`,
37
+ '',
38
+ `Run git diff origin/${baseBranch}...HEAD to see what changed. Then read the actual files that were modified.`,
39
+ '',
40
+ 'Original requirements:',
41
+ body,
42
+ ];
43
+ if (visionContext) {
44
+ sections.push('', '', '## Product Vision (guide your review decisions)', visionContext);
45
+ }
46
+ sections.push('', '## Review Checklist', '', '### 1. Functional Completeness (MOST IMPORTANT)', '- Does the implementation FULLY address the issue requirements?', '- Are there any acceptance criteria that were NOT implemented?', '- If backend API endpoints were created, are they called from the frontend?', '- If frontend components were created, do they have working backend endpoints?', '- Are there any dead code paths (created but never wired in)?', '- Does the feature work end-to-end (data flows from UI → API → database → back to UI)?', '', '### 2. Integration Gaps', '- Are new routes/endpoints registered in the server entry point?', '- Are new components imported and rendered in the app?', '- Are new database tables/columns used by the API?', '- Are there missing imports, missing route registrations, or orphaned files?', '', '### 3. Code Quality', '- Security issues (injection, XSS, auth bypass)', '- Missing error handling for user-facing operations', '- Missing tests for new functionality', '- Code follows project conventions', '', '### 4. UX Review', '- Do UI changes match the target user profile?', '- Are error states handled (loading, empty, error)?', '- Is the feature discoverable (can users find it)?', '', '## Actions', '', 'For any issues you find:', `- CRITICAL (gaps, missing wiring, broken features): FIX THEM directly, run tests, commit with "fix: address review findings for #${issueNum}"`, '- WARNING (quality, security): FIX THEM directly if possible', '- INFO (suggestions, minor improvements): Note them in your report but don\'t block', '', 'After fixing, output a structured review report:', '', '### Findings Fixed', '- (list what you found and fixed)', '', '### Remaining Gaps', '- (anything you couldn\'t fix — these need human attention)', '', '### Verification Notes', '- (what a human should manually check)');
47
+ return sections.join('\n');
48
+ }
49
+ /**
50
+ * Build the learning extraction prompt.
51
+ */
52
+ export function buildLearnPrompt(options) {
53
+ const { issueNum, title, status, retries, duration, diff, testOutput, reviewOutput, verifyOutput, body, } = options;
54
+ const today = new Date().toISOString().split('T')[0];
55
+ return `Analyze this completed development run. Output ONLY a markdown document with the exact structure below. Keep each section to 2-3 bullet points max. Be factual and concise -- no creative writing.
56
+
57
+ ## Run Info
58
+ - Issue: #${issueNum} "${title}"
59
+ - Status: ${status}
60
+ - Retries: ${retries}
61
+ - Duration: ${duration}s
62
+
63
+ ## Issue Requirements
64
+ ${body || '(no description)'}
65
+
66
+ ## Code Changes
67
+ ${diff || '(no diff available)'}
68
+
69
+ ## Test Results
70
+ ${testOutput || '(no test output)'}
71
+
72
+ ## Review Findings
73
+ ${reviewOutput || '(no review output)'}
74
+
75
+ ## Verification Results
76
+ ${verifyOutput || '(no verification output)'}
77
+
78
+ Output ONLY this markdown structure, nothing else:
79
+
80
+ ---
81
+ issue: ${issueNum}
82
+ status: ${status}
83
+ retries: ${retries}
84
+ duration: ${duration}
85
+ date: ${today}
86
+ ---
87
+ ## What Worked
88
+ - (list what went well)
89
+
90
+ ## What Failed
91
+ - (list what went wrong, or "Nothing" if all passed)
92
+
93
+ ## Patterns
94
+ - (reusable patterns discovered)
95
+
96
+ ## Anti-Patterns
97
+ - (mistakes to avoid in future)
98
+
99
+ ## Suggested Skill Updates
100
+ - (specific skill file changes, or "None")`;
101
+ }
102
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/lib/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Config } from './config.js';
2
+ import type { PipelineResult } from './pipeline.js';
3
+ export type SessionContext = {
4
+ name: string;
5
+ branch: string;
6
+ resultsDir: string;
7
+ logsDir: string;
8
+ results: PipelineResult[];
9
+ };
10
+ /**
11
+ * Create a new session context with timestamp-based name.
12
+ * Optionally creates a session branch when autoMerge is enabled.
13
+ */
14
+ export declare function createSession(config: Config): SessionContext;
15
+ /**
16
+ * Save a pipeline result to the session directory as JSON.
17
+ */
18
+ export declare function saveResult(session: SessionContext, result: PipelineResult): void;
19
+ /**
20
+ * Get the previous issue result formatted for prompt context.
21
+ * Returns null if no previous results exist.
22
+ */
23
+ export declare function getPreviousResult(session: SessionContext): string | null;
24
+ /**
25
+ * Finalize session: commit learnings to session branch, create session PR.
26
+ * Only runs when autoMerge is enabled and issues were processed.
27
+ */
28
+ export declare function finalizeSession(session: SessionContext, config: Config): Promise<string | null>;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Session Management — create sessions, save results, finalize with PR.
3
+ */
4
+ import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { log } from './logger.js';
7
+ import { exec, formatTimestamp } from './shell.js';
8
+ import { createPR } from './github.js';
9
+ /**
10
+ * Create a new session context with timestamp-based name.
11
+ * Optionally creates a session branch when autoMerge is enabled.
12
+ */
13
+ export function createSession(config) {
14
+ const now = new Date();
15
+ const timestamp = formatTimestamp(now);
16
+ const name = `session/${timestamp}`;
17
+ const branch = config.mergeTo || name;
18
+ const projectDir = process.cwd();
19
+ const resultsDir = join(projectDir, '.alpha-loop', 'sessions', name);
20
+ const logsDir = join(resultsDir, 'logs');
21
+ mkdirSync(resultsDir, { recursive: true });
22
+ mkdirSync(logsDir, { recursive: true });
23
+ // Create session branch and draft PR if auto-merge is enabled
24
+ if (config.autoMerge && !config.dryRun) {
25
+ // Fetch latest to ensure we have remote refs
26
+ exec('git fetch origin', { cwd: projectDir });
27
+ const branchExists = exec(`git rev-parse --verify "${branch}"`, { cwd: projectDir });
28
+ if (branchExists.exitCode !== 0) {
29
+ // Create from remote base branch first, fall back to local
30
+ const fromRemote = exec(`git checkout -b "${branch}" "origin/${config.baseBranch}"`, { cwd: projectDir });
31
+ if (fromRemote.exitCode !== 0) {
32
+ exec(`git checkout -b "${branch}" "${config.baseBranch}"`, { cwd: projectDir });
33
+ }
34
+ // Push session branch to remote so PRs can target it
35
+ exec(`git push origin "${branch}"`, { cwd: projectDir });
36
+ // Switch back to the original branch
37
+ exec(`git checkout -`, { cwd: projectDir });
38
+ log.info(`Created session branch: ${branch}`);
39
+ // Create a draft PR immediately so the session is visible in GitHub
40
+ try {
41
+ const draftPR = createPR({
42
+ repo: config.repo,
43
+ base: config.baseBranch,
44
+ head: branch,
45
+ title: `Session: ${name}`,
46
+ body: `## Session In Progress\n\n**Branch:** ${branch}\n**Started:** ${new Date().toISOString()}\n\nThis PR will be updated as issues are processed.\n\n---\n*Automated by alpha-loop*`,
47
+ cwd: projectDir,
48
+ });
49
+ log.success(`Session PR (draft): ${draftPR}`);
50
+ }
51
+ catch {
52
+ // Non-fatal — PR can be created later during finalization
53
+ log.warn('Could not create draft session PR — will create during finalization');
54
+ }
55
+ }
56
+ else {
57
+ log.info(`Session branch already exists: ${branch}`);
58
+ }
59
+ }
60
+ return { name, branch, resultsDir, logsDir, results: [] };
61
+ }
62
+ /**
63
+ * Save a pipeline result to the session directory as JSON.
64
+ */
65
+ export function saveResult(session, result) {
66
+ const filePath = join(session.resultsDir, `result-${result.issueNum}.json`);
67
+ writeFileSync(filePath, JSON.stringify(result, null, 2) + '\n');
68
+ log.info(`Session result saved: ${filePath}`);
69
+ }
70
+ /**
71
+ * Get the previous issue result formatted for prompt context.
72
+ * Returns null if no previous results exist.
73
+ */
74
+ export function getPreviousResult(session) {
75
+ if (session.results.length === 0)
76
+ return null;
77
+ const prev = session.results[session.results.length - 1];
78
+ return `## Previous Issue in This Session
79
+ - Issue #${prev.issueNum}: ${prev.title}
80
+ - Status: ${prev.status}
81
+ - Tests: ${prev.testsPassing ? 'PASSING' : 'FAILING'}
82
+ - Files changed: ${prev.filesChanged}
83
+ - Duration: ${prev.duration}s
84
+ ${prev.prUrl ? `- PR: ${prev.prUrl}` : ''}
85
+
86
+ Build on what was already done. Avoid duplicating work.`;
87
+ }
88
+ /**
89
+ * Finalize session: commit learnings to session branch, create session PR.
90
+ * Only runs when autoMerge is enabled and issues were processed.
91
+ */
92
+ export async function finalizeSession(session, config) {
93
+ if (!config.autoMerge)
94
+ return null;
95
+ if (session.branch === config.baseBranch)
96
+ return null;
97
+ if (session.results.length === 0)
98
+ return null;
99
+ if (config.dryRun) {
100
+ log.dry(`Would finalize session: ${session.branch} -> ${config.baseBranch}`);
101
+ return null;
102
+ }
103
+ log.step(`Finalizing session: ${session.branch}`);
104
+ const projectDir = process.cwd();
105
+ // Ensure we're on the session branch
106
+ exec('git fetch origin', { cwd: projectDir });
107
+ const checkout = exec(`git checkout "${session.branch}"`, { cwd: projectDir });
108
+ if (checkout.exitCode !== 0) {
109
+ log.warn('Could not checkout session branch for finalization');
110
+ return null;
111
+ }
112
+ // Stage learnings
113
+ const learningsDir = join(projectDir, '.alpha-loop', 'learnings');
114
+ if (existsSync(learningsDir)) {
115
+ exec('git add .alpha-loop/learnings/', { cwd: projectDir });
116
+ }
117
+ // Commit if there are staged changes
118
+ const diffResult = exec('git diff --cached --quiet', { cwd: projectDir });
119
+ if (diffResult.exitCode !== 0) {
120
+ const commitIssueCount = session.results.length;
121
+ exec(`git commit -m "chore: learnings from ${session.name}\n\nProcessed ${commitIssueCount} issue(s) in this session."`, { cwd: projectDir });
122
+ exec(`git push origin "${session.branch}"`, { cwd: projectDir });
123
+ }
124
+ // Create or update session PR
125
+ const issueCount = session.results.length;
126
+ const successCount = session.results.filter((r) => r.status === 'success').length;
127
+ const failureCount = issueCount - successCount;
128
+ const totalDuration = session.results.reduce((sum, r) => sum + r.duration, 0);
129
+ const prTitle = `Session: ${session.name} — ${successCount}/${issueCount} succeeded`;
130
+ const prBody = `## Session Summary
131
+
132
+ **Branch:** ${session.branch}
133
+ **Issues processed:** ${issueCount} (${successCount} succeeded, ${failureCount} failed)
134
+ **Total duration:** ${Math.round(totalDuration / 60)} minutes
135
+ **Completed:** ${new Date().toISOString()}
136
+
137
+ ### Issues
138
+ ${session.results.map((r) => `- #${r.issueNum}: ${r.title} — ${r.status === 'success' ? 'SUCCESS' : 'FAILURE'}${r.prUrl ? ` ([PR](${r.prUrl}))` : ''}`).join('\n')}
139
+
140
+ ---
141
+ This PR collects all changes from this session for final review before merging to ${config.baseBranch}.
142
+
143
+ Automated by alpha-loop`;
144
+ try {
145
+ const prUrl = createPR({
146
+ repo: config.repo,
147
+ base: config.baseBranch,
148
+ head: session.branch,
149
+ title: prTitle,
150
+ body: prBody,
151
+ cwd: projectDir,
152
+ });
153
+ log.success(`Session PR: ${prUrl}`);
154
+ return prUrl;
155
+ }
156
+ catch (err) {
157
+ // If createPR failed (e.g. nothing to compare), try creating via gh directly
158
+ log.warn(`createPR failed: ${err instanceof Error ? err.message : err}`);
159
+ try {
160
+ const fallback = exec(`gh pr create --repo "${config.repo}" --base "${config.baseBranch}" --head "${session.branch}" --title "${prTitle}" --body "Session finalization — see branch for details"`, { cwd: projectDir });
161
+ if (fallback.exitCode === 0 && fallback.stdout.trim()) {
162
+ log.success(`Session PR (fallback): ${fallback.stdout.trim()}`);
163
+ return fallback.stdout.trim();
164
+ }
165
+ }
166
+ catch {
167
+ // Fall through
168
+ }
169
+ log.warn('Could not create session PR — check branch manually');
170
+ return null;
171
+ }
172
+ }
173
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export type ExecResult = {
2
+ stdout: string;
3
+ stderr: string;
4
+ exitCode: number;
5
+ };
6
+ /**
7
+ * Execute a shell command synchronously and return structured result.
8
+ * Does not throw on non-zero exit — returns exitCode instead.
9
+ */
10
+ export declare function exec(command: string, options?: {
11
+ cwd?: string;
12
+ env?: Record<string, string>;
13
+ timeout?: number;
14
+ }): ExecResult;
15
+ /**
16
+ * Run a shell command asynchronously. Does not throw on non-zero exit.
17
+ */
18
+ export declare function execAsync(cmd: string, cwd?: string): Promise<ExecResult>;
19
+ /** Run a command with streaming output. Returns exit code. */
20
+ export declare function run(cmd: string, args: string[], options?: {
21
+ cwd?: string;
22
+ onStdout?: (line: string) => void;
23
+ onStderr?: (line: string) => void;
24
+ }): Promise<number>;
25
+ /**
26
+ * Check whether a command exists on the system PATH.
27
+ */
28
+ /**
29
+ * Format a Date as YYYYMMDD-HHMMSS for filenames and session names.
30
+ */
31
+ export declare function formatTimestamp(date: Date): string;
32
+ export declare function commandExists(cmd: string): boolean;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Shell execution helpers.
3
+ */
4
+ import { execSync as nodeExecSync, exec as nodeExec, spawn } from 'node:child_process';
5
+ import * as readline from 'node:readline';
6
+ /** Max buffer for async exec output (10 MB). */
7
+ const EXEC_MAX_BUFFER = 10 * 1024 * 1024;
8
+ /**
9
+ * Execute a shell command synchronously and return structured result.
10
+ * Does not throw on non-zero exit — returns exitCode instead.
11
+ */
12
+ export function exec(command, options) {
13
+ try {
14
+ const stdout = nodeExecSync(command, {
15
+ cwd: options?.cwd,
16
+ env: options?.env ? { ...process.env, ...options.env } : undefined,
17
+ timeout: options?.timeout,
18
+ encoding: 'utf-8',
19
+ stdio: ['pipe', 'pipe', 'pipe'],
20
+ });
21
+ return { stdout: stdout.trim(), stderr: '', exitCode: 0 };
22
+ }
23
+ catch (err) {
24
+ const e = err;
25
+ return {
26
+ stdout: (e.stdout ?? '').toString().trim(),
27
+ stderr: (e.stderr ?? '').toString().trim(),
28
+ exitCode: e.status ?? 1,
29
+ };
30
+ }
31
+ }
32
+ /**
33
+ * Run a shell command asynchronously. Does not throw on non-zero exit.
34
+ */
35
+ export async function execAsync(cmd, cwd) {
36
+ return new Promise((resolve) => {
37
+ nodeExec(cmd, { cwd, maxBuffer: EXEC_MAX_BUFFER }, (error, stdout, stderr) => {
38
+ let exitCode = 0;
39
+ if (error) {
40
+ const execErr = error;
41
+ exitCode = typeof execErr.code === 'number' ? execErr.code : 1;
42
+ }
43
+ resolve({
44
+ stdout: stdout?.toString() ?? '',
45
+ stderr: stderr?.toString() ?? '',
46
+ exitCode,
47
+ });
48
+ });
49
+ });
50
+ }
51
+ /** Run a command with streaming output. Returns exit code. */
52
+ export function run(cmd, args, options) {
53
+ return new Promise((resolve) => {
54
+ const child = spawn(cmd, args, {
55
+ cwd: options?.cwd,
56
+ stdio: ['pipe', 'pipe', 'pipe'],
57
+ });
58
+ if (child.stdout) {
59
+ const rl = readline.createInterface({ input: child.stdout });
60
+ rl.on('line', (line) => options?.onStdout?.(line));
61
+ }
62
+ if (child.stderr) {
63
+ const rl = readline.createInterface({ input: child.stderr });
64
+ rl.on('line', (line) => options?.onStderr?.(line));
65
+ }
66
+ child.on('close', (code) => resolve(code ?? 1));
67
+ });
68
+ }
69
+ /**
70
+ * Check whether a command exists on the system PATH.
71
+ */
72
+ /**
73
+ * Format a Date as YYYYMMDD-HHMMSS for filenames and session names.
74
+ */
75
+ export function formatTimestamp(date) {
76
+ const y = date.getFullYear();
77
+ const mo = String(date.getMonth() + 1).padStart(2, '0');
78
+ const d = String(date.getDate()).padStart(2, '0');
79
+ const h = String(date.getHours()).padStart(2, '0');
80
+ const mi = String(date.getMinutes()).padStart(2, '0');
81
+ const s = String(date.getSeconds()).padStart(2, '0');
82
+ return `${y}${mo}${d}-${h}${mi}${s}`;
83
+ }
84
+ export function commandExists(cmd) {
85
+ if (!/^[a-zA-Z0-9_-]+$/.test(cmd))
86
+ return false;
87
+ try {
88
+ nodeExecSync(`command -v "${cmd}"`, { stdio: 'ignore' });
89
+ return true;
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ //# sourceMappingURL=shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/lib/shell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Config } from './config.js';
2
+ export type TestResult = {
3
+ passed: boolean;
4
+ output: string;
5
+ };
6
+ /**
7
+ * Run the configured test command in a worktree.
8
+ * Returns structured result instead of throwing.
9
+ */
10
+ export declare function runTests(worktree: string, config: Config, logFile: string): TestResult;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Test Runner — execute tests in worktrees with retry support.
3
+ */
4
+ import { appendFileSync } from 'node:fs';
5
+ import { exec } from './shell.js';
6
+ import { log } from './logger.js';
7
+ /** Test runner timeout (5 minutes). */
8
+ const TEST_TIMEOUT_MS = 300_000;
9
+ /**
10
+ * Run the configured test command in a worktree.
11
+ * Returns structured result instead of throwing.
12
+ */
13
+ export function runTests(worktree, config, logFile) {
14
+ if (config.skipTests) {
15
+ log.info('Tests skipped (skipTests=true)');
16
+ return { passed: true, output: 'Tests skipped' };
17
+ }
18
+ if (config.dryRun) {
19
+ log.dry('Would run tests in worktree');
20
+ return { passed: true, output: 'Tests skipped (dry run)' };
21
+ }
22
+ log.step(`Running tests: ${config.testCommand}`);
23
+ const env = {};
24
+ if (config.runFull) {
25
+ env.RECORD_FIXTURES = 'true';
26
+ }
27
+ const result = exec(config.testCommand, {
28
+ cwd: worktree,
29
+ env: Object.keys(env).length > 0 ? env : undefined,
30
+ timeout: TEST_TIMEOUT_MS,
31
+ });
32
+ // Append test output to log file
33
+ if (logFile) {
34
+ try {
35
+ appendFileSync(logFile, `\n--- Test Output ---\n${result.stdout}\n${result.stderr}\n`);
36
+ }
37
+ catch {
38
+ // Log file write failure is non-fatal
39
+ }
40
+ }
41
+ const output = result.stdout + (result.stderr ? `\n${result.stderr}` : '');
42
+ if (result.exitCode === 0) {
43
+ log.success('All tests passed');
44
+ return { passed: true, output };
45
+ }
46
+ log.warn(`Tests failed (exit code ${result.exitCode})`);
47
+ return { passed: false, output };
48
+ }
49
+ // Note: Live verification (playwright-cli) is in src/lib/verify.ts
50
+ // This module only handles unit/integration test execution.
51
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/lib/testing.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAGlC,uCAAuC;AACvC,MAAM,eAAe,GAAG,OAAO,CAAC;AAOhC;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAgB,EAAE,MAAc,EAAE,OAAe;IACxE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,GAAG,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QAC3C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACnD,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,GAAG,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;IAC7D,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAEjD,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,GAAG,CAAC,eAAe,GAAG,MAAM,CAAC;IAC/B,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE;QACtC,GAAG,EAAE,QAAQ;QACb,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAClD,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;IAEH,iCAAiC;IACjC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,cAAc,CAAC,OAAO,EAAE,0BAA0B,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QACzF,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;QACxC,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAE3E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAChC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAClC,CAAC;IAED,GAAG,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,mEAAmE;AACnE,4DAA4D"}
@@ -0,0 +1,18 @@
1
+ import type { Config } from './config.js';
2
+ export type VerifyResult = {
3
+ passed: boolean;
4
+ output: string;
5
+ };
6
+ /**
7
+ * Run live verification using playwright-cli.
8
+ * Starts the app, sends agent to test it, returns result.
9
+ */
10
+ export declare function runVerify(options: {
11
+ worktree: string;
12
+ logFile: string;
13
+ issueNum: number;
14
+ title: string;
15
+ body: string;
16
+ config: Config;
17
+ sessionDir: string;
18
+ }): Promise<VerifyResult>;