luca 2.0.0 → 3.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.
- package/.github/workflows/release.yaml +170 -0
- package/AGENTS.md +99 -0
- package/CLAUDE.md +123 -0
- package/CNAME +1 -0
- package/README.md +275 -9
- package/RUNME.md +56 -0
- package/assistants/codingAssistant/ABOUT.md +5 -0
- package/assistants/codingAssistant/CORE.md +33 -0
- package/assistants/codingAssistant/hooks.ts +21 -0
- package/assistants/codingAssistant/tools.ts +12 -0
- package/assistants/inkbot/ABOUT.md +16 -0
- package/assistants/inkbot/CORE.md +330 -0
- package/assistants/inkbot/hooks.ts +6 -0
- package/assistants/inkbot/tools.ts +53 -0
- package/assistants/researcher/ABOUT.md +5 -0
- package/assistants/researcher/CORE.md +46 -0
- package/assistants/researcher/hooks.ts +16 -0
- package/assistants/researcher/tools.ts +237 -0
- package/bun.lock +2667 -0
- package/bunfig.toml +3 -0
- package/commands/audit-docs.ts +740 -0
- package/commands/build-bootstrap.ts +117 -0
- package/commands/build-python-bridge.ts +42 -0
- package/commands/build-scaffolds.ts +175 -0
- package/commands/bundle-consumer-project.ts +521 -0
- package/commands/generate-api-docs.ts +114 -0
- package/commands/inkbot.ts +874 -0
- package/commands/release.ts +80 -0
- package/commands/try-all-challenges.ts +543 -0
- package/commands/try-challenge.ts +100 -0
- package/dist/agi/container.server.d.ts +63 -0
- package/dist/agi/container.server.d.ts.map +1 -0
- package/dist/agi/endpoints/ask.d.ts +20 -0
- package/dist/agi/endpoints/ask.d.ts.map +1 -0
- package/dist/agi/endpoints/conversations/[id].d.ts +27 -0
- package/dist/agi/endpoints/conversations/[id].d.ts.map +1 -0
- package/dist/agi/endpoints/conversations.d.ts +18 -0
- package/dist/agi/endpoints/conversations.d.ts.map +1 -0
- package/dist/agi/endpoints/experts.d.ts +8 -0
- package/dist/agi/endpoints/experts.d.ts.map +1 -0
- package/dist/agi/feature.d.ts +9 -0
- package/dist/agi/feature.d.ts.map +1 -0
- package/dist/agi/features/assistant.d.ts +509 -0
- package/dist/agi/features/assistant.d.ts.map +1 -0
- package/dist/agi/features/assistants-manager.d.ts +236 -0
- package/dist/agi/features/assistants-manager.d.ts.map +1 -0
- package/dist/agi/features/autonomous-assistant.d.ts +281 -0
- package/dist/agi/features/autonomous-assistant.d.ts.map +1 -0
- package/dist/agi/features/browser-use.d.ts +479 -0
- package/dist/agi/features/browser-use.d.ts.map +1 -0
- package/dist/agi/features/claude-code.d.ts +824 -0
- package/dist/agi/features/claude-code.d.ts.map +1 -0
- package/dist/agi/features/conversation-history.d.ts +245 -0
- package/dist/agi/features/conversation-history.d.ts.map +1 -0
- package/dist/agi/features/conversation.d.ts +464 -0
- package/dist/agi/features/conversation.d.ts.map +1 -0
- package/dist/agi/features/docs-reader.d.ts +72 -0
- package/dist/agi/features/docs-reader.d.ts.map +1 -0
- package/dist/agi/features/file-tools.d.ts +110 -0
- package/dist/agi/features/file-tools.d.ts.map +1 -0
- package/dist/agi/features/luca-coder.d.ts +323 -0
- package/dist/agi/features/luca-coder.d.ts.map +1 -0
- package/dist/agi/features/openai-codex.d.ts +381 -0
- package/dist/agi/features/openai-codex.d.ts.map +1 -0
- package/dist/agi/features/openapi.d.ts +200 -0
- package/dist/agi/features/openapi.d.ts.map +1 -0
- package/dist/agi/features/skills-library.d.ts +167 -0
- package/dist/agi/features/skills-library.d.ts.map +1 -0
- package/dist/agi/index.d.ts +5 -0
- package/dist/agi/index.d.ts.map +1 -0
- package/dist/agi/lib/interceptor-chain.d.ts +44 -0
- package/dist/agi/lib/interceptor-chain.d.ts.map +1 -0
- package/dist/agi/lib/token-counter.d.ts +13 -0
- package/dist/agi/lib/token-counter.d.ts.map +1 -0
- package/dist/bootstrap/generated.d.ts +5 -0
- package/dist/bootstrap/generated.d.ts.map +1 -0
- package/dist/browser.d.ts +12 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/bus.d.ts +29 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/cli/build-info.d.ts +4 -0
- package/dist/cli/build-info.d.ts.map +1 -0
- package/dist/cli/cli.d.ts +3 -12
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/client.d.ts +60 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/clients/civitai/index.d.ts +472 -0
- package/dist/clients/civitai/index.d.ts.map +1 -0
- package/dist/clients/client-template.d.ts +30 -0
- package/dist/clients/client-template.d.ts.map +1 -0
- package/dist/clients/comfyui/index.d.ts +281 -0
- package/dist/clients/comfyui/index.d.ts.map +1 -0
- package/dist/clients/elevenlabs/index.d.ts +197 -0
- package/dist/clients/elevenlabs/index.d.ts.map +1 -0
- package/dist/clients/graph.d.ts +64 -0
- package/dist/clients/graph.d.ts.map +1 -0
- package/dist/clients/openai/index.d.ts +247 -0
- package/dist/clients/openai/index.d.ts.map +1 -0
- package/dist/clients/rest.d.ts +92 -0
- package/dist/clients/rest.d.ts.map +1 -0
- package/dist/clients/supabase/index.d.ts +176 -0
- package/dist/clients/supabase/index.d.ts.map +1 -0
- package/dist/clients/websocket.d.ts +127 -0
- package/dist/clients/websocket.d.ts.map +1 -0
- package/dist/command.d.ts +163 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/commands/bootstrap.d.ts +20 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/chat.d.ts +37 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/code.d.ts +28 -0
- package/dist/commands/code.d.ts.map +1 -0
- package/dist/commands/console.d.ts +22 -0
- package/dist/commands/console.d.ts.map +1 -0
- package/dist/commands/describe.d.ts +50 -0
- package/dist/commands/describe.d.ts.map +1 -0
- package/dist/commands/eval.d.ts +23 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/help.d.ts +25 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/introspect.d.ts +24 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/mcp.d.ts +35 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/prompt.d.ts +38 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/run.d.ts +24 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/sandbox-mcp.d.ts +34 -0
- package/dist/commands/sandbox-mcp.d.ts.map +1 -0
- package/dist/commands/save-api-docs.d.ts +21 -0
- package/dist/commands/save-api-docs.d.ts.map +1 -0
- package/dist/commands/scaffold.d.ts +24 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/select.d.ts +22 -0
- package/dist/commands/select.d.ts.map +1 -0
- package/dist/commands/serve.d.ts +29 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/container-describer.d.ts +144 -0
- package/dist/container-describer.d.ts.map +1 -0
- package/dist/container.d.ts +451 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/endpoint.d.ts +113 -0
- package/dist/endpoint.d.ts.map +1 -0
- package/dist/feature.d.ts +47 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/graft.d.ts +29 -0
- package/dist/graft.d.ts.map +1 -0
- package/dist/hash-object.d.ts +8 -0
- package/dist/hash-object.d.ts.map +1 -0
- package/dist/helper.d.ts +209 -0
- package/dist/helper.d.ts.map +1 -0
- package/dist/introspection/generated.node.d.ts +44623 -0
- package/dist/introspection/generated.node.d.ts.map +1 -0
- package/dist/introspection/generated.web.d.ts +1412 -0
- package/dist/introspection/generated.web.d.ts.map +1 -0
- package/dist/introspection/index.d.ts +156 -0
- package/dist/introspection/index.d.ts.map +1 -0
- package/dist/introspection/scan.d.ts +147 -0
- package/dist/introspection/scan.d.ts.map +1 -0
- package/dist/node/container.d.ts +256 -0
- package/dist/node/container.d.ts.map +1 -0
- package/dist/node/feature.d.ts +9 -0
- package/dist/node/feature.d.ts.map +1 -0
- package/dist/node/features/container-link.d.ts +213 -0
- package/dist/node/features/container-link.d.ts.map +1 -0
- package/dist/node/features/content-db.d.ts +354 -0
- package/dist/node/features/content-db.d.ts.map +1 -0
- package/dist/node/features/disk-cache.d.ts +236 -0
- package/dist/node/features/disk-cache.d.ts.map +1 -0
- package/dist/node/features/dns.d.ts +511 -0
- package/dist/node/features/dns.d.ts.map +1 -0
- package/dist/node/features/docker.d.ts +485 -0
- package/dist/node/features/docker.d.ts.map +1 -0
- package/dist/node/features/downloader.d.ts +73 -0
- package/dist/node/features/downloader.d.ts.map +1 -0
- package/dist/node/features/figlet-fonts.d.ts +4 -0
- package/dist/node/features/figlet-fonts.d.ts.map +1 -0
- package/dist/node/features/file-manager.d.ts +177 -0
- package/dist/node/features/file-manager.d.ts.map +1 -0
- package/dist/node/features/fs.d.ts +635 -0
- package/dist/node/features/fs.d.ts.map +1 -0
- package/dist/node/features/git.d.ts +329 -0
- package/dist/node/features/git.d.ts.map +1 -0
- package/dist/node/features/google-auth.d.ts +200 -0
- package/dist/node/features/google-auth.d.ts.map +1 -0
- package/dist/node/features/google-calendar.d.ts +194 -0
- package/dist/node/features/google-calendar.d.ts.map +1 -0
- package/dist/node/features/google-docs.d.ts +138 -0
- package/dist/node/features/google-docs.d.ts.map +1 -0
- package/dist/node/features/google-drive.d.ts +202 -0
- package/dist/node/features/google-drive.d.ts.map +1 -0
- package/dist/node/features/google-mail.d.ts +221 -0
- package/dist/node/features/google-mail.d.ts.map +1 -0
- package/dist/node/features/google-sheets.d.ts +157 -0
- package/dist/node/features/google-sheets.d.ts.map +1 -0
- package/dist/node/features/grep.d.ts +207 -0
- package/dist/node/features/grep.d.ts.map +1 -0
- package/dist/node/features/helpers.d.ts +236 -0
- package/dist/node/features/helpers.d.ts.map +1 -0
- package/dist/node/features/ink.d.ts +332 -0
- package/dist/node/features/ink.d.ts.map +1 -0
- package/dist/node/features/ipc-socket.d.ts +298 -0
- package/dist/node/features/ipc-socket.d.ts.map +1 -0
- package/dist/node/features/json-tree.d.ts +140 -0
- package/dist/node/features/json-tree.d.ts.map +1 -0
- package/dist/node/features/networking.d.ts +373 -0
- package/dist/node/features/networking.d.ts.map +1 -0
- package/dist/node/features/nlp.d.ts +125 -0
- package/dist/node/features/nlp.d.ts.map +1 -0
- package/dist/node/features/opener.d.ts +93 -0
- package/dist/node/features/opener.d.ts.map +1 -0
- package/dist/node/features/os.d.ts +168 -0
- package/dist/node/features/os.d.ts.map +1 -0
- package/dist/node/features/package-finder.d.ts +419 -0
- package/dist/node/features/package-finder.d.ts.map +1 -0
- package/dist/node/features/postgres.d.ts +173 -0
- package/dist/node/features/postgres.d.ts.map +1 -0
- package/dist/node/features/proc.d.ts +285 -0
- package/dist/node/features/proc.d.ts.map +1 -0
- package/dist/node/features/process-manager.d.ts +427 -0
- package/dist/node/features/process-manager.d.ts.map +1 -0
- package/dist/node/features/python.d.ts +477 -0
- package/dist/node/features/python.d.ts.map +1 -0
- package/dist/node/features/redis.d.ts +247 -0
- package/dist/node/features/redis.d.ts.map +1 -0
- package/dist/node/features/repl.d.ts +84 -0
- package/dist/node/features/repl.d.ts.map +1 -0
- package/dist/node/features/runpod.d.ts +527 -0
- package/dist/node/features/runpod.d.ts.map +1 -0
- package/dist/node/features/secure-shell.d.ts +145 -0
- package/dist/node/features/secure-shell.d.ts.map +1 -0
- package/dist/node/features/semantic-search.d.ts +207 -0
- package/dist/node/features/semantic-search.d.ts.map +1 -0
- package/dist/node/features/sqlite.d.ts +180 -0
- package/dist/node/features/sqlite.d.ts.map +1 -0
- package/dist/node/features/telegram.d.ts +173 -0
- package/dist/node/features/telegram.d.ts.map +1 -0
- package/dist/node/features/transpiler.d.ts +51 -0
- package/dist/node/features/transpiler.d.ts.map +1 -0
- package/dist/node/features/tts.d.ts +108 -0
- package/dist/node/features/tts.d.ts.map +1 -0
- package/dist/node/features/ui.d.ts +562 -0
- package/dist/node/features/ui.d.ts.map +1 -0
- package/dist/node/features/vault.d.ts +90 -0
- package/dist/node/features/vault.d.ts.map +1 -0
- package/dist/node/features/vm.d.ts +285 -0
- package/dist/node/features/vm.d.ts.map +1 -0
- package/dist/node/features/yaml-tree.d.ts +118 -0
- package/dist/node/features/yaml-tree.d.ts.map +1 -0
- package/dist/node/features/yaml.d.ts +127 -0
- package/dist/node/features/yaml.d.ts.map +1 -0
- package/dist/node.d.ts +67 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/python/generated.d.ts +2 -0
- package/dist/python/generated.d.ts.map +1 -0
- package/dist/react/index.d.ts +36 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/registry.d.ts +97 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/scaffolds/generated.d.ts +13 -0
- package/dist/scaffolds/generated.d.ts.map +1 -0
- package/dist/scaffolds/template.d.ts +11 -0
- package/dist/scaffolds/template.d.ts.map +1 -0
- package/dist/schemas/base.d.ts +254 -0
- package/dist/schemas/base.d.ts.map +1 -0
- package/dist/selector.d.ts +130 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/server.d.ts +89 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/servers/express.d.ts +104 -0
- package/dist/servers/express.d.ts.map +1 -0
- package/dist/servers/mcp.d.ts +201 -0
- package/dist/servers/mcp.d.ts.map +1 -0
- package/dist/servers/socket.d.ts +121 -0
- package/dist/servers/socket.d.ts.map +1 -0
- package/dist/state.d.ts +24 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/web/clients/socket.d.ts +37 -0
- package/dist/web/clients/socket.d.ts.map +1 -0
- package/dist/web/container.d.ts +55 -0
- package/dist/web/container.d.ts.map +1 -0
- package/dist/web/extension.d.ts +4 -0
- package/dist/web/extension.d.ts.map +1 -0
- package/dist/web/feature.d.ts +8 -0
- package/dist/web/feature.d.ts.map +1 -0
- package/dist/web/features/asset-loader.d.ts +35 -0
- package/dist/web/features/asset-loader.d.ts.map +1 -0
- package/dist/web/features/container-link.d.ts +167 -0
- package/dist/web/features/container-link.d.ts.map +1 -0
- package/dist/web/features/esbuild.d.ts +51 -0
- package/dist/web/features/esbuild.d.ts.map +1 -0
- package/dist/web/features/helpers.d.ts +140 -0
- package/dist/web/features/helpers.d.ts.map +1 -0
- package/dist/web/features/network.d.ts +69 -0
- package/dist/web/features/network.d.ts.map +1 -0
- package/dist/web/features/speech.d.ts +71 -0
- package/dist/web/features/speech.d.ts.map +1 -0
- package/dist/web/features/vault.d.ts +62 -0
- package/dist/web/features/vault.d.ts.map +1 -0
- package/dist/web/features/vm.d.ts +48 -0
- package/dist/web/features/vm.d.ts.map +1 -0
- package/dist/web/features/voice-recognition.d.ts +96 -0
- package/dist/web/features/voice-recognition.d.ts.map +1 -0
- package/dist/web/shims/isomorphic-vm.d.ts +22 -0
- package/dist/web/shims/isomorphic-vm.d.ts.map +1 -0
- package/index.html +1457 -0
- package/index.ts +1 -0
- package/install.sh +84 -0
- package/luca.cli.ts +16 -0
- package/luca.console.ts +9 -0
- package/main.py +6 -0
- package/package.json +219 -58
- package/public/index.html +1457 -0
- package/public/slides-ai-native.html +902 -0
- package/public/slides-intro.html +974 -0
- package/pyproject.toml +7 -0
- package/scripts/build-web.ts +28 -0
- package/scripts/examples/ask-luca-expert.ts +42 -0
- package/scripts/examples/assistant-questions.ts +12 -0
- package/scripts/examples/excalidraw-expert.ts +75 -0
- package/scripts/examples/expert-chat.ts +0 -0
- package/scripts/examples/file-manager.ts +14 -0
- package/scripts/examples/ideas.ts +12 -0
- package/scripts/examples/interactive-chat.ts +20 -0
- package/scripts/examples/openai-tool-calls.ts +113 -0
- package/scripts/examples/opening-a-web-browser.ts +5 -0
- package/scripts/examples/telegram-bot.ts +79 -0
- package/scripts/examples/using-assistant-with-mcp.ts +555 -0
- package/scripts/examples/using-claude-code.ts +10 -0
- package/scripts/examples/using-contentdb.ts +35 -0
- package/scripts/examples/using-conversations.ts +35 -0
- package/scripts/examples/using-disk-cache.ts +10 -0
- package/scripts/examples/using-docker-shell.ts +75 -0
- package/scripts/examples/using-elevenlabs.ts +25 -0
- package/scripts/examples/using-google-calendar.ts +57 -0
- package/scripts/examples/using-google-docs.ts +74 -0
- package/scripts/examples/using-google-drive.ts +74 -0
- package/scripts/examples/using-google-sheets.ts +89 -0
- package/scripts/examples/using-nlp.ts +55 -0
- package/scripts/examples/using-ollama.ts +11 -0
- package/scripts/examples/using-postgres.ts +55 -0
- package/scripts/examples/using-runpod.ts +32 -0
- package/scripts/examples/using-tts.ts +40 -0
- package/scripts/scaffold.ts +391 -0
- package/scripts/scratch.ts +15 -0
- package/scripts/stamp-build.sh +12 -0
- package/scripts/test-assistant-hooks.ts +13 -0
- package/scripts/test-docs-reader.ts +10 -0
- package/scripts/test-linux-binary.sh +80 -0
- package/scripts/update-introspection-data.ts +58 -0
- package/src/agi/README.md +14 -0
- package/src/agi/container.server.ts +156 -0
- package/src/agi/feature.ts +13 -0
- package/src/agi/features/agent-memory.ts +694 -0
- package/src/agi/features/assistant.ts +1653 -0
- package/src/agi/features/assistants-manager.ts +534 -0
- package/src/agi/features/autonomous-assistant.ts +431 -0
- package/src/agi/features/browser-use.ts +672 -0
- package/src/agi/features/claude-code.ts +1584 -0
- package/src/agi/features/coding-tools.ts +175 -0
- package/src/agi/features/conversation-history.ts +672 -0
- package/src/agi/features/conversation.ts +1494 -0
- package/src/agi/features/docs-reader.ts +167 -0
- package/src/agi/features/file-tools.ts +340 -0
- package/src/agi/features/luca-coder.ts +641 -0
- package/src/agi/features/mcp-bridge.ts +532 -0
- package/src/agi/features/openai-codex.ts +651 -0
- package/src/agi/features/openapi.ts +445 -0
- package/src/agi/features/skills-library.ts +557 -0
- package/src/agi/index.ts +6 -0
- package/src/agi/lib/interceptor-chain.ts +89 -0
- package/src/agi/lib/token-counter.ts +202 -0
- package/src/bootstrap/generated.ts +9791 -0
- package/src/browser.ts +25 -0
- package/src/bus.ts +122 -0
- package/src/cli/build-info.ts +4 -0
- package/src/cli/cli.ts +355 -0
- package/src/client.ts +170 -0
- package/src/clients/civitai/index.ts +537 -0
- package/src/clients/client-template.ts +41 -0
- package/src/clients/comfyui/index.ts +604 -0
- package/src/clients/elevenlabs/index.ts +317 -0
- package/src/clients/graph.ts +87 -0
- package/src/clients/openai/index.ts +456 -0
- package/src/clients/rest.ts +207 -0
- package/src/clients/supabase/index.ts +357 -0
- package/src/clients/voicebox/index.ts +300 -0
- package/src/clients/websocket.ts +251 -0
- package/src/command.ts +506 -0
- package/src/commands/bootstrap.ts +244 -0
- package/src/commands/chat.ts +309 -0
- package/src/commands/code.ts +371 -0
- package/src/commands/console.ts +189 -0
- package/src/commands/describe.ts +243 -0
- package/src/commands/eval.ts +67 -0
- package/src/commands/help.ts +240 -0
- package/src/commands/index.ts +19 -0
- package/src/commands/introspect.ts +218 -0
- package/src/commands/mcp.ts +64 -0
- package/src/commands/prompt.ts +1014 -0
- package/src/commands/run.ts +278 -0
- package/src/commands/sandbox-mcp.ts +343 -0
- package/src/commands/save-api-docs.ts +51 -0
- package/src/commands/scaffold.ts +225 -0
- package/src/commands/select.ts +99 -0
- package/src/commands/serve.ts +208 -0
- package/src/container-describer.ts +1091 -0
- package/src/container.ts +1199 -0
- package/src/endpoint.ts +365 -0
- package/src/entity.ts +173 -0
- package/src/feature.ts +118 -0
- package/src/graft.ts +181 -0
- package/src/hash-object.ts +97 -0
- package/src/helper.ts +849 -0
- package/src/introspection/generated.agi.ts +41200 -0
- package/src/introspection/generated.node.ts +28773 -0
- package/src/introspection/generated.web.ts +2272 -0
- package/src/introspection/index.ts +296 -0
- package/src/introspection/scan.ts +1136 -0
- package/src/node/container.ts +409 -0
- package/src/node/feature.ts +13 -0
- package/src/node/features/container-link.ts +559 -0
- package/src/node/features/content-db.ts +849 -0
- package/src/node/features/disk-cache.ts +388 -0
- package/src/node/features/display-result.ts +57 -0
- package/src/node/features/dns.ts +669 -0
- package/src/node/features/docker.ts +921 -0
- package/src/node/features/downloader.ts +79 -0
- package/src/node/features/figlet-fonts.ts +600 -0
- package/src/node/features/file-manager.ts +535 -0
- package/src/node/features/fs.ts +1050 -0
- package/src/node/features/git.ts +592 -0
- package/src/node/features/google-auth.ts +504 -0
- package/src/node/features/google-calendar.ts +306 -0
- package/src/node/features/google-docs.ts +412 -0
- package/src/node/features/google-drive.ts +346 -0
- package/src/node/features/google-mail.ts +540 -0
- package/src/node/features/google-sheets.ts +286 -0
- package/src/node/features/grep.ts +427 -0
- package/src/node/features/helpers.ts +762 -0
- package/src/node/features/ink.ts +490 -0
- package/src/node/features/ipc-socket.ts +649 -0
- package/src/node/features/json-tree.ts +170 -0
- package/src/node/features/networking.ts +961 -0
- package/src/node/features/nlp.ts +212 -0
- package/src/node/features/opener.ts +180 -0
- package/src/node/features/os.ts +403 -0
- package/src/node/features/package-finder.ts +540 -0
- package/src/node/features/postgres.ts +289 -0
- package/src/node/features/proc.ts +503 -0
- package/src/node/features/process-manager.ts +844 -0
- package/src/node/features/python.ts +912 -0
- package/src/node/features/redis.ts +446 -0
- package/src/node/features/repl.ts +212 -0
- package/src/node/features/runpod.ts +811 -0
- package/src/node/features/secure-shell.ts +261 -0
- package/src/node/features/semantic-search.ts +935 -0
- package/src/node/features/sqlite.ts +289 -0
- package/src/node/features/telegram.ts +343 -0
- package/src/node/features/transpiler.ts +160 -0
- package/src/node/features/tts.ts +185 -0
- package/src/node/features/ui.ts +791 -0
- package/src/node/features/vault.ts +153 -0
- package/src/node/features/vm.ts +462 -0
- package/src/node/features/yaml-tree.ts +148 -0
- package/src/node/features/yaml.ts +133 -0
- package/src/node.ts +76 -0
- package/src/python/bridge.py +220 -0
- package/src/python/generated.ts +226 -0
- package/src/react/index.ts +175 -0
- package/src/registry.ts +210 -0
- package/src/scaffolds/generated.ts +1814 -0
- package/src/scaffolds/template.ts +46 -0
- package/src/schemas/base.ts +296 -0
- package/src/selector.ts +352 -0
- package/src/server.ts +229 -0
- package/src/servers/express.ts +283 -0
- package/src/servers/mcp.ts +802 -0
- package/src/servers/socket.ts +258 -0
- package/src/state.ts +101 -0
- package/src/web/clients/socket.ts +99 -0
- package/src/web/container.ts +75 -0
- package/src/web/extension.ts +30 -0
- package/src/web/feature.ts +12 -0
- package/src/web/features/asset-loader.ts +72 -0
- package/src/web/features/container-link.ts +382 -0
- package/src/web/features/esbuild.ts +93 -0
- package/src/web/features/helpers.ts +291 -0
- package/src/web/features/network.ts +85 -0
- package/src/web/features/speech.ts +104 -0
- package/src/web/features/vault.ts +207 -0
- package/src/web/features/vm.ts +85 -0
- package/src/web/features/voice-recognition.ts +161 -0
- package/src/web/shims/isomorphic-vm.ts +149 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +58 -0
- package/uv.lock +8 -0
- package/LICENSE +0 -21
- package/dist/cli/cli.js +0 -48
- package/dist/cli/common.d.ts +0 -2
- package/dist/cli/common.js +0 -6
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -5
- package/dist/cli/run.d.ts +0 -1
- package/dist/cli/run.js +0 -38
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.js +0 -32
- package/dist/core/read.d.ts +0 -2
- package/dist/core/read.js +0 -29
- package/dist/core/request.d.ts +0 -1
- package/dist/core/request.js +0 -2
- package/dist/core/write.d.ts +0 -2
- package/dist/core/write.js +0 -21
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -5
- package/dist/utils/common.d.ts +0 -9
- package/dist/utils/common.js +0 -57
- package/dist/utils/consts.d.ts +0 -3
- package/dist/utils/consts.js +0 -11
- package/dist/utils/dict.d.ts +0 -1
- package/dist/utils/dict.js +0 -7
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.js +0 -21
- package/dist/utils/log.d.ts +0 -1
- package/dist/utils/log.js +0 -5
- package/dist/utils/types.d.ts +0 -1
- package/dist/utils/types.js +0 -2
- package/dist/utils/utils.test.d.ts +0 -1
- package/dist/utils/utils.test.js +0 -7
|
@@ -0,0 +1,244 @@
|
|
|
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 { NodeContainer } from '../node/container.js'
|
|
6
|
+
import { bootstrapFiles, bootstrapTemplates, bootstrapExamples, bootstrapTutorials } from '../bootstrap/generated.js'
|
|
7
|
+
import { generateScaffold } from '../scaffolds/template.js'
|
|
8
|
+
|
|
9
|
+
declare module '../command.js' {
|
|
10
|
+
interface AvailableCommands {
|
|
11
|
+
bootstrap: ReturnType<typeof commands.registerHandler>
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
16
|
+
output: z.string().default('.').describe('Output folder path (defaults to cwd)'),
|
|
17
|
+
'update-skill': z.boolean().default(false).describe('Only update .claude/skills/luca-framework in the current project'),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
async function bootstrap(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
21
|
+
const container = context.container as unknown as NodeContainer
|
|
22
|
+
const args = container.argv._ as string[]
|
|
23
|
+
const fs = container.feature('fs')
|
|
24
|
+
const ui = container.feature('ui')
|
|
25
|
+
const proc = container.feature('proc')
|
|
26
|
+
|
|
27
|
+
// ── --update-skill: refresh skill files in the current project ──
|
|
28
|
+
if (options['update-skill']) {
|
|
29
|
+
return await updateSkill(container, fs, ui)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Require an explicit target — don't silently bootstrap into cwd
|
|
33
|
+
let target = args[1] || (options.output !== '.' ? options.output : '')
|
|
34
|
+
|
|
35
|
+
if (!target) {
|
|
36
|
+
const answer = await ui.askQuestion('Project name (folder to create):')
|
|
37
|
+
target = answer?.question?.trim()
|
|
38
|
+
if (!target) {
|
|
39
|
+
ui.print.red('\n No project name given, aborting.\n')
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const outputDir = container.paths.resolve(target)
|
|
45
|
+
await fs.ensureFolder(outputDir)
|
|
46
|
+
const mkPath = (...segments: string[]) => container.paths.resolve(outputDir, ...segments)
|
|
47
|
+
|
|
48
|
+
ui.print.cyan('\n luca bootstrap\n')
|
|
49
|
+
|
|
50
|
+
// ── Check for AI coding tools ──────────────────────────────────
|
|
51
|
+
await checkToolAvailability(ui, proc)
|
|
52
|
+
|
|
53
|
+
// ── 1. .env (only if missing) ──────────────────────────────────
|
|
54
|
+
const envPath = mkPath('.env')
|
|
55
|
+
if (!fs.exists(envPath)) {
|
|
56
|
+
await writeFile(fs, ui, envPath, '', '.env')
|
|
57
|
+
} else {
|
|
58
|
+
ui.print.dim(' .env already exists, skipping')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── 2. CLAUDE.md ───────────────────────────────────────────────
|
|
62
|
+
await writeFile(fs, ui, mkPath('CLAUDE.md'), bootstrapFiles['CLAUDE'] || '', 'CLAUDE.md')
|
|
63
|
+
|
|
64
|
+
// ── 3. .claude/skills/luca-framework/ ──────────────────────────
|
|
65
|
+
const skillDir = mkPath('.claude', 'skills', 'luca-framework')
|
|
66
|
+
await fs.ensureFolder(skillDir)
|
|
67
|
+
await writeFile(fs, ui, container.paths.resolve(skillDir, 'SKILL.md'), bootstrapFiles['SKILL'] || '', '.claude/skills/luca-framework/SKILL.md')
|
|
68
|
+
|
|
69
|
+
// ── 3b. examples and tutorials ─────────────────────────────────
|
|
70
|
+
const examplesDir = container.paths.resolve(skillDir, 'references', 'examples')
|
|
71
|
+
await fs.ensureFolder(examplesDir)
|
|
72
|
+
for (const [filename, content] of Object.entries(bootstrapExamples)) {
|
|
73
|
+
await fs.writeFileAsync(container.paths.resolve(examplesDir, filename), content)
|
|
74
|
+
}
|
|
75
|
+
ui.print.cyan(` Writing ${Object.keys(bootstrapExamples).length} example docs...`)
|
|
76
|
+
|
|
77
|
+
const tutorialsDir = container.paths.resolve(skillDir, 'references', 'tutorials')
|
|
78
|
+
await fs.ensureFolder(tutorialsDir)
|
|
79
|
+
for (const [filename, content] of Object.entries(bootstrapTutorials)) {
|
|
80
|
+
await fs.writeFileAsync(container.paths.resolve(tutorialsDir, filename), content)
|
|
81
|
+
}
|
|
82
|
+
ui.print.cyan(` Writing ${Object.keys(bootstrapTutorials).length} tutorial docs...`)
|
|
83
|
+
|
|
84
|
+
// ── 4. docs/ folder ────────────────────────────────────────────
|
|
85
|
+
await fs.ensureFolder(mkPath('docs'))
|
|
86
|
+
await writeFile(fs, ui, mkPath('docs', 'models.ts'), bootstrapTemplates['docs-models'] || '', 'docs/models.ts')
|
|
87
|
+
await writeFile(fs, ui, mkPath('docs', 'README.md'), bootstrapTemplates['docs-readme'] || '', 'docs/README.md')
|
|
88
|
+
|
|
89
|
+
// ── 5. commands/about.ts ────────────────────────────────────────
|
|
90
|
+
await fs.ensureFolder(mkPath('commands'))
|
|
91
|
+
await writeFile(fs, ui, mkPath('commands', 'about.ts'), bootstrapTemplates['about-command'] || '', 'commands/about.ts')
|
|
92
|
+
|
|
93
|
+
// ── 6. features/example.ts (scaffold-based) ────────────────────
|
|
94
|
+
await fs.ensureFolder(mkPath('features'))
|
|
95
|
+
const featureCode = generateScaffold('feature', 'example', 'An example feature demonstrating the luca feature pattern')
|
|
96
|
+
|| bootstrapTemplates['example-feature'] || ''
|
|
97
|
+
await writeFile(fs, ui, mkPath('features', 'example.ts'), featureCode, 'features/example.ts')
|
|
98
|
+
|
|
99
|
+
// ── 7. endpoints/health.ts ─────────────────────────────────────
|
|
100
|
+
await fs.ensureFolder(mkPath('endpoints'))
|
|
101
|
+
await writeFile(fs, ui, mkPath('endpoints', 'health.ts'), bootstrapTemplates['health-endpoint'] || '', 'endpoints/health.ts')
|
|
102
|
+
|
|
103
|
+
// ── 8. luca.cli.ts ─────────────────────────────────────────────
|
|
104
|
+
await writeFile(fs, ui, mkPath('luca.cli.ts'), bootstrapTemplates['luca-cli'] || '', 'luca.cli.ts')
|
|
105
|
+
|
|
106
|
+
// ── 9. RUNME.md ────────────────────────────────────────────────
|
|
107
|
+
await writeFile(fs, ui, mkPath('RUNME.md'), bootstrapTemplates['runme'] || '', 'RUNME.md')
|
|
108
|
+
|
|
109
|
+
// ── 10. .claude/settings.json (permissions for AI coding tools) ──
|
|
110
|
+
const settingsPath = mkPath('.claude', 'settings.json')
|
|
111
|
+
const claudeSettings = {
|
|
112
|
+
permissions: {
|
|
113
|
+
allow: [
|
|
114
|
+
'Bash(luca *)',
|
|
115
|
+
'Bash(bun run *)',
|
|
116
|
+
'Bash(bun test *)',
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!fs.exists(settingsPath)) {
|
|
122
|
+
await fs.ensureFolder(mkPath('.claude'))
|
|
123
|
+
await writeFile(fs, ui, settingsPath, JSON.stringify(claudeSettings, null, 2) + '\n', '.claude/settings.json')
|
|
124
|
+
} else {
|
|
125
|
+
// Merge luca permissions into existing settings
|
|
126
|
+
try {
|
|
127
|
+
const existing = JSON.parse(fs.readFile(settingsPath) as string)
|
|
128
|
+
const perms = existing.permissions || {}
|
|
129
|
+
const allow = new Set(perms.allow || [])
|
|
130
|
+
for (const rule of claudeSettings.permissions.allow) {
|
|
131
|
+
allow.add(rule)
|
|
132
|
+
}
|
|
133
|
+
existing.permissions = { ...perms, allow: [...allow] }
|
|
134
|
+
await writeFile(fs, ui, settingsPath, JSON.stringify(existing, null, 2) + '\n', '.claude/settings.json (merged)')
|
|
135
|
+
} catch {
|
|
136
|
+
ui.print.yellow(' ⚠ Could not parse existing .claude/settings.json, skipping merge')
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Summary ────────────────────────────────────────────────────
|
|
141
|
+
ui.print('')
|
|
142
|
+
ui.print.green(' ✓ Bootstrap complete!\n')
|
|
143
|
+
ui.print(' Your project is ready. Here\'s what to try:\n')
|
|
144
|
+
ui.print(' luca — see available commands')
|
|
145
|
+
ui.print(' luca about — project info + discovered helpers')
|
|
146
|
+
ui.print(' luca serve — start the API server (try /api/health)')
|
|
147
|
+
ui.print(' luca describe fs — learn about any built-in feature')
|
|
148
|
+
ui.print(' luca RUNME — run the interactive markdown demo')
|
|
149
|
+
ui.print('')
|
|
150
|
+
ui.print(' Need to build something? Use scaffold:\n')
|
|
151
|
+
ui.print(' luca scaffold command deploy — add a CLI command')
|
|
152
|
+
ui.print(' luca scaffold feature cache — add a container feature')
|
|
153
|
+
ui.print(' luca scaffold endpoint users — add a REST route')
|
|
154
|
+
ui.print(' luca scaffold client github — add an API client')
|
|
155
|
+
ui.print(' luca scaffold server mqtt — add a server')
|
|
156
|
+
ui.print('')
|
|
157
|
+
ui.print.dim(' Run luca scaffold <type> --tutorial for a full guide on any type')
|
|
158
|
+
ui.print('')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
async function updateSkill(container: any, fs: any, ui: any) {
|
|
164
|
+
const skillDir = container.paths.resolve('.claude', 'skills', 'luca-framework')
|
|
165
|
+
|
|
166
|
+
// Wipe existing skill directory so stale files don't linger
|
|
167
|
+
if (fs.exists(skillDir)) {
|
|
168
|
+
await fs.rmdir(skillDir)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await fs.ensureFolder(skillDir)
|
|
172
|
+
await writeFile(fs, ui, container.paths.resolve(skillDir, 'SKILL.md'), bootstrapFiles['SKILL'] || '', '.claude/skills/luca-framework/SKILL.md')
|
|
173
|
+
|
|
174
|
+
const examplesDir = container.paths.resolve(skillDir, 'references', 'examples')
|
|
175
|
+
await fs.ensureFolder(examplesDir)
|
|
176
|
+
for (const [filename, content] of Object.entries(bootstrapExamples)) {
|
|
177
|
+
await fs.writeFileAsync(container.paths.resolve(examplesDir, filename), content)
|
|
178
|
+
}
|
|
179
|
+
ui.print.cyan(` Writing ${Object.keys(bootstrapExamples).length} example docs...`)
|
|
180
|
+
|
|
181
|
+
const tutorialsDir = container.paths.resolve(skillDir, 'references', 'tutorials')
|
|
182
|
+
await fs.ensureFolder(tutorialsDir)
|
|
183
|
+
for (const [filename, content] of Object.entries(bootstrapTutorials)) {
|
|
184
|
+
await fs.writeFileAsync(container.paths.resolve(tutorialsDir, filename), content)
|
|
185
|
+
}
|
|
186
|
+
ui.print.cyan(` Writing ${Object.keys(bootstrapTutorials).length} tutorial docs...`)
|
|
187
|
+
|
|
188
|
+
ui.print('')
|
|
189
|
+
ui.print.green(' ✓ Skill updated!')
|
|
190
|
+
ui.print('')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function writeFile(fs: any, ui: any, path: string, content: string, label: string) {
|
|
194
|
+
ui.print.cyan(` Writing ${label}...`)
|
|
195
|
+
await fs.writeFileAsync(path, content)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function checkToolAvailability(ui: any, proc: any) {
|
|
199
|
+
const tools: { name: string; found: boolean; envKey?: string; envFound?: boolean }[] = []
|
|
200
|
+
|
|
201
|
+
for (const name of ['claude', 'codex']) {
|
|
202
|
+
let found = false
|
|
203
|
+
try {
|
|
204
|
+
const result = await proc.exec(`which ${name}`, { silent: true })
|
|
205
|
+
found = result.exitCode === 0 && result.stdout.trim().length > 0
|
|
206
|
+
} catch {
|
|
207
|
+
found = false
|
|
208
|
+
}
|
|
209
|
+
tools.push({ name, found })
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const openaiKey = !!process.env.OPENAI_API_KEY
|
|
213
|
+
const hasAnyCodingTool = tools.some(t => t.found)
|
|
214
|
+
|
|
215
|
+
if (!hasAnyCodingTool) {
|
|
216
|
+
ui.print.yellow(' ┌─────────────────────────────────────────────────────────────┐')
|
|
217
|
+
ui.print.yellow(' │ No AI coding assistant detected (claude, codex) │')
|
|
218
|
+
ui.print.yellow(' │ │')
|
|
219
|
+
ui.print.yellow(' │ Luca works best with an AI coding assistant. │')
|
|
220
|
+
ui.print.yellow(' │ │')
|
|
221
|
+
ui.print.yellow(' │ Claude Code: https://docs.anthropic.com/en/docs/claude-code│')
|
|
222
|
+
ui.print.yellow(' │ Codex CLI: https://github.com/openai/codex │')
|
|
223
|
+
ui.print.yellow(' └─────────────────────────────────────────────────────────────┘')
|
|
224
|
+
ui.print('')
|
|
225
|
+
} else {
|
|
226
|
+
for (const t of tools) {
|
|
227
|
+
if (t.found) ui.print.green(` ✓ ${t.name} detected`)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!openaiKey) {
|
|
232
|
+
ui.print.dim(' ℹ OPENAI_API_KEY not set (only needed for codex/OpenAI features)')
|
|
233
|
+
} else {
|
|
234
|
+
ui.print.green(' ✓ OPENAI_API_KEY set')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ui.print('')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
commands.registerHandler('bootstrap', {
|
|
241
|
+
description: 'Scaffold a new luca project with commands, features, endpoints, docs, and AI assistant configuration',
|
|
242
|
+
argsSchema,
|
|
243
|
+
handler: bootstrap,
|
|
244
|
+
})
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import * as readline from 'readline'
|
|
3
|
+
import { commands } from '../command'
|
|
4
|
+
import { CommandOptionsSchema } from '../schemas/base'
|
|
5
|
+
import type { ContainerContext } from '../container'
|
|
6
|
+
|
|
7
|
+
declare module '../command.js' {
|
|
8
|
+
interface AvailableCommands {
|
|
9
|
+
chat: ReturnType<typeof commands.registerHandler>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const argsSchema = CommandOptionsSchema.extend({
|
|
14
|
+
model: z.string().optional().describe('Override the LLM model for the assistant'),
|
|
15
|
+
local: z.boolean().default(false).describe('Whether to use a local API server'),
|
|
16
|
+
resume: z.string().optional().describe('Thread ID or conversation ID to resume'),
|
|
17
|
+
list: z.boolean().optional().describe('List recent conversations and exit'),
|
|
18
|
+
historyMode: z.enum(['lifecycle', 'daily', 'persistent', 'session']).optional().describe('Override history persistence mode'),
|
|
19
|
+
offRecord: z.boolean().optional().describe('Alias for --history-mode lifecycle (ephemeral, no persistence)'),
|
|
20
|
+
clear: z.boolean().optional().describe('Clear the conversation history for the resolved history mode and exit'),
|
|
21
|
+
prependPrompt: z.string().optional().describe('Text or path to a markdown file to prepend to the system prompt'),
|
|
22
|
+
appendPrompt: z.string().optional().describe('Text or path to a markdown file to append to the system prompt'),
|
|
23
|
+
use: z.union([z.string(), z.array(z.string())]).optional().describe('Feature(s) to inject into the assistant via .use(). Supports options: --use "contentDb:rootPath=/tmp;lazy=true"'),
|
|
24
|
+
forbidTool: z.union([z.string(), z.array(z.string())]).optional().describe('Tool name patterns to exclude (supports * glob). Can be specified multiple times.'),
|
|
25
|
+
allowTool: z.union([z.string(), z.array(z.string())]).optional().describe('Tool name patterns to allow (strict allowlist, supports * glob). Can be specified multiple times.'),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export default async function chat(options: z.infer<typeof argsSchema>, context: ContainerContext) {
|
|
29
|
+
const container = context.container as any
|
|
30
|
+
const ui = container.feature('ui')
|
|
31
|
+
|
|
32
|
+
const manager = container.feature('assistantsManager')
|
|
33
|
+
await manager.discover()
|
|
34
|
+
|
|
35
|
+
const entries = manager.list()
|
|
36
|
+
|
|
37
|
+
if (entries.length === 0) {
|
|
38
|
+
console.error(ui.colors.red('No assistants found.'))
|
|
39
|
+
console.error(ui.colors.dim(` Create a directory with a CORE.md file anywhere in the project.`))
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const requestedName = container.argv._[1] as string | undefined
|
|
44
|
+
let name: string
|
|
45
|
+
|
|
46
|
+
if (requestedName) {
|
|
47
|
+
const entry = manager.get(requestedName)
|
|
48
|
+
if (!entry) {
|
|
49
|
+
const available = entries.map((e: any) => e.name).join(', ')
|
|
50
|
+
console.error(ui.colors.red(`Assistant "${requestedName}" not found.`))
|
|
51
|
+
console.error(ui.colors.dim(` Available: ${available}`))
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
name = requestedName
|
|
55
|
+
} else if (entries.length === 1) {
|
|
56
|
+
name = entries[0].name
|
|
57
|
+
} else {
|
|
58
|
+
const answers = await ui.wizard([
|
|
59
|
+
{
|
|
60
|
+
type: 'list',
|
|
61
|
+
name: 'assistant',
|
|
62
|
+
message: 'Choose an assistant',
|
|
63
|
+
choices: entries.map((e: any) => ({ name: e.name, value: e.name })),
|
|
64
|
+
},
|
|
65
|
+
])
|
|
66
|
+
name = answers.assistant
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Resolve history mode: --off-record overrides everything to lifecycle
|
|
70
|
+
// CLI defaults to 'daily' for interactive persistence
|
|
71
|
+
const historyMode = options.offRecord
|
|
72
|
+
? 'lifecycle'
|
|
73
|
+
: (options.historyMode || 'daily')
|
|
74
|
+
|
|
75
|
+
const createOptions: Record<string, any> = { historyMode, injectTimestamps: true }
|
|
76
|
+
if (options.model) createOptions.model = options.model
|
|
77
|
+
if (options.local) createOptions.local = options.local
|
|
78
|
+
if (options.forbidTool) createOptions.forbidTools = Array.isArray(options.forbidTool) ? options.forbidTool : [options.forbidTool]
|
|
79
|
+
if (options.allowTool) createOptions.allowTools = Array.isArray(options.allowTool) ? options.allowTool : [options.allowTool]
|
|
80
|
+
|
|
81
|
+
// Resolve --prepend-prompt / --append-prompt: if it's an existing file, read it; if it ends in .md but doesn't exist, error
|
|
82
|
+
const fs = container.feature('fs')
|
|
83
|
+
for (const flag of ['prependPrompt', 'appendPrompt'] as const) {
|
|
84
|
+
const raw = options[flag]
|
|
85
|
+
if (!raw) continue
|
|
86
|
+
const resolved = container.paths.resolve(raw)
|
|
87
|
+
if (fs.exists(resolved)) {
|
|
88
|
+
createOptions[flag] = fs.readFile(resolved)
|
|
89
|
+
} else if (raw.endsWith('.md')) {
|
|
90
|
+
console.error(ui.colors.red(`File not found: ${resolved}`))
|
|
91
|
+
process.exit(1)
|
|
92
|
+
} else {
|
|
93
|
+
createOptions[flag] = raw
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const assistant = manager.create(name, createOptions)
|
|
98
|
+
|
|
99
|
+
// --clear: wipe history for the current mode and exit
|
|
100
|
+
if (options.clear) {
|
|
101
|
+
const deleted = await assistant.clearHistory()
|
|
102
|
+
if (deleted > 0) {
|
|
103
|
+
console.log(ui.colors.green(` Cleared ${deleted} conversation(s) for ${ui.colors.cyan(name)} (${historyMode} mode).`))
|
|
104
|
+
} else {
|
|
105
|
+
console.log(ui.colors.dim(` No history to clear for ${ui.colors.cyan(name)} (${historyMode} mode).`))
|
|
106
|
+
}
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --list: show recent conversations and exit
|
|
111
|
+
if (options.list) {
|
|
112
|
+
const history = await assistant.listHistory({ limit: 20 })
|
|
113
|
+
if (history.length === 0) {
|
|
114
|
+
console.log(ui.colors.dim(' No saved conversations.'))
|
|
115
|
+
} else {
|
|
116
|
+
console.log()
|
|
117
|
+
console.log(ui.colors.dim(' Recent conversations:'))
|
|
118
|
+
console.log()
|
|
119
|
+
for (const meta of history) {
|
|
120
|
+
const date = new Date(meta.updatedAt).toLocaleString()
|
|
121
|
+
const msgs = ui.colors.dim(`(${meta.messageCount} messages)`)
|
|
122
|
+
console.log(` ${ui.colors.cyan(meta.thread)} ${msgs}`)
|
|
123
|
+
console.log(` ${ui.colors.dim(date)} - ${meta.title}`)
|
|
124
|
+
}
|
|
125
|
+
console.log()
|
|
126
|
+
console.log(ui.colors.dim(` Resume with: luca chat ${name} --resume <thread-id>`))
|
|
127
|
+
}
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --resume: set thread override before start
|
|
132
|
+
if (options.resume) {
|
|
133
|
+
assistant.resumeThread(options.resume)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const ink = container.feature('ink', { enable: true })
|
|
137
|
+
await ink.loadModules()
|
|
138
|
+
const React = ink.React
|
|
139
|
+
const { Text } = ink.components
|
|
140
|
+
|
|
141
|
+
// Use the raw ink render (sync) since modules are already loaded
|
|
142
|
+
const inkModule = await import('ink')
|
|
143
|
+
|
|
144
|
+
let responseBuffer = ''
|
|
145
|
+
let inkInstance: any = null
|
|
146
|
+
|
|
147
|
+
function mdElement(content: string) {
|
|
148
|
+
const rendered = content ? String(ui.markdown(content)).trimEnd() : ''
|
|
149
|
+
return React.createElement(Text, null, rendered)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
assistant.on('chunk', (text: string) => {
|
|
153
|
+
responseBuffer += text
|
|
154
|
+
if (!inkInstance) {
|
|
155
|
+
process.stdout.write('\n')
|
|
156
|
+
inkInstance = inkModule.render(mdElement(responseBuffer), { patchConsole: false })
|
|
157
|
+
} else {
|
|
158
|
+
inkInstance.rerender(mdElement(responseBuffer))
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
assistant.on('toolCall', (toolName: string, args: any) => {
|
|
163
|
+
if (inkInstance) { inkInstance.unmount(); inkInstance = null }
|
|
164
|
+
responseBuffer = ''
|
|
165
|
+
const argsStr = JSON.stringify(args).slice(0, 120)
|
|
166
|
+
process.stdout.write(ui.colors.dim(`\n ⟳ ${toolName}`) + ui.colors.dim(`(${argsStr})\n`))
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
assistant.on('toolResult', (toolName: string, result: any) => {
|
|
170
|
+
const preview = typeof result === 'string' ? result.slice(0, 100) : JSON.stringify(result).slice(0, 100)
|
|
171
|
+
process.stdout.write(ui.colors.green(` ✓ ${toolName}`) + ui.colors.dim(` → ${preview}${preview.length >= 100 ? '…' : ''}\n`))
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
assistant.on('toolError', (toolName: string, error: any) => {
|
|
175
|
+
const msg = error?.message || String(error)
|
|
176
|
+
process.stdout.write(ui.colors.red(` ✗ ${toolName}: ${msg}\n`))
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
assistant.on('response', () => {
|
|
180
|
+
if (inkInstance) { inkInstance.unmount(); inkInstance = null }
|
|
181
|
+
responseBuffer = ''
|
|
182
|
+
process.stdout.write('\n')
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// --use: inject features into the assistant
|
|
186
|
+
if (options.use) {
|
|
187
|
+
const items = Array.isArray(options.use) ? options.use : [options.use]
|
|
188
|
+
for (const item of items) {
|
|
189
|
+
const [namepart, optStr] = item.split(':')
|
|
190
|
+
if (!namepart) continue
|
|
191
|
+
const featureOpts: Record<string, any> = { enable: true }
|
|
192
|
+
if (optStr) {
|
|
193
|
+
for (const pair of optStr.split(';')) {
|
|
194
|
+
const [k, v] = pair.split('=')
|
|
195
|
+
if (k) featureOpts[k.trim()] = v?.trim() === 'true' ? true : v?.trim() === 'false' ? false : v?.trim()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const feature = container.feature(namepart.trim(), featureOpts)
|
|
199
|
+
assistant.use(feature)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Start the assistant (loads history if applicable)
|
|
204
|
+
await assistant.start()
|
|
205
|
+
|
|
206
|
+
const messageCount = assistant.messages?.length || 0
|
|
207
|
+
const isResuming = historyMode !== 'lifecycle' && messageCount > 1
|
|
208
|
+
|
|
209
|
+
let rl = readline.createInterface({
|
|
210
|
+
input: process.stdin,
|
|
211
|
+
output: process.stdout,
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
let rlClosed = false
|
|
215
|
+
rl.on('close', () => { rlClosed = true })
|
|
216
|
+
|
|
217
|
+
function ensureRl() {
|
|
218
|
+
if (rlClosed) {
|
|
219
|
+
rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
220
|
+
rlClosed = false
|
|
221
|
+
rl.on('close', () => { rlClosed = true })
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function prompt(): Promise<string> {
|
|
226
|
+
return new Promise((resolve) => {
|
|
227
|
+
ensureRl()
|
|
228
|
+
rl.question(ui.colors.dim(`\n${name} > `), (answer: string) => resolve(answer.trim()))
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log()
|
|
233
|
+
if (isResuming) {
|
|
234
|
+
console.log(ui.colors.dim(` Resuming conversation with ${ui.colors.cyan(name)} (${messageCount} messages). Type .exit to quit.`))
|
|
235
|
+
} else {
|
|
236
|
+
console.log(ui.colors.dim(` Chatting with ${ui.colors.cyan(name)}. Type .exit to quit.`))
|
|
237
|
+
}
|
|
238
|
+
if (historyMode !== 'lifecycle') {
|
|
239
|
+
console.log(ui.colors.dim(` Mode: ${historyMode}`))
|
|
240
|
+
}
|
|
241
|
+
console.log()
|
|
242
|
+
|
|
243
|
+
while (true) {
|
|
244
|
+
const question = await prompt()
|
|
245
|
+
|
|
246
|
+
if (!question) continue
|
|
247
|
+
if (question === '.exit') break
|
|
248
|
+
|
|
249
|
+
if (question === '/console') {
|
|
250
|
+
// Pause chat readline so the REPL can own stdin
|
|
251
|
+
rl.close()
|
|
252
|
+
|
|
253
|
+
// Build feature context like `luca console` does
|
|
254
|
+
const featureContext: Record<string, any> = {}
|
|
255
|
+
for (const fname of container.features.available) {
|
|
256
|
+
try { featureContext[fname] = container.feature(fname) } catch {}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const replPrompt = ui.colors.magenta('console') + ui.colors.dim(' > ')
|
|
260
|
+
const repl = container.feature('repl', { prompt: replPrompt })
|
|
261
|
+
|
|
262
|
+
console.log()
|
|
263
|
+
console.log(ui.colors.dim(' Dropping into console. The assistant is available as `assistant`.'))
|
|
264
|
+
console.log(ui.colors.dim(' Type .exit to return to chat.'))
|
|
265
|
+
console.log()
|
|
266
|
+
|
|
267
|
+
await repl.start({
|
|
268
|
+
context: {
|
|
269
|
+
...featureContext,
|
|
270
|
+
assistant,
|
|
271
|
+
console,
|
|
272
|
+
setTimeout, setInterval, clearTimeout, clearInterval,
|
|
273
|
+
fetch,
|
|
274
|
+
},
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// Wait for the REPL to close
|
|
278
|
+
await new Promise<void>((resolve) => {
|
|
279
|
+
repl._rl!.on('close', resolve)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// Resume chat readline
|
|
283
|
+
console.log()
|
|
284
|
+
console.log(ui.colors.dim(` Back in chat with ${ui.colors.cyan(name)}.`))
|
|
285
|
+
rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
286
|
+
rlClosed = false
|
|
287
|
+
rl.on('close', () => { rlClosed = true })
|
|
288
|
+
continue
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
await assistant.ask(question)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
rl.close()
|
|
295
|
+
|
|
296
|
+
// Show resume instruction for non-lifecycle modes
|
|
297
|
+
if (historyMode !== 'lifecycle' && assistant.currentThreadId) {
|
|
298
|
+
console.log()
|
|
299
|
+
console.log(ui.colors.dim(` Session saved. To resume this conversation:`))
|
|
300
|
+
console.log(ui.colors.dim(` luca chat ${name} --resume ${assistant.currentThreadId}`))
|
|
301
|
+
console.log()
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
commands.registerHandler('chat', {
|
|
306
|
+
description: 'Start an interactive chat session with a local assistant',
|
|
307
|
+
argsSchema,
|
|
308
|
+
handler: chat,
|
|
309
|
+
})
|