@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
@@ -94,21 +94,27 @@ export function formatKubectlPodsOutput(raw) {
94
94
  export function formatHelmListOutput(raw) {
95
95
  try {
96
96
  const releases = JSON.parse(raw);
97
- if (!Array.isArray(releases) || releases.length === 0)
97
+ if (!Array.isArray(releases) || releases.length === 0) {
98
98
  return 'No Helm releases found.';
99
+ }
99
100
  const lines = releases.map(r => {
100
101
  let emoji;
101
102
  const s = r.status?.toLowerCase() ?? '';
102
- if (s === 'deployed')
103
+ if (s === 'deployed') {
103
104
  emoji = '[OK]';
104
- else if (s === 'pending-install' || s === 'pending-upgrade')
105
+ }
106
+ else if (s === 'pending-install' || s === 'pending-upgrade') {
105
107
  emoji = '[!!]';
106
- else if (s === 'failed')
108
+ }
109
+ else if (s === 'failed') {
107
110
  emoji = '[XX]';
108
- else if (s === 'superseded')
111
+ }
112
+ else if (s === 'superseded') {
109
113
  emoji = '[~~]';
110
- else
114
+ }
115
+ else {
111
116
  emoji = ' ';
117
+ }
112
118
  return `${emoji} ${r.name} (${r.namespace}) — ${r.chart} rev.${r.revision} [${r.status}]`;
113
119
  });
114
120
  return lines.join('\n');
@@ -198,51 +204,57 @@ export const terraformTool = {
198
204
  // Build the terraform command
199
205
  let command;
200
206
  if (input.action === 'state-list') {
201
- command = `terraform -chdir=${input.workdir} state list${input.args ? ' ' + input.args : ''}`;
207
+ command = `terraform -chdir=${input.workdir} state list${input.args ? ` ${input.args}` : ''}`;
202
208
  }
203
209
  else if (input.action === 'state-show') {
204
- if (!input.state_address)
210
+ if (!input.state_address) {
205
211
  return err('state-show requires state_address');
212
+ }
206
213
  command = `terraform -chdir=${input.workdir} state show "${input.state_address}"`;
207
214
  }
208
215
  else if (input.action === 'state-rm') {
209
- if (!input.state_address)
216
+ if (!input.state_address) {
210
217
  return err('state-rm requires state_address');
218
+ }
211
219
  command = `terraform -chdir=${input.workdir} state rm "${input.state_address}"`;
212
220
  }
213
221
  else if (input.action === 'state-mv') {
214
- if (!input.state_address)
222
+ if (!input.state_address) {
215
223
  return err('state-mv requires state_address (format: "source dest")');
224
+ }
216
225
  command = `terraform -chdir=${input.workdir} state mv ${input.state_address}`;
217
226
  }
218
227
  else if (input.action === 'state') {
219
- command = `terraform -chdir=${input.workdir} state${input.args ? ' ' + input.args : ' list'}`;
228
+ command = `terraform -chdir=${input.workdir} state${input.args ? ` ${input.args}` : ' list'}`;
220
229
  }
221
230
  else if (input.action === 'output') {
222
- command = `terraform -chdir=${input.workdir} output -json${input.output_name ? ' ' + input.output_name : ''}`;
231
+ command = `terraform -chdir=${input.workdir} output -json${input.output_name ? ` ${input.output_name}` : ''}`;
223
232
  }
224
233
  else if (input.action === 'workspace-list') {
225
234
  command = `terraform -chdir=${input.workdir} workspace list`;
226
235
  }
227
236
  else if (input.action === 'workspace-select') {
228
- if (!input.workspace)
237
+ if (!input.workspace) {
229
238
  return err('workspace-select requires workspace name');
239
+ }
230
240
  command = `terraform -chdir=${input.workdir} workspace select "${input.workspace}"`;
231
241
  }
232
242
  else if (input.action === 'workspace-new') {
233
- if (!input.workspace)
243
+ if (!input.workspace) {
234
244
  return err('workspace-new requires workspace name');
245
+ }
235
246
  command = `terraform -chdir=${input.workdir} workspace new "${input.workspace}"`;
236
247
  }
237
248
  else if (input.action === 'providers') {
238
249
  command = `terraform -chdir=${input.workdir} providers`;
239
250
  }
240
251
  else if (input.action === 'graph') {
241
- command = `terraform -chdir=${input.workdir} graph${input.args ? ' ' + input.args : ''}`;
252
+ command = `terraform -chdir=${input.workdir} graph${input.args ? ` ${input.args}` : ''}`;
242
253
  }
243
254
  else if (input.action === 'force-unlock') {
244
- if (!input.lock_id)
255
+ if (!input.lock_id) {
245
256
  return err('force-unlock requires lock_id');
257
+ }
246
258
  command = `terraform -chdir=${input.workdir} force-unlock -force "${input.lock_id}"`;
247
259
  }
248
260
  else {
@@ -272,8 +284,9 @@ export const terraformTool = {
272
284
  // Replace the apply command with one that uses the plan file
273
285
  // Remove the -auto-approve flag since plan files don't need it
274
286
  const applyIdx = parts.indexOf('-auto-approve');
275
- if (applyIdx !== -1)
287
+ if (applyIdx !== -1) {
276
288
  parts.splice(applyIdx, 1);
289
+ }
277
290
  parts.push(planFile);
278
291
  }
279
292
  }
@@ -381,62 +394,80 @@ export const kubectlTool = {
381
394
  // Special handling for new actions
382
395
  if (input.action === 'patch') {
383
396
  const patchType = input.patch_type ?? 'strategic';
384
- if (!input.patch)
397
+ if (!input.patch) {
385
398
  return err('patch action requires patch field with JSON patch string');
386
- if (input.resource)
399
+ }
400
+ if (input.resource) {
387
401
  parts.push(input.resource);
388
- if (input.namespace)
402
+ }
403
+ if (input.namespace) {
389
404
  parts.push('-n', input.namespace);
405
+ }
390
406
  parts.push(`--type=${patchType}`);
391
407
  parts.push('-p', `'${input.patch}'`);
392
408
  }
393
409
  else if (input.action === 'port-forward') {
394
- if (input.resource)
410
+ if (input.resource) {
395
411
  parts.push(input.resource);
396
- if (input.namespace)
412
+ }
413
+ if (input.namespace) {
397
414
  parts.push('-n', input.namespace);
398
- if (input.args)
415
+ }
416
+ if (input.args) {
399
417
  parts.push(input.args);
418
+ }
400
419
  }
401
420
  else if (input.action === 'cp') {
402
421
  if (input.local_path && input.container_path) {
403
422
  parts.push(input.local_path, input.container_path);
404
423
  }
405
424
  else {
406
- if (input.args)
425
+ if (input.args) {
407
426
  parts.push(input.args);
427
+ }
408
428
  }
409
429
  }
410
430
  else if (input.action === 'top') {
411
- if (input.resource)
431
+ if (input.resource) {
412
432
  parts.push(input.resource);
413
- if (input.namespace)
433
+ }
434
+ if (input.namespace) {
414
435
  parts.push('-n', input.namespace);
415
- if (input.args)
436
+ }
437
+ if (input.args) {
416
438
  parts.push(input.args);
439
+ }
417
440
  }
418
441
  else if (input.action === 'cordon' || input.action === 'taint') {
419
- if (input.resource)
442
+ if (input.resource) {
420
443
  parts.push(input.resource);
421
- if (input.args)
444
+ }
445
+ if (input.args) {
422
446
  parts.push(input.args);
447
+ }
423
448
  }
424
449
  else if (input.action === 'drain') {
425
- if (input.resource)
450
+ if (input.resource) {
426
451
  parts.push(input.resource);
452
+ }
427
453
  parts.push('--ignore-daemonsets', '--delete-emptydir-data');
428
- if (input.args)
454
+ if (input.args) {
429
455
  parts.push(input.args);
456
+ }
430
457
  }
431
458
  else if (input.action === 'wait') {
432
- if (input.resource)
459
+ if (input.resource) {
433
460
  parts.push(input.resource);
434
- if (input.namespace)
461
+ }
462
+ if (input.namespace) {
435
463
  parts.push('-n', input.namespace);
436
- if (input.args)
464
+ }
465
+ if (input.args) {
437
466
  parts.push(input.args);
438
- else
467
+ }
468
+ else {
439
469
  parts.push('--for=condition=Ready', '--timeout=120s');
470
+ }
440
471
  }
441
472
  else if (input.action === 'diff') {
442
473
  // G12: kubectl diff — exit code 1 means diffs exist (not an error)
@@ -450,8 +481,9 @@ export const kubectlTool = {
450
481
  catch (diffErr) {
451
482
  const execError = diffErr;
452
483
  // Exit code 1 with stdout = normal diff output (changes detected)
453
- if (execError.code === 1 && execError.stdout)
484
+ if (execError.code === 1 && execError.stdout) {
454
485
  return ok(execError.stdout.trim());
486
+ }
455
487
  return err(errorMessage(diffErr));
456
488
  }
457
489
  }
@@ -477,8 +509,9 @@ export const kubectlTool = {
477
509
  const timeoutMs = ctx?.timeout ?? defaultKubectlTimeoutMs; // GAP-20: per-tool timeout from NIMBUS.md
478
510
  const result = await spawnExec(command, { onChunk: ctx.onProgress, timeout: timeoutMs });
479
511
  const combined = [result.stdout, result.stderr].filter(Boolean).join('\n');
480
- if (result.exitCode !== 0)
512
+ if (result.exitCode !== 0) {
481
513
  return err(`kubectl command failed:\n${combined}`);
514
+ }
482
515
  return ok(combined || '(no output)');
483
516
  }
484
517
  const cmdEnv = { ...process.env, ...(input.env ?? {}) };
@@ -537,8 +570,9 @@ export const helmTool = {
537
570
  // M5: Helm secrets plugin actions (SOPS-encrypted values)
538
571
  if (input.action === 'secrets-encrypt' || input.action === 'secrets-decrypt' || input.action === 'secrets-view') {
539
572
  const file = input.values;
540
- if (!file)
573
+ if (!file) {
541
574
  return err('helm secrets requires a values file path (values field)');
575
+ }
542
576
  const secretsAction = input.action.replace('secrets-', '');
543
577
  const command = `helm secrets ${secretsAction} ${file}`;
544
578
  const { stdout, stderr } = await execAsync(command, {
@@ -549,23 +583,26 @@ export const helmTool = {
549
583
  }
550
584
  // New introspection/repo actions
551
585
  if (['get-values', 'get-manifest', 'get-all', 'get-hooks'].includes(input.action)) {
552
- if (!input.release)
586
+ if (!input.release) {
553
587
  return err(`${input.action} requires a release name`);
588
+ }
554
589
  const subCmd = input.action.replace('get-', 'get ');
555
590
  const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
556
591
  const { stdout: getOut, stderr: getErr } = await execAsync(`helm ${subCmd} ${input.release}${nsFlag}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
557
592
  return ok([getOut, getErr].filter(Boolean).join('\n') || '(no output)');
558
593
  }
559
594
  if (input.action === 'status') {
560
- if (!input.release)
595
+ if (!input.release) {
561
596
  return err('status requires a release name');
597
+ }
562
598
  const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
563
599
  const { stdout: statusOut, stderr: statusErr } = await execAsync(`helm status ${input.release}${nsFlag}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
564
600
  return ok([statusOut, statusErr].filter(Boolean).join('\n') || '(no output)');
565
601
  }
566
602
  if (input.action === 'history') {
567
- if (!input.release)
603
+ if (!input.release) {
568
604
  return err('history requires a release name');
605
+ }
569
606
  const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
570
607
  try {
571
608
  const { stdout: histOut } = await execAsync(`helm history ${input.release}${nsFlag} --max 10 --output json`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
@@ -579,15 +616,17 @@ export const helmTool = {
579
616
  }
580
617
  }
581
618
  if (input.action === 'test') {
582
- if (!input.release)
619
+ if (!input.release) {
583
620
  return err('test requires a release name');
621
+ }
584
622
  const nsFlag = input.namespace ? ` -n ${input.namespace}` : '';
585
623
  const { stdout: testOut, stderr: testErr } = await execAsync(`helm test ${input.release}${nsFlag}`, { timeout: 120_000, maxBuffer: 5 * 1024 * 1024 });
586
624
  return ok([testOut, testErr].filter(Boolean).join('\n') || '(no output)');
587
625
  }
588
626
  if (input.action === 'repo-add') {
589
- if (!input.repo_name || !input.repo_url)
627
+ if (!input.repo_name || !input.repo_url) {
590
628
  return err('repo-add requires repo_name and repo_url');
629
+ }
591
630
  const { stdout: raOut, stderr: raErr } = await execAsync(`helm repo add ${input.repo_name} ${input.repo_url}`, { timeout: 30_000, maxBuffer: 1 * 1024 * 1024 });
592
631
  return ok([raOut, raErr].filter(Boolean).join('\n') || '(no output)');
593
632
  }
@@ -601,15 +640,17 @@ export const helmTool = {
601
640
  }
602
641
  if (input.action === 'search-repo') {
603
642
  const query = input.chart ?? input.release ?? '';
604
- if (!query)
643
+ if (!query) {
605
644
  return err('search-repo requires chart or release field as search term');
645
+ }
606
646
  const { stdout: srOut, stderr: srErr } = await execAsync(`helm search repo ${query}`, { timeout: 30_000, maxBuffer: 2 * 1024 * 1024 });
607
647
  return ok([srOut, srErr].filter(Boolean).join('\n') || '(no results)');
608
648
  }
609
649
  if (input.action === 'show-chart' || input.action === 'show-values') {
610
650
  const target = input.chart ?? input.release;
611
- if (!target)
651
+ if (!target) {
612
652
  return err(`${input.action} requires chart or release field`);
653
+ }
613
654
  const subCmd = input.action === 'show-chart' ? 'chart' : 'values';
614
655
  const { stdout: showOut, stderr: showErr } = await execAsync(`helm show ${subCmd} ${target}`, { timeout: 30_000, maxBuffer: 5 * 1024 * 1024 });
615
656
  return ok([showOut, showErr].filter(Boolean).join('\n') || '(no output)');
@@ -786,20 +827,22 @@ export const cloudDiscoverTool = {
786
827
  const publicIp = item.PublicIpAddress ?? '';
787
828
  const privateIp = item.PrivateIpAddress ?? '';
788
829
  const sgs = item.SecurityGroups ?? [];
789
- if (sgs.length > 0)
830
+ if (sgs.length > 0) {
790
831
  securityFlags.push('check-sg-rules');
832
+ }
791
833
  const flagStr = securityFlags.length > 0 ? ` [${securityFlags.join(', ')}]` : '';
792
834
  return ` - EC2: ${name} (${item.InstanceType ?? ''}) ${state}${az ? ` [${az}]` : ''}${publicIp ? ` pub:${publicIp}` : ''}${privateIp ? ` priv:${privateIp}` : ''}${flagStr}`;
793
835
  }
794
836
  // RDS formatter
795
837
  if (item.DBInstanceIdentifier) {
796
838
  const id = item.DBInstanceIdentifier;
797
- const engine = `${item.Engine ?? ''}${item.EngineVersion ? ' ' + item.EngineVersion : ''}`;
839
+ const engine = `${item.Engine ?? ''}${item.EngineVersion ? ` ${item.EngineVersion}` : ''}`;
798
840
  const status = item.DBInstanceStatus ?? '';
799
841
  const multiAz = item.MultiAZ ? 'Multi-AZ' : 'Single-AZ';
800
842
  const endpoint = item.Endpoint?.Address ?? '';
801
- if (!item.StorageEncrypted)
843
+ if (!item.StorageEncrypted) {
802
844
  securityFlags.push('unencrypted');
845
+ }
803
846
  const flagStr = securityFlags.length > 0 ? ` [${securityFlags.join(', ')}]` : '';
804
847
  return ` - RDS: ${id} (${engine}) ${status} ${multiAz}${endpoint ? ` -> ${endpoint}` : ''}${flagStr}`;
805
848
  }
@@ -849,8 +892,7 @@ export const cloudDiscoverTool = {
849
892
  const region = item.Placement?.AvailabilityZone || item.DBInstanceArn?.split(':')[3] || item.region || '';
850
893
  return ` - ${name}${type ? ` (${type})` : ''}${region ? ` [${region}]` : ''}`;
851
894
  });
852
- return ok(`Found ${items.length} resource(s):\n${summary.join('\n')}` +
853
- (items.length > 50 ? `\n\n[+${items.length - 50} more — use specific region/filter to narrow]` : ''));
895
+ return ok(`Found ${items.length} resource(s):\n${summary.join('\n')}${items.length > 50 ? `\n\n[+${items.length - 50} more — use specific region/filter to narrow]` : ''}`);
854
896
  }
855
897
  catch {
856
898
  // Not JSON or failed to parse — return raw output truncated
@@ -994,8 +1036,9 @@ export const costEstimateTool = {
994
1036
  }
995
1037
  if (input.target === 'lambda') {
996
1038
  const fn = input.function_name ?? input.workdir;
997
- if (!fn)
1039
+ if (!fn) {
998
1040
  return err('function_name required for Lambda cost estimation');
1041
+ }
999
1042
  try {
1000
1043
  const { stdout } = await execAsync(`aws lambda get-function-configuration --function-name ${fn} --output json`, { timeout: 15_000 });
1001
1044
  const cfg = JSON.parse(stdout);
@@ -1155,7 +1198,7 @@ export const driftDetectTool = {
1155
1198
  timeout: 120_000, maxBuffer: 10 * 1024 * 1024,
1156
1199
  });
1157
1200
  if (diffOut.trim()) {
1158
- results.push('## Tracked Resource Drift (kubectl diff):\n' + diffOut);
1201
+ results.push(`## Tracked Resource Drift (kubectl diff):\n${diffOut}`);
1159
1202
  }
1160
1203
  }
1161
1204
  catch { /* ignore */ }
@@ -1166,8 +1209,9 @@ export const driftDetectTool = {
1166
1209
  const clusterData = JSON.parse(clusterJson);
1167
1210
  for (const item of (clusterData.items ?? [])) {
1168
1211
  const kind = item.kind ?? 'Unknown';
1169
- if (!clusterResources[kind])
1212
+ if (!clusterResources[kind]) {
1170
1213
  clusterResources[kind] = new Set();
1214
+ }
1171
1215
  clusterResources[kind].add(`${item.metadata?.namespace ?? 'default'}/${item.metadata?.name}`);
1172
1216
  }
1173
1217
  }
@@ -1182,8 +1226,9 @@ export const driftDetectTool = {
1182
1226
  try {
1183
1227
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
1184
1228
  const full = joinPath(dir, entry.name);
1185
- if (entry.isDirectory())
1229
+ if (entry.isDirectory()) {
1186
1230
  scanDir(full);
1231
+ }
1187
1232
  else if (entry.name.endsWith('.yaml') || entry.name.endsWith('.yml')) {
1188
1233
  const fileContent = readFileSync(full, 'utf-8');
1189
1234
  const kindMatch = fileContent.match(/^kind:\s*(\S+)/m);
@@ -1219,9 +1264,7 @@ export const driftDetectTool = {
1219
1264
  }
1220
1265
  }
1221
1266
  if (untracked.length > 0) {
1222
- results.push(`## Untracked Cluster Resources (${untracked.length} total):\n` +
1223
- untracked.slice(0, 100).map(r => ` - ${r}`).join('\n') +
1224
- (untracked.length > 100 ? `\n ... and ${untracked.length - 100} more` : ''));
1267
+ results.push(`## Untracked Cluster Resources (${untracked.length} total):\n${untracked.slice(0, 100).map(r => ` - ${r}`).join('\n')}${untracked.length > 100 ? `\n ... and ${untracked.length - 100} more` : ''}`);
1225
1268
  }
1226
1269
  if (results.length === 0) {
1227
1270
  return ok('No drift detected in Kubernetes resources.');
@@ -1246,8 +1289,9 @@ export const driftDetectTool = {
1246
1289
  try {
1247
1290
  const { stdout } = await execAsync('helm list -A --output json', { timeout: 30_000 });
1248
1291
  const releases = JSON.parse(stdout || '[]');
1249
- if (releases.length === 0)
1292
+ if (releases.length === 0) {
1250
1293
  return ok('No Helm releases found.');
1294
+ }
1251
1295
  const lines = releases.map(r => ` ${r.name} (${r.namespace}): ${r.status} — ${r.chart}, updated ${r.updated}`);
1252
1296
  return ok(`Helm releases:\n${lines.join('\n')}\n\n` +
1253
1297
  `Note: Install helm-diff for detailed drift: helm plugin install https://github.com/databus23/helm-diff`);
@@ -1695,8 +1739,9 @@ export const dockerTool = {
1695
1739
  const lines = chunk.split('\n');
1696
1740
  for (const line of lines) {
1697
1741
  const trimmed = line.trim();
1698
- if (!trimmed)
1742
+ if (!trimmed) {
1699
1743
  continue;
1744
+ }
1700
1745
  // Keep: Step N/M, Using cache, Successfully built, error, FROM/RUN/COPY step info
1701
1746
  if (/^Step\s+\d+\/\d+/i.test(trimmed) ||
1702
1747
  /---> Using cache/i.test(trimmed) ||
@@ -1704,14 +1749,15 @@ export const dockerTool = {
1704
1749
  /Successfully tagged/i.test(trimmed) ||
1705
1750
  /error/i.test(trimmed) ||
1706
1751
  /warning/i.test(trimmed)) {
1707
- ctx.onProgress(line + '\n');
1752
+ ctx.onProgress(`${line}\n`);
1708
1753
  }
1709
1754
  }
1710
1755
  };
1711
1756
  const buildResult = await spawnExec(command, { onChunk: filteredProgress, timeout: ctx?.timeout ?? 300_000 });
1712
1757
  const combined = [buildResult.stdout, buildResult.stderr].filter(Boolean).join('\n');
1713
- if (buildResult.exitCode !== 0)
1758
+ if (buildResult.exitCode !== 0) {
1714
1759
  return err(`Docker build failed:\n${combined}`);
1760
+ }
1715
1761
  return ok(combined || 'Build complete.');
1716
1762
  }
1717
1763
  const { stdout, stderr } = await execAsync(command, {
@@ -1762,8 +1808,9 @@ export const secretsTool = {
1762
1808
  command = `${nsFlag}vault kv list -format=json ${input.path}`;
1763
1809
  break;
1764
1810
  case 'put':
1765
- if (!input.value)
1811
+ if (!input.value) {
1766
1812
  return err('value is required for put action');
1813
+ }
1767
1814
  command = `${nsFlag}vault kv put ${input.path} value=${input.value}`;
1768
1815
  break;
1769
1816
  case 'delete':
@@ -1787,8 +1834,9 @@ export const secretsTool = {
1787
1834
  command = `aws secretsmanager list-secrets ${regionFlag} --output json`;
1788
1835
  break;
1789
1836
  case 'put':
1790
- if (!input.value)
1837
+ if (!input.value) {
1791
1838
  return err('value is required for put action');
1839
+ }
1792
1840
  command = `aws secretsmanager put-secret-value --secret-id ${input.path} --secret-string '${input.value.replace(/'/g, "'\\''")}' ${regionFlag}`;
1793
1841
  break;
1794
1842
  case 'delete':
@@ -1812,8 +1860,9 @@ export const secretsTool = {
1812
1860
  command = `gcloud secrets list --format=json`;
1813
1861
  break;
1814
1862
  case 'put':
1815
- if (!input.value)
1863
+ if (!input.value) {
1816
1864
  return err('value is required for put action');
1865
+ }
1817
1866
  command = `echo '${input.value.replace(/'/g, "'\\''")}' | gcloud secrets create ${input.path} --data-file=-`;
1818
1867
  break;
1819
1868
  case 'delete':
@@ -1837,8 +1886,9 @@ export const secretsTool = {
1837
1886
  command = `az keyvault secret list ${vaultFlag} --output json`;
1838
1887
  break;
1839
1888
  case 'put':
1840
- if (!input.value)
1889
+ if (!input.value) {
1841
1890
  return err('value is required for put action');
1891
+ }
1842
1892
  command = `az keyvault secret set --name ${input.path} --value '${input.value.replace(/'/g, "'\\''")}' ${vaultFlag}`;
1843
1893
  break;
1844
1894
  case 'delete':
@@ -2028,7 +2078,7 @@ export const cicdTool = {
2028
2078
  const lines = combined.split('\n');
2029
2079
  const truncated = lines.length > 200;
2030
2080
  const output = truncated
2031
- ? lines.slice(0, 200).join('\n') + '\n\n... truncated (showing first 200 lines)'
2081
+ ? `${lines.slice(0, 200).join('\n')}\n\n... truncated (showing first 200 lines)`
2032
2082
  : combined;
2033
2083
  return ok(output || '(no output)');
2034
2084
  }
@@ -2064,8 +2114,9 @@ export const monitorTool = {
2064
2114
  const input = monitorSchema.parse(raw);
2065
2115
  // Parse relative times
2066
2116
  function parseTime(t, defaultSecs) {
2067
- if (!t)
2117
+ if (!t) {
2068
2118
  return Math.floor(Date.now() / 1000) - defaultSecs;
2119
+ }
2069
2120
  if (t.startsWith('-')) {
2070
2121
  const val = parseInt(t.slice(1));
2071
2122
  const unit = t.slice(-1);
@@ -2122,19 +2173,21 @@ export const monitorTool = {
2122
2173
  case 'datadog': {
2123
2174
  const apiKey = process.env.DD_API_KEY ?? '';
2124
2175
  const appKey = process.env.DD_APP_KEY ?? '';
2125
- if (!apiKey)
2176
+ if (!apiKey) {
2126
2177
  return err('DD_API_KEY environment variable not set');
2178
+ }
2127
2179
  const q = encodeURIComponent(input.query ?? 'avg:system.cpu.user{*}');
2128
2180
  const cmd = `curl -sf -H "DD-API-KEY: ${apiKey}" -H "DD-APPLICATION-KEY: ${appKey}" "https://api.datadoghq.com/api/v1/query?from=${startTs}&to=${endTs}&query=${q}"`;
2129
2181
  const { stdout } = await execAsync(cmd, { timeout: 30_000 });
2130
2182
  const data = JSON.parse(stdout);
2131
2183
  const series = (data.series ?? []).slice(0, 100);
2132
- return ok(`Datadog query (${series.length} series):\n` + JSON.stringify(series.map((s) => ({ metric: s.metric, points: s.pointlist.length })), null, 2));
2184
+ return ok(`Datadog query (${series.length} series):\n${JSON.stringify(series.map((s) => ({ metric: s.metric, points: s.pointlist.length })), null, 2)}`);
2133
2185
  }
2134
2186
  case 'newrelic': {
2135
2187
  const apiKey = process.env.NEW_RELIC_API_KEY ?? '';
2136
- if (!apiKey)
2188
+ if (!apiKey) {
2137
2189
  return err('NEW_RELIC_API_KEY environment variable not set');
2190
+ }
2138
2191
  const nrqlQuery = input.query ?? `SELECT average(cpuPercent) FROM SystemSample SINCE 1 hour ago`;
2139
2192
  const body = JSON.stringify({ query: `{ actor { nrql(accounts: 0, query: "${nrqlQuery.replace(/"/g, '\\"')}") { results } } }` });
2140
2193
  const cmd = `curl -sf -X POST -H "Content-Type: application/json" -H "API-Key: ${apiKey}" -d '${body.replace(/'/g, "'\\''")}' "https://api.newrelic.com/graphql"`;
@@ -2144,8 +2197,9 @@ export const monitorTool = {
2144
2197
  // Gap 5: PagerDuty alert management
2145
2198
  case 'pagerduty': {
2146
2199
  const pdKey = process.env.PD_API_KEY ?? '';
2147
- if (!pdKey)
2200
+ if (!pdKey) {
2148
2201
  return err('PD_API_KEY environment variable not set');
2202
+ }
2149
2203
  const authHeader = `-H "Authorization: Token token=${pdKey}" -H "Accept: application/vnd.pagerduty+json;version=2"`;
2150
2204
  switch (input.action) {
2151
2205
  case 'incidents':
@@ -2153,14 +2207,16 @@ export const monitorTool = {
2153
2207
  case 'alerts':
2154
2208
  return ok((await execAsync(`curl -sf ${authHeader} "https://api.pagerduty.com/alerts?limit=25"`, { timeout: 15_000 })).stdout.slice(0, 5000));
2155
2209
  case 'ack': {
2156
- if (!input.incident_id)
2210
+ if (!input.incident_id) {
2157
2211
  return err('incident_id required for ack action');
2212
+ }
2158
2213
  const body = JSON.stringify({ incident: { type: 'incident_reference', status: 'acknowledged' } });
2159
2214
  return ok((await execAsync(`curl -sf -X PUT ${authHeader} -H "Content-Type: application/json" -d '${body}' "https://api.pagerduty.com/incidents/${input.incident_id}"`, { timeout: 15_000 })).stdout.slice(0, 2000));
2160
2215
  }
2161
2216
  case 'resolve': {
2162
- if (!input.incident_id)
2217
+ if (!input.incident_id) {
2163
2218
  return err('incident_id required for resolve action');
2219
+ }
2164
2220
  const body = JSON.stringify({ incident: { type: 'incident_reference', status: 'resolved' } });
2165
2221
  return ok((await execAsync(`curl -sf -X PUT ${authHeader} -H "Content-Type: application/json" -d '${body}' "https://api.pagerduty.com/incidents/${input.incident_id}"`, { timeout: 15_000 })).stdout.slice(0, 2000));
2166
2222
  }
@@ -2173,22 +2229,25 @@ export const monitorTool = {
2173
2229
  // Gap 5: Opsgenie alert management
2174
2230
  case 'opsgenie': {
2175
2231
  const ogKey = process.env.OPSGENIE_API_KEY ?? '';
2176
- if (!ogKey)
2232
+ if (!ogKey) {
2177
2233
  return err('OPSGENIE_API_KEY environment variable not set');
2234
+ }
2178
2235
  const authHeader = `-H "Authorization: GenieKey ${ogKey}"`;
2179
2236
  switch (input.action) {
2180
2237
  case 'alerts':
2181
2238
  case 'incidents':
2182
2239
  return ok((await execAsync(`curl -sf ${authHeader} "https://api.opsgenie.com/v2/alerts?limit=25"`, { timeout: 15_000 })).stdout.slice(0, 5000));
2183
2240
  case 'ack': {
2184
- if (!input.incident_id)
2241
+ if (!input.incident_id) {
2185
2242
  return err('incident_id required for ack action');
2243
+ }
2186
2244
  const body = JSON.stringify({ note: 'Acknowledged via Nimbus' });
2187
2245
  return ok((await execAsync(`curl -sf -X POST ${authHeader} -H "Content-Type: application/json" -d '${JSON.stringify(body)}' "https://api.opsgenie.com/v2/alerts/${input.incident_id}/acknowledge"`, { timeout: 15_000 })).stdout.slice(0, 2000));
2188
2246
  }
2189
2247
  case 'resolve': {
2190
- if (!input.incident_id)
2248
+ if (!input.incident_id) {
2191
2249
  return err('incident_id required for resolve action');
2250
+ }
2192
2251
  const body = JSON.stringify({ note: 'Resolved via Nimbus' });
2193
2252
  return ok((await execAsync(`curl -sf -X POST ${authHeader} -H "Content-Type: application/json" -d '${JSON.stringify(body)}' "https://api.opsgenie.com/v2/alerts/${input.incident_id}/close"`, { timeout: 15_000 })).stdout.slice(0, 2000));
2194
2253
  }
@@ -2324,7 +2383,7 @@ export const gitopsTool = {
2324
2383
  const health = app?.status?.health?.status ?? 'Unknown';
2325
2384
  const sync = app?.status?.sync?.status ?? 'Unknown';
2326
2385
  const conditions = (app?.status?.conditions ?? []).map((c) => ` ${c.type}: ${c.message}`).join('\n');
2327
- return ok(`App: ${app?.metadata?.name}\nHealth: ${health}\nSync: ${sync}\n${conditions ? 'Conditions:\n' + conditions : ''}`);
2386
+ return ok(`App: ${app?.metadata?.name}\nHealth: ${health}\nSync: ${sync}\n${conditions ? `Conditions:\n${conditions}` : ''}`);
2328
2387
  }
2329
2388
  catch {
2330
2389
  // Fall through to raw output
@@ -2508,7 +2567,7 @@ export const logsTool = {
2508
2567
  const combined = [stdout, stderr].filter(Boolean).join('\n');
2509
2568
  const lines = combined.split('\n');
2510
2569
  const output = lines.length > maxLines
2511
- ? lines.slice(0, maxLines).join('\n') + `\n\n... truncated at ${maxLines} lines`
2570
+ ? `${lines.slice(0, maxLines).join('\n')}\n\n... truncated at ${maxLines} lines`
2512
2571
  : combined;
2513
2572
  return ok(output || '(no logs found)');
2514
2573
  }
@@ -2916,15 +2975,19 @@ export const awsTool = {
2916
2975
  try {
2917
2976
  const input = awsSchema.parse(raw);
2918
2977
  const parts = ['aws', input.service, input.action];
2919
- if (input.profile)
2978
+ if (input.profile) {
2920
2979
  parts.push('--profile', input.profile);
2921
- else if (process.env.AWS_PROFILE)
2980
+ }
2981
+ else if (process.env.AWS_PROFILE) {
2922
2982
  parts.push('--profile', process.env.AWS_PROFILE);
2923
- if (input.region)
2983
+ }
2984
+ if (input.region) {
2924
2985
  parts.push('--region', input.region);
2986
+ }
2925
2987
  parts.push('--output', input.output ?? 'json');
2926
- if (input.args)
2988
+ if (input.args) {
2927
2989
  parts.push(input.args);
2990
+ }
2928
2991
  const command = parts.join(' ');
2929
2992
  const env = { ...process.env };
2930
2993
  const { stdout, stderr } = await execAsync(command, {
@@ -2958,13 +3021,16 @@ export const gcloudTool = {
2958
3021
  try {
2959
3022
  const input = gcloudSchema.parse(raw);
2960
3023
  const parts = ['gcloud', input.service, input.action];
2961
- if (input.project)
3024
+ if (input.project) {
2962
3025
  parts.push('--project', input.project);
2963
- if (input.region)
3026
+ }
3027
+ if (input.region) {
2964
3028
  parts.push('--region', input.region);
3029
+ }
2965
3030
  parts.push('--format', input.output ?? 'json');
2966
- if (input.args)
3031
+ if (input.args) {
2967
3032
  parts.push(input.args);
3033
+ }
2968
3034
  const command = parts.join(' ');
2969
3035
  const { stdout, stderr } = await execAsync(command, {
2970
3036
  timeout: 60_000,
@@ -2996,13 +3062,16 @@ export const azTool = {
2996
3062
  try {
2997
3063
  const input = azSchema.parse(raw);
2998
3064
  const parts = ['az', input.service, input.action];
2999
- if (input.subscription)
3065
+ if (input.subscription) {
3000
3066
  parts.push('--subscription', input.subscription);
3001
- if (input.resource_group)
3067
+ }
3068
+ if (input.resource_group) {
3002
3069
  parts.push('--resource-group', input.resource_group);
3070
+ }
3003
3071
  parts.push('--output', input.output ?? 'json');
3004
- if (input.args)
3072
+ if (input.args) {
3005
3073
  parts.push(input.args);
3074
+ }
3006
3075
  const command = parts.join(' ');
3007
3076
  const { stdout, stderr } = await execAsync(command, {
3008
3077
  timeout: 60_000,
@@ -3049,21 +3118,25 @@ export const incidentTool = {
3049
3118
  try {
3050
3119
  if (action === 'list') {
3051
3120
  const params = new URLSearchParams();
3052
- if (status)
3121
+ if (status) {
3053
3122
  params.set('statuses[]', status);
3123
+ }
3054
3124
  params.set('limit', '20');
3055
3125
  const res = await fetch(`${baseUrl}/incidents?${params}`, { headers });
3056
- if (!res.ok)
3126
+ if (!res.ok) {
3057
3127
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3128
+ }
3058
3129
  const data = await res.json();
3059
- if (!data.incidents.length)
3130
+ if (!data.incidents.length) {
3060
3131
  return ok('No incidents found.');
3132
+ }
3061
3133
  return ok(data.incidents.map(i => `[${i.status.toUpperCase()}] ${i.id}: ${i.title} (${i.urgency}) — ${i.created_at}`).join('\n'));
3062
3134
  }
3063
3135
  if (action === 'get' && id) {
3064
3136
  const res = await fetch(`${baseUrl}/incidents/${id}`, { headers });
3065
- if (!res.ok)
3137
+ if (!res.ok) {
3066
3138
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3139
+ }
3067
3140
  const data = await res.json();
3068
3141
  const inc = data.incident;
3069
3142
  return ok(`ID: ${inc.id}\nTitle: ${inc.title}\nStatus: ${inc.status}\nUrgency: ${inc.urgency}\nCreated: ${inc.created_at}\n${inc.body?.details ? `Details: ${inc.body.details}` : ''}`);
@@ -3073,8 +3146,9 @@ export const incidentTool = {
3073
3146
  method: 'PUT', headers,
3074
3147
  body: JSON.stringify({ incident: { type: 'incident_reference', status: 'acknowledged' } }),
3075
3148
  });
3076
- if (!res.ok)
3149
+ if (!res.ok) {
3077
3150
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3151
+ }
3078
3152
  return ok(`Incident ${id} acknowledged.`);
3079
3153
  }
3080
3154
  if (action === 'resolve' && id) {
@@ -3082,26 +3156,30 @@ export const incidentTool = {
3082
3156
  method: 'PUT', headers,
3083
3157
  body: JSON.stringify({ incident: { type: 'incident_reference', status: 'resolved' } }),
3084
3158
  });
3085
- if (!res.ok)
3159
+ if (!res.ok) {
3086
3160
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3161
+ }
3087
3162
  return ok(`Incident ${id} resolved.`);
3088
3163
  }
3089
3164
  if (action === 'create') {
3090
- if (!title || !service_id)
3165
+ if (!title || !service_id) {
3091
3166
  return err('create action requires title and service_id');
3167
+ }
3092
3168
  const res = await fetch(`${baseUrl}/incidents`, {
3093
3169
  method: 'POST', headers,
3094
3170
  body: JSON.stringify({ incident: { type: 'incident', title, urgency: urgency ?? 'high', service: { id: service_id, type: 'service_reference' }, body: body ? { type: 'incident_body', details: body } : undefined } }),
3095
3171
  });
3096
- if (!res.ok)
3172
+ if (!res.ok) {
3097
3173
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3174
+ }
3098
3175
  const data = await res.json();
3099
3176
  return ok(`Incident created: ${data.incident.id}`);
3100
3177
  }
3101
3178
  if (action === 'on-call') {
3102
3179
  const res = await fetch(`${baseUrl}/oncalls?limit=10`, { headers });
3103
- if (!res.ok)
3180
+ if (!res.ok) {
3104
3181
  return err(`PagerDuty API error: ${res.status} ${res.statusText}`);
3182
+ }
3105
3183
  const data = await res.json();
3106
3184
  return ok(data.oncalls.map(o => `${o.user.summary}${o.schedule?.summary ? ` (${o.schedule.summary})` : ''} until ${o.end}`).join('\n') || 'No on-call data found.');
3107
3185
  }
@@ -3122,20 +3200,24 @@ export const incidentTool = {
3122
3200
  try {
3123
3201
  if (action === 'list') {
3124
3202
  const params = new URLSearchParams({ limit: '20', sort: 'createdAt', order: 'desc' });
3125
- if (status)
3203
+ if (status) {
3126
3204
  params.set('query', `status=${status}`);
3205
+ }
3127
3206
  const res = await fetch(`${baseUrl}/alerts?${params}`, { headers });
3128
- if (!res.ok)
3207
+ if (!res.ok) {
3129
3208
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3209
+ }
3130
3210
  const data = await res.json();
3131
- if (!data.data.length)
3211
+ if (!data.data.length) {
3132
3212
  return ok('No alerts found.');
3213
+ }
3133
3214
  return ok(data.data.map(a => `[${a.status.toUpperCase()}] ${a.tinyId}: ${a.message} (${a.priority}) — ${a.createdAt}`).join('\n'));
3134
3215
  }
3135
3216
  if (action === 'get' && id) {
3136
3217
  const res = await fetch(`${baseUrl}/alerts/${id}`, { headers });
3137
- if (!res.ok)
3218
+ if (!res.ok) {
3138
3219
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3220
+ }
3139
3221
  const data = await res.json();
3140
3222
  const a = data.data;
3141
3223
  return ok(`ID: ${a.id}\nMessage: ${a.message}\nStatus: ${a.status}\nPriority: ${a.priority}\nCreated: ${a.createdAt}\n${a.description ? `Description: ${a.description}` : ''}`);
@@ -3144,34 +3226,39 @@ export const incidentTool = {
3144
3226
  const res = await fetch(`${baseUrl}/alerts/${id}/acknowledge`, {
3145
3227
  method: 'POST', headers, body: JSON.stringify({ note: 'Acknowledged via Nimbus' }),
3146
3228
  });
3147
- if (!res.ok)
3229
+ if (!res.ok) {
3148
3230
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3231
+ }
3149
3232
  return ok(`Alert ${id} acknowledged.`);
3150
3233
  }
3151
3234
  if (action === 'resolve' && id) {
3152
3235
  const res = await fetch(`${baseUrl}/alerts/${id}/close`, {
3153
3236
  method: 'POST', headers, body: JSON.stringify({ note: 'Resolved via Nimbus' }),
3154
3237
  });
3155
- if (!res.ok)
3238
+ if (!res.ok) {
3156
3239
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3240
+ }
3157
3241
  return ok(`Alert ${id} resolved.`);
3158
3242
  }
3159
3243
  if (action === 'create') {
3160
- if (!title)
3244
+ if (!title) {
3161
3245
  return err('create action requires title');
3246
+ }
3162
3247
  const res = await fetch(`${baseUrl}/alerts`, {
3163
3248
  method: 'POST', headers,
3164
3249
  body: JSON.stringify({ message: title, description: body, priority: urgency === 'high' ? 'P1' : 'P3', teams: team_id ? [{ id: team_id }] : undefined }),
3165
3250
  });
3166
- if (!res.ok)
3251
+ if (!res.ok) {
3167
3252
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3253
+ }
3168
3254
  const data = await res.json();
3169
3255
  return ok(`Alert created. Request ID: ${data.requestId}`);
3170
3256
  }
3171
3257
  if (action === 'on-call') {
3172
3258
  const res = await fetch(`${baseUrl}/schedules/on-calls`, { headers });
3173
- if (!res.ok)
3259
+ if (!res.ok) {
3174
3260
  return err(`Opsgenie API error: ${res.status} ${res.statusText}`);
3261
+ }
3175
3262
  const data = await res.json();
3176
3263
  return ok(data.data.map(s => `${s._parent?.name}: ${s.onCallParticipants.map((p) => p.name).join(', ')}`).join('\n') || 'No on-call data.');
3177
3264
  }
@@ -3222,8 +3309,9 @@ export const generateInfraTool = {
3222
3309
  const files = [];
3223
3310
  for (const file of project.files) {
3224
3311
  const parts = file.path.split('/').slice(0, -1).join('/');
3225
- if (parts)
3312
+ if (parts) {
3226
3313
  mkdirSync(join(outputDir, parts), { recursive: true });
3314
+ }
3227
3315
  const filePath = join(outputDir, file.path);
3228
3316
  writeFileSync(filePath, file.content, 'utf-8');
3229
3317
  files.push(file.path);
@@ -3268,8 +3356,9 @@ export const generateInfraTool = {
3268
3356
  const files = [];
3269
3357
  for (const file of chartFiles) {
3270
3358
  const parts = file.path.split('/').slice(0, -1).join('/');
3271
- if (parts)
3359
+ if (parts) {
3272
3360
  mkdirSync(join(outputDir, parts), { recursive: true });
3361
+ }
3273
3362
  const filePath = join(outputDir, file.path);
3274
3363
  writeFileSync(filePath, file.content, 'utf-8');
3275
3364
  files.push(file.path);