@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
package/src/commands/tf/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface TfCommandOptions {
|
|
|
22
22
|
recursive?: boolean;
|
|
23
23
|
diff?: boolean;
|
|
24
24
|
type?: 'plan' | 'apply';
|
|
25
|
+
workspace?: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -114,28 +115,88 @@ export async function tfPlanCommand(options: TfCommandOptions = {}): Promise<voi
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
/**
|
|
117
|
-
* Apply Terraform changes
|
|
118
|
+
* Apply Terraform changes — shows plan first and prompts for confirmation
|
|
119
|
+
* unless --auto-approve or a planFile is provided.
|
|
118
120
|
*/
|
|
119
121
|
export async function tfApplyCommand(options: TfCommandOptions = {}): Promise<void> {
|
|
120
122
|
const directory = options.directory || process.cwd();
|
|
123
|
+
const { confirm } = await import('../../wizard/prompts');
|
|
124
|
+
const os = await import('os');
|
|
125
|
+
const path = await import('path');
|
|
121
126
|
|
|
122
127
|
ui.header('Terraform Apply');
|
|
123
128
|
ui.info(`Directory: ${directory}`);
|
|
124
129
|
|
|
125
|
-
if (!options.autoApprove && !options.planFile) {
|
|
126
|
-
ui.warning('Running with -auto-approve flag or specify a plan file for non-interactive mode');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
ui.startSpinner({ message: 'Applying Terraform changes...' });
|
|
130
|
-
|
|
131
130
|
try {
|
|
132
131
|
const available = await terraformClient.isAvailable();
|
|
133
132
|
if (!available) {
|
|
134
|
-
ui.
|
|
133
|
+
ui.error('Terraform Tools Service not available');
|
|
135
134
|
ui.error('Please ensure the Terraform Tools Service is running.');
|
|
136
135
|
return;
|
|
137
136
|
}
|
|
138
137
|
|
|
138
|
+
// Select workspace if specified
|
|
139
|
+
if (options.workspace) {
|
|
140
|
+
ui.startSpinner({ message: `Selecting workspace "${options.workspace}"...` });
|
|
141
|
+
await terraformClient.workspace.select(options.workspace, directory);
|
|
142
|
+
ui.stopSpinnerSuccess(`Workspace: ${options.workspace}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// If no planFile and not auto-approving, run plan first and prompt
|
|
146
|
+
if (!options.planFile && !options.autoApprove) {
|
|
147
|
+
const tmpPlanPath = path.join(os.tmpdir(), `nimbus-tfplan-${Date.now()}.bin`);
|
|
148
|
+
|
|
149
|
+
ui.startSpinner({ message: 'Running terraform plan...' });
|
|
150
|
+
const planResult = await terraformClient.plan(directory, {
|
|
151
|
+
varFile: options.varFile,
|
|
152
|
+
vars: options.vars,
|
|
153
|
+
out: tmpPlanPath,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (!planResult.success) {
|
|
157
|
+
ui.stopSpinnerFail('Terraform plan failed');
|
|
158
|
+
if (planResult.error) ui.error(planResult.error);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!planResult.hasChanges) {
|
|
163
|
+
ui.stopSpinnerSuccess('No changes — infrastructure is up-to-date');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
ui.stopSpinnerSuccess('Plan complete');
|
|
168
|
+
if (planResult.output) {
|
|
169
|
+
ui.box({ title: 'Terraform Plan', content: planResult.output });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const proceed = await confirm({ message: 'Apply these changes?', defaultValue: false });
|
|
173
|
+
if (!proceed) {
|
|
174
|
+
ui.info('Apply cancelled.');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Apply using the saved plan file
|
|
179
|
+
ui.startSpinner({ message: 'Applying Terraform changes...' });
|
|
180
|
+
const applyResult = await terraformClient.apply(directory, {
|
|
181
|
+
planFile: tmpPlanPath,
|
|
182
|
+
varFile: options.varFile,
|
|
183
|
+
vars: options.vars,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (applyResult.success) {
|
|
187
|
+
ui.stopSpinnerSuccess('Terraform apply completed successfully');
|
|
188
|
+
if (applyResult.output) {
|
|
189
|
+
ui.box({ title: 'Apply Output', content: applyResult.output });
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
ui.stopSpinnerFail('Terraform apply failed');
|
|
193
|
+
if (applyResult.error) ui.error(applyResult.error);
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// auto-approve or explicit planFile path: apply directly
|
|
199
|
+
ui.startSpinner({ message: 'Applying Terraform changes...' });
|
|
139
200
|
const result = await terraformClient.apply(directory, {
|
|
140
201
|
planFile: options.planFile,
|
|
141
202
|
autoApprove: options.autoApprove,
|
|
@@ -150,9 +211,7 @@ export async function tfApplyCommand(options: TfCommandOptions = {}): Promise<vo
|
|
|
150
211
|
}
|
|
151
212
|
} else {
|
|
152
213
|
ui.stopSpinnerFail('Terraform apply failed');
|
|
153
|
-
if (result.error)
|
|
154
|
-
ui.error(result.error);
|
|
155
|
-
}
|
|
214
|
+
if (result.error) ui.error(result.error);
|
|
156
215
|
}
|
|
157
216
|
} catch (error: any) {
|
|
158
217
|
ui.stopSpinnerFail('Error applying Terraform changes');
|
package/src/commands/upgrade.ts
CHANGED
|
@@ -24,6 +24,10 @@ export interface UpgradeOptions {
|
|
|
24
24
|
force?: boolean;
|
|
25
25
|
/** Check only, don't actually upgrade */
|
|
26
26
|
check?: boolean;
|
|
27
|
+
/** Show what would be upgraded without actually upgrading */
|
|
28
|
+
dryRun?: boolean;
|
|
29
|
+
/** Show the changelog for the latest version */
|
|
30
|
+
changelog?: boolean;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/** Detected installation method for nimbus. */
|
|
@@ -179,6 +183,35 @@ async function fetchGitHubReleaseVersion(): Promise<string | null> {
|
|
|
179
183
|
}
|
|
180
184
|
}
|
|
181
185
|
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Changelog fetching
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Fetch the raw CHANGELOG.md from GitHub. Returns the last ~20 lines on success,
|
|
192
|
+
* or null if unavailable.
|
|
193
|
+
*/
|
|
194
|
+
async function fetchChangelog(): Promise<string | null> {
|
|
195
|
+
try {
|
|
196
|
+
const urls = [
|
|
197
|
+
`https://raw.githubusercontent.com/${GITHUB_REPO}/main/CHANGELOG.md`,
|
|
198
|
+
`https://raw.githubusercontent.com/${GITHUB_REPO}/main/CHANGELOG`,
|
|
199
|
+
];
|
|
200
|
+
for (const url of urls) {
|
|
201
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
202
|
+
if (response.ok) {
|
|
203
|
+
const text = await response.text();
|
|
204
|
+
const lines = text.split('\n');
|
|
205
|
+
// Return last 20 lines or the full content if shorter
|
|
206
|
+
return lines.slice(-20).join('\n');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
182
215
|
// ---------------------------------------------------------------------------
|
|
183
216
|
// Installation method detection
|
|
184
217
|
// ---------------------------------------------------------------------------
|
|
@@ -412,6 +445,30 @@ export async function upgradeCommand(options: UpgradeOptions = {}): Promise<void
|
|
|
412
445
|
}
|
|
413
446
|
console.log('');
|
|
414
447
|
|
|
448
|
+
// --dry-run: show what would be upgraded without upgrading
|
|
449
|
+
if (options.dryRun) {
|
|
450
|
+
console.log(
|
|
451
|
+
`${yellow('Dry run:')} Current: ${bold(VERSION)} → Latest: ${bold(green(latestVersion))}`
|
|
452
|
+
);
|
|
453
|
+
console.log(dim(` Source: ${source === 'github' ? 'GitHub Releases' : 'npm registry'}`));
|
|
454
|
+
console.log('');
|
|
455
|
+
console.log(dim('No changes were made. Remove --dry-run to perform the upgrade.'));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// --changelog: fetch and display the changelog
|
|
460
|
+
if (options.changelog) {
|
|
461
|
+
console.log(bold(`Changelog for nimbus ${latestVersion}:`));
|
|
462
|
+
console.log('');
|
|
463
|
+
const cl = await fetchChangelog();
|
|
464
|
+
if (cl) {
|
|
465
|
+
console.log(cl);
|
|
466
|
+
} else {
|
|
467
|
+
console.log(dim(`Changelog not available. View releases at: https://github.com/${GITHUB_REPO}/releases`));
|
|
468
|
+
}
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
415
472
|
if (options.check) {
|
|
416
473
|
return;
|
|
417
474
|
}
|
package/src/commands/version.ts
CHANGED
|
@@ -66,52 +66,45 @@ function getBunVersion(): string | undefined {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Fetch
|
|
69
|
+
* Fetch DevOps CLI tool versions (for verbose mode)
|
|
70
70
|
*/
|
|
71
|
-
async function
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
{ name: '
|
|
75
|
-
{ name: '
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
{ name: 'terraform-tools', url: process.env.TERRAFORM_TOOLS_URL || 'http://localhost:3006' },
|
|
81
|
-
{ name: 'k8s-tools', url: process.env.K8S_TOOLS_URL || 'http://localhost:3007' },
|
|
82
|
-
{ name: 'helm-tools', url: process.env.HELM_TOOLS_URL || 'http://localhost:3008' },
|
|
71
|
+
async function fetchDevOpsVersions(): Promise<Record<string, string>> {
|
|
72
|
+
const { execFileSync } = await import('child_process');
|
|
73
|
+
const tools = [
|
|
74
|
+
{ name: 'terraform', args: ['version', '-json'] },
|
|
75
|
+
{ name: 'kubectl', args: ['version', '--client', '--output=json'] },
|
|
76
|
+
{ name: 'helm', args: ['version', '--short'] },
|
|
77
|
+
{ name: 'aws', args: ['--version'] },
|
|
78
|
+
{ name: 'gcloud', args: ['version'] },
|
|
79
|
+
{ name: 'az', args: ['version'] },
|
|
83
80
|
];
|
|
84
81
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
82
|
+
const versions: Record<string, string> = {};
|
|
83
|
+
for (const tool of tools) {
|
|
88
84
|
try {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
await Promise.all(
|
|
99
|
-
services.map(async service => {
|
|
85
|
+
const output = execFileSync(tool.name, tool.args, {
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
timeout: 5000,
|
|
88
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
89
|
+
});
|
|
90
|
+
// Try to parse version string from JSON output
|
|
91
|
+
let version = 'installed';
|
|
100
92
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
components[service.name] = 'unavailable';
|
|
107
|
-
}
|
|
93
|
+
const parsed = JSON.parse(output);
|
|
94
|
+
version =
|
|
95
|
+
parsed.terraform_version ||
|
|
96
|
+
parsed.clientVersion?.gitVersion ||
|
|
97
|
+
'installed';
|
|
108
98
|
} catch {
|
|
109
|
-
|
|
99
|
+
const match = output.match(/[\d]+\.[\d]+\.[\d]+/);
|
|
100
|
+
if (match) version = `v${match[0]}`;
|
|
110
101
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
versions[tool.name] = version;
|
|
103
|
+
} catch {
|
|
104
|
+
versions[tool.name] = 'not installed';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return versions;
|
|
115
108
|
}
|
|
116
109
|
|
|
117
110
|
/**
|
|
@@ -120,25 +113,35 @@ async function fetchComponentVersions(): Promise<Record<string, string>> {
|
|
|
120
113
|
export async function versionCommand(options: VersionOptions = {}): Promise<void> {
|
|
121
114
|
logger.debug('Running version command', { options });
|
|
122
115
|
|
|
116
|
+
const cliVersion = getCliVersion();
|
|
123
117
|
const versionInfo: VersionInfo = {
|
|
124
|
-
cli:
|
|
118
|
+
cli: cliVersion,
|
|
125
119
|
node: process.version,
|
|
126
120
|
bun: getBunVersion(),
|
|
127
121
|
platform: process.platform,
|
|
128
122
|
arch: process.arch,
|
|
129
123
|
};
|
|
130
124
|
|
|
131
|
-
// Fetch
|
|
125
|
+
// Fetch DevOps tool versions in verbose mode
|
|
132
126
|
if (options.verbose) {
|
|
133
|
-
ui.startSpinner({ message: '
|
|
134
|
-
versionInfo.components = await
|
|
127
|
+
ui.startSpinner({ message: 'Checking DevOps tool versions...' });
|
|
128
|
+
versionInfo.components = await fetchDevOpsVersions();
|
|
135
129
|
ui.stopSpinnerSuccess('');
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
// JSON output
|
|
132
|
+
// JSON output — expose `version` field (alias for `cli`) for L3 compatibility
|
|
139
133
|
if (options.json) {
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
const jsonOutput: Record<string, unknown> = {
|
|
135
|
+
version: cliVersion,
|
|
136
|
+
node: versionInfo.node,
|
|
137
|
+
platform: versionInfo.platform,
|
|
138
|
+
arch: versionInfo.arch,
|
|
139
|
+
cli: versionInfo.cli,
|
|
140
|
+
};
|
|
141
|
+
if (versionInfo.bun) jsonOutput.bun = versionInfo.bun;
|
|
142
|
+
if (versionInfo.components) jsonOutput.components = versionInfo.components;
|
|
143
|
+
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
144
|
+
process.exit(0);
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
// Human-readable output
|
|
@@ -156,11 +159,12 @@ export async function versionCommand(options: VersionOptions = {}): Promise<void
|
|
|
156
159
|
|
|
157
160
|
if (versionInfo.components) {
|
|
158
161
|
ui.newLine();
|
|
159
|
-
ui.print(`
|
|
162
|
+
ui.print(`DevOps Tools:`);
|
|
160
163
|
for (const [name, version] of Object.entries(versionInfo.components)) {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
const isInstalled = version !== 'not installed';
|
|
165
|
+
const icon = isInstalled ? '[+]' : '[-]';
|
|
166
|
+
const color = isInstalled ? 'green' : 'red';
|
|
167
|
+
ui.print(` ${ui.color(icon, color)} ${name.padEnd(12)} ${version}`);
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nimbus watch — Watch files and trigger agent runs on change (M3)
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* nimbus watch "*.tf" --run "validate the terraform changes"
|
|
6
|
+
* nimbus watch "src/**" --run "run tests" --debounce 2000
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { basename } from 'node:path';
|
|
10
|
+
import { FSWatcher, watch as fsWatch } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
export interface WatchOptions {
|
|
13
|
+
/** Glob pattern to watch (e.g. "*.tf") */
|
|
14
|
+
glob: string;
|
|
15
|
+
/** Agent prompt to run on change */
|
|
16
|
+
run?: string;
|
|
17
|
+
/** Debounce delay in ms (default 500) */
|
|
18
|
+
debounce?: number;
|
|
19
|
+
/** Auto-approve agent runs */
|
|
20
|
+
autoApprove?: boolean;
|
|
21
|
+
/** Maximum number of runs before stopping (0 = unlimited) */
|
|
22
|
+
maxRuns?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Simple glob pattern matching without external dependencies.
|
|
27
|
+
* Supports: *.ext, *.*, prefix/**, prefix/name.ext
|
|
28
|
+
*/
|
|
29
|
+
function matchGlob(filename: string, pattern: string): boolean {
|
|
30
|
+
// Normalize separators
|
|
31
|
+
const f = filename.replace(/\\/g, '/');
|
|
32
|
+
const p = pattern.replace(/\\/g, '/');
|
|
33
|
+
|
|
34
|
+
// Convert glob pattern to regex
|
|
35
|
+
const regexStr = p
|
|
36
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // escape regex special chars (but not * and ?)
|
|
37
|
+
.replace(/\*\*/g, '__GLOBSTAR__')
|
|
38
|
+
.replace(/\*/g, '[^/]*')
|
|
39
|
+
.replace(/\?/g, '[^/]')
|
|
40
|
+
.replace(/__GLOBSTAR__/g, '.*');
|
|
41
|
+
|
|
42
|
+
const re = new RegExp(`^${regexStr}$`);
|
|
43
|
+
// Also match against basename for simple patterns like "*.tf"
|
|
44
|
+
const base = basename(f);
|
|
45
|
+
return re.test(f) || (!/\//.test(p) && re.test(base));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Start watching files matching the glob pattern.
|
|
50
|
+
* On change, triggers an agent run with the configured prompt.
|
|
51
|
+
*/
|
|
52
|
+
export async function watchCommand(options: WatchOptions): Promise<void> {
|
|
53
|
+
const { glob, run, debounce = 500, autoApprove = false, maxRuns = 0 } = options;
|
|
54
|
+
|
|
55
|
+
if (!glob) {
|
|
56
|
+
console.error('Usage: nimbus watch <glob> --run "prompt"');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!run) {
|
|
61
|
+
console.error('Error: --run "prompt" is required for watch command');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const cwd = process.cwd();
|
|
66
|
+
console.log(`Watching ${glob} in ${cwd}...`);
|
|
67
|
+
console.log(`On change, will run: "${run}"`);
|
|
68
|
+
console.log('Press Ctrl+C to stop.\n');
|
|
69
|
+
|
|
70
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
71
|
+
let runCount = 0;
|
|
72
|
+
let isRunning = false;
|
|
73
|
+
|
|
74
|
+
async function triggerRun(changedFile: string): Promise<void> {
|
|
75
|
+
if (isRunning) {
|
|
76
|
+
return; // Skip concurrent runs
|
|
77
|
+
}
|
|
78
|
+
if (maxRuns > 0 && runCount >= maxRuns) {
|
|
79
|
+
console.log(`\nMaximum runs (${maxRuns}) reached. Stopping watcher.`);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
runCount++;
|
|
84
|
+
isRunning = true;
|
|
85
|
+
const prompt = `${run}\n\nChanged file: ${changedFile}`;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
console.log(`\n[${new Date().toLocaleTimeString()}] Change detected: ${changedFile}`);
|
|
89
|
+
console.log(`Running: nimbus run "${run}"\n`);
|
|
90
|
+
|
|
91
|
+
const { initApp } = await import('../app');
|
|
92
|
+
const { executeRun } = await import('../cli/run');
|
|
93
|
+
const { router } = await initApp();
|
|
94
|
+
await executeRun(router, {
|
|
95
|
+
prompt,
|
|
96
|
+
format: 'text',
|
|
97
|
+
autoApprove,
|
|
98
|
+
stdin: false,
|
|
99
|
+
stdinJson: false,
|
|
100
|
+
mode: 'build',
|
|
101
|
+
maxTurns: 10,
|
|
102
|
+
rawToolOutput: false,
|
|
103
|
+
schema: false,
|
|
104
|
+
dryRun: false,
|
|
105
|
+
exitOnError: false,
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error(`Run failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
|
+
} finally {
|
|
110
|
+
isRunning = false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function onChange(filename: string | null): void {
|
|
115
|
+
if (!filename) return;
|
|
116
|
+
|
|
117
|
+
// Check if the file matches the glob pattern
|
|
118
|
+
if (!matchGlob(filename, glob)) return;
|
|
119
|
+
|
|
120
|
+
// Debounce rapid successive changes
|
|
121
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
122
|
+
debounceTimer = setTimeout(() => {
|
|
123
|
+
debounceTimer = null;
|
|
124
|
+
triggerRun(filename).catch(err => {
|
|
125
|
+
console.error('Watch trigger error:', err);
|
|
126
|
+
});
|
|
127
|
+
}, debounce);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let watcher: FSWatcher;
|
|
131
|
+
try {
|
|
132
|
+
watcher = fsWatch(cwd, { recursive: true }, (_eventType, filename) => {
|
|
133
|
+
onChange(filename);
|
|
134
|
+
});
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(`Failed to start watcher: ${error instanceof Error ? error.message : String(error)}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Handle shutdown gracefully
|
|
141
|
+
process.on('SIGINT', () => {
|
|
142
|
+
console.log('\nWatcher stopped.');
|
|
143
|
+
watcher.close();
|
|
144
|
+
process.exit(0);
|
|
145
|
+
});
|
|
146
|
+
process.on('SIGTERM', () => {
|
|
147
|
+
watcher.close();
|
|
148
|
+
process.exit(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Keep process alive
|
|
152
|
+
await new Promise<void>(() => { /* intentionally never resolves */ });
|
|
153
|
+
}
|
package/src/compat/runtime.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/** Whether the current runtime is Bun. */
|
|
9
|
-
export const isBun = typeof globalThis.Bun !== 'undefined';
|
|
9
|
+
export const isBun = typeof (globalThis as any).Bun !== 'undefined';
|
|
10
10
|
|
|
11
11
|
/** Whether the current runtime is Node.js (without Bun). */
|
|
12
12
|
export const isNode = !isBun && typeof process !== 'undefined' && !!process.versions?.node;
|
package/src/compat/sqlite.ts
CHANGED
|
@@ -79,11 +79,81 @@ if (isBun) {
|
|
|
79
79
|
}
|
|
80
80
|
_Impl = BetterSqlite3;
|
|
81
81
|
} catch {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
// G1: Path 3 — sql.js fallback (pure WASM, no native build required)
|
|
83
|
+
try {
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
85
|
+
const initSqlJs = require('sql.js');
|
|
86
|
+
process.stderr.write(
|
|
87
|
+
'[nimbus] WARNING: Running in in-memory SQLite mode (no persistence).\n' +
|
|
88
|
+
'[nimbus] Install better-sqlite3 for full functionality: npm install better-sqlite3\n'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
let sqlJsDb: { run(sql: string, params?: unknown[]): void; prepare(sql: string): unknown; close(): void } | null = null;
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
const getSqlJsDb = (): any => {
|
|
95
|
+
if (!sqlJsDb) throw new Error('sql.js database not yet initialised — call new Database() first');
|
|
96
|
+
return sqlJsDb;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
|
+
class SqlJsDatabase {
|
|
101
|
+
constructor(_path: string) {
|
|
102
|
+
// Synchronously initialise — sql.js init is async but we use a trick:
|
|
103
|
+
// initSqlJs() returns a promise; we store a sentinel and resolve lazily.
|
|
104
|
+
// For simplicity, initialise synchronously with the default WASM bundle.
|
|
105
|
+
try {
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
const SQL = (initSqlJs as any)({ locateFile: () => '' });
|
|
108
|
+
if (SQL && SQL.then) {
|
|
109
|
+
// async — best-effort; DB will be ready after first await
|
|
110
|
+
SQL.then((s: { Database: new () => typeof sqlJsDb }) => { sqlJsDb = new s.Database(); });
|
|
111
|
+
} else {
|
|
112
|
+
sqlJsDb = new (SQL as { Database: new () => typeof sqlJsDb }).Database();
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
// fallback: leave sqlJsDb null, operations will throw with clear message
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exec(sql: string): void { getSqlJsDb().run(sql); }
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
prepare(sql: string): any {
|
|
121
|
+
const db = getSqlJsDb();
|
|
122
|
+
const stmt = db.prepare(sql);
|
|
123
|
+
return {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
run: (...params: unknown[]) => { stmt.run(params as any[]); return {}; },
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
|
+
get: (...params: unknown[]) => { stmt.bind(params as any[]); return stmt.step() ? stmt.getAsObject() : undefined; },
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
129
|
+
all: (...params: unknown[]) => { const rows: unknown[] = []; stmt.bind(params as any[]); while (stmt.step()) rows.push(stmt.getAsObject()); return rows; },
|
|
130
|
+
finalize: () => stmt.free(),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
close(): void { getSqlJsDb().close(); sqlJsDb = null; }
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
135
|
+
transaction<T>(fn: (...args: any[]) => T): (...args: any[]) => T {
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
137
|
+
return (...args: any[]) => {
|
|
138
|
+
getSqlJsDb().run('BEGIN');
|
|
139
|
+
try { const r = fn(...args); getSqlJsDb().run('COMMIT'); return r; }
|
|
140
|
+
catch (e) { try { getSqlJsDb().run('ROLLBACK'); } catch { /* ignore */ } throw e; }
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
run(sql: string, params?: unknown[]): unknown {
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
return params ? getSqlJsDb().run(sql, params as any[]) : getSqlJsDb().run(sql);
|
|
146
|
+
}
|
|
147
|
+
query(sql: string) { return this.prepare(sql); }
|
|
148
|
+
}
|
|
149
|
+
_Impl = SqlJsDatabase;
|
|
150
|
+
} catch {
|
|
151
|
+
throw new Error(
|
|
152
|
+
'Nimbus requires either the Bun runtime (bun:sqlite) or the better-sqlite3 package.\n' +
|
|
153
|
+
'Install better-sqlite3: npm install better-sqlite3\n' +
|
|
154
|
+
'Or install Bun: curl -fsSL https://bun.sh/install | bash'
|
|
155
|
+
);
|
|
156
|
+
}
|
|
87
157
|
}
|
|
88
158
|
}
|
|
89
159
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mode persistence per working directory.
|
|
3
|
+
*
|
|
4
|
+
* Saves and loads the agent mode (plan/build/deploy) for each project directory
|
|
5
|
+
* using a JSON file at ~/.nimbus/mode-config.json.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AgentMode } from '../agent/system-prompt';
|
|
9
|
+
|
|
10
|
+
const MODE_CACHE = new Map<string, AgentMode>();
|
|
11
|
+
|
|
12
|
+
function getConfigPath(): string {
|
|
13
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
|
|
14
|
+
return `${home}/.nimbus/mode-config.json`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function loadConfig(): Record<string, AgentMode> {
|
|
18
|
+
try {
|
|
19
|
+
const { readFileSync } = require('node:fs') as typeof import('node:fs');
|
|
20
|
+
const raw = readFileSync(getConfigPath(), 'utf-8');
|
|
21
|
+
return JSON.parse(raw) as Record<string, AgentMode>;
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function saveConfig(data: Record<string, AgentMode>): void {
|
|
28
|
+
try {
|
|
29
|
+
const { writeFileSync, mkdirSync } = require('node:fs') as typeof import('node:fs');
|
|
30
|
+
const { dirname } = require('node:path') as typeof import('node:path');
|
|
31
|
+
const configPath = getConfigPath();
|
|
32
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
33
|
+
writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
34
|
+
} catch {
|
|
35
|
+
// Non-critical — silently ignore
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load the saved agent mode for the given working directory.
|
|
41
|
+
* Returns null if no mode has been saved for this directory.
|
|
42
|
+
*/
|
|
43
|
+
export function loadModeForCwd(cwd: string): AgentMode | null {
|
|
44
|
+
if (MODE_CACHE.has(cwd)) return MODE_CACHE.get(cwd) ?? null;
|
|
45
|
+
const config = loadConfig();
|
|
46
|
+
const mode = config[cwd] as AgentMode | undefined;
|
|
47
|
+
if (mode && ['plan', 'build', 'deploy'].includes(mode)) {
|
|
48
|
+
MODE_CACHE.set(cwd, mode);
|
|
49
|
+
return mode;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Save the agent mode for the given working directory.
|
|
56
|
+
*/
|
|
57
|
+
export function saveModeForCwd(cwd: string, mode: AgentMode): void {
|
|
58
|
+
MODE_CACHE.set(cwd, mode);
|
|
59
|
+
const config = loadConfig();
|
|
60
|
+
config[cwd] = mode;
|
|
61
|
+
saveConfig(config);
|
|
62
|
+
}
|