luca 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/.github/workflows/release.yaml +1 -0
  2. package/CLAUDE.md +10 -2
  3. package/README.md +130 -112
  4. package/assistants/codingAssistant/CORE.md +6 -1
  5. package/assistants/codingAssistant/hooks.ts +1 -1
  6. package/assistants/inkbot/hooks.ts +1 -1
  7. package/assistants/inkbot/tools.ts +1 -1
  8. package/bun.lock +264 -321
  9. package/commands/audit-docs.ts +2 -2
  10. package/commands/build-bootstrap.ts +2 -3
  11. package/commands/build-python-bridge.ts +2 -3
  12. package/commands/build-scaffolds.ts +2 -3
  13. package/commands/bundle-consumer-project.ts +521 -0
  14. package/commands/generate-api-docs.ts +2 -2
  15. package/commands/inkbot.ts +2 -2
  16. package/commands/release.ts +2 -2
  17. package/commands/social.ts +137 -0
  18. package/commands/try-all-challenges.ts +3 -3
  19. package/commands/try-challenge.ts +3 -3
  20. package/datasets/lora/agentic-loop-session-candidates.jsonl +91 -0
  21. package/datasets/lora/agentic-loop-session-curation-summary.json +123 -0
  22. package/datasets/lora/luca-session-candidates.jsonl +29 -0
  23. package/datasets/lora/luca-session-curation-summary.json +121 -0
  24. package/datasets/lora/review-batch-1.jsonl +30 -0
  25. package/datasets/lora/review-manifest.json +41 -0
  26. package/datasets/lora/review-queue.jsonl +120 -0
  27. package/datasets/lora/review-schema.json +134 -0
  28. package/datasets/lora/review-template.jsonl +2 -0
  29. package/datasets/lora/review-ui.html +725 -0
  30. package/dist/agi/container.server.d.ts +2 -2
  31. package/dist/agi/features/assistant.d.ts +2 -2
  32. package/dist/agi/features/assistants-manager.d.ts +1 -1
  33. package/dist/agi/features/autonomous-assistant.d.ts +1 -1
  34. package/dist/agi/features/browser-use.d.ts +1 -1
  35. package/dist/agi/features/claude-code.d.ts +1 -1
  36. package/dist/agi/features/conversation-history.d.ts +2 -2
  37. package/dist/agi/features/conversation.d.ts +1 -1
  38. package/dist/agi/features/docs-reader.d.ts +1 -1
  39. package/dist/agi/features/file-tools.d.ts +1 -1
  40. package/dist/agi/features/luca-coder.d.ts +1 -1
  41. package/dist/agi/features/openai-codex.d.ts +1 -1
  42. package/dist/agi/features/skills-library.d.ts +1 -1
  43. package/dist/clients/civitai/index.d.ts +4 -4
  44. package/dist/clients/client-template.d.ts +4 -4
  45. package/dist/clients/comfyui/index.d.ts +2 -2
  46. package/dist/clients/elevenlabs/index.d.ts +2 -2
  47. package/dist/clients/openai/index.d.ts +2 -2
  48. package/dist/clients/supabase/index.d.ts +3 -3
  49. package/dist/command.d.ts +1 -1
  50. package/dist/node/container.d.ts +1 -1
  51. package/dist/node/features/helpers.d.ts +3 -3
  52. package/dist/node/features/semantic-search.d.ts +1 -1
  53. package/dist/node/features/vm.d.ts +3 -3
  54. package/dist/node.d.ts +1 -1
  55. package/dist/scaffolds/generated.d.ts +1 -1
  56. package/dist/selector.d.ts +1 -1
  57. package/features/cipher-social.ts +493 -0
  58. package/index.html +217 -190
  59. package/luca.console.ts +1 -1
  60. package/package.json +7 -2
  61. package/public/index.html +217 -190
  62. package/public/slides-ai-native.html +1 -1
  63. package/public/slides-intro.html +2 -2
  64. package/scripts/curate-claude-sessions.ts +561 -0
  65. package/scripts/examples/ask-luca-expert.ts +1 -1
  66. package/scripts/examples/assistant-questions.ts +1 -1
  67. package/scripts/examples/excalidraw-expert.ts +1 -1
  68. package/scripts/examples/file-manager.ts +1 -1
  69. package/scripts/examples/ideas.ts +1 -1
  70. package/scripts/examples/interactive-chat.ts +1 -1
  71. package/scripts/examples/opening-a-web-browser.ts +1 -1
  72. package/scripts/examples/telegram-bot.ts +1 -1
  73. package/scripts/examples/using-assistant-with-mcp.ts +1 -1
  74. package/scripts/examples/using-claude-code.ts +1 -1
  75. package/scripts/examples/using-contentdb.ts +2 -2
  76. package/scripts/examples/using-conversations.ts +1 -1
  77. package/scripts/examples/using-disk-cache.ts +1 -1
  78. package/scripts/examples/using-docker-shell.ts +1 -1
  79. package/scripts/examples/using-elevenlabs.ts +1 -1
  80. package/scripts/examples/using-google-calendar.ts +1 -1
  81. package/scripts/examples/using-google-docs.ts +1 -1
  82. package/scripts/examples/using-google-drive.ts +1 -1
  83. package/scripts/examples/using-google-sheets.ts +1 -1
  84. package/scripts/examples/using-nlp.ts +1 -1
  85. package/scripts/examples/using-ollama.ts +1 -1
  86. package/scripts/examples/using-postgres.ts +1 -1
  87. package/scripts/examples/using-runpod.ts +1 -1
  88. package/scripts/examples/using-tts.ts +1 -1
  89. package/scripts/scaffold.ts +5 -5
  90. package/scripts/scratch.ts +1 -1
  91. package/scripts/test-assistant-hooks.ts +1 -1
  92. package/scripts/test-docs-reader.ts +1 -1
  93. package/src/agi/container.server.ts +6 -2
  94. package/src/agi/features/agent-memory.ts +25 -25
  95. package/src/agi/features/assistant.ts +34 -5
  96. package/src/agi/features/assistants-manager.ts +122 -6
  97. package/src/agi/features/autonomous-assistant.ts +1 -1
  98. package/src/agi/features/browser-use.ts +20 -1
  99. package/src/agi/features/claude-code.ts +51 -5
  100. package/src/agi/features/coding-tools.ts +1 -1
  101. package/src/agi/features/conversation-history.ts +181 -4
  102. package/src/agi/features/conversation.ts +186 -15
  103. package/src/agi/features/docs-reader.ts +2 -2
  104. package/src/agi/features/file-tools.ts +49 -2
  105. package/src/agi/features/luca-coder.ts +7 -5
  106. package/src/agi/features/mcp-bridge.ts +532 -0
  107. package/src/agi/features/openai-codex.ts +2 -2
  108. package/src/agi/features/skills-library.ts +131 -52
  109. package/src/agi/lib/token-counter.ts +80 -0
  110. package/src/bootstrap/generated.ts +56 -57
  111. package/src/browser.ts +1 -1
  112. package/src/cli/build-info.ts +2 -2
  113. package/src/cli/cli.ts +2 -2
  114. package/src/clients/civitai/index.ts +5 -5
  115. package/src/clients/client-template.ts +4 -4
  116. package/src/clients/comfyui/index.ts +4 -4
  117. package/src/clients/elevenlabs/index.ts +4 -4
  118. package/src/clients/openai/index.ts +7 -7
  119. package/src/clients/supabase/index.ts +4 -4
  120. package/src/clients/voicebox/index.ts +4 -4
  121. package/src/command.ts +2 -1
  122. package/src/commands/chat.ts +1 -0
  123. package/src/commands/eval.ts +2 -56
  124. package/src/commands/introspect.ts +1 -1
  125. package/src/commands/prompt.ts +41 -9
  126. package/src/container-describer.ts +8 -1
  127. package/src/container.ts +13 -0
  128. package/src/entity.ts +2 -2
  129. package/src/helper.ts +1 -1
  130. package/src/introspection/generated.agi.ts +29596 -27654
  131. package/src/introspection/generated.node.ts +20284 -19247
  132. package/src/introspection/generated.web.ts +605 -584
  133. package/src/introspection/scan.ts +11 -6
  134. package/src/node/container.ts +9 -1
  135. package/src/node/features/content-db.ts +39 -2
  136. package/src/node/features/display-result.ts +57 -0
  137. package/src/node/features/helpers.ts +46 -7
  138. package/src/node/features/python.ts +25 -19
  139. package/src/node/features/repl.ts +1 -1
  140. package/src/node/features/secure-shell.ts +11 -17
  141. package/src/node/features/semantic-search.ts +2 -2
  142. package/src/node/features/socket-repl.ts +336 -0
  143. package/src/node/features/telnyx-assistant-connector.ts +1206 -0
  144. package/src/node/features/transpiler.ts +2 -3
  145. package/src/node/features/ui.ts +5 -0
  146. package/src/node/features/vm.ts +20 -3
  147. package/src/node.ts +3 -3
  148. package/src/python/generated.ts +0 -1
  149. package/src/scaffolds/generated.ts +82 -83
  150. package/src/selector.ts +1 -1
  151. package/src/servers/express.ts +1 -1
  152. package/src/web/features/helpers.ts +22 -0
  153. package/tsconfig.json +12 -12
  154. package/docs/CLI.md +0 -335
  155. package/docs/CNAME +0 -1
  156. package/docs/README.md +0 -60
  157. package/docs/TABLE-OF-CONTENTS.md +0 -183
  158. package/docs/apis/clients/elevenlabs.md +0 -308
  159. package/docs/apis/clients/graph.md +0 -107
  160. package/docs/apis/clients/openai.md +0 -429
  161. package/docs/apis/clients/rest.md +0 -161
  162. package/docs/apis/clients/websocket.md +0 -174
  163. package/docs/apis/features/agi/assistant.md +0 -625
  164. package/docs/apis/features/agi/assistants-manager.md +0 -282
  165. package/docs/apis/features/agi/auto-assistant.md +0 -279
  166. package/docs/apis/features/agi/browser-use.md +0 -802
  167. package/docs/apis/features/agi/claude-code.md +0 -884
  168. package/docs/apis/features/agi/conversation-history.md +0 -364
  169. package/docs/apis/features/agi/conversation.md +0 -548
  170. package/docs/apis/features/agi/docs-reader.md +0 -99
  171. package/docs/apis/features/agi/file-tools.md +0 -163
  172. package/docs/apis/features/agi/luca-coder.md +0 -407
  173. package/docs/apis/features/agi/openai-codex.md +0 -396
  174. package/docs/apis/features/agi/openapi.md +0 -138
  175. package/docs/apis/features/agi/semantic-search.md +0 -387
  176. package/docs/apis/features/agi/skills-library.md +0 -239
  177. package/docs/apis/features/node/container-link.md +0 -192
  178. package/docs/apis/features/node/content-db.md +0 -450
  179. package/docs/apis/features/node/disk-cache.md +0 -379
  180. package/docs/apis/features/node/dns.md +0 -652
  181. package/docs/apis/features/node/docker.md +0 -706
  182. package/docs/apis/features/node/downloader.md +0 -81
  183. package/docs/apis/features/node/esbuild.md +0 -60
  184. package/docs/apis/features/node/file-manager.md +0 -191
  185. package/docs/apis/features/node/fs.md +0 -1217
  186. package/docs/apis/features/node/git.md +0 -371
  187. package/docs/apis/features/node/google-auth.md +0 -193
  188. package/docs/apis/features/node/google-calendar.md +0 -202
  189. package/docs/apis/features/node/google-docs.md +0 -173
  190. package/docs/apis/features/node/google-drive.md +0 -246
  191. package/docs/apis/features/node/google-mail.md +0 -214
  192. package/docs/apis/features/node/google-sheets.md +0 -194
  193. package/docs/apis/features/node/grep.md +0 -292
  194. package/docs/apis/features/node/helpers.md +0 -164
  195. package/docs/apis/features/node/ink.md +0 -334
  196. package/docs/apis/features/node/ipc-socket.md +0 -249
  197. package/docs/apis/features/node/json-tree.md +0 -86
  198. package/docs/apis/features/node/networking.md +0 -316
  199. package/docs/apis/features/node/nlp.md +0 -133
  200. package/docs/apis/features/node/opener.md +0 -97
  201. package/docs/apis/features/node/os.md +0 -146
  202. package/docs/apis/features/node/package-finder.md +0 -392
  203. package/docs/apis/features/node/postgres.md +0 -234
  204. package/docs/apis/features/node/proc.md +0 -399
  205. package/docs/apis/features/node/process-manager.md +0 -305
  206. package/docs/apis/features/node/python.md +0 -604
  207. package/docs/apis/features/node/redis.md +0 -380
  208. package/docs/apis/features/node/repl.md +0 -88
  209. package/docs/apis/features/node/runpod.md +0 -674
  210. package/docs/apis/features/node/secure-shell.md +0 -176
  211. package/docs/apis/features/node/semantic-search.md +0 -408
  212. package/docs/apis/features/node/sqlite.md +0 -233
  213. package/docs/apis/features/node/telegram.md +0 -279
  214. package/docs/apis/features/node/transpiler.md +0 -74
  215. package/docs/apis/features/node/tts.md +0 -133
  216. package/docs/apis/features/node/ui.md +0 -701
  217. package/docs/apis/features/node/vault.md +0 -59
  218. package/docs/apis/features/node/vm.md +0 -75
  219. package/docs/apis/features/node/yaml-tree.md +0 -85
  220. package/docs/apis/features/node/yaml.md +0 -176
  221. package/docs/apis/features/web/asset-loader.md +0 -59
  222. package/docs/apis/features/web/container-link.md +0 -192
  223. package/docs/apis/features/web/esbuild.md +0 -54
  224. package/docs/apis/features/web/helpers.md +0 -164
  225. package/docs/apis/features/web/network.md +0 -44
  226. package/docs/apis/features/web/speech.md +0 -69
  227. package/docs/apis/features/web/vault.md +0 -59
  228. package/docs/apis/features/web/vm.md +0 -75
  229. package/docs/apis/features/web/voice.md +0 -84
  230. package/docs/apis/servers/express.md +0 -171
  231. package/docs/apis/servers/mcp.md +0 -238
  232. package/docs/apis/servers/websocket.md +0 -170
  233. package/docs/bootstrap/CLAUDE.md +0 -101
  234. package/docs/bootstrap/SKILL.md +0 -341
  235. package/docs/bootstrap/templates/about-command.ts +0 -41
  236. package/docs/bootstrap/templates/docs-models.ts +0 -22
  237. package/docs/bootstrap/templates/docs-readme.md +0 -43
  238. package/docs/bootstrap/templates/example-feature.ts +0 -53
  239. package/docs/bootstrap/templates/health-endpoint.ts +0 -15
  240. package/docs/bootstrap/templates/luca-cli.ts +0 -30
  241. package/docs/bootstrap/templates/runme.md +0 -54
  242. package/docs/challenges/caching-proxy.md +0 -16
  243. package/docs/challenges/content-db-round-trip.md +0 -14
  244. package/docs/challenges/custom-command.md +0 -9
  245. package/docs/challenges/file-watcher-pipeline.md +0 -11
  246. package/docs/challenges/grep-audit-report.md +0 -15
  247. package/docs/challenges/multi-feature-dashboard.md +0 -14
  248. package/docs/challenges/process-orchestrator.md +0 -17
  249. package/docs/challenges/rest-api-server-with-client.md +0 -12
  250. package/docs/challenges/script-runner-with-vm.md +0 -11
  251. package/docs/challenges/simple-rest-api.md +0 -15
  252. package/docs/challenges/websocket-serve-and-client.md +0 -11
  253. package/docs/challenges/yaml-config-system.md +0 -14
  254. package/docs/command-system-overhaul.md +0 -94
  255. package/docs/documentation-audit.md +0 -134
  256. package/docs/examples/assistant/CORE.md +0 -18
  257. package/docs/examples/assistant/hooks.ts +0 -3
  258. package/docs/examples/assistant/tools.ts +0 -10
  259. package/docs/examples/assistant-hooks-reference.ts +0 -171
  260. package/docs/examples/assistant-with-process-manager.md +0 -84
  261. package/docs/examples/content-db.md +0 -77
  262. package/docs/examples/disk-cache.md +0 -83
  263. package/docs/examples/docker.md +0 -101
  264. package/docs/examples/downloader.md +0 -70
  265. package/docs/examples/entity.md +0 -124
  266. package/docs/examples/esbuild.md +0 -80
  267. package/docs/examples/feature-as-tool-provider.md +0 -143
  268. package/docs/examples/file-manager.md +0 -82
  269. package/docs/examples/fs.md +0 -83
  270. package/docs/examples/git.md +0 -85
  271. package/docs/examples/google-auth.md +0 -88
  272. package/docs/examples/google-calendar.md +0 -94
  273. package/docs/examples/google-docs.md +0 -82
  274. package/docs/examples/google-drive.md +0 -96
  275. package/docs/examples/google-sheets.md +0 -95
  276. package/docs/examples/grep.md +0 -85
  277. package/docs/examples/ink-blocks.md +0 -75
  278. package/docs/examples/ink-renderer.md +0 -41
  279. package/docs/examples/ink.md +0 -103
  280. package/docs/examples/ipc-socket.md +0 -103
  281. package/docs/examples/json-tree.md +0 -91
  282. package/docs/examples/networking.md +0 -58
  283. package/docs/examples/nlp.md +0 -91
  284. package/docs/examples/opener.md +0 -78
  285. package/docs/examples/os.md +0 -72
  286. package/docs/examples/package-finder.md +0 -89
  287. package/docs/examples/postgres.md +0 -91
  288. package/docs/examples/proc.md +0 -81
  289. package/docs/examples/process-manager.md +0 -79
  290. package/docs/examples/python.md +0 -132
  291. package/docs/examples/repl.md +0 -93
  292. package/docs/examples/runpod.md +0 -119
  293. package/docs/examples/secure-shell.md +0 -92
  294. package/docs/examples/sqlite.md +0 -86
  295. package/docs/examples/structured-output-with-assistants.md +0 -144
  296. package/docs/examples/telegram.md +0 -77
  297. package/docs/examples/tts.md +0 -86
  298. package/docs/examples/ui.md +0 -80
  299. package/docs/examples/vault.md +0 -70
  300. package/docs/examples/vm.md +0 -86
  301. package/docs/examples/websocket-ask-and-reply-example.md +0 -128
  302. package/docs/examples/yaml-tree.md +0 -93
  303. package/docs/examples/yaml.md +0 -104
  304. package/docs/ideas/assistant-factory-pattern.md +0 -142
  305. package/docs/in-memory-fs.md +0 -4
  306. package/docs/introspection-audit.md +0 -49
  307. package/docs/introspection.md +0 -164
  308. package/docs/mcp/readme.md +0 -162
  309. package/docs/models.ts +0 -41
  310. package/docs/philosophy.md +0 -86
  311. package/docs/principles.md +0 -7
  312. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +0 -34
  313. package/docs/prompts/check-for-undocumented-features.md +0 -27
  314. package/docs/prompts/mcp-test-easy-command.md +0 -27
  315. package/docs/scaffolds/client.md +0 -149
  316. package/docs/scaffolds/command.md +0 -120
  317. package/docs/scaffolds/endpoint.md +0 -171
  318. package/docs/scaffolds/feature.md +0 -158
  319. package/docs/scaffolds/selector.md +0 -91
  320. package/docs/scaffolds/server.md +0 -196
  321. package/docs/selectors.md +0 -115
  322. package/docs/sessions/custom-command/attempt-log-2.md +0 -195
  323. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +0 -728
  324. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +0 -555
  325. package/docs/sessions/grep-audit-report/attempt-log-1.md +0 -289
  326. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +0 -679
  327. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +0 -1
  328. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +0 -920
  329. package/docs/sessions/simple-rest-api/attempt-log-1.md +0 -593
  330. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +0 -995
  331. package/docs/tutorials/00-bootstrap.md +0 -166
  332. package/docs/tutorials/01-getting-started.md +0 -106
  333. package/docs/tutorials/02-container.md +0 -210
  334. package/docs/tutorials/03-scripts.md +0 -194
  335. package/docs/tutorials/04-features-overview.md +0 -196
  336. package/docs/tutorials/05-state-and-events.md +0 -171
  337. package/docs/tutorials/06-servers.md +0 -157
  338. package/docs/tutorials/07-endpoints.md +0 -198
  339. package/docs/tutorials/08-commands.md +0 -252
  340. package/docs/tutorials/09-clients.md +0 -162
  341. package/docs/tutorials/10-creating-features.md +0 -203
  342. package/docs/tutorials/11-contentbase.md +0 -191
  343. package/docs/tutorials/12-assistants.md +0 -215
  344. package/docs/tutorials/13-introspection.md +0 -157
  345. package/docs/tutorials/14-type-system.md +0 -174
  346. package/docs/tutorials/15-project-patterns.md +0 -222
  347. package/docs/tutorials/16-google-features.md +0 -534
  348. package/docs/tutorials/17-tui-blocks.md +0 -530
  349. package/docs/tutorials/18-semantic-search.md +0 -334
  350. package/docs/tutorials/19-python-sessions.md +0 -401
  351. package/docs/tutorials/20-browser-esm.md +0 -234
  352. package/index.ts +0 -1
  353. package/src/agi/endpoints/ask.ts +0 -60
  354. package/src/agi/endpoints/conversations/[id].ts +0 -45
  355. package/src/agi/endpoints/conversations.ts +0 -31
  356. package/src/agi/endpoints/experts.ts +0 -37
  357. package/test/assistant-hooks.test.ts +0 -306
  358. package/test/assistant.test.ts +0 -81
  359. package/test/bus.test.ts +0 -134
  360. package/test/clients-servers.test.ts +0 -217
  361. package/test/command.test.ts +0 -267
  362. package/test/container-link.test.ts +0 -274
  363. package/test/conversation.test.ts +0 -220
  364. package/test/features.test.ts +0 -160
  365. package/test/fork-and-research.test.ts +0 -450
  366. package/test/integration.test.ts +0 -787
  367. package/test/interceptor-chain.test.ts +0 -61
  368. package/test/node-container.test.ts +0 -121
  369. package/test/python-session.test.ts +0 -105
  370. package/test/rate-limit.test.ts +0 -272
  371. package/test/semantic-search.test.ts +0 -550
  372. package/test/state.test.ts +0 -121
  373. package/test/vm-context.test.ts +0 -146
  374. package/test/vm-loadmodule.test.ts +0 -213
  375. package/test/websocket-ask.test.ts +0 -101
  376. package/test-integration/assistant.test.ts +0 -138
  377. package/test-integration/assistants-manager.test.ts +0 -113
  378. package/test-integration/claude-code.test.ts +0 -98
  379. package/test-integration/conversation-history.test.ts +0 -205
  380. package/test-integration/conversation.test.ts +0 -137
  381. package/test-integration/elevenlabs.test.ts +0 -55
  382. package/test-integration/google-services.test.ts +0 -80
  383. package/test-integration/helpers.ts +0 -89
  384. package/test-integration/memory.test.ts +0 -204
  385. package/test-integration/openai-codex.test.ts +0 -93
  386. package/test-integration/runpod.test.ts +0 -58
  387. package/test-integration/server-endpoints.test.ts +0 -97
  388. package/test-integration/telegram.test.ts +0 -46
