luca 1.1.2 → 3.0.0

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