@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.
Files changed (469) hide show
  1. package/bin/nimbus +26 -10
  2. package/bin/nimbus.cmd +41 -0
  3. package/bin/nimbus.mjs +70 -0
  4. package/completions/nimbus.bash +38 -0
  5. package/completions/nimbus.fish +48 -0
  6. package/completions/nimbus.zsh +81 -0
  7. package/dist/src/agent/compaction-agent.js +215 -0
  8. package/dist/src/agent/context-manager.js +385 -0
  9. package/dist/src/agent/context.js +322 -0
  10. package/dist/src/agent/deploy-preview.js +395 -0
  11. package/dist/src/agent/expand-files.js +95 -0
  12. package/dist/src/agent/index.js +18 -0
  13. package/dist/src/agent/loop.js +1535 -0
  14. package/dist/src/agent/modes.js +347 -0
  15. package/dist/src/agent/permissions.js +396 -0
  16. package/dist/src/agent/subagents/base.js +67 -0
  17. package/dist/src/agent/subagents/cost.js +45 -0
  18. package/dist/src/agent/subagents/explore.js +36 -0
  19. package/dist/src/agent/subagents/general.js +41 -0
  20. package/dist/src/agent/subagents/index.js +88 -0
  21. package/dist/src/agent/subagents/infra.js +52 -0
  22. package/dist/src/agent/subagents/security.js +60 -0
  23. package/dist/src/agent/system-prompt.js +860 -0
  24. package/dist/src/app.js +152 -0
  25. package/dist/src/audit/activity-log.js +209 -0
  26. package/dist/src/audit/compliance-checker.js +419 -0
  27. package/dist/src/audit/cost-tracker.js +231 -0
  28. package/dist/src/audit/index.js +10 -0
  29. package/dist/src/audit/security-scanner.js +490 -0
  30. package/dist/src/auth/guard.js +64 -0
  31. package/dist/src/auth/index.js +19 -0
  32. package/dist/src/auth/keychain.js +79 -0
  33. package/dist/src/auth/oauth.js +389 -0
  34. package/dist/src/auth/providers.js +415 -0
  35. package/dist/src/auth/sso.js +87 -0
  36. package/dist/src/auth/store.js +424 -0
  37. package/dist/src/auth/types.js +5 -0
  38. package/dist/src/cli/index.js +8 -0
  39. package/dist/src/cli/init.js +1048 -0
  40. package/dist/src/cli/openapi-spec.js +346 -0
  41. package/dist/src/cli/run.js +505 -0
  42. package/dist/src/cli/serve-auth.js +56 -0
  43. package/dist/src/cli/serve.js +432 -0
  44. package/dist/src/cli/web.js +50 -0
  45. package/dist/src/cli.js +1574 -0
  46. package/dist/src/clients/core-engine-client.js +156 -0
  47. package/dist/src/clients/enterprise-client.js +246 -0
  48. package/dist/src/clients/generator-client.js +219 -0
  49. package/dist/src/clients/git-client.js +367 -0
  50. package/dist/src/clients/github-client.js +229 -0
  51. package/dist/src/clients/helm-client.js +299 -0
  52. package/dist/src/clients/index.js +18 -0
  53. package/dist/src/clients/k8s-client.js +270 -0
  54. package/dist/src/clients/llm-client.js +119 -0
  55. package/dist/src/clients/rest-client.js +104 -0
  56. package/dist/src/clients/service-discovery.js +35 -0
  57. package/dist/src/clients/terraform-client.js +302 -0
  58. package/dist/src/clients/tools-client.js +1227 -0
  59. package/dist/src/clients/ws-client.js +93 -0
  60. package/dist/src/commands/alias.js +91 -0
  61. package/dist/src/commands/analyze/index.js +313 -0
  62. package/dist/src/commands/apply/helm.js +375 -0
  63. package/dist/src/commands/apply/index.js +176 -0
  64. package/dist/src/commands/apply/k8s.js +350 -0
  65. package/dist/src/commands/apply/terraform.js +465 -0
  66. package/dist/src/commands/ask.js +137 -0
  67. package/dist/src/commands/audit/index.js +322 -0
  68. package/dist/src/commands/auth-cloud.js +345 -0
  69. package/dist/src/commands/auth-list.js +112 -0
  70. package/dist/src/commands/auth-profile.js +104 -0
  71. package/dist/src/commands/auth-refresh.js +161 -0
  72. package/dist/src/commands/auth-status.js +122 -0
  73. package/dist/src/commands/aws/ec2.js +402 -0
  74. package/dist/src/commands/aws/iam.js +304 -0
  75. package/dist/src/commands/aws/index.js +108 -0
  76. package/dist/src/commands/aws/lambda.js +317 -0
  77. package/dist/src/commands/aws/rds.js +345 -0
  78. package/dist/src/commands/aws/s3.js +346 -0
  79. package/dist/src/commands/aws/vpc.js +302 -0
  80. package/dist/src/commands/aws-discover.js +413 -0
  81. package/dist/src/commands/aws-terraform.js +618 -0
  82. package/dist/src/commands/azure/aks.js +305 -0
  83. package/dist/src/commands/azure/functions.js +200 -0
  84. package/dist/src/commands/azure/index.js +93 -0
  85. package/dist/src/commands/azure/storage.js +378 -0
  86. package/dist/src/commands/azure/vm.js +291 -0
  87. package/dist/src/commands/billing/index.js +224 -0
  88. package/dist/src/commands/chat.js +259 -0
  89. package/dist/src/commands/completions.js +255 -0
  90. package/dist/src/commands/config.js +291 -0
  91. package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
  92. package/dist/src/commands/cost/estimator.js +73 -0
  93. package/dist/src/commands/cost/index.js +625 -0
  94. package/dist/src/commands/cost/parsers/terraform.js +234 -0
  95. package/dist/src/commands/cost/parsers/types.js +4 -0
  96. package/dist/src/commands/cost/pricing/aws.js +501 -0
  97. package/dist/src/commands/cost/pricing/azure.js +462 -0
  98. package/dist/src/commands/cost/pricing/gcp.js +359 -0
  99. package/dist/src/commands/cost/pricing/index.js +24 -0
  100. package/dist/src/commands/demo.js +196 -0
  101. package/dist/src/commands/deploy.js +215 -0
  102. package/dist/src/commands/doctor.js +1291 -0
  103. package/dist/src/commands/drift/index.js +674 -0
  104. package/dist/src/commands/explain.js +235 -0
  105. package/dist/src/commands/export.js +120 -0
  106. package/dist/src/commands/feedback.js +319 -0
  107. package/dist/src/commands/fix.js +263 -0
  108. package/dist/src/commands/fs/index.js +338 -0
  109. package/dist/src/commands/gcp/compute.js +266 -0
  110. package/dist/src/commands/gcp/functions.js +221 -0
  111. package/dist/src/commands/gcp/gke.js +357 -0
  112. package/dist/src/commands/gcp/iam.js +295 -0
  113. package/dist/src/commands/gcp/index.js +105 -0
  114. package/dist/src/commands/gcp/storage.js +232 -0
  115. package/dist/src/commands/generate-helm.js +1026 -0
  116. package/dist/src/commands/generate-k8s.js +1263 -0
  117. package/dist/src/commands/generate-terraform.js +1058 -0
  118. package/dist/src/commands/gh/index.js +663 -0
  119. package/dist/src/commands/git/index.js +1208 -0
  120. package/dist/src/commands/helm/index.js +985 -0
  121. package/dist/src/commands/help.js +639 -0
  122. package/dist/src/commands/history.js +120 -0
  123. package/dist/src/commands/import.js +782 -0
  124. package/dist/src/commands/incident.js +144 -0
  125. package/dist/src/commands/index.js +109 -0
  126. package/dist/src/commands/init.js +955 -0
  127. package/dist/src/commands/k8s/index.js +979 -0
  128. package/dist/src/commands/login.js +588 -0
  129. package/dist/src/commands/logout.js +61 -0
  130. package/dist/src/commands/logs.js +160 -0
  131. package/dist/src/commands/onboarding.js +382 -0
  132. package/dist/src/commands/pipeline.js +153 -0
  133. package/dist/src/commands/plan/display.js +216 -0
  134. package/dist/src/commands/plan/index.js +525 -0
  135. package/dist/src/commands/plugin.js +325 -0
  136. package/dist/src/commands/preview.js +356 -0
  137. package/dist/src/commands/profile.js +297 -0
  138. package/dist/src/commands/questionnaire.js +1021 -0
  139. package/dist/src/commands/resume.js +35 -0
  140. package/dist/src/commands/rollback.js +259 -0
  141. package/dist/src/commands/rollout.js +74 -0
  142. package/dist/src/commands/runbook.js +307 -0
  143. package/dist/src/commands/schedule.js +202 -0
  144. package/dist/src/commands/status.js +213 -0
  145. package/dist/src/commands/team/index.js +309 -0
  146. package/dist/src/commands/team-context.js +200 -0
  147. package/dist/src/commands/template.js +204 -0
  148. package/dist/src/commands/tf/index.js +989 -0
  149. package/dist/src/commands/upgrade.js +515 -0
  150. package/dist/src/commands/usage/index.js +118 -0
  151. package/dist/src/commands/version.js +145 -0
  152. package/dist/src/commands/watch.js +127 -0
  153. package/dist/src/compat/index.js +2 -0
  154. package/dist/src/compat/runtime.js +10 -0
  155. package/dist/src/compat/sqlite.js +144 -0
  156. package/dist/src/config/index.js +6 -0
  157. package/dist/src/config/manager.js +469 -0
  158. package/dist/src/config/mode-store.js +57 -0
  159. package/dist/src/config/profiles.js +66 -0
  160. package/dist/src/config/safety-policy.js +251 -0
  161. package/dist/src/config/schema.js +107 -0
  162. package/dist/src/config/types.js +311 -0
  163. package/dist/src/config/workspace-state.js +38 -0
  164. package/dist/src/context/context-db.js +138 -0
  165. package/dist/src/demo/index.js +295 -0
  166. package/dist/src/demo/scenarios/full-journey.js +226 -0
  167. package/dist/src/demo/scenarios/getting-started.js +124 -0
  168. package/dist/src/demo/scenarios/helm-release.js +334 -0
  169. package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
  170. package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
  171. package/dist/src/demo/types.js +6 -0
  172. package/dist/src/engine/cost-estimator.js +334 -0
  173. package/dist/src/engine/diagram-generator.js +192 -0
  174. package/dist/src/engine/drift-detector.js +688 -0
  175. package/dist/src/engine/executor.js +832 -0
  176. package/dist/src/engine/index.js +39 -0
  177. package/dist/src/engine/orchestrator.js +436 -0
  178. package/dist/src/engine/planner.js +616 -0
  179. package/dist/src/engine/safety.js +609 -0
  180. package/dist/src/engine/verifier.js +664 -0
  181. package/dist/src/enterprise/audit.js +241 -0
  182. package/dist/src/enterprise/auth.js +189 -0
  183. package/dist/src/enterprise/billing.js +512 -0
  184. package/dist/src/enterprise/index.js +16 -0
  185. package/dist/src/enterprise/teams.js +315 -0
  186. package/dist/src/generator/best-practices.js +1375 -0
  187. package/dist/src/generator/helm.js +495 -0
  188. package/dist/src/generator/index.js +11 -0
  189. package/dist/src/generator/intent-parser.js +420 -0
  190. package/dist/src/generator/kubernetes.js +773 -0
  191. package/dist/src/generator/terraform.js +1472 -0
  192. package/dist/src/history/index.js +6 -0
  193. package/dist/src/history/manager.js +199 -0
  194. package/dist/src/history/types.js +6 -0
  195. package/dist/src/hooks/config.js +318 -0
  196. package/dist/src/hooks/engine.js +317 -0
  197. package/dist/src/hooks/index.js +2 -0
  198. package/dist/src/llm/auth-bridge.js +157 -0
  199. package/dist/src/llm/circuit-breaker.js +116 -0
  200. package/dist/src/llm/config-loader.js +172 -0
  201. package/dist/src/llm/cost-calculator.js +137 -0
  202. package/dist/src/llm/index.js +7 -0
  203. package/dist/src/llm/model-aliases.js +99 -0
  204. package/dist/src/llm/provider-registry.js +57 -0
  205. package/dist/src/llm/providers/anthropic.js +430 -0
  206. package/dist/src/llm/providers/bedrock.js +409 -0
  207. package/dist/src/llm/providers/google.js +344 -0
  208. package/dist/src/llm/providers/ollama.js +661 -0
  209. package/dist/src/llm/providers/openai-compatible.js +289 -0
  210. package/dist/src/llm/providers/openai.js +284 -0
  211. package/dist/src/llm/providers/openrouter.js +293 -0
  212. package/dist/src/llm/router.js +844 -0
  213. package/dist/src/llm/types.js +69 -0
  214. package/dist/src/lsp/client.js +239 -0
  215. package/dist/src/lsp/languages.js +95 -0
  216. package/dist/src/lsp/manager.js +243 -0
  217. package/dist/src/mcp/client.js +289 -0
  218. package/dist/src/mcp/index.js +5 -0
  219. package/dist/src/mcp/manager.js +113 -0
  220. package/dist/src/nimbus.js +212 -0
  221. package/dist/src/plugins/index.js +13 -0
  222. package/dist/src/plugins/loader.js +280 -0
  223. package/dist/src/plugins/manager.js +282 -0
  224. package/dist/src/plugins/types.js +23 -0
  225. package/dist/src/scanners/cicd-scanner.js +230 -0
  226. package/dist/src/scanners/cloud-scanner.js +415 -0
  227. package/dist/src/scanners/framework-scanner.js +430 -0
  228. package/dist/src/scanners/iac-scanner.js +350 -0
  229. package/dist/src/scanners/index.js +454 -0
  230. package/dist/src/scanners/language-scanner.js +258 -0
  231. package/dist/src/scanners/package-manager-scanner.js +252 -0
  232. package/dist/src/scanners/types.js +6 -0
  233. package/dist/src/sessions/manager.js +395 -0
  234. package/dist/src/sessions/types.js +4 -0
  235. package/dist/src/sharing/sync.js +238 -0
  236. package/dist/src/sharing/viewer.js +131 -0
  237. package/dist/src/snapshots/index.js +1 -0
  238. package/dist/src/snapshots/manager.js +432 -0
  239. package/dist/src/state/artifacts.js +94 -0
  240. package/dist/src/state/audit.js +73 -0
  241. package/dist/src/state/billing.js +126 -0
  242. package/dist/src/state/checkpoints.js +81 -0
  243. package/dist/src/state/config.js +58 -0
  244. package/dist/src/state/conversations.js +7 -0
  245. package/dist/src/state/credentials.js +96 -0
  246. package/dist/src/state/db.js +53 -0
  247. package/dist/src/state/index.js +23 -0
  248. package/dist/src/state/messages.js +76 -0
  249. package/dist/src/state/projects.js +92 -0
  250. package/dist/src/state/schema.js +233 -0
  251. package/dist/src/state/sessions.js +79 -0
  252. package/dist/src/state/teams.js +131 -0
  253. package/dist/src/telemetry.js +91 -0
  254. package/dist/src/tools/aws-ops.js +747 -0
  255. package/dist/src/tools/azure-ops.js +491 -0
  256. package/dist/src/tools/file-ops.js +451 -0
  257. package/dist/src/tools/gcp-ops.js +559 -0
  258. package/dist/src/tools/git-ops.js +557 -0
  259. package/dist/src/tools/github-ops.js +460 -0
  260. package/dist/src/tools/helm-ops.js +634 -0
  261. package/dist/src/tools/index.js +16 -0
  262. package/dist/src/tools/k8s-ops.js +579 -0
  263. package/dist/src/tools/schemas/converter.js +129 -0
  264. package/dist/src/tools/schemas/devops.js +3319 -0
  265. package/dist/src/tools/schemas/index.js +19 -0
  266. package/dist/src/tools/schemas/standard.js +966 -0
  267. package/dist/src/tools/schemas/types.js +409 -0
  268. package/dist/src/tools/spawn-exec.js +109 -0
  269. package/dist/src/tools/terraform-ops.js +627 -0
  270. package/dist/src/types/config.js +1 -0
  271. package/dist/src/types/drift.js +4 -0
  272. package/dist/src/types/enterprise.js +5 -0
  273. package/dist/src/types/index.js +14 -0
  274. package/dist/src/types/plan.js +1 -0
  275. package/dist/src/types/request.js +1 -0
  276. package/dist/src/types/response.js +1 -0
  277. package/dist/src/types/service.js +1 -0
  278. package/dist/src/ui/App.js +1672 -0
  279. package/dist/src/ui/DeployPreview.js +60 -0
  280. package/dist/src/ui/FileDiffModal.js +108 -0
  281. package/dist/src/ui/Header.js +46 -0
  282. package/dist/src/ui/HelpModal.js +9 -0
  283. package/dist/src/ui/InputBox.js +408 -0
  284. package/dist/src/ui/MessageList.js +795 -0
  285. package/dist/src/ui/PermissionPrompt.js +72 -0
  286. package/dist/src/ui/StatusBar.js +109 -0
  287. package/dist/src/ui/TerminalPane.js +31 -0
  288. package/dist/src/ui/ToolCallDisplay.js +303 -0
  289. package/dist/src/ui/TreePane.js +83 -0
  290. package/dist/src/ui/chat-ui.js +721 -0
  291. package/dist/src/ui/index.js +11 -0
  292. package/dist/src/ui/ink/index.js +1325 -0
  293. package/dist/src/ui/streaming.js +137 -0
  294. package/dist/src/ui/theme.js +78 -0
  295. package/dist/src/ui/types.js +7 -0
  296. package/dist/src/utils/analytics.js +61 -0
  297. package/dist/src/utils/cost-warning.js +25 -0
  298. package/dist/src/utils/env.js +42 -0
  299. package/dist/src/utils/errors.js +54 -0
  300. package/dist/src/utils/event-bus.js +22 -0
  301. package/dist/src/utils/index.js +16 -0
  302. package/dist/src/utils/logger.js +150 -0
  303. package/dist/src/utils/rate-limiter.js +90 -0
  304. package/dist/src/utils/service-auth.js +36 -0
  305. package/dist/src/utils/validation.js +39 -0
  306. package/dist/src/version.js +3 -0
  307. package/dist/src/watcher/index.js +192 -0
  308. package/dist/src/wizard/approval.js +275 -0
  309. package/dist/src/wizard/index.js +13 -0
  310. package/dist/src/wizard/prompts.js +273 -0
  311. package/dist/src/wizard/types.js +4 -0
  312. package/dist/src/wizard/ui.js +453 -0
  313. package/dist/src/wizard/wizard.js +227 -0
  314. package/package.json +31 -23
  315. package/src/__tests__/alias.test.ts +133 -0
  316. package/src/__tests__/app.test.ts +1 -1
  317. package/src/__tests__/audit.test.ts +1 -1
  318. package/src/__tests__/circuit-breaker.test.ts +1 -1
  319. package/src/__tests__/cli-run.test.ts +237 -1
  320. package/src/__tests__/compat-sqlite.test.ts +68 -0
  321. package/src/__tests__/context-manager.test.ts +131 -1
  322. package/src/__tests__/context.test.ts +1 -1
  323. package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
  324. package/src/__tests__/doctor.test.ts +48 -0
  325. package/src/__tests__/enterprise.test.ts +1 -1
  326. package/src/__tests__/export.test.ts +236 -0
  327. package/src/__tests__/gap-11-18-20.test.ts +958 -0
  328. package/src/__tests__/generator.test.ts +1 -1
  329. package/src/__tests__/helm-streaming.test.ts +127 -0
  330. package/src/__tests__/hooks.test.ts +1 -1
  331. package/src/__tests__/incident.test.ts +179 -0
  332. package/src/__tests__/init.test.ts +55 -4
  333. package/src/__tests__/intent-parser.test.ts +1 -1
  334. package/src/__tests__/llm-router.test.ts +1 -1
  335. package/src/__tests__/logs.test.ts +107 -0
  336. package/src/__tests__/loop-errors.test.ts +244 -0
  337. package/src/__tests__/lsp.test.ts +1 -1
  338. package/src/__tests__/modes.test.ts +1 -1
  339. package/src/__tests__/perf-optimizations.test.ts +847 -0
  340. package/src/__tests__/permissions.test.ts +1 -1
  341. package/src/__tests__/pipeline.test.ts +50 -0
  342. package/src/__tests__/polish-phase3.test.ts +340 -0
  343. package/src/__tests__/profile.test.ts +237 -0
  344. package/src/__tests__/rollback.test.ts +83 -0
  345. package/src/__tests__/runbook.test.ts +219 -0
  346. package/src/__tests__/schedule.test.ts +206 -0
  347. package/src/__tests__/serve.test.ts +1 -1
  348. package/src/__tests__/sessions.test.ts +96 -1
  349. package/src/__tests__/sharing.test.ts +53 -1
  350. package/src/__tests__/snapshots.test.ts +1 -1
  351. package/src/__tests__/standalone-migration.test.ts +199 -0
  352. package/src/__tests__/state-db.test.ts +1 -1
  353. package/src/__tests__/status.test.ts +158 -0
  354. package/src/__tests__/stream-with-tools.test.ts +71 -25
  355. package/src/__tests__/subagents.test.ts +1 -1
  356. package/src/__tests__/system-prompt.test.ts +82 -3
  357. package/src/__tests__/terminal-gap-v2.test.ts +395 -0
  358. package/src/__tests__/terminal-parity.test.ts +393 -0
  359. package/src/__tests__/tf-apply.test.ts +187 -0
  360. package/src/__tests__/tool-converter.test.ts +1 -1
  361. package/src/__tests__/tool-schemas.test.ts +209 -4
  362. package/src/__tests__/tools.test.ts +4 -3
  363. package/src/__tests__/version-json.test.ts +184 -0
  364. package/src/__tests__/version.test.ts +1 -1
  365. package/src/__tests__/watch.test.ts +129 -0
  366. package/src/agent/compaction-agent.ts +40 -1
  367. package/src/agent/context-manager.ts +67 -3
  368. package/src/agent/deploy-preview.ts +62 -1
  369. package/src/agent/expand-files.ts +108 -0
  370. package/src/agent/loop.ts +1312 -31
  371. package/src/agent/permissions.ts +51 -4
  372. package/src/agent/system-prompt.ts +573 -19
  373. package/src/app.ts +58 -0
  374. package/src/audit/security-scanner.ts +45 -0
  375. package/src/auth/keychain.ts +82 -0
  376. package/src/auth/oauth.ts +15 -5
  377. package/src/cli/init.ts +378 -5
  378. package/src/cli/run.ts +407 -16
  379. package/src/cli/serve.ts +78 -1
  380. package/src/cli/web.ts +10 -6
  381. package/src/cli.ts +312 -1
  382. package/src/clients/service-discovery.ts +30 -25
  383. package/src/commands/alias.ts +100 -0
  384. package/src/commands/audit/index.ts +121 -2
  385. package/src/commands/auth-cloud.ts +113 -0
  386. package/src/commands/auth-refresh.ts +187 -0
  387. package/src/commands/aws-discover.ts +144 -251
  388. package/src/commands/aws-terraform.ts +68 -118
  389. package/src/commands/chat.ts +9 -3
  390. package/src/commands/completions.ts +268 -0
  391. package/src/commands/config.ts +26 -0
  392. package/src/commands/cost/index.ts +218 -2
  393. package/src/commands/deploy.ts +260 -0
  394. package/src/commands/doctor.ts +744 -152
  395. package/src/commands/drift/index.ts +371 -23
  396. package/src/commands/export.ts +146 -0
  397. package/src/commands/generate-k8s.ts +9 -61
  398. package/src/commands/generate-terraform.ts +191 -449
  399. package/src/commands/help.ts +212 -36
  400. package/src/commands/history.ts +8 -1
  401. package/src/commands/incident.ts +166 -0
  402. package/src/commands/init.ts +5 -0
  403. package/src/commands/login.ts +86 -1
  404. package/src/commands/logs.ts +167 -0
  405. package/src/commands/onboarding.ts +211 -34
  406. package/src/commands/pipeline.ts +186 -0
  407. package/src/commands/plugin.ts +398 -0
  408. package/src/commands/profile.ts +342 -0
  409. package/src/commands/questionnaire.ts +0 -98
  410. package/src/commands/resume.ts +26 -34
  411. package/src/commands/rollback.ts +315 -0
  412. package/src/commands/rollout.ts +88 -0
  413. package/src/commands/runbook.ts +346 -0
  414. package/src/commands/schedule.ts +236 -0
  415. package/src/commands/status.ts +252 -0
  416. package/src/commands/team-context.ts +220 -0
  417. package/src/commands/template.ts +58 -57
  418. package/src/commands/tf/index.ts +70 -11
  419. package/src/commands/upgrade.ts +57 -0
  420. package/src/commands/version.ts +54 -50
  421. package/src/commands/watch.ts +153 -0
  422. package/src/compat/runtime.ts +1 -1
  423. package/src/compat/sqlite.ts +75 -5
  424. package/src/config/mode-store.ts +62 -0
  425. package/src/config/profiles.ts +84 -0
  426. package/src/config/types.ts +83 -1
  427. package/src/config/workspace-state.ts +53 -0
  428. package/src/engine/cost-estimator.ts +52 -10
  429. package/src/engine/executor.ts +33 -2
  430. package/src/engine/planner.ts +68 -1
  431. package/src/generator/terraform.ts +8 -0
  432. package/src/history/manager.ts +2 -74
  433. package/src/hooks/engine.ts +5 -4
  434. package/src/llm/cost-calculator.ts +2 -2
  435. package/src/llm/providers/anthropic.ts +50 -21
  436. package/src/llm/router.ts +76 -7
  437. package/src/lsp/languages.ts +3 -0
  438. package/src/lsp/manager.ts +21 -5
  439. package/src/nimbus.ts +37 -18
  440. package/src/sessions/manager.ts +108 -1
  441. package/src/sharing/sync.ts +4 -0
  442. package/src/sharing/viewer.ts +66 -0
  443. package/src/tools/file-ops.ts +22 -0
  444. package/src/tools/schemas/devops.ts +3007 -117
  445. package/src/tools/schemas/standard.ts +5 -1
  446. package/src/tools/schemas/types.ts +31 -1
  447. package/src/tools/spawn-exec.ts +148 -0
  448. package/src/ui/App.tsx +1183 -66
  449. package/src/ui/DeployPreview.tsx +62 -57
  450. package/src/ui/FileDiffModal.tsx +162 -0
  451. package/src/ui/Header.tsx +87 -24
  452. package/src/ui/HelpModal.tsx +57 -0
  453. package/src/ui/InputBox.tsx +163 -10
  454. package/src/ui/MessageList.tsx +487 -40
  455. package/src/ui/PermissionPrompt.tsx +17 -5
  456. package/src/ui/StatusBar.tsx +122 -3
  457. package/src/ui/TerminalPane.tsx +84 -0
  458. package/src/ui/ToolCallDisplay.tsx +252 -18
  459. package/src/ui/TreePane.tsx +132 -0
  460. package/src/ui/chat-ui.ts +41 -44
  461. package/src/ui/ink/index.ts +771 -38
  462. package/src/ui/streaming.ts +1 -1
  463. package/src/ui/theme.ts +104 -0
  464. package/src/ui/types.ts +18 -0
  465. package/src/version.ts +1 -1
  466. package/src/watcher/index.ts +66 -15
  467. package/src/wizard/types.ts +1 -0
  468. package/src/wizard/ui.ts +1 -1
  469. package/tsconfig.json +2 -2
