@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
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Multi-account profile management.
3
+ *
4
+ * Profiles are stored at ~/.nimbus/profiles/<name>.json and merged into
5
+ * the app config at startup when --profile <name> is passed.
6
+ */
7
+
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+
11
+ export interface ConfigProfile {
12
+ name: string;
13
+ /** Anthropic API key for this profile */
14
+ anthropicApiKey?: string;
15
+ /** AWS profile name (maps to AWS_PROFILE env var) */
16
+ awsProfile?: string;
17
+ /** AWS region override */
18
+ awsRegion?: string;
19
+ /** GCP project ID */
20
+ gcpProject?: string;
21
+ /** Azure subscription ID */
22
+ azureSubscription?: string;
23
+ /** Default agent mode for this profile */
24
+ defaultMode?: string;
25
+ }
26
+
27
+ function getProfilesDir(): string {
28
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '/tmp';
29
+ return join(home, '.nimbus', 'profiles');
30
+ }
31
+
32
+ export function loadProfile(name: string): ConfigProfile | null {
33
+ const profilePath = join(getProfilesDir(), `${name}.json`);
34
+ if (!existsSync(profilePath)) return null;
35
+ try {
36
+ const raw = readFileSync(profilePath, 'utf-8');
37
+ return { name, ...JSON.parse(raw) } as ConfigProfile;
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ export function saveProfile(profile: ConfigProfile): void {
44
+ const profilesDir = getProfilesDir();
45
+ mkdirSync(profilesDir, { recursive: true });
46
+ const { name, ...rest } = profile;
47
+ writeFileSync(join(profilesDir, `${name}.json`), JSON.stringify(rest, null, 2), 'utf-8');
48
+ }
49
+
50
+ export function listProfiles(): string[] {
51
+ const profilesDir = getProfilesDir();
52
+ if (!existsSync(profilesDir)) return [];
53
+ try {
54
+ return readdirSync(profilesDir)
55
+ .filter(f => f.endsWith('.json'))
56
+ .map(f => f.slice(0, -5));
57
+ } catch {
58
+ return [];
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Apply a profile to the current process environment.
64
+ * This mutates process.env so all subsequent operations pick up the profile.
65
+ */
66
+ export function applyProfile(profile: ConfigProfile): void {
67
+ if (profile.anthropicApiKey) {
68
+ process.env.ANTHROPIC_API_KEY = profile.anthropicApiKey;
69
+ }
70
+ if (profile.awsProfile) {
71
+ process.env.AWS_PROFILE = profile.awsProfile;
72
+ }
73
+ if (profile.awsRegion) {
74
+ process.env.AWS_DEFAULT_REGION = profile.awsRegion;
75
+ process.env.AWS_REGION = profile.awsRegion;
76
+ }
77
+ if (profile.gcpProject) {
78
+ process.env.GCLOUD_PROJECT = profile.gcpProject;
79
+ process.env.GOOGLE_CLOUD_PROJECT = profile.gcpProject;
80
+ }
81
+ if (profile.azureSubscription) {
82
+ process.env.AZURE_SUBSCRIPTION_ID = profile.azureSubscription;
83
+ }
84
+ }
@@ -261,7 +261,11 @@ export type ConfigKey =
261
261
  | 'llm.cost_optimization.cheap_model'
262
262
  | 'llm.cost_optimization.expensive_model'
263
263
  | 'llm.cost_optimization.use_cheap_model_for'
264
- | 'llm.cost_optimization.use_expensive_model_for';
264
+ | 'llm.cost_optimization.use_expensive_model_for'
265
+ | 'primaryClouds'
266
+ | 'model'
267
+ | 'defaultCostBudget'
268
+ | 'agentTurnTimeoutSeconds';
265
269
 
266
270
  /**
267
271
  * Config key metadata for help/validation
@@ -524,4 +528,82 @@ export const CONFIG_KEYS: ConfigKeyInfo[] = [
524
528
  description: 'Ordered list of fallback LLM providers to try on failure (JSON array)',
525
529
  type: 'string',
526
530
  },
531
+ // M4: Primary cloud providers
532
+ {
533
+ key: 'primaryClouds',
534
+ description: 'Primary cloud providers (comma-separated: aws,gcp,azure)',
535
+ type: 'string',
536
+ },
537
+ // M4: Model override (validated against known model IDs)
538
+ {
539
+ key: 'model',
540
+ description: 'Default model ID (e.g. claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001)',
541
+ type: 'string',
542
+ },
543
+ // G16: Default cost budget per session
544
+ {
545
+ key: 'defaultCostBudget',
546
+ description: 'Default cost budget in USD per session (e.g. 1.00). 0 means unlimited.',
547
+ type: 'number',
548
+ defaultValue: 0,
549
+ },
550
+ // G21: Configurable agent turn silence timeout
551
+ {
552
+ key: 'agentTurnTimeoutSeconds',
553
+ description: 'Seconds to wait for LLM response before aborting (default: 60)',
554
+ type: 'number',
555
+ defaultValue: 60,
556
+ },
527
557
  ];
558
+
559
+ // ---------------------------------------------------------------------------
560
+ // M3: Per-project configuration (nimbus.yaml / .nimbus/config.json)
561
+ // ---------------------------------------------------------------------------
562
+
563
+ /**
564
+ * Per-project Nimbus configuration for team defaults.
565
+ * Stored in <project-root>/nimbus.yaml OR <project-root>/.nimbus/config.json.
566
+ * JSON-only format to avoid adding a YAML parser dependency.
567
+ */
568
+ export interface ProjectConfig {
569
+ /** Default Terraform workspace for this project */
570
+ defaultWorkspace?: string;
571
+ /** Default kubectl context for this project */
572
+ defaultContext?: string;
573
+ /** Default Kubernetes namespace */
574
+ defaultNamespace?: string;
575
+ /** Tool call patterns to auto-approve (e.g. ["kubectl get *", "terraform plan"]) */
576
+ autoApprove?: string[];
577
+ /** Environment names that should be treated as protected (require confirmation for destructive ops) */
578
+ protectedEnvironments?: string[];
579
+ }
580
+
581
+ /**
582
+ * Load per-project configuration from:
583
+ * 1. <cwd>/nimbus.yaml (JSON-parsed, no YAML dep)
584
+ * 2. <cwd>/.nimbus/config.json
585
+ *
586
+ * Returns null if neither file exists or both fail to parse.
587
+ */
588
+ export function loadProjectConfig(cwd: string): ProjectConfig | null {
589
+ const { existsSync, readFileSync } = require('node:fs') as typeof import('node:fs');
590
+ const { join } = require('node:path') as typeof import('node:path');
591
+
592
+ const candidates = [
593
+ join(cwd, 'nimbus.yaml'),
594
+ join(cwd, '.nimbus', 'config.json'),
595
+ ];
596
+
597
+ for (const file of candidates) {
598
+ try {
599
+ if (!existsSync(file)) continue;
600
+ const raw = readFileSync(file, 'utf-8');
601
+ const parsed = JSON.parse(raw) as ProjectConfig;
602
+ return parsed;
603
+ } catch {
604
+ // Try next candidate
605
+ }
606
+ }
607
+
608
+ return null;
609
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Workspace state persistence — saves terraform workspace + kubectl context
3
+ * per working directory to ~/.nimbus/workspace-state.json
4
+ */
5
+ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { homedir } from 'node:os';
8
+
9
+ export interface WorkspaceState {
10
+ terraformWorkspace?: string;
11
+ kubectlContext?: string;
12
+ awsProfile?: string;
13
+ awsRegion?: string;
14
+ gcpProject?: string;
15
+ lastSeen?: string; // ISO timestamp
16
+ }
17
+
18
+ type StateFile = Record<string, WorkspaceState>; // keyed by cwd
19
+
20
+ const STATE_PATH = join(homedir(), '.nimbus', 'workspace-state.json');
21
+
22
+ function loadStateFile(): StateFile {
23
+ try {
24
+ return JSON.parse(readFileSync(STATE_PATH, 'utf-8')) as StateFile;
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+
30
+ function saveStateFile(state: StateFile): void {
31
+ try {
32
+ mkdirSync(join(homedir(), '.nimbus'), { recursive: true });
33
+ writeFileSync(STATE_PATH, JSON.stringify(state, null, 2), 'utf-8');
34
+ } catch { /* non-critical */ }
35
+ }
36
+
37
+ export function loadWorkspaceState(cwd: string): WorkspaceState {
38
+ const all = loadStateFile();
39
+ return all[cwd] ?? {};
40
+ }
41
+
42
+ export function saveWorkspaceState(cwd: string, state: WorkspaceState): void {
43
+ const all = loadStateFile();
44
+ all[cwd] = { ...all[cwd], ...state, lastSeen: new Date().toISOString() };
45
+ saveStateFile(all);
46
+ }
47
+
48
+ export function mergeWorkspaceState(cwd: string, infra: Partial<WorkspaceState>): WorkspaceState {
49
+ const existing = loadWorkspaceState(cwd);
50
+ const merged = { ...existing, ...infra };
51
+ saveWorkspaceState(cwd, merged);
52
+ return merged;
53
+ }
@@ -96,16 +96,58 @@ export interface CostEstimate {
96
96
  * s3: Minimal storage estimate (< 100GB) ~= $5
97
97
  */
98
98
  const BASE_COMPONENT_COSTS: Record<string, number> = {
99
- vpc: 32, // NAT Gateway
100
- eks: 73, // Control plane
101
- rds: 50, // db.t3.micro + storage
102
- s3: 5, // Minimal storage
103
- ecs: 30, // Fargate minimal
104
- lambda: 2, // < 1M invocations/month
105
- cloudfront: 10, // < 1TB transfer/month
106
- elasticache: 25, // cache.t3.micro
107
- sqs: 1, // < 1M requests/month
108
- sns: 1, // < 1M notifications/month
99
+ // AWS Compute
100
+ vpc: 32,
101
+ eks: 73,
102
+ ecs: 30,
103
+ lambda: 2,
104
+ // EC2 instance types (monthly on-demand us-east-1)
105
+ 't3.micro': 8,
106
+ 't3.small': 16,
107
+ 't3.medium': 32,
108
+ 't3.large': 65,
109
+ 'm5.large': 70,
110
+ 'm5.xlarge': 140,
111
+ 'm5.2xlarge': 280,
112
+ 'c5.large': 62,
113
+ 'c5.xlarge': 124,
114
+ 'r5.large': 91,
115
+ 'r5.xlarge': 182,
116
+ // RDS instance types (monthly, single-AZ)
117
+ 'db.t3.micro': 14,
118
+ 'db.t3.small': 28,
119
+ 'db.t3.medium': 56,
120
+ 'db.r5.large': 175,
121
+ 'db.r5.xlarge': 350,
122
+ // AWS Data
123
+ rds: 50,
124
+ s3: 5,
125
+ elasticache: 25,
126
+ sqs: 1,
127
+ sns: 1,
128
+ cloudfront: 10,
129
+ // AWS Network/LB
130
+ 'aws_nat_gateway': 32,
131
+ 'aws_lb': 25,
132
+ 'aws_alb': 25,
133
+ 'aws_instance': 30,
134
+ 'aws_db_instance': 50,
135
+ 'aws_s3_bucket': 5,
136
+ 'aws_eks_cluster': 73,
137
+ 'aws_elasticache_cluster': 25,
138
+ 'aws_rds_cluster': 50,
139
+ 'aws_lambda_function': 2,
140
+ 'aws_cloudfront_distribution': 10,
141
+ // GCP
142
+ 'google_compute_instance': 30,
143
+ 'google_container_cluster': 73,
144
+ 'google_sql_database_instance': 50,
145
+ 'google_storage_bucket': 5,
146
+ // Azure
147
+ 'azurerm_virtual_machine': 30,
148
+ 'azurerm_kubernetes_cluster': 73,
149
+ 'azurerm_sql_database': 50,
150
+ 'azurerm_storage_account': 5,
109
151
  };
110
152
 
111
153
  /**
@@ -917,8 +917,39 @@ export class Executor {
917
917
  // Execute rollback action
918
918
  this.log(executionId, 'info', `Executing rollback: ${step.rollback_action}`);
919
919
 
920
- // Simulate rollback
921
- await this.sleep(1000);
920
+ // Parse and execute the rollback action
921
+ const action = step.rollback_action;
922
+ const [prefix, ...rest] = action.split(':');
923
+
924
+ let rollbackCmd: string;
925
+ if (prefix === 'terraform_destroy') {
926
+ const workdir = rest.join(':');
927
+ rollbackCmd = `terraform -chdir=${workdir} destroy -auto-approve -no-color`;
928
+ } else if (prefix === 'kubectl_rollout_undo') {
929
+ const [resource, namespace] = rest;
930
+ rollbackCmd = `kubectl rollout undo ${resource}${namespace ? ` -n ${namespace}` : ''}`;
931
+ } else if (prefix === 'helm_rollback') {
932
+ const [release, revision, namespace] = rest;
933
+ rollbackCmd = `helm rollback ${release} ${revision ?? '0'}${namespace ? ` -n ${namespace}` : ''}`;
934
+ } else if (prefix === 'bash') {
935
+ rollbackCmd = rest.join(':');
936
+ } else {
937
+ this.log(executionId, 'warn', `Unknown rollback prefix "${prefix}" — skipping`);
938
+ rollbackCmd = '';
939
+ }
940
+
941
+ if (rollbackCmd) {
942
+ this.log(executionId, 'info', `Running: ${rollbackCmd}`);
943
+ await new Promise<void>((resolve, reject) => {
944
+ const { exec } = require('node:child_process') as typeof import('node:child_process');
945
+ exec(rollbackCmd, { timeout: 600_000 }, (error, stdout, stderr) => {
946
+ if (stdout) this.log(executionId, 'info', stdout);
947
+ if (stderr) this.log(executionId, 'info', stderr);
948
+ if (error) reject(error);
949
+ else resolve();
950
+ });
951
+ });
952
+ }
922
953
 
923
954
  const completedAt = new Date();
924
955
 
@@ -115,6 +115,17 @@ const VALID_CATEGORIES = new Set(['security', 'cost', 'availability', 'performan
115
115
  // Planner
116
116
  // ==========================================
117
117
 
118
+ /** Detect the primary domain of a task description. */
119
+ function detectDomain(task: AgentTask): 'terraform' | 'kubernetes' | 'helm' | 'generic' {
120
+ const desc = (task.type + ' ' + JSON.stringify(task.context)).toLowerCase();
121
+ if (desc.includes('helm') || desc.includes('chart') || desc.includes('release')) return 'helm';
122
+ if (desc.includes('kubectl') || desc.includes('kubernetes') || desc.includes('pod') || desc.includes('deployment') || desc.includes('k8s')) return 'kubernetes';
123
+ if (desc.includes('terraform') || desc.includes('.tf') || desc.includes('infrastructure') || desc.includes('provider')) return 'terraform';
124
+ // Default to terraform for infrastructure tasks
125
+ if (task.type === 'deploy' || task.type === 'generate') return 'terraform';
126
+ return 'generic';
127
+ }
128
+
118
129
  export class Planner {
119
130
  private router: LLMRouter;
120
131
 
@@ -230,6 +241,16 @@ export class Planner {
230
241
  const steps: PlanStep[] = [];
231
242
  let order = 1;
232
243
 
244
+ // Domain-specific step generation
245
+ const domain = detectDomain(task);
246
+ if (domain === 'terraform') {
247
+ return this.generateTerraformSteps(task);
248
+ } else if (domain === 'kubernetes') {
249
+ return this.generateKubernetesSteps(task);
250
+ } else if (domain === 'helm') {
251
+ return this.generateHelmSteps(task);
252
+ }
253
+
233
254
  // Step 1: Validate requirements
234
255
  steps.push({
235
256
  id: `step_${order++}`,
@@ -325,7 +346,7 @@ export class Planner {
325
346
  },
326
347
  status: 'pending',
327
348
  depends_on: [steps[steps.length - 1].id],
328
- rollback_action: 'destroy_deployment',
349
+ rollback_action: `terraform_destroy:${task.context.environment ?? '.'}` as string,
329
350
  rollback_parameters: {
330
351
  provider: task.context.provider,
331
352
  environment: task.context.environment,
@@ -366,6 +387,52 @@ export class Planner {
366
387
  return steps;
367
388
  }
368
389
 
390
+ private generateTerraformSteps(task: AgentTask): PlanStep[] {
391
+ const steps: PlanStep[] = [];
392
+ let order = 1;
393
+ const env = task.context.environment ?? '.';
394
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform init — initialize providers and modules', action: 'terraform_init', parameters: { workdir: env }, status: 'pending' });
395
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform validate — check configuration syntax', action: 'terraform_validate', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
396
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform plan — preview changes', action: 'terraform_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'] });
397
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Review plan output with human', action: 'review_plan', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
398
+ if (task.type === 'deploy') {
399
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'terraform apply — apply approved changes', action: 'terraform_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'], rollback_action: `terraform_destroy:${env}`, rollback_parameters: { workdir: env } });
400
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'terraform output — capture outputs', action: 'terraform_output', parameters: { workdir: env }, status: 'pending', depends_on: ['step_5'] });
401
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify deployed infrastructure resources', action: 'verify_infra', parameters: { workdir: env }, status: 'pending', depends_on: ['step_6'] });
402
+ }
403
+ return steps;
404
+ }
405
+
406
+ private generateKubernetesSteps(task: AgentTask): PlanStep[] {
407
+ const steps: PlanStep[] = [];
408
+ let order = 1;
409
+ const env = task.context.environment ?? '.';
410
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'Validate Kubernetes manifests (dry-run client)', action: 'kubectl_dry_run', parameters: { workdir: env }, status: 'pending' });
411
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'kubectl diff — show what would change', action: 'kubectl_diff', parameters: { workdir: env }, status: 'pending', depends_on: ['step_1'] });
412
+ if (task.type === 'deploy') {
413
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'kubectl apply — deploy manifests', action: 'kubectl_apply', parameters: { workdir: env }, status: 'pending', depends_on: ['step_2'], rollback_action: 'kubectl_rollout_undo:deployment/app:default' });
414
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'kubectl rollout status — wait for rollout', action: 'kubectl_rollout_status', parameters: { workdir: env }, status: 'pending', depends_on: ['step_3'] });
415
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify all pods are running', action: 'verify_pods', parameters: { workdir: env }, status: 'pending', depends_on: ['step_4'] });
416
+ }
417
+ return steps;
418
+ }
419
+
420
+ private generateHelmSteps(task: AgentTask): PlanStep[] {
421
+ const steps: PlanStep[] = [];
422
+ let order = 1;
423
+ const release = task.context.components[0] ?? 'app';
424
+ const ns = task.context.environment ?? 'default';
425
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm lint — validate chart templates', action: 'helm_lint', parameters: { release, namespace: ns }, status: 'pending' });
426
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm template — render and preview manifests', action: 'helm_template', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_1'] });
427
+ if (task.type === 'deploy') {
428
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'validate', description: 'helm diff upgrade — show what would change', action: 'helm_diff', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_2'] });
429
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'deploy', description: 'helm upgrade --install --atomic — deploy release', action: 'helm_upgrade', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_3'], rollback_action: `helm_rollback:${release}:0:${ns}` });
430
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'helm test — run chart tests', action: 'helm_test', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_4'] });
431
+ steps.push({ id: `step_${order++}`, order: steps.length + 1, type: 'verify', description: 'Verify release is deployed and healthy', action: 'verify_release', parameters: { release, namespace: ns }, status: 'pending', depends_on: ['step_5'] });
432
+ }
433
+ return steps;
434
+ }
435
+
369
436
  /**
370
437
  * Analyze dependencies between steps
371
438
  */
