wiggum-cli 0.16.0 → 0.17.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/bin/ralph.js +0 -0
- package/dist/agent/memory/ingest.d.ts +14 -0
- package/dist/agent/memory/ingest.js +77 -0
- package/dist/agent/memory/store.d.ts +15 -0
- package/dist/agent/memory/store.js +98 -0
- package/dist/agent/memory/types.d.ts +16 -0
- package/dist/agent/memory/types.js +14 -0
- package/dist/agent/orchestrator.d.ts +7 -0
- package/dist/agent/orchestrator.js +266 -0
- package/dist/agent/resolve-config.d.ts +26 -0
- package/dist/agent/resolve-config.js +43 -0
- package/dist/agent/tools/backlog.d.ts +27 -0
- package/dist/agent/tools/backlog.js +51 -0
- package/dist/agent/tools/dry-run.d.ts +106 -0
- package/dist/agent/tools/dry-run.js +119 -0
- package/dist/agent/tools/execution.d.ts +51 -0
- package/dist/agent/tools/execution.js +256 -0
- package/dist/agent/tools/feature-state.d.ts +43 -0
- package/dist/agent/tools/feature-state.js +184 -0
- package/dist/agent/tools/introspection.d.ts +23 -0
- package/dist/agent/tools/introspection.js +40 -0
- package/dist/agent/tools/memory.d.ts +44 -0
- package/dist/agent/tools/memory.js +99 -0
- package/dist/agent/tools/preflight.d.ts +7 -0
- package/dist/agent/tools/preflight.js +137 -0
- package/dist/agent/tools/reporting.d.ts +58 -0
- package/dist/agent/tools/reporting.js +119 -0
- package/dist/agent/tools/schemas.d.ts +2 -0
- package/dist/agent/tools/schemas.js +3 -0
- package/dist/agent/types.d.ts +45 -0
- package/dist/agent/types.js +1 -0
- package/dist/ai/conversation/conversation-manager.js +8 -0
- package/dist/ai/conversation/url-fetcher.js +27 -0
- package/dist/ai/providers.js +5 -5
- package/dist/commands/agent.d.ts +17 -0
- package/dist/commands/agent.js +114 -0
- package/dist/commands/monitor.js +50 -183
- package/dist/commands/new-auto.d.ts +15 -0
- package/dist/commands/new-auto.js +237 -0
- package/dist/commands/run.js +20 -10
- package/dist/commands/sync.d.ts +15 -0
- package/dist/commands/sync.js +68 -0
- package/dist/generator/config.d.ts +1 -41
- package/dist/generator/config.js +7 -0
- package/dist/generator/index.d.ts +2 -2
- package/dist/generator/templates.d.ts +2 -0
- package/dist/generator/templates.js +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +115 -4
- package/dist/repl/command-parser.d.ts +5 -0
- package/dist/repl/command-parser.js +5 -0
- package/dist/templates/prompts/PROMPT.md.tmpl +13 -10
- package/dist/templates/prompts/PROMPT_e2e.md.tmpl +13 -7
- package/dist/templates/prompts/PROMPT_feature.md.tmpl +16 -3
- package/dist/templates/prompts/PROMPT_review_auto.md.tmpl +32 -12
- package/dist/templates/prompts/PROMPT_review_manual.md.tmpl +4 -1
- package/dist/templates/prompts/PROMPT_review_merge.md.tmpl +39 -14
- package/dist/templates/prompts/PROMPT_verify.md.tmpl +5 -2
- package/dist/templates/scripts/feature-loop.sh.tmpl +441 -69
- package/dist/tui/app.d.ts +19 -2
- package/dist/tui/app.js +22 -4
- package/dist/tui/components/IssuePicker.d.ts +27 -0
- package/dist/tui/components/IssuePicker.js +64 -0
- package/dist/tui/components/RunCompletionSummary.js +6 -3
- package/dist/tui/hooks/useAgentOrchestrator.d.ts +29 -0
- package/dist/tui/hooks/useAgentOrchestrator.js +453 -0
- package/dist/tui/orchestration/interview-orchestrator.d.ts +5 -1
- package/dist/tui/orchestration/interview-orchestrator.js +27 -6
- package/dist/tui/screens/AgentScreen.d.ts +21 -0
- package/dist/tui/screens/AgentScreen.js +159 -0
- package/dist/tui/screens/InitScreen.js +4 -0
- package/dist/tui/screens/InterviewScreen.d.ts +3 -1
- package/dist/tui/screens/InterviewScreen.js +146 -10
- package/dist/tui/screens/MainShell.d.ts +1 -1
- package/dist/tui/screens/MainShell.js +36 -1
- package/dist/tui/screens/RunScreen.js +38 -6
- package/dist/tui/utils/build-run-summary.d.ts +1 -1
- package/dist/tui/utils/build-run-summary.js +40 -84
- package/dist/tui/utils/clear-screen.d.ts +14 -0
- package/dist/tui/utils/clear-screen.js +16 -0
- package/dist/tui/utils/loop-status.d.ts +41 -1
- package/dist/tui/utils/loop-status.js +243 -35
- package/dist/tui/utils/pr-summary.d.ts +3 -2
- package/dist/tui/utils/pr-summary.js +41 -6
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.js +8 -0
- package/dist/utils/github.d.ts +32 -0
- package/dist/utils/github.js +106 -0
- package/package.json +4 -1
- package/src/templates/prompts/PROMPT.md.tmpl +13 -10
- package/src/templates/prompts/PROMPT_e2e.md.tmpl +13 -7
- package/src/templates/prompts/PROMPT_feature.md.tmpl +16 -3
- package/src/templates/prompts/PROMPT_review_auto.md.tmpl +32 -12
- package/src/templates/prompts/PROMPT_review_manual.md.tmpl +4 -1
- package/src/templates/prompts/PROMPT_review_merge.md.tmpl +39 -14
- package/src/templates/prompts/PROMPT_verify.md.tmpl +5 -2
- package/src/templates/scripts/feature-loop.sh.tmpl +441 -69
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { tool, zodSchema } from 'ai';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
function ghExec(args) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
execFile('gh', args, { timeout: 15000 }, (error, stdout) => {
|
|
7
|
+
if (error)
|
|
8
|
+
reject(error);
|
|
9
|
+
else
|
|
10
|
+
resolve(String(stdout));
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function createReportingTools(owner, repo) {
|
|
15
|
+
const commentOnIssue = tool({
|
|
16
|
+
description: 'Post a status comment on a GitHub issue.',
|
|
17
|
+
inputSchema: zodSchema(z.object({
|
|
18
|
+
issueNumber: z.number().int().describe('Issue number to comment on'),
|
|
19
|
+
body: z.string().describe('Comment body in markdown'),
|
|
20
|
+
})),
|
|
21
|
+
execute: async ({ issueNumber, body }) => {
|
|
22
|
+
try {
|
|
23
|
+
await ghExec([
|
|
24
|
+
'issue', 'comment', String(issueNumber),
|
|
25
|
+
'--repo', `${owner}/${repo}`,
|
|
26
|
+
'--body', body,
|
|
27
|
+
]);
|
|
28
|
+
return { success: true };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const createIssue = tool({
|
|
36
|
+
description: 'Create a new GitHub issue. Use for tech debt, blockers, bugs, or follow-up work discovered during development.',
|
|
37
|
+
inputSchema: zodSchema(z.object({
|
|
38
|
+
title: z.string().describe('Issue title'),
|
|
39
|
+
body: z.string().describe('Issue body in markdown'),
|
|
40
|
+
labels: z.array(z.string()).default([]).describe('Labels to apply (e.g. bug, P0, tech-debt, blocker)'),
|
|
41
|
+
})),
|
|
42
|
+
execute: async ({ title, body, labels }) => {
|
|
43
|
+
try {
|
|
44
|
+
const args = [
|
|
45
|
+
'issue', 'create',
|
|
46
|
+
'--repo', `${owner}/${repo}`,
|
|
47
|
+
'--title', title,
|
|
48
|
+
'--body', body,
|
|
49
|
+
];
|
|
50
|
+
for (const label of labels) {
|
|
51
|
+
args.push('--label', label);
|
|
52
|
+
}
|
|
53
|
+
const stdout = await ghExec(args);
|
|
54
|
+
const url = stdout.trim();
|
|
55
|
+
const match = url.match(/\/(\d+)\s*$/);
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
issueNumber: match ? parseInt(match[1], 10) : undefined,
|
|
59
|
+
url,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
const closeIssue = tool({
|
|
68
|
+
description: 'Close a GitHub issue. Use after verifying the work is actually shipped (PR merged, code on main).',
|
|
69
|
+
inputSchema: zodSchema(z.object({
|
|
70
|
+
issueNumber: z.number().int().describe('Issue number to close'),
|
|
71
|
+
comment: z.string().optional().describe('Optional closing comment'),
|
|
72
|
+
})),
|
|
73
|
+
execute: async ({ issueNumber, comment }) => {
|
|
74
|
+
try {
|
|
75
|
+
const args = [
|
|
76
|
+
'issue', 'close', String(issueNumber),
|
|
77
|
+
'--repo', `${owner}/${repo}`,
|
|
78
|
+
];
|
|
79
|
+
if (comment)
|
|
80
|
+
args.push('--comment', comment);
|
|
81
|
+
await ghExec(args);
|
|
82
|
+
return { success: true };
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const checkAllBoxes = tool({
|
|
90
|
+
description: 'Check all acceptance-criteria checkboxes in a GitHub issue body. Use after verifying all criteria are met.',
|
|
91
|
+
inputSchema: zodSchema(z.object({
|
|
92
|
+
issueNumber: z.number().int().describe('Issue number to update'),
|
|
93
|
+
})),
|
|
94
|
+
execute: async ({ issueNumber }) => {
|
|
95
|
+
try {
|
|
96
|
+
const body = await ghExec([
|
|
97
|
+
'issue', 'view', String(issueNumber),
|
|
98
|
+
'--repo', `${owner}/${repo}`,
|
|
99
|
+
'--json', 'body', '--jq', '.body',
|
|
100
|
+
]);
|
|
101
|
+
const updated = body.replace(/- \[ \]/g, '- [x]');
|
|
102
|
+
if (updated === body) {
|
|
103
|
+
return { success: true, changed: false, message: 'No unchecked boxes found' };
|
|
104
|
+
}
|
|
105
|
+
await ghExec([
|
|
106
|
+
'issue', 'edit', String(issueNumber),
|
|
107
|
+
'--repo', `${owner}/${repo}`,
|
|
108
|
+
'--body', updated,
|
|
109
|
+
]);
|
|
110
|
+
const checked = (updated.match(/- \[x\]/gi) ?? []).length;
|
|
111
|
+
return { success: true, changed: true, totalChecked: checked };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
return { commentOnIssue, createIssue, closeIssue, checkAllBoxes };
|
|
119
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { LanguageModel } from 'ai';
|
|
2
|
+
export type ReviewMode = 'manual' | 'auto' | 'merge';
|
|
3
|
+
export interface AgentConfig {
|
|
4
|
+
model: LanguageModel;
|
|
5
|
+
modelId?: string;
|
|
6
|
+
provider?: string;
|
|
7
|
+
projectRoot: string;
|
|
8
|
+
owner: string;
|
|
9
|
+
repo: string;
|
|
10
|
+
maxSteps?: number;
|
|
11
|
+
maxItems?: number;
|
|
12
|
+
labels?: string[];
|
|
13
|
+
reviewMode?: ReviewMode;
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
onStepUpdate?: (event: AgentStepEvent) => void;
|
|
16
|
+
onProgress?: (toolName: string, line: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface AgentStepEvent {
|
|
19
|
+
toolCalls: Array<{
|
|
20
|
+
toolName: string;
|
|
21
|
+
args: unknown;
|
|
22
|
+
}>;
|
|
23
|
+
toolResults: Array<{
|
|
24
|
+
toolName: string;
|
|
25
|
+
result: unknown;
|
|
26
|
+
}>;
|
|
27
|
+
completedItems: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AgentLogEntry {
|
|
30
|
+
timestamp: string;
|
|
31
|
+
message: string;
|
|
32
|
+
level: 'info' | 'warn' | 'error' | 'success';
|
|
33
|
+
}
|
|
34
|
+
export type AgentPhase = 'idle' | 'planning' | 'generating_spec' | 'running_loop' | 'reporting' | 'reflecting';
|
|
35
|
+
export interface AgentIssueState {
|
|
36
|
+
issueNumber: number;
|
|
37
|
+
title: string;
|
|
38
|
+
labels: string[];
|
|
39
|
+
phase: AgentPhase;
|
|
40
|
+
loopPhase?: string;
|
|
41
|
+
loopFeatureName?: string;
|
|
42
|
+
loopIterations?: number;
|
|
43
|
+
prUrl?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -150,6 +150,10 @@ Be concise but thorough. Focus on understanding the user's needs before proposin
|
|
|
150
150
|
model,
|
|
151
151
|
messages,
|
|
152
152
|
...(isReasoningModel(this.modelId) ? {} : { temperature: 0.7 }),
|
|
153
|
+
experimental_telemetry: {
|
|
154
|
+
isEnabled: true,
|
|
155
|
+
metadata: { agent: 'conversation-manager', provider: this.provider, model: this.modelId },
|
|
156
|
+
},
|
|
153
157
|
};
|
|
154
158
|
if (this.tools && Object.keys(this.tools).length > 0) {
|
|
155
159
|
options.tools = this.tools;
|
|
@@ -192,6 +196,10 @@ Be concise but thorough. Focus on understanding the user's needs before proposin
|
|
|
192
196
|
model,
|
|
193
197
|
messages,
|
|
194
198
|
...(isReasoningModel(this.modelId) ? {} : { temperature: 0.7 }),
|
|
199
|
+
experimental_telemetry: {
|
|
200
|
+
isEnabled: true,
|
|
201
|
+
metadata: { agent: 'conversation-manager', provider: this.provider, model: this.modelId },
|
|
202
|
+
},
|
|
195
203
|
});
|
|
196
204
|
let fullResponse = '';
|
|
197
205
|
for await (const textPart of result.textStream) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { readFileSync, existsSync } from 'node:fs';
|
|
6
6
|
import { resolve, isAbsolute } from 'node:path';
|
|
7
|
+
import { isGitHubIssueUrl, isGhInstalled, fetchGitHubIssue } from '../../utils/github.js';
|
|
7
8
|
const MAX_CONTENT_LENGTH = 10000;
|
|
8
9
|
const FETCH_TIMEOUT = 10000;
|
|
9
10
|
/**
|
|
@@ -127,11 +128,37 @@ function readFromFile(filePath, projectRoot) {
|
|
|
127
128
|
};
|
|
128
129
|
}
|
|
129
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Fetch GitHub issue content via gh CLI
|
|
133
|
+
*/
|
|
134
|
+
async function fetchGitHubContent(owner, repo, number) {
|
|
135
|
+
if (!(await isGhInstalled()))
|
|
136
|
+
return null;
|
|
137
|
+
const issue = await fetchGitHubIssue(owner, repo, number);
|
|
138
|
+
if (!issue)
|
|
139
|
+
return null;
|
|
140
|
+
let content = `# ${issue.title}\n\n${issue.body ?? ''}`;
|
|
141
|
+
const truncated = content.length > MAX_CONTENT_LENGTH;
|
|
142
|
+
if (truncated) {
|
|
143
|
+
content = content.slice(0, MAX_CONTENT_LENGTH) + '\n\n[Content truncated...]';
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
source: `GitHub issue #${number}`,
|
|
147
|
+
content,
|
|
148
|
+
truncated,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
130
151
|
/**
|
|
131
152
|
* Fetch content from a URL or local file path
|
|
132
153
|
*/
|
|
133
154
|
export async function fetchContent(input, projectRoot) {
|
|
134
155
|
if (isUrl(input)) {
|
|
156
|
+
const ghIssue = isGitHubIssueUrl(input);
|
|
157
|
+
if (ghIssue) {
|
|
158
|
+
const result = await fetchGitHubContent(ghIssue.owner, ghIssue.repo, ghIssue.number);
|
|
159
|
+
if (result)
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
135
162
|
return fetchFromUrl(input);
|
|
136
163
|
}
|
|
137
164
|
return readFromFile(input, projectRoot);
|
package/dist/ai/providers.js
CHANGED
|
@@ -35,12 +35,12 @@ export const KNOWN_API_KEYS = [
|
|
|
35
35
|
export const AVAILABLE_MODELS = {
|
|
36
36
|
anthropic: [
|
|
37
37
|
{ value: 'claude-opus-4-6', label: 'Claude Opus 4.6', hint: 'most capable' },
|
|
38
|
-
{ value: 'claude-sonnet-4-
|
|
38
|
+
{ value: 'claude-sonnet-4-6', label: 'Claude Sonnet 4.6', hint: 'recommended' },
|
|
39
39
|
{ value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', hint: 'fastest' },
|
|
40
40
|
],
|
|
41
41
|
openai: [
|
|
42
42
|
{ value: 'gpt-5.2', label: 'GPT-5.2', hint: 'most capable' },
|
|
43
|
-
{ value: 'gpt-5.
|
|
43
|
+
{ value: 'gpt-5.3-codex', label: 'GPT-5.3 Codex', hint: 'best for code' },
|
|
44
44
|
{ value: 'gpt-5.1', label: 'GPT-5.1', hint: 'previous gen' },
|
|
45
45
|
{ value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max', hint: 'previous codex' },
|
|
46
46
|
{ value: 'gpt-5-mini', label: 'GPT-5 Mini', hint: 'fastest' },
|
|
@@ -60,7 +60,7 @@ export const AVAILABLE_MODELS = {
|
|
|
60
60
|
* Using balanced models for good results
|
|
61
61
|
*/
|
|
62
62
|
const DEFAULT_MODELS = {
|
|
63
|
-
anthropic: 'claude-sonnet-4-
|
|
63
|
+
anthropic: 'claude-sonnet-4-6',
|
|
64
64
|
openai: 'gpt-5.2',
|
|
65
65
|
openrouter: 'google/gemini-3-pro-preview',
|
|
66
66
|
};
|
|
@@ -68,7 +68,7 @@ const DEFAULT_MODELS = {
|
|
|
68
68
|
* Anthropic shorthand aliases for legacy configs
|
|
69
69
|
*/
|
|
70
70
|
const ANTHROPIC_MODEL_ALIASES = {
|
|
71
|
-
sonnet: 'claude-sonnet-4-
|
|
71
|
+
sonnet: 'claude-sonnet-4-6',
|
|
72
72
|
opus: 'claude-opus-4-6',
|
|
73
73
|
haiku: 'claude-haiku-4-5-20251001',
|
|
74
74
|
};
|
|
@@ -177,7 +177,7 @@ const REASONING_MODELS = [
|
|
|
177
177
|
'o3', 'o3-mini',
|
|
178
178
|
'gpt-5', 'gpt-5.1', 'gpt-5-mini',
|
|
179
179
|
'gpt-5.1-codex', 'gpt-5.1-codex-max',
|
|
180
|
-
'gpt-5.2', 'gpt-5.
|
|
180
|
+
'gpt-5.2', 'gpt-5.3-codex',
|
|
181
181
|
];
|
|
182
182
|
/**
|
|
183
183
|
* Check if a model is a reasoning model that doesn't support temperature
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Command — Headless autonomous backlog executor
|
|
3
|
+
*
|
|
4
|
+
* Reads provider and model from ralph.config.cjs (set during wiggum init),
|
|
5
|
+
* detects GitHub remote, creates the agent orchestrator, and runs it
|
|
6
|
+
* in headless mode (generate or stream).
|
|
7
|
+
*/
|
|
8
|
+
export interface AgentOptions {
|
|
9
|
+
model?: string;
|
|
10
|
+
maxItems?: number;
|
|
11
|
+
maxSteps?: number;
|
|
12
|
+
labels?: string[];
|
|
13
|
+
reviewMode?: 'manual' | 'auto' | 'merge';
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
stream?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function agentCommand(options?: AgentOptions): Promise<void>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Command — Headless autonomous backlog executor
|
|
3
|
+
*
|
|
4
|
+
* Reads provider and model from ralph.config.cjs (set during wiggum init),
|
|
5
|
+
* detects GitHub remote, creates the agent orchestrator, and runs it
|
|
6
|
+
* in headless mode (generate or stream).
|
|
7
|
+
*/
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import { createAgentOrchestrator, } from '../agent/orchestrator.js';
|
|
10
|
+
import { resolveAgentEnv } from '../agent/resolve-config.js';
|
|
11
|
+
import { initTracing, flushTracing, traced, currentSpan } from '../utils/tracing.js';
|
|
12
|
+
export async function agentCommand(options = {}) {
|
|
13
|
+
const projectRoot = process.cwd();
|
|
14
|
+
// Initialize Braintrust tracing (no-op if BRAINTRUST_API_KEY not set)
|
|
15
|
+
initTracing();
|
|
16
|
+
// Resolve provider, model, and GitHub remote
|
|
17
|
+
let env;
|
|
18
|
+
try {
|
|
19
|
+
env = await resolveAgentEnv(projectRoot, { model: options.model });
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const { provider, model, modelId, owner, repo } = env;
|
|
26
|
+
// 4. Create orchestrator
|
|
27
|
+
const agentConfig = {
|
|
28
|
+
model,
|
|
29
|
+
modelId: modelId ?? undefined,
|
|
30
|
+
provider,
|
|
31
|
+
projectRoot,
|
|
32
|
+
owner,
|
|
33
|
+
repo,
|
|
34
|
+
maxSteps: options.maxSteps,
|
|
35
|
+
maxItems: options.maxItems,
|
|
36
|
+
labels: options.labels,
|
|
37
|
+
reviewMode: options.reviewMode,
|
|
38
|
+
dryRun: options.dryRun,
|
|
39
|
+
onStepUpdate: (event) => {
|
|
40
|
+
const log = options.stream
|
|
41
|
+
? (msg) => process.stdout.write(`${msg}\n`)
|
|
42
|
+
: (msg) => logger.info(msg);
|
|
43
|
+
for (const tc of event.toolCalls) {
|
|
44
|
+
log(`[tool] ${tc.toolName}`);
|
|
45
|
+
}
|
|
46
|
+
for (const tr of event.toolResults) {
|
|
47
|
+
const summary = typeof tr.result === 'object' && tr.result !== null
|
|
48
|
+
? tr.result.status ?? tr.result.success ?? 'done'
|
|
49
|
+
: 'done';
|
|
50
|
+
log(`[tool:done] ${tr.toolName} → ${summary}`);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
onProgress: (toolName, line) => {
|
|
54
|
+
const log = options.stream
|
|
55
|
+
? (msg) => process.stdout.write(`${msg}\n`)
|
|
56
|
+
: (msg) => logger.info(msg);
|
|
57
|
+
log(` [${toolName}] ${line}`);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const agent = createAgentOrchestrator(agentConfig);
|
|
61
|
+
// 5. Run in headless mode
|
|
62
|
+
logger.info(`Agent starting: ${owner}/${repo} with ${provider}/${modelId ?? 'default'}`);
|
|
63
|
+
try {
|
|
64
|
+
await traced(async () => {
|
|
65
|
+
currentSpan().log({
|
|
66
|
+
input: {
|
|
67
|
+
owner,
|
|
68
|
+
repo,
|
|
69
|
+
provider,
|
|
70
|
+
model: modelId ?? 'default',
|
|
71
|
+
maxItems: options.maxItems,
|
|
72
|
+
maxSteps: options.maxSteps,
|
|
73
|
+
labels: options.labels,
|
|
74
|
+
dryRun: options.dryRun ?? false,
|
|
75
|
+
stream: options.stream ?? false,
|
|
76
|
+
},
|
|
77
|
+
metadata: {
|
|
78
|
+
command: 'agent',
|
|
79
|
+
owner,
|
|
80
|
+
repo,
|
|
81
|
+
provider,
|
|
82
|
+
model: modelId ?? 'default',
|
|
83
|
+
dryRun: String(options.dryRun ?? false),
|
|
84
|
+
},
|
|
85
|
+
tags: ['agent'],
|
|
86
|
+
});
|
|
87
|
+
if (options.stream) {
|
|
88
|
+
const result = await agent.stream({ prompt: 'Begin working through the backlog.' });
|
|
89
|
+
let hasOutput = false;
|
|
90
|
+
for await (const chunk of result.textStream) {
|
|
91
|
+
process.stdout.write(chunk);
|
|
92
|
+
hasOutput = true;
|
|
93
|
+
}
|
|
94
|
+
if (hasOutput) {
|
|
95
|
+
process.stdout.write('\n');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const result = await agent.generate({ prompt: 'Begin working through the backlog.' });
|
|
100
|
+
if (result.text) {
|
|
101
|
+
console.log(result.text);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}, { name: 'agent-run' });
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
108
|
+
console.error(`Error: Agent failed — ${message}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
await flushTracing();
|
|
113
|
+
}
|
|
114
|
+
}
|