@travisennis/acai 0.0.12 → 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.
Files changed (237) hide show
  1. package/README.md +3 -2
  2. package/dist/agent/index.d.ts.map +1 -1
  3. package/dist/agent/index.js +2 -5
  4. package/dist/commands/history/types.d.ts +3 -2
  5. package/dist/commands/history/types.d.ts.map +1 -1
  6. package/dist/commands/init-project/utils.d.ts +0 -1
  7. package/dist/commands/init-project/utils.d.ts.map +1 -1
  8. package/dist/commands/init-project/utils.js +1 -1
  9. package/dist/commands/manager.d.ts.map +1 -1
  10. package/dist/commands/manager.js +0 -18
  11. package/dist/commands/share/html-renderer.d.ts.map +1 -1
  12. package/dist/commands/share/html-renderer.js +54 -48
  13. package/dist/commands/tools/index.js +39 -38
  14. package/dist/config/index.d.ts +0 -2
  15. package/dist/config/index.d.ts.map +1 -1
  16. package/dist/config/index.js +2 -6
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +94 -76
  20. package/dist/models/manager.d.ts +1 -10
  21. package/dist/models/manager.d.ts.map +1 -1
  22. package/dist/models/manager.js +2 -2
  23. package/dist/models/opencode-go-provider.d.ts +4 -4
  24. package/dist/models/opencode-go-provider.d.ts.map +1 -1
  25. package/dist/models/opencode-go-provider.js +29 -35
  26. package/dist/models/providers.d.ts +1 -1
  27. package/dist/models/providers.d.ts.map +1 -1
  28. package/dist/prompts/system-prompt.d.ts +0 -2
  29. package/dist/prompts/system-prompt.d.ts.map +1 -1
  30. package/dist/prompts/system-prompt.js +6 -10
  31. package/dist/repl/index.d.ts +27 -11
  32. package/dist/repl/index.d.ts.map +1 -1
  33. package/dist/repl/index.js +238 -254
  34. package/dist/skills/index.d.ts +12 -2
  35. package/dist/skills/index.d.ts.map +1 -1
  36. package/dist/skills/index.js +123 -73
  37. package/dist/terminal/control.d.ts +1 -21
  38. package/dist/terminal/control.d.ts.map +1 -1
  39. package/dist/terminal/control.js +1 -32
  40. package/dist/terminal/formatting.d.ts +0 -33
  41. package/dist/terminal/formatting.d.ts.map +1 -1
  42. package/dist/terminal/formatting.js +0 -91
  43. package/dist/terminal/highlight/theme.d.ts +0 -37
  44. package/dist/terminal/highlight/theme.d.ts.map +1 -1
  45. package/dist/terminal/highlight/theme.js +1 -79
  46. package/dist/terminal/keys.d.ts +0 -97
  47. package/dist/terminal/keys.d.ts.map +1 -1
  48. package/dist/terminal/keys.js +0 -194
  49. package/dist/terminal/select-prompt.d.ts.map +1 -1
  50. package/dist/terminal/select-prompt.js +65 -51
  51. package/dist/terminal/style.d.ts +1 -1
  52. package/dist/terminal/style.d.ts.map +1 -1
  53. package/dist/terminal/supports-color.d.ts.map +1 -1
  54. package/dist/terminal/supports-color.js +38 -20
  55. package/dist/terminal/supports-hyperlinks.d.ts +3 -0
  56. package/dist/terminal/supports-hyperlinks.d.ts.map +1 -1
  57. package/dist/terminal/supports-hyperlinks.js +72 -31
  58. package/dist/terminal/table/layout-manager.d.ts +0 -20
  59. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  60. package/dist/terminal/table/layout-manager.js +68 -44
  61. package/dist/terminal/table/utils.d.ts +0 -1
  62. package/dist/terminal/table/utils.d.ts.map +1 -1
  63. package/dist/terminal/table/utils.js +2 -4
  64. package/dist/tools/apply-patch.d.ts +5 -3
  65. package/dist/tools/apply-patch.d.ts.map +1 -1
  66. package/dist/tools/apply-patch.js +154 -123
  67. package/dist/tools/bash.d.ts.map +1 -1
  68. package/dist/tools/bash.js +11 -8
  69. package/dist/tools/dynamic-tool-loader.d.ts +6 -1
  70. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  71. package/dist/tools/dynamic-tool-loader.js +154 -135
  72. package/dist/tools/index.d.ts +3 -133
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/index.js +0 -20
  75. package/dist/tools/read-file.d.ts.map +1 -1
  76. package/dist/tools/read-file.js +32 -13
  77. package/dist/tools/skill.d.ts.map +1 -1
  78. package/dist/tools/skill.js +20 -13
  79. package/dist/tools/web-fetch.d.ts +2 -6
  80. package/dist/tools/web-fetch.d.ts.map +1 -1
  81. package/dist/tools/web-fetch.js +59 -36
  82. package/dist/tools/web-search.d.ts +0 -4
  83. package/dist/tools/web-search.d.ts.map +1 -1
  84. package/dist/tools/web-search.js +40 -32
  85. package/dist/tui/autocomplete/utils.d.ts +0 -15
  86. package/dist/tui/autocomplete/utils.d.ts.map +1 -1
  87. package/dist/tui/autocomplete/utils.js +0 -85
  88. package/dist/tui/autocomplete.d.ts +1 -1
  89. package/dist/tui/autocomplete.d.ts.map +1 -1
  90. package/dist/tui/autocomplete.js +1 -1
  91. package/dist/tui/components/editor.d.ts +12 -0
  92. package/dist/tui/components/editor.d.ts.map +1 -1
  93. package/dist/tui/components/editor.js +181 -195
  94. package/dist/tui/components/input.d.ts +4 -0
  95. package/dist/tui/components/input.d.ts.map +1 -1
  96. package/dist/tui/components/input.js +33 -31
  97. package/dist/tui/components/markdown.d.ts +30 -0
  98. package/dist/tui/components/markdown.d.ts.map +1 -1
  99. package/dist/tui/components/markdown.js +274 -242
  100. package/dist/tui/components/modal.d.ts.map +1 -1
  101. package/dist/tui/components/modal.js +11 -11
  102. package/dist/tui/components/select-list.d.ts +5 -0
  103. package/dist/tui/components/select-list.d.ts.map +1 -1
  104. package/dist/tui/components/select-list.js +69 -98
  105. package/dist/tui/components/table.d.ts +6 -0
  106. package/dist/tui/components/table.d.ts.map +1 -1
  107. package/dist/tui/components/table.js +61 -52
  108. package/dist/tui/index.d.ts +1 -8
  109. package/dist/tui/index.d.ts.map +1 -1
  110. package/dist/tui/index.js +0 -8
  111. package/dist/tui/tui.d.ts +9 -0
  112. package/dist/tui/tui.d.ts.map +1 -1
  113. package/dist/tui/tui.js +78 -48
  114. package/dist/tui/utils.d.ts +7 -0
  115. package/dist/tui/utils.d.ts.map +1 -1
  116. package/dist/tui/utils.js +117 -53
  117. package/dist/utils/bash.d.ts.map +1 -1
  118. package/dist/utils/bash.js +121 -104
  119. package/dist/utils/command-protection.d.ts +3 -2
  120. package/dist/utils/command-protection.d.ts.map +1 -1
  121. package/dist/utils/command-protection.js +42 -33
  122. package/dist/utils/filesystem/operations.d.ts +0 -15
  123. package/dist/utils/filesystem/operations.d.ts.map +1 -1
  124. package/dist/utils/filesystem/operations.js +1 -38
  125. package/dist/utils/filesystem/security.d.ts.map +1 -1
  126. package/dist/utils/filesystem/security.js +22 -15
  127. package/dist/utils/filetype-detection.d.ts +0 -1
  128. package/dist/utils/filetype-detection.d.ts.map +1 -1
  129. package/dist/utils/filetype-detection.js +0 -12
  130. package/dist/utils/formatting.d.ts +0 -30
  131. package/dist/utils/formatting.d.ts.map +1 -1
  132. package/dist/utils/formatting.js +0 -44
  133. package/dist/utils/git.d.ts +4 -6
  134. package/dist/utils/git.d.ts.map +1 -1
  135. package/dist/utils/git.js +36 -46
  136. package/dist/utils/ignore.d.ts +1 -1
  137. package/dist/utils/ignore.d.ts.map +1 -1
  138. package/dist/utils/ignore.js +1 -1
  139. package/dist/utils/process.d.ts.map +1 -1
  140. package/dist/utils/process.js +77 -79
  141. package/dist/utils/yaml.d.ts +0 -1
  142. package/dist/utils/yaml.d.ts.map +1 -1
  143. package/dist/utils/yaml.js +80 -89
  144. package/dist/utils/zod.d.ts +0 -3
  145. package/dist/utils/zod.d.ts.map +1 -1
  146. package/dist/utils/zod.js +0 -7
  147. package/package.json +25 -24
  148. package/dist/agent/sub-agent.d.ts +0 -23
  149. package/dist/agent/sub-agent.d.ts.map +0 -1
  150. package/dist/agent/sub-agent.js +0 -109
  151. package/dist/commands/add-directory/index.d.ts +0 -3
  152. package/dist/commands/add-directory/index.d.ts.map +0 -1
  153. package/dist/commands/add-directory/index.js +0 -50
  154. package/dist/commands/add-directory/utils.d.ts +0 -3
  155. package/dist/commands/add-directory/utils.d.ts.map +0 -1
  156. package/dist/commands/add-directory/utils.js +0 -15
  157. package/dist/commands/clear/index.d.ts +0 -3
  158. package/dist/commands/clear/index.d.ts.map +0 -1
  159. package/dist/commands/clear/index.js +0 -13
  160. package/dist/commands/generate-rules/index.d.ts +0 -3
  161. package/dist/commands/generate-rules/index.d.ts.map +0 -1
  162. package/dist/commands/generate-rules/index.js +0 -206
  163. package/dist/commands/generate-rules/service.d.ts +0 -22
  164. package/dist/commands/generate-rules/service.d.ts.map +0 -1
  165. package/dist/commands/generate-rules/service.js +0 -103
  166. package/dist/commands/generate-rules/utils.d.ts +0 -5
  167. package/dist/commands/generate-rules/utils.d.ts.map +0 -1
  168. package/dist/commands/generate-rules/utils.js +0 -25
  169. package/dist/commands/handoff/index.d.ts +0 -3
  170. package/dist/commands/handoff/index.d.ts.map +0 -1
  171. package/dist/commands/handoff/index.js +0 -97
  172. package/dist/commands/handoff/utils.d.ts +0 -4
  173. package/dist/commands/handoff/utils.d.ts.map +0 -1
  174. package/dist/commands/handoff/utils.js +0 -123
  175. package/dist/commands/list-directories/index.d.ts +0 -3
  176. package/dist/commands/list-directories/index.d.ts.map +0 -1
  177. package/dist/commands/list-directories/index.js +0 -35
  178. package/dist/commands/pickup/index.d.ts +0 -3
  179. package/dist/commands/pickup/index.d.ts.map +0 -1
  180. package/dist/commands/pickup/index.js +0 -141
  181. package/dist/commands/pickup/types.d.ts +0 -6
  182. package/dist/commands/pickup/types.d.ts.map +0 -1
  183. package/dist/commands/pickup/types.js +0 -1
  184. package/dist/commands/pickup/utils.d.ts +0 -7
  185. package/dist/commands/pickup/utils.d.ts.map +0 -1
  186. package/dist/commands/pickup/utils.js +0 -56
  187. package/dist/commands/remove-directory/index.d.ts +0 -3
  188. package/dist/commands/remove-directory/index.d.ts.map +0 -1
  189. package/dist/commands/remove-directory/index.js +0 -55
  190. package/dist/commands/review/index.d.ts +0 -3
  191. package/dist/commands/review/index.d.ts.map +0 -1
  192. package/dist/commands/review/index.js +0 -12
  193. package/dist/commands/review/review-panel.d.ts +0 -3
  194. package/dist/commands/review/review-panel.d.ts.map +0 -1
  195. package/dist/commands/review/review-panel.js +0 -186
  196. package/dist/commands/review/utils.d.ts +0 -18
  197. package/dist/commands/review/utils.d.ts.map +0 -1
  198. package/dist/commands/review/utils.js +0 -146
  199. package/dist/commands/shell/index.d.ts +0 -3
  200. package/dist/commands/shell/index.d.ts.map +0 -1
  201. package/dist/commands/shell/index.js +0 -96
  202. package/dist/subagents/index.d.ts +0 -16
  203. package/dist/subagents/index.d.ts.map +0 -1
  204. package/dist/subagents/index.js +0 -231
  205. package/dist/terminal/index.d.ts +0 -9
  206. package/dist/terminal/index.d.ts.map +0 -1
  207. package/dist/terminal/index.js +0 -8
  208. package/dist/tools/agent.d.ts +0 -27
  209. package/dist/tools/agent.d.ts.map +0 -1
  210. package/dist/tools/agent.js +0 -81
  211. package/dist/tools/directory-tree.d.ts +0 -28
  212. package/dist/tools/directory-tree.d.ts.map +0 -1
  213. package/dist/tools/directory-tree.js +0 -154
  214. package/dist/tools/glob.d.ts +0 -35
  215. package/dist/tools/glob.d.ts.map +0 -1
  216. package/dist/tools/glob.js +0 -167
  217. package/dist/tools/grep.d.ts +0 -100
  218. package/dist/tools/grep.d.ts.map +0 -1
  219. package/dist/tools/grep.js +0 -608
  220. package/dist/tools/ls.d.ts +0 -26
  221. package/dist/tools/ls.d.ts.map +0 -1
  222. package/dist/tools/ls.js +0 -83
  223. package/dist/tui/components/header.d.ts +0 -21
  224. package/dist/tui/components/header.d.ts.map +0 -1
  225. package/dist/tui/components/header.js +0 -63
  226. package/dist/utils/bash/parse.d.ts +0 -19
  227. package/dist/utils/bash/parse.d.ts.map +0 -1
  228. package/dist/utils/bash/parse.js +0 -223
  229. package/dist/utils/bash/quote.d.ts +0 -6
  230. package/dist/utils/bash/quote.d.ts.map +0 -1
  231. package/dist/utils/bash/quote.js +0 -23
  232. package/dist/utils/generators.d.ts +0 -3
  233. package/dist/utils/generators.d.ts.map +0 -1
  234. package/dist/utils/generators.js +0 -25
  235. package/dist/utils/glob.d.ts +0 -52
  236. package/dist/utils/glob.d.ts.map +0 -1
  237. package/dist/utils/glob.js +0 -376
