@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,452 @@
1
+ /**
2
+ * Preview Command
3
+ *
4
+ * Preview infrastructure changes without applying them
5
+ *
6
+ * Usage:
7
+ * nimbus preview terraform [directory]
8
+ * nimbus preview k8s [directory]
9
+ * nimbus preview helm [chart]
10
+ */
11
+
12
+ import { logger } from '../utils';
13
+ import { ui } from '../wizard/ui';
14
+ import { terraformClient, k8sClient, helmClient } from '../clients';
15
+ import { loadSafetyPolicy, evaluateSafety, type SafetyContext } from '../config/safety-policy';
16
+ import { displaySafetySummary } from '../wizard/approval';
17
+
18
+ export interface PreviewOptions {
19
+ /** Type of infrastructure to preview */
20
+ type: 'terraform' | 'k8s' | 'helm';
21
+ /** Directory or chart path */
22
+ directory?: string;
23
+ /** Output format */
24
+ format?: 'table' | 'json' | 'diff';
25
+ /** Show detailed output */
26
+ verbose?: boolean;
27
+ /** Skip safety checks */
28
+ skipSafety?: boolean;
29
+ /** Target specific resources (terraform) */
30
+ target?: string;
31
+ /** Namespace (k8s) */
32
+ namespace?: string;
33
+ /** Release name (helm) */
34
+ release?: string;
35
+ /** Values file (helm) */
36
+ valuesFile?: string;
37
+ }
38
+
39
+ /**
40
+ * Preview command handler
41
+ */
42
+ export async function previewCommand(options: PreviewOptions): Promise<void> {
43
+ logger.info('Running preview', { type: options.type });
44
+
45
+ ui.newLine();
46
+ ui.header(`Preview ${capitalize(options.type)} Changes`);
47
+
48
+ switch (options.type) {
49
+ case 'terraform':
50
+ await previewTerraform(options);
51
+ break;
52
+ case 'k8s':
53
+ await previewKubernetes(options);
54
+ break;
55
+ case 'helm':
56
+ await previewHelm(options);
57
+ break;
58
+ default:
59
+ ui.error(`Unknown preview type: ${options.type}`);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Preview Terraform changes
65
+ */
66
+ async function previewTerraform(options: PreviewOptions): Promise<void> {
67
+ const directory = options.directory || '.';
68
+
69
+ ui.info(`Directory: ${directory}`);
70
+ ui.newLine();
71
+
72
+ // Check if terraform client is available
73
+ const clientAvailable = await terraformClient.isAvailable();
74
+
75
+ if (clientAvailable) {
76
+ await previewTerraformWithService(options);
77
+ } else {
78
+ await previewTerraformWithCLI(options);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Preview Terraform using service
84
+ */
85
+ async function previewTerraformWithService(options: PreviewOptions): Promise<void> {
86
+ const directory = options.directory || '.';
87
+
88
+ ui.startSpinner({ message: 'Creating execution plan...' });
89
+
90
+ try {
91
+ const result = await terraformClient.plan(directory, {});
92
+
93
+ ui.stopSpinnerSuccess('Plan created');
94
+ ui.newLine();
95
+
96
+ if (!result.success) {
97
+ ui.error(`Plan failed: ${result.error}`);
98
+ return;
99
+ }
100
+
101
+ // Display plan
102
+ displayTerraformPlan(result, options);
103
+
104
+ // Run safety checks if not skipped
105
+ if (!options.skipSafety) {
106
+ await runSafetyChecks('plan', 'terraform', result.output, options);
107
+ }
108
+ } catch (error) {
109
+ ui.stopSpinnerFail('Plan failed');
110
+ ui.error((error as Error).message);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Preview Terraform using local CLI
116
+ */
117
+ async function previewTerraformWithCLI(options: PreviewOptions): Promise<void> {
118
+ const { spawn } = await import('child_process');
119
+ const directory = options.directory || '.';
120
+
121
+ const args = ['plan', '-no-color'];
122
+
123
+ if (options.target) {
124
+ args.push('-target', options.target);
125
+ }
126
+
127
+ ui.info(`Running: terraform ${args.join(' ')}`);
128
+ ui.newLine();
129
+
130
+ return new Promise(resolve => {
131
+ let output = '';
132
+
133
+ const proc = spawn('terraform', args, {
134
+ cwd: directory,
135
+ stdio: ['inherit', 'pipe', 'pipe'],
136
+ });
137
+
138
+ proc.stdout?.on('data', data => {
139
+ const text = data.toString();
140
+ output += text;
141
+ process.stdout.write(text);
142
+ });
143
+
144
+ proc.stderr?.on('data', data => {
145
+ process.stderr.write(data);
146
+ });
147
+
148
+ proc.on('error', error => {
149
+ ui.error(`Failed to run terraform: ${error.message}`);
150
+ ui.info('Make sure terraform is installed and in your PATH');
151
+ resolve();
152
+ });
153
+
154
+ proc.on('close', async code => {
155
+ if (code === 0) {
156
+ ui.newLine();
157
+ ui.success('Plan preview complete');
158
+
159
+ // Run safety checks if not skipped
160
+ if (!options.skipSafety) {
161
+ await runSafetyChecks('plan', 'terraform', output, options);
162
+ }
163
+ } else {
164
+ ui.newLine();
165
+ ui.error(`Terraform plan failed with exit code ${code}`);
166
+ }
167
+ resolve();
168
+ });
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Display Terraform plan results
174
+ */
175
+ function displayTerraformPlan(
176
+ result: { success: boolean; hasChanges: boolean; output: string },
177
+ options: PreviewOptions
178
+ ): void {
179
+ if (!result.hasChanges) {
180
+ ui.success('No changes. Infrastructure is up to date.');
181
+ return;
182
+ }
183
+
184
+ if (options.format === 'json') {
185
+ console.log(JSON.stringify({ hasChanges: result.hasChanges, output: result.output }, null, 2));
186
+ return;
187
+ }
188
+
189
+ // Parse changes from output
190
+ const addMatch = result.output.match(/(\d+) to add/);
191
+ const changeMatch = result.output.match(/(\d+) to change/);
192
+ const destroyMatch = result.output.match(/(\d+) to destroy/);
193
+
194
+ const add = parseInt(addMatch?.[1] || '0', 10);
195
+ const change = parseInt(changeMatch?.[1] || '0', 10);
196
+ const destroy = parseInt(destroyMatch?.[1] || '0', 10);
197
+
198
+ // Display summary table
199
+ ui.print(ui.bold('Plan Summary:'));
200
+ ui.newLine();
201
+
202
+ if (add > 0) {
203
+ ui.print(` ${ui.color(`+ ${add} to add`, 'green')}`);
204
+ }
205
+ if (change > 0) {
206
+ ui.print(` ${ui.color(`~ ${change} to change`, 'yellow')}`);
207
+ }
208
+ if (destroy > 0) {
209
+ ui.print(` ${ui.color(`- ${destroy} to destroy`, 'red')}`);
210
+ }
211
+
212
+ // Show detailed output if verbose
213
+ if (options.verbose) {
214
+ ui.newLine();
215
+ ui.print(ui.bold('Detailed Changes:'));
216
+ ui.newLine();
217
+ ui.print(result.output);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Preview Kubernetes changes
223
+ */
224
+ async function previewKubernetes(options: PreviewOptions): Promise<void> {
225
+ const directory = options.directory || '.';
226
+ const namespace = options.namespace || 'default';
227
+
228
+ ui.info(`Directory: ${directory}`);
229
+ ui.info(`Namespace: ${namespace}`);
230
+ ui.newLine();
231
+
232
+ // Check if k8s client is available
233
+ const clientAvailable = await k8sClient.isAvailable();
234
+
235
+ // K8s client doesn't have a diff method, always use CLI
236
+ // If client is available, we could use dry-run apply in the future
237
+ if (clientAvailable) {
238
+ ui.info('Using kubectl diff for preview...');
239
+ }
240
+
241
+ // Use kubectl diff CLI
242
+ await previewKubernetesWithCLI(options);
243
+ }
244
+
245
+ /**
246
+ * Preview Kubernetes using kubectl
247
+ */
248
+ async function previewKubernetesWithCLI(options: PreviewOptions): Promise<void> {
249
+ const { spawn } = await import('child_process');
250
+ const directory = options.directory || '.';
251
+ const namespace = options.namespace || 'default';
252
+
253
+ const args = ['diff', '-f', directory, '-n', namespace];
254
+
255
+ ui.info(`Running: kubectl ${args.join(' ')}`);
256
+ ui.newLine();
257
+
258
+ return new Promise(resolve => {
259
+ const proc = spawn('kubectl', args, {
260
+ stdio: 'inherit',
261
+ });
262
+
263
+ proc.on('error', error => {
264
+ ui.error(`Failed to run kubectl: ${error.message}`);
265
+ ui.info('Make sure kubectl is installed and configured');
266
+ resolve();
267
+ });
268
+
269
+ proc.on('close', code => {
270
+ ui.newLine();
271
+ if (code === 0) {
272
+ ui.success('No changes detected');
273
+ } else if (code === 1) {
274
+ ui.info('Changes detected (see diff above)');
275
+ } else {
276
+ ui.error(`kubectl diff failed with exit code ${code}`);
277
+ }
278
+ resolve();
279
+ });
280
+ });
281
+ }
282
+
283
+ /**
284
+ * Display Kubernetes diff
285
+ */
286
+ function _displayK8sDiff(
287
+ result: { success: boolean; hasDiff: boolean; output?: string },
288
+ options: PreviewOptions
289
+ ): void {
290
+ if (options.format === 'json') {
291
+ console.log(JSON.stringify(result, null, 2));
292
+ return;
293
+ }
294
+
295
+ ui.print(ui.bold('Kubernetes Diff:'));
296
+ ui.newLine();
297
+
298
+ if (result.output) {
299
+ // Color the diff output
300
+ const lines = result.output.split('\n');
301
+ for (const line of lines) {
302
+ if (line.startsWith('+')) {
303
+ ui.print(ui.color(line, 'green'));
304
+ } else if (line.startsWith('-')) {
305
+ ui.print(ui.color(line, 'red'));
306
+ } else if (line.startsWith('@')) {
307
+ ui.print(ui.color(line, 'cyan'));
308
+ } else {
309
+ ui.print(line);
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Preview Helm changes
317
+ */
318
+ async function previewHelm(options: PreviewOptions): Promise<void> {
319
+ const chart = options.directory || '.';
320
+ const release = options.release || 'preview';
321
+ const namespace = options.namespace || 'default';
322
+
323
+ ui.info(`Chart: ${chart}`);
324
+ ui.info(`Release: ${release}`);
325
+ ui.info(`Namespace: ${namespace}`);
326
+ ui.newLine();
327
+
328
+ // Check if helm client is available
329
+ const clientAvailable = await helmClient.isAvailable();
330
+
331
+ // Helm client doesn't have a diff method, always use CLI
332
+ // If client is available, we could use template comparison in the future
333
+ if (clientAvailable) {
334
+ ui.info('Using helm template for preview...');
335
+ }
336
+
337
+ // Use helm template CLI
338
+ await previewHelmWithCLI(options);
339
+ }
340
+
341
+ /**
342
+ * Preview Helm using helm CLI
343
+ */
344
+ async function previewHelmWithCLI(options: PreviewOptions): Promise<void> {
345
+ const { spawn } = await import('child_process');
346
+ const chart = options.directory || '.';
347
+ const release = options.release || 'preview';
348
+ const namespace = options.namespace || 'default';
349
+
350
+ // Use helm template to preview what would be generated
351
+ const args = ['template', release, chart, '-n', namespace];
352
+
353
+ if (options.valuesFile) {
354
+ args.push('-f', options.valuesFile);
355
+ }
356
+
357
+ ui.info(`Running: helm ${args.join(' ')}`);
358
+ ui.newLine();
359
+
360
+ return new Promise(resolve => {
361
+ const proc = spawn('helm', args, {
362
+ stdio: 'inherit',
363
+ });
364
+
365
+ proc.on('error', error => {
366
+ ui.error(`Failed to run helm: ${error.message}`);
367
+ ui.info('Make sure helm is installed and in your PATH');
368
+ resolve();
369
+ });
370
+
371
+ proc.on('close', code => {
372
+ ui.newLine();
373
+ if (code === 0) {
374
+ ui.success('Template preview complete');
375
+ } else {
376
+ ui.error(`helm template failed with exit code ${code}`);
377
+ }
378
+ resolve();
379
+ });
380
+ });
381
+ }
382
+
383
+ /**
384
+ * Display Helm diff
385
+ */
386
+ function _displayHelmDiff(
387
+ result: { success: boolean; hasDiff?: boolean; output?: string },
388
+ options: PreviewOptions
389
+ ): void {
390
+ if (options.format === 'json') {
391
+ console.log(JSON.stringify(result, null, 2));
392
+ return;
393
+ }
394
+
395
+ if (!result.hasDiff) {
396
+ ui.success('No changes. Helm release is up to date.');
397
+ return;
398
+ }
399
+
400
+ ui.print(ui.bold('Helm Diff:'));
401
+ ui.newLine();
402
+
403
+ if (result.output) {
404
+ ui.print(result.output);
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Run safety checks on the preview
410
+ */
411
+ async function runSafetyChecks(
412
+ operation: string,
413
+ type: 'terraform' | 'kubernetes' | 'helm',
414
+ output: string,
415
+ options: PreviewOptions
416
+ ): Promise<void> {
417
+ const policy = loadSafetyPolicy();
418
+
419
+ const context: SafetyContext = {
420
+ operation,
421
+ type,
422
+ planOutput: output,
423
+ metadata: {
424
+ directory: options.directory,
425
+ namespace: options.namespace,
426
+ },
427
+ };
428
+
429
+ const result = evaluateSafety(context, policy);
430
+
431
+ ui.newLine();
432
+ displaySafetySummary({
433
+ operation: `${type} ${operation}`,
434
+ risks: result.risks,
435
+ passed: result.passed,
436
+ });
437
+
438
+ if (result.requiresApproval) {
439
+ ui.newLine();
440
+ ui.warning('This operation will require approval when applied');
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Capitalize first letter
446
+ */
447
+ function capitalize(str: string): string {
448
+ return str.charAt(0).toUpperCase() + str.slice(1);
449
+ }
450
+
451
+ // Export as default
452
+ export default previewCommand;