luca 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) 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 +220 -322
  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/try-all-challenges.ts +3 -3
  18. package/commands/try-challenge.ts +3 -3
  19. package/dist/agi/container.server.d.ts +2 -2
  20. package/dist/agi/features/assistant.d.ts +2 -2
  21. package/dist/agi/features/assistants-manager.d.ts +1 -1
  22. package/dist/agi/features/autonomous-assistant.d.ts +1 -1
  23. package/dist/agi/features/browser-use.d.ts +1 -1
  24. package/dist/agi/features/claude-code.d.ts +1 -1
  25. package/dist/agi/features/conversation-history.d.ts +2 -2
  26. package/dist/agi/features/conversation.d.ts +1 -1
  27. package/dist/agi/features/docs-reader.d.ts +1 -1
  28. package/dist/agi/features/file-tools.d.ts +1 -1
  29. package/dist/agi/features/luca-coder.d.ts +1 -1
  30. package/dist/agi/features/openai-codex.d.ts +1 -1
  31. package/dist/agi/features/skills-library.d.ts +1 -1
  32. package/dist/clients/civitai/index.d.ts +4 -4
  33. package/dist/clients/client-template.d.ts +4 -4
  34. package/dist/clients/comfyui/index.d.ts +2 -2
  35. package/dist/clients/elevenlabs/index.d.ts +2 -2
  36. package/dist/clients/openai/index.d.ts +2 -2
  37. package/dist/clients/supabase/index.d.ts +3 -3
  38. package/dist/command.d.ts +1 -1
  39. package/dist/node/container.d.ts +1 -1
  40. package/dist/node/features/helpers.d.ts +3 -3
  41. package/dist/node/features/semantic-search.d.ts +1 -1
  42. package/dist/node/features/vm.d.ts +3 -3
  43. package/dist/node.d.ts +1 -1
  44. package/dist/scaffolds/generated.d.ts +1 -1
  45. package/dist/selector.d.ts +1 -1
  46. package/index.html +217 -190
  47. package/luca.console.ts +1 -1
  48. package/package.json +2 -2
  49. package/public/index.html +217 -190
  50. package/public/slides-ai-native.html +1 -1
  51. package/public/slides-intro.html +2 -2
  52. package/scripts/examples/ask-luca-expert.ts +1 -1
  53. package/scripts/examples/assistant-questions.ts +1 -1
  54. package/scripts/examples/excalidraw-expert.ts +1 -1
  55. package/scripts/examples/file-manager.ts +1 -1
  56. package/scripts/examples/ideas.ts +1 -1
  57. package/scripts/examples/interactive-chat.ts +1 -1
  58. package/scripts/examples/opening-a-web-browser.ts +1 -1
  59. package/scripts/examples/telegram-bot.ts +1 -1
  60. package/scripts/examples/using-assistant-with-mcp.ts +1 -1
  61. package/scripts/examples/using-claude-code.ts +1 -1
  62. package/scripts/examples/using-contentdb.ts +2 -2
  63. package/scripts/examples/using-conversations.ts +1 -1
  64. package/scripts/examples/using-disk-cache.ts +1 -1
  65. package/scripts/examples/using-docker-shell.ts +1 -1
  66. package/scripts/examples/using-elevenlabs.ts +1 -1
  67. package/scripts/examples/using-google-calendar.ts +1 -1
  68. package/scripts/examples/using-google-docs.ts +1 -1
  69. package/scripts/examples/using-google-drive.ts +1 -1
  70. package/scripts/examples/using-google-sheets.ts +1 -1
  71. package/scripts/examples/using-nlp.ts +1 -1
  72. package/scripts/examples/using-ollama.ts +1 -1
  73. package/scripts/examples/using-postgres.ts +1 -1
  74. package/scripts/examples/using-runpod.ts +1 -1
  75. package/scripts/examples/using-tts.ts +1 -1
  76. package/scripts/scaffold.ts +5 -5
  77. package/scripts/scratch.ts +1 -1
  78. package/scripts/test-assistant-hooks.ts +1 -1
  79. package/scripts/test-docs-reader.ts +1 -1
  80. package/src/agi/container.server.ts +6 -2
  81. package/src/agi/features/agent-memory.ts +25 -25
  82. package/src/agi/features/assistant.ts +34 -5
  83. package/src/agi/features/assistants-manager.ts +122 -6
  84. package/src/agi/features/autonomous-assistant.ts +1 -1
  85. package/src/agi/features/browser-use.ts +20 -1
  86. package/src/agi/features/claude-code.ts +51 -5
  87. package/src/agi/features/coding-tools.ts +1 -1
  88. package/src/agi/features/conversation-history.ts +181 -4
  89. package/src/agi/features/conversation.ts +186 -15
  90. package/src/agi/features/docs-reader.ts +2 -2
  91. package/src/agi/features/file-tools.ts +49 -2
  92. package/src/agi/features/luca-coder.ts +7 -5
  93. package/src/agi/features/mcp-bridge.ts +532 -0
  94. package/src/agi/features/openai-codex.ts +2 -2
  95. package/src/agi/features/skills-library.ts +131 -52
  96. package/src/agi/lib/token-counter.ts +80 -0
  97. package/src/bootstrap/generated.ts +56 -57
  98. package/src/browser.ts +1 -1
  99. package/src/cli/build-info.ts +2 -2
  100. package/src/cli/cli.ts +2 -2
  101. package/src/clients/civitai/index.ts +5 -5
  102. package/src/clients/client-template.ts +4 -4
  103. package/src/clients/comfyui/index.ts +4 -4
  104. package/src/clients/elevenlabs/index.ts +4 -4
  105. package/src/clients/openai/index.ts +7 -7
  106. package/src/clients/supabase/index.ts +4 -4
  107. package/src/clients/voicebox/index.ts +4 -4
  108. package/src/command.ts +2 -1
  109. package/src/commands/chat.ts +1 -0
  110. package/src/commands/eval.ts +2 -56
  111. package/src/commands/introspect.ts +1 -1
  112. package/src/commands/prompt.ts +41 -9
  113. package/src/container-describer.ts +8 -1
  114. package/src/container.ts +13 -0
  115. package/src/entity.ts +2 -2
  116. package/src/helper.ts +1 -1
  117. package/src/introspection/generated.agi.ts +28563 -27571
  118. package/src/introspection/generated.node.ts +20281 -20194
  119. package/src/introspection/generated.web.ts +605 -584
  120. package/src/introspection/scan.ts +11 -6
  121. package/src/node/container.ts +1 -1
  122. package/src/node/features/content-db.ts +39 -2
  123. package/src/node/features/display-result.ts +57 -0
  124. package/src/node/features/helpers.ts +42 -15
  125. package/src/node/features/python.ts +25 -19
  126. package/src/node/features/repl.ts +1 -1
  127. package/src/node/features/secure-shell.ts +11 -17
  128. package/src/node/features/semantic-search.ts +2 -2
  129. package/src/node/features/transpiler.ts +2 -3
  130. package/src/node/features/ui.ts +5 -0
  131. package/src/node/features/vm.ts +3 -3
  132. package/src/node.ts +3 -3
  133. package/src/python/generated.ts +0 -1
  134. package/src/scaffolds/generated.ts +82 -83
  135. package/src/selector.ts +1 -1
  136. package/src/servers/express.ts +1 -1
  137. package/src/web/features/helpers.ts +22 -0
  138. package/tsconfig.json +12 -12
  139. package/docs/CLI.md +0 -335
  140. package/docs/CNAME +0 -1
  141. package/docs/README.md +0 -60
  142. package/docs/TABLE-OF-CONTENTS.md +0 -183
  143. package/docs/apis/clients/elevenlabs.md +0 -308
  144. package/docs/apis/clients/graph.md +0 -107
  145. package/docs/apis/clients/openai.md +0 -429
  146. package/docs/apis/clients/rest.md +0 -161
  147. package/docs/apis/clients/websocket.md +0 -174
  148. package/docs/apis/features/agi/assistant.md +0 -625
  149. package/docs/apis/features/agi/assistants-manager.md +0 -282
  150. package/docs/apis/features/agi/auto-assistant.md +0 -279
  151. package/docs/apis/features/agi/browser-use.md +0 -802
  152. package/docs/apis/features/agi/claude-code.md +0 -884
  153. package/docs/apis/features/agi/conversation-history.md +0 -364
  154. package/docs/apis/features/agi/conversation.md +0 -548
  155. package/docs/apis/features/agi/docs-reader.md +0 -99
  156. package/docs/apis/features/agi/file-tools.md +0 -163
  157. package/docs/apis/features/agi/luca-coder.md +0 -407
  158. package/docs/apis/features/agi/openai-codex.md +0 -396
  159. package/docs/apis/features/agi/openapi.md +0 -138
  160. package/docs/apis/features/agi/semantic-search.md +0 -387
  161. package/docs/apis/features/agi/skills-library.md +0 -239
  162. package/docs/apis/features/node/container-link.md +0 -192
  163. package/docs/apis/features/node/content-db.md +0 -450
  164. package/docs/apis/features/node/disk-cache.md +0 -379
  165. package/docs/apis/features/node/dns.md +0 -652
  166. package/docs/apis/features/node/docker.md +0 -706
  167. package/docs/apis/features/node/downloader.md +0 -81
  168. package/docs/apis/features/node/esbuild.md +0 -60
  169. package/docs/apis/features/node/file-manager.md +0 -191
  170. package/docs/apis/features/node/fs.md +0 -1217
  171. package/docs/apis/features/node/git.md +0 -371
  172. package/docs/apis/features/node/google-auth.md +0 -193
  173. package/docs/apis/features/node/google-calendar.md +0 -202
  174. package/docs/apis/features/node/google-docs.md +0 -173
  175. package/docs/apis/features/node/google-drive.md +0 -246
  176. package/docs/apis/features/node/google-mail.md +0 -214
  177. package/docs/apis/features/node/google-sheets.md +0 -194
  178. package/docs/apis/features/node/grep.md +0 -292
  179. package/docs/apis/features/node/helpers.md +0 -164
  180. package/docs/apis/features/node/ink.md +0 -334
  181. package/docs/apis/features/node/ipc-socket.md +0 -249
  182. package/docs/apis/features/node/json-tree.md +0 -86
  183. package/docs/apis/features/node/networking.md +0 -316
  184. package/docs/apis/features/node/nlp.md +0 -133
  185. package/docs/apis/features/node/opener.md +0 -97
  186. package/docs/apis/features/node/os.md +0 -146
  187. package/docs/apis/features/node/package-finder.md +0 -392
  188. package/docs/apis/features/node/postgres.md +0 -234
  189. package/docs/apis/features/node/proc.md +0 -399
  190. package/docs/apis/features/node/process-manager.md +0 -305
  191. package/docs/apis/features/node/python.md +0 -604
  192. package/docs/apis/features/node/redis.md +0 -380
  193. package/docs/apis/features/node/repl.md +0 -88
  194. package/docs/apis/features/node/runpod.md +0 -674
  195. package/docs/apis/features/node/secure-shell.md +0 -176
  196. package/docs/apis/features/node/semantic-search.md +0 -408
  197. package/docs/apis/features/node/sqlite.md +0 -233
  198. package/docs/apis/features/node/telegram.md +0 -279
  199. package/docs/apis/features/node/transpiler.md +0 -74
  200. package/docs/apis/features/node/tts.md +0 -133
  201. package/docs/apis/features/node/ui.md +0 -701
  202. package/docs/apis/features/node/vault.md +0 -59
  203. package/docs/apis/features/node/vm.md +0 -75
  204. package/docs/apis/features/node/yaml-tree.md +0 -85
  205. package/docs/apis/features/node/yaml.md +0 -176
  206. package/docs/apis/features/web/asset-loader.md +0 -59
  207. package/docs/apis/features/web/container-link.md +0 -192
  208. package/docs/apis/features/web/esbuild.md +0 -54
  209. package/docs/apis/features/web/helpers.md +0 -164
  210. package/docs/apis/features/web/network.md +0 -44
  211. package/docs/apis/features/web/speech.md +0 -69
  212. package/docs/apis/features/web/vault.md +0 -59
  213. package/docs/apis/features/web/vm.md +0 -75
  214. package/docs/apis/features/web/voice.md +0 -84
  215. package/docs/apis/servers/express.md +0 -171
  216. package/docs/apis/servers/mcp.md +0 -238
  217. package/docs/apis/servers/websocket.md +0 -170
  218. package/docs/bootstrap/CLAUDE.md +0 -101
  219. package/docs/bootstrap/SKILL.md +0 -341
  220. package/docs/bootstrap/templates/about-command.ts +0 -41
  221. package/docs/bootstrap/templates/docs-models.ts +0 -22
  222. package/docs/bootstrap/templates/docs-readme.md +0 -43
  223. package/docs/bootstrap/templates/example-feature.ts +0 -53
  224. package/docs/bootstrap/templates/health-endpoint.ts +0 -15
  225. package/docs/bootstrap/templates/luca-cli.ts +0 -30
  226. package/docs/bootstrap/templates/runme.md +0 -54
  227. package/docs/challenges/caching-proxy.md +0 -16
  228. package/docs/challenges/content-db-round-trip.md +0 -14
  229. package/docs/challenges/custom-command.md +0 -9
  230. package/docs/challenges/file-watcher-pipeline.md +0 -11
  231. package/docs/challenges/grep-audit-report.md +0 -15
  232. package/docs/challenges/multi-feature-dashboard.md +0 -14
  233. package/docs/challenges/process-orchestrator.md +0 -17
  234. package/docs/challenges/rest-api-server-with-client.md +0 -12
  235. package/docs/challenges/script-runner-with-vm.md +0 -11
  236. package/docs/challenges/simple-rest-api.md +0 -15
  237. package/docs/challenges/websocket-serve-and-client.md +0 -11
  238. package/docs/challenges/yaml-config-system.md +0 -14
  239. package/docs/command-system-overhaul.md +0 -94
  240. package/docs/documentation-audit.md +0 -134
  241. package/docs/examples/assistant/CORE.md +0 -18
  242. package/docs/examples/assistant/hooks.ts +0 -3
  243. package/docs/examples/assistant/tools.ts +0 -10
  244. package/docs/examples/assistant-hooks-reference.ts +0 -171
  245. package/docs/examples/assistant-with-process-manager.md +0 -84
  246. package/docs/examples/content-db.md +0 -77
  247. package/docs/examples/disk-cache.md +0 -83
  248. package/docs/examples/docker.md +0 -101
  249. package/docs/examples/downloader.md +0 -70
  250. package/docs/examples/entity.md +0 -124
  251. package/docs/examples/esbuild.md +0 -80
  252. package/docs/examples/feature-as-tool-provider.md +0 -143
  253. package/docs/examples/file-manager.md +0 -82
  254. package/docs/examples/fs.md +0 -83
  255. package/docs/examples/git.md +0 -85
  256. package/docs/examples/google-auth.md +0 -88
  257. package/docs/examples/google-calendar.md +0 -94
  258. package/docs/examples/google-docs.md +0 -82
  259. package/docs/examples/google-drive.md +0 -96
  260. package/docs/examples/google-sheets.md +0 -95
  261. package/docs/examples/grep.md +0 -85
  262. package/docs/examples/ink-blocks.md +0 -75
  263. package/docs/examples/ink-renderer.md +0 -41
  264. package/docs/examples/ink.md +0 -103
  265. package/docs/examples/ipc-socket.md +0 -103
  266. package/docs/examples/json-tree.md +0 -91
  267. package/docs/examples/networking.md +0 -58
  268. package/docs/examples/nlp.md +0 -91
  269. package/docs/examples/opener.md +0 -78
  270. package/docs/examples/os.md +0 -72
  271. package/docs/examples/package-finder.md +0 -89
  272. package/docs/examples/postgres.md +0 -91
  273. package/docs/examples/proc.md +0 -81
  274. package/docs/examples/process-manager.md +0 -79
  275. package/docs/examples/python.md +0 -132
  276. package/docs/examples/repl.md +0 -93
  277. package/docs/examples/runpod.md +0 -119
  278. package/docs/examples/secure-shell.md +0 -92
  279. package/docs/examples/sqlite.md +0 -86
  280. package/docs/examples/structured-output-with-assistants.md +0 -144
  281. package/docs/examples/telegram.md +0 -77
  282. package/docs/examples/tts.md +0 -86
  283. package/docs/examples/ui.md +0 -80
  284. package/docs/examples/vault.md +0 -70
  285. package/docs/examples/vm.md +0 -86
  286. package/docs/examples/websocket-ask-and-reply-example.md +0 -128
  287. package/docs/examples/yaml-tree.md +0 -93
  288. package/docs/examples/yaml.md +0 -104
  289. package/docs/ideas/assistant-factory-pattern.md +0 -142
  290. package/docs/in-memory-fs.md +0 -4
  291. package/docs/introspection-audit.md +0 -49
  292. package/docs/introspection.md +0 -164
  293. package/docs/mcp/readme.md +0 -162
  294. package/docs/models.ts +0 -41
  295. package/docs/philosophy.md +0 -86
  296. package/docs/principles.md +0 -7
  297. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +0 -34
  298. package/docs/prompts/check-for-undocumented-features.md +0 -27
  299. package/docs/prompts/mcp-test-easy-command.md +0 -27
  300. package/docs/scaffolds/client.md +0 -149
  301. package/docs/scaffolds/command.md +0 -120
  302. package/docs/scaffolds/endpoint.md +0 -171
  303. package/docs/scaffolds/feature.md +0 -158
  304. package/docs/scaffolds/selector.md +0 -91
  305. package/docs/scaffolds/server.md +0 -196
  306. package/docs/selectors.md +0 -115
  307. package/docs/sessions/custom-command/attempt-log-2.md +0 -195
  308. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +0 -728
  309. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +0 -555
  310. package/docs/sessions/grep-audit-report/attempt-log-1.md +0 -289
  311. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +0 -679
  312. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +0 -1
  313. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +0 -920
  314. package/docs/sessions/simple-rest-api/attempt-log-1.md +0 -593
  315. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +0 -995
  316. package/docs/tutorials/00-bootstrap.md +0 -166
  317. package/docs/tutorials/01-getting-started.md +0 -106
  318. package/docs/tutorials/02-container.md +0 -210
  319. package/docs/tutorials/03-scripts.md +0 -194
  320. package/docs/tutorials/04-features-overview.md +0 -196
  321. package/docs/tutorials/05-state-and-events.md +0 -171
  322. package/docs/tutorials/06-servers.md +0 -157
  323. package/docs/tutorials/07-endpoints.md +0 -198
  324. package/docs/tutorials/08-commands.md +0 -252
  325. package/docs/tutorials/09-clients.md +0 -162
  326. package/docs/tutorials/10-creating-features.md +0 -203
  327. package/docs/tutorials/11-contentbase.md +0 -191
  328. package/docs/tutorials/12-assistants.md +0 -215
  329. package/docs/tutorials/13-introspection.md +0 -157
  330. package/docs/tutorials/14-type-system.md +0 -174
  331. package/docs/tutorials/15-project-patterns.md +0 -222
  332. package/docs/tutorials/16-google-features.md +0 -534
  333. package/docs/tutorials/17-tui-blocks.md +0 -530
  334. package/docs/tutorials/18-semantic-search.md +0 -334
  335. package/docs/tutorials/19-python-sessions.md +0 -401
  336. package/docs/tutorials/20-browser-esm.md +0 -234
  337. package/src/agi/endpoints/ask.ts +0 -60
  338. package/src/agi/endpoints/conversations/[id].ts +0 -45
  339. package/src/agi/endpoints/conversations.ts +0 -31
  340. package/src/agi/endpoints/experts.ts +0 -37
  341. package/test/assistant-hooks.test.ts +0 -306
  342. package/test/assistant.test.ts +0 -81
  343. package/test/bus.test.ts +0 -134
  344. package/test/clients-servers.test.ts +0 -217
  345. package/test/command.test.ts +0 -267
  346. package/test/container-link.test.ts +0 -274
  347. package/test/conversation.test.ts +0 -220
  348. package/test/features.test.ts +0 -160
  349. package/test/fork-and-research.test.ts +0 -450
  350. package/test/integration.test.ts +0 -787
  351. package/test/interceptor-chain.test.ts +0 -61
  352. package/test/node-container.test.ts +0 -121
  353. package/test/python-session.test.ts +0 -105
  354. package/test/rate-limit.test.ts +0 -272
  355. package/test/semantic-search.test.ts +0 -550
  356. package/test/state.test.ts +0 -121
  357. package/test/vm-context.test.ts +0 -146
  358. package/test/vm-loadmodule.test.ts +0 -213
  359. package/test/websocket-ask.test.ts +0 -101
  360. package/test-integration/assistant.test.ts +0 -138
  361. package/test-integration/assistants-manager.test.ts +0 -113
  362. package/test-integration/claude-code.test.ts +0 -98
  363. package/test-integration/conversation-history.test.ts +0 -205
  364. package/test-integration/conversation.test.ts +0 -137
  365. package/test-integration/elevenlabs.test.ts +0 -55
  366. package/test-integration/google-services.test.ts +0 -80
  367. package/test-integration/helpers.ts +0 -89
  368. package/test-integration/memory.test.ts +0 -204
  369. package/test-integration/openai-codex.test.ts +0 -93
  370. package/test-integration/runpod.test.ts +0 -58
  371. package/test-integration/server-endpoints.test.ts +0 -97
  372. package/test-integration/telegram.test.ts +0 -46