@@ -109,48 +109,9 @@ export function parseTextSchema(content) {
109
109
  description = line.slice(12).trim();
110
110
  continue;
111
111
  }
112
- // Parse parameters: "paramName: type [optional|required] description text"
113
- const paramMatch = line.match(/^(\w+):\s*(string|number|boolean)\s+(optional|required)?\s*(.*)$/);
114
- if (paramMatch) {
115
- const [, paramName, paramType, requirement, paramDescription] = paramMatch;
116
- parameters.push({
117
- name: paramName,
118
- type: paramType,
119
- description: paramDescription || `Parameter ${paramName}`,
120
- required: requirement !== "optional",
121
- });
122
- continue;
123
- }
124
- // Handle params without type keyword (default to string)
125
- const simpleMatch = line.match(/^(\w+):\s*(.*)$/);
126
- if (simpleMatch &&
127
- !line.startsWith("name:") &&
128
- !line.startsWith("description:")) {
129
- const [, paramName, rest] = simpleMatch;
130
- const typeMatch = rest.match(/^(string|number|boolean)\s*(.*)$/);
131
- if (typeMatch) {
132
- const [, paramType, afterType] = typeMatch;
133
- const optMatch = afterType.match(/^(optional|required)\s*(.*)$/);
134
- parameters.push({
135
- name: paramName,
136
- type: paramType,
137
- description: optMatch
138
- ? optMatch[2] || `Parameter ${paramName}`
139
- : afterType || `Parameter ${paramName}`,
140
- required: !optMatch || optMatch[1] !== "optional",
141
- });
142
- }
143
- else {
144
- const optMatch = rest.match(/^(optional|required)\s*(.*)$/);
145
- parameters.push({
146
- name: paramName,
147
- type: "string",
148
- description: optMatch
149
- ? optMatch[2] || rest
150
- : rest || `Parameter ${paramName}`,
151
- required: !optMatch || optMatch[1] !== "optional",
152
- });
153
- }
112
+ const param = parseTextSchemaParameter(line);
113
+ if (param) {
114
+ parameters.push(param);
154
115
  }
155
116
  }
