clearclaw 0.6.1 → 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.
- package/dist/channel/slack.d.ts +3 -3
- package/dist/channel/slack.js +45 -19
- package/dist/channel/telegram.d.ts +3 -3
- package/dist/channel/telegram.js +30 -12
- package/dist/config.d.ts +13 -5
- package/dist/config.js +41 -33
- package/dist/engine/acp.d.ts +9 -0
- package/dist/engine/acp.js +247 -0
- package/dist/engine/async-queue.d.ts +14 -0
- package/dist/engine/async-queue.js +45 -0
- package/dist/engine/claude-code.d.ts +2 -0
- package/dist/engine/claude-code.js +54 -12
- package/dist/engine/registry.d.ts +16 -0
- package/dist/engine/registry.js +32 -0
- package/dist/format.d.ts +7 -9
- package/dist/format.js +71 -82
- package/dist/index.js +34 -5
- package/dist/orchestrator.d.ts +3 -4
- package/dist/orchestrator.js +99 -41
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +35 -0
- package/dist/tool-handlers.d.ts +1 -1
- package/dist/tool-handlers.js +2 -2
- package/dist/types.d.ts +51 -12
- package/dist/types.js +12 -1
- package/package.json +4 -2
- package/prompts/ONBOARDING.md +56 -0
- package/prompts/SYSTEM.md +164 -0
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
16
|
+
export declare function formatToolStatusLine(tool: ToolCall): string;
|
|
16
17
|
/**
|
|
17
|
-
* Format a permission prompt for display.
|
|
18
|
-
*
|
|
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(
|
|
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
|
|
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(
|
|
28
|
-
const full = `🔧 ${formatToolStatus(
|
|
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.
|
|
35
|
-
*
|
|
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(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
case "
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
case "
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
return
|
|
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
|
-
/**
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
case "
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
case "
|
|
93
|
-
return
|
|
94
|
-
case "
|
|
95
|
-
return
|
|
96
|
-
case "
|
|
97
|
-
return
|
|
98
|
-
case "
|
|
99
|
-
return
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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
|
|
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");
|
package/dist/orchestrator.d.ts
CHANGED
|
@@ -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
|
-
|
|
5
|
+
engines: Map<string, Engine>;
|
|
6
6
|
config: Config;
|
|
7
7
|
}
|
|
8
8
|
export declare class Orchestrator {
|
|
9
9
|
private channel;
|
|
10
|
-
private
|
|
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
|
}
|