@build-astron-co/nimbus 0.4.1 → 0.4.3

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 (435) hide show
  1. package/CHANGELOG.md +268 -89
  2. package/README.md +26 -567
  3. package/dist/src/agent/compaction-agent.js +24 -12
  4. package/dist/src/agent/context-manager.js +2 -1
  5. package/dist/src/agent/expand-files.js +2 -1
  6. package/dist/src/agent/loop.js +71 -33
  7. package/dist/src/agent/permissions.js +4 -2
  8. package/dist/src/agent/system-prompt.js +34 -17
  9. package/dist/src/app.js +1 -1
  10. package/dist/src/auth/keychain.js +8 -4
  11. package/dist/src/auth/store.js +70 -107
  12. package/dist/src/cli/init.js +35 -19
  13. package/dist/src/cli/run.js +18 -10
  14. package/dist/src/cli/serve.js +4 -2
  15. package/dist/src/cli.js +52 -11
  16. package/dist/src/commands/alias.js +5 -3
  17. package/dist/src/commands/audit/index.js +2 -1
  18. package/dist/src/commands/aws-terraform.js +36 -18
  19. package/dist/src/commands/completions.js +1 -1
  20. package/dist/src/commands/config.js +3 -2
  21. package/dist/src/commands/connect-github.js +92 -0
  22. package/dist/src/commands/cost/index.js +3 -2
  23. package/dist/src/commands/deploy.js +15 -10
  24. package/dist/src/commands/doctor.js +9 -6
  25. package/dist/src/commands/drift/index.js +2 -1
  26. package/dist/src/commands/export.js +5 -3
  27. package/dist/src/commands/generate-terraform.js +110 -2
  28. package/dist/src/commands/import.js +3 -3
  29. package/dist/src/commands/incident.js +10 -5
  30. package/dist/src/commands/login.js +8 -93
  31. package/dist/src/commands/logs.js +16 -8
  32. package/dist/src/commands/onboarding.js +6 -4
  33. package/dist/src/commands/pipeline.js +6 -3
  34. package/dist/src/commands/plugin.js +3 -2
  35. package/dist/src/commands/profile.js +27 -14
  36. package/dist/src/commands/questionnaire.js +1 -1
  37. package/dist/src/commands/rollback.js +3 -2
  38. package/dist/src/commands/rollout.js +5 -3
  39. package/dist/src/commands/runbook.js +17 -10
  40. package/dist/src/commands/schedule.js +10 -5
  41. package/dist/src/commands/status.js +2 -1
  42. package/dist/src/commands/team-context.js +12 -7
  43. package/dist/src/commands/template.js +1 -1
  44. package/dist/src/commands/tf/index.js +6 -3
  45. package/dist/src/commands/upgrade.js +5 -3
  46. package/dist/src/commands/version.js +6 -3
  47. package/dist/src/commands/watch.js +6 -3
  48. package/dist/src/compat/sqlite.js +5 -3
  49. package/dist/src/config/mode-store.js +2 -1
  50. package/dist/src/config/profiles.js +4 -2
  51. package/dist/src/config/types.js +2 -1
  52. package/dist/src/engine/executor.js +8 -4
  53. package/dist/src/engine/planner.js +9 -5
  54. package/dist/src/llm/providers/anthropic.js +6 -3
  55. package/dist/src/llm/providers/ollama.js +1 -1
  56. package/dist/src/llm/router.js +22 -7
  57. package/dist/src/nimbus.js +1 -0
  58. package/dist/src/sessions/manager.js +6 -3
  59. package/dist/src/sharing/viewer.js +2 -1
  60. package/dist/src/tools/file-ops.js +1 -2
  61. package/dist/src/tools/schemas/devops.js +197 -108
  62. package/dist/src/tools/schemas/standard.js +1 -1
  63. package/dist/src/ui/App.js +25 -13
  64. package/dist/src/ui/FileDiffModal.js +22 -11
  65. package/dist/src/ui/HelpModal.js +2 -1
  66. package/dist/src/ui/InputBox.js +6 -3
  67. package/dist/src/ui/MessageList.js +40 -20
  68. package/dist/src/ui/TerminalPane.js +2 -1
  69. package/dist/src/ui/ToolCallDisplay.js +12 -6
  70. package/dist/src/ui/TreePane.js +2 -1
  71. package/dist/src/ui/ink/index.js +37 -21
  72. package/dist/src/version.js +1 -1
  73. package/dist/src/watcher/index.js +8 -4
  74. package/package.json +3 -5
  75. package/src/__tests__/alias.test.ts +0 -133
  76. package/src/__tests__/app.test.ts +0 -76
  77. package/src/__tests__/audit.test.ts +0 -877
  78. package/src/__tests__/circuit-breaker.test.ts +0 -116
  79. package/src/__tests__/cli-run.test.ts +0 -351
  80. package/src/__tests__/compat-sqlite.test.ts +0 -68
  81. package/src/__tests__/context-manager.test.ts +0 -632
  82. package/src/__tests__/context.test.ts +0 -242
  83. package/src/__tests__/devops-terminal-gaps.test.ts +0 -718
  84. package/src/__tests__/doctor.test.ts +0 -48
  85. package/src/__tests__/enterprise.test.ts +0 -401
  86. package/src/__tests__/export.test.ts +0 -236
  87. package/src/__tests__/gap-11-18-20.test.ts +0 -958
  88. package/src/__tests__/generator.test.ts +0 -433
  89. package/src/__tests__/helm-streaming.test.ts +0 -127
  90. package/src/__tests__/hooks.test.ts +0 -582
  91. package/src/__tests__/incident.test.ts +0 -179
  92. package/src/__tests__/init.test.ts +0 -487
  93. package/src/__tests__/intent-parser.test.ts +0 -229
  94. package/src/__tests__/llm-router.test.ts +0 -209
  95. package/src/__tests__/logs.test.ts +0 -107
  96. package/src/__tests__/loop-errors.test.ts +0 -244
  97. package/src/__tests__/lsp.test.ts +0 -293
  98. package/src/__tests__/modes.test.ts +0 -336
  99. package/src/__tests__/perf-optimizations.test.ts +0 -847
  100. package/src/__tests__/permissions.test.ts +0 -338
  101. package/src/__tests__/pipeline.test.ts +0 -50
  102. package/src/__tests__/polish-phase3.test.ts +0 -340
  103. package/src/__tests__/profile.test.ts +0 -237
  104. package/src/__tests__/rollback.test.ts +0 -83
  105. package/src/__tests__/runbook.test.ts +0 -219
  106. package/src/__tests__/schedule.test.ts +0 -206
  107. package/src/__tests__/serve.test.ts +0 -275
  108. package/src/__tests__/sessions.test.ts +0 -322
  109. package/src/__tests__/sharing.test.ts +0 -340
  110. package/src/__tests__/snapshots.test.ts +0 -581
  111. package/src/__tests__/standalone-migration.test.ts +0 -199
  112. package/src/__tests__/state-db.test.ts +0 -334
  113. package/src/__tests__/status.test.ts +0 -158
  114. package/src/__tests__/stream-with-tools.test.ts +0 -778
  115. package/src/__tests__/subagents.test.ts +0 -176
  116. package/src/__tests__/system-prompt.test.ts +0 -248
  117. package/src/__tests__/terminal-gap-v2.test.ts +0 -395
  118. package/src/__tests__/terminal-parity.test.ts +0 -393
  119. package/src/__tests__/tf-apply.test.ts +0 -187
  120. package/src/__tests__/tool-converter.test.ts +0 -256
  121. package/src/__tests__/tool-schemas.test.ts +0 -602
  122. package/src/__tests__/tools.test.ts +0 -144
  123. package/src/__tests__/version-json.test.ts +0 -184
  124. package/src/__tests__/version.test.ts +0 -49
  125. package/src/__tests__/watch.test.ts +0 -129
  126. package/src/agent/compaction-agent.ts +0 -266
  127. package/src/agent/context-manager.ts +0 -499
  128. package/src/agent/context.ts +0 -427
  129. package/src/agent/deploy-preview.ts +0 -487
  130. package/src/agent/expand-files.ts +0 -108
  131. package/src/agent/index.ts +0 -68
  132. package/src/agent/loop.ts +0 -1998
  133. package/src/agent/modes.ts +0 -429
  134. package/src/agent/permissions.ts +0 -513
  135. package/src/agent/subagents/base.ts +0 -116
  136. package/src/agent/subagents/cost.ts +0 -51
  137. package/src/agent/subagents/explore.ts +0 -42
  138. package/src/agent/subagents/general.ts +0 -54
  139. package/src/agent/subagents/index.ts +0 -102
  140. package/src/agent/subagents/infra.ts +0 -59
  141. package/src/agent/subagents/security.ts +0 -69
  142. package/src/agent/system-prompt.ts +0 -990
  143. package/src/app.ts +0 -180
  144. package/src/audit/activity-log.ts +0 -290
  145. package/src/audit/compliance-checker.ts +0 -540
  146. package/src/audit/cost-tracker.ts +0 -318
  147. package/src/audit/index.ts +0 -23
  148. package/src/audit/security-scanner.ts +0 -641
  149. package/src/auth/guard.ts +0 -75
  150. package/src/auth/index.ts +0 -56
  151. package/src/auth/keychain.ts +0 -82
  152. package/src/auth/oauth.ts +0 -465
  153. package/src/auth/providers.ts +0 -470
  154. package/src/auth/sso.ts +0 -113
  155. package/src/auth/store.ts +0 -505
  156. package/src/auth/types.ts +0 -187
  157. package/src/build.ts +0 -141
  158. package/src/cli/index.ts +0 -16
  159. package/src/cli/init.ts +0 -1227
  160. package/src/cli/openapi-spec.ts +0 -356
  161. package/src/cli/run.ts +0 -628
  162. package/src/cli/serve-auth.ts +0 -80
  163. package/src/cli/serve.ts +0 -539
  164. package/src/cli/web.ts +0 -71
  165. package/src/cli.ts +0 -1728
  166. package/src/clients/core-engine-client.ts +0 -227
  167. package/src/clients/enterprise-client.ts +0 -334
  168. package/src/clients/generator-client.ts +0 -351
  169. package/src/clients/git-client.ts +0 -627
  170. package/src/clients/github-client.ts +0 -410
  171. package/src/clients/helm-client.ts +0 -504
  172. package/src/clients/index.ts +0 -80
  173. package/src/clients/k8s-client.ts +0 -497
  174. package/src/clients/llm-client.ts +0 -161
  175. package/src/clients/rest-client.ts +0 -130
  176. package/src/clients/service-discovery.ts +0 -38
  177. package/src/clients/terraform-client.ts +0 -482
  178. package/src/clients/tools-client.ts +0 -1843
  179. package/src/clients/ws-client.ts +0 -115
  180. package/src/commands/alias.ts +0 -100
  181. package/src/commands/analyze/index.ts +0 -352
  182. package/src/commands/apply/helm.ts +0 -473
  183. package/src/commands/apply/index.ts +0 -213
  184. package/src/commands/apply/k8s.ts +0 -454
  185. package/src/commands/apply/terraform.ts +0 -582
  186. package/src/commands/ask.ts +0 -167
  187. package/src/commands/audit/index.ts +0 -357
  188. package/src/commands/auth-cloud.ts +0 -407
  189. package/src/commands/auth-list.ts +0 -134
  190. package/src/commands/auth-profile.ts +0 -121
  191. package/src/commands/auth-refresh.ts +0 -187
  192. package/src/commands/auth-status.ts +0 -141
  193. package/src/commands/aws/ec2.ts +0 -501
  194. package/src/commands/aws/iam.ts +0 -397
  195. package/src/commands/aws/index.ts +0 -133
  196. package/src/commands/aws/lambda.ts +0 -396
  197. package/src/commands/aws/rds.ts +0 -439
  198. package/src/commands/aws/s3.ts +0 -439
  199. package/src/commands/aws/vpc.ts +0 -393
  200. package/src/commands/aws-discover.ts +0 -542
  201. package/src/commands/aws-terraform.ts +0 -755
  202. package/src/commands/azure/aks.ts +0 -376
  203. package/src/commands/azure/functions.ts +0 -253
  204. package/src/commands/azure/index.ts +0 -116
  205. package/src/commands/azure/storage.ts +0 -478
  206. package/src/commands/azure/vm.ts +0 -355
  207. package/src/commands/billing/index.ts +0 -256
  208. package/src/commands/chat.ts +0 -320
  209. package/src/commands/completions.ts +0 -268
  210. package/src/commands/config.ts +0 -372
  211. package/src/commands/cost/cloud-cost-estimator.ts +0 -266
  212. package/src/commands/cost/estimator.ts +0 -79
  213. package/src/commands/cost/index.ts +0 -810
  214. package/src/commands/cost/parsers/terraform.ts +0 -273
  215. package/src/commands/cost/parsers/types.ts +0 -25
  216. package/src/commands/cost/pricing/aws.ts +0 -544
  217. package/src/commands/cost/pricing/azure.ts +0 -499
  218. package/src/commands/cost/pricing/gcp.ts +0 -396
  219. package/src/commands/cost/pricing/index.ts +0 -40
  220. package/src/commands/demo.ts +0 -250
  221. package/src/commands/deploy.ts +0 -260
  222. package/src/commands/doctor.ts +0 -1386
  223. package/src/commands/drift/index.ts +0 -787
  224. package/src/commands/explain.ts +0 -277
  225. package/src/commands/export.ts +0 -146
  226. package/src/commands/feedback.ts +0 -389
  227. package/src/commands/fix.ts +0 -324
  228. package/src/commands/fs/index.ts +0 -402
  229. package/src/commands/gcp/compute.ts +0 -325
  230. package/src/commands/gcp/functions.ts +0 -271
  231. package/src/commands/gcp/gke.ts +0 -438
  232. package/src/commands/gcp/iam.ts +0 -344
  233. package/src/commands/gcp/index.ts +0 -129
  234. package/src/commands/gcp/storage.ts +0 -284
  235. package/src/commands/generate-helm.ts +0 -1249
  236. package/src/commands/generate-k8s.ts +0 -1508
  237. package/src/commands/generate-terraform.ts +0 -1202
  238. package/src/commands/gh/index.ts +0 -863
  239. package/src/commands/git/index.ts +0 -1343
  240. package/src/commands/helm/index.ts +0 -1126
  241. package/src/commands/help.ts +0 -715
  242. package/src/commands/history.ts +0 -149
  243. package/src/commands/import.ts +0 -868
  244. package/src/commands/incident.ts +0 -166
  245. package/src/commands/index.ts +0 -367
  246. package/src/commands/init.ts +0 -1051
  247. package/src/commands/k8s/index.ts +0 -1137
  248. package/src/commands/login.ts +0 -716
  249. package/src/commands/logout.ts +0 -83
  250. package/src/commands/logs.ts +0 -167
  251. package/src/commands/onboarding.ts +0 -405
  252. package/src/commands/pipeline.ts +0 -186
  253. package/src/commands/plan/display.ts +0 -279
  254. package/src/commands/plan/index.ts +0 -599
  255. package/src/commands/plugin.ts +0 -398
  256. package/src/commands/preview.ts +0 -452
  257. package/src/commands/profile.ts +0 -342
  258. package/src/commands/questionnaire.ts +0 -1172
  259. package/src/commands/resume.ts +0 -47
  260. package/src/commands/rollback.ts +0 -315
  261. package/src/commands/rollout.ts +0 -88
  262. package/src/commands/runbook.ts +0 -346
  263. package/src/commands/schedule.ts +0 -236
  264. package/src/commands/status.ts +0 -252
  265. package/src/commands/team/index.ts +0 -346
  266. package/src/commands/team-context.ts +0 -220
  267. package/src/commands/template.ts +0 -233
  268. package/src/commands/tf/index.ts +0 -1093
  269. package/src/commands/upgrade.ts +0 -607
  270. package/src/commands/usage/index.ts +0 -134
  271. package/src/commands/version.ts +0 -174
  272. package/src/commands/watch.ts +0 -153
  273. package/src/compat/index.ts +0 -2
  274. package/src/compat/runtime.ts +0 -12
  275. package/src/compat/sqlite.ts +0 -177
  276. package/src/config/index.ts +0 -17
  277. package/src/config/manager.ts +0 -530
  278. package/src/config/mode-store.ts +0 -62
  279. package/src/config/profiles.ts +0 -84
  280. package/src/config/safety-policy.ts +0 -358
  281. package/src/config/schema.ts +0 -125
  282. package/src/config/types.ts +0 -609
  283. package/src/config/workspace-state.ts +0 -53
  284. package/src/context/context-db.ts +0 -199
  285. package/src/demo/index.ts +0 -349
  286. package/src/demo/scenarios/full-journey.ts +0 -229
  287. package/src/demo/scenarios/getting-started.ts +0 -127
  288. package/src/demo/scenarios/helm-release.ts +0 -341
  289. package/src/demo/scenarios/k8s-deployment.ts +0 -194
  290. package/src/demo/scenarios/terraform-vpc.ts +0 -170
  291. package/src/demo/types.ts +0 -92
  292. package/src/engine/cost-estimator.ts +0 -480
  293. package/src/engine/diagram-generator.ts +0 -256
  294. package/src/engine/drift-detector.ts +0 -902
  295. package/src/engine/executor.ts +0 -1066
  296. package/src/engine/index.ts +0 -76
  297. package/src/engine/orchestrator.ts +0 -636
  298. package/src/engine/planner.ts +0 -787
  299. package/src/engine/safety.ts +0 -743
  300. package/src/engine/verifier.ts +0 -770
  301. package/src/enterprise/audit.ts +0 -348
  302. package/src/enterprise/auth.ts +0 -270
  303. package/src/enterprise/billing.ts +0 -822
  304. package/src/enterprise/index.ts +0 -17
  305. package/src/enterprise/teams.ts +0 -443
  306. package/src/generator/best-practices.ts +0 -1608
  307. package/src/generator/helm.ts +0 -630
  308. package/src/generator/index.ts +0 -37
  309. package/src/generator/intent-parser.ts +0 -514
  310. package/src/generator/kubernetes.ts +0 -976
  311. package/src/generator/terraform.ts +0 -1875
  312. package/src/history/index.ts +0 -8
  313. package/src/history/manager.ts +0 -250
  314. package/src/history/types.ts +0 -34
  315. package/src/hooks/config.ts +0 -432
  316. package/src/hooks/engine.ts +0 -392
  317. package/src/hooks/index.ts +0 -4
  318. package/src/llm/auth-bridge.ts +0 -198
  319. package/src/llm/circuit-breaker.ts +0 -140
  320. package/src/llm/config-loader.ts +0 -201
  321. package/src/llm/cost-calculator.ts +0 -171
  322. package/src/llm/index.ts +0 -8
  323. package/src/llm/model-aliases.ts +0 -115
  324. package/src/llm/provider-registry.ts +0 -63
  325. package/src/llm/providers/anthropic.ts +0 -462
  326. package/src/llm/providers/bedrock.ts +0 -477
  327. package/src/llm/providers/google.ts +0 -405
  328. package/src/llm/providers/ollama.ts +0 -767
  329. package/src/llm/providers/openai-compatible.ts +0 -340
  330. package/src/llm/providers/openai.ts +0 -328
  331. package/src/llm/providers/openrouter.ts +0 -338
  332. package/src/llm/router.ts +0 -1104
  333. package/src/llm/types.ts +0 -232
  334. package/src/lsp/client.ts +0 -298
  335. package/src/lsp/languages.ts +0 -119
  336. package/src/lsp/manager.ts +0 -294
  337. package/src/mcp/client.ts +0 -402
  338. package/src/mcp/index.ts +0 -5
  339. package/src/mcp/manager.ts +0 -133
  340. package/src/nimbus.ts +0 -233
  341. package/src/plugins/index.ts +0 -27
  342. package/src/plugins/loader.ts +0 -334
  343. package/src/plugins/manager.ts +0 -376
  344. package/src/plugins/types.ts +0 -284
  345. package/src/scanners/cicd-scanner.ts +0 -258
  346. package/src/scanners/cloud-scanner.ts +0 -466
  347. package/src/scanners/framework-scanner.ts +0 -469
  348. package/src/scanners/iac-scanner.ts +0 -388
  349. package/src/scanners/index.ts +0 -539
  350. package/src/scanners/language-scanner.ts +0 -276
  351. package/src/scanners/package-manager-scanner.ts +0 -277
  352. package/src/scanners/types.ts +0 -172
  353. package/src/sessions/manager.ts +0 -472
  354. package/src/sessions/types.ts +0 -44
  355. package/src/sharing/sync.ts +0 -300
  356. package/src/sharing/viewer.ts +0 -163
  357. package/src/snapshots/index.ts +0 -2
  358. package/src/snapshots/manager.ts +0 -530
  359. package/src/state/artifacts.ts +0 -147
  360. package/src/state/audit.ts +0 -137
  361. package/src/state/billing.ts +0 -240
  362. package/src/state/checkpoints.ts +0 -117
  363. package/src/state/config.ts +0 -67
  364. package/src/state/conversations.ts +0 -14
  365. package/src/state/credentials.ts +0 -154
  366. package/src/state/db.ts +0 -58
  367. package/src/state/index.ts +0 -26
  368. package/src/state/messages.ts +0 -115
  369. package/src/state/projects.ts +0 -123
  370. package/src/state/schema.ts +0 -236
  371. package/src/state/sessions.ts +0 -147
  372. package/src/state/teams.ts +0 -200
  373. package/src/telemetry.ts +0 -108
  374. package/src/tools/aws-ops.ts +0 -952
  375. package/src/tools/azure-ops.ts +0 -579
  376. package/src/tools/file-ops.ts +0 -615
  377. package/src/tools/gcp-ops.ts +0 -625
  378. package/src/tools/git-ops.ts +0 -773
  379. package/src/tools/github-ops.ts +0 -799
  380. package/src/tools/helm-ops.ts +0 -943
  381. package/src/tools/index.ts +0 -17
  382. package/src/tools/k8s-ops.ts +0 -819
  383. package/src/tools/schemas/converter.ts +0 -184
  384. package/src/tools/schemas/devops.ts +0 -3502
  385. package/src/tools/schemas/index.ts +0 -73
  386. package/src/tools/schemas/standard.ts +0 -1148
  387. package/src/tools/schemas/types.ts +0 -735
  388. package/src/tools/spawn-exec.ts +0 -148
  389. package/src/tools/terraform-ops.ts +0 -862
  390. package/src/types/ambient.d.ts +0 -193
  391. package/src/types/config.ts +0 -83
  392. package/src/types/drift.ts +0 -116
  393. package/src/types/enterprise.ts +0 -335
  394. package/src/types/index.ts +0 -20
  395. package/src/types/plan.ts +0 -44
  396. package/src/types/request.ts +0 -65
  397. package/src/types/response.ts +0 -54
  398. package/src/types/service.ts +0 -51
  399. package/src/ui/App.tsx +0 -2114
  400. package/src/ui/DeployPreview.tsx +0 -174
  401. package/src/ui/FileDiffModal.tsx +0 -162
  402. package/src/ui/Header.tsx +0 -131
  403. package/src/ui/HelpModal.tsx +0 -57
  404. package/src/ui/InputBox.tsx +0 -503
  405. package/src/ui/MessageList.tsx +0 -1032
  406. package/src/ui/PermissionPrompt.tsx +0 -163
  407. package/src/ui/StatusBar.tsx +0 -277
  408. package/src/ui/TerminalPane.tsx +0 -84
  409. package/src/ui/ToolCallDisplay.tsx +0 -643
  410. package/src/ui/TreePane.tsx +0 -132
  411. package/src/ui/chat-ui.ts +0 -850
  412. package/src/ui/index.ts +0 -33
  413. package/src/ui/ink/index.ts +0 -1444
  414. package/src/ui/streaming.ts +0 -176
  415. package/src/ui/theme.ts +0 -104
  416. package/src/ui/types.ts +0 -75
  417. package/src/utils/analytics.ts +0 -72
  418. package/src/utils/cost-warning.ts +0 -27
  419. package/src/utils/env.ts +0 -46
  420. package/src/utils/errors.ts +0 -69
  421. package/src/utils/event-bus.ts +0 -38
  422. package/src/utils/index.ts +0 -24
  423. package/src/utils/logger.ts +0 -171
  424. package/src/utils/rate-limiter.ts +0 -121
  425. package/src/utils/service-auth.ts +0 -49
  426. package/src/utils/validation.ts +0 -53
  427. package/src/version.ts +0 -4
  428. package/src/watcher/index.ts +0 -214
  429. package/src/wizard/approval.ts +0 -383
  430. package/src/wizard/index.ts +0 -25
  431. package/src/wizard/prompts.ts +0 -338
  432. package/src/wizard/types.ts +0 -172
  433. package/src/wizard/ui.ts +0 -556
  434. package/src/wizard/wizard.ts +0 -304
  435. package/tsconfig.json +0 -24
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * AuthStore - Credential Persistence Manager
3
3
  * Manages storage and retrieval of authentication credentials at ~/.nimbus/auth.json
