@build-astron-co/nimbus 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +628 -0
  3. package/bin/nimbus +38 -0
  4. package/package.json +80 -0
  5. package/src/__tests__/app.test.ts +76 -0
  6. package/src/__tests__/audit.test.ts +877 -0
  7. package/src/__tests__/circuit-breaker.test.ts +116 -0
  8. package/src/__tests__/cli-run.test.ts +115 -0
  9. package/src/__tests__/context-manager.test.ts +502 -0
  10. package/src/__tests__/context.test.ts +242 -0
  11. package/src/__tests__/enterprise.test.ts +401 -0
  12. package/src/__tests__/generator.test.ts +433 -0
  13. package/src/__tests__/hooks.test.ts +582 -0
  14. package/src/__tests__/init.test.ts +436 -0
  15. package/src/__tests__/intent-parser.test.ts +229 -0
  16. package/src/__tests__/llm-router.test.ts +209 -0
  17. package/src/__tests__/lsp.test.ts +293 -0
  18. package/src/__tests__/modes.test.ts +336 -0
  19. package/src/__tests__/permissions.test.ts +338 -0
  20. package/src/__tests__/serve.test.ts +275 -0
  21. package/src/__tests__/sessions.test.ts +227 -0
  22. package/src/__tests__/sharing.test.ts +288 -0
  23. package/src/__tests__/snapshots.test.ts +581 -0
  24. package/src/__tests__/state-db.test.ts +334 -0
  25. package/src/__tests__/stream-with-tools.test.ts +732 -0
  26. package/src/__tests__/subagents.test.ts +176 -0
  27. package/src/__tests__/system-prompt.test.ts +169 -0
  28. package/src/__tests__/tool-converter.test.ts +256 -0
  29. package/src/__tests__/tool-schemas.test.ts +397 -0
  30. package/src/__tests__/tools.test.ts +143 -0
  31. package/src/__tests__/version.test.ts +49 -0
  32. package/src/agent/compaction-agent.ts +227 -0
  33. package/src/agent/context-manager.ts +435 -0
  34. package/src/agent/context.ts +427 -0
  35. package/src/agent/deploy-preview.ts +426 -0
  36. package/src/agent/index.ts +68 -0
  37. package/src/agent/loop.ts +717 -0
  38. package/src/agent/modes.ts +429 -0
  39. package/src/agent/permissions.ts +466 -0
  40. package/src/agent/subagents/base.ts +116 -0
  41. package/src/agent/subagents/cost.ts +51 -0
  42. package/src/agent/subagents/explore.ts +42 -0
  43. package/src/agent/subagents/general.ts +54 -0
  44. package/src/agent/subagents/index.ts +102 -0
  45. package/src/agent/subagents/infra.ts +59 -0
  46. package/src/agent/subagents/security.ts +69 -0
  47. package/src/agent/system-prompt.ts +436 -0
  48. package/src/app.ts +122 -0
  49. package/src/audit/activity-log.ts +290 -0
  50. package/src/audit/compliance-checker.ts +540 -0
  51. package/src/audit/cost-tracker.ts +318 -0
  52. package/src/audit/index.ts +23 -0
  53. package/src/audit/security-scanner.ts +596 -0
  54. package/src/auth/guard.ts +75 -0
  55. package/src/auth/index.ts +56 -0
  56. package/src/auth/oauth.ts +455 -0
  57. package/src/auth/providers.ts +470 -0
  58. package/src/auth/sso.ts +113 -0
  59. package/src/auth/store.ts +505 -0
  60. package/src/auth/types.ts +187 -0
  61. package/src/build.ts +141 -0
  62. package/src/cli/index.ts +16 -0
  63. package/src/cli/init.ts +854 -0
  64. package/src/cli/openapi-spec.ts +356 -0
  65. package/src/cli/run.ts +237 -0
  66. package/src/cli/serve-auth.ts +80 -0
  67. package/src/cli/serve.ts +462 -0
  68. package/src/cli/web.ts +67 -0
  69. package/src/cli.ts +1417 -0
  70. package/src/clients/core-engine-client.ts +227 -0
  71. package/src/clients/enterprise-client.ts +334 -0
  72. package/src/clients/generator-client.ts +351 -0
  73. package/src/clients/git-client.ts +627 -0
  74. package/src/clients/github-client.ts +410 -0
  75. package/src/clients/helm-client.ts +504 -0
  76. package/src/clients/index.ts +80 -0
  77. package/src/clients/k8s-client.ts +497 -0
  78. package/src/clients/llm-client.ts +161 -0
  79. package/src/clients/rest-client.ts +130 -0
  80. package/src/clients/service-discovery.ts +33 -0
  81. package/src/clients/terraform-client.ts +482 -0
  82. package/src/clients/tools-client.ts +1843 -0
  83. package/src/clients/ws-client.ts +115 -0
  84. package/src/commands/analyze/index.ts +352 -0
  85. package/src/commands/apply/helm.ts +473 -0
  86. package/src/commands/apply/index.ts +213 -0
  87. package/src/commands/apply/k8s.ts +454 -0
  88. package/src/commands/apply/terraform.ts +582 -0
  89. package/src/commands/ask.ts +167 -0
  90. package/src/commands/audit/index.ts +238 -0
  91. package/src/commands/auth-cloud.ts +294 -0
  92. package/src/commands/auth-list.ts +134 -0
  93. package/src/commands/auth-profile.ts +121 -0
  94. package/src/commands/auth-status.ts +141 -0
  95. package/src/commands/aws/ec2.ts +501 -0
  96. package/src/commands/aws/iam.ts +397 -0
  97. package/src/commands/aws/index.ts +133 -0
  98. package/src/commands/aws/lambda.ts +396 -0
  99. package/src/commands/aws/rds.ts +439 -0
  100. package/src/commands/aws/s3.ts +439 -0
  101. package/src/commands/aws/vpc.ts +393 -0
  102. package/src/commands/aws-discover.ts +649 -0
  103. package/src/commands/aws-terraform.ts +805 -0
  104. package/src/commands/azure/aks.ts +376 -0
  105. package/src/commands/azure/functions.ts +253 -0
  106. package/src/commands/azure/index.ts +116 -0
  107. package/src/commands/azure/storage.ts +478 -0
  108. package/src/commands/azure/vm.ts +355 -0
  109. package/src/commands/billing/index.ts +256 -0
  110. package/src/commands/chat.ts +314 -0
  111. package/src/commands/config.ts +346 -0
  112. package/src/commands/cost/cloud-cost-estimator.ts +266 -0
  113. package/src/commands/cost/estimator.ts +79 -0
  114. package/src/commands/cost/index.ts +594 -0
  115. package/src/commands/cost/parsers/terraform.ts +273 -0
  116. package/src/commands/cost/parsers/types.ts +25 -0
  117. package/src/commands/cost/pricing/aws.ts +544 -0
  118. package/src/commands/cost/pricing/azure.ts +499 -0
  119. package/src/commands/cost/pricing/gcp.ts +396 -0
  120. package/src/commands/cost/pricing/index.ts +40 -0
  121. package/src/commands/demo.ts +250 -0
  122. package/src/commands/doctor.ts +794 -0
  123. package/src/commands/drift/index.ts +439 -0
  124. package/src/commands/explain.ts +277 -0
  125. package/src/commands/feedback.ts +389 -0
  126. package/src/commands/fix.ts +324 -0
  127. package/src/commands/fs/index.ts +402 -0
  128. package/src/commands/gcp/compute.ts +325 -0
  129. package/src/commands/gcp/functions.ts +271 -0
  130. package/src/commands/gcp/gke.ts +438 -0
  131. package/src/commands/gcp/iam.ts +344 -0
  132. package/src/commands/gcp/index.ts +129 -0
  133. package/src/commands/gcp/storage.ts +284 -0
  134. package/src/commands/generate-helm.ts +1249 -0
  135. package/src/commands/generate-k8s.ts +1560 -0
  136. package/src/commands/generate-terraform.ts +1460 -0
  137. package/src/commands/gh/index.ts +863 -0
  138. package/src/commands/git/index.ts +1343 -0
  139. package/src/commands/helm/index.ts +1126 -0
  140. package/src/commands/help.ts +539 -0
  141. package/src/commands/history.ts +142 -0
  142. package/src/commands/import.ts +868 -0
  143. package/src/commands/index.ts +367 -0
  144. package/src/commands/init.ts +1046 -0
  145. package/src/commands/k8s/index.ts +1137 -0
  146. package/src/commands/login.ts +631 -0
  147. package/src/commands/logout.ts +83 -0
  148. package/src/commands/onboarding.ts +228 -0
  149. package/src/commands/plan/display.ts +279 -0
  150. package/src/commands/plan/index.ts +599 -0
  151. package/src/commands/preview.ts +452 -0
  152. package/src/commands/questionnaire.ts +1270 -0
  153. package/src/commands/resume.ts +55 -0
  154. package/src/commands/team/index.ts +346 -0
  155. package/src/commands/template.ts +232 -0
  156. package/src/commands/tf/index.ts +1034 -0
  157. package/src/commands/upgrade.ts +550 -0
  158. package/src/commands/usage/index.ts +134 -0
  159. package/src/commands/version.ts +170 -0
  160. package/src/compat/index.ts +2 -0
  161. package/src/compat/runtime.ts +12 -0
  162. package/src/compat/sqlite.ts +107 -0
  163. package/src/config/index.ts +17 -0
  164. package/src/config/manager.ts +530 -0
  165. package/src/config/safety-policy.ts +358 -0
  166. package/src/config/schema.ts +125 -0
  167. package/src/config/types.ts +527 -0
  168. package/src/context/context-db.ts +199 -0
  169. package/src/demo/index.ts +349 -0
  170. package/src/demo/scenarios/full-journey.ts +229 -0
  171. package/src/demo/scenarios/getting-started.ts +127 -0
  172. package/src/demo/scenarios/helm-release.ts +341 -0
  173. package/src/demo/scenarios/k8s-deployment.ts +194 -0
  174. package/src/demo/scenarios/terraform-vpc.ts +170 -0
  175. package/src/demo/types.ts +92 -0
  176. package/src/engine/cost-estimator.ts +438 -0
  177. package/src/engine/diagram-generator.ts +256 -0
  178. package/src/engine/drift-detector.ts +902 -0
  179. package/src/engine/executor.ts +1035 -0
  180. package/src/engine/index.ts +76 -0
  181. package/src/engine/orchestrator.ts +636 -0
  182. package/src/engine/planner.ts +720 -0
  183. package/src/engine/safety.ts +743 -0
  184. package/src/engine/verifier.ts +770 -0
  185. package/src/enterprise/audit.ts +348 -0
  186. package/src/enterprise/auth.ts +270 -0
  187. package/src/enterprise/billing.ts +822 -0
  188. package/src/enterprise/index.ts +17 -0
  189. package/src/enterprise/teams.ts +443 -0
  190. package/src/generator/best-practices.ts +1608 -0
  191. package/src/generator/helm.ts +630 -0
  192. package/src/generator/index.ts +37 -0
  193. package/src/generator/intent-parser.ts +514 -0
  194. package/src/generator/kubernetes.ts +976 -0
  195. package/src/generator/terraform.ts +1867 -0
  196. package/src/history/index.ts +8 -0
  197. package/src/history/manager.ts +322 -0
  198. package/src/history/types.ts +34 -0
  199. package/src/hooks/config.ts +432 -0
  200. package/src/hooks/engine.ts +391 -0
  201. package/src/hooks/index.ts +4 -0
  202. package/src/llm/auth-bridge.ts +198 -0
  203. package/src/llm/circuit-breaker.ts +140 -0
  204. package/src/llm/config-loader.ts +201 -0
  205. package/src/llm/cost-calculator.ts +171 -0
  206. package/src/llm/index.ts +8 -0
  207. package/src/llm/model-aliases.ts +115 -0
  208. package/src/llm/provider-registry.ts +63 -0
  209. package/src/llm/providers/anthropic.ts +433 -0
  210. package/src/llm/providers/bedrock.ts +477 -0
  211. package/src/llm/providers/google.ts +405 -0
  212. package/src/llm/providers/ollama.ts +767 -0
  213. package/src/llm/providers/openai-compatible.ts +340 -0
  214. package/src/llm/providers/openai.ts +328 -0
  215. package/src/llm/providers/openrouter.ts +338 -0
  216. package/src/llm/router.ts +1035 -0
  217. package/src/llm/types.ts +232 -0
  218. package/src/lsp/client.ts +298 -0
  219. package/src/lsp/languages.ts +116 -0
  220. package/src/lsp/manager.ts +278 -0
  221. package/src/mcp/client.ts +402 -0
  222. package/src/mcp/index.ts +5 -0
  223. package/src/mcp/manager.ts +133 -0
  224. package/src/nimbus.ts +214 -0
  225. package/src/plugins/index.ts +27 -0
  226. package/src/plugins/loader.ts +334 -0
  227. package/src/plugins/manager.ts +376 -0
  228. package/src/plugins/types.ts +284 -0
  229. package/src/scanners/cicd-scanner.ts +258 -0
  230. package/src/scanners/cloud-scanner.ts +466 -0
  231. package/src/scanners/framework-scanner.ts +469 -0
  232. package/src/scanners/iac-scanner.ts +388 -0
  233. package/src/scanners/index.ts +539 -0
  234. package/src/scanners/language-scanner.ts +276 -0
  235. package/src/scanners/package-manager-scanner.ts +277 -0
  236. package/src/scanners/types.ts +172 -0
  237. package/src/sessions/manager.ts +365 -0
  238. package/src/sessions/types.ts +44 -0
  239. package/src/sharing/sync.ts +296 -0
  240. package/src/sharing/viewer.ts +97 -0
  241. package/src/snapshots/index.ts +2 -0
  242. package/src/snapshots/manager.ts +530 -0
  243. package/src/state/artifacts.ts +147 -0
  244. package/src/state/audit.ts +137 -0
  245. package/src/state/billing.ts +240 -0
  246. package/src/state/checkpoints.ts +117 -0
  247. package/src/state/config.ts +67 -0
  248. package/src/state/conversations.ts +14 -0
  249. package/src/state/credentials.ts +154 -0
  250. package/src/state/db.ts +58 -0
  251. package/src/state/index.ts +26 -0
  252. package/src/state/messages.ts +115 -0
  253. package/src/state/projects.ts +123 -0
  254. package/src/state/schema.ts +236 -0
  255. package/src/state/sessions.ts +147 -0
  256. package/src/state/teams.ts +200 -0
  257. package/src/telemetry.ts +108 -0
  258. package/src/tools/aws-ops.ts +952 -0
  259. package/src/tools/azure-ops.ts +579 -0
  260. package/src/tools/file-ops.ts +593 -0
  261. package/src/tools/gcp-ops.ts +625 -0
  262. package/src/tools/git-ops.ts +773 -0
  263. package/src/tools/github-ops.ts +799 -0
  264. package/src/tools/helm-ops.ts +943 -0
  265. package/src/tools/index.ts +17 -0
  266. package/src/tools/k8s-ops.ts +819 -0
  267. package/src/tools/schemas/converter.ts +184 -0
  268. package/src/tools/schemas/devops.ts +612 -0
  269. package/src/tools/schemas/index.ts +73 -0
  270. package/src/tools/schemas/standard.ts +1144 -0
  271. package/src/tools/schemas/types.ts +705 -0
  272. package/src/tools/terraform-ops.ts +862 -0
  273. package/src/types/ambient.d.ts +193 -0
  274. package/src/types/config.ts +83 -0
  275. package/src/types/drift.ts +116 -0
  276. package/src/types/enterprise.ts +335 -0
  277. package/src/types/index.ts +20 -0
  278. package/src/types/plan.ts +44 -0
  279. package/src/types/request.ts +65 -0
  280. package/src/types/response.ts +54 -0
  281. package/src/types/service.ts +51 -0
  282. package/src/ui/App.tsx +997 -0
  283. package/src/ui/DeployPreview.tsx +169 -0
  284. package/src/ui/Header.tsx +68 -0
  285. package/src/ui/InputBox.tsx +350 -0
  286. package/src/ui/MessageList.tsx +585 -0
  287. package/src/ui/PermissionPrompt.tsx +151 -0
  288. package/src/ui/StatusBar.tsx +158 -0
  289. package/src/ui/ToolCallDisplay.tsx +409 -0
  290. package/src/ui/chat-ui.ts +853 -0
  291. package/src/ui/index.ts +33 -0
  292. package/src/ui/ink/index.ts +711 -0
  293. package/src/ui/streaming.ts +176 -0
  294. package/src/ui/types.ts +57 -0
  295. package/src/utils/analytics.ts +72 -0
  296. package/src/utils/cost-warning.ts +27 -0
  297. package/src/utils/env.ts +46 -0
  298. package/src/utils/errors.ts +69 -0
  299. package/src/utils/event-bus.ts +38 -0
  300. package/src/utils/index.ts +24 -0
  301. package/src/utils/logger.ts +171 -0
  302. package/src/utils/rate-limiter.ts +121 -0
  303. package/src/utils/service-auth.ts +49 -0
  304. package/src/utils/validation.ts +53 -0
  305. package/src/version.ts +4 -0
  306. package/src/watcher/index.ts +163 -0
  307. package/src/wizard/approval.ts +383 -0
  308. package/src/wizard/index.ts +25 -0
  309. package/src/wizard/prompts.ts +338 -0
  310. package/src/wizard/types.ts +171 -0
  311. package/src/wizard/ui.ts +556 -0
  312. package/src/wizard/wizard.ts +304 -0
  313. package/tsconfig.json +24 -0
