@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.
- package/CLAUDE.md +10 -1
- package/RUNME.md +56 -0
- package/bun.lock +1 -1
- package/commands/build-bootstrap.ts +78 -0
- package/commands/build-scaffolds.ts +24 -2
- package/commands/try-all-challenges.ts +543 -0
- package/commands/try-challenge.ts +100 -0
- package/docs/README.md +52 -80
- package/docs/TABLE-OF-CONTENTS.md +82 -51
- package/docs/apis/clients/elevenlabs.md +232 -8
- package/docs/apis/clients/graph.md +59 -8
- package/docs/apis/clients/openai.md +362 -2
- package/docs/apis/clients/rest.md +122 -2
- package/docs/apis/clients/websocket.md +71 -17
- package/docs/apis/features/agi/assistant.md +9 -3
- package/docs/apis/features/agi/assistants-manager.md +2 -2
- package/docs/apis/features/agi/claude-code.md +153 -14
- package/docs/apis/features/agi/conversation-history.md +15 -3
- package/docs/apis/features/agi/conversation.md +133 -20
- package/docs/apis/features/agi/openai-codex.md +90 -12
- package/docs/apis/features/agi/skills-library.md +23 -5
- package/docs/apis/features/node/container-link.md +59 -0
- package/docs/apis/features/node/content-db.md +1 -1
- package/docs/apis/features/node/disk-cache.md +1 -1
- package/docs/apis/features/node/dns.md +1 -0
- package/docs/apis/features/node/docker.md +2 -1
- package/docs/apis/features/node/esbuild.md +4 -3
- package/docs/apis/features/node/file-manager.md +13 -4
- package/docs/apis/features/node/fs.md +726 -171
- package/docs/apis/features/node/git.md +1 -0
- package/docs/apis/features/node/google-auth.md +23 -4
- package/docs/apis/features/node/google-calendar.md +14 -2
- package/docs/apis/features/node/google-docs.md +15 -2
- package/docs/apis/features/node/google-drive.md +21 -3
- package/docs/apis/features/node/google-sheets.md +14 -2
- package/docs/apis/features/node/grep.md +2 -0
- package/docs/apis/features/node/helpers.md +29 -0
- package/docs/apis/features/node/ink.md +2 -2
- package/docs/apis/features/node/networking.md +39 -4
- package/docs/apis/features/node/os.md +28 -0
- package/docs/apis/features/node/postgres.md +26 -4
- package/docs/apis/features/node/proc.md +37 -28
- package/docs/apis/features/node/process-manager.md +33 -5
- package/docs/apis/features/node/repl.md +1 -1
- package/docs/apis/features/node/runpod.md +1 -0
- package/docs/apis/features/node/secure-shell.md +7 -0
- package/docs/apis/features/node/semantic-search.md +12 -5
- package/docs/apis/features/node/sqlite.md +26 -4
- package/docs/apis/features/node/telegram.md +30 -5
- package/docs/apis/features/node/tts.md +17 -2
- package/docs/apis/features/node/ui.md +1 -1
- package/docs/apis/features/node/vault.md +4 -9
- package/docs/apis/features/node/vm.md +3 -12
- package/docs/apis/features/node/window-manager.md +128 -20
- package/docs/apis/features/web/asset-loader.md +13 -1
- package/docs/apis/features/web/container-link.md +59 -0
- package/docs/apis/features/web/esbuild.md +4 -3
- package/docs/apis/features/web/helpers.md +29 -0
- package/docs/apis/features/web/network.md +16 -2
- package/docs/apis/features/web/speech.md +16 -2
- package/docs/apis/features/web/vault.md +4 -9
- package/docs/apis/features/web/vm.md +3 -12
- package/docs/apis/features/web/voice.md +18 -1
- package/docs/apis/servers/express.md +18 -2
- package/docs/apis/servers/mcp.md +29 -4
- package/docs/apis/servers/websocket.md +34 -6
- package/docs/bootstrap/CLAUDE.md +100 -0
- package/docs/bootstrap/SKILL.md +222 -0
- package/docs/bootstrap/templates/about-command.ts +41 -0
- package/docs/bootstrap/templates/docs-models.ts +22 -0
- package/docs/bootstrap/templates/docs-readme.md +43 -0
- package/docs/bootstrap/templates/example-feature.ts +53 -0
- package/docs/bootstrap/templates/health-endpoint.ts +15 -0
- package/docs/bootstrap/templates/luca-cli.ts +25 -0
- package/docs/bootstrap/templates/runme.md +54 -0
- package/docs/challenges/caching-proxy.md +16 -0
- package/docs/challenges/content-db-round-trip.md +14 -0
- package/docs/challenges/custom-command.md +9 -0
- package/docs/challenges/file-watcher-pipeline.md +11 -0
- package/docs/challenges/grep-audit-report.md +15 -0
- package/docs/challenges/multi-feature-dashboard.md +14 -0
- package/docs/challenges/process-orchestrator.md +17 -0
- package/docs/challenges/rest-api-server-with-client.md +12 -0
- package/docs/challenges/script-runner-with-vm.md +11 -0
- package/docs/challenges/simple-rest-api.md +15 -0
- package/docs/challenges/websocket-serve-and-client.md +11 -0
- package/docs/challenges/yaml-config-system.md +14 -0
- package/docs/command-system-overhaul.md +94 -0
- package/docs/examples/assistant/CORE.md +18 -0
- package/docs/examples/assistant/hooks.ts +3 -0
- package/docs/examples/assistant/tools.ts +10 -0
- package/docs/examples/window-manager-layouts.md +180 -0
- package/docs/in-memory-fs.md +4 -0
- package/docs/models.ts +13 -10
- package/docs/philosophy.md +4 -3
- package/docs/reports/console-hmr-design.md +170 -0
- package/docs/reports/helper-semantic-search.md +72 -0
- package/docs/scaffolds/client.md +29 -20
- package/docs/scaffolds/command.md +64 -50
- package/docs/scaffolds/endpoint.md +31 -36
- package/docs/scaffolds/feature.md +28 -18
- package/docs/scaffolds/selector.md +91 -0
- package/docs/scaffolds/server.md +18 -9
- package/docs/selectors.md +115 -0
- package/docs/sessions/custom-command/attempt-log-2.md +195 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-1.md +728 -0
- package/docs/sessions/file-watcher-pipeline/attempt-log-2.md +555 -0
- package/docs/sessions/grep-audit-report/attempt-log-1.md +289 -0
- package/docs/sessions/multi-feature-dashboard/attempt-log-2.md +679 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-1.md +1 -0
- package/docs/sessions/rest-api-server-with-client/attempt-log-3.md +920 -0
- package/docs/sessions/simple-rest-api/attempt-log-1.md +593 -0
- package/docs/sessions/websocket-serve-and-client/attempt-log-2.md +995 -0
- package/docs/tutorials/00-bootstrap.md +148 -0
- package/docs/tutorials/07-endpoints.md +7 -7
- package/docs/tutorials/08-commands.md +153 -72
- package/luca.cli.ts +3 -0
- package/package.json +6 -5
- package/public/index.html +1430 -0
- package/scripts/examples/using-ollama.ts +2 -1
- package/scripts/update-introspection-data.ts +2 -2
- package/src/agi/endpoints/experts.ts +1 -1
- package/src/agi/features/assistant.ts +7 -0
- package/src/agi/features/assistants-manager.ts +5 -5
- package/src/agi/features/claude-code.ts +263 -3
- package/src/agi/features/conversation-history.ts +7 -1
- package/src/agi/features/conversation.ts +26 -3
- package/src/agi/features/openai-codex.ts +26 -2
- package/src/agi/features/openapi.ts +6 -1
- package/src/agi/features/skills-library.ts +9 -1
- package/src/bootstrap/generated.ts +595 -0
- package/src/cli/cli.ts +64 -21
- package/src/client.ts +23 -357
- package/src/clients/civitai/index.ts +1 -1
- package/src/clients/client-template.ts +1 -1
- package/src/clients/comfyui/index.ts +13 -2
- package/src/clients/elevenlabs/index.ts +2 -1
- package/src/clients/graph.ts +87 -0
- package/src/clients/openai/index.ts +10 -1
- package/src/clients/rest.ts +207 -0
- package/src/clients/websocket.ts +176 -0
- package/src/command.ts +281 -34
- package/src/commands/bootstrap.ts +185 -0
- package/src/commands/chat.ts +5 -4
- package/src/commands/describe.ts +341 -4
- package/src/commands/help.ts +35 -9
- package/src/commands/index.ts +3 -0
- package/src/commands/introspect.ts +92 -2
- package/src/commands/prompt.ts +5 -6
- package/src/commands/run.ts +75 -10
- package/src/commands/save-api-docs.ts +49 -0
- package/src/commands/scaffold.ts +169 -23
- package/src/commands/select.ts +94 -0
- package/src/commands/serve.ts +10 -1
- package/src/container.ts +15 -0
- package/src/endpoint.ts +19 -0
- package/src/graft.ts +181 -0
- package/src/introspection/generated.agi.ts +12458 -8968
- package/src/introspection/generated.node.ts +10573 -7145
- package/src/introspection/generated.web.ts +1 -1
- package/src/introspection/index.ts +26 -0
- package/src/node/container.ts +6 -7
- package/src/node/features/content-db.ts +49 -2
- package/src/node/features/disk-cache.ts +16 -9
- package/src/node/features/dns.ts +16 -3
- package/src/node/features/docker.ts +16 -4
- package/src/node/features/esbuild.ts +22 -2
- package/src/node/features/file-manager.ts +184 -29
- package/src/node/features/fs.ts +704 -248
- package/src/node/features/git.ts +21 -8
- package/src/node/features/grep.ts +23 -3
- package/src/node/features/helpers.ts +372 -43
- package/src/node/features/networking.ts +39 -4
- package/src/node/features/opener.ts +28 -15
- package/src/node/features/os.ts +76 -0
- package/src/node/features/port-exposer.ts +11 -1
- package/src/node/features/postgres.ts +17 -1
- package/src/node/features/proc.ts +4 -1
- package/src/node/features/python.ts +63 -14
- package/src/node/features/repl.ts +11 -7
- package/src/node/features/runpod.ts +16 -3
- package/src/node/features/secure-shell.ts +27 -2
- package/src/node/features/semantic-search.ts +12 -1
- package/src/node/features/ui.ts +5 -69
- package/src/node/features/vm.ts +17 -0
- package/src/node/features/window-manager.ts +68 -20
- package/src/node.ts +5 -0
- package/src/scaffolds/generated.ts +492 -290
- package/src/scaffolds/template.ts +9 -0
- package/src/schemas/base.ts +46 -5
- package/src/selector.ts +282 -0
- package/src/server.ts +11 -0
- package/src/servers/express.ts +27 -12
- package/src/servers/socket.ts +45 -11
- package/src/web/clients/socket.ts +4 -1
- package/src/web/container.ts +2 -1
- package/src/web/features/network.ts +7 -1
- package/src/web/features/voice-recognition.ts +16 -1
- package/test/clients-servers.test.ts +2 -1
- package/test/command.test.ts +267 -0
- package/test/vm-context.test.ts +146 -0
- package/test-integration/assistants-manager.test.ts +10 -20
- package/docs/apis/features/node/launcher-app-command-listener.md +0 -145
- package/docs/examples/launcher-app-command-listener.md +0 -120
- package/docs/tasks/web-container-helper-discovery.md +0 -71
- package/docs/todos.md +0 -1
- package/scripts/test-command-listener.ts +0 -123
- 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
|
|
90
|
-
|
|
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
|
-
- `
|
|
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`,
|
|
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.
|
|
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
|
|
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.
|
|
49
|
-
3.
|
|
50
|
-
4.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
57
|
+
## Command Module Patterns
|
|
61
58
|
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
231
|
+
When the CLI runs a command, it calls `cmd.dispatch()` which:
|
|
130
232
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Deploy Command
|
|
239
|
+
You can also dispatch commands programmatically:
|
|
142
240
|
|
|
143
241
|
```typescript
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
167
|
-
// ... deployment logic
|
|
246
|
+
## Conventions
|
|
168
247
|
|
|
169
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soederpop/luca",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
105
|
+
"contentbase": "^0.1.1",
|
|
105
106
|
"cors": "^2.8.5",
|
|
106
107
|
"detect-port": "^1.5.1",
|
|
107
108
|
"dotenv": "^17.2.4",
|