@@ -1,252 +0,0 @@
1
- ---
2
- title: Writing Commands
3
- tags: [commands, cli, luca-cli, scripts, args]
4
- ---
5
-
6
- # Writing Commands
7
-
8
- Commands are CLI actions that the `luca` command discovers and runs. They are Helper subclasses under the hood — the framework grafts your module exports into a proper Command class at runtime, so you get the full Helper lifecycle (state, events, introspection) without ceremony.
9
-
10
- ## Basic Command
11
-
12
- ```typescript
13
- // commands/seed.ts
14
- import { z } from 'zod'
15
- import type { ContainerContext } from '@soederpop/luca'
16
-
17
- export const description = 'Seed the database with sample data'
18
-
19
- export const argsSchema = z.object({
20
- count: z.number().default(10).describe('Number of records to seed'),
21
- table: z.string().optional().describe('Specific table to seed'),
22
- })
23
-
24
- export default async function seed(options: z.infer<typeof argsSchema>, context: ContainerContext) {
25
- const { container } = context
26
-
27
- console.log(`Seeding ${options.count} records...`)
28
-
29
- for (let i = 0; i < options.count; i++) {
30
- console.log(` Created record ${i + 1}`)
31
- }
32
-
33
- console.log('Done.')
34
- }
35
- ```
36
-
37
- Run it:
38
-
39
- ```bash
40
- luca seed --count 20 --table users
41
- ```
42
-
43
- ## How Discovery Works
44
-
45
- When you run `luca <command>`, the CLI:
46
-
47
- 1. Loads built-in commands (serve, run, eval, describe, etc.)
48
- 2. Loads `luca.cli.ts` if present (for project-level container customization)
49
- 3. Discovers project commands via the `helpers` feature — scans `commands/` for `.ts` files
50
- 4. Discovers user commands from `~/.luca/commands/`
51
- 5. The filename becomes the command name: `commands/seed.ts` → `luca seed`
52
-
53
- Discovery routes through the `helpers` feature (`container.feature('helpers')`), which handles native import vs VM loading and deduplicates concurrent discovery calls. Commands loaded through the VM get `container` injected as a global.
54
-
55
- The `LUCA_COMMAND_DISCOVERY` env var controls discovery: `"disable"` skips all, `"no-local"` skips project, `"no-home"` skips user commands.
56
-
57
- ## Command Module Patterns
58
-
59
- ### Pattern 1: Default Export Function (recommended for project commands)
60
-
61
- The simplest pattern — export a default async function. The function becomes the command's `run` method.
62
-
63
- ```typescript
64
- // commands/greet.ts
65
- import { z } from 'zod'
66
- import type { ContainerContext } from '@soederpop/luca'
67
-
68
- export const description = 'Greet someone'
69
- export const argsSchema = z.object({
70
- name: z.string().default('world').describe('Who to greet'),
71
- })
72
-
73
- export default async function greet(options: z.infer<typeof argsSchema>, context: ContainerContext) {
74
- console.log(`Hello, ${options.name}!`)
75
- }
76
- ```
77
-
78
- ### Pattern 2: Object Default Export with handler
79
-
80
- Useful when you want to co-locate all exports in one object:
81
-
82
- ```typescript
83
- // commands/deploy.ts
84
- import { z } from 'zod'
85
- import type { ContainerContext } from '@soederpop/luca'
86
-
87
- export const argsSchema = z.object({
88
- env: z.enum(['staging', 'production']).describe('Target environment'),
89
- dryRun: z.boolean().default(false).describe('Preview without deploying'),
90
- })
91
-
92
- async function deploy(options: z.infer<typeof argsSchema>, context: ContainerContext) {
93
- const { container } = context
94
- if (options.dryRun) {
95
- console.log(`[DRY RUN] Would deploy to ${options.env}`)
96
- return
97
- }
98
- console.log(`Deploying ${container.git.sha} to ${options.env}...`)
99
- }
100
-
101
- export default {
102
- description: 'Deploy the application',
103
- argsSchema,
104
- handler: deploy,
105
- }
106
- ```
107
-
108
- ### Pattern 3: registerHandler (used by built-in commands)
109
-
110
- Built-in commands use `commands.registerHandler()` for explicit registration. This is the pattern used in `src/commands/`:
111
-
112
- ```typescript
113
- // src/commands/my-builtin.ts
114
- import { z } from 'zod'
115
- import { commands } from '../command'
116
- import { CommandOptionsSchema } from '../schemas/base'
117
- import type { ContainerContext } from '../container'
118
-
119
- declare module '../command.js' {
120
- interface AvailableCommands {
121
- 'my-builtin': ReturnType<typeof commands.registerHandler>
122
- }
123
- }
124
-
125
- export const argsSchema = CommandOptionsSchema.extend({
126
- verbose: z.boolean().default(false).describe('Enable verbose output'),
127
- })
128
-
129
- export default async function myBuiltin(options: z.infer<typeof argsSchema>, context: ContainerContext) {
130
- // implementation
131
- }
132
-
133
- commands.registerHandler('my-builtin', {
134
- description: 'A built-in command',
135
- argsSchema,
136
- handler: myBuiltin,
137
- })
138
- ```
139
-
140
- Project commands generally don't need `registerHandler` — discovery handles registration automatically.
141
-
142
- ## Module Exports Reference
143
-
144
- | Export | Type | Description |
145
- |--------|------|-------------|
146
- | `default` | function or object | Async function becomes `run`, or object with `{ description, argsSchema, handler }` |
147
- | `description` | string | Help text shown in `luca --help` |
148
- | `argsSchema` | Zod schema | Defines accepted flags, parsed from CLI args automatically |
149
- | `positionals` | string[] | Names for positional arguments (mapped from `container.argv._`) |
150
- | `run` | function | Named export alternative to default function — grafted as the command's run method |
151
- | `handler` | function | Legacy alternative to `run` — receives parsed args via `parseArgs()` |
152
-
153
- When discovery loads your module, `graftModule()` synthesizes a Command subclass from these exports. The `run` or `handler` function becomes the command's implementation, schemas become static properties, and any other exported functions become methods on the command instance.
154
-
155
- ## Arguments and Schemas
156
-
157
- The `argsSchema` uses Zod to define what flags your command accepts. These are parsed from the CLI automatically:
158
-
159
- ```typescript
160
- export const argsSchema = z.object({
161
- // String flag: --name "John"
162
- name: z.string().describe('User name'),
163
-
164
- // Number flag: --port 3000
165
- port: z.number().default(3000).describe('Port number'),
166
-
167
- // Boolean flag: --verbose
168
- verbose: z.boolean().default(false).describe('Enable verbose logging'),
169
-
170
- // Optional flag: --output file.json
171
- output: z.string().optional().describe('Output file path'),
172
-
173
- // Enum flag: --format json
174
- format: z.enum(['json', 'csv', 'table']).default('table').describe('Output format'),
175
- })
176
- ```
177
-
178
- ### Positional Arguments
179
-
180
- Export a `positionals` array to map CLI positional args into named fields on `options`. Each entry names the corresponding positional — `positionals[0]` maps `_[1]` (the first arg after the command name), `positionals[1]` maps `_[2]`, etc.
181
-
182
- ```typescript
183
- export const positionals = ['target', 'destination']
184
-
185
- export const argsSchema = z.object({
186
- target: z.string().describe('Source path to operate on'),
187
- destination: z.string().optional().describe('Where to write output'),
188
- })
189
-
190
- // luca my-command ./src ./out
191
- // => options.target === './src', options.destination === './out'
192
- ```
193
-
194
- Positional mapping only applies when dispatched from the CLI. For programmatic dispatch (`cmd.dispatch({ target: './src' }, 'headless')`), args are already named.
195
-
196
- The raw positional array is still available as `options._` if you need it — `_[0]` is always the command name:
197
-
198
- ```typescript
199
- // luca greet Alice Bob
200
- // options._ => ['greet', 'Alice', 'Bob']
201
- ```
202
-
203
- ## Using the Container
204
-
205
- Commands receive a context with the full container:
206
-
207
- ```typescript
208
- export default async function handler(options: any, context: ContainerContext) {
209
- const { container } = context
210
-
211
- // File system operations
212
- const config = container.fs.readJson('./config.json')
213
-
214
- // Git info (these are getters, not methods)
215
- const branch = container.git.branch
216
- const sha = container.git.sha
217
-
218
- // Terminal UI
219
- container.ui.colors.green('Success!')
220
-
221
- // Run external processes (synchronous, returns string)
222
- const result = container.proc.exec('ls -la')
223
-
224
- // Use any feature
225
- const cache = container.feature('diskCache', { path: './.cache' })
226
- }
227
- ```
228
-
229
- ## Command Dispatch
230
-
231
- When the CLI runs a command, it calls `cmd.dispatch()` which:
232
-
233
- 1. Reads raw input from `container.argv` (or explicit args if called programmatically)
234
- 2. Validates args against `argsSchema` if present
235
- 3. Maps positional args if `positionals` is declared
236
- 4. Intercepts `--help` to show auto-generated help text
237
- 5. Calls `run(parsedOptions, context)` with the validated, typed options
238
-
239
- You can also dispatch commands programmatically:
240
-
241
- ```typescript
242
- const cmd = container.command('seed')
243
- await cmd.dispatch({ count: 20, table: 'users' }, 'headless')
244
- ```
245
-
246
- ## Conventions
247
-
248
- - **File location**: `commands/<name>.ts` in the project root. Auto-discovered by the CLI.
249
- - **Naming**: kebab-case filenames. `commands/build-site.ts` → `luca build-site`.
250
- - **Use the container**: Never import `fs`, `path`, `child_process` directly. Use `container.feature('fs')`, `container.paths`, `container.feature('proc')`.
251
- - **Exit codes**: Return nothing for success. Throw for errors — the CLI catches and reports them.
252
- - **Help text**: Use `.describe()` on every schema field — it powers `luca <command> --help`.
@@ -1,162 +0,0 @@
1
- ---
2
- title: Using Clients
3
- tags: [clients, rest, graphql, websocket, http, api, axios]
4
- ---
5
-
6
- # Using Clients
7
-
8
- Clients connect your application to external services. Luca provides built-in clients for REST APIs, GraphQL, and WebSocket connections.
9
-
10
- ## REST Client
11
-
12
- The REST client wraps axios with Luca's helper patterns (state, events, introspection):
13
-
14
- ```typescript
15
- const api = container.client('rest', {
16
- baseURL: 'https://api.example.com',
17
- headers: {
18
- Authorization: 'Bearer my-token',
19
- },
20
- })
21
-
22
- await api.connect()
23
-
24
- // Standard HTTP methods
25
- const users = await api.get('/users')
26
- const user = await api.get('/users/123')
27
- const created = await api.post('/users', { name: 'Alice', email: 'alice@example.com' })
28
- const updated = await api.put('/users/123', { name: 'Alice Updated' })
29
- await api.delete('/users/123')
30
- ```
31
-
32
- ### REST Client Events
33
-
34
- ```typescript
35
- api.on('failure', (error) => {
36
- console.error('Request failed:', error.message)
37
- })
38
-
39
- // State changes track connection status
40
- api.state.observe((type, key, value) => {
41
- if (key === 'connected') {
42
- console.log(`Client connected: ${value}`)
43
- }
44
- })
45
- ```
46
-
47
- ## GraphQL Client
48
-
49
- For GraphQL APIs, use the REST client's `post()` method to send queries and mutations:
50
-
51
- ```typescript
52
- const graph = container.client('rest', {
53
- baseURL: 'https://api.example.com/graphql',
54
- headers: { Authorization: 'Bearer my-token' },
55
- })
56
-
57
- await graph.connect()
58
-
59
- // Send a query
60
- const result = await graph.post('/', {
61
- query: `
62
- query GetUser($id: ID!) {
63
- user(id: $id) {
64
- name
65
- email
66
- posts { title }
67
- }
68
- }
69
- `,
70
- variables: { id: '123' },
71
- })
72
-
73
- // Send a mutation
74
- const mutationResult = await graph.post('/', {
75
- query: `
76
- mutation CreatePost($input: PostInput!) {
77
- createPost(input: $input) {
78
- id
79
- title
80
- }
81
- }
82
- `,
83
- variables: { input: { title: 'Hello World', body: '...' } },
84
- })
85
- ```
86
-
87
- ## WebSocket Client
88
-
89
- The WebSocket client wraps a raw `WebSocket` connection:
90
-
91
- ```typescript
92
- const ws = container.client('websocket', {
93
- baseURL: 'wss://realtime.example.com',
94
- })
95
-
96
- await ws.connect()
97
-
98
- // Access the underlying WebSocket via ws.ws
99
- ws.ws.onmessage = (event) => {
100
- console.log('Received:', event.data)
101
- }
102
-
103
- ws.ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }))
104
-
105
- // Clean up
106
- ws.ws.close()
107
- ```
108
-
109
- ## Discovering Clients
110
-
111
- ```typescript
112
- container.clients.available // ['rest', 'graph', 'websocket']
113
- container.clients.describe('rest')
114
- ```
115
-
116
- ## Using Clients in Endpoints
117
-
118
- ```typescript
119
- // endpoints/proxy.ts
120
- import { z } from 'zod'
121
- import type { EndpointContext } from '@soederpop/luca'
122
-
123
- export const path = '/api/external-data'
124
-
125
- export const getSchema = z.object({
126
- query: z.string().describe('Search query'),
127
- })
128
-
129
- export async function get(params: z.infer<typeof getSchema>, ctx: EndpointContext) {
130
- const api = ctx.container.client('rest', {
131
- baseURL: 'https://external-api.com',
132
- })
133
-
134
- await api.connect()
135
- const data = await api.get(`/search?q=${encodeURIComponent(params.query)}`)
136
-
137
- return { results: data }
138
- }
139
- ```
140
-
141
- ## Using Clients in Features
142
-
143
- ```typescript
144
- class WeatherService extends Feature<WeatherState, WeatherOptions> {
145
- private api: any
146
-
147
- async initialize() {
148
- this.api = this.container.client('rest', {
149
- baseURL: 'https://api.weather.com',
150
- headers: { 'X-API-Key': this.options.apiKey },
151
- })
152
- await this.api.connect()
153
- }
154
-
155
- async getForecast(city: string) {
156
- const data = await this.api.get(`/forecast/${encodeURIComponent(city)}`)
157
- this.state.set('lastForecast', data)
158
- this.emit('forecastFetched', data)
159
- return data
160
- }
161
- }
162
- ```
@@ -1,203 +0,0 @@
1
- ---
2
- title: Creating Custom Features
3
- tags: [features, custom, extend, zod, state, events, module-augmentation, helper]
4
- ---
5
-
6
- # Creating Custom Features
7
-
8
- You can create your own features to encapsulate domain logic, then register them so they're available through `container.feature('yourFeature')` with full type safety.
9
-
10
- ## Anatomy of a Feature
11
-
12
- A feature has:
13
- - **State** -- observable, defined by a Zod schema
14
- - **Options** -- configuration passed at creation, defined by a Zod schema
15
- - **Events** -- typed event bus
16
- - **Methods** -- your domain logic
17
- - **Access to the container** -- via `this.container`
18
-
19
- ## Basic Example
20
-
21
- ```typescript
22
- import { z } from 'zod'
23
- import { Feature, features, FeatureStateSchema, FeatureOptionsSchema } from '@soederpop/luca'
24
-
25
- // Define state schema by extending the base FeatureStateSchema
26
- export const CounterStateSchema = FeatureStateSchema.extend({
27
- count: z.number().describe('Current count value'),
28
- lastUpdated: z.string().optional().describe('ISO timestamp of last update'),
29
- })
30
- export type CounterState = z.infer<typeof CounterStateSchema>
31
-
32
- // Define options schema by extending the base FeatureOptionsSchema
33
- export const CounterOptionsSchema = FeatureOptionsSchema.extend({
34
- initialCount: z.number().default(0).describe('Starting count value'),
35
- step: z.number().default(1).describe('Increment step size'),
36
- })
37
- export type CounterOptions = z.infer<typeof CounterOptionsSchema>
38
-
39
- /**
40
- * A simple counter feature that demonstrates the feature pattern.
41
- * Tracks a count value with observable state and events.
42
- */
43
- export class Counter extends Feature<CounterState, CounterOptions> {
44
- static override stateSchema = CounterStateSchema
45
- static override optionsSchema = CounterOptionsSchema
46
-
47
- /** Called when the feature is created */
48
- async initialize() {
49
- this.state.set('count', this.options.initialCount ?? 0)
50
- }
51
-
52
- /** Increment the counter by the configured step */
53
- increment() {
54
- const current = this.state.get('count') || 0
55
- const next = current + (this.options.step ?? 1)
56
- this.state.set('count', next)
57
- this.state.set('lastUpdated', new Date().toISOString())
58
- this.emit('incremented', next)
59
- return next
60
- }
61
-
62
- /** Decrement the counter by the configured step */
63
- decrement() {
64
- const current = this.state.get('count') || 0
65
- const next = current - (this.options.step ?? 1)
66
- this.state.set('count', next)
67
- this.state.set('lastUpdated', new Date().toISOString())
68
- this.emit('decremented', next)
69
- return next
70
- }
71
-
72
- /** Reset the counter to its initial value */
73
- reset() {
74
- this.state.set('count', this.options.initialCount ?? 0)
75
- this.emit('reset')
76
- }
77
-
78
- /** Get the current count */
79
- get value(): number {
80
- return this.state.get('count') || 0
81
- }
82
- }
83
-
84
- // Register the feature
85
- features.register('counter', Counter)
86
-
87
- // Module augmentation for type safety
88
- declare module '@soederpop/luca' {
89
- interface AvailableFeatures {
90
- counter: typeof Counter
91
- }
92
- }
93
- ```
94
-
95
- ## Using Your Feature
96
-
97
- ```typescript
98
- import './features/counter' // Side-effect import to register
99
-
100
- const counter = container.feature('counter', { initialCount: 10, step: 5 })
101
-
102
- counter.on('incremented', (value) => {
103
- console.log(`Count is now ${value}`)
104
- })
105
-
106
- counter.increment() // 15
107
- counter.increment() // 20
108
- counter.value // 20
109
- counter.reset() // Back to 10
110
-
111
- // Observe state changes
112
- counter.state.observe((type, key, value) => {
113
- console.log(`${key} ${type}d:`, value)
114
- })
115
- ```
116
-
117
- ## Enabling on the Container
118
-
119
- If your feature should be a container-level singleton with a shortcut:
120
-
121
- ```typescript
122
- export class Counter extends Feature<CounterState, CounterOptions> {
123
- // This creates the container.counter shortcut when enabled
124
- static override shortcut = 'features.counter' as const
125
- // ...
126
- }
127
-
128
- // Enable it
129
- container.feature('counter', { enable: true })
130
-
131
- // Now accessible as:
132
- container.counter.increment()
133
- ```
134
-
135
- ## Feature with Container Access
136
-
137
- Features can access other features and the full container:
138
-
139
- ```typescript
140
- export class Analytics extends Feature<AnalyticsState, AnalyticsOptions> {
141
- /** Log an event, writing to disk cache for persistence */
142
- async logEvent(name: string, data: Record<string, any>) {
143
- const cache = this.container.feature('diskCache', { path: './.analytics' })
144
- const timestamp = new Date().toISOString()
145
-
146
- await cache.set(`event:${timestamp}`, { name, data, timestamp })
147
-
148
- this.state.set('totalEvents', (this.state.get('totalEvents') || 0) + 1)
149
- this.emit('eventLogged', { name, data })
150
- }
151
-
152
- /** Get recent events from the cache */
153
- async recentEvents(limit = 10) {
154
- const fs = this.container.fs
155
- // ... read from cache directory
156
- }
157
- }
158
- ```
159
-
160
- ## Documenting Your Feature
161
-
162
- Document your classes, methods, and getters with JSDoc. This is important because Luca's introspection system extracts these docs and makes them available at runtime:
163
-
164
- ```typescript
165
- /**
166
- * Manages user sessions with automatic expiration and renewal.
167
- * Sessions are persisted to disk and can survive process restarts.
168
- */
169
- export class SessionManager extends Feature<SessionState, SessionOptions> {
170
- /**
171
- * Create a new session for the given user.
172
- * Returns a session token that can be used for authentication.
173
- */
174
- async createSession(userId: string): Promise<string> {
175
- // ...
176
- }
177
-
178
- /** The number of currently active sessions */
179
- get activeCount(): number {
180
- return this.state.get('sessions')?.length || 0
181
- }
182
- }
183
- ```
184
-
185
- Then anyone (human or AI) can discover your feature:
186
-
187
- ```typescript
188
- container.features.describe('sessionManager')
189
- // Returns the full markdown documentation extracted from your JSDoc
190
-
191
- // Quick discovery — list available methods and getters
192
- const session = container.feature('sessionManager')
193
- session.$methods // => ['createSession', ...]
194
- session.$getters // => ['activeCount', ...]
195
- ```
196
-
197
- ## Best Practices
198
-
199
- 1. **Use Zod `.describe()` on schema fields** -- these descriptions appear in introspection and help documentation
200
- 2. **Emit events for significant actions** -- enables reactive patterns and decoupled observers
201
- 3. **Use state for observable values** -- don't hide important state in private variables if consumers need to watch it
202
- 4. **Access the container, not imports** -- prefer `this.container.feature('fs')` over importing fs directly, so the feature works in any container
203
- 5. **Document everything** -- JSDoc on the class, methods, and getters feeds the introspection system