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,557 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
+ import { type AvailableFeatures } from 'luca/feature'
4
+ import { Feature } from '../feature.js'
5
+ import { parse } from 'contentbase'
6
+ import type { DocsReader } from './docs-reader.js'
7
+ import Assistant from './assistant.js'
8
+
9
+ declare module 'luca/feature' {
10
+ interface AvailableFeatures {
11
+ skillsLibrary: typeof SkillsLibrary
12
+ }
13
+ }
14
+
15
+ export interface SkillInfo {
16
+ /** Skill name derived from folder name or frontmatter */
17
+ name: string
18
+ /** Description from frontmatter */
19
+ description: string
20
+ /** Absolute path to the skill folder (dirname of SKILL.md) */
21
+ path: string
22
+ /** Absolute path to SKILL.md */
23
+ skillFilePath: string
24
+ /** Which location this skill was found in */
25
+ locationPath: string
26
+ /** All frontmatter metadata */
27
+ meta: Record<string, unknown>
28
+ }
29
+
30
+ export const SkillsLibraryStateSchema = FeatureStateSchema.extend({
31
+ loaded: z.boolean().describe('Whether skill locations have been scanned'),
32
+ locations: z.array(z.string()).describe('Tracked skill location folder paths'),
33
+ skillCount: z.number().describe('Total number of discovered skills'),
34
+ skills: z.record(z.string(), z.any()).describe('Discovered skills keyed by name'),
35
+ })
36
+
37
+ export const SkillsLibraryOptionsSchema = FeatureOptionsSchema.extend({
38
+ configPath: z.string().optional().describe('Override path for skills.json (defaults to ~/.luca/skills.json)'),
39
+ only: z.array(z.string()).optional().describe('Glob patterns to filter which skills are exposed. When set, only matching skills are available. Supports * wildcards (e.g. "luca-*", "react-ink").'),
40
+ locations: z.array(z.string()).optional().describe('Additional skill location directories to scan for this instance only. Not persisted to skills.json — other consumers will not see these.'),
41
+ useAgentsFolders: z.boolean().optional().default(false).describe('When true, automatically scan conventional agent skill folders: .claude/skills and .agents/skills in both the home directory and project cwd.'),
42
+ })
43
+
44
+ export const SkillsLibraryEventsSchema = FeatureEventsSchema.extend({
45
+ started: z.tuple([]).describe('Fired after all skill locations have been scanned'),
46
+ locationAdded: z.tuple([z.string().describe('The absolute path of the added location')]).describe('Fired when a new skill location is registered'),
47
+ skillDiscovered: z.tuple([z.any().describe('The SkillInfo object')]).describe('Fired when a skill is discovered during scanning'),
48
+ }).describe('SkillsLibrary events')
49
+
50
+ export type SkillsLibraryState = z.infer<typeof SkillsLibraryStateSchema>
51
+ export type SkillsLibraryOptions = z.infer<typeof SkillsLibraryOptionsSchema>
52
+
53
+ /**
54
+ * Manages a registry of skill locations — folders containing SKILL.md files.
55
+ *
56
+ * Persists known locations to ~/.luca/skills.json and scans them on start.
57
+ * Each skill folder can be opened as a DocsReader for AI-assisted Q&A.
58
+ * Exposes tools for assistant integration via assistant.use(skillsLibrary).
59
+ *
60
+ * No paths are scanned by default — callers must explicitly provide locations
61
+ * via the `locations` option or `addLocation()`. Set `useAgentsFolders: true`
62
+ * to automatically scan conventional agent skill folders (.claude/skills and
63
+ * .agents/skills in both $HOME and cwd).
64
+ *
65
+ * @extends Feature
66
+ * @example
67
+ * ```typescript
68
+ * const lib = container.feature('skillsLibrary', { locations: ['./my-skills'] })
69
+ * await lib.start()
70
+ * lib.list() // => SkillInfo[]
71
+ *
72
+ * // Or opt in to conventional agent folders:
73
+ * const lib2 = container.feature('skillsLibrary', { useAgentsFolders: true })
74
+ * await lib2.start()
75
+ * ```
76
+ */
77
+ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOptions> {
78
+ static override stateSchema = SkillsLibraryStateSchema
79
+ static override optionsSchema = SkillsLibraryOptionsSchema
80
+ static override eventsSchema = SkillsLibraryEventsSchema
81
+ static override shortcut = 'features.skillsLibrary' as const
82
+
83
+ static { Feature.register(this, 'skillsLibrary') }
84
+
85
+ /** Tools for assistant integration via assistant.use(skillsLibrary). */
86
+ static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
87
+ searchAvailableSkills: {
88
+ schema: z.object({
89
+ query: z.string().optional().describe('A keyword or phrase to filter skills by name or description. Omit to list all available skills.'),
90
+ }).describe('Discover what skills are available. Call this first when you need specialized knowledge — skills are curated guides and reference material for specific domains (frameworks, tools, patterns). Returns skill names and descriptions so you can decide which to load.'),
91
+ },
92
+ loadSkill: {
93
+ schema: z.object({
94
+ skillName: z.string().describe('The exact skill name as returned by searchAvailableSkills'),
95
+ }).describe('Load a skill\'s full reference content (SKILL.md). This gives you detailed guidance, examples, and best practices for that domain. Load a skill before attempting work in an unfamiliar area — the content is curated to prevent common mistakes.'),
96
+ },
97
+ askSkillBasedQuestion: {
98
+ schema: z.object({
99
+ skillName: z.string().describe('The exact skill name to query'),
100
+ question: z.string().describe('A specific question about the skill\'s domain. Be precise — "how do I add a new feature to the container?" is better than "tell me about features".'),
101
+ }).describe('Ask a focused question about a skill\'s domain using AI-assisted document reading. Use this when you need a specific answer from a skill rather than reading the whole thing. More efficient than loadSkill for targeted lookups.'),
102
+ },
103
+ }
104
+
105
+ /** @returns Default state. */
106
+ override get initialState(): SkillsLibraryState {
107
+ return {
108
+ ...super.initialState,
109
+ started: false,
110
+ locations: [],
111
+ skillCount: 0,
112
+ skills: {},
113
+ }
114
+ }
115
+
116
+ override setupToolsConsumer(assistant: Feature) {
117
+ if (!(assistant instanceof Assistant)) {
118
+ throw new Error('Skills library tools require an Assistant instance (including subclasses).')
119
+ }
120
+
121
+ const a : Assistant = assistant as Assistant
122
+ const { container } = a
123
+ const skillsLibrary = this
124
+ const skillCount = Object.keys(this.filteredSkills).length
125
+ const isSmallSet = skillCount <= 10
126
+
127
+ if (!isSmallSet && !this.options.only) {
128
+ if (!process.env.LUCA_SKILLS_NO_WARN) {
129
+ container.feature('ui').print.yellow(`SkillsLibrary: ${skillCount} skills loaded with no \`only\` filter. Use container.feature('skillsLibrary', { only: ['pattern*'] }) to limit, or set LUCA_SKILLS_NO_WARN=1 to silence.`)
130
+ }
131
+ }
132
+
133
+ if (isSmallSet) {
134
+ const table = Object.entries(this.skillsTable)
135
+ .map(([name, desc]) => `- **${name}**: ${desc}`)
136
+ .join('\n')
137
+
138
+ a.addSystemPromptExtension('skillsLibrary', [
139
+ '## Skills Library',
140
+ '',
141
+ 'You have access to the following curated skills — domain-specific reference guides with examples, patterns, and best practices.',
142
+ '',
143
+ '**Available skills:**',
144
+ table,
145
+ '',
146
+ '**When to use skills:**',
147
+ '- When working in an unfamiliar domain or framework — load the skill before writing code',
148
+ '- When you see "Required Skills" in a message — load those skills immediately with `loadSkill` before answering',
149
+ '',
150
+ '**Workflow:** Choose a skill from the list above → `loadSkill` to get the full guide → follow its patterns. Use `askSkillBasedQuestion` for targeted lookups when you don\'t need the whole guide.',
151
+ '',
152
+ '**Skills are authoritative.** When a loaded skill contradicts your general knowledge, follow the skill — it reflects project-specific conventions and decisions.',
153
+ ].join('\n'))
154
+ } else {
155
+ a.addSystemPromptExtension('skillsLibrary', [
156
+ '## Skills Library',
157
+ '',
158
+ `You have access to a large library of ${skillCount} curated skills — domain-specific reference guides with examples, patterns, and best practices.`,
159
+ '',
160
+ '**When to use skills:**',
161
+ '- When working in an unfamiliar domain or framework — search for a skill before writing code',
162
+ '- When the user asks about a topic that might have a matching skill — search first',
163
+ '- When you see "Required Skills" in a message — load those skills immediately with `loadSkill` before answering',
164
+ '',
165
+ '**Workflow:** `searchAvailableSkills` → find relevant skill → `loadSkill` to get the full guide → follow its patterns. Use `askSkillBasedQuestion` for targeted lookups when you don\'t need the whole guide.',
166
+ '',
167
+ '**Skills are authoritative.** When a loaded skill contradicts your general knowledge, follow the skill — it reflects project-specific conventions and decisions.',
168
+ ].join('\n'))
169
+ }
170
+
171
+ const preloadSkills : string[] = []
172
+ if (a.meta.skills) {
173
+ if (Array.isArray(a.meta.skills)) {
174
+ preloadSkills.push(...a.meta.skills)
175
+ } else {
176
+ preloadSkills.push(a.meta.skills)
177
+ }
178
+ }
179
+
180
+ // Only use the fork-based auto-detection for small skill sets
181
+ if (isSmallSet) {
182
+ async function beforeAskCheckIfWeNeedSkills(ctx: any, next: any) {
183
+ const { question } = ctx
184
+ const skills = await skillsLibrary.findRelevantSkillsForAssistant(a, question as string)
185
+
186
+ const allSkillsToLoad : string[] = container.utils.lodash.uniq([
187
+ ...skills,
188
+ ...preloadSkills,
189
+ ])
190
+
191
+ if (allSkillsToLoad.length) {
192
+ ctx.question = `${ctx.question} \n\n## Required Skills\nYou will need to load the following skills to answer this question: ${allSkillsToLoad.join(', ')}`
193
+ }
194
+
195
+ a.interceptors.beforeAsk.remove(beforeAskCheckIfWeNeedSkills)
196
+
197
+ await next()
198
+ }
199
+
200
+ assistant.intercept('beforeAsk', beforeAskCheckIfWeNeedSkills as any)
201
+ }
202
+
203
+ return assistant
204
+ }
205
+
206
+ /** Discovered skills keyed by name (unfiltered). */
207
+ get skills(): Record<string, SkillInfo> {
208
+ return (this.state.get('skills') || {}) as Record<string, SkillInfo>
209
+ }
210
+
211
+ /** Skills filtered by the `only` option when set. */
212
+ get filteredSkills(): Record<string, SkillInfo> {
213
+ const all = this.skills
214
+ const only = this.options.only
215
+ if (!only || only.length === 0) return all
216
+
217
+ const result: Record<string, SkillInfo> = {}
218
+ for (const [name, info] of Object.entries(all)) {
219
+ if (only.some(pattern => this.matchPattern(pattern, name))) {
220
+ result[name] = info
221
+ }
222
+ }
223
+ return result
224
+ }
225
+
226
+ get availableSkills() {
227
+ return Object.keys(this.filteredSkills)
228
+ }
229
+
230
+ get skillsTable() : Record<string, string> {
231
+ const skills = this.filteredSkills
232
+
233
+ return Object.fromEntries(
234
+ Object.keys(skills).map((name) => [name, skills[name]!.description])
235
+ )
236
+ }
237
+
238
+ /** Match a name against a glob pattern (* wildcards). */
239
+ private matchPattern(pattern: string, name: string): boolean {
240
+ if (pattern === '*') return true
241
+ if (!pattern.includes('*')) return pattern === name
242
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')
243
+ return new RegExp(`^${escaped}$`).test(name)
244
+ }
245
+
246
+ /** Resolved path to the skills.json config file. */
247
+ get configPath(): string {
248
+ if (this.options.configPath) return this.options.configPath
249
+ const { os, paths } = this.container
250
+ return paths.resolve(os.homedir, '.luca', 'skills.json')
251
+ }
252
+
253
+ /** Whether the library has been loaded. */
254
+ get isStarted(): boolean {
255
+ return !!this.state.get('started')
256
+ }
257
+
258
+ /** Expand ~ to home directory in a path. */
259
+ private expandHome(p: string): string {
260
+ return p.replace(/^\~/, this.container.os.homedir)
261
+ }
262
+
263
+ /** Read the persisted config, creating it if it doesn't exist. */
264
+ private readConfig(): { locations: string[] } {
265
+ const { fs } = this.container
266
+
267
+ if (!fs.exists(this.configPath)) {
268
+ const defaultConfig = { locations: [] as string[] }
269
+ this.writeConfig(defaultConfig)
270
+ return defaultConfig
271
+ }
272
+
273
+ return fs.readJson(this.configPath)
274
+ }
275
+
276
+ /** Write the config back to disk. */
277
+ private writeConfig(config: { locations: string[] }): void {
278
+ const { fs, os, paths } = this.container
279
+ fs.mkdirp(paths.resolve(os.homedir, '.luca'))
280
+ fs.writeJson(this.configPath, config, 2)
281
+ }
282
+
283
+ /**
284
+ * Start the skills library: read config, scan all locations.
285
+ *
286
+ * @returns This instance for chaining
287
+ */
288
+ async start(): Promise<SkillsLibrary> {
289
+ if (this.isStarted) return this
290
+
291
+ const { uniq } = this.container.utils.lodash
292
+ const config = this.readConfig()
293
+ const configLocations = config.locations.map(l => this.expandHome(l))
294
+ const instanceLocations = (this.options.locations || []).map(l => this.expandHome(l))
295
+ const agentsFolders: string[] = []
296
+ if (this.options.useAgentsFolders) {
297
+ const { paths, os, cwd } = this.container as any
298
+ // Check conventional agent skill locations (home + project cwd)
299
+ const candidates = [
300
+ paths.resolve(os.homedir, '.claude', 'skills'),
301
+ paths.resolve(os.homedir, '.agents', 'skills'),
302
+ paths.resolve(cwd, '.claude', 'skills'),
303
+ paths.resolve(cwd, '.agents', 'skills'),
304
+ ]
305
+ agentsFolders.push(...candidates)
306
+ }
307
+
308
+ const allLocations = uniq([
309
+ ...configLocations,
310
+ ...instanceLocations,
311
+ ...agentsFolders,
312
+ ]).filter(Boolean).filter(l => (this.container as any).fs.exists(l))
313
+ this.state.set('locations', allLocations)
314
+
315
+ for (const loc of allLocations) {
316
+ await this.scanLocation(loc)
317
+ }
318
+
319
+ this.state.set('started', true)
320
+ this.state.set('skillCount', Object.keys(this.skills).length)
321
+ this.emit('started')
322
+
323
+ return this
324
+ }
325
+
326
+ /**
327
+ * Add a new skill location folder and scan it for skills.
328
+ *
329
+ * @param locationPath - Path to a directory containing skill subfolders with SKILL.md
330
+ */
331
+ async addLocation(locationPath: string): Promise<void> {
332
+ const resolved = this.expandHome(locationPath)
333
+ const current = this.state.get('locations') as string[]
334
+
335
+ if (current.includes(resolved)) return
336
+
337
+ const updated = [...current, resolved]
338
+ this.state.set('locations', updated)
339
+
340
+ // Persist — store the original (unexpanded) path for portability
341
+ const config = this.readConfig()
342
+ if (!config.locations.includes(locationPath)) {
343
+ config.locations.push(locationPath)
344
+ this.writeConfig(config)
345
+ }
346
+
347
+ await this.scanLocation(resolved)
348
+ this.state.set('skillCount', Object.keys(this.skills).length)
349
+ this.emit('locationAdded', resolved)
350
+ }
351
+
352
+ /**
353
+ * Remove a skill location and its skills from the library.
354
+ *
355
+ * @param locationPath - The location path to remove
356
+ */
357
+ async removeLocation(locationPath: string): Promise<void> {
358
+ const resolved = this.expandHome(locationPath)
359
+ const current = this.state.get('locations') as string[]
360
+ this.state.set('locations', current.filter(l => l !== resolved))
361
+
362
+ // Remove skills from this location
363
+ const remaining: Record<string, SkillInfo> = {}
364
+ for (const [name, info] of Object.entries(this.skills)) {
365
+ if (info.locationPath !== resolved) {
366
+ remaining[name] = info
367
+ }
368
+ }
369
+ this.state.set('skills', remaining)
370
+ this.state.set('skillCount', Object.keys(remaining).length)
371
+
372
+ // Persist
373
+ const config = this.readConfig()
374
+ config.locations = config.locations.filter(l => this.expandHome(l) !== resolved)
375
+ this.writeConfig(config)
376
+ }
377
+
378
+ /**
379
+ * Scan a location folder for skill subfolders containing SKILL.md.
380
+ *
381
+ * @param locationPath - Absolute path to scan
382
+ */
383
+ async scanLocation(locationPath: string): Promise<void> {
384
+ const { fs, paths } = this.container
385
+ if (!fs.exists(locationPath)) return
386
+
387
+ const entries = fs.readdirSync(locationPath)
388
+
389
+ for (const entry of entries) {
390
+ const skillDir = paths.resolve(locationPath, entry)
391
+ const skillFile = paths.resolve(skillDir, 'SKILL.md')
392
+
393
+ if (!fs.exists(skillFile)) continue
394
+
395
+ try {
396
+ const parsed = await parse(skillFile)
397
+ const meta = (parsed.meta || {}) as Record<string, unknown>
398
+ const name = entry
399
+
400
+ const info: SkillInfo = {
401
+ name,
402
+ description: (meta.description as string) || '',
403
+ path: skillDir,
404
+ skillFilePath: skillFile,
405
+ locationPath,
406
+ meta,
407
+ }
408
+
409
+ this.state.set('skills', { ...this.skills, [name]: info })
410
+ this.emit('skillDiscovered', info)
411
+ } catch {
412
+ // Skip unparseable skill files
413
+ }
414
+ }
415
+ }
416
+
417
+ /** Return all discovered skills (respects the `only` filter). */
418
+ list(): SkillInfo[] {
419
+ return Object.values(this.filteredSkills)
420
+ }
421
+
422
+ /** Find a skill by name. */
423
+ find(skillName: string): SkillInfo | undefined {
424
+ return this.skills[skillName]
425
+ }
426
+
427
+ /**
428
+ * Create a DocsReader for a skill's folder, enabling AI-assisted Q&A.
429
+ *
430
+ * @param skillName - Name of the skill to create a reader for
431
+ * @returns A DocsReader instance rooted at the skill's folder
432
+ */
433
+ createSkillReader(skillName: string): DocsReader {
434
+ const skill = this.find(skillName)
435
+ if (!skill) throw new Error(`Skill "${skillName}" not found in the library`)
436
+
437
+ return this.container.feature('docsReader', { contentDb: skill.path })
438
+ }
439
+
440
+ /**
441
+ * Create a tmp directory containing symlinked/copied skill folders by name,
442
+ * suitable for passing to claude --add-dir.
443
+ *
444
+ * @param skillNames - Array of skill names to include
445
+ * @returns Absolute path to the created directory
446
+ */
447
+ ensureFolderCreatedWithSkillsByName(skillNames: string[]): string {
448
+ const { fs, paths, os } = this.container
449
+ const hash = this.container.utils.hashObject(skillNames.sort())
450
+ const dir = paths.resolve(os.tmpdir, 'luca-skills', hash)
451
+
452
+ if (fs.exists(dir)) return dir
453
+
454
+ fs.mkdirp(dir)
455
+
456
+ for (const name of skillNames) {
457
+ const skill = this.find(name)
458
+ if (!skill) throw new Error(`Skill "${name}" not found in the library`)
459
+
460
+ const dest = paths.resolve(dir, name)
461
+ if (!fs.exists(dest)) {
462
+ fs.copy(skill.path, dest)
463
+ }
464
+ }
465
+
466
+ return dir
467
+ }
468
+
469
+ // --- Tool handlers for assistant.use(skillsLibrary) ---
470
+
471
+ /** Search available skills, optionally filtered by a query string. Respects the `only` filter. */
472
+ async searchAvailableSkills({ query }: { query?: string } = {}): Promise<string> {
473
+ if (!this.isStarted) await this.start()
474
+
475
+ let skills = Object.values(this.filteredSkills)
476
+
477
+ if (query) {
478
+ const q = query.toLowerCase()
479
+ skills = skills.filter(s =>
480
+ s.name.toLowerCase().includes(q) ||
481
+ s.description.toLowerCase().includes(q)
482
+ )
483
+ }
484
+
485
+ if (skills.length === 0) return 'No skills found.'
486
+
487
+ return skills.map(s => `- **${s.name}**: ${s.description || '(no description)'}\n Path: ${s.path}`).join('\n')
488
+ }
489
+
490
+ /** Load a skill's full SKILL.md content and metadata. */
491
+ async loadSkill({ skillName }: { skillName: string }): Promise<string> {
492
+ if (!this.isStarted) await this.start()
493
+
494
+ const skill = this.find(skillName)
495
+ if (!skill) return `Skill "${skillName}" not found.`
496
+
497
+ const content = this.container.fs.readFile(skill.skillFilePath)
498
+
499
+ return `# Skill: ${skill.name}\n\n**Description:** ${skill.description}\n**Path:** ${skill.path}\n\n---\n\n${content}`
500
+ }
501
+
502
+ /** Ask a question about a specific skill using a DocsReader. */
503
+ async askSkillBasedQuestion({ skillName, question }: { skillName: string; question: string }): Promise<string> {
504
+ if (!this.isStarted) await this.start()
505
+
506
+ const reader = this.createSkillReader(skillName)
507
+ const answer = await reader.ask(question)
508
+ return answer
509
+ }
510
+
511
+ /**
512
+ * Fork the given assistant and ask it which skills (if any) are relevant
513
+ * to the user's query. Returns an array of skill names that should be loaded
514
+ * before the real question is answered.
515
+ *
516
+ * The fork is ephemeral (historyMode: 'none') and uses structured output so
517
+ * the result is always a clean string array — never free text.
518
+ *
519
+ * @param assistant - The assistant instance to fork
520
+ * @param userQuery - The user's original question
521
+ * @returns Array of skill names relevant to the query (may be empty)
522
+ */
523
+ async findRelevantSkillsForAssistant(assistant: Assistant, userQuery: string): Promise<string[]> {
524
+ if (!this.isStarted) await this.start()
525
+
526
+ const skills = this.list()
527
+ if (skills.length === 0) return []
528
+
529
+ const responseSchema = z.object({
530
+ skills: z.array(z.string()).describe('Names of skills relevant to the query. Empty array if none apply.'),
531
+ })
532
+
533
+ const skillsDescription = Object.entries(this.skillsTable)
534
+ .map(([title,description]) => `- **${title}**: ${description}`)
535
+ .join("\n")
536
+
537
+ const prompt = this.container.ui.endent(`You are a routing assistant. Given a user query and a list of available skills, determine which skills (if any) should be loaded to help answer the query.
538
+ Available skills:
539
+ -------
540
+ ${skillsDescription}
541
+
542
+ User query: ${userQuery}
543
+
544
+ Return only the skill names that are directly relevant. Return an empty array if none apply. Do not load skills speculatively — only include ones that would materially help answer this specific query.`)
545
+
546
+ const fork = assistant.conversation.fork()
547
+ const result = await fork.ask(prompt, { schema: responseSchema }) as unknown as { skills: string[] }
548
+
549
+ const found = result.skills.filter(name => this.find(name) !== undefined)
550
+
551
+ this.emit('foundSkills', found, assistant, userQuery)
552
+
553
+ return found
554
+ }
555
+ }
556
+
557
+ export default SkillsLibrary
@@ -0,0 +1,6 @@
1
+ import container from './container.server'
2
+ import type { AGIContainer } from './container.server'
3
+
4
+ export * from './container.server'
5
+
6
+ export default container as AGIContainer
@@ -0,0 +1,89 @@
1
+ /**
2
+ * A composable middleware chain. Each interceptor receives a mutable
3
+ * context and a `next` function. Calling `next()` continues the chain;
4
+ * skipping it short-circuits.
5
+ */
6
+
7
+ export type InterceptorFn<T> = (ctx: T, next: () => Promise<void>) => Promise<void>
8
+
9
+ export class InterceptorChain<T> {
10
+ private fns: InterceptorFn<T>[] = []
11
+
12
+ add(fn: InterceptorFn<T>): void {
13
+ this.fns.push(fn)
14
+ }
15
+
16
+ remove(fn: InterceptorFn<T>): void {
17
+ const idx = this.fns.indexOf(fn)
18
+ if (idx !== -1) this.fns.splice(idx, 1)
19
+ }
20
+
21
+ get hasInterceptors(): boolean {
22
+ return this.fns.length > 0
23
+ }
24
+
25
+ get size(): number {
26
+ return this.fns.length
27
+ }
28
+
29
+ clear(): void {
30
+ this.fns = []
31
+ }
32
+
33
+ clone(): InterceptorChain<T> {
34
+ const copy = new InterceptorChain<T>()
35
+ for (const fn of this.fns) copy.add(fn)
36
+ return copy
37
+ }
38
+
39
+ async run(ctx: T, final: () => Promise<void>): Promise<void> {
40
+ let index = 0
41
+ const fns = this.fns
42
+
43
+ const next = async (): Promise<void> => {
44
+ if (index < fns.length) {
45
+ const fn = fns[index++]!
46
+ await fn(ctx, next)
47
+ } else {
48
+ await final()
49
+ }
50
+ }
51
+
52
+ await next()
53
+ }
54
+ }
55
+
56
+ export interface BeforeAskCtx {
57
+ question: string | any[]
58
+ options?: any
59
+ result?: string
60
+ }
61
+
62
+ export interface ToolCallCtx {
63
+ name: string
64
+ args: Record<string, any>
65
+ result?: string
66
+ error?: any
67
+ skip?: boolean
68
+ }
69
+
70
+ export interface BeforeResponseCtx {
71
+ text: string
72
+ }
73
+
74
+ export interface BeforeTurnCtx {
75
+ turn: number
76
+ isFollowUp: boolean
77
+ messages: any[]
78
+ skip?: boolean
79
+ }
80
+
81
+ export interface InterceptorPoints {
82
+ beforeAsk: BeforeAskCtx
83
+ beforeTurn: BeforeTurnCtx
84
+ beforeToolCall: ToolCallCtx
85
+ afterToolCall: ToolCallCtx
86
+ beforeResponse: BeforeResponseCtx
87
+ }
88
+
89
+ export type InterceptorPoint = keyof InterceptorPoints