@@ -0,0 +1,1126 @@
1
+ /**
2
+ * Helm Commands
3
+ *
4
+ * CLI commands for Helm operations
5
+ */
6
+
7
+ import { helmClient } from '../../clients';
8
+ import { ui } from '../../wizard/ui';
9
+ import { confirmWithResourceName } from '../../wizard/approval';
10
+ import { showDestructionCostWarning } from '../../utils/cost-warning';
11
+ import { historyManager } from '../../history';
12
+
13
+ export interface HelmCommandOptions {
14
+ namespace?: string;
15
+ allNamespaces?: boolean;
16
+ values?: Record<string, any>;
17
+ valuesFile?: string;
18
+ version?: string;
19
+ wait?: boolean;
20
+ timeout?: string;
21
+ createNamespace?: boolean;
22
+ dryRun?: boolean;
23
+ keepHistory?: boolean;
24
+ install?: boolean;
25
+ repo?: string;
26
+ yes?: boolean;
27
+ // package-specific
28
+ destination?: string;
29
+ appVersion?: string;
30
+ dependencyUpdate?: boolean;
31
+ // status-specific
32
+ revision?: number;
33
+ // template-specific
34
+ set?: Record<string, string>;
35
+ }
36
+
37
+ /**
38
+ * List Helm releases
39
+ */
40
+ export async function helmListCommand(options: HelmCommandOptions = {}): Promise<void> {
41
+ ui.header('Helm Releases');
42
+
43
+ if (options.allNamespaces) {
44
+ ui.info('Showing releases across all namespaces');
45
+ } else if (options.namespace) {
46
+ ui.info(`Namespace: ${options.namespace}`);
47
+ }
48
+
49
+ ui.startSpinner({ message: 'Fetching Helm releases...' });
50
+
51
+ try {
52
+ const available = await helmClient.isAvailable();
53
+ if (!available) {
54
+ ui.stopSpinnerFail('Helm Tools Service not available');
55
+ ui.error('Please ensure the Helm Tools Service is running.');
56
+ return;
57
+ }
58
+
59
+ const result = await helmClient.list({
60
+ namespace: options.namespace,
61
+ allNamespaces: options.allNamespaces,
62
+ });
63
+
64
+ if (result.success) {
65
+ ui.stopSpinnerSuccess(`Found ${result.releases.length} releases`);
66
+
67
+ if (result.releases.length > 0) {
68
+ ui.table({
69
+ columns: [
70
+ { key: 'name', header: 'Name' },
71
+ { key: 'namespace', header: 'Namespace' },
72
+ { key: 'revision', header: 'Revision' },
73
+ { key: 'status', header: 'Status' },
74
+ { key: 'chart', header: 'Chart' },
75
+ { key: 'appVersion', header: 'App Version' },
76
+ { key: 'updated', header: 'Updated' },
77
+ ],
78
+ data: result.releases.map(release => ({
79
+ name: release.name,
80
+ namespace: release.namespace,
81
+ revision: release.revision,
82
+ status: release.status,
83
+ chart: release.chart,
84
+ appVersion: release.appVersion,
85
+ updated: release.updated,
86
+ })),
87
+ });
88
+ } else {
89
+ ui.info('No releases found');
90
+ }
91
+ } else {
92
+ ui.stopSpinnerFail('Failed to list releases');
93
+ if (result.error) {
94
+ ui.error(result.error);
95
+ }
96
+ }
97
+ } catch (error: any) {
98
+ ui.stopSpinnerFail('Error listing Helm releases');
99
+ ui.error(error.message);
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Install a Helm chart
105
+ */
106
+ export async function helmInstallCommand(
107
+ releaseName: string,
108
+ chart: string,
109
+ options: HelmCommandOptions = {}
110
+ ): Promise<void> {
111
+ ui.header('Helm Install');
112
+ ui.info(`Release: ${releaseName}`);
113
+ ui.info(`Chart: ${chart}`);
114
+
115
+ if (options.namespace) {
116
+ ui.info(`Namespace: ${options.namespace}`);
117
+ }
118
+ if (options.version) {
119
+ ui.info(`Version: ${options.version}`);
120
+ }
121
+
122
+ ui.startSpinner({ message: `Installing ${chart}...` });
123
+
124
+ try {
125
+ const available = await helmClient.isAvailable();
126
+ if (!available) {
127
+ ui.stopSpinnerFail('Helm Tools Service not available');
128
+ ui.error('Please ensure the Helm Tools Service is running.');
129
+ return;
130
+ }
131
+
132
+ const result = await helmClient.install(releaseName, chart, {
133
+ namespace: options.namespace,
134
+ values: options.values,
135
+ valuesFile: options.valuesFile,
136
+ version: options.version,
137
+ wait: options.wait,
138
+ timeout: options.timeout,
139
+ createNamespace: options.createNamespace,
140
+ dryRun: options.dryRun,
141
+ });
142
+
143
+ if (result.success) {
144
+ ui.stopSpinnerSuccess(`Installed ${releaseName}`);
145
+
146
+ ui.info(`Status: ${result.release.status}`);
147
+ ui.info(`Revision: ${result.release.revision}`);
148
+
149
+ if (result.output) {
150
+ ui.box({ title: 'Output', content: result.output });
151
+ }
152
+ } else {
153
+ ui.stopSpinnerFail('Failed to install chart');
154
+ if (result.error) {
155
+ ui.error(result.error);
156
+ }
157
+ }
158
+ } catch (error: any) {
159
+ ui.stopSpinnerFail('Error installing Helm chart');
160
+ ui.error(error.message);
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Upgrade a Helm release
166
+ */
167
+ export async function helmUpgradeCommand(
168
+ releaseName: string,
169
+ chart: string,
170
+ options: HelmCommandOptions = {}
171
+ ): Promise<void> {
172
+ ui.header('Helm Upgrade');
173
+ ui.info(`Release: ${releaseName}`);
174
+ ui.info(`Chart: ${chart}`);
175
+
176
+ if (options.namespace) {
177
+ ui.info(`Namespace: ${options.namespace}`);
178
+ }
179
+ if (options.version) {
180
+ ui.info(`Version: ${options.version}`);
181
+ }
182
+ if (options.install) {
183
+ ui.info('Install if not exists: yes');
184
+ }
185
+
186
+ ui.startSpinner({ message: `Upgrading ${releaseName}...` });
187
+
188
+ try {
189
+ const available = await helmClient.isAvailable();
190
+ if (!available) {
191
+ ui.stopSpinnerFail('Helm Tools Service not available');
192
+ ui.error('Please ensure the Helm Tools Service is running.');
193
+ return;
194
+ }
195
+
196
+ const result = await helmClient.upgrade(releaseName, chart, {
197
+ namespace: options.namespace,
198
+ values: options.values,
199
+ valuesFile: options.valuesFile,
200
+ version: options.version,
201
+ wait: options.wait,
202
+ timeout: options.timeout,
203
+ install: options.install,
204
+ dryRun: options.dryRun,
205
+ });
206
+
207
+ if (result.success) {
208
+ ui.stopSpinnerSuccess(`Upgraded ${releaseName}`);
209
+
210
+ ui.info(`Status: ${result.release.status}`);
211
+ ui.info(`Revision: ${result.release.revision}`);
212
+
213
+ if (result.output) {
214
+ ui.box({ title: 'Output', content: result.output });
215
+ }
216
+ } else {
217
+ ui.stopSpinnerFail('Failed to upgrade release');
218
+ if (result.error) {
219
+ ui.error(result.error);
220
+ }
221
+ }
222
+ } catch (error: any) {
223
+ ui.stopSpinnerFail('Error upgrading Helm release');
224
+ ui.error(error.message);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Uninstall a Helm release
230
+ */
231
+ export async function helmUninstallCommand(
232
+ releaseName: string,
233
+ options: HelmCommandOptions = {}
234
+ ): Promise<void> {
235
+ ui.header('Helm Uninstall');
236
+ ui.info(`Release: ${releaseName}`);
237
+
238
+ if (options.namespace) {
239
+ ui.info(`Namespace: ${options.namespace}`);
240
+ }
241
+
242
+ // Show release info warning before destructive operation
243
+ ui.newLine();
244
+ ui.warning(`Destructive operation: uninstalling helm release "${releaseName}"`);
245
+ ui.print(` ${ui.color('Release:', 'yellow')} ${releaseName}`);
246
+ ui.print(` ${ui.color('Namespace:', 'yellow')} ${options.namespace || 'default'}`);
247
+ ui.print(` ${ui.color('Keep history:', 'yellow')} ${options.keepHistory ? 'yes' : 'no'}`);
248
+ ui.print(
249
+ ` ${ui.color('Impact:', 'red')} All resources managed by this release will be deleted.`
250
+ );
251
+
252
+ // Attempt to show release details if service is available
253
+ try {
254
+ const available = await helmClient.isAvailable();
255
+ if (available) {
256
+ const statusResult = await helmClient.status(releaseName, {
257
+ namespace: options.namespace,
258
+ });
259
+ if (statusResult?.release) {
260
+ ui.print(` ${ui.color('Chart:', 'yellow')} ${statusResult.release.chart || 'unknown'}`);
261
+ ui.print(
262
+ ` ${ui.color('Revision:', 'yellow')} ${statusResult.release.revision || 'unknown'}`
263
+ );
264
+ ui.print(` ${ui.color('Status:', 'yellow')} ${statusResult.release.status || 'unknown'}`);
265
+ }
266
+ }
267
+ } catch {
268
+ // Best-effort release info display -- silently skip on failure
269
+ }
270
+
271
+ // Show cost warning before destructive operation
272
+ await showDestructionCostWarning(process.cwd());
273
+
274
+ // Require confirmation for destructive operation
275
+ if (!options.dryRun && !options.yes) {
276
+ const confirmed = await confirmWithResourceName(releaseName, 'helm release');
277
+ if (!confirmed) {
278
+ return;
279
+ }
280
+ }
281
+
282
+ ui.startSpinner({ message: `Uninstalling ${releaseName}...` });
283
+
284
+ try {
285
+ const available = await helmClient.isAvailable();
286
+ if (!available) {
287
+ ui.stopSpinnerFail('Helm Tools Service not available');
288
+ ui.error('Please ensure the Helm Tools Service is running.');
289
+ return;
290
+ }
291
+
292
+ const result = await helmClient.uninstall(releaseName, {
293
+ namespace: options.namespace,
294
+ keepHistory: options.keepHistory,
295
+ dryRun: options.dryRun,
296
+ });
297
+
298
+ if (result.success) {
299
+ ui.stopSpinnerSuccess(`Uninstalled ${releaseName}`);
300
+
301
+ if (result.output) {
302
+ ui.box({ title: 'Output', content: result.output });
303
+ }
304
+ } else {
305
+ ui.stopSpinnerFail('Failed to uninstall release');
306
+ if (result.error) {
307
+ ui.error(result.error);
308
+ }
309
+ }
310
+ } catch (error: any) {
311
+ ui.stopSpinnerFail('Error uninstalling Helm release');
312
+ ui.error(error.message);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Rollback a Helm release
318
+ */
319
+ export async function helmRollbackCommand(
320
+ releaseName: string,
321
+ revision: number,
322
+ options: HelmCommandOptions = {}
323
+ ): Promise<void> {
324
+ ui.header('Helm Rollback');
325
+ ui.info(`Release: ${releaseName}`);
326
+ ui.info(`Revision: ${revision}`);
327
+
328
+ if (options.namespace) {
329
+ ui.info(`Namespace: ${options.namespace}`);
330
+ }
331
+
332
+ ui.startSpinner({ message: `Rolling back ${releaseName} to revision ${revision}...` });
333
+
334
+ try {
335
+ const available = await helmClient.isAvailable();
336
+ if (!available) {
337
+ ui.stopSpinnerFail('Helm Tools Service not available');
338
+ ui.error('Please ensure the Helm Tools Service is running.');
339
+ return;
340
+ }
341
+
342
+ const result = await helmClient.rollback(releaseName, revision, {
343
+ namespace: options.namespace,
344
+ wait: options.wait,
345
+ dryRun: options.dryRun,
346
+ });
347
+
348
+ if (result.success) {
349
+ ui.stopSpinnerSuccess(`Rolled back ${releaseName} to revision ${revision}`);
350
+
351
+ if (result.output) {
352
+ ui.box({ title: 'Output', content: result.output });
353
+ }
354
+ } else {
355
+ ui.stopSpinnerFail('Failed to rollback release');
356
+ if (result.error) {
357
+ ui.error(result.error);
358
+ }
359
+ }
360
+ } catch (error: any) {
361
+ ui.stopSpinnerFail('Error rolling back Helm release');
362
+ ui.error(error.message);
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Show release history
368
+ */
369
+ export async function helmHistoryCommand(
370
+ releaseName: string,
371
+ options: HelmCommandOptions = {}
372
+ ): Promise<void> {
373
+ ui.header(`Helm History - ${releaseName}`);
374
+
375
+ if (options.namespace) {
376
+ ui.info(`Namespace: ${options.namespace}`);
377
+ }
378
+
379
+ ui.startSpinner({ message: 'Fetching release history...' });
380
+
381
+ try {
382
+ const available = await helmClient.isAvailable();
383
+ if (!available) {
384
+ ui.stopSpinnerFail('Helm Tools Service not available');
385
+ ui.error('Please ensure the Helm Tools Service is running.');
386
+ return;
387
+ }
388
+
389
+ const result = await helmClient.history(releaseName, {
390
+ namespace: options.namespace,
391
+ });
392
+
393
+ if (result.success) {
394
+ ui.stopSpinnerSuccess(`Found ${result.history.length} revisions`);
395
+
396
+ if (result.history.length > 0) {
397
+ // Display history - the history format can vary, so we just show it raw
398
+ for (const entry of result.history) {
399
+ ui.info(JSON.stringify(entry));
400
+ }
401
+ }
402
+ } else {
403
+ ui.stopSpinnerFail('Failed to get release history');
404
+ if (result.error) {
405
+ ui.error(result.error);
406
+ }
407
+ }
408
+ } catch (error: any) {
409
+ ui.stopSpinnerFail('Error getting Helm history');
410
+ ui.error(error.message);
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Search for Helm charts
416
+ */
417
+ export async function helmSearchCommand(
418
+ keyword: string,
419
+ options: HelmCommandOptions = {}
420
+ ): Promise<void> {
421
+ ui.header(`Helm Search - "${keyword}"`);
422
+
423
+ if (options.repo) {
424
+ ui.info(`Repository: ${options.repo}`);
425
+ }
426
+
427
+ ui.startSpinner({ message: 'Searching for charts...' });
428
+
429
+ try {
430
+ const available = await helmClient.isAvailable();
431
+ if (!available) {
432
+ ui.stopSpinnerFail('Helm Tools Service not available');
433
+ ui.error('Please ensure the Helm Tools Service is running.');
434
+ return;
435
+ }
436
+
437
+ const result = await helmClient.search(keyword, {
438
+ repo: options.repo,
439
+ });
440
+
441
+ if (result.success) {
442
+ ui.stopSpinnerSuccess(`Found ${result.charts.length} charts`);
443
+
444
+ if (result.charts.length > 0) {
445
+ ui.table({
446
+ columns: [
447
+ { key: 'name', header: 'Name' },
448
+ { key: 'version', header: 'Version' },
449
+ { key: 'appVersion', header: 'App Version' },
450
+ { key: 'description', header: 'Description' },
451
+ ],
452
+ data: result.charts.map(chart => ({
453
+ name: chart.name,
454
+ version: chart.version,
455
+ appVersion: chart.appVersion,
456
+ description: chart.description,
457
+ })),
458
+ });
459
+ } else {
460
+ ui.info('No charts found');
461
+ }
462
+ } else {
463
+ ui.stopSpinnerFail('Failed to search charts');
464
+ if (result.error) {
465
+ ui.error(result.error);
466
+ }
467
+ }
468
+ } catch (error: any) {
469
+ ui.stopSpinnerFail('Error searching Helm charts');
470
+ ui.error(error.message);
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Add a Helm repository
476
+ */
477
+ export async function helmRepoAddCommand(name: string, url: string): Promise<void> {
478
+ ui.header('Helm Repo Add');
479
+ ui.info(`Name: ${name}`);
480
+ ui.info(`URL: ${url}`);
481
+
482
+ ui.startSpinner({ message: `Adding repository ${name}...` });
483
+
484
+ try {
485
+ const available = await helmClient.isAvailable();
486
+ if (!available) {
487
+ ui.stopSpinnerFail('Helm Tools Service not available');
488
+ ui.error('Please ensure the Helm Tools Service is running.');
489
+ return;
490
+ }
491
+
492
+ const result = await helmClient.repoAdd(name, url);
493
+
494
+ if (result.success) {
495
+ ui.stopSpinnerSuccess(`Added repository ${name}`);
496
+
497
+ if (result.output) {
498
+ ui.info(result.output);
499
+ }
500
+ } else {
501
+ ui.stopSpinnerFail('Failed to add repository');
502
+ if (result.error) {
503
+ ui.error(result.error);
504
+ }
505
+ }
506
+ } catch (error: any) {
507
+ ui.stopSpinnerFail('Error adding Helm repository');
508
+ ui.error(error.message);
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Update Helm repositories
514
+ */
515
+ export async function helmRepoUpdateCommand(): Promise<void> {
516
+ ui.header('Helm Repo Update');
517
+
518
+ ui.startSpinner({ message: 'Updating repositories...' });
519
+
520
+ try {
521
+ const available = await helmClient.isAvailable();
522
+ if (!available) {
523
+ ui.stopSpinnerFail('Helm Tools Service not available');
524
+ ui.error('Please ensure the Helm Tools Service is running.');
525
+ return;
526
+ }
527
+
528
+ const result = await helmClient.repoUpdate();
529
+
530
+ if (result.success) {
531
+ ui.stopSpinnerSuccess('Repositories updated');
532
+
533
+ if (result.output) {
534
+ ui.info(result.output);
535
+ }
536
+ } else {
537
+ ui.stopSpinnerFail('Failed to update repositories');
538
+ if (result.error) {
539
+ ui.error(result.error);
540
+ }
541
+ }
542
+ } catch (error: any) {
543
+ ui.stopSpinnerFail('Error updating Helm repositories');
544
+ ui.error(error.message);
545
+ }
546
+ }
547
+
548
+ /**
549
+ * Show chart information
550
+ */
551
+ export async function helmShowCommand(
552
+ chart: string,
553
+ options: HelmCommandOptions & { subcommand?: 'all' | 'chart' | 'readme' | 'values' | 'crds' } = {}
554
+ ): Promise<void> {
555
+ const sub = options.subcommand || 'all';
556
+ ui.header(`Helm Show ${sub} - ${chart}`);
557
+
558
+ if (options.version) {
559
+ ui.info(`Version: ${options.version}`);
560
+ }
561
+
562
+ ui.startSpinner({ message: `Fetching chart info for ${chart}...` });
563
+
564
+ try {
565
+ const available = await helmClient.isAvailable();
566
+ if (!available) {
567
+ ui.stopSpinnerFail('Helm Tools Service not available');
568
+ ui.error('Please ensure the Helm Tools Service is running.');
569
+ return;
570
+ }
571
+
572
+ const result = await helmClient.show(chart, {
573
+ subcommand: sub,
574
+ version: options.version,
575
+ });
576
+
577
+ if (result.success) {
578
+ ui.stopSpinnerSuccess(`Chart info retrieved`);
579
+ if (result.output) {
580
+ console.log(result.output);
581
+ }
582
+ } else {
583
+ ui.stopSpinnerFail('Failed to show chart info');
584
+ if (result.error) {
585
+ ui.error(result.error);
586
+ }
587
+ }
588
+ } catch (error: any) {
589
+ ui.stopSpinnerFail('Error showing chart info');
590
+ ui.error(error.message);
591
+ }
592
+ }
593
+
594
+ /**
595
+ * Lint a Helm chart
596
+ */
597
+ export async function helmLintCommand(
598
+ chartPath: string,
599
+ options: HelmCommandOptions & { strict?: boolean; valuesFiles?: string[] } = {}
600
+ ): Promise<void> {
601
+ ui.header('Helm Lint');
602
+ ui.info(`Chart: ${chartPath}`);
603
+
604
+ if (options.strict) {
605
+ ui.info('Mode: strict');
606
+ }
607
+
608
+ ui.startSpinner({ message: `Linting ${chartPath}...` });
609
+
610
+ try {
611
+ const available = await helmClient.isAvailable();
612
+ if (!available) {
613
+ ui.stopSpinnerFail('Helm Tools Service not available');
614
+ ui.error('Please ensure the Helm Tools Service is running.');
615
+ return;
616
+ }
617
+
618
+ const result = await helmClient.lint(chartPath, {
619
+ strict: options.strict,
620
+ valuesFiles: options.valuesFiles,
621
+ namespace: options.namespace,
622
+ });
623
+
624
+ if (result.success) {
625
+ const lintData = result.data;
626
+ const messages: any[] = lintData?.messages || [];
627
+ const errors = messages.filter((m: any) => m.severity === 'error');
628
+ const warnings = messages.filter((m: any) => m.severity === 'warning');
629
+
630
+ if (errors.length === 0) {
631
+ ui.stopSpinnerSuccess(
632
+ `Chart linted successfully${warnings.length > 0 ? ` (${warnings.length} warnings)` : ''}`
633
+ );
634
+ } else {
635
+ ui.stopSpinnerFail(`Lint found ${errors.length} error(s)`);
636
+ }
637
+
638
+ if (messages.length > 0) {
639
+ ui.newLine();
640
+ for (const msg of messages) {
641
+ if (msg.severity === 'error') {
642
+ ui.error(` ${msg.message || msg}`);
643
+ } else {
644
+ ui.warning(` ${msg.message || msg}`);
645
+ }
646
+ }
647
+ }
648
+
649
+ if (lintData?.output) {
650
+ ui.newLine();
651
+ ui.box({ title: 'Lint Output', content: lintData.output });
652
+ }
653
+ } else {
654
+ ui.stopSpinnerFail('Lint failed');
655
+ if (result.error) {
656
+ ui.error(typeof result.error === 'string' ? result.error : 'Unknown error');
657
+ }
658
+ }
659
+ } catch (error: any) {
660
+ ui.stopSpinnerFail('Error linting Helm chart');
661
+ ui.error(error.message);
662
+ }
663
+ }
664
+
665
+ /**
666
+ * Render chart templates locally without installing
667
+ */
668
+ export async function helmTemplateCommand(
669
+ releaseName: string,
670
+ chart: string,
671
+ options: HelmCommandOptions & { set?: Record<string, string> } = {}
672
+ ): Promise<void> {
673
+ ui.header('Helm Template');
674
+ ui.info(`Release name: ${releaseName}`);
675
+ ui.info(`Chart: ${chart}`);
676
+
677
+ if (options.namespace) {
678
+ ui.info(`Namespace: ${options.namespace}`);
679
+ }
680
+ if (options.version) {
681
+ ui.info(`Version: ${options.version}`);
682
+ }
683
+
684
+ ui.startSpinner({ message: `Rendering templates for ${chart}...` });
685
+
686
+ try {
687
+ const available = await helmClient.isAvailable();
688
+ if (!available) {
689
+ ui.stopSpinnerFail('Helm Tools Service not available');
690
+ ui.error('Please ensure the Helm Tools Service is running.');
691
+ return;
692
+ }
693
+
694
+ const result = await helmClient.template(releaseName, chart, {
695
+ namespace: options.namespace,
696
+ values: options.values,
697
+ valuesFile: options.valuesFile,
698
+ set: options.set,
699
+ version: options.version,
700
+ });
701
+
702
+ if (result.success) {
703
+ ui.stopSpinnerSuccess('Templates rendered successfully');
704
+
705
+ if (result.manifests) {
706
+ ui.newLine();
707
+ console.log(result.manifests);
708
+ }
709
+ } else {
710
+ ui.stopSpinnerFail('Failed to render templates');
711
+ if (result.error) {
712
+ ui.error(result.error);
713
+ }
714
+ }
715
+ } catch (error: any) {
716
+ ui.stopSpinnerFail('Error rendering Helm templates');
717
+ ui.error(error.message);
718
+ }
719
+ }
720
+
721
+ /**
722
+ * Package a chart directory into a chart archive
723
+ */
724
+ export async function helmPackageCommand(
725
+ chartPath: string,
726
+ options: HelmCommandOptions & {
727
+ destination?: string;
728
+ appVersion?: string;
729
+ dependencyUpdate?: boolean;
730
+ } = {}
731
+ ): Promise<void> {
732
+ ui.header('Helm Package');
733
+ ui.info(`Chart path: ${chartPath}`);
734
+
735
+ if (options.destination) {
736
+ ui.info(`Destination: ${options.destination}`);
737
+ }
738
+ if (options.version) {
739
+ ui.info(`Version: ${options.version}`);
740
+ }
741
+ if (options.appVersion) {
742
+ ui.info(`App version: ${options.appVersion}`);
743
+ }
744
+
745
+ ui.startSpinner({ message: `Packaging chart ${chartPath}...` });
746
+
747
+ try {
748
+ const available = await helmClient.isAvailable();
749
+ if (!available) {
750
+ ui.stopSpinnerFail('Helm Tools Service not available');
751
+ ui.error('Please ensure the Helm Tools Service is running.');
752
+ return;
753
+ }
754
+
755
+ const result = await helmClient.package(chartPath, {
756
+ destination: options.destination,
757
+ version: options.version,
758
+ appVersion: options.appVersion,
759
+ dependencyUpdate: options.dependencyUpdate,
760
+ });
761
+
762
+ if (result.success) {
763
+ ui.stopSpinnerSuccess('Chart packaged successfully');
764
+
765
+ if (result.output) {
766
+ ui.box({ title: 'Output', content: result.output });
767
+ }
768
+ } else {
769
+ ui.stopSpinnerFail('Failed to package chart');
770
+ if (result.error) {
771
+ ui.error(result.error);
772
+ }
773
+ }
774
+ } catch (error: any) {
775
+ ui.stopSpinnerFail('Error packaging Helm chart');
776
+ ui.error(error.message);
777
+ }
778
+ }
779
+
780
+ /**
781
+ * Manage chart dependencies
782
+ */
783
+ export async function helmDependencyCommand(
784
+ depSubcommand: string,
785
+ chartPath: string
786
+ ): Promise<void> {
787
+ const action = depSubcommand === 'build' ? 'build' : 'update';
788
+ ui.header(`Helm Dependency ${action.charAt(0).toUpperCase() + action.slice(1)}`);
789
+ ui.info(`Chart path: ${chartPath}`);
790
+
791
+ ui.startSpinner({ message: `Running dependency ${action} for ${chartPath}...` });
792
+
793
+ try {
794
+ const available = await helmClient.isAvailable();
795
+ if (!available) {
796
+ ui.stopSpinnerFail('Helm Tools Service not available');
797
+ ui.error('Please ensure the Helm Tools Service is running.');
798
+ return;
799
+ }
800
+
801
+ const result =
802
+ action === 'build'
803
+ ? await helmClient.dependencyBuild(chartPath)
804
+ : await helmClient.dependencyUpdate(chartPath);
805
+
806
+ if (result.success) {
807
+ ui.stopSpinnerSuccess(`Dependency ${action} completed`);
808
+
809
+ if (result.output) {
810
+ ui.box({ title: 'Output', content: result.output });
811
+ }
812
+ } else {
813
+ ui.stopSpinnerFail(`Failed to ${action} dependencies`);
814
+ if (result.error) {
815
+ ui.error(result.error);
816
+ }
817
+ }
818
+ } catch (error: any) {
819
+ ui.stopSpinnerFail(`Error running dependency ${action}`);
820
+ ui.error(error.message);
821
+ }
822
+ }
823
+
824
+ /**
825
+ * Show the status of a named release
826
+ */
827
+ export async function helmStatusCommand(
828
+ releaseName: string,
829
+ options: HelmCommandOptions & { revision?: number } = {}
830
+ ): Promise<void> {
831
+ ui.header(`Helm Status - ${releaseName}`);
832
+
833
+ if (options.namespace) {
834
+ ui.info(`Namespace: ${options.namespace}`);
835
+ }
836
+ if (options.revision !== undefined) {
837
+ ui.info(`Revision: ${options.revision}`);
838
+ }
839
+
840
+ ui.startSpinner({ message: `Fetching status for ${releaseName}...` });
841
+
842
+ try {
843
+ const available = await helmClient.isAvailable();
844
+ if (!available) {
845
+ ui.stopSpinnerFail('Helm Tools Service not available');
846
+ ui.error('Please ensure the Helm Tools Service is running.');
847
+ return;
848
+ }
849
+
850
+ const result = await helmClient.statusDetailed(releaseName, {
851
+ namespace: options.namespace,
852
+ revision: options.revision,
853
+ });
854
+
855
+ if (result.success) {
856
+ ui.stopSpinnerSuccess(`Status retrieved for ${releaseName}`);
857
+
858
+ if (result.status) {
859
+ const statusData = result.status;
860
+ if (typeof statusData === 'object') {
861
+ ui.newLine();
862
+ if (statusData.name) {
863
+ ui.info(`Name: ${statusData.name}`);
864
+ }
865
+ if (statusData.namespace) {
866
+ ui.info(`Namespace: ${statusData.namespace}`);
867
+ }
868
+ if (statusData.status) {
869
+ ui.info(`Status: ${statusData.status}`);
870
+ }
871
+ if (statusData.revision) {
872
+ ui.info(`Revision: ${statusData.revision}`);
873
+ }
874
+ if (statusData.chart) {
875
+ ui.info(`Chart: ${statusData.chart}`);
876
+ }
877
+ if (statusData.appVersion) {
878
+ ui.info(`App Ver: ${statusData.appVersion}`);
879
+ }
880
+ if (statusData.updated) {
881
+ ui.info(`Updated: ${statusData.updated}`);
882
+ }
883
+ if (statusData.description) {
884
+ ui.newLine();
885
+ ui.info(`Description: ${statusData.description}`);
886
+ }
887
+ if (statusData.notes) {
888
+ ui.newLine();
889
+ ui.box({ title: 'Notes', content: statusData.notes });
890
+ }
891
+ } else if (typeof statusData === 'string') {
892
+ ui.newLine();
893
+ console.log(statusData);
894
+ }
895
+ }
896
+ } else {
897
+ ui.stopSpinnerFail(`Failed to get status for ${releaseName}`);
898
+ if (result.error) {
899
+ ui.error(result.error);
900
+ }
901
+ }
902
+ } catch (error: any) {
903
+ ui.stopSpinnerFail('Error fetching Helm release status');
904
+ ui.error(error.message);
905
+ }
906
+ }
907
+
908
+ /**
909
+ * Main helm command router
910
+ */
911
+ export async function helmCommand(subcommand: string, args: string[]): Promise<void> {
912
+ const options: HelmCommandOptions = {};
913
+
914
+ // Extract positional args and options
915
+ const positionalArgs: string[] = [];
916
+
917
+ for (let i = 0; i < args.length; i++) {
918
+ const arg = args[i];
919
+ if (arg === '-n' || arg === '--namespace') {
920
+ options.namespace = args[++i];
921
+ } else if (arg === '-A' || arg === '--all-namespaces') {
922
+ options.allNamespaces = true;
923
+ } else if (arg === '-f' || arg === '--values') {
924
+ options.valuesFile = args[++i];
925
+ } else if (arg === '--version') {
926
+ options.version = args[++i];
927
+ } else if (arg === '--wait') {
928
+ options.wait = true;
929
+ } else if (arg === '--timeout') {
930
+ options.timeout = args[++i];
931
+ } else if (arg === '--create-namespace') {
932
+ options.createNamespace = true;
933
+ } else if (arg === '--dry-run') {
934
+ options.dryRun = true;
935
+ } else if (arg === '--keep-history') {
936
+ options.keepHistory = true;
937
+ } else if (arg === '-i' || arg === '--install') {
938
+ options.install = true;
939
+ } else if (arg === '--yes' || arg === '-y') {
940
+ options.yes = true;
941
+ } else if (arg === '--repo') {
942
+ options.repo = args[++i];
943
+ } else if (arg === '--destination' || arg === '-d') {
944
+ options.destination = args[++i];
945
+ } else if (arg === '--app-version') {
946
+ options.appVersion = args[++i];
947
+ } else if (arg === '--dependency-update') {
948
+ options.dependencyUpdate = true;
949
+ } else if (arg === '--revision') {
950
+ options.revision = parseInt(args[++i], 10);
951
+ } else if (arg.startsWith('--set=')) {
952
+ const setExpr = arg.slice(6);
953
+ const eqIdx = setExpr.indexOf('=');
954
+ if (eqIdx !== -1) {
955
+ const key = setExpr.slice(0, eqIdx);
956
+ const value = setExpr.slice(eqIdx + 1);
957
+ options.set = options.set || {};
958
+ options.set[key] = value;
959
+ options.values = options.values || {};
960
+ options.values[key] = value;
961
+ }
962
+ } else if (arg === '--set') {
963
+ const setExpr = args[++i] || '';
964
+ const eqIdx = setExpr.indexOf('=');
965
+ if (eqIdx !== -1) {
966
+ const key = setExpr.slice(0, eqIdx);
967
+ const value = setExpr.slice(eqIdx + 1);
968
+ options.set = options.set || {};
969
+ options.set[key] = value;
970
+ options.values = options.values || {};
971
+ options.values[key] = value;
972
+ }
973
+ } else if (!arg.startsWith('-')) {
974
+ positionalArgs.push(arg);
975
+ }
976
+ }
977
+
978
+ const startTime = Date.now();
979
+ const entry = historyManager.addEntry('helm', [subcommand, ...args]);
980
+
981
+ try {
982
+ switch (subcommand) {
983
+ case 'list':
984
+ case 'ls':
985
+ await helmListCommand(options);
986
+ break;
987
+ case 'install':
988
+ if (positionalArgs.length < 2) {
989
+ ui.error('Usage: nimbus helm install <release-name> <chart>');
990
+ return;
991
+ }
992
+ await helmInstallCommand(positionalArgs[0], positionalArgs[1], options);
993
+ break;
994
+ case 'upgrade':
995
+ if (positionalArgs.length < 2) {
996
+ ui.error('Usage: nimbus helm upgrade <release-name> <chart>');
997
+ return;
998
+ }
999
+ await helmUpgradeCommand(positionalArgs[0], positionalArgs[1], options);
1000
+ break;
1001
+ case 'uninstall':
1002
+ case 'delete':
1003
+ if (positionalArgs.length < 1) {
1004
+ ui.error('Usage: nimbus helm uninstall <release-name>');
1005
+ return;
1006
+ }
1007
+ await helmUninstallCommand(positionalArgs[0], options);
1008
+ break;
1009
+ case 'rollback':
1010
+ if (positionalArgs.length < 2) {
1011
+ ui.error('Usage: nimbus helm rollback <release-name> <revision>');
1012
+ return;
1013
+ }
1014
+ await helmRollbackCommand(positionalArgs[0], parseInt(positionalArgs[1], 10), options);
1015
+ break;
1016
+ case 'history':
1017
+ if (positionalArgs.length < 1) {
1018
+ ui.error('Usage: nimbus helm history <release-name>');
1019
+ return;
1020
+ }
1021
+ await helmHistoryCommand(positionalArgs[0], options);
1022
+ break;
1023
+ case 'search':
1024
+ if (positionalArgs.length < 1) {
1025
+ ui.error('Usage: nimbus helm search <keyword>');
1026
+ return;
1027
+ }
1028
+ await helmSearchCommand(positionalArgs[0], options);
1029
+ break;
1030
+ case 'show':
1031
+ if (positionalArgs.length < 1) {
1032
+ ui.error('Usage: nimbus helm show <chart> [--subcommand all|chart|readme|values|crds]');
1033
+ return;
1034
+ }
1035
+ {
1036
+ // First positional arg might be the subcommand (all, chart, readme, values, crds)
1037
+ const validSubs = ['all', 'chart', 'readme', 'values', 'crds'];
1038
+ let showSub: 'all' | 'chart' | 'readme' | 'values' | 'crds' = 'all';
1039
+ let chartName = positionalArgs[0];
1040
+ if (validSubs.includes(positionalArgs[0]) && positionalArgs[1]) {
1041
+ showSub = positionalArgs[0] as typeof showSub;
1042
+ chartName = positionalArgs[1];
1043
+ }
1044
+ await helmShowCommand(chartName, { ...options, subcommand: showSub });
1045
+ }
1046
+ break;
1047
+ case 'repo':
1048
+ if (positionalArgs[0] === 'add' && positionalArgs.length >= 3) {
1049
+ await helmRepoAddCommand(positionalArgs[1], positionalArgs[2]);
1050
+ } else if (positionalArgs[0] === 'update') {
1051
+ await helmRepoUpdateCommand();
1052
+ } else {
1053
+ ui.error('Usage: nimbus helm repo add <name> <url> | nimbus helm repo update');
1054
+ }
1055
+ break;
1056
+ case 'lint':
1057
+ if (positionalArgs.length < 1) {
1058
+ ui.error('Usage: nimbus helm lint <chart-path> [--strict]');
1059
+ return;
1060
+ }
1061
+ {
1062
+ const strict = args.includes('--strict');
1063
+ const valuesFiles: string[] = [];
1064
+ for (let i = 0; i < args.length; i++) {
1065
+ if ((args[i] === '-f' || args[i] === '--values') && args[i + 1]) {
1066
+ valuesFiles.push(args[i + 1]);
1067
+ }
1068
+ }
1069
+ await helmLintCommand(positionalArgs[0], { ...options, strict, valuesFiles });
1070
+ }
1071
+ break;
1072
+ case 'template':
1073
+ if (positionalArgs.length < 2) {
1074
+ ui.error(
1075
+ 'Usage: nimbus helm template <release-name> <chart> [--namespace <ns>] [--values <file>] [--set key=val] [--version <ver>]'
1076
+ );
1077
+ return;
1078
+ }
1079
+ await helmTemplateCommand(positionalArgs[0], positionalArgs[1], options);
1080
+ break;
1081
+ case 'package':
1082
+ if (positionalArgs.length < 1) {
1083
+ ui.error(
1084
+ 'Usage: nimbus helm package <chart-path> [--destination <dir>] [--version <ver>] [--app-version <ver>]'
1085
+ );
1086
+ return;
1087
+ }
1088
+ await helmPackageCommand(positionalArgs[0], options);
1089
+ break;
1090
+ case 'dependency':
1091
+ case 'dep': {
1092
+ // Supports: nimbus helm dependency update <chart-path>
1093
+ // nimbus helm dependency build <chart-path>
1094
+ const depSubcommand = positionalArgs[0] || 'update';
1095
+ const depChartPath = positionalArgs[1];
1096
+ if (!depChartPath) {
1097
+ ui.error('Usage: nimbus helm dependency <update|build> <chart-path>');
1098
+ return;
1099
+ }
1100
+ await helmDependencyCommand(depSubcommand, depChartPath);
1101
+ break;
1102
+ }
1103
+ case 'status':
1104
+ if (positionalArgs.length < 1) {
1105
+ ui.error(
1106
+ 'Usage: nimbus helm status <release-name> [--namespace <ns>] [--revision <rev>]'
1107
+ );
1108
+ return;
1109
+ }
1110
+ await helmStatusCommand(positionalArgs[0], options);
1111
+ break;
1112
+ default:
1113
+ ui.error(`Unknown helm subcommand: ${subcommand}`);
1114
+ ui.info(
1115
+ 'Available commands: list, install, upgrade, uninstall, rollback, history, search, show, repo, lint, template, package, dependency, status'
1116
+ );
1117
+ }
1118
+
1119
+ historyManager.completeEntry(entry.id, 'success', Date.now() - startTime);
1120
+ } catch (error: any) {
1121
+ historyManager.completeEntry(entry.id, 'failure', Date.now() - startTime, {
1122
+ error: error.message,
1123
+ });
1124
+ throw error;
1125
+ }
1126
+ }