@ai-sdlc/orchestrator 0.1.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 (203) hide show
  1. package/dist/adapters.d.ts +60 -0
  2. package/dist/adapters.js +160 -0
  3. package/dist/admission.d.ts +49 -0
  4. package/dist/admission.js +90 -0
  5. package/dist/analysis/analyzer.d.ts +10 -0
  6. package/dist/analysis/analyzer.js +67 -0
  7. package/dist/analysis/complexity-scorer.d.ts +24 -0
  8. package/dist/analysis/complexity-scorer.js +39 -0
  9. package/dist/analysis/context-builder.d.ts +14 -0
  10. package/dist/analysis/context-builder.js +79 -0
  11. package/dist/analysis/convention-detector.d.ts +9 -0
  12. package/dist/analysis/convention-detector.js +177 -0
  13. package/dist/analysis/dependency-parser.d.ts +17 -0
  14. package/dist/analysis/dependency-parser.js +167 -0
  15. package/dist/analysis/file-walker.d.ts +18 -0
  16. package/dist/analysis/file-walker.js +158 -0
  17. package/dist/analysis/hotspot-analyzer.d.ts +28 -0
  18. package/dist/analysis/hotspot-analyzer.js +85 -0
  19. package/dist/analysis/index.d.ts +13 -0
  20. package/dist/analysis/index.js +12 -0
  21. package/dist/analysis/pattern-detector.d.ts +10 -0
  22. package/dist/analysis/pattern-detector.js +86 -0
  23. package/dist/analysis/types.d.ts +94 -0
  24. package/dist/analysis/types.js +5 -0
  25. package/dist/audit-archival.d.ts +48 -0
  26. package/dist/audit-archival.js +72 -0
  27. package/dist/audit-export.d.ts +45 -0
  28. package/dist/audit-export.js +100 -0
  29. package/dist/audit-extended.d.ts +28 -0
  30. package/dist/audit-extended.js +38 -0
  31. package/dist/audit-scheduler.d.ts +32 -0
  32. package/dist/audit-scheduler.js +52 -0
  33. package/dist/audit-sqlite-sink.d.ts +11 -0
  34. package/dist/audit-sqlite-sink.js +51 -0
  35. package/dist/autonomy-tracker.d.ts +76 -0
  36. package/dist/autonomy-tracker.js +277 -0
  37. package/dist/check-runs.d.ts +37 -0
  38. package/dist/check-runs.js +72 -0
  39. package/dist/cli/commands/agents.d.ts +6 -0
  40. package/dist/cli/commands/agents.js +33 -0
  41. package/dist/cli/commands/complexity.d.ts +6 -0
  42. package/dist/cli/commands/complexity.js +35 -0
  43. package/dist/cli/commands/cost.d.ts +6 -0
  44. package/dist/cli/commands/cost.js +53 -0
  45. package/dist/cli/commands/dashboard.d.ts +9 -0
  46. package/dist/cli/commands/dashboard.js +50 -0
  47. package/dist/cli/commands/health.d.ts +6 -0
  48. package/dist/cli/commands/health.js +33 -0
  49. package/dist/cli/commands/init.d.ts +6 -0
  50. package/dist/cli/commands/init.js +127 -0
  51. package/dist/cli/commands/routing.d.ts +6 -0
  52. package/dist/cli/commands/routing.js +46 -0
  53. package/dist/cli/commands/run.d.ts +6 -0
  54. package/dist/cli/commands/run.js +36 -0
  55. package/dist/cli/commands/start.d.ts +6 -0
  56. package/dist/cli/commands/start.js +26 -0
  57. package/dist/cli/commands/status.d.ts +6 -0
  58. package/dist/cli/commands/status.js +34 -0
  59. package/dist/cli/dashboard-renderer.d.ts +24 -0
  60. package/dist/cli/dashboard-renderer.js +129 -0
  61. package/dist/cli/formatters/index.d.ts +6 -0
  62. package/dist/cli/formatters/index.js +18 -0
  63. package/dist/cli/formatters/json.d.ts +5 -0
  64. package/dist/cli/formatters/json.js +7 -0
  65. package/dist/cli/formatters/minimal.d.ts +5 -0
  66. package/dist/cli/formatters/minimal.js +36 -0
  67. package/dist/cli/formatters/table.d.ts +5 -0
  68. package/dist/cli/formatters/table.js +184 -0
  69. package/dist/cli/index.d.ts +6 -0
  70. package/dist/cli/index.js +35 -0
  71. package/dist/compliance-extended.d.ts +41 -0
  72. package/dist/compliance-extended.js +58 -0
  73. package/dist/config.d.ts +26 -0
  74. package/dist/config.js +66 -0
  75. package/dist/context-enrichment.d.ts +33 -0
  76. package/dist/context-enrichment.js +125 -0
  77. package/dist/cost-governance.d.ts +22 -0
  78. package/dist/cost-governance.js +106 -0
  79. package/dist/cost-tracker.d.ts +75 -0
  80. package/dist/cost-tracker.js +177 -0
  81. package/dist/defaults.d.ts +140 -0
  82. package/dist/defaults.js +218 -0
  83. package/dist/deploy/flyio-target.d.ts +19 -0
  84. package/dist/deploy/flyio-target.js +114 -0
  85. package/dist/deploy/index.d.ts +15 -0
  86. package/dist/deploy/index.js +11 -0
  87. package/dist/deploy/kubernetes-target.d.ts +25 -0
  88. package/dist/deploy/kubernetes-target.js +124 -0
  89. package/dist/deploy/metrics-collector.d.ts +31 -0
  90. package/dist/deploy/metrics-collector.js +75 -0
  91. package/dist/deploy/rollout-controller.d.ts +42 -0
  92. package/dist/deploy/rollout-controller.js +192 -0
  93. package/dist/deploy/rollout-types.d.ts +86 -0
  94. package/dist/deploy/rollout-types.js +5 -0
  95. package/dist/deploy/types.d.ts +47 -0
  96. package/dist/deploy/types.js +5 -0
  97. package/dist/deploy/vercel-target.d.ts +16 -0
  98. package/dist/deploy/vercel-target.js +115 -0
  99. package/dist/discovery.d.ts +26 -0
  100. package/dist/discovery.js +46 -0
  101. package/dist/episodic-enhanced.d.ts +88 -0
  102. package/dist/episodic-enhanced.js +212 -0
  103. package/dist/execute.d.ts +81 -0
  104. package/dist/execute.js +703 -0
  105. package/dist/fix-ci.d.ts +61 -0
  106. package/dist/fix-ci.js +380 -0
  107. package/dist/handoff-executor.d.ts +61 -0
  108. package/dist/handoff-executor.js +119 -0
  109. package/dist/index.d.ts +70 -0
  110. package/dist/index.js +87 -0
  111. package/dist/instrumented.d.ts +27 -0
  112. package/dist/instrumented.js +34 -0
  113. package/dist/logger.d.ts +17 -0
  114. package/dist/logger.js +37 -0
  115. package/dist/multi-repo/impact-analyzer.d.ts +22 -0
  116. package/dist/multi-repo/impact-analyzer.js +95 -0
  117. package/dist/multi-repo/index.d.ts +5 -0
  118. package/dist/multi-repo/index.js +4 -0
  119. package/dist/multi-repo/monorepo-detector.d.ts +16 -0
  120. package/dist/multi-repo/monorepo-detector.js +246 -0
  121. package/dist/multi-repo/service-map-builder.d.ts +24 -0
  122. package/dist/multi-repo/service-map-builder.js +235 -0
  123. package/dist/multi-repo/types.d.ts +54 -0
  124. package/dist/multi-repo/types.js +5 -0
  125. package/dist/notifications/index.d.ts +4 -0
  126. package/dist/notifications/index.js +4 -0
  127. package/dist/notifications/notification-router.d.ts +62 -0
  128. package/dist/notifications/notification-router.js +206 -0
  129. package/dist/notifications/slack-messenger.d.ts +26 -0
  130. package/dist/notifications/slack-messenger.js +69 -0
  131. package/dist/notifications/teams-messenger.d.ts +21 -0
  132. package/dist/notifications/teams-messenger.js +85 -0
  133. package/dist/notifications.d.ts +13 -0
  134. package/dist/notifications.js +15 -0
  135. package/dist/orchestration.d.ts +20 -0
  136. package/dist/orchestration.js +53 -0
  137. package/dist/orchestrator.d.ts +160 -0
  138. package/dist/orchestrator.js +372 -0
  139. package/dist/otel-exporter.d.ts +37 -0
  140. package/dist/otel-exporter.js +128 -0
  141. package/dist/plugin.d.ts +45 -0
  142. package/dist/plugin.js +8 -0
  143. package/dist/policy-evaluators.d.ts +40 -0
  144. package/dist/policy-evaluators.js +57 -0
  145. package/dist/process-escalation.d.ts +38 -0
  146. package/dist/process-escalation.js +129 -0
  147. package/dist/progressive-gates.d.ts +54 -0
  148. package/dist/progressive-gates.js +135 -0
  149. package/dist/provenance.d.ts +29 -0
  150. package/dist/provenance.js +61 -0
  151. package/dist/reconcilers.d.ts +28 -0
  152. package/dist/reconcilers.js +37 -0
  153. package/dist/runners/claude-code.d.ts +17 -0
  154. package/dist/runners/claude-code.js +200 -0
  155. package/dist/runners/codex.d.ts +19 -0
  156. package/dist/runners/codex.js +153 -0
  157. package/dist/runners/copilot-stub.d.ts +9 -0
  158. package/dist/runners/copilot-stub.js +15 -0
  159. package/dist/runners/copilot.d.ts +15 -0
  160. package/dist/runners/copilot.js +119 -0
  161. package/dist/runners/cursor-stub.d.ts +9 -0
  162. package/dist/runners/cursor-stub.js +15 -0
  163. package/dist/runners/cursor.d.ts +20 -0
  164. package/dist/runners/cursor.js +140 -0
  165. package/dist/runners/devin-stub.d.ts +9 -0
  166. package/dist/runners/devin-stub.js +15 -0
  167. package/dist/runners/generic-llm.d.ts +51 -0
  168. package/dist/runners/generic-llm.js +125 -0
  169. package/dist/runners/index.d.ts +8 -0
  170. package/dist/runners/index.js +7 -0
  171. package/dist/runners/runner-registry.d.ts +50 -0
  172. package/dist/runners/runner-registry.js +156 -0
  173. package/dist/runners/types.d.ts +59 -0
  174. package/dist/runners/types.js +7 -0
  175. package/dist/security.d.ts +51 -0
  176. package/dist/security.js +70 -0
  177. package/dist/shared.d.ts +134 -0
  178. package/dist/shared.js +284 -0
  179. package/dist/state/index.d.ts +4 -0
  180. package/dist/state/index.js +3 -0
  181. package/dist/state/schema.d.ts +16 -0
  182. package/dist/state/schema.js +264 -0
  183. package/dist/state/store.d.ts +109 -0
  184. package/dist/state/store.js +644 -0
  185. package/dist/state/types.d.ts +190 -0
  186. package/dist/state/types.js +5 -0
  187. package/dist/structured-logger.d.ts +20 -0
  188. package/dist/structured-logger.js +64 -0
  189. package/dist/task-decomposer.d.ts +61 -0
  190. package/dist/task-decomposer.js +265 -0
  191. package/dist/telemetry-extended.d.ts +24 -0
  192. package/dist/telemetry-extended.js +31 -0
  193. package/dist/types.d.ts +9 -0
  194. package/dist/types.js +7 -0
  195. package/dist/validate-agent-output.d.ts +30 -0
  196. package/dist/validate-agent-output.js +81 -0
  197. package/dist/validate-issue.d.ts +25 -0
  198. package/dist/validate-issue.js +102 -0
  199. package/dist/watch.d.ts +43 -0
  200. package/dist/watch.js +99 -0
  201. package/dist/webhook-manager.d.ts +38 -0
  202. package/dist/webhook-manager.js +66 -0
  203. package/package.json +59 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Claude Code runner — invokes Claude Code CLI in --print mode
