@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,612 @@
1
+ /**
2
+ * DevOps Tool Definitions
3
+ *
4
+ * Defines the 9 DevOps-specific tools available to the Nimbus agentic loop.
5
+ * Each tool wraps existing infrastructure operations from `src/tools/` modules
6
+ * or invokes the appropriate CLI via child_process.
7
+ *
8
+ * Tools:
9
+ * terraform, kubectl, helm, cloud_discover, cost_estimate,
10
+ * drift_detect, deploy_preview, git, task
11
+ *
12
+ * @module tools/schemas/devops
13
+ */
14
+
15
+ import { z } from 'zod';
16
+ import { exec } from 'node:child_process';
17
+ import { promisify } from 'node:util';
18
+ import type { ToolDefinition, ToolResult } from './types';
19
+
20
+ const execAsync = promisify(exec);
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Helpers
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /** Build a successful ToolResult. */
27
+ function ok(output: string): ToolResult {
28
+ return { output, isError: false };
29
+ }
30
+
31
+ /** Build an error ToolResult. */
32
+ function err(message: string): ToolResult {
33
+ return { output: '', error: message, isError: true };
34
+ }
35
+
36
+ /**
37
+ * Extract a readable message from an unknown error value. If the error
38
+ * originates from `child_process.exec` it may carry `stdout` / `stderr`
39
+ * properties that provide richer context than `message` alone.
40
+ */
41
+ function errorMessage(error: unknown): string {
42
+ if (error !== null && typeof error === 'object' && 'stdout' in error) {
43
+ const execErr = error as {
44
+ stdout?: string;
45
+ stderr?: string;
46
+ message?: string;
47
+ };
48
+ const combined = [execErr.stdout, execErr.stderr].filter(Boolean).join('\n');
49
+ return combined || execErr.message || 'Command failed';
50
+ }
51
+ return error instanceof Error ? error.message : String(error);
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // 1. terraform
56
+ // ---------------------------------------------------------------------------
57
+
58
+ const terraformSchema = z.object({
59
+ action: z
60
+ .enum(['init', 'plan', 'apply', 'validate', 'fmt', 'destroy', 'import', 'state'])
61
+ .describe('The Terraform sub-command to run'),
62
+ workdir: z.string().describe('Working directory containing the Terraform configuration'),
63
+ args: z.string().optional().describe('Additional CLI arguments'),
64
+ var_file: z.string().optional().describe('Path to a .tfvars variable file'),
65
+ });
66
+
67
+ export const terraformTool: ToolDefinition = {
68
+ name: 'terraform',
69
+ description:
70
+ 'Execute Terraform operations. Supports init, plan, apply, validate, fmt, destroy, import, and state commands.',
71
+ inputSchema: terraformSchema,
72
+ permissionTier: 'always_ask',
73
+ category: 'devops',
74
+ isDestructive: true,
75
+
76
+ async execute(raw: unknown): Promise<ToolResult> {
77
+ try {
78
+ const input = terraformSchema.parse(raw);
79
+
80
+ const parts: string[] = ['terraform', `-chdir=${input.workdir}`, input.action];
81
+
82
+ if (input.var_file) {
83
+ parts.push(`-var-file=${input.var_file}`);
84
+ }
85
+
86
+ // Auto-approve for apply/destroy -- the permission engine handles
87
+ // user confirmation before execute() is ever called.
88
+ if (input.action === 'apply' || input.action === 'destroy') {
89
+ parts.push('-auto-approve');
90
+ }
91
+
92
+ // Add -no-color for cleaner output in non-TTY contexts.
93
+ if (['plan', 'apply', 'destroy', 'init'].includes(input.action)) {
94
+ parts.push('-no-color');
95
+ }
96
+
97
+ if (input.args) {
98
+ parts.push(input.args);
99
+ }
100
+
101
+ const command = parts.join(' ');
102
+ const { stdout, stderr } = await execAsync(command, {
103
+ timeout: 600_000, // 10 minutes
104
+ maxBuffer: 10 * 1024 * 1024,
105
+ });
106
+
107
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
108
+ return ok(combined || '(no output)');
109
+ } catch (error: unknown) {
110
+ return err(`Terraform command failed: ${errorMessage(error)}`);
111
+ }
112
+ },
113
+ };
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // 2. kubectl
117
+ // ---------------------------------------------------------------------------
118
+
119
+ const kubectlSchema = z.object({
120
+ action: z
121
+ .enum(['get', 'apply', 'delete', 'logs', 'scale', 'rollout', 'exec', 'describe'])
122
+ .describe('The kubectl sub-command to run'),
123
+ resource: z.string().optional().describe('Resource type and/or name (e.g., "pods my-pod")'),
124
+ namespace: z.string().optional().describe('Kubernetes namespace'),
125
+ args: z.string().optional().describe('Additional CLI arguments'),
126
+ });
127
+
128
+ export const kubectlTool: ToolDefinition = {
129
+ name: 'kubectl',
130
+ description: 'Execute kubectl operations against a Kubernetes cluster.',
131
+ inputSchema: kubectlSchema,
132
+ permissionTier: 'always_ask',
133
+ category: 'devops',
134
+ isDestructive: true,
135
+
136
+ async execute(raw: unknown): Promise<ToolResult> {
137
+ try {
138
+ const input = kubectlSchema.parse(raw);
139
+
140
+ const parts: string[] = ['kubectl', input.action];
141
+
142
+ if (input.resource) {
143
+ parts.push(input.resource);
144
+ }
145
+
146
+ if (input.namespace) {
147
+ parts.push('-n', input.namespace);
148
+ }
149
+
150
+ if (input.args) {
151
+ parts.push(input.args);
152
+ }
153
+
154
+ const command = parts.join(' ');
155
+ const { stdout, stderr } = await execAsync(command, {
156
+ timeout: 120_000,
157
+ maxBuffer: 10 * 1024 * 1024,
158
+ });
159
+
160
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
161
+ return ok(combined || '(no output)');
162
+ } catch (error: unknown) {
163
+ return err(`kubectl command failed: ${errorMessage(error)}`);
164
+ }
165
+ },
166
+ };
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // 3. helm
170
+ // ---------------------------------------------------------------------------
171
+
172
+ const helmSchema = z.object({
173
+ action: z
174
+ .enum(['install', 'upgrade', 'uninstall', 'list', 'rollback', 'template', 'lint'])
175
+ .describe('The Helm sub-command to run'),
176
+ release: z.string().optional().describe('Helm release name'),
177
+ chart: z.string().optional().describe('Chart reference (e.g., "bitnami/nginx")'),
178
+ values: z.string().optional().describe('Path to a values.yaml file'),
179
+ namespace: z.string().optional().describe('Kubernetes namespace for the release'),
180
+ });
181
+
182
+ export const helmTool: ToolDefinition = {
183
+ name: 'helm',
184
+ description: 'Execute Helm operations for Kubernetes package management.',
185
+ inputSchema: helmSchema,
186
+ permissionTier: 'always_ask',
187
+ category: 'devops',
188
+ isDestructive: true,
189
+
190
+ async execute(raw: unknown): Promise<ToolResult> {
191
+ try {
192
+ const input = helmSchema.parse(raw);
193
+
194
+ const parts: string[] = ['helm', input.action];
195
+
196
+ if (input.release) {
197
+ parts.push(input.release);
198
+ }
199
+
200
+ if (input.chart) {
201
+ parts.push(input.chart);
202
+ }
203
+
204
+ if (input.values) {
205
+ parts.push('-f', input.values);
206
+ }
207
+
208
+ if (input.namespace) {
209
+ parts.push('-n', input.namespace);
210
+ }
211
+
212
+ const command = parts.join(' ');
213
+ const { stdout, stderr } = await execAsync(command, {
214
+ timeout: 300_000, // 5 minutes
215
+ maxBuffer: 10 * 1024 * 1024,
216
+ });
217
+
218
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
219
+ return ok(combined || '(no output)');
220
+ } catch (error: unknown) {
221
+ return err(`Helm command failed: ${errorMessage(error)}`);
222
+ }
223
+ },
224
+ };
225
+
226
+ // ---------------------------------------------------------------------------
227
+ // 4. cloud_discover
228
+ // ---------------------------------------------------------------------------
229
+
230
+ const cloudDiscoverSchema = z.object({
231
+ provider: z.enum(['aws', 'gcp', 'azure']).describe('Cloud provider to discover resources from'),
232
+ resource_type: z
233
+ .string()
234
+ .describe('Resource type to discover (e.g., "ec2", "compute instances", "vm")'),
235
+ region: z.string().optional().describe('Cloud region to scope the discovery'),
236
+ });
237
+
238
+ export const cloudDiscoverTool: ToolDefinition = {
239
+ name: 'cloud_discover',
240
+ description:
241
+ 'Discover cloud resources across AWS, GCP, or Azure. Returns a list of resources of the specified type.',
242
+ inputSchema: cloudDiscoverSchema,
243
+ permissionTier: 'auto_allow',
244
+ category: 'devops',
245
+
246
+ async execute(raw: unknown): Promise<ToolResult> {
247
+ try {
248
+ const input = cloudDiscoverSchema.parse(raw);
249
+
250
+ let command: string;
251
+
252
+ switch (input.provider) {
253
+ case 'aws': {
254
+ const regionFlag = input.region ? ` --region ${input.region}` : '';
255
+ command = `aws ${input.resource_type} describe-instances${regionFlag} --output json`;
256
+ break;
257
+ }
258
+ case 'gcp': {
259
+ const regionFlag = input.region ? ` --regions=${input.region}` : '';
260
+ command = `gcloud ${input.resource_type} list${regionFlag} --format json`;
261
+ break;
262
+ }
263
+ case 'azure': {
264
+ command = `az ${input.resource_type} list --output json`;
265
+ break;
266
+ }
267
+ }
268
+
269
+ const { stdout, stderr } = await execAsync(command, {
270
+ timeout: 120_000,
271
+ maxBuffer: 10 * 1024 * 1024,
272
+ });
273
+
274
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
275
+ return ok(combined || '(no resources found)');
276
+ } catch (error: unknown) {
277
+ return err(`Cloud discovery failed: ${errorMessage(error)}`);
278
+ }
279
+ },
280
+ };
281
+
282
+ // ---------------------------------------------------------------------------
283
+ // 5. cost_estimate
284
+ // ---------------------------------------------------------------------------
285
+
286
+ const costEstimateSchema = z.object({
287
+ plan_file: z.string().optional().describe('Path to a saved Terraform plan file'),
288
+ workdir: z.string().optional().describe('Working directory containing Terraform configuration'),
289
+ });
290
+
291
+ export const costEstimateTool: ToolDefinition = {
292
+ name: 'cost_estimate',
293
+ description: 'Estimate infrastructure costs based on a Terraform plan or working directory.',
294
+ inputSchema: costEstimateSchema,
295
+ permissionTier: 'auto_allow',
296
+ category: 'devops',
297
+
298
+ async execute(raw: unknown): Promise<ToolResult> {
299
+ try {
300
+ const input = costEstimateSchema.parse(raw);
301
+
302
+ if (!input.plan_file && !input.workdir) {
303
+ return err('Either plan_file or workdir must be provided.');
304
+ }
305
+
306
+ const cwd = input.workdir ?? '.';
307
+ const planArg = input.plan_file ?? '';
308
+
309
+ // Attempt to extract resource information from a Terraform plan.
310
+ const showCommand = planArg
311
+ ? `terraform show -json ${planArg}`
312
+ : `terraform -chdir=${cwd} show -json`;
313
+
314
+ const { stdout } = await execAsync(showCommand, {
315
+ timeout: 60_000,
316
+ maxBuffer: 10 * 1024 * 1024,
317
+ });
318
+
319
+ // Parse the plan JSON to count resources.
320
+ let resourceCount = 0;
321
+ let resourceTypes: string[] = [];
322
+
323
+ try {
324
+ const plan = JSON.parse(stdout);
325
+ const changes = plan?.resource_changes ?? [];
326
+ resourceCount = changes.length;
327
+ resourceTypes = [
328
+ ...new Set(changes.map((r: { type?: string }) => r.type).filter(Boolean)),
329
+ ] as string[];
330
+ } catch {
331
+ // If JSON parsing fails, fall back to a basic output.
332
+ return ok(
333
+ `Cost estimate (raw plan output):\n${stdout.slice(0, 5000)}` +
334
+ '\n\nNote: Full cost estimation requires integration with a pricing API (e.g., Infracost).'
335
+ );
336
+ }
337
+
338
+ const lines = [
339
+ '--- Cost Estimate (Placeholder) ---',
340
+ '',
341
+ `Total resources: ${resourceCount}`,
342
+ `Resource types: ${resourceTypes.join(', ') || 'none'}`,
343
+ '',
344
+ 'Note: Accurate cost estimation requires integration with a pricing API',
345
+ 'such as Infracost. This is a resource-count summary only.',
346
+ ];
347
+
348
+ return ok(lines.join('\n'));
349
+ } catch (error: unknown) {
350
+ return err(`Cost estimation failed: ${errorMessage(error)}`);
351
+ }
352
+ },
353
+ };
354
+
355
+ // ---------------------------------------------------------------------------
356
+ // 6. drift_detect
357
+ // ---------------------------------------------------------------------------
358
+
359
+ const driftDetectSchema = z.object({
360
+ workdir: z.string().describe('Working directory containing IaC configuration'),
361
+ provider: z
362
+ .enum(['terraform', 'kubernetes', 'helm'])
363
+ .optional()
364
+ .default('terraform')
365
+ .describe('IaC provider to check for drift (default: terraform)'),
366
+ });
367
+
368
+ export const driftDetectTool: ToolDefinition = {
369
+ name: 'drift_detect',
370
+ description: 'Detect infrastructure drift between desired state (IaC) and actual state.',
371
+ inputSchema: driftDetectSchema,
372
+ permissionTier: 'auto_allow',
373
+ category: 'devops',
374
+
375
+ async execute(raw: unknown): Promise<ToolResult> {
376
+ try {
377
+ const input = driftDetectSchema.parse(raw);
378
+
379
+ switch (input.provider) {
380
+ case 'terraform': {
381
+ // Exit code 0 = no changes, 1 = error, 2 = changes detected.
382
+ const command = `terraform -chdir=${input.workdir} plan -detailed-exitcode -no-color`;
383
+
384
+ try {
385
+ const { stdout, stderr } = await execAsync(command, {
386
+ timeout: 300_000,
387
+ maxBuffer: 10 * 1024 * 1024,
388
+ });
389
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
390
+ return ok(`No drift detected.\n\n${combined}`);
391
+ } catch (planError: unknown) {
392
+ // Exit code 2 from terraform plan means drift was detected.
393
+ if (
394
+ planError !== null &&
395
+ typeof planError === 'object' &&
396
+ 'code' in planError &&
397
+ (planError as { code: number }).code === 2
398
+ ) {
399
+ const execErr = planError as { stdout?: string; stderr?: string };
400
+ const output = [execErr.stdout, execErr.stderr].filter(Boolean).join('\n');
401
+ return ok(`DRIFT DETECTED\n\n${output}`);
402
+ }
403
+ throw planError;
404
+ }
405
+ }
406
+
407
+ case 'kubernetes': {
408
+ // Use kubectl diff to detect drift in Kubernetes manifests.
409
+ const command = `kubectl diff -f ${input.workdir} 2>&1 || true`;
410
+ const { stdout } = await execAsync(command, {
411
+ timeout: 120_000,
412
+ maxBuffer: 10 * 1024 * 1024,
413
+ });
414
+
415
+ if (!stdout.trim()) {
416
+ return ok('No drift detected in Kubernetes resources.');
417
+ }
418
+ return ok(`DRIFT DETECTED\n\n${stdout}`);
419
+ }
420
+
421
+ case 'helm': {
422
+ // Use helm diff plugin if available, otherwise fall back to helm get.
423
+ const command = `helm list -A --output json`;
424
+ const { stdout } = await execAsync(command, {
425
+ timeout: 120_000,
426
+ maxBuffer: 10 * 1024 * 1024,
427
+ });
428
+ return ok(
429
+ `Helm releases:\n${stdout}\n\nNote: Install the helm-diff plugin for detailed drift detection.`
430
+ );
431
+ }
432
+ }
433
+ } catch (error: unknown) {
434
+ return err(`Drift detection failed: ${errorMessage(error)}`);
435
+ }
436
+ },
437
+ };
438
+
439
+ // ---------------------------------------------------------------------------
440
+ // 7. deploy_preview
441
+ // ---------------------------------------------------------------------------
442
+
443
+ const deployPreviewSchema = z.object({
444
+ action: z
445
+ .string()
446
+ .describe('The deployment action (e.g., terraform apply, kubectl apply, helm install)'),
447
+ workdir: z.string().describe('Working directory for the deployment'),
448
+ });
449
+
450
+ export const deployPreviewTool: ToolDefinition = {
451
+ name: 'deploy_preview',
452
+ description: 'Generate a dry-run preview of infrastructure changes with blast radius analysis.',
453
+ inputSchema: deployPreviewSchema,
454
+ permissionTier: 'auto_allow',
455
+ category: 'devops',
456
+
457
+ async execute(raw: unknown): Promise<ToolResult> {
458
+ try {
459
+ const input = deployPreviewSchema.parse(raw);
460
+ const actionLower = input.action.toLowerCase();
461
+
462
+ let command: string;
463
+
464
+ if (actionLower.includes('terraform')) {
465
+ command = `terraform -chdir=${input.workdir} plan -no-color`;
466
+ } else if (actionLower.includes('kubectl')) {
467
+ command = `kubectl apply --dry-run=client -f ${input.workdir} 2>&1`;
468
+ } else if (actionLower.includes('helm')) {
469
+ // For helm, use template to preview rendered manifests.
470
+ command = `helm template ${input.workdir}`;
471
+ } else {
472
+ return err(
473
+ `Unsupported action: ${input.action}. ` + 'Supported keywords: terraform, kubectl, helm.'
474
+ );
475
+ }
476
+
477
+ const { stdout, stderr } = await execAsync(command, {
478
+ timeout: 300_000,
479
+ maxBuffer: 10 * 1024 * 1024,
480
+ });
481
+
482
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
483
+
484
+ // Build a summary header.
485
+ const lines = [
486
+ '--- Deploy Preview (Dry Run) ---',
487
+ `Action: ${input.action}`,
488
+ `Workdir: ${input.workdir}`,
489
+ '',
490
+ combined || '(no changes detected)',
491
+ ];
492
+
493
+ return ok(lines.join('\n'));
494
+ } catch (error: unknown) {
495
+ return err(`Deploy preview failed: ${errorMessage(error)}`);
496
+ }
497
+ },
498
+ };
499
+
500
+ // ---------------------------------------------------------------------------
501
+ // 8. git
502
+ // ---------------------------------------------------------------------------
503
+
504
+ const gitSchema = z.object({
505
+ action: z
506
+ .enum(['status', 'add', 'commit', 'push', 'pull', 'branch', 'checkout', 'diff', 'log'])
507
+ .describe('The git sub-command to run'),
508
+ args: z.string().optional().describe('Additional CLI arguments'),
509
+ });
510
+
511
+ export const gitTool: ToolDefinition = {
512
+ name: 'git',
513
+ description:
514
+ 'Execute git operations. Supports status, add, commit, push, pull, branch, checkout, diff, and log.',
515
+ inputSchema: gitSchema,
516
+ permissionTier: 'ask_once',
517
+ category: 'devops',
518
+ isDestructive: true,
519
+
520
+ async execute(raw: unknown): Promise<ToolResult> {
521
+ try {
522
+ const input = gitSchema.parse(raw);
523
+
524
+ const parts: string[] = ['git', input.action];
525
+
526
+ if (input.args) {
527
+ parts.push(input.args);
528
+ }
529
+
530
+ const command = parts.join(' ');
531
+ const { stdout, stderr } = await execAsync(command, {
532
+ timeout: 60_000,
533
+ maxBuffer: 10 * 1024 * 1024,
534
+ });
535
+
536
+ const combined = [stdout, stderr].filter(Boolean).join('\n');
537
+ return ok(combined || '(no output)');
538
+ } catch (error: unknown) {
539
+ return err(`git command failed: ${errorMessage(error)}`);
540
+ }
541
+ },
542
+ };
543
+
544
+ // ---------------------------------------------------------------------------
545
+ // 9. task (subagent)
546
+ // ---------------------------------------------------------------------------
547
+
548
+ const taskSchema = z.object({
549
+ prompt: z.string().describe('The task for the subagent to perform'),
550
+ agent: z
551
+ .enum(['explore', 'infra', 'security', 'cost', 'general'])
552
+ .optional()
553
+ .default('general')
554
+ .describe('Subagent specialization to handle the task (default: general)'),
555
+ });
556
+
557
+ export const taskTool: ToolDefinition = {
558
+ name: 'task',
559
+ description:
560
+ 'Spawn a subagent to handle a specific task. The subagent runs with its own isolated context and returns results. Use for parallelizable research, code exploration, security audits, cost analysis, or infrastructure checks.',
561
+ inputSchema: taskSchema,
562
+ permissionTier: 'auto_allow',
563
+ category: 'devops',
564
+
565
+ async execute(raw: unknown): Promise<ToolResult> {
566
+ try {
567
+ const input = taskSchema.parse(raw);
568
+
569
+ // Get the LLM router from the app context
570
+ const { getAppContext } = await import('../../app');
571
+ const ctx = getAppContext();
572
+ if (!ctx) {
573
+ return err('App not initialised. Cannot spawn subagent.');
574
+ }
575
+
576
+ // Create and run the appropriate subagent
577
+ const { createSubagent } = await import('../../agent/subagents/index');
578
+ const subagent = createSubagent(input.agent as any);
579
+ const result = await subagent.run(input.prompt, ctx.router);
580
+
581
+ const header = [
582
+ `[Subagent: ${input.agent}]`,
583
+ `Turns: ${result.turns} | Tokens: ${result.totalTokens}`,
584
+ result.interrupted ? '(interrupted)' : '',
585
+ '---',
586
+ ]
587
+ .filter(Boolean)
588
+ .join('\n');
589
+
590
+ return ok(`${header}\n${result.output}`);
591
+ } catch (error: unknown) {
592
+ return err(`Subagent execution failed: ${errorMessage(error)}`);
593
+ }
594
+ },
595
+ };
596
+
597
+ // ---------------------------------------------------------------------------
598
+ // Aggregate export
599
+ // ---------------------------------------------------------------------------
600
+
601
+ /** All 9 DevOps tools as an ordered array. */
602
+ export const devopsTools: ToolDefinition[] = [
603
+ terraformTool,
604
+ kubectlTool,
605
+ helmTool,
606
+ cloudDiscoverTool,
607
+ costEstimateTool,
608
+ driftDetectTool,
609
+ deployPreviewTool,
610
+ gitTool,
611
+ taskTool,
612
+ ];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Tool Schemas — Barrel re-exports
3
+ *
4
+ * All tool definitions, types, and converter utilities for the
5
+ * Nimbus agentic tool-use system.
6
+ */
7
+
8
+ // Core types and registry
9
+ export {
10
+ type PermissionTier,
11
+ type ToolCategory,
12
+ type ToolResult,
13
+ type ToolDefinition,
14
+ type ToolInput,
15
+ type AnthropicTool,
16
+ type OpenAITool,
17
+ type GoogleTool,
18
+ PERMISSION_TIER_ORDER,
19
+ permissionTierIndex,
20
+ zodToJsonSchema,
21
+ toAnthropicTool,
22
+ toOpenAITool,
23
+ toGoogleTool,
24
+ ToolRegistry,
25
+ defaultToolRegistry,
26
+ } from './types';
27
+
28
+ // Standard tools (11)
29
+ export {
30
+ readFileTool,
31
+ editFileTool,
32
+ multiEditTool,
33
+ writeFileTool,
34
+ bashTool,
35
+ globTool,
36
+ grepTool,
37
+ listDirTool,
38
+ webfetchTool,
39
+ todoReadTool,
40
+ todoWriteTool,
41
+ standardTools,
42
+ } from './standard';
43
+
44
+ // DevOps tools (9)
45
+ export {
46
+ terraformTool,
47
+ kubectlTool,
48
+ helmTool,
49
+ cloudDiscoverTool,
50
+ costEstimateTool,
51
+ driftDetectTool,
52
+ deployPreviewTool,
53
+ gitTool,
54
+ taskTool,
55
+ devopsTools,
56
+ } from './devops';
57
+
58
+ // Converter utilities
59
+ export {
60
+ type Provider,
61
+ toAnthropicFormat,
62
+ toOpenAIFormat,
63
+ toGoogleFormat,
64
+ toProviderFormat,
65
+ getToolNames,
66
+ } from './converter';
67
+
68
+ // Combined all tools
69
+ import { standardTools } from './standard';
70
+ import { devopsTools } from './devops';
71
+
72
+ /** All 20 built-in tools (11 standard + 9 DevOps). MCP tools are added dynamically. */
73
+ export const allBuiltinTools = [...standardTools, ...devopsTools];