@travisennis/acai 0.0.12 → 0.0.14

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 (242) hide show
  1. package/README.md +5 -2
  2. package/dist/agent/index.d.ts.map +1 -1
  3. package/dist/agent/index.js +91 -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 +3 -7
  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 +6 -4
  24. package/dist/models/opencode-go-provider.d.ts.map +1 -1
  25. package/dist/models/opencode-go-provider.js +55 -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/sessions/manager.d.ts +41 -0
  35. package/dist/sessions/manager.d.ts.map +1 -1
  36. package/dist/sessions/manager.js +53 -0
  37. package/dist/sessions/summary.d.ts.map +1 -1
  38. package/dist/sessions/summary.js +18 -0
  39. package/dist/skills/index.d.ts +12 -2
  40. package/dist/skills/index.d.ts.map +1 -1
  41. package/dist/skills/index.js +123 -73
  42. package/dist/terminal/control.d.ts +1 -21
  43. package/dist/terminal/control.d.ts.map +1 -1
  44. package/dist/terminal/control.js +1 -32
  45. package/dist/terminal/formatting.d.ts +0 -33
  46. package/dist/terminal/formatting.d.ts.map +1 -1
  47. package/dist/terminal/formatting.js +0 -91
  48. package/dist/terminal/highlight/theme.d.ts +0 -37
  49. package/dist/terminal/highlight/theme.d.ts.map +1 -1
  50. package/dist/terminal/highlight/theme.js +1 -79
  51. package/dist/terminal/keys.d.ts +0 -97
  52. package/dist/terminal/keys.d.ts.map +1 -1
  53. package/dist/terminal/keys.js +0 -194
  54. package/dist/terminal/select-prompt.d.ts.map +1 -1
  55. package/dist/terminal/select-prompt.js +65 -51
  56. package/dist/terminal/style.d.ts +1 -1
  57. package/dist/terminal/style.d.ts.map +1 -1
  58. package/dist/terminal/supports-color.d.ts.map +1 -1
  59. package/dist/terminal/supports-color.js +38 -20
  60. package/dist/terminal/supports-hyperlinks.d.ts +3 -0
  61. package/dist/terminal/supports-hyperlinks.d.ts.map +1 -1
  62. package/dist/terminal/supports-hyperlinks.js +72 -31
  63. package/dist/terminal/table/layout-manager.d.ts +0 -20
  64. package/dist/terminal/table/layout-manager.d.ts.map +1 -1
  65. package/dist/terminal/table/layout-manager.js +68 -44
  66. package/dist/terminal/table/utils.d.ts +0 -1
  67. package/dist/terminal/table/utils.d.ts.map +1 -1
  68. package/dist/terminal/table/utils.js +2 -4
  69. package/dist/tools/apply-patch.d.ts +5 -3
  70. package/dist/tools/apply-patch.d.ts.map +1 -1
  71. package/dist/tools/apply-patch.js +154 -123
  72. package/dist/tools/bash.d.ts.map +1 -1
  73. package/dist/tools/bash.js +11 -8
  74. package/dist/tools/dynamic-tool-loader.d.ts +6 -1
  75. package/dist/tools/dynamic-tool-loader.d.ts.map +1 -1
  76. package/dist/tools/dynamic-tool-loader.js +154 -135
  77. package/dist/tools/index.d.ts +3 -133
  78. package/dist/tools/index.d.ts.map +1 -1
  79. package/dist/tools/index.js +0 -20
  80. package/dist/tools/read-file.d.ts.map +1 -1
  81. package/dist/tools/read-file.js +32 -13
  82. package/dist/tools/skill.d.ts.map +1 -1
  83. package/dist/tools/skill.js +20 -13
  84. package/dist/tools/web-fetch.d.ts +2 -6
  85. package/dist/tools/web-fetch.d.ts.map +1 -1
  86. package/dist/tools/web-fetch.js +59 -36
  87. package/dist/tools/web-search.d.ts +0 -4
  88. package/dist/tools/web-search.d.ts.map +1 -1
  89. package/dist/tools/web-search.js +40 -32
  90. package/dist/tui/autocomplete/utils.d.ts +0 -15
  91. package/dist/tui/autocomplete/utils.d.ts.map +1 -1
  92. package/dist/tui/autocomplete/utils.js +0 -85
  93. package/dist/tui/autocomplete.d.ts +1 -1
  94. package/dist/tui/autocomplete.d.ts.map +1 -1
  95. package/dist/tui/autocomplete.js +1 -1
  96. package/dist/tui/components/editor.d.ts +12 -0
  97. package/dist/tui/components/editor.d.ts.map +1 -1
  98. package/dist/tui/components/editor.js +181 -195
  99. package/dist/tui/components/input.d.ts +4 -0
  100. package/dist/tui/components/input.d.ts.map +1 -1
  101. package/dist/tui/components/input.js +33 -31
  102. package/dist/tui/components/markdown.d.ts +30 -0
  103. package/dist/tui/components/markdown.d.ts.map +1 -1
  104. package/dist/tui/components/markdown.js +274 -242
  105. package/dist/tui/components/modal.d.ts.map +1 -1
  106. package/dist/tui/components/modal.js +11 -11
  107. package/dist/tui/components/select-list.d.ts +5 -0
  108. package/dist/tui/components/select-list.d.ts.map +1 -1
  109. package/dist/tui/components/select-list.js +69 -98
  110. package/dist/tui/components/table.d.ts +6 -0
  111. package/dist/tui/components/table.d.ts.map +1 -1
  112. package/dist/tui/components/table.js +61 -52
  113. package/dist/tui/index.d.ts +1 -8
  114. package/dist/tui/index.d.ts.map +1 -1
  115. package/dist/tui/index.js +0 -8
  116. package/dist/tui/tui.d.ts +9 -0
  117. package/dist/tui/tui.d.ts.map +1 -1
  118. package/dist/tui/tui.js +78 -48
  119. package/dist/tui/utils.d.ts +7 -0
  120. package/dist/tui/utils.d.ts.map +1 -1
  121. package/dist/tui/utils.js +117 -53
  122. package/dist/utils/bash.d.ts.map +1 -1
  123. package/dist/utils/bash.js +156 -104
  124. package/dist/utils/command-protection.d.ts +3 -6
  125. package/dist/utils/command-protection.d.ts.map +1 -1
  126. package/dist/utils/command-protection.js +213 -100
  127. package/dist/utils/filesystem/operations.d.ts +0 -15
  128. package/dist/utils/filesystem/operations.d.ts.map +1 -1
  129. package/dist/utils/filesystem/operations.js +1 -38
  130. package/dist/utils/filesystem/security.d.ts.map +1 -1
  131. package/dist/utils/filesystem/security.js +22 -15
  132. package/dist/utils/filetype-detection.d.ts +0 -1
  133. package/dist/utils/filetype-detection.d.ts.map +1 -1
  134. package/dist/utils/filetype-detection.js +0 -12
  135. package/dist/utils/formatting.d.ts +0 -30
  136. package/dist/utils/formatting.d.ts.map +1 -1
  137. package/dist/utils/formatting.js +0 -44
  138. package/dist/utils/git.d.ts +4 -6
  139. package/dist/utils/git.d.ts.map +1 -1
  140. package/dist/utils/git.js +36 -46
  141. package/dist/utils/ignore.d.ts +1 -1
  142. package/dist/utils/ignore.d.ts.map +1 -1
  143. package/dist/utils/ignore.js +1 -1
  144. package/dist/utils/process.d.ts.map +1 -1
  145. package/dist/utils/process.js +77 -79
  146. package/dist/utils/yaml.d.ts +0 -1
  147. package/dist/utils/yaml.d.ts.map +1 -1
  148. package/dist/utils/yaml.js +80 -89
  149. package/dist/utils/zod.d.ts +0 -3
  150. package/dist/utils/zod.d.ts.map +1 -1
  151. package/dist/utils/zod.js +0 -7
  152. package/package.json +25 -24
  153. package/dist/agent/sub-agent.d.ts +0 -23
  154. package/dist/agent/sub-agent.d.ts.map +0 -1
  155. package/dist/agent/sub-agent.js +0 -109
  156. package/dist/commands/add-directory/index.d.ts +0 -3
  157. package/dist/commands/add-directory/index.d.ts.map +0 -1
  158. package/dist/commands/add-directory/index.js +0 -50
  159. package/dist/commands/add-directory/utils.d.ts +0 -3
  160. package/dist/commands/add-directory/utils.d.ts.map +0 -1
  161. package/dist/commands/add-directory/utils.js +0 -15
  162. package/dist/commands/clear/index.d.ts +0 -3
  163. package/dist/commands/clear/index.d.ts.map +0 -1
  164. package/dist/commands/clear/index.js +0 -13
  165. package/dist/commands/generate-rules/index.d.ts +0 -3
  166. package/dist/commands/generate-rules/index.d.ts.map +0 -1
  167. package/dist/commands/generate-rules/index.js +0 -206
  168. package/dist/commands/generate-rules/service.d.ts +0 -22
  169. package/dist/commands/generate-rules/service.d.ts.map +0 -1
  170. package/dist/commands/generate-rules/service.js +0 -103
  171. package/dist/commands/generate-rules/utils.d.ts +0 -5
  172. package/dist/commands/generate-rules/utils.d.ts.map +0 -1
  173. package/dist/commands/generate-rules/utils.js +0 -25
  174. package/dist/commands/handoff/index.d.ts +0 -3
  175. package/dist/commands/handoff/index.d.ts.map +0 -1
  176. package/dist/commands/handoff/index.js +0 -97
  177. package/dist/commands/handoff/utils.d.ts +0 -4
  178. package/dist/commands/handoff/utils.d.ts.map +0 -1
  179. package/dist/commands/handoff/utils.js +0 -123
  180. package/dist/commands/list-directories/index.d.ts +0 -3
  181. package/dist/commands/list-directories/index.d.ts.map +0 -1
  182. package/dist/commands/list-directories/index.js +0 -35
  183. package/dist/commands/pickup/index.d.ts +0 -3
  184. package/dist/commands/pickup/index.d.ts.map +0 -1
  185. package/dist/commands/pickup/index.js +0 -141
  186. package/dist/commands/pickup/types.d.ts +0 -6
  187. package/dist/commands/pickup/types.d.ts.map +0 -1
  188. package/dist/commands/pickup/types.js +0 -1
  189. package/dist/commands/pickup/utils.d.ts +0 -7
  190. package/dist/commands/pickup/utils.d.ts.map +0 -1
  191. package/dist/commands/pickup/utils.js +0 -56
  192. package/dist/commands/remove-directory/index.d.ts +0 -3
  193. package/dist/commands/remove-directory/index.d.ts.map +0 -1
  194. package/dist/commands/remove-directory/index.js +0 -55
  195. package/dist/commands/review/index.d.ts +0 -3
  196. package/dist/commands/review/index.d.ts.map +0 -1
  197. package/dist/commands/review/index.js +0 -12
  198. package/dist/commands/review/review-panel.d.ts +0 -3
  199. package/dist/commands/review/review-panel.d.ts.map +0 -1
  200. package/dist/commands/review/review-panel.js +0 -186
  201. package/dist/commands/review/utils.d.ts +0 -18
  202. package/dist/commands/review/utils.d.ts.map +0 -1
  203. package/dist/commands/review/utils.js +0 -146
  204. package/dist/commands/shell/index.d.ts +0 -3
  205. package/dist/commands/shell/index.d.ts.map +0 -1
  206. package/dist/commands/shell/index.js +0 -96
  207. package/dist/subagents/index.d.ts +0 -16
  208. package/dist/subagents/index.d.ts.map +0 -1
  209. package/dist/subagents/index.js +0 -231
  210. package/dist/terminal/index.d.ts +0 -9
  211. package/dist/terminal/index.d.ts.map +0 -1
  212. package/dist/terminal/index.js +0 -8
  213. package/dist/tools/agent.d.ts +0 -27
  214. package/dist/tools/agent.d.ts.map +0 -1
  215. package/dist/tools/agent.js +0 -81
  216. package/dist/tools/directory-tree.d.ts +0 -28
  217. package/dist/tools/directory-tree.d.ts.map +0 -1
  218. package/dist/tools/directory-tree.js +0 -154
  219. package/dist/tools/glob.d.ts +0 -35
  220. package/dist/tools/glob.d.ts.map +0 -1
  221. package/dist/tools/glob.js +0 -167
  222. package/dist/tools/grep.d.ts +0 -100
  223. package/dist/tools/grep.d.ts.map +0 -1
  224. package/dist/tools/grep.js +0 -608
  225. package/dist/tools/ls.d.ts +0 -26
  226. package/dist/tools/ls.d.ts.map +0 -1
  227. package/dist/tools/ls.js +0 -83
  228. package/dist/tui/components/header.d.ts +0 -21
  229. package/dist/tui/components/header.d.ts.map +0 -1
  230. package/dist/tui/components/header.js +0 -63
  231. package/dist/utils/bash/parse.d.ts +0 -19
  232. package/dist/utils/bash/parse.d.ts.map +0 -1
  233. package/dist/utils/bash/parse.js +0 -223
  234. package/dist/utils/bash/quote.d.ts +0 -6
  235. package/dist/utils/bash/quote.d.ts.map +0 -1
  236. package/dist/utils/bash/quote.js +0 -23
  237. package/dist/utils/generators.d.ts +0 -3
  238. package/dist/utils/generators.d.ts.map +0 -1
  239. package/dist/utils/generators.js +0 -25
  240. package/dist/utils/glob.d.ts +0 -52
  241. package/dist/utils/glob.d.ts.map +0 -1
  242. package/dist/utils/glob.js +0 -376
