@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,426 @@
1
+ /**
2
+ * Deploy Preview
3
+ *
4
+ * Generates dry-run previews for infrastructure changes with
5
+ * blast radius analysis. Shows what will be created, modified,
6
+ * or destroyed before the user approves execution.
7
+ */
8
+
9
+ import { exec } from 'node:child_process';
10
+ import { promisify } from 'node:util';
11
+
12
+ const execAsync = promisify(exec);
13
+
14
+ /** A single resource change in the preview */
15
+ export interface ResourceChange {
16
+ /** Resource type and name (e.g., "aws_instance.web") */
17
+ resource: string;
18
+ /** Change action */
19
+ action: 'create' | 'update' | 'destroy' | 'replace' | 'read' | 'no-op';
20
+ /** Brief description of what changes */
21
+ details?: string;
22
+ }
23
+
24
+ /** The full deploy preview result */
25
+ export interface DeployPreview {
26
+ /** The deployment tool (terraform/kubectl/helm) */
27
+ tool: string;
28
+ /** The action being previewed */
29
+ action: string;
30
+ /** Working directory */
31
+ workdir: string;
32
+ /** Individual resource changes */
33
+ changes: ResourceChange[];
34
+ /** Summary counts */
35
+ summary: {
36
+ toCreate: number;
37
+ toUpdate: number;
38
+ toDestroy: number;
39
+ toReplace: number;
40
+ unchanged: number;
41
+ };
42
+ /** Raw plan output */
43
+ rawOutput: string;
44
+ /** Estimated cost impact (if available) */
45
+ costImpact?: string;
46
+ /** Whether the preview succeeded */
47
+ success: boolean;
48
+ /** Error message if preview failed */
49
+ error?: string;
50
+ }
51
+
52
+ /**
53
+ * Generate a deploy preview for the given action.
54
+ *
55
+ * Dispatches to the appropriate provider-specific preview generator
56
+ * based on the action keyword (terraform, kubectl/kubernetes, or helm).
57
+ *
58
+ * @param action - The action keyword (e.g., "terraform apply", "kubectl apply", "helm install")
59
+ * @param workdir - Working directory containing the IaC files
60
+ * @returns DeployPreview result
61
+ */
62
+ export async function generateDeployPreview(
63
+ action: string,
64
+ workdir: string
65
+ ): Promise<DeployPreview> {
66
+ const actionLower = action.toLowerCase();
67
+
68
+ if (actionLower.includes('terraform')) {
69
+ return generateTerraformPreview(workdir);
70
+ }
71
+ if (actionLower.includes('kubectl') || actionLower.includes('kubernetes')) {
72
+ return generateKubectlPreview(action, workdir);
73
+ }
74
+ if (actionLower.includes('helm')) {
75
+ return generateHelmPreview(action, workdir);
76
+ }
77
+
78
+ return {
79
+ tool: 'unknown',
80
+ action,
81
+ workdir,
82
+ changes: [],
83
+ summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
84
+ rawOutput: '',
85
+ success: false,
86
+ error: `Unsupported action for deploy preview: ${action}`,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Format a deploy preview into a human-readable string.
92
+ *
93
+ * Produces a summary block with change counts, a detailed change list
94
+ * using +/~/- symbols, optional cost impact, and a blast-radius warning
95
+ * when destructive operations are present.
96
+ *
97
+ * @param preview - The deploy preview to format
98
+ * @returns Formatted multi-line string suitable for terminal display
99
+ */
100
+ export function formatDeployPreview(preview: DeployPreview): string {
101
+ if (!preview.success) {
102
+ return `Deploy Preview Failed\n${'='.repeat(40)}\n\nError: ${preview.error}\n`;
103
+ }
104
+
105
+ const lines: string[] = [];
106
+ lines.push(`Deploy Preview: ${preview.tool} ${preview.action}`);
107
+ lines.push('='.repeat(50));
108
+ lines.push(`Working directory: ${preview.workdir}`);
109
+ lines.push('');
110
+
111
+ // Summary
112
+ const { toCreate, toUpdate, toDestroy, toReplace, unchanged } = preview.summary;
113
+ lines.push('Summary:');
114
+ if (toCreate > 0) {
115
+ lines.push(` + ${toCreate} to create`);
116
+ }
117
+ if (toUpdate > 0) {
118
+ lines.push(` ~ ${toUpdate} to update`);
119
+ }
120
+ if (toReplace > 0) {
121
+ lines.push(` +/- ${toReplace} to replace`);
122
+ }
123
+ if (toDestroy > 0) {
124
+ lines.push(` - ${toDestroy} to destroy`);
125
+ }
126
+ if (unchanged > 0) {
127
+ lines.push(` = ${unchanged} unchanged`);
128
+ }
129
+ lines.push('');
130
+
131
+ // Detailed changes
132
+ if (preview.changes.length > 0) {
133
+ lines.push('Changes:');
134
+ for (const change of preview.changes) {
135
+ const symbol = getChangeSymbol(change.action);
136
+ const detail = change.details ? ` (${change.details})` : '';
137
+ lines.push(` ${symbol} ${change.resource}${detail}`);
138
+ }
139
+ lines.push('');
140
+ }
141
+
142
+ // Cost impact
143
+ if (preview.costImpact) {
144
+ lines.push(`Cost Impact: ${preview.costImpact}`);
145
+ lines.push('');
146
+ }
147
+
148
+ // Blast radius warning
149
+ if (toDestroy > 0) {
150
+ lines.push('WARNING: This operation will DESTROY resources.');
151
+ lines.push('');
152
+ }
153
+
154
+ return lines.join('\n');
155
+ }
156
+
157
+ // ---------------------------------------------------------------
158
+ // Provider-specific preview generators
159
+ // ---------------------------------------------------------------
160
+
161
+ /**
162
+ * Run `terraform plan` and parse the human-readable output into
163
+ * structured {@link ResourceChange} entries.
164
+ */
165
+ async function generateTerraformPreview(workdir: string): Promise<DeployPreview> {
166
+ const base = {
167
+ tool: 'terraform',
168
+ action: 'apply',
169
+ workdir,
170
+ };
171
+
172
+ try {
173
+ // Run terraform plan with JSON output
174
+ const { stdout, stderr } = await execAsync(
175
+ 'terraform plan -no-color -detailed-exitcode 2>&1 || true',
176
+ { cwd: workdir, timeout: 300_000, maxBuffer: 10 * 1024 * 1024 }
177
+ );
178
+
179
+ const output = stdout || stderr;
180
+ const changes = parseTerraformPlanOutput(output);
181
+ const summary = summarizeChanges(changes);
182
+
183
+ return {
184
+ ...base,
185
+ changes,
186
+ summary,
187
+ rawOutput: output,
188
+ success: true,
189
+ };
190
+ } catch (error: unknown) {
191
+ const msg = error instanceof Error ? error.message : String(error);
192
+ return {
193
+ ...base,
194
+ changes: [],
195
+ summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
196
+ rawOutput: '',
197
+ success: false,
198
+ error: msg,
199
+ };
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Run `kubectl diff` against the current cluster state and parse
205
+ * the unified-diff output into {@link ResourceChange} entries.
206
+ */
207
+ async function generateKubectlPreview(action: string, workdir: string): Promise<DeployPreview> {
208
+ const base = {
209
+ tool: 'kubectl',
210
+ action,
211
+ workdir,
212
+ };
213
+
214
+ try {
215
+ const { stdout } = await execAsync('kubectl diff -f . 2>&1 || true', {
216
+ cwd: workdir,
217
+ timeout: 60_000,
218
+ maxBuffer: 10 * 1024 * 1024,
219
+ });
220
+
221
+ const changes = parseKubectlDiffOutput(stdout);
222
+ const summary = summarizeChanges(changes);
223
+
224
+ return {
225
+ ...base,
226
+ changes,
227
+ summary,
228
+ rawOutput: stdout,
229
+ success: true,
230
+ };
231
+ } catch (error: unknown) {
232
+ const msg = error instanceof Error ? error.message : String(error);
233
+ return {
234
+ ...base,
235
+ changes: [],
236
+ summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
237
+ rawOutput: '',
238
+ success: false,
239
+ error: msg,
240
+ };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Run `helm template` to render the chart locally and report the
246
+ * rendered manifests as a single create change.
247
+ */
248
+ async function generateHelmPreview(action: string, workdir: string): Promise<DeployPreview> {
249
+ const base = {
250
+ tool: 'helm',
251
+ action,
252
+ workdir,
253
+ };
254
+
255
+ try {
256
+ // Use helm template to show what would be deployed
257
+ const { stdout } = await execAsync('helm template . 2>&1', {
258
+ cwd: workdir,
259
+ timeout: 60_000,
260
+ maxBuffer: 10 * 1024 * 1024,
261
+ });
262
+
263
+ return {
264
+ ...base,
265
+ changes: [{ resource: 'helm-chart', action: 'create', details: 'Template rendered' }],
266
+ summary: { toCreate: 1, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
267
+ rawOutput: stdout,
268
+ success: true,
269
+ };
270
+ } catch (error: unknown) {
271
+ const msg = error instanceof Error ? error.message : String(error);
272
+ return {
273
+ ...base,
274
+ changes: [],
275
+ summary: { toCreate: 0, toUpdate: 0, toDestroy: 0, toReplace: 0, unchanged: 0 },
276
+ rawOutput: '',
277
+ success: false,
278
+ error: msg,
279
+ };
280
+ }
281
+ }
282
+
283
+ // ---------------------------------------------------------------
284
+ // Output parsers
285
+ // ---------------------------------------------------------------
286
+
287
+ /**
288
+ * Parse the human-readable `terraform plan` output into structured
289
+ * {@link ResourceChange} entries.
290
+ *
291
+ * Recognises lines such as:
292
+ * `# aws_instance.web will be created`
293
+ * `# aws_instance.web will be updated in-place`
294
+ * `# aws_instance.web will be destroyed`
295
+ * `# aws_instance.web must be replaced`
296
+ *
297
+ * Falls back to the summary line (`Plan: X to add, Y to change, Z to destroy.`)
298
+ * when no individual resource lines are found.
299
+ */
300
+ function parseTerraformPlanOutput(output: string): ResourceChange[] {
301
+ const changes: ResourceChange[] = [];
302
+ const lines = output.split('\n');
303
+
304
+ for (const line of lines) {
305
+ // Match lines like: " # aws_instance.web will be created"
306
+ const createMatch = line.match(/^\s*#\s+(\S+)\s+will be created/);
307
+ if (createMatch) {
308
+ changes.push({ resource: createMatch[1], action: 'create' });
309
+ continue;
310
+ }
311
+
312
+ const updateMatch = line.match(/^\s*#\s+(\S+)\s+will be updated/);
313
+ if (updateMatch) {
314
+ changes.push({ resource: updateMatch[1], action: 'update' });
315
+ continue;
316
+ }
317
+
318
+ const destroyMatch = line.match(/^\s*#\s+(\S+)\s+will be destroyed/);
319
+ if (destroyMatch) {
320
+ changes.push({ resource: destroyMatch[1], action: 'destroy' });
321
+ continue;
322
+ }
323
+
324
+ const replaceMatch = line.match(/^\s*#\s+(\S+)\s+must be replaced/);
325
+ if (replaceMatch) {
326
+ changes.push({ resource: replaceMatch[1], action: 'replace' });
327
+ continue;
328
+ }
329
+
330
+ // Also match: "Plan: X to add, Y to change, Z to destroy."
331
+ const summaryMatch = line.match(
332
+ /Plan:\s+(\d+)\s+to add,\s+(\d+)\s+to change,\s+(\d+)\s+to destroy/
333
+ );
334
+ if (summaryMatch && changes.length === 0) {
335
+ // If we didn't find specific resources, create generic entries
336
+ const toAdd = parseInt(summaryMatch[1]);
337
+ const toChange = parseInt(summaryMatch[2]);
338
+ const toDestroy = parseInt(summaryMatch[3]);
339
+
340
+ for (let i = 0; i < toAdd; i++) {
341
+ changes.push({ resource: `resource_${i + 1}`, action: 'create' });
342
+ }
343
+ for (let i = 0; i < toChange; i++) {
344
+ changes.push({ resource: `resource_${toAdd + i + 1}`, action: 'update' });
345
+ }
346
+ for (let i = 0; i < toDestroy; i++) {
347
+ changes.push({ resource: `resource_${toAdd + toChange + i + 1}`, action: 'destroy' });
348
+ }
349
+ }
350
+ }
351
+
352
+ return changes;
353
+ }
354
+
355
+ /**
356
+ * Parse `kubectl diff` unified-diff output into structured
357
+ * {@link ResourceChange} entries.
358
+ *
359
+ * Each `diff -u` header is treated as a separate resource update.
360
+ * An empty or "no changes" output yields an empty array.
361
+ */
362
+ function parseKubectlDiffOutput(output: string): ResourceChange[] {
363
+ const changes: ResourceChange[] = [];
364
+ const resourceRegex = /^diff -u.*\/(\S+)/gm;
365
+ let match;
366
+
367
+ while ((match = resourceRegex.exec(output)) !== null) {
368
+ changes.push({ resource: match[1], action: 'update' });
369
+ }
370
+
371
+ // If output is empty, no changes
372
+ if (output.trim() === '' || output.includes('no changes')) {
373
+ return [];
374
+ }
375
+
376
+ // If we found no specific resources but have output, assume updates
377
+ if (changes.length === 0 && output.trim().length > 0) {
378
+ changes.push({ resource: 'kubernetes-resources', action: 'update' });
379
+ }
380
+
381
+ return changes;
382
+ }
383
+
384
+ /**
385
+ * Aggregate an array of {@link ResourceChange} entries into the
386
+ * summary counts used by {@link DeployPreview}.
387
+ */
388
+ function summarizeChanges(changes: ResourceChange[]): DeployPreview['summary'] {
389
+ return {
390
+ toCreate: changes.filter(c => c.action === 'create').length,
391
+ toUpdate: changes.filter(c => c.action === 'update').length,
392
+ toDestroy: changes.filter(c => c.action === 'destroy').length,
393
+ toReplace: changes.filter(c => c.action === 'replace').length,
394
+ unchanged: changes.filter(c => c.action === 'no-op').length,
395
+ };
396
+ }
397
+
398
+ /**
399
+ * Map a {@link ResourceChange} action to a single-character symbol
400
+ * for display in the formatted preview.
401
+ *
402
+ * | Action | Symbol |
403
+ * |-----------|--------|
404
+ * | create | `+` |
405
+ * | update | `~` |
406
+ * | destroy | `-` |
407
+ * | replace | `+/-` |
408
+ * | read | `>` |
409
+ * | no-op | `=` |
410
+ */
411
+ function getChangeSymbol(action: ResourceChange['action']): string {
412
+ switch (action) {
413
+ case 'create':
414
+ return '+';
415
+ case 'update':
416
+ return '~';
417
+ case 'destroy':
418
+ return '-';
419
+ case 'replace':
420
+ return '+/-';
421
+ case 'read':
422
+ return '>';
423
+ case 'no-op':
424
+ return '=';
425
+ }
426
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Agent System — Barrel re-exports
3
+ *
4
+ * Core agentic loop, system prompt builder, permission engine,
5
+ * context manager, and subagent system.
6
+ */
7
+
8
+ // Core agentic loop
9
+ export {
10
+ type AgentLoopOptions,
11
+ type AgentLoopResult,
12
+ type ToolCallInfo,
13
+ runAgentLoop,
14
+ getToolsForMode,
15
+ } from './loop';
16
+
17
+ // System prompt builder
18
+ export {
19
+ type AgentMode,
20
+ type SystemPromptOptions,
21
+ buildSystemPrompt,
22
+ loadNimbusMd,
23
+ } from './system-prompt';
24
+
25
+ // Permission engine
26
+ export {
27
+ type PermissionDecision,
28
+ type PermissionContext,
29
+ type PermissionSessionState,
30
+ type PermissionConfig,
31
+ checkPermission,
32
+ createPermissionState,
33
+ approveForSession,
34
+ approveActionForSession,
35
+ } from './permissions';
36
+
37
+ // Context manager — @file/@folder reference resolution
38
+ export {
39
+ type FileReference,
40
+ type ContextOptions,
41
+ type ContextResult,
42
+ resolveReferences,
43
+ buildContextInjection,
44
+ fuzzyFileSearch,
45
+ } from './context';
46
+
47
+ // Deploy preview system
48
+ export {
49
+ type ResourceChange,
50
+ type DeployPreview,
51
+ generateDeployPreview,
52
+ formatDeployPreview,
53
+ } from './deploy-preview';
54
+
55
+ // Subagent system
56
+ export {
57
+ Subagent,
58
+ type SubagentConfig,
59
+ type SubagentResult,
60
+ type SubagentType,
61
+ createSubagent,
62
+ parseAgentMention,
63
+ createExploreSubagent,
64
+ createInfraSubagent,
65
+ createSecuritySubagent,
66
+ createCostSubagent,
67
+ createGeneralSubagent,
68
+ } from './subagents';