kernelbot 1.0.14 → 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 +110 -19
- package/src/prompts/system.js +4 -1
- package/src/tools/coding.js +1 -3
- package/src/utils/config.js +1 -0
package/package.json
CHANGED
package/src/coder.js
CHANGED
|
@@ -1,20 +1,96 @@
|
|
|
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;
|
|
7
71
|
this.timeout = (config.claude_code?.timeout_seconds || 600) * 1000;
|
|
72
|
+
this.model = config.claude_code?.model || null;
|
|
8
73
|
}
|
|
9
74
|
|
|
10
75
|
async run({ workingDirectory, prompt, maxTurns, onOutput }) {
|
|
11
76
|
const logger = getLogger();
|
|
12
77
|
const turns = maxTurns || this.maxTurns;
|
|
13
78
|
|
|
79
|
+
// Auto-setup Claude Code if not configured
|
|
80
|
+
ensureClaudeCodeSetup();
|
|
81
|
+
|
|
14
82
|
logger.info(`Spawning Claude Code in ${workingDirectory}`);
|
|
15
83
|
|
|
16
84
|
return new Promise((resolve, reject) => {
|
|
17
|
-
const args = [
|
|
85
|
+
const args = [
|
|
86
|
+
'-p', prompt,
|
|
87
|
+
'--max-turns', String(turns),
|
|
88
|
+
'--output-format', 'stream-json',
|
|
89
|
+
'--dangerously-skip-permissions',
|
|
90
|
+
];
|
|
91
|
+
if (this.model) {
|
|
92
|
+
args.push('--model', this.model);
|
|
93
|
+
}
|
|
18
94
|
|
|
19
95
|
const child = spawn('claude', args, {
|
|
20
96
|
cwd: workingDirectory,
|
|
@@ -22,25 +98,33 @@ export class ClaudeCodeSpawner {
|
|
|
22
98
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
23
99
|
});
|
|
24
100
|
|
|
25
|
-
let
|
|
101
|
+
let fullOutput = '';
|
|
26
102
|
let stderr = '';
|
|
27
103
|
let buffer = '';
|
|
104
|
+
let resultText = '';
|
|
28
105
|
|
|
29
106
|
child.stdout.on('data', (data) => {
|
|
30
|
-
|
|
31
|
-
stdout += chunk;
|
|
32
|
-
buffer += chunk;
|
|
107
|
+
buffer += data.toString();
|
|
33
108
|
|
|
34
|
-
//
|
|
109
|
+
// Process complete JSON lines
|
|
35
110
|
const lines = buffer.split('\n');
|
|
36
|
-
buffer = lines.pop(); // keep incomplete line
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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);
|
|
44
128
|
}
|
|
45
129
|
});
|
|
46
130
|
|
|
@@ -56,16 +140,23 @@ export class ClaudeCodeSpawner {
|
|
|
56
140
|
child.on('close', (code) => {
|
|
57
141
|
clearTimeout(timer);
|
|
58
142
|
|
|
59
|
-
//
|
|
60
|
-
if (buffer.trim()
|
|
61
|
-
|
|
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);
|
|
62
153
|
}
|
|
63
154
|
|
|
64
|
-
if (code !== 0 && !
|
|
155
|
+
if (code !== 0 && !fullOutput) {
|
|
65
156
|
reject(new Error(`Claude Code exited with code ${code}: ${stderr}`));
|
|
66
157
|
} else {
|
|
67
158
|
resolve({
|
|
68
|
-
output:
|
|
159
|
+
output: resultText || fullOutput.trim(),
|
|
69
160
|
stderr: stderr.trim(),
|
|
70
161
|
exitCode: code,
|
|
71
162
|
});
|
package/src/prompts/system.js
CHANGED
|
@@ -10,12 +10,15 @@ You have full access to the operating system through your tools:
|
|
|
10
10
|
${toolList}
|
|
11
11
|
|
|
12
12
|
## Coding Tasks (writing code, fixing bugs, reviewing code, scaffolding projects)
|
|
13
|
+
IMPORTANT: You MUST NOT write code yourself using read_file/write_file. ALWAYS delegate coding to Claude Code.
|
|
13
14
|
1. Use git tools to clone the repo and create a branch
|
|
14
|
-
2. Use spawn_claude_code to do the actual coding work inside the repo
|
|
15
|
+
2. Use spawn_claude_code to do the actual coding work inside the repo — give it a clear, detailed prompt describing exactly what to build or fix
|
|
15
16
|
3. After Claude Code finishes, use git tools to commit and push
|
|
16
17
|
4. Use GitHub tools to create the PR
|
|
17
18
|
5. Report back with the PR link
|
|
18
19
|
|
|
20
|
+
You are the orchestrator. Claude Code is the coder. Never use read_file + write_file to modify source code — that's Claude Code's job. You handle git, GitHub, and infrastructure. Claude Code handles all code changes.
|
|
21
|
+
|
|
19
22
|
## Non-Coding Tasks (monitoring, deploying, restarting services, checking status)
|
|
20
23
|
- Use OS, Docker, process, network, and monitoring tools directly
|
|
21
24
|
- No need to spawn Claude Code for these
|
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) {
|