karajan-code 1.9.4 → 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 +47 -10
package/package.json
CHANGED
|
@@ -70,6 +70,37 @@ function createStreamJsonFilter(onOutput) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
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.
|
|
90
|
+
*/
|
|
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 || "";
|
|
102
|
+
}
|
|
103
|
+
|
|
73
104
|
export class ClaudeAgent extends BaseAgent {
|
|
74
105
|
async runTask(task) {
|
|
75
106
|
const role = task.role || "coder";
|
|
@@ -81,28 +112,34 @@ export class ClaudeAgent extends BaseAgent {
|
|
|
81
112
|
if (task.onOutput) {
|
|
82
113
|
args.push("--output-format", "stream-json");
|
|
83
114
|
const streamFilter = createStreamJsonFilter(task.onOutput);
|
|
84
|
-
const res = await runCommand(resolveBin("claude"), args, {
|
|
115
|
+
const res = await runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
85
116
|
onOutput: streamFilter,
|
|
86
117
|
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
87
118
|
timeout: task.timeoutMs
|
|
88
|
-
});
|
|
89
|
-
const
|
|
90
|
-
|
|
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 };
|
|
91
123
|
}
|
|
92
124
|
|
|
93
|
-
|
|
94
|
-
|
|
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 };
|
|
95
131
|
}
|
|
96
132
|
|
|
97
133
|
async reviewTask(task) {
|
|
98
|
-
const args = ["-p", task.prompt, "--output-format", "json"];
|
|
134
|
+
const args = ["-p", task.prompt, "--output-format", "stream-json"];
|
|
99
135
|
const model = this.getRoleModel(task.role || "reviewer");
|
|
100
136
|
if (model) args.push("--model", model);
|
|
101
|
-
const res = await runCommand(resolveBin("claude"), args, {
|
|
137
|
+
const res = await runCommand(resolveBin("claude"), args, cleanExecaOpts({
|
|
102
138
|
onOutput: task.onOutput,
|
|
103
139
|
silenceTimeoutMs: task.silenceTimeoutMs,
|
|
104
140
|
timeout: task.timeoutMs
|
|
105
|
-
});
|
|
106
|
-
|
|
141
|
+
}));
|
|
142
|
+
const raw = pickOutput(res);
|
|
143
|
+
return { ok: res.exitCode === 0, output: raw, error: res.exitCode !== 0 ? raw : "", exitCode: res.exitCode };
|
|
107
144
|
}
|
|
108
145
|
}
|