@build-astron-co/nimbus 0.2.0 → 0.4.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/bin/nimbus +26 -10
- package/bin/nimbus.cmd +41 -0
- package/bin/nimbus.mjs +70 -0
- package/completions/nimbus.bash +38 -0
- package/completions/nimbus.fish +48 -0
- package/completions/nimbus.zsh +81 -0
- package/dist/src/agent/compaction-agent.js +215 -0
- package/dist/src/agent/context-manager.js +385 -0
- package/dist/src/agent/context.js +322 -0
- package/dist/src/agent/deploy-preview.js +395 -0
- package/dist/src/agent/expand-files.js +95 -0
- package/dist/src/agent/index.js +18 -0
- package/dist/src/agent/loop.js +1535 -0
- package/dist/src/agent/modes.js +347 -0
- package/dist/src/agent/permissions.js +396 -0
- package/dist/src/agent/subagents/base.js +67 -0
- package/dist/src/agent/subagents/cost.js +45 -0
- package/dist/src/agent/subagents/explore.js +36 -0
- package/dist/src/agent/subagents/general.js +41 -0
- package/dist/src/agent/subagents/index.js +88 -0
- package/dist/src/agent/subagents/infra.js +52 -0
- package/dist/src/agent/subagents/security.js +60 -0
- package/dist/src/agent/system-prompt.js +860 -0
- package/dist/src/app.js +152 -0
- package/dist/src/audit/activity-log.js +209 -0
- package/dist/src/audit/compliance-checker.js +419 -0
- package/dist/src/audit/cost-tracker.js +231 -0
- package/dist/src/audit/index.js +10 -0
- package/dist/src/audit/security-scanner.js +490 -0
- package/dist/src/auth/guard.js +64 -0
- package/dist/src/auth/index.js +19 -0
- package/dist/src/auth/keychain.js +79 -0
- package/dist/src/auth/oauth.js +389 -0
- package/dist/src/auth/providers.js +415 -0
- package/dist/src/auth/sso.js +87 -0
- package/dist/src/auth/store.js +424 -0
- package/dist/src/auth/types.js +5 -0
- package/dist/src/cli/index.js +8 -0
- package/dist/src/cli/init.js +1048 -0
- package/dist/src/cli/openapi-spec.js +346 -0
- package/dist/src/cli/run.js +505 -0
- package/dist/src/cli/serve-auth.js +56 -0
- package/dist/src/cli/serve.js +432 -0
- package/dist/src/cli/web.js +50 -0
- package/dist/src/cli.js +1574 -0
- package/dist/src/clients/core-engine-client.js +156 -0
- package/dist/src/clients/enterprise-client.js +246 -0
- package/dist/src/clients/generator-client.js +219 -0
- package/dist/src/clients/git-client.js +367 -0
- package/dist/src/clients/github-client.js +229 -0
- package/dist/src/clients/helm-client.js +299 -0
- package/dist/src/clients/index.js +18 -0
- package/dist/src/clients/k8s-client.js +270 -0
- package/dist/src/clients/llm-client.js +119 -0
- package/dist/src/clients/rest-client.js +104 -0
- package/dist/src/clients/service-discovery.js +35 -0
- package/dist/src/clients/terraform-client.js +302 -0
- package/dist/src/clients/tools-client.js +1227 -0
- package/dist/src/clients/ws-client.js +93 -0
- package/dist/src/commands/alias.js +91 -0
- package/dist/src/commands/analyze/index.js +313 -0
- package/dist/src/commands/apply/helm.js +375 -0
- package/dist/src/commands/apply/index.js +176 -0
- package/dist/src/commands/apply/k8s.js +350 -0
- package/dist/src/commands/apply/terraform.js +465 -0
- package/dist/src/commands/ask.js +137 -0
- package/dist/src/commands/audit/index.js +322 -0
- package/dist/src/commands/auth-cloud.js +345 -0
- package/dist/src/commands/auth-list.js +112 -0
- package/dist/src/commands/auth-profile.js +104 -0
- package/dist/src/commands/auth-refresh.js +161 -0
- package/dist/src/commands/auth-status.js +122 -0
- package/dist/src/commands/aws/ec2.js +402 -0
- package/dist/src/commands/aws/iam.js +304 -0
- package/dist/src/commands/aws/index.js +108 -0
- package/dist/src/commands/aws/lambda.js +317 -0
- package/dist/src/commands/aws/rds.js +345 -0
- package/dist/src/commands/aws/s3.js +346 -0
- package/dist/src/commands/aws/vpc.js +302 -0
- package/dist/src/commands/aws-discover.js +413 -0
- package/dist/src/commands/aws-terraform.js +618 -0
- package/dist/src/commands/azure/aks.js +305 -0
- package/dist/src/commands/azure/functions.js +200 -0
- package/dist/src/commands/azure/index.js +93 -0
- package/dist/src/commands/azure/storage.js +378 -0
- package/dist/src/commands/azure/vm.js +291 -0
- package/dist/src/commands/billing/index.js +224 -0
- package/dist/src/commands/chat.js +259 -0
- package/dist/src/commands/completions.js +255 -0
- package/dist/src/commands/config.js +291 -0
- package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
- package/dist/src/commands/cost/estimator.js +73 -0
- package/dist/src/commands/cost/index.js +625 -0
- package/dist/src/commands/cost/parsers/terraform.js +234 -0
- package/dist/src/commands/cost/parsers/types.js +4 -0
- package/dist/src/commands/cost/pricing/aws.js +501 -0
- package/dist/src/commands/cost/pricing/azure.js +462 -0
- package/dist/src/commands/cost/pricing/gcp.js +359 -0
- package/dist/src/commands/cost/pricing/index.js +24 -0
- package/dist/src/commands/demo.js +196 -0
- package/dist/src/commands/deploy.js +215 -0
- package/dist/src/commands/doctor.js +1291 -0
- package/dist/src/commands/drift/index.js +674 -0
- package/dist/src/commands/explain.js +235 -0
- package/dist/src/commands/export.js +120 -0
- package/dist/src/commands/feedback.js +319 -0
- package/dist/src/commands/fix.js +263 -0
- package/dist/src/commands/fs/index.js +338 -0
- package/dist/src/commands/gcp/compute.js +266 -0
- package/dist/src/commands/gcp/functions.js +221 -0
- package/dist/src/commands/gcp/gke.js +357 -0
- package/dist/src/commands/gcp/iam.js +295 -0
- package/dist/src/commands/gcp/index.js +105 -0
- package/dist/src/commands/gcp/storage.js +232 -0
- package/dist/src/commands/generate-helm.js +1026 -0
- package/dist/src/commands/generate-k8s.js +1263 -0
- package/dist/src/commands/generate-terraform.js +1058 -0
- package/dist/src/commands/gh/index.js +663 -0
- package/dist/src/commands/git/index.js +1208 -0
- package/dist/src/commands/helm/index.js +985 -0
- package/dist/src/commands/help.js +639 -0
- package/dist/src/commands/history.js +120 -0
- package/dist/src/commands/import.js +782 -0
- package/dist/src/commands/incident.js +144 -0
- package/dist/src/commands/index.js +109 -0
- package/dist/src/commands/init.js +955 -0
- package/dist/src/commands/k8s/index.js +979 -0
- package/dist/src/commands/login.js +588 -0
- package/dist/src/commands/logout.js +61 -0
- package/dist/src/commands/logs.js +160 -0
- package/dist/src/commands/onboarding.js +382 -0
- package/dist/src/commands/pipeline.js +153 -0
- package/dist/src/commands/plan/display.js +216 -0
- package/dist/src/commands/plan/index.js +525 -0
- package/dist/src/commands/plugin.js +325 -0
- package/dist/src/commands/preview.js +356 -0
- package/dist/src/commands/profile.js +297 -0
- package/dist/src/commands/questionnaire.js +1021 -0
- package/dist/src/commands/resume.js +35 -0
- package/dist/src/commands/rollback.js +259 -0
- package/dist/src/commands/rollout.js +74 -0
- package/dist/src/commands/runbook.js +307 -0
- package/dist/src/commands/schedule.js +202 -0
- package/dist/src/commands/status.js +213 -0
- package/dist/src/commands/team/index.js +309 -0
- package/dist/src/commands/team-context.js +200 -0
- package/dist/src/commands/template.js +204 -0
- package/dist/src/commands/tf/index.js +989 -0
- package/dist/src/commands/upgrade.js +515 -0
- package/dist/src/commands/usage/index.js +118 -0
- package/dist/src/commands/version.js +145 -0
- package/dist/src/commands/watch.js +127 -0
- package/dist/src/compat/index.js +2 -0
- package/dist/src/compat/runtime.js +10 -0
- package/dist/src/compat/sqlite.js +144 -0
- package/dist/src/config/index.js +6 -0
- package/dist/src/config/manager.js +469 -0
- package/dist/src/config/mode-store.js +57 -0
- package/dist/src/config/profiles.js +66 -0
- package/dist/src/config/safety-policy.js +251 -0
- package/dist/src/config/schema.js +107 -0
- package/dist/src/config/types.js +311 -0
- package/dist/src/config/workspace-state.js +38 -0
- package/dist/src/context/context-db.js +138 -0
- package/dist/src/demo/index.js +295 -0
- package/dist/src/demo/scenarios/full-journey.js +226 -0
- package/dist/src/demo/scenarios/getting-started.js +124 -0
- package/dist/src/demo/scenarios/helm-release.js +334 -0
- package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
- package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
- package/dist/src/demo/types.js +6 -0
- package/dist/src/engine/cost-estimator.js +334 -0
- package/dist/src/engine/diagram-generator.js +192 -0
- package/dist/src/engine/drift-detector.js +688 -0
- package/dist/src/engine/executor.js +832 -0
- package/dist/src/engine/index.js +39 -0
- package/dist/src/engine/orchestrator.js +436 -0
- package/dist/src/engine/planner.js +616 -0
- package/dist/src/engine/safety.js +609 -0
- package/dist/src/engine/verifier.js +664 -0
- package/dist/src/enterprise/audit.js +241 -0
- package/dist/src/enterprise/auth.js +189 -0
- package/dist/src/enterprise/billing.js +512 -0
- package/dist/src/enterprise/index.js +16 -0
- package/dist/src/enterprise/teams.js +315 -0
- package/dist/src/generator/best-practices.js +1375 -0
- package/dist/src/generator/helm.js +495 -0
- package/dist/src/generator/index.js +11 -0
- package/dist/src/generator/intent-parser.js +420 -0
- package/dist/src/generator/kubernetes.js +773 -0
- package/dist/src/generator/terraform.js +1472 -0
- package/dist/src/history/index.js +6 -0
- package/dist/src/history/manager.js +199 -0
- package/dist/src/history/types.js +6 -0
- package/dist/src/hooks/config.js +318 -0
- package/dist/src/hooks/engine.js +317 -0
- package/dist/src/hooks/index.js +2 -0
- package/dist/src/llm/auth-bridge.js +157 -0
- package/dist/src/llm/circuit-breaker.js +116 -0
- package/dist/src/llm/config-loader.js +172 -0
- package/dist/src/llm/cost-calculator.js +137 -0
- package/dist/src/llm/index.js +7 -0
- package/dist/src/llm/model-aliases.js +99 -0
- package/dist/src/llm/provider-registry.js +57 -0
- package/dist/src/llm/providers/anthropic.js +430 -0
- package/dist/src/llm/providers/bedrock.js +409 -0
- package/dist/src/llm/providers/google.js +344 -0
- package/dist/src/llm/providers/ollama.js +661 -0
- package/dist/src/llm/providers/openai-compatible.js +289 -0
- package/dist/src/llm/providers/openai.js +284 -0
- package/dist/src/llm/providers/openrouter.js +293 -0
- package/dist/src/llm/router.js +844 -0
- package/dist/src/llm/types.js +69 -0
- package/dist/src/lsp/client.js +239 -0
- package/dist/src/lsp/languages.js +95 -0
- package/dist/src/lsp/manager.js +243 -0
- package/dist/src/mcp/client.js +289 -0
- package/dist/src/mcp/index.js +5 -0
- package/dist/src/mcp/manager.js +113 -0
- package/dist/src/nimbus.js +212 -0
- package/dist/src/plugins/index.js +13 -0
- package/dist/src/plugins/loader.js +280 -0
- package/dist/src/plugins/manager.js +282 -0
- package/dist/src/plugins/types.js +23 -0
- package/dist/src/scanners/cicd-scanner.js +230 -0
- package/dist/src/scanners/cloud-scanner.js +415 -0
- package/dist/src/scanners/framework-scanner.js +430 -0
- package/dist/src/scanners/iac-scanner.js +350 -0
- package/dist/src/scanners/index.js +454 -0
- package/dist/src/scanners/language-scanner.js +258 -0
- package/dist/src/scanners/package-manager-scanner.js +252 -0
- package/dist/src/scanners/types.js +6 -0
- package/dist/src/sessions/manager.js +395 -0
- package/dist/src/sessions/types.js +4 -0
- package/dist/src/sharing/sync.js +238 -0
- package/dist/src/sharing/viewer.js +131 -0
- package/dist/src/snapshots/index.js +1 -0
- package/dist/src/snapshots/manager.js +432 -0
- package/dist/src/state/artifacts.js +94 -0
- package/dist/src/state/audit.js +73 -0
- package/dist/src/state/billing.js +126 -0
- package/dist/src/state/checkpoints.js +81 -0
- package/dist/src/state/config.js +58 -0
- package/dist/src/state/conversations.js +7 -0
- package/dist/src/state/credentials.js +96 -0
- package/dist/src/state/db.js +53 -0
- package/dist/src/state/index.js +23 -0
- package/dist/src/state/messages.js +76 -0
- package/dist/src/state/projects.js +92 -0
- package/dist/src/state/schema.js +233 -0
- package/dist/src/state/sessions.js +79 -0
- package/dist/src/state/teams.js +131 -0
- package/dist/src/telemetry.js +91 -0
- package/dist/src/tools/aws-ops.js +747 -0
- package/dist/src/tools/azure-ops.js +491 -0
- package/dist/src/tools/file-ops.js +451 -0
- package/dist/src/tools/gcp-ops.js +559 -0
- package/dist/src/tools/git-ops.js +557 -0
- package/dist/src/tools/github-ops.js +460 -0
- package/dist/src/tools/helm-ops.js +634 -0
- package/dist/src/tools/index.js +16 -0
- package/dist/src/tools/k8s-ops.js +579 -0
- package/dist/src/tools/schemas/converter.js +129 -0
- package/dist/src/tools/schemas/devops.js +3319 -0
- package/dist/src/tools/schemas/index.js +19 -0
- package/dist/src/tools/schemas/standard.js +966 -0
- package/dist/src/tools/schemas/types.js +409 -0
- package/dist/src/tools/spawn-exec.js +109 -0
- package/dist/src/tools/terraform-ops.js +627 -0
- package/dist/src/types/config.js +1 -0
- package/dist/src/types/drift.js +4 -0
- package/dist/src/types/enterprise.js +5 -0
- package/dist/src/types/index.js +14 -0
- package/dist/src/types/plan.js +1 -0
- package/dist/src/types/request.js +1 -0
- package/dist/src/types/response.js +1 -0
- package/dist/src/types/service.js +1 -0
- package/dist/src/ui/App.js +1672 -0
- package/dist/src/ui/DeployPreview.js +60 -0
- package/dist/src/ui/FileDiffModal.js +108 -0
- package/dist/src/ui/Header.js +46 -0
- package/dist/src/ui/HelpModal.js +9 -0
- package/dist/src/ui/InputBox.js +408 -0
- package/dist/src/ui/MessageList.js +795 -0
- package/dist/src/ui/PermissionPrompt.js +72 -0
- package/dist/src/ui/StatusBar.js +109 -0
- package/dist/src/ui/TerminalPane.js +31 -0
- package/dist/src/ui/ToolCallDisplay.js +303 -0
- package/dist/src/ui/TreePane.js +83 -0
- package/dist/src/ui/chat-ui.js +721 -0
- package/dist/src/ui/index.js +11 -0
- package/dist/src/ui/ink/index.js +1325 -0
- package/dist/src/ui/streaming.js +137 -0
- package/dist/src/ui/theme.js +78 -0
- package/dist/src/ui/types.js +7 -0
- package/dist/src/utils/analytics.js +61 -0
- package/dist/src/utils/cost-warning.js +25 -0
- package/dist/src/utils/env.js +42 -0
- package/dist/src/utils/errors.js +54 -0
- package/dist/src/utils/event-bus.js +22 -0
- package/dist/src/utils/index.js +16 -0
- package/dist/src/utils/logger.js +150 -0
- package/dist/src/utils/rate-limiter.js +90 -0
- package/dist/src/utils/service-auth.js +36 -0
- package/dist/src/utils/validation.js +39 -0
- package/dist/src/version.js +3 -0
- package/dist/src/watcher/index.js +192 -0
- package/dist/src/wizard/approval.js +275 -0
- package/dist/src/wizard/index.js +13 -0
- package/dist/src/wizard/prompts.js +273 -0
- package/dist/src/wizard/types.js +4 -0
- package/dist/src/wizard/ui.js +453 -0
- package/dist/src/wizard/wizard.js +227 -0
- package/package.json +31 -23
- package/src/__tests__/alias.test.ts +133 -0
- package/src/__tests__/app.test.ts +1 -1
- package/src/__tests__/audit.test.ts +1 -1
- package/src/__tests__/circuit-breaker.test.ts +1 -1
- package/src/__tests__/cli-run.test.ts +237 -1
- package/src/__tests__/compat-sqlite.test.ts +68 -0
- package/src/__tests__/context-manager.test.ts +131 -1
- package/src/__tests__/context.test.ts +1 -1
- package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
- package/src/__tests__/doctor.test.ts +48 -0
- package/src/__tests__/enterprise.test.ts +1 -1
- package/src/__tests__/export.test.ts +236 -0
- package/src/__tests__/gap-11-18-20.test.ts +958 -0
- package/src/__tests__/generator.test.ts +1 -1
- package/src/__tests__/helm-streaming.test.ts +127 -0
- package/src/__tests__/hooks.test.ts +1 -1
- package/src/__tests__/incident.test.ts +179 -0
- package/src/__tests__/init.test.ts +55 -4
- package/src/__tests__/intent-parser.test.ts +1 -1
- package/src/__tests__/llm-router.test.ts +1 -1
- package/src/__tests__/logs.test.ts +107 -0
- package/src/__tests__/loop-errors.test.ts +244 -0
- package/src/__tests__/lsp.test.ts +1 -1
- package/src/__tests__/modes.test.ts +1 -1
- package/src/__tests__/perf-optimizations.test.ts +847 -0
- package/src/__tests__/permissions.test.ts +1 -1
- package/src/__tests__/pipeline.test.ts +50 -0
- package/src/__tests__/polish-phase3.test.ts +340 -0
- package/src/__tests__/profile.test.ts +237 -0
- package/src/__tests__/rollback.test.ts +83 -0
- package/src/__tests__/runbook.test.ts +219 -0
- package/src/__tests__/schedule.test.ts +206 -0
- package/src/__tests__/serve.test.ts +1 -1
- package/src/__tests__/sessions.test.ts +96 -1
- package/src/__tests__/sharing.test.ts +53 -1
- package/src/__tests__/snapshots.test.ts +1 -1
- package/src/__tests__/standalone-migration.test.ts +199 -0
- package/src/__tests__/state-db.test.ts +1 -1
- package/src/__tests__/status.test.ts +158 -0
- package/src/__tests__/stream-with-tools.test.ts +71 -25
- package/src/__tests__/subagents.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +82 -3
- package/src/__tests__/terminal-gap-v2.test.ts +395 -0
- package/src/__tests__/terminal-parity.test.ts +393 -0
- package/src/__tests__/tf-apply.test.ts +187 -0
- package/src/__tests__/tool-converter.test.ts +1 -1
- package/src/__tests__/tool-schemas.test.ts +209 -4
- package/src/__tests__/tools.test.ts +4 -3
- package/src/__tests__/version-json.test.ts +184 -0
- package/src/__tests__/version.test.ts +1 -1
- package/src/__tests__/watch.test.ts +129 -0
- package/src/agent/compaction-agent.ts +40 -1
- package/src/agent/context-manager.ts +67 -3
- package/src/agent/deploy-preview.ts +62 -1
- package/src/agent/expand-files.ts +108 -0
- package/src/agent/loop.ts +1312 -31
- package/src/agent/permissions.ts +51 -4
- package/src/agent/system-prompt.ts +573 -19
- package/src/app.ts +58 -0
- package/src/audit/security-scanner.ts +45 -0
- package/src/auth/keychain.ts +82 -0
- package/src/auth/oauth.ts +15 -5
- package/src/cli/init.ts +378 -5
- package/src/cli/run.ts +407 -16
- package/src/cli/serve.ts +78 -1
- package/src/cli/web.ts +10 -6
- package/src/cli.ts +312 -1
- package/src/clients/service-discovery.ts +30 -25
- package/src/commands/alias.ts +100 -0
- package/src/commands/audit/index.ts +121 -2
- package/src/commands/auth-cloud.ts +113 -0
- package/src/commands/auth-refresh.ts +187 -0
- package/src/commands/aws-discover.ts +144 -251
- package/src/commands/aws-terraform.ts +68 -118
- package/src/commands/chat.ts +9 -3
- package/src/commands/completions.ts +268 -0
- package/src/commands/config.ts +26 -0
- package/src/commands/cost/index.ts +218 -2
- package/src/commands/deploy.ts +260 -0
- package/src/commands/doctor.ts +744 -152
- package/src/commands/drift/index.ts +371 -23
- package/src/commands/export.ts +146 -0
- package/src/commands/generate-k8s.ts +9 -61
- package/src/commands/generate-terraform.ts +191 -449
- package/src/commands/help.ts +212 -36
- package/src/commands/history.ts +8 -1
- package/src/commands/incident.ts +166 -0
- package/src/commands/init.ts +5 -0
- package/src/commands/login.ts +86 -1
- package/src/commands/logs.ts +167 -0
- package/src/commands/onboarding.ts +211 -34
- package/src/commands/pipeline.ts +186 -0
- package/src/commands/plugin.ts +398 -0
- package/src/commands/profile.ts +342 -0
- package/src/commands/questionnaire.ts +0 -98
- package/src/commands/resume.ts +26 -34
- package/src/commands/rollback.ts +315 -0
- package/src/commands/rollout.ts +88 -0
- package/src/commands/runbook.ts +346 -0
- package/src/commands/schedule.ts +236 -0
- package/src/commands/status.ts +252 -0
- package/src/commands/team-context.ts +220 -0
- package/src/commands/template.ts +58 -57
- package/src/commands/tf/index.ts +70 -11
- package/src/commands/upgrade.ts +57 -0
- package/src/commands/version.ts +54 -50
- package/src/commands/watch.ts +153 -0
- package/src/compat/runtime.ts +1 -1
- package/src/compat/sqlite.ts +75 -5
- package/src/config/mode-store.ts +62 -0
- package/src/config/profiles.ts +84 -0
- package/src/config/types.ts +83 -1
- package/src/config/workspace-state.ts +53 -0
- package/src/engine/cost-estimator.ts +52 -10
- package/src/engine/executor.ts +33 -2
- package/src/engine/planner.ts +68 -1
- package/src/generator/terraform.ts +8 -0
- package/src/history/manager.ts +2 -74
- package/src/hooks/engine.ts +5 -4
- package/src/llm/cost-calculator.ts +2 -2
- package/src/llm/providers/anthropic.ts +50 -21
- package/src/llm/router.ts +76 -7
- package/src/lsp/languages.ts +3 -0
- package/src/lsp/manager.ts +21 -5
- package/src/nimbus.ts +37 -18
- package/src/sessions/manager.ts +108 -1
- package/src/sharing/sync.ts +4 -0
- package/src/sharing/viewer.ts +66 -0
- package/src/tools/file-ops.ts +22 -0
- package/src/tools/schemas/devops.ts +3007 -117
- package/src/tools/schemas/standard.ts +5 -1
- package/src/tools/schemas/types.ts +31 -1
- package/src/tools/spawn-exec.ts +148 -0
- package/src/ui/App.tsx +1183 -66
- package/src/ui/DeployPreview.tsx +62 -57
- package/src/ui/FileDiffModal.tsx +162 -0
- package/src/ui/Header.tsx +87 -24
- package/src/ui/HelpModal.tsx +57 -0
- package/src/ui/InputBox.tsx +163 -10
- package/src/ui/MessageList.tsx +487 -40
- package/src/ui/PermissionPrompt.tsx +17 -5
- package/src/ui/StatusBar.tsx +122 -3
- package/src/ui/TerminalPane.tsx +84 -0
- package/src/ui/ToolCallDisplay.tsx +252 -18
- package/src/ui/TreePane.tsx +132 -0
- package/src/ui/chat-ui.ts +41 -44
- package/src/ui/ink/index.ts +771 -38
- package/src/ui/streaming.ts +1 -1
- package/src/ui/theme.ts +104 -0
- package/src/ui/types.ts +18 -0
- package/src/version.ts +1 -1
- package/src/watcher/index.ts +66 -15
- package/src/wizard/types.ts +1 -0
- package/src/wizard/ui.ts +1 -1
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client
|
|
3
|
+
*
|
|
4
|
+
* Connects to Model Context Protocol servers (local command-based or
|
|
5
|
+
* remote HTTP-based) and dynamically registers their tools into
|
|
6
|
+
* the Nimbus tool registry.
|
|
7
|
+
*
|
|
8
|
+
* MCP specification: https://modelcontextprotocol.io/
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
/**
|
|
13
|
+
* MCP Client that connects to a single MCP server.
|
|
14
|
+
*/
|
|
15
|
+
export class MCPClient {
|
|
16
|
+
config;
|
|
17
|
+
process = null;
|
|
18
|
+
connected = false;
|
|
19
|
+
requestId = 0;
|
|
20
|
+
pendingRequests = new Map();
|
|
21
|
+
buffer = '';
|
|
22
|
+
tools = [];
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
/** Whether the client is connected to the server */
|
|
27
|
+
get isConnected() {
|
|
28
|
+
return this.connected;
|
|
29
|
+
}
|
|
30
|
+
/** The tools discovered from this server */
|
|
31
|
+
get discoveredTools() {
|
|
32
|
+
return this.tools;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Connect to the MCP server.
|
|
36
|
+
* For command servers, spawns the process and initializes via JSON-RPC.
|
|
37
|
+
* For HTTP servers, sends a GET to check availability.
|
|
38
|
+
*/
|
|
39
|
+
async connect() {
|
|
40
|
+
if (this.connected) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (this.config.type === 'command') {
|
|
44
|
+
await this.connectCommand();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
await this.connectHttp();
|
|
48
|
+
}
|
|
49
|
+
this.connected = true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Discover tools from the connected server.
|
|
53
|
+
*/
|
|
54
|
+
async listTools() {
|
|
55
|
+
if (!this.connected) {
|
|
56
|
+
await this.connect();
|
|
57
|
+
}
|
|
58
|
+
if (this.config.type === 'command') {
|
|
59
|
+
const response = (await this.sendRequest('tools/list', {}));
|
|
60
|
+
this.tools = response.tools ?? [];
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// HTTP server
|
|
64
|
+
const headers = {};
|
|
65
|
+
if (this.config.token) {
|
|
66
|
+
headers['Authorization'] = `Bearer ${this.config.token}`;
|
|
67
|
+
}
|
|
68
|
+
const response = await fetch(`${this.config.url}/tools/list`, { headers });
|
|
69
|
+
const data = (await response.json());
|
|
70
|
+
this.tools = data.tools ?? [];
|
|
71
|
+
}
|
|
72
|
+
return this.tools;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Call a tool on the MCP server.
|
|
76
|
+
*/
|
|
77
|
+
async callTool(name, input) {
|
|
78
|
+
if (!this.connected) {
|
|
79
|
+
await this.connect();
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
let result;
|
|
83
|
+
if (this.config.type === 'command') {
|
|
84
|
+
result = (await this.sendRequest('tools/call', {
|
|
85
|
+
name,
|
|
86
|
+
arguments: input,
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const headers = {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
};
|
|
93
|
+
if (this.config.token) {
|
|
94
|
+
headers['Authorization'] = `Bearer ${this.config.token}`;
|
|
95
|
+
}
|
|
96
|
+
const response = await fetch(`${this.config.url}/tools/call`, {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
headers,
|
|
99
|
+
body: JSON.stringify({ name, arguments: input }),
|
|
100
|
+
});
|
|
101
|
+
result = (await response.json());
|
|
102
|
+
}
|
|
103
|
+
// MCP tool results have content array
|
|
104
|
+
const content = result.content ?? [];
|
|
105
|
+
const textParts = content
|
|
106
|
+
.filter((c) => c.type === 'text' && typeof c.text === 'string')
|
|
107
|
+
.map(c => c.text);
|
|
108
|
+
return {
|
|
109
|
+
output: textParts.join('\n') || JSON.stringify(result),
|
|
110
|
+
isError: result.isError ?? false,
|
|
111
|
+
error: result.isError ? textParts.join('\n') : undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
116
|
+
return {
|
|
117
|
+
output: '',
|
|
118
|
+
error: `MCP tool call failed: ${msg}`,
|
|
119
|
+
isError: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Convert discovered MCP tools to Nimbus ToolDefinition format.
|
|
125
|
+
*/
|
|
126
|
+
toToolDefinitions() {
|
|
127
|
+
return this.tools.map(mcpTool => ({
|
|
128
|
+
name: `mcp_${this.config.name}_${mcpTool.name}`,
|
|
129
|
+
description: `[MCP: ${this.config.name}] ${mcpTool.description}`,
|
|
130
|
+
inputSchema: jsonSchemaToZod(mcpTool.inputSchema),
|
|
131
|
+
execute: async (input) => this.callTool(mcpTool.name, input),
|
|
132
|
+
permissionTier: 'ask_once',
|
|
133
|
+
category: 'mcp',
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Disconnect from the server.
|
|
138
|
+
*/
|
|
139
|
+
async disconnect() {
|
|
140
|
+
if (this.process) {
|
|
141
|
+
this.process.kill();
|
|
142
|
+
this.process = null;
|
|
143
|
+
}
|
|
144
|
+
this.connected = false;
|
|
145
|
+
this.tools = [];
|
|
146
|
+
this.pendingRequests.clear();
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------
|
|
149
|
+
// Internal: Command-based server
|
|
150
|
+
// ---------------------------------------------------------------
|
|
151
|
+
async connectCommand() {
|
|
152
|
+
if (!this.config.command) {
|
|
153
|
+
throw new Error(`MCP server '${this.config.name}' has type 'command' but no command specified`);
|
|
154
|
+
}
|
|
155
|
+
this.process = spawn(this.config.command, this.config.args ?? [], {
|
|
156
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
157
|
+
env: { ...process.env, ...this.config.env },
|
|
158
|
+
});
|
|
159
|
+
// Handle stdout (JSON-RPC responses)
|
|
160
|
+
this.process.stdout?.on('data', (data) => {
|
|
161
|
+
this.buffer += data.toString();
|
|
162
|
+
this.processBuffer();
|
|
163
|
+
});
|
|
164
|
+
this.process.on('exit', () => {
|
|
165
|
+
this.connected = false;
|
|
166
|
+
});
|
|
167
|
+
// Send initialize request
|
|
168
|
+
await this.sendRequest('initialize', {
|
|
169
|
+
protocolVersion: '2024-11-05',
|
|
170
|
+
capabilities: {},
|
|
171
|
+
clientInfo: { name: 'nimbus', version: '0.2.0' },
|
|
172
|
+
});
|
|
173
|
+
// Send initialized notification
|
|
174
|
+
this.sendNotification('notifications/initialized', {});
|
|
175
|
+
}
|
|
176
|
+
async connectHttp() {
|
|
177
|
+
if (!this.config.url) {
|
|
178
|
+
throw new Error(`MCP server '${this.config.name}' has type 'http' but no URL specified`);
|
|
179
|
+
}
|
|
180
|
+
// Ping the server
|
|
181
|
+
const headers = {};
|
|
182
|
+
if (this.config.token) {
|
|
183
|
+
headers['Authorization'] = `Bearer ${this.config.token}`;
|
|
184
|
+
}
|
|
185
|
+
const response = await fetch(this.config.url, { headers });
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
throw new Error(`MCP server '${this.config.name}' returned HTTP ${response.status}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
sendRequest(method, params) {
|
|
191
|
+
return new Promise((resolve, reject) => {
|
|
192
|
+
const id = ++this.requestId;
|
|
193
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
194
|
+
const message = {
|
|
195
|
+
jsonrpc: '2.0',
|
|
196
|
+
id,
|
|
197
|
+
method,
|
|
198
|
+
params,
|
|
199
|
+
};
|
|
200
|
+
if (this.process?.stdin) {
|
|
201
|
+
this.process.stdin.write(`${JSON.stringify(message)}\n`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
reject(new Error('MCP server process stdin not available'));
|
|
205
|
+
}
|
|
206
|
+
// Timeout after 30 seconds
|
|
207
|
+
setTimeout(() => {
|
|
208
|
+
if (this.pendingRequests.has(id)) {
|
|
209
|
+
this.pendingRequests.delete(id);
|
|
210
|
+
reject(new Error(`MCP request timed out: ${method}`));
|
|
211
|
+
}
|
|
212
|
+
}, 30_000);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
sendNotification(method, params) {
|
|
216
|
+
const message = {
|
|
217
|
+
jsonrpc: '2.0',
|
|
218
|
+
method,
|
|
219
|
+
params,
|
|
220
|
+
};
|
|
221
|
+
if (this.process?.stdin) {
|
|
222
|
+
this.process.stdin.write(`${JSON.stringify(message)}\n`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
processBuffer() {
|
|
226
|
+
const lines = this.buffer.split('\n');
|
|
227
|
+
this.buffer = lines.pop() ?? '';
|
|
228
|
+
for (const line of lines) {
|
|
229
|
+
if (!line.trim()) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const message = JSON.parse(line);
|
|
234
|
+
if (message.id !== undefined && this.pendingRequests.has(message.id)) {
|
|
235
|
+
const pending = this.pendingRequests.get(message.id);
|
|
236
|
+
this.pendingRequests.delete(message.id);
|
|
237
|
+
if (message.error) {
|
|
238
|
+
pending.reject(new Error(message.error.message));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
pending.resolve(message.result);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Skip non-JSON lines
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Convert a JSON Schema object to a Zod schema.
|
|
253
|
+
* Handles basic types used in MCP tool definitions.
|
|
254
|
+
*/
|
|
255
|
+
function jsonSchemaToZod(schema) {
|
|
256
|
+
if (!schema || schema.type !== 'object') {
|
|
257
|
+
return z.object({});
|
|
258
|
+
}
|
|
259
|
+
const shape = {};
|
|
260
|
+
const required = new Set(schema.required ?? []);
|
|
261
|
+
for (const [key, prop] of Object.entries(schema.properties ?? {})) {
|
|
262
|
+
let fieldSchema;
|
|
263
|
+
switch (prop.type) {
|
|
264
|
+
case 'string':
|
|
265
|
+
fieldSchema = prop.enum ? z.enum(prop.enum) : z.string();
|
|
266
|
+
break;
|
|
267
|
+
case 'number':
|
|
268
|
+
case 'integer':
|
|
269
|
+
fieldSchema = z.number();
|
|
270
|
+
break;
|
|
271
|
+
case 'boolean':
|
|
272
|
+
fieldSchema = z.boolean();
|
|
273
|
+
break;
|
|
274
|
+
case 'array':
|
|
275
|
+
fieldSchema = z.array(prop.items ? jsonSchemaToZod(prop.items) : z.unknown());
|
|
276
|
+
break;
|
|
277
|
+
case 'object':
|
|
278
|
+
fieldSchema = jsonSchemaToZod(prop);
|
|
279
|
+
break;
|
|
280
|
+
default:
|
|
281
|
+
fieldSchema = z.unknown();
|
|
282
|
+
}
|
|
283
|
+
if (prop.description) {
|
|
284
|
+
fieldSchema = fieldSchema.describe(prop.description);
|
|
285
|
+
}
|
|
286
|
+
shape[key] = required.has(key) ? fieldSchema : fieldSchema.optional();
|
|
287
|
+
}
|
|
288
|
+
return z.object(shape);
|
|
289
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages multiple MCP server connections and provides a unified
|
|
5
|
+
* interface for tool discovery and registration.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { MCPClient } from './client';
|
|
11
|
+
export class MCPManager {
|
|
12
|
+
clients = new Map();
|
|
13
|
+
initialized = false;
|
|
14
|
+
/**
|
|
15
|
+
* Load MCP server configurations from config files.
|
|
16
|
+
* Searches: .nimbus/mcp.json, nimbus.json, ~/.nimbus/mcp.json
|
|
17
|
+
*/
|
|
18
|
+
async loadConfig(cwd) {
|
|
19
|
+
const configPaths = [
|
|
20
|
+
cwd ? path.join(cwd, '.nimbus', 'mcp.json') : null,
|
|
21
|
+
cwd ? path.join(cwd, 'nimbus.json') : null,
|
|
22
|
+
path.join(homedir(), '.nimbus', 'mcp.json'),
|
|
23
|
+
].filter((p) => p !== null);
|
|
24
|
+
for (const configPath of configPaths) {
|
|
25
|
+
if (fs.existsSync(configPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
28
|
+
const config = JSON.parse(content);
|
|
29
|
+
if (config.mcpServers) {
|
|
30
|
+
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
31
|
+
if (!this.clients.has(name)) {
|
|
32
|
+
this.clients.set(name, new MCPClient({ ...serverConfig, name }));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Skip invalid config files
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.initialized = true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Connect to all configured MCP servers and discover tools.
|
|
46
|
+
* Lazy servers are skipped until explicitly needed.
|
|
47
|
+
*/
|
|
48
|
+
async connectAll() {
|
|
49
|
+
if (!this.initialized) {
|
|
50
|
+
await this.loadConfig();
|
|
51
|
+
}
|
|
52
|
+
const connectPromises = [];
|
|
53
|
+
for (const [name, client] of this.clients) {
|
|
54
|
+
if (!client.config.lazy) {
|
|
55
|
+
connectPromises.push(client
|
|
56
|
+
.connect()
|
|
57
|
+
.then(() => client.listTools())
|
|
58
|
+
.then(() => undefined)
|
|
59
|
+
.catch((err) => {
|
|
60
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
61
|
+
console.warn(`MCP server '${name}' failed to connect: ${msg}`);
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
await Promise.all(connectPromises);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all tool definitions from connected MCP servers.
|
|
69
|
+
*/
|
|
70
|
+
getAllTools() {
|
|
71
|
+
const tools = [];
|
|
72
|
+
for (const client of this.clients.values()) {
|
|
73
|
+
if (client.isConnected) {
|
|
74
|
+
tools.push(...client.toToolDefinitions());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return tools;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Register all MCP tools into a tool registry.
|
|
81
|
+
*/
|
|
82
|
+
registerTools(registry) {
|
|
83
|
+
for (const tool of this.getAllTools()) {
|
|
84
|
+
try {
|
|
85
|
+
registry.register(tool);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Skip duplicate tool names
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get a specific client by server name.
|
|
94
|
+
*/
|
|
95
|
+
getClient(name) {
|
|
96
|
+
return this.clients.get(name);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Disconnect all MCP servers.
|
|
100
|
+
*/
|
|
101
|
+
async disconnectAll() {
|
|
102
|
+
const disconnectPromises = Array.from(this.clients.values()).map(client => client.disconnect());
|
|
103
|
+
await Promise.all(disconnectPromises);
|
|
104
|
+
}
|
|
105
|
+
/** Number of configured servers */
|
|
106
|
+
get serverCount() {
|
|
107
|
+
return this.clients.size;
|
|
108
|
+
}
|
|
109
|
+
/** Number of connected servers */
|
|
110
|
+
get connectedCount() {
|
|
111
|
+
return Array.from(this.clients.values()).filter(c => c.isConnected).length;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Nimbus CLI — Main Entry Point
|
|
4
|
+
*
|
|
5
|
+
* Single-process embedded architecture. All modules (LLM, tools, state,
|
|
6
|
+
* enterprise, engine, generator) run in-process — no HTTP services needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun src/nimbus.ts --version
|
|
10
|
+
* bun src/nimbus.ts --help
|
|
11
|
+
* bun src/nimbus.ts chat
|
|
12
|
+
* bun src/nimbus.ts ask "what is terraform"
|
|
13
|
+
* bun src/nimbus.ts generate terraform --provider aws
|
|
14
|
+
* bun src/nimbus.ts tf plan
|
|
15
|
+
*/
|
|
16
|
+
import { VERSION } from './version';
|
|
17
|
+
/**
|
|
18
|
+
* Non-blocking update check. Fires a single HTTPS HEAD request to npm
|
|
19
|
+
* and prints a one-liner to stderr if a newer version exists. The check
|
|
20
|
+
* uses a 3-second timeout so it never slows startup.
|
|
21
|
+
*/
|
|
22
|
+
function checkForUpdates() {
|
|
23
|
+
// Only check for interactive TTY sessions, not in CI
|
|
24
|
+
if (!process.stderr.isTTY || process.env.CI || process.env.NIMBUS_NO_UPDATE_CHECK) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
// Fire-and-forget — deferred to let the TUI render first
|
|
28
|
+
(async () => {
|
|
29
|
+
try {
|
|
30
|
+
// Small delay so the check doesn't compete with startup I/O
|
|
31
|
+
await new Promise(r => setTimeout(r, 500));
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
34
|
+
const res = await fetch('https://registry.npmjs.org/@build-astron-co/nimbus/latest', {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
headers: { Accept: 'application/json' },
|
|
37
|
+
});
|
|
38
|
+
clearTimeout(timeout);
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const data = (await res.json());
|
|
43
|
+
const latest = data.version;
|
|
44
|
+
if (!latest || latest === VERSION) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Simple semver comparison: split on dots, compare numerically
|
|
48
|
+
const current = VERSION.split('.').map(Number);
|
|
49
|
+
const remote = latest.split('.').map(Number);
|
|
50
|
+
let isNewer = false;
|
|
51
|
+
for (let i = 0; i < 3; i++) {
|
|
52
|
+
if ((remote[i] ?? 0) > (current[i] ?? 0)) {
|
|
53
|
+
isNewer = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
if ((remote[i] ?? 0) < (current[i] ?? 0)) {
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (isNewer) {
|
|
61
|
+
process.stderr.write(`\x1b[33m Update available: ${VERSION} → ${latest}. Run: nimbus upgrade\x1b[0m\n`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Network errors are silently ignored
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Global error handlers (Gap 6: prevent silent crashes from unhandled promises)
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
process.on('unhandledRejection', reason => {
|
|
73
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
74
|
+
process.stderr.write(`\x1b[31mUnhandled promise rejection: ${msg}\x1b[0m\n`);
|
|
75
|
+
// Don't exit — let the TUI continue running. The user can see the error
|
|
76
|
+
// and decide whether to continue or quit.
|
|
77
|
+
});
|
|
78
|
+
process.on('uncaughtException', error => {
|
|
79
|
+
process.stderr.write(`\x1b[31mUncaught exception: ${error.message}\x1b[0m\n`);
|
|
80
|
+
if (error.stack) {
|
|
81
|
+
process.stderr.write(`\x1b[2m${error.stack}\x1b[0m\n`);
|
|
82
|
+
}
|
|
83
|
+
// For uncaught exceptions, set exit code but let cleanup run
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
});
|
|
86
|
+
async function main() {
|
|
87
|
+
let args = process.argv.slice(2);
|
|
88
|
+
// G11: Global --project / --cwd flag: change working directory before command dispatch
|
|
89
|
+
const cwdFlagIdx = args.findIndex(a => a === '--project' || a === '--cwd');
|
|
90
|
+
if (cwdFlagIdx !== -1 && args[cwdFlagIdx + 1]) {
|
|
91
|
+
const { resolve } = await import('node:path');
|
|
92
|
+
const targetDir = resolve(args[cwdFlagIdx + 1]);
|
|
93
|
+
process.chdir(targetDir);
|
|
94
|
+
args.splice(cwdFlagIdx, 2);
|
|
95
|
+
}
|
|
96
|
+
// Handle --version and -v before anything else (no init needed)
|
|
97
|
+
if (args[0] === '--version' || args[0] === '-v') {
|
|
98
|
+
console.log(`nimbus ${VERSION}`);
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
// Handle --completion-install / completion <shell> — print shell completion script
|
|
102
|
+
if (args[0] === '--completion-install' || args[0] === 'completion') {
|
|
103
|
+
const shell = args[1] ?? 'bash';
|
|
104
|
+
const { join } = await import('node:path');
|
|
105
|
+
const { readFileSync } = await import('node:fs');
|
|
106
|
+
const { fileURLToPath } = await import('node:url');
|
|
107
|
+
const dir = join(fileURLToPath(import.meta.url), '../../completions');
|
|
108
|
+
try {
|
|
109
|
+
process.stdout.write(readFileSync(join(dir, `nimbus.${shell}`), 'utf-8'));
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
console.error(`No completion script for shell: ${shell}. Available: bash, zsh, fish`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
// Show help when explicitly requested — handles both `nimbus --help`
|
|
118
|
+
// and subcommand-level help like `nimbus chat --help` or `nimbus tf -h`.
|
|
119
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
120
|
+
const otherArgs = args.filter(a => a !== '--help' && a !== '-h');
|
|
121
|
+
args.length = 0;
|
|
122
|
+
args.push('help', ...otherArgs);
|
|
123
|
+
}
|
|
124
|
+
// Kick off a non-blocking update check (fire-and-forget)
|
|
125
|
+
checkForUpdates();
|
|
126
|
+
// Default no-args: launch chat (or onboarding if first run)
|
|
127
|
+
if (args.length === 0) {
|
|
128
|
+
const { needsOnboarding } = await import('./commands/onboarding');
|
|
129
|
+
if (needsOnboarding()) {
|
|
130
|
+
args[0] = 'onboarding';
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
args[0] = 'chat';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Initialize the application (SQLite, LLM router, etc.)
|
|
137
|
+
const { initApp, shutdownApp } = await import('./app');
|
|
138
|
+
// Register shutdown hooks for clean exit.
|
|
139
|
+
// Use process.exitCode instead of process.exit() to avoid racing
|
|
140
|
+
// with finally blocks and async cleanup in the TUI.
|
|
141
|
+
let cleaningUp = false;
|
|
142
|
+
const cleanup = async () => {
|
|
143
|
+
if (cleaningUp) {
|
|
144
|
+
return;
|
|
145
|
+
} // Prevent double-cleanup on rapid Ctrl+C
|
|
146
|
+
cleaningUp = true;
|
|
147
|
+
await shutdownApp();
|
|
148
|
+
process.exitCode = 0;
|
|
149
|
+
};
|
|
150
|
+
process.on('SIGINT', cleanup);
|
|
151
|
+
process.on('SIGTERM', cleanup);
|
|
152
|
+
try {
|
|
153
|
+
// Commands that don't need full initialization
|
|
154
|
+
const noInitCommands = new Set([
|
|
155
|
+
'help',
|
|
156
|
+
'version',
|
|
157
|
+
'doctor',
|
|
158
|
+
'onboarding',
|
|
159
|
+
'upgrade',
|
|
160
|
+
'update',
|
|
161
|
+
]);
|
|
162
|
+
if (!noInitCommands.has(args[0])) {
|
|
163
|
+
// Show brief startup indicator for interactive commands
|
|
164
|
+
if (args[0] === 'chat' && process.stderr.isTTY) {
|
|
165
|
+
process.stderr.write('\x1b[2mStarting Nimbus...\x1b[0m');
|
|
166
|
+
}
|
|
167
|
+
await initApp();
|
|
168
|
+
if (args[0] === 'chat' && process.stderr.isTTY) {
|
|
169
|
+
process.stderr.write('\r\x1b[K');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Resolve aliases before dispatching (L2)
|
|
173
|
+
try {
|
|
174
|
+
const { resolveAlias } = await import('./commands/alias');
|
|
175
|
+
args = resolveAlias(args);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* alias file not found — ignore */
|
|
179
|
+
}
|
|
180
|
+
// Import and run CLI command router
|
|
181
|
+
const { runCommand } = await import('./cli');
|
|
182
|
+
await runCommand(args);
|
|
183
|
+
// After onboarding, clear auth cache, initialize app, and launch chat
|
|
184
|
+
if (args[0] === 'onboarding') {
|
|
185
|
+
// Clear the auth-bridge cache so the router picks up freshly-saved credentials
|
|
186
|
+
try {
|
|
187
|
+
const { clearAuthCache } = await import('./llm/auth-bridge');
|
|
188
|
+
clearAuthCache();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* non-critical */
|
|
192
|
+
}
|
|
193
|
+
await initApp();
|
|
194
|
+
await runCommand(['chat']);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
const msg = error.message || String(error);
|
|
199
|
+
if (error.code === 'MODULE_NOT_FOUND') {
|
|
200
|
+
console.error(`Error: Missing module — ${msg}`);
|
|
201
|
+
console.error('Run "npm install" to install dependencies.');
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.error(`Error: ${msg}`);
|
|
205
|
+
}
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
await shutdownApp();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
main();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nimbus Plugin System — Public API
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the primary class and types needed by the rest of the
|
|
5
|
+
* application and by external consumers that want to integrate with the
|
|
6
|
+
* plugin system without importing internal submodules directly.
|
|
7
|
+
*
|
|
8
|
+
* @module plugins
|
|
9
|
+
*/
|
|
10
|
+
// Manager (class + singleton)
|
|
11
|
+
export { PluginManager, pluginManager } from './manager';
|
|
12
|
+
// Loader (class — exposed for testing and advanced use)
|
|
13
|
+
export { PluginLoader } from './loader';
|