156
117
  if (!name || !description) {
@@ -168,7 +129,55 @@ export function parseTextSchema(content) {
168
129
  needsApproval: true,
169
130
  };
170
131
  }
171
- export function parseToolMetadata(output) {
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) {
172
181
  try {
173
182
  const parsed = JSON.parse(output.trim());
174
183
  return toolMetadataSchema.parse(parsed);
@@ -275,6 +284,36 @@ async function getMetadata(scriptPath, sessionContext) {
275
284
  });
276
285
  });
277
286
  }
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
+ }
278
317
  async function spawnChildProcess(scriptPath, params, abortSignal, sessionContext, format = "json") {
279
318
  const interpreter = resolveToolInterpreter(scriptPath);
280
319
  if (!interpreter) {
@@ -331,32 +370,7 @@ async function spawnChildProcess(scriptPath, params, abortSignal, sessionContext
331
370
  clearTimeout(timer);
332
371
  if (hasTimedOut)
333
372
  return;
334
- let output = stdout.trim();
335
- const maxOutputBytes = 2000000;
336
- if (output.length > maxOutputBytes) {
337
- output = `${output.substring(0, maxOutputBytes)}\n[Output truncated]`;
338
- }
339
- // If no stdout, prefer stderr so callers get useful info
340
- const errText = stderr.trim();
341
- if (!output && errText) {
342
- output = errText;
343
- }
344
- // Fallback to a non-empty placeholder to satisfy callers
345
- if (!output) {
346
- output = "[No output from dynamic tool]";
347
- }
348
- // Attempt to parse as JSON if structured
349
- if (output && (output.startsWith("{") || output.startsWith("["))) {
350
- try {
351
- resolve(JSON.stringify(JSON.parse(output)));
352
- }
353
- catch {
354
- resolve(output);
355
- }
356
- }
357
- else {
358
- resolve(output);
359
- }
373
+ resolve(processChildOutput(stdout, stderr));
360
374
  });
361
375
  if (abortSignal) {
362
376
  abortSignal.addEventListener("abort", () => {
@@ -427,81 +441,86 @@ export async function loadDynamicTools({ baseDir, existingToolNames = [], sessio
427
441
  const projectToolsDir = path.join(baseDir, ".acai", "tools");
428
442
  const userToolsDir = path.join(os.homedir(), ".acai", "tools");
429
443
  const toolMap = new Map();
430
- const scanDir = async (dir, isProject = false) => {
431
- if (!fs.existsSync(dir))
432
- return;
433
- try {
434
- const files = fs.readdirSync(dir).filter((f) => {
435
- // Known extensions
436
- if (KNOWN_EXTENSIONS.some((ext) => f.endsWith(ext))) {
437
- return true;
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);
451
+ try {
452
+ const stats = fs.statSync(fullPath);
453
+ if (stats.mode & 0o111)
454
+ return true;
438
455
  }
439
- // Extensionless files that are executable
440
- if (!path.extname(f)) {
441
- const fullPath = path.join(dir, f);
442
- try {
443
- const stats = fs.statSync(fullPath);
444
- if (stats.mode & 0o111)
445
- return true;
446
- }
447
- catch {
448
- // Can't stat, skip
449
- }
456
+ catch {
457
+ // Can't stat, skip
450
458
  }
451
- return false;
452
- });
453
- for (const file of files) {
454
- // Skip .tool files, they are handled separately
455
- if (file.endsWith(".tool"))
456
- continue;
457
- const scriptPath = path.join(dir, file);
458
- try {
459
- const metadata = await getMetadata(scriptPath, sessionContext);
460
- if (metadata) {
461
- toolMap.set(metadata.name, { path: scriptPath, metadata });
462
- logger.info(`Loaded ${isProject ? "project" : "user"} tool: ${metadata.name}`);
463
- }
464
- else {
465
- logger.warn(`Skipped invalid tool: ${file}`);
466
- }
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}`);
467
473
  }
468
- catch (e) {
469
- logger.error(`Error scanning ${file}: ${e}`);
474
+ else {
475
+ logger.warn(`Skipped invalid tool: ${file}`);
470
476
  }
471
477
  }
472
- // Scan for .tool files (text schema with companion executable)
473
- const allDirFiles = fs.readdirSync(dir);
474
- const allToolFiles = allDirFiles.filter((f) => f.endsWith(".tool"));
475
- for (const toolFile of allToolFiles) {
476
- const toolPath = path.join(dir, toolFile);
477
- try {
478
- const content = fs.readFileSync(toolPath, "utf8");
479
- const metadata = parseTextSchema(content);
480
- if (!metadata) {
481
- logger.warn(`Failed to parse .tool file: ${toolFile}`);
482
- continue;
483
- }
484
- const baseName = toolFile.slice(0, -5); // Remove .tool extension
485
- const companionPath = findCompanion(dir, baseName);
486
- if (!companionPath) {
487
- logger.warn(`No companion executable found for ${toolFile}`);
488
- continue;
489
- }
490
- const metadataWithFormat = {
491
- ...metadata,
492
- format: "text",
493
- };
494
- toolMap.set(metadata.name, {
495
- path: companionPath,
496
- metadata: metadataWithFormat,
497
- });
498
- logger.info(`Loaded ${isProject ? "project" : "user"} tool: ${metadata.name} (from .tool file)`);
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;
499
494
  }
500
- catch (e) {
501
- logger.error(`Error reading .tool file ${toolFile}: ${e}`);
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;
502
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}`);
503
513
  }
504
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
+ }
505
524
  catch (e) {
506
525
  logger.error(`Error reading dir ${dir}: ${e}`);
507
526
  }
@@ -147,99 +147,6 @@ export declare function initTools({ workspace, }: {
147
147
  lineCount: number | null;
148
148
  }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
149
149
  };
150
- readonly Glob: {
151
- toolDef: {
152
- description: string;
153
- inputSchema: import("zod").ZodObject<{
154
- patterns: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodArray<import("zod").ZodString>]>>>;
155
- path: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodString>>;
156
- gitignore: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
157
- recursive: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
158
- expandDirectories: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
159
- ignoreFiles: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodUnion<readonly [import("zod").ZodString, import("zod").ZodArray<import("zod").ZodString>]>>>>;
160
- cwd: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedString<unknown>>>>;
161
- maxResults: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
162
- }, import("zod/v4/core").$strip>;
163
- };
164
- display({ patterns, path }: {
165
- patterns: string | string[];
166
- path: string;
167
- gitignore: boolean | null;
168
- recursive: boolean | null;
169
- expandDirectories: boolean | null;
170
- ignoreFiles: string | string[] | null;
171
- cwd: string | null;
172
- maxResults: number | null;
173
- }): string;
174
- execute({ patterns, path, gitignore, recursive, expandDirectories, ignoreFiles, cwd, maxResults, }: {
175
- patterns: string | string[];
176
- path: string;
177
- gitignore: boolean | null;
178
- recursive: boolean | null;
179
- expandDirectories: boolean | null;
180
- ignoreFiles: string | string[] | null;
181
- cwd: string | null;
182
- maxResults: number | null;
183
- }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
184
- };
185
- readonly Grep: {
186
- toolDef: {
187
- description: string;
188
- inputSchema: import("zod").ZodObject<{
189
- pattern: import("zod").ZodString;
190
- path: import("zod").ZodString;
191
- recursive: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
192
- ignoreCase: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
193
- filePattern: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedString<unknown>>>>;
194
- contextLines: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
195
- searchIgnored: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
196
- literal: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
197
- maxResults: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
198
- }, import("zod/v4/core").$strip>;
199
- };
200
- display({ pattern, path, filePattern, recursive, ignoreCase, contextLines, }: {
201
- pattern: string;
202
- path: string;
203
- recursive: boolean | null;
204
- ignoreCase: boolean | null;
205
- filePattern: string | null;
206
- contextLines: number | null;
207
- searchIgnored: boolean | null;
208
- literal: boolean | null;
209
- maxResults: number | null;
210
- }): string;
211
- execute({ pattern, path, recursive, ignoreCase, filePattern, contextLines, searchIgnored, literal, maxResults, }: {
212
- pattern: string;
213
- path: string;
214
- recursive: boolean | null;
215
- ignoreCase: boolean | null;
216
- filePattern: string | null;
217
- contextLines: number | null;
218
- searchIgnored: boolean | null;
219
- literal: boolean | null;
220
- maxResults: number | null;
221
- }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
222
- };
223
- readonly DirectoryTree: {
224
- toolDef: {
225
- description: string;
226
- inputSchema: import("zod").ZodObject<{
227
- path: import("zod").ZodString;
228
- maxResults: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
229
- maxDepth: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
230
- }, import("zod/v4/core").$strip>;
231
- };
232
- display({ path, maxDepth, maxResults }: {
233
- path: string;
234
- maxResults: number | null;
235
- maxDepth: number | null;
236
- }): string;
237
- execute({ path, maxResults, maxDepth }: {
238
- path: string;
239
- maxResults: number | null;
240
- maxDepth: number | null;
241
- }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
242
- };
243
150
  readonly Think: {
244
151
  toolDef: {
245
152
  description: string;
@@ -252,23 +159,6 @@ export declare function initTools({ workspace, }: {
252
159
  thought: import("zod").ZodString;
253
160
  }, import("zod/v4/core").$strip>>, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
254
161
  };
255
- readonly LS: {
256
- toolDef: {
257
- description: string;
258
- inputSchema: import("zod").ZodObject<{
259
- path: import("zod").ZodOptional<import("zod").ZodString>;
260
- limit: import("zod").ZodDefault<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
261
- }, import("zod/v4/core").$strip>;
262
- };
263
- display({ path: providedPath, limit }: {
264
- limit: number | null;
265
- path?: string | undefined;
266
- }): string;
267
- execute({ path: providedPath, limit }: {
268
- limit: number | null;
269
- path?: string | undefined;
270
- }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
271
- };
272
162
  readonly Skill: {
273
163
  toolDef: {
274
164
  description: string;
@@ -286,26 +176,6 @@ export declare function initTools({ workspace, }: {
286
176
  args?: string | undefined;
287
177
  }, { abortSignal }: import("./types.ts").ToolExecutionOptions): Promise<string>;
288
178
  };
289
- readonly Agent: {
290
- toolDef: {
291
- description: string;
292
- inputSchema: import("zod").ZodObject<{
293
- prompt: import("zod").ZodString;
294
- type: import("zod").ZodString;
295
- timeout: import("zod").ZodOptional<import("zod").ZodNumber>;
296
- }, import("zod/v4/core").$strip>;
297
- };
298
- display: ({ prompt, type }: import("zod").infer<import("zod").ZodObject<{
299
- prompt: import("zod").ZodString;
300
- type: import("zod").ZodString;
301
- timeout: import("zod").ZodOptional<import("zod").ZodNumber>;
302
- }, import("zod/v4/core").$strip>>) => string;
303
- execute: ({ prompt, type, timeout }: import("zod").infer<import("zod").ZodObject<{
304
- prompt: import("zod").ZodString;
305
- type: import("zod").ZodString;
306
- timeout: import("zod").ZodOptional<import("zod").ZodNumber>;
307
- }, import("zod/v4/core").$strip>>, { abortSignal }: import("./types.ts").ToolExecutionOptions) => Promise<string>;
308
- };
309
179
  readonly WebSearch: {
310
180
  toolDef: {
311
181
  description: string;
@@ -340,8 +210,8 @@ export declare function initTools({ workspace, }: {
340
210
  output: import("zod").ZodDefault<import("zod").ZodEnum<{
341
211
  markdown: "markdown";
342
212
  text: "text";
343
- html: "html";
344
213
  json: "json";
214
+ html: "html";
345
215
  }>>;
346
216
  jina: import("zod").ZodOptional<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedBoolean<unknown>>>>;
347
217
  timeout: import("zod").ZodOptional<import("zod").ZodPreprocess<import("zod").ZodNullable<import("zod").ZodCoercedNumber<unknown>>>>;
@@ -350,14 +220,14 @@ export declare function initTools({ workspace, }: {
350
220
  };
351
221
  display({ url }: {
352
222
  url: string;
353
- output: "markdown" | "text" | "html" | "json";
223
+ output: "markdown" | "text" | "json" | "html";
354
224
  jina?: boolean | null | undefined;
355
225
  timeout?: number | null | undefined;
356
226
  headers?: boolean | null | undefined;
357
227
  }): string;
358
228
  execute(options: {
359
229
  url: string;
360
- output: "markdown" | "text" | "html" | "json";
230
+ output: "markdown" | "text" | "json" | "html";
361
231
  jina?: boolean | null | undefined;
362
232
  timeout?: number | null | undefined;
363
233
  headers?: boolean | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAoBxE,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,UAAU,CAAC,IAAI,MAAM,eAAe,CAAC,OAAO,SAAS,CAAC,GAAG,eAAe,CACvE,OAAO,SAAS,CACjB,CAAC,CAAC,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,UAAU,CAAC,IAAI,MAAM,eAAe,CAAC,OAAO,SAAS,CAAC,GAAG,IAAI,CAC5D,OAAO,EACP,MAAM,CACP;CACF,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAKtD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,sBAAsB,CAElE;AAED,wBAAsB,SAAS,CAAC,EAC9B,SAAS,GACV,EAAE;IACD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAYxE,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,UAAU,CAAC,IAAI,MAAM,eAAe,CAAC,OAAO,SAAS,CAAC,GAAG,eAAe,CACvE,OAAO,SAAS,CACjB,CAAC,CAAC,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,CAAC,UAAU,CAAC,IAAI,MAAM,eAAe,CAAC,OAAO,SAAS,CAAC,GAAG,IAAI,CAC5D,OAAO,EACP,MAAM,CACP;CACF,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAKtD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,sBAAsB,CAElE;AAED,wBAAsB,SAAS,CAAC,EAC9B,SAAS,GACV,EAAE;IACD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DA"}
@@ -1,14 +1,9 @@
1
1
  import { config } from "../config/index.js";
2
2
  import { ActivatedSkillsTracker } from "../skills/activated-tracker.js";
3
- import { AgentTool, createAgentTools } from "./agent.js";
4
3
  import { ApplyPatchTool, createApplyPatchTool } from "./apply-patch.js";
5
4
  import { BashTool, createBashTool } from "./bash.js";
6
- import { createDirectoryTreeTool, DirectoryTreeTool, } from "./directory-tree.js";
7
5
  import { loadDynamicTools } from "./dynamic-tool-loader.js";
8
6
  import { createEditFileTool, EditFileTool } from "./edit-file.js";
9
- import { createGlobTool, GlobTool } from "./glob.js";
10
- import { createGrepTool, GrepTool } from "./grep.js";
11
- import { createLsTool, LsTool } from "./ls.js";
12
7
  import { createReadFileTool, ReadFileTool } from "./read-file.js";
13
8
  import { createSaveFileTool, SaveFileTool } from "./save-file.js";
14
9
  import { createSkillTool, SkillTool } from "./skill.js";
@@ -28,15 +23,10 @@ export async function initTools({ workspace, }) {
28
23
  const readFileTool = await createReadFileTool({ workspace });
29
24
  const editFileTool = await createEditFileTool({ workspace });
30
25
  const saveFileTool = await createSaveFileTool({ workspace });
31
- const directoryTreeTool = await createDirectoryTreeTool({ workspace });
32
- const globTool = createGlobTool();
33
- const grepTool = createGrepTool();
34
26
  const thinkTool = createThinkTool();
35
- const lsTool = await createLsTool({ workspace });
36
27
  const projectConfig = await config.getConfig();
37
28
  const bashTool = await createBashTool({ workspace, env: projectConfig.env });
38
29
  const skillTool = await createSkillTool(activatedSkillsTracker);
39
- const agentTool = await createAgentTools({ workspace });
40
30
  const webSearchTool = await createWebSearchTool();
41
31
  const webFetchTool = await createWebFetchTool();
42
32
  const applyPatchTool = await createApplyPatchTool({ workspace });
@@ -48,13 +38,8 @@ export async function initTools({ workspace, }) {
48
38
  BashTool.name,
49
39
  SaveFileTool.name,
50
40
  ReadFileTool.name,
51
- GlobTool.name,
52
- GrepTool.name,
53
- DirectoryTreeTool.name,
54
41
  ThinkTool.name,
55
- LsTool.name,
56
42
  SkillTool.name,
57
- AgentTool.name,
58
43
  WebSearchTool.name,
59
44
  WebFetchTool.name,
60
45
  ],
@@ -71,13 +56,8 @@ export async function initTools({ workspace, }) {
71
56
  [BashTool.name]: bashTool,
72
57
  [SaveFileTool.name]: saveFileTool,
73
58
  [ReadFileTool.name]: readFileTool,
74
- [GlobTool.name]: globTool,
75
- [GrepTool.name]: grepTool,
76
- [DirectoryTreeTool.name]: directoryTreeTool,
77
59
  [ThinkTool.name]: thinkTool,
78
- [LsTool.name]: lsTool,
79
60
  [SkillTool.name]: skillTool,
80
- [AgentTool.name]: agentTool,
81
61
  [WebSearchTool.name]: webSearchTool,
82
62
  [WebFetchTool.name]: webFetchTool,
83
63
  // Add dynamic tools - they already have toolDef structure
@@ -1 +1 @@
1
- {"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../source/tools/read-file.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAsBvD,eAAO,MAAM,YAAY;;CAExB,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;iBAkBf,CAAC;AAEH,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvD,eAAO,MAAM,kBAAkB,GAAU,SAAS;IAChD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;;;;;;;;;;;;;;;0DAOyD,mBAAmB;qEAUpE,mBAAmB,mBACL,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EAsBrB,CAAC"}
1
+ {"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../source/tools/read-file.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAsBvD,eAAO,MAAM,YAAY;;CAExB,CAAC;AAEF,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;iBAkBf,CAAC;AAEH,KAAK,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvD,eAAO,MAAM,kBAAkB,GAAU,SAAS;IAChD,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;;;;;;;;;;;;;;;0DAQyD,mBAAmB;qEAUpE,mBAAmB,mBACL,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EAqBrB,CAAC"}
@@ -43,7 +43,7 @@ export const createReadFileTool = async (options) => {
43
43
  const { primaryDir } = options.workspace;
44
44
  return {
45
45
  toolDef: {
46
- description: "Read the contents of a file.",
46
+ description: "Read the contents of a file. When you need to read multiple files, ALWAYS issue multiple Read tool calls in the same assistant message rather than reading one, waiting for the result, then reading the next. The runtime executes parallel tool calls concurrently, so batching reads is several times faster than serial reads.",
47
47
  inputSchema,
48
48
  },
49
49
  display({ path: providedPath, startLine, lineCount }) {
@@ -57,8 +57,8 @@ export const createReadFileTool = async (options) => {
57
57
  const filePath = resolveReadPath(providedPath, primaryDir);
58
58
  const effectiveEncoding = encoding ?? "utf-8";
59
59
  const file = await readFileContent(filePath, effectiveEncoding, startLine, lineCount, abortSignal);
60
- validateByteLimit(file, effectiveEncoding, BYTE_LIMIT);
61
- return file;
60
+ const totalLines = file === "" ? 0 : file.split("\n").length;
61
+ return applyByteLimit(file, effectiveEncoding, BYTE_LIMIT, totalLines);
62
62
  },
63
63
  };
64
64
  };
@@ -105,15 +105,34 @@ async function readFileLines(filePath, startLine, lineCount, abortSignal) {
105
105
  }
106
106
  return lines.join("\n");
107
107
  }
108
- // Validate that file content doesn't exceed byte limit
109
- function validateByteLimit(file, encoding, maxBytes) {
110
- const byteLength = Buffer.byteLength(file, encoding);
111
- if (byteLength > maxBytes) {
112
- const fileSizeKb = (byteLength / 1024).toFixed(1);
113
- const limitKb = (maxBytes / 1024).toFixed(0);
114
- throw new Error(`File (${fileSizeKb}KB) exceeds the ${limitKb}KB read limit. To read this file, use one of these options:\n` +
115
- "• Use startLine and lineCount to read specific portions (e.g., startLine: 1, lineCount: 100)\n" +
116
- "• Use the Grep tool to search for specific content\n" +
117
- `• Use the Bash tool with 'tail' or 'head' commands`);
108
+ // Truncate content to a byte limit, safely handling multi-byte encodings.
109
+ // Returns the truncated content with a message indicating how much remains
110
+ // and the total number of lines in the original content.
111
+ function applyByteLimit(content, encoding, maxBytes, totalLines) {
112
+ const byteLength = Buffer.byteLength(content, encoding);
113
+ if (byteLength <= maxBytes) {
114
+ return content;
118
115
  }
116
+ const truncated = truncateToByteLimit(content, encoding, maxBytes);
117
+ const truncatedBytes = Buffer.byteLength(truncated, encoding);
118
+ const remainingBytes = byteLength - truncatedBytes;
119
+ const limitKb = (maxBytes / 1024).toFixed(0);
120
+ const remainingKb = (remainingBytes / 1024).toFixed(1);
121
+ const message = [
122
+ "",
123
+ `[File truncated at ${limitKb}KB limit. ${remainingKb}KB remaining. Total lines: ${totalLines.toLocaleString()}.]`,
124
+ "To read more, use the startLine and lineCount parameters, or use Bash with head/tail/sed.",
125
+ ].join("\n");
126
+ return truncated + message;
127
+ }
128
+ // Safely truncate a string to a maximum number of bytes in the given encoding.
129
+ // Uses TextDecoder for UTF-8 to avoid splitting multi-byte characters.
130
+ function truncateToByteLimit(content, encoding, maxBytes) {
131
+ if (encoding === "utf-8" || encoding === "utf8") {
132
+ const decoder = new TextDecoder("utf-8");
133
+ return decoder.decode(Buffer.from(content, encoding).subarray(0, maxBytes), {
134
+ stream: true,
135
+ });
136
+ }
137
+ return Buffer.from(content, encoding).toString(encoding, 0, maxBytes);
119
138
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../source/tools/skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAI7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,SAAS;;CAErB,CAAC;AAEF,QAAA,MAAM,WAAW;;;iBAKf,CAAC;AAEH,KAAK,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AA+BpD,wBAAsB,eAAe,CACnC,sBAAsB,EAAE,sBAAsB;;;;;;;;kCAcd,gBAAgB,GAAG,MAAM;wCAIzB,gBAAgB,mBAC3B,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;GA8DrB"}
1
+ {"version":3,"file":"skill.d.ts","sourceRoot":"","sources":["../../source/tools/skill.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAK7E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,SAAS;;CAErB,CAAC;AAEF,QAAA,MAAM,WAAW;;;iBAKf,CAAC;AAEH,KAAK,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AA2DpD,wBAAsB,eAAe,CACnC,sBAAsB,EAAE,sBAAsB;;;;;;;;kCAcd,gBAAgB,GAAG,MAAM;wCAIzB,gBAAgB,mBAC3B,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;GAgDrB"}