clearclaw 0.6.0 → 0.7.0

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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Bridges callback-driven event producers to AsyncIterable consumers.
3
+ *
4
+ * Used by AcpEngine to convert ACP's sessionUpdate callbacks into
5
+ * the AsyncIterable<EngineEvent> that ClearClaw's Engine interface requires.
6
+ */
7
+ export class AsyncQueue {
8
+ buffer = [];
9
+ waiting = [];
10
+ closed = false;
11
+ push(value) {
12
+ if (this.closed)
13
+ return;
14
+ const waiter = this.waiting.shift();
15
+ if (waiter) {
16
+ waiter({ value, done: false });
17
+ }
18
+ else {
19
+ this.buffer.push(value);
20
+ }
21
+ }
22
+ close() {
23
+ this.closed = true;
24
+ for (const waiter of this.waiting) {
25
+ waiter({ value: undefined, done: true });
26
+ }
27
+ this.waiting.length = 0;
28
+ }
29
+ [Symbol.asyncIterator]() {
30
+ return {
31
+ next: () => {
32
+ if (this.buffer.length > 0) {
33
+ return Promise.resolve({ value: this.buffer.shift(), done: false });
34
+ }
35
+ if (this.closed) {
36
+ return Promise.resolve({ value: undefined, done: true });
37
+ }
38
+ return new Promise((resolve) => {
39
+ this.waiting.push(resolve);
40
+ });
41
+ },
42
+ };
43
+ }
44
+ }
45
+ //# sourceMappingURL=async-queue.js.map
@@ -1,6 +1,8 @@
1
1
  import type { Engine, EngineEvent, RunTurnOpts, SessionInfo } from "../types.js";
2
2
  export declare class ClaudeCodeEngine implements Engine {
3
+ private readonly executablePath?;
3
4
  name: string;
5
+ constructor(executablePath?: string | undefined);
4
6
  listSessions(cwd: string): Promise<SessionInfo[]>;
5
7
  runTurn(opts: RunTurnOpts): AsyncIterable<EngineEvent>;
6
8
  }
@@ -1,8 +1,58 @@
1
1
  import { query, listSessions, } from "@anthropic-ai/claude-agent-sdk";
2
2
  import log from "../logger.js";
