@bamptee/aia-code 2.0.13 → 2.0.15
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/package.json
CHANGED
|
@@ -12,10 +12,14 @@ export async function generate(prompt, model, { verbose = false, apply = false,
|
|
|
12
12
|
}
|
|
13
13
|
if (apply) {
|
|
14
14
|
args.push('--allowedTools', 'Edit,Write,Bash,Read,Glob,Grep');
|
|
15
|
+
// Use stream-json for real-time output visibility
|
|
16
|
+
args.push('--output-format', 'stream-json');
|
|
17
|
+
// Include partial messages for token-by-token streaming
|
|
18
|
+
args.push('--include-partial-messages');
|
|
15
19
|
}
|
|
16
20
|
if (verbose || apply) {
|
|
17
21
|
args.push('--verbose');
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
return runCli('claude', args, { stdin: prompt, verbose: verbose || apply, apply, onData, cwd });
|
|
24
|
+
return runCli('claude', args, { stdin: prompt, verbose: verbose || apply, apply, onData, cwd, streamJson: apply });
|
|
21
25
|
}
|
|
@@ -3,24 +3,124 @@ import { Readable } from 'node:stream';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_IDLE_TIMEOUT_MS = 180_000;
|
|
6
|
-
const AGENT_IDLE_TIMEOUT_MS =
|
|
6
|
+
const AGENT_IDLE_TIMEOUT_MS = 1_800_000; // 30 minutes pour éviter les timeouts sur les tâches complexes
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Parse stream-json events from Claude CLI and extract human-readable output
|
|
10
|
+
*/
|
|
11
|
+
function parseStreamJsonEvent(line, onData, state = {}) {
|
|
12
|
+
try {
|
|
13
|
+
const event = JSON.parse(line);
|
|
14
|
+
|
|
15
|
+
// Extract readable content based on event type
|
|
16
|
+
if (event.type === 'system' && event.subtype === 'init') {
|
|
17
|
+
const msg = `[init] Model: ${event.model}, Tools: ${event.tools?.length || 0}\n`;
|
|
18
|
+
if (onData) onData({ type: 'stdout', text: msg });
|
|
19
|
+
return { result: null, state };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Stream events (partial messages) - token by token streaming
|
|
23
|
+
if (event.type === 'stream_event' && event.event) {
|
|
24
|
+
const streamEvent = event.event;
|
|
25
|
+
|
|
26
|
+
// Content block delta - actual text tokens
|
|
27
|
+
if (streamEvent.type === 'content_block_delta' && streamEvent.delta) {
|
|
28
|
+
if (streamEvent.delta.type === 'text_delta' && streamEvent.delta.text) {
|
|
29
|
+
if (onData) onData({ type: 'stdout', text: streamEvent.delta.text });
|
|
30
|
+
}
|
|
31
|
+
// Tool use input delta
|
|
32
|
+
if (streamEvent.delta.type === 'input_json_delta' && streamEvent.delta.partial_json) {
|
|
33
|
+
// Tool input streaming - don't output raw JSON
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Content block start - new block starting
|
|
38
|
+
if (streamEvent.type === 'content_block_start' && streamEvent.content_block) {
|
|
39
|
+
if (streamEvent.content_block.type === 'tool_use') {
|
|
40
|
+
const toolMsg = `\n[tool] ${streamEvent.content_block.name}\n`;
|
|
41
|
+
if (onData) onData({ type: 'stdout', text: toolMsg });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Message start
|
|
46
|
+
if (streamEvent.type === 'message_start') {
|
|
47
|
+
// New message starting - could add indicator
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Message stop
|
|
51
|
+
if (streamEvent.type === 'message_stop') {
|
|
52
|
+
if (onData) onData({ type: 'stdout', text: '\n' });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { result: null, state };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Complete assistant message (non-streaming)
|
|
59
|
+
if (event.type === 'assistant' && event.message?.content) {
|
|
60
|
+
for (const block of event.message.content) {
|
|
61
|
+
if (block.type === 'text' && block.text) {
|
|
62
|
+
if (onData) onData({ type: 'stdout', text: block.text });
|
|
63
|
+
} else if (block.type === 'tool_use') {
|
|
64
|
+
const toolMsg = `\n[tool] ${block.name}\n`;
|
|
65
|
+
if (onData) onData({ type: 'stdout', text: toolMsg });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { result: null, state };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// User message (tool results)
|
|
72
|
+
if (event.type === 'user' && event.message?.content) {
|
|
73
|
+
for (const block of event.message.content) {
|
|
74
|
+
if (block.type === 'tool_result') {
|
|
75
|
+
const status = block.is_error ? '✗' : '✓';
|
|
76
|
+
const resultMsg = `\n[${status}] Tool completed\n`;
|
|
77
|
+
if (onData) onData({ type: 'stdout', text: resultMsg });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { result: null, state };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Final result
|
|
84
|
+
if (event.type === 'result') {
|
|
85
|
+
return { result: event.result || '', state };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { result: null, state };
|
|
89
|
+
} catch {
|
|
90
|
+
// Not valid JSON, return as-is
|
|
91
|
+
return { result: null, state };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function runCli(command, args, { stdin: stdinData, verbose = false, apply = false, idleTimeoutMs, onData, cwd, streamJson = false } = {}) {
|
|
9
96
|
if (!idleTimeoutMs) {
|
|
10
97
|
idleTimeoutMs = apply ? AGENT_IDLE_TIMEOUT_MS : DEFAULT_IDLE_TIMEOUT_MS;
|
|
11
98
|
}
|
|
12
99
|
return new Promise((resolve, reject) => {
|
|
13
|
-
|
|
100
|
+
// Log the full command for debugging (allows manual replay)
|
|
101
|
+
const shellCmd = [command, ...args].map(a => /[\s"']/.test(a) ? `'${a.replace(/'/g, "'\\''")}'` : a).join(' ');
|
|
102
|
+
console.error(chalk.gray(`[AI] Run: ${shellCmd}`));
|
|
103
|
+
if (stdinData) {
|
|
104
|
+
console.error(chalk.gray(`[AI] stdin: ${stdinData.length > 200 ? stdinData.slice(0, 200) + '...' : stdinData} (${(stdinData.length / 1024).toFixed(1)}KB)`));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Remove CLAUDECODE from env to avoid conflicts
|
|
108
|
+
const { CLAUDECODE: _, ...cleanEnv } = process.env;
|
|
14
109
|
const child = spawn(command, args, {
|
|
15
110
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
16
|
-
env: { ...
|
|
111
|
+
env: { ...cleanEnv, FORCE_COLOR: '0' },
|
|
17
112
|
cwd,
|
|
18
113
|
});
|
|
19
114
|
|
|
115
|
+
console.error(chalk.gray(`[AI] PID: ${child.pid}`));
|
|
116
|
+
|
|
20
117
|
const chunks = [];
|
|
21
118
|
let stderr = '';
|
|
22
119
|
let settled = false;
|
|
23
120
|
let gotFirstOutput = false;
|
|
121
|
+
let jsonBuffer = ''; // Buffer for incomplete JSON lines
|
|
122
|
+
let finalResult = ''; // Store the final result from stream-json
|
|
123
|
+
let parserState = {}; // State for stream parser
|
|
24
124
|
|
|
25
125
|
function resetTimer() {
|
|
26
126
|
clearTimeout(timer);
|
|
@@ -47,10 +147,32 @@ export function runCli(command, args, { stdin: stdinData, verbose = false, apply
|
|
|
47
147
|
console.error(chalk.gray('[AI] First stdout received — agent is running'));
|
|
48
148
|
}
|
|
49
149
|
const text = data.toString();
|
|
50
|
-
if (verbose) process.stdout.write(text);
|
|
51
|
-
chunks.push(text);
|
|
52
|
-
if (onData) onData({ type: 'stdout', text });
|
|
53
150
|
resetTimer();
|
|
151
|
+
|
|
152
|
+
if (streamJson) {
|
|
153
|
+
// Parse stream-json format line by line
|
|
154
|
+
jsonBuffer += text;
|
|
155
|
+
const lines = jsonBuffer.split('\n');
|
|
156
|
+
jsonBuffer = lines.pop() || ''; // Keep incomplete line in buffer
|
|
157
|
+
|
|
158
|
+
for (const line of lines) {
|
|
159
|
+
if (!line.trim()) continue;
|
|
160
|
+
const { result, state } = parseStreamJsonEvent(line, onData, parserState);
|
|
161
|
+
parserState = state;
|
|
162
|
+
if (result !== null) {
|
|
163
|
+
finalResult = result;
|
|
164
|
+
}
|
|
165
|
+
if (verbose) {
|
|
166
|
+
// In verbose mode, also show raw JSON for debugging
|
|
167
|
+
process.stdout.write(chalk.gray(line + '\n'));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
// Original behavior for non-stream-json
|
|
172
|
+
if (verbose) process.stdout.write(text);
|
|
173
|
+
chunks.push(text);
|
|
174
|
+
if (onData) onData({ type: 'stdout', text });
|
|
175
|
+
}
|
|
54
176
|
});
|
|
55
177
|
|
|
56
178
|
child.stderr.on('data', (data) => {
|
|
@@ -75,11 +197,21 @@ export function runCli(command, args, { stdin: stdinData, verbose = false, apply
|
|
|
75
197
|
}
|
|
76
198
|
});
|
|
77
199
|
|
|
78
|
-
child.on('close', (code) => {
|
|
200
|
+
child.on('close', (code, signal) => {
|
|
201
|
+
console.error(chalk.gray(`[AI] Process exited: code=${code} signal=${signal || 'none'}`));
|
|
202
|
+
// Process any remaining data in the buffer
|
|
203
|
+
if (streamJson && jsonBuffer.trim()) {
|
|
204
|
+
const { result } = parseStreamJsonEvent(jsonBuffer, onData, parserState);
|
|
205
|
+
if (result !== null) {
|
|
206
|
+
finalResult = result;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
79
210
|
if (code !== 0) {
|
|
80
211
|
finish(new Error(`${command} exited with code ${code}:\n${stderr.trim()}`));
|
|
81
212
|
} else {
|
|
82
|
-
|
|
213
|
+
// For stream-json, return the extracted result; otherwise return chunks
|
|
214
|
+
finish(null, streamJson ? finalResult : chunks.join(''));
|
|
83
215
|
}
|
|
84
216
|
});
|
|
85
217
|
|
package/src/providers/gemini.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { runCli } from './cli-runner.js';
|
|
2
2
|
|
|
3
|
-
export async function generate(prompt, model, { verbose = false, apply = false, onData } = {}) {
|
|
3
|
+
export async function generate(prompt, model, { verbose = false, apply = false, onData, cwd } = {}) {
|
|
4
4
|
const args = [];
|
|
5
5
|
if (model) {
|
|
6
6
|
args.push('-m', model);
|
|
7
7
|
}
|
|
8
8
|
if (apply) {
|
|
9
9
|
args.push('--sandbox', 'false');
|
|
10
|
+
// Use stream-json for real-time output visibility
|
|
11
|
+
args.push('--output-format', 'stream-json');
|
|
10
12
|
}
|
|
11
13
|
args.push('-');
|
|
12
14
|
|
|
13
|
-
return runCli('gemini', args, { stdin: prompt, verbose: verbose || apply, apply, onData });
|
|
15
|
+
return runCli('gemini', args, { stdin: prompt, verbose: verbose || apply, apply, onData, cwd, streamJson: apply });
|
|
14
16
|
}
|
package/src/providers/openai.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runCli } from './cli-runner.js';
|
|
2
2
|
|
|
3
|
-
export async function generate(prompt, model, { verbose = false, apply = false, onData } = {}) {
|
|
3
|
+
export async function generate(prompt, model, { verbose = false, apply = false, onData, cwd } = {}) {
|
|
4
4
|
const args = ['exec'];
|
|
5
5
|
if (model) {
|
|
6
6
|
args.push('-c', `model="${model}"`);
|
|
@@ -10,5 +10,6 @@ export async function generate(prompt, model, { verbose = false, apply = false,
|
|
|
10
10
|
}
|
|
11
11
|
args.push('-');
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Note: Codex CLI does not support stream-json output format
|
|
14
|
+
return runCli('codex', args, { stdin: prompt, verbose: verbose || apply, apply, onData, cwd });
|
|
14
15
|
}
|