luca 2.0.0 → 3.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 (532) hide show
  1. package/.github/workflows/release.yaml +170 -0
  2. package/AGENTS.md +99 -0
  3. package/CLAUDE.md +123 -0
  4. package/CNAME +1 -0
  5. package/README.md +275 -9
  6. package/RUNME.md +56 -0
  7. package/assistants/codingAssistant/ABOUT.md +5 -0
  8. package/assistants/codingAssistant/CORE.md +33 -0
  9. package/assistants/codingAssistant/hooks.ts +21 -0
  10. package/assistants/codingAssistant/tools.ts +12 -0
  11. package/assistants/inkbot/ABOUT.md +16 -0
  12. package/assistants/inkbot/CORE.md +330 -0
  13. package/assistants/inkbot/hooks.ts +6 -0
  14. package/assistants/inkbot/tools.ts +53 -0
  15. package/assistants/researcher/ABOUT.md +5 -0
  16. package/assistants/researcher/CORE.md +46 -0
  17. package/assistants/researcher/hooks.ts +16 -0
  18. package/assistants/researcher/tools.ts +237 -0
  19. package/bun.lock +2667 -0
  20. package/bunfig.toml +3 -0
  21. package/commands/audit-docs.ts +740 -0
  22. package/commands/build-bootstrap.ts +117 -0
  23. package/commands/build-python-bridge.ts +42 -0
  24. package/commands/build-scaffolds.ts +175 -0
  25. package/commands/bundle-consumer-project.ts +521 -0
  26. package/commands/generate-api-docs.ts +114 -0
  27. package/commands/inkbot.ts +874 -0
  28. package/commands/release.ts +80 -0
  29. package/commands/try-all-challenges.ts +543 -0
  30. package/commands/try-challenge.ts +100 -0
  31. package/dist/agi/container.server.d.ts +63 -0
  32. package/dist/agi/container.server.d.ts.map +1 -0
  33. package/dist/agi/endpoints/ask.d.ts +20 -0
  34. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  35. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  36. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  37. package/dist/agi/endpoints/conversations.d.ts +18 -0
  38. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  39. package/dist/agi/endpoints/experts.d.ts +8 -0
  40. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  41. package/dist/agi/feature.d.ts +9 -0
  42. package/dist/agi/feature.d.ts.map +1 -0
  43. package/dist/agi/features/assistant.d.ts +509 -0
  44. package/dist/agi/features/assistant.d.ts.map +1 -0
  45. package/dist/agi/features/assistants-manager.d.ts +236 -0
  46. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  47. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  48. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  49. package/dist/agi/features/browser-use.d.ts +479 -0
  50. package/dist/agi/features/browser-use.d.ts.map +1 -0
  51. package/dist/agi/features/claude-code.d.ts +824 -0
  52. package/dist/agi/features/claude-code.d.ts.map +1 -0
  53. package/dist/agi/features/conversation-history.d.ts +245 -0
  54. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  55. package/dist/agi/features/conversation.d.ts +464 -0
  56. package/dist/agi/features/conversation.d.ts.map +1 -0
  57. package/dist/agi/features/docs-reader.d.ts +72 -0
  58. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  59. package/dist/agi/features/file-tools.d.ts +110 -0
  60. package/dist/agi/features/file-tools.d.ts.map +1 -0
  61. package/dist/agi/features/luca-coder.d.ts +323 -0
  62. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  63. package/dist/agi/features/openai-codex.d.ts +381 -0
  64. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  65. package/dist/agi/features/openapi.d.ts +200 -0
  66. package/dist/agi/features/openapi.d.ts.map +1 -0
  67. package/dist/agi/features/skills-library.d.ts +167 -0
  68. package/dist/agi/features/skills-library.d.ts.map +1 -0
  69. package/dist/agi/index.d.ts +5 -0
  70. package/dist/agi/index.d.ts.map +1 -0
  71. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  72. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  73. package/dist/agi/lib/token-counter.d.ts +13 -0
  74. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  75. package/dist/bootstrap/generated.d.ts +5 -0
  76. package/dist/bootstrap/generated.d.ts.map +1 -0
  77. package/dist/browser.d.ts +12 -0
  78. package/dist/browser.d.ts.map +1 -0
  79. package/dist/bus.d.ts +29 -0
  80. package/dist/bus.d.ts.map +1 -0
  81. package/dist/cli/build-info.d.ts +4 -0
  82. package/dist/cli/build-info.d.ts.map +1 -0
  83. package/dist/cli/cli.d.ts +3 -12
  84. package/dist/cli/cli.d.ts.map +1 -0
  85. package/dist/client.d.ts +60 -0
  86. package/dist/client.d.ts.map +1 -0
  87. package/dist/clients/civitai/index.d.ts +472 -0
  88. package/dist/clients/civitai/index.d.ts.map +1 -0
  89. package/dist/clients/client-template.d.ts +30 -0
  90. package/dist/clients/client-template.d.ts.map +1 -0
  91. package/dist/clients/comfyui/index.d.ts +281 -0
  92. package/dist/clients/comfyui/index.d.ts.map +1 -0
  93. package/dist/clients/elevenlabs/index.d.ts +197 -0
  94. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  95. package/dist/clients/graph.d.ts +64 -0
  96. package/dist/clients/graph.d.ts.map +1 -0
  97. package/dist/clients/openai/index.d.ts +247 -0
  98. package/dist/clients/openai/index.d.ts.map +1 -0
  99. package/dist/clients/rest.d.ts +92 -0
  100. package/dist/clients/rest.d.ts.map +1 -0
  101. package/dist/clients/supabase/index.d.ts +176 -0
  102. package/dist/clients/supabase/index.d.ts.map +1 -0
  103. package/dist/clients/websocket.d.ts +127 -0
  104. package/dist/clients/websocket.d.ts.map +1 -0
  105. package/dist/command.d.ts +163 -0
  106. package/dist/command.d.ts.map +1 -0
  107. package/dist/commands/bootstrap.d.ts +20 -0
  108. package/dist/commands/bootstrap.d.ts.map +1 -0
  109. package/dist/commands/chat.d.ts +37 -0
  110. package/dist/commands/chat.d.ts.map +1 -0
  111. package/dist/commands/code.d.ts +28 -0
  112. package/dist/commands/code.d.ts.map +1 -0
  113. package/dist/commands/console.d.ts +22 -0
  114. package/dist/commands/console.d.ts.map +1 -0
  115. package/dist/commands/describe.d.ts +50 -0
  116. package/dist/commands/describe.d.ts.map +1 -0
  117. package/dist/commands/eval.d.ts +23 -0
  118. package/dist/commands/eval.d.ts.map +1 -0
  119. package/dist/commands/help.d.ts +25 -0
  120. package/dist/commands/help.d.ts.map +1 -0
  121. package/dist/commands/index.d.ts +18 -0
  122. package/dist/commands/index.d.ts.map +1 -0
  123. package/dist/commands/introspect.d.ts +24 -0
  124. package/dist/commands/introspect.d.ts.map +1 -0
  125. package/dist/commands/mcp.d.ts +35 -0
  126. package/dist/commands/mcp.d.ts.map +1 -0
  127. package/dist/commands/prompt.d.ts +38 -0
  128. package/dist/commands/prompt.d.ts.map +1 -0
  129. package/dist/commands/run.d.ts +24 -0
  130. package/dist/commands/run.d.ts.map +1 -0
  131. package/dist/commands/sandbox-mcp.d.ts +34 -0
  132. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  133. package/dist/commands/save-api-docs.d.ts +21 -0
  134. package/dist/commands/save-api-docs.d.ts.map +1 -0
  135. package/dist/commands/scaffold.d.ts +24 -0
  136. package/dist/commands/scaffold.d.ts.map +1 -0
  137. package/dist/commands/select.d.ts +22 -0
  138. package/dist/commands/select.d.ts.map +1 -0
  139. package/dist/commands/serve.d.ts +29 -0
  140. package/dist/commands/serve.d.ts.map +1 -0
  141. package/dist/container-describer.d.ts +144 -0
  142. package/dist/container-describer.d.ts.map +1 -0
  143. package/dist/container.d.ts +451 -0
  144. package/dist/container.d.ts.map +1 -0
  145. package/dist/endpoint.d.ts +113 -0
  146. package/dist/endpoint.d.ts.map +1 -0
  147. package/dist/feature.d.ts +47 -0
  148. package/dist/feature.d.ts.map +1 -0
  149. package/dist/graft.d.ts +29 -0
  150. package/dist/graft.d.ts.map +1 -0
  151. package/dist/hash-object.d.ts +8 -0
  152. package/dist/hash-object.d.ts.map +1 -0
  153. package/dist/helper.d.ts +209 -0
  154. package/dist/helper.d.ts.map +1 -0
  155. package/dist/introspection/generated.node.d.ts +44623 -0
  156. package/dist/introspection/generated.node.d.ts.map +1 -0
  157. package/dist/introspection/generated.web.d.ts +1412 -0
  158. package/dist/introspection/generated.web.d.ts.map +1 -0
  159. package/dist/introspection/index.d.ts +156 -0
  160. package/dist/introspection/index.d.ts.map +1 -0
  161. package/dist/introspection/scan.d.ts +147 -0
  162. package/dist/introspection/scan.d.ts.map +1 -0
  163. package/dist/node/container.d.ts +256 -0
  164. package/dist/node/container.d.ts.map +1 -0
  165. package/dist/node/feature.d.ts +9 -0
  166. package/dist/node/feature.d.ts.map +1 -0
  167. package/dist/node/features/container-link.d.ts +213 -0
  168. package/dist/node/features/container-link.d.ts.map +1 -0
  169. package/dist/node/features/content-db.d.ts +354 -0
  170. package/dist/node/features/content-db.d.ts.map +1 -0
  171. package/dist/node/features/disk-cache.d.ts +236 -0
  172. package/dist/node/features/disk-cache.d.ts.map +1 -0
  173. package/dist/node/features/dns.d.ts +511 -0
  174. package/dist/node/features/dns.d.ts.map +1 -0
  175. package/dist/node/features/docker.d.ts +485 -0
  176. package/dist/node/features/docker.d.ts.map +1 -0
  177. package/dist/node/features/downloader.d.ts +73 -0
  178. package/dist/node/features/downloader.d.ts.map +1 -0
  179. package/dist/node/features/figlet-fonts.d.ts +4 -0
  180. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  181. package/dist/node/features/file-manager.d.ts +177 -0
  182. package/dist/node/features/file-manager.d.ts.map +1 -0
  183. package/dist/node/features/fs.d.ts +635 -0
  184. package/dist/node/features/fs.d.ts.map +1 -0
  185. package/dist/node/features/git.d.ts +329 -0
  186. package/dist/node/features/git.d.ts.map +1 -0
  187. package/dist/node/features/google-auth.d.ts +200 -0
  188. package/dist/node/features/google-auth.d.ts.map +1 -0
  189. package/dist/node/features/google-calendar.d.ts +194 -0
  190. package/dist/node/features/google-calendar.d.ts.map +1 -0
  191. package/dist/node/features/google-docs.d.ts +138 -0
  192. package/dist/node/features/google-docs.d.ts.map +1 -0
  193. package/dist/node/features/google-drive.d.ts +202 -0
  194. package/dist/node/features/google-drive.d.ts.map +1 -0
  195. package/dist/node/features/google-mail.d.ts +221 -0
  196. package/dist/node/features/google-mail.d.ts.map +1 -0
  197. package/dist/node/features/google-sheets.d.ts +157 -0
  198. package/dist/node/features/google-sheets.d.ts.map +1 -0
  199. package/dist/node/features/grep.d.ts +207 -0
  200. package/dist/node/features/grep.d.ts.map +1 -0
  201. package/dist/node/features/helpers.d.ts +236 -0
  202. package/dist/node/features/helpers.d.ts.map +1 -0
  203. package/dist/node/features/ink.d.ts +332 -0
  204. package/dist/node/features/ink.d.ts.map +1 -0
  205. package/dist/node/features/ipc-socket.d.ts +298 -0
  206. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  207. package/dist/node/features/json-tree.d.ts +140 -0
  208. package/dist/node/features/json-tree.d.ts.map +1 -0
  209. package/dist/node/features/networking.d.ts +373 -0
  210. package/dist/node/features/networking.d.ts.map +1 -0
  211. package/dist/node/features/nlp.d.ts +125 -0
  212. package/dist/node/features/nlp.d.ts.map +1 -0
  213. package/dist/node/features/opener.d.ts +93 -0
  214. package/dist/node/features/opener.d.ts.map +1 -0
  215. package/dist/node/features/os.d.ts +168 -0
  216. package/dist/node/features/os.d.ts.map +1 -0
  217. package/dist/node/features/package-finder.d.ts +419 -0
  218. package/dist/node/features/package-finder.d.ts.map +1 -0
  219. package/dist/node/features/postgres.d.ts +173 -0
  220. package/dist/node/features/postgres.d.ts.map +1 -0
  221. package/dist/node/features/proc.d.ts +285 -0
  222. package/dist/node/features/proc.d.ts.map +1 -0
  223. package/dist/node/features/process-manager.d.ts +427 -0
  224. package/dist/node/features/process-manager.d.ts.map +1 -0
  225. package/dist/node/features/python.d.ts +477 -0
  226. package/dist/node/features/python.d.ts.map +1 -0
  227. package/dist/node/features/redis.d.ts +247 -0
  228. package/dist/node/features/redis.d.ts.map +1 -0
  229. package/dist/node/features/repl.d.ts +84 -0
  230. package/dist/node/features/repl.d.ts.map +1 -0
  231. package/dist/node/features/runpod.d.ts +527 -0
  232. package/dist/node/features/runpod.d.ts.map +1 -0
  233. package/dist/node/features/secure-shell.d.ts +145 -0
  234. package/dist/node/features/secure-shell.d.ts.map +1 -0
  235. package/dist/node/features/semantic-search.d.ts +207 -0
  236. package/dist/node/features/semantic-search.d.ts.map +1 -0
  237. package/dist/node/features/sqlite.d.ts +180 -0
  238. package/dist/node/features/sqlite.d.ts.map +1 -0
  239. package/dist/node/features/telegram.d.ts +173 -0
  240. package/dist/node/features/telegram.d.ts.map +1 -0
  241. package/dist/node/features/transpiler.d.ts +51 -0
  242. package/dist/node/features/transpiler.d.ts.map +1 -0
  243. package/dist/node/features/tts.d.ts +108 -0
  244. package/dist/node/features/tts.d.ts.map +1 -0
  245. package/dist/node/features/ui.d.ts +562 -0
  246. package/dist/node/features/ui.d.ts.map +1 -0
  247. package/dist/node/features/vault.d.ts +90 -0
  248. package/dist/node/features/vault.d.ts.map +1 -0
  249. package/dist/node/features/vm.d.ts +285 -0
  250. package/dist/node/features/vm.d.ts.map +1 -0
  251. package/dist/node/features/yaml-tree.d.ts +118 -0
  252. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  253. package/dist/node/features/yaml.d.ts +127 -0
  254. package/dist/node/features/yaml.d.ts.map +1 -0
  255. package/dist/node.d.ts +67 -0
  256. package/dist/node.d.ts.map +1 -0
  257. package/dist/python/generated.d.ts +2 -0
  258. package/dist/python/generated.d.ts.map +1 -0
  259. package/dist/react/index.d.ts +36 -0
  260. package/dist/react/index.d.ts.map +1 -0
  261. package/dist/registry.d.ts +97 -0
  262. package/dist/registry.d.ts.map +1 -0
  263. package/dist/scaffolds/generated.d.ts +13 -0
  264. package/dist/scaffolds/generated.d.ts.map +1 -0
  265. package/dist/scaffolds/template.d.ts +11 -0
  266. package/dist/scaffolds/template.d.ts.map +1 -0
  267. package/dist/schemas/base.d.ts +254 -0
  268. package/dist/schemas/base.d.ts.map +1 -0
  269. package/dist/selector.d.ts +130 -0
  270. package/dist/selector.d.ts.map +1 -0
  271. package/dist/server.d.ts +89 -0
  272. package/dist/server.d.ts.map +1 -0
  273. package/dist/servers/express.d.ts +104 -0
  274. package/dist/servers/express.d.ts.map +1 -0
  275. package/dist/servers/mcp.d.ts +201 -0
  276. package/dist/servers/mcp.d.ts.map +1 -0
  277. package/dist/servers/socket.d.ts +121 -0
  278. package/dist/servers/socket.d.ts.map +1 -0
  279. package/dist/state.d.ts +24 -0
  280. package/dist/state.d.ts.map +1 -0
  281. package/dist/web/clients/socket.d.ts +37 -0
  282. package/dist/web/clients/socket.d.ts.map +1 -0
  283. package/dist/web/container.d.ts +55 -0
  284. package/dist/web/container.d.ts.map +1 -0
  285. package/dist/web/extension.d.ts +4 -0
  286. package/dist/web/extension.d.ts.map +1 -0
  287. package/dist/web/feature.d.ts +8 -0
  288. package/dist/web/feature.d.ts.map +1 -0
  289. package/dist/web/features/asset-loader.d.ts +35 -0
  290. package/dist/web/features/asset-loader.d.ts.map +1 -0
  291. package/dist/web/features/container-link.d.ts +167 -0
  292. package/dist/web/features/container-link.d.ts.map +1 -0
  293. package/dist/web/features/esbuild.d.ts +51 -0
  294. package/dist/web/features/esbuild.d.ts.map +1 -0
  295. package/dist/web/features/helpers.d.ts +140 -0
  296. package/dist/web/features/helpers.d.ts.map +1 -0
  297. package/dist/web/features/network.d.ts +69 -0
  298. package/dist/web/features/network.d.ts.map +1 -0
  299. package/dist/web/features/speech.d.ts +71 -0
  300. package/dist/web/features/speech.d.ts.map +1 -0
  301. package/dist/web/features/vault.d.ts +62 -0
  302. package/dist/web/features/vault.d.ts.map +1 -0
  303. package/dist/web/features/vm.d.ts +48 -0
  304. package/dist/web/features/vm.d.ts.map +1 -0
  305. package/dist/web/features/voice-recognition.d.ts +96 -0
  306. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  307. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  308. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  309. package/index.html +1457 -0
  310. package/index.ts +1 -0
  311. package/install.sh +84 -0
  312. package/luca.cli.ts +16 -0
  313. package/luca.console.ts +9 -0
  314. package/main.py +6 -0
  315. package/package.json +219 -58
  316. package/public/index.html +1457 -0
  317. package/public/slides-ai-native.html +902 -0
  318. package/public/slides-intro.html +974 -0
  319. package/pyproject.toml +7 -0
  320. package/scripts/build-web.ts +28 -0
  321. package/scripts/examples/ask-luca-expert.ts +42 -0
  322. package/scripts/examples/assistant-questions.ts +12 -0
  323. package/scripts/examples/excalidraw-expert.ts +75 -0
  324. package/scripts/examples/expert-chat.ts +0 -0
  325. package/scripts/examples/file-manager.ts +14 -0
  326. package/scripts/examples/ideas.ts +12 -0
  327. package/scripts/examples/interactive-chat.ts +20 -0
  328. package/scripts/examples/openai-tool-calls.ts +113 -0
  329. package/scripts/examples/opening-a-web-browser.ts +5 -0
  330. package/scripts/examples/telegram-bot.ts +79 -0
  331. package/scripts/examples/using-assistant-with-mcp.ts +555 -0
  332. package/scripts/examples/using-claude-code.ts +10 -0
  333. package/scripts/examples/using-contentdb.ts +35 -0
  334. package/scripts/examples/using-conversations.ts +35 -0
  335. package/scripts/examples/using-disk-cache.ts +10 -0
  336. package/scripts/examples/using-docker-shell.ts +75 -0
  337. package/scripts/examples/using-elevenlabs.ts +25 -0
  338. package/scripts/examples/using-google-calendar.ts +57 -0
  339. package/scripts/examples/using-google-docs.ts +74 -0
  340. package/scripts/examples/using-google-drive.ts +74 -0
  341. package/scripts/examples/using-google-sheets.ts +89 -0
  342. package/scripts/examples/using-nlp.ts +55 -0
  343. package/scripts/examples/using-ollama.ts +11 -0
  344. package/scripts/examples/using-postgres.ts +55 -0
  345. package/scripts/examples/using-runpod.ts +32 -0
  346. package/scripts/examples/using-tts.ts +40 -0
  347. package/scripts/scaffold.ts +391 -0
  348. package/scripts/scratch.ts +15 -0
  349. package/scripts/stamp-build.sh +12 -0
  350. package/scripts/test-assistant-hooks.ts +13 -0
  351. package/scripts/test-docs-reader.ts +10 -0
  352. package/scripts/test-linux-binary.sh +80 -0
  353. package/scripts/update-introspection-data.ts +58 -0
  354. package/src/agi/README.md +14 -0
  355. package/src/agi/container.server.ts +156 -0
  356. package/src/agi/feature.ts +13 -0
  357. package/src/agi/features/agent-memory.ts +694 -0
  358. package/src/agi/features/assistant.ts +1653 -0
  359. package/src/agi/features/assistants-manager.ts +534 -0
  360. package/src/agi/features/autonomous-assistant.ts +431 -0
  361. package/src/agi/features/browser-use.ts +672 -0
  362. package/src/agi/features/claude-code.ts +1584 -0
  363. package/src/agi/features/coding-tools.ts +175 -0
  364. package/src/agi/features/conversation-history.ts +672 -0
  365. package/src/agi/features/conversation.ts +1494 -0
  366. package/src/agi/features/docs-reader.ts +167 -0
  367. package/src/agi/features/file-tools.ts +340 -0
  368. package/src/agi/features/luca-coder.ts +641 -0
  369. package/src/agi/features/mcp-bridge.ts +532 -0
  370. package/src/agi/features/openai-codex.ts +651 -0
  371. package/src/agi/features/openapi.ts +445 -0
  372. package/src/agi/features/skills-library.ts +557 -0
  373. package/src/agi/index.ts +6 -0
  374. package/src/agi/lib/interceptor-chain.ts +89 -0
  375. package/src/agi/lib/token-counter.ts +202 -0
  376. package/src/bootstrap/generated.ts +9791 -0
  377. package/src/browser.ts +25 -0
  378. package/src/bus.ts +122 -0
  379. package/src/cli/build-info.ts +4 -0
  380. package/src/cli/cli.ts +355 -0
  381. package/src/client.ts +170 -0
  382. package/src/clients/civitai/index.ts +537 -0
  383. package/src/clients/client-template.ts +41 -0
  384. package/src/clients/comfyui/index.ts +604 -0
  385. package/src/clients/elevenlabs/index.ts +317 -0
  386. package/src/clients/graph.ts +87 -0
  387. package/src/clients/openai/index.ts +456 -0
  388. package/src/clients/rest.ts +207 -0
  389. package/src/clients/supabase/index.ts +357 -0
  390. package/src/clients/voicebox/index.ts +300 -0
  391. package/src/clients/websocket.ts +251 -0
  392. package/src/command.ts +506 -0
  393. package/src/commands/bootstrap.ts +244 -0
  394. package/src/commands/chat.ts +309 -0
  395. package/src/commands/code.ts +371 -0
  396. package/src/commands/console.ts +189 -0
  397. package/src/commands/describe.ts +243 -0
  398. package/src/commands/eval.ts +67 -0
  399. package/src/commands/help.ts +240 -0
  400. package/src/commands/index.ts +19 -0
  401. package/src/commands/introspect.ts +218 -0
  402. package/src/commands/mcp.ts +64 -0
  403. package/src/commands/prompt.ts +1014 -0
  404. package/src/commands/run.ts +278 -0
  405. package/src/commands/sandbox-mcp.ts +343 -0
  406. package/src/commands/save-api-docs.ts +51 -0
  407. package/src/commands/scaffold.ts +225 -0
  408. package/src/commands/select.ts +99 -0
  409. package/src/commands/serve.ts +208 -0
  410. package/src/container-describer.ts +1091 -0
  411. package/src/container.ts +1199 -0
  412. package/src/endpoint.ts +365 -0
  413. package/src/entity.ts +173 -0
  414. package/src/feature.ts +118 -0
  415. package/src/graft.ts +181 -0
  416. package/src/hash-object.ts +97 -0
  417. package/src/helper.ts +849 -0
  418. package/src/introspection/generated.agi.ts +41200 -0
  419. package/src/introspection/generated.node.ts +28773 -0
  420. package/src/introspection/generated.web.ts +2272 -0
  421. package/src/introspection/index.ts +296 -0
  422. package/src/introspection/scan.ts +1136 -0
  423. package/src/node/container.ts +409 -0
  424. package/src/node/feature.ts +13 -0
  425. package/src/node/features/container-link.ts +559 -0
  426. package/src/node/features/content-db.ts +849 -0
  427. package/src/node/features/disk-cache.ts +388 -0
  428. package/src/node/features/display-result.ts +57 -0
  429. package/src/node/features/dns.ts +669 -0
  430. package/src/node/features/docker.ts +921 -0
  431. package/src/node/features/downloader.ts +79 -0
  432. package/src/node/features/figlet-fonts.ts +600 -0
  433. package/src/node/features/file-manager.ts +535 -0
  434. package/src/node/features/fs.ts +1050 -0
  435. package/src/node/features/git.ts +592 -0
  436. package/src/node/features/google-auth.ts +504 -0
  437. package/src/node/features/google-calendar.ts +306 -0
  438. package/src/node/features/google-docs.ts +412 -0
  439. package/src/node/features/google-drive.ts +346 -0
  440. package/src/node/features/google-mail.ts +540 -0
  441. package/src/node/features/google-sheets.ts +286 -0
  442. package/src/node/features/grep.ts +427 -0
  443. package/src/node/features/helpers.ts +762 -0
  444. package/src/node/features/ink.ts +490 -0
  445. package/src/node/features/ipc-socket.ts +649 -0
  446. package/src/node/features/json-tree.ts +170 -0
  447. package/src/node/features/networking.ts +961 -0
  448. package/src/node/features/nlp.ts +212 -0
  449. package/src/node/features/opener.ts +180 -0
  450. package/src/node/features/os.ts +403 -0
  451. package/src/node/features/package-finder.ts +540 -0
  452. package/src/node/features/postgres.ts +289 -0
  453. package/src/node/features/proc.ts +503 -0
  454. package/src/node/features/process-manager.ts +844 -0
  455. package/src/node/features/python.ts +912 -0
  456. package/src/node/features/redis.ts +446 -0
  457. package/src/node/features/repl.ts +212 -0
  458. package/src/node/features/runpod.ts +811 -0
  459. package/src/node/features/secure-shell.ts +261 -0
  460. package/src/node/features/semantic-search.ts +935 -0
  461. package/src/node/features/sqlite.ts +289 -0
  462. package/src/node/features/telegram.ts +343 -0
  463. package/src/node/features/transpiler.ts +160 -0
  464. package/src/node/features/tts.ts +185 -0
  465. package/src/node/features/ui.ts +791 -0
  466. package/src/node/features/vault.ts +153 -0
  467. package/src/node/features/vm.ts +462 -0
  468. package/src/node/features/yaml-tree.ts +148 -0
  469. package/src/node/features/yaml.ts +133 -0
  470. package/src/node.ts +76 -0
  471. package/src/python/bridge.py +220 -0
  472. package/src/python/generated.ts +226 -0
  473. package/src/react/index.ts +175 -0
  474. package/src/registry.ts +210 -0
  475. package/src/scaffolds/generated.ts +1814 -0
  476. package/src/scaffolds/template.ts +46 -0
  477. package/src/schemas/base.ts +296 -0
  478. package/src/selector.ts +352 -0
  479. package/src/server.ts +229 -0
  480. package/src/servers/express.ts +283 -0
  481. package/src/servers/mcp.ts +802 -0
  482. package/src/servers/socket.ts +258 -0
  483. package/src/state.ts +101 -0
  484. package/src/web/clients/socket.ts +99 -0
  485. package/src/web/container.ts +75 -0
  486. package/src/web/extension.ts +30 -0
  487. package/src/web/feature.ts +12 -0
  488. package/src/web/features/asset-loader.ts +72 -0
  489. package/src/web/features/container-link.ts +382 -0
  490. package/src/web/features/esbuild.ts +93 -0
  491. package/src/web/features/helpers.ts +291 -0
  492. package/src/web/features/network.ts +85 -0
  493. package/src/web/features/speech.ts +104 -0
  494. package/src/web/features/vault.ts +207 -0
  495. package/src/web/features/vm.ts +85 -0
  496. package/src/web/features/voice-recognition.ts +161 -0
  497. package/src/web/shims/isomorphic-vm.ts +149 -0
  498. package/tsconfig.build.json +12 -0
  499. package/tsconfig.json +58 -0
  500. package/uv.lock +8 -0
  501. package/LICENSE +0 -21
  502. package/dist/cli/cli.js +0 -48
  503. package/dist/cli/common.d.ts +0 -2
  504. package/dist/cli/common.js +0 -6
  505. package/dist/cli/index.d.ts +0 -2
  506. package/dist/cli/index.js +0 -5
  507. package/dist/cli/run.d.ts +0 -1
  508. package/dist/cli/run.js +0 -38
  509. package/dist/core/index.d.ts +0 -4
  510. package/dist/core/index.js +0 -32
  511. package/dist/core/read.d.ts +0 -2
  512. package/dist/core/read.js +0 -29
  513. package/dist/core/request.d.ts +0 -1
  514. package/dist/core/request.js +0 -2
  515. package/dist/core/write.d.ts +0 -2
  516. package/dist/core/write.js +0 -21
  517. package/dist/index.d.ts +0 -1
  518. package/dist/index.js +0 -5
  519. package/dist/utils/common.d.ts +0 -9
  520. package/dist/utils/common.js +0 -57
  521. package/dist/utils/consts.d.ts +0 -3
  522. package/dist/utils/consts.js +0 -11
  523. package/dist/utils/dict.d.ts +0 -1
  524. package/dist/utils/dict.js +0 -7
  525. package/dist/utils/index.d.ts +0 -5
  526. package/dist/utils/index.js +0 -21
  527. package/dist/utils/log.d.ts +0 -1
  528. package/dist/utils/log.js +0 -5
  529. package/dist/utils/types.d.ts +0 -1
  530. package/dist/utils/types.js +0 -2
  531. package/dist/utils/utils.test.d.ts +0 -1
  532. package/dist/utils/utils.test.js +0 -7
