@soederpop/luca 0.0.6 → 0.0.8

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 (208) hide show
  1. package/CLAUDE.md +10 -1
  2. package/RUNME.md +56 -0
  3. package/bun.lock +1 -1
  4. package/commands/build-bootstrap.ts +78 -0
  5. package/commands/build-scaffolds.ts +24 -2
  6. package/commands/try-all-challenges.ts +543 -0
  7. package/commands/try-challenge.ts +100 -0
  8. package/docs/README.md +52 -80
  9. package/docs/TABLE-OF-CONTENTS.md +82 -51
  10. package/docs/apis/clients/elevenlabs.md +232 -8
  11. package/docs/apis/clients/graph.md +59 -8
  12. package/docs/apis/clients/openai.md +362 -2
  13. package/docs/apis/clients/rest.md +122 -2
  14. package/docs/apis/clients/websocket.md +71 -17
  15. package/docs/apis/features/agi/assistant.md +9 -3
  16. package/docs/apis/features/agi/assistants-manager.md +2 -2
  17. package/docs/apis/features/agi/claude-code.md +153 -14
  18. package/docs/apis/features/agi/conversation-history.md +15 -3
  19. package/docs/apis/features/agi/conversation.md +133 -20
  20. package/docs/apis/features/agi/openai-codex.md +90 -12
  21. package/docs/apis/features/agi/skills-library.md +23 -5
  22. package/docs/apis/features/node/container-link.md +59 -0
  23. package/docs/apis/features/node/content-db.md +1 -1
  24. package/docs/apis/features/node/disk-cache.md +1 -1
  25. package/docs/apis/features/node/dns.md +1 -0
  26. package/docs/apis/features/node/docker.md +2 -1
  27. package/docs/apis/features/node/esbuild.md +4 -3
  28. package/docs/apis/features/node/file-manager.md +13 -4
  29. package/docs/apis/features/node/fs.md +726 -171
  30. package/docs/apis/features/node/git.md +1 -0
  31. package/docs/apis/features/node/google-auth.md +23 -4
  32. package/docs/apis/features/node/google-calendar.md +14 -2
  33. package/docs/apis/features/node/google-docs.md +15 -2
  34. package/docs/apis/features/node/google-drive.md +21 -3
  35. package/docs/apis/features/node/google-sheets.md +14 -2
  36. package/docs/apis/features/node/grep.md +2 -0
  37. package/docs/apis/features/node/helpers.md +29 -0
  38. package/docs/apis/features/node/ink.md +2 -2
  39. package/docs/apis/features/node/networking.md +39 -4
  40. package/docs/apis/features/node/os.md +28 -0
  41. package/docs/apis/features/node/postgres.md +26 -4
  42. package/docs/apis/features/node/proc.md +37 -28
  43. package/docs/apis/features/node/process-manager.md +33 -5
  44. package/docs/apis/features/node/repl.md +1 -1
  45. package/docs/apis/features/node/runpod.md +1 -0
  46. package/docs/apis/features/node/secure-shell.md +7 -0
  47. package/docs/apis/features/node/semantic-search.md +12 -5
  48. package/docs/apis/features/node/sqlite.md +26 -4
  49. package/docs/apis/features/node/telegram.md +30 -5
  50. package/docs/apis/features/node/tts.md +17 -2
  51. package/docs/apis/features/node/ui.md +1 -1
  52. package/docs/apis/features/node/vault.md +4 -9
  53. package/docs/apis/features/node/vm.md +3 -12
  54. package/docs/apis/features/node/window-manager.md +128 -20
  55. package/docs/apis/features/web/asset-loader.md +13 -1
  56. package/docs/apis/features/web/container-link.md +59 -0
  57. package/docs/apis/features/web/esbuild.md +4 -3
  58. package/docs/apis/features/web/helpers.md +29 -0
  59. package/docs/apis/features/web/network.md +16 -2
  60. package/docs/apis/features/web/speech.md +16 -2
  61. package/docs/apis/features/web/vault.md +4 -9
  62. package/docs/apis/features/web/vm.md +3 -12
  63. package/docs/apis/features/web/voice.md +18 -1
  64. package/docs/apis/servers/express.md +18 -2
  65. package/docs/apis/servers/mcp.md +29 -4
  66. package/docs/apis/servers/websocket.md +34 -6
  67. package/docs/bootstrap/CLAUDE.md +100 -0
  68. package/docs/bootstrap/SKILL.md +222 -0
  69. package/docs/bootstrap/templates/about-command.ts +41 -0
  70. package/docs/bootstrap/templates/docs-models.ts +22 -0
  71. package/docs/bootstrap/templates/docs-readme.md +43 -0
  72. package/docs/bootstrap/templates/example-feature.ts +53 -0
  73. package/docs/bootstrap/templates/health-endpoint.ts +15 -0
  74. package/docs/bootstrap/templates/luca-cli.ts +25 -0
  75. package/docs/bootstrap/templates/runme.md +54 -0
  76. package/docs/challenges/caching-proxy.md +16 -0
  77. package/docs/challenges/content-db-round-trip.md +14 -0
  78. package/docs/challenges/custom-command.md +9 -0
  79. package/docs/challenges/file-watcher-pipeline.md +11 -0
  80. package/docs/challenges/grep-audit-report.md +15 -0
  81. package/docs/challenges/multi-feature-dashboard.md +14 -0
  82. package/docs/challenges/process-orchestrator.md +17 -0
  83. package/docs/challenges/rest-api-server-with-client.md +12 -0
  84. package/docs/challenges/script-runner-with-vm.md +11 -0
  85. package/docs/challenges/simple-rest-api.md +15 -0
  86. package/docs/challenges/websocket-serve-and-client.md +11 -0
  87. package/docs/challenges/yaml-config-system.md +14 -0
  88. package/docs/command-system-overhaul.md +94 -0
  89. package/docs/examples/assistant/CORE.md +18 -0
  90. package/docs/examples/assistant/hooks.ts +3 -0
  91. package/docs/examples/assistant/tools.ts +10 -0
  92. package/docs/examples/window-manager-layouts.md +180 -0
  93. package/docs/in-memory-fs.md +4 -0
  94. package/docs/models.ts +13 -10
  95. package/docs/philosophy.md +4 -3
  96. package/docs/reports/console-hmr-design.md +170 -0
  97. package/docs/reports/helper-semantic-search.md +72 -0
  98. package/docs/scaffolds/client.md +29 -20
  99. package/docs/scaffolds/command.md +64 -50
  100. package/docs/scaffolds/endpoint.md +31 -36
  101. package/docs/scaffolds/feature.md +28 -18
  102. package/docs/scaffolds/selector.md +91 -0
  103. package/docs/scaffolds/server.md +18 -9
  104. package/docs/selectors.md +115 -0
  105. package/docs/sessions/custom-command/attempt-log-2.md +195 -0
  106. package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
  107. package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
  108. package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
  109. package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
  110. package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
  111. package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
  112. package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
  113. package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
  114. package/docs/tutorials/00-bootstrap.md +148 -0
  115. package/docs/tutorials/07-endpoints.md +7 -7
  116. package/docs/tutorials/08-commands.md +153 -72
  117. package/luca.cli.ts +3 -0
  118. package/package.json +6 -5
  119. package/public/index.html +1430 -0
  120. package/scripts/examples/using-ollama.ts +2 -1
  121. package/scripts/update-introspection-data.ts +2 -2
  122. package/src/agi/endpoints/experts.ts +1 -1
  123. package/src/agi/features/assistant.ts +7 -0
  124. package/src/agi/features/assistants-manager.ts +5 -5
  125. package/src/agi/features/claude-code.ts +263 -3
  126. package/src/agi/features/conversation-history.ts +7 -1
  127. package/src/agi/features/conversation.ts +26 -3
  128. package/src/agi/features/openai-codex.ts +26 -2
  129. package/src/agi/features/openapi.ts +6 -1
  130. package/src/agi/features/skills-library.ts +9 -1
  131. package/src/bootstrap/generated.ts +595 -0
  132. package/src/cli/cli.ts +64 -21
  133. package/src/client.ts +23 -357
  134. package/src/clients/civitai/index.ts +1 -1
  135. package/src/clients/client-template.ts +1 -1
  136. package/src/clients/comfyui/index.ts +13 -2
  137. package/src/clients/elevenlabs/index.ts +2 -1
  138. package/src/clients/graph.ts +87 -0
  139. package/src/clients/openai/index.ts +10 -1
  140. package/src/clients/rest.ts +207 -0
  141. package/src/clients/websocket.ts +176 -0
  142. package/src/command.ts +281 -34
  143. package/src/commands/bootstrap.ts +185 -0
  144. package/src/commands/chat.ts +5 -4
  145. package/src/commands/describe.ts +341 -4
  146. package/src/commands/help.ts +35 -9
  147. package/src/commands/index.ts +3 -0
  148. package/src/commands/introspect.ts +92 -2
  149. package/src/commands/prompt.ts +5 -6
  150. package/src/commands/run.ts +75 -10
  151. package/src/commands/save-api-docs.ts +49 -0
  152. package/src/commands/scaffold.ts +169 -23
  153. package/src/commands/select.ts +94 -0
  154. package/src/commands/serve.ts +10 -1
  155. package/src/container.ts +15 -0
  156. package/src/endpoint.ts +19 -0
  157. package/src/graft.ts +181 -0
  158. package/src/introspection/generated.agi.ts +12458 -8968
  159. package/src/introspection/generated.node.ts +10573 -7145
  160. package/src/introspection/generated.web.ts +1 -1
  161. package/src/introspection/index.ts +26 -0
  162. package/src/node/container.ts +6 -7
  163. package/src/node/features/content-db.ts +49 -2
  164. package/src/node/features/disk-cache.ts +16 -9
  165. package/src/node/features/dns.ts +16 -3
  166. package/src/node/features/docker.ts +16 -4
  167. package/src/node/features/esbuild.ts +22 -2
  168. package/src/node/features/file-manager.ts +184 -29
  169. package/src/node/features/fs.ts +704 -248
  170. package/src/node/features/git.ts +21 -8
  171. package/src/node/features/grep.ts +23 -3
  172. package/src/node/features/helpers.ts +372 -43
  173. package/src/node/features/networking.ts +39 -4
  174. package/src/node/features/opener.ts +28 -15
  175. package/src/node/features/os.ts +76 -0
  176. package/src/node/features/port-exposer.ts +11 -1
  177. package/src/node/features/postgres.ts +17 -1
  178. package/src/node/features/proc.ts +4 -1
  179. package/src/node/features/python.ts +63 -14
  180. package/src/node/features/repl.ts +11 -7
  181. package/src/node/features/runpod.ts +16 -3
  182. package/src/node/features/secure-shell.ts +27 -2
  183. package/src/node/features/semantic-search.ts +12 -1
  184. package/src/node/features/ui.ts +5 -69
  185. package/src/node/features/vm.ts +17 -0
  186. package/src/node/features/window-manager.ts +68 -20
  187. package/src/node.ts +5 -0
  188. package/src/scaffolds/generated.ts +492 -290
  189. package/src/scaffolds/template.ts +9 -0
  190. package/src/schemas/base.ts +46 -5
  191. package/src/selector.ts +282 -0
  192. package/src/server.ts +11 -0
  193. package/src/servers/express.ts +27 -12
  194. package/src/servers/socket.ts +45 -11
  195. package/src/web/clients/socket.ts +4 -1
  196. package/src/web/container.ts +2 -1
  197. package/src/web/features/network.ts +7 -1
  198. package/src/web/features/voice-recognition.ts +16 -1
  199. package/test/clients-servers.test.ts +2 -1
  200. package/test/command.test.ts +267 -0
  201. package/test/vm-context.test.ts +146 -0
  202. package/test-integration/assistants-manager.test.ts +10 -20
  203. package/docs/apis/features/node/launcher-app-command-listener.md +0 -145
  204. package/docs/examples/launcher-app-command-listener.md +0 -120
  205. package/docs/tasks/web-container-helper-discovery.md +0 -71
  206. package/docs/todos.md +0 -1
  207. package/scripts/test-command-listener.ts +0 -123
  208. package/src/node/features/launcher-app-command-listener.ts +0 -389
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: "Bootstrap: Learning the Container at Runtime"
3
+ tags:
4
+ - bootstrap
5
+ - introspection
6
+ - repl
7
+ - agent
8
+ - discovery
9
+ - quickstart
10
+ ---
11
+ # Bootstrap: Learning the Container at Runtime
12
+
13
+ You don't need to memorize the Luca API. The container tells you everything it can do — at runtime. This tutorial teaches you the discovery pattern so you can explore any feature, client, server, or command without reading docs.
14
+
15
+ ## Start with `luca eval`
16
+
17
+ The `eval` command runs JavaScript with the container in scope. All features are available as top-level variables.
18
+
19
+ ```bash
20
+ # What features are available?
21
+ luca eval "container.features.available"
22
+ # => ['fs', 'git', 'proc', 'vm', 'networking', 'os', 'grep', ...]
23
+
24
+ # What clients?
25
+ luca eval "container.clients.available"
26
+
27
+ # What servers?
28
+ luca eval "container.servers.available"
29
+
30
+ # What commands?
31
+ luca eval "container.commands.available"
32
+ ```
33
+
34
+ ## Describe Anything
35
+
36
+ The `luca describe` command generates API docs for any helper. It reads JSDoc, Zod schemas, and method signatures to produce markdown documentation.
37
+
38
+ ```bash
39
+ # Describe the container itself
40
+ luca describe
41
+
42
+ # Describe a feature
43
+ luca describe fs
44
+
45
+ # Describe multiple at once
46
+ luca describe git fs proc
47
+
48
+ # Show only specific sections
49
+ luca describe fs --methods --examples
50
+
51
+ # Describe all features
52
+ luca describe features
53
+ ```
54
+
55
+ In code, the same works via registries:
56
+
57
+ ```js
58
+ container.features.describe('fs') // markdown docs for fs
59
+ container.features.describeAll() // condensed overview of all features
60
+ container.clients.describe('rest') // docs for the rest client
61
+ ```
62
+
63
+ ## The Discovery Pattern
64
+
65
+ Every registry follows the same shape. Once you know the pattern, you can explore anything:
66
+
67
+ ```js
68
+ // List what's available
69
+ container.features.available
70
+ container.clients.available
71
+ container.servers.available
72
+ container.commands.available
73
+
74
+ // Get docs for a specific helper
75
+ container.features.describe('fs')
76
+ container.clients.describe('rest')
77
+ container.servers.describe('express')
78
+
79
+ // Check if something exists
80
+ container.features.has('fs') // => true
81
+
82
+ // Get a helper instance
83
+ const fs = container.feature('fs')
84
+ const rest = container.client('rest')
85
+ ```
86
+
87
+ ## Instance Introspection
88
+
89
+ Once you have a helper instance, it can describe itself:
90
+
91
+ ```js
92
+ const fs = container.feature('fs')
93
+
94
+ // Structured introspection (object with methods, getters, events, state, options)
95
+ fs.introspect()
96
+
97
+ // Human-readable markdown
98
+ fs.introspectAsText()
99
+ ```
100
+
101
+ The container itself is introspectable:
102
+
103
+ ```js
104
+ container.inspect() // structured object with all registries, state, events
105
+ container.inspectAsText() // full markdown overview
106
+ ```
107
+
108
+ ## The REPL
109
+
110
+ For interactive exploration, use `luca console`. It gives you a persistent REPL with the container and all features in scope:
111
+
112
+ ```bash
113
+ luca console
114
+ ```
115
+
116
+ Inside the REPL, you can tab-complete, call methods, and explore interactively. Variables survive across lines.
117
+
118
+ ## Feature Shortcuts
119
+
120
+ In eval and REPL contexts, core features are available as top-level variables — no need to call `container.feature()`:
121
+
122
+ ```bash
123
+ luca eval "fs.readFile('package.json')"
124
+ luca eval "git.branch"
125
+ luca eval "proc.exec('ls')"
126
+ luca eval "grep.search('.', 'TODO')"
127
+ ```
128
+
129
+ ## Quick Reference
130
+
131
+ | Want to know... | Ask |
132
+ |--------------------------------|----------------------------------------|
133
+ | What registries exist? | `container.registries` |
134
+ | What features are available? | `container.features.available` |
135
+ | Full docs for a feature? | `container.features.describe('fs')` |
136
+ | All features at a glance? | `container.features.describeAll()` |
137
+ | Structured introspection? | `feature.introspect()` |
138
+ | What state does it have? | `feature.state.current` |
139
+ | What events does it emit? | `feature.introspect().events` |
140
+ | Full container overview? | `container.inspectAsText()` |
141
+ | CLI docs for a helper? | `luca describe <name>` |
142
+
143
+ ## What's Next
144
+
145
+ - [The Container](./02-container.md) — deep dive into state, events, and lifecycle
146
+ - [Scripts](./03-scripts.md) — run scripts and executable markdown notebooks
147
+ - [Features Overview](./04-features-overview.md) — explore built-in features
148
+ - [Writing Commands](./08-commands.md) — add CLI commands to your project
@@ -86,12 +86,11 @@ export async function put(params: z.infer<typeof putSchema>, ctx: EndpointContex
86
86
  return { user: { id, ...params }, message: 'Updated' }
87
87
  }
