karajan-code 1.9.5 → 1.9.6
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/agents/claude-agent.js +43 -23
package/package.json
CHANGED
|
@@ -71,16 +71,34 @@ function createStreamJsonFilter(onOutput) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
* Build
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
74
|
+
* Build clean execa options for Claude subprocess.
|
|
75
|
+
*
|
|
76
|
+
* Three critical fixes for running `claude -p` from Node.js:
|
|
77
|
+
*
|
|
78
|
+
* 1. Strip CLAUDECODE env var — Claude Code 2.x sets this to block nested
|
|
79
|
+
* sessions. The spawned `claude -p` is a separate non-interactive
|
|
80
|
+
* invocation, not a true nested session.
|
|
81
|
+
*
|
|
82
|
+
* 2. Detach stdin (stdin: "ignore") — When launched from Node.js (which is
|
|
83
|
+
* how Claude Code / Karajan MCP runs), the child inherits the parent's
|
|
84
|
+
* stdin. `claude -p` then blocks waiting to read from a stdin that the
|
|
85
|
+
* parent is already consuming. Ignoring stdin prevents the hang.
|
|
86
|
+
*
|
|
87
|
+
* 3. Claude Code 2.x writes all structured output (stream-json, json) to
|
|
88
|
+
* stderr, NOT stdout. The agent must read from stderr for the actual
|
|
89
|
+
* response data.
|
|
80
90
|
*/
|
|
81
|
-
function
|
|
82
|
-
const { CLAUDECODE, ...
|
|
83
|
-
return
|
|
91
|
+
function cleanExecaOpts(extra = {}) {
|
|
92
|
+
const { CLAUDECODE, ...env } = process.env;
|
|
93
|
+
return { env, stdin: "ignore", ...extra };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Pick the best raw output from a claude subprocess result.
|
|
98
|
+
* Claude 2.x sends structured output to stderr; stdout is often empty.
|
|
99
|
+
*/
|
|
100
|
+
function pickOutput(res) {
|
|
101
|
+
return res.stdout || res.stderr || "";
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
export class ClaudeAgent extends BaseAgent {
|
|
@@ -90,36 +108,38 @@ export class ClaudeAgent extends BaseAgent {
|
|
|
90
108
|
const model = this.getRoleModel(role);
|
|
91
109
|
if (model) args.push("--model", model);
|
|
92
110
|
|
|
93
|
-
const env = cleanEnv();
|
|
94
|
-
|
|
95
111
|
// Use stream-json when onOutput is provided to get real-time feedback
|
|
96
112
|
if (task.onOutput) {
|
|
97
113
|
args.push("--output-format", "stream-json");
|
|
98
114
|
const streamFilter = createStreamJsonFilter(task.onOutput);
|
|
99
|
-
const res = await runCommand(resolveBin("claude"), args, {
|
|
100
|
-
env,
|
|
115
|
+
const res = await runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
101
116
|
onOutput: streamFilter,
|
|
102
117
|
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
103
118
|
timeout: task.timeoutMs
|
|
104
|
-
});
|
|
105
|
-
const
|
|
106
|
-
|
|
119
|
+
}));
|
|
120
|
+
const raw = pickOutput(res);
|
|
121
|
+
const output = extractTextFromStreamJson(raw);
|
|
122
|
+
return { ok: res.exitCode === 0, output, error: res.exitCode !== 0 ? raw : "", exitCode: res.exitCode };
|
|
107
123
|
}
|
|
108
124
|
|
|
109
|
-
|
|
110
|
-
|
|
125
|
+
// Without streaming, use json output to get structured response via stderr
|
|
126
|
+
args.push("--output-format", "json");
|
|
127
|
+
const res = await runCommand(resolveBin("claude"), args, cleanExecaOpts());
|
|
128
|
+
const raw = pickOutput(res);
|
|
129
|
+
const output = extractTextFromStreamJson(raw);
|
|
130
|
+
return { ok: res.exitCode === 0, output, error: res.exitCode !== 0 ? raw : "", exitCode: res.exitCode };
|
|
111
131
|
}
|
|
112
132
|
|
|
113
133
|
async reviewTask(task) {
|
|
114
|
-
const args = ["-p", task.prompt, "--output-format", "json"];
|
|
134
|
+
const args = ["-p", task.prompt, "--output-format", "stream-json"];
|
|
115
135
|
const model = this.getRoleModel(task.role || "reviewer");
|
|
116
136
|
if (model) args.push("--model", model);
|
|
117
|
-
const res = await runCommand(resolveBin("claude"), args, {
|
|
118
|
-
env: cleanEnv(),
|
|
137
|
+
const res = await runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
119
138
|
onOutput: task.onOutput,
|
|
120
139
|
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
121
140
|
timeout: task.timeoutMs
|
|
122
|
-
});
|
|
123
|
-
|
|
141
|
+
}));
|
|
142
|
+
const raw = pickOutput(res);
|
|
143
|
+
return { ok: res.exitCode === 0, output: raw, error: res.exitCode !== 0 ? raw : "", exitCode: res.exitCode };
|
|
124
144
|
}
|
|
125
145
|
}
|