@soederpop/luca 0.1.2 → 0.1.3

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 (366) hide show
  1. package/CLAUDE.md +2 -0
  2. package/assistants/codingAssistant/hooks.ts +1 -5
  3. package/assistants/inkbot/CORE.md +69 -0
  4. package/assistants/inkbot/hooks.ts +14 -0
  5. package/assistants/inkbot/tools.ts +47 -0
  6. package/commands/inkbot.ts +353 -0
  7. package/dist/agi/container.server.d.ts +63 -0
  8. package/dist/agi/container.server.d.ts.map +1 -0
  9. package/dist/agi/endpoints/ask.d.ts +20 -0
  10. package/dist/agi/endpoints/ask.d.ts.map +1 -0
  11. package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
  12. package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
  13. package/dist/agi/endpoints/conversations.d.ts +18 -0
  14. package/dist/agi/endpoints/conversations.d.ts.map +1 -0
  15. package/dist/agi/endpoints/experts.d.ts +8 -0
  16. package/dist/agi/endpoints/experts.d.ts.map +1 -0
  17. package/dist/agi/feature.d.ts +9 -0
  18. package/dist/agi/feature.d.ts.map +1 -0
  19. package/dist/agi/features/assistant.d.ts +509 -0
  20. package/dist/agi/features/assistant.d.ts.map +1 -0
  21. package/dist/agi/features/assistants-manager.d.ts +236 -0
  22. package/dist/agi/features/assistants-manager.d.ts.map +1 -0
  23. package/dist/agi/features/autonomous-assistant.d.ts +281 -0
  24. package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
  25. package/dist/agi/features/browser-use.d.ts +479 -0
  26. package/dist/agi/features/browser-use.d.ts.map +1 -0
  27. package/dist/agi/features/claude-code.d.ts +824 -0
  28. package/dist/agi/features/claude-code.d.ts.map +1 -0
  29. package/dist/agi/features/conversation-history.d.ts +245 -0
  30. package/dist/agi/features/conversation-history.d.ts.map +1 -0
  31. package/dist/agi/features/conversation.d.ts +464 -0
  32. package/dist/agi/features/conversation.d.ts.map +1 -0
  33. package/dist/agi/features/docs-reader.d.ts +72 -0
  34. package/dist/agi/features/docs-reader.d.ts.map +1 -0
  35. package/dist/agi/features/file-tools.d.ts +110 -0
  36. package/dist/agi/features/file-tools.d.ts.map +1 -0
  37. package/dist/agi/features/luca-coder.d.ts +323 -0
  38. package/dist/agi/features/luca-coder.d.ts.map +1 -0
  39. package/dist/agi/features/openai-codex.d.ts +381 -0
  40. package/dist/agi/features/openai-codex.d.ts.map +1 -0
  41. package/dist/agi/features/openapi.d.ts +200 -0
  42. package/dist/agi/features/openapi.d.ts.map +1 -0
  43. package/dist/agi/features/skills-library.d.ts +167 -0
  44. package/dist/agi/features/skills-library.d.ts.map +1 -0
  45. package/dist/agi/index.d.ts +5 -0
  46. package/dist/agi/index.d.ts.map +1 -0
  47. package/dist/agi/lib/interceptor-chain.d.ts +44 -0
  48. package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
  49. package/dist/agi/lib/token-counter.d.ts +13 -0
  50. package/dist/agi/lib/token-counter.d.ts.map +1 -0
  51. package/dist/bootstrap/generated.d.ts +5 -0
  52. package/dist/bootstrap/generated.d.ts.map +1 -0
  53. package/dist/browser.d.ts +12 -0
  54. package/dist/browser.d.ts.map +1 -0
  55. package/dist/bus.d.ts +29 -0
  56. package/dist/bus.d.ts.map +1 -0
  57. package/dist/cli/build-info.d.ts +4 -0
  58. package/dist/cli/build-info.d.ts.map +1 -0
  59. package/dist/cli/cli.d.ts +3 -0
  60. package/dist/cli/cli.d.ts.map +1 -0
  61. package/dist/client.d.ts +60 -0
  62. package/dist/client.d.ts.map +1 -0
  63. package/dist/clients/civitai/index.d.ts +472 -0
  64. package/dist/clients/civitai/index.d.ts.map +1 -0
  65. package/dist/clients/client-template.d.ts +30 -0
  66. package/dist/clients/client-template.d.ts.map +1 -0
  67. package/dist/clients/comfyui/index.d.ts +281 -0
  68. package/dist/clients/comfyui/index.d.ts.map +1 -0
  69. package/dist/clients/elevenlabs/index.d.ts +197 -0
  70. package/dist/clients/elevenlabs/index.d.ts.map +1 -0
  71. package/dist/clients/graph.d.ts +64 -0
  72. package/dist/clients/graph.d.ts.map +1 -0
  73. package/dist/clients/openai/index.d.ts +247 -0
  74. package/dist/clients/openai/index.d.ts.map +1 -0
  75. package/dist/clients/rest.d.ts +92 -0
  76. package/dist/clients/rest.d.ts.map +1 -0
  77. package/dist/clients/supabase/index.d.ts +176 -0
  78. package/dist/clients/supabase/index.d.ts.map +1 -0
  79. package/dist/clients/websocket.d.ts +127 -0
  80. package/dist/clients/websocket.d.ts.map +1 -0
  81. package/dist/command.d.ts +163 -0
  82. package/dist/command.d.ts.map +1 -0
  83. package/dist/commands/bootstrap.d.ts +20 -0
  84. package/dist/commands/bootstrap.d.ts.map +1 -0
  85. package/dist/commands/chat.d.ts +37 -0
  86. package/dist/commands/chat.d.ts.map +1 -0
  87. package/dist/commands/code.d.ts +28 -0
  88. package/dist/commands/code.d.ts.map +1 -0
  89. package/dist/commands/console.d.ts +22 -0
  90. package/dist/commands/console.d.ts.map +1 -0
  91. package/dist/commands/describe.d.ts +50 -0
  92. package/dist/commands/describe.d.ts.map +1 -0
  93. package/dist/commands/eval.d.ts +23 -0
  94. package/dist/commands/eval.d.ts.map +1 -0
  95. package/dist/commands/help.d.ts +25 -0
  96. package/dist/commands/help.d.ts.map +1 -0
  97. package/dist/commands/index.d.ts +18 -0
  98. package/dist/commands/index.d.ts.map +1 -0
  99. package/dist/commands/introspect.d.ts +24 -0
  100. package/dist/commands/introspect.d.ts.map +1 -0
  101. package/dist/commands/mcp.d.ts +35 -0
  102. package/dist/commands/mcp.d.ts.map +1 -0
  103. package/dist/commands/prompt.d.ts +38 -0
  104. package/dist/commands/prompt.d.ts.map +1 -0
  105. package/dist/commands/run.d.ts +24 -0
  106. package/dist/commands/run.d.ts.map +1 -0
  107. package/dist/commands/sandbox-mcp.d.ts +34 -0
  108. package/dist/commands/sandbox-mcp.d.ts.map +1 -0
  109. package/dist/commands/save-api-docs.d.ts +21 -0
  110. package/dist/commands/save-api-docs.d.ts.map +1 -0
  111. package/dist/commands/scaffold.d.ts +24 -0
  112. package/dist/commands/scaffold.d.ts.map +1 -0
  113. package/dist/commands/select.d.ts +22 -0
  114. package/dist/commands/select.d.ts.map +1 -0
  115. package/dist/commands/serve.d.ts +29 -0
  116. package/dist/commands/serve.d.ts.map +1 -0
  117. package/dist/container-describer.d.ts +144 -0
  118. package/dist/container-describer.d.ts.map +1 -0
  119. package/dist/container.d.ts +451 -0
  120. package/dist/container.d.ts.map +1 -0
  121. package/dist/endpoint.d.ts +113 -0
  122. package/dist/endpoint.d.ts.map +1 -0
  123. package/dist/feature.d.ts +47 -0
  124. package/dist/feature.d.ts.map +1 -0
  125. package/dist/graft.d.ts +29 -0
  126. package/dist/graft.d.ts.map +1 -0
  127. package/dist/hash-object.d.ts +8 -0
  128. package/dist/hash-object.d.ts.map +1 -0
  129. package/dist/helper.d.ts +209 -0
  130. package/dist/helper.d.ts.map +1 -0
  131. package/dist/introspection/generated.node.d.ts +44623 -0
  132. package/dist/introspection/generated.node.d.ts.map +1 -0
  133. package/dist/introspection/generated.web.d.ts +1412 -0
  134. package/dist/introspection/generated.web.d.ts.map +1 -0
  135. package/dist/introspection/index.d.ts +156 -0
  136. package/dist/introspection/index.d.ts.map +1 -0
  137. package/dist/introspection/scan.d.ts +147 -0
  138. package/dist/introspection/scan.d.ts.map +1 -0
  139. package/dist/node/container.d.ts +256 -0
  140. package/dist/node/container.d.ts.map +1 -0
  141. package/dist/node/feature.d.ts +9 -0
  142. package/dist/node/feature.d.ts.map +1 -0
  143. package/dist/node/features/container-link.d.ts +213 -0
  144. package/dist/node/features/container-link.d.ts.map +1 -0
  145. package/dist/node/features/content-db.d.ts +354 -0
  146. package/dist/node/features/content-db.d.ts.map +1 -0
  147. package/dist/node/features/disk-cache.d.ts +236 -0
  148. package/dist/node/features/disk-cache.d.ts.map +1 -0
  149. package/dist/node/features/dns.d.ts +511 -0
  150. package/dist/node/features/dns.d.ts.map +1 -0
  151. package/dist/node/features/docker.d.ts +485 -0
  152. package/dist/node/features/docker.d.ts.map +1 -0
  153. package/dist/node/features/downloader.d.ts +73 -0
  154. package/dist/node/features/downloader.d.ts.map +1 -0
  155. package/dist/node/features/figlet-fonts.d.ts +4 -0
  156. package/dist/node/features/figlet-fonts.d.ts.map +1 -0
  157. package/dist/node/features/file-manager.d.ts +177 -0
  158. package/dist/node/features/file-manager.d.ts.map +1 -0
  159. package/dist/node/features/fs.d.ts +635 -0
  160. package/dist/node/features/fs.d.ts.map +1 -0
  161. package/dist/node/features/git.d.ts +329 -0
  162. package/dist/node/features/git.d.ts.map +1 -0
  163. package/dist/node/features/google-auth.d.ts +200 -0
  164. package/dist/node/features/google-auth.d.ts.map +1 -0
  165. package/dist/node/features/google-calendar.d.ts +194 -0
  166. package/dist/node/features/google-calendar.d.ts.map +1 -0
  167. package/dist/node/features/google-docs.d.ts +138 -0
  168. package/dist/node/features/google-docs.d.ts.map +1 -0
  169. package/dist/node/features/google-drive.d.ts +202 -0
  170. package/dist/node/features/google-drive.d.ts.map +1 -0
  171. package/dist/node/features/google-mail.d.ts +221 -0
  172. package/dist/node/features/google-mail.d.ts.map +1 -0
  173. package/dist/node/features/google-sheets.d.ts +157 -0
  174. package/dist/node/features/google-sheets.d.ts.map +1 -0
  175. package/dist/node/features/grep.d.ts +207 -0
  176. package/dist/node/features/grep.d.ts.map +1 -0
  177. package/dist/node/features/helpers.d.ts +236 -0
  178. package/dist/node/features/helpers.d.ts.map +1 -0
  179. package/dist/node/features/ink.d.ts +332 -0
  180. package/dist/node/features/ink.d.ts.map +1 -0
  181. package/dist/node/features/ipc-socket.d.ts +298 -0
  182. package/dist/node/features/ipc-socket.d.ts.map +1 -0
  183. package/dist/node/features/json-tree.d.ts +140 -0
  184. package/dist/node/features/json-tree.d.ts.map +1 -0
  185. package/dist/node/features/networking.d.ts +373 -0
  186. package/dist/node/features/networking.d.ts.map +1 -0
  187. package/dist/node/features/nlp.d.ts +125 -0
  188. package/dist/node/features/nlp.d.ts.map +1 -0
  189. package/dist/node/features/opener.d.ts +93 -0
  190. package/dist/node/features/opener.d.ts.map +1 -0
  191. package/dist/node/features/os.d.ts +168 -0
  192. package/dist/node/features/os.d.ts.map +1 -0
  193. package/dist/node/features/package-finder.d.ts +419 -0
  194. package/dist/node/features/package-finder.d.ts.map +1 -0
  195. package/dist/node/features/postgres.d.ts +173 -0
  196. package/dist/node/features/postgres.d.ts.map +1 -0
  197. package/dist/node/features/proc.d.ts +285 -0
  198. package/dist/node/features/proc.d.ts.map +1 -0
  199. package/dist/node/features/process-manager.d.ts +427 -0
  200. package/dist/node/features/process-manager.d.ts.map +1 -0
  201. package/dist/node/features/python.d.ts +477 -0
  202. package/dist/node/features/python.d.ts.map +1 -0
  203. package/dist/node/features/redis.d.ts +247 -0
  204. package/dist/node/features/redis.d.ts.map +1 -0
  205. package/dist/node/features/repl.d.ts +84 -0
  206. package/dist/node/features/repl.d.ts.map +1 -0
  207. package/dist/node/features/runpod.d.ts +527 -0
  208. package/dist/node/features/runpod.d.ts.map +1 -0
  209. package/dist/node/features/secure-shell.d.ts +145 -0
  210. package/dist/node/features/secure-shell.d.ts.map +1 -0
  211. package/dist/node/features/semantic-search.d.ts +207 -0
  212. package/dist/node/features/semantic-search.d.ts.map +1 -0
  213. package/dist/node/features/sqlite.d.ts +180 -0
  214. package/dist/node/features/sqlite.d.ts.map +1 -0
  215. package/dist/node/features/telegram.d.ts +173 -0
  216. package/dist/node/features/telegram.d.ts.map +1 -0
  217. package/dist/node/features/transpiler.d.ts +51 -0
  218. package/dist/node/features/transpiler.d.ts.map +1 -0
  219. package/dist/node/features/tts.d.ts +108 -0
  220. package/dist/node/features/tts.d.ts.map +1 -0
  221. package/dist/node/features/ui.d.ts +562 -0
  222. package/dist/node/features/ui.d.ts.map +1 -0
  223. package/dist/node/features/vault.d.ts +90 -0
  224. package/dist/node/features/vault.d.ts.map +1 -0
  225. package/dist/node/features/vm.d.ts +285 -0
  226. package/dist/node/features/vm.d.ts.map +1 -0
  227. package/dist/node/features/yaml-tree.d.ts +118 -0
  228. package/dist/node/features/yaml-tree.d.ts.map +1 -0
  229. package/dist/node/features/yaml.d.ts +127 -0
  230. package/dist/node/features/yaml.d.ts.map +1 -0
  231. package/dist/node.d.ts +67 -0
  232. package/dist/node.d.ts.map +1 -0
  233. package/dist/python/generated.d.ts +2 -0
  234. package/dist/python/generated.d.ts.map +1 -0
  235. package/dist/react/index.d.ts +36 -0
  236. package/dist/react/index.d.ts.map +1 -0
  237. package/dist/registry.d.ts +97 -0
  238. package/dist/registry.d.ts.map +1 -0
  239. package/dist/scaffolds/generated.d.ts +13 -0
  240. package/dist/scaffolds/generated.d.ts.map +1 -0
  241. package/dist/scaffolds/template.d.ts +11 -0
  242. package/dist/scaffolds/template.d.ts.map +1 -0
  243. package/dist/schemas/base.d.ts +254 -0
  244. package/dist/schemas/base.d.ts.map +1 -0
  245. package/dist/selector.d.ts +130 -0
  246. package/dist/selector.d.ts.map +1 -0
  247. package/dist/server.d.ts +89 -0
  248. package/dist/server.d.ts.map +1 -0
  249. package/dist/servers/express.d.ts +104 -0
  250. package/dist/servers/express.d.ts.map +1 -0
  251. package/dist/servers/mcp.d.ts +201 -0
  252. package/dist/servers/mcp.d.ts.map +1 -0
  253. package/dist/servers/socket.d.ts +121 -0
  254. package/dist/servers/socket.d.ts.map +1 -0
  255. package/dist/state.d.ts +24 -0
  256. package/dist/state.d.ts.map +1 -0
  257. package/dist/web/clients/socket.d.ts +37 -0
  258. package/dist/web/clients/socket.d.ts.map +1 -0
  259. package/dist/web/container.d.ts +55 -0
  260. package/dist/web/container.d.ts.map +1 -0
  261. package/dist/web/extension.d.ts +4 -0
  262. package/dist/web/extension.d.ts.map +1 -0
  263. package/dist/web/feature.d.ts +8 -0
  264. package/dist/web/feature.d.ts.map +1 -0
  265. package/dist/web/features/asset-loader.d.ts +35 -0
  266. package/dist/web/features/asset-loader.d.ts.map +1 -0
  267. package/dist/web/features/container-link.d.ts +167 -0
  268. package/dist/web/features/container-link.d.ts.map +1 -0
  269. package/dist/web/features/esbuild.d.ts +51 -0
  270. package/dist/web/features/esbuild.d.ts.map +1 -0
  271. package/dist/web/features/helpers.d.ts +140 -0
  272. package/dist/web/features/helpers.d.ts.map +1 -0
  273. package/dist/web/features/network.d.ts +69 -0
  274. package/dist/web/features/network.d.ts.map +1 -0
  275. package/dist/web/features/speech.d.ts +71 -0
  276. package/dist/web/features/speech.d.ts.map +1 -0
  277. package/dist/web/features/vault.d.ts +62 -0
  278. package/dist/web/features/vault.d.ts.map +1 -0
  279. package/dist/web/features/vm.d.ts +48 -0
  280. package/dist/web/features/vm.d.ts.map +1 -0
  281. package/dist/web/features/voice-recognition.d.ts +96 -0
  282. package/dist/web/features/voice-recognition.d.ts.map +1 -0
  283. package/dist/web/shims/isomorphic-vm.d.ts +22 -0
  284. package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
  285. package/docs/apis/features/agi/assistant.md +1 -0
  286. package/docs/apis/features/agi/assistants-manager.md +62 -2
  287. package/docs/apis/features/agi/auto-assistant.md +11 -109
  288. package/docs/apis/features/agi/claude-code.md +138 -0
  289. package/docs/apis/features/agi/conversation.md +60 -31
  290. package/docs/apis/features/agi/luca-coder.md +407 -0
  291. package/docs/apis/features/agi/openapi.md +2 -2
  292. package/docs/apis/features/agi/skills-library.md +12 -0
  293. package/docs/apis/features/node/python.md +81 -11
  294. package/docs/apis/features/node/transpiler.md +74 -0
  295. package/docs/apis/features/web/esbuild.md +0 -6
  296. package/docs/apis/servers/mcp.md +2 -2
  297. package/docs/examples/entity.md +124 -0
  298. package/package.json +73 -21
  299. package/src/agi/feature.ts +13 -0
  300. package/src/agi/features/assistant.ts +36 -25
  301. package/src/agi/features/assistants-manager.ts +70 -5
  302. package/src/agi/features/autonomous-assistant.ts +1 -5
  303. package/src/agi/features/browser-use.ts +2 -2
  304. package/src/agi/features/claude-code.ts +165 -1
  305. package/src/agi/features/conversation-history.ts +2 -6
  306. package/src/agi/features/conversation.ts +95 -3
  307. package/src/agi/features/docs-reader.ts +2 -1
  308. package/src/agi/features/file-tools.ts +2 -2
  309. package/src/agi/features/luca-coder.ts +1 -5
  310. package/src/agi/features/openai-codex.ts +1 -1
  311. package/src/agi/features/openapi.ts +3 -3
  312. package/src/agi/features/skills-library.ts +87 -6
  313. package/src/agi/lib/interceptor-chain.ts +10 -0
  314. package/src/agi/lib/token-counter.ts +1 -1
  315. package/src/bootstrap/generated.ts +126 -1
  316. package/src/bus.ts +27 -5
  317. package/src/cli/build-info.ts +2 -2
  318. package/src/client.ts +2 -2
  319. package/src/clients/elevenlabs/index.ts +5 -0
  320. package/src/commands/bootstrap.ts +2 -1
  321. package/src/commands/chat.ts +1 -0
  322. package/src/commands/code.ts +4 -2
  323. package/src/commands/prompt.ts +34 -34
  324. package/src/commands/sandbox-mcp.ts +69 -163
  325. package/src/commands/save-api-docs.ts +10 -8
  326. package/src/commands/select.ts +8 -3
  327. package/src/container-describer.ts +70 -84
  328. package/src/container.ts +93 -3
  329. package/src/endpoint.ts +1 -1
  330. package/src/entity.ts +173 -0
  331. package/src/feature.ts +3 -3
  332. package/src/helper.ts +8 -4
  333. package/src/introspection/generated.agi.ts +1246 -798
  334. package/src/introspection/generated.node.ts +892 -798
  335. package/src/introspection/generated.web.ts +95 -3
  336. package/src/introspection/scan.ts +1 -1
  337. package/src/node/container.ts +1 -1
  338. package/src/node/features/content-db.ts +3 -3
  339. package/src/node/features/file-manager.ts +10 -9
  340. package/src/node/features/git.ts +5 -5
  341. package/src/node/features/helpers.ts +1 -1
  342. package/src/node/features/json-tree.ts +1 -1
  343. package/src/node/features/os.ts +3 -3
  344. package/src/node/features/package-finder.ts +1 -1
  345. package/src/node/features/process-manager.ts +1 -1
  346. package/src/node/features/python.ts +3 -3
  347. package/src/node/features/redis.ts +1 -1
  348. package/src/node/features/repl.ts +2 -2
  349. package/src/node/features/transpiler.ts +2 -2
  350. package/src/node/features/ui.ts +1 -1
  351. package/src/node/features/vm.ts +3 -3
  352. package/src/node/features/yaml-tree.ts +1 -1
  353. package/src/node.ts +1 -0
  354. package/src/python/generated.ts +1 -1
  355. package/src/scaffolds/generated.ts +1 -1
  356. package/src/selector.ts +74 -4
  357. package/src/server.ts +2 -2
  358. package/src/servers/mcp.ts +6 -6
  359. package/src/web/features/helpers.ts +1 -1
  360. package/src/web/features/network.ts +1 -0
  361. package/test/conversation.test.ts +220 -0
  362. package/tsconfig.build.json +12 -0
  363. package/tsconfig.json +1 -1
  364. package/scripts/examples/telegram-ink-ui.ts +0 -302
  365. package/scripts/examples/using-openai-codex.ts +0 -23
  366. package/scripts/examples/vm-loading-esm-modules.ts +0 -16
