@soederpop/luca 0.1.2 → 0.2.1

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 (381) hide show
  1. package/.github/workflows/release.yaml +167 -0
  2. package/CLAUDE.md +2 -0
  3. package/README.md +3 -0
  4. package/assistants/codingAssistant/ABOUT.md +3 -0
  5. package/assistants/codingAssistant/CORE.md +22 -17
  6. package/assistants/codingAssistant/hooks.ts +17 -4
  7. package/assistants/codingAssistant/tools.ts +1 -106
  8. package/assistants/inkbot/ABOUT.md +5 -0
  9. package/assistants/inkbot/CORE.md +71 -0
  10. package/assistants/inkbot/hooks.ts +14 -0
  11. package/assistants/inkbot/tools.ts +47 -0
  12. package/bun.lock +20 -4
  13. package/commands/inkbot.ts +353 -0
  14. package/commands/release.ts +75 -181
  15. package/dist/agi/container.server.d.ts +63 -0
  16. package/dist/agi/container.server.d.ts.map +1 -0
  17. package/dist/agi/endpoints/ask.d.ts +20 -0
  18. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  19. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  20. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  21. package/dist/agi/endpoints/conversations.d.ts +18 -0
  22. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  23. package/dist/agi/endpoints/experts.d.ts +8 -0
  24. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  25. package/dist/agi/feature.d.ts +9 -0
  26. package/dist/agi/feature.d.ts.map +1 -0
  27. package/dist/agi/features/assistant.d.ts +509 -0
  28. package/dist/agi/features/assistant.d.ts.map +1 -0
  29. package/dist/agi/features/assistants-manager.d.ts +236 -0
  30. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  31. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  32. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  33. package/dist/agi/features/browser-use.d.ts +479 -0
  34. package/dist/agi/features/browser-use.d.ts.map +1 -0
  35. package/dist/agi/features/claude-code.d.ts +824 -0
  36. package/dist/agi/features/claude-code.d.ts.map +1 -0
  37. package/dist/agi/features/conversation-history.d.ts +245 -0
  38. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  39. package/dist/agi/features/conversation.d.ts +464 -0
  40. package/dist/agi/features/conversation.d.ts.map +1 -0
  41. package/dist/agi/features/docs-reader.d.ts +72 -0
  42. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  43. package/dist/agi/features/file-tools.d.ts +110 -0
  44. package/dist/agi/features/file-tools.d.ts.map +1 -0
  45. package/dist/agi/features/luca-coder.d.ts +323 -0
  46. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  47. package/dist/agi/features/openai-codex.d.ts +381 -0
  48. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  49. package/dist/agi/features/openapi.d.ts +200 -0
  50. package/dist/agi/features/openapi.d.ts.map +1 -0
  51. package/dist/agi/features/skills-library.d.ts +167 -0
  52. package/dist/agi/features/skills-library.d.ts.map +1 -0
  53. package/dist/agi/index.d.ts +5 -0
  54. package/dist/agi/index.d.ts.map +1 -0
  55. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  56. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  57. package/dist/agi/lib/token-counter.d.ts +13 -0
  58. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  59. package/dist/bootstrap/generated.d.ts +5 -0
  60. package/dist/bootstrap/generated.d.ts.map +1 -0
  61. package/dist/browser.d.ts +12 -0
  62. package/dist/browser.d.ts.map +1 -0
  63. package/dist/bus.d.ts +29 -0
  64. package/dist/bus.d.ts.map +1 -0
  65. package/dist/cli/build-info.d.ts +4 -0
  66. package/dist/cli/build-info.d.ts.map +1 -0
  67. package/dist/cli/cli.d.ts +3 -0
  68. package/dist/cli/cli.d.ts.map +1 -0
  69. package/dist/client.d.ts +60 -0
  70. package/dist/client.d.ts.map +1 -0
  71. package/dist/clients/civitai/index.d.ts +472 -0
  72. package/dist/clients/civitai/index.d.ts.map +1 -0
  73. package/dist/clients/client-template.d.ts +30 -0
  74. package/dist/clients/client-template.d.ts.map +1 -0
  75. package/dist/clients/comfyui/index.d.ts +281 -0
  76. package/dist/clients/comfyui/index.d.ts.map +1 -0
  77. package/dist/clients/elevenlabs/index.d.ts +197 -0
  78. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  79. package/dist/clients/graph.d.ts +64 -0
  80. package/dist/clients/graph.d.ts.map +1 -0
  81. package/dist/clients/openai/index.d.ts +247 -0
  82. package/dist/clients/openai/index.d.ts.map +1 -0
  83. package/dist/clients/rest.d.ts +92 -0
  84. package/dist/clients/rest.d.ts.map +1 -0
  85. package/dist/clients/supabase/index.d.ts +176 -0
  86. package/dist/clients/supabase/index.d.ts.map +1 -0
  87. package/dist/clients/websocket.d.ts +127 -0
  88. package/dist/clients/websocket.d.ts.map +1 -0
  89. package/dist/command.d.ts +163 -0
  90. package/dist/command.d.ts.map +1 -0
  91. package/dist/commands/bootstrap.d.ts +20 -0
  92. package/dist/commands/bootstrap.d.ts.map +1 -0
  93. package/dist/commands/chat.d.ts +37 -0
  94. package/dist/commands/chat.d.ts.map +1 -0
  95. package/dist/commands/code.d.ts +28 -0
  96. package/dist/commands/code.d.ts.map +1 -0
  97. package/dist/commands/console.d.ts +22 -0
  98. package/dist/commands/console.d.ts.map +1 -0
  99. package/dist/commands/describe.d.ts +50 -0
  100. package/dist/commands/describe.d.ts.map +1 -0
  101. package/dist/commands/eval.d.ts +23 -0
  102. package/dist/commands/eval.d.ts.map +1 -0
  103. package/dist/commands/help.d.ts +25 -0
  104. package/dist/commands/help.d.ts.map +1 -0
  105. package/dist/commands/index.d.ts +18 -0
  106. package/dist/commands/index.d.ts.map +1 -0
  107. package/dist/commands/introspect.d.ts +24 -0
  108. package/dist/commands/introspect.d.ts.map +1 -0
  109. package/dist/commands/mcp.d.ts +35 -0
  110. package/dist/commands/mcp.d.ts.map +1 -0
  111. package/dist/commands/prompt.d.ts +38 -0
  112. package/dist/commands/prompt.d.ts.map +1 -0
  113. package/dist/commands/run.d.ts +24 -0
  114. package/dist/commands/run.d.ts.map +1 -0
  115. package/dist/commands/sandbox-mcp.d.ts +34 -0
  116. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  117. package/dist/commands/save-api-docs.d.ts +21 -0
  118. package/dist/commands/save-api-docs.d.ts.map +1 -0
  119. package/dist/commands/scaffold.d.ts +24 -0
  120. package/dist/commands/scaffold.d.ts.map +1 -0
  121. package/dist/commands/select.d.ts +22 -0
  122. package/dist/commands/select.d.ts.map +1 -0
  123. package/dist/commands/serve.d.ts +29 -0
  124. package/dist/commands/serve.d.ts.map +1 -0
  125. package/dist/container-describer.d.ts +144 -0
  126. package/dist/container-describer.d.ts.map +1 -0
  127. package/dist/container.d.ts +451 -0
  128. package/dist/container.d.ts.map +1 -0
  129. package/dist/endpoint.d.ts +113 -0
  130. package/dist/endpoint.d.ts.map +1 -0
  131. package/dist/feature.d.ts +47 -0
  132. package/dist/feature.d.ts.map +1 -0
  133. package/dist/graft.d.ts +29 -0
  134. package/dist/graft.d.ts.map +1 -0
  135. package/dist/hash-object.d.ts +8 -0
  136. package/dist/hash-object.d.ts.map +1 -0
  137. package/dist/helper.d.ts +209 -0
  138. package/dist/helper.d.ts.map +1 -0
  139. package/dist/introspection/generated.node.d.ts +44623 -0
  140. package/dist/introspection/generated.node.d.ts.map +1 -0
  141. package/dist/introspection/generated.web.d.ts +1412 -0
  142. package/dist/introspection/generated.web.d.ts.map +1 -0
  143. package/dist/introspection/index.d.ts +156 -0
  144. package/dist/introspection/index.d.ts.map +1 -0
  145. package/dist/introspection/scan.d.ts +147 -0
  146. package/dist/introspection/scan.d.ts.map +1 -0
  147. package/dist/node/container.d.ts +256 -0
  148. package/dist/node/container.d.ts.map +1 -0
  149. package/dist/node/feature.d.ts +9 -0
  150. package/dist/node/feature.d.ts.map +1 -0
  151. package/dist/node/features/container-link.d.ts +213 -0
  152. package/dist/node/features/container-link.d.ts.map +1 -0
  153. package/dist/node/features/content-db.d.ts +354 -0
  154. package/dist/node/features/content-db.d.ts.map +1 -0
  155. package/dist/node/features/disk-cache.d.ts +236 -0
  156. package/dist/node/features/disk-cache.d.ts.map +1 -0
  157. package/dist/node/features/dns.d.ts +511 -0
  158. package/dist/node/features/dns.d.ts.map +1 -0
  159. package/dist/node/features/docker.d.ts +485 -0
  160. package/dist/node/features/docker.d.ts.map +1 -0
  161. package/dist/node/features/downloader.d.ts +73 -0
  162. package/dist/node/features/downloader.d.ts.map +1 -0
  163. package/dist/node/features/figlet-fonts.d.ts +4 -0
  164. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  165. package/dist/node/features/file-manager.d.ts +177 -0
  166. package/dist/node/features/file-manager.d.ts.map +1 -0
  167. package/dist/node/features/fs.d.ts +635 -0
  168. package/dist/node/features/fs.d.ts.map +1 -0
  169. package/dist/node/features/git.d.ts +329 -0
  170. package/dist/node/features/git.d.ts.map +1 -0
  171. package/dist/node/features/google-auth.d.ts +200 -0
  172. package/dist/node/features/google-auth.d.ts.map +1 -0
  173. package/dist/node/features/google-calendar.d.ts +194 -0
  174. package/dist/node/features/google-calendar.d.ts.map +1 -0
  175. package/dist/node/features/google-docs.d.ts +138 -0
  176. package/dist/node/features/google-docs.d.ts.map +1 -0
  177. package/dist/node/features/google-drive.d.ts +202 -0
  178. package/dist/node/features/google-drive.d.ts.map +1 -0
  179. package/dist/node/features/google-mail.d.ts +221 -0
  180. package/dist/node/features/google-mail.d.ts.map +1 -0
  181. package/dist/node/features/google-sheets.d.ts +157 -0
  182. package/dist/node/features/google-sheets.d.ts.map +1 -0
  183. package/dist/node/features/grep.d.ts +207 -0
  184. package/dist/node/features/grep.d.ts.map +1 -0
  185. package/dist/node/features/helpers.d.ts +236 -0
  186. package/dist/node/features/helpers.d.ts.map +1 -0
  187. package/dist/node/features/ink.d.ts +332 -0
  188. package/dist/node/features/ink.d.ts.map +1 -0
  189. package/dist/node/features/ipc-socket.d.ts +298 -0
  190. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  191. package/dist/node/features/json-tree.d.ts +140 -0
  192. package/dist/node/features/json-tree.d.ts.map +1 -0
  193. package/dist/node/features/networking.d.ts +373 -0
  194. package/dist/node/features/networking.d.ts.map +1 -0
  195. package/dist/node/features/nlp.d.ts +125 -0
  196. package/dist/node/features/nlp.d.ts.map +1 -0
  197. package/dist/node/features/opener.d.ts +93 -0
  198. package/dist/node/features/opener.d.ts.map +1 -0
  199. package/dist/node/features/os.d.ts +168 -0
  200. package/dist/node/features/os.d.ts.map +1 -0
  201. package/dist/node/features/package-finder.d.ts +419 -0
  202. package/dist/node/features/package-finder.d.ts.map +1 -0
  203. package/dist/node/features/postgres.d.ts +173 -0
  204. package/dist/node/features/postgres.d.ts.map +1 -0
  205. package/dist/node/features/proc.d.ts +285 -0
  206. package/dist/node/features/proc.d.ts.map +1 -0
  207. package/dist/node/features/process-manager.d.ts +427 -0
  208. package/dist/node/features/process-manager.d.ts.map +1 -0
  209. package/dist/node/features/python.d.ts +477 -0
  210. package/dist/node/features/python.d.ts.map +1 -0
  211. package/dist/node/features/redis.d.ts +247 -0
  212. package/dist/node/features/redis.d.ts.map +1 -0
  213. package/dist/node/features/repl.d.ts +84 -0
  214. package/dist/node/features/repl.d.ts.map +1 -0
  215. package/dist/node/features/runpod.d.ts +527 -0
  216. package/dist/node/features/runpod.d.ts.map +1 -0
  217. package/dist/node/features/secure-shell.d.ts +145 -0
  218. package/dist/node/features/secure-shell.d.ts.map +1 -0
  219. package/dist/node/features/semantic-search.d.ts +207 -0
  220. package/dist/node/features/semantic-search.d.ts.map +1 -0
  221. package/dist/node/features/sqlite.d.ts +180 -0
  222. package/dist/node/features/sqlite.d.ts.map +1 -0
  223. package/dist/node/features/telegram.d.ts +173 -0
  224. package/dist/node/features/telegram.d.ts.map +1 -0
  225. package/dist/node/features/transpiler.d.ts +51 -0
  226. package/dist/node/features/transpiler.d.ts.map +1 -0
  227. package/dist/node/features/tts.d.ts +108 -0
  228. package/dist/node/features/tts.d.ts.map +1 -0
  229. package/dist/node/features/ui.d.ts +562 -0
  230. package/dist/node/features/ui.d.ts.map +1 -0
  231. package/dist/node/features/vault.d.ts +90 -0
  232. package/dist/node/features/vault.d.ts.map +1 -0
  233. package/dist/node/features/vm.d.ts +285 -0
  234. package/dist/node/features/vm.d.ts.map +1 -0
  235. package/dist/node/features/yaml-tree.d.ts +118 -0
  236. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  237. package/dist/node/features/yaml.d.ts +127 -0
  238. package/dist/node/features/yaml.d.ts.map +1 -0
  239. package/dist/node.d.ts +67 -0
  240. package/dist/node.d.ts.map +1 -0
  241. package/dist/python/generated.d.ts +2 -0
  242. package/dist/python/generated.d.ts.map +1 -0
  243. package/dist/react/index.d.ts +36 -0
  244. package/dist/react/index.d.ts.map +1 -0
  245. package/dist/registry.d.ts +97 -0
  246. package/dist/registry.d.ts.map +1 -0
  247. package/dist/scaffolds/generated.d.ts +13 -0
  248. package/dist/scaffolds/generated.d.ts.map +1 -0
  249. package/dist/scaffolds/template.d.ts +11 -0
  250. package/dist/scaffolds/template.d.ts.map +1 -0
  251. package/dist/schemas/base.d.ts +254 -0
  252. package/dist/schemas/base.d.ts.map +1 -0
  253. package/dist/selector.d.ts +130 -0
  254. package/dist/selector.d.ts.map +1 -0
  255. package/dist/server.d.ts +89 -0
  256. package/dist/server.d.ts.map +1 -0
  257. package/dist/servers/express.d.ts +104 -0
  258. package/dist/servers/express.d.ts.map +1 -0
  259. package/dist/servers/mcp.d.ts +201 -0
  260. package/dist/servers/mcp.d.ts.map +1 -0
  261. package/dist/servers/socket.d.ts +121 -0
  262. package/dist/servers/socket.d.ts.map +1 -0
  263. package/dist/state.d.ts +24 -0
  264. package/dist/state.d.ts.map +1 -0
  265. package/dist/web/clients/socket.d.ts +37 -0
  266. package/dist/web/clients/socket.d.ts.map +1 -0
  267. package/dist/web/container.d.ts +55 -0
  268. package/dist/web/container.d.ts.map +1 -0
  269. package/dist/web/extension.d.ts +4 -0
  270. package/dist/web/extension.d.ts.map +1 -0
  271. package/dist/web/feature.d.ts +8 -0
  272. package/dist/web/feature.d.ts.map +1 -0
  273. package/dist/web/features/asset-loader.d.ts +35 -0
  274. package/dist/web/features/asset-loader.d.ts.map +1 -0
  275. package/dist/web/features/container-link.d.ts +167 -0
  276. package/dist/web/features/container-link.d.ts.map +1 -0
  277. package/dist/web/features/esbuild.d.ts +51 -0
  278. package/dist/web/features/esbuild.d.ts.map +1 -0
  279. package/dist/web/features/helpers.d.ts +140 -0
  280. package/dist/web/features/helpers.d.ts.map +1 -0
  281. package/dist/web/features/network.d.ts +69 -0
  282. package/dist/web/features/network.d.ts.map +1 -0
  283. package/dist/web/features/speech.d.ts +71 -0
  284. package/dist/web/features/speech.d.ts.map +1 -0
  285. package/dist/web/features/vault.d.ts +62 -0
  286. package/dist/web/features/vault.d.ts.map +1 -0
  287. package/dist/web/features/vm.d.ts +48 -0
  288. package/dist/web/features/vm.d.ts.map +1 -0
  289. package/dist/web/features/voice-recognition.d.ts +96 -0
  290. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  291. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  292. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  293. package/docs/apis/features/agi/assistant.md +1 -0
  294. package/docs/apis/features/agi/assistants-manager.md +62 -2
  295. package/docs/apis/features/agi/auto-assistant.md +11 -109
  296. package/docs/apis/features/agi/claude-code.md +138 -0
  297. package/docs/apis/features/agi/conversation.md +60 -31
  298. package/docs/apis/features/agi/luca-coder.md +407 -0
  299. package/docs/apis/features/agi/openapi.md +2 -2
  300. package/docs/apis/features/agi/skills-library.md +12 -0
  301. package/docs/apis/features/node/python.md +81 -11
  302. package/docs/apis/features/node/transpiler.md +74 -0
  303. package/docs/apis/features/web/esbuild.md +0 -6
  304. package/docs/apis/servers/mcp.md +2 -2
  305. package/docs/examples/entity.md +124 -0
  306. package/docs/ideas/assistant-factory-pattern.md +142 -0
  307. package/package.json +74 -21
  308. package/src/agi/container.server.ts +10 -0
  309. package/src/agi/feature.ts +13 -0
  310. package/src/agi/features/agent-memory.ts +694 -0
  311. package/src/agi/features/assistant.ts +37 -26
  312. package/src/agi/features/assistants-manager.ts +95 -5
  313. package/src/agi/features/autonomous-assistant.ts +1 -5
  314. package/src/agi/features/browser-use.ts +32 -2
  315. package/src/agi/features/claude-code.ts +165 -1
  316. package/src/agi/features/coding-tools.ts +175 -0
  317. package/src/agi/features/conversation-history.ts +2 -6
  318. package/src/agi/features/conversation.ts +95 -3
  319. package/src/agi/features/docs-reader.ts +2 -1
  320. package/src/agi/features/file-tools.ts +35 -28
  321. package/src/agi/features/luca-coder.ts +1 -5
  322. package/src/agi/features/openai-codex.ts +1 -1
  323. package/src/agi/features/openapi.ts +3 -3
  324. package/src/agi/features/skills-library.ts +111 -13
  325. package/src/agi/lib/interceptor-chain.ts +10 -0
  326. package/src/agi/lib/token-counter.ts +1 -1
  327. package/src/bootstrap/generated.ts +126 -1
  328. package/src/bus.ts +27 -5
  329. package/src/cli/build-info.ts +2 -2
  330. package/src/client.ts +2 -2
  331. package/src/clients/elevenlabs/index.ts +5 -0
  332. package/src/clients/voicebox/index.ts +300 -0
  333. package/src/commands/bootstrap.ts +2 -1
  334. package/src/commands/chat.ts +1 -0
  335. package/src/commands/code.ts +4 -2
  336. package/src/commands/prompt.ts +34 -34
  337. package/src/commands/sandbox-mcp.ts +69 -163
  338. package/src/commands/save-api-docs.ts +10 -8
  339. package/src/commands/select.ts +8 -3
  340. package/src/container-describer.ts +70 -84
  341. package/src/container.ts +93 -3
  342. package/src/endpoint.ts +1 -1
  343. package/src/entity.ts +173 -0
  344. package/src/feature.ts +3 -3
  345. package/src/helper.ts +8 -4
  346. package/src/introspection/generated.agi.ts +3012 -1356
  347. package/src/introspection/generated.node.ts +179 -33
  348. package/src/introspection/generated.web.ts +95 -3
  349. package/src/introspection/scan.ts +1 -1
  350. package/src/node/container.ts +1 -1
  351. package/src/node/features/content-db.ts +57 -30
  352. package/src/node/features/file-manager.ts +10 -9
  353. package/src/node/features/git.ts +5 -5
  354. package/src/node/features/helpers.ts +1 -1
  355. package/src/node/features/json-tree.ts +1 -1
  356. package/src/node/features/os.ts +3 -3
  357. package/src/node/features/package-finder.ts +1 -1
  358. package/src/node/features/process-manager.ts +51 -18
  359. package/src/node/features/python.ts +3 -3
  360. package/src/node/features/redis.ts +1 -1
  361. package/src/node/features/repl.ts +2 -2
  362. package/src/node/features/transpiler.ts +2 -2
  363. package/src/node/features/ui.ts +1 -1
  364. package/src/node/features/vm.ts +3 -3
  365. package/src/node/features/yaml-tree.ts +1 -1
  366. package/src/node.ts +1 -0
  367. package/src/python/generated.ts +1 -1
  368. package/src/scaffolds/generated.ts +1 -1
  369. package/src/selector.ts +74 -4
  370. package/src/server.ts +2 -2
  371. package/src/servers/mcp.ts +6 -6
  372. package/src/web/features/helpers.ts +1 -1
  373. package/src/web/features/network.ts +1 -0
  374. package/test/assistant.test.ts +14 -5
  375. package/test/conversation.test.ts +220 -0
  376. package/test-integration/memory.test.ts +204 -0
  377. package/tsconfig.build.json +12 -0
  378. package/tsconfig.json +1 -1
  379. package/scripts/examples/telegram-ink-ui.ts +0 -302
  380. package/scripts/examples/using-openai-codex.ts +0 -23
  381. package/scripts/examples/vm-loading-esm-modules.ts +0 -16
