@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,284 @@
1
+ /**
2
+ * GCP Cloud Storage CLI Commands
3
+ *
4
+ * Operations for Cloud Storage buckets and objects
5
+ */
6
+
7
+ import { execFile } from 'child_process';
8
+ import { promisify } from 'util';
9
+ import { logger } from '../../utils';
10
+ import { ui } from '../../wizard/ui';
11
+ import {
12
+ loadSafetyPolicy,
13
+ evaluateSafety,
14
+ type SafetyContext,
15
+ type SafetyCheckResult,
16
+ } from '../../config/safety-policy';
17
+ import { promptForApproval, displaySafetySummary } from '../../wizard/approval';
18
+ import type { GcpCommandOptions } from './index';
19
+
20
+ const execFileAsync = promisify(execFile);
21
+
22
+ /**
23
+ * Run storage safety checks
24
+ */
25
+ async function runStorageSafetyChecks(
26
+ action: string,
27
+ target: string,
28
+ options: GcpCommandOptions
29
+ ): Promise<SafetyCheckResult> {
30
+ const safetyPolicy = loadSafetyPolicy();
31
+
32
+ const context: SafetyContext = {
33
+ operation: action,
34
+ type: 'gcp',
35
+ environment: options.project || 'default',
36
+ resources: [target],
37
+ metadata: {
38
+ resourceType: 'storage',
39
+ resourceId: target,
40
+ },
41
+ };
42
+
43
+ return evaluateSafety(context, safetyPolicy);
44
+ }
45
+
46
+ /**
47
+ * Main Cloud Storage command router
48
+ */
49
+ export async function storageCommand(
50
+ action: string,
51
+ args: string[],
52
+ options: GcpCommandOptions
53
+ ): Promise<void> {
54
+ logger.info('Running GCP Storage command', { action, args, options });
55
+
56
+ switch (action) {
57
+ case 'ls':
58
+ await listBucketsOrObjects(args[0], options);
59
+ break;
60
+
61
+ case 'cp':
62
+ if (args.length < 2) {
63
+ ui.error('Source and destination required');
64
+ return;
65
+ }
66
+ await copyObject(args[0], args[1], options);
67
+ break;
68
+
69
+ case 'rm':
70
+ if (!args[0]) {
71
+ ui.error('Object path required');
72
+ return;
73
+ }
74
+ await removeObject(args[0], options);
75
+ break;
76
+
77
+ case 'mb':
78
+ if (!args[0]) {
79
+ ui.error('Bucket name required');
80
+ return;
81
+ }
82
+ await makeBucket(args[0], options);
83
+ break;
84
+
85
+ case 'rb':
86
+ if (!args[0]) {
87
+ ui.error('Bucket name required');
88
+ return;
89
+ }
90
+ await removeBucket(args[0], options);
91
+ break;
92
+
93
+ default:
94
+ showStorageHelp();
95
+ break;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * List buckets or objects
101
+ */
102
+ async function listBucketsOrObjects(
103
+ path: string | undefined,
104
+ _options: GcpCommandOptions
105
+ ): Promise<void> {
106
+ const gsutilArgs = ['ls'];
107
+ if (path) {
108
+ gsutilArgs.push(path);
109
+ }
110
+
111
+ try {
112
+ ui.startSpinner({ message: path ? `Listing objects in ${path}...` : 'Listing buckets...' });
113
+ const { stdout } = await execFileAsync('gsutil', gsutilArgs);
114
+ ui.stopSpinnerSuccess(path ? `Objects in ${path}` : 'Buckets listed');
115
+
116
+ const items = stdout.trim().split('\n').filter(Boolean);
117
+
118
+ if (items.length === 0) {
119
+ ui.info(path ? 'No objects found' : 'No buckets found');
120
+ return;
121
+ }
122
+
123
+ ui.header(path ? `Objects in ${path}` : 'Cloud Storage Buckets');
124
+ ui.newLine();
125
+
126
+ ui.print(`Found ${items.length} item(s)\n`);
127
+
128
+ for (const item of items) {
129
+ if (item.startsWith('gs://')) {
130
+ // Bucket
131
+ ui.print(` ${ui.color(item, 'cyan')}`);
132
+ } else {
133
+ // Object
134
+ ui.print(` ${item}`);
135
+ }
136
+ }
137
+ } catch (error: unknown) {
138
+ ui.stopSpinnerFail('Failed to list');
139
+ const message = error instanceof Error ? error.message : 'Unknown error';
140
+ logger.error('Failed to list storage', { error: message });
141
+ ui.error(`Failed to list: ${message}`);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Copy objects
147
+ */
148
+ async function copyObject(
149
+ source: string,
150
+ destination: string,
151
+ _options: GcpCommandOptions
152
+ ): Promise<void> {
153
+ const gsutilArgs = ['cp', source, destination];
154
+
155
+ try {
156
+ ui.startSpinner({ message: `Copying ${source} to ${destination}...` });
157
+ await execFileAsync('gsutil', gsutilArgs);
158
+ ui.stopSpinnerSuccess(`Copied ${source} to ${destination}`);
159
+ } catch (error: unknown) {
160
+ ui.stopSpinnerFail('Failed to copy');
161
+ const message = error instanceof Error ? error.message : 'Unknown error';
162
+ logger.error('Failed to copy object', { error: message });
163
+ ui.error(`Failed to copy: ${message}`);
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Remove an object (requires safety approval)
169
+ */
170
+ async function removeObject(path: string, options: GcpCommandOptions): Promise<void> {
171
+ // Run safety checks
172
+ const safetyResult = await runStorageSafetyChecks('delete', path, options);
173
+
174
+ displaySafetySummary({
175
+ operation: `rm ${path}`,
176
+ risks: safetyResult.risks,
177
+ passed: safetyResult.passed,
178
+ });
179
+
180
+ if (safetyResult.requiresApproval) {
181
+ const result = await promptForApproval({
182
+ title: 'Remove Storage Object',
183
+ operation: `gsutil rm ${path}`,
184
+ risks: safetyResult.risks,
185
+ });
186
+
187
+ if (!result.approved) {
188
+ ui.warning('Operation cancelled');
189
+ return;
190
+ }
191
+ }
192
+
193
+ const gsutilArgs = ['rm', path];
194
+
195
+ try {
196
+ ui.startSpinner({ message: `Removing ${path}...` });
197
+ await execFileAsync('gsutil', gsutilArgs);
198
+ ui.stopSpinnerSuccess(`Removed ${path}`);
199
+ } catch (error: unknown) {
200
+ ui.stopSpinnerFail('Failed to remove');
201
+ const message = error instanceof Error ? error.message : 'Unknown error';
202
+ logger.error('Failed to remove object', { error: message });
203
+ ui.error(`Failed to remove: ${message}`);
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Create a bucket
209
+ */
210
+ async function makeBucket(bucketName: string, options: GcpCommandOptions): Promise<void> {
211
+ const gsutilArgs = ['mb'];
212
+ if (options.project) {
213
+ gsutilArgs.push('-p', options.project);
214
+ }
215
+ if (options.region) {
216
+ gsutilArgs.push('-l', options.region);
217
+ }
218
+ gsutilArgs.push(bucketName.startsWith('gs://') ? bucketName : `gs://${bucketName}`);
219
+
220
+ try {
221
+ ui.startSpinner({ message: `Creating bucket ${bucketName}...` });
222
+ await execFileAsync('gsutil', gsutilArgs);
223
+ ui.stopSpinnerSuccess(`Bucket ${bucketName} created`);
224
+ } catch (error: unknown) {
225
+ ui.stopSpinnerFail('Failed to create bucket');
226
+ const message = error instanceof Error ? error.message : 'Unknown error';
227
+ logger.error('Failed to create bucket', { error: message });
228
+ ui.error(`Failed to create bucket: ${message}`);
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Remove a bucket (requires safety approval)
234
+ */
235
+ async function removeBucket(bucketName: string, options: GcpCommandOptions): Promise<void> {
236
+ const bucket = bucketName.startsWith('gs://') ? bucketName : `gs://${bucketName}`;
237
+
238
+ // Run safety checks
239
+ const safetyResult = await runStorageSafetyChecks('delete', bucket, options);
240
+
241
+ displaySafetySummary({
242
+ operation: `rb ${bucket}`,
243
+ risks: safetyResult.risks,
244
+ passed: safetyResult.passed,
245
+ });
246
+
247
+ if (safetyResult.requiresApproval) {
248
+ const result = await promptForApproval({
249
+ title: 'Remove Storage Bucket',
250
+ operation: `gsutil rb ${bucket}`,
251
+ risks: safetyResult.risks,
252
+ });
253
+
254
+ if (!result.approved) {
255
+ ui.warning('Operation cancelled');
256
+ return;
257
+ }
258
+ }
259
+
260
+ const gsutilArgs = ['rb', bucket];
261
+
262
+ try {
263
+ ui.startSpinner({ message: `Removing bucket ${bucket}...` });
264
+ await execFileAsync('gsutil', gsutilArgs);
265
+ ui.stopSpinnerSuccess(`Bucket ${bucket} removed`);
266
+ } catch (error: unknown) {
267
+ ui.stopSpinnerFail('Failed to remove bucket');
268
+ const message = error instanceof Error ? error.message : 'Unknown error';
269
+ logger.error('Failed to remove bucket', { error: message });
270
+ ui.error(`Failed to remove bucket: ${message}`);
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Show Storage help
276
+ */
277
+ function showStorageHelp(): void {
278
+ ui.print(ui.bold('Cloud Storage Commands:'));
279
+ ui.print(' ls [gs://bucket] List buckets or objects');
280
+ ui.print(' cp <src> <dst> Copy objects');
281
+ ui.print(' rm <path> Remove object (requires approval)');
282
+ ui.print(' mb <bucket> Create bucket');
283
+ ui.print(' rb <bucket> Remove bucket (requires approval)');
284
+ }