4
- * API keys and access tokens are encrypted at rest using AES-256-GCM.
4
+ * Credentials are stored as plain JSON with 0600 file permissions (industry standard).
5
5
  */
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
8
  import * as os from 'os';
9
9
  import * as crypto from 'crypto';
10
10
  const AUTH_FILE_VERSION = 1;
11
+ /** Legacy encryption prefix — used only for one-time migration of old files. */
12
+ const ENC_PREFIX = 'enc:';
11
13
  /**
12
14
  * Default empty auth file structure
13
15
  */
@@ -22,125 +24,78 @@ function createEmptyAuthFile() {
22
24
  };
23
25
  }
24
26
  // ---------------------------------------------------------------------------
25
- // Encryption constants and helpers (AES-256-GCM)
27
+ // Legacy decryption used ONLY during one-time migration of enc:-prefixed values.
28
+ // This function is intentionally kept minimal; it is removed from the hot path.
26
29
  // ---------------------------------------------------------------------------
27
- const ENCRYPTION_ALGORITHM = 'aes-256-gcm';
28
- const KEY_LENGTH = 32;
29
- const IV_LENGTH = 16;
30
- const AUTH_TAG_LENGTH = 16;
31
- const SALT = 'nimbus-auth-v1';
32
- const ENC_PREFIX = 'enc:';
33
- /**
34
- * Build a machine-specific fingerprint from hostname, homedir, and username.
35
- * This is not cryptographically perfect, but it prevents casual copy-paste of
36
- * the auth file between machines.
37
- */
38
- function getMachineFingerprint() {
39
- const hostname = os.hostname();
40
- const homedir = os.homedir();
41
- const username = os.userInfo().username;
42
- return `${hostname}${homedir}${username}`;
43
- }
44
- /**
45
- * Derive a 256-bit encryption key from the machine fingerprint using PBKDF2.
46
- */
47
- function deriveKey() {
48
- return crypto.pbkdf2Sync(getMachineFingerprint(), SALT, 100000, KEY_LENGTH, 'sha256');
49
- }
50
- /**
51
- * Encrypt a plaintext string with AES-256-GCM.
52
- * Returns a base64-encoded blob containing iv + authTag + ciphertext.
53
- */
54
- function encryptValue(plaintext) {
55
- try {
56
- const key = deriveKey();
57
- const iv = crypto.randomBytes(IV_LENGTH);
58
- const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, key, iv, {
59
- authTagLength: AUTH_TAG_LENGTH,
60
- });
61
- const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
62
- const authTag = cipher.getAuthTag();
63
- // Layout: iv (16) + authTag (16) + ciphertext (variable)
64
- const combined = Buffer.concat([iv, authTag, encrypted]);
65
- return ENC_PREFIX + combined.toString('base64');
66
- }
67
- catch {
68
- // On any encryption error, return the original value so the system
69
- // can continue to operate.
70
- return plaintext;
71
- }
30
+ const _LEGACY_ALGORITHM = 'aes-256-gcm';
31
+ const _LEGACY_KEY_LENGTH = 32;
32
+ const _LEGACY_IV_LENGTH = 16;
33
+ const _LEGACY_AUTH_TAG_LENGTH = 16;
34
+ const _LEGACY_SALT = 'nimbus-auth-v1';
35
+ function _legacyDeriveKey() {
36
+ const fingerprint = `${os.hostname()}${os.homedir()}${os.userInfo().username}`;
37
+ return crypto.pbkdf2Sync(fingerprint, _LEGACY_SALT, 100000, _LEGACY_KEY_LENGTH, 'sha256');
72
38
  }
