@soederpop/luca 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/CLAUDE.md +71 -0
  2. package/README.md +78 -0
  3. package/bun.lock +2928 -0
  4. package/bunfig.toml +3 -0
  5. package/commands/audit-docs.ts +740 -0
  6. package/commands/build-scaffolds.ts +154 -0
  7. package/commands/generate-api-docs.ts +114 -0
  8. package/commands/update-introspection.ts +67 -0
  9. package/docs/CLI.md +335 -0
  10. package/docs/README.md +88 -0
  11. package/docs/TABLE-OF-CONTENTS.md +157 -0
  12. package/docs/apis/clients/elevenlabs.md +84 -0
  13. package/docs/apis/clients/graph.md +56 -0
  14. package/docs/apis/clients/openai.md +69 -0
  15. package/docs/apis/clients/rest.md +41 -0
  16. package/docs/apis/clients/websocket.md +107 -0
  17. package/docs/apis/features/agi/assistant.md +471 -0
  18. package/docs/apis/features/agi/assistants-manager.md +154 -0
  19. package/docs/apis/features/agi/claude-code.md +602 -0
  20. package/docs/apis/features/agi/conversation-history.md +352 -0
  21. package/docs/apis/features/agi/conversation.md +333 -0
  22. package/docs/apis/features/agi/docs-reader.md +121 -0
  23. package/docs/apis/features/agi/openai-codex.md +318 -0
  24. package/docs/apis/features/agi/openapi.md +138 -0
  25. package/docs/apis/features/agi/semantic-search.md +387 -0
  26. package/docs/apis/features/agi/skills-library.md +216 -0
  27. package/docs/apis/features/node/container-link.md +133 -0
  28. package/docs/apis/features/node/content-db.md +313 -0
  29. package/docs/apis/features/node/disk-cache.md +379 -0
  30. package/docs/apis/features/node/dns.md +651 -0
  31. package/docs/apis/features/node/docker.md +705 -0
  32. package/docs/apis/features/node/downloader.md +81 -0
  33. package/docs/apis/features/node/esbuild.md +59 -0
  34. package/docs/apis/features/node/file-manager.md +182 -0
  35. package/docs/apis/features/node/fs.md +581 -0
  36. package/docs/apis/features/node/git.md +330 -0
  37. package/docs/apis/features/node/google-auth.md +174 -0
  38. package/docs/apis/features/node/google-calendar.md +187 -0
  39. package/docs/apis/features/node/google-docs.md +151 -0
  40. package/docs/apis/features/node/google-drive.md +225 -0
  41. package/docs/apis/features/node/google-sheets.md +179 -0
  42. package/docs/apis/features/node/grep.md +290 -0
  43. package/docs/apis/features/node/helpers.md +135 -0
  44. package/docs/apis/features/node/ink.md +334 -0
  45. package/docs/apis/features/node/ipc-socket.md +260 -0
  46. package/docs/apis/features/node/json-tree.md +86 -0
  47. package/docs/apis/features/node/launcher-app-command-listener.md +145 -0
  48. package/docs/apis/features/node/networking.md +281 -0
  49. package/docs/apis/features/node/nlp.md +133 -0
  50. package/docs/apis/features/node/opener.md +97 -0
  51. package/docs/apis/features/node/os.md +118 -0
  52. package/docs/apis/features/node/package-finder.md +402 -0
  53. package/docs/apis/features/node/postgres.md +212 -0
  54. package/docs/apis/features/node/proc.md +430 -0
  55. package/docs/apis/features/node/process-manager.md +210 -0
  56. package/docs/apis/features/node/python.md +278 -0
  57. package/docs/apis/features/node/repl.md +88 -0
  58. package/docs/apis/features/node/runpod.md +673 -0
  59. package/docs/apis/features/node/secure-shell.md +169 -0
  60. package/docs/apis/features/node/semantic-search.md +401 -0
  61. package/docs/apis/features/node/sqlite.md +211 -0
  62. package/docs/apis/features/node/telegram.md +254 -0
  63. package/docs/apis/features/node/tts.md +118 -0
  64. package/docs/apis/features/node/ui.md +703 -0
  65. package/docs/apis/features/node/vault.md +64 -0
  66. package/docs/apis/features/node/vm.md +84 -0
  67. package/docs/apis/features/node/window-manager.md +337 -0
  68. package/docs/apis/features/node/yaml-tree.md +85 -0
  69. package/docs/apis/features/node/yaml.md +176 -0
  70. package/docs/apis/features/web/asset-loader.md +47 -0
  71. package/docs/apis/features/web/container-link.md +133 -0
  72. package/docs/apis/features/web/esbuild.md +59 -0
  73. package/docs/apis/features/web/helpers.md +135 -0
  74. package/docs/apis/features/web/network.md +30 -0
  75. package/docs/apis/features/web/speech.md +55 -0
  76. package/docs/apis/features/web/vault.md +64 -0
  77. package/docs/apis/features/web/vm.md +84 -0
  78. package/docs/apis/features/web/voice.md +67 -0
  79. package/docs/apis/servers/express.md +127 -0
  80. package/docs/apis/servers/mcp.md +213 -0
  81. package/docs/apis/servers/websocket.md +99 -0
  82. package/docs/documentation-audit.md +134 -0
  83. package/docs/examples/content-db.md +77 -0
  84. package/docs/examples/disk-cache.md +83 -0
  85. package/docs/examples/docker.md +101 -0
  86. package/docs/examples/downloader.md +70 -0
  87. package/docs/examples/esbuild.md +80 -0
  88. package/docs/examples/file-manager.md +82 -0
  89. package/docs/examples/fs.md +83 -0
  90. package/docs/examples/git.md +85 -0
  91. package/docs/examples/google-auth.md +88 -0
  92. package/docs/examples/google-calendar.md +94 -0
  93. package/docs/examples/google-docs.md +82 -0
  94. package/docs/examples/google-drive.md +96 -0
  95. package/docs/examples/google-sheets.md +95 -0
  96. package/docs/examples/grep.md +85 -0
  97. package/docs/examples/ink-blocks.md +75 -0
  98. package/docs/examples/ink-renderer.md +41 -0
  99. package/docs/examples/ink.md +103 -0
  100. package/docs/examples/ipc-socket.md +103 -0
  101. package/docs/examples/json-tree.md +91 -0
  102. package/docs/examples/launcher-app-command-listener.md +120 -0
  103. package/docs/examples/networking.md +58 -0
  104. package/docs/examples/nlp.md +91 -0
  105. package/docs/examples/opener.md +78 -0
  106. package/docs/examples/os.md +72 -0
  107. package/docs/examples/package-finder.md +89 -0
  108. package/docs/examples/port-exposer.md +89 -0
  109. package/docs/examples/postgres.md +91 -0
  110. package/docs/examples/proc.md +81 -0
  111. package/docs/examples/process-manager.md +79 -0
  112. package/docs/examples/python.md +91 -0
  113. package/docs/examples/repl.md +93 -0
  114. package/docs/examples/runpod.md +119 -0
  115. package/docs/examples/secure-shell.md +92 -0
  116. package/docs/examples/sqlite.md +86 -0
  117. package/docs/examples/telegram.md +77 -0
  118. package/docs/examples/tts.md +86 -0
  119. package/docs/examples/ui.md +80 -0
  120. package/docs/examples/vault.md +70 -0
  121. package/docs/examples/vm.md +86 -0
  122. package/docs/examples/window-manager.md +125 -0
  123. package/docs/examples/yaml-tree.md +93 -0
  124. package/docs/examples/yaml.md +104 -0
  125. package/docs/ideas/class-registration-refactor-possibilities.md +197 -0
  126. package/docs/ideas/container-use-api.md +9 -0
  127. package/docs/ideas/easy-auth-for-express-servers-and-luca-serve.md +0 -0
  128. package/docs/ideas/feature-stacks.md +22 -0
  129. package/docs/ideas/luca-cli-self-sufficiency-demo.md +23 -0
  130. package/docs/ideas/mcp-design.md +9 -0
  131. package/docs/ideas/web-container-debugging-feature.md +13 -0
  132. package/docs/introspection-audit.md +49 -0
  133. package/docs/introspection.md +154 -0
  134. package/docs/mcp/readme.md +162 -0
  135. package/docs/models.ts +38 -0
  136. package/docs/philosophy.md +85 -0
  137. package/docs/principles.md +7 -0
  138. package/docs/prompts/audit-codebase-for-failures-to-use-the-container.md +34 -0
  139. package/docs/prompts/mcp-test-easy-command.md +27 -0
  140. package/docs/reports/assistant-bugs.md +38 -0
  141. package/docs/reports/attach-pattern-usage.md +18 -0
  142. package/docs/reports/code-audit-results.md +391 -0
  143. package/docs/reports/introspection-audit-tasks.md +378 -0
  144. package/docs/reports/luca-mcp-improvements.md +128 -0
  145. package/docs/scaffolds/client.md +140 -0
  146. package/docs/scaffolds/command.md +106 -0
  147. package/docs/scaffolds/endpoint.md +176 -0
  148. package/docs/scaffolds/feature.md +148 -0
  149. package/docs/scaffolds/server.md +187 -0
  150. package/docs/tasks/web-container-helper-discovery.md +71 -0
  151. package/docs/todos.md +1 -0
  152. package/docs/tutorials/01-getting-started.md +106 -0
  153. package/docs/tutorials/02-container.md +210 -0
  154. package/docs/tutorials/03-scripts.md +194 -0
  155. package/docs/tutorials/04-features-overview.md +196 -0
  156. package/docs/tutorials/05-state-and-events.md +171 -0
  157. package/docs/tutorials/06-servers.md +157 -0
  158. package/docs/tutorials/07-endpoints.md +198 -0
  159. package/docs/tutorials/08-commands.md +171 -0
  160. package/docs/tutorials/09-clients.md +162 -0
  161. package/docs/tutorials/10-creating-features.md +198 -0
  162. package/docs/tutorials/11-contentbase.md +191 -0
  163. package/docs/tutorials/12-assistants.md +215 -0
  164. package/docs/tutorials/13-introspection.md +147 -0
  165. package/docs/tutorials/14-type-system.md +174 -0
  166. package/docs/tutorials/15-project-patterns.md +222 -0
  167. package/docs/tutorials/16-google-features.md +534 -0
  168. package/docs/tutorials/17-tui-blocks.md +530 -0
  169. package/docs/tutorials/18-semantic-search.md +334 -0
  170. package/index.ts +1 -0
  171. package/luca.console.ts +9 -0
  172. package/main.py +6 -0
  173. package/package.json +154 -0
  174. package/pyproject.toml +7 -0
  175. package/scripts/animations/chrome-glitch.ts +55 -0
  176. package/scripts/animations/index.ts +16 -0
  177. package/scripts/animations/neon-pulse.ts +64 -0
  178. package/scripts/animations/types.ts +6 -0
  179. package/scripts/build-web.ts +28 -0
  180. package/scripts/examples/ask-luca-expert.ts +42 -0
  181. package/scripts/examples/assistant-questions.ts +12 -0
  182. package/scripts/examples/excalidraw-expert.ts +75 -0
  183. package/scripts/examples/expert-chat.ts +0 -0
  184. package/scripts/examples/file-manager.ts +14 -0
  185. package/scripts/examples/ideas.ts +12 -0
  186. package/scripts/examples/interactive-chat.ts +20 -0
  187. package/scripts/examples/openai-tool-calls.ts +113 -0
  188. package/scripts/examples/opening-a-web-browser.ts +5 -0
  189. package/scripts/examples/telegram-bot.ts +79 -0
  190. package/scripts/examples/telegram-ink-ui.ts +302 -0
  191. package/scripts/examples/using-assistant-with-mcp.ts +560 -0
  192. package/scripts/examples/using-claude-code.ts +10 -0
  193. package/scripts/examples/using-contentdb.ts +35 -0
  194. package/scripts/examples/using-conversations.ts +35 -0
  195. package/scripts/examples/using-disk-cache.ts +10 -0
  196. package/scripts/examples/using-docker-shell.ts +75 -0
  197. package/scripts/examples/using-elevenlabs.ts +25 -0
  198. package/scripts/examples/using-google-calendar.ts +57 -0
  199. package/scripts/examples/using-google-docs.ts +74 -0
  200. package/scripts/examples/using-google-drive.ts +74 -0
  201. package/scripts/examples/using-google-sheets.ts +89 -0
  202. package/scripts/examples/using-nlp.ts +55 -0
  203. package/scripts/examples/using-ollama.ts +10 -0
  204. package/scripts/examples/using-openai-codex.ts +23 -0
  205. package/scripts/examples/using-postgres.ts +55 -0
  206. package/scripts/examples/using-runpod.ts +32 -0
  207. package/scripts/examples/using-tts.ts +40 -0
  208. package/scripts/examples/vm-loading-esm-modules.ts +16 -0
  209. package/scripts/scaffold.ts +391 -0
  210. package/scripts/scratch.ts +15 -0
  211. package/scripts/test-command-listener.ts +123 -0
  212. package/scripts/test-window-manager-lifecycle.ts +86 -0
  213. package/scripts/test-window-manager.ts +43 -0
  214. package/scripts/update-introspection-data.ts +58 -0
  215. package/src/agi/README.md +14 -0
  216. package/src/agi/container.server.ts +114 -0
  217. package/src/agi/endpoints/ask.ts +60 -0
  218. package/src/agi/endpoints/conversations/[id].ts +45 -0
  219. package/src/agi/endpoints/conversations.ts +31 -0
  220. package/src/agi/endpoints/experts.ts +37 -0
  221. package/src/agi/features/assistant.ts +767 -0
  222. package/src/agi/features/assistants-manager.ts +260 -0
  223. package/src/agi/features/claude-code.ts +1111 -0
  224. package/src/agi/features/conversation-history.ts +497 -0
  225. package/src/agi/features/conversation.ts +799 -0
  226. package/src/agi/features/openai-codex.ts +631 -0
  227. package/src/agi/features/openapi.ts +438 -0
  228. package/src/agi/features/skills-library.ts +425 -0
  229. package/src/agi/index.ts +6 -0
  230. package/src/agi/lib/token-counter.ts +122 -0
  231. package/src/browser.ts +25 -0
  232. package/src/bus.ts +100 -0
  233. package/src/cli/cli.ts +70 -0
  234. package/src/client.ts +461 -0
  235. package/src/clients/civitai/index.ts +541 -0
  236. package/src/clients/client-template.ts +41 -0
  237. package/src/clients/comfyui/index.ts +597 -0
  238. package/src/clients/elevenlabs/index.ts +291 -0
  239. package/src/clients/openai/index.ts +451 -0
  240. package/src/clients/supabase/index.ts +366 -0
  241. package/src/command.ts +164 -0
  242. package/src/commands/chat.ts +182 -0
  243. package/src/commands/console.ts +192 -0
  244. package/src/commands/describe.ts +433 -0
  245. package/src/commands/eval.ts +116 -0
  246. package/src/commands/help.ts +214 -0
  247. package/src/commands/index.ts +14 -0
  248. package/src/commands/mcp.ts +64 -0
  249. package/src/commands/prompt.ts +807 -0
  250. package/src/commands/run.ts +257 -0
  251. package/src/commands/sandbox-mcp.ts +439 -0
  252. package/src/commands/scaffold.ts +79 -0
  253. package/src/commands/serve.ts +172 -0
  254. package/src/container.ts +781 -0
  255. package/src/endpoint.ts +340 -0
  256. package/src/feature.ts +75 -0
  257. package/src/hash-object.ts +97 -0
  258. package/src/helper.ts +543 -0
  259. package/src/introspection/generated.agi.ts +23388 -0
  260. package/src/introspection/generated.node.ts +18899 -0
  261. package/src/introspection/generated.web.ts +2021 -0
  262. package/src/introspection/index.ts +256 -0
  263. package/src/introspection/scan.ts +912 -0
  264. package/src/node/container.ts +354 -0
  265. package/src/node/feature.ts +13 -0
  266. package/src/node/features/container-link.ts +558 -0
  267. package/src/node/features/content-db.ts +475 -0
  268. package/src/node/features/disk-cache.ts +382 -0
  269. package/src/node/features/dns.ts +655 -0
  270. package/src/node/features/docker.ts +912 -0
  271. package/src/node/features/downloader.ts +92 -0
  272. package/src/node/features/esbuild.ts +68 -0
  273. package/src/node/features/file-manager.ts +357 -0
  274. package/src/node/features/fs.ts +534 -0
  275. package/src/node/features/git.ts +492 -0
  276. package/src/node/features/google-auth.ts +502 -0
  277. package/src/node/features/google-calendar.ts +300 -0
  278. package/src/node/features/google-docs.ts +404 -0
  279. package/src/node/features/google-drive.ts +339 -0
  280. package/src/node/features/google-sheets.ts +279 -0
  281. package/src/node/features/grep.ts +406 -0
  282. package/src/node/features/helpers.ts +374 -0
  283. package/src/node/features/ink.ts +490 -0
  284. package/src/node/features/ipc-socket.ts +459 -0
  285. package/src/node/features/json-tree.ts +188 -0
  286. package/src/node/features/launcher-app-command-listener.ts +388 -0
  287. package/src/node/features/networking.ts +925 -0
  288. package/src/node/features/nlp.ts +211 -0
  289. package/src/node/features/opener.ts +166 -0
  290. package/src/node/features/os.ts +157 -0
  291. package/src/node/features/package-finder.ts +539 -0
  292. package/src/node/features/port-exposer.ts +342 -0
  293. package/src/node/features/postgres.ts +273 -0
  294. package/src/node/features/proc.ts +502 -0
  295. package/src/node/features/process-manager.ts +542 -0
  296. package/src/node/features/python.ts +444 -0
  297. package/src/node/features/repl.ts +194 -0
  298. package/src/node/features/runpod.ts +802 -0
  299. package/src/node/features/secure-shell.ts +248 -0
  300. package/src/node/features/semantic-search.ts +924 -0
  301. package/src/node/features/sqlite.ts +289 -0
  302. package/src/node/features/telegram.ts +342 -0
  303. package/src/node/features/tts.ts +184 -0
  304. package/src/node/features/ui.ts +857 -0
  305. package/src/node/features/vault.ts +164 -0
  306. package/src/node/features/vm.ts +312 -0
  307. package/src/node/features/window-manager.ts +804 -0
  308. package/src/node/features/yaml-tree.ts +149 -0
  309. package/src/node/features/yaml.ts +132 -0
  310. package/src/node.ts +70 -0
  311. package/src/react/index.ts +175 -0
  312. package/src/registry.ts +199 -0
  313. package/src/scaffolds/generated.ts +1613 -0
  314. package/src/scaffolds/template.ts +37 -0
  315. package/src/schemas/base.ts +255 -0
  316. package/src/server.ts +135 -0
  317. package/src/servers/express.ts +209 -0
  318. package/src/servers/mcp.ts +805 -0
  319. package/src/servers/socket.ts +120 -0
  320. package/src/state.ts +101 -0
  321. package/src/web/clients/socket.ts +82 -0
  322. package/src/web/container.ts +74 -0
  323. package/src/web/extension.ts +30 -0
  324. package/src/web/feature.ts +12 -0
  325. package/src/web/features/asset-loader.ts +64 -0
  326. package/src/web/features/container-link.ts +385 -0
  327. package/src/web/features/esbuild.ts +79 -0
  328. package/src/web/features/helpers.ts +267 -0
  329. package/src/web/features/network.ts +61 -0
  330. package/src/web/features/speech.ts +87 -0
  331. package/src/web/features/vault.ts +189 -0
  332. package/src/web/features/vm.ts +78 -0
  333. package/src/web/features/voice-recognition.ts +129 -0
  334. package/src/web/shims/isomorphic-vm.ts +149 -0
  335. package/test/bus.test.ts +134 -0
  336. package/test/clients-servers.test.ts +216 -0
  337. package/test/container-link.test.ts +274 -0
  338. package/test/features.test.ts +160 -0
  339. package/test/integration.test.ts +787 -0
  340. package/test/node-container.test.ts +121 -0
  341. package/test/rate-limit.test.ts +272 -0
  342. package/test/semantic-search.test.ts +550 -0
  343. package/test/state.test.ts +121 -0
  344. package/test-integration/assistant.test.ts +138 -0
  345. package/test-integration/assistants-manager.test.ts +123 -0
  346. package/test-integration/claude-code.test.ts +98 -0
  347. package/test-integration/conversation-history.test.ts +205 -0
  348. package/test-integration/conversation.test.ts +137 -0
  349. package/test-integration/elevenlabs.test.ts +55 -0
  350. package/test-integration/google-services.test.ts +80 -0
  351. package/test-integration/helpers.ts +89 -0
  352. package/test-integration/openai-codex.test.ts +93 -0
  353. package/test-integration/runpod.test.ts +58 -0
  354. package/test-integration/server-endpoints.test.ts +97 -0
  355. package/test-integration/skills-library.test.ts +157 -0
  356. package/test-integration/telegram.test.ts +46 -0
  357. package/tsconfig.json +58 -0
  358. package/uv.lock +8 -0
