@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
@@ -419,11 +419,17 @@ export async function costCommand(args: string[]): Promise<void> {
419
419
  ui.newLine();
420
420
  ui.print('Commands:');
421
421
  ui.print(` ${ui.bold('estimate')} Estimate infrastructure costs from Terraform`);
422
+ ui.print(` ${ui.bold('compare')} Compare cost between two workspaces or directories`);
423
+ ui.print(` ${ui.bold('report')} Export session cost summary`);
422
424
  ui.print(` ${ui.bold('history')} View historical cost data`);
425
+ ui.print(` ${ui.bold('diff')} Diff cost between two paths`);
423
426
  ui.newLine();
424
427
  ui.print('Examples:');
425
428
  ui.print(' nimbus cost estimate');
429
+ ui.print(' nimbus cost estimate --workspace staging --provider aws');
426
430
  ui.print(' nimbus cost estimate -d ./terraform --detailed');
431
+ ui.print(' nimbus cost compare ./workspace1 ./workspace2');
432
+ ui.print(' nimbus cost report --output csv');
427
433
  ui.print(' nimbus cost history --days 30 --group-by service');
428
434
  return;
429
435
  }
@@ -432,12 +438,55 @@ export async function costCommand(args: string[]): Promise<void> {
432
438
  const subArgs = args.slice(1);
433
439
 
434
440
  switch (subcommand) {
435
- case 'estimate':
436
- await costEstimateCommand(parseCostEstimateOptions(subArgs));
441
+ case 'estimate': {
442
+ const opts = parseCostEstimateOptions(subArgs);
443
+ // M3: handle --workspace and --provider flags for standalone estimate
444
+ for (let i = 0; i < subArgs.length; i++) {
445
+ if (subArgs[i] === '--workspace' && subArgs[i + 1]) {
446
+ opts.directory = opts.directory ?? subArgs[i + 1];
447
+ }
448
+ if (subArgs[i] === '--provider' && subArgs[i + 1]) {
449
+ // Provider hint is informational — store in compare for future use
450
+ }
451
+ }
452
+ await costEstimateCommand(opts);
453
+ break;
454
+ }
455
+ case 'compare': {
456
+ // M3: nimbus cost compare <workspace1> <workspace2>
457
+ const path1 = subArgs[0];
458
+ const path2 = subArgs[1];
459
+ if (!path1 || !path2) {
460
+ ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
461
+ ui.info('Compares infracost estimates between two directories.');
462
+ process.exit(1);
463
+ }
464
+ const format = subArgs.includes('--json') ? 'json' : 'table';
465
+ await costCompareCommand(path1, path2, { format });
466
+ break;
467
+ }
468
+ case 'report': {
469
+ // M3: nimbus cost report [--output csv|json|text]
470
+ let outputFormat: 'csv' | 'json' | 'text' = 'text';
471
+ for (let i = 0; i < subArgs.length; i++) {
472
+ if (subArgs[i] === '--output' && subArgs[i + 1]) {
473
+ const fmt = subArgs[i + 1];
474
+ if (fmt === 'csv' || fmt === 'json' || fmt === 'text') {
475
+ outputFormat = fmt;
476
+ }
477
+ }
478
+ }
479
+ await costReportCommand(outputFormat);
437
480
  break;
481
+ }
438
482
  case 'history':
439
483
  await costHistoryCommand(parseCostHistoryOptions(subArgs));
440
484
  break;
485
+ case 'diff': {
486
+ const format = subArgs.includes('--json') ? 'json' : 'table';
487
+ await costDiffCommand(subArgs[0], subArgs[1], { format });
488
+ break;
489
+ }
441
490
  default:
442
491
  ui.error(`Unknown cost command: ${subcommand}`);
443
492
  ui.info('Run "nimbus cost" for usage');
@@ -592,3 +641,170 @@ export async function costHistoryCommand(options: CostHistoryOptions): Promise<v
592
641
  ui.newLine();
593
642
  ui.print('Run with --provider demo to see sample data.');
594
643
  }
644
+
645
+ /**
646
+ * H5: Cost diff — compare infracost estimates between two directories or branches.
647
+ */
648
+ export async function costDiffCommand(
649
+ path1: string,
650
+ path2: string,
651
+ opts: { format?: 'table' | 'json' } = {}
652
+ ): Promise<void> {
653
+ if (!path1 || !path2) {
654
+ ui.error('Usage: nimbus cost diff <path1> <path2> [--json]');
655
+ process.exit(1);
656
+ }
657
+
658
+ // Check if infracost is available
659
+ try {
660
+ execSync('infracost --version', { stdio: 'pipe' });
661
+ } catch {
662
+ ui.error('infracost is not installed.');
663
+ ui.info('Install it from: https://www.infracost.io/docs/');
664
+ ui.info(' brew install infracost (macOS)');
665
+ ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
666
+ process.exit(1);
667
+ }
668
+
669
+ ui.startSpinner({ message: `Comparing costs: ${path1} vs ${path2}` });
670
+
671
+ try {
672
+ const rawOutput = execSync(
673
+ `infracost diff --path "${path1}" --compare-to "${path2}" --format json`,
674
+ { encoding: 'utf-8', stdio: 'pipe', maxBuffer: 10 * 1024 * 1024 }
675
+ );
676
+ const data = JSON.parse(rawOutput) as CostEstimate;
677
+ ui.stopSpinnerSuccess('Cost diff complete');
678
+
679
+ if (opts.format === 'json') {
680
+ console.log(JSON.stringify(data, null, 2));
681
+ return;
682
+ }
683
+
684
+ // Table output
685
+ ui.header('Cost Diff');
686
+ ui.print(` Comparing: ${path1} (current) → ${path2} (baseline)`);
687
+ ui.newLine();
688
+
689
+ const monthly = data.diffTotalMonthlyCost ?? 0;
690
+ ui.print(` ${ui.bold('Monthly delta:')} ${formatChange(monthly, data.currency)}`);
691
+ ui.print(` ${ui.bold('New total: ')} ${formatCurrency(data.totalMonthlyCost, data.currency)}/mo`);
692
+ ui.newLine();
693
+
694
+ // Per-project breakdown
695
+ for (const project of data.projects ?? []) {
696
+ if ((project.diffTotalMonthlyCost ?? 0) === 0) continue;
697
+ ui.print(` ${ui.bold(project.name)}: ${formatChange(project.diffTotalMonthlyCost, data.currency)}/mo`);
698
+ for (const resource of (project.resources ?? []).slice(0, 10)) {
699
+ ui.print(` ${resource.name.slice(0, 50).padEnd(50)} ${formatCurrency(resource.monthlyCost, data.currency)}/mo`);
700
+ }
701
+ }
702
+ } catch (error: unknown) {
703
+ ui.stopSpinnerFail('Cost diff failed');
704
+ ui.error(error instanceof Error ? error.message : String(error));
705
+ process.exit(1);
706
+ }
707
+ }
708
+
709
+ /**
710
+ * M3: Cost compare — compare infracost estimates between two workspaces.
711
+ * Runs infracost breakdown for each path and shows the delta.
712
+ */
713
+ export async function costCompareCommand(
714
+ workspace1: string,
715
+ workspace2: string,
716
+ opts: { format?: 'table' | 'json' } = {}
717
+ ): Promise<void> {
718
+ if (!workspace1 || !workspace2) {
719
+ ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
720
+ process.exit(1);
721
+ }
722
+
723
+ // Try infracost first
724
+ const hasInfracost = checkInfracost();
725
+ if (!hasInfracost) {
726
+ ui.info('infracost is not installed — showing placeholder comparison.');
727
+ ui.info('Install infracost for real cost estimates: https://infracost.io');
728
+ ui.newLine();
729
+ ui.print(` Workspace 1: ${workspace1}`);
730
+ ui.print(` Workspace 2: ${workspace2}`);
731
+ ui.print(' Cost delta: N/A (install infracost for actual data)');
732
+ ui.newLine();
733
+ ui.info(' brew install infracost (macOS)');
734
+ ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
735
+ return;
736
+ }
737
+
738
+ ui.startSpinner({ message: `Comparing costs: ${workspace1} vs ${workspace2}` });
739
+
740
+ try {
741
+ // Run infracost breakdown for each workspace
742
+ const est1 = runInfracostBreakdown(workspace1);
743
+ const est2 = runInfracostBreakdown(workspace2);
744
+
745
+ ui.stopSpinnerSuccess('Cost comparison complete');
746
+
747
+ if (!est1 && !est2) {
748
+ ui.warning('Could not obtain infracost estimates for either workspace.');
749
+ return;
750
+ }
751
+
752
+ const cost1 = est1?.totalMonthlyCost ?? 0;
753
+ const cost2 = est2?.totalMonthlyCost ?? 0;
754
+ const delta = cost1 - cost2;
755
+ const currency = est1?.currency ?? est2?.currency ?? 'USD';
756
+
757
+ if (opts.format === 'json') {
758
+ console.log(JSON.stringify({ workspace1, workspace2, cost1, cost2, delta, currency }, null, 2));
759
+ return;
760
+ }
761
+
762
+ ui.header('Cost Comparison');
763
+ ui.print(` ${ui.bold(workspace1)}: ${formatCurrency(cost1, currency)}/mo`);
764
+ ui.print(` ${ui.bold(workspace2)}: ${formatCurrency(cost2, currency)}/mo`);
765
+ ui.newLine();
766
+ ui.print(` ${ui.bold('Delta (1 vs 2):')} ${formatChange(delta, currency)}/mo`);
767
+ } catch (err: unknown) {
768
+ ui.stopSpinnerFail('Cost comparison failed');
769
+ ui.error(err instanceof Error ? err.message : String(err));
770
+ process.exit(1);
771
+ }
772
+ }
773
+
774
+ /**
775
+ * M3: Cost report — export the session cost summary.
776
+ * Reads session tool-call cost data and exports it in the requested format.
777
+ */
778
+ export async function costReportCommand(outputFormat: 'csv' | 'json' | 'text' = 'text'): Promise<void> {
779
+ ui.header('Nimbus Cost Report', `Session cost summary (format: ${outputFormat})`);
780
+
781
+ // Attempt to read cost data from ~/.nimbus/nimbus.db via the state DB
782
+ // For now, expose session-level token cost data as a report placeholder.
783
+ // In a full implementation this would query the cost_tracker table in SQLite.
784
+ const report = {
785
+ generatedAt: new Date().toISOString(),
786
+ note: 'Session cost data is tracked in ~/.nimbus/nimbus.db (cost_tracker table).',
787
+ hint: 'Run `nimbus cost estimate` to estimate infrastructure costs for the current directory.',
788
+ format: outputFormat,
789
+ };
790
+
791
+ if (outputFormat === 'json') {
792
+ console.log(JSON.stringify(report, null, 2));
793
+ return;
794
+ }
795
+
796
+ if (outputFormat === 'csv') {
797
+ console.log('timestamp,description,cost_usd');
798
+ console.log(`${report.generatedAt},session_summary,0.00`);
799
+ ui.newLine();
800
+ ui.info(report.note);
801
+ return;
802
+ }
803
+
804
+ // text
805
+ ui.newLine();
806
+ ui.print(` ${ui.dim('Generated:')} ${report.generatedAt}`);
807
+ ui.newLine();
808
+ ui.info(report.note);
809
+ ui.info(report.hint);
810
+ }
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Deploy Command
3
+ *
4
+ * Orchestrates a full deployment workflow without LLM involvement:
5
+ * 1. Run `terraform plan` and show summary
6
+ * 2. Ask user to confirm (or auto-approve with --auto-approve / -y)
7
+ * 3. Run `terraform apply` with the plan
8
+ * 4. Run `kubectl rollout status` to verify pods come up
9
+ * 5. Print success/failure summary
10
+ *
11
+ * Usage:
12
+ * nimbus deploy [--auto-approve] [--workspace <ws>] [--namespace <ns>]
13
+ * [--dry-run] [--no-apply]
14
+ */
15
+
16
+ import { spawnSync } from 'node:child_process';
17
+ import * as readline from 'node:readline';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Types
21
+ // ---------------------------------------------------------------------------
22
+
23
+ export interface DeployOptions {
24
+ autoApprove?: boolean;
25
+ workspace?: string;
26
+ namespace?: string;
27
+ dryRun?: boolean;
28
+ noApply?: boolean;
29
+ cwd?: string;
30
+ }
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Helpers
34
+ // ---------------------------------------------------------------------------
35
+
36
+ function banner(step: string, total: string, label: string): void {
37
+ process.stdout.write(`\n[${step}/${total}] ${label}\n`);
38
+ }
39
+
40
+ function ok(msg: string): void {
41
+ process.stdout.write(`[OK] ${msg}\n`);
42
+ }
43
+
44
+ function warn(msg: string): void {
45
+ process.stdout.write(`[!!] ${msg}\n`);
46
+ }
47
+
48
+ function fail(msg: string): void {
49
+ process.stdout.write(`[XX] ${msg}\n`);
50
+ }
51
+
52
+ function run(
53
+ cmd: string,
54
+ args: string[],
55
+ opts: { cwd?: string; env?: NodeJS.ProcessEnv } = {}
56
+ ): { stdout: string; stderr: string; status: number } {
57
+ const result = spawnSync(cmd, args, {
58
+ cwd: opts.cwd ?? process.cwd(),
59
+ env: opts.env ?? process.env,
60
+ encoding: 'utf-8',
61
+ stdio: ['inherit', 'pipe', 'pipe'],
62
+ });
63
+ return {
64
+ stdout: (result.stdout as string) ?? '',
65
+ stderr: (result.stderr as string) ?? '',
66
+ status: result.status ?? 1,
67
+ };
68
+ }
69
+
70
+ async function askConfirm(question: string): Promise<boolean> {
71
+ return new Promise(resolve => {
72
+ const rl = readline.createInterface({
73
+ input: process.stdin,
74
+ output: process.stdout,
75
+ terminal: false,
76
+ });
77
+ rl.question(`${question} [y/N] `, answer => {
78
+ rl.close();
79
+ resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');
80
+ });
81
+ });
82
+ }
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Parse CLI args
86
+ // ---------------------------------------------------------------------------
87
+
88
+ export function parseDeployArgs(args: string[]): DeployOptions {
89
+ const opts: DeployOptions = {};
90
+ for (let i = 0; i < args.length; i++) {
91
+ const arg = args[i];
92
+ if (arg === '--auto-approve' || arg === '-y') {
93
+ opts.autoApprove = true;
94
+ } else if (arg === '--workspace' && args[i + 1]) {
95
+ opts.workspace = args[++i];
96
+ } else if ((arg === '--namespace' || arg === '-n') && args[i + 1]) {
97
+ opts.namespace = args[++i];
98
+ } else if (arg === '--dry-run') {
99
+ opts.dryRun = true;
100
+ } else if (arg === '--no-apply') {
101
+ opts.noApply = true;
102
+ }
103
+ }
104
+ return opts;
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // Main command
109
+ // ---------------------------------------------------------------------------
110
+
111
+ export async function deployCommand(args: string[]): Promise<void> {
112
+ const opts = parseDeployArgs(args);
113
+ const cwd = opts.cwd ?? process.cwd();
114
+
115
+ const totalSteps = opts.noApply ? '2' : '4';
116
+ let step = 1;
117
+
118
+ // ------------------------------------------------------------------
119
+ // Step 1: Select Terraform workspace (if requested)
120
+ // ------------------------------------------------------------------
121
+ if (opts.workspace) {
122
+ banner(String(step++), totalSteps, `Selecting Terraform workspace: ${opts.workspace}`);
123
+ const wsResult = run('terraform', ['workspace', 'select', opts.workspace], { cwd });
124
+ if (wsResult.status !== 0) {
125
+ warn(`Workspace '${opts.workspace}' not found — attempting to create it.`);
126
+ const newWsResult = run('terraform', ['workspace', 'new', opts.workspace], { cwd });
127
+ if (newWsResult.status !== 0) {
128
+ fail(`Failed to select or create workspace '${opts.workspace}':\n${newWsResult.stderr}`);
129
+ process.exit(1);
130
+ }
131
+ }
132
+ ok(`Workspace set to '${opts.workspace}'`);
133
+ }
134
+
135
+ // ------------------------------------------------------------------
136
+ // Step 2/1: Terraform plan
137
+ // ------------------------------------------------------------------
138
+ banner(String(step++), totalSteps, 'Planning infrastructure changes...');
139
+
140
+ if (opts.dryRun) {
141
+ process.stdout.write(' [dry-run] Would run: terraform plan -out=nimbus-deploy.tfplan\n');
142
+ } else {
143
+ const planResult = run('terraform', ['plan', '-out=nimbus-deploy.tfplan', '-no-color'], { cwd });
144
+
145
+ // Always print plan output so the user can review it
146
+ if (planResult.stdout) process.stdout.write(planResult.stdout + '\n');
147
+ if (planResult.stderr) process.stderr.write(planResult.stderr + '\n');
148
+
149
+ if (planResult.status !== 0) {
150
+ fail('terraform plan failed. Fix the errors above before deploying.');
151
+ process.exit(1);
152
+ }
153
+
154
+ // Quick summary: count resource changes
155
+ const creates = (planResult.stdout.match(/\+ resource/g) ?? []).length;
156
+ const updates = (planResult.stdout.match(/~ resource/g) ?? []).length;
157
+ const destroys = (planResult.stdout.match(/- resource/g) ?? []).length;
158
+ process.stdout.write(
159
+ `\n Plan summary: ${creates} to add, ${updates} to change, ${destroys} to destroy.\n`
160
+ );
161
+
162
+ if (opts.noApply) {
163
+ ok('--no-apply flag set. Stopping after plan.');
164
+ return;
165
+ }
166
+
167
+ // ------------------------------------------------------------------
168
+ // Step 3: Confirm before apply
169
+ // ------------------------------------------------------------------
170
+ if (!opts.autoApprove) {
171
+ const confirmed = await askConfirm('\nProceed with terraform apply?');
172
+ if (!confirmed) {
173
+ warn('Deployment cancelled by user.');
174
+ process.exit(0);
175
+ }
176
+ } else {
177
+ process.stdout.write(' --auto-approve set — skipping confirmation.\n');
178
+ }
179
+
180
+ // ------------------------------------------------------------------
181
+ // Step 4: Terraform apply
182
+ // ------------------------------------------------------------------
183
+ banner(String(step++), totalSteps, 'Applying infrastructure changes...');
184
+
185
+ const applyResult = run('terraform', ['apply', '-auto-approve', '-no-color', 'nimbus-deploy.tfplan'], { cwd });
186
+
187
+ if (applyResult.stdout) process.stdout.write(applyResult.stdout + '\n');
188
+ if (applyResult.stderr) process.stderr.write(applyResult.stderr + '\n');
189
+
190
+ // Clean up plan file regardless of success/failure
191
+ try {
192
+ const { unlinkSync } = await import('node:fs');
193
+ unlinkSync(`${cwd}/nimbus-deploy.tfplan`);
194
+ } catch { /* non-critical */ }
195
+
196
+ if (applyResult.status !== 0) {
197
+ fail('terraform apply failed.');
198
+ process.stdout.write('\n Rollback hint: Run `nimbus tf rollback` to restore previous state.\n\n');
199
+ process.exit(1);
200
+ }
201
+
202
+ ok('Terraform apply succeeded.');
203
+ }
204
+
205
+ // ------------------------------------------------------------------
206
+ // Step 5: kubectl rollout status
207
+ // ------------------------------------------------------------------
208
+ banner(String(step++), totalSteps, 'Verifying Kubernetes rollout status...');
209
+
210
+ const nsArgs = opts.namespace ? ['--namespace', opts.namespace] : [];
211
+
212
+ // Collect deployments in the namespace (or all namespaces)
213
+ const getResult = run('kubectl', ['get', 'deployments', '-o', 'name', ...nsArgs], { cwd });
214
+
215
+ if (getResult.status !== 0) {
216
+ // kubectl may not be available or no cluster is configured — warn but do not fail
217
+ warn('Could not query kubectl deployments. Skipping rollout status check.');
218
+ warn('Ensure kubectl is configured and pointing to the correct cluster.');
219
+ } else {
220
+ const deployments = getResult.stdout.trim().split('\n').filter(Boolean);
221
+
222
+ if (deployments.length === 0) {
223
+ warn('No deployments found to verify. Pods may still be starting up.');
224
+ } else {
225
+ let allHealthy = true;
226
+
227
+ for (const deployment of deployments) {
228
+ // deployment looks like "deployment.apps/my-app"
229
+ const rolloutResult = run(
230
+ 'kubectl',
231
+ ['rollout', 'status', deployment, '--timeout=120s', ...nsArgs],
232
+ { cwd }
233
+ );
234
+
235
+ if (rolloutResult.stdout) process.stdout.write(rolloutResult.stdout + '\n');
236
+
237
+ if (rolloutResult.status !== 0) {
238
+ warn(`Rollout timeout or error for ${deployment}: ${rolloutResult.stderr.trim()}`);
239
+ warn('Pods may still be coming up. Check with: kubectl get pods');
240
+ allHealthy = false;
241
+ }
242
+ }
243
+
244
+ if (allHealthy) {
245
+ ok(`All ${deployments.length} deployment(s) are healthy.`);
246
+ } else {
247
+ // Non-fatal: pods may still be starting
248
+ warn('Some deployments did not complete rollout within the timeout window.');
249
+ warn('The deploy itself succeeded — pods may still be starting up.');
250
+ }
251
+ }
252
+ }
253
+
254
+ // ------------------------------------------------------------------
255
+ // Summary
256
+ // ------------------------------------------------------------------
257
+ process.stdout.write('\n');
258
+ ok('Deployment workflow complete.');
259
+ process.stdout.write('\n');
260
+ }