@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.
- package/dist/adapters.d.ts +60 -0
- package/dist/adapters.js +160 -0
- package/dist/admission.d.ts +49 -0
- package/dist/admission.js +90 -0
- package/dist/analysis/analyzer.d.ts +10 -0
- package/dist/analysis/analyzer.js +67 -0
- package/dist/analysis/complexity-scorer.d.ts +24 -0
- package/dist/analysis/complexity-scorer.js +39 -0
- package/dist/analysis/context-builder.d.ts +14 -0
- package/dist/analysis/context-builder.js +79 -0
- package/dist/analysis/convention-detector.d.ts +9 -0
- package/dist/analysis/convention-detector.js +177 -0
- package/dist/analysis/dependency-parser.d.ts +17 -0
- package/dist/analysis/dependency-parser.js +167 -0
- package/dist/analysis/file-walker.d.ts +18 -0
- package/dist/analysis/file-walker.js +158 -0
- package/dist/analysis/hotspot-analyzer.d.ts +28 -0
- package/dist/analysis/hotspot-analyzer.js +85 -0
- package/dist/analysis/index.d.ts +13 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/pattern-detector.d.ts +10 -0
- package/dist/analysis/pattern-detector.js +86 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.js +5 -0
- package/dist/audit-archival.d.ts +48 -0
- package/dist/audit-archival.js +72 -0
- package/dist/audit-export.d.ts +45 -0
- package/dist/audit-export.js +100 -0
- package/dist/audit-extended.d.ts +28 -0
- package/dist/audit-extended.js +38 -0
- package/dist/audit-scheduler.d.ts +32 -0
- package/dist/audit-scheduler.js +52 -0
- package/dist/audit-sqlite-sink.d.ts +11 -0
- package/dist/audit-sqlite-sink.js +51 -0
- package/dist/autonomy-tracker.d.ts +76 -0
- package/dist/autonomy-tracker.js +277 -0
- package/dist/check-runs.d.ts +37 -0
- package/dist/check-runs.js +72 -0
- package/dist/cli/commands/agents.d.ts +6 -0
- package/dist/cli/commands/agents.js +33 -0
- package/dist/cli/commands/complexity.d.ts +6 -0
- package/dist/cli/commands/complexity.js +35 -0
- package/dist/cli/commands/cost.d.ts +6 -0
- package/dist/cli/commands/cost.js +53 -0
- package/dist/cli/commands/dashboard.d.ts +9 -0
- package/dist/cli/commands/dashboard.js +50 -0
- package/dist/cli/commands/health.d.ts +6 -0
- package/dist/cli/commands/health.js +33 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/routing.d.ts +6 -0
- package/dist/cli/commands/routing.js +46 -0
- package/dist/cli/commands/run.d.ts +6 -0
- package/dist/cli/commands/run.js +36 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.js +34 -0
- package/dist/cli/dashboard-renderer.d.ts +24 -0
- package/dist/cli/dashboard-renderer.js +129 -0
- package/dist/cli/formatters/index.d.ts +6 -0
- package/dist/cli/formatters/index.js +18 -0
- package/dist/cli/formatters/json.d.ts +5 -0
- package/dist/cli/formatters/json.js +7 -0
- package/dist/cli/formatters/minimal.d.ts +5 -0
- package/dist/cli/formatters/minimal.js +36 -0
- package/dist/cli/formatters/table.d.ts +5 -0
- package/dist/cli/formatters/table.js +184 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/compliance-extended.d.ts +41 -0
- package/dist/compliance-extended.js +58 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +66 -0
- package/dist/context-enrichment.d.ts +33 -0
- package/dist/context-enrichment.js +125 -0
- package/dist/cost-governance.d.ts +22 -0
- package/dist/cost-governance.js +106 -0
- package/dist/cost-tracker.d.ts +75 -0
- package/dist/cost-tracker.js +177 -0
- package/dist/defaults.d.ts +140 -0
- package/dist/defaults.js +218 -0
- package/dist/deploy/flyio-target.d.ts +19 -0
- package/dist/deploy/flyio-target.js +114 -0
- package/dist/deploy/index.d.ts +15 -0
- package/dist/deploy/index.js +11 -0
- package/dist/deploy/kubernetes-target.d.ts +25 -0
- package/dist/deploy/kubernetes-target.js +124 -0
- package/dist/deploy/metrics-collector.d.ts +31 -0
- package/dist/deploy/metrics-collector.js +75 -0
- package/dist/deploy/rollout-controller.d.ts +42 -0
- package/dist/deploy/rollout-controller.js +192 -0
- package/dist/deploy/rollout-types.d.ts +86 -0
- package/dist/deploy/rollout-types.js +5 -0
- package/dist/deploy/types.d.ts +47 -0
- package/dist/deploy/types.js +5 -0
- package/dist/deploy/vercel-target.d.ts +16 -0
- package/dist/deploy/vercel-target.js +115 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.js +46 -0
- package/dist/episodic-enhanced.d.ts +88 -0
- package/dist/episodic-enhanced.js +212 -0
- package/dist/execute.d.ts +81 -0
- package/dist/execute.js +703 -0
- package/dist/fix-ci.d.ts +61 -0
- package/dist/fix-ci.js +380 -0
- package/dist/handoff-executor.d.ts +61 -0
- package/dist/handoff-executor.js +119 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +87 -0
- package/dist/instrumented.d.ts +27 -0
- package/dist/instrumented.js +34 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.js +37 -0
- package/dist/multi-repo/impact-analyzer.d.ts +22 -0
- package/dist/multi-repo/impact-analyzer.js +95 -0
- package/dist/multi-repo/index.d.ts +5 -0
- package/dist/multi-repo/index.js +4 -0
- package/dist/multi-repo/monorepo-detector.d.ts +16 -0
- package/dist/multi-repo/monorepo-detector.js +246 -0
- package/dist/multi-repo/service-map-builder.d.ts +24 -0
- package/dist/multi-repo/service-map-builder.js +235 -0
- package/dist/multi-repo/types.d.ts +54 -0
- package/dist/multi-repo/types.js +5 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/notification-router.d.ts +62 -0
- package/dist/notifications/notification-router.js +206 -0
- package/dist/notifications/slack-messenger.d.ts +26 -0
- package/dist/notifications/slack-messenger.js +69 -0
- package/dist/notifications/teams-messenger.d.ts +21 -0
- package/dist/notifications/teams-messenger.js +85 -0
- package/dist/notifications.d.ts +13 -0
- package/dist/notifications.js +15 -0
- package/dist/orchestration.d.ts +20 -0
- package/dist/orchestration.js +53 -0
- package/dist/orchestrator.d.ts +160 -0
- package/dist/orchestrator.js +372 -0
- package/dist/otel-exporter.d.ts +37 -0
- package/dist/otel-exporter.js +128 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +8 -0
- package/dist/policy-evaluators.d.ts +40 -0
- package/dist/policy-evaluators.js +57 -0
- package/dist/process-escalation.d.ts +38 -0
- package/dist/process-escalation.js +129 -0
- package/dist/progressive-gates.d.ts +54 -0
- package/dist/progressive-gates.js +135 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +61 -0
- package/dist/reconcilers.d.ts +28 -0
- package/dist/reconcilers.js +37 -0
- package/dist/runners/claude-code.d.ts +17 -0
- package/dist/runners/claude-code.js +200 -0
- package/dist/runners/codex.d.ts +19 -0
- package/dist/runners/codex.js +153 -0
- package/dist/runners/copilot-stub.d.ts +9 -0
- package/dist/runners/copilot-stub.js +15 -0
- package/dist/runners/copilot.d.ts +15 -0
- package/dist/runners/copilot.js +119 -0
- package/dist/runners/cursor-stub.d.ts +9 -0
- package/dist/runners/cursor-stub.js +15 -0
- package/dist/runners/cursor.d.ts +20 -0
- package/dist/runners/cursor.js +140 -0
- package/dist/runners/devin-stub.d.ts +9 -0
- package/dist/runners/devin-stub.js +15 -0
- package/dist/runners/generic-llm.d.ts +51 -0
- package/dist/runners/generic-llm.js +125 -0
- package/dist/runners/index.d.ts +8 -0
- package/dist/runners/index.js +7 -0
- package/dist/runners/runner-registry.d.ts +50 -0
- package/dist/runners/runner-registry.js +156 -0
- package/dist/runners/types.d.ts +59 -0
- package/dist/runners/types.js +7 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +70 -0
- package/dist/shared.d.ts +134 -0
- package/dist/shared.js +284 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +3 -0
- package/dist/state/schema.d.ts +16 -0
- package/dist/state/schema.js +264 -0
- package/dist/state/store.d.ts +109 -0
- package/dist/state/store.js +644 -0
- package/dist/state/types.d.ts +190 -0
- package/dist/state/types.js +5 -0
- package/dist/structured-logger.d.ts +20 -0
- package/dist/structured-logger.js +64 -0
- package/dist/task-decomposer.d.ts +61 -0
- package/dist/task-decomposer.js +265 -0
- package/dist/telemetry-extended.d.ts +24 -0
- package/dist/telemetry-extended.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +7 -0
- package/dist/validate-agent-output.d.ts +30 -0
- package/dist/validate-agent-output.js +81 -0
- package/dist/validate-issue.d.ts +25 -0
- package/dist/validate-issue.js +102 -0
- package/dist/watch.d.ts +43 -0
- package/dist/watch.js +99 -0
- package/dist/webhook-manager.d.ts +38 -0
- package/dist/webhook-manager.js +66 -0
- 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
|