@phuetz/code-buddy 2.0.0 → 2.1.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 +73 -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 +165 -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.js +19 -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 +1 -0
- package/dist/codebuddy/fleet-tool-defs.js +24 -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.js +1 -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 +4 -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.js +4 -0
- 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 +4 -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 +179 -0
- package/dist/doctor/integrations.js +227 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -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.js +6 -0
- 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/transports.d.ts +10 -5
- package/dist/mcp/transports.js +61 -99
- 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 +13 -5
- package/dist/services/runtime-settings-context.d.ts +73 -0
- package/dist/services/runtime-settings-context.js +69 -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 +21 -28
- 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 +9 -0
- package/dist/tools/registry/fleet-tools.js +22 -1
- 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/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/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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { CommandHandlerResult } from './branch-handlers.js';
|
|
10
10
|
/**
|
|
11
11
|
* Provider for model routing state.
|
|
12
|
-
* Set
|
|
12
|
+
* Set by the conversation-loop dispatcher from the live agent.
|
|
13
13
|
*/
|
|
14
14
|
export interface SwitchModelProvider {
|
|
15
15
|
/** Get list of available model names */
|
|
@@ -23,6 +23,21 @@ export function setSwitchModelProvider(provider) {
|
|
|
23
23
|
* /switch auto — Return to auto-routing / default
|
|
24
24
|
*/
|
|
25
25
|
export async function handleSwitch(args) {
|
|
26
|
+
try {
|
|
27
|
+
return await runSwitch(args);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return {
|
|
31
|
+
handled: true,
|
|
32
|
+
entry: {
|
|
33
|
+
type: 'assistant',
|
|
34
|
+
content: `Model switch failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
35
|
+
timestamp: new Date(),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function runSwitch(args) {
|
|
26
41
|
const modelName = args[0]?.trim();
|
|
27
42
|
// No provider set — try to get models from config
|
|
28
43
|
if (!providerRef) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CodeBuddyClient } from "../../codebuddy/client.js";
|
|
2
2
|
import { AITestRunner, createAITestRunner } from "../../testing/ai-integration-tests.js";
|
|
3
|
-
import stringWidth from "string-width";
|
|
4
3
|
/**
|
|
5
4
|
* Generate Tests - Create test scaffolds
|
|
6
5
|
*/
|
|
@@ -47,7 +46,7 @@ export async function handleAITest(args, codebuddyClient) {
|
|
|
47
46
|
const option = args[0]?.toLowerCase();
|
|
48
47
|
// Check for API key
|
|
49
48
|
const apiKey = process.env.GROK_API_KEY;
|
|
50
|
-
if (!apiKey) {
|
|
49
|
+
if (!codebuddyClient && !apiKey) {
|
|
51
50
|
return {
|
|
52
51
|
handled: true,
|
|
53
52
|
entry: {
|
|
@@ -66,9 +65,8 @@ Set your API key to run integration tests.`,
|
|
|
66
65
|
// Fallback: create client from environment variables
|
|
67
66
|
const model = process.env.GROK_MODEL || process.env.OPENAI_MODEL;
|
|
68
67
|
const baseURL = process.env.GROK_BASE_URL || process.env.OPENAI_BASE_URL;
|
|
69
|
-
client = new CodeBuddyClient(apiKey, model, baseURL);
|
|
68
|
+
client = new CodeBuddyClient(apiKey ?? '', model, baseURL);
|
|
70
69
|
}
|
|
71
|
-
const currentModel = client.getCurrentModel();
|
|
72
70
|
const currentBaseURL = client.getBaseURL();
|
|
73
71
|
// Detect local models (LM Studio, Ollama) and increase timeout
|
|
74
72
|
const isLocalModel = currentBaseURL.includes(':1234') ||
|
|
@@ -86,87 +84,11 @@ Set your API key to run integration tests.`,
|
|
|
86
84
|
testTools: option !== 'stream',
|
|
87
85
|
testStreaming: option !== 'tools',
|
|
88
86
|
};
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
'Basic Completion': '🧠',
|
|
92
|
-
'Simple Math': '🔢',
|
|
93
|
-
'JSON Output': '📋',
|
|
94
|
-
'Code Generation': '💻',
|
|
95
|
-
'Context Understanding': '🧩',
|
|
96
|
-
'Streaming Response': '🌊',
|
|
97
|
-
'Tool Calling': '🔧',
|
|
98
|
-
'Error Handling': '🛡️',
|
|
99
|
-
'Long Context': '📚',
|
|
100
|
-
};
|
|
101
|
-
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
102
|
-
let spinnerIndex = 0;
|
|
103
|
-
let currentTest = '';
|
|
104
|
-
const completedTests = [];
|
|
105
|
-
let spinnerInterval = null;
|
|
106
|
-
// Create client and run tests
|
|
87
|
+
// Ink owns the terminal. Render the final report through the conversation
|
|
88
|
+
// instead of writing cursor-control sequences over the active UI.
|
|
107
89
|
try {
|
|
108
90
|
const runner = createAITestRunner(client, testOptions);
|
|
109
|
-
// Helper to pad string to width accounting for emoji visual width
|
|
110
|
-
const padEnd = (str, targetWidth) => {
|
|
111
|
-
const currentWidth = stringWidth(str);
|
|
112
|
-
if (currentWidth >= targetWidth)
|
|
113
|
-
return str;
|
|
114
|
-
return str + ' '.repeat(targetWidth - currentWidth);
|
|
115
|
-
};
|
|
116
|
-
const W = 60; // box width
|
|
117
|
-
// Build progress display with proper emoji width handling
|
|
118
|
-
const buildProgressDisplay = () => {
|
|
119
|
-
const lines = [];
|
|
120
|
-
lines.push('┌' + '─'.repeat(W - 2) + '┐');
|
|
121
|
-
lines.push('│' + padEnd(' 🧪 AI INTEGRATION TESTS IN PROGRESS', W - 2) + '│');
|
|
122
|
-
lines.push('├' + '─'.repeat(W - 2) + '┤');
|
|
123
|
-
lines.push('│' + padEnd(` Model: ${currentModel}`, W - 2) + '│');
|
|
124
|
-
lines.push('├' + '─'.repeat(W - 2) + '┤');
|
|
125
|
-
// Show completed tests
|
|
126
|
-
for (const test of completedTests) {
|
|
127
|
-
lines.push('│' + padEnd(` ${test}`, W - 2) + '│');
|
|
128
|
-
}
|
|
129
|
-
// Show current test with spinner
|
|
130
|
-
if (currentTest) {
|
|
131
|
-
const emoji = testEmojis[currentTest] || '🔬';
|
|
132
|
-
const spinner = spinnerFrames[spinnerIndex % spinnerFrames.length];
|
|
133
|
-
lines.push('│' + padEnd(` ${spinner} ${emoji} ${currentTest}...`, W - 2) + '│');
|
|
134
|
-
}
|
|
135
|
-
lines.push('└' + '─'.repeat(W - 2) + '┘');
|
|
136
|
-
return lines.join('\n');
|
|
137
|
-
};
|
|
138
|
-
// Track progress
|
|
139
|
-
runner.on('test:start', ({ name }) => {
|
|
140
|
-
currentTest = name;
|
|
141
|
-
});
|
|
142
|
-
runner.on('test:complete', (result) => {
|
|
143
|
-
const emoji = testEmojis[result.name] || '🔬';
|
|
144
|
-
const status = result.passed ? '✅' : '❌';
|
|
145
|
-
const duration = result.duration ? `${(result.duration / 1000).toFixed(1)}s` : '';
|
|
146
|
-
completedTests.push(`${status} ${emoji} ${result.name} ${duration}`);
|
|
147
|
-
currentTest = '';
|
|
148
|
-
});
|
|
149
|
-
runner.on('test:skipped', ({ name }) => {
|
|
150
|
-
const emoji = testEmojis[name] || '🔬';
|
|
151
|
-
completedTests.push(`⏭️ ${emoji} ${name} (skipped)`);
|
|
152
|
-
});
|
|
153
|
-
// Start spinner animation (write to stderr to not interfere with output)
|
|
154
|
-
spinnerInterval = setInterval(() => {
|
|
155
|
-
spinnerIndex++;
|
|
156
|
-
// Clear and redraw progress (using ANSI escape codes)
|
|
157
|
-
const progress = buildProgressDisplay();
|
|
158
|
-
process.stderr.write(`\x1b[${completedTests.length + 7}A\x1b[0J${progress}\n`);
|
|
159
|
-
}, 100);
|
|
160
|
-
// Show initial progress
|
|
161
|
-
process.stderr.write('\n' + buildProgressDisplay() + '\n');
|
|
162
91
|
const suite = await runner.runAll();
|
|
163
|
-
// Stop spinner
|
|
164
|
-
if (spinnerInterval) {
|
|
165
|
-
clearInterval(spinnerInterval);
|
|
166
|
-
}
|
|
167
|
-
// Clear progress display
|
|
168
|
-
const clearLines = completedTests.length + 8;
|
|
169
|
-
process.stderr.write(`\x1b[${clearLines}A\x1b[0J`);
|
|
170
92
|
// Format final results
|
|
171
93
|
const resultContent = AITestRunner.formatResults(suite);
|
|
172
94
|
return {
|
|
@@ -179,10 +101,6 @@ Set your API key to run integration tests.`,
|
|
|
179
101
|
};
|
|
180
102
|
}
|
|
181
103
|
catch (error) {
|
|
182
|
-
// Stop spinner on error
|
|
183
|
-
if (spinnerInterval) {
|
|
184
|
-
clearInterval(spinnerInterval);
|
|
185
|
-
}
|
|
186
104
|
return {
|
|
187
105
|
handled: true,
|
|
188
106
|
entry: {
|
|
@@ -4,17 +4,17 @@ import { getThemeManager } from "../../themes/theme-manager.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export function handleTheme(args) {
|
|
6
6
|
const themeManager = getThemeManager();
|
|
7
|
-
const action = args[0]?.toLowerCase();
|
|
7
|
+
const action = (args[0]?.toLowerCase() === "set" ? args[1] : args[0])?.toLowerCase();
|
|
8
8
|
let content;
|
|
9
|
-
if (!action || action === "list") {
|
|
9
|
+
if (!action || action === "list" || action === "status" || action === "current") {
|
|
10
10
|
const themes = themeManager.getAvailableThemes();
|
|
11
11
|
const currentTheme = themeManager.getCurrentTheme();
|
|
12
|
-
content = `🎨
|
|
12
|
+
content = `🎨 Current theme: ${currentTheme.name} (${currentTheme.id})\nAvailable Themes\n${"═".repeat(40)}\n\n`;
|
|
13
13
|
for (const theme of themes) {
|
|
14
14
|
const isCurrent = theme.id === currentTheme.id;
|
|
15
15
|
const marker = isCurrent ? "▶" : " ";
|
|
16
16
|
const builtinMarker = theme.isBuiltin ? "" : " (custom)";
|
|
17
|
-
content += `${marker} ${theme.name}${builtinMarker}\n`;
|
|
17
|
+
content += `${marker} ${theme.name} (${theme.id})${builtinMarker}\n`;
|
|
18
18
|
content += ` ${theme.description}\n\n`;
|
|
19
19
|
}
|
|
20
20
|
content += `\n💡 Usage: /theme <name>\n`;
|
|
@@ -28,7 +28,13 @@ export function handleTheme(args) {
|
|
|
28
28
|
content = `🎨 Theme Changed!\n\n`;
|
|
29
29
|
content += `Now using: ${theme.name}\n`;
|
|
30
30
|
content += `${theme.description}\n\n`;
|
|
31
|
-
content +=
|
|
31
|
+
content += `The theme is active immediately.`;
|
|
32
|
+
if (themeManager.getPreferenceSaveStatus() === false) {
|
|
33
|
+
content += `\nCould not save the preference; this change may be lost after restarting Buddy.`;
|
|
34
|
+
}
|
|
35
|
+
if (themeManager.getOverrideKeys().colors.length > 0) {
|
|
36
|
+
content += `\nCustom color overrides are still applied.`;
|
|
37
|
+
}
|
|
32
38
|
}
|
|
33
39
|
else {
|
|
34
40
|
const themes = themeManager.getAvailableThemes();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ChatGptAuth } from '../providers/codex-oauth.js';
|
|
2
|
+
interface LoginOutput {
|
|
3
|
+
stdout: (message: string) => unknown;
|
|
4
|
+
warn: (message: string) => unknown;
|
|
5
|
+
}
|
|
6
|
+
/** Keep the callback alive so a failed browser launch can be completed manually. */
|
|
7
|
+
export declare function loginChatGptWithBrowser(output: LoginOutput): Promise<ChatGptAuth>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import open from 'open';
|
|
2
|
+
import { loginInteractive } from '../providers/codex-oauth.js';
|
|
3
|
+
/** Keep the callback alive so a failed browser launch can be completed manually. */
|
|
4
|
+
export function loginChatGptWithBrowser(output) {
|
|
5
|
+
return loginInteractive(async (url) => {
|
|
6
|
+
output.stdout('If the browser does not open, copy this entire URL into your browser:');
|
|
7
|
+
output.stdout(url);
|
|
8
|
+
output.stdout('Keep this terminal open until sign-in completes.\n');
|
|
9
|
+
try {
|
|
10
|
+
await open(url);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
output.warn('Could not open the browser automatically. Open the URL above manually; login is still waiting.');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=login-chatgpt.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export declare function importMCPFile(filename: string, source: 'hermes' | 'openclaw', options?: {
|
|
3
|
+
dryRun?: boolean;
|
|
4
|
+
output?: string;
|
|
5
|
+
}): {
|
|
6
|
+
written: boolean;
|
|
7
|
+
servers: import("../mcp/types.js").MCPServerConfig[];
|
|
8
|
+
warnings: string[];
|
|
9
|
+
rejected: string[];
|
|
10
|
+
};
|
|
11
|
+
export declare function registerMCPImportCommand(command: Command): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { normalizeMCPImports } from '../mcp/import-normalize.js';
|
|
5
|
+
import { writeJsonAtomicSync } from '../utils/atomic-write.js';
|
|
6
|
+
const object = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
export function importMCPFile(filename, source, options = {}) {
|
|
8
|
+
const stat = fs.statSync(filename);
|
|
9
|
+
if (!stat.isFile() || stat.size > 1024 * 1024)
|
|
10
|
+
throw new Error('MCP import file must be at most 1 MiB');
|
|
11
|
+
let input;
|
|
12
|
+
try {
|
|
13
|
+
input = parse(fs.readFileSync(filename, 'utf8'), { uniqueKeys: true });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error('Invalid MCP import JSON/YAML');
|
|
17
|
+
}
|
|
18
|
+
if (!object(input))
|
|
19
|
+
throw new Error('MCP import requires an object');
|
|
20
|
+
const map = input.mcpServers ?? input.mcp_servers ?? (object(input.mcp) ? input.mcp.servers : undefined) ?? input;
|
|
21
|
+
const normalized = normalizeMCPImports(map, source);
|
|
22
|
+
if (options.dryRun || normalized.servers.length === 0)
|
|
23
|
+
return { ...normalized, written: false };
|
|
24
|
+
const output = options.output ?? path.join(process.cwd(), '.codebuddy', 'mcp.json');
|
|
25
|
+
let existing = {};
|
|
26
|
+
if (fs.existsSync(output)) {
|
|
27
|
+
const target = fs.lstatSync(output);
|
|
28
|
+
if (!target.isFile() || target.isSymbolicLink() || target.size > 1024 * 1024)
|
|
29
|
+
throw new Error('Invalid existing MCP target');
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(fs.readFileSync(output, 'utf8'));
|
|
32
|
+
if (!object(parsed) || (parsed.mcpServers !== undefined && !object(parsed.mcpServers)))
|
|
33
|
+
throw new Error();
|
|
34
|
+
existing = parsed;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error('Existing MCP configuration is invalid; not replaced');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const servers = object(existing.mcpServers) ? existing.mcpServers : {};
|
|
41
|
+
for (const server of normalized.servers) {
|
|
42
|
+
if (Object.hasOwn(servers, server.name))
|
|
43
|
+
normalized.warnings.push(`${server.name}: existing configuration preserved`);
|
|
44
|
+
else
|
|
45
|
+
servers[server.name] = server;
|
|
46
|
+
}
|
|
47
|
+
fs.mkdirSync(path.dirname(output), { recursive: true, mode: 0o700 });
|
|
48
|
+
writeJsonAtomicSync(output, { ...existing, mcpServers: servers }, { mode: 0o600 });
|
|
49
|
+
return { ...normalized, written: true };
|
|
50
|
+
}
|
|
51
|
+
export function registerMCPImportCommand(command) {
|
|
52
|
+
command.command('import <file>').requiredOption('--from <source>', 'hermes or openclaw')
|
|
53
|
+
.option('--dry-run', 'Show sanitized mapping without writing or connecting')
|
|
54
|
+
.option('--output <file>', 'Target MCP JSON file (default project .codebuddy/mcp.json)')
|
|
55
|
+
.description('Import MCP declarations; credentials become environment references and existing entries are preserved')
|
|
56
|
+
.action((file, options) => {
|
|
57
|
+
if (options.from !== 'hermes' && options.from !== 'openclaw')
|
|
58
|
+
command.error('Source must be hermes or openclaw');
|
|
59
|
+
try {
|
|
60
|
+
process.stdout.write(JSON.stringify(importMCPFile(file, options.from, options), null, 2) + '\n');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
command.error('MCP import failed; check input/target configuration. No credentials are printed.');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=mcp-import.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { MCPManager } from '../mcp/client.js';
|
|
3
|
+
import { loadMCPConfig } from '../mcp/config.js';
|
|
4
|
+
import { getErrorMessage } from '../types/index.js';
|
|
5
|
+
const MAX_ARGUMENT_BYTES = 1024 * 1024;
|
|
6
|
+
export function registerMCPInvocationCommands(command) {
|
|
7
|
+
command.command('tools <server>')
|
|
8
|
+
.description('Print one configured MCP server tool catalog as JSON, without an LLM')
|
|
9
|
+
.option('--query <text>', 'Filter tool names and descriptions before printing schemas')
|
|
10
|
+
.action(async (server, options) => run(server, undefined, options));
|
|
11
|
+
command.command('call <server> <tool>')
|
|
12
|
+
.description('Invoke one MCP tool directly and print its result as JSON')
|
|
13
|
+
.option('--args <json>', 'JSON object of tool arguments')
|
|
14
|
+
.option('--args-file <path>', 'Read JSON arguments from a file (maximum 1 MiB)')
|
|
15
|
+
.action(async (server, tool, options) => {
|
|
16
|
+
await run(server, tool, options);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async function run(server, tool, options = {}) {
|
|
20
|
+
const manager = new MCPManager();
|
|
21
|
+
try {
|
|
22
|
+
if (options.args !== undefined && options.argsFile !== undefined) {
|
|
23
|
+
throw new Error('Use either --args or --args-file');
|
|
24
|
+
}
|
|
25
|
+
let raw = options.args ?? '{}';
|
|
26
|
+
if (options.argsFile !== undefined) {
|
|
27
|
+
const info = await stat(options.argsFile);
|
|
28
|
+
if (!info.isFile() || info.size > MAX_ARGUMENT_BYTES)
|
|
29
|
+
throw new Error('Arguments must be a regular file no larger than 1 MiB');
|
|
30
|
+
raw = await readFile(options.argsFile, 'utf8');
|
|
31
|
+
}
|
|
32
|
+
if (Buffer.byteLength(raw) > MAX_ARGUMENT_BYTES)
|
|
33
|
+
throw new Error('Arguments exceed 1 MiB');
|
|
34
|
+
let args;
|
|
35
|
+
try {
|
|
36
|
+
args = JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new Error('Tool arguments must be valid JSON');
|
|
40
|
+
}
|
|
41
|
+
if (!args || typeof args !== 'object' || Array.isArray(args))
|
|
42
|
+
throw new Error('Tool arguments must be a JSON object');
|
|
43
|
+
const config = loadMCPConfig().servers.find(candidate => candidate.name === server);
|
|
44
|
+
if (!config)
|
|
45
|
+
throw new Error(`Enabled MCP server not found: ${server}`);
|
|
46
|
+
await manager.addServer(config);
|
|
47
|
+
if (!tool) {
|
|
48
|
+
const catalog = manager.getTools().filter(item => item.serverName === server);
|
|
49
|
+
const query = options.query?.toLowerCase();
|
|
50
|
+
const tools = query ? catalog.filter(item => `${item.name} ${item.description}`.toLowerCase().includes(query)) : catalog;
|
|
51
|
+
console.log(JSON.stringify({ server, totalTools: catalog.length, tools }));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const qualified = `mcp__${server}__${tool}`;
|
|
55
|
+
const result = await manager.callTool(qualified, args);
|
|
56
|
+
console.log(JSON.stringify(result));
|
|
57
|
+
if (result.isError)
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.log(JSON.stringify({ isError: true, error: getErrorMessage(error) }));
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await manager.dispose();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=mcp-invoke.js.map
|
package/dist/commands/mcp.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { registerMCPImportCommand } from './mcp-import.js';
|
|
2
|
+
import { registerMCPInvocationCommands } from './mcp-invoke.js';
|
|
1
3
|
import { Command } from 'commander';
|
|
2
4
|
import { addMCPServer, removeMCPServer, loadMCPConfig, PREDEFINED_SERVERS, setMCPServerEnabled, } from '../mcp/config.js';
|
|
3
5
|
import { getMCPManager } from '../codebuddy/tools.js';
|
|
@@ -52,6 +54,7 @@ export async function serveMCP(options = {}) {
|
|
|
52
54
|
export function createMCPCommand() {
|
|
53
55
|
const mcpCommand = new Command('mcp');
|
|
54
56
|
mcpCommand.description('Manage MCP servers or expose Code Buddy over MCP');
|
|
57
|
+
registerMCPImportCommand(mcpCommand);
|
|
55
58
|
mcpCommand
|
|
56
59
|
.command('serve')
|
|
57
60
|
.description('Expose Code Buddy tools as an MCP server over stdio')
|
|
@@ -524,6 +527,7 @@ export function createMCPCommand() {
|
|
|
524
527
|
}
|
|
525
528
|
}
|
|
526
529
|
});
|
|
530
|
+
registerMCPInvocationCommands(mcpCommand);
|
|
527
531
|
return mcpCommand;
|
|
528
532
|
}
|
|
529
533
|
//# sourceMappingURL=mcp.js.map
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* Supports running from YAML/JSON files, listing, validating, and status checks.
|
|
6
6
|
*/
|
|
7
7
|
import { Command } from 'commander';
|
|
8
|
+
import type { PipelineConfig, ApprovalGateConfig } from '../workflows/pipeline.js';
|
|
9
|
+
/** Standalone CLI bridge; tool policy, trust and confirmations stay in ToolHandler. */
|
|
10
|
+
export declare function createPipelineRuntime(): Pick<PipelineConfig, 'toolExecutor' | 'approvalHandler'> & {
|
|
11
|
+
dispose(): Promise<void>;
|
|
12
|
+
};
|
|
8
13
|
/**
|
|
9
14
|
* Pipeline definition loaded from a file (YAML/JSON)
|
|
10
15
|
*/
|
|
@@ -14,10 +19,11 @@ export interface PipelineFileDefinition {
|
|
|
14
19
|
version?: string;
|
|
15
20
|
steps: Array<{
|
|
16
21
|
name: string;
|
|
17
|
-
type?: 'tool' | 'skill' | 'function' | 'transform';
|
|
22
|
+
type?: 'tool' | 'skill' | 'function' | 'transform' | 'approval';
|
|
18
23
|
args?: Record<string, unknown>;
|
|
19
24
|
timeout?: number;
|
|
20
25
|
label?: string;
|
|
26
|
+
approvalGate?: Pick<ApprovalGateConfig, 'message' | 'timeoutMs'>;
|
|
21
27
|
}>;
|
|
22
28
|
config?: {
|
|
23
29
|
maxSteps?: number;
|
|
@@ -6,6 +6,90 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import { logger } from '../utils/logger.js';
|
|
9
|
+
/** Standalone CLI bridge; tool policy, trust and confirmations stay in ToolHandler. */
|
|
10
|
+
export function createPipelineRuntime() {
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
let handler;
|
|
13
|
+
let marketplace;
|
|
14
|
+
let repair;
|
|
15
|
+
let service;
|
|
16
|
+
let readline;
|
|
17
|
+
let ownsListener = false;
|
|
18
|
+
const onConfirmation = (options) => {
|
|
19
|
+
void (async () => {
|
|
20
|
+
try {
|
|
21
|
+
const { createInterface } = await import('node:readline/promises');
|
|
22
|
+
readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
23
|
+
process.stderr.write(`${JSON.stringify({ operation: options.operation, target: options.filename,
|
|
24
|
+
preview: options.diffPreview ?? options.content })}\n`);
|
|
25
|
+
const answer = await readline.question('Approve this operation? [y/N] ');
|
|
26
|
+
service?.confirmOperation(/^(y|yes|o|oui)$/i.test(answer.trim()));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
service?.rejectOperation('Approval input closed');
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
readline?.close();
|
|
33
|
+
readline = undefined;
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
};
|
|
37
|
+
const confirmations = async () => {
|
|
38
|
+
if (!service) {
|
|
39
|
+
const { ConfirmationService } = await import('../utils/confirmation-service.js');
|
|
40
|
+
service = ConfirmationService.getInstance();
|
|
41
|
+
if (process.stdin.isTTY && service.listenerCount('confirmation-requested') === 0) {
|
|
42
|
+
service.on('confirmation-requested', onConfirmation);
|
|
43
|
+
ownsListener = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return service;
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
toolExecutor: async (name, args) => {
|
|
50
|
+
await confirmations();
|
|
51
|
+
if (!handler) {
|
|
52
|
+
const [{ ToolHandler }, { CheckpointManager }, { HooksManager }, { PluginMarketplace }, { RepairCoordinator }] = await Promise.all([
|
|
53
|
+
import('../agent/tool-handler.js'), import('../checkpoints/checkpoint-manager.js'),
|
|
54
|
+
import('../hooks/lifecycle-hooks.js'), import('../plugins/marketplace.js'),
|
|
55
|
+
import('../agent/execution/repair-coordinator.js'),
|
|
56
|
+
]);
|
|
57
|
+
marketplace = new PluginMarketplace({ autoUpdate: false });
|
|
58
|
+
repair = new RepairCoordinator({ enabled: false });
|
|
59
|
+
handler = new ToolHandler({ checkpointManager: new CheckpointManager(),
|
|
60
|
+
hooksManager: new HooksManager(process.cwd()), marketplace, repairCoordinator: repair });
|
|
61
|
+
handler.setWorkingDirectory(process.cwd());
|
|
62
|
+
}
|
|
63
|
+
const { randomUUID } = await import('node:crypto');
|
|
64
|
+
const result = await handler.executeTool({ id: `pipeline_${randomUUID()}`, type: 'function',
|
|
65
|
+
function: { name, arguments: JSON.stringify(args) } }, { surface: 'cli', abortSignal: controller.signal });
|
|
66
|
+
return { success: result.success, output: result.output ?? '', error: result.error };
|
|
67
|
+
},
|
|
68
|
+
approvalHandler: async (gate) => {
|
|
69
|
+
const confirmation = await confirmations();
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
confirmation.rejectOperation('Pipeline approval timed out');
|
|
72
|
+
readline?.close();
|
|
73
|
+
}, gate.timeoutMs);
|
|
74
|
+
try {
|
|
75
|
+
const result = await confirmation.requestConfirmation({ operation: 'Approve pipeline step',
|
|
76
|
+
filename: gate.message, toolName: 'pipeline_approval', forcePrompt: true }, 'tool');
|
|
77
|
+
return { approved: result.confirmed, timestamp: new Date(), comment: result.feedback };
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
dispose: async () => {
|
|
84
|
+
controller.abort();
|
|
85
|
+
if (ownsListener)
|
|
86
|
+
service?.off('confirmation-requested', onConfirmation);
|
|
87
|
+
readline?.close();
|
|
88
|
+
repair?.dispose();
|
|
89
|
+
await marketplace?.dispose();
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
9
93
|
/**
|
|
10
94
|
* Load a pipeline definition from a YAML or JSON file.
|
|
11
95
|
* Uses lazy imports for yaml parsing.
|
|
@@ -86,8 +170,12 @@ export function validatePipelineDefinition(definition) {
|
|
|
86
170
|
warnings.push(`Step ${i + 1}: duplicate step name "${step.name}"`);
|
|
87
171
|
}
|
|
88
172
|
stepNames.add(step.name);
|
|
89
|
-
if (step.type && !['tool', 'skill', 'function', 'transform'].includes(step.type)) {
|
|
90
|
-
errors.push(`Step ${i + 1} ("${step.name}"): invalid type "${step.type}" (must be tool, skill, function, or
|
|
173
|
+
if (step.type && !['tool', 'skill', 'function', 'transform', 'approval'].includes(step.type)) {
|
|
174
|
+
errors.push(`Step ${i + 1} ("${step.name}"): invalid type "${step.type}" (must be tool, skill, function, transform, or approval)`);
|
|
175
|
+
}
|
|
176
|
+
if (step.approvalGate && (typeof step.approvalGate.message !== 'string' ||
|
|
177
|
+
!Number.isFinite(step.approvalGate.timeoutMs) || step.approvalGate.timeoutMs <= 0)) {
|
|
178
|
+
errors.push(`Step ${i + 1}: approvalGate requires a message and positive timeoutMs`);
|
|
91
179
|
}
|
|
92
180
|
if (step.timeout !== undefined && (typeof step.timeout !== 'number' || step.timeout <= 0)) {
|
|
93
181
|
warnings.push(`Step ${i + 1} ("${step.name}"): timeout should be a positive number`);
|
|
@@ -117,6 +205,8 @@ export function createPipelineCommand() {
|
|
|
117
205
|
.option('-t, --timeout <ms>', 'Override default step timeout in milliseconds')
|
|
118
206
|
.option('--dry-run', 'Validate and show steps without executing')
|
|
119
207
|
.action(async (file, options) => {
|
|
208
|
+
let runtime;
|
|
209
|
+
let compositor;
|
|
120
210
|
try {
|
|
121
211
|
console.log(`Loading pipeline from: ${file}`);
|
|
122
212
|
const definition = await loadPipelineFile(file);
|
|
@@ -164,7 +254,9 @@ export function createPipelineCommand() {
|
|
|
164
254
|
if (definition.config?.maxDurationMs) {
|
|
165
255
|
config.maxDurationMs = definition.config.maxDurationMs;
|
|
166
256
|
}
|
|
167
|
-
|
|
257
|
+
runtime = createPipelineRuntime();
|
|
258
|
+
compositor = new PipelineCompositor({ ...config, toolExecutor: runtime.toolExecutor,
|
|
259
|
+
approvalHandler: runtime.approvalHandler });
|
|
168
260
|
// Set up event listeners for progress
|
|
169
261
|
compositor.on('step:start', (step, index) => {
|
|
170
262
|
console.log(` [${index + 1}/${definition.steps.length}] Running: ${step.name}...`);
|
|
@@ -175,11 +267,13 @@ export function createPipelineCommand() {
|
|
|
175
267
|
});
|
|
176
268
|
// Convert file definition steps to PipelineStep format
|
|
177
269
|
const steps = definition.steps.map(step => ({
|
|
178
|
-
type:
|
|
270
|
+
type: step.type || 'tool',
|
|
179
271
|
name: step.name,
|
|
180
272
|
args: step.args || {},
|
|
181
273
|
timeout: step.timeout,
|
|
182
274
|
label: step.label,
|
|
275
|
+
approvalGate: step.type === 'approval' ? { message: step.approvalGate?.message ?? step.name,
|
|
276
|
+
timeoutMs: step.approvalGate?.timeoutMs ?? 300000, requireExplicit: true } : undefined,
|
|
183
277
|
}));
|
|
184
278
|
console.log(`\nRunning pipeline: ${definition.name} (${steps.length} steps)`);
|
|
185
279
|
const result = await compositor.execute(steps);
|
|
@@ -191,14 +285,17 @@ export function createPipelineCommand() {
|
|
|
191
285
|
}
|
|
192
286
|
else {
|
|
193
287
|
console.error(`\nPipeline failed: ${result.error || 'Unknown error'}`);
|
|
194
|
-
process.
|
|
288
|
+
process.exitCode = 1;
|
|
195
289
|
}
|
|
196
|
-
compositor.dispose();
|
|
197
290
|
}
|
|
198
291
|
catch (error) {
|
|
199
292
|
const msg = error instanceof Error ? error.message : String(error);
|
|
200
293
|
logger.error(`Pipeline error: ${msg}`);
|
|
201
|
-
process.
|
|
294
|
+
process.exitCode = 1;
|
|
295
|
+
}
|
|
296
|
+
finally {
|
|
297
|
+
compositor?.dispose();
|
|
298
|
+
await runtime?.dispose();
|
|
202
299
|
}
|
|
203
300
|
});
|
|
204
301
|
// List available pipeline definitions
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { ResourceCatalog, resourceSchema, selectionWarning } from '../fleet/resource-catalog.js';
|
|
4
|
+
export function createResourcesCommand() {
|
|
5
|
+
const command = new Command('resources').description('Explicit resource inventory and bounded read-only health probes (JSON output)');
|
|
6
|
+
const run = (action) => async () => {
|
|
7
|
+
try {
|
|
8
|
+
process.stdout.write(`${JSON.stringify(await action(), null, 2)}\n`);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
command.error(error instanceof Error ? error.message : 'RESOURCE_OPERATION_FAILED');
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
command.command('schema').description('Show the resource registration fields').action(() => {
|
|
15
|
+
process.stdout.write(`${JSON.stringify({ kinds: resourceSchema.shape.kind.options,
|
|
16
|
+
fields: ['id', 'kind', 'hostId', 'declaredCapabilities', 'endpointRef', 'healthPath', 'permissions', 'ttlMs', 'timeoutMs'],
|
|
17
|
+
endpointRef: 'Environment variable containing HTTP(S) origin; no embedded credentials, path, query or fragment',
|
|
18
|
+
permissions: { probe: false, use: false }, load: 'unknown (not collected)', usageConfirmed: false }, null, 2)}\n`);
|
|
19
|
+
});
|
|
20
|
+
command.command('add <file>').description('Register one explicit JSON resource; never scans the network').action(async (file) => {
|
|
21
|
+
await run(async () => {
|
|
22
|
+
const stat = await fs.stat(file);
|
|
23
|
+
if (!stat.isFile() || stat.size > 16384)
|
|
24
|
+
throw new Error('RESOURCE_INPUT_TOO_LARGE');
|
|
25
|
+
let input;
|
|
26
|
+
try {
|
|
27
|
+
input = JSON.parse(await fs.readFile(file, 'utf8'));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error('INVALID_RESOURCE_JSON');
|
|
31
|
+
}
|
|
32
|
+
return new ResourceCatalog().add(input);
|
|
33
|
+
})();
|
|
34
|
+
});
|
|
35
|
+
command.command('remove <id>').description('Remove an explicit registration and its observation').action(async (id) => {
|
|
36
|
+
await run(() => new ResourceCatalog().remove(id))();
|
|
37
|
+
});
|
|
38
|
+
command.command('list').description('List declarations and observation freshness; no network I/O').action(run(() => new ResourceCatalog().list()));
|
|
39
|
+
command.command('status <id>').description('Read one resource state; does not refresh observations').action(async (id) => {
|
|
40
|
+
await run(async () => {
|
|
41
|
+
const result = (await new ResourceCatalog().list()).find(e => e.resource.id === id);
|
|
42
|
+
if (!result)
|
|
43
|
+
throw new Error('RESOURCE_NOT_FOUND');
|
|
44
|
+
return result;
|
|
45
|
+
})();
|
|
46
|
+
});
|
|
47
|
+
command.command('probe <id>').description('Run one explicitly permitted, bounded GET health probe').action(async (id) => {
|
|
48
|
+
await run(() => new ResourceCatalog().probe(id))();
|
|
49
|
+
});
|
|
50
|
+
command.command('select <capability>').option('--kind <kind>', 'Required resource kind').description('Select fresh compatible resource; no task dispatch or automatic replay').action(async (capability, options) => {
|
|
51
|
+
await run(async () => {
|
|
52
|
+
const parsed = options.kind === undefined ? undefined : resourceSchema.shape.kind.safeParse(options.kind);
|
|
53
|
+
if (parsed && !parsed.success)
|
|
54
|
+
throw new Error('INVALID_RESOURCE_KIND');
|
|
55
|
+
const result = await new ResourceCatalog().select(capability, parsed?.data);
|
|
56
|
+
if (!result.selected)
|
|
57
|
+
process.exitCode = 2;
|
|
58
|
+
const warning = selectionWarning(result.selected);
|
|
59
|
+
return warning ? { ...result, warning } : result;
|
|
60
|
+
})();
|
|
61
|
+
});
|
|
62
|
+
return command;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=resources.js.map
|