@@ -1,608 +0,0 @@
1
- import { execFile, } from "node:child_process";
2
- import { inspect } from "node:util";
3
- import { z } from "zod";
4
- import style from "../terminal/style.js";
5
- import { toDisplayPath } from "../utils/filesystem/path-display.js";
6
- import { convertNullString } from "../utils/zod.js";
7
- // default limit
8
- const DEFAULT_MAX_RESULTS = 100;
9
- export const GrepTool = {
10
- name: "Grep",
11
- };
12
- const inputSchema = z.object({
13
- pattern: z
14
- .string()
15
- .describe("The search pattern (regex by default, or fixed-string if literal=true or auto-detected as unbalanced)"),
16
- path: z.string().describe("The path to search in"),
17
- recursive: z
18
- .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
19
- .default(null)
20
- .describe("Search recursively. (default: true))"),
21
- ignoreCase: z
22
- .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
23
- .default(null)
24
- .describe("Use case-sensitive search. (default: false)"),
25
- filePattern: z
26
- .preprocess((val) => convertNullString(val), z.coerce.string().nullable())
27
- .default(null)
28
- .describe("Glob pattern to filter files (e.g., '*.ts', '**/*.test.js'). (Default: no filtering)"),
29
- contextLines: z
30
- .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
31
- .default(null)
32
- .describe("The number of context lines needed in search results. (Default: 0)"),
33
- searchIgnored: z
34
- .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
35
- .default(null)
36
- .describe("Search ignored files. (Default: false)"),
37
- literal: z
38
- .preprocess((val) => convertNullString(val), z.coerce.boolean().nullable())
39
- .default(null)
40
- .describe("Pass true for fixed-string search (-F), false for regex, (Default: auto-detects unbalanced patterns like mismatched parentheses/brackets.)"),
41
- maxResults: z
42
- .preprocess((val) => convertNullString(val), z.coerce.number().nullable())
43
- .default(null)
44
- .describe("Maximum number of matches to return. Set to 0 for no limit. (Default: 100)"),
45
- });
46
- export const createGrepTool = () => {
47
- return {
48
- toolDef: {
49
- description: "Search file contents using ripgrep.",
50
- inputSchema,
51
- },
52
- display({ pattern, path, filePattern, recursive, ignoreCase, contextLines, }) {
53
- // grok doesn't follow my instructions
54
- const safeFilePattern = filePattern === "null" || filePattern === "undefined"
55
- ? null
56
- : filePattern;
57
- // Enhanced tool-init with detailed search parameters
58
- const displayPath = toDisplayPath(path);
59
- let initMessage = `${style.cyan(inspect(pattern))} in ${style.cyan(displayPath)}`;
60
- if (safeFilePattern) {
61
- initMessage += ` ${style.dim(`(filter: ${safeFilePattern})`)}`;
62
- }
63
- if (recursive === false) {
64
- initMessage += ` ${style.dim("(non-recursive)")}`;
65
- }
66
- if (ignoreCase) {
67
- initMessage += ` ${style.dim("(case-insensitive)")}`;
68
- }
69
- if (contextLines && contextLines > 0) {
70
- initMessage += ` ${style.dim(`(with ${contextLines} context line${contextLines === 1 ? "" : "s"})`)}`;
71
- }
72
- return initMessage;
73
- },
74
- async execute({ pattern, path, recursive, ignoreCase, filePattern, contextLines, searchIgnored, literal, maxResults, }, { abortSignal }) {
75
- if (abortSignal?.aborted) {
76
- throw new Error("Grep search aborted");
77
- }
78
- // Validate path - default to cwd if not provided
79
- const effectivePath = typeof path === "string" && path.trim() !== "" ? path : process.cwd();
80
- try {
81
- // Compute likelyUnbalancedRegex once and pass through
82
- const isLikelyUnbalanced = likelyUnbalancedRegex(pattern);
83
- let effectiveLiteral = null;
84
- if (literal === true) {
85
- effectiveLiteral = true;
86
- }
87
- else if (literal === false) {
88
- effectiveLiteral = false;
89
- }
90
- else {
91
- effectiveLiteral = isLikelyUnbalanced;
92
- }
93
- const effectiveMaxResults = maxResults ?? DEFAULT_MAX_RESULTS;
94
- const safeFilePattern = filePattern === "null" || filePattern === "undefined"
95
- ? null
96
- : filePattern;
97
- const grepResult = await grepFilesStructured(pattern, effectivePath, {
98
- recursive,
99
- ignoreCase,
100
- filePattern: safeFilePattern,
101
- contextLines,
102
- searchIgnored,
103
- literal: effectiveLiteral,
104
- maxResults: effectiveMaxResults,
105
- likelyUnbalanced: isLikelyUnbalanced,
106
- }, abortSignal);
107
- return grepResult.rawOutput;
108
- }
109
- catch (error) {
110
- const errorMessage = error.message;
111
- let userFriendlyError = `Error searching for "${pattern}" in ${effectivePath}: ${errorMessage}`;
112
- if (errorMessage.includes("No such file or directory")) {
113
- userFriendlyError = `Path not found: "${effectivePath}"`;
114
- if (filePattern) {
115
- userFriendlyError += ` with file pattern "${filePattern}"`;
116
- }
117
- userFriendlyError += " - check if the path exists and is accessible";
118
- }
119
- else if (errorMessage.includes("permission denied")) {
120
- userFriendlyError = `Permission denied accessing "${effectivePath}"`;
121
- }
122
- else if (errorMessage.includes("Regex parse error")) {
123
- userFriendlyError = `Invalid search pattern "${pattern}" - try using literal=true for fixed-string search`;
124
- }
125
- throw new Error(userFriendlyError);
126
- }
127
- },
128
- };
129
- };
130
- /**
131
- * Skips past a character class [...], handling escaped characters.
132
- * Returns the new index after the character class.
133
- */
134
- function skipCharacterClass(pattern, startIndex) {
135
- let i = startIndex + 1;
136
- while (i < pattern.length && pattern[i] !== "]") {
137
- if (pattern[i] === "\\")
138
- i++;
139
- i++;
140
- }
141
- return i;
142
- }
143
- /**
144
- * Checks if a character is valid inside braces (digits 0-9 or comma).
145
- */
146
- function isValidBraceChar(c) {
147
- return (c >= "0" && c <= "9") || c === ",";
148
- }
149
- /**
150
- * Checks if empty braces have a preceding atom.
151
- * Empty braces with no preceding atom are treated as literal.
152
- */
153
- function hasPrecedingAtom(pattern, startIndex) {
154
- if (startIndex === 0)
155
- return false;
156
- const prev = pattern[startIndex - 1];
157
- return /\S/.test(prev);
158
- }
159
- /**
160
- * Parses and validates the content inside braces {..}.
161
- * Returns true if the brace content is invalid.
162
- */
163
- function isInvalidBraceContent(pattern, startIndex) {
164
- const j = startIndex + 1;
165
- // Find closing brace and check for invalid characters
166
- let hasDigits = false;
167
- let k = j;
168
- while (k < pattern.length && pattern[k] !== "}") {
169
- if (!isValidBraceChar(pattern[k])) {
170
- return true;
171
- }
172
- if (pattern[k] >= "0" && pattern[k] <= "9") {
173
- hasDigits = true;
174
- }
175
- k++;
176
- }
177
- // No closing brace found
178
- if (k >= pattern.length) {
179
- return true;
180
- }
181
- // Empty braces {} with preceding atom are invalid
182
- if (!hasDigits && hasPrecedingAtom(pattern, startIndex)) {
183
- return true;
184
- }
185
- return false;
186
- }
187
- /**
188
- * Check for invalid repetition operators (e.g., {n}, {n,}, {n,m}) outside of character classes.
189
- * Returns true if any invalid repetition operators are found.
190
- */
191
- function hasInvalidRepetition(pattern) {
192
- for (let i = 0; i < pattern.length; i++) {
193
- const ch = pattern[i];
194
- if (ch === "\\") {
195
- i++; // Skip the next character (escaped)
196
- continue;
197
- }
198
- if (ch === "[") {
199
- i = skipCharacterClass(pattern, i);
200
- continue;
201
- }
202
- // Unmatched closing brace is invalid
203
- if (ch === "}") {
204
- return true;
205
- }
206
- // Handle opening brace
207
- if (ch === "{") {
208
- if (isInvalidBraceContent(pattern, i)) {
209
- return true;
210
- }
211
- i = findClosingBrace(pattern, i);
212
- }
213
- }
214
- return false;
215
- }
216
- /**
217
- * Finds the index of the closing brace matching an opening brace.
218
- * Returns the index of the closing brace, or the last index of pattern if not found.
219
- */
220
- function findClosingBrace(pattern, startIndex) {
221
- let j = startIndex + 1;
222
- while (j < pattern.length && pattern[j] !== "}") {
223
- j++;
224
- }
225
- return j;
226
- }
227
- /**
228
- * Count bracket/paren/brace pairs in a regex pattern, excluding character classes.
229
- */
230
- function countBrackets(pattern) {
231
- const counts = {
232
- openParen: 0,
233
- closeParen: 0,
234
- openBracket: 0,
235
- closeBracket: 0,
236
- openBrace: 0,
237
- closeBrace: 0,
238
- };
239
- let escaped = false;
240
- let inCharacterClass = false;
241
- for (let i = 0; i < pattern.length; i++) {
242
- const ch = pattern[i];
243
- if (escaped) {
244
- escaped = false;
245
- continue;
246
- }
247
- if (ch === "\\") {
248
- escaped = true;
249
- continue;
250
- }
251
- // Track character class boundaries
252
- if (ch === "[" && !inCharacterClass) {
253
- inCharacterClass = true;
254
- counts.openBracket++;
255
- continue;
256
- }
257
- if (ch === "]" && inCharacterClass) {
258
- inCharacterClass = false;
259
- counts.closeBracket++;
260
- continue;
261
- }
262
- // Only count brackets/parens/braces outside of character classes
263
- if (!inCharacterClass) {
264
- switch (ch) {
265
- case "(":
266
- counts.openParen++;
267
- break;
268
- case ")":
269
- counts.closeParen++;
270
- break;
271
- case "{":
272
- counts.openBrace++;
273
- break;
274
- case "}":
275
- counts.closeBrace++;
276
- break;
277
- default:
278
- break;
279
- }
280
- }
281
- }
282
- return counts;
283
- }
284
- /**
285
- * Check if a pattern contains regex metacharacters.
286
- * Returns true if the pattern likely needs regex mode.
287
- */
288
- function containsRegexMetacharacters(pattern) {
289
- // Regex metacharacters: ^ $ . * + ? [ ] ( ) { } | \
290
- const metacharacterPattern = /[\^$.*+?[\](){}|\\]/;
291
- return metacharacterPattern.test(pattern);
292
- }
293
- export function likelyUnbalancedRegex(pattern) {
294
- // First check: unbalanced brackets/parentheses/braces = likely a typo, use literal
295
- const counts = countBrackets(pattern);
296
- // Check for unbalanced brackets, parentheses, and braces
297
- const hasUnbalancedBrackets = counts.openBracket !== counts.closeBracket;
298
- const hasUnbalancedParens = counts.openParen !== counts.closeParen;
299
- const hasUnbalancedBraces = counts.openBrace !== counts.closeBrace;
300
- // Also check for invalid repetition operators
301
- const hasInvalidRepetitionFlag = hasInvalidRepetition(pattern);
302
- // If pattern has unbalanced syntax, treat as literal (user probably meant to type literal)
303
- if (hasUnbalancedBrackets ||
304
- hasUnbalancedParens ||
305
- hasUnbalancedBraces ||
306
- hasInvalidRepetitionFlag) {
307
- return true;
308
- }
309
- // Second check: if pattern has regex metacharacters, treat as regex (return false)
310
- // This allows ripgrep to handle the pattern as a proper regex
311
- if (containsRegexMetacharacters(pattern)) {
312
- return false;
313
- }
314
- // Default: simple alphanumeric strings should use literal mode
315
- return true;
316
- }
317
- /**
318
- * Build grep command args array directly
319
- *
320
- * @param pattern - The regex pattern to search for
321
- * @param path - The path to search in
322
- * @param options - Additional options for the grep command
323
- * @returns The args array for the grep command
324
- */
325
- export function buildGrepArgs(pattern, path, options = {}) {
326
- const effectiveRecursive = options.recursive === null ? true : options.recursive;
327
- const effectiveIgnoreCase = options.ignoreCase === null ? false : options.ignoreCase;
328
- const effectiveSearchIgnored = options.searchIgnored === null ? false : options.searchIgnored;
329
- const effectiveFilePattern = options.filePattern;
330
- const effectiveContextLines = options.contextLines;
331
- // Use the pre-computed likelyUnbalanced result if available
332
- let effectiveLiteral;
333
- if (options.literal === true) {
334
- effectiveLiteral = true;
335
- }
336
- else if (options.literal === false) {
337
- effectiveLiteral = false;
338
- }
339
- else if (options.likelyUnbalanced !== undefined) {
340
- effectiveLiteral = options.likelyUnbalanced;
341
- }
342
- else {
343
- effectiveLiteral = likelyUnbalancedRegex(pattern);
344
- }
345
- const args = ["--json"];
346
- if (effectiveRecursive === false) {
347
- args.push("--max-depth=0");
348
- }
349
- if (effectiveIgnoreCase) {
350
- args.push("--ignore-case");
351
- }
352
- if (effectiveContextLines !== null && effectiveContextLines !== undefined) {
353
- args.push(`--context=${effectiveContextLines}`);
354
- }
355
- if (effectiveFilePattern !== null && effectiveFilePattern !== undefined) {
356
- args.push(`--glob=${effectiveFilePattern}`);
357
- }
358
- if (effectiveSearchIgnored) {
359
- args.push("--no-ignore");
360
- }
361
- if (effectiveLiteral) {
362
- args.push("-F");
363
- }
364
- // Use ripgrep's --max-count flag to limit matches per file for efficiency
365
- // This helps prevent any single file from dominating results
366
- if (options.maxResults !== null &&
367
- options.maxResults !== undefined &&
368
- options.maxResults > 0) {
369
- // Use a reasonable per-file limit (max 100 per file) to balance efficiency and completeness
370
- const perFileLimit = Math.min(options.maxResults, 100);
371
- args.push(`--max-count=${perFileLimit}`);
372
- }
373
- args.push(pattern);
374
- args.push(path);
375
- return args;
376
- }
377
- /**
378
- * Parse ripgrep JSON output and extract structured match information
379
- */
380
- export function parseRipgrepJsonOutput(content) {
381
- if (!content || content.trim() === "") {
382
- return [];
383
- }
384
- const parsed = [];
385
- const lines = content.trim().split("\n");
386
- for (const line of lines) {
387
- if (!line.trim()) {
388
- continue;
389
- }
390
- try {
391
- const rgResult = JSON.parse(line);
392
- // Handle different ripgrep message types
393
- if (rgResult.type === "match") {
394
- const data = rgResult.data;
395
- parsed.push({
396
- file: data.path?.text ?? data.path?.bytes?.toString(),
397
- line: data.line_number ?? 0,
398
- content: data.lines?.text ?? data.line ?? "",
399
- isMatch: true,
400
- lineNumber: data.line_number,
401
- absolutePath: data.absolute_path?.text ?? data.absolute_path?.bytes?.toString(),
402
- submatches: data.submatches?.map((sm) => ({
403
- start: sm.start,
404
- end: sm.end,
405
- text: sm.text,
406
- })),
407
- });
408
- }
409
- else if (rgResult.type === "context") {
410
- const data = rgResult.data;
411
- parsed.push({
412
- file: data.path?.text ?? data.path?.bytes?.toString(),
413
- line: data.line_number ?? 0,
414
- content: data.lines?.text ?? data.line ?? "",
415
- isMatch: false,
416
- isContext: true,
417
- lineNumber: data.line_number,
418
- absolutePath: data.absolute_path?.text ?? data.absolute_path?.bytes?.toString(),
419
- });
420
- }
421
- // Ignore other message types like "begin", "end", "summary"
422
- }
423
- catch { }
424
- }
425
- return parsed;
426
- }
427
- /**
428
- * Convert parsed JSON matches back to legacy line-number format for backwards compatibility
429
- */
430
- function matchesToLegacyFormat(matches) {
431
- const lines = [];
432
- for (const match of matches) {
433
- const lineNum = match.lineNumber ?? match.line;
434
- const file = match.file ?? match.absolutePath;
435
- if (file) {
436
- if (match.isMatch) {
437
- lines.push(`${file}:${lineNum}:${match.content}`);
438
- }
439
- else if (match.isContext) {
440
- lines.push(`${file}-${lineNum}-${match.content}`);
441
- }
442
- }
443
- else {
444
- if (match.isMatch) {
445
- lines.push(`${lineNum}:${match.content}`);
446
- }
447
- else if (match.isContext) {
448
- lines.push(`${lineNum}-${match.content}`);
449
- }
450
- }
451
- }
452
- return lines.join("\n");
453
- }
454
- /**
455
- * Count actual matches (excluding context lines)
456
- */
457
- export function countActualMatches(parsed) {
458
- return parsed.filter((match) => match.isMatch && !match.isContext).length;
459
- }
460
- /**
461
- * Count context lines
462
- */
463
- export function countContextLines(parsed) {
464
- return parsed.filter((match) => match.isContext).length;
465
- }
466
- function isContextNearKeptMatch(matches, index, indicesToKeep, contextWindow) {
467
- for (let j = index - 1; j >= Math.max(0, index - contextWindow); j--) {
468
- if (matches[j].isMatch && !matches[j].isContext && indicesToKeep.has(j)) {
469
- return true;
470
- }
471
- }
472
- for (let j = index + 1; j < Math.min(matches.length, index + contextWindow + 1); j++) {
473
- if (matches[j].isMatch && !matches[j].isContext && indicesToKeep.has(j)) {
474
- return true;
475
- }
476
- }
477
- return false;
478
- }
479
- /**
480
- * Truncate matches to a maximum number of results, preserving context lines for kept matches
481
- */
482
- export function truncateMatches(matches, maxResults) {
483
- if (!maxResults || maxResults <= 0) {
484
- return { truncated: matches, isTruncated: false };
485
- }
486
- // Find indices of actual matches (excluding context lines)
487
- const matchIndices = [];
488
- for (let i = 0; i < matches.length; i++) {
489
- if (matches[i].isMatch && !matches[i].isContext) {
490
- matchIndices.push(i);
491
- }
492
- }
493
- if (matchIndices.length <= maxResults) {
494
- return { truncated: matches, isTruncated: false };
495
- }
496
- // Get the indices of matches we want to keep
497
- const indicesToKeep = new Set();
498
- for (let i = 0; i < maxResults; i++) {
499
- indicesToKeep.add(matchIndices[i]);
500
- }
501
- // Build truncated result: include all kept matches AND their associated context lines
502
- const truncated = [];
503
- let matchesKept = 0;
504
- const contextWindow = 3; // Include up to 3 context lines around each match
505
- for (let i = 0; i < matches.length; i++) {
506
- const match = matches[i];
507
- if (match.isMatch && !match.isContext) {
508
- // This is an actual match
509
- if (matchesKept < maxResults) {
510
- truncated.push(match);
511
- matchesKept++;
512
- }
513
- else {
514
- }
515
- }
516
- else if (match.isContext) {
517
- if (isContextNearKeptMatch(matches, i, indicesToKeep, contextWindow)) {
518
- truncated.push(match);
519
- }
520
- }
521
- else {
522
- // Other types, include them
523
- truncated.push(match);
524
- }
525
- }
526
- return {
527
- truncated,
528
- isTruncated: true,
529
- };
530
- }
531
- export async function grepFilesStructured(pattern, path, options = {}, abortSignal) {
532
- try {
533
- const args = buildGrepArgs(pattern, path, options);
534
- // Use execFile for async execution with proper abort signal handling
535
- const rawOutput = await new Promise((resolve, reject) => {
536
- const child = execFile("rg", args, {
537
- encoding: "utf-8",
538
- stdio: ["pipe", "pipe", "pipe"],
539
- });
540
- let stdout = "";
541
- let stderr = "";
542
- child.stdout?.on("data", (data) => {
543
- stdout += data;
544
- });
545
- child.stderr?.on("data", (data) => {
546
- stderr += data;
547
- });
548
- child.on("close", (code) => {
549
- if (code === 0 || code === 1) {
550
- resolve(stdout);
551
- }
552
- else {
553
- reject(new Error(stderr || `ripgrep exited with code ${code}`));
554
- }
555
- });
556
- child.on("error", (err) => {
557
- reject(err);
558
- });
559
- if (abortSignal) {
560
- abortSignal.addEventListener("abort", () => {
561
- child.kill("SIGTERM");
562
- reject(new Error("Grep search aborted"));
563
- });
564
- }
565
- });
566
- // Parse JSON output from ripgrep
567
- const parsedMatches = parseRipgrepJsonOutput(rawOutput);
568
- // If JSON parsing resulted in no matches, check if it's a "no matches" case
569
- // (ripgrep --json returns empty for no matches)
570
- const hasMatches = parsedMatches.length > 0;
571
- const matchCount = countActualMatches(parsedMatches);
572
- // Use the maxResults from options (which will be set by the execute function)
573
- const maxResults = options.maxResults;
574
- const { truncated, isTruncated } = truncateMatches(parsedMatches, maxResults);
575
- const displayedCount = countActualMatches(truncated);
576
- const displayedContextCount = countContextLines(truncated);
577
- // Convert to legacy format for backwards compatibility
578
- const legacyOutput = matchesToLegacyFormat(truncated);
579
- const finalOutput = legacyOutput || "No matches found.";
580
- return {
581
- rawOutput: finalOutput,
582
- parsedMatches: truncated,
583
- matchCount,
584
- displayedCount,
585
- contextCount: displayedContextCount,
586
- hasMatches,
587
- isTruncated,
588
- };
589
- }
590
- catch (error) {
591
- const execError = error;
592
- const exitCode = execError.code;
593
- if (exitCode === "1") {
594
- return {
595
- rawOutput: "No matches found.",
596
- parsedMatches: [],
597
- matchCount: 0,
598
- contextCount: 0,
599
- hasMatches: false,
600
- };
601
- }
602
- if (exitCode === "2") {
603
- const stderrStr = execError.message;
604
- throw new Error(`Regex parse error in pattern "${pattern}": ${stderrStr}`);
605
- }
606
- throw new Error(`Error executing ripgrep: ${execError.message}`);
607
- }
608
- }
@@ -1,26 +0,0 @@
1
- import { z } from "zod";
2
- import type { WorkspaceContext } from "../index.ts";
3
- import type { ToolExecutionOptions } from "./types.ts";
4
- export declare const LsTool: {
5
- name: "LS";
6
- };
7
- declare const inputSchema: z.ZodObject<{
8
- path: z.ZodOptional<z.ZodString>;
9
- limit: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
10
- }, z.core.$strip>;
11
- type LsInputSchema = z.infer<typeof inputSchema>;
12
- export declare const createLsTool: (options: {
13
- workspace: WorkspaceContext;
14
- }) => Promise<{
15
- toolDef: {
16
- description: string;
17
- inputSchema: z.ZodObject<{
18
- path: z.ZodOptional<z.ZodString>;
19
- limit: z.ZodDefault<z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>>;
20
- }, z.core.$strip>;
21
- };
22
- display({ path: providedPath, limit }: LsInputSchema): string;
23
- execute({ path: providedPath, limit }: LsInputSchema, { abortSignal }: ToolExecutionOptions): Promise<string>;
24
- }>;
25
- export {};
26
- //# sourceMappingURL=ls.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ls.d.ts","sourceRoot":"","sources":["../../source/tools/ls.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAMpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,MAAM;;CAElB,CAAC;AAEF,QAAA,MAAM,WAAW;;;iBASf,CAAC;AAEH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAIjD,eAAO,MAAM,YAAY,GAAU,SAAS;IAC1C,SAAS,EAAE,gBAAgB,CAAC;CAC7B;;;;;;;;2CAU0C,aAAa;2CAOnB,aAAa,mBAC3B,oBAAoB,GACpC,OAAO,CAAC,MAAM,CAAC;EA6DrB,CAAC"}