@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,625 @@
1
+ /**
2
+ * Cost Commands
3
+ *
4
+ * Commands for infrastructure cost estimation and tracking
5
+ */
6
+ import { ui } from '../../wizard/ui';
7
+ import { select } from '../../wizard/prompts';
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import { execSync } from 'node:child_process';
11
+ import { CostEstimator } from './estimator';
12
+ // ==========================================
13
+ // Parsers
14
+ // ==========================================
15
+ /**
16
+ * Parse cost estimate options
17
+ */
18
+ export function parseCostEstimateOptions(args) {
19
+ const options = {};
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === '--directory' && args[i + 1]) {
23
+ options.directory = args[++i];
24
+ }
25
+ else if (arg === '-d' && args[i + 1]) {
26
+ options.directory = args[++i];
27
+ }
28
+ else if (arg === '--format' && args[i + 1]) {
29
+ options.format = args[++i];
30
+ }
31
+ else if (arg === '--detailed') {
32
+ options.detailed = true;
33
+ }
34
+ else if (arg === '--compare' && args[i + 1]) {
35
+ options.compare = args[++i];
36
+ }
37
+ else if (!arg.startsWith('-') && !options.directory) {
38
+ options.directory = arg;
39
+ }
40
+ }
41
+ return options;
42
+ }
43
+ /**
44
+ * Parse cost history options
45
+ */
46
+ export function parseCostHistoryOptions(args) {
47
+ const options = {
48
+ days: 30,
49
+ groupBy: 'service',
50
+ };
51
+ for (let i = 0; i < args.length; i++) {
52
+ const arg = args[i];
53
+ if (arg === '--days' && args[i + 1]) {
54
+ options.days = parseInt(args[++i], 10);
55
+ }
56
+ else if (arg === '--group-by' && args[i + 1]) {
57
+ options.groupBy = args[++i];
58
+ }
59
+ else if (arg === '--provider' && args[i + 1]) {
60
+ options.provider = args[++i];
61
+ }
62
+ else if (arg === '--format' && args[i + 1]) {
63
+ options.format = args[++i];
64
+ }
65
+ }
66
+ return options;
67
+ }
68
+ // ==========================================
69
+ // Helpers
70
+ // ==========================================
71
+ /**
72
+ * Check if infracost is installed
73
+ */
74
+ function checkInfracost() {
75
+ try {
76
+ execSync('infracost --version', { stdio: 'pipe' });
77
+ return true;
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ /**
84
+ * Run infracost breakdown
85
+ */
86
+ function runInfracostBreakdown(directory) {
87
+ try {
88
+ const result = execSync(`infracost breakdown --path "${directory}" --format json`, {
89
+ stdio: 'pipe',
90
+ encoding: 'utf-8',
91
+ maxBuffer: 10 * 1024 * 1024,
92
+ });
93
+ return JSON.parse(result);
94
+ }
95
+ catch (error) {
96
+ return null;
97
+ }
98
+ }
99
+ /**
100
+ * Format currency
101
+ */
102
+ function formatCurrency(amount, currency = 'USD') {
103
+ return new Intl.NumberFormat('en-US', {
104
+ style: 'currency',
105
+ currency,
106
+ minimumFractionDigits: 2,
107
+ maximumFractionDigits: 2,
108
+ }).format(amount);
109
+ }
110
+ /**
111
+ * Format change with color
112
+ */
113
+ function formatChange(amount, currency = 'USD') {
114
+ if (amount === 0) {
115
+ return ui.color('$0.00', 'dim');
116
+ }
117
+ else if (amount > 0) {
118
+ return ui.color(`+${formatCurrency(amount, currency)}`, 'red');
119
+ }
120
+ else {
121
+ return ui.color(formatCurrency(amount, currency), 'green');
122
+ }
123
+ }
124
+ /**
125
+ * Get mock cost history (in real implementation, would query cloud provider)
126
+ */
127
+ function getMockCostHistory(options) {
128
+ const entries = [];
129
+ const now = new Date();
130
+ const days = options.days || 30;
131
+ // Generate mock data for demonstration
132
+ const services = ['EC2', 'RDS', 'S3', 'Lambda', 'CloudWatch'];
133
+ for (let i = days - 1; i >= 0; i--) {
134
+ const date = new Date(now);
135
+ date.setDate(date.getDate() - i);
136
+ const dateStr = date.toISOString().split('T')[0];
137
+ for (const service of services) {
138
+ const baseCost = {
139
+ EC2: 45,
140
+ RDS: 35,
141
+ S3: 5,
142
+ Lambda: 10,
143
+ CloudWatch: 3,
144
+ }[service] || 10;
145
+ // Add some variance
146
+ const variance = (Math.random() - 0.5) * baseCost * 0.2;
147
+ const cost = baseCost + variance;
148
+ entries.push({
149
+ date: dateStr,
150
+ service,
151
+ cost: parseFloat(cost.toFixed(2)),
152
+ change: parseFloat(variance.toFixed(2)),
153
+ });
154
+ }
155
+ }
156
+ return entries;
157
+ }
158
+ // ==========================================
159
+ // Display Functions
160
+ // ==========================================
161
+ /**
162
+ * Display cost estimate
163
+ */
164
+ function displayCostEstimate(estimate, detailed = false) {
165
+ ui.newLine();
166
+ ui.section('Cost Estimate');
167
+ ui.print(` ${ui.dim('Generated:')} ${new Date(estimate.timeGenerated).toLocaleString()}`);
168
+ ui.print(` ${ui.dim('Currency:')} ${estimate.currency}`);
169
+ ui.newLine();
170
+ // Summary
171
+ ui.print(` ${ui.bold('Monthly Cost:')} ${ui.color(formatCurrency(estimate.totalMonthlyCost), 'cyan')}`);
172
+ ui.print(` ${ui.bold('Hourly Cost:')} ${formatCurrency(estimate.totalHourlyCost)}`);
173
+ if (estimate.diffTotalMonthlyCost !== 0) {
174
+ ui.print(` ${ui.bold('Change:')} ${formatChange(estimate.diffTotalMonthlyCost)}`);
175
+ }
176
+ ui.newLine();
177
+ // Resource summary
178
+ ui.print(` ${ui.dim('Resources detected:')} ${estimate.summary.totalDetectedResources}`);
179
+ ui.print(` ${ui.dim('Resources supported:')} ${estimate.summary.totalSupportedResources}`);
180
+ ui.print(` ${ui.dim('Resources unsupported:')} ${estimate.summary.totalUnsupportedResources}`);
181
+ ui.newLine();
182
+ // Project breakdown
183
+ for (const project of estimate.projects) {
184
+ ui.section(`Project: ${project.name}`);
185
+ ui.print(` ${ui.dim('Monthly:')} ${formatCurrency(project.totalMonthlyCost)}`);
186
+ if (project.diffTotalMonthlyCost !== 0) {
187
+ ui.print(` ${ui.dim('Change:')} ${formatChange(project.diffTotalMonthlyCost)}`);
188
+ }
189
+ if (detailed && project.resources.length > 0) {
190
+ ui.newLine();
191
+ ui.print(' Resources:');
192
+ // Group by type
193
+ const byType = {};
194
+ for (const resource of project.resources) {
195
+ const type = resource.resourceType;
196
+ if (!byType[type]) {
197
+ byType[type] = [];
198
+ }
199
+ byType[type].push(resource);
200
+ }
201
+ for (const [type, resources] of Object.entries(byType)) {
202
+ const typeCost = resources.reduce((sum, r) => sum + r.monthlyCost, 0);
203
+ ui.newLine();
204
+ ui.print(` ${ui.bold(type)} (${resources.length}) - ${formatCurrency(typeCost)}/mo`);
205
+ for (const resource of resources.slice(0, 5)) {
206
+ const cost = formatCurrency(resource.monthlyCost);
207
+ ui.print(` ${resource.name}: ${cost}/mo`);
208
+ }
209
+ if (resources.length > 5) {
210
+ ui.print(ui.dim(` ... and ${resources.length - 5} more`));
211
+ }
212
+ }
213
+ }
214
+ }
215
+ // Unsupported resources warning
216
+ if (Object.keys(estimate.summary.unsupportedResourceCounts).length > 0) {
217
+ ui.newLine();
218
+ ui.warning('Some resources could not be priced:');
219
+ for (const [type, count] of Object.entries(estimate.summary.unsupportedResourceCounts)) {
220
+ ui.print(` ${ui.dim('•')} ${type}: ${count}`);
221
+ }
222
+ }
223
+ }
224
+ /**
225
+ * Display cost history
226
+ */
227
+ function displayCostHistory(entries, groupBy) {
228
+ ui.newLine();
229
+ ui.section('Cost History');
230
+ if (entries.length === 0) {
231
+ ui.info('No cost data available for the specified period.');
232
+ return;
233
+ }
234
+ // Group entries by the specified field
235
+ const groups = {};
236
+ for (const entry of entries) {
237
+ const key = groupBy === 'service' ? entry.service : entry.resource || entry.service;
238
+ if (!groups[key]) {
239
+ groups[key] = [];
240
+ }
241
+ groups[key].push(entry);
242
+ }
243
+ // Calculate totals per group
244
+ const totals = [];
245
+ for (const [group, groupEntries] of Object.entries(groups)) {
246
+ const total = groupEntries.reduce((sum, e) => sum + e.cost, 0);
247
+ const avg = total / groupEntries.length;
248
+ // Calculate trend (last 7 days vs previous 7 days)
249
+ const recent = groupEntries.slice(-7).reduce((sum, e) => sum + e.cost, 0);
250
+ const previous = groupEntries.slice(-14, -7).reduce((sum, e) => sum + e.cost, 0);
251
+ const trend = previous > 0 ? ((recent - previous) / previous) * 100 : 0;
252
+ totals.push({ group, total, avg, trend });
253
+ }
254
+ // Sort by total cost
255
+ totals.sort((a, b) => b.total - a.total);
256
+ // Display table
257
+ const grandTotal = totals.reduce((sum, t) => sum + t.total, 0);
258
+ ui.print(` ${ui.dim('Total period cost:')} ${ui.color(formatCurrency(grandTotal), 'cyan')}`);
259
+ ui.newLine();
260
+ // Table header
261
+ const colWidths = { group: 20, total: 15, avg: 15, trend: 15 };
262
+ const header = [
263
+ groupBy.charAt(0).toUpperCase() + groupBy.slice(1).padEnd(colWidths.group),
264
+ 'Total'.padStart(colWidths.total),
265
+ 'Daily Avg'.padStart(colWidths.avg),
266
+ 'Trend (7d)'.padStart(colWidths.trend),
267
+ ].join(' ');
268
+ ui.print(` ${ui.dim(header)}`);
269
+ ui.print(` ${ui.dim('-'.repeat(header.length))}`);
270
+ for (const { group, total, avg, trend } of totals.slice(0, 10)) {
271
+ const trendStr = trend > 0
272
+ ? ui.color(`+${trend.toFixed(1)}%`, 'red')
273
+ : trend < 0
274
+ ? ui.color(`${trend.toFixed(1)}%`, 'green')
275
+ : ui.color('0.0%', 'dim');
276
+ const row = [
277
+ group.substring(0, colWidths.group).padEnd(colWidths.group),
278
+ formatCurrency(total).padStart(colWidths.total),
279
+ formatCurrency(avg).padStart(colWidths.avg),
280
+ trendStr.padStart(colWidths.trend + 10), // Extra for ANSI codes
281
+ ].join(' ');
282
+ ui.print(` ${row}`);
283
+ }
284
+ if (totals.length > 10) {
285
+ ui.newLine();
286
+ ui.print(ui.dim(` ... and ${totals.length - 10} more ${groupBy}s`));
287
+ }
288
+ }
289
+ // ==========================================
290
+ // Commands
291
+ // ==========================================
292
+ /**
293
+ * Cost parent command
294
+ */
295
+ export async function costCommand(args) {
296
+ if (args.length === 0) {
297
+ ui.header('Nimbus Cost', 'Infrastructure cost estimation and tracking');
298
+ ui.newLine();
299
+ ui.print('Usage: nimbus cost <command> [options]');
300
+ ui.newLine();
301
+ ui.print('Commands:');
302
+ ui.print(` ${ui.bold('estimate')} Estimate infrastructure costs from Terraform`);
303
+ ui.print(` ${ui.bold('compare')} Compare cost between two workspaces or directories`);
304
+ ui.print(` ${ui.bold('report')} Export session cost summary`);
305
+ ui.print(` ${ui.bold('history')} View historical cost data`);
306
+ ui.print(` ${ui.bold('diff')} Diff cost between two paths`);
307
+ ui.newLine();
308
+ ui.print('Examples:');
309
+ ui.print(' nimbus cost estimate');
310
+ ui.print(' nimbus cost estimate --workspace staging --provider aws');
311
+ ui.print(' nimbus cost estimate -d ./terraform --detailed');
312
+ ui.print(' nimbus cost compare ./workspace1 ./workspace2');
313
+ ui.print(' nimbus cost report --output csv');
314
+ ui.print(' nimbus cost history --days 30 --group-by service');
315
+ return;
316
+ }
317
+ const subcommand = args[0];
318
+ const subArgs = args.slice(1);
319
+ switch (subcommand) {
320
+ case 'estimate': {
321
+ const opts = parseCostEstimateOptions(subArgs);
322
+ // M3: handle --workspace and --provider flags for standalone estimate
323
+ for (let i = 0; i < subArgs.length; i++) {
324
+ if (subArgs[i] === '--workspace' && subArgs[i + 1]) {
325
+ opts.directory = opts.directory ?? subArgs[i + 1];
326
+ }
327
+ if (subArgs[i] === '--provider' && subArgs[i + 1]) {
328
+ // Provider hint is informational — store in compare for future use
329
+ }
330
+ }
331
+ await costEstimateCommand(opts);
332
+ break;
333
+ }
334
+ case 'compare': {
335
+ // M3: nimbus cost compare <workspace1> <workspace2>
336
+ const path1 = subArgs[0];
337
+ const path2 = subArgs[1];
338
+ if (!path1 || !path2) {
339
+ ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
340
+ ui.info('Compares infracost estimates between two directories.');
341
+ process.exit(1);
342
+ }
343
+ const format = subArgs.includes('--json') ? 'json' : 'table';
344
+ await costCompareCommand(path1, path2, { format });
345
+ break;
346
+ }
347
+ case 'report': {
348
+ // M3: nimbus cost report [--output csv|json|text]
349
+ let outputFormat = 'text';
350
+ for (let i = 0; i < subArgs.length; i++) {
351
+ if (subArgs[i] === '--output' && subArgs[i + 1]) {
352
+ const fmt = subArgs[i + 1];
353
+ if (fmt === 'csv' || fmt === 'json' || fmt === 'text') {
354
+ outputFormat = fmt;
355
+ }
356
+ }
357
+ }
358
+ await costReportCommand(outputFormat);
359
+ break;
360
+ }
361
+ case 'history':
362
+ await costHistoryCommand(parseCostHistoryOptions(subArgs));
363
+ break;
364
+ case 'diff': {
365
+ const format = subArgs.includes('--json') ? 'json' : 'table';
366
+ await costDiffCommand(subArgs[0], subArgs[1], { format });
367
+ break;
368
+ }
369
+ default:
370
+ ui.error(`Unknown cost command: ${subcommand}`);
371
+ ui.info('Run "nimbus cost" for usage');
372
+ }
373
+ }
374
+ /**
375
+ * Cost estimate command
376
+ */
377
+ export async function costEstimateCommand(options) {
378
+ const directory = options.directory || process.cwd();
379
+ ui.header('Nimbus Cost Estimate', directory);
380
+ // Check for Terraform files
381
+ const hasTerraform = fs.existsSync(path.join(directory, 'main.tf')) ||
382
+ fs.existsSync(path.join(directory, 'terraform.tf')) ||
383
+ fs.readdirSync(directory).some(f => f.endsWith('.tf'));
384
+ if (!hasTerraform) {
385
+ ui.warning('No Terraform files found in the specified directory.');
386
+ ui.info('Cost estimation requires Terraform configuration files.');
387
+ return;
388
+ }
389
+ // Check for infracost
390
+ if (!checkInfracost()) {
391
+ ui.info('Infracost is not installed. Using built-in cost estimator.');
392
+ ui.newLine();
393
+ ui.startSpinner({ message: 'Running built-in cost estimation...' });
394
+ try {
395
+ const result = await CostEstimator.estimateDirectory(directory);
396
+ ui.stopSpinnerSuccess('Cost estimation complete');
397
+ if (options.format === 'json') {
398
+ console.log(JSON.stringify(result, null, 2));
399
+ return;
400
+ }
401
+ displayCostEstimate(result, options.detailed || false);
402
+ ui.newLine();
403
+ ui.info('For more accurate pricing, install Infracost:');
404
+ ui.print(` ${ui.dim('brew install infracost')} (macOS)`);
405
+ ui.print(` ${ui.dim('curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh')} (Linux)`);
406
+ }
407
+ catch (error) {
408
+ ui.stopSpinnerFail('Cost estimation failed');
409
+ ui.error(`Built-in estimator error: ${error instanceof Error ? error.message : String(error)}`);
410
+ }
411
+ return;
412
+ }
413
+ ui.startSpinner({ message: 'Running cost estimation...' });
414
+ const estimate = runInfracostBreakdown(directory);
415
+ if (!estimate) {
416
+ ui.stopSpinnerFail('Cost estimation failed');
417
+ ui.error('Failed to run infracost. Make sure you have authenticated with "infracost auth login"');
418
+ return;
419
+ }
420
+ ui.stopSpinnerSuccess('Cost estimation complete');
421
+ if (options.format === 'json') {
422
+ console.log(JSON.stringify(estimate, null, 2));
423
+ return;
424
+ }
425
+ displayCostEstimate(estimate, options.detailed || false);
426
+ }
427
+ /**
428
+ * Cost history command
429
+ */
430
+ export async function costHistoryCommand(options) {
431
+ ui.header('Nimbus Cost History', `Last ${options.days} days`);
432
+ // In a real implementation, this would query cloud provider cost APIs
433
+ // For now, we'll use mock data or show instructions
434
+ if (!options.provider) {
435
+ const providerChoice = await select({
436
+ message: 'Select cloud provider:',
437
+ options: [
438
+ { label: 'AWS', value: 'aws', description: 'Amazon Web Services Cost Explorer' },
439
+ { label: 'GCP', value: 'gcp', description: 'Google Cloud Billing' },
440
+ { label: 'Azure', value: 'azure', description: 'Azure Cost Management' },
441
+ { label: 'Demo', value: 'demo', description: 'Show demo data' },
442
+ ],
443
+ });
444
+ options.provider = providerChoice;
445
+ }
446
+ if (options.provider === 'demo') {
447
+ ui.startSpinner({ message: 'Loading cost history...' });
448
+ // Simulate API call delay
449
+ await new Promise(resolve => setTimeout(resolve, 1000));
450
+ const entries = getMockCostHistory(options);
451
+ ui.stopSpinnerSuccess(`Loaded ${entries.length} entries`);
452
+ if (options.format === 'json') {
453
+ console.log(JSON.stringify(entries, null, 2));
454
+ return;
455
+ }
456
+ displayCostHistory(entries, options.groupBy || 'service');
457
+ ui.newLine();
458
+ ui.warning('This is demo data. Connect to a real cloud provider for actual costs.');
459
+ return;
460
+ }
461
+ // Real provider - show instructions
462
+ ui.newLine();
463
+ ui.info(`To view ${options.provider?.toUpperCase()} cost history, you need to configure credentials.`);
464
+ ui.newLine();
465
+ switch (options.provider) {
466
+ case 'aws':
467
+ ui.print('AWS Cost Explorer API requires:');
468
+ ui.print(' 1. AWS credentials configured (aws configure)');
469
+ ui.print(' 2. Cost Explorer enabled in your AWS account');
470
+ ui.print(' 3. IAM permissions for ce:GetCostAndUsage');
471
+ break;
472
+ case 'gcp':
473
+ ui.print('GCP Billing API requires:');
474
+ ui.print(' 1. GCP credentials configured (gcloud auth)');
475
+ ui.print(' 2. Billing export enabled to BigQuery');
476
+ ui.print(' 3. IAM permissions for bigquery.jobs.create');
477
+ break;
478
+ case 'azure':
479
+ ui.print('Azure Cost Management API requires:');
480
+ ui.print(' 1. Azure CLI authenticated (az login)');
481
+ ui.print(' 2. Reader role on the subscription');
482
+ ui.print(' 3. Cost Management permissions');
483
+ break;
484
+ }
485
+ ui.newLine();
486
+ ui.print('Run with --provider demo to see sample data.');
487
+ }
488
+ /**
489
+ * H5: Cost diff — compare infracost estimates between two directories or branches.
490
+ */
491
+ export async function costDiffCommand(path1, path2, opts = {}) {
492
+ if (!path1 || !path2) {
493
+ ui.error('Usage: nimbus cost diff <path1> <path2> [--json]');
494
+ process.exit(1);
495
+ }
496
+ // Check if infracost is available
497
+ try {
498
+ execSync('infracost --version', { stdio: 'pipe' });
499
+ }
500
+ catch {
501
+ ui.error('infracost is not installed.');
502
+ ui.info('Install it from: https://www.infracost.io/docs/');
503
+ ui.info(' brew install infracost (macOS)');
504
+ ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
505
+ process.exit(1);
506
+ }
507
+ ui.startSpinner({ message: `Comparing costs: ${path1} vs ${path2}` });
508
+ try {
509
+ const rawOutput = execSync(`infracost diff --path "${path1}" --compare-to "${path2}" --format json`, { encoding: 'utf-8', stdio: 'pipe', maxBuffer: 10 * 1024 * 1024 });
510
+ const data = JSON.parse(rawOutput);
511
+ ui.stopSpinnerSuccess('Cost diff complete');
512
+ if (opts.format === 'json') {
513
+ console.log(JSON.stringify(data, null, 2));
514
+ return;
515
+ }
516
+ // Table output
517
+ ui.header('Cost Diff');
518
+ ui.print(` Comparing: ${path1} (current) → ${path2} (baseline)`);
519
+ ui.newLine();
520
+ const monthly = data.diffTotalMonthlyCost ?? 0;
521
+ ui.print(` ${ui.bold('Monthly delta:')} ${formatChange(monthly, data.currency)}`);
522
+ ui.print(` ${ui.bold('New total: ')} ${formatCurrency(data.totalMonthlyCost, data.currency)}/mo`);
523
+ ui.newLine();
524
+ // Per-project breakdown
525
+ for (const project of data.projects ?? []) {
526
+ if ((project.diffTotalMonthlyCost ?? 0) === 0)
527
+ continue;
528
+ ui.print(` ${ui.bold(project.name)}: ${formatChange(project.diffTotalMonthlyCost, data.currency)}/mo`);
529
+ for (const resource of (project.resources ?? []).slice(0, 10)) {
530
+ ui.print(` ${resource.name.slice(0, 50).padEnd(50)} ${formatCurrency(resource.monthlyCost, data.currency)}/mo`);
531
+ }
532
+ }
533
+ }
534
+ catch (error) {
535
+ ui.stopSpinnerFail('Cost diff failed');
536
+ ui.error(error instanceof Error ? error.message : String(error));
537
+ process.exit(1);
538
+ }
539
+ }
540
+ /**
541
+ * M3: Cost compare — compare infracost estimates between two workspaces.
542
+ * Runs infracost breakdown for each path and shows the delta.
543
+ */
544
+ export async function costCompareCommand(workspace1, workspace2, opts = {}) {
545
+ if (!workspace1 || !workspace2) {
546
+ ui.error('Usage: nimbus cost compare <workspace1> <workspace2>');
547
+ process.exit(1);
548
+ }
549
+ // Try infracost first
550
+ const hasInfracost = checkInfracost();
551
+ if (!hasInfracost) {
552
+ ui.info('infracost is not installed — showing placeholder comparison.');
553
+ ui.info('Install infracost for real cost estimates: https://infracost.io');
554
+ ui.newLine();
555
+ ui.print(` Workspace 1: ${workspace1}`);
556
+ ui.print(` Workspace 2: ${workspace2}`);
557
+ ui.print(' Cost delta: N/A (install infracost for actual data)');
558
+ ui.newLine();
559
+ ui.info(' brew install infracost (macOS)');
560
+ ui.info(' curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh (Linux)');
561
+ return;
562
+ }
563
+ ui.startSpinner({ message: `Comparing costs: ${workspace1} vs ${workspace2}` });
564
+ try {
565
+ // Run infracost breakdown for each workspace
566
+ const est1 = runInfracostBreakdown(workspace1);
567
+ const est2 = runInfracostBreakdown(workspace2);
568
+ ui.stopSpinnerSuccess('Cost comparison complete');
569
+ if (!est1 && !est2) {
570
+ ui.warning('Could not obtain infracost estimates for either workspace.');
571
+ return;
572
+ }
573
+ const cost1 = est1?.totalMonthlyCost ?? 0;
574
+ const cost2 = est2?.totalMonthlyCost ?? 0;
575
+ const delta = cost1 - cost2;
576
+ const currency = est1?.currency ?? est2?.currency ?? 'USD';
577
+ if (opts.format === 'json') {
578
+ console.log(JSON.stringify({ workspace1, workspace2, cost1, cost2, delta, currency }, null, 2));
579
+ return;
580
+ }
581
+ ui.header('Cost Comparison');
582
+ ui.print(` ${ui.bold(workspace1)}: ${formatCurrency(cost1, currency)}/mo`);
583
+ ui.print(` ${ui.bold(workspace2)}: ${formatCurrency(cost2, currency)}/mo`);
584
+ ui.newLine();
585
+ ui.print(` ${ui.bold('Delta (1 vs 2):')} ${formatChange(delta, currency)}/mo`);
586
+ }
587
+ catch (err) {
588
+ ui.stopSpinnerFail('Cost comparison failed');
589
+ ui.error(err instanceof Error ? err.message : String(err));
590
+ process.exit(1);
591
+ }
592
+ }
593
+ /**
594
+ * M3: Cost report — export the session cost summary.
595
+ * Reads session tool-call cost data and exports it in the requested format.
596
+ */
597
+ export async function costReportCommand(outputFormat = 'text') {
598
+ ui.header('Nimbus Cost Report', `Session cost summary (format: ${outputFormat})`);
599
+ // Attempt to read cost data from ~/.nimbus/nimbus.db via the state DB
600
+ // For now, expose session-level token cost data as a report placeholder.
601
+ // In a full implementation this would query the cost_tracker table in SQLite.
602
+ const report = {
603
+ generatedAt: new Date().toISOString(),
604
+ note: 'Session cost data is tracked in ~/.nimbus/nimbus.db (cost_tracker table).',
605
+ hint: 'Run `nimbus cost estimate` to estimate infrastructure costs for the current directory.',
606
+ format: outputFormat,
607
+ };
608
+ if (outputFormat === 'json') {
609
+ console.log(JSON.stringify(report, null, 2));
610
+ return;
611
+ }
612
+ if (outputFormat === 'csv') {
613
+ console.log('timestamp,description,cost_usd');
614
+ console.log(`${report.generatedAt},session_summary,0.00`);
615
+ ui.newLine();
616
+ ui.info(report.note);
617
+ return;
618
+ }
619
+ // text
620
+ ui.newLine();
621
+ ui.print(` ${ui.dim('Generated:')} ${report.generatedAt}`);
622
+ ui.newLine();
623
+ ui.info(report.note);
624
+ ui.info(report.hint);
625
+ }