@@ -0,0 +1,257 @@
1
+ import { z } from 'zod'
2
+ import { commands } from '../command.js'
3
+ import { CommandOptionsSchema } from '../schemas/base.js'
4
+ import type { ContainerContext } from '../container.js'
5
+
6
+ declare module '../command.js' {
7
+ interface AvailableCommands {
8
+ run: ReturnType<typeof commands.registerHandler>
9
+ }
10
+ }
11
+
12
+ export const argsSchema = CommandOptionsSchema.extend({
13
+ safe: z.boolean().default(false).describe('Require approval before each code block (markdown mode)'),
14
+ console: z.boolean().default(false).describe('Start an interactive REPL after executing a markdown file, with all accumulated context'),
15
+ onlySections: z.string().optional().describe('Comma-separated list of section headings to run (case-insensitive, markdown only)'),
16
+ })
17
+
18
+ function resolveScript(ref: string, context: ContainerContext): string | null {
19
+ const container = context.container as any
20
+ const candidates = [
21
+ ref,
22
+ `${ref}.ts`,
23
+ `${ref}.js`,
24
+ `${ref}.md`,
25
+ ]
26
+
27
+ for (const candidate of candidates) {
28
+ const resolved = container.paths.resolve(candidate)
29
+ if (container.fs.exists(resolved)) return resolved
30
+ }
31
+
32
+ return null
33
+ }
34
+
35
+ /**
36
+ * Find the ## Blocks section in the AST and return the indices to skip
37
+ * plus the tsx/jsx code block values to register.
38
+ */
39
+ function extractBlocksSection(children: any[]): { skipIndices: Set<number>, blockSources: string[] } {
40
+ const skipIndices = new Set<number>()
41
+ const blockSources: string[] = []
42
+
43
+ let inBlocks = false
44
+ for (let i = 0; i < children.length; i++) {
45
+ const node = children[i]
46
+
47
+ if (node.type === 'heading' && node.depth === 2) {
48
+ const text = (node.children || []).map((c: any) => c.value || '').join('').trim()
49
+ if (text === 'Blocks') {
50
+ inBlocks = true
51
+ skipIndices.add(i)
52
+ continue
53
+ } else if (inBlocks) {
54
+ // hit the next ## heading — stop collecting
55
+ break
56
+ }
57
+ }
58
+
59
+ if (inBlocks) {
60
+ skipIndices.add(i)
61
+ if (node.type === 'code' && (node.lang === 'tsx' || node.lang === 'jsx')) {
62
+ blockSources.push(node.value)
63
+ }
64
+ }
65
+ }
66
+
67
+ return { skipIndices, blockSources }
68
+ }
69
+
70
+ async function runMarkdown(scriptPath: string, options: z.infer<typeof argsSchema>, context: ContainerContext) {
71
+ console.clear()
72
+ const container = context.container as any
73
+ const requireApproval = options.safe
74
+ await container.docs.load()
75
+
76
+ const doc = await container.docs.parseMarkdownAtPath(scriptPath)
77
+
78
+ const esbuild = container.feature('esbuild')
79
+ const ink = container.feature('ink', { enable: true })
80
+ await ink.loadModules()
81
+
82
+ const vm = container.feature('vm')
83
+ const render = async (name: string, data?: any) => ink.renderBlock(name, data)
84
+ const renderAsync = async (name: string, data?: any, options?: { timeout?: number }) => ink.renderBlockAsync(name, data, options)
85
+ const shared = vm.createContext({
86
+ console, ink, render, renderAsync,
87
+ setTimeout, clearTimeout, setInterval, clearInterval,
88
+ fetch, URL, URLSearchParams,
89
+ ...container.context,
90
+ })
91
+
92
+ // ─── Parse and register ## Blocks section ──────────────────────────
93
+ const { skipIndices, blockSources } = extractBlocksSection(doc.ast.children)
94
+
95
+ for (const source of blockSources) {
96
+ const keysBefore = new Set(Object.keys(shared))
97
+ const { code: transformed } = esbuild.transformSync(source, { loader: 'tsx', format: 'cjs' })
98
+
99
+ const hasTopLevelAwait = /\bawait\b/.test(transformed)
100
+ const wrapped = hasTopLevelAwait
101
+ ? `(async function() { ${transformed} })()`
102
+ : transformed
103
+
104
+ await vm.run(wrapped, shared)
105
+
106
+ // auto-register any new functions as blocks
107
+ for (const key of Object.keys(shared)) {
108
+ if (!keysBefore.has(key) && typeof shared[key] === 'function') {
109
+ ink.registerBlock(key, shared[key])
110
+ }
111
+ }
112
+ }
113
+
114
+ // ─── Build section filter from --only-sections ───────────────────
115
+ let allowedIndices: Set<number> | null = null
116
+ if (options.onlySections) {
117
+ const requestedSections = options.onlySections.split(',').map(s => s.trim())
118
+ allowedIndices = new Set<number>()
119
+ for (const sectionName of requestedSections) {
120
+ try {
121
+ const sectionNodes = doc.extractSection(sectionName)
122
+ for (const node of sectionNodes) {
123
+ const idx = doc.ast.children.indexOf(node as any)
124
+ if (idx !== -1) allowedIndices.add(idx)
125
+ }
126
+ } catch {
127
+ // Section not found — skip silently
128
+ }
129
+ }
130
+ }
131
+
132
+ // ─── Execute document ──────────────────────────────────────────────
133
+ const children = doc.ast.children
134
+ for (let i = 0; i < children.length; i++) {
135
+ if (skipIndices.has(i)) continue
136
+ if (allowedIndices && !allowedIndices.has(i)) continue
137
+
138
+ const node = children[i]
139
+ if (node.type === 'code') {
140
+ const { value, lang, meta } = node
141
+
142
+ if (lang !== 'ts' && lang !== 'js' && lang !== 'tsx' && lang !== 'jsx') {
143
+ console.log(container.ui.markdown(['```' + (lang || ''), value, '```'].join('\n')))
144
+ continue
145
+ }
146
+
147
+ if (meta && typeof meta === 'string' && meta.toLowerCase().includes('skip')) continue
148
+
149
+ console.log(container.ui.markdown(['```' + lang, value, '```'].join('\n')))
150
+
151
+ if (requireApproval) {
152
+ const answer = await container.ui.askQuestion('Run this block? (y/n)')
153
+ if (answer.question.toLowerCase() !== 'y') continue
154
+ }
155
+
156
+ // Transform tsx/jsx through esbuild, and also ts for consistency
157
+ const needsTransform = lang === 'tsx' || lang === 'jsx'
158
+ let code = value
159
+
160
+ if (needsTransform) {
161
+ const { code: transformed } = esbuild.transformSync(value, {
162
+ loader: lang as 'tsx' | 'jsx',
163
+ format: 'cjs',
164
+ })
165
+ code = transformed
166
+ }
167
+
168
+ const hasTopLevelAwait = /\bawait\b/.test(code)
169
+ code = hasTopLevelAwait
170
+ ? `(async function() { ${code} })()`
171
+ : code
172
+
173
+ await vm.run(code, shared)
174
+
175
+ // if we enabled any features, they will be in the context object
176
+ Object.assign(shared, container.context)
177
+ } else {
178
+ const md = doc.stringify({ type: 'root', children: [node] })
179
+ console.log(container.ui.markdown(md))
180
+ }
181
+ }
182
+
183
+ return shared
184
+ }
185
+
186
+ async function runScript(scriptPath: string, context: ContainerContext) {
187
+ const container = context.container as any
188
+
189
+ const { exitCode, stderr } = await container.proc.runScript(scriptPath)
190
+
191
+ if (exitCode === 0) return
192
+
193
+ console.error(`\nScript failed with exit code ${exitCode}.\n`)
194
+ if (stderr.length) {
195
+ console.error(stderr.join('\n'))
196
+ }
197
+ }
198
+
199
+ async function diagnoseError(_scriptPath: string, error: Error, _context: ContainerContext) {
200
+ console.error(`\n${error.stack || error.message}\n`)
201
+ }
202
+
203
+ export default async function run(options: z.infer<typeof argsSchema>, context: ContainerContext) {
204
+ const container = context.container as any
205
+ const fileRef = container.argv._[1] as string
206
+
207
+ if (!fileRef) {
208
+ console.error('Usage: luca run <file>')
209
+ process.exit(1)
210
+ }
211
+
212
+ const scriptPath = resolveScript(fileRef, context)
213
+
214
+ if (!scriptPath) {
215
+ console.error(`Could not find script: ${fileRef}`)
216
+ process.exit(1)
217
+ }
218
+
219
+ try {
220
+ if (scriptPath.endsWith('.md')) {
221
+ const shared = await runMarkdown(scriptPath, options, context)
222
+
223
+ if (options.console) {
224
+ const ui = container.feature('ui')
225
+ const prompt = ui.colors.cyan('luca') + ui.colors.dim(' > ')
226
+
227
+ console.log()
228
+ console.log(ui.colors.dim(' Entering REPL with markdown context. Type .exit to quit.'))
229
+ console.log()
230
+
231
+ const repl = container.feature('repl', { prompt })
232
+ await repl.start({
233
+ context: {
234
+ ...shared,
235
+ console,
236
+ setTimeout,
237
+ setInterval,
238
+ clearTimeout,
239
+ clearInterval,
240
+ fetch,
241
+ Bun,
242
+ },
243
+ })
244
+ }
245
+ } else {
246
+ await runScript(scriptPath, context)
247
+ }
248
+ } catch (err: any) {
249
+ await diagnoseError(scriptPath, err instanceof Error ? err : new Error(String(err)), context)
250
+ }
251
+ }
252
+
253
+ commands.registerHandler('run', {
254
+ description: 'Run a script or markdown file (.ts, .js, .md)',
255
+ argsSchema,
256
+ handler: run,
257
+ })
@@ -0,0 +1,439 @@
1
+ import { z } from 'zod'
2
+ import { commands } from '../command.js'
3
+ import { CommandOptionsSchema } from '../schemas/base.js'
4
+ import type { ContainerContext } from '../container.js'
5
+ import type { MCPServer } from '../servers/mcp.js'
6
+ import { scaffolds, mcpReadme } from '../scaffolds/generated.js'
7
+ import { generateScaffold } from '../scaffolds/template.js'
8
+
9
+ declare module '../command.js' {
10
+ interface AvailableCommands {
11
+ 'sandbox-mcp': ReturnType<typeof commands.registerHandler>
12
+ }
13
+ }
14
+
15
+ export const argsSchema = CommandOptionsSchema.extend({
16
+ transport: z.enum(['stdio', 'http']).default('stdio').describe('Transport type (stdio or http)'),
17
+ port: z.number().default(3002).describe('Port for HTTP transport'),
18
+ mcpCompat: z.enum(['standard', 'codex']).optional()
19
+ .describe('HTTP compatibility profile. Defaults to standard. Can also be set via MCP_HTTP_COMPAT.'),
20
+ stdioCompat: z.enum(['standard', 'codex', 'auto']).optional()
21
+ .describe('Stdio framing compatibility profile. Defaults to standard. Can also be set via MCP_STDIO_COMPAT.'),
22
+ })
23
+
24
+ export default async function mcpSandbox(options: z.infer<typeof argsSchema>, context: ContainerContext) {
25
+ const container = context.container as any
26
+ const envCompat = process.env.MCP_HTTP_COMPAT?.toLowerCase()
27
+ const resolvedCompat = options.mcpCompat || (envCompat === 'codex' ? 'codex' : 'standard')
28
+ const envStdioCompat = process.env.MCP_STDIO_COMPAT?.toLowerCase()
29
+ const resolvedStdioCompat = options.stdioCompat
30
+ || (envStdioCompat === 'codex' || envStdioCompat === 'auto' ? envStdioCompat : 'standard')
31
+
32
+ const mcpServer = container.server('mcp', {
33
+ transport: options.transport,
34
+ port: options.port,
35
+ serverName: 'luca-sandbox',
36
+ serverVersion: container.manifest?.version || '1.0.0',
37
+ mcpCompat: options.mcpCompat,
38
+ stdioCompat: options.stdioCompat,
39
+ }) as MCPServer
40
+
41
+ // Persistent VM context shared across eval calls so variables survive between invocations
42
+ const vmFeature = container.feature('vm')
43
+ const sandboxContext = vmFeature.createContext({
44
+ container,
45
+ console: {
46
+ log: (...args: any[]) => args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' '),
47
+ error: (...args: any[]) => args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' '),
48
+ warn: (...args: any[]) => args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' '),
49
+ },
50
+ setTimeout,
51
+ setInterval,
52
+ clearTimeout,
53
+ clearInterval,
54
+ fetch,
55
+ Bun,
56
+ })
57
+
58
+ // Pre-populate sandbox with all enabled features
59
+ for (const name of container.features.available) {
60
+ try {
61
+ vmFeature.runSync(`var ${name} = container.feature('${name}')`, sandboxContext)
62
+ } catch {}
63
+ }
64
+
65
+ // --- Tool: read_me ---
66
+ mcpServer.tool('read_me', {
67
+ description: [
68
+ 'Returns the Luca framework development guide. Call this BEFORE writing any code in a luca project.',
69
+ 'Contains the import conventions, capability map, and workflow for discovering and using container features.',
70
+ 'You should call this tool at the start of every session.',
71
+ ].join('\n'),
72
+ schema: z.object({}),
73
+ handler: () => mcpReadme,
74
+ })
75
+
76
+ // --- Tool: find_capability ---
77
+ mcpServer.tool('find_capability', {
78
+ description: [
79
+ 'Search for container capabilities by intent. Returns the full catalog of available features, clients,',
80
+ 'and servers with their descriptions so you can find what you need before writing code.',
81
+ 'Call this when you need to do something and aren\'t sure which helper provides it.',
82
+ 'Prefer this over installing npm packages — the container likely already has what you need.',
83
+ ].join('\n'),
84
+ schema: z.object({}),
85
+ handler: () => {
86
+ const sections: string[] = []
87
+
88
+ try {
89
+ sections.push(container.features.describeAll())
90
+ } catch {}
91
+
92
+ try {
93
+ sections.push(container.clients.describeAll())
94
+ } catch {}
95
+
96
+ try {
97
+ sections.push(container.servers.describeAll())
98
+ } catch {}
99
+
100
+ return sections.join('\n\n---\n\n')
101
+ },
102
+ })
103
+
104
+ // --- Tool: scaffold ---
105
+ mcpServer.tool('scaffold', {
106
+ description: [
107
+ 'Generate correct boilerplate for a new luca helper (feature, client, server, command, or endpoint).',
108
+ 'Returns the complete file content with your name and description filled in.',
109
+ 'Write this to a file, then fill in your implementation.',
110
+ 'The scaffold follows all luca conventions including schemas, jsdoc, module augmentation, and registration.',
111
+ ].join('\n'),
112
+ schema: z.object({
113
+ type: z.enum(['feature', 'client', 'server', 'command', 'endpoint'])
114
+ .describe('What kind of helper to scaffold'),
115
+ name: z.string()
116
+ .describe('Name for the helper (e.g. "diskCache", "myApi", "healthCheck")'),
117
+ description: z.string().optional()
118
+ .describe('Brief description of what this helper does'),
119
+ }),
120
+ handler: (args) => {
121
+ const result = generateScaffold(args.type, args.name, args.description)
122
+ return result || `No scaffold template available for type: ${args.type}`
123
+ },
124
+ })
125
+
126
+ // --- Tool: eval ---
127
+ mcpServer.tool('eval', {
128
+ description: [
129
+ 'Evaluate JavaScript/TypeScript code in a Luca container sandbox.',
130
+ 'Use this to prototype and test container API calls before writing them into files.',
131
+ 'The sandbox has all features available as top-level variables.',
132
+ '',
133
+ 'The sandbox has a live `container` object and all enabled features as top-level variables',
134
+ '(e.g. `fs`, `git`, `ui`, `vm`, `proc`, `networking`, etc).',
135
+ '',
136
+ 'Variables you define persist across calls, so you can build up state incrementally.',
137
+ '',
138
+ 'The result of the last expression is returned. For async code, use `await`.',
139
+ '',
140
+ 'Quick reference:',
141
+ ' container.features.available — list available feature names',
142
+ ' container.clients.available — list available client names',
143
+ ' container.servers.available — list available server names',
144
+ ' container.commands.available — list available command names',
145
+ ' container.features.describe(n) — get docs for a feature by name',
146
+ ' container.clients.describe(n) — get docs for a client by name',
147
+ ' container.inspectAsText() — full container introspection',
148
+ ' fs.readFile(path) — read a file',
149
+ ' fs.readdir(dir) — list directory contents',
150
+ ' proc.exec(cmd) — run a shell command',
151
+ ].join('\n'),
152
+ schema: z.object({
153
+ code: z.string().describe('JavaScript code to evaluate in the Luca container sandbox'),
154
+ }),
155
+ handler: async (args) => {
156
+ try {
157
+ // Wrap code containing top-level await in an async IIFE so the VM can handle it.
158
+ // Try to return the last expression's value by prepending `return` to the last statement.
159
+ let code = args.code
160
+ if (/\bawait\b/.test(code) && !/^\s*\(?\s*async\b/.test(code)) {
161
+ const lines = code.split('\n')
162
+ const lastLine = lines[lines.length - 1]
163
+ // If the last line doesn't start with a keyword that can't be returned, add return
164
+ if (!/^\s*(var|let|const|if|for|while|switch|try|throw|class|function)\b/.test(lastLine)) {
165
+ lines[lines.length - 1] = `return ${lastLine}`
166
+ }
167
+ code = `(async () => { ${lines.join('\n')} })()`
168
+ }
169
+ const result = await vmFeature.run(code, sandboxContext)
170
+
171
+ let text: string
172
+ if (result === undefined) {
173
+ text = 'undefined'
174
+ } else if (result === null) {
175
+ text = 'null'
176
+ } else if (typeof result === 'string') {
177
+ text = result
178
+ } else if (typeof result === 'object') {
179
+ try {
180
+ text = JSON.stringify(result, null, 2)
181
+ } catch {
182
+ text = String(result)
183
+ }
184
+ } else {
185
+ text = String(result)
186
+ }
187
+
188
+ return { content: [{ type: 'text' as const, text }] }
189
+ } catch (error: any) {
190
+ return {
191
+ content: [{ type: 'text' as const, text: `Error: ${error.message}\n\n${error.stack || ''}` }],
192
+ isError: true,
193
+ }
194
+ }
195
+ },
196
+ })
197
+
198
+ // --- Tool: inspect_container ---
199
+ mcpServer.tool('inspect_container', {
200
+ description: [
201
+ 'Inspect the Luca container — registries, state, methods, events, environment.',
202
+ '',
203
+ 'Returns a markdown overview of the container. Optionally filter to a specific section.',
204
+ '',
205
+ 'Sections: "methods", "getters", "events", "state", "options", "envVars"',
206
+ 'Leave section empty for the full overview.',
207
+ ].join('\n'),
208
+ schema: z.object({
209
+ section: z.enum(['methods', 'getters', 'events', 'state', 'options', 'envVars']).optional()
210
+ .describe('Optional section to filter to. Omit for full overview.'),
211
+ }),
212
+ handler: (args) => {
213
+ return container.inspectAsText(args.section)
214
+ },
215
+ })
216
+
217
+ // --- Tool: list_registry ---
218
+ mcpServer.tool('list_registry', {
219
+ description: [
220
+ 'List available items in a container registry.',
221
+ '',
222
+ 'Returns names and brief descriptions for all registered helpers in the chosen registry.',
223
+ 'Use describe_helper to get full documentation for a specific item.',
224
+ ].join('\n'),
225
+ schema: z.object({
226
+ registry: z.enum(['features', 'clients', 'servers', 'commands', 'endpoints'])
227
+ .describe('Which registry to list'),
228
+ }),
229
+ handler: (args) => {
230
+ const registry = (container as any)[args.registry]
231
+ if (!registry) return `Unknown registry: ${args.registry}`
232
+ const names: string[] = registry.available
233
+ if (names.length === 0) return `No ${args.registry} registered.`
234
+ const lines = [`# Available ${args.registry}\n`]
235
+ for (const name of names) {
236
+ try {
237
+ const Ctor = registry.lookup(name) as any
238
+ const desc = Ctor.description || ''
239
+ lines.push(`- **${name}**${desc ? `: ${desc}` : ''}`)
240
+ } catch {
241
+ lines.push(`- **${name}**`)
242
+ }
243
+ }
244
+ return lines.join('\n')
245
+ },
246
+ })
247
+
248
+ // --- Tool: describe_helper ---
249
+ mcpServer.tool('describe_helper', {
250
+ description: [
251
+ 'Get full documentation for a specific helper (feature, client, server, command, endpoint).',
252
+ 'This is the API documentation for any luca helper. There is no other documentation available —',
253
+ 'call this before writing code that uses a feature, client, or server.',
254
+ '',
255
+ 'Returns markdown with options, state schema, methods, getters, events, env vars, and descriptions.',
256
+ 'Use list_registry or find_capability first to see what is available.',
257
+ ].join('\n'),
258
+ schema: z.object({
259
+ registry: z.enum(['features', 'clients', 'servers', 'commands', 'endpoints'])
260
+ .describe('Which registry the helper belongs to'),
261
+ name: z.string().describe('Name of the helper (e.g. "fs", "rest", "express")'),
262
+ section: z.enum(['methods', 'getters', 'events', 'state', 'options', 'envVars']).optional()
263
+ .describe('Optional section to filter to. Omit for full documentation.'),
264
+ }),
265
+ handler: (args) => {
266
+ const registry = (container as any)[args.registry]
267
+ if (!registry) return `Unknown registry: ${args.registry}`
268
+ if (!registry.has(args.name)) {
269
+ return `"${args.name}" not found in ${args.registry}. Available: ${registry.available.join(', ')}`
270
+ }
271
+ try {
272
+ const Ctor = registry.lookup(args.name) as any
273
+ return Ctor.introspectAsText(args.section)
274
+ } catch (e: any) {
275
+ return `Error describing ${args.name}: ${e.message}`
276
+ }
277
+ },
278
+ })
279
+
280
+ // --- Tool: inspect_helper_instance ---
281
+ mcpServer.tool('inspect_helper_instance', {
282
+ description: [
283
+ 'Inspect a live helper instance (enabled feature, active client/server).',
284
+ 'Use this to inspect a live, running instance — see its current state,',
285
+ 'check method signatures, and understand runtime behavior.',
286
+ '',
287
+ 'Creates or retrieves the helper and returns introspectAsText() — the same',
288
+ 'rich markdown documentation available on any Helper instance at runtime.',
289
+ 'Optionally filter to a specific section.',
290
+ ].join('\n'),
291
+ schema: z.object({
292
+ type: z.enum(['feature', 'client', 'server'])
293
+ .describe('What kind of helper to inspect'),
294
+ name: z.string().describe('Name of the helper (e.g. "fs", "rest", "express")'),
295
+ section: z.enum(['methods', 'getters', 'events', 'state', 'options', 'envVars']).optional()
296
+ .describe('Optional section to filter to. Omit for full introspection.'),
297
+ }),
298
+ handler: (args) => {
299
+ try {
300
+ let instance: any
301
+ if (args.type === 'feature') {
302
+ instance = container.feature(args.name as any)
303
+ } else if (args.type === 'client') {
304
+ instance = container.client(args.name as any)
305
+ } else if (args.type === 'server') {
306
+ instance = container.server(args.name as any)
307
+ }
308
+ return instance.introspectAsText(args.section)
309
+ } catch (e: any) {
310
+ return `Error inspecting ${args.type} "${args.name}": ${e.message}`
311
+ }
312
+ },
313
+ })
314
+
315
+ // --- Prompt: discover ---
316
+ mcpServer.prompt('discover', {
317
+ description: 'Learn how to explore the Luca container and discover available features, clients, servers, and commands.',
318
+ handler: () => [{
319
+ role: 'user' as const,
320
+ content: [
321
+ '# Luca Container Sandbox',
322
+ '',
323
+ 'You have access to a live Luca container through the `eval` tool. The sandbox is a persistent JavaScript environment with a `container` global and all enabled features available as top-level variables.',
324
+ '',
325
+ '## Discovering what is available',
326
+ '',
327
+ 'The container has registries for each helper type. Each registry has:',
328
+ '- `.available` — array of registered names',
329
+ '- `.describe(name)` — returns markdown docs for one helper',
330
+ '- `.describeAll()` — returns a condensed overview of all helpers (name + description)',
331
+ '',
332
+ '### Registries',
333
+ '```',
334
+ 'container.features.available // Features (fs, git, ui, vm, proc, ...)',
335
+ 'container.clients.available // Clients (rest, websocket, graphql, ...)',
336
+ 'container.servers.available // Servers (express, mcp, socket, ...)',
337
+ 'container.commands.available // Commands (run, console, serve, ...)',
338
+ '```',
339
+ '',
340
+ '### Getting documentation',
341
+ '```',
342
+ 'container.features.describe("fs") // Docs for the fs feature',
343
+ 'container.features.describe("vm") // Docs for the vm feature',
344
+ 'container.clients.describe("rest") // Docs for the rest client',
345
+ 'container.inspectAsText() // Full container introspection',
346
+ 'container.inspectAsText("methods") // Just the methods section',
347
+ 'container.inspectAsText("state") // Just the state section',
348
+ '```',
349
+ '',
350
+ '### Using features directly',
351
+ 'All enabled features are available as top-level variables:',
352
+ '```',
353
+ 'fs.readFile("package.json") // Read a file',
354
+ 'fs.readdir("src") // List directory',
355
+ 'git.log({ max: 5 }) // Recent git commits',
356
+ 'proc.exec("ls -la") // Run a shell command',
357
+ '```',
358
+ '',
359
+ '### Persistent state',
360
+ 'Variables you define in one eval call persist to the next:',
361
+ '```',
362
+ '// Call 1',
363
+ 'const data = fs.readFile("package.json")',
364
+ '// Call 2',
365
+ 'JSON.parse(data).name // still has `data` from previous call',
366
+ '```',
367
+ '',
368
+ '## Recommended first steps',
369
+ '1. `container.features.available` — see what features exist',
370
+ '2. Pick an interesting feature and `container.features.describe("name")` it',
371
+ '3. Try using the feature directly',
372
+ ].join('\n'),
373
+ }],
374
+ })
375
+
376
+ // --- Prompt: introspect ---
377
+ mcpServer.prompt('introspect', {
378
+ description: 'Get full introspection of the Luca container — all registries, state, methods, events, and environment info.',
379
+ handler: async () => {
380
+ const text = container.inspectAsText()
381
+ return [{
382
+ role: 'user' as const,
383
+ content: `Here is the full container introspection:\n\n${text}`,
384
+ }]
385
+ },
386
+ })
387
+
388
+ // --- Resource: container-info ---
389
+ mcpServer.resource('luca://container/info', {
390
+ name: 'Container Info',
391
+ description: 'Full introspection of the running Luca container',
392
+ mimeType: 'text/markdown',
393
+ handler: () => container.inspectAsText(),
394
+ })
395
+
396
+ // --- Resource: feature list ---
397
+ mcpServer.resource('luca://features', {
398
+ name: 'Available Features',
399
+ description: 'List of all registered features with descriptions',
400
+ mimeType: 'text/markdown',
401
+ handler: () => {
402
+ const lines = ['# Available Features\n']
403
+ for (const name of container.features.available) {
404
+ try {
405
+ const Ctor = container.features.lookup(name) as any
406
+ const desc = Ctor.description || ''
407
+ lines.push(`- **${name}**: ${desc}`)
408
+ } catch {
409
+ lines.push(`- **${name}**`)
410
+ }
411
+ }
412
+ return lines.join('\n')
413
+ },
414
+ })
415
+
416
+ // Start the server
417
+ await mcpServer.start({
418
+ transport: options.transport,
419
+ port: options.port,
420
+ mcpCompat: options.mcpCompat,
421
+ stdioCompat: options.stdioCompat,
422
+ })
423
+
424
+ if (options.transport === 'http') {
425
+ console.log(`\nLuca Sandbox MCP listening on http://localhost:${options.port}/mcp`)
426
+ console.log(`Compatibility: ${resolvedCompat}`)
427
+ } else {
428
+ console.error(`Luca Sandbox MCP started (stdio transport)`)
429
+ console.error(`Stdio Compatibility: ${resolvedStdioCompat}`)
430
+ console.error(`Tools: read_me, find_capability, scaffold, eval, inspect_container, list_registry, describe_helper, inspect_helper_instance`)
431
+ console.error(`Prompts: discover, introspect | Resources: luca://container/info, luca://features`)
432
+ }
433
+ }
434
+
435
+ commands.registerHandler('sandbox-mcp', {
436
+ description: 'Start an MCP server with a Luca container sandbox for AI agents to explore and test code',
437
+ argsSchema,
438
+ handler: mcpSandbox,
439
+ })