@travisennis/acai 0.0.11 → 0.0.13
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/README.md +5 -5
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +2 -5
- package/dist/commands/history/types.d.ts +3 -2
- package/dist/commands/history/types.d.ts.map +1 -1
- package/dist/commands/init-project/utils.d.ts +0 -1
- package/dist/commands/init-project/utils.d.ts.map +1 -1
- package/dist/commands/init-project/utils.js +1 -12
- package/dist/commands/manager.d.ts.map +1 -1
- package/dist/commands/manager.js +6 -19
- package/dist/commands/resources/index.d.ts.map +1 -1
- package/dist/commands/resources/index.js +4 -1
- package/dist/commands/session/index.d.ts.map +1 -1
- package/dist/commands/session/index.js +6 -0
- package/dist/commands/session/types.d.ts +1 -0
- package/dist/commands/session/types.d.ts.map +1 -1
- package/dist/commands/share/html-renderer.d.ts.map +1 -1
- package/dist/commands/share/html-renderer.js +54 -48
- package/dist/commands/tools/index.d.ts +3 -0
- package/dist/commands/tools/index.d.ts.map +1 -0
- package/dist/commands/tools/index.js +191 -0
- package/dist/commands/tools/templates.d.ts +6 -0
- package/dist/commands/tools/templates.d.ts.map +1 -0
- package/dist/commands/tools/templates.js +97 -0
- package/dist/config/index.d.ts +5 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +42 -6
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +98 -68
- package/dist/models/anthropic-provider.d.ts +1 -1
- package/dist/models/deepseek-provider.d.ts +3 -3
- package/dist/models/deepseek-provider.js +17 -17
- package/dist/models/google-provider.d.ts +2 -4
- package/dist/models/google-provider.d.ts.map +1 -1
- package/dist/models/google-provider.js +2 -17
- package/dist/models/groq-provider.d.ts +2 -4
- package/dist/models/groq-provider.d.ts.map +1 -1
- package/dist/models/groq-provider.js +3 -21
- package/dist/models/manager.d.ts +1 -10
- package/dist/models/manager.d.ts.map +1 -1
- package/dist/models/manager.js +2 -2
- package/dist/models/opencode-go-provider.d.ts +11 -1
- package/dist/models/opencode-go-provider.d.ts.map +1 -1
- package/dist/models/opencode-go-provider.js +130 -0
- package/dist/models/opencode-zen-provider.d.ts +3 -3
- package/dist/models/opencode-zen-provider.d.ts.map +1 -1
- package/dist/models/opencode-zen-provider.js +26 -32
- package/dist/models/openrouter-provider.d.ts +4 -15
- package/dist/models/openrouter-provider.d.ts.map +1 -1
- package/dist/models/openrouter-provider.js +26 -169
- package/dist/models/providers.d.ts +1 -1
- package/dist/models/providers.d.ts.map +1 -1
- package/dist/models/xai-provider.d.ts +1 -2
- package/dist/models/xai-provider.d.ts.map +1 -1
- package/dist/models/xai-provider.js +0 -13
- package/dist/prompts/manager.d.ts.map +1 -1
- package/dist/prompts/manager.js +5 -1
- package/dist/prompts/system-prompt.d.ts +1 -2
- package/dist/prompts/system-prompt.d.ts.map +1 -1
- package/dist/prompts/system-prompt.js +26 -15
- package/dist/repl/index.d.ts +28 -13
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +242 -305
- package/dist/skills/activated-tracker.d.ts +11 -0
- package/dist/skills/activated-tracker.d.ts.map +1 -0
- package/dist/skills/activated-tracker.js +16 -0
- package/dist/skills/index.d.ts +12 -2
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +127 -71
- package/dist/terminal/control.d.ts +1 -21
- package/dist/terminal/control.d.ts.map +1 -1
- package/dist/terminal/control.js +1 -32
- package/dist/terminal/formatting.d.ts +0 -33
- package/dist/terminal/formatting.d.ts.map +1 -1
- package/dist/terminal/formatting.js +0 -91
- package/dist/terminal/highlight/theme.d.ts +0 -37
- package/dist/terminal/highlight/theme.d.ts.map +1 -1
- package/dist/terminal/highlight/theme.js +1 -79
- package/dist/terminal/keys.d.ts +0 -97
- package/dist/terminal/keys.d.ts.map +1 -1
- package/dist/terminal/keys.js +0 -194
- package/dist/terminal/select-prompt.d.ts.map +1 -1
- package/dist/terminal/select-prompt.js +65 -51
- package/dist/terminal/style.d.ts +1 -1
- package/dist/terminal/style.d.ts.map +1 -1
- package/dist/terminal/supports-color.d.ts.map +1 -1
- package/dist/terminal/supports-color.js +38 -20
- package/dist/terminal/supports-hyperlinks.d.ts +3 -0
- package/dist/terminal/supports-hyperlinks.d.ts.map +1 -1
- package/dist/terminal/supports-hyperlinks.js +72 -31
- package/dist/terminal/table/layout-manager.d.ts +0 -20
- package/dist/terminal/table/layout-manager.d.ts.map +1 -1
- package/dist/terminal/table/layout-manager.js +68 -44
- package/dist/terminal/table/utils.d.ts +0 -1
- package/dist/terminal/table/utils.d.ts.map +1 -1
- package/dist/terminal/table/utils.js +2 -4
- package/dist/tools/apply-patch.d.ts +5 -3
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +154 -123
- package/dist/tools/bash.d.ts +4 -4
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +27 -13
- package/dist/tools/dynamic-tool-loader.d.ts +17 -3
- package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
- package/dist/tools/dynamic-tool-loader.js +360 -81
- package/dist/tools/edit-file.d.ts +2 -2
- package/dist/tools/index.d.ts +27 -151
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -21
- package/dist/tools/read-file.d.ts +8 -8
- package/dist/tools/read-file.d.ts.map +1 -1
- package/dist/tools/read-file.js +32 -13
- package/dist/tools/save-file.d.ts +4 -4
- package/dist/tools/skill.d.ts +2 -1
- package/dist/tools/skill.d.ts.map +1 -1
- package/dist/tools/skill.js +64 -14
- package/dist/tools/types.d.ts +8 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/web-fetch.d.ts +8 -12
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +86 -44
- package/dist/tools/web-search.d.ts +4 -8
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +41 -33
- package/dist/tui/autocomplete/utils.d.ts +0 -15
- package/dist/tui/autocomplete/utils.d.ts.map +1 -1
- package/dist/tui/autocomplete/utils.js +0 -85
- package/dist/tui/autocomplete.d.ts +1 -1
- package/dist/tui/autocomplete.d.ts.map +1 -1
- package/dist/tui/autocomplete.js +1 -1
- package/dist/tui/components/editor.d.ts +12 -0
- package/dist/tui/components/editor.d.ts.map +1 -1
- package/dist/tui/components/editor.js +181 -195
- package/dist/tui/components/footer.d.ts +0 -2
- package/dist/tui/components/footer.d.ts.map +1 -1
- package/dist/tui/components/footer.js +1 -17
- package/dist/tui/components/input.d.ts +4 -0
- package/dist/tui/components/input.d.ts.map +1 -1
- package/dist/tui/components/input.js +33 -31
- package/dist/tui/components/markdown.d.ts +30 -0
- package/dist/tui/components/markdown.d.ts.map +1 -1
- package/dist/tui/components/markdown.js +274 -242
- package/dist/tui/components/modal.d.ts.map +1 -1
- package/dist/tui/components/modal.js +11 -11
- package/dist/tui/components/select-list.d.ts +5 -0
- package/dist/tui/components/select-list.d.ts.map +1 -1
- package/dist/tui/components/select-list.js +69 -98
- package/dist/tui/components/table.d.ts +6 -0
- package/dist/tui/components/table.d.ts.map +1 -1
- package/dist/tui/components/table.js +61 -52
- package/dist/tui/index.d.ts +1 -8
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +0 -8
- package/dist/tui/tui.d.ts +9 -0
- package/dist/tui/tui.d.ts.map +1 -1
- package/dist/tui/tui.js +78 -48
- package/dist/tui/utils.d.ts +7 -0
- package/dist/tui/utils.d.ts.map +1 -1
- package/dist/tui/utils.js +117 -53
- package/dist/utils/bash.d.ts.map +1 -1
- package/dist/utils/bash.js +121 -104
- package/dist/utils/binary-output.d.ts +32 -0
- package/dist/utils/binary-output.d.ts.map +1 -0
- package/dist/utils/binary-output.js +127 -0
- package/dist/utils/command-protection.d.ts +3 -2
- package/dist/utils/command-protection.d.ts.map +1 -1
- package/dist/utils/command-protection.js +121 -29
- package/dist/utils/filesystem/operations.d.ts +0 -15
- package/dist/utils/filesystem/operations.d.ts.map +1 -1
- package/dist/utils/filesystem/operations.js +1 -38
- package/dist/utils/filesystem/security.d.ts.map +1 -1
- package/dist/utils/filesystem/security.js +22 -15
- package/dist/utils/filetype-detection.d.ts +0 -1
- package/dist/utils/filetype-detection.d.ts.map +1 -1
- package/dist/utils/filetype-detection.js +0 -12
- package/dist/utils/formatting.d.ts +0 -30
- package/dist/utils/formatting.d.ts.map +1 -1
- package/dist/utils/formatting.js +0 -44
- package/dist/utils/git.d.ts +4 -6
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +36 -46
- package/dist/utils/ignore.d.ts +1 -1
- package/dist/utils/ignore.d.ts.map +1 -1
- package/dist/utils/ignore.js +1 -1
- package/dist/utils/parsing.d.ts +1 -1
- package/dist/utils/parsing.d.ts.map +1 -1
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +77 -79
- package/dist/utils/yaml.d.ts +0 -1
- package/dist/utils/yaml.d.ts.map +1 -1
- package/dist/utils/yaml.js +80 -89
- package/dist/utils/zod.d.ts +0 -3
- package/dist/utils/zod.d.ts.map +1 -1
- package/dist/utils/zod.js +0 -7
- package/package.json +32 -29
- package/dist/agent/sub-agent.d.ts +0 -23
- package/dist/agent/sub-agent.d.ts.map +0 -1
- package/dist/agent/sub-agent.js +0 -109
- package/dist/commands/add-directory/index.d.ts +0 -3
- package/dist/commands/add-directory/index.d.ts.map +0 -1
- package/dist/commands/add-directory/index.js +0 -50
- package/dist/commands/add-directory/utils.d.ts +0 -3
- package/dist/commands/add-directory/utils.d.ts.map +0 -1
- package/dist/commands/add-directory/utils.js +0 -15
- package/dist/commands/clear/index.d.ts +0 -3
- package/dist/commands/clear/index.d.ts.map +0 -1
- package/dist/commands/clear/index.js +0 -13
- package/dist/commands/generate-rules/index.d.ts +0 -3
- package/dist/commands/generate-rules/index.d.ts.map +0 -1
- package/dist/commands/generate-rules/index.js +0 -206
- package/dist/commands/generate-rules/service.d.ts +0 -22
- package/dist/commands/generate-rules/service.d.ts.map +0 -1
- package/dist/commands/generate-rules/service.js +0 -103
- package/dist/commands/generate-rules/utils.d.ts +0 -5
- package/dist/commands/generate-rules/utils.d.ts.map +0 -1
- package/dist/commands/generate-rules/utils.js +0 -25
- package/dist/commands/handoff/index.d.ts +0 -3
- package/dist/commands/handoff/index.d.ts.map +0 -1
- package/dist/commands/handoff/index.js +0 -97
- package/dist/commands/handoff/utils.d.ts +0 -4
- package/dist/commands/handoff/utils.d.ts.map +0 -1
- package/dist/commands/handoff/utils.js +0 -123
- package/dist/commands/list-directories/index.d.ts +0 -3
- package/dist/commands/list-directories/index.d.ts.map +0 -1
- package/dist/commands/list-directories/index.js +0 -35
- package/dist/commands/pickup/index.d.ts +0 -3
- package/dist/commands/pickup/index.d.ts.map +0 -1
- package/dist/commands/pickup/index.js +0 -141
- package/dist/commands/pickup/types.d.ts +0 -6
- package/dist/commands/pickup/types.d.ts.map +0 -1
- package/dist/commands/pickup/types.js +0 -1
- package/dist/commands/pickup/utils.d.ts +0 -7
- package/dist/commands/pickup/utils.d.ts.map +0 -1
- package/dist/commands/pickup/utils.js +0 -56
- package/dist/commands/remove-directory/index.d.ts +0 -3
- package/dist/commands/remove-directory/index.d.ts.map +0 -1
- package/dist/commands/remove-directory/index.js +0 -55
- package/dist/commands/review/index.d.ts +0 -3
- package/dist/commands/review/index.d.ts.map +0 -1
- package/dist/commands/review/index.js +0 -12
- package/dist/commands/review/review-panel.d.ts +0 -3
- package/dist/commands/review/review-panel.d.ts.map +0 -1
- package/dist/commands/review/review-panel.js +0 -186
- package/dist/commands/review/utils.d.ts +0 -18
- package/dist/commands/review/utils.d.ts.map +0 -1
- package/dist/commands/review/utils.js +0 -146
- package/dist/commands/shell/index.d.ts +0 -3
- package/dist/commands/shell/index.d.ts.map +0 -1
- package/dist/commands/shell/index.js +0 -96
- package/dist/modes/manager.d.ts +0 -24
- package/dist/modes/manager.d.ts.map +0 -1
- package/dist/modes/manager.js +0 -77
- package/dist/modes/prompts.d.ts +0 -2
- package/dist/modes/prompts.d.ts.map +0 -1
- package/dist/modes/prompts.js +0 -142
- package/dist/subagents/index.d.ts +0 -16
- package/dist/subagents/index.d.ts.map +0 -1
- package/dist/subagents/index.js +0 -231
- package/dist/terminal/index.d.ts +0 -9
- package/dist/terminal/index.d.ts.map +0 -1
- package/dist/terminal/index.js +0 -8
- package/dist/tools/agent.d.ts +0 -27
- package/dist/tools/agent.d.ts.map +0 -1
- package/dist/tools/agent.js +0 -81
- package/dist/tools/directory-tree.d.ts +0 -28
- package/dist/tools/directory-tree.d.ts.map +0 -1
- package/dist/tools/directory-tree.js +0 -152
- package/dist/tools/glob.d.ts +0 -35
- package/dist/tools/glob.d.ts.map +0 -1
- package/dist/tools/glob.js +0 -159
- package/dist/tools/grep.d.ts +0 -100
- package/dist/tools/grep.d.ts.map +0 -1
- package/dist/tools/grep.js +0 -601
- package/dist/tools/ls.d.ts +0 -26
- package/dist/tools/ls.d.ts.map +0 -1
- package/dist/tools/ls.js +0 -82
- package/dist/tui/components/header.d.ts +0 -21
- package/dist/tui/components/header.d.ts.map +0 -1
- package/dist/tui/components/header.js +0 -63
- package/dist/utils/bash/parse.d.ts +0 -19
- package/dist/utils/bash/parse.d.ts.map +0 -1
- package/dist/utils/bash/parse.js +0 -223
- package/dist/utils/bash/quote.d.ts +0 -6
- package/dist/utils/bash/quote.d.ts.map +0 -1
- package/dist/utils/bash/quote.js +0 -23
- package/dist/utils/generators.d.ts +0 -3
- package/dist/utils/generators.d.ts.map +0 -1
- package/dist/utils/generators.js +0 -25
- package/dist/utils/glob.d.ts +0 -52
- package/dist/utils/glob.d.ts.map +0 -1
- package/dist/utils/glob.js +0 -376
|
@@ -18,7 +18,166 @@ const toolMetadataSchema = z.object({
|
|
|
18
18
|
})),
|
|
19
19
|
needsApproval: z.boolean().default(true),
|
|
20
20
|
});
|
|
21
|
-
|
|
21
|
+
const KNOWN_EXTENSIONS = [
|
|
22
|
+
".js",
|
|
23
|
+
".mjs",
|
|
24
|
+
".cjs",
|
|
25
|
+
".sh",
|
|
26
|
+
".bash",
|
|
27
|
+
".zsh",
|
|
28
|
+
".py",
|
|
29
|
+
".rb",
|
|
30
|
+
".tool",
|
|
31
|
+
];
|
|
32
|
+
const EXTENSION_INTERPRETER_MAP = {
|
|
33
|
+
".js": process.execPath,
|
|
34
|
+
".mjs": process.execPath,
|
|
35
|
+
".cjs": process.execPath,
|
|
36
|
+
".sh": "/bin/bash",
|
|
37
|
+
".bash": "/bin/bash",
|
|
38
|
+
".zsh": "/bin/zsh",
|
|
39
|
+
".py": "python3",
|
|
40
|
+
".rb": "ruby",
|
|
41
|
+
};
|
|
42
|
+
export function getShebang(scriptPath) {
|
|
43
|
+
try {
|
|
44
|
+
const fd = fs.openSync(scriptPath, "r");
|
|
45
|
+
const buffer = Buffer.alloc(256);
|
|
46
|
+
const bytesRead = fs.readSync(fd, buffer, 0, 256, 0);
|
|
47
|
+
fs.closeSync(fd);
|
|
48
|
+
const content = buffer.toString("utf8", 0, bytesRead);
|
|
49
|
+
if (content.startsWith("#!")) {
|
|
50
|
+
return content.slice(2).trim().split("\n")[0].trim();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Can't read file, skip
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
export function parseShebang(shebang, scriptPath) {
|
|
59
|
+
if (shebang.startsWith("/usr/bin/env ")) {
|
|
60
|
+
const interpreter = shebang
|
|
61
|
+
.slice("/usr/bin/env ".length)
|
|
62
|
+
.trim()
|
|
63
|
+
.split(" ")[0];
|
|
64
|
+
return { command: interpreter, args: [scriptPath] };
|
|
65
|
+
}
|
|
66
|
+
const parts = shebang.split(" ");
|
|
67
|
+
return { command: parts[0], args: [...parts.slice(1), scriptPath] };
|
|
68
|
+
}
|
|
69
|
+
export function resolveToolInterpreter(scriptPath) {
|
|
70
|
+
// 1. Check shebang
|
|
71
|
+
const shebang = getShebang(scriptPath);
|
|
72
|
+
if (shebang) {
|
|
73
|
+
return parseShebang(shebang, scriptPath);
|
|
74
|
+
}
|
|
75
|
+
// 2. Check extension
|
|
76
|
+
const ext = path.extname(scriptPath).toLowerCase();
|
|
77
|
+
if (EXTENSION_INTERPRETER_MAP[ext]) {
|
|
78
|
+
return { command: EXTENSION_INTERPRETER_MAP[ext], args: [scriptPath] };
|
|
79
|
+
}
|
|
80
|
+
// 3. Extensionless executable
|
|
81
|
+
if (!ext) {
|
|
82
|
+
try {
|
|
83
|
+
const stats = fs.statSync(scriptPath);
|
|
84
|
+
if (stats.mode & 0o111) {
|
|
85
|
+
return { command: scriptPath, args: [] };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// File doesn't exist or can't stat, skip
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
export function parseTextSchema(content) {
|
|
95
|
+
const lines = content
|
|
96
|
+
.trim()
|
|
97
|
+
.split("\n")
|
|
98
|
+
.map((l) => l.trim())
|
|
99
|
+
.filter((l) => l && !l.startsWith("#") && !l.startsWith("//"));
|
|
100
|
+
let name;
|
|
101
|
+
let description;
|
|
102
|
+
const parameters = [];
|
|
103
|
+
for (const line of lines) {
|
|
104
|
+
if (line.startsWith("name:")) {
|
|
105
|
+
name = line.slice(5).trim();
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (line.startsWith("description:")) {
|
|
109
|
+
description = line.slice(12).trim();
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const param = parseTextSchemaParameter(line);
|
|
113
|
+
if (param) {
|
|
114
|
+
parameters.push(param);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!name || !description) {
|
|
118
|
+
logger.warn("Text schema missing required name or description");
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
|
|
122
|
+
logger.warn(`Invalid tool name: ${name}`);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
name,
|
|
127
|
+
description,
|
|
128
|
+
parameters,
|
|
129
|
+
needsApproval: true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Parse a single line as a text schema parameter definition.
|
|
134
|
+
* Supports formats:
|
|
135
|
+
* - "paramName: type [optional|required] description"
|
|
136
|
+
* - "paramName: [optional|required] description" (type defaults to string)
|
|
137
|
+
* - "paramName: description" (type defaults to string, required)
|
|
138
|
+
*/
|
|
139
|
+
function parseTextSchemaParameter(line) {
|
|
140
|
+
// Parse parameters: "paramName: type [optional|required] description text"
|
|
141
|
+
const fullMatch = line.match(/^(\w+):\s*(string|number|boolean)\s+(optional|required)?\s*(.*)$/);
|
|
142
|
+
if (fullMatch) {
|
|
143
|
+
const [, paramName, paramType, requirement, paramDescription] = fullMatch;
|
|
144
|
+
return {
|
|
145
|
+
name: paramName,
|
|
146
|
+
type: paramType,
|
|
147
|
+
description: paramDescription || `Parameter ${paramName}`,
|
|
148
|
+
required: requirement !== "optional",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Handle params without type keyword (default to string)
|
|
152
|
+
const simpleMatch = line.match(/^(\w+):\s*(.*)$/);
|
|
153
|
+
if (!simpleMatch) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const [, paramName, rest] = simpleMatch;
|
|
157
|
+
const typeMatch = rest.match(/^(string|number|boolean)\s*(.*)$/);
|
|
158
|
+
if (typeMatch) {
|
|
159
|
+
const [, paramType, afterType] = typeMatch;
|
|
160
|
+
const optMatch = afterType.match(/^(optional|required)\s*(.*)$/);
|
|
161
|
+
return {
|
|
162
|
+
name: paramName,
|
|
163
|
+
type: paramType,
|
|
164
|
+
description: optMatch
|
|
165
|
+
? optMatch[2] || `Parameter ${paramName}`
|
|
166
|
+
: afterType || `Parameter ${paramName}`,
|
|
167
|
+
required: optMatch?.[1] !== "optional",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const optMatch = rest.match(/^(optional|required)\s*(.*)$/);
|
|
171
|
+
return {
|
|
172
|
+
name: paramName,
|
|
173
|
+
type: "string",
|
|
174
|
+
description: optMatch
|
|
175
|
+
? optMatch[2] || rest
|
|
176
|
+
: rest || `Parameter ${paramName}`,
|
|
177
|
+
required: optMatch?.[1] !== "optional",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function parseToolMetadata(output) {
|
|
22
181
|
try {
|
|
23
182
|
const parsed = JSON.parse(output.trim());
|
|
24
183
|
return toolMetadataSchema.parse(parsed);
|
|
@@ -58,20 +217,29 @@ function generateZodSchema(parameters) {
|
|
|
58
217
|
}
|
|
59
218
|
return z.object(fields);
|
|
60
219
|
}
|
|
61
|
-
async function getMetadata(scriptPath) {
|
|
220
|
+
async function getMetadata(scriptPath, sessionContext) {
|
|
221
|
+
const interpreter = resolveToolInterpreter(scriptPath);
|
|
222
|
+
if (!interpreter) {
|
|
223
|
+
logger.warn(`No valid interpreter for ${scriptPath}, skipping.`);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
62
226
|
return new Promise((resolve) => {
|
|
63
227
|
let child;
|
|
64
228
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
229
|
+
const env = {
|
|
230
|
+
...process.env,
|
|
231
|
+
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
232
|
+
TOOL_ACTION: "describe",
|
|
233
|
+
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
234
|
+
NODE_ENV: "production",
|
|
235
|
+
};
|
|
236
|
+
if (sessionContext) {
|
|
237
|
+
env["ACAI_SESSION_ID"] = sessionContext.sessionId;
|
|
238
|
+
env["ACAI_PROJECT_DIR"] = sessionContext.projectDir;
|
|
239
|
+
env["ACAI_AGENT_NAME"] = sessionContext.agentName;
|
|
240
|
+
}
|
|
241
|
+
child = spawn(interpreter.command, interpreter.args, {
|
|
242
|
+
env,
|
|
75
243
|
stdio: ["ignore", "pipe", "pipe"],
|
|
76
244
|
});
|
|
77
245
|
}
|
|
@@ -96,11 +264,18 @@ async function getMetadata(scriptPath) {
|
|
|
96
264
|
}
|
|
97
265
|
try {
|
|
98
266
|
const metadata = parseToolMetadata(stdout);
|
|
99
|
-
resolve(metadata);
|
|
267
|
+
resolve({ ...metadata, format: "json" });
|
|
100
268
|
}
|
|
101
|
-
catch
|
|
102
|
-
|
|
103
|
-
|
|
269
|
+
catch {
|
|
270
|
+
// JSON parse failed, try text format
|
|
271
|
+
const textMetadata = parseTextSchema(stdout);
|
|
272
|
+
if (textMetadata) {
|
|
273
|
+
resolve({ ...textMetadata, format: "text" });
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
logger.error(`Failed to parse metadata from ${scriptPath}: not valid JSON or text format`);
|
|
277
|
+
resolve(null);
|
|
278
|
+
}
|
|
104
279
|
}
|
|
105
280
|
});
|
|
106
281
|
child.on("error", (err) => {
|
|
@@ -109,22 +284,70 @@ async function getMetadata(scriptPath) {
|
|
|
109
284
|
});
|
|
110
285
|
});
|
|
111
286
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Processes child process stdout/stderr into a result string.
|
|
289
|
+
* Trims, truncates oversized output, falls back to stderr or a placeholder,
|
|
290
|
+
* and attempts JSON parsing for structured output.
|
|
291
|
+
*/
|
|
292
|
+
export function processChildOutput(stdout, stderr) {
|
|
293
|
+
const maxOutputBytes = 2_000_000;
|
|
294
|
+
let output = stdout.trim();
|
|
295
|
+
if (output.length > maxOutputBytes) {
|
|
296
|
+
output = `${output.substring(0, maxOutputBytes)}\n[Output truncated]`;
|
|
297
|
+
}
|
|
298
|
+
if (!output) {
|
|
299
|
+
const errText = stderr.trim();
|
|
300
|
+
if (errText) {
|
|
301
|
+
output = errText;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (!output) {
|
|
305
|
+
output = "[No output from dynamic tool]";
|
|
306
|
+
}
|
|
307
|
+
if (output.startsWith("{") || output.startsWith("[")) {
|
|
308
|
+
try {
|
|
309
|
+
return JSON.stringify(JSON.parse(output));
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
return output;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return output;
|
|
316
|
+
}
|
|
317
|
+
async function spawnChildProcess(scriptPath, params, abortSignal, sessionContext, format = "json") {
|
|
318
|
+
const interpreter = resolveToolInterpreter(scriptPath);
|
|
319
|
+
if (!interpreter) {
|
|
320
|
+
throw new Error(`No valid interpreter for ${scriptPath}`);
|
|
321
|
+
}
|
|
322
|
+
let paramsInput;
|
|
323
|
+
if (format === "text") {
|
|
324
|
+
paramsInput = `${Object.entries(params)
|
|
325
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
326
|
+
.join("\n")}\n`;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
const paramsArray = Object.entries(params).map(([name, value]) => ({
|
|
330
|
+
name,
|
|
331
|
+
value,
|
|
332
|
+
}));
|
|
333
|
+
paramsInput = `${JSON.stringify(paramsArray)}\n`;
|
|
334
|
+
}
|
|
335
|
+
const env = {
|
|
336
|
+
...process.env,
|
|
337
|
+
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
338
|
+
TOOL_ACTION: "execute",
|
|
339
|
+
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
340
|
+
NODE_ENV: "production",
|
|
341
|
+
};
|
|
342
|
+
if (sessionContext) {
|
|
343
|
+
env["ACAI_SESSION_ID"] = sessionContext.sessionId;
|
|
344
|
+
env["ACAI_PROJECT_DIR"] = sessionContext.projectDir;
|
|
345
|
+
env["ACAI_AGENT_NAME"] = sessionContext.agentName;
|
|
346
|
+
}
|
|
118
347
|
return new Promise((resolve, reject) => {
|
|
119
|
-
const child = spawn(
|
|
348
|
+
const child = spawn(interpreter.command, interpreter.args, {
|
|
120
349
|
cwd: path.dirname(scriptPath),
|
|
121
|
-
env
|
|
122
|
-
...process.env,
|
|
123
|
-
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
124
|
-
TOOL_ACTION: "execute",
|
|
125
|
-
// biome-ignore lint/style/useNamingConvention: Environment variables are conventionally uppercase
|
|
126
|
-
NODE_ENV: "production",
|
|
127
|
-
},
|
|
350
|
+
env,
|
|
128
351
|
stdio: ["pipe", "pipe", "pipe"],
|
|
129
352
|
});
|
|
130
353
|
let stdout = "";
|
|
@@ -135,7 +358,7 @@ async function spawnChildProcess(scriptPath, params, abortSignal) {
|
|
|
135
358
|
child.kill();
|
|
136
359
|
reject(new Error("Execution timed out after 30 seconds"));
|
|
137
360
|
}, 30000);
|
|
138
|
-
child.stdin.write(
|
|
361
|
+
child.stdin.write(paramsInput);
|
|
139
362
|
child.stdin.end();
|
|
140
363
|
child.stdout.on("data", (data) => {
|
|
141
364
|
stdout += data.toString();
|
|
@@ -147,32 +370,7 @@ async function spawnChildProcess(scriptPath, params, abortSignal) {
|
|
|
147
370
|
clearTimeout(timer);
|
|
148
371
|
if (hasTimedOut)
|
|
149
372
|
return;
|
|
150
|
-
|
|
151
|
-
const maxOutputBytes = 2000000;
|
|
152
|
-
if (output.length > maxOutputBytes) {
|
|
153
|
-
output = `${output.substring(0, maxOutputBytes)}\n[Output truncated]`;
|
|
154
|
-
}
|
|
155
|
-
// If no stdout, prefer stderr so callers get useful info
|
|
156
|
-
const errText = stderr.trim();
|
|
157
|
-
if (!output && errText) {
|
|
158
|
-
output = errText;
|
|
159
|
-
}
|
|
160
|
-
// Fallback to a non-empty placeholder to satisfy callers
|
|
161
|
-
if (!output) {
|
|
162
|
-
output = "[No output from dynamic tool]";
|
|
163
|
-
}
|
|
164
|
-
// Attempt to parse as JSON if structured
|
|
165
|
-
if (output && (output.startsWith("{") || output.startsWith("["))) {
|
|
166
|
-
try {
|
|
167
|
-
resolve(JSON.stringify(JSON.parse(output)));
|
|
168
|
-
}
|
|
169
|
-
catch {
|
|
170
|
-
resolve(output);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
resolve(output);
|
|
175
|
-
}
|
|
373
|
+
resolve(processChildOutput(stdout, stderr));
|
|
176
374
|
});
|
|
177
375
|
if (abortSignal) {
|
|
178
376
|
abortSignal.addEventListener("abort", () => {
|
|
@@ -181,9 +379,10 @@ async function spawnChildProcess(scriptPath, params, abortSignal) {
|
|
|
181
379
|
}
|
|
182
380
|
});
|
|
183
381
|
}
|
|
184
|
-
function createDynamicTool(scriptPath, metadata) {
|
|
382
|
+
function createDynamicTool(scriptPath, metadata, sessionContext) {
|
|
185
383
|
const inputSchema = generateZodSchema(metadata.parameters);
|
|
186
384
|
const toolName = metadata.name;
|
|
385
|
+
const format = metadata.format;
|
|
187
386
|
return {
|
|
188
387
|
[toolName]: {
|
|
189
388
|
toolDef: {
|
|
@@ -193,7 +392,7 @@ function createDynamicTool(scriptPath, metadata) {
|
|
|
193
392
|
display() {
|
|
194
393
|
return "running";
|
|
195
394
|
},
|
|
196
|
-
async execute(input, { abortSignal }) {
|
|
395
|
+
async execute(input, { abortSignal, sessionContext: executionContext }) {
|
|
197
396
|
if (abortSignal?.aborted) {
|
|
198
397
|
throw new Error("Execution aborted");
|
|
199
398
|
}
|
|
@@ -203,12 +402,36 @@ function createDynamicTool(scriptPath, metadata) {
|
|
|
203
402
|
catch (e) {
|
|
204
403
|
throw new Error(`Invalid parameters for tool ${metadata.name}: ${e.message}`);
|
|
205
404
|
}
|
|
206
|
-
|
|
405
|
+
// Prefer execution-time context over load-time context
|
|
406
|
+
const context = executionContext ?? sessionContext;
|
|
407
|
+
return spawnChildProcess(scriptPath, input, abortSignal, context, format);
|
|
207
408
|
},
|
|
208
409
|
},
|
|
209
410
|
};
|
|
210
411
|
}
|
|
211
|
-
|
|
412
|
+
function findCompanion(dir, baseName) {
|
|
413
|
+
const companionExtensions = [
|
|
414
|
+
".sh",
|
|
415
|
+
".bash",
|
|
416
|
+
".zsh",
|
|
417
|
+
".py",
|
|
418
|
+
".rb",
|
|
419
|
+
".js",
|
|
420
|
+
".mjs",
|
|
421
|
+
".cjs",
|
|
422
|
+
"",
|
|
423
|
+
];
|
|
424
|
+
for (const ext of companionExtensions) {
|
|
425
|
+
const candidate = path.join(dir, baseName + ext);
|
|
426
|
+
if (fs.existsSync(candidate)) {
|
|
427
|
+
const interpreter = resolveToolInterpreter(candidate);
|
|
428
|
+
if (interpreter)
|
|
429
|
+
return candidate;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
export async function loadDynamicTools({ baseDir, existingToolNames = [], sessionContext, }) {
|
|
212
435
|
const projectConfig = await config.getConfig();
|
|
213
436
|
const dynamicConfig = projectConfig.tools.dynamicTools;
|
|
214
437
|
if (!dynamicConfig.enabled) {
|
|
@@ -218,30 +441,86 @@ export async function loadDynamicTools({ baseDir, existingToolNames = [], }) {
|
|
|
218
441
|
const projectToolsDir = path.join(baseDir, ".acai", "tools");
|
|
219
442
|
const userToolsDir = path.join(os.homedir(), ".acai", "tools");
|
|
220
443
|
const toolMap = new Map();
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
for (const file of files) {
|
|
229
|
-
const scriptPath = path.join(dir, file);
|
|
444
|
+
const filterEligibleFiles = (dir) => {
|
|
445
|
+
return fs.readdirSync(dir).filter((f) => {
|
|
446
|
+
if (KNOWN_EXTENSIONS.some((ext) => f.endsWith(ext))) {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
if (!path.extname(f)) {
|
|
450
|
+
const fullPath = path.join(dir, f);
|
|
230
451
|
try {
|
|
231
|
-
const
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
logger.info(`Loaded ${isProject ? "project" : "user"} tool: ${metadata.name}`);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
logger.warn(`Skipped invalid tool: ${file}`);
|
|
238
|
-
}
|
|
452
|
+
const stats = fs.statSync(fullPath);
|
|
453
|
+
if (stats.mode & 0o111)
|
|
454
|
+
return true;
|
|
239
455
|
}
|
|
240
|
-
catch
|
|
241
|
-
|
|
456
|
+
catch {
|
|
457
|
+
// Can't stat, skip
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return false;
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
const loadScriptTools = async (dir, files, isProject) => {
|
|
464
|
+
for (const file of files) {
|
|
465
|
+
if (file.endsWith(".tool"))
|
|
466
|
+
continue;
|
|
467
|
+
const scriptPath = path.join(dir, file);
|
|
468
|
+
try {
|
|
469
|
+
const metadata = await getMetadata(scriptPath, sessionContext);
|
|
470
|
+
if (metadata) {
|
|
471
|
+
toolMap.set(metadata.name, { path: scriptPath, metadata });
|
|
472
|
+
logger.info(`Loaded ${isProject ? "project" : "user"} tool: ${metadata.name}`);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
logger.warn(`Skipped invalid tool: ${file}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
logger.error(`Error scanning ${file}: ${e}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
const loadDotToolFiles = async (dir, isProject) => {
|
|
484
|
+
const allDirFiles = fs.readdirSync(dir);
|
|
485
|
+
const allToolFiles = allDirFiles.filter((f) => f.endsWith(".tool"));
|
|
486
|
+
for (const toolFile of allToolFiles) {
|
|
487
|
+
const toolPath = path.join(dir, toolFile);
|
|
488
|
+
try {
|
|
489
|
+
const content = fs.readFileSync(toolPath, "utf8");
|
|
490
|
+
const metadata = parseTextSchema(content);
|
|
491
|
+
if (!metadata) {
|
|
492
|
+
logger.warn(`Failed to parse .tool file: ${toolFile}`);
|
|
493
|
+
continue;
|
|
242
494
|
}
|
|
495
|
+
const baseName = toolFile.slice(0, -5);
|
|
496
|
+
const companionPath = findCompanion(dir, baseName);
|
|
497
|
+
if (!companionPath) {
|
|
498
|
+
logger.warn(`No companion executable found for ${toolFile}`);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const metadataWithFormat = {
|
|
502
|
+
...metadata,
|
|
503
|
+
format: "text",
|
|
504
|
+
};
|
|
505
|
+
toolMap.set(metadata.name, {
|
|
506
|
+
path: companionPath,
|
|
507
|
+
metadata: metadataWithFormat,
|
|
508
|
+
});
|
|
509
|
+
logger.info(`Loaded ${isProject ? "project" : "user"} tool: ${metadata.name} (from .tool file)`);
|
|
510
|
+
}
|
|
511
|
+
catch (e) {
|
|
512
|
+
logger.error(`Error reading .tool file ${toolFile}: ${e}`);
|
|
243
513
|
}
|
|
244
514
|
}
|
|
515
|
+
};
|
|
516
|
+
const scanDir = async (dir, isProject = false) => {
|
|
517
|
+
if (!fs.existsSync(dir))
|
|
518
|
+
return;
|
|
519
|
+
try {
|
|
520
|
+
const files = filterEligibleFiles(dir);
|
|
521
|
+
await loadScriptTools(dir, files, isProject);
|
|
522
|
+
await loadDotToolFiles(dir, isProject);
|
|
523
|
+
}
|
|
245
524
|
catch (e) {
|
|
246
525
|
logger.error(`Error reading dir ${dir}: ${e}`);
|
|
247
526
|
}
|
|
@@ -274,7 +553,7 @@ export async function loadDynamicTools({ baseDir, existingToolNames = [], }) {
|
|
|
274
553
|
logger.warn(`Duplicate dynamic tool name '${toolName}' found. Skipping duplicate.`);
|
|
275
554
|
continue;
|
|
276
555
|
}
|
|
277
|
-
Object.assign(tools, createDynamicTool(path, metadata));
|
|
556
|
+
Object.assign(tools, createDynamicTool(path, metadata, sessionContext));
|
|
278
557
|
}
|
|
279
558
|
if (conflictingTools.length > 0) {
|
|
280
559
|
logger.warn(`Warning: ${conflictingTools.length} dynamic tool(s) skipped due to name conflicts: ${conflictingTools.join(", ")}`);
|
|
@@ -6,7 +6,7 @@ export declare const EditFileTool: {
|
|
|
6
6
|
};
|
|
7
7
|
declare const inputSchema: z.ZodObject<{
|
|
8
8
|
path: z.ZodString;
|
|
9
|
-
edits: z.
|
|
9
|
+
edits: z.ZodPreprocess<z.ZodArray<z.ZodObject<{
|
|
10
10
|
oldText: z.ZodString;
|
|
11
11
|
newText: z.ZodString;
|
|
12
12
|
}, z.core.$strip>>>;
|
|
@@ -19,7 +19,7 @@ export declare const createEditFileTool: (options: {
|
|
|
19
19
|
description: string;
|
|
20
20
|
inputSchema: z.ZodObject<{
|
|
21
21
|
path: z.ZodString;
|
|
22
|
-
edits: z.
|
|
22
|
+
edits: z.ZodPreprocess<z.ZodArray<z.ZodObject<{
|
|
23
23
|
oldText: z.ZodString;
|
|
24
24
|
newText: z.ZodString;
|
|
25
25
|
}, z.core.$strip>>>;
|