@@ -135,9 +135,10 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
135
135
  }
136
136
 
137
137
  const pattern = path.join(srcPath, '**/*.ts');
138
- return await glob(pattern, {
138
+ const files = await glob(pattern, {
139
139
  ignore: ['**/*.d.ts', '**/node_modules/**']
140
140
  });
141
+ return files.sort();
141
142
  }
142
143
 
143
144
  private async analyzeFile(filePath: string): Promise<{ helpers: HelperIntrospection[], containers: Partial<ContainerIntrospection>[] }> {
@@ -1099,23 +1100,27 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
1099
1100
  }
1100
1101
  imports += ` } from '${importSource}';\n\n`;
1101
1102
 
1102
- const registrations = results.map(result => {
1103
+ // Sort by id/className for deterministic output across runs
1104
+ const sortedResults = [...results].sort((a, b) => a.id.localeCompare(b.id));
1105
+ const sortedContainers = [...containerResults].sort((a, b) => (a.className || '').localeCompare(b.className || ''));
1106
+
1107
+ const registrations = sortedResults.map(result => {
1103
1108
  const data = JSON.stringify(result, null, 2);
1104
1109
  return `setBuildTimeData('${result.id}', ${data});`;
1105
1110
  }).join('\n\n');
1106
1111
 
1107
1112
  let containerRegistrations = '';
1108
1113
  if (hasContainers) {
1109
- containerRegistrations = '\n\n// Container introspection data\n' + containerResults.map(result => {
1114
+ containerRegistrations = '\n\n// Container introspection data\n' + sortedContainers.map(result => {
1110
1115
  const data = JSON.stringify(result, null, 2);
1111
1116
  return `setContainerBuildTimeData('${result.className}', ${data});`;
1112
1117
  }).join('\n\n');
1113
1118
  }
1114
1119
 
1115
- const exportStatement = `\nexport const introspectionData = ${JSON.stringify(results, null, 2)};\n`;
1116
- const containerExport = hasContainers ? `\nexport const containerIntrospectionData = ${JSON.stringify(containerResults, null, 2)};\n` : '';
1120
+ const exportStatement = `\nexport const introspectionData = ${JSON.stringify(sortedResults, null, 2)};\n`;
1121
+ const containerExport = hasContainers ? `\nexport const containerIntrospectionData = ${JSON.stringify(sortedContainers, null, 2)};\n` : '';
1117
1122
 
1118
- return `${imports}// Auto-generated introspection registry data\n// Generated at: ${new Date().toISOString()}\n\n${registrations}${containerRegistrations}${exportStatement}${containerExport}`;
1123
+ return `${imports}// Auto-generated introspection registry data\n\n${registrations}${containerRegistrations}${exportStatement}${containerExport}`;
1119
1124
  }
1120
1125
  }
1121
1126
 
@@ -64,6 +64,8 @@ import "./features/container-link";
64
64
  import "./features/semantic-search";
65
65
  import "./features/dns";
66
66
  import "./features/redis";
67
+ import "./features/socket-repl";
68
+ import "./features/telnyx-assistant-connector";
67
69
 
68
70
  import type { ChildProcess } from "./features/proc";
69
71
  import type { DiskCache } from "./features/disk-cache";
@@ -108,6 +110,8 @@ import type { ContainerLink } from './features/container-link';
108
110
  import type { SemanticSearch } from './features/semantic-search';
109
111
  import type { Dns } from './features/dns';
110
112
  import type { Redis } from './features/redis';
113
+ import type { SocketRepl } from './features/socket-repl';
114
+ import type { TelnyxAssistantConnector } from './features/telnyx-assistant-connector';
111
115
  export { State };
112
116
 
113
117
  export {
@@ -147,6 +151,8 @@ export {
147
151
  type SemanticSearch,
148
152
  type Dns,
149
153
  type Redis,
154
+ type SocketRepl,
155
+ type TelnyxAssistantConnector,
150
156
  type Transpiler,
151
157
  };
152
158
 
@@ -213,6 +219,8 @@ export interface NodeFeatures extends AvailableFeatures {
213
219
  semanticSearch: typeof SemanticSearch;
214
220
  dns: typeof Dns;
215
221
  redis: typeof Redis;
222
+ socketRepl: typeof SocketRepl;
223
+ telnyxAssistantConnector: typeof TelnyxAssistantConnector;
216
224
  }
217
225
 
218
226
  export type ClientsAndServersInterface = ClientsInterface & ServersInterface & CommandsInterface & EndpointsInterface & SelectorsInterface;
@@ -240,7 +248,7 @@ export interface NodeContainerState extends ContainerState {
240
248
  *
241
249
  * @example
242
250
  * ```ts
243
- * import container from '@soederpop/luca/node'
251
+ * import container from 'luca/node'
244
252
  *
245
253
  * // File operations
246
254
  * const content = container.fs.readFile('README.md')
@@ -1,6 +1,6 @@
1
1
  import { Feature } from '../feature.js'
2
2
  import * as contentbaseExports from 'contentbase'
3
- import { parse, Collection, extractSections, type ModelDefinition } from 'contentbase'
3
+ import { parse, Collection, Document, extractSections, type ModelDefinition } from 'contentbase'
4
4
  import { z } from 'zod'
5
5
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
6
6
  import { realpathSync } from 'node:fs'
@@ -184,7 +184,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
184
184
 
185
185
  const vm = this.container.feature('vm') as any
186
186
 
187
- // Seed luca modules first (helpers does this for @soederpop/luca)
187
+ // Seed luca modules first (helpers does this for luca)
188
188
  const helpers = this.container.feature('helpers') as any
189
189
  if (helpers?.seedVirtualModules) {
190
190
  helpers.seedVirtualModules()
@@ -300,6 +300,43 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
300
300
  return parse(path)
301
301
  }
302
302
 
303
+ /**
304
+ * Get a document object by collection ID, file path, or inline markdown string.
305
+ * Exactly one of `id`, `path`, or `content` must be provided.
306
+ *
307
+ * @param options.id - Collection document ID (e.g. `'guides/intro'`); auto-loads the collection if needed
308
+ * @param options.path - Absolute or relative path to a markdown file on disk
309
+ * @param options.content - Raw markdown string; returned as an in-memory Document
310
+ * @returns The Document instance
311
+ * @example
312
+ * ```typescript
313
+ * // By collection document ID
314
+ * const doc = await contentDb.document({ id: 'guides/intro' })
315
+ *
316
+ * // By file path
317
+ * const doc = await contentDb.document({ path: '/absolute/path/to/file.md' })
318
+ *
319
+ * // In-memory from a markdown string
320
+ * const doc = contentDb.document({ content: '# Hello\n\nworld' })
321
+ * ```
322
+ */
323
+ async document(options: { id: string; path?: never; content?: never } | { path: string; id?: never; content?: never } | { content: string; id?: never; path?: never }): Promise<Document> {
324
+ if ('content' in options && options.content != null) {
325
+ return new Document({ content: options.content, path: '/virtual/document.md' })
326
+ }
327
+
328
+ if ('path' in options && options.path != null) {
329
+ return parse(options.path) as unknown as Document
330
+ }
331
+
332
+ if ('id' in options && options.id != null) {
333
+ if (!this.isLoaded) await this.load()
334
+ return this.collection.document(options.id) as unknown as Document
335
+ }
336
+
337
+ throw new Error('document() requires exactly one of: id, path, or content')
338
+ }
339
+
303
340
  /**
304
341
  * Load the collection, discovering models from models.ts and parsing all documents.
305
342
  *
@@ -0,0 +1,57 @@
1
+ import { inspect } from 'util'
2
+
3
+ const BUILTIN_TYPES = new Set(['Object', 'Array', 'Map', 'Set', 'Date', 'RegExp', 'Promise', 'Error', 'Number', 'String', 'Boolean'])
4
+
5
+ export function displayResult(value: any) {
6
+ if (typeof value !== 'object' || value === null) {
7
+ console.log(value)
8
+ return
9
+ }
10
+
11
+ const hasCustomInspect = typeof value[Symbol.for('nodejs.util.inspect.custom')] === 'function'
12
+ const ctorName = value.constructor?.name
13
+ const isClassInstance = ctorName && !BUILTIN_TYPES.has(ctorName)
14
+
15
+ // Objects with custom inspect or builtins: use standard inspect
16
+ if (hasCustomInspect || !isClassInstance) {
17
+ console.log(inspect(value, { colors: true, depth: 4 }))
18
+ return
19
+ }
20
+
21
+ // Class instances: show clean data (no _ props, no functions)
22
+ const data: Record<string, any> = {}
23
+ for (const [k, v] of Object.entries(value)) {
24
+ if (k.startsWith('_') || typeof v === 'function') continue
25
+ data[k] = v
26
+ }
27
+ const body = inspect(data, { colors: true, depth: 3 })
28
+ console.log(`${ctorName} ${body}`)
29
+
30
+ // Collect methods and getters from own + prototype chain
31
+ const methods: string[] = []
32
+ const getters: string[] = []
33
+
34
+ for (const [k, v] of Object.entries(value)) {
35
+ if (k.startsWith('_')) continue
36
+ if (typeof v === 'function') methods.push(k)
37
+ }
38
+
39
+ let proto = Object.getPrototypeOf(value)
40
+ while (proto && proto !== Object.prototype) {
41
+ for (const k of Object.getOwnPropertyNames(proto)) {
42
+ if (k === 'constructor' || k.startsWith('_')) continue
43
+ const desc = Object.getOwnPropertyDescriptor(proto, k)
44
+ if (!desc) continue
45
+ if (desc.get && !getters.includes(k)) getters.push(k)
46
+ else if (typeof desc.value === 'function' && !methods.includes(k)) methods.push(k)
47
+ }
48
+ proto = Object.getPrototypeOf(proto)
49
+ }
50
+
51
+ if (getters.length || methods.length) {
52
+ const parts: string[] = []
53
+ if (getters.length) parts.push(` \x1b[36mgetters:\x1b[0m ${getters.sort().join(', ')}`)
54
+ if (methods.length) parts.push(` \x1b[36mmethods:\x1b[0m ${methods.sort().map(m => m + '()').join(', ')}`)
55
+ console.log(parts.join('\n'))
56
+ }
57
+ }
@@ -3,6 +3,8 @@ import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '.
3
3
  import { Feature } from '../feature.js'
4
4
  import { Feature as UniversalFeature } from '../../feature.js'
5
5
  import { Client, clients } from '../../client.js'
6
+ import { allHelperInstances } from '../../container.js'
7
+ import type { Helper } from '../../helper.js'
6
8
  import { RestClient } from '../../clients/rest.js'
7
9
  import { GraphClient } from '../../clients/graph.js'
8
10
  import { WebSocketClient } from '../../clients/websocket.js'
@@ -108,14 +110,14 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
108
110
 
109
111
  /**
110
112
  * Whether to use native `import()` for loading project helpers.
111
- * True only if `@soederpop/luca` is actually resolvable in `node_modules`.
113
+ * True only if `luca` is actually resolvable in `node_modules`.
112
114
  * Warns when `node_modules` exists but the package is missing.
113
115
  */
114
116
  get useNativeImport(): boolean {
115
117
  const hasNodeModules = existsSync(resolve(this.rootDir, 'node_modules'))
116
118
  const hasLuca = hasNodeModules && existsSync(resolve(this.rootDir, 'node_modules', '@soederpop', 'luca'))
117
119
 
118
- // VM bundling handles missing @soederpop/luca gracefully — no warning needed
120
+ // VM bundling handles missing luca gracefully — no warning needed
119
121
 
120
122
  return hasLuca
121
123
  }
@@ -126,7 +128,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
126
128
 
127
129
  /**
128
130
  * Seeds the VM feature with virtual modules so that project-level files
129
- * can `import` / `require('@soederpop/luca')`, `zod`, etc. without
131
+ * can `import` / `require('luca')`, `zod`, etc. without
130
132
  * needing them in `node_modules`.
131
133
  *
132
134
  * Called automatically when `useNativeImport` is false.
@@ -138,7 +140,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
138
140
 
139
141
  const vm = this.container.feature('vm') as unknown as VM
140
142
 
141
- // Provide the full @soederpop/luca barrel — everything node.ts exports
143
+ // Provide the full luca barrel — everything node.ts exports
142
144
  // We build the exports object from the already-loaded modules in memory
143
145
  const lucaExports: Record<string, any> = {
144
146
  // Core classes
@@ -208,11 +210,23 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
208
210
  schemasModule.FeatureEventsSchema = FeatureEventsSchema
209
211
  }
210
212
 
213
+ vm.defineModule('luca', lucaExports)
214
+ vm.defineModule('luca/schemas', schemasModule)
215
+ vm.defineModule('luca/node', lucaExports)
216
+
217
+ // Deep import paths AIs and developers might reach for
218
+ vm.defineModule('luca/client', { Client, ClientsRegistry: clients.constructor, default: Client })
219
+ vm.defineModule('luca/server', { Server, ServersRegistry: servers.constructor, default: Server })
220
+ vm.defineModule('luca/clients/rest', { RestClient, default: RestClient })
221
+ vm.defineModule('luca/clients/graph', { GraphClient, default: GraphClient })
222
+ vm.defineModule('luca/clients/websocket', { WebSocketClient, default: WebSocketClient })
223
+ vm.defineModule('luca/servers/express', { ExpressServer, default: ExpressServer })
224
+ vm.defineModule('luca/servers/socket', { WebsocketServer, default: WebsocketServer })
225
+
226
+ // Legacy package name aliases for backwards compatibility
211
227
  vm.defineModule('@soederpop/luca', lucaExports)
212
228
  vm.defineModule('@soederpop/luca/schemas', schemasModule)
213
229
  vm.defineModule('@soederpop/luca/node', lucaExports)
214
-
215
- // Deep import paths AIs and developers might reach for
216
230
  vm.defineModule('@soederpop/luca/client', { Client, ClientsRegistry: clients.constructor, default: Client })
217
231
  vm.defineModule('@soederpop/luca/server', { Server, ServersRegistry: servers.constructor, default: Server })
218
232
  vm.defineModule('@soederpop/luca/clients/rest', { RestClient, default: RestClient })
@@ -224,6 +238,26 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
224
238
  vm.defineModule('zod', { z, default: { z } })
225
239
  }
226
240
 
241
+ /**
242
+ * Returns all instantiated helper instances across all types, optionally filtered by class.
243
+ *
244
+ * @param FilterClass - When provided, only instances of this class are returned.
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * // All instances of any type
249
+ * container.helpers.getInstances()
250
+ *
251
+ * // All Assistant instances
252
+ * const assistants = container.helpers.getInstances(Assistant)
253
+ * ```
254
+ */
255
+ getInstances(): Helper[]
256
+ getInstances<T extends Helper>(FilterClass: new (...args: any[]) => T): T[]
257
+ getInstances<T extends Helper>(FilterClass?: new (...args: any[]) => T): Helper[] | T[] {
258
+ return FilterClass ? allHelperInstances(FilterClass) : allHelperInstances()
259
+ }
260
+
227
261
  /**
228
262
  * Returns a unified view of all available helpers across all registries.
229
263
  * Each key is a registry type, each value is the list of helper names in that registry.
@@ -458,7 +492,11 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
458
492
  try {
459
493
  const fm = await this.ensureFileManager()
460
494
  const absPatterns = [`${dir}/*.ts`, `${dir}/**/*.ts`]
461
- const relPatterns = [`${type}/*.ts`, `${type}/**/*.ts`]
495
+ // Only use relative patterns when rootDir matches the container cwd,
496
+ // otherwise the fileManager (rooted in cwd) returns files from the
497
+ // wrong project which then get resolved against this.rootDir.
498
+ const useRelative = this.rootDir === this.container.cwd
499
+ const relPatterns = useRelative ? [`${type}/*.ts`, `${type}/**/*.ts`] : []
462
500
  const matched = fm.match([...absPatterns, ...relPatterns])
463
501
  files = matched.map((f: string) => f.startsWith('/') ? f : resolve(this.rootDir, f))
464
502
  } catch {}
@@ -608,6 +646,7 @@ export class Helpers extends Feature<HelpersState, HelpersOptions> {
608
646
  const Grafted = graftModule(Command as any, {
609
647
  description: commandModule.description,
610
648
  argsSchema: commandModule.argsSchema,
649
+ positionals: commandModule.positionals ?? mod.positionals,
611
650
  handler: commandModule.handler,
612
651
  }, name, 'commands')
613
652
  commands.register(name, Grafted as any)
@@ -1,8 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
3
  import { Feature } from "../feature.js";
4
- import { existsSync } from 'fs';
5
- import { join, resolve } from 'path';
6
4
  import { tmpdir } from 'os';
7
5
  import { bridgeScript } from '../../python/generated.js';
8
6
  import type { ChildProcess } from 'child_process';
@@ -161,7 +159,7 @@ export class Python<
161
159
 
162
160
  // Setup project directory
163
161
  if (this.options.dir) {
164
- this.state.set('projectDir', resolve(this.options.dir))
162
+ this.state.set('projectDir', this.container.paths.resolve(this.options.dir))
165
163
  } else {
166
164
  this.state.set('projectDir', this.container.cwd)
167
165
  }
@@ -170,7 +168,7 @@ export class Python<
170
168
  await this.detectEnvironment()
171
169
 
172
170
  // Execute context script if provided
173
- if (this.options.contextScript && existsSync(this.options.contextScript)) {
171
+ if (this.options.contextScript && this.container.feature('fs').exists(this.options.contextScript)) {
174
172
  await this.execute(`exec(open('${this.options.contextScript}').read())`)
175
173
  }
176
174
 
@@ -217,6 +215,8 @@ export class Python<
217
215
  let environmentType: PythonState['environmentType'] = null
218
216
 
219
217
  const proc = this.container.feature('proc')
218
+ const fs = this.container.feature('fs')
219
+ const paths = this.container.paths
220
220
 
221
221
  /** Resolve a binary to its full path via `which`, falling back to the bare name. */
222
222
  const resolveBin = (name: string): string => {
@@ -228,12 +228,14 @@ export class Python<
228
228
  pythonPath = this.options.pythonPath
229
229
  environmentType = 'system'
230
230
  }
231
- // Check for uv
232
- else if (existsSync(join(projectDir, 'uv.lock')) || existsSync(join(projectDir, 'pyproject.toml'))) {
231
+
232
+ // Check for uv independent so a missing uv binary falls through to conda/venv
233
+ if (!pythonPath && (fs.exists(paths.resolve(projectDir, 'uv.lock')) || fs.exists(paths.resolve(projectDir, 'pyproject.toml')))) {
233
234
  try {
234
235
  const uvBin = resolveBin('uv')
235
236
  const result = await proc.execAndCapture(`${uvBin} run python --version`)
236
- if (result.exitCode === 0) {
237
+ // execAndCapture returns exitCode 0 on ENOENT — check result.error to confirm the binary actually ran
238
+ if (result.exitCode === 0 && !result.error) {
237
239
  pythonPath = `${uvBin} run python`
238
240
  environmentType = 'uv'
239
241
  }
@@ -241,12 +243,13 @@ export class Python<
241
243
  // Fall through to next detection method
242
244
  }
243
245
  }
246
+
244
247
  // Check for conda
245
- else if (existsSync(join(projectDir, 'environment.yml')) || existsSync(join(projectDir, 'conda.yml'))) {
248
+ if (!pythonPath && (fs.exists(paths.resolve(projectDir, 'environment.yml')) || fs.exists(paths.resolve(projectDir, 'conda.yml')))) {
246
249
  try {
247
250
  const condaBin = resolveBin('conda')
248
251
  const result = await proc.execAndCapture(`${condaBin} run python --version`)
249
- if (result.exitCode === 0) {
252
+ if (result.exitCode === 0 && !result.error) {
250
253
  pythonPath = `${condaBin} run python`
251
254
  environmentType = 'conda'
252
255
  }
@@ -254,14 +257,15 @@ export class Python<
254
257
  // Fall through to next detection method
255
258
  }
256
259
  }
260
+
257
261
  // Check for venv
258
- else if (existsSync(join(projectDir, 'venv')) || existsSync(join(projectDir, '.venv'))) {
259
- const venvPath = existsSync(join(projectDir, 'venv')) ? 'venv' : '.venv'
262
+ if (!pythonPath && (fs.exists(paths.resolve(projectDir, 'venv')) || fs.exists(paths.resolve(projectDir, '.venv')))) {
263
+ const venvPath = fs.exists(paths.resolve(projectDir, 'venv')) ? 'venv' : '.venv'
260
264
  const venvPython = process.platform === 'win32'
261
- ? join(projectDir, venvPath, 'Scripts', 'python.exe')
262
- : join(projectDir, venvPath, 'bin', 'python')
265
+ ? paths.resolve(projectDir, venvPath, 'Scripts', 'python.exe')
266
+ : paths.resolve(projectDir, venvPath, 'bin', 'python')
263
267
 
264
- if (existsSync(venvPython)) {
268
+ if (fs.exists(venvPython)) {
265
269
  pythonPath = venvPython
266
270
  environmentType = 'venv'
267
271
  }
@@ -317,6 +321,8 @@ export class Python<
317
321
  */
318
322
  async installDependencies(): Promise<{ stdout: string; stderr: string; exitCode: number }> {
319
323
  const proc = this.container.feature('proc')
324
+ const fs = this.container.feature('fs')
325
+ const paths = this.container.paths
320
326
  const projectDir = this.state.get('projectDir')!
321
327
  const environmentType = this.state.get('environmentType')
322
328
 
@@ -330,9 +336,9 @@ export class Python<
330
336
  installCommand = 'uv sync'
331
337
  break
332
338
  case 'conda':
333
- if (existsSync(join(projectDir, 'environment.yml'))) {
339
+ if (fs.exists(paths.resolve(projectDir, 'environment.yml'))) {
334
340
  installCommand = 'conda env update -f environment.yml'
335
- } else if (existsSync(join(projectDir, 'conda.yml'))) {
341
+ } else if (fs.exists(paths.resolve(projectDir, 'conda.yml'))) {
336
342
  installCommand = 'conda env update -f conda.yml'
337
343
  } else {
338
344
  installCommand = 'conda install --file requirements.txt'
@@ -341,10 +347,10 @@ export class Python<
341
347
  case 'venv':
342
348
  case 'system':
343
349
  default:
344
- if (existsSync(join(projectDir, 'requirements.txt'))) {
350
+ if (fs.exists(paths.resolve(projectDir, 'requirements.txt'))) {
345
351
  const pythonPath = this.state.get('pythonPath')!
346
352
  installCommand = `${pythonPath} -m pip install -r requirements.txt`
347
- } else if (existsSync(join(projectDir, 'pyproject.toml'))) {
353
+ } else if (fs.exists(paths.resolve(projectDir, 'pyproject.toml'))) {
348
354
  const pythonPath = this.state.get('pythonPath')!
349
355
  installCommand = `${pythonPath} -m pip install -e .`
350
356
  } else {
@@ -406,7 +412,7 @@ export class Python<
406
412
  // Create temporary script in system temp dir (not inside the project)
407
413
  const tempDir = `${tmpdir()}/luca-python-temp`
408
414
  await fs.ensureFolder(tempDir)
409
- const scriptPath = join(tempDir, `script-${Date.now()}.py`)
415
+ const scriptPath = this.container.paths.resolve(tempDir, `script-${Date.now()}.py`)
410
416
 
411
417
  // Build the Python script
412
418
  let script = ''
@@ -3,7 +3,7 @@ import { FeatureStateSchema, FeatureOptionsSchema } from '../../schemas/base.js'
3
3
  import { Feature } from "../feature.js";
4
4
  import vm from 'vm'
5
5
  import readline from 'readline'
6
- import { displayResult } from '../../commands/eval.js'
6
+ import { displayResult } from './display-result.js'
7
7
 
8
8
  export const ReplStateSchema = FeatureStateSchema.extend({
9
9
  started: z.boolean().optional().describe('Whether the REPL server has been started'),
@@ -109,8 +109,8 @@ export class SecureShell extends Feature<SecureShellState, SecureShellOptions> {
109
109
  sshCmd += ` -i "${key}"`
110
110
  }
111
111
 
112
- // Disable host key checking for automation (optional - you may want to remove this for security)
113
- sshCmd += ` -o StrictHostKeyChecking=no`
112
+ // Batch mode fails immediately instead of hanging on interactive prompts
113
+ sshCmd += ` -o BatchMode=yes -o StrictHostKeyChecking=no`
114
114
 
115
115
  sshCmd += ` ${username}@${host}`
116
116
 
@@ -129,8 +129,8 @@ export class SecureShell extends Feature<SecureShellState, SecureShellOptions> {
129
129
  scpCmd += ` -i "${key}"`
130
130
  }
131
131
 
132
- // Disable host key checking for automation
133
- scpCmd += ` -o StrictHostKeyChecking=no`
132
+ // Batch mode fails immediately instead of hanging on interactive prompts
133
+ scpCmd += ` -o BatchMode=yes -o StrictHostKeyChecking=no`
134
134
 
135
135
  return scpCmd
136
136
  }
@@ -150,18 +150,12 @@ export class SecureShell extends Feature<SecureShellState, SecureShellOptions> {
150
150
  * ```
151
151
  */
152
152
  async testConnection(): Promise<boolean> {
153
- try {
154
- const testCmd = `${this.buildSSHConnectionString()} "echo 'connection_test'"`
155
- const result = await this.proc.execAndCapture(testCmd)
156
-
157
- if (result.exitCode === 0 && result.stdout.trim() === 'connection_test') {
158
- this.setState({ connected: true })
159
- return true
160
- } else {
161
- this.setState({ connected: false })
162
- return false
163
- }
164
- } catch (error) {
153
+ const result = await this.exec(`echo 'connected'`).catch(e => '')
154
+
155
+ if (String(result).trim() === 'connected') {
156
+ this.setState({ connected: true })
157
+ return true
158
+ } else {
165
159
  this.setState({ connected: false })
166
160
  return false
167
161
  }
@@ -264,4 +258,4 @@ export class SecureShell extends Feature<SecureShellState, SecureShellOptions> {
264
258
  }
265
259
  }
266
260
 
267
- export default SecureShell
261
+ export default SecureShell
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod'
2
2
  import { FeatureStateSchema, FeatureOptionsSchema, FeatureEventsSchema } from '../../schemas/base.js'
3
- import { type AvailableFeatures } from '@soederpop/luca/feature'
3
+ import { type AvailableFeatures } from 'luca/feature'
4
4
  import { Feature } from '../feature.js'
5
5
  import { Database } from 'bun:sqlite'
6
6
  import { createHash } from 'node:crypto'
@@ -8,7 +8,7 @@ import { mkdirSync, existsSync, statSync } from 'node:fs'
8
8
  import { dirname, join } from 'node:path'
9
9
  import { homedir } from 'node:os'
10
10
 
11
- declare module '@soederpop/luca/feature' {
11
+ declare module 'luca/feature' {
12
12
  interface AvailableFeatures {
13
13
  semanticSearch: typeof SemanticSearch
14
14
  }