@@ -7,7 +7,6 @@
7
7
  */
8
8
 
9
9
  import { logger } from '../utils';
10
- import { RestClient } from '../clients';
11
10
  import {
12
11
  createWizard,
13
12
  ui,
@@ -18,14 +17,11 @@ import {
18
17
  type StepResult,
19
18
  } from '../wizard';
20
19
  import { awsDiscoverCommand, type AwsDiscoverOptions } from './aws-discover';
20
+ import { generateTerraformProject } from '../generator/terraform';
21
21
  import { readFile, writeFile } from 'node:fs/promises';
22
22
  import * as path from 'path';
23
23
  import * as fs from 'fs';
24
24
 
25
- // AWS Tools Service client
26
- const awsToolsUrl = process.env.AWS_TOOLS_SERVICE_URL || 'http://localhost:3009';
27
- const awsClient = new RestClient(awsToolsUrl);
28
-
29
25
  /**
30
26
  * Terraform generation context
31
27
  */
@@ -364,85 +360,54 @@ async function outputLocationStep(ctx: AwsTerraformContext): Promise<StepResult>
364
360
  }
365
361
 
366
362
  /**
367
- * Step 3: Generate Terraform
363
+ * Step 3: Generate Terraform using local generator
368
364
  */
369
365
  async function generateStep(ctx: AwsTerraformContext): Promise<StepResult> {
370
366
  ui.startSpinner({ message: 'Generating Terraform configurations...' });
371
367
 
372
368
  try {
373
- let response: any;
374
-
375
- // Generate from discovery session or direct resources
376
- if (ctx.discoverySessionId) {
377
- response = await awsClient.post<{
378
- terraformSessionId: string;
379
- files: Record<string, string>;
380
- summary: GenerationSummary;
381
- imports: any[];
382
- importScript: string;
383
- }>('/api/aws/terraform/generate', {
384
- sessionId: ctx.discoverySessionId,
385
- options: {
386
- organizeByService: ctx.organizeByService,
387
- generateImportBlocks: ctx.generateImportBlocks,
388
- terraformVersion: ctx.terraformVersion,
389
- awsProviderVersion: ctx.awsProviderVersion,
390
- },
391
- });
392
- } else if (ctx.resources && ctx.resources.length > 0) {
393
- response = await awsClient.post<{
394
- terraformSessionId: string;
395
- files: Record<string, string>;
396
- summary: GenerationSummary;
397
- imports: any[];
398
- importScript: string;
399
- }>('/api/aws/terraform/generate-direct', {
400
- resources: ctx.resources,
401
- options: {
402
- organizeByService: ctx.organizeByService,
403
- generateImportBlocks: ctx.generateImportBlocks,
404
- terraformVersion: ctx.terraformVersion,
405
- awsProviderVersion: ctx.awsProviderVersion,
406
- },
407
- });
408
- } else {
409
- ui.stopSpinnerFail('No resources to generate from');
410
- return { success: false, error: 'No resources available' };
369
+ // Derive components from discovered resources
370
+ const resourceTypes = (ctx.resources ?? []).map(r => r.type);
371
+ const components: string[] = [];
372
+ if (resourceTypes.some(t => t.includes('VPC') || t.includes('Subnet'))) components.push('vpc');
373
+ if (resourceTypes.some(t => t.includes('EC2') || t.includes('Instance'))) components.push('ec2');
374
+ if (resourceTypes.some(t => t.includes('S3'))) components.push('s3');
375
+ if (resourceTypes.some(t => t.includes('RDS'))) components.push('rds');
376
+ if (resourceTypes.some(t => t.includes('EKS'))) components.push('eks');
377
+ if (components.length === 0) components.push('vpc', 's3');
378
+
379
+ const generatedProject = await generateTerraformProject({
380
+ projectName: 'infrastructure',
381
+ provider: 'aws',
382
+ region: ctx.resources?.[0]?.region || 'us-east-1',
383
+ components,
384
+ });
385
+
386
+ const fileMap: Record<string, string> = {};
387
+ for (const file of generatedProject.files) {
388
+ fileMap[file.path] = file.content;
411
389
  }
412
390
 
413
- if (!response.success || !response.data) {
414
- ui.stopSpinnerFail(`Generation failed: ${response.error || 'Unknown error'}`);
415
- return { success: false, error: response.error || 'Generation failed' };
416
- }
417
-
418
- const { terraformSessionId, files, summary, importScript } = response.data;
391
+ const summary: GenerationSummary = {
392
+ totalResources: ctx.resources?.length ?? 0,
393
+ mappedResources: ctx.resources?.length ?? 0,
394
+ unmappedResources: 0,
395
+ filesGenerated: generatedProject.files.length,
396
+ servicesIncluded: components,
397
+ regionsIncluded: [...new Set((ctx.resources ?? []).map(r => r.region).filter(Boolean))],
398
+ };
419
399
 
420
- ui.stopSpinnerSuccess(`Generated ${Object.keys(files).length} files`);
400
+ ui.stopSpinnerSuccess(`Generated ${Object.keys(fileMap).length} file(s)`);
421
401
 
422
402
  // Add starter kit files if requested
423
- const allFiles = { ...files };
424
-
425
- if (ctx.includeReadme) {
426
- allFiles['README.md'] = generateReadme(summary);
427
- }
428
-
429
- if (ctx.includeGitignore) {
430
- allFiles['.gitignore'] = generateGitignore();
431
- }
432
-
433
- if (ctx.includeMakefile) {
434
- allFiles['Makefile'] = generateMakefile();
435
- }
436
-
437
- if (ctx.generateImportScript && importScript) {
438
- allFiles['import.sh'] = importScript;
439
- }
403
+ if (ctx.includeReadme) fileMap['README.md'] = generateReadme(summary);
404
+ if (ctx.includeGitignore) fileMap['.gitignore'] = generateGitignore();
405
+ if (ctx.includeMakefile) fileMap['Makefile'] = generateMakefile();
440
406
 
441
407
  return {
442
408
  success: true,
443
409
  data: {
444
- terraformSessionId,
445
- generatedFiles: allFiles,
410
+ generatedFiles: fileMap,
446
411
  summary,
447
412
  },
448
413
  };
@@ -722,39 +687,41 @@ async function runNonInteractive(options: AwsTerraformOptions): Promise<void> {
722
687
  resources = inventory.resources;
723
688
  }
724
689
 
725
- // Generate Terraform
690
+ // Generate Terraform using local generator
726
691
  ui.startSpinner({ message: 'Generating Terraform configurations...' });
727
692
 
728
693
  try {
729
- let response: any;
730
-
731
- if (options.sessionId) {
732
- response = await awsClient.post('/api/aws/terraform/generate', {
733
- sessionId: options.sessionId,
734
- options: {
735
- organizeByService: options.organizeByService ?? true,
736
- generateImportBlocks: options.importBlocks ?? true,
737
- terraformVersion: options.terraformVersion || '1.5.0',
738
- },
739
- });
740
- } else if (resources) {
741
- response = await awsClient.post('/api/aws/terraform/generate-direct', {
742
- resources,
743
- options: {
744
- organizeByService: options.organizeByService ?? true,
745
- generateImportBlocks: options.importBlocks ?? true,
746
- terraformVersion: options.terraformVersion || '1.5.0',
747
- },
748
- });
694
+ const resourceTypes = (resources ?? []).map(r => r.type);
695
+ const components: string[] = [];
696
+ if (resourceTypes.some(t => t.includes('VPC') || t.includes('Subnet'))) components.push('vpc');
697
+ if (resourceTypes.some(t => t.includes('EC2') || t.includes('Instance'))) components.push('ec2');
698
+ if (resourceTypes.some(t => t.includes('S3'))) components.push('s3');
699
+ if (resourceTypes.some(t => t.includes('RDS'))) components.push('rds');
700
+ if (resourceTypes.some(t => t.includes('EKS'))) components.push('eks');
701
+ if (components.length === 0) components.push('vpc', 's3');
702
+
703
+ const generatedProject = await generateTerraformProject({
704
+ projectName: 'infrastructure',
705
+ provider: 'aws',
706
+ region: resources?.[0]?.region || 'us-east-1',
707
+ components,
708
+ });
709
+
710
+ const files: Record<string, string> = {};
711
+ for (const file of generatedProject.files) {
712
+ files[file.path] = file.content;
749
713
  }
750
714
 
751
- if (!response.success || !response.data) {
752
- ui.stopSpinnerFail('Generation failed');
753
- process.exit(1);
754
- }
715
+ const summary: GenerationSummary = {
716
+ totalResources: resources?.length ?? 0,
717
+ mappedResources: resources?.length ?? 0,
718
+ unmappedResources: 0,
719
+ filesGenerated: generatedProject.files.length,
720
+ servicesIncluded: components,
721
+ regionsIncluded: [...new Set((resources ?? []).map(r => r.region).filter(Boolean))],
722
+ };
755
723
 
756
- const { files, summary, importScript } = response.data;
757
- ui.stopSpinnerSuccess(`Generated ${Object.keys(files).length} files`);
724
+ ui.stopSpinnerSuccess(`Generated ${Object.keys(files).length} file(s)`);
758
725
 
759
726
  // Write files
760
727
  const outputPath = options.output || './terraform-aws';
@@ -764,38 +731,21 @@ async function runNonInteractive(options: AwsTerraformOptions): Promise<void> {
764
731
  fs.mkdirSync(outputPath, { recursive: true });
765
732
  }
766
733
 
767
- // Add starter kit if requested
768
- if (options.includeStarterKit || options.includeReadme) {
769
- files['README.md'] = generateReadme(summary);
770
- }
771
- if (options.includeStarterKit || options.includeGitignore) {
772
- files['.gitignore'] = generateGitignore();
773
- }
774
- if (options.includeStarterKit || options.includeMakefile) {
775
- files['Makefile'] = generateMakefile();
776
- }
777
- if (importScript && (options.importScript ?? true)) {
778
- files['import.sh'] = importScript;
779
- }
734
+ if (options.includeStarterKit || options.includeReadme) files['README.md'] = generateReadme(summary);
735
+ if (options.includeStarterKit || options.includeGitignore) files['.gitignore'] = generateGitignore();
736
+ if (options.includeStarterKit || options.includeMakefile) files['Makefile'] = generateMakefile();
780
737
 
781
738
  for (const [fileName, content] of Object.entries(files)) {
782
739
  const filePath = path.join(outputPath, fileName);
783
740
  await writeFile(filePath, content as string, 'utf-8');
784
741
  }
785
742
 
786
- if (files['import.sh']) {
787
- fs.chmodSync(path.join(outputPath, 'import.sh'), '755');
788
- }
789
-
790
743
  ui.stopSpinnerSuccess(`Wrote ${Object.keys(files).length} files to ${outputPath}`);
791
744
 
792
- // Show summary
793
745
  ui.newLine();
794
746
  ui.success('Generation complete!');
795
747
  ui.print(` Output: ${outputPath}`);
796
- ui.print(
797
- ` Resources: ${summary.mappedResources} mapped, ${summary.unmappedResources} unmapped`
798
- );
748
+ ui.print(` Resources: ${summary.mappedResources} mapped`);
799
749
  } catch (error: any) {
800
750
  ui.stopSpinnerFail(`Generation failed: ${error.message}`);
801
751
  process.exit(1);
@@ -30,6 +30,10 @@ export interface ChatOptions {
30
30
  ui?: 'ink' | 'readline';
31
31
  /** Resume the most recent chat session */
32
32
  continue?: boolean;
33
+ /** Pre-loaded initial prompt sent as the first user message automatically */
34
+ initialPrompt?: string;
35
+ /** Initial agent mode */
36
+ mode?: 'plan' | 'build' | 'deploy';
33
37
  }
34
38
 
35
39
  /**
@@ -108,6 +112,8 @@ export async function chatCommand(options: ChatOptions = {}): Promise<void> {
108
112
  systemPrompt: options.systemPrompt,
109
113
  showTokenCount: options.showTokenCount,
110
114
  resumeSessionId,
115
+ initialPrompt: options.initialPrompt,
116
+ mode: options.mode as any,
111
117
  });
112
118
  return;
113
119
  } catch {
@@ -282,11 +288,11 @@ function getPersonaSystemPrompt(
282
288
  verbosity: 'minimal' | 'normal' | 'detailed'
283
289
  ): string {
284
290
  // Base identity shared across all personas
285
- const baseIdentity = `You are Nimbus, an AI-powered cloud engineering assistant. You help users with:
291
+ const baseIdentity = `You are Nimbus, an AI-powered DevOps engineering agent. You help users with:
286
292
 
287
- - Infrastructure as Code (Terraform, CloudFormation, Pulumi)
288
- - Kubernetes operations and configurations
293
+ - Infrastructure as Code (Terraform, Kubernetes, Helm)
289
294
  - Cloud provider operations (AWS, GCP, Azure)
295
+ - Kubernetes operations and configurations
290
296
  - DevOps best practices and CI/CD pipelines
291
297
  - Troubleshooting infrastructure issues`;
292
298
 
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Shell Completion Auto-Installer (L7)
3
+ *
4
+ * Installs shell completions for bash, zsh, and fish.
5
+ * Completion scripts are generated dynamically from the CLI command list.
6
+ */
7
+
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import * as os from 'node:os';
11
+ import { ui } from '../wizard/ui';
12
+
13
+ const COMMANDS = [
14
+ 'chat', 'run', 'init', 'login', 'logout', 'doctor', 'cost',
15
+ 'sessions', 'audit', 'share', 'upgrade', 'serve', 'web',
16
+ 'auth-refresh', 'plugin', 'completions', 'team-context',
17
+ 'logs', 'pipeline', 'alias',
18
+ ];
19
+
20
+ /**
21
+ * L4: Get available kubectl contexts for shell completion.
22
+ */
23
+ function getKubeContexts(): string[] {
24
+ try {
25
+ const { execFileSync } = require('node:child_process') as typeof import('node:child_process');
26
+ return execFileSync(
27
+ 'kubectl',
28
+ ['config', 'get-contexts', '--no-headers', '-o', 'name'],
29
+ { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] }
30
+ )
31
+ .trim()
32
+ .split('\n')
33
+ .filter(Boolean);
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+
39
+ /**
40
+ * L2: Get terraform workspaces for dynamic shell completion.
41
+ */
42
+ function getTerraformWorkspaces(): string[] {
43
+ try {
44
+ const { execFileSync } = require('node:child_process') as typeof import('node:child_process');
45
+ return execFileSync('terraform', ['workspace', 'list'], {
46
+ encoding: 'utf-8',
47
+ timeout: 3000,
48
+ stdio: ['pipe', 'pipe', 'pipe'],
49
+ })
50
+ .split('\n')
51
+ .map((l: string) => l.replace(/^\*?\s*/, '').trim())
52
+ .filter(Boolean);
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+
58
+ /**
59
+ * L2: Get helm releases for dynamic shell completion.
60
+ */
61
+ function getHelmReleases(): string[] {
62
+ try {
63
+ const { execFileSync } = require('node:child_process') as typeof import('node:child_process');
64
+ return execFileSync('helm', ['list', '--short'], {
65
+ encoding: 'utf-8',
66
+ timeout: 3000,
67
+ stdio: ['pipe', 'pipe', 'pipe'],
68
+ })
69
+ .trim()
70
+ .split('\n')
71
+ .filter(Boolean);
72
+ } catch {
73
+ return [];
74
+ }
75
+ }
76
+
77
+ /**
78
+ * L2: Dynamic completion dispatcher.
79
+ * Called from shell with: nimbus __complete <prev_word> <curr_word>
80
+ */
81
+ export function dynamicComplete(prevWord: string, _currWord: string): void {
82
+ let suggestions: string[] = [];
83
+
84
+ if (prevWord === 'workspace' || prevWord === 'ws') {
85
+ suggestions = getTerraformWorkspaces();
86
+ } else if (prevWord === 'switch' || prevWord === 'context') {
87
+ suggestions = getKubeContexts();
88
+ } else if (prevWord === 'upgrade' || prevWord === 'rollback') {
89
+ suggestions = getHelmReleases();
90
+ }
91
+
92
+ if (suggestions.length > 0) {
93
+ process.stdout.write(suggestions.join('\n') + '\n');
94
+ }
95
+ }
96
+
97
+ const FLAGS = [
98
+ '--help', '--version', '--model', '--mode', '--auto-approve',
99
+ '--format', '--verbose', '--json', '--fix',
100
+ ];
101
+
102
+ function generateBashCompletion(): string {
103
+ return `# Nimbus bash completion
104
+ _nimbus_completions() {
105
+ local cur prev
106
+ COMPREPLY=()
107
+ cur="\${COMP_WORDS[COMP_CWORD]}"
108
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
109
+
110
+ local commands="${COMMANDS.join(' ')}"
111
+ local flags="${FLAGS.join(' ')}"
112
+
113
+ if [ \$COMP_CWORD -eq 1 ]; then
114
+ COMPREPLY=( \$(compgen -W "\$commands" -- "\$cur") )
115
+ else
116
+ COMPREPLY=( \$(compgen -W "\$flags" -- "\$cur") )
117
+ fi
118
+ }
119
+
120
+ complete -F _nimbus_completions nimbus
121
+ `;
122
+ }
123
+
124
+ function generateZshCompletion(): string {
125
+ const commandDefs = COMMANDS.map(c => ` '${c}:${c} command'`).join('\n');
126
+ const kubeContexts = getKubeContexts();
127
+ const contextCompletion = kubeContexts.length > 0
128
+ ? `\n '--context[kubectl context]:context:(${kubeContexts.join(' ')})' \\`
129
+ : '';
130
+ return `#compdef nimbus
131
+ # Nimbus zsh completion
132
+
133
+ _nimbus() {
134
+ local state
135
+
136
+ _arguments \\
137
+ '1: :->command' \\${contextCompletion}
138
+ '*: :->args'
139
+
140
+ case \$state in
141
+ command)
142
+ _describe 'nimbus commands' \\
143
+ (
144
+ ${commandDefs}
145
+ )
146
+ ;;
147
+ args)
148
+ _arguments \\
149
+ '--help[Show help]' \\
150
+ '--version[Show version]' \\
151
+ '--model[Specify LLM model]:model:' \\
152
+ '--mode[Agent mode (plan/build/deploy)]:mode:(plan build deploy)' \\
153
+ '--auto-approve[Auto-approve all actions]' \\
154
+ '--format[Output format]:format:(json text)'
155
+ ;;
156
+ esac
157
+ }
158
+
159
+ _nimbus
160
+ `;
161
+ }
162
+
163
+ function generateFishCompletion(): string {
164
+ const cmdCompletions = COMMANDS.map(
165
+ c => `complete -c nimbus -f -n '__fish_use_subcommand' -a '${c}'`
166
+ ).join('\n');
167
+
168
+ return `# Nimbus fish completion
169
+
170
+ ${cmdCompletions}
171
+
172
+ complete -c nimbus -l help -d 'Show help'
173
+ complete -c nimbus -l version -d 'Show version'
174
+ complete -c nimbus -l model -d 'Specify LLM model' -r
175
+ complete -c nimbus -l mode -d 'Agent mode' -r -a 'plan build deploy'
176
+ complete -c nimbus -l auto-approve -d 'Auto-approve all actions'
177
+ complete -c nimbus -l format -d 'Output format' -r -a 'json text'
178
+ `;
179
+ }
180
+
181
+ export async function completionsCommand(subcommand: string): Promise<void> {
182
+ switch (subcommand) {
183
+ case 'install': {
184
+ const shell = path.basename(process.env.SHELL ?? '/bin/bash');
185
+ ui.info(`Detected shell: ${shell}`);
186
+
187
+ if (shell === 'bash') {
188
+ const dir = path.join(os.homedir(), '.bash_completion.d');
189
+ const file = path.join(dir, 'nimbus.bash');
190
+ fs.mkdirSync(dir, { recursive: true });
191
+ fs.writeFileSync(file, generateBashCompletion(), 'utf-8');
192
+
193
+ // Add source to .bashrc if not already there
194
+ const bashrc = path.join(os.homedir(), '.bashrc');
195
+ const sourceLine = `\n# Nimbus completions\n[ -f "${file}" ] && source "${file}"\n`;
196
+ let alreadyConfigured = false;
197
+ try {
198
+ const existing = fs.readFileSync(bashrc, 'utf-8');
199
+ if (!existing.includes('nimbus.bash')) {
200
+ fs.appendFileSync(bashrc, sourceLine);
201
+ } else {
202
+ alreadyConfigured = true;
203
+ }
204
+ } catch {
205
+ fs.appendFileSync(bashrc, sourceLine);
206
+ }
207
+ ui.print(`${ui.color('✓', 'green')} Installed bash completions to ${file}`);
208
+ if (alreadyConfigured) {
209
+ ui.print(' Already configured, skipping.');
210
+ } else {
211
+ ui.print(' Restart your shell or run: source ~/.bashrc');
212
+ }
213
+ } else if (shell === 'zsh') {
214
+ const dir = path.join(os.homedir(), '.zsh', 'completions');
215
+ const file = path.join(dir, '_nimbus');
216
+ fs.mkdirSync(dir, { recursive: true });
217
+ fs.writeFileSync(file, generateZshCompletion(), 'utf-8');
218
+ ui.print(`${ui.color('✓', 'green')} Installed zsh completions to ${file}`);
219
+ ui.print(' Add to .zshrc: fpath=(~/.zsh/completions $fpath) && autoload -U compinit && compinit');
220
+ ui.print(' Restart your shell or run: source ~/.zshrc');
221
+ } else if (shell === 'fish') {
222
+ const dir = path.join(os.homedir(), '.config', 'fish', 'completions');
223
+ const file = path.join(dir, 'nimbus.fish');
224
+ fs.mkdirSync(dir, { recursive: true });
225
+ fs.writeFileSync(file, generateFishCompletion(), 'utf-8');
226
+ ui.print(`${ui.color('✓', 'green')} Installed fish completions to ${file}`);
227
+ ui.print(' Fish completions are active immediately. Restart your shell to confirm.');
228
+ } else {
229
+ ui.warning(`Shell "${shell}" not supported. Supported: bash, zsh, fish`);
230
+ }
231
+ break;
232
+ }
233
+
234
+ case 'uninstall': {
235
+ const shell = path.basename(process.env.SHELL ?? '/bin/bash');
236
+ const files: string[] = [
237
+ path.join(os.homedir(), '.bash_completion.d', 'nimbus.bash'),
238
+ path.join(os.homedir(), '.zsh', 'completions', '_nimbus'),
239
+ path.join(os.homedir(), '.config', 'fish', 'completions', 'nimbus.fish'),
240
+ ];
241
+ let removed = 0;
242
+ for (const f of files) {
243
+ if (fs.existsSync(f)) {
244
+ fs.unlinkSync(f);
245
+ ui.print(`${ui.color('✓', 'green')} Removed ${f}`);
246
+ removed++;
247
+ }
248
+ }
249
+ if (removed === 0) {
250
+ ui.info('No completion files found to remove.');
251
+ } else {
252
+ ui.info(`Removed ${removed} completion file(s). Reload your shell.`);
253
+ }
254
+ void shell; // suppress unused warning
255
+ break;
256
+ }
257
+
258
+ case '__complete': {
259
+ // L2: Dynamic completion dispatcher — called from shell completion scripts
260
+ // Usage: nimbus completions __complete <prev_word> <curr_word>
261
+ // (args passed as positional args by the shell)
262
+ break;
263
+ }
264
+
265
+ default:
266
+ ui.print('Usage: nimbus completions <install|uninstall>');
267
+ }
268
+ }
@@ -115,6 +115,32 @@ export async function configSetCommand(options: ConfigSetOptions): Promise<void>
115
115
  process.exit(1);
116
116
  }
117
117
 
118
+ // M4: Validate model key against known model IDs
119
+ if (key === 'model' || key === 'llm.defaultModel') {
120
+ const knownModels = [
121
+ 'claude-opus-4-6',
122
+ 'claude-sonnet-4-6',
123
+ 'claude-haiku-4-5-20251001',
124
+ 'claude-opus-4-20250514',
125
+ 'claude-sonnet-4-20250514',
126
+ 'claude-haiku-4-5',
127
+ 'anthropic/claude-opus-4-6',
128
+ 'anthropic/claude-sonnet-4-6',
129
+ 'anthropic/claude-haiku-4-5-20251001',
130
+ 'anthropic/claude-opus-4-20250514',
131
+ 'anthropic/claude-sonnet-4-20250514',
132
+ ];
133
+ if (!knownModels.includes(value)) {
134
+ ui.warning(`"${value}" is not a recognized Nimbus model ID.`);
135
+ ui.info('Known models: ' + knownModels.slice(0, 6).join(', '));
136
+ if (!options.nonInteractive) {
137
+ const { confirm: confirmPrompt } = await import('../wizard/prompts');
138
+ const proceed = await confirmPrompt({ message: 'Set this model anyway?', defaultValue: false });
139
+ if (!proceed) return;
140
+ }
141
+ }
142
+ }
143
+
118
144
  // Parse and set value
119
145
  const parsedValue = configManager.parseValue(key, value);
120
146
  configManager.set(key, parsedValue);