kernelbot 1.0.15 → 1.0.16
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 +1 -1
- package/src/coder.js +106 -19
- package/src/tools/coding.js +1 -3
package/package.json
CHANGED
package/src/coder.js
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
+
import { existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
2
5
|
import { getLogger } from './utils/logger.js';
|
|
3
6
|
|
|
7
|
+
function ensureClaudeCodeSetup() {
|
|
8
|
+
const logger = getLogger();
|
|
9
|
+
const claudeJson = join(homedir(), '.claude.json');
|
|
10
|
+
const claudeDir = join(homedir(), '.claude');
|
|
11
|
+
|
|
12
|
+
// Create ~/.claude/ directory if missing
|
|
13
|
+
if (!existsSync(claudeDir)) {
|
|
14
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
15
|
+
logger.info('Created ~/.claude/ directory');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Create ~/.claude.json with onboarding completed if missing
|
|
19
|
+
if (!existsSync(claudeJson)) {
|
|
20
|
+
const defaults = {
|
|
21
|
+
hasCompletedOnboarding: true,
|
|
22
|
+
theme: 'dark',
|
|
23
|
+
shiftEnterKeyBindingInstalled: true,
|
|
24
|
+
};
|
|
25
|
+
writeFileSync(claudeJson, JSON.stringify(defaults, null, 2));
|
|
26
|
+
logger.info('Created ~/.claude.json with default settings (skipping setup wizard)');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseStreamEvent(line, onOutput, logger) {
|
|
31
|
+
let event;
|
|
32
|
+
try {
|
|
33
|
+
event = JSON.parse(line);
|
|
34
|
+
} catch {
|
|
35
|
+
return; // skip non-JSON lines
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Assistant text message
|
|
39
|
+
if (event.type === 'message' && event.role === 'assistant') {
|
|
40
|
+
const texts = (event.content || [])
|
|
41
|
+
.filter((b) => b.type === 'text' && b.text?.trim())
|
|
42
|
+
.map((b) => b.text.trim());
|
|
43
|
+
if (texts.length > 0) {
|
|
44
|
+
const msg = texts.join('\n');
|
|
45
|
+
logger.info(`Claude Code: ${msg.slice(0, 200)}`);
|
|
46
|
+
if (onOutput) onOutput(`💬 *Claude Code:*\n${msg}`).catch(() => {});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Tool use
|
|
51
|
+
if (event.type === 'tool_use') {
|
|
52
|
+
const name = event.name || 'unknown';
|
|
53
|
+
const input = event.input || {};
|
|
54
|
+
const summary = input.command || input.file_path || input.pattern || input.query || JSON.stringify(input).slice(0, 100);
|
|
55
|
+
logger.info(`Claude Code tool: ${name}: ${String(summary).slice(0, 150)}`);
|
|
56
|
+
if (onOutput) onOutput(`🔨 \`${name}: ${String(summary).slice(0, 150)}\``).catch(() => {});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Result
|
|
60
|
+
if (event.type === 'result') {
|
|
61
|
+
const status = event.status || 'done';
|
|
62
|
+
const duration = event.duration_ms ? `${(event.duration_ms / 1000).toFixed(1)}s` : '';
|
|
63
|
+
logger.info(`Claude Code finished: ${status} ${duration}`);
|
|
64
|
+
if (onOutput) onOutput(`✅ Claude Code finished (${status}${duration ? ` in ${duration}` : ''})`).catch(() => {});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
4
68
|
export class ClaudeCodeSpawner {
|
|
5
69
|
constructor(config) {
|
|
6
70
|
this.maxTurns = config.claude_code?.max_turns || 50;
|
|
@@ -12,10 +76,18 @@ export class ClaudeCodeSpawner {
|
|
|
12
76
|
const logger = getLogger();
|
|
13
77
|
const turns = maxTurns || this.maxTurns;
|
|
14
78
|
|
|
79
|
+
// Auto-setup Claude Code if not configured
|
|
80
|
+
ensureClaudeCodeSetup();
|
|
81
|
+
|
|
15
82
|
logger.info(`Spawning Claude Code in ${workingDirectory}`);
|
|
16
83
|
|
|
17
84
|
return new Promise((resolve, reject) => {
|
|
18
|
-
const args = [
|
|
85
|
+
const args = [
|
|
86
|
+
'-p', prompt,
|
|
87
|
+
'--max-turns', String(turns),
|
|
88
|
+
'--output-format', 'stream-json',
|
|
89
|
+
'--dangerously-skip-permissions',
|
|
90
|
+
];
|
|
19
91
|
if (this.model) {
|
|
20
92
|
args.push('--model', this.model);
|
|
21
93
|
}
|
|
@@ -26,25 +98,33 @@ export class ClaudeCodeSpawner {
|
|
|
26
98
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
27
99
|
});
|
|
28
100
|
|
|
29
|
-
let
|
|
101
|
+
let fullOutput = '';
|
|
30
102
|
let stderr = '';
|
|
31
103
|
let buffer = '';
|
|
104
|
+
let resultText = '';
|
|
32
105
|
|
|
33
106
|
child.stdout.on('data', (data) => {
|
|
34
|
-
|
|
35
|
-
stdout += chunk;
|
|
36
|
-
buffer += chunk;
|
|
107
|
+
buffer += data.toString();
|
|
37
108
|
|
|
38
|
-
//
|
|
109
|
+
// Process complete JSON lines
|
|
39
110
|
const lines = buffer.split('\n');
|
|
40
|
-
buffer = lines.pop(); // keep incomplete line
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
111
|
+
buffer = lines.pop(); // keep incomplete line
|
|
112
|
+
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
const trimmed = line.trim();
|
|
115
|
+
if (!trimmed) continue;
|
|
116
|
+
|
|
117
|
+
fullOutput += trimmed + '\n';
|
|
118
|
+
|
|
119
|
+
// Extract final result text
|
|
120
|
+
try {
|
|
121
|
+
const event = JSON.parse(trimmed);
|
|
122
|
+
if (event.type === 'result' && event.result) {
|
|
123
|
+
resultText = event.result;
|
|
124
|
+
}
|
|
125
|
+
} catch {}
|
|
126
|
+
|
|
127
|
+
parseStreamEvent(trimmed, onOutput, logger);
|
|
48
128
|
}
|
|
49
129
|
});
|
|
50
130
|
|
|
@@ -60,16 +140,23 @@ export class ClaudeCodeSpawner {
|
|
|
60
140
|
child.on('close', (code) => {
|
|
61
141
|
clearTimeout(timer);
|
|
62
142
|
|
|
63
|
-
//
|
|
64
|
-
if (buffer.trim()
|
|
65
|
-
|
|
143
|
+
// Process remaining buffer
|
|
144
|
+
if (buffer.trim()) {
|
|
145
|
+
fullOutput += buffer.trim();
|
|
146
|
+
try {
|
|
147
|
+
const event = JSON.parse(buffer.trim());
|
|
148
|
+
if (event.type === 'result' && event.result) {
|
|
149
|
+
resultText = event.result;
|
|
150
|
+
}
|
|
151
|
+
} catch {}
|
|
152
|
+
parseStreamEvent(buffer.trim(), onOutput, logger);
|
|
66
153
|
}
|
|
67
154
|
|
|
68
|
-
if (code !== 0 && !
|
|
155
|
+
if (code !== 0 && !fullOutput) {
|
|
69
156
|
reject(new Error(`Claude Code exited with code ${code}: ${stderr}`));
|
|
70
157
|
} else {
|
|
71
158
|
resolve({
|
|
72
|
-
output:
|
|
159
|
+
output: resultText || fullOutput.trim(),
|
|
73
160
|
stderr: stderr.trim(),
|
|
74
161
|
exitCode: code,
|
|
75
162
|
});
|
package/src/tools/coding.js
CHANGED
|
@@ -41,9 +41,7 @@ export const handlers = {
|
|
|
41
41
|
workingDirectory: params.working_directory,
|
|
42
42
|
prompt: params.prompt,
|
|
43
43
|
maxTurns: params.max_turns,
|
|
44
|
-
onOutput: context.onUpdate
|
|
45
|
-
? (text) => context.onUpdate(`📟 \`Claude Code:\`\n${text}`)
|
|
46
|
-
: null,
|
|
44
|
+
onOutput: context.onUpdate || null,
|
|
47
45
|
});
|
|
48
46
|
return { success: true, output: result.output };
|
|
49
47
|
} catch (err) {
|