@soederpop/luca 0.0.23 → 0.0.26

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 (67) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +6 -1
  3. package/assistants/codingAssistant/hooks.ts +0 -1
  4. package/assistants/lucaExpert/CORE.md +37 -0
  5. package/assistants/lucaExpert/hooks.ts +9 -0
  6. package/assistants/lucaExpert/tools.ts +177 -0
  7. package/commands/build-bootstrap.ts +41 -1
  8. package/docs/TABLE-OF-CONTENTS.md +0 -1
  9. package/docs/apis/clients/rest.md +5 -5
  10. package/docs/apis/features/agi/assistant.md +1 -1
  11. package/docs/apis/features/agi/conversation-history.md +6 -7
  12. package/docs/apis/features/agi/conversation.md +1 -1
  13. package/docs/apis/features/agi/semantic-search.md +1 -1
  14. package/docs/bootstrap/CLAUDE.md +1 -1
  15. package/docs/bootstrap/SKILL.md +7 -3
  16. package/docs/bootstrap/templates/luca-cli.ts +5 -0
  17. package/docs/mcp/readme.md +1 -1
  18. package/docs/tutorials/00-bootstrap.md +18 -0
  19. package/package.json +2 -2
  20. package/scripts/stamp-build.sh +12 -0
  21. package/scripts/test-docs-reader.ts +10 -0
  22. package/src/agi/container.server.ts +8 -5
  23. package/src/agi/features/assistant.ts +210 -55
  24. package/src/agi/features/assistants-manager.ts +138 -66
  25. package/src/agi/features/conversation.ts +46 -14
  26. package/src/agi/features/docs-reader.ts +166 -0
  27. package/src/agi/features/openapi.ts +1 -1
  28. package/src/agi/features/skills-library.ts +257 -313
  29. package/src/bootstrap/generated.ts +8163 -6
  30. package/src/cli/build-info.ts +4 -0
  31. package/src/cli/cli.ts +2 -1
  32. package/src/command.ts +75 -0
  33. package/src/commands/bootstrap.ts +16 -1
  34. package/src/commands/describe.ts +29 -1089
  35. package/src/commands/eval.ts +6 -1
  36. package/src/commands/sandbox-mcp.ts +17 -7
  37. package/src/container-describer.ts +1098 -0
  38. package/src/container.ts +11 -0
  39. package/src/helper.ts +56 -2
  40. package/src/introspection/generated.agi.ts +1684 -799
  41. package/src/introspection/generated.node.ts +964 -572
  42. package/src/introspection/generated.web.ts +9 -1
  43. package/src/node/container.ts +1 -1
  44. package/src/node/features/content-db.ts +268 -13
  45. package/src/node/features/fs.ts +18 -0
  46. package/src/node/features/git.ts +90 -0
  47. package/src/node/features/grep.ts +1 -1
  48. package/src/node/features/proc.ts +1 -0
  49. package/src/node/features/tts.ts +1 -1
  50. package/src/node/features/vm.ts +48 -0
  51. package/src/scaffolds/generated.ts +2 -2
  52. package/src/server.ts +40 -0
  53. package/src/servers/express.ts +2 -0
  54. package/src/servers/mcp.ts +1 -0
  55. package/src/servers/socket.ts +2 -0
  56. package/assistants/architect/CORE.md +0 -3
  57. package/assistants/architect/hooks.ts +0 -3
  58. package/assistants/architect/tools.ts +0 -10
  59. package/docs/apis/features/agi/skills-library.md +0 -234
  60. package/docs/reports/assistant-bugs.md +0 -38
  61. package/docs/reports/attach-pattern-usage.md +0 -18
  62. package/docs/reports/code-audit-results.md +0 -391
  63. package/docs/reports/console-hmr-design.md +0 -170
  64. package/docs/reports/helper-semantic-search.md +0 -72
  65. package/docs/reports/introspection-audit-tasks.md +0 -378
  66. package/docs/reports/luca-mcp-improvements.md +0 -128
  67. package/test-integration/skills-library.test.ts +0 -157