3
+ * and collects the result via git diff.
4
+ */
5
+ import { spawn, execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ import { DEFAULT_MODEL, DEFAULT_ALLOWED_TOOLS, DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_LINT_COMMAND, DEFAULT_FORMAT_COMMAND, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
8
+ import { formatContextForPrompt } from '../analysis/context-builder.js';
9
+ const execFileAsync = promisify(execFile);
10
+ export function buildPrompt(ctx) {
11
+ const lintCmd = ctx.lintCommand ?? DEFAULT_LINT_COMMAND;
12
+ const fmtCmd = ctx.formatCommand ?? DEFAULT_FORMAT_COMMAND;
13
+ const lines = [
14
+ `You are fixing issue #${ctx.issueNumber}: ${ctx.issueTitle}`,
15
+ '',
16
+ '## Issue Description',
17
+ ctx.issueBody,
18
+ '',
19
+ ];
20
+ if (ctx.ciErrors) {
21
+ let step = 0;
22
+ lines.push('## CI Failure Logs', '', '```', ctx.ciErrors, '```', '', '## Instructions', `${++step}. Analyze the CI failure logs above to identify the root cause.`, `${++step}. Read the relevant source files to understand the context.`, `${++step}. Fix the errors that caused CI to fail.`);
23
+ if (fmtCmd) {
24
+ lines.push(`${++step}. If the failure is a formatting/prettier error, run \`${fmtCmd}\` to auto-fix it.`);
25
+ }
26
+ if (lintCmd && fmtCmd) {
27
+ lines.push(`${++step}. After making ANY code changes, always run \`${lintCmd}\` and \`${fmtCmd}\` to catch issues before committing.`);
28
+ }
29
+ else if (lintCmd) {
30
+ lines.push(`${++step}. After making ANY code changes, always run \`${lintCmd}\` to catch issues before committing.`);
31
+ }
32
+ else if (fmtCmd) {
33
+ lines.push(`${++step}. After making ANY code changes, always run \`${fmtCmd}\` to catch issues before committing.`);
34
+ }
35
+ lines.push(`${++step}. Write or update tests if needed to cover your fix.`, `${++step}. Do NOT modify files matching the blocked paths below.`, `${++step}. Keep your changes to at most ${ctx.constraints.maxFilesPerChange} files.`);
36
+ }
37
+ else {
38
+ let step = 0;
39
+ lines.push('## Instructions', `${++step}. Read the relevant source files to understand the codebase.`, `${++step}. Implement the fix or feature described in the issue.`, `${++step}. Write or update tests to cover your changes.`);
40
+ if (lintCmd && fmtCmd) {
41
+ lines.push(`${++step}. After making code changes, run \`${lintCmd}\` and \`${fmtCmd}\` to ensure CI will pass.`);
42
+ }
43
+ else if (lintCmd) {
44
+ lines.push(`${++step}. After making code changes, run \`${lintCmd}\` to ensure CI will pass.`);
45
+ }
46
+ else if (fmtCmd) {
47
+ lines.push(`${++step}. After making code changes, run \`${fmtCmd}\` to ensure CI will pass.`);
48
+ }
49
+ lines.push(`${++step}. Do NOT modify files matching the blocked paths below.`, `${++step}. Keep your changes to at most ${ctx.constraints.maxFilesPerChange} files.`);
50
+ }
51
+ lines.push('', '## Constraints', `- Maximum files to change: ${ctx.constraints.maxFilesPerChange}`, `- Tests required: ${ctx.constraints.requireTests}`, `- Blocked paths (do NOT modify): ${ctx.constraints.blockedPaths.join(', ') || 'none'}`);
52
+ // Append relevant episodic memory if available
53
+ if (ctx.memory) {
54
+ const episodes = ctx.memory.episodic.search(`issue-${ctx.issueNumber}`);
55
+ if (episodes.length > 0) {
56
+ lines.push('', '## Previous Context');
57
+ for (const ep of episodes.slice(0, 5)) {
58
+ const summary = ep.metadata && typeof ep.metadata === 'object' && 'summary' in ep.metadata
59
+ ? ep.metadata.summary
60
+ : ep.key;
61
+ lines.push(`- ${summary}`);
62
+ }
63
+ }
64
+ }
65
+ // Append episodic context if available
66
+ if (ctx.episodicContext) {
67
+ lines.push('', ctx.episodicContext);
68
+ }
69
+ // Append codebase context if available
70
+ if (ctx.codebaseContext) {
71
+ lines.push('', formatContextForPrompt(ctx.codebaseContext));
72
+ }
73
+ return lines.join('\n');
74
+ }
75
+ async function gitExec(workDir, args) {
76
+ const { stdout } = await execFileAsync('git', args, { cwd: workDir });
77
+ return stdout.trim();
78
+ }
79
+ function runClaude(prompt, workDir, opts) {
80
+ const tools = opts?.allowedTools?.join(',') ?? DEFAULT_ALLOWED_TOOLS;
81
+ const timeoutMs = opts?.timeoutMs ?? DEFAULT_RUNNER_TIMEOUT_MS;
82
+ return new Promise((resolve, reject) => {
83
+ const model = opts?.model ?? process.env.AI_SDLC_MODEL ?? DEFAULT_MODEL;
84
+ const child = spawn('claude', ['-p', '--model', model, '--allowedTools', tools], {
85
+ cwd: workDir,
86
+ stdio: ['pipe', 'pipe', 'pipe'],
87
+ env: { ...process.env },
88
+ timeout: timeoutMs,
89
+ });
90
+ const chunks = [];
91
+ const errChunks = [];
92
+ child.stdout.on('data', (data) => chunks.push(data));
93
+ child.stderr.on('data', (data) => errChunks.push(data));
94
+ child.on('close', (code) => {
95
+ const stdout = Buffer.concat(chunks).toString('utf-8');
96
+ const stderr = Buffer.concat(errChunks).toString('utf-8');
97
+ if (code === 0) {
98
+ resolve({ stdout, stderr, model });
99
+ }
100
+ else {
101
+ reject(new Error(`claude exited with code ${code}: ${stderr || stdout}`));
102
+ }
103
+ });
104
+ child.on('error', reject);
105
+ // Send prompt via stdin and close it
106
+ child.stdin.write(prompt);
107
+ child.stdin.end();
108
+ });
109
+ }
110
+ /**
111
+ * Parse token usage from Claude CLI stderr output.
112
+ * Claude Code CLI outputs token info to stderr in various formats.
113
+ */
114
+ export function parseTokenUsage(stderr, model) {
115
+ // Try to match patterns like "Input tokens: 1234" / "Output tokens: 5678"
116
+ const inputMatch = stderr.match(/input[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
117
+ const outputMatch = stderr.match(/output[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
118
+ if (inputMatch || outputMatch) {
119
+ const cacheMatch = stderr.match(/cache[\s_-]*(?:read|hit)[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
120
+ return {
121
+ inputTokens: inputMatch ? parseInt(inputMatch[1].replace(/,/g, ''), 10) : 0,
122
+ outputTokens: outputMatch ? parseInt(outputMatch[1].replace(/,/g, ''), 10) : 0,
123
+ cacheReadTokens: cacheMatch ? parseInt(cacheMatch[1].replace(/,/g, ''), 10) : undefined,
124
+ model,
125
+ };
126
+ }
127
+ // Try to match total tokens pattern
128
+ const totalMatch = stderr.match(/total[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
129
+ if (totalMatch) {
130
+ const total = parseInt(totalMatch[1].replace(/,/g, ''), 10);
131
+ // Estimate 70% input / 30% output split
132
+ return {
133
+ inputTokens: Math.round(total * 0.7),
134
+ outputTokens: Math.round(total * 0.3),
135
+ model,
136
+ };
137
+ }
138
+ return undefined;
139
+ }
140
+ export class ClaudeCodeRunner {
141
+ async run(ctx) {
142
+ const prompt = buildPrompt(ctx);
143
+ try {
144
+ // Invoke Claude Code CLI in print mode, sending prompt via stdin
145
+ const result = await runClaude(prompt, ctx.workDir, {
146
+ allowedTools: ctx.allowedTools,
147
+ timeoutMs: ctx.timeoutMs,
148
+ model: ctx.model,
149
+ });
150
+ // Parse token usage from stderr
151
+ const tokenUsage = parseTokenUsage(result.stderr, result.model);
152
+ // Collect changed files
153
+ const diffOutput = await gitExec(ctx.workDir, ['diff', '--name-only']);
154
+ const untrackedOutput = await gitExec(ctx.workDir, [
155
+ 'ls-files',
156
+ '--others',
157
+ '--exclude-standard',
158
+ ]);
159
+ const filesChanged = [
160
+ ...diffOutput.split('\n').filter(Boolean),
161
+ ...untrackedOutput.split('\n').filter(Boolean),
162
+ ];
163
+ if (filesChanged.length === 0) {
164
+ return {
165
+ success: false,
166
+ filesChanged: [],
167
+ summary: 'Agent made no changes',
168
+ error: 'No files were modified',
169
+ tokenUsage,
170
+ };
171
+ }
172
+ // Stage and commit
173
+ await gitExec(ctx.workDir, ['add', '-A']);
174
+ const tmpl = ctx.commitMessageTemplate ?? DEFAULT_COMMIT_MESSAGE_TEMPLATE;
175
+ const coAuthor = ctx.commitCoAuthor ?? DEFAULT_COMMIT_CO_AUTHOR;
176
+ const commitMsg = tmpl
177
+ .replace(/\{issueNumber\}/g, String(ctx.issueNumber))
178
+ .replace(/\{issueTitle\}/g, ctx.issueTitle);
179
+ await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
180
+ return {
181
+ success: true,
182
+ filesChanged,
183
+ summary: result.stdout.slice(0, 2000),
184
+ tokenUsage,
185
+ };
186
+ }
187
+ catch (err) {
188
+ const message = err instanceof Error ? err.message : String(err);
189
+ return {
190
+ success: false,
191
+ filesChanged: [],
192
+ summary: 'Agent execution failed',
193
+ error: message,
194
+ };
195
+ }
196
+ }
197
+ }
198
+ /** @deprecated Use ClaudeCodeRunner instead */
199
+ export const GitHubActionsRunner = ClaudeCodeRunner;
200
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * OpenAI Codex CLI runner — invokes `codex exec -` with stdin prompt
3
+ * in --full-auto --json mode and collects the result via git diff.
4
+ */
5
+ import type { AgentRunner, AgentContext, AgentResult, TokenUsage } from './types.js';
6
+ import { buildPrompt } from './claude-code.js';
7
+ export { buildPrompt };
8
+ /**
9
+ * Parse token usage from Codex CLI output.
10
+ *
11
+ * Enhanced parser: first tries to parse NDJSON events from stderr
12
+ * (looking for `usage` or `token_usage` fields), accumulating totals
13
+ * across multiple events. Falls back to regex matching.
14
+ */
15
+ export declare function parseTokenUsage(stderr: string, model: string): TokenUsage | undefined;
16
+ export declare class CodexRunner implements AgentRunner {
17
+ run(ctx: AgentContext): Promise<AgentResult>;
18
+ }
19
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1,153 @@
1
+ /**
2
+ * OpenAI Codex CLI runner — invokes `codex exec -` with stdin prompt
3
+ * in --full-auto --json mode and collects the result via git diff.
4
+ */
5
+ import { spawn, execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ import { DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_CODEX_MODEL, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
8
+ import { buildPrompt } from './claude-code.js';
9
+ export { buildPrompt };
10
+ const execFileAsync = promisify(execFile);
11
+ async function gitExec(workDir, args) {
12
+ const { stdout } = await execFileAsync('git', args, { cwd: workDir });
13
+ return stdout.trim();
14
+ }
15
+ /**
16
+ * Parse token usage from Codex CLI output.
17
+ *
18
+ * Enhanced parser: first tries to parse NDJSON events from stderr
19
+ * (looking for `usage` or `token_usage` fields), accumulating totals
20
+ * across multiple events. Falls back to regex matching.
21
+ */
22
+ export function parseTokenUsage(stderr, model) {
23
+ // First pass: try parsing NDJSON events with usage data
24
+ let inputTotal = 0;
25
+ let outputTotal = 0;
26
+ let foundJson = false;
27
+ const lines = stderr.split('\n').filter(Boolean);
28
+ for (const line of lines) {
29
+ try {
30
+ const obj = JSON.parse(line);
31
+ const usage = obj.usage ?? obj.token_usage;
32
+ if (usage && typeof usage === 'object') {
33
+ const input = usage.input_tokens ?? usage.prompt_tokens ?? 0;
34
+ const output = usage.output_tokens ?? usage.completion_tokens ?? 0;
35
+ if (typeof input === 'number')
36
+ inputTotal += input;
37
+ if (typeof output === 'number')
38
+ outputTotal += output;
39
+ foundJson = true;
40
+ }
41
+ }
42
+ catch {
43
+ // Not JSON — skip
44
+ }
45
+ }
46
+ if (foundJson && (inputTotal > 0 || outputTotal > 0)) {
47
+ return { inputTokens: inputTotal, outputTokens: outputTotal, model };
48
+ }
49
+ // Fallback: regex matching
50
+ const inputMatch = stderr.match(/input[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
51
+ const outputMatch = stderr.match(/output[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
52
+ if (inputMatch || outputMatch) {
53
+ return {
54
+ inputTokens: inputMatch ? parseInt(inputMatch[1].replace(/,/g, ''), 10) : 0,
55
+ outputTokens: outputMatch ? parseInt(outputMatch[1].replace(/,/g, ''), 10) : 0,
56
+ model,
57
+ };
58
+ }
59
+ const totalMatch = stderr.match(/total[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
60
+ if (totalMatch) {
61
+ const total = parseInt(totalMatch[1].replace(/,/g, ''), 10);
62
+ return {
63
+ inputTokens: Math.round(total * 0.7),
64
+ outputTokens: Math.round(total * 0.3),
65
+ model,
66
+ };
67
+ }
68
+ return undefined;
69
+ }
70
+ export class CodexRunner {
71
+ async run(ctx) {
72
+ const prompt = buildPrompt(ctx);
73
+ const timeoutMs = ctx.timeoutMs ?? DEFAULT_RUNNER_TIMEOUT_MS;
74
+ const model = DEFAULT_CODEX_MODEL ?? 'codex-default';
75
+ try {
76
+ const args = ['exec', '-', '--full-auto', '--json'];
77
+ if (DEFAULT_CODEX_MODEL) {
78
+ args.push('-m', DEFAULT_CODEX_MODEL);
79
+ }
80
+ const { stdout, stderr } = await new Promise((resolve, reject) => {
81
+ const child = spawn('codex', args, {
82
+ cwd: ctx.workDir,
83
+ stdio: ['pipe', 'pipe', 'pipe'],
84
+ env: { ...process.env },
85
+ timeout: timeoutMs,
86
+ });
87
+ const chunks = [];
88
+ const errChunks = [];
89
+ child.stdout.on('data', (data) => chunks.push(data));
90
+ child.stderr.on('data', (data) => errChunks.push(data));
91
+ child.on('close', (code) => {
92
+ const out = Buffer.concat(chunks).toString('utf-8');
93
+ const err = Buffer.concat(errChunks).toString('utf-8');
94
+ if (code === 0) {
95
+ resolve({ stdout: out, stderr: err });
96
+ }
97
+ else {
98
+ reject(new Error(`codex exited with code ${code}: ${err || out}`));
99
+ }
100
+ });
101
+ child.on('error', reject);
102
+ // Send prompt via stdin and close it
103
+ child.stdin.write(prompt);
104
+ child.stdin.end();
105
+ });
106
+ const tokenUsage = parseTokenUsage(stderr, model);
107
+ // Collect changed files
108
+ const diffOutput = await gitExec(ctx.workDir, ['diff', '--name-only']);
109
+ const untrackedOutput = await gitExec(ctx.workDir, [
110
+ 'ls-files',
111
+ '--others',
112
+ '--exclude-standard',
113
+ ]);
114
+ const filesChanged = [
115
+ ...diffOutput.split('\n').filter(Boolean),
116
+ ...untrackedOutput.split('\n').filter(Boolean),
117
+ ];
118
+ if (filesChanged.length === 0) {
119
+ return {
120
+ success: false,
121
+ filesChanged: [],
122
+ summary: 'Agent made no changes',
123
+ error: 'No files were modified',
124
+ tokenUsage,
125
+ };
126
+ }
127
+ // Stage and commit
128
+ await gitExec(ctx.workDir, ['add', '-A']);
129
+ const tmpl = ctx.commitMessageTemplate ?? DEFAULT_COMMIT_MESSAGE_TEMPLATE;
130
+ const coAuthor = ctx.commitCoAuthor ?? DEFAULT_COMMIT_CO_AUTHOR;
131
+ const commitMsg = tmpl
132
+ .replace(/\{issueNumber\}/g, String(ctx.issueNumber))
133
+ .replace(/\{issueTitle\}/g, ctx.issueTitle);
134
+ await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
135
+ return {
136
+ success: true,
137
+ filesChanged,
138
+ summary: stdout.slice(0, 2000),
139
+ tokenUsage,
140
+ };
141
+ }
142
+ catch (err) {
143
+ const message = err instanceof Error ? err.message : String(err);
144
+ return {
145
+ success: false,
146
+ filesChanged: [],
147
+ summary: 'Agent execution failed',
148
+ error: message,
149
+ };
150
+ }
151
+ }
152
+ }
153
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stub runner for GitHub Copilot — placeholder for future implementation.
3
+ */
4
+ import type { AgentRunner, AgentContext, AgentResult } from './types.js';
5
+ export declare class CopilotStubRunner implements AgentRunner {
6
+ readonly name = "copilot";
7
+ run(_ctx: AgentContext): Promise<AgentResult>;
8
+ }
9
+ //# sourceMappingURL=copilot-stub.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Stub runner for GitHub Copilot — placeholder for future implementation.
3
+ */
4
+ export class CopilotStubRunner {
5
+ name = 'copilot';
6
+ async run(_ctx) {
7
+ return {
8
+ success: false,
9
+ filesChanged: [],
10
+ summary: 'GitHub Copilot runner not yet implemented',
11
+ error: 'STUB: GitHub Copilot integration requires Copilot Workspace API access',
12
+ };
13
+ }
14
+ }
15
+ //# sourceMappingURL=copilot-stub.js.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * GitHub Copilot CLI runner — invokes `copilot` in --yolo mode
3
+ * and collects the result via git diff.
4
+ */
5
+ import type { AgentRunner, AgentContext, AgentResult, TokenUsage } from './types.js';
6
+ import { buildPrompt } from './claude-code.js';
7
+ export { buildPrompt };
8
+ /**
9
+ * Parse token usage from Copilot CLI stderr output.
10
+ */
11
+ export declare function parseTokenUsage(stderr: string, model: string): TokenUsage | undefined;
12
+ export declare class CopilotRunner implements AgentRunner {
13
+ run(ctx: AgentContext): Promise<AgentResult>;
14
+ }
15
+ //# sourceMappingURL=copilot.d.ts.map
@@ -0,0 +1,119 @@
1
+ /**
2
+ * GitHub Copilot CLI runner — invokes `copilot` in --yolo mode
3
+ * and collects the result via git diff.
4
+ */
5
+ import { spawn, execFile } from 'node:child_process';
6
+ import { promisify } from 'node:util';
7
+ import { DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_COPILOT_MODEL, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
8
+ import { buildPrompt } from './claude-code.js';
9
+ export { buildPrompt };
10
+ const execFileAsync = promisify(execFile);
11
+ async function gitExec(workDir, args) {
12
+ const { stdout } = await execFileAsync('git', args, { cwd: workDir });
13
+ return stdout.trim();
14
+ }
15
+ /**
16
+ * Parse token usage from Copilot CLI stderr output.
17
+ */
18
+ export function parseTokenUsage(stderr, model) {
19
+ const inputMatch = stderr.match(/input[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
20
+ const outputMatch = stderr.match(/output[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
21
+ if (inputMatch || outputMatch) {
22
+ return {
23
+ inputTokens: inputMatch ? parseInt(inputMatch[1].replace(/,/g, ''), 10) : 0,
24
+ outputTokens: outputMatch ? parseInt(outputMatch[1].replace(/,/g, ''), 10) : 0,
25
+ model,
26
+ };
27
+ }
28
+ const totalMatch = stderr.match(/total[\s_-]*tokens?[:\s]+(\d[\d,]*)/i);
29
+ if (totalMatch) {
30
+ const total = parseInt(totalMatch[1].replace(/,/g, ''), 10);
31
+ return {
32
+ inputTokens: Math.round(total * 0.7),
33
+ outputTokens: Math.round(total * 0.3),
34
+ model,
35
+ };
36
+ }
37
+ return undefined;
38
+ }
39
+ export class CopilotRunner {
40
+ async run(ctx) {
41
+ const prompt = buildPrompt(ctx);
42
+ const timeoutMs = ctx.timeoutMs ?? DEFAULT_RUNNER_TIMEOUT_MS;
43
+ const model = DEFAULT_COPILOT_MODEL ?? 'copilot-default';
44
+ try {
45
+ const args = ['-p', prompt, '--yolo'];
46
+ if (DEFAULT_COPILOT_MODEL) {
47
+ args.push('--model', DEFAULT_COPILOT_MODEL);
48
+ }
49
+ const { stdout, stderr } = await new Promise((resolve, reject) => {
50
+ const child = spawn('copilot', args, {
51
+ cwd: ctx.workDir,
52
+ stdio: ['ignore', 'pipe', 'pipe'],
53
+ env: { ...process.env },
54
+ timeout: timeoutMs,
55
+ });
56
+ const chunks = [];
57
+ const errChunks = [];
58
+ child.stdout.on('data', (data) => chunks.push(data));
59
+ child.stderr.on('data', (data) => errChunks.push(data));
60
+ child.on('close', (code) => {
61
+ const out = Buffer.concat(chunks).toString('utf-8');
62
+ const err = Buffer.concat(errChunks).toString('utf-8');
63
+ if (code === 0) {
64
+ resolve({ stdout: out, stderr: err });
65
+ }
66
+ else {
67
+ reject(new Error(`copilot exited with code ${code}: ${err || out}`));
68
+ }
69
+ });
70
+ child.on('error', reject);
71
+ });
72
+ const tokenUsage = parseTokenUsage(stderr, model);
73
+ // Collect changed files
74
+ const diffOutput = await gitExec(ctx.workDir, ['diff', '--name-only']);
75
+ const untrackedOutput = await gitExec(ctx.workDir, [
76
+ 'ls-files',
77
+ '--others',
78
+ '--exclude-standard',
79
+ ]);
80
+ const filesChanged = [
81
+ ...diffOutput.split('\n').filter(Boolean),
82
+ ...untrackedOutput.split('\n').filter(Boolean),
83
+ ];
84
+ if (filesChanged.length === 0) {
85
+ return {
86
+ success: false,
87
+ filesChanged: [],
88
+ summary: 'Agent made no changes',
89
+ error: 'No files were modified',
90
+ tokenUsage,
91
+ };
92
+ }
93
+ // Stage and commit
94
+ await gitExec(ctx.workDir, ['add', '-A']);
95
+ const tmpl = ctx.commitMessageTemplate ?? DEFAULT_COMMIT_MESSAGE_TEMPLATE;
96
+ const coAuthor = ctx.commitCoAuthor ?? DEFAULT_COMMIT_CO_AUTHOR;
97
+ const commitMsg = tmpl
98
+ .replace(/\{issueNumber\}/g, String(ctx.issueNumber))
99
+ .replace(/\{issueTitle\}/g, ctx.issueTitle);
100
+ await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
101
+ return {
102
+ success: true,
103
+ filesChanged,
104
+ summary: stdout.slice(0, 2000),
105
+ tokenUsage,
106
+ };
107
+ }
108
+ catch (err) {
109
+ const message = err instanceof Error ? err.message : String(err);
110
+ return {
111
+ success: false,
112
+ filesChanged: [],
113
+ summary: 'Agent execution failed',
114
+ error: message,
115
+ };
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=copilot.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stub runner for Cursor — placeholder for future implementation.
3
+ */
4
+ import type { AgentRunner, AgentContext, AgentResult } from './types.js';
5
+ export declare class CursorStubRunner implements AgentRunner {
6
+ readonly name = "cursor";
7
+ run(_ctx: AgentContext): Promise<AgentResult>;
8
+ }
9
+ //# sourceMappingURL=cursor-stub.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Stub runner for Cursor — placeholder for future implementation.
3
+ */
4
+ export class CursorStubRunner {
5
+ name = 'cursor';
6
+ async run(_ctx) {
7
+ return {
8
+ success: false,
9
+ filesChanged: [],
10
+ summary: 'Cursor runner not yet implemented',
11
+ error: 'STUB: Cursor integration requires Cursor Agent API access',
12
+ };
13
+ }
14
+ }
15
+ //# sourceMappingURL=cursor-stub.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cursor CLI runner — invokes `cursor-agent` in --print mode with
3
+ * stream-json output and collects the result via git diff.
4
+ */
5
+ import type { AgentRunner, AgentContext, AgentResult, TokenUsage } from './types.js';
6
+ import { buildPrompt } from './claude-code.js';
7
+ export { buildPrompt };
8
+ /**
9
+ * Parse NDJSON stream output from cursor-agent --output-format=stream-json.
10
+ * Extracts the final assistant message content for use as a summary.
11
+ */
12
+ export declare function parseStreamJson(stdout: string): string;
13
+ /**
14
+ * Parse token usage from Cursor CLI stderr output.
15
+ */
16
+ export declare function parseTokenUsage(stderr: string, model: string): TokenUsage | undefined;
17
+ export declare class CursorRunner implements AgentRunner {
18
+ run(ctx: AgentContext): Promise<AgentResult>;
19
+ }
20
+ //# sourceMappingURL=cursor.d.ts.map