@@ -5,6 +5,7 @@ import { z } from 'zod'
5
5
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
6
6
  import { realpathSync } from 'node:fs'
7
7
  import type { GrepOptions } from './grep.js'
8
+ import type { Helper } from '../../helper.js'
8
9
 
9
10
  export const ContentDbStateSchema = FeatureStateSchema.extend({
10
11
  loaded: z.boolean().default(false).describe('Whether the content collection has been loaded and parsed'),
@@ -46,70 +47,96 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
46
47
  static { Feature.register(this, 'contentDb') }
47
48
 
48
49
  /** Tools that any assistant can use to progressively explore this collection. */
49
- static tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
50
+ static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
50
51
  getCollectionOverview: {
51
52
  schema: z.object({}).describe(
52
- 'Get a high-level overview of the document collection: models, document counts, directory tree, and search index status. Call this first to understand what is available.'
53
+ 'Get a high-level overview of the document collection: what models exist, how many documents each has, the directory tree, and search index status. Call this FIRST to understand the collection before exploring individual documents.'
53
54
  ),
54
55
  },
55
56
  listDocuments: {
56
57
  schema: z.object({
57
- model: z.string().optional().describe('Filter to documents belonging to this model name'),
58
- glob: z.string().optional().describe('Glob pattern to filter document path IDs (e.g. "guides/*", "apis/**")'),
58
+ model: z.string().optional().describe('Filter to documents of this model type (e.g. "Plan", "Task"). Get model names from getCollectionOverview.'),
59
+ glob: z.string().optional().describe('Glob pattern to filter by document path (e.g. "guides/*", "apis/**/*.md")'),
59
60
  }).describe(
60
- 'List available document IDs in the collection, optionally filtered by model or glob pattern.'
61
+ 'List document IDs in the collection. Use this to browse what\'s available before reading. Filter by model or glob to narrow results.'
61
62
  ),
62
63
  },
63
64
  readDocument: {
64
65
  schema: z.object({
65
- id: z.string().describe('The document path ID to read (e.g. "guides/intro")'),
66
- include: z.array(z.string()).optional().describe('Only return these section headings'),
67
- exclude: z.array(z.string()).optional().describe('Remove these section headings from the output'),
68
- meta: z.boolean().optional().describe('Include YAML frontmatter in the output'),
66
+ id: z.string().describe('The document path ID (e.g. "guides/intro", "apis/auth"). Get valid IDs from listDocuments.'),
67
+ include: z.array(z.string()).optional().describe('Only return sections with these headings. Use to read specific parts of long documents without loading everything.'),
68
+ exclude: z.array(z.string()).optional().describe('Skip sections with these headings. Use to filter out irrelevant parts.'),
69
+ meta: z.boolean().optional().describe('Include the YAML frontmatter (title, status, tags, etc.) in the output. Useful for understanding document metadata.'),
69
70
  }).describe(
70
- 'Read a single document by its path ID. Use include/exclude to read only specific sections and avoid loading unnecessary content.'
71
+ 'Read a document by its path ID. Use include/exclude to request only the sections you need don\'t load an entire document when you only need one section.'
71
72
  ),
72
73
  },
73
74
  readMultipleDocuments: {
74
75
  schema: z.object({
75
- ids: z.array(z.string()).describe('Array of document path IDs to read'),
76
- include: z.array(z.string()).optional().describe('Only return these section headings from each document'),
77
- exclude: z.array(z.string()).optional().describe('Remove these section headings from each document'),
78
- meta: z.boolean().optional().describe('Include YAML frontmatter in the output'),
76
+ ids: z.array(z.string()).describe('Array of document path IDs to read. Get valid IDs from listDocuments.'),
77
+ include: z.array(z.string()).optional().describe('Only return sections with these headings from each document.'),
78
+ exclude: z.array(z.string()).optional().describe('Skip sections with these headings from each document.'),
79
+ meta: z.boolean().optional().describe('Include YAML frontmatter for each document.'),
79
80
  }).describe(
80
- 'Read multiple documents at once, concatenated with dividers. Use include/exclude to focus on relevant sections.'
81
+ 'Read multiple documents in one call. More efficient than calling readDocument in a loop. Returns documents concatenated with dividers.'
81
82
  ),
82
83
  },
83
84
  queryDocuments: {
84
85
  schema: z.object({
85
- model: z.string().describe('The model name to query (e.g. "Plan", "Task")'),
86
- where: z.string().optional().describe('Filter conditions as JSON string, e.g. \'{ "meta.status": "approved" }\' or \'{ "meta.priority": { "$gt": 3 } }\''),
87
- sort: z.string().optional().describe('Sort specification as JSON string, e.g. \'{ "meta.priority": "desc" }\''),
88
- limit: z.number().optional().describe('Maximum number of results to return'),
89
- offset: z.number().optional().describe('Number of results to skip'),
90
- select: z.array(z.string()).optional().describe('Fields to include in output (e.g. ["id", "title", "meta.status"])'),
86
+ model: z.string().describe('The model name to query (e.g. "Plan", "Task", "Guide"). Must match a model defined in the collection — check getCollectionOverview.'),
87
+ where: z.string().optional().describe('MongoDB-style filter as a JSON string. Dot notation for nested fields. Examples: \'{"meta.status": "approved"}\', \'{"meta.priority": {"$gt": 3}}\', \'{"meta.tags": {"$in": ["urgent"]}}\''),
88
+ sort: z.string().optional().describe('Sort as a JSON string. Example: \'{"meta.priority": "desc"}\', \'{"meta.createdAt": "asc"}\''),
89
+ limit: z.number().optional().describe('Maximum number of results. Default: all matching documents.'),
90
+ offset: z.number().optional().describe('Skip this many results (for pagination).'),
91
+ select: z.array(z.string()).optional().describe('Only include these fields in output (e.g. ["id", "title", "meta.status"]). Reduces noise when you only need specific metadata.'),
91
92
  }).describe(
92
- 'Query documents by model with MongoDB-style filtering, sorting, and pagination. Returns serialized model instances.'
93
+ 'Query documents by model with filtering, sorting, and pagination. Use this when you need to find documents matching specific criteria (status, priority, tags, dates) rather than browsing by name.'
93
94
  ),
94
95
  },
95
96
  searchContent: {
96
97
  schema: z.object({
97
- pattern: z.string().describe('Regex pattern to search for across all documents'),
98
- caseSensitive: z.boolean().optional().describe('Whether the search is case-sensitive (default: false)'),
98
+ pattern: z.string().describe('Regex pattern to search for across all document content. Examples: "TODO|FIXME", "authentication", "def.*handler"'),
99
+ caseSensitive: z.boolean().optional().describe('Case-sensitive search. Default: false (case insensitive).'),
99
100
  }).describe(
100
- 'Text/regex search (grep) across all documents in the collection. Returns matching lines with file context.'
101
+ 'Text/regex search (grep) across all documents. Use for exact pattern matching, code references, or finding specific terms. Returns matching lines with file context. For natural language questions, use semanticSearch instead.'
101
102
  ),
102
103
  },
103
104
  semanticSearch: {
104
105
  schema: z.object({
105
- query: z.string().describe('Natural language search query'),
106
- limit: z.number().optional().describe('Maximum number of results (default: 10)'),
106
+ query: z.string().describe('A natural language question or topic description. Example: "how does the authentication flow work?" or "deployment configuration options"'),
107
+ limit: z.number().optional().describe('Maximum results to return. Default: 10.'),
107
108
  }).describe(
108
- 'Semantic search across documents using keyword + vector similarity. Falls back to text search if no search index exists.'
109
+ 'Search documents using natural language — combines keyword matching with semantic similarity. Best for questions and topic exploration. Falls back to text search if no vector index exists. For exact pattern matching, use searchContent instead.'
109
110
  ),
110
111
  },
111
112
  }
112
113
 
114
+ /**
115
+ * When an assistant uses contentDb, inject system prompt guidance
116
+ * about progressive document exploration.
117
+ */
118
+ override setupToolsConsumer(consumer: Helper) {
119
+ if (typeof (consumer as any).addSystemPromptExtension === 'function') {
120
+ (consumer as any).addSystemPromptExtension('contentDb', [
121
+ '## Document Collection',
122
+ '',
123
+ 'You have access to a structured document collection (markdown files with frontmatter, organized by model/type).',
124
+ '',
125
+ '**Progressive exploration — go broad to narrow:**',
126
+ '1. `getCollectionOverview` — start here. Shows models, document counts, and directory structure.',
127
+ '2. `listDocuments` — browse document IDs, optionally filtered by model or glob.',
128
+ '3. `readDocument` — read a specific document. Use `include`/`exclude` to skip irrelevant sections.',
129
+ '4. `queryDocuments` — filter documents by metadata (status, priority, tags, etc.) with MongoDB-style queries.',
130
+ '',
131
+ '**Searching:**',
132
+ '- `semanticSearch` — best for natural language questions ("how does authentication work?")',
133
+ '- `searchContent` — best for exact patterns, code references, or regex across all documents',
134
+ '',
135
+ '**Efficiency:** Don\'t read entire documents when you only need one section. Use `include` to request specific headings. Use `readMultipleDocuments` to batch reads instead of calling `readDocument` in a loop.',
136
+ ].join('\n'))
137
+ }
138
+ }
139
+
113
140
  override get initialState(): ContentDbState {
114
141
  return {
115
142
  ...super.initialState,
@@ -460,7 +487,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
460
487
  // Dynamically import and attach SemanticSearch if not already registered
461
488
  const { SemanticSearch } = await import('./semantic-search.js')
462
489
  if (!this.container.features.available.includes('semanticSearch')) {
463
- SemanticSearch.attach(this.container as any)
490
+ ;(SemanticSearch as any).attach(this.container as any)
464
491
  }
465
492
 
466
493
  // Store search index in ~/.luca/contentbase/{hash}/ keyed by the real (symlink-resolved) collection path
@@ -660,7 +687,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
660
687
  get queries(): Record<string, ReturnType<typeof this.query>> {
661
688
  const queryChains: [string, ReturnType<typeof this.query>][] = []
662
689
  for (const modelName of this.modelNames) {
663
- const queryChain = this.query(this.models[modelName])
690
+ const queryChain = this.query(this.models[modelName]!)
664
691
  const pluralized = this.container.utils.stringUtils.pluralize(modelName).toLowerCase()
665
692
  queryChains.push([modelName.toLowerCase(), queryChain])
666
693
  queryChains.push([pluralized, queryChain])
@@ -147,7 +147,7 @@ export class FileManager<
147
147
 
148
148
  /** Returns the list of directories currently being watched. */
149
149
  get watchedPaths(): string[] {
150
- return this.state.get("watchedPaths") || [];
150
+ return (this.state.get("watchedPaths") as string[] | undefined) || [];
151
151
  }
152
152
 
153
153
  /**
@@ -199,7 +199,7 @@ export class FileManager<
199
199
  const { git } = this.container;
200
200
  if (!git.isRepo) return null;
201
201
 
202
- const gitDir = pathJoin(git.repoRoot, '.git');
202
+ const gitDir = pathJoin(git.repoRoot!, '.git');
203
203
  const head = readFileSync(pathJoin(gitDir, 'HEAD'), 'utf8').trim();
204
204
 
205
205
  // Detached HEAD — already a sha
@@ -217,7 +217,7 @@ export class FileManager<
217
217
  const ref = head.slice(5);
218
218
  const packed = readFileSync(packedRefsPath, 'utf8');
219
219
  const match = packed.match(new RegExp(`^([0-9a-f]{40}) ${ref}`, 'm'));
220
- if (match) return match[1];
220
+ if (match) return match[1] ?? null;
221
221
  }
222
222
 
223
223
  return null;
@@ -334,8 +334,8 @@ export class FileManager<
334
334
  if (cwdRelative) {
335
335
  const prefix = cwdRelative + '/';
336
336
  for (let i = 0; i < fileIds.length; i++) {
337
- if (fileIds[i].startsWith(prefix)) {
338
- fileIds[i] = fileIds[i].slice(prefix.length);
337
+ if (fileIds[i]!.startsWith(prefix)) {
338
+ fileIds[i] = fileIds[i]!.slice(prefix.length);
339
339
  }
340
340
  }
341
341
  }
@@ -422,12 +422,12 @@ export class FileManager<
422
422
 
423
423
  // If already watching, just add the new paths
424
424
  if (this.isWatching && this.watcher) {
425
- const currentPaths: string[] = this.state.get("watchedPaths") || [];
425
+ const currentPaths: string[] = (this.state.get("watchedPaths") as string[] | undefined) || [];
426
426
  const newPaths = pathsToWatch.filter(p => !currentPaths.includes(p));
427
427
 
428
428
  if (newPaths.length) {
429
429
  this.watcher.add(newPaths);
430
- this.state.set("watchedPaths", [...currentPaths, ...newPaths]);
430
+ this.state.set("watchedPaths", [...currentPaths, ...newPaths] as any);
431
431
  }
432
432
 
433
433
  return;
@@ -481,7 +481,7 @@ export class FileManager<
481
481
 
482
482
  watcher.on("ready", () => {
483
483
  this.state.set("watching", true);
484
- this.state.set("watchedPaths", pathsToWatch);
484
+ this.state.set("watchedPaths", pathsToWatch as any);
485
485
  });
486
486
 
487
487
  this.watcher = watcher;
@@ -495,7 +495,7 @@ export class FileManager<
495
495
  if (this.watcher) {
496
496
  this.watcher.close();
497
497
  this.state.set("watching", false);
498
- this.state.set("watchedPaths", []);
498
+ this.state.set("watchedPaths", [] as any);
499
499
  this.watcher = null;
500
500
  }
501
501
  }
@@ -508,6 +508,7 @@ export class FileManager<
508
508
  const stats = statSync(absolutePath);
509
509
  this.files.set(path, {
510
510
  dirname: dir,
511
+ relativeDirname: this.container.paths.relative(dir),
511
512
  absolutePath,
512
513
  relativePath: path,
513
514
  name,
@@ -155,7 +155,7 @@ export class Git extends Feature {
155
155
  */
156
156
  get branch(): string | null {
157
157
  if(!this.isRepo) { return null }
158
- return this.container.feature('proc').exec(`${this.gitPath} branch`).split("\n").filter(line => line.startsWith('*')).map(line => line.replace('*', '').trim()).pop()
158
+ return this.container.feature('proc').exec(`${this.gitPath} branch`).split("\n").filter(line => line.startsWith('*')).map(line => line.replace('*', '').trim()).pop() ?? null
159
159
  }
160
160
 
161
161
  /**
@@ -230,7 +230,7 @@ export class Git extends Feature {
230
230
  */
231
231
  get repoRoot(): string | null {
232
232
  if (this.state.has('repoRoot')) {
233
- return this.state.get('repoRoot')
233
+ return this.state.get('repoRoot') ?? null
234
234
  }
235
235
 
236
236
  const repoRoot = this.container.fs.findUp('.git')
@@ -507,12 +507,12 @@ export class Git extends Feature {
507
507
  if (str.startsWith('github.com/')) str = str.replace('github.com/', '')
508
508
  if (str.includes('#')) {
509
509
  const parts = str.split('#')
510
- str = parts[0]
511
- ref = parts[1]
510
+ str = parts[0] ?? str
511
+ ref = parts[1] ?? ref
512
512
  }
513
513
 
514
514
  const parts = str.split('/')
515
- return { user: parts[0], repo: parts[1], ref, subdir: parts.slice(2).join('/') }
515
+ return { user: parts[0] ?? '', repo: parts[1] ?? '', ref, subdir: parts.slice(2).join('/') }
516
516
  }
517
517
 
518
518
  /**
@@ -201,7 +201,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
201
201
  }
202
202
 
203
203
  // Schemas
204
- const schemasModule = { CommandOptionsSchema: commands.baseClass?.optionsSchema || z.object({}) }
204
+ const schemasModule: Record<string, any> = { CommandOptionsSchema: commands.baseClass?.optionsSchema || z.object({}) }
205
205
  try {
206
206
  // Pull all base schemas from the already-loaded schemas/base module
207
207
  const baseSchemas = require('../../schemas/base.js')
@@ -121,7 +121,7 @@ export class JsonTree<T extends JsonTreeState = JsonTreeState> extends Feature<T
121
121
  for (const file of jsonFiles.filter(Boolean)) {
122
122
  if (file?.relativePath) {
123
123
  const fileContent = fileSystem.readFile(file.relativePath);
124
- const fileData = JSON.parse(fileContent);
124
+ const fileData = JSON.parse(String(fileContent));
125
125
  const path = file.relativePath
126
126
  .replace(/\.json$/, "")
127
127
  .replace(basePath + "/", "")
@@ -207,12 +207,12 @@ export class OS extends Feature {
207
207
  displays.push({
208
208
  name: d._name ?? 'Unknown',
209
209
  resolution: {
210
- width: resMatch ? parseInt(resMatch[1], 10) : 0,
211
- height: resMatch ? parseInt(resMatch[2], 10) : 0,
210
+ width: resMatch ? parseInt(resMatch[1] ?? '0', 10) : 0,
211
+ height: resMatch ? parseInt(resMatch[2] ?? '0', 10) : 0,
212
212
  },
213
213
  retina: /retina/i.test(d._spdisplays_resolution ?? '') || /retina/i.test(d.spdisplays_display_type ?? ''),
214
214
  main: d.spdisplays_main === 'spdisplays_yes' || /yes/i.test(d.spdisplays_main ?? ''),
215
- refreshRate: hzMatch ? parseFloat(hzMatch[1]) : undefined,
215
+ refreshRate: hzMatch ? parseFloat(hzMatch[1] ?? '0') : undefined,
216
216
  connectionType: d.spdisplays_connection_type ?? undefined,
217
217
  })
218
218
  }
@@ -296,7 +296,7 @@ export class PackageFinder<
296
296
  */
297
297
  get scopes(): string[] {
298
298
  return Array.from(
299
- new Set(this.packageNames.filter(p => p.startsWith('@')).map(p => p.split('/')[0]))
299
+ new Set(this.packageNames.filter(p => p.startsWith('@')).map(p => p.split('/')[0]).filter((s): s is string => s !== undefined))
300
300
  )
301
301
  }
302
302
 
@@ -4,6 +4,7 @@ import { Feature } from '../feature.js'
4
4
  import { State } from '../../state.js'
5
5
  import { Bus, type EventMap } from '../../bus.js'
6
6
  import type { ChildProcess } from './proc.js'
7
+ import type { Helper } from '../../helper.js'
7
8
 
8
9
  // ─── Output Buffer ─────────────────────────────────────────────────────────
9
10
 
@@ -436,46 +437,46 @@ export class ProcessManager extends Feature {
436
437
  static { Feature.register(this, 'processManager') }
437
438
 
438
439
  /** Tools that an assistant can use to spawn and manage processes. */
439
- static tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
440
+ static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
440
441
  spawnProcess: {
441
442
  schema: z.object({
442
- command: z.string().describe('The command to execute (e.g. "node", "bun", "python")'),
443
- args: z.string().optional().describe('Space-separated arguments to pass to the command'),
444
- tag: z.string().optional().describe('A label for this process so you can find it later'),
445
- cwd: z.string().optional().describe('Working directory for the process'),
443
+ command: z.string().describe('The executable to run (e.g. "node", "bun", "python"). NOT a shell command — use runCommand for shell syntax like pipes or &&.'),
444
+ args: z.string().optional().describe('Arguments as a single space-separated string. WARNING: spaces are used to split args, so paths with spaces will break. For complex argument quoting, prefer runCommand instead.'),
445
+ tag: z.string().optional().describe('A short, descriptive label for this process (e.g. "api-server", "file-watcher"). Always set a tag — it makes the process easy to find later with getProcessOutput and killProcess.'),
446
+ cwd: z.string().optional().describe('Working directory for the process. Defaults to the project root.'),
446
447
  }).describe(
447
- 'Spawn a long-running process (server, watcher, daemon) that runs in the background. Returns immediately with a process ID you can use to check status or kill it later.'
448
+ 'Start a long-running background process (server, watcher, daemon). Returns immediately with a process ID the process keeps running. Use this for anything that runs indefinitely. After spawning, call getProcessOutput to check if it started successfully. Always set a tag.'
448
449
  ),
449
450
  },
450
451
  runCommand: {
451
452
  schema: z.object({
452
- command: z.string().describe('The command to execute (e.g. "npm install", "bun test")'),
453
- cwd: z.string().optional().describe('Working directory for the command'),
453
+ command: z.string().describe('A full shell command string (executed via sh -c). Supports pipes, &&, redirects, env vars, globs — anything you can type in a terminal. Examples: "bun test", "npm install && npm run build", "cat logs/*.txt | grep ERROR"'),
454
+ cwd: z.string().optional().describe('Working directory for the command. Defaults to the project root.'),
454
455
  }).describe(
455
- 'Run a command and wait for it to complete. Returns the full stdout/stderr output and exit code. Use this for commands you expect to finish (builds, installs, tests).'
456
+ 'Run a shell command and wait for it to complete. Returns stdout, stderr, and exit code. Use this for commands that finish on their own — builds, installs, tests, one-off scripts. For anything that runs forever (servers, watchers), use spawnProcess instead.'
456
457
  ),
457
458
  },
458
459
  listProcesses: {
459
460
  schema: z.object({}).describe(
460
- 'List all tracked processes with their status, PID, command, uptime, and a preview of recent output.'
461
+ 'List all tracked background processes with their status, PID, command, uptime, and the last few lines of output. Call this to get an overview before deciding which process to inspect or kill.'
461
462
  ),
462
463
  },
463
464
  getProcessOutput: {
464
465
  schema: z.object({
465
- id: z.string().optional().describe('The process ID to get output for'),
466
- tag: z.string().optional().describe('The tag of the process to get output for'),
467
- stream: z.string().optional().describe('Which stream to read: "stdout" (default) or "stderr"'),
466
+ id: z.string().optional().describe('The process ID (returned by spawnProcess). Provide either id or tag, not both.'),
467
+ tag: z.string().optional().describe('The tag you assigned when spawning the process. Provide either id or tag, not both.'),
468
+ stream: z.string().optional().describe('"stdout" (default) or "stderr". Check stderr when a process crashes or behaves unexpectedly.'),
468
469
  }).describe(
469
- 'Peek at a process\'s buffered output — shows the first 20 lines and last 50 lines of stdout or stderr.'
470
+ 'Read a background process\'s buffered output — the first 20 lines (startup) and last 50 lines (recent activity). Call this after spawning to verify the process started correctly, and periodically to monitor its health.'
470
471
  ),
471
472
  },
472
473
  killProcess: {
473
474
  schema: z.object({
474
- id: z.string().optional().describe('The process ID to kill'),
475
- tag: z.string().optional().describe('The tag of the process to kill'),
476
- signal: z.string().optional().describe('Signal to send: "SIGTERM" (default, graceful) or "SIGKILL" (force)'),
475
+ id: z.string().optional().describe('The process ID to kill. Provide either id or tag, not both.'),
476
+ tag: z.string().optional().describe('The tag of the process to kill. Provide either id or tag, not both.'),
477
+ signal: z.string().optional().describe('"SIGTERM" (default) for graceful shutdown, "SIGKILL" to force-kill a stuck process. Try SIGTERM first.'),
477
478
  }).describe(
478
- 'Kill a running process by ID or tag.'
479
+ 'Stop a running background process. Use SIGTERM (default) for graceful shutdown. If a process doesn\'t respond, follow up with SIGKILL. Always clean up processes you spawned when they\'re no longer needed.'
479
480
  ),
480
481
  },
481
482
  }
@@ -593,6 +594,38 @@ export class ProcessManager extends Feature {
593
594
  return { id: handler.id, status: handler.status, signal, message: 'Process killed.' }
594
595
  }
595
596
 
597
+ /**
598
+ * When an assistant uses processManager, inject system prompt guidance
599
+ * about how to manage processes safely and effectively.
600
+ */
601
+ override setupToolsConsumer(consumer: Helper) {
602
+ if (typeof (consumer as any).addSystemPromptExtension === 'function') {
603
+ (consumer as any).addSystemPromptExtension('processManager', [
604
+ '## Process Management',
605
+ '',
606
+ '**Choosing the right tool:**',
607
+ '- `runCommand` — for anything that finishes on its own (builds, tests, installs, queries). Blocks until done.',
608
+ '- `spawnProcess` — for anything that runs indefinitely (servers, watchers, tails). Returns immediately.',
609
+ '- When in doubt: if you\'d press Ctrl-C to stop it, use `spawnProcess`. If you\'d wait for it, use `runCommand`.',
610
+ '',
611
+ '**After spawning a process:**',
612
+ '1. Always assign a descriptive `tag` so you can reference it later',
613
+ '2. Call `getProcessOutput` within a few seconds to verify it started correctly',
614
+ '3. Check `stderr` if the process crashes or output looks wrong',
615
+ '',
616
+ '**Monitoring:**',
617
+ '- Call `listProcesses` to see all running and finished processes at a glance',
618
+ '- Call `getProcessOutput` to read recent output — it keeps the first 20 and last 50 lines',
619
+ '- A process with status "crashed" exited with a non-zero code — check its stderr for the error',
620
+ '',
621
+ '**Cleanup:**',
622
+ '- Always `killProcess` background processes when they\'re no longer needed',
623
+ '- Use SIGTERM (default) first for graceful shutdown. Only use SIGKILL if SIGTERM doesn\'t work.',
624
+ '- If you spawned it, you\'re responsible for killing it',
625
+ ].join('\n'))
626
+ }
627
+ }
628
+
596
629
  // ─── Core API ───────────────────────────────────────────────────────────
597
630
 
598
631
  /**
@@ -59,7 +59,7 @@ export const PythonEventsSchema = FeatureEventsSchema.extend({
59
59
  }).describe('Install result')]).describe('When dependency installation fails'),
60
60
  codeExecuted: z.tuple([z.object({
61
61
  code: z.string().describe('The Python code that was executed'),
62
- variables: z.record(z.any()).describe('Variables passed to the execution'),
62
+ variables: z.record(z.string(), z.any()).describe('Variables passed to the execution'),
63
63
  result: z.object({
64
64
  stdout: z.string().describe('Standard output'),
65
65
  stderr: z.string().describe('Standard error'),
@@ -68,7 +68,7 @@ export const PythonEventsSchema = FeatureEventsSchema.extend({
68
68
  }).describe('Code execution details')]).describe('When Python code finishes executing'),
69
69
  fileExecuted: z.tuple([z.object({
70
70
  filePath: z.string().describe('Path to the executed Python file'),
71
- variables: z.record(z.any()).describe('Variables passed as arguments'),
71
+ variables: z.record(z.string(), z.any()).describe('Variables passed as arguments'),
72
72
  result: z.object({
73
73
  stdout: z.string().describe('Standard output'),
74
74
  stderr: z.string().describe('Standard error'),
@@ -532,7 +532,7 @@ export class Python<
532
532
  */
533
533
  private _parsePythonCommand(extraArgs: string[]): { command: string, args: string[] } {
534
534
  const parts = this.pythonPath.split(/\s+/)
535
- return { command: parts[0], args: [...parts.slice(1), ...extraArgs] }
535
+ return { command: parts[0] ?? 'python', args: [...parts.slice(1), ...extraArgs] }
536
536
  }
537
537
 
538
538
  /**
@@ -400,7 +400,7 @@ export class RedisFeature extends Feature<RedisState, RedisOptions> {
400
400
  )
401
401
 
402
402
  if (existing) {
403
- if (existing.state !== 'running') {
403
+ if (existing.status !== 'running') {
404
404
  await docker.startContainer(name)
405
405
  }
406
406
  return existing.id
@@ -101,7 +101,7 @@ export class Repl<
101
101
 
102
102
  // Load existing history
103
103
  try {
104
- const content = fs.readFile(this._historyPath, 'utf-8')
104
+ const content = this.container.fs.readFile(this._historyPath, 'utf-8') as string
105
105
  this._history = content.split('\n').filter(Boolean).reverse()
106
106
  } catch {}
107
107
 
@@ -204,7 +204,7 @@ export class Repl<
204
204
  private _saveHistory(line: string) {
205
205
  if (!this._historyPath || !line.trim()) return
206
206
  try {
207
- fs.appendFileSync(this._historyPath, line + '\n')
207
+ this.container.fs.appendFile(this._historyPath, line + '\n')
208
208
  } catch {}
209
209
  }
210
210
  }
@@ -45,8 +45,8 @@ function esmToCjs(code: string): string {
45
45
  .replace(/^export\s+\{([^}]*)\}\s*;?$/gm, (_match, body: string) => {
46
46
  return body.split(',').map(s => {
47
47
  const parts = s.trim().split(/\s+as\s+/)
48
- const local = parts[0].trim()
49
- const exported = (parts[1] || parts[0]).trim()
48
+ const local = (parts[0] ?? '').trim()
49
+ const exported = (parts[1] ?? parts[0] ?? '').trim()
50
50
  return local ? `exports['${exported}'] = ${local};` : ''
51
51
  }).filter(Boolean).join(' ')
52
52
  })
@@ -2,7 +2,7 @@ import { z } from 'zod'
2
2
  import { FeatureStateSchema } from '../../schemas/base.js'
3
3
  import { Feature } from "../feature.js";
4
4
  import colors from "chalk";
5
- import type { Fonts } from "figlet";
5
+ import type { FontName as Fonts } from "figlet";
6
6
  import { figlet, fontNames } from "./figlet-fonts.js";
7
7
  import inquirer from "inquirer";
8
8
  import { marked } from 'marked';
@@ -219,9 +219,9 @@ export class VM<
219
219
 
220
220
  // Find the last non-empty line
221
221
  let lastIdx = lines.length - 1
222
- while (lastIdx > 0 && !lines[lastIdx].trim()) lastIdx--
222
+ while (lastIdx > 0 && !(lines[lastIdx] ?? '').trim()) lastIdx--
223
223
 
224
- let lastLine = lines[lastIdx]!
224
+ let lastLine = lines[lastIdx] ?? ''
225
225
 
226
226
  // For single-line code with semicolons (e.g. CLI eval), split the last line
227
227
  // into statements and only try to return the final statement.
@@ -390,7 +390,7 @@ export class VM<
390
390
  if (!fs.exists(filePath)) return {}
391
391
 
392
392
  const raw = fs.readFile(filePath)
393
- const { code } = this.container.feature('transpiler').transformSync(raw, { format: 'cjs' })
393
+ const { code } = this.container.feature('transpiler').transformSync(String(raw), { format: 'cjs' })
394
394
 
395
395
  const sharedExports = {}
396
396
  const { context } = this.performSync(code, {
@@ -113,7 +113,7 @@ export class YamlTree<T extends YamlTreeState = YamlTreeState> extends Feature<T
113
113
  for (const file of yamlFiles.filter(Boolean)) {
114
114
  if(file?.relativePath) {
115
115
  const fileContent = fileSystem.readFile(file.relativePath);
116
- const fileData = yamlFeature.parse(fileContent);
116
+ const fileData = yamlFeature.parse(String(fileContent));
117
117
  const path = file.relativePath.replace(/\.ya?ml$/, "").replace(basePath + "/", "").split("/").filter(v => v?.length).map(p => camelCase(p));
118
118
  set(tree, path, fileData)
119
119
  }
package/src/node.ts CHANGED
@@ -65,6 +65,7 @@ export type { AvailableFeatures, FeatureOptions, FeatureState } from './feature'
65
65
  export type { NodeContainer, NodeFeatures } from './node/container'
66
66
  export type { AvailableServers, StartOptions, ServersInterface } from './server'
67
67
  export type { HelperState, HelperOptions } from './helper'
68
+ export type { Entity } from './entity'
68
69
  export type { EventMap } from './bus'
69
70
  export type { SetStateValue, StateChangeType } from './state'
70
71
 
@@ -1,5 +1,5 @@
1
1
  // Auto-generated Python bridge script
2
- // Generated at: 2026-03-30T06:52:57.841Z
2
+ // Generated at: 2026-04-05T06:58:08.827Z
3
3
  // Source: src/python/bridge.py
4
4
  //
5
5
  // Do not edit manually. Run: luca build-python-bridge
@@ -1,5 +1,5 @@
1
1
  // Auto-generated scaffold and MCP readme content
2
- // Generated at: 2026-03-30T06:52:56.013Z
2
+ // Generated at: 2026-04-05T06:58:07.148Z
3
3
  // Source: docs/scaffolds/*.md, docs/examples/assistant/, and docs/mcp/readme.md
4
4
  //
5
5
  // Do not edit manually. Run: luca build-scaffolds