@@ -1,391 +0,0 @@
1
- ---
2
- title: Code Audit - Container Abstraction Violations
3
- status: active
4
- type: report
5
- createdAt: 2026-02-26
6
- updatedAt: 2026-02-26
7
- ---
8
-
9
- # Code Audit: Container Abstraction Violations
10
-
11
- Audit of `src/**/features/**`, `src/**/clients/**`, `src/clients/**`, `src/servers/**`, and `src/commands/**` for violations of the container abstraction pattern.
12
-
13
- **Excluded from audit**: `fs.ts`, `proc.ts`, `git.ts`, `os.ts` — these are core wrappers that intentionally import Node builtins. `file-manager.ts` is allowed to use `path` directly.
14
-
15
- ## Severity Legend
16
-
17
- - **HIGH**: Breaks Node.js compatibility, or completely bypasses container when equivalent exists
18
- - **MEDIUM**: Uses builtins/process.env when container provides an alternative
19
- - **LOW**: Minor style issue, or acceptable edge case
20
-
21
- ---
22
-
23
- ## Summary
24
-
25
- | Category | Files Audited | Files with Violations | HIGH | MEDIUM | LOW |
26
- |----------|--------------|----------------------|------|--------|-----|
27
- | Node Features | 14 | 11 | 19 | 14 | 6 |
28
- | AGI Features | 10 | 3 | 12 | 4 | 0 |
29
- | Clients | 7 | 3 | 3 | 2 | 0 |
30
- | Servers | 3 | 2 | 1 | 1 | 0 |
31
- | Commands | 11 | 5 | 3 | 5 | 2 |
32
- | **Total** | **45** | **24** | **38** | **26** | **8** |
33
-
34
- ### Top Offenders (by violation count)
35
-
36
- 1. **`skills-library.ts`** — 7 HIGH (imports `path`, `os`, `fs/promises` and uses throughout)
37
- 2. **`claude-code.ts`** — 8 violations (3x `Bun.spawn`, 1x `Bun.write`, 2x dynamic `fs/promises`, 2x `process.env`)
38
- 3. **`launcher-app-command-listener.ts`** — 7 HIGH (fs, path, os at module top-level)
39
- 4. **`window-manager.ts`** — 7 HIGH (same pattern as launcher-app)
40
- 5. **`python.ts`** — 9+ uses of `existsSync`/`join` from builtins
41
- 6. **`package-finder.ts`** — 5 violations (partially migrated, inconsistent)
42
- 7. **`comfyui/index.ts`** — 5 violations in one method (dynamic imports + `Bun.write`)
43
- 8. **`openai-codex.ts`** — 4 violations (3x `Bun.spawn`, 2x `process.env`)
44
-
45
- ### Bug Found
46
-
47
- **`repl.ts` lines 92, 192** — Uses `fs.readFileSync` and `fs.appendFileSync` but **never imports `fs`**. This will throw `ReferenceError` at runtime when history persistence is used.
48
-
49
- ---
50
-
51
- ## Detailed Findings
52
-
53
- ### Node Features (`src/node/features/`)
54
-
55
- #### `package-finder.ts`
56
-
57
- | Line(s) | Violation | Should Use | Severity |
58
- |---------|-----------|-----------|----------|
59
- | 4 | `import { readdir, readFile } from 'fs/promises'` | `this.container.fs` | HIGH |
60
- | 5 | `import { resolve, join, basename } from 'path'` | `this.container.paths` | MEDIUM |
61
- | 242 | `readFile(path)` direct usage | `this.container.fs.readFileAsync()` | HIGH |
62
- | 471, 477 | `readdir(...)` direct usage | `this.container.fs.readdirAsync()` | HIGH |
63
- | 475, 477 | `join(...)` direct usage | `this.container.paths.join()` | MEDIUM |
64
-
65
- Note: Line 523 correctly uses `this.container.fs.findUpAsync` — file is partially migrated but inconsistent.
66
-
67
- #### `python.ts`
68
-
69
- | Line(s) | Violation | Should Use | Severity |
70
- |---------|-----------|-----------|----------|
71
- | 4 | `import { existsSync } from 'fs'` | `this.container.fs.exists()` | HIGH |
72
- | 5 | `import { join, resolve } from 'path'` | `this.container.paths` | MEDIUM |
73
- | 84 | `resolve(this.options.dir)` | `this.container.paths.resolve()` | MEDIUM |
74
- | 93, 145, 158, 171-172, 177, 245-246, 256, 259 | 9+ calls to `existsSync()` | `this.container.fs.exists()` | HIGH |
75
- | 145, 158, 171+ | `join(...)` throughout `detectEnvironment()` | `this.container.paths.join()` | MEDIUM |
76
-
77
- Note: `execute()` method (line 313+) correctly uses container features — only `detectEnvironment()` and `enable()` are violating.
78
-
79
- #### `launcher-app-command-listener.ts`
80
-
81
- | Line(s) | Violation | Should Use | Severity |
82
- |---------|-----------|-----------|----------|
83
- | 5 | `import { homedir } from 'os'` | `this.container.feature('os').homedir` | MEDIUM |
84
- | 6 | `import { join, dirname } from 'path'` | `this.container.paths` | MEDIUM |
85
- | 7 | `import { existsSync, unlinkSync, mkdirSync } from 'fs'` | `this.container.fs` | HIGH |
86
- | 9-15 | `homedir()`, `join(...)` at module top-level (before container exists) | Must defer to runtime (`enable()` or getter) | HIGH |
87
- | 250-253 | `dirname()`, `existsSync()`, `mkdirSync()` | `this.container.paths`/`this.container.fs` | HIGH |
88
- | 260-263 | `existsSync()`, `unlinkSync()` | `this.container.fs` | HIGH |
89
- | 307-308 | `existsSync()`, `unlinkSync()` | `this.container.fs` | HIGH |
90
-
91
- #### `window-manager.ts`
92
-
93
- | Line(s) | Violation | Should Use | Severity |
94
- |---------|-----------|-----------|----------|
95
- | 5 | `import { homedir } from 'os'` | `this.container.feature('os').homedir` | MEDIUM |
96
- | 6 | `import { join, dirname } from 'path'` | `this.container.paths` | MEDIUM |
97
- | 8 | `import { existsSync, unlinkSync, mkdirSync } from 'fs'` | `this.container.fs` | HIGH |
98
- | 10-16 | `homedir()`, `join(...)` at module top-level | Must defer to runtime | HIGH |
99
- | 354-372 | `dirname()`, `existsSync()`, `mkdirSync()`, `unlinkSync()` | Container equivalents | HIGH |
100
- | 420-421 | `existsSync()`, `unlinkSync()` | `this.container.fs` | HIGH |
101
-
102
- #### `helpers.ts`
103
-
104
- | Line(s) | Violation | Should Use | Severity |
105
- |---------|-----------|-----------|----------|
106
- | 11 | `import { resolve, parse } from 'path'` | `this.container.paths` | MEDIUM |
107
- | 134, 258 | `resolve(this.rootDir, ...)` | `this.container.paths.resolve()` | MEDIUM |
108
- | 324 | `const { Glob } = globalThis.Bun \|\| (await import('bun'))` — unguarded `Bun.Glob` | Guard with `this.container.isBun` or use container glob | HIGH |
109
-
110
- #### `tmux.ts`
111
-
112
- | Line(s) | Violation | Should Use | Severity |
113
- |---------|-----------|-----------|----------|
114
- | 479, 531 | `process.env.TMUX` | Container environment handling | MEDIUM |
115
- | 505-509 | `process.platform === 'darwin'` / `'linux'` | `this.container.feature('os').platform` | LOW |
116
- | 543-544 | `process.argv` direct access | Container equivalent | LOW |
117
- | 546 | `import('child_process')` then `execSync(...)` | `this.container.feature('proc')` | HIGH |
118
-
119
- #### `process-manager.ts`
120
-
121
- | Line(s) | Violation | Should Use | Severity |
122
- |---------|-----------|-----------|----------|
123
- | 104 | `ReturnType<typeof Bun.spawn>` — type depends on Bun | Runtime-agnostic type or guard | HIGH |
124
- | 164 | `Bun.spawn(...)` — unguarded | `this.container.feature('proc')` or guard with `isBun` | HIGH |
125
- | 166 | `{ ...process.env, ...spawnOptions.env }` | Container environment | MEDIUM |
126
- | 508-530 | `process.on('exit')`, `process.on('SIGINT')`, etc. | Container lifecycle hooks | MEDIUM |
127
-
128
- #### `google-auth.ts`
129
-
130
- | Line(s) | Violation | Should Use | Severity |
131
- |---------|-----------|-----------|----------|
132
- | 108, 115, 123, 147, 486 | `process.env.GOOGLE_*` (5 occurrences) | Container env handling (already declares `static envVars`) | MEDIUM |
133
- | 247 | `Bun.serve({...})` — unguarded | Guard with `isBun` or use container server | HIGH |
134
-
135
- #### `repl.ts`
136
-
137
- | Line(s) | Violation | Should Use | Severity |
138
- |---------|-----------|-----------|----------|
139
- | 92 | `fs.readFileSync(...)` — **`fs` is NOT imported** | `this.container.fs.readFileSync()` | **HIGH (BUG)** |
140
- | 172 | `Bun.inspect(result, ...)` — unguarded | Guard with `this.container.isBun` | MEDIUM |
141
- | 192 | `fs.appendFileSync(...)` — **`fs` is NOT imported** | `this.container.fs` | **HIGH (BUG)** |
142
-
143
- #### `tts.ts`
144
-
145
- | Line(s) | Violation | Should Use | Severity |
146
- |---------|-----------|-----------|----------|
147
- | 67 | `process.env.RUNPOD_API_KEY` | Container env handling | MEDIUM |
148
-
149
- Otherwise clean — correctly uses `this.container.paths`, `this.container.fs`, `this.container.feature('os')`.
150
-
151
- #### `downloader.ts`
152
-
153
- | Line(s) | Violation | Should Use | Severity |
154
- |---------|-----------|-----------|----------|
155
- | 3 | `import fetch from 'cross-fetch'` | Global `fetch` (available in Bun and Node 18+) or container REST client | HIGH |
156
-
157
- Otherwise clean — correctly uses `this.container.fs.writeFileAsync` and `this.container.paths.resolve`.
158
-
159
- #### `runpod.ts`
160
-
161
- | Line(s) | Violation | Should Use | Severity |
162
- |---------|-----------|-----------|----------|
163
- | 4 | `import axios from 'axios'` | `this.container.client('rest')` or global `fetch` | HIGH |
164
- | 46 | `process.env.RUNPOD_API_KEY` | Container env handling | MEDIUM |
165
-
166
- #### `telegram.ts`
167
-
168
- | Line(s) | Violation | Should Use | Severity |
169
- |---------|-----------|-----------|----------|
170
- | 97 | `process.env.TELEGRAM_BOT_TOKEN` | Container env handling | MEDIUM |
171
-
172
- Otherwise clean — properly uses `container.server('express')` and `container.feature('ui')`.
173
-
174
- ---
175
-
176
- ### AGI Features (`src/agi/features/`)
177
-
178
- #### `skills-library.ts`
179
-
180
- | Line(s) | Violation | Should Use | Severity |
181
- |---------|-----------|-----------|----------|
182
- | 2 | `import path from 'path'` | `this.container.paths` | HIGH |
183
- | 3 | `import os from 'os'` | `this.container.paths.homedir` or container os feature | HIGH |
184
- | 4 | `import fs from 'fs/promises'` | `this.container.fs` | HIGH |
185
- | 131 | `path.resolve(os.homedir(), '.luca', 'skills')` | `this.container.paths.resolve(...)` | HIGH |
186
- | 250 | `await fs.mkdir(..., { recursive: true })` | `this.container.fs.mkdirp()` or `ensureFolder()` | HIGH |
187
- | 330-331 | `path.resolve(...)` in `remove()` | `this.container.paths.resolve()` | HIGH |
188
- | 335 | `await fs.rm(skillDir, { recursive: true })` | `this.container.fs.rm()` | HIGH |
189
-
190
- Worst AGI offender — three Node builtin imports at top level used throughout.
191
-
192
- #### `claude-code.ts`
193
-
194
- | Line(s) | Violation | Should Use | Severity |
195
- |---------|-----------|-----------|----------|
196
- | 7 | `import type { Subprocess } from 'bun'` | Runtime-agnostic type | MEDIUM |
197
- | 275 | `Bun.spawn([this.claudePath, '--version'], ...)` | Container `proc` feature | HIGH |
198
- | 334 | `const { appendFile } = await import('node:fs/promises')` | `this.container.fs` | HIGH |
199
- | 382 | `process.env.TMPDIR` | Container temp dir handling | MEDIUM |
200
- | 384 | `await Bun.write(tmpPath, ...)` | Container fs write | HIGH |
201
- | 619-624 | `Bun.spawn(...)` + `{ ...process.env }` | Container `proc` + env | HIGH |
202
- | 750-755 | `Bun.spawn(...)` + `{ ...process.env }` | Same | HIGH |
203
- | 920 | `const { unlink } = await import('node:fs/promises')` | `this.container.fs` | HIGH |
204
-
205
- Completely Bun-locked. Three `Bun.spawn` call sites, one `Bun.write`, two dynamic `fs/promises` imports.
206
-
207
- #### `openai-codex.ts`
208
-
209
- | Line(s) | Violation | Should Use | Severity |
210
- |---------|-----------|-----------|----------|
211
- | 7 | `import type { Subprocess } from 'bun'` | Runtime-agnostic type | MEDIUM |
212
- | 174 | `Bun.spawn([this.codexPath, '--version'], ...)` | Container `proc` feature | HIGH |
213
- | 375-379 | `Bun.spawn(...)` + `{ ...process.env }` | Container `proc` + env | HIGH |
214
- | 502-507 | `Bun.spawn(...)` + `{ ...process.env }` | Same | HIGH |
215
-
216
- Same pattern as `claude-code.ts`.
217
-
218
- #### Clean AGI Files
219
-
220
- - `conversation.ts` — Clean
221
- - `conversation-history.ts` — Clean (uses `container.feature('diskCache')`)
222
- - `assistant.ts` — Exemplary (uses `container.fs`, `container.paths`, `container.feature('vm')`)
223
- - `assistants-manager.ts` — Clean (uses `container.paths`, `container.fs`)
224
- - `docs-reader.ts` — Clean
225
- - `openapi.ts` — Clean (uses global `fetch`)
226
- - `heartbeat.ts` — Clean (uses `container.feature('proc')`, `container.feature('vm')`, etc.)
227
-
228
- ---
229
-
230
- ### Clients (`src/clients/`, `src/web/clients/`)
231
-
232
- #### `comfyui/index.ts`
233
-
234
- | Line(s) | Violation | Should Use | Severity |
235
- |---------|-----------|-----------|----------|
236
- | 531 | `const { mkdir } = await import("fs/promises")` | `this.container.fs.ensureFolder()` | HIGH |
237
- | 532 | `const { join } = await import("path")` | `this.container.paths.join()` | MEDIUM |
238
- | 533 | `await mkdir(options.outputDir, { recursive: true })` | `this.container.fs.ensureFolder()` | HIGH |
239
- | 537 | `const localPath = join(...)` | `this.container.paths.join()` | MEDIUM |
240
- | 538 | `await Bun.write(localPath, buf)` — unguarded | `this.container.fs.writeFileAsync()` | HIGH |
241
-
242
- All 5 violations are in the `runWorkflow` method (lines 530-541).
243
-
244
- #### `openai/index.ts`
245
-
246
- | Line(s) | Violation | Should Use | Severity |
247
- |---------|-----------|-----------|----------|
248
- | 76 | `process.env.OPENAI_API_KEY` | Container env (already declares `static envVars`) | MEDIUM |
249
-
250
- #### `elevenlabs/index.ts`
251
-
252
- | Line(s) | Violation | Should Use | Severity |
253
- |---------|-----------|-----------|----------|
254
- | 100 | `process.env.ELEVENLABS_API_KEY` | Container env (already declares `static envVars`) | MEDIUM |
255
-
256
- #### Clean Client Files
257
-
258
- - `civitai/index.ts` — Exemplary (uses `container.fs`, `container.paths`, `container.feature("downloader")`)
259
- - `supabase/index.ts` — Clean
260
- - `client-template.ts` — Clean
261
- - `web/clients/socket.ts` — Clean
262
-
263
- ---
264
-
265
- ### Servers (`src/servers/`)
266
-
267
- #### `express.ts`
268
-
269
- | Line(s) | Violation | Should Use | Severity |
270
- |---------|-----------|-----------|----------|
271
- | 123 | `new Bun.Glob('**/*.ts')` — unguarded | Container glob/fs.walk or guard with `isBun` | HIGH |
272
-
273
- The entire `useEndpoints` method (lines 123-146) depends on `Bun.Glob`.
274
-
275
- #### `mcp.ts`
276
-
277
- | Line(s) | Violation | Should Use | Severity |
278
- |---------|-----------|-----------|----------|
279
- | 440 | `const { randomUUID } = await import('node:crypto')` | `crypto.randomUUID()` (global Web Crypto) or `this.container.utils.uuid()` | MEDIUM |
280
-
281
- #### Clean Server Files
282
-
283
- - `socket.ts` — Clean
284
-
285
- ---
286
-
287
- ### Commands (`src/commands/`)
288
-
289
- #### `prompt.ts`
290
-
291
- | Line(s) | Violation | Should Use | Severity |
292
- |---------|-----------|-----------|----------|
293
- | 250 | `await Bun.write(resolvedPath, updated)` — unguarded | `container.fs.writeFileAsync()` (already destructures `fs` on line 205!) | HIGH |
294
- | 256 | `await Bun.write(outPath, markdown)` — unguarded | `container.fs.writeFileAsync()` | HIGH |
295
- | 85, 88, 118, 147-179 | Extensive `process.stdout.write()` for streaming | Container `ui` feature | MEDIUM |
296
-
297
- Inconsistent — lines 205+ use `container.fs` properly, but lines 250/256 use `Bun.write`.
298
-
299
- #### `chat.ts`
300
-
301
- | Line(s) | Violation | Should Use | Severity |
302
- |---------|-----------|-----------|----------|
303
- | 2 | `import * as readline from 'readline'` | Container `repl` feature or `ui.askQuestion()` | HIGH |
304
- | 94-97 | `readline.createInterface({ input: process.stdin, output: process.stdout })` | Container input abstraction | HIGH |
305
- | 68-69, 76, 81, 86, 90 | `process.stdout.write(...)` | Container `ui` feature | MEDIUM |
306
-
307
- The `console` command demonstrates the correct pattern using `container.feature('repl')`.
308
-
309
- #### `console.ts`
310
-
311
- | Line(s) | Violation | Should Use | Severity |
312
- |---------|-----------|-----------|----------|
313
- | 67 | `Bun` injected into REPL context without guard | Guard with `container.isBun` | MEDIUM |
314
-
315
- #### `sandbox-mcp.ts`
316
-
317
- | Line(s) | Violation | Should Use | Severity |
318
- |---------|-----------|-----------|----------|
319
- | 42 | `Bun` injected into VM context without guard | Guard with `container.isBun` | MEDIUM |
320
-
321
- #### `run.ts`
322
-
323
- | Line(s) | Violation | Should Use | Severity |
324
- |---------|-----------|-----------|----------|
325
- | 218 | `Bun` injected into REPL context without guard | Guard with `container.isBun` | MEDIUM |
326
-
327
- #### Clean Command Files
328
-
329
- - `eval.ts` — Clean
330
- - `describe.ts` — Exemplary
331
- - `help.ts` — Clean
332
- - `index.ts` — Clean (barrel file)
333
- - `serve.ts` — Clean (uses container throughout)
334
- - `mcp.ts` — Clean
335
-
336
- ---
337
-
338
- ## Recurring Patterns
339
-
340
- ### 1. `process.env` for API Keys (MEDIUM, 10+ files)
341
- Many features/clients declare `static envVars = [...]` but then read `process.env.KEY` directly in getters. The container should mediate this. Affected: `google-auth.ts`, `tts.ts`, `runpod.ts`, `telegram.ts`, `openai/index.ts`, `elevenlabs/index.ts`.
342
-
343
- ### 2. Unguarded `Bun.spawn` (HIGH, 5 files)
344
- `claude-code.ts`, `openai-codex.ts`, `process-manager.ts` all call `Bun.spawn` without checking `container.isBun`. These need either proc feature usage or runtime guards.
345
-
346
- ### 3. Unguarded `Bun.write` (HIGH, 3 files)
347
- `prompt.ts`, `claude-code.ts`, `comfyui/index.ts` use `Bun.write` when `container.fs.writeFileAsync()` is available.
348
-
349
- ### 4. Module-level `homedir()`/`join()` (HIGH, 2 files)
350
- `launcher-app-command-listener.ts` and `window-manager.ts` compute paths at import time using `os.homedir()` and `path.join()`, before any container exists. These must be deferred to `enable()` or computed lazily.
351
-
352
- ### 5. Third-party HTTP imports (HIGH, 2 files)
353
- `downloader.ts` imports `cross-fetch` and `runpod.ts` imports `axios` when global `fetch` and `container.client('rest')` are available.
354
-
355
- ### 6. Unguarded `Bun.Glob` (HIGH, 2 files)
356
- `helpers.ts` and `express.ts` use `Bun.Glob` without runtime guards. The container's fs feature provides glob/walk capabilities.
357
-
358
- ---
359
-
360
- ## Recommended Fix Priority
361
-
362
- ### Priority 1 — Bugs
363
- - [ ] `repl.ts` — Fix missing `fs` import (lines 92, 192). Use `this.container.fs` instead.
364
-
365
- ### Priority 2 — Bun-only code (breaks Node compatibility)
366
- - [ ] `claude-code.ts` — Replace `Bun.spawn` with proc feature or add `isBun` guards
367
- - [ ] `openai-codex.ts` — Same pattern as claude-code
368
- - [ ] `process-manager.ts` — Replace `Bun.spawn` with proc feature or guard
369
- - [ ] `google-auth.ts` — Replace `Bun.serve` with guard or container server
370
- - [ ] `express.ts` — Replace `Bun.Glob` with container glob or guard
371
- - [ ] `helpers.ts` — Replace `Bun.Glob` with container glob or guard
372
- - [ ] `prompt.ts` — Replace `Bun.write` with `container.fs.writeFileAsync`
373
- - [ ] `comfyui/index.ts` — Replace `Bun.write` + dynamic imports with container equivalents
374
-
375
- ### Priority 3 — Direct builtin imports (bypasses container)
376
- - [ ] `skills-library.ts` — Replace all `path`, `os`, `fs/promises` with container equivalents
377
- - [ ] `package-finder.ts` — Complete migration to container fs/paths
378
- - [ ] `python.ts` — Replace `existsSync`/`join` with container equivalents
379
- - [ ] `launcher-app-command-listener.ts` — Replace fs/path/os with container, defer module-level computations
380
- - [ ] `window-manager.ts` — Same as launcher-app
381
- - [ ] `downloader.ts` — Remove `cross-fetch`, use global `fetch`
382
- - [ ] `runpod.ts` — Remove `axios`, use global `fetch` or container REST client
383
- - [ ] `chat.ts` — Replace `readline` with container `repl` feature
384
-
385
- ### Priority 4 — `process.env` standardization
386
- - [ ] Create or document a standard pattern for env var access across features/clients
387
- - [ ] Update `google-auth.ts`, `tts.ts`, `runpod.ts`, `telegram.ts`, `openai/index.ts`, `elevenlabs/index.ts`
388
-
389
- ### Priority 5 — Minor guards
390
- - [ ] `console.ts`, `sandbox-mcp.ts`, `run.ts` — Guard `Bun` global injection with `container.isBun`
391
- - [ ] `tmux.ts` — Replace `execSync` dynamic import with `container.feature('proc')`
@@ -1,170 +0,0 @@
1
- ---
2
- tags:
3
- - console
4
- - hmr
5
- - repl
6
- - design
7
- ---
8
- # Console HMR Design Research
9
-
10
- Add a `--hmr` flag to `luca console` that watches source files and hot-swaps feature instances in the live REPL session, preserving state where possible.
11
-
12
- ## How the Console Works Today
13
-
14
- The `luca console` command (`src/commands/console.ts`) creates a REPL that:
15
-
16
- 1. Calls `container.helpers.discoverAll()` to load all features, commands, endpoints
17
- 2. Snapshots every available feature into a `featureContext` object via `container.feature(name)` for each name
18
- 3. Optionally loads a `luca.console.ts` project module and merges its exports
19
- 4. Optionally runs `--eval` code/script/markdown before the REPL starts
20
- 5. Creates a `Repl` feature instance with a `vm.Context` built from the snapshot
21
- 6. Enters a hand-rolled readline loop (`repl.ts`) that evaluates expressions in that VM context
22
-
23
- The REPL's `_vmContext` is a **mutable plain object** — variables can be reassigned at runtime (`ctx.featureName = newInstance`). Tab completion reads from `Object.keys(ctx)` dynamically, so new/replaced bindings are immediately visible.
24
-
25
- ## Architectural Facts Relevant to HMR
26
-
27
- ### helperCache is module-private
28
-
29
- `container.ts:618` — `const helperCache = new Map()`. There is no public API to evict or replace a cached feature instance. Calling `container.feature('fs')` with the same options always returns the same object. Any HMR implementation needs either:
30
- - A new `container.evictHelper(cacheKey)` method
31
- - A bypass that creates instances outside the cache
32
-
33
- ### attachToContainer uses configurable: true
34
-
35
- `feature.ts` — `Object.defineProperty(this.container, shortcutName, { get: () => this, configurable: true })`. The property descriptor **can** be redefined, which is the only existing affordance for swapping a feature on the container object.
36
-
37
- ### vm.loadModule() always reads fresh from disk
38
-
39
- `vm.ts` — reads file content via `container.fs.readFile()`, transpiles with esbuild `transformSync`, runs in a `vm.Script` context. No module cache to bust. But this runs in a CJS-like VM sandbox — real ES `import` statements inside the file won't work.
40
-
41
- ### Bun import() cache busting
42
-
43
- The only cache-busting pattern in the codebase is `Endpoint.reload()` (`endpoint.ts:176`): `import(\`${path}?t=${Date.now()}\`)`. This works for Bun's native module loader and preserves real ES module semantics.
44
-
45
- ### State has no serialize/deserialize
46
-
47
- `State` (`state.ts`) is an in-memory observable key-value bag with `set()`, `setState()`, `clear()`, and observer callbacks. There is no `toJSON()`/`fromSnapshot()` API. Transferring state between instances requires manually reading `state.current` from old and calling `state.setState()` on new.
48
-
49
- ### FileManager has chokidar file watching
50
-
51
- `file-manager.ts` — `fileManager.watch()` uses chokidar and emits `"file:change"` events with `{ type, path }`. Currently not wired to anything automatically. This is the primitive we'd compose for watching source files.
52
-
53
- ### Feature self-registration is a static side effect
54
-
55
- Features register via `static { Feature.register(this, 'name') }` which stores the **class constructor** in a module-level `FeaturesRegistry` Map. Re-importing a module would call `register()` again with a new constructor — the registry would need to handle overwrites.
56
-
57
- ## The HMR Flow (Conceptual)
58
-
59
- ```
60
- [file change detected]
61
- → identify which feature(s) the file maps to
62
- → re-import the module (cache-busted)
63
- → new class constructor registers over old one
64
- → snapshot old instance state: state.current + any serializable instance data
65
- → evict old instance from helperCache
66
- → create new instance via container.feature(name)
67
- → transfer state: newInstance.state.setState(oldState)
68
- → patch REPL vm context: ctx[featureName] = newInstance
69
- → re-define container shortcut property to point to new instance
70
- → print "[HMR] Reloaded: featureName" in REPL
71
- ```
72
-
73
- ## Open Design Questions
74
-
75
- ### 1. Scope — which files trigger a reload?
76
-
77
- - Just feature source files (`src/node/features/*.ts`)?
78
- - Also command files, `luca.console.ts`, endpoint files?
79
- - Or anything under `src/`?
80
-
81
- Recommendation: Start with feature files only. Commands and endpoints are less stateful and easier to add later.
82
-
83
- ### 2. State transfer strategy
84
-
85
- - **Best-effort `state.current` transfer**: Read `oldInstance.state.current`, call `newInstance.state.setState(snapshot)`. Simple, covers most cases.
86
- - **Opt-in hooks**: Features declare `serialize()` / `deserialize()` methods for fine-grained control (e.g., FileManager could note which directories it was watching but not try to transfer the chokidar FSWatcher handle).
87
- - **Hybrid**: Always transfer `state.current`, and if the feature has a `hmrSerialize()` hook, use that for additional instance data.
88
-
89
- Non-state instance data (open file handles, chokidar watchers, readline interfaces, cached esbuild services) cannot be naively transferred. Features with complex resources would need explicit HMR support or accept that those resources restart fresh.
90
-
91
- ### 3. Module re-import strategy
92
-
93
- Two options:
94
-
95
- **Option A — Bun `import()` with `?t=` cache busting**: Real ES module semantics, `import` statements inside the feature file work. The new module's `static {}` block re-registers the class. This is what `Endpoint.reload()` already does.
96
-
97
- **Option B — `vm.loadModule()`**: Always reads fresh from disk, no cache issues. But runs in a CJS sandbox — internal `import` statements won't resolve. Feature files heavily use `import`, so this likely won't work.
98
-
99
- Recommendation: Option A. It's proven in the codebase and preserves full module semantics.
100
-
101
- ### 4. Registry re-registration
102
-
103
- `Feature.register()` currently does `features.register(id, SubClass)` which calls `registry.members.set(id, SubClass)`. A re-import with a new class constructor would overwrite the old entry. This actually works — `Map.set` overwrites silently. But we should verify there are no side effects in `interceptRegistration` hooks or other registration logic that would break.
104
-
105
- ### 5. REPL context patching — automatic vs explicit
106
-
107
- - **Automatic**: FileManager watches, detects change, swaps feature, patches `ctx[name]`, prints HMR message. User sees updated behavior on next expression.
108
- - **Explicit**: User types `hmr.reload('fs')` or similar in the REPL to trigger a reload manually.
109
- - **Both**: Auto-reload on file change, plus a manual `hmr.reload('name')` for forcing reloads or reloading things that aren't file-backed.
110
-
111
- Recommendation: Both. Auto is the main UX, manual is the escape hatch.
112
-
113
- ### 6. Feedback in the REPL
114
-
115
- Print a colored message when a feature reloads:
116
- ```
117
- [HMR] Reloaded: fs (state transferred)
118
- [HMR] Reloaded: diskCache (fresh — no prior state)
119
- [HMR] Error reloading vm: SyntaxError: Unexpected token (kept old instance)
120
- ```
121
-
122
- This should be non-intrusive — printed above the prompt line if possible.
123
-
124
- ### 7. Failure mode
125
-
126
- If the new code has a syntax error or the constructor throws:
127
- - Keep the old instance alive
128
- - Print the error in the REPL
129
- - Do not crash the session
130
-
131
- This is the only sane approach for a dev tool.
132
-
133
- ## Implementation Sketch
134
-
135
- ### New infrastructure needed
136
-
137
- 1. **`container.evictHelper(type, id, options?)`** — public method on Container that deletes from `helperCache` and cleans up `featureIdToHelperCacheKeyMap` and `contextMap`
138
- 2. **`Feature.prototype.hmrSerialize?()` / `hmrDeserialize?(data)`** — optional hooks for features that need custom state transfer beyond `state.current`
139
- 3. **`registry.register()` handling overwrites** — verify this works cleanly, add a `"re-registered"` event if useful
140
- 4. **File-to-feature mapping** — a way to know that `src/node/features/disk-cache.ts` corresponds to the `diskCache` feature ID
141
-
142
- ### Changes to existing code
143
-
144
- 1. **`src/commands/console.ts`** — add `--hmr` flag to `argsSchema`, wire up file watching and the reload loop when enabled
145
- 2. **`src/container.ts`** — expose `evictHelper()` (or a more targeted `replaceFeature()`)
146
- 3. **`src/node/features/repl.ts`** — expose a method to patch the VM context (or just expose `_vmContext` which is already accessible)
147
-
148
- ### Rough dependency graph
149
-
150
- ```
151
- argsSchema adds --hmr flag
152
- → console handler checks for --hmr
153
- → starts FileManager.watch() on src/ directory
154
- → subscribes to "file:change" events
155
- → on change: resolveFeatureFromPath(changedFile)
156
- → cache-bust import the module
157
- → container.evictHelper('feature', featureId)
158
- → newInstance = container.feature(featureId, { enable: wasEnabled })
159
- → transfer state from old → new
160
- → patch repl._vmContext[featureId] = newInstance
161
- → print HMR message
162
- ```
163
-
164
- ## Risks and Unknowns
165
-
166
- - **Circular dependency during re-import**: If feature A imports feature B at module level, and both are being reloaded, the order matters. May need to batch reloads or do a dependency-aware reload order.
167
- - **Event listener cleanup**: Old feature instances may have registered listeners on the container event bus. Need to remove those or they'll fire on stale instances.
168
- - **Observer cleanup**: State observers from the old instance need to be unsubscribed or they'll leak.
169
- - **Features that modify globals**: Some features might set up global state (process event handlers, etc.) that won't be cleaned up by replacing the instance.
170
- - **The `?t=` trick and TypeScript**: Bun handles `import('./foo.ts?t=123')` but we should verify this works for all feature files, especially those with complex re-exports.
@@ -1,72 +0,0 @@
1
- ---
2
- tags:
3
- - feature-design
4
- - semantic-search
5
- - introspection
6
- status: draft
7
- ---
8
- # Helper Semantic Search Feature
9
-
10
- Build semantic search over all describable luca helpers so AI assistants can find the right feature/client/server by describing what they need.
11
-
12
- ## Motivation
13
-
14
- The `luca describe` system produces rich markdown for every helper (features, clients, servers, commands, endpoints, selectors). An AI assistant working with luca currently has to know the exact name of a helper to look it up. Semantic search would let it say "I need to run shell commands" and find `proc`, or "I need to cache things to disk" and find `diskCache`.
15
-
16
- Storage location: `~/.luca/embeddings/`
17
-
18
- ## What Already Exists
19
-
20
- Two systems that combine perfectly:
21
-
22
- 1. **SemanticSearch feature** (`src/node/features/semantic-search.ts`) — SQLite-backed embedding engine with OpenAI/local GGUF providers, section-based chunking, BM25 + vector + hybrid search with RRF fusion.
23
-
24
- 2. **Introspection system** (`src/introspection/`) — Every helper produces structured `HelperIntrospection` JSON and rendered markdown via `Helper.introspectAsText()`. Build-time AST scanning (JSDoc) + runtime Zod schema reflection. The `__INTROSPECTION__` map holds everything.
25
-
26
- ## Proposed Design
27
-
28
- ### Approach
29
-
30
- Reuse the existing `SemanticSearch` feature directly. Create a new feature (e.g. `helperSearch`) that:
31
-
32
- 1. Iterates all registries, calls `introspectAsText()` on each helper to get markdown
33
- 2. Structures the markdown as `DocumentInput` objects with sections (methods, getters, events, state, options)
34
- 3. Feeds them to SemanticSearch for embedding and indexing
35
- 4. Exposes a `search(query)` method that delegates to hybridSearch
36
-
37
- ### Storage
38
-
39
- DB stored at `~/.luca/embeddings/helpers.<provider>-<model>.sqlite`, scoped by provider+model like contentbase does.
40
-
41
- ### When to Build Index
42
-
43
- - Lazy on first search if no index exists or if stale
44
- - Explicit rebuild via `luca search --rebuild`
45
- - Content hash gating from SemanticSearch handles incremental updates automatically
46
-
47
- ### CLI Surface
48
-
49
- `luca search "file operations"` — returns ranked helpers with snippets showing why they matched.
50
-
51
- ### MCP / AI Assistant Surface
52
-
53
- Expose as a tool in the luca-sandbox MCP so AI assistants can search for helpers by describing what they need.
54
-
55
- ## Open Questions
56
-
57
- 1. **Scope** — Index just core luca helpers, or also project-level commands/endpoints/selectors discovered at runtime? Suggestion: core always, project-level optionally.
58
-
59
- 2. **Granularity** — One document per helper (full describe output) vs chunked by section (methods, events, state). Suggestion: chunk by section so "run shell commands" matches `proc.exec` specifically.
60
-
61
- 3. **Primary consumer** — MCP tool for AI assistants, CLI for humans, or both? Suggestion: both.
62
-
63
- 4. **Embedding provider default** — OpenAI (higher quality, needs API key) or local GGUF (works offline, lower quality)? Suggestion: OpenAI default with local fallback.
64
-
65
- ## Key Files
66
-
67
- - `src/node/features/semantic-search.ts` — The embedding engine to reuse
68
- - `src/introspection/index.ts` — `__INTROSPECTION__` map, `HelperIntrospection` type
69
- - `src/helper.ts` — `Helper.introspect()`, `Helper.introspectAsText()`, markdown renderers
70
- - `src/registry.ts` — Registry base class, `describe()`, `describeAll()`
71
- - `src/commands/describe.ts` — Current describe command (target resolution, rendering)
72
- - `src/node/features/content-db.ts` — Reference for how contentDb wraps SemanticSearch