88
88
 
89
- // Use a named const + re-export for delete (reserved word)
90
- const del = async (_params: any, ctx: EndpointContext) => {
89
+ // Use `destroy` for DELETE it's a reserved word in JS
90
+ export async function destroy(_params: any, ctx: EndpointContext) {
91
91
  const { id } = ctx.params
92
92
  return { message: `User ${id} deleted` }
93
93
  }
94
- export { del as delete }
95
94
  ```
96
95
 
97
96
  ## The EndpointContext
@@ -124,9 +123,10 @@ Export any of these handler functions:
124
123
  - `post` -- POST requests
125
124
  - `put` -- PUT requests
126
125
  - `patch` -- PATCH requests
127
- - `delete` -- DELETE requests
126
+ - `destroy` -- DELETE requests (preferred — avoids the `delete` reserved word)
127
+ - `delete` -- DELETE requests (also works via `export { del as delete }`)
128
128
 
129
- Each can have a corresponding schema export: `getSchema`, `postSchema`, `putSchema`, `patchSchema`, `deleteSchema`.
129
+ Each can have a corresponding schema export: `getSchema`, `postSchema`, `putSchema`, `patchSchema`, `destroySchema` / `deleteSchema`.
130
130
 
131
131
  ## What Gets Exported
132
132
 
@@ -135,8 +135,8 @@ Each can have a corresponding schema export: `getSchema`, `postSchema`, `putSche
135
135
  | `path` | Yes | The route path (e.g. `/api/users`, `/api/users/:id`) |
136
136
  | `description` | No | Human-readable description (used in OpenAPI spec) |
137
137
  | `tags` | No | Array of tags for OpenAPI grouping |
138
- | `get`, `post`, etc. | At least one | Handler functions |
139
- | `getSchema`, `postSchema`, etc. | No | Zod schemas for request validation |
138
+ | `get`, `post`, `put`, `patch`, `destroy` | At least one | Handler functions (`destroy` maps to DELETE) |
139
+ | `getSchema`, `postSchema`, `destroySchema`, etc. | No | Zod schemas for request validation |
140
140
 
141
141
  ## Starting the Server
142
142
 
@@ -5,7 +5,7 @@ tags: [commands, cli, luca-cli, scripts, args]
5
5
 
6
6
  # Writing Commands
7
7
 
8
- Commands are CLI actions that the `luca` command discovers and runs. Projects can define their own commands in a `commands/` directory at the project root.
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
9
 
10
10
  ## Basic Command
11
11
 
@@ -21,7 +21,7 @@ export const argsSchema = z.object({
21
21
  table: z.string().optional().describe('Specific table to seed'),
22
22
  })
23
23
 
24
- export async function handler(options: z.infer<typeof argsSchema>, context: ContainerContext) {
24
+ export default async function seed(options: z.infer<typeof argsSchema>, context: ContainerContext) {
25
25
  const { container } = context
26
26
 
27
27
  console.log(`Seeding ${options.count} records...`)
@@ -44,22 +44,117 @@ luca seed --count 20 --table users
44
44
 
45
45
  When you run `luca <command>`, the CLI:
46
46
 
47
- 1. Loads built-in commands (serve, run, etc.)
48
- 2. Looks for a `commands/` directory in your project root
49
- 3. Scans for `.ts` files and registers them as commands
50
- 4. The filename becomes the command name: `commands/seed.ts` -> `luca seed`
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`
51
52
 
52
- ## Command File Structure
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.
53
54
 
54
- | Export | Required | Description |
55
- |--------|----------|-------------|
56
- | `handler` | Yes | Async function that runs the command |
57
- | `argsSchema` | No | Zod schema defining accepted arguments |
58
- | `description` | No | Help text for the command |
55
+ The `LUCA_COMMAND_DISCOVERY` env var controls discovery: `"disable"` skips all, `"no-local"` skips project, `"no-home"` skips user commands.
59
56
 
60
- ## Arguments and the Schema
57
+ ## Command Module Patterns
61
58
 
62
- The `argsSchema` uses Zod to define what flags your command accepts. These are parsed from the command line automatically:
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:
63
158
 
64
159
  ```typescript
65
160
  export const argsSchema = z.object({
@@ -80,12 +175,37 @@ export const argsSchema = z.object({
80
175
  })
81
176
  ```
82
177
 
83
- ## Using the Container in Commands
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
84
204
 
85
205
  Commands receive a context with the full container:
86
206
 
87
207
  ```typescript
88
- export async function handler(options: any, context: ContainerContext) {
208
+ export default async function handler(options: any, context: ContainerContext) {
89
209
  const { container } = context
90
210
 
91
211
  // File system operations
@@ -106,66 +226,27 @@ export async function handler(options: any, context: ContainerContext) {
106
226
  }
107
227
  ```
108
228
 
109
- ## Examples
110
-
111
- ### Database Migration Command
112
-
113
- ```typescript
114
- // commands/migrate.ts
115
- import { z } from 'zod'
116
- import type { ContainerContext } from '@soederpop/luca'
117
-
118
- export const description = 'Run database migrations'
119
-
120
- export const argsSchema = z.object({
121
- direction: z.enum(['up', 'down']).default('up').describe('Migration direction'),
122
- steps: z.number().default(1).describe('Number of migrations to run'),
123
- })
124
-
125
- export async function handler(options: z.infer<typeof argsSchema>, context: ContainerContext) {
126
- const { container } = context
127
- const { files } = container.fs.walk('./migrations', { include: ['*.sql'] })
229
+ ## Command Dispatch
128
230
 
129
- console.log(`Running ${options.steps} migration(s) ${options.direction}...`)
231
+ When the CLI runs a command, it calls `cmd.dispatch()` which:
130
232
 
131
- for (const file of files.slice(0, options.steps)) {
132
- console.log(` Applying: ${file}`)
133
- const sql = container.fs.readFile(file)
134
- // ... execute sql
135
- }
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
136
238
 
137
- console.log('Migrations complete.')
138
- }
139
- ```
140
-
141
- ### Deploy Command
239
+ You can also dispatch commands programmatically:
142
240
 
143
241
  ```typescript
144
- // commands/deploy.ts
145
- import { z } from 'zod'
146
- import type { ContainerContext } from '@soederpop/luca'
147
-
148
- export const description = 'Deploy the application'
149
-
150
- export const argsSchema = z.object({
151
- env: z.enum(['staging', 'production']).describe('Target environment'),
152
- dryRun: z.boolean().default(false).describe('Preview without deploying'),
153
- })
154
-
155
- export async function handler(options: z.infer<typeof argsSchema>, context: ContainerContext) {
156
- const { container } = context
157
-
158
- if (options.dryRun) {
159
- console.log(`[DRY RUN] Would deploy to ${options.env}`)
160
- return
161
- }
162
-
163
- const sha = container.git.sha
164
- console.log(`Deploying ${sha} to ${options.env}...`)
242
+ const cmd = container.command('seed')
243
+ await cmd.dispatch({ count: 20, table: 'users' }, 'headless')
244
+ ```
165
245
 
166
- container.proc.exec('bun run build')
167
- // ... deployment logic
246
+ ## Conventions
168
247
 
169
- console.log('Deployed successfully.')
170
- }
171
- ```
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`.
package/luca.cli.ts ADDED
@@ -0,0 +1,3 @@
1
+ export async function main(container) {
2
+ container.addContext('luca', container)
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soederpop/luca",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "website": "https://luca.soederpop.com",
5
5
  "description": "lightweight universal conversational architecture AKA Le Ultimate Component Architecture AKA Last Universal Common Ancestor, part AI part Human",
6
6
  "author": "jon soeder aka the people's champ <jon@soederpop.com>",
@@ -48,12 +48,13 @@
48
48
  "clean": "rm -rf dist build package",
49
49
  "build:web": "bun run scripts/build-web.ts",
50
50
  "build:introspection": "bun run src/cli/cli.ts introspect",
51
- "compile": "bun build ./src/cli/cli.ts --compile --outfile dist/luca --external node-llama-cpp",
51
+ "compile": "bun run build:introspection && bun run build:scaffolds && bun run build:bootstrap && bun build ./src/cli/cli.ts --compile --outfile dist/luca --external node-llama-cpp",
52
52
  "typecheck": "tsc -p tsconfig.json --noEmit",
53
53
  "build:scaffolds": "bun run src/cli/cli.ts build-scaffolds",
54
+ "build:bootstrap": "bun run src/cli/cli.ts build-bootstrap",
54
55
  "test": "bun test test/*.test.ts",
55
- "update-all-docs": "bun run test && bun run build:introspection && bun run src/cli/cli.ts generate-api-docs && bun run build:scaffolds",
56
- "precommit": "bun run update-all-docs && git add docs/apis/ src/introspection/generated.*.ts src/scaffolds/generated.ts && bun compile",
56
+ "update-all-docs": "bun run test && bun run build:introspection && bun run src/cli/cli.ts generate-api-docs && bun run build:scaffolds && bun run build:bootstrap",
57
+ "precommit": "bun run update-all-docs && git add docs/apis/ src/introspection/generated.*.ts src/scaffolds/generated.ts src/bootstrap/generated.ts && bun compile",
57
58
  "test:integration": "bun test ./test-integration/"
58
59
  },
59
60
  "devDependencies": {
@@ -101,7 +102,7 @@
101
102
  "chokidar": "^3.5.3",
102
103
  "cli-markdown": "^3.5.0",
103
104
  "compromise": "^14.14.5",
104
- "contentbase": "^0.0.5",
105
+ "contentbase": "^0.1.1",
105
106
  "cors": "^2.8.5",
106
107
  "detect-port": "^1.5.1",
107
108
  "dotenv": "^17.2.4",