@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,53 @@
1
+ import { z } from 'zod'
2
+ import { FeatureStateSchema, FeatureOptionsSchema } from '@soederpop/luca'
3
+ import { Feature } from '@soederpop/luca'
4
+ import type { ContainerContext } from '@soederpop/luca'
5
+
6
+ declare module '@soederpop/luca' {
7
+ interface AvailableFeatures {
8
+ example: typeof Example
9
+ }
10
+ }
11
+
12
+ const ExampleStateSchema = FeatureStateSchema.extend({
13
+ greetCount: z.number().default(0).describe('Number of times greet() has been called'),
14
+ })
15
+ type ExampleState = z.infer<typeof ExampleStateSchema>
16
+
17
+ const ExampleOptionsSchema = FeatureOptionsSchema.extend({})
18
+ type ExampleOptions = z.infer<typeof ExampleOptionsSchema>
19
+
20
+ /**
21
+ * An example feature demonstrating the luca feature pattern.
22
+ *
23
+ * Discovered automatically by `container.helpers.discoverAll()`
24
+ * and available as `container.feature('example')`.
25
+ *
26
+ * To learn more: `luca scaffold feature --tutorial`
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const example = container.feature('example')
31
+ * example.greet('Luca') // => "Hello, Luca! (greeting #1)"
32
+ * ```
33
+ */
34
+ export class Example extends Feature<ExampleState, ExampleOptions> {
35
+ static override shortcut = 'features.example' as const
36
+ static override stateSchema = ExampleStateSchema
37
+ static override optionsSchema = ExampleOptionsSchema
38
+ static override description = 'An example feature demonstrating the luca feature pattern'
39
+ static { Feature.register(this, 'example') }
40
+
41
+ /**
42
+ * A simple method to show the feature works.
43
+ * @param name - Name to greet
44
+ * @returns Greeting string
45
+ */
46
+ greet(name = 'World') {
47
+ const count = (this.state.get('greetCount') || 0) + 1
48
+ this.state.set('greetCount', count)
49
+ return `Hello, ${name}! (greeting #${count})`
50
+ }
51
+ }
52
+
53
+ export default Example
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Health check endpoint.
3
+ * Accessible at GET /api/health when you run `luca serve`.
4
+ */
5
+ export const path = '/api/health'
6
+ export const description = 'Health check endpoint'
7
+ export const tags = ['health']
8
+
9
+ export async function get(_params: any, ctx: any) {
10
+ return {
11
+ status: 'ok',
12
+ timestamp: Date.now(),
13
+ uptime: process.uptime(),
14
+ }
15
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * luca.cli.ts — Project-level CLI customization
3
+ *
4
+ * This file is automatically loaded by the `luca` CLI before any command runs.
5
+ * Use it to:
6
+ *
7
+ * - Discover project-level helpers (features, commands, endpoints)
8
+ * - Register custom context variables accessible in `luca eval`
9
+ * - Set up project-specific container configuration
10
+ *
11
+ * Exports:
12
+ * main(container) — called at CLI startup, before command execution
13
+ * onStart(container) — called when the container's 'started' event fires
14
+ *
15
+ * Example:
16
+ * export async function main(container: any) {
17
+ * await container.helpers.discoverAll()
18
+ * container.addContext('myFeature', container.feature('myFeature'))
19
+ * }
20
+ */
21
+
22
+ export async function main(container: any) {
23
+ // Discover project-level helpers (commands/, features/, endpoints/)
24
+ await container.helpers.discoverAll()
25
+ }
@@ -0,0 +1,54 @@
1
+ # Runnable Markdown
2
+
3
+ The `luca` CLI allows you to run markdown blocks as long as they're tagged with `ts` in the language.
4
+
5
+ ```ts
6
+ const banner = ui.banner('LUCA', {
7
+ font: 'Puffy',
8
+ colors: ['red','white','blue']
9
+ })
10
+
11
+ ui.print(banner)
12
+ ```
13
+
14
+ What is kind of cool is ( so long as there's no top-level-await in the block ) the context will preserve:
15
+
16
+ ```ts
17
+ if(typeof banner === 'undefined') {
18
+ ui.print.red('uh oh, something broke.')
19
+ }
20
+ ```
21
+
22
+ You can skip blocks too with the skip tag in the language of the fenced block
23
+
24
+ ```ts skip
25
+ console.log('Not gonna say anything')
26
+ ```
27
+
28
+ Did you hear something? No.
29
+
30
+ Something even cooler is the ability to render React blocks. This makes luca kind of like a poor man's MDX. I just define some Blocks in the markdown by creating an h2 `## Blocks` section with a fenced codeblock that uses `tsx`. The `ink.components` and `ink.React` globals are injected into the scope.
31
+
32
+ ## Blocks
33
+
34
+ ```tsx
35
+ const { Box, Text } = ink.components
36
+ const React = ink.React
37
+
38
+ function Greeting({ name, role }) {
39
+ return (
40
+ <Box borderStyle="round" padding={1}>
41
+ <Text color="green" bold>Hello {name}!</Text>
42
+ <Text dimColor> ({role})</Text>
43
+ </Box>
44
+ )
45
+ }
46
+ ```
47
+
48
+ ## Rendering React Blocks
49
+
50
+ Then I can use the Blocks in code.
51
+
52
+ ```ts
53
+ await render('Greeting', { name: 'Jon', role: 'Humble Servant' })
54
+ ```
@@ -0,0 +1,16 @@
1
+ # Build a Caching Proxy
2
+
3
+ Create `luca proxy` that starts a local HTTP server which proxies incoming requests to a configurable upstream URL, caching responses with a configurable TTL (default 60 seconds).
4
+
5
+ Support these flags:
6
+
7
+ - `luca proxy --upstream https://jsonplaceholder.typicode.com` — set the upstream
8
+ - `luca proxy --ttl 120` — cache TTL in seconds
9
+ - `luca proxy --flush` — clear the cache and exit
10
+ - `luca proxy --stats` — show cache hit/miss ratio and exit
11
+
12
+ When running, every request to `localhost:<port>/anything` should proxy to `<upstream>/anything`, serving from cache on hit.
13
+
14
+ ## After you are done
15
+
16
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,14 @@
1
+ # Content DB Round-Trip
2
+
3
+ Populate a `docs/recipes/` collection with 5 recipe documents. Each document should have frontmatter with `title`, `tags` (array), `difficulty` (easy/medium/hard), and a markdown body with ingredients and steps.
4
+
5
+ Create two commands:
6
+
7
+ - `luca recipes search <term>` — queries the collection by tag or title match and prints matching recipes
8
+ - `luca recipes export` — writes a `recipes-summary.json` file with all recipe titles, tags, and difficulty levels
9
+
10
+ All document access must go through the container's document system, not raw file reads.
11
+
12
+ ## After you are done
13
+
14
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,9 @@
1
+ # Build a custom command
2
+
3
+ Build a custom command with luca called `hello`
4
+
5
+ I should be able to run the command `luca hello $whatever` and it should output the word $whatever with an ascii art gradient, using only the features available in luca.
6
+
7
+ ## After you are done
8
+
9
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,11 @@
1
+ # Build a File Watcher Pipeline
2
+
3
+ Watch a folder called `inbox/` for new `.json` files. When a new file appears, validate that it has a `name` and `email` field. Move valid files to `inbox/valid/` and invalid files to `inbox/invalid/`.
4
+
5
+ Create `luca watch` to start the watcher and `luca status` to report how many files have been processed, how many were valid, and how many were invalid.
6
+
7
+ Include a `luca seed` command that drops a few sample `.json` files into `inbox/` so you can test it.
8
+
9
+ ## After you are done
10
+
11
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,15 @@
1
+ # Grep Audit Report
2
+
3
+ Build `luca audit <path>` that scans a codebase at the given path and finds:
4
+
5
+ - TODO and FIXME comments
6
+ - `console.log` statements
7
+ - Hardcoded secret patterns (strings that look like API keys, tokens, passwords in assignments)
8
+
9
+ Generate a markdown report at `docs/reports/audit-<timestamp>.md` summarizing the findings with file paths and line numbers.
10
+
11
+ Support `luca audit <path> --json` for machine-readable output instead of markdown.
12
+
13
+ ## After you are done
14
+
15
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,14 @@
1
+ # Build a Multi-Feature Dashboard
2
+
3
+ Create a `luca dashboard` command that displays a live-updating terminal UI showing:
4
+
5
+ - Git status: current branch, number of dirty files
6
+ - Cache stats: number of cached entries
7
+ - OS info: CPU usage, free memory
8
+ - Network: whether https://example.com is reachable
9
+
10
+ The dashboard should refresh every 2 seconds with a nice terminal layout.
11
+
12
+ ## After you are done
13
+
14
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,17 @@
1
+ # Process Orchestrator
2
+
3
+ Build `luca dev` that starts 3 processes simultaneously:
4
+
5
+ 1. An HTTP API server on port 3000
6
+ 2. A WebSocket server on port 3001
7
+ 3. A file watcher that logs changes in the project directory
8
+
9
+ Multiplex their logs to stdout with color-coded prefixes (e.g. `[API]`, `[WS]`, `[WATCH]`). Gracefully shut down all processes on ctrl+c.
10
+
11
+ Support `luca dev --only api,ws` to start a subset of the processes.
12
+
13
+ Create a simple endpoint and websocket handler so there's something to actually hit when testing.
14
+
15
+ ## After you are done
16
+
17
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,12 @@
1
+ # Build a REST server with a matching REST Client
2
+
3
+ Create a command `luca start` which will use a custom luca express server you build and start it.
4
+
5
+ Also create a rest client that can call the methods it exposes.
6
+
7
+ The `luca connect` command should use the client and verify that all the servers endpoints work.
8
+
9
+
10
+ ## After you are done
11
+
12
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,11 @@
1
+ # Script Runner with VM
2
+
3
+ Create a `scripts/` folder with a few `.ts` files that each export a `run()` function. These scripts should do simple things — format a date, generate a UUID, compute a hash of some input.
4
+
5
+ Build a `luca exec <scriptName>` command that loads and runs the named script in a sandboxed environment. The script should have access to the container but not direct filesystem access.
6
+
7
+ Add `luca exec --list` to show all available scripts with a short description parsed from a comment or export in each file.
8
+
9
+ ## After you are done
10
+
11
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,15 @@
1
+ ---
2
+ lastRanAt: 1773554828927
3
+ durationMs: 601701
4
+ outputTokens: 1646
5
+ ---
6
+
7
+ # Build an API
8
+
9
+ I should be able to serve the api with `luca serve`
10
+
11
+ The API should use sqlite to serve an example api that lets me browse authors and books.
12
+
13
+ ## After you are done
14
+
15
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,11 @@
1
+ # Build a websocket server with a matching websocket client
2
+
3
+ Use the luca framework to build a websocket server you can start with a `luca start` command.
4
+
5
+ Also build a websocket based client that can connect to that server and speak the same language ( as opposed to just raw websocket calls )
6
+
7
+ Create a command called `luca connect` that uses this client and verifies that the server responds as expected.
8
+
9
+ ## After you are done
10
+
11
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,14 @@
1
+ # YAML Config System
2
+
3
+ Build a command suite for managing project configuration stored in `luca.config.yaml`:
4
+
5
+ - `luca config set <key> <value>` — set a config value (support nested keys like `server.port`)
6
+ - `luca config get <key>` — get a config value
7
+ - `luca config list` — print the full config as a formatted table
8
+ - `luca config delete <key>` — remove a key
9
+
10
+ Create the config file if it doesn't exist.
11
+
12
+ ## After you are done
13
+
14
+ Write a LESSONS.md in the attempt folder that describes what you learned, what you struggled with, and what you could have been supplied with up front either in the CLAUDE.md or in the skills that come with luca so you could achieve the goal quicker and with less trouble.
@@ -0,0 +1,94 @@
1
+ # Command System Overhaul
2
+
3
+ The command system was pretty much 100% vibe coded and not reviewed. It was only until the first round of evals that I noticed some issues with it and dug into the code and noticed a few things with its implementation that are not consistent with the rest of the project.
4
+
5
+ My main issue is it doesn't actually use the luca Registry, the Command is not even a subclass of Helper, even though it is easily one of the most obvious examples, however there are quirks to it that let me empathize with the agent for its choices, so I want to resolve those conflicts so we can arrive at the correct design.
6
+
7
+ ## Original Vision For Commands
8
+
9
+ 1) For a user of the compiled `luca` binary, any `commands/:commandName.ts` will be available as `luca commandName`
10
+ 2) The luca framework itself comes with core commands in `src/commands/*` that are bundled with the `luca` binary from bun
11
+ 3) A user can also have `~/.luca/commands` folder which gets picked up
12
+ 4) commands can export a zod schema that can be used for validation, documentation for CLI help screens
13
+
14
+ ## Command Helper Options / State
15
+
16
+ A Command's constructor options are higher level things about the environment the command is being invoked in:
17
+
18
+ - target ( cli (output to stdout, stderr etc), headless (e.g. run through the js api in code, stdout, stderr needs to be captured and returned)
19
+
20
+
21
+ ## Developer Experience Requirements
22
+
23
+ Unlike features, clients, servers, which necessarily require a lot more forethought, a command should be very quick to author
24
+
25
+ for this reason, there needs to be a way that we can dynamically generate Command helper subclasses, without requiring the user to go through the ceremony of doing that.
26
+
27
+ Instead, a command module should be able to simply export a couple of expected named objects / types, and have the Command helper subclass generated and registered in the command registry at runtime.
28
+
29
+ ## Idea for General Reusable DX Feature
30
+
31
+ This approach will allow us to accomplish the DX requirement for commands in a way that could make authoring every type of helper simpler as well, without having to modify the core class based implementation.
32
+
33
+ Given a typescript module called serve
34
+
35
+
36
+ ```ts
37
+ import type { SimpleCommand } from '@soederpop/luca'
38
+
39
+ // SimpleCommand is a ts generic that makes it simpler to construct the actual class ServeCommand<CommandHelperOptions,COmmandHelperState> since all commands share the same base constructor options and have the same state, but we will need to capture
40
+ // the run options schema
41
+ declare module '@soederpop/luca' {
42
+ interface AvailableCommands {
43
+ serve: typeof Command
44
+ }
45
+ }
46
+
47
+
48
+ // container is gonna be global here if they don't import anything
49
+ // because this module will be run through the container's vm instead of being directly imported
50
+ const runOptionsSchema = z.object({
51
+
52
+ })
53
+
54
+ export type ServeRunOptions = z.infer<typeof runOptionsSchema>
55
+
56
+ export async function run(options: ServeRunOptions) {
57
+
58
+ }
59
+ ```
60
+
61
+ This module could be grafted on to a subclass that gets generated at runtime and meets all the requirements
62
+
63
+ ## CLI Internal Implementation idea
64
+
65
+ This is pseudocode, but will illustrate how I think we can accomplish the above.
66
+
67
+ The CLI would just call the execute method with whatever container.argv is as its input (container.argv is the result of minimist)
68
+
69
+ Any other kind of dispatcher that is using commands and wants to accept arbitrary subclasses can also use this execute method, and not have to worry about the simple command overriding the run method
70
+
71
+ ```ts
72
+ type CommandOptions, CommandState etc ( zod schema inferred, make sure to follow feature, client, server implementations )
73
+
74
+ class Command extends Helper<CommandOptions,CommandState,COmmandEvents> {
75
+ // private, but we should allow the CLI to reach in and call this ?
76
+ private async execute(params) {
77
+ return this.run(params)
78
+ }
79
+
80
+ // NOTE: CommandRunOptions is not the same as the CommandOptions or HelperOptions passed to the constructor
81
+ // In fact the command options are more high level (e.g, is it running from the CLI? dispatched over RPC, an MCP call
82
+ // an agent tool? Commands are just atomic units of work that can be requested.
83
+ run(options: CommandRunOptions) {
84
+ // you don't need to super this ever, any subclass of command implements its own run
85
+ // SimpleCommands can export run and safely override this, because the CLI doesn't call run directly it calls execute
86
+ }
87
+ }
88
+ ```
89
+
90
+
91
+
92
+
93
+
94
+
@@ -0,0 +1,18 @@
1
+ # Luca Assistant Example
2
+
3
+ You are currently an example / template "Assistant" provided by the Luca framework. ( You'll probably have no idea what that is, don't worry, it doesn't matter ).
4
+
5
+ You are what gets scaffolded when a user writes the `luca scaffold assistant` command.
6
+
7
+ In luca, an Assistant is backed by a folder which has a few components:
8
+
9
+ - CORE.md -- this is a markdown file that will get injected into the system prompt of a chat completion call
10
+ - tools.ts -- this file is expected to export functions, and a schemas object whose keys are the names of the functions that get exported, and whose values are zod v4 schemas that describe the parameters
11
+ - hooks.ts -- this file is expexted to export functions, whose names match the events emitted by the luca assistant helper
12
+
13
+ Currently, the user is chatting with you from the `luca chat` CLI.
14
+
15
+ You should tell them what each of these files is and how to edit them.
16
+
17
+ It is also important for them to know that the luca `container` is globally available for them in the context of the `tools.ts` and `hooks.ts` files.
18
+
@@ -0,0 +1,3 @@
1
+ export function started() {
2
+ console.log('Assistant started!')
3
+ }
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod'
2
+
3
+ export const schemas = {
4
+ README: z.object({}).describe('CALL THIS README FUNCTION AS EARLY AS POSSIBLE')
5
+ }
6
+
7
+ export function README(options: z.infer<typeof schemas.README>) {
8
+ return 'YO YO'
9
+ }
10
+
@@ -0,0 +1,180 @@
1
+ ---
2
+ title: "Window Manager Layouts"
3
+ tags: [windowManager, layout, native, ipc, macos, multi-window]
4
+ lastTested: null
5
+ lastTestPassed: null
6
+ ---
7
+
8
+ # Window Manager Layouts
9
+
10
+ Spawn and manage multiple windows at once using the layout API. Layouts let you declare groups of browser and terminal windows that open in parallel, or sequence multiple groups so each batch waits for the previous one to finish.
11
+
12
+ ## Overview
13
+
14
+ The `windowManager` feature exposes two layout methods:
15
+
16
+ - **`spawnLayout(config)`** — spawns all entries in parallel, returns `WindowHandle[]`
17
+ - **`spawnLayouts(configs)`** — spawns multiple layouts sequentially (each layout's windows still spawn in parallel), returns `WindowHandle[][]`
18
+
19
+ Each entry in a layout is a `LayoutEntry` — either a browser window or a TTY window. Type detection is automatic: if the entry has a `command` field or `type: 'tty'`, it's a terminal. Otherwise it's a browser window.
20
+
21
+ ## Setup
22
+
23
+ ```ts
24
+ const wm = container.feature('windowManager', {
25
+ autoListen: true,
26
+ requestTimeoutMs: 10000
27
+ })
28
+ console.log('Window Manager ready')
29
+ ```
30
+
31
+ ## Single Layout — Parallel Windows
32
+
33
+ Spawn a mix of browser and terminal windows that all open at the same time.
34
+
35
+ ```ts
36
+ const handles = await wm.spawnLayout([
37
+ { url: 'https://github.com', width: '50%', height: '100%', x: 0, y: 0 },
38
+ { url: 'https://soederpop.com', width: '50%', height: '100%', x: '50%', y: 0 },
39
+ { command: 'top', title: 'System Monitor', width: 900, height: 400, x: 0, y: 720 },
40
+ ])
41
+
42
+ console.log('Spawned', handles.length, 'windows')
43
+ handles.forEach((h, i) => console.log(` [${i}] windowId: ${h.windowId}`))
44
+ ```
45
+
46
+ All three windows open simultaneously. The returned `handles` array preserves the same order as the config entries, so `handles[0]` corresponds to the GitHub window, `handles[1]` to HN, and `handles[2]` to htop.
47
+
48
+ ## Percentage-Based Dimensions
49
+
50
+ Dimensions (`width`, `height`, `x`, `y`) accept percentage strings resolved against the primary display. This makes layouts portable across different screen resolutions.
51
+
52
+ ```ts skip
53
+ // Side-by-side: two windows each taking half the screen
54
+ const handles = await wm.spawnLayout([
55
+ { url: 'https://github.com', width: '50%', height: '100%', x: '0%', y: '0%' },
56
+ { url: 'https://news.ycombinator.com', width: '50%', height: '100%', x: '50%', y: '0%' },
57
+ ])
58
+ ```
59
+
60
+ You can mix absolute and percentage values freely:
61
+
62
+ ```ts skip
63
+ const handles = await wm.spawnLayout([
64
+ { url: 'https://example.com', width: '75%', height: 600, x: '12.5%', y: 50 },
65
+ { command: 'htop', width: '100%', height: '30%', x: '0%', y: '70%' },
66
+ ])
67
+ ```
68
+
69
+ ## Explicit Type Field
70
+
71
+ You can be explicit about entry types using the `type` field. This is equivalent to the implicit detection but more readable when mixing window types.
72
+
73
+ ```ts skip
74
+ const handles = await wm.spawnLayout([
75
+ { type: 'window', url: 'https://example.com', width: 800, height: 600 },
76
+ { type: 'tty', command: 'tail -f /var/log/system.log', title: 'Logs' },
77
+ ])
78
+
79
+ console.log('Browser window:', handles[0].windowId)
80
+ console.log('TTY window:', handles[1].windowId)
81
+ ```
82
+
83
+ ## Sequential Layouts
84
+
85
+ Use `spawnLayouts()` when you need windows to appear in stages. Each layout batch spawns in parallel, but the next batch waits until the previous one is fully ready.
86
+
87
+ ```ts skip
88
+ const [dashboards, tools] = await wm.spawnLayouts([
89
+ // First batch: main content
90
+ [
91
+ { url: 'https://grafana.internal/d/api-latency', width: 960, height: 800, x: 0, y: 0 },
92
+ { url: 'https://grafana.internal/d/error-rate', width: 960, height: 800, x: 970, y: 0 },
93
+ ],
94
+ // Second batch: supporting tools (opens after dashboards are ready)
95
+ [
96
+ { command: 'htop', title: 'CPU', width: 640, height: 400, x: 0, y: 820 },
97
+ { command: 'tail -f /var/log/system.log', title: 'Logs', width: 640, height: 400, x: 650, y: 820 },
98
+ ],
99
+ ])
100
+
101
+ console.log('Dashboards:', dashboards.map(h => h.windowId))
102
+ console.log('Tools:', tools.map(h => h.windowId))
103
+ ```
104
+
105
+ This is useful when the second batch depends on the first being visible — for example, positioning tool windows below dashboard windows.
106
+
107
+ ## Lifecycle Events on Layout Handles
108
+
109
+ Every handle returned from a layout supports the same event API as a single `spawn()` call. You can listen for `close` and `terminalExited` events on each handle independently.
110
+
111
+ ```ts skip
112
+ const handles = await wm.spawnLayout([
113
+ { url: 'https://example.com', width: 800, height: 600 },
114
+ { command: 'sleep 5 && echo done', title: 'Short Task' },
115
+ ])
116
+
117
+ const [browser, terminal] = handles
118
+
119
+ browser.on('close', () => console.log('Browser window closed'))
120
+
121
+ terminal.on('terminalExited', (info) => {
122
+ console.log('Terminal process finished:', info)
123
+ })
124
+ ```
125
+
126
+ ## Window Chrome Options
127
+
128
+ Layout entries support all the same window chrome options as regular `spawn()` calls.
129
+
130
+ ```ts skip
131
+ const handles = await wm.spawnLayout([
132
+ {
133
+ url: 'https://example.com',
134
+ width: 400,
135
+ height: 300,
136
+ alwaysOnTop: true,
137
+ window: {
138
+ decorations: 'hiddenTitleBar',
139
+ transparent: true,
140
+ shadow: true,
141
+ opacity: 0.9,
142
+ }
143
+ },
144
+ {
145
+ url: 'https://example.com/overlay',
146
+ width: 200,
147
+ height: 100,
148
+ window: {
149
+ decorations: 'none',
150
+ clickThrough: true,
151
+ transparent: true,
152
+ }
153
+ },
154
+ ])
155
+ ```
156
+
157
+ ## Operating on All Handles
158
+
159
+ Since layouts return arrays of `WindowHandle`, you can easily batch operations across all windows.
160
+
161
+ ```ts skip
162
+ const handles = await wm.spawnLayout([
163
+ { url: 'https://example.com/a', width: 600, height: 400 },
164
+ { url: 'https://example.com/b', width: 600, height: 400 },
165
+ { url: 'https://example.com/c', width: 600, height: 400 },
166
+ ])
167
+
168
+ // Navigate all windows to the same URL
169
+ await Promise.all(handles.map(h => h.navigate('https://example.com/updated')))
170
+
171
+ // Screenshot all windows
172
+ await Promise.all(handles.map((h, i) => h.screengrab(`./layout-${i}.png`)))
173
+
174
+ // Close all windows
175
+ await Promise.all(handles.map(h => h.close()))
176
+ ```
177
+
178
+ ## Summary
179
+
180
+ The layout API builds on top of `spawn()` and `spawnTTY()` to orchestrate multi-window setups. Use `spawnLayout()` for a single batch of parallel windows, and `spawnLayouts()` when you need staged sequences. Every returned handle supports the full `WindowHandle` API — events, navigation, eval, screenshots, and more.
@@ -0,0 +1,4 @@
1
+ # In Memory FS
2
+
3
+ If we rigorously ensured all file system operations of other features, servers, clients, etc
4
+ went through the container.fs system, we could very easily build in memory file systems