@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,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nimbus status — Infrastructure Status Dashboard
|
|
3
|
+
*
|
|
4
|
+
* Runs concurrent checks against common DevOps CLIs and shows a dashboard
|
|
5
|
+
* of the current cloud/infra context.
|
|
6
|
+
*
|
|
7
|
+
* G18: New command added to the gap fix plan.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* nimbus status
|
|
11
|
+
* nimbus status --json
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { ui } from '../wizard';
|
|
15
|
+
|
|
16
|
+
/** Options for the status command. */
|
|
17
|
+
export interface StatusOptions {
|
|
18
|
+
/** Output as JSON instead of table. */
|
|
19
|
+
json?: boolean;
|
|
20
|
+
/** L3: Show full snapshot including LLM config and active profile. */
|
|
21
|
+
verbose?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface StatusInfo {
|
|
25
|
+
k8sContext?: string;
|
|
26
|
+
tfWorkspace?: string;
|
|
27
|
+
awsAccount?: string;
|
|
28
|
+
awsRegion?: string;
|
|
29
|
+
gcpProject?: string;
|
|
30
|
+
lastDriftScan?: string;
|
|
31
|
+
// C2 enhancements
|
|
32
|
+
sessionCount?: number | string;
|
|
33
|
+
model?: string;
|
|
34
|
+
provider?: string;
|
|
35
|
+
nimbusMdSize?: number;
|
|
36
|
+
nimbusMdFound?: boolean;
|
|
37
|
+
dbSizeMB?: number;
|
|
38
|
+
// M1: Helm and pod health
|
|
39
|
+
helmFailedCount?: number;
|
|
40
|
+
helmTotalCount?: number;
|
|
41
|
+
unhealthyPodCount?: number;
|
|
42
|
+
errors: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run the nimbus status command.
|
|
47
|
+
* Checks k8s context, tf workspace, AWS identity, and GCP project concurrently.
|
|
48
|
+
*/
|
|
49
|
+
export async function statusCommand(options: StatusOptions = {}): Promise<void> {
|
|
50
|
+
const { execFileSync } = await import('node:child_process');
|
|
51
|
+
const { existsSync, statSync, readFileSync } = await import('node:fs');
|
|
52
|
+
const { join } = await import('node:path');
|
|
53
|
+
const { homedir } = await import('node:os');
|
|
54
|
+
|
|
55
|
+
const info: StatusInfo = { errors: [] };
|
|
56
|
+
|
|
57
|
+
const run = (cmd: string, args: string[]): string | undefined => {
|
|
58
|
+
try {
|
|
59
|
+
return execFileSync(cmd, args, {
|
|
60
|
+
encoding: 'utf-8',
|
|
61
|
+
timeout: 8000,
|
|
62
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
63
|
+
}).trim();
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Run all checks concurrently (M1: add helm + pod health)
|
|
70
|
+
const [k8sCtx, tfWs, awsIdentityRaw, gcpProject, helmListRaw, podUnhealthyRaw] = await Promise.allSettled([
|
|
71
|
+
Promise.resolve(run('kubectl', ['config', 'current-context'])),
|
|
72
|
+
Promise.resolve(run('terraform', ['workspace', 'show'])),
|
|
73
|
+
Promise.resolve(run('aws', ['sts', 'get-caller-identity', '--output', 'json'])),
|
|
74
|
+
Promise.resolve(run('gcloud', ['config', 'get-value', 'project'])),
|
|
75
|
+
Promise.resolve(run('helm', ['list', '-A', '--output', 'json'])),
|
|
76
|
+
Promise.resolve(run('kubectl', ['get', 'pods', '-A', '--field-selector=status.phase!=Running', '--no-headers'])),
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
if (k8sCtx.status === 'fulfilled' && k8sCtx.value) {
|
|
80
|
+
info.k8sContext = k8sCtx.value;
|
|
81
|
+
}
|
|
82
|
+
if (tfWs.status === 'fulfilled' && tfWs.value) {
|
|
83
|
+
info.tfWorkspace = tfWs.value;
|
|
84
|
+
}
|
|
85
|
+
if (awsIdentityRaw.status === 'fulfilled' && awsIdentityRaw.value) {
|
|
86
|
+
try {
|
|
87
|
+
const identity = JSON.parse(awsIdentityRaw.value);
|
|
88
|
+
info.awsAccount = identity.Account;
|
|
89
|
+
// Try to get region separately
|
|
90
|
+
const region = run('aws', ['configure', 'get', 'region']);
|
|
91
|
+
if (region) info.awsRegion = region;
|
|
92
|
+
} catch {
|
|
93
|
+
// Could not parse AWS identity
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (gcpProject.status === 'fulfilled' && gcpProject.value && gcpProject.value !== '(unset)') {
|
|
97
|
+
info.gcpProject = gcpProject.value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// M1: Parse helm release health
|
|
101
|
+
if (helmListRaw.status === 'fulfilled' && helmListRaw.value) {
|
|
102
|
+
try {
|
|
103
|
+
const releases = JSON.parse(helmListRaw.value) as Array<{ status: string }>;
|
|
104
|
+
info.helmTotalCount = releases.length;
|
|
105
|
+
info.helmFailedCount = releases.filter(r => r.status !== 'deployed').length;
|
|
106
|
+
} catch { /* non-critical */ }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// M1: Parse unhealthy pod count
|
|
110
|
+
if (podUnhealthyRaw.status === 'fulfilled' && podUnhealthyRaw.value) {
|
|
111
|
+
const lines = podUnhealthyRaw.value.trim().split('\n').filter(Boolean);
|
|
112
|
+
info.unhealthyPodCount = lines.length;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check last drift scan from SQLite + session count
|
|
116
|
+
try {
|
|
117
|
+
const { getDb } = await import('../state/db');
|
|
118
|
+
const db = getDb();
|
|
119
|
+
const row = db.prepare(`
|
|
120
|
+
SELECT created_at FROM sessions
|
|
121
|
+
ORDER BY created_at DESC LIMIT 1
|
|
122
|
+
`).get() as { created_at?: string } | undefined;
|
|
123
|
+
if (row?.created_at) {
|
|
124
|
+
info.lastDriftScan = row.created_at;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// C2: Count sessions
|
|
128
|
+
const countRow = db.prepare('SELECT COUNT(*) as cnt FROM sessions').get() as { cnt?: number } | undefined;
|
|
129
|
+
info.sessionCount = countRow?.cnt ?? 0;
|
|
130
|
+
} catch {
|
|
131
|
+
// DB not available
|
|
132
|
+
info.sessionCount = 'N/A';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// C2: Read model and provider from ~/.nimbus/config.json
|
|
136
|
+
const configJsonPath = join(homedir(), '.nimbus', 'config.json');
|
|
137
|
+
try {
|
|
138
|
+
if (existsSync(configJsonPath)) {
|
|
139
|
+
const configRaw = readFileSync(configJsonPath, 'utf-8');
|
|
140
|
+
const config = JSON.parse(configRaw) as Record<string, unknown>;
|
|
141
|
+
info.model = typeof config.model === 'string' ? config.model : 'claude-sonnet-4-6';
|
|
142
|
+
info.provider = typeof config.provider === 'string' ? config.provider : 'anthropic';
|
|
143
|
+
} else {
|
|
144
|
+
info.model = 'claude-sonnet-4-6';
|
|
145
|
+
info.provider = 'anthropic';
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
info.model = 'claude-sonnet-4-6';
|
|
149
|
+
info.provider = 'anthropic';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// C2: Check NIMBUS.md in cwd
|
|
153
|
+
const nimbusMdPaths = [
|
|
154
|
+
join(process.cwd(), 'NIMBUS.md'),
|
|
155
|
+
join(process.cwd(), '.nimbus', 'NIMBUS.md'),
|
|
156
|
+
];
|
|
157
|
+
for (const p of nimbusMdPaths) {
|
|
158
|
+
try {
|
|
159
|
+
if (existsSync(p)) {
|
|
160
|
+
const stat = statSync(p);
|
|
161
|
+
info.nimbusMdFound = true;
|
|
162
|
+
info.nimbusMdSize = stat.size;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
} catch { /* skip */ }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// C2: Get DB file size
|
|
169
|
+
const dbPath = join(homedir(), '.nimbus', 'nimbus.db');
|
|
170
|
+
try {
|
|
171
|
+
if (existsSync(dbPath)) {
|
|
172
|
+
const stat = statSync(dbPath);
|
|
173
|
+
info.dbSizeMB = Math.round((stat.size / (1024 * 1024)) * 10) / 10;
|
|
174
|
+
}
|
|
175
|
+
} catch { /* skip */ }
|
|
176
|
+
|
|
177
|
+
if (options.json) {
|
|
178
|
+
console.log(JSON.stringify(info, null, 2));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Pretty-print dashboard
|
|
183
|
+
// Build session line
|
|
184
|
+
const sessionLine = typeof info.sessionCount === 'number'
|
|
185
|
+
? `${info.sessionCount} sessions`
|
|
186
|
+
: 'N/A';
|
|
187
|
+
|
|
188
|
+
// Build NIMBUS.md line
|
|
189
|
+
const nimbusMdLine = info.nimbusMdFound && info.nimbusMdSize !== undefined
|
|
190
|
+
? ` loaded (${info.nimbusMdSize >= 1024
|
|
191
|
+
? `${(info.nimbusMdSize / 1024).toFixed(1)}KB`
|
|
192
|
+
: `${info.nimbusMdSize}B`})`
|
|
193
|
+
: ' not found';
|
|
194
|
+
|
|
195
|
+
// Build DB size line
|
|
196
|
+
const dbLine = info.dbSizeMB !== undefined
|
|
197
|
+
? `~/.nimbus/nimbus.db (${info.dbSizeMB}MB)`
|
|
198
|
+
: '~/.nimbus/nimbus.db (not found)';
|
|
199
|
+
|
|
200
|
+
ui.newLine();
|
|
201
|
+
ui.box({
|
|
202
|
+
title: 'Nimbus Infrastructure Status',
|
|
203
|
+
content: [
|
|
204
|
+
'',
|
|
205
|
+
` Session: ${sessionLine}`,
|
|
206
|
+
` Model: ${info.model ?? 'claude-sonnet-4-6'}`,
|
|
207
|
+
` Provider: ${info.provider ?? 'anthropic'} \u2713`,
|
|
208
|
+
'',
|
|
209
|
+
' Infrastructure:',
|
|
210
|
+
info.k8sContext
|
|
211
|
+
? ` Kubernetes: context=${info.k8sContext}`
|
|
212
|
+
: ' Kubernetes: (not configured)',
|
|
213
|
+
info.tfWorkspace
|
|
214
|
+
? ` Terraform: workspace=${info.tfWorkspace} \u2713 initialized`
|
|
215
|
+
: ' Terraform: (not in a terraform directory)',
|
|
216
|
+
info.awsAccount
|
|
217
|
+
? ` AWS: account=${info.awsAccount}${info.awsRegion ? ` region=${info.awsRegion}` : ''}`
|
|
218
|
+
: ' AWS: (not configured)',
|
|
219
|
+
info.gcpProject
|
|
220
|
+
? ` GCP: project=${info.gcpProject}`
|
|
221
|
+
: '',
|
|
222
|
+
// M1: Helm release health
|
|
223
|
+
...(info.helmTotalCount !== undefined
|
|
224
|
+
? [
|
|
225
|
+
info.helmFailedCount && info.helmFailedCount > 0
|
|
226
|
+
? ` [!] ${info.helmFailedCount} Helm release(s) in failed state (${info.helmTotalCount} total)`
|
|
227
|
+
: ` Helm: ${info.helmTotalCount} release(s) deployed`,
|
|
228
|
+
]
|
|
229
|
+
: []),
|
|
230
|
+
// M1: Pod health warnings
|
|
231
|
+
...(info.unhealthyPodCount !== undefined && info.unhealthyPodCount > 0
|
|
232
|
+
? [` [!] ${info.unhealthyPodCount} pod(s) not running`]
|
|
233
|
+
: []),
|
|
234
|
+
'',
|
|
235
|
+
` NIMBUS.md:${nimbusMdLine}`,
|
|
236
|
+
` DB: ${dbLine}`,
|
|
237
|
+
'',
|
|
238
|
+
info.lastDriftScan
|
|
239
|
+
? ` Last session: ${new Date(info.lastDriftScan).toLocaleString()}`
|
|
240
|
+
: ' Last session: (none)',
|
|
241
|
+
'',
|
|
242
|
+
' Quick actions: nimbus plan | nimbus apply | nimbus logs',
|
|
243
|
+
'',
|
|
244
|
+
].filter(line => line !== undefined) as string[],
|
|
245
|
+
style: 'rounded',
|
|
246
|
+
borderColor: 'cyan',
|
|
247
|
+
padding: 0,
|
|
248
|
+
});
|
|
249
|
+
ui.newLine();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default statusCommand;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team NIMBUS.md Distribution (L6)
|
|
3
|
+
*
|
|
4
|
+
* Share project context files with team members.
|
|
5
|
+
*
|
|
6
|
+
* nimbus team-context push — share current NIMBUS.md via sharing service
|
|
7
|
+
* nimbus team-context pull <url> — download NIMBUS.md, show diff, prompt before overwrite
|
|
8
|
+
*
|
|
9
|
+
* Also supports NIMBUS_INSTRUCTIONS_URL env var: on startup, fetch that URL
|
|
10
|
+
* and use as NIMBUS.md if local file not present.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { ui } from '../wizard/ui';
|
|
16
|
+
|
|
17
|
+
const NIMBUS_MD_PATHS = [
|
|
18
|
+
path.join(process.cwd(), 'NIMBUS.md'),
|
|
19
|
+
path.join(process.cwd(), '.nimbus', 'NIMBUS.md'),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
function findLocalNimbusMd(): string | null {
|
|
23
|
+
for (const p of NIMBUS_MD_PATHS) {
|
|
24
|
+
if (fs.existsSync(p)) return p;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function computeDiff(oldContent: string, newContent: string): string {
|
|
30
|
+
const oldLines = oldContent.split('\n');
|
|
31
|
+
const newLines = newContent.split('\n');
|
|
32
|
+
const lines: string[] = [];
|
|
33
|
+
|
|
34
|
+
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
35
|
+
for (let i = 0; i < maxLen; i++) {
|
|
36
|
+
const old = oldLines[i];
|
|
37
|
+
const newLine = newLines[i];
|
|
38
|
+
if (old === undefined) {
|
|
39
|
+
lines.push(`+ ${newLine}`);
|
|
40
|
+
} else if (newLine === undefined) {
|
|
41
|
+
lines.push(`- ${old}`);
|
|
42
|
+
} else if (old !== newLine) {
|
|
43
|
+
lines.push(`- ${old}`);
|
|
44
|
+
lines.push(`+ ${newLine}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Path to the team.md file in the current project's .nimbus/ directory */
|
|
51
|
+
const TEAM_MD_PATH = path.join(process.cwd(), '.nimbus', 'team.md');
|
|
52
|
+
|
|
53
|
+
/** Regex to strip lines containing sensitive info */
|
|
54
|
+
const SENSITIVE_PATTERN = /SENSITIVE:/i;
|
|
55
|
+
|
|
56
|
+
/** Strip lines containing "SENSITIVE:" from NIMBUS.md before sharing */
|
|
57
|
+
function stripSensitiveLines(content: string): string {
|
|
58
|
+
return content
|
|
59
|
+
.split('\n')
|
|
60
|
+
.filter(line => !SENSITIVE_PATTERN.test(line))
|
|
61
|
+
.join('\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Merge non-duplicate sections from teamContent into localContent.
|
|
66
|
+
* Sections are identified by Markdown headings (## ...).
|
|
67
|
+
*/
|
|
68
|
+
function mergeSections(localContent: string, teamContent: string): string {
|
|
69
|
+
// Split into sections by heading
|
|
70
|
+
const sectionRegex = /(?=^#{1,3} .+$)/m;
|
|
71
|
+
const localSections = localContent.split(sectionRegex);
|
|
72
|
+
const teamSections = teamContent.split(sectionRegex);
|
|
73
|
+
|
|
74
|
+
const localHeadings = new Set(
|
|
75
|
+
localSections
|
|
76
|
+
.map(s => s.match(/^(#{1,3} .+)$/m)?.[1]?.trim())
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const newSections = teamSections.filter(s => {
|
|
81
|
+
const heading = s.match(/^(#{1,3} .+)$/m)?.[1]?.trim();
|
|
82
|
+
return heading && !localHeadings.has(heading);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (newSections.length === 0) return localContent;
|
|
86
|
+
|
|
87
|
+
const merged = localContent.trimEnd() + '\n\n' + newSections.join('\n').trimStart();
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function teamContextCommand(subcommand: string, args: string[]): Promise<void> {
|
|
92
|
+
switch (subcommand) {
|
|
93
|
+
case 'push': {
|
|
94
|
+
// Git-based push: read NIMBUS.md, strip SENSITIVE lines, write to .nimbus/team.md
|
|
95
|
+
const localPath = findLocalNimbusMd();
|
|
96
|
+
if (!localPath) {
|
|
97
|
+
ui.warning('No NIMBUS.md found in current directory. Run `nimbus init` first.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const content = fs.readFileSync(localPath, 'utf-8');
|
|
102
|
+
const sanitized = stripSensitiveLines(content);
|
|
103
|
+
|
|
104
|
+
// Ensure .nimbus/ directory exists
|
|
105
|
+
const nimbusDir = path.dirname(TEAM_MD_PATH);
|
|
106
|
+
if (!fs.existsSync(nimbusDir)) {
|
|
107
|
+
fs.mkdirSync(nimbusDir, { recursive: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(TEAM_MD_PATH, sanitized, 'utf-8');
|
|
111
|
+
ui.print(`${ui.color('✓', 'green')} Written sanitized context to ${TEAM_MD_PATH}`);
|
|
112
|
+
ui.dim(`Removed all lines containing "SENSITIVE:"`);
|
|
113
|
+
ui.newLine();
|
|
114
|
+
|
|
115
|
+
// Stage the file with git
|
|
116
|
+
try {
|
|
117
|
+
const { execFileSync } = await import('node:child_process');
|
|
118
|
+
execFileSync('git', ['add', TEAM_MD_PATH], {
|
|
119
|
+
encoding: 'utf-8',
|
|
120
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
121
|
+
timeout: 10_000,
|
|
122
|
+
});
|
|
123
|
+
ui.print(`${ui.color('✓', 'green')} Staged .nimbus/team.md with git`);
|
|
124
|
+
ui.print(' Next step: git commit -m "chore: update team context" && git push');
|
|
125
|
+
} catch {
|
|
126
|
+
ui.dim('(git stage skipped — not a git repo or git not in PATH)');
|
|
127
|
+
ui.print(' Copy .nimbus/team.md to your repository and commit/push it manually.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ui.newLine();
|
|
131
|
+
ui.info('Note: Add .nimbus/team.md to version control (do NOT add .nimbus/team.md to .gitignore).');
|
|
132
|
+
ui.dim('Add NIMBUS.md to .gitignore if it contains sensitive local config.');
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
case 'pull': {
|
|
137
|
+
// Git-based pull: read .nimbus/team.md from cwd, diff, merge
|
|
138
|
+
if (!fs.existsSync(TEAM_MD_PATH)) {
|
|
139
|
+
ui.error(`.nimbus/team.md not found in ${process.cwd()}`);
|
|
140
|
+
ui.dim('Run `git pull` to fetch the latest team context, then re-run this command.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const teamContent = fs.readFileSync(TEAM_MD_PATH, 'utf-8');
|
|
145
|
+
const targetPath = NIMBUS_MD_PATHS[0];
|
|
146
|
+
const existing = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, 'utf-8') : null;
|
|
147
|
+
|
|
148
|
+
if (existing) {
|
|
149
|
+
const diff = computeDiff(existing, teamContent);
|
|
150
|
+
if (!diff) {
|
|
151
|
+
ui.info('NIMBUS.md is already up to date with team.md.');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
ui.header('NIMBUS.md vs .nimbus/team.md diff');
|
|
156
|
+
for (const line of diff.split('\n')) {
|
|
157
|
+
if (line.startsWith('+')) {
|
|
158
|
+
ui.print(ui.color(line, 'green'));
|
|
159
|
+
} else if (line.startsWith('-')) {
|
|
160
|
+
ui.print(ui.color(line, 'red'));
|
|
161
|
+
} else {
|
|
162
|
+
ui.print(line);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
ui.newLine();
|
|
166
|
+
|
|
167
|
+
// Prompt for confirmation
|
|
168
|
+
const { input: inputPrompt } = await import('../wizard/prompts');
|
|
169
|
+
const answer = await inputPrompt({
|
|
170
|
+
message: 'Merge new sections from team.md into NIMBUS.md? [y/N]',
|
|
171
|
+
defaultValue: 'N',
|
|
172
|
+
});
|
|
173
|
+
if (answer.toLowerCase() !== 'y') {
|
|
174
|
+
ui.info('Aborted — NIMBUS.md not changed.');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Merge: append non-duplicate sections
|
|
179
|
+
const merged = mergeSections(existing, teamContent);
|
|
180
|
+
fs.writeFileSync(targetPath, merged, 'utf-8');
|
|
181
|
+
ui.print(`${ui.color('✓', 'green')} Merged team context into ${targetPath}`);
|
|
182
|
+
} else {
|
|
183
|
+
// No local NIMBUS.md — use team.md as starting point
|
|
184
|
+
fs.writeFileSync(targetPath, teamContent, 'utf-8');
|
|
185
|
+
ui.print(`${ui.color('✓', 'green')} Created ${targetPath} from .nimbus/team.md`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
ui.newLine();
|
|
189
|
+
ui.dim('Note: Add NIMBUS.md to .gitignore to keep local config private.');
|
|
190
|
+
ui.dim('Add .nimbus/team.md to version control to share project context with team.');
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
default:
|
|
195
|
+
ui.print('Usage: nimbus team-context <push|pull>');
|
|
196
|
+
ui.print('');
|
|
197
|
+
ui.print(' push Strip SENSITIVE: lines from NIMBUS.md, write to .nimbus/team.md, and git add');
|
|
198
|
+
ui.print(' pull Read .nimbus/team.md, diff against NIMBUS.md, merge on confirmation');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Fetch NIMBUS.md from NIMBUS_INSTRUCTIONS_URL env var.
|
|
204
|
+
* Called on startup when no local NIMBUS.md exists.
|
|
205
|
+
*/
|
|
206
|
+
export async function fetchRemoteNimbusMd(): Promise<string | null> {
|
|
207
|
+
const url = process.env.NIMBUS_INSTRUCTIONS_URL;
|
|
208
|
+
if (!url) return null;
|
|
209
|
+
|
|
210
|
+
const localPath = findLocalNimbusMd();
|
|
211
|
+
if (localPath) return null; // local file takes priority
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
|
|
215
|
+
if (!response.ok) return null;
|
|
216
|
+
return await response.text();
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
package/src/commands/template.ts
CHANGED
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* CLI commands for managing infrastructure templates
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { getDb } from '../state/db';
|
|
8
|
+
import { randomUUID } from 'crypto';
|
|
8
9
|
import { ui } from '../wizard/ui';
|
|
9
10
|
|
|
10
|
-
const STATE_SERVICE_URL = process.env.STATE_SERVICE_URL || 'http://localhost:3004';
|
|
11
|
-
|
|
12
11
|
export interface TemplateCommandOptions {
|
|
13
12
|
type?: string;
|
|
14
13
|
name?: string;
|
|
@@ -24,34 +23,35 @@ async function templateListCommand(options: TemplateCommandOptions = {}): Promis
|
|
|
24
23
|
ui.startSpinner({ message: 'Fetching templates...' });
|
|
25
24
|
|
|
26
25
|
try {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
26
|
+
const db = getDb();
|
|
27
|
+
const templates = db
|
|
28
|
+
.prepare(
|
|
29
|
+
'SELECT * FROM templates WHERE (? IS NULL OR type=?) ORDER BY created_at DESC'
|
|
30
|
+
)
|
|
31
|
+
.all(options.type ?? null, options.type ?? null) as Array<Record<string, unknown>>;
|
|
32
|
+
|
|
33
|
+
ui.stopSpinnerSuccess(`Found ${templates.length} template(s)`);
|
|
34
|
+
|
|
35
|
+
if (templates.length > 0) {
|
|
36
|
+
ui.table({
|
|
37
|
+
columns: [
|
|
38
|
+
{ key: 'id', header: 'ID' },
|
|
39
|
+
{ key: 'name', header: 'Name' },
|
|
40
|
+
{ key: 'type', header: 'Type' },
|
|
41
|
+
{ key: 'createdAt', header: 'Created' },
|
|
42
|
+
],
|
|
43
|
+
data: templates.map((t) => ({
|
|
44
|
+
id: typeof t.id === 'string' ? t.id.substring(0, 8) : '-',
|
|
45
|
+
name: typeof t.name === 'string' ? t.name : '-',
|
|
46
|
+
type: typeof t.type === 'string' ? t.type : '-',
|
|
47
|
+
createdAt:
|
|
48
|
+
typeof t.created_at === 'string'
|
|
49
|
+
? new Date(t.created_at).toLocaleDateString()
|
|
50
|
+
: '-',
|
|
51
|
+
})),
|
|
52
|
+
});
|
|
53
53
|
} else {
|
|
54
|
-
ui.
|
|
54
|
+
ui.info('No templates found. Use "nimbus template save" to create one.');
|
|
55
55
|
}
|
|
56
56
|
} catch (error: any) {
|
|
57
57
|
ui.stopSpinnerFail('Error fetching templates');
|
|
@@ -70,22 +70,31 @@ async function templateGetCommand(
|
|
|
70
70
|
ui.startSpinner({ message: 'Fetching template...' });
|
|
71
71
|
|
|
72
72
|
try {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
73
|
+
const db = getDb();
|
|
74
|
+
const template = db
|
|
75
|
+
.prepare('SELECT * FROM templates WHERE id LIKE ?')
|
|
76
|
+
.get(id + '%') as Record<string, unknown> | undefined;
|
|
75
77
|
|
|
76
|
-
if (
|
|
78
|
+
if (template) {
|
|
77
79
|
ui.stopSpinnerSuccess('Template retrieved');
|
|
78
|
-
const template = result.data;
|
|
79
80
|
|
|
80
81
|
ui.print(` ${ui.color('Name:', 'cyan')} ${template.name || '-'}`);
|
|
81
82
|
ui.print(` ${ui.color('Type:', 'cyan')} ${template.type || '-'}`);
|
|
82
83
|
ui.print(` ${ui.color('ID:', 'cyan')} ${template.id || '-'}`);
|
|
83
|
-
ui.print(` ${ui.color('Created:', 'cyan')} ${template.
|
|
84
|
+
ui.print(` ${ui.color('Created:', 'cyan')} ${template.created_at || '-'}`);
|
|
85
|
+
|
|
86
|
+
let variables: Record<string, unknown> = {};
|
|
87
|
+
try {
|
|
88
|
+
variables =
|
|
89
|
+
typeof template.variables === 'string'
|
|
90
|
+
? JSON.parse(template.variables)
|
|
91
|
+
: {};
|
|
92
|
+
} catch { /* ignore */ }
|
|
84
93
|
|
|
85
|
-
if (
|
|
94
|
+
if (Object.keys(variables).length > 0) {
|
|
86
95
|
ui.newLine();
|
|
87
96
|
ui.print(` ${ui.color('Variables:', 'cyan')}`);
|
|
88
|
-
for (const [key, val] of Object.entries(
|
|
97
|
+
for (const [key, val] of Object.entries(variables)) {
|
|
89
98
|
ui.print(` ${key}: ${JSON.stringify(val)}`);
|
|
90
99
|
}
|
|
91
100
|
}
|
|
@@ -134,22 +143,14 @@ async function templateSaveCommand(options: TemplateCommandOptions = {}): Promis
|
|
|
134
143
|
ui.startSpinner({ message: 'Saving template...' });
|
|
135
144
|
|
|
136
145
|
try {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
if (result.success) {
|
|
146
|
-
ui.stopSpinnerSuccess(`Template "${options.name}" saved`);
|
|
147
|
-
if (result.data?.id) {
|
|
148
|
-
ui.info(`ID: ${result.data.id}`);
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
ui.stopSpinnerFail('Failed to save template');
|
|
152
|
-
}
|
|
146
|
+
const db = getDb();
|
|
147
|
+
const id = randomUUID();
|
|
148
|
+
db.prepare(
|
|
149
|
+
'INSERT INTO templates (id,name,type,content,variables,created_at,updated_at) VALUES (?,?,?,?,?,datetime("now"),datetime("now"))'
|
|
150
|
+
).run(id, options.name, options.type || 'terraform', content, '{}');
|
|
151
|
+
|
|
152
|
+
ui.stopSpinnerSuccess(`Template "${options.name}" saved`);
|
|
153
|
+
ui.info(`ID: ${id}`);
|
|
153
154
|
} catch (error: any) {
|
|
154
155
|
ui.stopSpinnerFail('Error saving template');
|
|
155
156
|
ui.error(error.message);
|
|
@@ -164,13 +165,13 @@ async function templateDeleteCommand(id: string): Promise<void> {
|
|
|
164
165
|
ui.startSpinner({ message: 'Deleting template...' });
|
|
165
166
|
|
|
166
167
|
try {
|
|
167
|
-
const
|
|
168
|
-
const result =
|
|
168
|
+
const db = getDb();
|
|
169
|
+
const result = db.prepare('DELETE FROM templates WHERE id=?').run(id);
|
|
169
170
|
|
|
170
|
-
if (result.
|
|
171
|
+
if ((result as any).changes > 0) {
|
|
171
172
|
ui.stopSpinnerSuccess('Template deleted');
|
|
172
173
|
} else {
|
|
173
|
-
ui.stopSpinnerFail('
|
|
174
|
+
ui.stopSpinnerFail('Template not found');
|
|
174
175
|
}
|
|
175
176
|
} catch (error: any) {
|
|
176
177
|
ui.stopSpinnerFail('Error deleting template');
|