73
39
  /**
74
- * Decrypt an encrypted value produced by encryptValue().
75
- * If decryption fails (e.g. wrong machine, corrupted data, or the value was
76
- * never encrypted), the original string is returned for backward compatibility.
40
+ * Attempt to decrypt a value that was encrypted with the old AES-256-GCM scheme.
41
+ * Returns null if decryption fails (wrong machine, corrupted, etc.).
77
42
  */
78
- function decryptValue(encrypted) {
43
+ function _legacyDecrypt(encrypted) {
79
44
  try {
80
- // Strip the enc: prefix
81
45
  const payload = encrypted.slice(ENC_PREFIX.length);
82
46
  const combined = Buffer.from(payload, 'base64');
83
- if (combined.length < IV_LENGTH + AUTH_TAG_LENGTH) {
84
- // Too short to be a valid encrypted payload -- return as-is
85
- return encrypted;
47
+ if (combined.length < _LEGACY_IV_LENGTH + _LEGACY_AUTH_TAG_LENGTH) {
48
+ return null;
86
49
  }
87
- const iv = combined.subarray(0, IV_LENGTH);
88
- const authTag = combined.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
89
- const ciphertext = combined.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
90
- const key = deriveKey();
91
- const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, key, iv, {
92
- authTagLength: AUTH_TAG_LENGTH,
50
+ const iv = combined.subarray(0, _LEGACY_IV_LENGTH);
51
+ const authTag = combined.subarray(_LEGACY_IV_LENGTH, _LEGACY_IV_LENGTH + _LEGACY_AUTH_TAG_LENGTH);
52
+ const ciphertext = combined.subarray(_LEGACY_IV_LENGTH + _LEGACY_AUTH_TAG_LENGTH);
53
+ const key = _legacyDeriveKey();
54
+ const decipher = crypto.createDecipheriv(_LEGACY_ALGORITHM, key, iv, {
55
+ authTagLength: _LEGACY_AUTH_TAG_LENGTH,
93
56
  });
94
57
  decipher.setAuthTag(authTag);
95
- const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
96
- return decrypted.toString('utf8');
58
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf8');
97
59
  }
98
60
  catch {
99
- // Decryption failed -- could be a plain-text value from before encryption
100
- // was introduced, or the file was moved between machines.
101
- return encrypted;
61
+ return null;
102
62
  }
103
63
  }
104
- // ---------------------------------------------------------------------------
105
- // Encryption helpers for the AuthFile structure
106
- // ---------------------------------------------------------------------------
107
- /**
108
- * Deep-clone the auth file and encrypt sensitive fields before persistence.
109
- */
110
- function encryptAuthFile(authFile) {
111
- const clone = JSON.parse(JSON.stringify(authFile));
112
- // Encrypt provider API keys
113
- for (const providerName of Object.keys(clone.providers)) {
114
- const cred = clone.providers[providerName];
115
- if (cred?.apiKey && cred.apiKey.length > 0 && !cred.apiKey.startsWith(ENC_PREFIX)) {
116
- cred.apiKey = encryptValue(cred.apiKey);
117
- }
118
- }
119
- // Encrypt GitHub access token
120
- if (clone.identity.github?.accessToken &&
121
- clone.identity.github.accessToken.length > 0 &&
122
- !clone.identity.github.accessToken.startsWith(ENC_PREFIX)) {
123
- clone.identity.github.accessToken = encryptValue(clone.identity.github.accessToken);
124
- }
125
- return clone;
126
- }
127
64
  /**
128
- * Decrypt sensitive fields in an auth file that was loaded from disk.
129
- * Plain-text values (from before encryption was added) pass through unchanged.
65
+ * One-time migration: if any sensitive fields in the loaded auth file still carry
66
+ * the old `enc:` prefix, attempt to decrypt them. On success the plain-text value
67
+ * is kept in memory and will be written back as plain JSON on the next save().
68
+ * On failure the field is cleared and a warning is printed.
130
69
  */
131
- function decryptAuthFile(authFile) {
132
- // Decrypt provider API keys
70
+ function migrateEncryptedFields(authFile) {
71
+ // Provider API keys
133
72
  for (const providerName of Object.keys(authFile.providers)) {
134
73
  const cred = authFile.providers[providerName];
135
- if (cred?.apiKey && cred.apiKey.startsWith(ENC_PREFIX)) {
136
- cred.apiKey = decryptValue(cred.apiKey);
74
+ if (cred?.apiKey?.startsWith(ENC_PREFIX)) {
75
+ const decrypted = _legacyDecrypt(cred.apiKey);
76
+ if (decrypted !== null) {
77
+ cred.apiKey = decrypted;
78
+ }
79
+ else {
80
+ process.stderr.write(`[nimbus] Warning: could not decrypt stored API key for provider "${providerName}". ` +
81
+ 'The key has been cleared — please run `nimbus login` to re-enter it.\n');
82
+ delete cred.apiKey;
83
+ }
137
84
  }
138
85
  }
139
- // Decrypt GitHub access token
86
+ // GitHub access token
140
87
  if (authFile.identity.github?.accessToken?.startsWith(ENC_PREFIX)) {
141
- authFile.identity.github.accessToken = decryptValue(authFile.identity.github.accessToken);
88
+ const decrypted = _legacyDecrypt(authFile.identity.github.accessToken);
89
+ if (decrypted !== null) {
90
+ authFile.identity.github.accessToken = decrypted;
91
+ }
92
+ else {
93
+ process.stderr.write('[nimbus] Warning: could not decrypt stored GitHub access token. ' +
94
+ 'The token has been cleared — please run `nimbus connect github` to re-authenticate.\n');
95
+ // Clear the github identity since accessToken is required (not optional in the type)
96
+ delete authFile.identity.github;
97
+ }
142
98
  }
143
- return authFile;
144
99
  }
145
100
  /**
146
101
  * AuthStore class for credential persistence
@@ -169,8 +124,8 @@ export class AuthStore {
169
124
  }
170
125
  /**
171
126
  * Load auth file from disk, creating if necessary.
172
- * Encrypted values are transparently decrypted so all public accessors
173
- * return plain-text credentials.
127
+ * Stored as plain JSON (industry standard: gh, terraform, AWS CLI all do this).
128
+ * Performs a one-time migration for legacy enc:-prefixed values from older versions.
174
129
  */
175
130
  load() {
176
131
  if (this.authFile) {
@@ -192,8 +147,19 @@ export class AuthStore {
192
147
  // Ensure required fields exist
193
148
  parsed.identity = parsed.identity || {};
194
149
  parsed.providers = parsed.providers || {};
195
- // Decrypt sensitive fields (backward-compatible with plain-text files)
196
- this.authFile = decryptAuthFile(parsed);
150
+ // One-time migration: decrypt any legacy enc:-prefixed fields and save back as plain text
151
+ const hasLegacyEncryption = Object.values(parsed.providers).some(c => c?.apiKey?.startsWith(ENC_PREFIX))
152
+ || parsed.identity.github?.accessToken?.startsWith(ENC_PREFIX);
153
+ if (hasLegacyEncryption) {
154
+ migrateEncryptedFields(parsed);
155
+ // Persist the decrypted values immediately so migration only runs once
156
+ this.authFile = parsed;
157
+ try {
158
+ this.save(parsed);
159
+ }
160
+ catch { /* non-critical — in-memory values are still correct */ }
161
+ }
162
+ this.authFile = parsed;
197
163
  return this.authFile;
198
164
  }
199
165
  catch {
@@ -204,8 +170,7 @@ export class AuthStore {
204
170
  }
205
171
  /**
206
172
  * Save auth file to disk with secure permissions (0600).
207
- * Sensitive fields are encrypted before writing so they are never stored
208
- * in plain text.
173
+ * Credentials are stored as plain JSON file permissions provide the security boundary.
209
174
  */
210
175
  save(authFile) {
211
176
  this.ensureDirectory();
@@ -215,9 +180,7 @@ export class AuthStore {
215
180
  }
216
181
  fileToSave.updatedAt = new Date().toISOString();
217
182
  this.authFile = fileToSave;
218
- // Encrypt sensitive fields in a deep clone before writing to disk
219
- const encrypted = encryptAuthFile(fileToSave);
220
- const content = JSON.stringify(encrypted, null, 2);
183
+ const content = JSON.stringify(fileToSave, null, 2);
221
184
  fs.writeFileSync(this.authPath, content, { mode: 0o600 });
222
185
  // Ensure permissions are set correctly even if file already existed
223
186
  fs.chmodSync(this.authPath, 0o600);
@@ -299,8 +299,9 @@ export async function discoverInfraContext(dir) {
299
299
  let match;
300
300
  while ((match = profileRegex.exec(awsConfig)) !== null) {
301
301
  const profileName = match[1];
302
- if (profileName === 'default')
303
- continue; // handled separately
302
+ if (profileName === 'default') {
303
+ continue;
304
+ } // handled separately
304
305
  // Extract region from the profile block
305
306
  const blockStart = match.index + match[0].length;
306
307
  const nextBlock = awsConfig.indexOf('\n[', blockStart);
@@ -308,8 +309,9 @@ export async function discoverInfraContext(dir) {
308
309
  const regionMatch = block.match(/^\s*region\s*=\s*(.+)$/m);
309
310
  profiles.push({ profile: profileName, region: regionMatch?.[1]?.trim() });
310
311
  }
311
- if (profiles.length > 0)
312
- ctx.awsProfiles = profiles.slice(0, 10); // max 10 profiles
312
+ if (profiles.length > 0) {
313
+ ctx.awsProfiles = profiles.slice(0, 10);
314
+ } // max 10 profiles
313
315
  }
314
316
  }
315
317
  catch { /* ignore */ }
@@ -318,8 +320,9 @@ export async function discoverInfraContext(dir) {
318
320
  const proj = execFileSync('gcloud', ['config', 'get-value', 'project'], {
319
321
  encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
320
322
  }).trim();
321
- if (proj && proj !== '(unset)')
323
+ if (proj && proj !== '(unset)') {
322
324
  ctx.gcpProject = proj;
325
+ }
323
326
  }
324
327
  catch { /* ignore */ }
325
328
  // G16: Count K8s namespaces and deployments
@@ -364,21 +367,27 @@ export async function discoverInfraContext(dir) {
364
367
  */
365
368
  export function formatInfraContext(ctx) {
366
369
  const parts = [];
367
- if (ctx.terraformWorkspace)
370
+ if (ctx.terraformWorkspace) {
368
371
  parts.push(`tf-workspace: ${ctx.terraformWorkspace}`);
369
- if (ctx.kubectlContext)
372
+ }
373
+ if (ctx.kubectlContext) {
370
374
  parts.push(`k8s-context: ${ctx.kubectlContext}`);
375
+ }
371
376
  if (ctx.helmReleases && ctx.helmReleases.length > 0) {
372
377
  parts.push(`helm-releases: ${ctx.helmReleases.slice(0, 3).join(', ')}${ctx.helmReleases.length > 3 ? ` +${ctx.helmReleases.length - 3} more` : ''}`);
373
378
  }
374
- if (ctx.awsAccount)
379
+ if (ctx.awsAccount) {
375
380
  parts.push(`aws-account: ${ctx.awsAccount}`);
376
- if (ctx.awsRegion)
381
+ }
382
+ if (ctx.awsRegion) {
377
383
  parts.push(`aws-region: ${ctx.awsRegion}`);
378
- if (ctx.gcpProject)
384
+ }
385
+ if (ctx.gcpProject) {
379
386
  parts.push(`gcp-project: ${ctx.gcpProject}`);
380
- if (ctx.cicdPipeline)
387
+ }
388
+ if (ctx.cicdPipeline) {
381
389
  parts.push(`cicd: ${ctx.cicdPipeline}`);
390
+ }
382
391
  return parts.length > 0 ? parts.join(' | ') : 'no infra context detected';
383
392
  }
384
393
  /**
@@ -730,7 +739,7 @@ export function generateNimbusMd(detection, _dir, infraCtx) {
730
739
  }
731
740
  }
732
741
  const runbookSection = foundRunbooks.length > 0
733
- ? foundRunbooks.join('\n') + '\n\nRefer to these runbooks for incident response and operational procedures.'
742
+ ? `${foundRunbooks.join('\n')}\n\nRefer to these runbooks for incident response and operational procedures.`
734
743
  : '<!-- Add runbook references, e.g.:\n- docs/runbooks/cert-rotation.md -->';
735
744
  lines.push('## Runbooks');
736
745
  lines.push('');
@@ -884,10 +893,12 @@ export async function runInit(options) {
884
893
  for (let idx = 0; idx < maxLen; idx++) {
885
894
  const o = oldLines[idx];
886
895
  const n = newLines[idx];
887
- if (o === undefined)
896
+ if (o === undefined) {
888
897
  diffLines.push(`+ ${n}`);
889
- else if (n === undefined)
898
+ }
899
+ else if (n === undefined) {
890
900
  diffLines.push(`- ${o}`);
901
+ }
891
902
  else if (o !== n) {
892
903
  diffLines.push(`- ${o}`);
893
904
  diffLines.push(`+ ${n}`);
@@ -896,15 +907,19 @@ export async function runInit(options) {
896
907
  if (diffLines.length > 0) {
897
908
  log('\nNIMBUS.md changes:');
898
909
  for (const dl of diffLines.slice(0, 50)) {
899
- if (dl.startsWith('+'))
910
+ if (dl.startsWith('+')) {
900
911
  log(` \x1b[32m${dl}\x1b[0m`);
901
- else if (dl.startsWith('-'))
912
+ }
913
+ else if (dl.startsWith('-')) {
902
914
  log(` \x1b[31m${dl}\x1b[0m`);
903
- else
915
+ }
916
+ else {
904
917
  log(` ${dl}`);
918
+ }
905
919
  }
906
- if (diffLines.length > 50)
920
+ if (diffLines.length > 50) {
907
921
  log(` ... and ${diffLines.length - 50} more changes`);
922
+ }
908
923
  log('');
909
924
  log('Apply these changes? [y/N]');
910
925
  // Synchronous readline for non-quiet mode
@@ -1006,8 +1021,9 @@ export async function runInit(options) {
1006
1021
  for (const sub of subdirs.slice(0, 20)) {
1007
1022
  const subPath = path.join(dir, sub);
1008
1023
  const hasTf = fs.readdirSync(subPath).some(f => f.endsWith('.tf'));
1009
- if (hasTf)
1024
+ if (hasTf) {
1010
1025
  tfRoots.push(sub);
1026
+ }
1011
1027
  }
1012
1028
  if (tfRoots.length > 1) {
1013
1029
  monorepoSection = `\n## Terraform Modules (Monorepo)\n\nThis is a monorepo with multiple Terraform roots:\n${tfRoots.map(r => `- \`./${r}/\``).join('\n')}\n\nTo target a specific root, \`cd\` into the directory or specify the path.\n`;
@@ -153,7 +153,7 @@ export async function executeRun(router, options) {
153
153
  },
154
154
  required: ['success', 'output', 'cost', 'turns', 'toolCalls', 'errors'],
155
155
  };
156
- process.stdout.write(JSON.stringify(schema, null, 2) + '\n');
156
+ process.stdout.write(`${JSON.stringify(schema, null, 2)}\n`);
157
157
  return {
158
158
  success: true,
159
159
  output: '',
@@ -164,12 +164,15 @@ export async function executeRun(router, options) {
164
164
  };
165
165
  }
166
166
  // H3: Inject context/workspace/namespace into environment before agent loop
167
- if (options.context)
167
+ if (options.context) {
168
168
  process.env.KUBECTL_CONTEXT = options.context;
169
- if (options.workspace)
169
+ }
170
+ if (options.workspace) {
170
171
  process.env.TF_WORKSPACE = options.workspace;
171
- if (options.namespace)
172
+ }
173
+ if (options.namespace) {
172
174
  process.env.K8S_NAMESPACE = options.namespace;
175
+ }
173
176
  // Get prompt from stdin if requested
174
177
  let prompt = options.prompt;
175
178
  if (options.stdin && !prompt) {
@@ -178,17 +181,21 @@ export async function executeRun(router, options) {
178
181
  if (options.stdinJson && stdinContent) {
179
182
  try {
180
183
  const config = JSON.parse(stdinContent);
181
- if (typeof config.prompt === 'string')
184
+ if (typeof config.prompt === 'string') {
182
185
  prompt = config.prompt;
186
+ }
183
187
  if (config.mode === 'plan' || config.mode === 'build' || config.mode === 'deploy') {
184
188
  options = { ...options, mode: config.mode };
185
189
  }
186
- if (typeof config.model === 'string')
190
+ if (typeof config.model === 'string') {
187
191
  options = { ...options, model: config.model };
188
- if (typeof config.autoApprove === 'boolean')
192
+ }
193
+ if (typeof config.autoApprove === 'boolean') {
189
194
  options = { ...options, autoApprove: config.autoApprove };
190
- if (typeof config.maxTurns === 'number')
195
+ }
196
+ if (typeof config.maxTurns === 'number') {
191
197
  options = { ...options, maxTurns: config.maxTurns };
198
+ }
192
199
  }
193
200
  catch {
194
201
  // If JSON parse fails, treat stdin as raw prompt text
@@ -406,8 +413,9 @@ export async function executeRun(router, options) {
406
413
  console.log(divider);
407
414
  console.log('');
408
415
  // Also print final text output
409
- if (output)
410
- process.stdout.write(output + '\n');
416
+ if (output) {
417
+ process.stdout.write(`${output}\n`);
418
+ }
411
419
  }
412
420
  const runResult = {
413
421
  success: !result.interrupted,
@@ -191,10 +191,12 @@ export async function serveCommand(options) {
191
191
  const nimbusDir = join(homedir(), '.nimbus');
192
192
  mkdirSync(nimbusDir, { recursive: true });
193
193
  const childArgs = [process.argv[1], 'serve', '--port', String(port)];
194
- if (options.host)
194
+ if (options.host) {
195
195
  childArgs.push('--host', options.host);
196
- if (options.auth)
196
+ }
197
+ if (options.auth) {
197
198
  childArgs.push('--auth', options.auth);
199
+ }
198
200
  const child = spawn(process.execPath, childArgs, {
199
201
  detached: true,
200
202
  stdio: 'ignore',
package/dist/src/cli.js CHANGED
@@ -94,7 +94,7 @@ async function showSubcommandHelp(command, _args) {
94
94
  };
95
95
  const help = COMMAND_HELP[command];
96
96
  if (help) {
97
- process.stdout.write(help + '\n');
97
+ process.stdout.write(`${help}\n`);
98
98
  }
99
99
  else {
100
100
  await helpCommand({});
@@ -250,6 +250,16 @@ export async function runCommand(args) {
250
250
  await onboardingCommand({});
251
251
  return;
252
252
  }
253
+ // nimbus connect <provider>
254
+ if (command === 'connect') {
255
+ if (subcommand === 'github') {
256
+ const { connectGitHubCommand } = await import('./commands/connect-github');
257
+ await connectGitHubCommand();
258
+ return;
259
+ }
260
+ process.stderr.write(`Unknown connect target: ${subcommand || '(none)'}. Supported: nimbus connect github\n`);
261
+ process.exit(1);
262
+ }
253
263
  // nimbus version
254
264
  if (command === 'version' || command === '-v' || command === '--version') {
255
265
  const options = {};
@@ -499,6 +509,27 @@ export async function runCommand(args) {
499
509
  else if (arg === '--mock') {
500
510
  options.mock = true;
501
511
  }
512
+ else if (arg === '--yes' || arg === '-y') {
513
+ options.yes = true;
514
+ }
515
+ else if (arg === '--provider' && args[i + 1]) {
516
+ options.provider = args[++i];
517
+ }
518
+ else if (arg === '--gcp-project' && args[i + 1]) {
519
+ options.gcpProject = args[++i];
520
+ }
521
+ else if (arg === '--azure-subscription' && args[i + 1]) {
522
+ options.azureSubscription = args[++i];
523
+ }
524
+ else if (arg === '--json') {
525
+ options.jsonOutput = true;
526
+ }
527
+ else if (arg === '--questionnaire') {
528
+ options.questionnaire = true;
529
+ }
530
+ else if (arg === '--conversational') {
531
+ options.conversational = true;
532
+ }
502
533
  }
503
534
  await generateTerraformCommand(options);
504
535
  return;
@@ -1393,8 +1424,9 @@ export async function runCommand(args) {
1393
1424
  if (command === 'status') {
1394
1425
  const statusOptions = {};
1395
1426
  for (let i = 1; i < args.length; i++) {
1396
- if (args[i] === '--json')
1427
+ if (args[i] === '--json') {
1397
1428
  statusOptions.json = true;
1429
+ }
1398
1430
  }
1399
1431
  await statusCommand(statusOptions);
1400
1432
  return;
@@ -1403,8 +1435,9 @@ export async function runCommand(args) {
1403
1435
  if (command === 'context') {
1404
1436
  const contextOptions = { verbose: true };
1405
1437
  for (let i = 1; i < args.length; i++) {
1406
- if (args[i] === '--json')
1438
+ if (args[i] === '--json') {
1407
1439
  contextOptions.json = true;
1440
+ }
1408
1441
  }
1409
1442
  await statusCommand(contextOptions);
1410
1443
  return;
@@ -1419,10 +1452,12 @@ export async function runCommand(args) {
1419
1452
  const rolloutOptions = { deployment };
1420
1453
  for (let i = 1; i < args.length; i++) {
1421
1454
  const arg = args[i];
1422
- if ((arg === '--namespace' || arg === '-n') && args[i + 1])
1455
+ if ((arg === '--namespace' || arg === '-n') && args[i + 1]) {
1423
1456
  rolloutOptions.namespace = args[++i];
1424
- else if (arg === '--timeout' && args[i + 1])
1457
+ }
1458
+ else if (arg === '--timeout' && args[i + 1]) {
1425
1459
  rolloutOptions.timeout = args[++i];
1460
+ }
1426
1461
  }
1427
1462
  await rolloutCommand(rolloutOptions);
1428
1463
  return;
@@ -1432,18 +1467,24 @@ export async function runCommand(args) {
1432
1467
  const rollbackOptions = {};
1433
1468
  for (let i = 1; i < args.length; i++) {
1434
1469
  const arg = args[i];
1435
- if (arg === '--helm' && args[i + 1])
1470
+ if (arg === '--helm' && args[i + 1]) {
1436
1471
  rollbackOptions.helm = args[++i];
1437
- else if (arg === '--k8s' && args[i + 1])
1472
+ }
1473
+ else if (arg === '--k8s' && args[i + 1]) {
1438
1474
  rollbackOptions.k8s = args[++i];
1439
- else if (arg === '--namespace' && args[i + 1])
1475
+ }
1476
+ else if (arg === '--namespace' && args[i + 1]) {
1440
1477
  rollbackOptions.namespace = args[++i];
1441
- else if (arg === '--tf')
1478
+ }
1479
+ else if (arg === '--tf') {
1442
1480
  rollbackOptions.tf = true;
1443
- else if (arg === '--terraform')
1481
+ }
1482
+ else if (arg === '--terraform') {
1444
1483
  rollbackOptions.terraform = true;
1445
- else if (arg === '--tf-dir' && args[i + 1])
1484
+ }
1485
+ else if (arg === '--tf-dir' && args[i + 1]) {
1446
1486
  rollbackOptions.tfDir = args[++i];
1487
+ }
1447
1488
  }
1448
1489
  await rollbackCommand(rollbackOptions);
1449
1490
  return;
@@ -31,12 +31,14 @@ function saveAliases(aliases) {
31
31
  * Returns the original args unchanged if no alias matches.
32
32
  */
33
33
  export function resolveAlias(args) {
34
- if (!args.length)
34
+ if (!args.length) {
35
35
  return args;
36
+ }
36
37
  const aliases = loadAliases();
37
38
  const expanded = aliases[args[0]];
38
- if (!expanded)
39
+ if (!expanded) {
39
40
  return args;
41
+ }
40
42
  // Split the alias value on spaces and prepend to remaining args
41
43
  return [...expanded.split(' '), ...args.slice(1)];
42
44
  }
@@ -73,7 +75,7 @@ export async function aliasCommand(subcommand, args) {
73
75
  return;
74
76
  }
75
77
  // Create alias: subcommand is "<name>=<rest>" or subcommand is the name and args hold the expansion
76
- const raw = subcommand + (args.length ? ' ' + args.join(' ') : '');
78
+ const raw = subcommand + (args.length ? ` ${args.join(' ')}` : '');
77
79
  const eqIdx = raw.indexOf('=');
78
80
  if (eqIdx === -1) {
79
81
  ui.error('Usage: nimbus alias <name>=<command> or nimbus alias list or nimbus alias remove <name>');
@@ -252,8 +252,9 @@ export async function auditScanCommand(options) {
252
252
  ? 'yellow'
253
253
  : 'white';
254
254
  ui.print(`[${ui.color(finding.severity, severityColor)}] ${finding.id}: ${finding.title}`);
255
- if (finding.file)
255
+ if (finding.file) {
256
256
  ui.dim(` File: ${finding.file}${finding.line ? `:${finding.line}` : ''}`);
257
+ }
257
258
  ui.dim(` ${finding.recommendation}`);
258
259
  ui.newLine();
259
260
  }