@soederpop/luca 0.0.2

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 (358) hide show
  1. package/CLAUDE.md +71 -0
  2. package/README.md +78 -0
  3. package/bun.lock +2928 -0
  4. package/bunfig.toml +3 -0
  5. package/commands/audit-docs.ts +740 -0
  6. package/commands/build-scaffolds.ts +154 -0
  7. package/commands/generate-api-docs.ts +114 -0
  8. package/commands/update-introspection.ts +67 -0
  9. package/docs/CLI.md +335 -0
  10. package/docs/README.md +88 -0
  11. package/docs/TABLE-OF-CONTENTS.md +157 -0
  12. package/docs/apis/clients/elevenlabs.md +84 -0
  13. package/docs/apis/clients/graph.md +56 -0
  14. package/docs/apis/clients/openai.md +69 -0
  15. package/docs/apis/clients/rest.md +41 -0
  16. package/docs/apis/clients/websocket.md +107 -0
  17. package/docs/apis/features/agi/assistant.md +471 -0
  18. package/docs/apis/features/agi/assistants-manager.md +154 -0
  19. package/docs/apis/features/agi/claude-code.md +602 -0
  20. package/docs/apis/features/agi/conversation-history.md +352 -0
  21. package/docs/apis/features/agi/conversation.md +333 -0
  22. package/docs/apis/features/agi/docs-reader.md +121 -0
  23. package/docs/apis/features/agi/openai-codex.md +318 -0
  24. package/docs/apis/features/agi/openapi.md +138 -0
  25. package/docs/apis/features/agi/semantic-search.md +387 -0
  26. package/docs/apis/features/agi/skills-library.md +216 -0
  27. package/docs/apis/features/node/container-link.md +133 -0
  28. package/docs/apis/features/node/content-db.md +313 -0
  29. package/docs/apis/features/node/disk-cache.md +379 -0
  30. package/docs/apis/features/node/dns.md +651 -0
  31. package/docs/apis/features/node/docker.md +705 -0
  32. package/docs/apis/features/node/downloader.md +81 -0
  33. package/docs/apis/features/node/esbuild.md +59 -0
  34. package/docs/apis/features/node/file-manager.md +182 -0
  35. package/docs/apis/features/node/fs.md +581 -0
  36. package/docs/apis/features/node/git.md +330 -0
  37. package/docs/apis/features/node/google-auth.md +174 -0
  38. package/docs/apis/features/node/google-calendar.md +187 -0
  39. package/docs/apis/features/node/google-docs.md +151 -0
  40. package/docs/apis/features/node/google-drive.md +225 -0
  41. package/docs/apis/features/node/google-sheets.md +179 -0
  42. package/docs/apis/features/node/grep.md +290 -0
  43. package/docs/apis/features/node/helpers.md +135 -0
  44. package/docs/apis/features/node/ink.md +334 -0
  45. package/docs/apis/features/node/ipc-socket.md +260 -0
  46. package/docs/apis/features/node/json-tree.md +86 -0
  47. package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
  48. package/docs/apis/features/node/networking.md +281 -0
  49. package/docs/apis/features/node/nlp.md +133 -0
  50. package/docs/apis/features/node/opener.md +97 -0
  51. package/docs/apis/features/node/os.md +118 -0
  52. package/docs/apis/features/node/package-finder.md +402 -0
  53. package/docs/apis/features/node/postgres.md +212 -0
  54. package/docs/apis/features/node/proc.md +430 -0
  55. package/docs/apis/features/node/process-manager.md +210 -0
  56. package/docs/apis/features/node/python.md +278 -0
  57. package/docs/apis/features/node/repl.md +88 -0
  58. package/docs/apis/features/node/runpod.md +673 -0
  59. package/docs/apis/features/node/secure-shell.md +169 -0
  60. package/docs/apis/features/node/semantic-search.md +401 -0
  61. package/docs/apis/features/node/sqlite.md +211 -0
  62. package/docs/apis/features/node/telegram.md +254 -0
  63. package/docs/apis/features/node/tts.md +118 -0
  64. package/docs/apis/features/node/ui.md +703 -0
  65. package/docs/apis/features/node/vault.md +64 -0
  66. package/docs/apis/features/node/vm.md +84 -0
  67. package/docs/apis/features/node/window-manager.md +337 -0
  68. package/docs/apis/features/node/yaml-tree.md +85 -0
  69. package/docs/apis/features/node/yaml.md +176 -0
  70. package/docs/apis/features/web/asset-loader.md +47 -0
  71. package/docs/apis/features/web/container-link.md +133 -0
  72. package/docs/apis/features/web/esbuild.md +59 -0
  73. package/docs/apis/features/web/helpers.md +135 -0
  74. package/docs/apis/features/web/network.md +30 -0
  75. package/docs/apis/features/web/speech.md +55 -0
  76. package/docs/apis/features/web/vault.md +64 -0
  77. package/docs/apis/features/web/vm.md +84 -0
  78. package/docs/apis/features/web/voice.md +67 -0
  79. package/docs/apis/servers/express.md +127 -0
  80. package/docs/apis/servers/mcp.md +213 -0
  81. package/docs/apis/servers/websocket.md +99 -0
  82. package/docs/documentation-audit.md +134 -0
  83. package/docs/examples/content-db.md +77 -0
  84. package/docs/examples/disk-cache.md +83 -0
  85. package/docs/examples/docker.md +101 -0
  86. package/docs/examples/downloader.md +70 -0
  87. package/docs/examples/esbuild.md +80 -0
  88. package/docs/examples/file-manager.md +82 -0
  89. package/docs/examples/fs.md +83 -0
  90. package/docs/examples/git.md +85 -0
  91. package/docs/examples/google-auth.md +88 -0
  92. package/docs/examples/google-calendar.md +94 -0
  93. package/docs/examples/google-docs.md +82 -0
  94. package/docs/examples/google-drive.md +96 -0
  95. package/docs/examples/google-sheets.md +95 -0
  96. package/docs/examples/grep.md +85 -0
  97. package/docs/examples/ink-blocks.md +75 -0
  98. package/docs/examples/ink-renderer.md +41 -0
  99. package/docs/examples/ink.md +103 -0
  100. package/docs/examples/ipc-socket.md +103 -0
  101. package/docs/examples/json-tree.md +91 -0
  102. package/docs/examples/launcher-app-command-listener.md +120 -0
  103. package/docs/examples/networking.md +58 -0
  104. package/docs/examples/nlp.md +91 -0
  105. package/docs/examples/opener.md +78 -0
  106. package/docs/examples/os.md +72 -0
  107. package/docs/examples/package-finder.md +89 -0
  108. package/docs/examples/port-exposer.md +89 -0
  109. package/docs/examples/postgres.md +91 -0
  110. package/docs/examples/proc.md +81 -0
  111. package/docs/examples/process-manager.md +79 -0
  112. package/docs/examples/python.md +91 -0
  113. package/docs/examples/repl.md +93 -0
  114. package/docs/examples/runpod.md +119 -0
  115. package/docs/examples/secure-shell.md +92 -0
  116. package/docs/examples/sqlite.md +86 -0
  117. package/docs/examples/telegram.md +77 -0
  118. package/docs/examples/tts.md +86 -0
  119. package/docs/examples/ui.md +80 -0
  120. package/docs/examples/vault.md +70 -0
  121. package/docs/examples/vm.md +86 -0
  122. package/docs/examples/window-manager.md +125 -0
  123. package/docs/examples/yaml-tree.md +93 -0
  124. package/docs/examples/yaml.md +104 -0
  125. package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
  126. package/docs/ideas/container-use-api.md +9 -0
  127. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  128. package/docs/ideas/feature-stacks.md +22 -0
  129. package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
  130. package/docs/ideas/mcp-design.md +9 -0
  131. package/docs/ideas/web-container-debugging-feature.md +13 -0
  132. package/docs/introspection-audit.md +49 -0
  133. package/docs/introspection.md +154 -0
  134. package/docs/mcp/readme.md +162 -0
  135. package/docs/models.ts +38 -0
  136. package/docs/philosophy.md +85 -0
  137. package/docs/principles.md +7 -0
  138. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  139. package/docs/prompts/mcp-test-easy-command.md +27 -0
  140. package/docs/reports/assistant-bugs.md +38 -0
  141. package/docs/reports/attach-pattern-usage.md +18 -0
  142. package/docs/reports/code-audit-results.md +391 -0
  143. package/docs/reports/introspection-audit-tasks.md +378 -0
  144. package/docs/reports/luca-mcp-improvements.md +128 -0
  145. package/docs/scaffolds/client.md +140 -0
  146. package/docs/scaffolds/command.md +106 -0
  147. package/docs/scaffolds/endpoint.md +176 -0
  148. package/docs/scaffolds/feature.md +148 -0
  149. package/docs/scaffolds/server.md +187 -0
  150. package/docs/tasks/web-container-helper-discovery.md +71 -0
  151. package/docs/todos.md +1 -0
  152. package/docs/tutorials/01-getting-started.md +106 -0
  153. package/docs/tutorials/02-container.md +210 -0
  154. package/docs/tutorials/03-scripts.md +194 -0
  155. package/docs/tutorials/04-features-overview.md +196 -0
  156. package/docs/tutorials/05-state-and-events.md +171 -0
  157. package/docs/tutorials/06-servers.md +157 -0
  158. package/docs/tutorials/07-endpoints.md +198 -0
  159. package/docs/tutorials/08-commands.md +171 -0
  160. package/docs/tutorials/09-clients.md +162 -0
  161. package/docs/tutorials/10-creating-features.md +198 -0
  162. package/docs/tutorials/11-contentbase.md +191 -0
  163. package/docs/tutorials/12-assistants.md +215 -0
  164. package/docs/tutorials/13-introspection.md +147 -0
  165. package/docs/tutorials/14-type-system.md +174 -0
  166. package/docs/tutorials/15-project-patterns.md +222 -0
  167. package/docs/tutorials/16-google-features.md +534 -0
  168. package/docs/tutorials/17-tui-blocks.md +530 -0
  169. package/docs/tutorials/18-semantic-search.md +334 -0
  170. package/index.ts +1 -0
  171. package/luca.console.ts +9 -0
  172. package/main.py +6 -0
  173. package/package.json +154 -0
  174. package/pyproject.toml +7 -0
  175. package/scripts/animations/chrome-glitch.ts +55 -0
  176. package/scripts/animations/index.ts +16 -0
  177. package/scripts/animations/neon-pulse.ts +64 -0
  178. package/scripts/animations/types.ts +6 -0
  179. package/scripts/build-web.ts +28 -0
  180. package/scripts/examples/ask-luca-expert.ts +42 -0
  181. package/scripts/examples/assistant-questions.ts +12 -0
  182. package/scripts/examples/excalidraw-expert.ts +75 -0
  183. package/scripts/examples/expert-chat.ts +0 -0
  184. package/scripts/examples/file-manager.ts +14 -0
  185. package/scripts/examples/ideas.ts +12 -0
  186. package/scripts/examples/interactive-chat.ts +20 -0
  187. package/scripts/examples/openai-tool-calls.ts +113 -0
  188. package/scripts/examples/opening-a-web-browser.ts +5 -0
  189. package/scripts/examples/telegram-bot.ts +79 -0
  190. package/scripts/examples/telegram-ink-ui.ts +302 -0
  191. package/scripts/examples/using-assistant-with-mcp.ts +560 -0
  192. package/scripts/examples/using-claude-code.ts +10 -0
  193. package/scripts/examples/using-contentdb.ts +35 -0
  194. package/scripts/examples/using-conversations.ts +35 -0
  195. package/scripts/examples/using-disk-cache.ts +10 -0
  196. package/scripts/examples/using-docker-shell.ts +75 -0
  197. package/scripts/examples/using-elevenlabs.ts +25 -0
  198. package/scripts/examples/using-google-calendar.ts +57 -0
  199. package/scripts/examples/using-google-docs.ts +74 -0
  200. package/scripts/examples/using-google-drive.ts +74 -0
  201. package/scripts/examples/using-google-sheets.ts +89 -0
  202. package/scripts/examples/using-nlp.ts +55 -0
  203. package/scripts/examples/using-ollama.ts +10 -0
  204. package/scripts/examples/using-openai-codex.ts +23 -0
  205. package/scripts/examples/using-postgres.ts +55 -0
  206. package/scripts/examples/using-runpod.ts +32 -0
  207. package/scripts/examples/using-tts.ts +40 -0
  208. package/scripts/examples/vm-loading-esm-modules.ts +16 -0
  209. package/scripts/scaffold.ts +391 -0
  210. package/scripts/scratch.ts +15 -0
  211. package/scripts/test-command-listener.ts +123 -0
  212. package/scripts/test-window-manager-lifecycle.ts +86 -0
  213. package/scripts/test-window-manager.ts +43 -0
  214. package/scripts/update-introspection-data.ts +58 -0
  215. package/src/agi/README.md +14 -0
  216. package/src/agi/container.server.ts +114 -0
  217. package/src/agi/endpoints/ask.ts +60 -0
  218. package/src/agi/endpoints/conversations/[id].ts +45 -0
  219. package/src/agi/endpoints/conversations.ts +31 -0
  220. package/src/agi/endpoints/experts.ts +37 -0
  221. package/src/agi/features/assistant.ts +767 -0
  222. package/src/agi/features/assistants-manager.ts +260 -0
  223. package/src/agi/features/claude-code.ts +1111 -0
  224. package/src/agi/features/conversation-history.ts +497 -0
  225. package/src/agi/features/conversation.ts +799 -0
  226. package/src/agi/features/openai-codex.ts +631 -0
  227. package/src/agi/features/openapi.ts +438 -0
  228. package/src/agi/features/skills-library.ts +425 -0
  229. package/src/agi/index.ts +6 -0
  230. package/src/agi/lib/token-counter.ts +122 -0
  231. package/src/browser.ts +25 -0
  232. package/src/bus.ts +100 -0
  233. package/src/cli/cli.ts +70 -0
  234. package/src/client.ts +461 -0
  235. package/src/clients/civitai/index.ts +541 -0
  236. package/src/clients/client-template.ts +41 -0
  237. package/src/clients/comfyui/index.ts +597 -0
  238. package/src/clients/elevenlabs/index.ts +291 -0
  239. package/src/clients/openai/index.ts +451 -0
  240. package/src/clients/supabase/index.ts +366 -0
  241. package/src/command.ts +164 -0
  242. package/src/commands/chat.ts +182 -0
  243. package/src/commands/console.ts +192 -0
  244. package/src/commands/describe.ts +433 -0
  245. package/src/commands/eval.ts +116 -0
  246. package/src/commands/help.ts +214 -0
  247. package/src/commands/index.ts +14 -0
  248. package/src/commands/mcp.ts +64 -0
  249. package/src/commands/prompt.ts +807 -0
  250. package/src/commands/run.ts +257 -0
  251. package/src/commands/sandbox-mcp.ts +439 -0
  252. package/src/commands/scaffold.ts +79 -0
  253. package/src/commands/serve.ts +172 -0
  254. package/src/container.ts +781 -0
  255. package/src/endpoint.ts +340 -0
  256. package/src/feature.ts +75 -0
  257. package/src/hash-object.ts +97 -0
  258. package/src/helper.ts +543 -0
  259. package/src/introspection/generated.agi.ts +23388 -0
  260. package/src/introspection/generated.node.ts +18899 -0
  261. package/src/introspection/generated.web.ts +2021 -0
  262. package/src/introspection/index.ts +256 -0
  263. package/src/introspection/scan.ts +912 -0
  264. package/src/node/container.ts +354 -0
  265. package/src/node/feature.ts +13 -0
  266. package/src/node/features/container-link.ts +558 -0
  267. package/src/node/features/content-db.ts +475 -0
  268. package/src/node/features/disk-cache.ts +382 -0
  269. package/src/node/features/dns.ts +655 -0
  270. package/src/node/features/docker.ts +912 -0
  271. package/src/node/features/downloader.ts +92 -0
  272. package/src/node/features/esbuild.ts +68 -0
  273. package/src/node/features/file-manager.ts +357 -0
  274. package/src/node/features/fs.ts +534 -0
  275. package/src/node/features/git.ts +492 -0
  276. package/src/node/features/google-auth.ts +502 -0
  277. package/src/node/features/google-calendar.ts +300 -0
  278. package/src/node/features/google-docs.ts +404 -0
  279. package/src/node/features/google-drive.ts +339 -0
  280. package/src/node/features/google-sheets.ts +279 -0
  281. package/src/node/features/grep.ts +406 -0
  282. package/src/node/features/helpers.ts +374 -0
  283. package/src/node/features/ink.ts +490 -0
  284. package/src/node/features/ipc-socket.ts +459 -0
  285. package/src/node/features/json-tree.ts +188 -0
  286. package/src/node/features/launcher-app-command-listener.ts +388 -0
  287. package/src/node/features/networking.ts +925 -0
  288. package/src/node/features/nlp.ts +211 -0
  289. package/src/node/features/opener.ts +166 -0
  290. package/src/node/features/os.ts +157 -0
  291. package/src/node/features/package-finder.ts +539 -0
  292. package/src/node/features/port-exposer.ts +342 -0
  293. package/src/node/features/postgres.ts +273 -0
  294. package/src/node/features/proc.ts +502 -0
  295. package/src/node/features/process-manager.ts +542 -0
  296. package/src/node/features/python.ts +444 -0
  297. package/src/node/features/repl.ts +194 -0
  298. package/src/node/features/runpod.ts +802 -0
  299. package/src/node/features/secure-shell.ts +248 -0
  300. package/src/node/features/semantic-search.ts +924 -0
  301. package/src/node/features/sqlite.ts +289 -0
  302. package/src/node/features/telegram.ts +342 -0
  303. package/src/node/features/tts.ts +184 -0
  304. package/src/node/features/ui.ts +857 -0
  305. package/src/node/features/vault.ts +164 -0
  306. package/src/node/features/vm.ts +312 -0
  307. package/src/node/features/window-manager.ts +804 -0
  308. package/src/node/features/yaml-tree.ts +149 -0
  309. package/src/node/features/yaml.ts +132 -0
  310. package/src/node.ts +70 -0
  311. package/src/react/index.ts +175 -0
  312. package/src/registry.ts +199 -0
  313. package/src/scaffolds/generated.ts +1613 -0
  314. package/src/scaffolds/template.ts +37 -0
  315. package/src/schemas/base.ts +255 -0
  316. package/src/server.ts +135 -0
  317. package/src/servers/express.ts +209 -0
  318. package/src/servers/mcp.ts +805 -0
  319. package/src/servers/socket.ts +120 -0
  320. package/src/state.ts +101 -0
  321. package/src/web/clients/socket.ts +82 -0
  322. package/src/web/container.ts +74 -0
  323. package/src/web/extension.ts +30 -0
  324. package/src/web/feature.ts +12 -0
  325. package/src/web/features/asset-loader.ts +64 -0
  326. package/src/web/features/container-link.ts +385 -0
  327. package/src/web/features/esbuild.ts +79 -0
  328. package/src/web/features/helpers.ts +267 -0
  329. package/src/web/features/network.ts +61 -0
  330. package/src/web/features/speech.ts +87 -0
  331. package/src/web/features/vault.ts +189 -0
  332. package/src/web/features/vm.ts +78 -0
  333. package/src/web/features/voice-recognition.ts +129 -0
  334. package/src/web/shims/isomorphic-vm.ts +149 -0
  335. package/test/bus.test.ts +134 -0
  336. package/test/clients-servers.test.ts +216 -0
  337. package/test/container-link.test.ts +274 -0
  338. package/test/features.test.ts +160 -0
  339. package/test/integration.test.ts +787 -0
  340. package/test/node-container.test.ts +121 -0
  341. package/test/rate-limit.test.ts +272 -0
  342. package/test/semantic-search.test.ts +550 -0
  343. package/test/state.test.ts +121 -0
  344. package/test-integration/assistant.test.ts +138 -0
  345. package/test-integration/assistants-manager.test.ts +123 -0
  346. package/test-integration/claude-code.test.ts +98 -0
  347. package/test-integration/conversation-history.test.ts +205 -0
  348. package/test-integration/conversation.test.ts +137 -0
  349. package/test-integration/elevenlabs.test.ts +55 -0
  350. package/test-integration/google-services.test.ts +80 -0
  351. package/test-integration/helpers.ts +89 -0
  352. package/test-integration/openai-codex.test.ts +93 -0
  353. package/test-integration/runpod.test.ts +58 -0
  354. package/test-integration/server-endpoints.test.ts +97 -0
  355. package/test-integration/skills-library.test.ts +157 -0
  356. package/test-integration/telegram.test.ts +46 -0
  357. package/tsconfig.json +58 -0
  358. package/uv.lock +8 -0
