karajan-code 4.1.5 → 4.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "4.1.5",
3
+ "version": "4.1.6",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -0,0 +1,65 @@
1
+ import { BaseAgent } from "./base-agent.js";
2
+ import { resolveBin } from "./resolve-bin.js";
3
+
4
+ /**
5
+ * GitHub Copilot CLI (@github/copilot, binary `copilot`) — KJC-TSK-0666.
6
+ * The seventh built-in agent, and the user-chosen third AI for solomon
7
+ * arbitration (gemini tier retired, Qwen OAuth free tier discontinued).
8
+ *
9
+ * Verified live against copilot 1.0.73:
10
+ * - `-p <prompt>` runs non-interactive; copilot has NO stdin prompt channel
11
+ * (stdin is treated as context, spawning an agentic run) — so the prompt
12
+ * travels as an argument. Same documented E2BIG limit as claude-agent
13
+ * (KJC-BUG-0121) for multi-hundred-KB diffs.
14
+ * - `--output-format json` emits JSONL; the final text lives in the
15
+ * `assistant.message` event's `data.content`, run metadata in `result`.
16
+ * - Default model routing is automatic and varies per run (haiku, gpt-5-mini
17
+ * observed) — pin it per role via `roles.<role>.model` when neutrality
18
+ * matters (e.g. a gemini-family model for solomon).
19
+ */
20
+ export class CopilotAgent extends BaseAgent {
21
+ async runTask(task) {
22
+ // Coder mode IS an agentic run: file edits and shell need permissions.
23
+ return this._exec(task, this.getRoleModel(task.role || "coder"), ["--allow-all-tools"]);
24
+ }
25
+
26
+ async reviewTask(task) {
27
+ // Review/arbitration answers from the prompt alone — no tool grants, and
28
+ // an explicit no-tools instruction so copilot never stalls waiting for
29
+ // an approval that headless mode cannot give.
30
+ return this._exec(
31
+ { ...task, prompt: `Do NOT use any tools. Answer directly from this prompt only.\n\n${task.prompt}` },
32
+ this.getRoleModel(task.role || "reviewer"),
33
+ [],
34
+ );
35
+ }
36
+
37
+ async _exec(task, model, extraArgs) {
38
+ const args = ["-p", task.prompt, "--output-format", "json", "--no-color", ...extraArgs];
39
+ if (model) args.push("--model", model);
40
+ const res = await this.runCommand(resolveBin("copilot"), args, {
41
+ onOutput: task.onOutput,
42
+ silenceTimeoutMs: task.silenceTimeoutMs,
43
+ timeout: task.timeoutMs
44
+ });
45
+ const output = extractCopilotOutput(res.stdout);
46
+ return { ok: res.exitCode === 0, output: output ?? res.stdout, error: res.stderr, exitCode: res.exitCode };
47
+ }
48
+ }
49
+
50
+ /** Last `assistant.message` content from copilot's JSONL stream (null if none). */
51
+ export function extractCopilotOutput(stdout) {
52
+ if (!stdout) return null;
53
+ let content = null;
54
+ for (const line of stdout.split("\n")) {
55
+ const t = line.trim();
56
+ if (!t.startsWith("{")) continue;
57
+ try {
58
+ const obj = JSON.parse(t);
59
+ if (obj?.type === "assistant.message" && typeof obj?.data?.content === "string") {
60
+ content = obj.data.content;
61
+ }
62
+ } catch { /* non-JSON noise between events */ }
63
+ }
64
+ return content;
65
+ }
@@ -4,6 +4,7 @@ import { GeminiAgent } from "./gemini-agent.js";
4
4
  import { AiderAgent } from "./aider-agent.js";
5
5
  import { OpenCodeAgent } from "./opencode-agent.js";
6
6
  import { QwenAgent } from "./qwen-agent.js";
7
+ import { CopilotAgent } from "./copilot-agent.js";
7
8
 
8
9
  const agentRegistry = new Map();
9
10
 
@@ -51,3 +52,4 @@ registerAgent("gemini", GeminiAgent, { bin: "gemini", installUrl: "https://githu
51
52
  registerAgent("aider", AiderAgent, { bin: "aider", installUrl: "https://aider.chat/docs/install.html" });
52
53
  registerAgent("opencode", OpenCodeAgent, { bin: "opencode", installUrl: "https://opencode.ai" });
53
54
  registerAgent("qwen", QwenAgent, { bin: "qwen", installUrl: "https://github.com/QwenLM/qwen-code" });
55
+ registerAgent("copilot", CopilotAgent, { bin: "copilot", installUrl: "https://docs.github.com/copilot/how-tos/use-copilot-agents/use-copilot-cli" });
@@ -8,7 +8,8 @@ const KNOWN_AGENTS = [
8
8
  { name: "gemini", install: getInstallCommand("gemini") },
9
9
  { name: "aider", install: getInstallCommand("aider") },
10
10
  { name: "opencode", install: getInstallCommand("opencode") },
11
- { name: "qwen", install: getInstallCommand("qwen") }
11
+ { name: "qwen", install: getInstallCommand("qwen") },
12
+ { name: "copilot", install: getInstallCommand("copilot") }
12
13
  ];
13
14
 
14
15
  export async function checkBinary(name, versionArg = "--version") {
@@ -56,6 +56,11 @@ const INSTALL_COMMANDS = {
56
56
  linux: "npm install -g @qwen-code/qwen-code",
57
57
  windows: "npm install -g @qwen-code/qwen-code"
58
58
  },
59
+ copilot: {
60
+ macos: "npm install -g @github/copilot",
61
+ linux: "npm install -g @github/copilot",
62
+ windows: "npm install -g @github/copilot"
63
+ },
59
64
  docker: {
60
65
  macos: "brew install --cask docker",
61
66
  linux: "sudo apt install docker.io docker-compose-v2 (or see https://docs.docker.com/engine/install/)",