@@ -0,0 +1,1014 @@
1
+ import { z } from 'zod'
2
+ import { Document } from 'contentbase'
3
+ import { commands } from '../command.js'
4
+ import { CommandOptionsSchema } from '../schemas/base.js'
5
+ import type { ContainerContext } from '../container.js'
6
+
7
+ declare module '../command.js' {
8
+ interface AvailableCommands {
9
+ prompt: ReturnType<typeof commands.registerHandler>
10
+ }
11
+ }
12
+
13
+ export const argsSchema = CommandOptionsSchema.extend({
14
+ model: z.string().optional().describe('Override the LLM model (assistant mode only)'),
15
+ 'include-frontmatter': z.boolean().default(false).describe('Keep YAML frontmatter in the prompt instead of stripping it before sending to the agent.'),
16
+ 'skip-eval': z.boolean().default(false).describe('Skip execution of fenced code blocks in the prompt file'),
17
+ 'permission-mode': z.enum(['default', 'acceptEdits', 'bypassPermissions', 'plan']).default('acceptEdits').describe('Permission mode for CLI agents (default: acceptEdits)'),
18
+ 'in-folder': z.string().optional().describe('Run the CLI agent in this directory (resolved via container.paths)'),
19
+ 'out-file': z.string().optional().describe('Save session output as a markdown file'),
20
+ 'include-output': z.boolean().default(false).describe('Include tool call outputs in the markdown (requires --out-file)'),
21
+ 'inputs-file': z.string().optional().describe('Path to a JSON or YAML file supplying input values'),
22
+ 'parallel': z.boolean().default(false).describe('Run multiple prompt files in parallel with side-by-side terminal UI'),
23
+ 'exclude-sections': z.string().optional().describe('Comma-separated list of section headings to exclude from the prompt'),
24
+ 'chrome': z.boolean().default(false).describe('Launch Claude Code with a Chrome browser tool'),
25
+ 'dry-run': z.boolean().default(false).describe('Display the resolved prompt and options without running the assistant'),
26
+ 'local': z.boolean().default(false).describe('Use local models (sets ANTHROPIC_BASE_URL and model for claudeCode, or local flag for assistants)'),
27
+ })
28
+
29
+ function normalizeTarget(raw: string): string {
30
+ const lower = raw.toLowerCase().replace(/[-_]/g, '')
31
+ if (/claude/.test(lower)) return 'claude'
32
+ if (/codex/.test(lower) || /openai/.test(lower)) return 'codex'
33
+ return raw
34
+ }
35
+
36
+ const CLI_TARGETS = new Set(['claude', 'codex'])
37
+
38
+ function formatSessionMarkdown(events: any[], includeOutput: boolean): string {
39
+ const lines: string[] = []
40
+
41
+ for (const event of events) {
42
+ if (event.type === 'assistant' || event.type === 'message') {
43
+ const role = event.message?.role ?? event.role
44
+ if (role && role !== 'assistant') continue
45
+
46
+ const content = event.message?.content ?? event.content
47
+ if (!Array.isArray(content)) continue
48
+
49
+ for (const block of content) {
50
+ if (block.type === 'text' && block.text) {
51
+ lines.push(block.text)
52
+ lines.push('')
53
+ } else if (block.type === 'tool_use') {
54
+ lines.push(`**${block.name}**`)
55
+ lines.push('```json')
56
+ lines.push(JSON.stringify(block.input, null, 2))
57
+ lines.push('```')
58
+ lines.push('')
59
+ }
60
+ }
61
+ } else if ((event.type === 'tool_result' || event.type === 'function_call_output') && includeOutput) {
62
+ const rawContent = event.type === 'function_call_output' ? event.output : event.content
63
+ const content = typeof rawContent === 'string' ? rawContent : JSON.stringify(rawContent, null, 2)
64
+ lines.push('```')
65
+ lines.push(content)
66
+ lines.push('```')
67
+ lines.push('')
68
+ }
69
+ }
70
+
71
+ return lines.join('\n')
72
+ }
73
+
74
+ interface RunStats {
75
+ collectedEvents: any[]
76
+ durationMs: number
77
+ outputTokens: number
78
+ }
79
+
80
+ interface PreparedPrompt {
81
+ resolvedPath: string
82
+ promptContent: string
83
+ filename: string
84
+ agentOptions: Record<string, any>
85
+ }
86
+
87
+ async function runClaudeOrCodex(target: 'claude' | 'codex', promptContent: string, container: any, options: z.infer<typeof argsSchema>, agentOptions: Record<string, any> = {}): Promise<RunStats> {
88
+ const ui = container.feature('ui')
89
+ const featureName = target === 'claude' ? 'claudeCode' : 'openaiCodex'
90
+ const feature = container.feature(featureName)
91
+
92
+ const available = await feature.checkAvailability()
93
+ if (!available) {
94
+ console.error(`${target} CLI is not available. Make sure it is installed and in your PATH.`)
95
+ process.exit(1)
96
+ }
97
+
98
+ let outputTokens = 0
99
+
100
+ // Stream text via deltas for real-time output
101
+ feature.on('session:delta', ({ text }: { text: string }) => {
102
+ process.stdout.write(text)
103
+ })
104
+
105
+ // Render complete messages — only tool_use blocks and final text (markdown formatted)
106
+ feature.on('session:message', ({ message }: { message: any }) => {
107
+ const role = message?.message?.role ?? message?.role
108
+ if (role && role !== 'assistant') return
109
+
110
+ const content = message?.message?.content ?? message?.content
111
+ if (!Array.isArray(content)) return
112
+
113
+ const usage = message?.message?.usage ?? message?.usage
114
+ if (usage?.output_tokens) outputTokens += usage.output_tokens
115
+
116
+ // Skip partial messages — text is already rendered via session:delta
117
+ const stopReason = message?.message?.stop_reason ?? message?.stop_reason
118
+ if (!stopReason) return
119
+
120
+ for (const block of content) {
121
+ if (block.type === 'tool_use') {
122
+ const argsStr = JSON.stringify(block.input).slice(0, 120)
123
+ process.stdout.write(ui.colors.dim(`\n ⟳ ${block.name}`) + ui.colors.dim(`(${argsStr})\n`))
124
+ }
125
+ }
126
+ })
127
+
128
+ // Collect structured events for --out-file
129
+ const collectedEvents: any[] = []
130
+ if (options['out-file']) {
131
+ feature.on('session:event', ({ event }: { event: any }) => {
132
+ // Skip partial assistant messages (streaming tokens) — only collect complete messages with stop_reason
133
+ if (event.type === 'assistant') {
134
+ if (event.message?.stop_reason) collectedEvents.push(event)
135
+ return
136
+ }
137
+ if (event.type === 'tool_result' || event.type === 'message' || event.type === 'function_call_output' || event.type === 'item.completed' || event.type === 'turn.completed') {
138
+ collectedEvents.push(event)
139
+ }
140
+ })
141
+ }
142
+
143
+ const runOptions: Record<string, any> = { streaming: true, ...agentOptions }
144
+
145
+ if (options['in-folder']) {
146
+ runOptions.cwd = container.paths.resolve(options['in-folder'])
147
+ }
148
+
149
+ if (target === 'claude') {
150
+ runOptions.permissionMode = options['permission-mode']
151
+ if (options.chrome) runOptions.chrome = true
152
+ if (options.local) runOptions.local = true
153
+ }
154
+
155
+ // CLI flags override agentOptions from frontmatter
156
+ if (options.model) runOptions.model = options.model
157
+
158
+ const startTime = Date.now()
159
+ const sessionId = await feature.start(promptContent, runOptions)
160
+ const session = await feature.waitForSession(sessionId)
161
+
162
+ if (session.status === 'error') {
163
+ console.error(session.error || 'Session failed')
164
+ process.exit(1)
165
+ }
166
+
167
+ process.stdout.write('\n')
168
+
169
+ return { collectedEvents, durationMs: Date.now() - startTime, outputTokens }
170
+ }
171
+
172
+ async function runAssistant(name: string, promptContent: string, options: z.infer<typeof argsSchema>, container: any, agentOptions: Record<string, any> = {}): Promise<RunStats> {
173
+ const ui = container.feature('ui')
174
+ const manager = container.feature('assistantsManager')
175
+ await manager.discover()
176
+
177
+ const entry = manager.get(name)
178
+ if (!entry) {
179
+ const entries = manager.list()
180
+ const available = entries.length ? entries.map((e: any) => e.name).join(', ') : '(none)'
181
+ console.error(`Assistant "${name}" not found. Available: ${available}`)
182
+ process.exit(1)
183
+ }
184
+
185
+ const createOptions: Record<string, any> = { ...agentOptions }
186
+ // CLI flags override agentOptions from frontmatter
187
+ if (options.model) createOptions.model = options.model
188
+ if (options.local) createOptions.local = true
189
+
190
+ const assistant = manager.create(name, createOptions)
191
+ let isFirstChunk = true
192
+
193
+ // Collect structured events for --out-file
194
+ const collectedEvents: any[] = []
195
+ let textBuffer = ''
196
+
197
+ assistant.on('chunk', (text: string) => {
198
+ if (isFirstChunk) {
199
+ process.stdout.write('\n')
200
+ isFirstChunk = false
201
+ }
202
+ process.stdout.write(text)
203
+ if (options['out-file']) {
204
+ textBuffer += text
205
+ }
206
+ })
207
+
208
+ assistant.on('toolCall', (toolName: string, args: any) => {
209
+ const argsStr = JSON.stringify(args).slice(0, 120)
210
+ process.stdout.write(ui.colors.dim(`\n ⟳ ${toolName}`) + ui.colors.dim(`(${argsStr})\n`))
211
+ if (options['out-file']) {
212
+ collectedEvents.push({ type: 'assistant', message: { content: [{ type: 'tool_use', name: toolName, input: args }] } })
213
+ }
214
+ })
215
+
216
+ assistant.on('toolResult', (toolName: string, result: any) => {
217
+ const preview = typeof result === 'string' ? result.slice(0, 100) : JSON.stringify(result).slice(0, 100)
218
+ process.stdout.write(ui.colors.green(` ✓ ${toolName}`) + ui.colors.dim(` → ${preview}${preview.length >= 100 ? '…' : ''}\n`))
219
+ if (options['out-file']) {
220
+ collectedEvents.push({ type: 'tool_result', content: typeof result === 'string' ? result : JSON.stringify(result, null, 2) })
221
+ }
222
+ })
223
+
224
+ assistant.on('toolError', (toolName: string, error: any) => {
225
+ const msg = error?.message || String(error)
226
+ process.stdout.write(ui.colors.red(` ✗ ${toolName}: ${msg}\n`))
227
+ })
228
+
229
+ const startTime = Date.now()
230
+ await assistant.ask(promptContent)
231
+ process.stdout.write('\n')
232
+
233
+ // Flush accumulated text buffer as a single event
234
+ if (textBuffer) {
235
+ collectedEvents.push({ type: 'assistant', message: { content: [{ type: 'text', text: textBuffer }] } })
236
+ textBuffer = ''
237
+ }
238
+
239
+ return { collectedEvents, durationMs: Date.now() - startTime, outputTokens: 0 }
240
+ }
241
+
242
+ async function runParallel(
243
+ target: string,
244
+ prepared: PreparedPrompt[],
245
+ options: z.infer<typeof argsSchema>,
246
+ container: any,
247
+ ): Promise<void> {
248
+ const { fs, paths } = container
249
+ const ink = container.feature('ink', { enable: true })
250
+ await ink.loadModules()
251
+
252
+ const React = ink.React
253
+ const h = React.createElement
254
+ const { Box, Text } = ink.components
255
+ const { useApp, useInput, useStdout } = ink.hooks
256
+ const { useState, useEffect } = React
257
+
258
+ const MAX_LINES = 500
259
+
260
+ // Mutable state that event handlers write to directly.
261
+ // The Ink component reads this on a timer to trigger re-renders.
262
+ const promptStates = prepared.map((p) => ({
263
+ filename: p.filename,
264
+ resolvedPath: p.resolvedPath,
265
+ status: 'running' as 'running' | 'done' | 'error',
266
+ lines: [] as string[],
267
+ outputTokens: 0,
268
+ startTime: Date.now(),
269
+ durationMs: 0,
270
+ collectedEvents: [] as any[],
271
+ error: undefined as string | undefined,
272
+ }))
273
+
274
+ const sessionMap = new Map<string, number>()
275
+ let allDone = false
276
+ let userAborted = false
277
+
278
+ function pushLines(idx: number, text: string) {
279
+ const newLines = text.split('\n')
280
+ promptStates[idx]!.lines.push(...newLines)
281
+ if (promptStates[idx]!.lines.length > MAX_LINES) {
282
+ promptStates[idx]!.lines = promptStates[idx]!.lines.slice(-MAX_LINES)
283
+ }
284
+ }
285
+
286
+ function pushToolLine(idx: number, text: string) {
287
+ promptStates[idx]!.lines.push(text)
288
+ if (promptStates[idx]!.lines.length > MAX_LINES) {
289
+ promptStates[idx]!.lines.splice(0, 1)
290
+ }
291
+ }
292
+
293
+ const runOptions: Record<string, any> = { streaming: true }
294
+ if (options['in-folder']) {
295
+ runOptions.cwd = container.paths.resolve(options['in-folder'])
296
+ }
297
+
298
+ const isCli = CLI_TARGETS.has(target)
299
+ let sessionPromise: Promise<any>
300
+
301
+ if (isCli) {
302
+ const featureName = target === 'claude' ? 'claudeCode' : 'openaiCodex'
303
+ const feature = container.feature(featureName)
304
+
305
+ const available = await feature.checkAvailability()
306
+ if (!available) {
307
+ console.error(`${target} CLI is not available. Make sure it is installed and in your PATH.`)
308
+ process.exit(1)
309
+ }
310
+
311
+ if (target === 'claude') {
312
+ runOptions.permissionMode = options['permission-mode']
313
+ if (options.chrome) runOptions.chrome = true
314
+ if (options.local) runOptions.local = true
315
+ }
316
+
317
+ feature.on('session:message', ({ sessionId, message }: { sessionId: string; message: any }) => {
318
+ const idx = sessionMap.get(sessionId)
319
+ if (idx === undefined) return
320
+
321
+ const role = message?.message?.role ?? message?.role
322
+ if (role && role !== 'assistant') return
323
+
324
+ const content = message?.message?.content ?? message?.content
325
+ if (!Array.isArray(content)) return
326
+
327
+ const usage = message?.message?.usage ?? message?.usage
328
+ if (usage?.output_tokens) promptStates[idx]!.outputTokens += usage.output_tokens
329
+
330
+ for (const block of content) {
331
+ if (block.type === 'text' && block.text) {
332
+ pushLines(idx, block.text)
333
+ } else if (block.type === 'tool_use') {
334
+ const argsStr = JSON.stringify(block.input).slice(0, 80)
335
+ pushToolLine(idx, ` > ${block.name}(${argsStr})`)
336
+ }
337
+ }
338
+ })
339
+
340
+ if (options['out-file']) {
341
+ feature.on('session:event', ({ sessionId, event }: { sessionId: string; event: any }) => {
342
+ const idx = sessionMap.get(sessionId)
343
+ if (idx === undefined) return
344
+ // Skip partial assistant messages (streaming tokens) — only collect complete messages
345
+ if (event.type === 'assistant') {
346
+ if (event.message?.stop_reason) promptStates[idx]!.collectedEvents.push(event)
347
+ return
348
+ }
349
+ if (event.type === 'tool_result' || event.type === 'message' || event.type === 'function_call_output') {
350
+ promptStates[idx]!.collectedEvents.push(event)
351
+ }
352
+ })
353
+ }
354
+
355
+ // Start all sessions — merge per-prompt agentOptions with shared runOptions
356
+ for (let i = 0; i < prepared.length; i++) {
357
+ const perPromptOptions = { ...prepared[i]!.agentOptions, ...runOptions }
358
+ if (options.model) perPromptOptions.model = options.model
359
+ const id = await feature.start(prepared[i]!.promptContent, perPromptOptions)
360
+ sessionMap.set(id, i)
361
+ }
362
+
363
+ const ids = [...sessionMap.keys()]
364
+ sessionPromise = Promise.allSettled(ids.map((id) => feature.waitForSession(id))).then((results) => {
365
+ results.forEach((r, ri) => {
366
+ const id = ids[ri]!
367
+ const idx = sessionMap.get(id)!
368
+ promptStates[idx]!.durationMs = Date.now() - promptStates[idx]!.startTime
369
+ if (r.status === 'fulfilled' && r.value?.status === 'error') {
370
+ promptStates[idx]!.status = 'error'
371
+ promptStates[idx]!.error = r.value?.error || 'Session failed'
372
+ } else if (r.status === 'rejected') {
373
+ promptStates[idx]!.status = 'error'
374
+ promptStates[idx]!.error = String(r.reason)
375
+ } else {
376
+ promptStates[idx]!.status = 'done'
377
+ }
378
+ })
379
+ allDone = true
380
+ })
381
+ } else {
382
+ // Assistant targets
383
+ const manager = container.feature('assistantsManager')
384
+ await manager.discover()
385
+
386
+ const entry = manager.get(target)
387
+ if (!entry) {
388
+ const entries = manager.list()
389
+ const available = entries.length ? entries.map((e: any) => e.name).join(', ') : '(none)'
390
+ console.error(`Assistant "${target}" not found. Available: ${available}`)
391
+ process.exit(1)
392
+ }
393
+
394
+ const lineBuffers: string[] = prepared.map(() => '')
395
+ const textBuffers: string[] = prepared.map(() => '')
396
+
397
+ const assistants = prepared.map((p, i) => {
398
+ const createOptions: Record<string, any> = { ...p.agentOptions }
399
+ if (options.model) createOptions.model = options.model
400
+ if (options.local) createOptions.local = true
401
+ const assistant = manager.create(target, createOptions)
402
+
403
+ assistant.on('chunk', (text: string) => {
404
+ lineBuffers[i] += text
405
+ const parts = lineBuffers[i]!.split('\n')
406
+ lineBuffers[i] = parts.pop() || ''
407
+ if (parts.length) {
408
+ promptStates[i]!.lines.push(...parts)
409
+ if (promptStates[i]!.lines.length > MAX_LINES) {
410
+ promptStates[i]!.lines = promptStates[i]!.lines.slice(-MAX_LINES)
411
+ }
412
+ }
413
+ if (options['out-file']) {
414
+ textBuffers[i] += text
415
+ }
416
+ })
417
+
418
+ assistant.on('toolCall', (toolName: string, args: any) => {
419
+ const argsStr = JSON.stringify(args).slice(0, 80)
420
+ pushToolLine(i, ` > ${toolName}(${argsStr})`)
421
+ if (options['out-file']) {
422
+ promptStates[i]!.collectedEvents.push({
423
+ type: 'assistant',
424
+ message: { content: [{ type: 'tool_use', name: toolName, input: args }] },
425
+ })
426
+ }
427
+ })
428
+
429
+ assistant.on('toolResult', (toolName: string, result: any) => {
430
+ const preview = typeof result === 'string' ? result.slice(0, 60) : JSON.stringify(result).slice(0, 60)
431
+ pushToolLine(i, ` ✓ ${toolName} → ${preview}`)
432
+ if (options['out-file']) {
433
+ promptStates[i]!.collectedEvents.push({
434
+ type: 'tool_result',
435
+ content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
436
+ })
437
+ }
438
+ })
439
+
440
+ assistant.on('toolError', (toolName: string, error: any) => {
441
+ const msg = error?.message || String(error)
442
+ pushToolLine(i, ` ✗ ${toolName}: ${msg}`)
443
+ })
444
+
445
+ return assistant
446
+ })
447
+
448
+ sessionPromise = Promise.allSettled(assistants.map((a, i) => a.ask(prepared[i]!.promptContent))).then(
449
+ (results) => {
450
+ results.forEach((r, i) => {
451
+ promptStates[i]!.durationMs = Date.now() - promptStates[i]!.startTime
452
+ // Flush remaining line buffer
453
+ if (lineBuffers[i]) {
454
+ promptStates[i]!.lines.push(lineBuffers[i]!)
455
+ lineBuffers[i] = ''
456
+ }
457
+ // Flush accumulated text as a single event for out-file
458
+ if (textBuffers[i]) {
459
+ promptStates[i]!.collectedEvents.push({ type: 'assistant', message: { content: [{ type: 'text', text: textBuffers[i] }] } })
460
+ textBuffers[i] = ''
461
+ }
462
+ if (r.status === 'rejected') {
463
+ promptStates[i]!.status = 'error'
464
+ promptStates[i]!.error = String(r.reason)
465
+ } else {
466
+ promptStates[i]!.status = 'done'
467
+ }
468
+ })
469
+ allDone = true
470
+ },
471
+ )
472
+ }
473
+
474
+ // --- Ink React Component ---
475
+ function App() {
476
+ const { exit } = useApp()
477
+ const { stdout } = useStdout()
478
+ const [tick, setTick] = useState(0)
479
+
480
+ const cols = stdout?.columns || 120
481
+ const rows = stdout?.rows || 30
482
+ const numPrompts = prepared.length
483
+ const colWidth = Math.max(30, Math.floor(cols / numPrompts))
484
+ const visibleLines = Math.max(5, rows - 7)
485
+
486
+ useEffect(() => {
487
+ const timer = setInterval(() => setTick((t: number) => t + 1), 200)
488
+ return () => clearInterval(timer)
489
+ }, [])
490
+
491
+ useEffect(() => {
492
+ if (allDone) {
493
+ setTimeout(() => exit(), 400)
494
+ }
495
+ }, [tick])
496
+
497
+ useInput((input: string, key: any) => {
498
+ if (input === 'q' || (key.ctrl && input === 'c')) {
499
+ userAborted = true
500
+ if (isCli) {
501
+ const featureName = target === 'claude' ? 'claudeCode' : 'openaiCodex'
502
+ const feature = container.feature(featureName)
503
+ for (const [sid] of sessionMap) {
504
+ try {
505
+ feature.abort(sid)
506
+ } catch {}
507
+ }
508
+ }
509
+ exit()
510
+ }
511
+ })
512
+
513
+ const formatElapsed = (ms: number) => {
514
+ const s = Math.floor(ms / 1000)
515
+ const m = Math.floor(s / 60)
516
+ const sec = s % 60
517
+ return `${m}:${String(sec).padStart(2, '0')}`
518
+ }
519
+
520
+ const runningCount = promptStates.filter((p) => p.status === 'running').length
521
+
522
+ return h(
523
+ Box,
524
+ { flexDirection: 'column', width: cols },
525
+ // Header
526
+ h(
527
+ Box,
528
+ { justifyContent: 'space-between', paddingX: 1, marginBottom: 1 },
529
+ h(Text, { bold: true, color: '#61dafb' }, 'LUCA PROMPT // PARALLEL'),
530
+ h(Text, { dimColor: true }, `${runningCount} running / ${numPrompts} total`),
531
+ ),
532
+ // Columns
533
+ h(
534
+ Box,
535
+ { flexDirection: 'row' },
536
+ ...promptStates.map((ps, i) => {
537
+ const elapsed = ps.status === 'running' ? Date.now() - ps.startTime : ps.durationMs
538
+ const borderColor = ps.status === 'running' ? 'cyan' : ps.status === 'done' ? 'green' : 'red'
539
+ const statusLabel =
540
+ ps.status === 'running'
541
+ ? `RUNNING ${formatElapsed(elapsed)}`
542
+ : ps.status === 'done'
543
+ ? `DONE ${formatElapsed(ps.durationMs)}`
544
+ : `ERROR`
545
+ const tail = ps.lines.slice(-visibleLines)
546
+
547
+ return h(
548
+ Box,
549
+ {
550
+ key: String(i),
551
+ flexDirection: 'column',
552
+ width: colWidth,
553
+ borderStyle: 'round',
554
+ borderColor,
555
+ paddingX: 1,
556
+ height: visibleLines + 4,
557
+ },
558
+ h(Text, { bold: true }, ps.filename),
559
+ h(Text, { color: borderColor, dimColor: ps.status === 'done' }, statusLabel),
560
+ h(Text, { dimColor: true }, '\u2500'.repeat(Math.max(1, colWidth - 4))),
561
+ h(Text, { wrap: 'truncate' }, tail.join('\n')),
562
+ )
563
+ }),
564
+ ),
565
+ // Footer
566
+ h(Box, { paddingX: 1 }, h(Text, { dimColor: true }, 'q: quit all')),
567
+ )
568
+ }
569
+
570
+ await ink.render(h(App))
571
+ await ink.waitUntilExit()
572
+
573
+ if (userAborted) return
574
+
575
+ // Wait for sessions to fully settle
576
+ await sessionPromise
577
+
578
+ // Post-completion: out-files
579
+ if (options['out-file']) {
580
+ const base = options['out-file']
581
+ const dotIdx = base.lastIndexOf('.')
582
+ const ext = dotIdx > 0 ? base.slice(dotIdx) : '.md'
583
+ const stem = dotIdx > 0 ? base.slice(0, dotIdx) : base
584
+
585
+ for (let i = 0; i < promptStates.length; i++) {
586
+ const ps = promptStates[i]!
587
+ if (!ps.collectedEvents.length) continue
588
+ const promptBasename = paths.basename(prepared[i]!.resolvedPath)
589
+ const promptStem = promptBasename.lastIndexOf('.') > 0 ? promptBasename.slice(0, promptBasename.lastIndexOf('.')) : promptBasename
590
+ const outPath = paths.resolve(`${stem}-${promptStem}${ext}`)
591
+ const markdown = formatSessionMarkdown(ps.collectedEvents, options['include-output'])
592
+ await Bun.write(outPath, markdown)
593
+ console.log(`Session saved to ${outPath}`)
594
+ }
595
+ }
596
+
597
+ // Print summary
598
+ const errors = promptStates.filter((p) => p.status === 'error')
599
+ if (errors.length) {
600
+ console.error(`\n${errors.length} prompt(s) failed:`)
601
+ for (const ps of errors) {
602
+ console.error(` ${ps.filename}: ${ps.error}`)
603
+ }
604
+ }
605
+ }
606
+
607
+ interface InputDef {
608
+ description?: string
609
+ required?: boolean
610
+ default?: any
611
+ type?: string
612
+ choices?: string[]
613
+ }
614
+
615
+ function parseInputDefs(meta: Record<string, any>): Record<string, InputDef> | null {
616
+ if (!meta?.inputs || typeof meta.inputs !== 'object') return null
617
+ const defs: Record<string, InputDef> = {}
618
+ for (const [key, val] of Object.entries(meta.inputs)) {
619
+ if (typeof val === 'object' && val !== null) {
620
+ defs[key] = val as InputDef
621
+ } else {
622
+ // Shorthand: `topic: "What to write about"` means description-only, required
623
+ defs[key] = { description: typeof val === 'string' ? val : String(val) }
624
+ }
625
+ }
626
+ return Object.keys(defs).length ? defs : null
627
+ }
628
+
629
+ async function resolveInputs(
630
+ inputDefs: Record<string, InputDef>,
631
+ options: z.infer<typeof argsSchema>,
632
+ container: any,
633
+ ): Promise<Record<string, any>> {
634
+ const { fs, paths } = container
635
+ const yaml = container.feature('yaml')
636
+ const ui = container.feature('ui')
637
+
638
+ // Layer 1: inputs-file (lowest priority of supplied values)
639
+ let fileInputs: Record<string, any> = {}
640
+ if (options['inputs-file']) {
641
+ const filePath = paths.resolve(options['inputs-file'])
642
+ const raw = fs.readFile(filePath) as string
643
+ if (filePath.endsWith('.json')) {
644
+ fileInputs = JSON.parse(raw)
645
+ } else {
646
+ fileInputs = yaml.parse(raw) || {}
647
+ }
648
+ }
649
+
650
+ // Layer 2: CLI flags (highest priority) — any unknown option that matches an input name
651
+ const cliInputs: Record<string, any> = {}
652
+ const argv = container.argv as Record<string, any>
653
+ for (const key of Object.keys(inputDefs)) {
654
+ if (argv[key] !== undefined) {
655
+ cliInputs[key] = argv[key]
656
+ }
657
+ }
658
+
659
+ // Merge: CLI > file > defaults
660
+ const supplied: Record<string, any> = {}
661
+ for (const [key, def] of Object.entries(inputDefs)) {
662
+ if (cliInputs[key] !== undefined) {
663
+ supplied[key] = cliInputs[key]
664
+ } else if (fileInputs[key] !== undefined) {
665
+ supplied[key] = fileInputs[key]
666
+ } else if (def.default !== undefined) {
667
+ supplied[key] = def.default
668
+ }
669
+ }
670
+
671
+ // Find missing required inputs
672
+ const missing: string[] = []
673
+ for (const [key, def] of Object.entries(inputDefs)) {
674
+ const isRequired = def.required !== false // default to required
675
+ if (isRequired && supplied[key] === undefined) {
676
+ missing.push(key)
677
+ }
678
+ }
679
+
680
+ if (missing.length === 0) return supplied
681
+
682
+ // In parallel mode, we can't run an interactive wizard
683
+ if ((options as any).parallel) {
684
+ console.error(`Missing required inputs for parallel mode (use --inputs-file or CLI flags): ${missing.join(', ')}`)
685
+ process.exit(1)
686
+ }
687
+
688
+ // Build wizard questions for missing inputs
689
+ const questions = missing.map((key) => {
690
+ const def = inputDefs[key]!
691
+ const q: Record<string, any> = {
692
+ name: key,
693
+ message: def.description || key,
694
+ }
695
+
696
+ // Auto-infer type
697
+ if (def.choices?.length) {
698
+ q.type = 'list'
699
+ q.choices = def.choices
700
+ } else if (def.type) {
701
+ q.type = def.type
702
+ } else {
703
+ q.type = 'input'
704
+ }
705
+
706
+ if (def.default !== undefined) {
707
+ q.default = def.default
708
+ }
709
+
710
+ return q
711
+ })
712
+
713
+ const answers = await ui.wizard(questions, supplied)
714
+ return { ...supplied, ...answers }
715
+ }
716
+
717
+ function substituteInputs(content: string, inputs: Record<string, any>): string {
718
+ return content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
719
+ return inputs[key] !== undefined ? String(inputs[key]) : match
720
+ })
721
+ }
722
+
723
+ async function executePromptFile(resolvedPath: string, container: any, inputs?: Record<string, any>): Promise<string> {
724
+ if (!container.docs.isLoaded) await container.docs.load()
725
+ const doc = await container.docs.parseMarkdownAtPath(resolvedPath)
726
+ const vm = container.feature('vm')
727
+ const parts: string[] = []
728
+
729
+ const capturedLines: string[] = []
730
+ const captureConsole = {
731
+ log: (...args: any[]) => capturedLines.push(args.map(String).join(' ')),
732
+ error: (...args: any[]) => capturedLines.push(args.map(String).join(' ')),
733
+ warn: (...args: any[]) => capturedLines.push(args.map(String).join(' ')),
734
+ info: (...args: any[]) => capturedLines.push(args.map(String).join(' ')),
735
+ }
736
+
737
+ const shared = vm.createContext({
738
+ ...container.context,
739
+ INPUTS: inputs || {},
740
+ console: captureConsole,
741
+ setTimeout, clearTimeout, setInterval, clearInterval,
742
+ fetch, URL, URLSearchParams,
743
+ })
744
+
745
+ for (const node of doc.ast.children) {
746
+ if (node.type === 'code') {
747
+ const { value, lang, meta } = node
748
+ if (!lang || !['ts', 'js', 'tsx', 'jsx'].includes(lang)) {
749
+ parts.push(doc.stringify({ type: 'root', children: [node] }))
750
+ continue
751
+ }
752
+ if (meta && typeof meta === 'string' && meta.toLowerCase().includes('skip')) continue
753
+
754
+ capturedLines.length = 0
755
+ let code = value
756
+ if (lang === 'tsx' || lang === 'jsx') {
757
+ const transpiler = container.feature('transpiler')
758
+ const { code: transformed } = transpiler.transformSync(value, { loader: lang as 'tsx' | 'jsx', format: 'cjs' })
759
+ code = transformed
760
+ }
761
+
762
+ await vm.run(code, shared)
763
+ Object.assign(shared, container.context)
764
+
765
+ if (capturedLines.length) {
766
+ parts.push(capturedLines.join('\n'))
767
+ }
768
+ } else {
769
+ parts.push(doc.stringify({ type: 'root', children: [node] }))
770
+ }
771
+ }
772
+
773
+ return parts.join('\n\n')
774
+ }
775
+
776
+ async function preparePrompt(
777
+ filePath: string,
778
+ options: z.infer<typeof argsSchema>,
779
+ container: any,
780
+ ): Promise<PreparedPrompt | null> {
781
+ const { fs, paths } = container
782
+
783
+ let resolvedPath = paths.resolve(filePath)
784
+ if (!fs.exists(resolvedPath)) {
785
+ // Try common fallbacks: add .md extension, docs/ prefix, or both
786
+ const candidates = [
787
+ `${resolvedPath}.md`,
788
+ paths.resolve('docs', filePath),
789
+ paths.resolve('docs', `${filePath}.md`),
790
+ ]
791
+ const found = candidates.find((c) => fs.exists(c))
792
+ if (!found) {
793
+ console.error(`Prompt file not found: ${resolvedPath}`)
794
+ return null
795
+ }
796
+ resolvedPath = found
797
+ }
798
+
799
+ let content = fs.readFile(resolvedPath) as string
800
+
801
+ // Parse frontmatter for input definitions and agentOptions
802
+ let resolvedInputs: Record<string, any> = {}
803
+ let agentOptions: Record<string, any> = {}
804
+ let hasInputDefs = false
805
+ if (content.startsWith('---')) {
806
+ const fmEnd = content.indexOf('\n---', 3)
807
+ if (fmEnd !== -1) {
808
+ const yaml = container.feature('yaml')
809
+ const meta = yaml.parse(content.slice(4, fmEnd)) || {}
810
+ const inputDefs = parseInputDefs(meta)
811
+ if (inputDefs) {
812
+ hasInputDefs = true
813
+ resolvedInputs = await resolveInputs(inputDefs, options, container)
814
+ }
815
+ if (meta.agentOptions && typeof meta.agentOptions === 'object') {
816
+ agentOptions = { ...meta.agentOptions }
817
+ }
818
+ }
819
+ }
820
+
821
+ if (options['inputs-file'] && !hasInputDefs) {
822
+ console.warn(`Warning: --inputs-file was provided but ${filePath} does not define any inputs in its frontmatter`)
823
+ }
824
+
825
+ let promptContent: string
826
+ if (options['skip-eval']) {
827
+ // Strip frontmatter but don't execute code blocks
828
+ if (content.startsWith('---')) {
829
+ const fmEnd = content.indexOf('\n---', 3)
830
+ if (fmEnd !== -1) {
831
+ promptContent = content.slice(fmEnd + 4).trimStart()
832
+ } else {
833
+ promptContent = content
834
+ }
835
+ } else {
836
+ promptContent = content
837
+ }
838
+ } else {
839
+ promptContent = await executePromptFile(resolvedPath, container, resolvedInputs)
840
+ }
841
+
842
+ // Re-attach frontmatter if requested
843
+ if (options['include-frontmatter'] && content.startsWith('---')) {
844
+ const fmEnd = content.indexOf('\n---', 3)
845
+ if (fmEnd !== -1) {
846
+ const frontmatter = content.slice(0, fmEnd + 4)
847
+ promptContent = frontmatter + '\n' + promptContent
848
+ }
849
+ }
850
+
851
+ // Substitute {{key}} placeholders with resolved input values
852
+ if (Object.keys(resolvedInputs).length) {
853
+ promptContent = substituteInputs(promptContent, resolvedInputs)
854
+ }
855
+
856
+ // Exclude sections by heading name
857
+ if (options['exclude-sections']) {
858
+ const headings = options['exclude-sections'].split(',').map((s) => s.trim()).filter(Boolean)
859
+ let doc = new Document({ id: filePath, content: promptContent, collection: null as any })
860
+
861
+ for (const heading of headings) {
862
+ try {
863
+ doc = doc.removeSection(heading)
864
+ } catch {
865
+ // Section not found — skip silently
866
+ }
867
+ }
868
+
869
+ promptContent = doc.content
870
+ }
871
+
872
+ return {
873
+ resolvedPath,
874
+ promptContent,
875
+ filename: paths.basename(resolvedPath),
876
+ agentOptions,
877
+ }
878
+ }
879
+
880
+ export default async function prompt(options: z.infer<typeof argsSchema>, context: ContainerContext) {
881
+ const container = context.container as any
882
+ const { fs, paths } = container
883
+
884
+ let target = container.argv._[1] as string | undefined
885
+ const allPaths = (container.argv._.slice(2) as string[]).filter(Boolean)
886
+
887
+ // If only one arg given and it looks like a file path, default target to claude
888
+ if (target && allPaths.length === 0) {
889
+ const candidate = paths.resolve(target)
890
+ if (fs.exists(candidate)) {
891
+ allPaths.push(target)
892
+ // this gives a way for you to say on a per project basis what you want the default coding assistant to be for the prompt command
893
+ // TODO need to document this somewhere
894
+ const { codingAssistant } = (container.manifest.luca || {})
895
+ target = codingAssistant || 'claude'
896
+ }
897
+ }
898
+
899
+ // Normalize target aliases (e.g. claude-code → claude, openai-codex → codex)
900
+ if (target) target = normalizeTarget(target)
901
+
902
+ if (!target || allPaths.length === 0) {
903
+ console.error('Usage: luca prompt [claude|codex|assistant-name] <path/to/prompt.md> [more paths...]')
904
+ process.exit(1)
905
+ }
906
+
907
+ // --- Parallel mode ---
908
+ if (options.parallel && allPaths.length > 1) {
909
+ if (allPaths.length > 4) {
910
+ console.error('--parallel supports a maximum of 4 concurrent prompts')
911
+ process.exit(1)
912
+ }
913
+
914
+ const prepared: PreparedPrompt[] = []
915
+ for (const pp of allPaths) {
916
+ const p = await preparePrompt(pp, options, container)
917
+ if (p) prepared.push(p)
918
+ }
919
+
920
+ if (prepared.length === 0) {
921
+ console.error('No prompt files to run (all skipped).')
922
+ process.exit(1)
923
+ }
924
+
925
+ if (options['dry-run']) {
926
+ const ui = container.feature('ui')
927
+ console.log(ui.colors.bold('\n── Dry Run (Parallel) ──\n'))
928
+ console.log(ui.colors.bold('Target:'), target)
929
+ console.log(ui.colors.bold('Prompts:'), prepared.length)
930
+ for (const p of prepared) {
931
+ console.log(ui.colors.bold(`\n── ${p.filename} ──`))
932
+ console.log(ui.colors.dim(` Path: ${p.resolvedPath}`))
933
+ console.log(ui.colors.dim(` Length: ${p.promptContent.length} chars`))
934
+ if (Object.keys(p.agentOptions).length) {
935
+ console.log(ui.colors.dim(' Agent options:'))
936
+ for (const [key, val] of Object.entries(p.agentOptions)) {
937
+ const display = typeof val === 'object' ? JSON.stringify(val) : val
938
+ console.log(ui.colors.dim(` ${key}: ${display}`))
939
+ }
940
+ }
941
+ console.log('')
942
+ process.stdout.write(ui.markdown(p.promptContent))
943
+ }
944
+ return
945
+ }
946
+
947
+ if (prepared.length > 1) {
948
+ await runParallel(target, prepared, options, container)
949
+ return
950
+ }
951
+ // Only 1 left after filtering — fall through to single mode
952
+ }
953
+
954
+ // --- Single prompt mode ---
955
+ const promptPath = allPaths[0]!
956
+ const p = await preparePrompt(promptPath, options, container)
957
+
958
+ if (!p) {
959
+ process.exit(1)
960
+ }
961
+
962
+ const ui = container.feature('ui')
963
+
964
+ if (options['dry-run']) {
965
+ const runOptions: Record<string, any> = { ...p.agentOptions }
966
+ if (options.model) runOptions.model = options.model
967
+ if (options['in-folder']) runOptions.cwd = container.paths.resolve(options['in-folder'])
968
+ if (options['out-file']) runOptions.outFile = options['out-file']
969
+ if (options['include-output']) runOptions.includeOutput = true
970
+ if (options['exclude-sections']) runOptions.excludeSections = options['exclude-sections']
971
+ if (CLI_TARGETS.has(target)) {
972
+ runOptions.permissionMode = options['permission-mode']
973
+ if (options.chrome) runOptions.chrome = true
974
+ }
975
+
976
+ console.log(ui.colors.bold('\n── Dry Run ──\n'))
977
+ console.log(ui.colors.bold('Target:'), target)
978
+ console.log(ui.colors.bold('Prompt file:'), p.resolvedPath)
979
+ console.log(ui.colors.bold('Prompt length:'), `${p.promptContent.length} chars`)
980
+ if (Object.keys(runOptions).length) {
981
+ console.log(ui.colors.bold('Options:'))
982
+ for (const [key, val] of Object.entries(runOptions)) {
983
+ const display = typeof val === 'object' ? JSON.stringify(val) : val
984
+ console.log(` ${key}: ${display}`)
985
+ }
986
+ }
987
+ console.log(ui.colors.bold('\n── Prompt Content ──\n'))
988
+ process.stdout.write(ui.markdown(p.promptContent))
989
+ return
990
+ }
991
+
992
+ process.stdout.write(ui.markdown(p.promptContent))
993
+
994
+ let stats: RunStats
995
+
996
+ if (CLI_TARGETS.has(target)) {
997
+ stats = await runClaudeOrCodex(target as 'claude' | 'codex', p.promptContent, container, options, p.agentOptions)
998
+ } else {
999
+ stats = await runAssistant(target, p.promptContent, options, container, p.agentOptions)
1000
+ }
1001
+
1002
+ if (options['out-file'] && stats.collectedEvents.length) {
1003
+ const markdown = formatSessionMarkdown(stats.collectedEvents, options['include-output'])
1004
+ const outPath = paths.resolve(options['out-file'])
1005
+ await Bun.write(outPath, markdown)
1006
+ console.log(`Session saved to ${outPath}`)
1007
+ }
1008
+ }
1009
+
1010
+ commands.registerHandler('prompt', {
1011
+ description: 'Send a prompt file to an assistant, Claude Code, or OpenAI Codex',
1012
+ argsSchema,
1013
+ handler: prompt,
1014
+ })