@phuetz/code-buddy 2.0.0 → 2.2.0
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/README.fr.md +56 -0
- package/README.md +74 -16
- package/codebuddy-runtime.json +5 -5
- package/dist/agent/codebuddy-agent.d.ts +18 -0
- package/dist/agent/codebuddy-agent.js +45 -8
- package/dist/agent/execution/agent-executor.d.ts +0 -9
- package/dist/agent/execution/agent-executor.js +176 -12
- package/dist/agent/execution/context-pipeline.d.ts +9 -0
- package/dist/agent/execution/context-pipeline.js +64 -2
- package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
- package/dist/agent/execution/tool-loop-guard.js +165 -0
- package/dist/agent/execution/tool-selection-strategy.d.ts +7 -0
- package/dist/agent/execution/tool-selection-strategy.js +32 -2
- package/dist/agent/facades/session-facade.d.ts +0 -6
- package/dist/agent/facades/session-facade.js +0 -45
- package/dist/agent/hermes-claw-migrate.d.ts +0 -21
- package/dist/agent/hermes-claw-migrate.js +9 -4
- package/dist/agent/hermes-protocol-gateways.js +8 -1
- package/dist/agent/repo-profiler.js +17 -4
- package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
- package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
- package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
- package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
- package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
- package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
- package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
- package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
- package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
- package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
- package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
- package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
- package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
- package/dist/agent/self-improvement/proposal-store.js +79 -17
- package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
- package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
- package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
- package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
- package/dist/agent/self-improvement/skill-engine.js +385 -13
- package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
- package/dist/agent/self-improvement/skill-gate.js +17 -2
- package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
- package/dist/agent/self-improvement/skill-mutator.js +51 -15
- package/dist/agent/self-improvement/skill-types.d.ts +2 -1
- package/dist/agent/self-improvement/types.d.ts +11 -0
- package/dist/agent/tool-handler.d.ts +1 -1
- package/dist/agent/tool-handler.js +28 -9
- package/dist/auth/profile-manager.js +1 -1
- package/dist/channels/companion-channel-turn.d.ts +53 -4
- package/dist/channels/companion-channel-turn.js +218 -16
- package/dist/channels/slash-parity.d.ts +4 -6
- package/dist/channels/slash-parity.js +9 -44
- package/dist/cli/session-commands.d.ts +9 -0
- package/dist/cli/session-commands.js +45 -5
- package/dist/cli/session-picker.d.ts +43 -0
- package/dist/cli/session-picker.js +128 -0
- package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
- package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
- package/dist/codebuddy/fleet-tool-defs.d.ts +2 -0
- package/dist/codebuddy/fleet-tool-defs.js +55 -0
- package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
- package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +7 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +29 -1
- package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
- package/dist/codebuddy/ragchat-tool-defs.js +19 -0
- package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
- package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
- package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
- package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
- package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
- package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
- package/dist/codebuddy/tools.d.ts +0 -6
- package/dist/codebuddy/tools.js +25 -3
- package/dist/commands/cli/daemon-commands.js +3 -1
- package/dist/commands/cli/evolve-command.js +21 -5
- package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
- package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
- package/dist/commands/cli/fleet-commands.js +29 -0
- package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
- package/dist/commands/cli/fleet-mission-commands.js +72 -0
- package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
- package/dist/commands/cli/fleet-rooms-commands.js +289 -0
- package/dist/commands/cli/tools-commands.js +2 -1
- package/dist/commands/cli/triage-command.d.ts +8 -0
- package/dist/commands/cli/triage-command.js +72 -0
- package/dist/commands/cli/utility-commands.js +25 -1
- package/dist/commands/client-dispatcher.d.ts +7 -0
- package/dist/commands/client-dispatcher.js +73 -21
- package/dist/commands/cron-cli/index.d.ts +3 -1
- package/dist/commands/cron-cli/index.js +25 -0
- package/dist/commands/device-auth.d.ts +10 -0
- package/dist/commands/device-auth.js +91 -0
- package/dist/commands/enhanced-command-handler.d.ts +9 -0
- package/dist/commands/enhanced-command-handler.js +28 -6
- package/dist/commands/handlers/agent-handlers.d.ts +1 -1
- package/dist/commands/handlers/agent-handlers.js +4 -4
- package/dist/commands/handlers/channel-handlers.js +53 -2
- package/dist/commands/handlers/export-handlers.d.ts +3 -2
- package/dist/commands/handlers/export-handlers.js +55 -28
- package/dist/commands/handlers/extra-handlers.js +5 -1
- package/dist/commands/handlers/grill-me-handler.js +26 -18
- package/dist/commands/handlers/memory-handlers.d.ts +6 -2
- package/dist/commands/handlers/memory-handlers.js +35 -15
- package/dist/commands/handlers/missing-handlers.d.ts +2 -2
- package/dist/commands/handlers/missing-handlers.js +12 -4
- package/dist/commands/handlers/resources-handler.d.ts +12 -0
- package/dist/commands/handlers/resources-handler.js +52 -0
- package/dist/commands/handlers/starter-handlers.d.ts +1 -1
- package/dist/commands/handlers/starter-handlers.js +32 -2
- package/dist/commands/handlers/switch-handler.d.ts +1 -1
- package/dist/commands/handlers/switch-handler.js +15 -0
- package/dist/commands/handlers/test-handlers.js +4 -86
- package/dist/commands/handlers/ui-handlers.js +11 -5
- package/dist/commands/login-chatgpt.d.ts +8 -0
- package/dist/commands/login-chatgpt.js +17 -0
- package/dist/commands/mcp-import.d.ts +11 -0
- package/dist/commands/mcp-import.js +67 -0
- package/dist/commands/mcp-invoke.d.ts +2 -0
- package/dist/commands/mcp-invoke.js +68 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +66 -21
- package/dist/commands/pipeline.d.ts +7 -1
- package/dist/commands/pipeline.js +104 -7
- package/dist/commands/resources.d.ts +2 -0
- package/dist/commands/resources.js +64 -0
- package/dist/commands/skills-cli/index.js +35 -12
- package/dist/commands/slash/builtin-commands.js +16 -3
- package/dist/commands/slash/index.d.ts +3 -1
- package/dist/commands/slash/index.js +2 -0
- package/dist/commands/slash/surfaces.d.ts +53 -0
- package/dist/commands/slash/surfaces.js +177 -0
- package/dist/commands/slash/types.d.ts +17 -0
- package/dist/commands/try.d.ts +1 -1
- package/dist/commands/try.js +1 -1
- package/dist/companion/companion-identity.d.ts +43 -0
- package/dist/companion/companion-identity.js +136 -0
- package/dist/companion/companion-toolset.d.ts +66 -0
- package/dist/companion/companion-toolset.js +368 -0
- package/dist/companion/companion-turn.d.ts +10 -2
- package/dist/companion/companion-turn.js +38 -1
- package/dist/config/code-exec-policy.d.ts +25 -0
- package/dist/config/code-exec-policy.js +39 -0
- package/dist/config/model-tools.d.ts +9 -0
- package/dist/config/model-tools.js +103 -0
- package/dist/context/context-manager-v2.js +2 -2
- package/dist/context/lm-resizer-compressor.js +3 -2
- package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
- package/dist/context/lm-resizer-diagnostics.js +30 -0
- package/dist/context/tool-output-masking.js +3 -4
- package/dist/curator/curator.d.ts +6 -0
- package/dist/curator/curator.js +53 -3
- package/dist/daemon/cron-agent-bridge.d.ts +6 -0
- package/dist/daemon/cron-agent-bridge.js +31 -4
- package/dist/doctor/index.d.ts +11 -2
- package/dist/doctor/index.js +84 -21
- package/dist/doctor/integrations.d.ts +197 -0
- package/dist/doctor/integrations.js +241 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -0
- package/dist/errors/crash-handler.js +2 -0
- package/dist/fleet/collaboration.d.ts +44 -0
- package/dist/fleet/collaboration.js +170 -0
- package/dist/fleet/fleet-listener.d.ts +1 -0
- package/dist/fleet/fleet-listener.js +87 -46
- package/dist/fleet/fleet-registry.d.ts +2 -0
- package/dist/fleet/resource-catalog.d.ts +245 -0
- package/dist/fleet/resource-catalog.js +211 -0
- package/dist/fleet/rooms/room-access.d.ts +138 -0
- package/dist/fleet/rooms/room-access.js +196 -0
- package/dist/fleet/rooms/room-client.d.ts +158 -0
- package/dist/fleet/rooms/room-client.js +685 -0
- package/dist/fleet/rooms/room-event.d.ts +139 -0
- package/dist/fleet/rooms/room-event.js +330 -0
- package/dist/fleet/rooms/room-filter.d.ts +48 -0
- package/dist/fleet/rooms/room-filter.js +152 -0
- package/dist/fleet/rooms/room-hub.d.ts +154 -0
- package/dist/fleet/rooms/room-hub.js +363 -0
- package/dist/fleet/rooms/room-identity.d.ts +26 -0
- package/dist/fleet/rooms/room-identity.js +82 -0
- package/dist/fleet/rooms/room-lock.d.ts +34 -0
- package/dist/fleet/rooms/room-lock.js +180 -0
- package/dist/fleet/rooms/room-observations.d.ts +7 -0
- package/dist/fleet/rooms/room-observations.js +61 -0
- package/dist/fleet/rooms/room-server.d.ts +28 -0
- package/dist/fleet/rooms/room-server.js +75 -0
- package/dist/fleet/rooms/room-store.d.ts +119 -0
- package/dist/fleet/rooms/room-store.js +448 -0
- package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
- package/dist/fleet/rooms/room-ws-bridge.js +137 -0
- package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
- package/dist/fleet/sensory-mission-bridge.js +136 -0
- package/dist/git/worktree-sessions.js +1 -0
- package/dist/harness/contract.d.ts +4 -4
- package/dist/harness/fleet-supervisor.d.ts +16 -0
- package/dist/harness/fleet-supervisor.js +64 -0
- package/dist/harness/index.d.ts +4 -0
- package/dist/harness/index.js +4 -0
- package/dist/harness/mission-runner.d.ts +3 -0
- package/dist/harness/mission-runner.js +53 -0
- package/dist/harness/mission-store.d.ts +93 -0
- package/dist/harness/mission-store.js +297 -0
- package/dist/harness/tool-harness.d.ts +56 -0
- package/dist/harness/tool-harness.js +153 -0
- package/dist/hooks/use-enhanced-input.js +48 -4
- package/dist/hooks/use-input-handler.d.ts +1 -0
- package/dist/hooks/use-input-handler.js +82 -22
- package/dist/hooks/use-input-history.js +1 -0
- package/dist/identity/lisa-introspection.js +5 -2
- package/dist/identity/operational-self-model.d.ts +2 -0
- package/dist/identity/operational-self-model.js +4 -0
- package/dist/index.js +52 -6
- package/dist/mcp/client.d.ts +11 -0
- package/dist/mcp/client.js +68 -3
- package/dist/mcp/config.d.ts +20 -0
- package/dist/mcp/config.js +121 -8
- package/dist/mcp/import-normalize.d.ts +12 -0
- package/dist/mcp/import-normalize.js +114 -0
- package/dist/mcp/mcp-oauth-constants.d.ts +8 -0
- package/dist/mcp/mcp-oauth-constants.js +9 -0
- package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
- package/dist/mcp/mcp-oauth-provider.js +241 -0
- package/dist/mcp/mcp-oauth.d.ts +60 -1
- package/dist/mcp/mcp-oauth.js +241 -65
- package/dist/mcp/transports.d.ts +14 -5
- package/dist/mcp/transports.js +112 -101
- package/dist/mcp/types.d.ts +5 -0
- package/dist/media/comfyui-recipe-contract.d.ts +7 -7
- package/dist/memory/facts-memory.d.ts +11 -1
- package/dist/memory/facts-memory.js +47 -8
- package/dist/memory/memory-candidate-queue.d.ts +2 -2
- package/dist/memory/memory-candidate-queue.js +6 -6
- package/dist/memory/persistent-memory.d.ts +13 -8
- package/dist/memory/persistent-memory.js +77 -30
- package/dist/nodes/device-node.js +5 -9
- package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
- package/dist/observability/run-event-writer.d.ts +21 -0
- package/dist/observability/run-event-writer.js +81 -0
- package/dist/observability/run-store.d.ts +5 -0
- package/dist/observability/run-store.js +18 -13
- package/dist/observability/run-trajectory.js +1 -1
- package/dist/optimization/prompt-cache.d.ts +19 -2
- package/dist/optimization/prompt-cache.js +41 -6
- package/dist/orchestration/orchestrator.d.ts +21 -0
- package/dist/orchestration/orchestrator.js +122 -15
- package/dist/persistence/session-content.d.ts +8 -0
- package/dist/persistence/session-content.js +76 -0
- package/dist/persistence/session-handoff.d.ts +64 -0
- package/dist/persistence/session-handoff.js +80 -0
- package/dist/persistence/session-history.d.ts +4 -0
- package/dist/persistence/session-history.js +38 -0
- package/dist/persistence/session-store.d.ts +9 -0
- package/dist/persistence/session-store.js +48 -20
- package/dist/personas/persona-manager.js +17 -1
- package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
- package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
- package/dist/plugins/plugin-manager.js +10 -1
- package/dist/prompts/prompt-manager.js +11 -4
- package/dist/prompts/system-base.js +6 -2
- package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
- package/dist/protocols/a2a/codebuddy-executor.js +9 -8
- package/dist/protocols/a2a/index.d.ts +1 -1
- package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
- package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
- package/dist/protocols/a2a/peer-config.d.ts +20 -0
- package/dist/protocols/a2a/peer-config.js +55 -0
- package/dist/providers/codex-oauth.js +30 -6
- package/dist/providers/model-provider-compat.js +2 -1
- package/dist/sandbox/os-sandbox.d.ts +1 -1
- package/dist/scheduler/cron-scheduler.d.ts +15 -2
- package/dist/scheduler/cron-scheduler.js +35 -4
- package/dist/scheduler/job-notepad.d.ts +69 -0
- package/dist/scheduler/job-notepad.js +339 -0
- package/dist/security/audit-logger.d.ts +1 -1
- package/dist/security/compute-confinement.d.ts +4 -0
- package/dist/security/compute-confinement.js +89 -0
- package/dist/security/session-encryption.d.ts +4 -0
- package/dist/security/session-encryption.js +31 -0
- package/dist/sensory/voice-loop.d.ts +17 -0
- package/dist/sensory/voice-loop.js +72 -15
- package/dist/server/auth/device-session-context.d.ts +9 -0
- package/dist/server/auth/device-session-context.js +13 -0
- package/dist/server/auth/device-store.d.ts +89 -0
- package/dist/server/auth/device-store.js +261 -0
- package/dist/server/auth/device-token.d.ts +3 -0
- package/dist/server/auth/device-token.js +6 -0
- package/dist/server/auth/jwt.js +4 -0
- package/dist/server/index.d.ts +0 -10
- package/dist/server/index.js +28 -0
- package/dist/server/middleware/auth.js +3 -2
- package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
- package/dist/server/routes/a2a-jsonrpc.js +66 -0
- package/dist/server/routes/a2a-protocol.d.ts +12 -0
- package/dist/server/routes/a2a-protocol.js +1 -1
- package/dist/server/routes/device-auth.d.ts +4 -0
- package/dist/server/routes/device-auth.js +40 -0
- package/dist/server/types.d.ts +4 -0
- package/dist/server/websocket/handler.d.ts +13 -0
- package/dist/server/websocket/handler.js +102 -17
- package/dist/services/prompt-builder.d.ts +2 -0
- package/dist/services/prompt-builder.js +16 -6
- package/dist/services/runtime-settings-context.d.ts +81 -0
- package/dist/services/runtime-settings-context.js +79 -0
- package/dist/skills/executor.js +2 -0
- package/dist/skills/hub.js +16 -14
- package/dist/skills/local-inventory.d.ts +21 -0
- package/dist/skills/local-inventory.js +72 -0
- package/dist/skills/skill-loader.js +2 -2
- package/dist/skills/skill-usage-store.d.ts +49 -0
- package/dist/skills/skill-usage-store.js +121 -0
- package/dist/testing/ai-integration-tests.js +8 -5
- package/dist/themes/theme-manager.d.ts +12 -0
- package/dist/themes/theme-manager.js +29 -0
- package/dist/themes/theme-schema.d.ts +24 -24
- package/dist/tools/a2a-call-tool.d.ts +34 -0
- package/dist/tools/a2a-call-tool.js +75 -0
- package/dist/tools/bash/streaming-executor.d.ts +1 -0
- package/dist/tools/bash/streaming-executor.js +78 -90
- package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
- package/dist/tools/code-exec-preflight-runner.js +254 -0
- package/dist/tools/code-exec-preflight.d.ts +44 -0
- package/dist/tools/code-exec-preflight.js +501 -0
- package/dist/tools/code-exec-tool.d.ts +48 -1
- package/dist/tools/code-exec-tool.js +191 -47
- package/dist/tools/comment-watcher.js +29 -12
- package/dist/tools/core-code-inspection.d.ts +56 -0
- package/dist/tools/core-code-inspection.js +86 -0
- package/dist/tools/create-skill-tool.d.ts +2 -3
- package/dist/tools/create-skill-tool.js +16 -75
- package/dist/tools/cronjob-tool.d.ts +1 -0
- package/dist/tools/cronjob-tool.js +1 -0
- package/dist/tools/env-doctor-tool.d.ts +2 -0
- package/dist/tools/env-doctor-tool.js +4 -3
- package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
- package/dist/tools/execute-code-rpc-invoker.js +35 -19
- package/dist/tools/execute-code-runner.d.ts +2 -0
- package/dist/tools/execute-code-runner.js +52 -17
- package/dist/tools/fleet-room-tool.d.ts +104 -0
- package/dist/tools/fleet-room-tool.js +124 -0
- package/dist/tools/integration-tool-hints.d.ts +4 -0
- package/dist/tools/integration-tool-hints.js +19 -0
- package/dist/tools/list-peers-tool.d.ts +3 -0
- package/dist/tools/list-peers-tool.js +11 -4
- package/dist/tools/media-generation-tool.d.ts +16 -1
- package/dist/tools/media-generation-tool.js +341 -9
- package/dist/tools/merge-conflict-tool.js +53 -25
- package/dist/tools/metadata.d.ts +1 -5
- package/dist/tools/metadata.js +30 -28
- package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
- package/dist/tools/peer-tool-invoke-tool.js +396 -0
- package/dist/tools/ragchat-tool.d.ts +39 -0
- package/dist/tools/ragchat-tool.js +105 -0
- package/dist/tools/registry/fleet-tools.d.ts +21 -3
- package/dist/tools/registry/fleet-tools.js +137 -4
- package/dist/tools/registry/index.d.ts +1 -1
- package/dist/tools/registry/index.js +2 -2
- package/dist/tools/registry/knowledge-tools.d.ts +2 -2
- package/dist/tools/registry/knowledge-tools.js +2 -2
- package/dist/tools/registry/lessons-tools.d.ts +7 -0
- package/dist/tools/registry/lessons-tools.js +43 -3
- package/dist/tools/registry/memory-tools.d.ts +1 -1
- package/dist/tools/registry/memory-tools.js +8 -8
- package/dist/tools/registry/research-tools.d.ts +1 -0
- package/dist/tools/registry/research-tools.js +4 -13
- package/dist/tools/registry/self-describe-tools.d.ts +1 -1
- package/dist/tools/registry/self-describe-tools.js +43 -30
- package/dist/tools/resource-catalog-tool.d.ts +38 -0
- package/dist/tools/resource-catalog-tool.js +46 -0
- package/dist/tools/skills-inspection-tool.js +30 -1
- package/dist/tools/stream-tool-output.d.ts +3 -0
- package/dist/tools/stream-tool-output.js +33 -0
- package/dist/tools/tool-call-scheduler.d.ts +10 -0
- package/dist/tools/tool-call-scheduler.js +58 -0
- package/dist/tools/tool-effect.d.ts +5 -0
- package/dist/tools/tool-effect.js +26 -0
- package/dist/tools/tool-search.d.ts +12 -11
- package/dist/tools/tool-search.js +67 -26
- package/dist/tools/types.d.ts +6 -0
- package/dist/ui/components/ChatHistory.d.ts +1 -1
- package/dist/ui/components/ChatHistory.js +2 -2
- package/dist/ui/components/ChatInput.d.ts +2 -1
- package/dist/ui/components/ChatInput.js +42 -88
- package/dist/ui/components/ChatInterface.d.ts +4 -2
- package/dist/ui/components/ChatInterface.js +27 -63
- package/dist/ui/components/CommandSuggestions.d.ts +0 -1
- package/dist/ui/components/CommandSuggestions.js +4 -6
- package/dist/ui/components/FileAutocomplete.js +1 -1
- package/dist/ui/components/KeyboardHelp.js +8 -6
- package/dist/ui/components/ModelSelection.js +6 -5
- package/dist/ui/components/StatusBar.d.ts +2 -1
- package/dist/ui/components/StatusBar.js +8 -7
- package/dist/ui/context/theme-context.js +3 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/utils/markdown-renderer.js +4 -13
- package/dist/utils/atomic-write.d.ts +22 -0
- package/dist/utils/atomic-write.js +29 -0
- package/dist/utils/bounded-output.d.ts +16 -0
- package/dist/utils/bounded-output.js +63 -0
- package/dist/utils/config-validation/schema.d.ts +2 -2
- package/dist/utils/device-store-file.d.ts +4 -0
- package/dist/utils/device-store-file.js +40 -0
- package/dist/utils/export-manager.d.ts +9 -0
- package/dist/utils/export-manager.js +19 -0
- package/dist/utils/first-use-hints.d.ts +25 -0
- package/dist/utils/first-use-hints.js +58 -0
- package/dist/utils/graceful-shutdown.d.ts +8 -0
- package/dist/utils/graceful-shutdown.js +23 -7
- package/dist/utils/history-manager.js +1 -1
- package/dist/utils/update-notifier.js +16 -18
- package/dist/wizard/environment-detection.d.ts +5 -1
- package/dist/wizard/environment-detection.js +49 -7
- package/dist/wizard/onboarding.d.ts +5 -1
- package/dist/wizard/onboarding.js +90 -30
- package/dist/wizard/provider-onboarding.d.ts +2 -0
- package/dist/wizard/provider-onboarding.js +55 -17
- package/package.json +7 -3
|
@@ -94,6 +94,15 @@ export declare class ExportManager extends EventEmitter {
|
|
|
94
94
|
filePath?: string;
|
|
95
95
|
error?: string;
|
|
96
96
|
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Export an in-memory conversation (e.g. the live CLI transcript) to a file,
|
|
99
|
+
* with the same redaction and formatting as session exports.
|
|
100
|
+
*/
|
|
101
|
+
exportConversationData(data: ConversationExport, format: ExportFormat, options?: Partial<ExportOptions>, prefix?: string): Promise<{
|
|
102
|
+
success: boolean;
|
|
103
|
+
filePath?: string;
|
|
104
|
+
error?: string;
|
|
105
|
+
}>;
|
|
97
106
|
/**
|
|
98
107
|
* Export session to string
|
|
99
108
|
*/
|
|
@@ -511,6 +511,25 @@ export class ExportManager extends EventEmitter {
|
|
|
511
511
|
return { success: false, error: errorMsg };
|
|
512
512
|
}
|
|
513
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Export an in-memory conversation (e.g. the live CLI transcript) to a file,
|
|
516
|
+
* with the same redaction and formatting as session exports.
|
|
517
|
+
*/
|
|
518
|
+
async exportConversationData(data, format, options = {}, prefix = 'conversation') {
|
|
519
|
+
try {
|
|
520
|
+
const content = this.formatExport(data, format, options);
|
|
521
|
+
const filePath = path.join(this.outputDir, this.generateFilename(prefix, format));
|
|
522
|
+
await this.ensureOutputDir();
|
|
523
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
524
|
+
this.emit('conversation:exported', { filePath, format });
|
|
525
|
+
return { success: true, filePath };
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
529
|
+
this.emit('error', error);
|
|
530
|
+
return { success: false, error: errorMsg };
|
|
531
|
+
}
|
|
532
|
+
}
|
|
514
533
|
/**
|
|
515
534
|
* Export session to string
|
|
516
535
|
*/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-use hints (comparatif plan P4, optional part).
|
|
3
|
+
*
|
|
4
|
+
* A short contextual tip shown ONCE per profile, the first time the user meets
|
|
5
|
+
* a surprising behaviour: a command unavailable on this surface, a message
|
|
6
|
+
* queued behind a running turn, a tool output shortened for the model (exact
|
|
7
|
+
* output recoverable through restore_context).
|
|
8
|
+
*
|
|
9
|
+
* Persistence: one marker file per hint under ~/.codebuddy/hints/ created with
|
|
10
|
+
* O_EXCL, so concurrent processes (CLI + Cowork) show a hint at most once.
|
|
11
|
+
* `CODEBUDDY_HINTS=off` disables every hint. Never throws: an unwritable
|
|
12
|
+
* profile means no hint rather than a repeated one.
|
|
13
|
+
*/
|
|
14
|
+
export type FirstUseHintId = 'surface_unavailable' | 'message_queued' | 'restore_context';
|
|
15
|
+
export declare const FIRST_USE_HINTS: Readonly<Record<FirstUseHintId, {
|
|
16
|
+
en: string;
|
|
17
|
+
fr: string;
|
|
18
|
+
}>>;
|
|
19
|
+
export declare function firstUseHintsDir(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Return the hint text the first time `id` is requested in this profile, then
|
|
22
|
+
* null forever. The marker is created before the text is returned.
|
|
23
|
+
*/
|
|
24
|
+
export declare function takeFirstUseHint(id: FirstUseHintId, lang?: 'en' | 'fr', dir?: string): string | null;
|
|
25
|
+
export declare function resetFirstUseHints(dir?: string): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-use hints (comparatif plan P4, optional part).
|
|
3
|
+
*
|
|
4
|
+
* A short contextual tip shown ONCE per profile, the first time the user meets
|
|
5
|
+
* a surprising behaviour: a command unavailable on this surface, a message
|
|
6
|
+
* queued behind a running turn, a tool output shortened for the model (exact
|
|
7
|
+
* output recoverable through restore_context).
|
|
8
|
+
*
|
|
9
|
+
* Persistence: one marker file per hint under ~/.codebuddy/hints/ created with
|
|
10
|
+
* O_EXCL, so concurrent processes (CLI + Cowork) show a hint at most once.
|
|
11
|
+
* `CODEBUDDY_HINTS=off` disables every hint. Never throws: an unwritable
|
|
12
|
+
* profile means no hint rather than a repeated one.
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import os from 'node:os';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
export const FIRST_USE_HINTS = Object.freeze({
|
|
18
|
+
surface_unavailable: {
|
|
19
|
+
en: 'Tip: greyed-out commands are not available on this surface yet; run them in the terminal with `buddy`.',
|
|
20
|
+
fr: 'Astuce : les commandes grisées ne sont pas encore pilotables ici ; lance-les dans un terminal avec `buddy`.',
|
|
21
|
+
},
|
|
22
|
+
message_queued: {
|
|
23
|
+
en: 'Tip: this message was queued while Buddy was busy and runs now; messages sent during a turn always run in order.',
|
|
24
|
+
fr: 'Astuce : un message envoyé pendant un tour est mis en file et partira à la fin du tour en cours.',
|
|
25
|
+
},
|
|
26
|
+
restore_context: {
|
|
27
|
+
en: 'Tip: long tool outputs are shortened for the model; the exact output stays recoverable with restore_context.',
|
|
28
|
+
fr: 'Astuce : les longues sorties d’outils sont raccourcies pour le modèle ; la sortie exacte reste récupérable avec restore_context.',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
export function firstUseHintsDir() {
|
|
32
|
+
return process.env.CODEBUDDY_HINTS_DIR || path.join(os.homedir(), '.codebuddy', 'hints');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Return the hint text the first time `id` is requested in this profile, then
|
|
36
|
+
* null forever. The marker is created before the text is returned.
|
|
37
|
+
*/
|
|
38
|
+
export function takeFirstUseHint(id, lang = 'en', dir = firstUseHintsDir()) {
|
|
39
|
+
if ((process.env.CODEBUDDY_HINTS ?? '').toLowerCase() === 'off')
|
|
40
|
+
return null;
|
|
41
|
+
const hint = FIRST_USE_HINTS[id];
|
|
42
|
+
if (!hint)
|
|
43
|
+
return null;
|
|
44
|
+
try {
|
|
45
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
46
|
+
fs.writeFileSync(path.join(dir, id), `${new Date().toISOString()}\n`, { flag: 'wx', mode: 0o600 });
|
|
47
|
+
return hint[lang];
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function resetFirstUseHints(dir = firstUseHintsDir()) {
|
|
54
|
+
for (const id of Object.keys(FIRST_USE_HINTS)) {
|
|
55
|
+
fs.rmSync(path.join(dir, id), { force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=first-use-hints.js.map
|
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
* Intercepts signals: SIGINT (Ctrl+C), SIGTERM (kill), SIGHUP (terminal closed)
|
|
12
12
|
*/
|
|
13
13
|
import { type Disposable } from './disposable.js';
|
|
14
|
+
/**
|
|
15
|
+
* Exit code after a shutdown signal. Only an explicit headless run (`-p`, loop,
|
|
16
|
+
* try: CODEBUDDY_HEADLESS=true) reports SIGINT as 130 (USER_CANCELLED), so a
|
|
17
|
+
* script can tell an interruption from success. Servers and daemons whose stdout
|
|
18
|
+
* is merely not a TTY (systemd) keep exit 0: a normal SIGTERM stop must not be
|
|
19
|
+
* reported as a failure and trigger Restart=on-failure.
|
|
20
|
+
*/
|
|
21
|
+
export declare function signalExitCode(signal: string, env?: NodeJS.ProcessEnv): number;
|
|
14
22
|
export interface ShutdownOptions {
|
|
15
23
|
/** Maximum time to wait for shutdown in milliseconds (default: 30000) */
|
|
16
24
|
timeoutMs: number;
|
|
@@ -12,6 +12,22 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { logger, getLogger } from './logger.js';
|
|
14
14
|
import { DisposableManager, registerDisposable } from './disposable.js';
|
|
15
|
+
import { EXIT_CODES } from './exit-codes.js';
|
|
16
|
+
/**
|
|
17
|
+
* Exit code after a shutdown signal. Only an explicit headless run (`-p`, loop,
|
|
18
|
+
* try: CODEBUDDY_HEADLESS=true) reports SIGINT as 130 (USER_CANCELLED), so a
|
|
19
|
+
* script can tell an interruption from success. Servers and daemons whose stdout
|
|
20
|
+
* is merely not a TTY (systemd) keep exit 0: a normal SIGTERM stop must not be
|
|
21
|
+
* reported as a failure and trigger Restart=on-failure.
|
|
22
|
+
*/
|
|
23
|
+
export function signalExitCode(signal, env = process.env) {
|
|
24
|
+
if (env.CODEBUDDY_HEADLESS !== 'true')
|
|
25
|
+
return 0;
|
|
26
|
+
return signal === 'SIGINT' ? EXIT_CODES.USER_CANCELLED : 0;
|
|
27
|
+
}
|
|
28
|
+
function shutdownProgressStream() {
|
|
29
|
+
return process.stdout.isTTY ? process.stdout : process.stderr;
|
|
30
|
+
}
|
|
15
31
|
const DEFAULT_OPTIONS = {
|
|
16
32
|
timeoutMs: 30000, // 30 seconds max before force exit
|
|
17
33
|
forceExitOnTimeout: true,
|
|
@@ -69,7 +85,7 @@ export class GracefulShutdownManager {
|
|
|
69
85
|
// Show user-friendly message
|
|
70
86
|
logger.info(`Received ${signal}. Shutting down gracefully...`);
|
|
71
87
|
try {
|
|
72
|
-
await this.shutdown({ exitCode:
|
|
88
|
+
await this.shutdown({ exitCode: signalExitCode(signal) });
|
|
73
89
|
}
|
|
74
90
|
catch (error) {
|
|
75
91
|
logger.error('Error during shutdown', error instanceof Error ? error : new Error(String(error)));
|
|
@@ -232,7 +248,7 @@ export class GracefulShutdownManager {
|
|
|
232
248
|
for (const handler of this.handlers) {
|
|
233
249
|
try {
|
|
234
250
|
if (showProgress) {
|
|
235
|
-
|
|
251
|
+
shutdownProgressStream().write(` [shutdown] ${handler.name}...`);
|
|
236
252
|
}
|
|
237
253
|
logger.debug(`Running shutdown handler: ${handler.name}`);
|
|
238
254
|
const result = handler.handler();
|
|
@@ -241,14 +257,14 @@ export class GracefulShutdownManager {
|
|
|
241
257
|
}
|
|
242
258
|
this.completedHandlers.add(handler.name);
|
|
243
259
|
if (showProgress) {
|
|
244
|
-
|
|
260
|
+
shutdownProgressStream().write(' done\n');
|
|
245
261
|
}
|
|
246
262
|
logger.debug(`Completed shutdown handler: ${handler.name}`);
|
|
247
263
|
}
|
|
248
264
|
catch (error) {
|
|
249
265
|
this.completedHandlers.add(handler.name); // Mark as completed even on error
|
|
250
266
|
if (showProgress) {
|
|
251
|
-
|
|
267
|
+
shutdownProgressStream().write(' failed\n');
|
|
252
268
|
}
|
|
253
269
|
logger.error(`Error in shutdown handler ${handler.name}:`, error instanceof Error ? error : new Error(String(error)));
|
|
254
270
|
// Continue with other handlers even if one fails
|
|
@@ -257,18 +273,18 @@ export class GracefulShutdownManager {
|
|
|
257
273
|
// Then, dispose all registered disposables
|
|
258
274
|
try {
|
|
259
275
|
if (showProgress) {
|
|
260
|
-
|
|
276
|
+
shutdownProgressStream().write(' [shutdown] disposing resources...');
|
|
261
277
|
}
|
|
262
278
|
logger.debug('Disposing all registered resources...');
|
|
263
279
|
await DisposableManager.getInstance().disposeAll();
|
|
264
280
|
if (showProgress) {
|
|
265
|
-
|
|
281
|
+
shutdownProgressStream().write(' done\n');
|
|
266
282
|
}
|
|
267
283
|
logger.debug('All resources disposed');
|
|
268
284
|
}
|
|
269
285
|
catch (error) {
|
|
270
286
|
if (showProgress) {
|
|
271
|
-
|
|
287
|
+
shutdownProgressStream().write(' failed\n');
|
|
272
288
|
}
|
|
273
289
|
logger.error('Error disposing resources:', error instanceof Error ? error : new Error(String(error)));
|
|
274
290
|
}
|
|
@@ -29,7 +29,7 @@ export class HistoryManager {
|
|
|
29
29
|
};
|
|
30
30
|
static DEFAULT_CONFIG = {
|
|
31
31
|
maxEntries: 1000, // Increased default limit
|
|
32
|
-
historyFile
|
|
32
|
+
get historyFile() { return path.join(os.homedir(), '.codebuddy', 'history.json'); },
|
|
33
33
|
excludePrefixes: [], // Include all commands by default
|
|
34
34
|
includeSlashCommands: true, // Include slash commands
|
|
35
35
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Checks for new versions and notifies users (mistral-vibe style).
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
9
|
import https from 'https';
|
|
@@ -24,27 +24,17 @@ const DEFAULT_CONFIG = {
|
|
|
24
24
|
*/
|
|
25
25
|
function getCurrentVersion() {
|
|
26
26
|
try {
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// npm global install (node_modules/@phuetz/code-buddy/package.json)
|
|
33
|
-
join(process.execPath, '..', '..', 'lib', 'node_modules', '@phuetz', 'code-buddy', 'package.json'),
|
|
34
|
-
];
|
|
35
|
-
for (const pkgPath of possiblePaths) {
|
|
36
|
-
if (existsSync(pkgPath)) {
|
|
37
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
38
|
-
if (pkg.name?.includes('code-buddy')) {
|
|
39
|
-
return pkg.version || '0.0.0';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
27
|
+
// Resolve our own manifest in both src/utils and dist/utils. The caller's
|
|
28
|
+
// working directory and Node executable do not identify a global npm install.
|
|
29
|
+
const pkg = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf-8'));
|
|
30
|
+
if (pkg.name === '@phuetz/code-buddy' && typeof pkg.version === 'string' && pkg.version.trim()) {
|
|
31
|
+
return pkg.version;
|
|
42
32
|
}
|
|
43
|
-
return '0.0.0';
|
|
44
33
|
}
|
|
45
34
|
catch {
|
|
46
|
-
|
|
35
|
+
// An unknown installed version must never produce a fictitious upgrade.
|
|
47
36
|
}
|
|
37
|
+
return null;
|
|
48
38
|
}
|
|
49
39
|
/**
|
|
50
40
|
* Fetch latest version from npm registry
|
|
@@ -147,6 +137,10 @@ export class UpdateNotifier {
|
|
|
147
137
|
return null;
|
|
148
138
|
}
|
|
149
139
|
const currentVersion = getCurrentVersion();
|
|
140
|
+
if (!currentVersion) {
|
|
141
|
+
this.updateInfo = null;
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
150
144
|
// Check cache first
|
|
151
145
|
const cache = loadCache();
|
|
152
146
|
if (cache && !shouldCheckForUpdates(this.config)) {
|
|
@@ -184,6 +178,10 @@ export class UpdateNotifier {
|
|
|
184
178
|
*/
|
|
185
179
|
async forceCheck() {
|
|
186
180
|
const currentVersion = getCurrentVersion();
|
|
181
|
+
if (!currentVersion) {
|
|
182
|
+
this.updateInfo = null;
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
187
185
|
const latestVersion = await fetchLatestVersion(this.config.packageName, this.config.registryUrl);
|
|
188
186
|
if (!latestVersion) {
|
|
189
187
|
return null;
|
|
@@ -54,6 +54,8 @@ export declare function detectOllama(): Promise<DetectedCapability>;
|
|
|
54
54
|
export declare function detectLmStudio(): Promise<DetectedCapability>;
|
|
55
55
|
/** Check the ChatGPT (Codex) OAuth credential file. */
|
|
56
56
|
export declare function detectChatGptOAuth(): Promise<DetectedCapability>;
|
|
57
|
+
/** Offline variant: credential presence only, never refreshes tokens. */
|
|
58
|
+
export declare function detectChatGptOAuthOffline(): Promise<DetectedCapability>;
|
|
57
59
|
/** Check the xAI (Grok) OAuth credential file. */
|
|
58
60
|
export declare function detectXaiOAuth(): Promise<DetectedCapability>;
|
|
59
61
|
/** Report API keys present in the environment (not billed to us, so free:false). */
|
|
@@ -62,6 +64,8 @@ export declare function detectApiKeys(): DetectedCapability[];
|
|
|
62
64
|
* One call to learn everything the setup surfaces need. Free local/OAuth probes
|
|
63
65
|
* run in parallel; the API-key scan is synchronous.
|
|
64
66
|
*/
|
|
65
|
-
export declare function detectEnvironment(
|
|
67
|
+
export declare function detectEnvironment(options?: {
|
|
68
|
+
offline?: boolean;
|
|
69
|
+
}): Promise<EnvironmentSnapshot>;
|
|
66
70
|
/** Compact human summary of what was detected — used by wizard + doctor. */
|
|
67
71
|
export declare function renderDetectionSummary(snapshot: EnvironmentSnapshot): string;
|
|
@@ -121,6 +121,40 @@ export async function detectChatGptOAuth() {
|
|
|
121
121
|
return base;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
+
/** Offline variant: credential presence only, never refreshes tokens. */
|
|
125
|
+
export async function detectChatGptOAuthOffline() {
|
|
126
|
+
const base = {
|
|
127
|
+
id: 'chatgpt',
|
|
128
|
+
label: 'ChatGPT subscription (OAuth, $0)',
|
|
129
|
+
kind: 'oauth',
|
|
130
|
+
free: true,
|
|
131
|
+
available: false,
|
|
132
|
+
detail: 'not signed in',
|
|
133
|
+
setupCommand: 'buddy login',
|
|
134
|
+
};
|
|
135
|
+
try {
|
|
136
|
+
const { hasCodexCredentials } = await import('../providers/codex-oauth.js');
|
|
137
|
+
if (!hasCodexCredentials())
|
|
138
|
+
return base;
|
|
139
|
+
return { ...base, available: true, detail: 'credentials present (not verified offline)', setupCommand: undefined };
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return base;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function notProbedLocalRuntime(id, label, rawHost, setupCommand) {
|
|
146
|
+
const host = normalizeHost(rawHost);
|
|
147
|
+
return Promise.resolve({
|
|
148
|
+
id,
|
|
149
|
+
label,
|
|
150
|
+
kind: 'local',
|
|
151
|
+
free: true,
|
|
152
|
+
available: false,
|
|
153
|
+
detail: 'not probed (--offline)',
|
|
154
|
+
baseURL: `${host}/v1`,
|
|
155
|
+
setupCommand,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
124
158
|
/** Check the xAI (Grok) OAuth credential file. */
|
|
125
159
|
export async function detectXaiOAuth() {
|
|
126
160
|
const base = {
|
|
@@ -167,13 +201,21 @@ export function detectApiKeys() {
|
|
|
167
201
|
* One call to learn everything the setup surfaces need. Free local/OAuth probes
|
|
168
202
|
* run in parallel; the API-key scan is synchronous.
|
|
169
203
|
*/
|
|
170
|
-
export async function detectEnvironment() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
204
|
+
export async function detectEnvironment(options = {}) {
|
|
205
|
+
// Offline: no HTTP probe and no OAuth refresh — only local credential files are read.
|
|
206
|
+
const [ollama, lmstudio, chatgpt, xai] = await Promise.all(options.offline
|
|
207
|
+
? [
|
|
208
|
+
notProbedLocalRuntime('ollama', 'Ollama (local, $0)', process.env.OLLAMA_HOST || OLLAMA_DEFAULT_HOST, 'ollama serve'),
|
|
209
|
+
notProbedLocalRuntime('lmstudio', 'LM Studio (local, $0)', process.env.LMSTUDIO_HOST || LMSTUDIO_DEFAULT_HOST, 'Start the LM Studio local server'),
|
|
210
|
+
detectChatGptOAuthOffline(),
|
|
211
|
+
detectXaiOAuth(),
|
|
212
|
+
]
|
|
213
|
+
: [
|
|
214
|
+
detectOllama(),
|
|
215
|
+
detectLmStudio(),
|
|
216
|
+
detectChatGptOAuth(),
|
|
217
|
+
detectXaiOAuth(),
|
|
218
|
+
]);
|
|
177
219
|
const apiKeys = detectApiKeys();
|
|
178
220
|
const capabilities = [ollama, lmstudio, chatgpt, xai, ...apiKeys];
|
|
179
221
|
// Free path priority: a local Ollama that actually has a model → ChatGPT
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
1
2
|
import { type EnvironmentSnapshot } from './environment-detection.js';
|
|
2
3
|
export interface OnboardingResult {
|
|
3
4
|
provider: string;
|
|
@@ -47,7 +48,7 @@ export declare const ONBOARDING_PHASES: OnboardingPhase[];
|
|
|
47
48
|
* - an entered API key → encrypted credential store (never plaintext in settings),
|
|
48
49
|
* plus `process.env[envVar]` so the current process resolves immediately.
|
|
49
50
|
* - ollama → default `OLLAMA_HOST` so auto-detection picks it up.
|
|
50
|
-
*
|
|
51
|
+
* Persistence errors propagate so the wizard cannot report a false success.
|
|
51
52
|
*/
|
|
52
53
|
export declare function persistProviderSelection(guide: OnboardingProviderGuide, model: string, apiKey: string): Promise<void>;
|
|
53
54
|
export declare const PROJECT_FOLDER_QUESTION = "Which project folder should Code Buddy use?";
|
|
@@ -67,4 +68,7 @@ export declare function renderCapabilitiesFooter(): string;
|
|
|
67
68
|
* recommended free path at index 0. Availability annotations are appended to
|
|
68
69
|
* the label so the newcomer sees "(detected)" without extra prose. */
|
|
69
70
|
export declare function orderGuidesByDetection(snapshot: EnvironmentSnapshot): OnboardingProviderGuide[];
|
|
71
|
+
/** Offer to run the isolated 60-second demo right after setup — ending the
|
|
72
|
+
* wizard on proof (a green test), not prose. */
|
|
73
|
+
export declare function offerOnboardingDemo(rl: readline.Interface, result: OnboardingResult): Promise<void>;
|
|
70
74
|
export declare function runOnboarding(): Promise<OnboardingResult | null>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
|
-
import {
|
|
3
|
+
import { existsSync, readFileSync, mkdirSync } from 'fs';
|
|
4
|
+
import { writeJsonAtomicSync } from '../utils/atomic-write.js';
|
|
4
5
|
import { isAbsolute, join, resolve } from 'path';
|
|
5
6
|
import { getValidationConfigForGuide, validateProviderKey, } from './provider-onboarding.js';
|
|
6
7
|
import { detectEnvironment, renderDetectionSummary, } from './environment-detection.js';
|
|
@@ -165,31 +166,39 @@ export const ONBOARDING_PHASES = [
|
|
|
165
166
|
successCheck: 'the selected layer has a doctor/status command or a focused smoke prompt.',
|
|
166
167
|
},
|
|
167
168
|
];
|
|
168
|
-
const TTS_PROVIDERS = ['
|
|
169
|
+
const TTS_PROVIDERS = ['piper', 'espeak', 'say', 'audioreader'];
|
|
170
|
+
class OnboardingCancelled extends Error {
|
|
171
|
+
}
|
|
169
172
|
function ask(rl, question, defaultValue) {
|
|
170
|
-
return new Promise((resolve) => {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const onClose = () => reject(new OnboardingCancelled('Setup cancelled.'));
|
|
175
|
+
rl.once('close', onClose);
|
|
171
176
|
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
172
177
|
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
178
|
+
rl.removeListener('close', onClose);
|
|
173
179
|
resolve(answer.trim() || defaultValue || '');
|
|
174
180
|
});
|
|
175
181
|
});
|
|
176
182
|
}
|
|
177
|
-
function askChoice(rl, question, choices, defaultIdx) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
rl
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
183
|
+
async function askChoice(rl, question, choices, defaultIdx) {
|
|
184
|
+
console.log(`\n ${question}`);
|
|
185
|
+
choices.forEach((c, i) => console.log(` ${i + 1}. ${c}${i === defaultIdx ? ' (default)' : ''}`));
|
|
186
|
+
while (true) {
|
|
187
|
+
const answer = await ask(rl, 'Choice', String(defaultIdx + 1));
|
|
188
|
+
const idx = Number(answer) - 1;
|
|
189
|
+
if (/^\d+$/.test(answer) && Number.isInteger(idx) && idx >= 0 && idx < choices.length) {
|
|
190
|
+
return choices[idx];
|
|
191
|
+
}
|
|
192
|
+
console.log(` Please enter a number from 1 to ${choices.length}.`);
|
|
193
|
+
}
|
|
187
194
|
}
|
|
188
195
|
/** Read a secret without echoing it (masks keystrokes with '*'). The captured
|
|
189
196
|
* value is always correct regardless of echo; masking is cosmetic, and falls
|
|
190
197
|
* back to a visible prompt if the terminal doesn't support muting. */
|
|
191
198
|
function askSecret(rl, question) {
|
|
192
|
-
return new Promise((resolve) => {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const onClose = () => reject(new OnboardingCancelled('Setup cancelled.'));
|
|
201
|
+
rl.once('close', onClose);
|
|
193
202
|
const rlAny = rl;
|
|
194
203
|
const original = rlAny._writeToOutput?.bind(rl);
|
|
195
204
|
let muted = false;
|
|
@@ -202,6 +211,7 @@ function askSecret(rl, question) {
|
|
|
202
211
|
};
|
|
203
212
|
}
|
|
204
213
|
rl.question(` ${question}: `, (answer) => {
|
|
214
|
+
rl.removeListener('close', onClose);
|
|
205
215
|
if (typeof original === 'function')
|
|
206
216
|
rlAny._writeToOutput = original;
|
|
207
217
|
resolve(answer.trim());
|
|
@@ -216,10 +226,10 @@ function askSecret(rl, question) {
|
|
|
216
226
|
* - an entered API key → encrypted credential store (never plaintext in settings),
|
|
217
227
|
* plus `process.env[envVar]` so the current process resolves immediately.
|
|
218
228
|
* - ollama → default `OLLAMA_HOST` so auto-detection picks it up.
|
|
219
|
-
*
|
|
229
|
+
* Persistence errors propagate so the wizard cannot report a false success.
|
|
220
230
|
*/
|
|
221
231
|
export async function persistProviderSelection(guide, model, apiKey) {
|
|
222
|
-
|
|
232
|
+
{
|
|
223
233
|
const { getSettingsManager } = await import('../utils/settings-manager.js');
|
|
224
234
|
getSettingsManager().saveUserSettings({
|
|
225
235
|
provider: guide.id,
|
|
@@ -229,13 +239,11 @@ export async function persistProviderSelection(guide, model, apiKey) {
|
|
|
229
239
|
...(guide.baseURL ? { baseURL: guide.baseURL } : {}),
|
|
230
240
|
});
|
|
231
241
|
}
|
|
232
|
-
catch { /* non-fatal */ }
|
|
233
242
|
if (guide.authMode === 'api-key' && apiKey) {
|
|
234
|
-
|
|
243
|
+
{
|
|
235
244
|
const { getCredentialManager } = await import('../security/credential-manager.js');
|
|
236
245
|
getCredentialManager().setApiKey(apiKey);
|
|
237
246
|
}
|
|
238
|
-
catch { /* non-fatal */ }
|
|
239
247
|
if (guide.envVar)
|
|
240
248
|
process.env[guide.envVar] = apiKey;
|
|
241
249
|
}
|
|
@@ -255,7 +263,13 @@ export function writeConfig(configDir, result) {
|
|
|
255
263
|
mkdirSync(configDir, { recursive: true });
|
|
256
264
|
const authMode = result.authMode ?? getProviderGuide(result.provider).authMode;
|
|
257
265
|
const recommendedNextCommands = result.recommendedNextCommands ?? buildRecommendedNextCommands(result);
|
|
266
|
+
const configPath = join(configDir, 'config.json');
|
|
267
|
+
const previous = existsSync(configPath) ? JSON.parse(readFileSync(configPath, 'utf8')) : {};
|
|
268
|
+
if (!previous || typeof previous !== 'object' || Array.isArray(previous)) {
|
|
269
|
+
throw new Error('Existing project config must be a JSON object; it was not overwritten.');
|
|
270
|
+
}
|
|
258
271
|
const config = {
|
|
272
|
+
...previous,
|
|
259
273
|
provider: result.provider,
|
|
260
274
|
model: result.model,
|
|
261
275
|
authMode,
|
|
@@ -269,7 +283,9 @@ export function writeConfig(configDir, result) {
|
|
|
269
283
|
if (result.ttsProvider) {
|
|
270
284
|
config.ttsProvider = result.ttsProvider;
|
|
271
285
|
}
|
|
272
|
-
|
|
286
|
+
if (!result.ttsEnabled)
|
|
287
|
+
delete config.ttsProvider;
|
|
288
|
+
writeJsonAtomicSync(configPath, config);
|
|
273
289
|
}
|
|
274
290
|
export function getProviderGuide(provider) {
|
|
275
291
|
return (PROVIDER_GUIDES.find((guide) => guide.id === provider)
|
|
@@ -460,7 +476,7 @@ async function runQuickStart(rl, snapshot, projectDir) {
|
|
|
460
476
|
const free = snapshot.recommendedFree;
|
|
461
477
|
const ollama = snapshot.capabilities.find((c) => c.id === 'ollama');
|
|
462
478
|
// Case A: a ready free path exists — offer it directly.
|
|
463
|
-
if (free) {
|
|
479
|
+
if (free && PROVIDER_GUIDES.some((guide) => guide.id === free.id)) {
|
|
464
480
|
const answer = (await ask(rl, `Start now on ${free.label} — free, no API key needed? (Y/n)`, 'y')).toLowerCase();
|
|
465
481
|
if (answer === 'y' || answer === 'yes' || answer === '') {
|
|
466
482
|
const guide = getProviderGuide(free.id);
|
|
@@ -514,17 +530,40 @@ function finishQuickStart(provider, model, baseURL, projectDir) {
|
|
|
514
530
|
}
|
|
515
531
|
/** Offer to run the isolated 60-second demo right after setup — ending the
|
|
516
532
|
* wizard on proof (a green test), not prose. */
|
|
517
|
-
async function
|
|
533
|
+
export async function offerOnboardingDemo(rl, result) {
|
|
534
|
+
if (!['chatgpt', 'ollama', 'lmstudio'].includes(result.provider)) {
|
|
535
|
+
console.log(' Verify this provider with the first-chat command below.');
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
518
538
|
const answer = (await ask(rl, 'Run the 60-second demo now to confirm it works? (Y/n)', 'y')).toLowerCase();
|
|
519
539
|
if (answer === 'y' || answer === 'yes' || answer === '') {
|
|
520
540
|
try {
|
|
521
541
|
const { runTryDemo } = await import('../commands/try.js');
|
|
522
542
|
rl.pause();
|
|
523
|
-
await
|
|
524
|
-
|
|
543
|
+
const { getSettingsManager } = await import('../utils/settings-manager.js');
|
|
544
|
+
const code = await runTryDemo({
|
|
545
|
+
verbose: false,
|
|
546
|
+
resolveProvider: async () => ({
|
|
547
|
+
kind: result.provider === 'chatgpt' ? 'chatgpt' : 'ollama',
|
|
548
|
+
label: `${result.provider} (${result.model})`,
|
|
549
|
+
apiKey: result.provider === 'chatgpt' ? 'oauth-chatgpt' : 'local',
|
|
550
|
+
baseURL: result.provider === 'chatgpt'
|
|
551
|
+
? 'https://chatgpt.com/backend-api/codex'
|
|
552
|
+
: getSettingsManager().getBaseURL(),
|
|
553
|
+
model: result.model,
|
|
554
|
+
}),
|
|
555
|
+
});
|
|
556
|
+
if (code !== 0) {
|
|
557
|
+
process.exitCode = code;
|
|
558
|
+
console.log(' Setup saved, but the demo failed. First chat is not verified.');
|
|
559
|
+
}
|
|
525
560
|
}
|
|
526
561
|
catch {
|
|
527
|
-
|
|
562
|
+
process.exitCode = 1;
|
|
563
|
+
console.log('\n The demo failed. First chat is not verified.\n');
|
|
564
|
+
}
|
|
565
|
+
finally {
|
|
566
|
+
rl.resume();
|
|
528
567
|
}
|
|
529
568
|
}
|
|
530
569
|
else {
|
|
@@ -562,7 +601,7 @@ export async function runOnboarding() {
|
|
|
562
601
|
if (quick) {
|
|
563
602
|
console.log(renderCapabilitiesFooter());
|
|
564
603
|
console.log('');
|
|
565
|
-
await
|
|
604
|
+
await offerOnboardingDemo(rl, quick);
|
|
566
605
|
return quick;
|
|
567
606
|
}
|
|
568
607
|
console.log(renderOnboardingRoadmap());
|
|
@@ -571,8 +610,15 @@ export async function runOnboarding() {
|
|
|
571
610
|
// default lands on the recommended free path when there is one.
|
|
572
611
|
const orderedGuides = orderGuidesByDetection(snapshot);
|
|
573
612
|
const provider = await askChoice(rl, 'Which AI provider do you want to use?', orderedGuides.map((guide) => `${guide.id} — ${guide.label}`), 0).then((choice) => choice.split(/\s+—\s+/)[0] ?? choice);
|
|
574
|
-
const
|
|
575
|
-
const
|
|
613
|
+
const selectedGuide = getProviderGuide(provider);
|
|
614
|
+
const detected = snapshot.capabilities.find((capability) => capability.id === provider);
|
|
615
|
+
const guide = selectedGuide.authMode === 'local' && detected?.baseURL
|
|
616
|
+
? { ...selectedGuide, baseURL: detected.baseURL }
|
|
617
|
+
: selectedGuide;
|
|
618
|
+
const defaultValidation = getValidationConfigForGuide(provider);
|
|
619
|
+
const validationConfig = guide.authMode === 'local' && guide.baseURL && defaultValidation
|
|
620
|
+
? { ...defaultValidation, baseUrl: guide.baseURL.replace(/\/v1\/?$/, '') }
|
|
621
|
+
: defaultValidation;
|
|
576
622
|
// 2. Authentication — captured AND verified inline so you leave the wizard
|
|
577
623
|
// ready to chat. A rejected key never gets persisted; a successful probe
|
|
578
624
|
// also hands us the provider's real model list for step 3.
|
|
@@ -651,10 +697,18 @@ export async function runOnboarding() {
|
|
|
651
697
|
recommendedNextCommands: buildRecommendedNextCommands({ provider, apiKey, model }),
|
|
652
698
|
...(ttsProvider ? { ttsProvider } : {}),
|
|
653
699
|
};
|
|
700
|
+
{
|
|
701
|
+
const { getTTSManager } = await import('../input/text-to-speech.js');
|
|
702
|
+
getTTSManager().updateConfig({
|
|
703
|
+
enabled: ttsEnabled,
|
|
704
|
+
autoSpeak: ttsEnabled,
|
|
705
|
+
...(ttsProvider ? { provider: ttsProvider } : {}),
|
|
706
|
+
});
|
|
707
|
+
}
|
|
654
708
|
// 6. Write project config into the folder the user chose
|
|
655
709
|
applyOnboardingProjectConfig(projectDir, result);
|
|
656
710
|
// 7. Summary
|
|
657
|
-
const ready = verified
|
|
711
|
+
const ready = verified;
|
|
658
712
|
console.log('');
|
|
659
713
|
console.log(` ${ready ? 'Setup complete — you\'re ready to go!' : 'Setup saved.'}`);
|
|
660
714
|
console.log('');
|
|
@@ -688,10 +742,16 @@ export async function runOnboarding() {
|
|
|
688
742
|
console.log('');
|
|
689
743
|
// Close with a real, runnable test so the wizard ends on proof, not prose.
|
|
690
744
|
if (ready) {
|
|
691
|
-
await
|
|
745
|
+
await offerOnboardingDemo(rl, result);
|
|
692
746
|
}
|
|
693
747
|
return result;
|
|
694
748
|
}
|
|
749
|
+
catch (error) {
|
|
750
|
+
if (!(error instanceof OnboardingCancelled))
|
|
751
|
+
throw error;
|
|
752
|
+
console.log('\n Setup cancelled.');
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
695
755
|
finally {
|
|
696
756
|
rl.close();
|
|
697
757
|
}
|