@@ -0,0 +1,406 @@
1
+ import { features, Feature } from '../feature.js'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
+
4
+ /** Shell-escape a string using single quotes (safe for all characters) */
5
+ function shellQuote(s: string): string {
6
+ return "'" + s.replace(/'/g, "'\\''") + "'"
7
+ }
8
+
9
+ export type GrepMatch = {
10
+ file: string
11
+ line: number
12
+ column?: number
13
+ content: string
14
+ }
15
+
16
+ type GrepOptions = {
17
+ /** Pattern to search for (string or regex) */
18
+ pattern: string
19
+ /** Directory or file to search in (defaults to container cwd) */
20
+ path?: string
21
+ /** Glob patterns to include (e.g. '*.ts') */
22
+ include?: string | string[]
23
+ /** Glob patterns to exclude (e.g. 'node_modules') */
24
+ exclude?: string | string[]
25
+ /** Case insensitive search */
26
+ ignoreCase?: boolean
27
+ /** Treat pattern as a fixed string, not regex */
28
+ fixedStrings?: boolean
29
+ /** Search recursively (default: true) */
30
+ recursive?: boolean
31
+ /** Include hidden files */
32
+ hidden?: boolean
33
+ /** Max number of results to return */
34
+ maxResults?: number
35
+ /** Number of context lines before match */
36
+ before?: number
37
+ /** Number of context lines after match */
38
+ after?: number
39
+ /** Only return filenames, not match details */
40
+ filesOnly?: boolean
41
+ /** Invert match (return lines that don't match) */
42
+ invert?: boolean
43
+ /** Match whole words only */
44
+ wordMatch?: boolean
45
+ /** Additional raw flags to pass to grep/ripgrep */
46
+ rawFlags?: string[]
47
+ }
48
+
49
+ /**
50
+ * The Grep feature provides utilities for searching file contents using ripgrep (rg) or grep.
51
+ *
52
+ * Returns structured results as arrays of `{ file, line, column, content }` objects
53
+ * with paths relative to the container cwd. Also provides convenience methods for
54
+ * common search patterns.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const grep = container.feature('grep')
59
+ *
60
+ * // Basic search
61
+ * const results = await grep.search({ pattern: 'TODO' })
62
+ * // [{ file: 'src/index.ts', line: 42, column: 5, content: '// TODO: fix this' }, ...]
63
+ *
64
+ * // Find all imports of a module
65
+ * const imports = await grep.imports('lodash')
66
+ *
67
+ * // Find function/class/variable definitions
68
+ * const defs = await grep.definitions('MyClass')
69
+ *
70
+ * // Just get filenames containing a pattern
71
+ * const files = await grep.filesContaining('API_KEY')
72
+ * ```
73
+ *
74
+ * @extends Feature
75
+ */
76
+ export class Grep extends Feature {
77
+ static override shortcut = 'features.grep' as const
78
+ static override stateSchema = FeatureStateSchema
79
+ static override optionsSchema = FeatureOptionsSchema
80
+
81
+ private _hasRipgrep: boolean | null = null
82
+
83
+ /** Whether ripgrep (rg) is available on this system */
84
+ get hasRipgrep(): boolean {
85
+ if (this._hasRipgrep !== null) return this._hasRipgrep
86
+ try {
87
+ this.container.feature('proc').exec('which rg')
88
+ this._hasRipgrep = true
89
+ } catch {
90
+ this._hasRipgrep = false
91
+ }
92
+ return this._hasRipgrep
93
+ }
94
+
95
+ /**
96
+ * Search for a pattern in files and return structured results.
97
+ *
98
+ * @param {GrepOptions} options - Search options
99
+ * @returns {Promise<GrepMatch[]>} Array of match objects with relative file paths
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * // Search for a pattern in TypeScript files
104
+ * const results = await grep.search({
105
+ * pattern: 'useState',
106
+ * include: '*.tsx',
107
+ * exclude: 'node_modules'
108
+ * })
109
+ *
110
+ * // Case insensitive search with context
111
+ * const results = await grep.search({
112
+ * pattern: 'error',
113
+ * ignoreCase: true,
114
+ * before: 2,
115
+ * after: 2
116
+ * })
117
+ * ```
118
+ */
119
+ async search(options: GrepOptions): Promise<GrepMatch[]> {
120
+ const cmd = this.buildCommand(options)
121
+ const proc = this.container.feature('proc')
122
+
123
+ try {
124
+ const output = proc.exec(cmd, {
125
+ cwd: this.container.cwd,
126
+ maxBuffer: 1024 * 1024 * 50,
127
+ })
128
+
129
+ if (!output.length) return []
130
+
131
+ return this.parseResults(output, options)
132
+ } catch {
133
+ // grep returns exit code 1 when no matches found
134
+ return []
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Find files containing a pattern. Returns just the relative file paths.
140
+ *
141
+ * @param {string} pattern - The pattern to search for
142
+ * @param {Omit<GrepOptions, 'pattern' | 'filesOnly'>} [options] - Additional search options
143
+ * @returns {Promise<string[]>} Array of relative file paths
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const files = await grep.filesContaining('TODO')
148
+ * // ['src/index.ts', 'src/utils.ts']
149
+ * ```
150
+ */
151
+ async filesContaining(pattern: string, options: Omit<GrepOptions, 'pattern' | 'filesOnly'> = {}): Promise<string[]> {
152
+ const results = await this.search({ ...options, pattern, filesOnly: true })
153
+ return results.map(r => r.file)
154
+ }
155
+
156
+ /**
157
+ * Find import/require statements for a module or path.
158
+ *
159
+ * @param {string} moduleOrPath - The module name or path to search for in imports
160
+ * @param {Omit<GrepOptions, 'pattern'>} [options] - Additional search options
161
+ * @returns {Promise<GrepMatch[]>} Array of matches
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const lodashImports = await grep.imports('lodash')
166
+ * const localImports = await grep.imports('./utils')
167
+ * ```
168
+ */
169
+ async imports(moduleOrPath: string, options: Omit<GrepOptions, 'pattern'> = {}): Promise<GrepMatch[]> {
170
+ const escaped = moduleOrPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
171
+ const pattern = `(import|require).*['"\`]${escaped}[/'"\`]?`
172
+ return this.search({ ...options, pattern })
173
+ }
174
+
175
+ /**
176
+ * Find function, class, type, or variable definitions matching a name.
177
+ *
178
+ * @param {string} name - The identifier name to search for definitions of
179
+ * @param {Omit<GrepOptions, 'pattern'>} [options] - Additional search options
180
+ * @returns {Promise<GrepMatch[]>} Array of matches
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * const defs = await grep.definitions('MyComponent')
185
+ * const classDefs = await grep.definitions('UserService')
186
+ * ```
187
+ */
188
+ async definitions(name: string, options: Omit<GrepOptions, 'pattern'> = {}): Promise<GrepMatch[]> {
189
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
190
+ const pattern = `(function|class|const|let|var|type|interface|enum|export)\\s+(async\\s+)?${escaped}\\b`
191
+ return this.search({ ...options, pattern })
192
+ }
193
+
194
+ /**
195
+ * Find TODO, FIXME, HACK, and XXX comments.
196
+ *
197
+ * @param {Omit<GrepOptions, 'pattern'>} [options] - Additional search options
198
+ * @returns {Promise<GrepMatch[]>} Array of matches
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * const todos = await grep.todos()
203
+ * const fixmes = await grep.todos({ include: '*.ts' })
204
+ * ```
205
+ */
206
+ async todos(options: Omit<GrepOptions, 'pattern'> = {}): Promise<GrepMatch[]> {
207
+ return this.search({ ...options, pattern: '(TODO|FIXME|HACK|XXX)\\b' })
208
+ }
209
+
210
+ /**
211
+ * Count the number of matches for a pattern.
212
+ *
213
+ * @param {string} pattern - The pattern to count
214
+ * @param {Omit<GrepOptions, 'pattern'>} [options] - Additional search options
215
+ * @returns {Promise<number>} Total number of matching lines
216
+ *
217
+ * @example
218
+ * ```typescript
219
+ * const count = await grep.count('console.log')
220
+ * console.log(`Found ${count} console.log statements`)
221
+ * ```
222
+ */
223
+ async count(pattern: string, options: Omit<GrepOptions, 'pattern'> = {}): Promise<number> {
224
+ const results = await this.search({ ...options, pattern })
225
+ return results.length
226
+ }
227
+
228
+ /**
229
+ * Search and replace across files. Returns the list of files that would be affected.
230
+ * Does NOT modify files — use the returned file list to do the replacement yourself.
231
+ *
232
+ * @param {string} pattern - The pattern to search for
233
+ * @param {Omit<GrepOptions, 'pattern'>} [options] - Additional search options
234
+ * @returns {Promise<{ file: string, matches: GrepMatch[] }[]>} Array of files with their matches, grouped by file
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * const affected = await grep.findForReplace('oldFunctionName')
239
+ * // [{ file: 'src/a.ts', matches: [...] }, { file: 'src/b.ts', matches: [...] }]
240
+ * ```
241
+ */
242
+ async findForReplace(pattern: string, options: Omit<GrepOptions, 'pattern'> = {}): Promise<{ file: string, matches: GrepMatch[] }[]> {
243
+ const results = await this.search({ ...options, pattern })
244
+ const grouped = new Map<string, GrepMatch[]>()
245
+
246
+ for (const match of results) {
247
+ if (!grouped.has(match.file)) grouped.set(match.file, [])
248
+ grouped.get(match.file)!.push(match)
249
+ }
250
+
251
+ return Array.from(grouped.entries()).map(([file, matches]) => ({ file, matches }))
252
+ }
253
+
254
+ /** Build the grep/rg command from options */
255
+ private buildCommand(options: GrepOptions): string {
256
+ const {
257
+ pattern,
258
+ path,
259
+ include,
260
+ exclude,
261
+ ignoreCase = false,
262
+ fixedStrings = false,
263
+ recursive = true,
264
+ hidden = false,
265
+ maxResults,
266
+ before,
267
+ after,
268
+ filesOnly = false,
269
+ invert = false,
270
+ wordMatch = false,
271
+ rawFlags = [],
272
+ } = options
273
+
274
+ const useRg = this.hasRipgrep
275
+ const flags: string[] = []
276
+
277
+ if (useRg) {
278
+ // ripgrep mode
279
+ flags.push('--no-heading', '--line-number', '--column')
280
+
281
+ if (filesOnly) flags.push('--files-with-matches')
282
+ if (ignoreCase) flags.push('--ignore-case')
283
+ if (fixedStrings) flags.push('--fixed-strings')
284
+ if (hidden) flags.push('--hidden')
285
+ if (invert) flags.push('--invert-match')
286
+ if (wordMatch) flags.push('--word-regexp')
287
+ if (maxResults) flags.push(`--max-count=${maxResults}`)
288
+ if (before) flags.push(`--before-context=${before}`)
289
+ if (after) flags.push(`--after-context=${after}`)
290
+
291
+ const includes = Array.isArray(include) ? include : (include ? [include] : [])
292
+ for (const g of includes) {
293
+ flags.push(`--glob=${shellQuote(g)}`)
294
+ }
295
+
296
+ const excludes = Array.isArray(exclude) ? exclude : (exclude ? [exclude] : [])
297
+ for (const g of excludes) {
298
+ flags.push(`--glob=${shellQuote('!' + g)}`)
299
+ }
300
+
301
+ flags.push(...rawFlags)
302
+
303
+ const searchPath = path || '.'
304
+ return `rg ${flags.join(' ')} -e ${shellQuote(pattern)} ${shellQuote(searchPath)}`
305
+ } else {
306
+ // fallback to grep — use -E for extended regex (supports ?, +, |, (), {})
307
+ flags.push('-r', '-n', '-E')
308
+
309
+ if (filesOnly) flags.push('-l')
310
+ if (ignoreCase) flags.push('-i')
311
+ if (fixedStrings) flags.push('-F')
312
+ if (invert) flags.push('-v')
313
+ if (wordMatch) flags.push('-w')
314
+ if (maxResults) flags.push(`-m ${maxResults}`)
315
+ if (before) flags.push(`-B ${before}`)
316
+ if (after) flags.push(`-A ${after}`)
317
+
318
+ const includes = Array.isArray(include) ? include : (include ? [include] : [])
319
+ for (const g of includes) {
320
+ flags.push(`--include=${shellQuote(g)}`)
321
+ }
322
+
323
+ const excludes = Array.isArray(exclude) ? exclude : (exclude ? [exclude] : [])
324
+ for (const g of excludes) {
325
+ flags.push(`--exclude-dir=${shellQuote(g)}`)
326
+ }
327
+
328
+ if (!recursive) {
329
+ const idx = flags.indexOf('-r')
330
+ if (idx !== -1) flags.splice(idx, 1)
331
+ }
332
+
333
+ flags.push(...rawFlags)
334
+
335
+ const searchPath = path || '.'
336
+ return `grep ${flags.join(' ')} -e ${shellQuote(pattern)} ${shellQuote(searchPath)}`
337
+ }
338
+ }
339
+
340
+ /** Parse raw grep/rg output into structured results */
341
+ private parseResults(output: string, options: GrepOptions): GrepMatch[] {
342
+ const cwd = this.container.cwd
343
+ const lines = output.split('\n').filter(l => l.length > 0)
344
+ const results: GrepMatch[] = []
345
+
346
+ if (options.filesOnly) {
347
+ for (const line of lines) {
348
+ const filePath = line.trim()
349
+ if (!filePath) continue
350
+ results.push({
351
+ file: this.relativize(filePath, cwd),
352
+ line: 0,
353
+ content: '',
354
+ })
355
+ }
356
+ return results
357
+ }
358
+
359
+ // Context lines (from -B/-A) are separated by -- and use - instead of :
360
+ // Skip separator lines
361
+ const useRg = this.hasRipgrep
362
+
363
+ for (const line of lines) {
364
+ if (line === '--') continue
365
+
366
+ // rg format: file:line:column:content
367
+ // grep format: file:line:content
368
+ let match: GrepMatch | null = null
369
+
370
+ if (useRg) {
371
+ const m = line.match(/^(.+?):(\d+):(\d+):(.*)$/)
372
+ if (m) {
373
+ match = {
374
+ file: this.relativize(m[1]!, cwd),
375
+ line: parseInt(m[2]!, 10),
376
+ column: parseInt(m[3]!, 10),
377
+ content: m[4]!,
378
+ }
379
+ }
380
+ } else {
381
+ const m = line.match(/^(.+?):(\d+):(.*)$/)
382
+ if (m) {
383
+ match = {
384
+ file: this.relativize(m[1]!, cwd),
385
+ line: parseInt(m[2]!, 10),
386
+ content: m[3]!,
387
+ }
388
+ }
389
+ }
390
+
391
+ if (match) results.push(match)
392
+ }
393
+
394
+ return results
395
+ }
396
+
397
+ /** Make a path relative to cwd */
398
+ private relativize(filePath: string, cwd: string): string {
399
+ // If already relative (starts with ./), just clean it
400
+ if (filePath.startsWith('./')) return filePath.slice(2)
401
+ if (filePath.startsWith('/')) return this.container.paths.relative(cwd, filePath)
402
+ return filePath
403
+ }
404
+ }
405
+
406
+ export default features.register('grep', Grep)