3
- import { formatToolDescription } from "../format.js";
3
+ /** Map Claude Code SDK tool call to ToolCall. */
4
+ function toToolCall(toolName, input, toolUseId) {
5
+ switch (toolName) {
6
+ case "Edit":
7
+ return {
8
+ action: "edit", toolName, toolUseId,
9
+ path: String(input.file_path ?? ""),
10
+ before: String(input.old_string ?? ""),
11
+ after: String(input.new_string ?? ""),
12
+ };
13
+ case "Write":
14
+ return {
15
+ action: "write", toolName, toolUseId,
16
+ path: String(input.file_path ?? ""),
17
+ content: String(input.content ?? ""),
18
+ };
19
+ case "Bash":
20
+ return {
21
+ action: "execute", toolName, toolUseId,
22
+ command: String(input.command ?? ""),
23
+ };
24
+ case "Read":
25
+ case "Glob":
26
+ return {
27
+ action: "read", toolName, toolUseId,
28
+ paths: [String(input.file_path ?? input.pattern ?? "")],
29
+ };
30
+ case "Grep":
31
+ return {
32
+ action: "search", toolName, toolUseId,
33
+ pattern: String(input.pattern ?? ""),
34
+ paths: input.path ? [String(input.path)] : undefined,
35
+ };
36
+ case "WebSearch":
37
+ return {
38
+ action: "search", toolName, toolUseId,
39
+ pattern: String(input.query ?? ""),
40
+ };
41
+ case "WebFetch":
42
+ return {
43
+ action: "fetch", toolName, toolUseId,
44
+ url: String(input.url ?? ""),
45
+ };
46
+ default:
47
+ return { action: "other", toolName, toolUseId, ...input };
48
+ }
49
+ }
4
50
  export class ClaudeCodeEngine {
51
+ executablePath;
5
52
  name = "claude-code";
53
+ constructor(executablePath) {
54
+ this.executablePath = executablePath;
55
+ }
6
56
  async listSessions(cwd) {
7
57
  const sessions = await listSessions({ dir: cwd, limit: 10 });
8
58
  return sessions
@@ -27,14 +77,7 @@ export class ClaudeCodeEngine {
27
77
  ? { resume: sessionId }
28
78
  : {};
29
79
  const canUseTool = async (toolName, input, options) => {
30
- const description = formatToolDescription(toolName, input);
31
- const resp = await onPermissionRequest({
32
- toolName,
33
- input,
34
- description,
35
- reason: options.decisionReason,
36
- toolUseId: options.toolUseID,
37
- });
80
+ const resp = await onPermissionRequest(toToolCall(toolName, input, options.toolUseID));
38
81
  if (resp.decision === "allow") {
39
82
  return { behavior: "allow", updatedInput: resp.updatedInput ?? input };
40
83
  }
@@ -63,6 +106,7 @@ export class ClaudeCodeEngine {
63
106
  ...sessionOpts,
64
107
  cwd,
65
108
  permissionMode,
109
+ ...(this.executablePath ? { pathToClaudeCodeExecutable: this.executablePath } : {}),
66
110
  allowDangerouslySkipPermissions: permissionMode === "bypassPermissions" ? true : undefined,
67
111
  canUseTool: permissionMode === "bypassPermissions" ? undefined : canUseTool,
68
112
  abortController,
@@ -110,9 +154,7 @@ export class ClaudeCodeEngine {
110
154
  toolUseIdToName.set(toolUseId, block.name);
111
155
  yield {
112
156
  type: "tool_use",
113
- toolName: block.name,
114
- input: block.input,
115
- toolUseId,
157
+ tool: toToolCall(block.name, block.input, toolUseId),
116
158
  };
117
159
  }
118
160
  }
@@ -0,0 +1,16 @@
1
+ import type { Engine } from "../types.js";
2
+ export interface SpawnConfig {
3
+ command: string;
4
+ args: string[];
5
+ }
6
+ /** All known engine names (for validation / setup prompts). */
7
+ export declare const ENGINE_NAMES: readonly ["claude-code", ...string[]];
8
+ /** Return the CLI command an engine needs on PATH. */
9
+ export declare function engineCommand(name: string): string | undefined;
10
+ /**
11
+ * Build the engine map: claude-code (Agent SDK) + known ACP agents.
12
+ * Engines are lightweight — they store config, not running processes.
13
+ *
14
+ * @param enginePaths - resolved executable paths from config (e.g. { "claude-code": "/usr/local/bin/claude" })
15
+ */
16
+ export declare function createEngineMap(enginePaths?: Record<string, string>): Map<string, Engine>;
@@ -0,0 +1,32 @@
1
+ import { ClaudeCodeEngine } from "./claude-code.js";
2
+ import { AcpEngine } from "./acp.js";
3
+ /** Known ACP agent spawn configurations. */
4
+ const KNOWN_ACP_AGENTS = {
5
+ kiro: { command: "kiro-cli", args: ["acp"] },
6
+ };
7
+ /** All known engine names (for validation / setup prompts). */
8
+ export const ENGINE_NAMES = ["claude-code", ...Object.keys(KNOWN_ACP_AGENTS)];
9
+ /** CLI binary each engine needs on PATH. */
10
+ const ENGINE_COMMANDS = {
11
+ "claude-code": "claude",
12
+ ...Object.fromEntries(Object.entries(KNOWN_ACP_AGENTS).map(([name, cfg]) => [name, cfg.command])),
13
+ };
14
+ /** Return the CLI command an engine needs on PATH. */
15
+ export function engineCommand(name) {
16
+ return ENGINE_COMMANDS[name];
17
+ }
18
+ /**
19
+ * Build the engine map: claude-code (Agent SDK) + known ACP agents.
20
+ * Engines are lightweight — they store config, not running processes.
21
+ *
22
+ * @param enginePaths - resolved executable paths from config (e.g. { "claude-code": "/usr/local/bin/claude" })
23
+ */
24
+ export function createEngineMap(enginePaths = {}) {
25
+ const engines = new Map();
26
+ engines.set("claude-code", new ClaudeCodeEngine(enginePaths["claude-code"]));
27
+ for (const [name, config] of Object.entries(KNOWN_ACP_AGENTS)) {
28
+ engines.set(name, new AcpEngine(name, config));
29
+ }
30
+ return engines;
31
+ }
32
+ //# sourceMappingURL=registry.js.map
package/dist/format.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Uses the `diff` library for unified diffs in Edit permission prompts.
5
5
  */
6
+ import { type ToolCall } from "./types.js";
6
7
  /**
7
8
  * Summary line for the rolling tool message after a turn completes.
8
9
  * e.g. "🔧 3× Read, 2× Grep, 1× Bash"
@@ -12,12 +13,13 @@ export declare function formatToolCallSummary(toolCalls: Record<string, number>)
12
13
  * Short one-liner for the rolling tool message. Includes key detail
13
14
  * but truncated to stay compact.
14
15
  */
15
- export declare function formatToolStatusLine(toolName: string, input: Record<string, unknown>): string;
16
+ export declare function formatToolStatusLine(tool: ToolCall): string;
16
17
  /**
17
- * Format a permission prompt for display. Returns a MarkdownV2 string.
18
- * Edit/Write include the diff/preview; other tools get a one-line description.
18
+ * Format a permission prompt for display.
19
+ * Switches on action type: edit shows diff, write shows preview,
20
+ * execute shows command, others show available detail.
19
21
  */
20
- export declare function formatPermissionPrompt(toolName: string, input: Record<string, unknown>, description: string): string;
22
+ export declare function formatPermissionPrompt(tool: ToolCall): string;
21
23
  /**
22
24
  * Relative time string: "just now", "5m ago", "2h ago", "3d ago".
23
25
  */
@@ -42,13 +44,9 @@ export declare function truncateButtonLabel(index: number, label: string): strin
42
44
  * Format an ExitPlanMode permission prompt.
43
45
  * Renders plan text from the description or input fields.
44
46
  */
45
- export declare function formatExitPlanMode(input: Record<string, unknown>, description: string): string;
47
+ export declare function formatExitPlanMode(input: Record<string, unknown>): string;
46
48
  /**
47
49
  * Format a TodoWrite task list for display.
48
50
  */
49
51
  export declare function formatTodoList(input: Record<string, unknown>): string;
50
- /**
51
- * Human-readable one-liner for a tool invocation (used in engine's canUseTool).
52
- */
53
- export declare function formatToolDescription(toolName: string, input: Record<string, unknown>): string;
54
52
  export {};
package/dist/format.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Uses the `diff` library for unified diffs in Edit permission prompts.
5
5
  */
6
6
  import { createTwoFilesPatch } from "diff";
7
+ import { isKnownToolCall } from "./types.js";
7
8
  const MAX_LINES = 200;
8
9
  const MAX_STATUS_LEN = 60;
9
10
  /**
@@ -24,83 +25,86 @@ export function formatToolCallSummary(toolCalls) {
24
25
  * Short one-liner for the rolling tool message. Includes key detail
25
26
  * but truncated to stay compact.
26
27
  */
27
- export function formatToolStatusLine(toolName, input) {
28
- const full = `🔧 ${formatToolStatus(toolName, input)}`;
28
+ export function formatToolStatusLine(tool) {
29
+ const full = `🔧 ${formatToolStatus(tool)}`;
29
30
  if (full.length <= MAX_STATUS_LEN)
30
31
  return full;
31
32
  return `${full.slice(0, MAX_STATUS_LEN - 1)}…`;
32
33
  }
33
34
  /**
34
- * Format a permission prompt for display. Returns a MarkdownV2 string.
35
- * Edit/Write include the diff/preview; other tools get a one-line description.
35
+ * Format a permission prompt for display.
36
+ * Switches on action type: edit shows diff, write shows preview,
37
+ * execute shows command, others show available detail.
36
38
  */
37
- export function formatPermissionPrompt(toolName, input, description) {
38
- if (toolName === "Edit") {
39
- const edit = input;
40
- const patch = createTwoFilesPatch(`a/${edit.file_path}`, `b/${edit.file_path}`, edit.old_string, edit.new_string, "", "", { context: 3 });
41
- const lines = patch.split("\n");
42
- const start = lines.findIndex((l) => l.startsWith("---"));
43
- const diffLines = start >= 0 ? lines.slice(start) : lines;
44
- const body = truncateLines(diffLines, MAX_LINES);
45
- return `🔐 Allow Edit? ${edit.file_path}\n\`\`\`diff\n${body}\n\`\`\``;
46
- }
47
- if (toolName === "Write") {
48
- const write = input;
49
- const lines = write.content.split("\n");
50
- const body = truncateLines(lines, MAX_LINES);
51
- return `🔐 Allow Write? ${write.file_path} (${lines.length} lines)\n\`\`\`\n${body}\n\`\`\``;
52
- }
53
- // Non-Edit/Write tools: emoji header + detail in code block
54
- const detail = formatToolDetail(toolName, input);
55
- const body = truncateLines(detail.split("\n"), MAX_LINES);
56
- return `🔐 Allow ${toolName}?\n\`\`\`\n${body}\n\`\`\``;
39
+ export function formatPermissionPrompt(tool) {
40
+ const detail = formatToolDetail(tool);
41
+ if (!detail)
42
+ return `🔐 Allow ${tool.toolName}?`;
43
+ return `🔐 Allow ${tool.toolName}?\n${detail}`;
57
44
  }
58
- function formatToolStatus(toolName, input) {
59
- switch (toolName) {
60
- case "Bash":
61
- return `Bash: ${input.command ?? "(unknown)"}`;
62
- case "Read":
63
- return `Read: ${input.file_path ?? "(unknown)"}`;
64
- case "Edit":
65
- return `Edit: ${input.file_path ?? "(unknown)"}`;
66
- case "Write":
67
- return `Write: ${input.file_path ?? "(unknown)"}`;
68
- case "Glob":
69
- return `Glob: ${input.pattern ?? "(unknown)"}`;
70
- case "Grep":
71
- return `Grep: ${input.pattern ?? "(unknown)"}`;
72
- case "WebFetch":
73
- return `WebFetch: ${input.url ?? "(unknown)"}`;
74
- case "WebSearch":
75
- return `WebSearch: ${input.query ?? "(unknown)"}`;
76
- case "Agent":
77
- return `Agent: ${input.description ?? "(unknown)"}`;
78
- default:
79
- return toolName;
45
+ /** Multi-line detail for permission prompts (diff, content preview, command, etc.). */
46
+ function formatToolDetail(tool) {
47
+ if (!isKnownToolCall(tool))
48
+ return formatUnknownDetail(tool);
49
+ switch (tool.action) {
50
+ case "edit": {
51
+ if (!tool.before && !tool.after)
52
+ return tool.path;
53
+ const patch = createTwoFilesPatch(`a/${tool.path}`, `b/${tool.path}`, tool.before, tool.after, "", "", { context: 3 });
54
+ const lines = patch.split("\n");
55
+ const start = lines.findIndex((l) => l.startsWith("---"));
56
+ const diffLines = start >= 0 ? lines.slice(start) : lines;
57
+ return `${tool.path}\n\`\`\`diff\n${truncateLines(diffLines, MAX_LINES)}\n\`\`\``;
58
+ }
59
+ case "write": {
60
+ const lines = tool.content.split("\n");
61
+ return `${tool.path} (${lines.length} lines)\n\`\`\`\n${truncateLines(lines, MAX_LINES)}\n\`\`\``;
62
+ }
63
+ case "execute":
64
+ return `\`\`\`\n${truncateLines(tool.command.split("\n"), MAX_LINES)}\n\`\`\``;
65
+ case "read":
66
+ return `\`\`\`\n${tool.paths.join("\n")}\n\`\`\``;
67
+ case "search":
68
+ return `\`\`\`\n${tool.pattern}\n\`\`\``;
69
+ case "fetch":
70
+ return `\`\`\`\n${tool.url}\n\`\`\``;
80
71
  }
81
72
  }
82
- /** Extract the key detail for a tool's permission prompt (command, query, URL, etc.). */
83
- function formatToolDetail(toolName, input) {
84
- switch (toolName) {
85
- case "Bash":
86
- return String(input.command ?? "");
87
- case "Read":
88
- case "Edit":
89
- case "Write":
90
- return String(input.file_path ?? "");
91
- case "Glob":
92
- case "Grep":
93
- return String(input.pattern ?? "");
94
- case "WebSearch":
95
- return String(input.query ?? "");
96
- case "WebFetch":
97
- return String(input.url ?? "");
98
- case "Agent":
99
- return String(input.description ?? "");
100
- default:
101
- return JSON.stringify(input, null, 2);
73
+ /** One-liner summary for tool status display. */
74
+ function formatToolStatus(tool) {
75
+ if (!isKnownToolCall(tool)) {
76
+ const summary = formatUnknownSummary(tool);
77
+ return summary ? `${tool.toolName}: ${summary}` : tool.toolName;
78
+ }
79
+ switch (tool.action) {
80
+ case "edit":
81
+ case "write":
82
+ return `${tool.toolName}: ${tool.path}`;
83
+ case "execute":
84
+ return `${tool.toolName}: ${tool.command}`;
85
+ case "read":
86
+ return `${tool.toolName}: ${tool.paths.join(", ")}`;
87
+ case "search":
88
+ return `${tool.toolName}: ${tool.pattern}`;
89
+ case "fetch":
90
+ return `${tool.toolName}: ${tool.url}`;
102
91
  }
103
92
  }
93
+ /** Extract displayable info from an unknown tool call's generic fields. */
94
+ function formatUnknownSummary(tool) {
95
+ const parts = [];
96
+ if ("paths" in tool && Array.isArray(tool.paths))
97
+ parts.push(...tool.paths);
98
+ if ("detail" in tool && tool.detail)
99
+ parts.push(String(tool.detail));
100
+ return parts.length > 0 ? parts.join(", ") : null;
101
+ }
102
+ function formatUnknownDetail(tool) {
103
+ const summary = formatUnknownSummary(tool);
104
+ if (!summary)
105
+ return null;
106
+ return `\`\`\`\n${truncateLines(summary.split("\n"), MAX_LINES)}\n\`\`\``;
107
+ }
104
108
  /**
105
109
  * Relative time string: "just now", "5m ago", "2h ago", "3d ago".
106
110
  */
@@ -145,7 +149,7 @@ export function truncateButtonLabel(index, label) {
145
149
  * Format an ExitPlanMode permission prompt.
146
150
  * Renders plan text from the description or input fields.
147
151
  */
148
- export function formatExitPlanMode(input, description) {
152
+ export function formatExitPlanMode(input) {
149
153
  const plan = (typeof input.plan === "string" ? input.plan : null)
150
154
  || JSON.stringify(input, null, 2);
151
155
  const lines = plan.split("\n");
@@ -176,19 +180,4 @@ function truncateLines(lines, max) {
176
180
  const remaining = lines.length - max;
177
181
  return `${shown}\n... (${remaining} more lines)`;
178
182
  }
179
- /**
180
- * Human-readable one-liner for a tool invocation (used in engine's canUseTool).
181
- */
182
- export function formatToolDescription(toolName, input) {
183
- switch (toolName) {
184
- case "Bash":
185
- return `Bash: ${input.command ?? "(unknown command)"}`;
186
- case "Edit":
187
- case "Write":
188
- case "Read":
189
- return `${toolName}: ${input.file_path ?? "(unknown file)"}`;
190
- default:
191
- return `Allow ${toolName}?`;
192
- }
193
- }
194
183
  //# sourceMappingURL=format.js.map
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
2
3
  import readline from "node:readline";
3
4
  import { Config } from "./config.js";
4
5
  import log, { initLogger } from "./logger.js";
5
- import { ClaudeCodeEngine } from "./engine/claude-code.js";
6
+ import { createEngineMap, ENGINE_NAMES, engineCommand } from "./engine/registry.js";
6
7
  import { TelegramChannel } from "./channel/telegram.js";
7
8
  import { SlackChannel } from "./channel/slack.js";
8
9
  import { Orchestrator } from "./orchestrator.js";
9
10
  async function runDaemon() {
10
11
  const config = new Config().resolve();
11
- config.syncSkills();
12
12
  initLogger(config.logPath);
13
13
  // The Claude Agent SDK has a bug: handleControlRequest() writes to the
14
14
  // subprocess stdin without a .catch(), so aborting during a permission
@@ -42,7 +42,8 @@ async function runDaemon() {
42
42
  });
43
43
  };
44
44
  const channel = createChannel(config.channel, (userId) => config.isAuthorized(userId), onUnauthorizedDM);
45
- await new Orchestrator({ channel, engine: new ClaudeCodeEngine(), config }).start();
45
+ const enginePaths = Object.fromEntries(Object.entries(config.engines).map(([name, entry]) => [name, entry.path]));
46
+ await new Orchestrator({ channel, engines: createEngineMap(enginePaths), config }).start();
46
47
  }
47
48
  async function runApprove(args) {
48
49
  const code = args[0];
@@ -56,7 +57,7 @@ async function runApprove(args) {
56
57
  console.error("Invalid or expired pairing code.");
57
58
  process.exit(1);
58
59
  }
59
- config.approveUser(pairing.userId, pairing.userName, pairing.chatId);
60
+ config.approveUser(pairing.userId, pairing.userName);
60
61
  console.log(`Approved ${pairing.userName} (${pairing.userId})`);
61
62
  }
62
63
  const SETUP_TIMEOUT_MS = 5 * 60 * 1000;
@@ -87,8 +88,36 @@ async function runSetup() {
87
88
  throw new Error("Bot token required.");
88
89
  channelConfig = { type: "telegram", botToken };
89
90
  }
91
+ let engineName;
92
+ const engineList = ENGINE_NAMES.join("/");
93
+ while (true) {
94
+ engineName = (await ask(`Default engine [${engineList}]: `)).trim().toLowerCase();
95
+ if (!ENGINE_NAMES.includes(engineName)) {
96
+ console.log(` Please enter one of: ${engineList}`);
97
+ continue;
98
+ }
99
+ const cmd = engineCommand(engineName);
100
+ if (cmd) {
101
+ try {
102
+ execFileSync("which", [cmd], { stdio: "ignore" });
103
+ }
104
+ catch {
105
+ console.log(` "${cmd}" not found on PATH. Install it first or choose another engine.`);
106
+ continue;
107
+ }
108
+ }
109
+ break;
110
+ }
111
+ // Resolve executable path for the chosen engine
112
+ const cmd = engineCommand(engineName);
113
+ const resolvedPath = cmd
114
+ ? execFileSync("which", [cmd], { encoding: "utf-8" }).trim()
115
+ : engineName; // SDK-based engines don't have a standalone binary
90
116
  const config = new Config();
91
117
  config.setChannel(channelConfig);
118
+ config.setEngines([
119
+ { name: engineName, default: true, path: resolvedPath },
120
+ ]);
92
121
  console.log("Saved to ~/.clearclaw/config.json\n");
93
122
  config.resolve();
94
123
  let resolveFirstDM;
@@ -118,7 +147,7 @@ async function runSetup() {
118
147
  await channel.sendMessage(chatId, "Pairing code was invalid. Please try again.");
119
148
  return;
120
149
  }
121
- config.approveUser(pairing.userId, pairing.userName, pairing.chatId);
150
+ config.approveUser(pairing.userId, pairing.userName);
122
151
  console.log(` Approved ${pairing.userName} (${pairing.userId})`);
123
152
  await channel.sendMessage(chatId, "You're approved! The bot is being set up — you'll be able to chat soon.");
124
153
  console.log("\nSetup complete! Start the daemon:\n clearclaw daemon\n");
@@ -2,17 +2,18 @@ import type { Config } from "./config.js";
2
2
  import type { Channel, Engine } from "./types.js";
3
3
  export interface OrchestratorOpts {
4
4
  channel: Channel;
5
- engine: Engine;
5
+ engines: Map<string, Engine>;
6
6
  config: Config;
7
7
  }
8
8
  export declare class Orchestrator {
9
9
  private channel;
10
- private engine;
10
+ private engines;
11
11
  private config;
12
12
  private chats;
13
13
  private tasks;
14
14
  constructor(opts: OrchestratorOpts);
15
15
  private chat;
16
+ private engineFor;
16
17
  start(): Promise<void>;
17
18
  stop(): Promise<void>;
18
19
  /** Effective behavior: tasks→assistant, workspace→explicit setting or home→assistant / project→relay. */
@@ -27,6 +28,4 @@ export declare class Orchestrator {
27
28
  private buildMcpTools;
28
29
  private handlePermission;
29
30
  private updateStatusMessage;
30
- /** Read default workspace CLAUDE.md to append to non-default workspace sessions. */
31
- private readDefaultPrompt;
32
31
  }