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,641 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
+ import { Feature } from '../feature.js'
4
+ import type { Assistant } from './assistant.js'
5
+ import type { ToolCallCtx } from '../lib/interceptor-chain.js'
6
+
7
+ declare module 'luca/feature' {
8
+ interface AvailableFeatures {
9
+ lucaCoder: typeof LucaCoder
10
+ }
11
+ }
12
+
13
+ /** Permission level for a tool. 'allow' runs immediately, 'ask' blocks for user approval, 'deny' rejects. */
14
+ export type PermissionLevel = 'allow' | 'ask' | 'deny'
15
+
16
+ /** A pending approval awaiting user decision. */
17
+ export interface PendingApproval {
18
+ id: string
19
+ toolName: string
20
+ args: Record<string, any>
21
+ timestamp: number
22
+ resolve: (decision: 'approve' | 'deny') => void
23
+ }
24
+
25
+ /** Tool bundle spec — either a feature name string, or an object with filtering. */
26
+ export type ToolBundleSpec = string | {
27
+ feature: string
28
+ only?: string[]
29
+ except?: string[]
30
+ }
31
+
32
+ export const LucaCoderEventsSchema = FeatureEventsSchema.extend({
33
+ started: z.tuple([]).describe('Emitted when the luca coder has been initialized'),
34
+ permissionRequest: z.tuple([z.object({
35
+ id: z.string().describe('Unique approval ID'),
36
+ toolName: z.string().describe('The tool requesting permission'),
37
+ args: z.record(z.string(), z.any()).describe('The arguments the tool was called with'),
38
+ })]).describe('Emitted when a tool call requires user approval'),
39
+ permissionGranted: z.tuple([z.string().describe('Approval ID')]).describe('Emitted when a pending tool call is approved'),
40
+ permissionDenied: z.tuple([z.string().describe('Approval ID')]).describe('Emitted when a pending tool call is denied'),
41
+ toolBlocked: z.tuple([z.string().describe('Tool name'), z.string().describe('Reason')]).describe('Emitted when a tool call is blocked by deny policy'),
42
+ // Forwarded from inner assistant
43
+ chunk: z.tuple([z.string().describe('A chunk of streamed text')]).describe('Forwarded: streamed token chunk from the inner assistant'),
44
+ response: z.tuple([z.string().describe('The final response text')]).describe('Forwarded: complete response from the inner assistant'),
45
+ toolCall: z.tuple([z.string().describe('Tool name'), z.any().describe('Tool arguments')]).describe('Forwarded: a tool was called'),
46
+ toolResult: z.tuple([z.string().describe('Tool name'), z.any().describe('Result value')]).describe('Forwarded: a tool returned a result'),
47
+ toolError: z.tuple([z.string().describe('Tool name'), z.any().describe('Error')]).describe('Forwarded: a tool call failed'),
48
+ })
49
+
50
+ export const LucaCoderStateSchema = FeatureStateSchema.extend({
51
+ started: z.boolean().describe('Whether the assistant has been initialized'),
52
+ permissions: z.record(z.string(), z.enum(['allow', 'ask', 'deny'])).describe('Permission level per tool name'),
53
+ defaultPermission: z.enum(['allow', 'ask', 'deny']).describe('Permission level for tools not explicitly configured'),
54
+ pendingApprovals: z.array(z.object({
55
+ id: z.string(),
56
+ toolName: z.string(),
57
+ args: z.record(z.string(), z.any()),
58
+ timestamp: z.number(),
59
+ })).describe('Tool calls currently awaiting user approval'),
60
+ approvalHistory: z.array(z.object({
61
+ id: z.string(),
62
+ toolName: z.string(),
63
+ decision: z.enum(['approve', 'deny']),
64
+ timestamp: z.number(),
65
+ })).describe('Recent approval decisions'),
66
+ loadedSkills: z.array(z.string()).describe('Names of skills auto-loaded into context'),
67
+ })
68
+
69
+ export const LucaCoderOptionsSchema = FeatureOptionsSchema.extend({
70
+ /** Tool bundles to stack — feature names or objects with filtering. */
71
+ tools: z.array(z.union([
72
+ z.string(),
73
+ z.object({
74
+ feature: z.string(),
75
+ only: z.array(z.string()).optional(),
76
+ except: z.array(z.string()).optional(),
77
+ }),
78
+ ])).default(['fileTools']).describe('Tool bundles to register on the inner assistant'),
79
+
80
+ /** Per-tool permission overrides. */
81
+ permissions: z.record(z.string(), z.enum(['allow', 'ask', 'deny'])).default({}).describe('Permission level per tool name'),
82
+
83
+ /** Default permission for tools not in the permissions map. */
84
+ defaultPermission: z.enum(['allow', 'ask', 'deny']).default('ask').describe('Default permission level for unconfigured tools'),
85
+
86
+ /** System prompt for the inner assistant. */
87
+ systemPrompt: z.string().optional().describe('System prompt for the inner assistant'),
88
+
89
+ /** Model to use. */
90
+ model: z.string().optional().describe('OpenAI model override'),
91
+
92
+ /** Maximum output tokens per completion. */
93
+ maxTokens: z.number().default(2048).describe('Maximum number of output tokens per completion'),
94
+
95
+ /** Whether to use a local API server. */
96
+ local: z.boolean().default(false).describe('Use a local API server for the inner assistant'),
97
+
98
+ /** History mode for the inner assistant. */
99
+ historyMode: z.enum(['lifecycle', 'daily', 'persistent', 'session']).optional().describe('Conversation history persistence mode'),
100
+
101
+ /** Assistant folder — if provided, loads CORE.md/tools.ts/hooks.ts from disk. */
102
+ folder: z.string().optional().describe('Assistant folder for disk-based definitions'),
103
+
104
+ /** Skills to auto-load into the system prompt context. If not specified, auto-detects luca-framework. */
105
+ skills: z.array(z.string()).optional().describe('Skill names to auto-load into the system prompt'),
106
+
107
+ /** Whether to auto-detect and load the luca-framework skill from conventional agent skill folders. Defaults to true. */
108
+ autoLoadLucaSkill: z.boolean().default(true).describe('Auto-load luca-framework skill if found in agent skill folders (.claude/skills or .agents/skills)'),
109
+ })
110
+
111
+ export type LucaCoderState = z.infer<typeof LucaCoderStateSchema>
112
+ export type LucaCoderOptions = z.infer<typeof LucaCoderOptionsSchema>
113
+
114
+ /**
115
+ * A coding assistant that owns a lower-level Assistant instance and
116
+ * gates all tool calls through a permission system.
117
+ *
118
+ * Comes with built-in Bash tool (via proc.execAndCapture) and auto-loads
119
+ * the luca-framework skill when found in conventional agent skill folders (.claude/skills or .agents/skills).
120
+ *
121
+ * Tools are stacked from feature bundles (fileTools, etc.)
122
+ * and each tool can be set to 'allow' (runs immediately), 'ask' (blocks
123
+ * until user approves/denies), or 'deny' (always rejected).
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const coder = container.feature('lucaCoder', {
128
+ * tools: ['fileTools'],
129
+ * permissions: {
130
+ * readFile: 'allow',
131
+ * searchFiles: 'allow',
132
+ * writeFile: 'ask',
133
+ * bash: 'ask',
134
+ * },
135
+ * defaultPermission: 'ask',
136
+ * systemPrompt: 'You are a coding assistant.',
137
+ * })
138
+ *
139
+ * coder.on('permissionRequest', ({ id, toolName, args }) => {
140
+ * console.log(`Tool "${toolName}" wants to run with`, args)
141
+ * coder.approve(id) // or coder.deny(id)
142
+ * })
143
+ *
144
+ * await coder.ask('Refactor the auth module to use async/await')
145
+ * ```
146
+ *
147
+ * @extends Feature
148
+ */
149
+ export class LucaCoder extends Feature<LucaCoderState, LucaCoderOptions> {
150
+ static override shortcut = 'features.lucaCoder' as const
151
+ static override stateSchema = LucaCoderStateSchema
152
+ static override optionsSchema = LucaCoderOptionsSchema
153
+ static override eventsSchema = LucaCoderEventsSchema
154
+
155
+ static { Feature.register(this, 'lucaCoder') }
156
+
157
+ /**
158
+ * Default system prompt that establishes baseline coding assistant identity
159
+ * and luca framework knowledge. Project-specific CLAUDE.md and loaded skills
160
+ * are appended below this — the skill will reinforce and expand on these
161
+ * fundamentals without conflicting.
162
+ */
163
+ static defaultSystemPrompt = [
164
+ 'You are an autonomous coding assistant operating inside a luca framework project.',
165
+ 'You have access to file system tools and a bash shell. Use them to explore, understand, and modify code.',
166
+ '',
167
+ '## Working Style',
168
+ '- Always read and understand code before modifying it. Use searchFiles and readFile first.',
169
+ '- Use editFile for surgical changes to existing files — prefer it over writeFile for modifications.',
170
+ '- Explain what you plan to do before doing it.',
171
+ '- Deliver incrementally — each change should leave the project in a working state.',
172
+ '',
173
+ '## The Luca Framework',
174
+ 'This project uses luca — a dependency injection container for bun. The `luca` CLI is available in your shell.',
175
+ '',
176
+ '### Essential CLI Commands',
177
+ '- `luca` — list all available commands',
178
+ '- `luca describe features` / `luca describe clients` / `luca describe servers` — see what the container provides',
179
+ '- `luca describe <name>` — full docs for any feature, client, or server (e.g. `luca describe fs`, `luca describe git`)',
180
+ '- `luca describe <name>.<member>` — docs for a specific method or getter',
181
+ '- `luca eval "expression"` — run JS/TS with the container in scope (great for testing ideas)',
182
+ '- `luca scaffold <type> <name>` — generate boilerplate for new helpers',
183
+ '- `luca scaffold <type> --tutorial` — read the full guide for building that helper type',
184
+ '',
185
+ '### Container Rules',
186
+ '- NEVER import from `fs`, `path`, or other Node builtins. Use `container.feature(\'fs\')` and `container.paths`.',
187
+ '- The container provides everything you need: YAML, SQLite, REST client, grep, chalk, lodash, uuid, and more.',
188
+ '- Use `luca describe` liberally — it is faster and more reliable than searching source files.',
189
+ '- Use `luca eval` to test container code before wiring up full handlers.',
190
+ '',
191
+ '### Project Structure',
192
+ '- `commands/` — CLI commands run via `luca <name>`',
193
+ '- `endpoints/` — HTTP routes served via `luca serve`',
194
+ '- `features/` — custom container features',
195
+ '- Auto-discovered modules: commands, endpoints, features, clients, servers',
196
+ ].join('\n')
197
+
198
+ /** The inner assistant instance. Created during start(). */
199
+ private _assistant: Assistant | null = null
200
+
201
+ /** Map of pending approval promises keyed by ID. */
202
+ private _pendingResolvers = new Map<string, (decision: 'approve' | 'deny') => void>()
203
+
204
+ override get initialState(): LucaCoderState {
205
+ return {
206
+ ...super.initialState,
207
+ started: false,
208
+ permissions: this.options.permissions || {},
209
+ defaultPermission: this.options.defaultPermission || 'ask',
210
+ pendingApprovals: [],
211
+ approvalHistory: [],
212
+ loadedSkills: [],
213
+ }
214
+ }
215
+
216
+
217
+ /** The inner assistant. Throws if not started. */
218
+ get assistant(): Assistant {
219
+ if (!this._assistant) throw new Error('LucaCoder not started. Call start() first.')
220
+ return this._assistant
221
+ }
222
+
223
+ /** Current permission map from state. */
224
+ get permissions(): Record<string, PermissionLevel> {
225
+ return this.state.get('permissions') as Record<string, PermissionLevel>
226
+ }
227
+
228
+ /** Current pending approvals. */
229
+ get pendingApprovals(): PendingApproval[] {
230
+ const stored = this.state.get('pendingApprovals') as Array<{ id: string; toolName: string; args: Record<string, any>; timestamp: number }>
231
+ return stored.map(p => ({
232
+ ...p,
233
+ resolve: this._pendingResolvers.get(p.id) || (() => {}),
234
+ }))
235
+ }
236
+
237
+ /** Whether the assistant is started and ready. */
238
+ get isStarted(): boolean {
239
+ return this.state.get('started') as boolean
240
+ }
241
+
242
+ /** The tools registered on the inner assistant. */
243
+ get tools(): Record<string, any> {
244
+ return this._assistant?.tools || {}
245
+ }
246
+
247
+ /** The conversation on the inner assistant (if started). */
248
+ get conversation() {
249
+ return this._assistant?.conversation
250
+ }
251
+
252
+ /** Messages from the inner assistant's conversation. */
253
+ get messages() {
254
+ return this._assistant?.messages || []
255
+ }
256
+
257
+ // -------------------------------------------------------------------------
258
+ // Permission management
259
+ // -------------------------------------------------------------------------
260
+
261
+ /** Get the effective permission level for a tool. */
262
+ getPermission(toolName: string): PermissionLevel {
263
+ const perms = this.permissions
264
+ if (perms[toolName]) return perms[toolName]
265
+ return this.state.get('defaultPermission') as PermissionLevel
266
+ }
267
+
268
+ /** Set permission level for one or more tools. */
269
+ setPermission(toolName: string | string[], level: PermissionLevel): this {
270
+ const names = Array.isArray(toolName) ? toolName : [toolName]
271
+ const perms = { ...this.permissions }
272
+ for (const name of names) {
273
+ perms[name] = level
274
+ }
275
+ this.state.set('permissions', perms)
276
+ return this
277
+ }
278
+
279
+ /** Set the default permission level for unconfigured tools. */
280
+ setDefaultPermission(level: PermissionLevel): this {
281
+ this.state.set('defaultPermission', level)
282
+ return this
283
+ }
284
+
285
+ /** Allow a tool (or tools) to run without approval. */
286
+ permitTool(...toolNames: string[]): this {
287
+ return this.setPermission(toolNames, 'allow')
288
+ }
289
+
290
+ /** Require approval before a tool (or tools) can run. */
291
+ gateTool(...toolNames: string[]): this {
292
+ return this.setPermission(toolNames, 'ask')
293
+ }
294
+
295
+ /** Block a tool (or tools) from ever running. */
296
+ blockTool(...toolNames: string[]): this {
297
+ return this.setPermission(toolNames, 'deny')
298
+ }
299
+
300
+ // -------------------------------------------------------------------------
301
+ // Approval flow
302
+ // -------------------------------------------------------------------------
303
+
304
+ /** Approve a pending tool call by ID. The tool will execute. */
305
+ approve(id: string): this {
306
+ const resolver = this._pendingResolvers.get(id)
307
+ if (resolver) {
308
+ resolver('approve')
309
+ this._removePending(id)
310
+ this._recordDecision(id, 'approve')
311
+ this.emit('permissionGranted', id)
312
+ }
313
+ return this
314
+ }
315
+
316
+ /** Deny a pending tool call by ID. The tool call will be skipped. */
317
+ deny(id: string): this {
318
+ const resolver = this._pendingResolvers.get(id)
319
+ if (resolver) {
320
+ resolver('deny')
321
+ this._removePending(id)
322
+ this._recordDecision(id, 'deny')
323
+ this.emit('permissionDenied', id)
324
+ }
325
+ return this
326
+ }
327
+
328
+ /** Approve all pending tool calls. */
329
+ approveAll(): this {
330
+ for (const { id } of this.pendingApprovals) {
331
+ this.approve(id)
332
+ }
333
+ return this
334
+ }
335
+
336
+ /** Deny all pending tool calls. */
337
+ denyAll(): this {
338
+ for (const { id } of this.pendingApprovals) {
339
+ this.deny(id)
340
+ }
341
+ return this
342
+ }
343
+
344
+ // -------------------------------------------------------------------------
345
+ // Bash tool
346
+ // -------------------------------------------------------------------------
347
+
348
+ /**
349
+ * Execute a shell command string and return its output.
350
+ * Uses proc.execAndCapture under the hood — runs `sh -c <command>`.
351
+ */
352
+ async bash({ command, cwd, timeout }: { command: string; cwd?: string; timeout?: number }): Promise<{
353
+ exitCode: number
354
+ stdout: string
355
+ stderr: string
356
+ success: boolean
357
+ }> {
358
+ const proc = this.container.feature('proc')
359
+ const options: Record<string, any> = {
360
+ cwd: cwd ?? this.container.cwd,
361
+ }
362
+ if (timeout) options.timeout = timeout
363
+
364
+ const result = await proc.execAndCapture(command, options)
365
+
366
+ return {
367
+ exitCode: result.exitCode,
368
+ stdout: result.stdout,
369
+ stderr: result.stderr,
370
+ success: result.exitCode === 0,
371
+ }
372
+ }
373
+
374
+ // -------------------------------------------------------------------------
375
+ // Project instructions
376
+ // -------------------------------------------------------------------------
377
+
378
+ /**
379
+ * Read project instruction files (CLAUDE.md, AGENTS.md, LUCA.md) from the
380
+ * project root if they exist, and return their combined content.
381
+ */
382
+ private _loadProjectInstructions(): string {
383
+ const { fs, paths } = this.container
384
+ const cwd = this.container.cwd
385
+ const candidates = ['CLAUDE.md', 'AGENTS.md', 'LUCA.md']
386
+ const sections: string[] = []
387
+ const seen = new Set<string>()
388
+
389
+ for (const filename of candidates) {
390
+ const filePath = paths.resolve(cwd, filename)
391
+ if (!fs.exists(filePath)) continue
392
+
393
+ // Resolve symlinks so we don't read the same file twice
394
+ const realPath = fs.realpath(filePath)
395
+ if (seen.has(realPath)) continue
396
+ seen.add(realPath)
397
+
398
+ const content = fs.readFile(filePath)
399
+ sections.push(`# ${filename}\n\n${content}`)
400
+ }
401
+
402
+ return sections.join('\n\n---\n\n')
403
+ }
404
+
405
+ // -------------------------------------------------------------------------
406
+ // Skill loading
407
+ // -------------------------------------------------------------------------
408
+
409
+ /**
410
+ * Detect and load skills into the system prompt context.
411
+ * Auto-loads luca-framework if found and autoLoadLucaSkill is true.
412
+ */
413
+ private async _loadSkillsIntoContext(): Promise<string> {
414
+ const { fs, paths, os } = this.container
415
+ const skillContent: string[] = []
416
+ const loadedSkills: string[] = []
417
+
418
+ // Check for luca-framework skill in conventional agent skill folders
419
+ if (this.options.autoLoadLucaSkill !== false) {
420
+ const skillLocations = [
421
+ paths.resolve(this.container.cwd, '.claude', 'skills', 'luca-framework', 'SKILL.md'),
422
+ paths.resolve(this.container.cwd, '.agents', 'skills', 'luca-framework', 'SKILL.md'),
423
+ paths.resolve(os.homedir, '.claude', 'skills', 'luca-framework', 'SKILL.md'),
424
+ paths.resolve(os.homedir, '.agents', 'skills', 'luca-framework', 'SKILL.md'),
425
+ ]
426
+
427
+ for (const skillPath of skillLocations) {
428
+ if (fs.exists(skillPath)) {
429
+ const content = fs.readFile(skillPath)
430
+ skillContent.push(`# Skill: luca-framework\n\n${content}`)
431
+ loadedSkills.push('luca-framework')
432
+ break // Only load once
433
+ }
434
+ }
435
+ }
436
+
437
+ // Load explicitly requested skills via skillsLibrary
438
+ if (this.options.skills?.length) {
439
+ const lib = this.container.feature('skillsLibrary')
440
+ if (!lib.isStarted) await lib.start()
441
+
442
+ for (const skillName of this.options.skills) {
443
+ if (loadedSkills.includes(skillName)) continue
444
+ const skill = lib.find(skillName)
445
+ if (skill) {
446
+ const content = fs.readFile(skill.skillFilePath)
447
+ skillContent.push(`# Skill: ${skillName}\n\n${content}`)
448
+ loadedSkills.push(skillName)
449
+ }
450
+ }
451
+ }
452
+
453
+ this.state.set('loadedSkills', loadedSkills)
454
+ return skillContent.join('\n\n---\n\n')
455
+ }
456
+
457
+ // -------------------------------------------------------------------------
458
+ // Lifecycle
459
+ // -------------------------------------------------------------------------
460
+
461
+ /**
462
+ * Initialize the inner assistant, register the bash tool, stack tool bundles,
463
+ * auto-load skills, and wire up the permission interceptor.
464
+ */
465
+ async start(): Promise<this> {
466
+ if (this.isStarted) return this
467
+
468
+ // Load project instructions and skill content for system prompt
469
+ const projectInstructions = this._loadProjectInstructions()
470
+ const skillContext = await this._loadSkillsIntoContext()
471
+
472
+ // Build the system prompt: default baseline is always present,
473
+ // user-provided systemPrompt is additive on top of it
474
+ const parts: string[] = [LucaCoder.defaultSystemPrompt]
475
+ if (this.options.systemPrompt) parts.push(this.options.systemPrompt)
476
+ if (projectInstructions) parts.push(projectInstructions)
477
+ if (skillContext) parts.push(skillContext)
478
+ const systemPrompt = parts.join('\n\n---\n\n')
479
+
480
+ // Create the inner assistant
481
+ const assistantOpts: Record<string, any> = {}
482
+ if (systemPrompt) assistantOpts.systemPrompt = systemPrompt
483
+ if (this.options.model) assistantOpts.model = this.options.model
484
+ if (this.options.maxTokens) assistantOpts.maxTokens = this.options.maxTokens
485
+ if (this.options.local) assistantOpts.local = this.options.local
486
+ if (this.options.historyMode) assistantOpts.historyMode = this.options.historyMode
487
+ if (this.options.folder) assistantOpts.folder = this.options.folder
488
+
489
+ this._assistant = this.container.feature('assistant', assistantOpts)
490
+
491
+ // Register the built-in bash tool
492
+ const bashSchema = z.object({
493
+ command: z.string().describe('The shell command to execute. Supports pipes, redirects, chaining with && or ;'),
494
+ cwd: z.string().optional().describe('Working directory for the command. Defaults to project root.'),
495
+ timeout: z.number().optional().describe('Timeout in milliseconds. Defaults to no timeout.'),
496
+ }).describe('Execute a shell command and return stdout, stderr, and exit code. Use this for running builds, tests, git commands, installing packages, or any shell operation.')
497
+
498
+ this._assistant.addTool(
499
+ 'bash',
500
+ (args: { command: string; cwd?: string; timeout?: number }) => this.bash(args),
501
+ bashSchema,
502
+ )
503
+
504
+ // Stack tool bundles
505
+ for (const spec of this.options.tools) {
506
+ this._stackToolBundle(spec)
507
+ }
508
+
509
+ // Always register skillsLibrary tools so the assistant can discover
510
+ // and load additional skills dynamically during a conversation
511
+ const skillsLib = this.container.feature('skillsLibrary')
512
+ if (!skillsLib.isStarted) await skillsLib.start()
513
+ this._assistant.use(skillsLib as any)
514
+
515
+ // Wire the permission interceptor
516
+ this._assistant.intercept('beforeToolCall', async (ctx: ToolCallCtx, next: () => Promise<void>) => {
517
+ const policy = this.getPermission(ctx.name)
518
+
519
+ if (policy === 'deny') {
520
+ ctx.skip = true
521
+ ctx.result = JSON.stringify({ blocked: true, tool: ctx.name, reason: 'Permission denied by policy.' })
522
+ this.emit('toolBlocked', ctx.name, 'deny policy')
523
+ return
524
+ }
525
+
526
+ if (policy === 'allow') {
527
+ await next()
528
+ return
529
+ }
530
+
531
+ // 'ask' — block until user decides
532
+ const decision = await this._requestApproval(ctx.name, ctx.args)
533
+
534
+ if (decision === 'approve') {
535
+ await next()
536
+ } else {
537
+ ctx.skip = true
538
+ ctx.result = JSON.stringify({ blocked: true, tool: ctx.name, reason: 'User denied this action.' })
539
+ }
540
+ })
541
+
542
+ // Forward events from inner assistant
543
+ this._assistant.on('chunk', (text: string) => this.emit('chunk', text))
544
+ this._assistant.on('response', (text: string) => this.emit('response', text))
545
+ this._assistant.on('toolCall', (name: string, args: any) => this.emit('toolCall', name, args))
546
+ this._assistant.on('toolResult', (name: string, result: any) => this.emit('toolResult', name, result))
547
+ this._assistant.on('toolError', (name: string, error: any) => this.emit('toolError', name, error))
548
+
549
+ // Start the inner assistant
550
+ await this._assistant.start()
551
+
552
+ this.state.set('started', true)
553
+ this.emit('started')
554
+
555
+ return this
556
+ }
557
+
558
+ /**
559
+ * Ask the coder a question. Auto-starts if needed.
560
+ * Tool calls will be gated by the permission system.
561
+ */
562
+ async ask(question: string, options?: Record<string, any>): Promise<string> {
563
+ if (!this.isStarted) await this.start()
564
+ return this.assistant.ask(question, options)
565
+ }
566
+
567
+ /**
568
+ * Add a tool bundle after initialization. Useful for dynamically
569
+ * extending the assistant's capabilities.
570
+ */
571
+ use(spec: ToolBundleSpec): this {
572
+ this._stackToolBundle(spec)
573
+ return this
574
+ }
575
+
576
+ // -------------------------------------------------------------------------
577
+ // Internal
578
+ // -------------------------------------------------------------------------
579
+
580
+ /** Resolve a tool bundle spec and register its tools on the inner assistant. */
581
+ private _stackToolBundle(spec: ToolBundleSpec): void {
582
+ if (!this._assistant) throw new Error('Cannot stack tools before start()')
583
+
584
+ const featureName = typeof spec === 'string' ? spec : spec.feature
585
+ const feature = this.container.feature(featureName as any)
586
+
587
+ if (typeof spec === 'string') {
588
+ // No filtering — pass the feature directly so assistant.use() calls
589
+ // both _registerTools AND setupToolsConsumer (for system prompt extensions)
590
+ this._assistant.use(feature as any)
591
+ } else {
592
+ // Filtering with only/except — must call toTools with filter opts,
593
+ // then manually trigger setupToolsConsumer
594
+ const filterOpts = { only: spec.only, except: spec.except }
595
+ const tools = (feature as any).toTools(filterOpts)
596
+ this._assistant.use(tools)
597
+ if (typeof (feature as any).setupToolsConsumer === 'function') {
598
+ (feature as any).setupToolsConsumer(this._assistant)
599
+ }
600
+ }
601
+ }
602
+
603
+ /** Create a pending approval, emit the event, and return a promise that resolves with the decision. */
604
+ private _requestApproval(toolName: string, args: Record<string, any>): Promise<'approve' | 'deny'> {
605
+ const id = this.container.utils.uuid()
606
+
607
+ return new Promise<'approve' | 'deny'>((resolve) => {
608
+ this._pendingResolvers.set(id, resolve)
609
+
610
+ const pending = [...(this.state.get('pendingApprovals') as any[])]
611
+ pending.push({ id, toolName, args, timestamp: Date.now() })
612
+ this.state.set('pendingApprovals', pending)
613
+
614
+ this.emit('permissionRequest', { id, toolName, args })
615
+ })
616
+ }
617
+
618
+ /** Remove a pending approval from state. */
619
+ private _removePending(id: string): void {
620
+ this._pendingResolvers.delete(id)
621
+ const pending = (this.state.get('pendingApprovals') as any[]).filter((p: any) => p.id !== id)
622
+ this.state.set('pendingApprovals', pending)
623
+ }
624
+
625
+ /** Record a decision in the approval history. */
626
+ private _recordDecision(id: string, decision: 'approve' | 'deny'): void {
627
+ const pending = (this.state.get('pendingApprovals') as any[]).find((p: any) => p.id === id)
628
+ const history = [...(this.state.get('approvalHistory') as any[])]
629
+ history.push({
630
+ id,
631
+ toolName: pending?.toolName || 'unknown',
632
+ decision,
633
+ timestamp: Date.now(),
634
+ })
635
+ // Keep last 100 entries
636
+ if (history.length > 100) history.splice(0, history.length - 100)
637
+ this.state.set('approvalHistory', history)
638
+ }
639
+ }
640
+
641
+ export default LucaCoder