@@ -1865,3 +1865,11 @@ output "alias_arn" {
1865
1865
  }
1866
1866
  }
1867
1867
  }
1868
+
1869
+ /**
1870
+ * Convenience function — instantiate the generator and run it.
1871
+ * Used by generate-terraform.ts and aws-terraform.ts.
1872
+ */
1873
+ export async function generateTerraformProject(config: TerraformProjectConfig): Promise<GeneratedProject> {
1874
+ return new TerraformProjectGenerator().generate(config);
1875
+ }
@@ -40,11 +40,9 @@ function createEmptyHistoryFile(): HistoryFile {
40
40
  export class HistoryManager {
41
41
  private historyPath: string;
42
42
  private historyFile: HistoryFile | null = null;
43
- private stateServiceUrl: string;
44
43
 
45
44
  constructor(historyPath?: string) {
46
45
  this.historyPath = historyPath || path.join(os.homedir(), '.nimbus', 'history.json');
47
- this.stateServiceUrl = process.env.STATE_SERVICE_URL || 'http://localhost:3004';
48
46
  }
49
47
 
50
48
  /**
@@ -118,66 +116,6 @@ export class HistoryManager {
118
116
  fs.writeFileSync(this.historyPath, content, { mode: 0o600 });
119
117
  }
120
118
 
121
- /**
122
- * Sync a history entry to the remote State Service.
123
- * Fire-and-forget: never throws on network or parse errors.
124
- */
125
- private async syncToStateService(entry: HistoryEntry): Promise<void> {
126
- try {
127
- await fetch(`${this.stateServiceUrl}/api/state/history`, {
128
- method: 'POST',
129
- headers: { 'Content-Type': 'application/json' },
130
- body: JSON.stringify(entry),
131
- });
132
- } catch {
133
- // Fire-and-forget: swallow all errors silently
134
- console.warn('[nimbus] Failed to sync history entry to State Service');
135
- }
136
- }
137
-
138
- /**
139
- * Load history entries from the remote State Service.
140
- * Returns the entries array on success, or null on any failure
141
- * so the caller can fall back to local storage.
142
- */
143
- private async loadFromStateService(options: HistoryQueryOptions): Promise<HistoryEntry[] | null> {
144
- try {
145
- const params = new URLSearchParams();
146
- if (options.limit !== undefined) {
147
- params.set('limit', String(options.limit));
148
- }
149
- if (options.since) {
150
- params.set('since', options.since);
151
- }
152
- if (options.until) {
153
- params.set('until', options.until);
154
- }
155
- if (options.command) {
156
- params.set('command', options.command);
157
- }
158
- if (options.status) {
159
- params.set('status', options.status);
160
- }
161
-
162
- const queryString = params.toString();
163
- const url = `${this.stateServiceUrl}/api/state/history${queryString ? `?${queryString}` : ''}`;
164
-
165
- const response = await fetch(url);
166
- if (!response.ok) {
167
- return null;
168
- }
169
-
170
- const body = (await response.json()) as { success?: boolean; data?: HistoryEntry[] };
171
- if (body.success && Array.isArray(body.data)) {
172
- return body.data;
173
- }
174
- return null;
175
- } catch {
176
- // Network error, parse error, etc. — fall back to local
177
- return null;
178
- }
179
- }
180
-
181
119
  /**
182
120
  * Add a new entry to history
183
121
  */
@@ -202,9 +140,6 @@ export class HistoryManager {
202
140
 
203
141
  this.save(historyFile);
204
142
 
205
- // Fire-and-forget: sync to State Service without blocking
206
- this.syncToStateService(entry).catch(() => {});
207
-
208
143
  return entry;
209
144
  }
210
145
 
@@ -245,17 +180,10 @@ export class HistoryManager {
245
180
 
246
181
  /**
247
182
  * Get history entries with optional filtering.
248
- * Attempts to load from the remote State Service first;
249
- * falls back to local file storage on any failure.
183
+ * Reads from local file storage.
250
184
  */
251
185
  async getEntries(options: HistoryQueryOptions = {}): Promise<HistoryEntry[]> {
252
- // Try remote State Service first
253
- const remoteEntries = await this.loadFromStateService(options);
254
- if (remoteEntries !== null) {
255
- return remoteEntries;
256
- }
257
-
258
- // Fall back to local file
186
+ // Read from local file
259
187
  const historyFile = this.load();
260
188
  let entries = [...historyFile.entries];
261
189
 
@@ -242,13 +242,14 @@ export class HookEngine {
242
242
  }
243
243
 
244
244
  // Write context JSON to stdin
245
- try {
246
- if (child.stdin) {
245
+ if (child.stdin) {
246
+ child.stdin.on('error', () => { /* EPIPE or other write errors — ignore */ });
247
+ try {
247
248
  child.stdin.write(JSON.stringify(context));
248
249
  child.stdin.end();
250
+ } catch {
251
+ // stdin may already be closed -- ignore
249
252
  }
250
- } catch {
251
- // stdin may already be closed -- ignore
252
253
  }
253
254
 
254
255
  // Collect stdout and stderr
@@ -138,13 +138,13 @@ export function calculateCost(
138
138
 
139
139
  const providerPricing = PRICING[provider];
140
140
  if (!providerPricing) {
141
- logger.warn(`No pricing data for provider "${provider}", returning zero cost`);
141
+ logger.debug(`No pricing data for provider "${provider}", returning zero cost`);
142
142
  return { costUSD: 0, breakdown: { input: 0, output: 0 } };
143
143
  }
144
144
 
145
145
  const entry = providerPricing[model];
146
146
  if (!entry) {
147
- logger.warn(
147
+ logger.debug(
148
148
  `No pricing data for model "${model}" on provider "${provider}", returning zero cost`
149
149
  );
150
150
  return { costUSD: 0, breakdown: { input: 0, output: 0 } };