@@ -2,7 +2,7 @@
2
2
  import { z } from 'zod'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
4
4
  import { type AvailableFeatures } from '@soederpop/luca/feature'
5
- import { Feature } from '@soederpop/luca/feature'
5
+ import { Feature } from '../feature.js'
6
6
 
7
7
  declare module '@soederpop/luca/feature' {
8
8
  interface AvailableFeatures {
@@ -1349,6 +1349,170 @@ export class ClaudeCode extends Feature<ClaudeCodeState, ClaudeCodeOptions> {
1349
1349
  }
1350
1350
  }
1351
1351
 
1352
+ /**
1353
+ * List all Claude Code processes currently registered in ~/.claude/sessions/.
1354
+ * Returns each session's metadata along with whether the process is still alive.
1355
+ *
1356
+ * @returns {Promise<Array<{ pid: number; sessionId: string; cwd: string; startedAt: number; kind: string; entrypoint: string; alive: boolean }>>}
1357
+ *
1358
+ * @example
1359
+ * const sessions = await cc.listProcessSessions()
1360
+ * for (const s of sessions) {
1361
+ * console.log(`[${s.alive ? 'LIVE' : 'dead'}] PID ${s.pid} in ${s.cwd}`)
1362
+ * }
1363
+ */
1364
+ async listProcessSessions(): Promise<Array<{
1365
+ pid: number
1366
+ sessionId: string
1367
+ cwd: string
1368
+ startedAt: number
1369
+ kind: string
1370
+ entrypoint: string
1371
+ alive: boolean
1372
+ }>> {
1373
+ const fs = this.container.feature('fs')
1374
+ const proc = this.container.feature('proc')
1375
+ const home = process.env.HOME ?? '/tmp'
1376
+ const sessionsDir = `${home}/.claude/sessions`
1377
+
1378
+ let files: string[]
1379
+ try {
1380
+ files = await fs.readdir(sessionsDir)
1381
+ } catch {
1382
+ return []
1383
+ }
1384
+
1385
+ const jsonFiles = files.filter((f: string) => f.endsWith('.json'))
1386
+
1387
+ const results = await Promise.all(jsonFiles.map(async (file: string) => {
1388
+ try {
1389
+ const raw = await fs.readFile(`${sessionsDir}/${file}`, 'utf8')
1390
+ const data = JSON.parse(raw)
1391
+ let alive = false
1392
+ try {
1393
+ await proc.exec(`kill -0 ${data.pid}`)
1394
+ alive = true
1395
+ } catch {
1396
+ alive = false
1397
+ }
1398
+ return { ...data, alive }
1399
+ } catch {
1400
+ return null
1401
+ }
1402
+ }))
1403
+
1404
+ return results.filter(Boolean)
1405
+ }
1406
+
1407
+ /**
1408
+ * Read a single process session by PID from ~/.claude/sessions/<pid>.json.
1409
+ *
1410
+ * @param {number} pid - The process ID
1411
+ * @returns {Promise<{ pid: number; sessionId: string; cwd: string; startedAt: number; kind: string; entrypoint: string } | null>}
1412
+ *
1413
+ * @example
1414
+ * const session = await cc.getProcessSession(12345)
1415
+ * console.log(session?.cwd)
1416
+ */
1417
+ async getProcessSession(pid: number): Promise<{
1418
+ pid: number
1419
+ sessionId: string
1420
+ cwd: string
1421
+ startedAt: number
1422
+ kind: string
1423
+ entrypoint: string
1424
+ } | null> {
1425
+ const fs = this.container.feature('fs')
1426
+ const home = process.env.HOME ?? '/tmp'
1427
+ try {
1428
+ const raw = await fs.readFile(`${home}/.claude/sessions/${pid}.json`, 'utf8')
1429
+ return JSON.parse(raw)
1430
+ } catch {
1431
+ return null
1432
+ }
1433
+ }
1434
+
1435
+ /**
1436
+ * Read the conversation history for a Claude Code session from its JSONL file in
1437
+ * ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl.
1438
+ *
1439
+ * Returns an array of parsed message objects (user, assistant, tool_use, tool_result).
1440
+ *
1441
+ * @param {string} sessionId - The Claude CLI session ID (from listProcessSessions or getProcessSession)
1442
+ * @param {string} cwd - The working directory of the session (used to locate the project folder)
1443
+ * @returns {Promise<any[]>} Array of parsed JSONL records
1444
+ *
1445
+ * @example
1446
+ * const sessions = await cc.listProcessSessions()
1447
+ * const s = sessions[0]
1448
+ * const history = await cc.getConversationHistory(s.sessionId, s.cwd)
1449
+ * console.log(history.length, 'turns')
1450
+ */
1451
+ async getConversationHistory(sessionId: string, cwd?: string): Promise<any[]> {
1452
+ const fs = this.container.feature('fs')
1453
+ const home = process.env.HOME ?? '/tmp'
1454
+ const resolvedCwd = cwd ?? this.options.cwd ?? (this.container as any).cwd
1455
+ const encodedCwd = resolvedCwd.replace(/\//g, '-').replace(/@/g, '-')
1456
+ const filePath = `${home}/.claude/projects/${encodedCwd}/${sessionId}.jsonl`
1457
+ try {
1458
+ const raw = await fs.readFile(filePath, 'utf8')
1459
+ return raw
1460
+ .split('\n')
1461
+ .filter((line: string) => line.trim())
1462
+ .map((line: string) => JSON.parse(line))
1463
+ } catch {
1464
+ return []
1465
+ }
1466
+ }
1467
+
1468
+ /**
1469
+ * List all conversation sessions stored for a given working directory.
1470
+ * Reads ~/.claude/projects/<encoded-cwd>/ and returns metadata for each .jsonl file.
1471
+ *
1472
+ * @param {string} cwd - The working directory path to look up
1473
+ * @returns {Promise<Array<{ sessionId: string; filePath: string; messageCount: number }>>}
1474
+ *
1475
+ * @example
1476
+ * const sessions = await cc.listSessionsForCwd('/Users/me/my-project')
1477
+ * for (const s of sessions) {
1478
+ * console.log(s.sessionId, s.messageCount, 'messages')
1479
+ * }
1480
+ */
1481
+ async listSessionsForCwd(cwd?: string): Promise<Array<{
1482
+ sessionId: string
1483
+ filePath: string
1484
+ messageCount: number
1485
+ }>> {
1486
+ const fs = this.container.feature('fs')
1487
+ const home = process.env.HOME ?? '/tmp'
1488
+ const resolvedCwd = cwd ?? this.options.cwd ?? (this.container as any).cwd
1489
+ const encodedCwd = resolvedCwd.replace(/\//g, '-').replace(/@/g, '-')
1490
+ const projectDir = `${home}/.claude/projects/${encodedCwd}`
1491
+
1492
+ let files: string[]
1493
+ try {
1494
+ files = await fs.readdir(projectDir)
1495
+ } catch {
1496
+ return []
1497
+ }
1498
+
1499
+ const jsonlFiles = files.filter((f: string) => f.endsWith('.jsonl'))
1500
+
1501
+ const results = await Promise.all(jsonlFiles.map(async (file: string) => {
1502
+ const sessionId = file.replace(/\.jsonl$/, '')
1503
+ const filePath = `${projectDir}/${file}`
1504
+ try {
1505
+ const raw = await fs.readFile(filePath, 'utf8')
1506
+ const messageCount = raw.split('\n').filter((l: string) => l.trim()).length
1507
+ return { sessionId, filePath, messageCount }
1508
+ } catch {
1509
+ return { sessionId, filePath, messageCount: 0 }
1510
+ }
1511
+ }))
1512
+
1513
+ return results
1514
+ }
1515
+
1352
1516
  /**
1353
1517
  * Clean up any temp MCP config files created during sessions.
1354
1518
  */
@@ -1,8 +1,8 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { type AvailableFeatures } from '@soederpop/luca/feature'
4
- import { Feature } from '@soederpop/luca/feature'
5
- import { NodeContainer, type DiskCache, type NodeFeatures } from '@soederpop/luca/node/container'
4
+ import { Feature } from '../feature.js'
5
+ import type { DiskCache } from '@soederpop/luca/node/container'
6
6
  import type { Message } from './conversation'
7
7
 
8
8
  declare module '@soederpop/luca/feature' {
@@ -95,10 +95,6 @@ export class ConversationHistory extends Feature<ConversationHistoryState, Conve
95
95
  }
96
96
  }
97
97
 
98
- /** @returns The parent NodeContainer, narrowed from the base Container type. */
99
- override get container() {
100
- return super.container as NodeContainer<NodeFeatures, any>
101
- }
102
98
 
103
99
  /** @returns The diskCache feature instance used for persistence, configured with the optional cachePath. */
104
100
  get diskCache(): DiskCache {
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { type AvailableFeatures } from '@soederpop/luca/feature'
4
- import { Feature } from '@soederpop/luca/feature'
4
+ import { Feature } from '../feature.js'
5
5
  import type { OpenAIClient } from '../../clients/openai';
6
6
  import type OpenAI from 'openai';
7
7
  import type { ConversationHistory } from './conversation-history';
@@ -212,9 +212,12 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
212
212
  /** The active structured output schema for the current ask() call, if any. */
213
213
  private _activeSchema: z.ZodType | null = null
214
214
 
215
- /** Resolved max tokens: per-call override > options-level > default 512. */
215
+ /** Registered stubs: matched against user input to short-circuit the API with a canned response. */
216
+ private _stubs: Array<{ matcher: string | RegExp; response: string | (() => string) }> = []
217
+
218
+ /** Resolved max tokens: per-call override > options-level. Undefined means no limit (model default). */
216
219
  private get maxTokens(): number | undefined {
217
- return (this.state.get('callMaxTokens') as number | null) ?? this.options.maxTokens ?? 512
220
+ return (this.state.get('callMaxTokens') as number | null) ?? this.options.maxTokens ?? undefined
218
221
  }
219
222
 
220
223
  /** @returns Default state seeded from options: id, thread, model, initial history, and zero token usage. */
@@ -276,6 +279,22 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
276
279
  return this
277
280
  }
278
281
 
282
+ /**
283
+ * Register a hardcoded stub response that bypasses the API when the user's message matches.
284
+ * Streaming is still simulated — chunk/preview events fire word-by-word.
285
+ *
286
+ * @param matcher - Exact string match, substring, or RegExp tested against user input
287
+ * @param response - The text to stream back, or a zero-arg function that returns it
288
+ *
289
+ * @example
290
+ * conversation.stub('hello', 'Hi there!')
291
+ * conversation.stub(/weather/i, () => 'Sunny and 72°F.')
292
+ */
293
+ stub(matcher: string | RegExp, response: string | (() => string)): this {
294
+ this._stubs.push({ matcher, response })
295
+ return this
296
+ }
297
+
279
298
  /** Returns configured remote MCP servers keyed by server label. */
280
299
  get mcpServers(): Record<string, ConversationMCPServer> {
281
300
  return (this.options.mcpServers || {}) as Record<string, ConversationMCPServer>
@@ -286,6 +305,30 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
286
305
  return this.state.get('messages') || []
287
306
  }
288
307
 
308
+ /**
309
+ * Fork the conversation into a new independent instance.
310
+ * The fork inherits the same system prompt, tools, and full message history,
311
+ * but has its own identity and state — changes in either direction do not affect the other.
312
+ *
313
+ * @param overrides - Optional option overrides for the forked conversation (e.g. different model or title)
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * const fork = conversation.fork()
318
+ * await fork.ask('What if we took a different approach?')
319
+ * // original conversation is unchanged
320
+ * ```
321
+ */
322
+ fork(overrides: Partial<ConversationOptions> = {}): Conversation {
323
+ return this.container.feature('conversation', {
324
+ ...this.options,
325
+ id: undefined,
326
+ history: JSON.parse(JSON.stringify(this.messages)),
327
+ tools: { ...this.tools },
328
+ ...overrides,
329
+ })
330
+ }
331
+
289
332
  /** Returns the OpenAI model name being used for completions. */
290
333
  get model(): string {
291
334
  return this.state.get('model')!
@@ -518,6 +561,11 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
518
561
  this.emit('userMessage', content)
519
562
 
520
563
  try {
564
+ const stubText = this._matchStub(typeof content === 'string' ? content : '')
565
+ if (stubText !== null) {
566
+ return await this._streamStub(stubText)
567
+ }
568
+
521
569
  let raw: string
522
570
 
523
571
  if (this.apiMode === 'responses') {
@@ -764,6 +812,50 @@ export class Conversation extends Feature<ConversationState, ConversationOptions
764
812
  }
765
813
  }
766
814
 
815
+ /** Check registered stubs against user input. Returns the response text, or null if no match. */
816
+ private _matchStub(input: string): string | null {
817
+ for (const { matcher, response } of this._stubs) {
818
+ const matched = typeof matcher === 'string'
819
+ ? input === matcher || input.includes(matcher)
820
+ : matcher.test(input)
821
+ if (matched) {
822
+ return typeof response === 'function' ? response() : response
823
+ }
824
+ }
825
+ return null
826
+ }
827
+
828
+ /**
829
+ * Simulate a streaming response for a hardcoded stub text.
830
+ * Emits chunk/preview events word-by-word, yielding between each to keep the event loop alive.
831
+ */
832
+ private async _streamStub(text: string): Promise<string> {
833
+ this.state.set('streaming', true)
834
+ this.emit('turnStart', { turn: 1, isFollowUp: false })
835
+
836
+ let accumulated = ''
837
+ const chunks = text.match(/\S+\s*/g) ?? [text]
838
+
839
+ try {
840
+ for (const chunk of chunks) {
841
+ accumulated += chunk
842
+ this.emit('chunk', chunk)
843
+ this.emit('preview', accumulated)
844
+ await Promise.resolve()
845
+ }
846
+ } finally {
847
+ this.state.set('streaming', false)
848
+ }
849
+
850
+ const trimmed = text
851
+ this.pushMessage({ role: 'assistant', content: trimmed })
852
+ this.state.set('lastResponse', trimmed)
853
+ this.emit('turnEnd', { turn: 1, hasToolCalls: false })
854
+ this.emit('response', trimmed)
855
+
856
+ return trimmed
857
+ }
858
+
767
859
  /**
768
860
  * Runs the streaming Responses API loop. Handles local function calls by
769
861
  * executing handlers and submitting `function_call_output` items until
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { type AvailableFeatures, Feature } from '@soederpop/luca/feature'
3
+ import { type AvailableFeatures } from '@soederpop/luca/feature'
4
+ import { Feature } from '../feature.js'
4
5
  import type { ContentDb } from '@/node.js'
5
6
  import type Assistant from './assistant.js'
6
7
 
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature } from '@soederpop/luca/feature'
3
+ import { Feature } from '../feature.js'
4
4
  import type { FS } from '../../node/features/fs.js'
5
5
  import type { Grep, GrepMatch } from '../../node/features/grep.js'
6
6
  import type { Helper } from '../../helper.js'
@@ -38,7 +38,7 @@ export class FileTools extends Feature {
38
38
 
39
39
  static { Feature.register(this, 'fileTools') }
40
40
 
41
- static tools: Record<string, { schema: z.ZodType; description?: string }> = {
41
+ static override tools: Record<string, { schema: z.ZodType; description?: string }> = {
42
42
  readFile: {
43
43
  description: 'Read the contents of a file. Returns the text content. Use offset/limit to read portions of large files.',
44
44
  schema: z.object({
@@ -1,7 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { Feature } from '@soederpop/luca/feature'
4
- import type { AGIContainer } from '../container.server.js'
3
+ import { Feature } from '../feature.js'
5
4
  import type { Assistant } from './assistant.js'
6
5
  import type { ToolCallCtx } from '../lib/interceptor-chain.js'
7
6
 
@@ -214,9 +213,6 @@ export class LucaCoder extends Feature<LucaCoderState, LucaCoderOptions> {
214
213
  }
215
214
  }
216
215
 
217
- override get container(): AGIContainer {
218
- return super.container as AGIContainer
219
- }
220
216
 
221
217
  /** The inner assistant. Throws if not started. */
222
218
  get assistant(): Assistant {
@@ -2,7 +2,7 @@
2
2
  import { z } from 'zod'
3
3
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
4
4
  import { type AvailableFeatures } from '@soederpop/luca/feature'
5
- import { Feature } from '@soederpop/luca/feature'
5
+ import { Feature } from '../feature.js'
6
6
 
7
7
  declare module '@soederpop/luca/feature' {
8
8
  interface AvailableFeatures {
@@ -1,4 +1,4 @@
1
- import { Feature } from '../../feature.js'
1
+ import { Feature } from '../feature.js'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { z } from 'zod'
4
4
  import { camelCase } from 'lodash-es'
@@ -95,7 +95,7 @@ export interface OpenAIToolDef {
95
95
  * api.endpoint('getPetById')
96
96
  *
97
97
  * // Convert to OpenAI tool definitions
98
- * api.toTools()
98
+ * api.toOpenAITools()
99
99
  *
100
100
  * // Convert a single endpoint to a function definition
101
101
  * api.toFunction('getPetById')
@@ -205,7 +205,7 @@ export class OpenAPI extends Feature<OpenAPIState, OpenAPIOptions> {
205
205
  * @param {Function} [filter] - Optional predicate to select which endpoints to include
206
206
  * @returns {OpenAIToolDef[]} Array of tool definitions ready for the OpenAI tools parameter
207
207
  */
208
- toTools(filter?: (ep: EndpointInfo) => boolean): OpenAIToolDef[] {
208
+ toOpenAITools(filter?: (ep: EndpointInfo) => boolean): OpenAIToolDef[] {
209
209
  const eps = filter ? this.endpoints.filter(filter) : this.endpoints
210
210
  return eps.map((ep) => ({
211
211
  type: 'function' as const,
@@ -1,9 +1,10 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { type AvailableFeatures, Feature } from '@soederpop/luca/feature'
3
+ import { type AvailableFeatures } from '@soederpop/luca/feature'
4
+ import { Feature } from '../feature.js'
4
5
  import { parse } from 'contentbase'
5
6
  import type { DocsReader } from './docs-reader.js'
6
- import type Assistant from './assistant.js'
7
+ import Assistant from './assistant.js'
7
8
 
8
9
  declare module '@soederpop/luca/feature' {
9
10
  interface AvailableFeatures {
@@ -72,7 +73,7 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
72
73
  static { Feature.register(this, 'skillsLibrary') }
73
74
 
74
75
  /** Tools for assistant integration via assistant.use(skillsLibrary). */
75
- static tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
76
+ static override tools: Record<string, { schema: z.ZodType; handler?: Function }> = {
76
77
  searchAvailableSkills: {
77
78
  schema: z.object({
78
79
  query: z.string().optional().describe('Optional search term to filter skills by name or description'),
@@ -102,9 +103,46 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
102
103
  }
103
104
  }
104
105
 
105
- override setupToolsConsumer(assistant: Assistant) {
106
- console.log('setting up tools consumer', assistant.uuid)
107
- assistant.state.set('toolsSetup', true)
106
+ override setupToolsConsumer(assistant: Feature) {
107
+ if (!(assistant instanceof Assistant)) {
108
+ throw new Error('Skills library tools require an Assistant instance (including subclasses).')
109
+ }
110
+
111
+ const a : Assistant = assistant as Assistant
112
+
113
+ const { container } = a
114
+
115
+ const skillsLibrary = this
116
+
117
+ const preloadSkills : string[] = []
118
+ if (a.meta.skills) {
119
+ if (Array.isArray(a.meta.skills)) {
120
+ preloadSkills.push(...a.meta.skills)
121
+ } else {
122
+ preloadSkills.push(a.meta.skills)
123
+ }
124
+ }
125
+
126
+ async function beforeAskCheckIfWeNeedSkills(ctx: any, next: any) {
127
+ const { question } = ctx
128
+ const skills = await skillsLibrary.findRelevantSkillsForAssistant(a, question as string)
129
+
130
+ const allSkillsToLoad : string[] = container.utils.lodash.uniq([
131
+ ...skills,
132
+ ...preloadSkills,
133
+ ])
134
+
135
+ if (allSkillsToLoad.length) {
136
+ ctx.question = `${ctx.question} \n\n## Required Skills\nYou will need to load the following skills to answer this question: ${skills.join(', ')}`
137
+ }
138
+
139
+ a.interceptors.beforeAsk.remove(beforeAskCheckIfWeNeedSkills)
140
+
141
+ await next()
142
+ }
143
+
144
+ assistant.intercept('beforeAsk', beforeAskCheckIfWeNeedSkills as any)
145
+
108
146
  return assistant
109
147
  }
110
148
 
@@ -375,6 +413,49 @@ export class SkillsLibrary extends Feature<SkillsLibraryState, SkillsLibraryOpti
375
413
  const answer = await reader.ask(question)
376
414
  return answer
377
415
  }
416
+
417
+ /**
418
+ * Fork the given assistant and ask it which skills (if any) are relevant
419
+ * to the user's query. Returns an array of skill names that should be loaded
420
+ * before the real question is answered.
421
+ *
422
+ * The fork is ephemeral (historyMode: 'none') and uses structured output so
423
+ * the result is always a clean string array — never free text.
424
+ *
425
+ * @param assistant - The assistant instance to fork
426
+ * @param userQuery - The user's original question
427
+ * @returns Array of skill names relevant to the query (may be empty)
428
+ */
429
+ async findRelevantSkillsForAssistant(assistant: Assistant, userQuery: string): Promise<string[]> {
430
+ if (!this.isStarted) await this.start()
431
+
432
+ const skills = this.list()
433
+ if (skills.length === 0) return []
434
+
435
+ const responseSchema = z.object({
436
+ skills: z.array(z.string()).describe('Names of skills relevant to the query. Empty array if none apply.'),
437
+ })
438
+
439
+ const skillsDescription = Object.entries(this.skillsTable)
440
+ .map(([title,description]) => `- **${title}**: ${description}`)
441
+ .join("\n")
442
+
443
+ 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.
444
+ Available skills:
445
+ -------
446
+ ${skillsDescription}
447
+
448
+ User query: ${userQuery}
449
+
450
+ 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.`)
451
+
452
+ const fork = assistant.conversation.fork()
453
+ const result = await fork.ask(prompt, { schema: responseSchema }) as unknown as { skills: string[] }
454
+
455
+ console.log('Got a result', result)
456
+
457
+ return result.skills.filter(name => this.find(name) !== undefined)
458
+ }
378
459
  }
379
460
 
380
461
  export default SkillsLibrary
@@ -26,6 +26,16 @@ export class InterceptorChain<T> {
26
26
  return this.fns.length
27
27
  }
28
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
+
29
39
  async run(ctx: T, final: () => Promise<void>): Promise<void> {
30
40
  let index = 0
31
41
  const fns = this.fns
@@ -39,7 +39,7 @@ export function getContextWindow(model: string): number {
39
39
  }
40
40
  }
41
41
 
42
- return best ? MODEL_CONTEXT_WINDOWS[best] : DEFAULT_CONTEXT_WINDOW
42
+ return best ? MODEL_CONTEXT_WINDOWS[best] ?? DEFAULT_CONTEXT_WINDOW : DEFAULT_CONTEXT_WINDOW
43
43
  }
44
44
 
45
45
  /** Get a cached tiktoken encoder for a model (falls back to o200k_base). */