@build-astron-co/nimbus 0.2.0 → 0.4.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 (469) hide show
  1. package/bin/nimbus +26 -10
  2. package/bin/nimbus.cmd +41 -0
  3. package/bin/nimbus.mjs +70 -0
  4. package/completions/nimbus.bash +38 -0
  5. package/completions/nimbus.fish +48 -0
  6. package/completions/nimbus.zsh +81 -0
  7. package/dist/src/agent/compaction-agent.js +215 -0
  8. package/dist/src/agent/context-manager.js +385 -0
  9. package/dist/src/agent/context.js +322 -0
  10. package/dist/src/agent/deploy-preview.js +395 -0
  11. package/dist/src/agent/expand-files.js +95 -0
  12. package/dist/src/agent/index.js +18 -0
  13. package/dist/src/agent/loop.js +1535 -0
  14. package/dist/src/agent/modes.js +347 -0
  15. package/dist/src/agent/permissions.js +396 -0
  16. package/dist/src/agent/subagents/base.js +67 -0
  17. package/dist/src/agent/subagents/cost.js +45 -0
  18. package/dist/src/agent/subagents/explore.js +36 -0
  19. package/dist/src/agent/subagents/general.js +41 -0
  20. package/dist/src/agent/subagents/index.js +88 -0
  21. package/dist/src/agent/subagents/infra.js +52 -0
  22. package/dist/src/agent/subagents/security.js +60 -0
  23. package/dist/src/agent/system-prompt.js +860 -0
  24. package/dist/src/app.js +152 -0
  25. package/dist/src/audit/activity-log.js +209 -0
  26. package/dist/src/audit/compliance-checker.js +419 -0
  27. package/dist/src/audit/cost-tracker.js +231 -0
  28. package/dist/src/audit/index.js +10 -0
  29. package/dist/src/audit/security-scanner.js +490 -0
  30. package/dist/src/auth/guard.js +64 -0
  31. package/dist/src/auth/index.js +19 -0
  32. package/dist/src/auth/keychain.js +79 -0
  33. package/dist/src/auth/oauth.js +389 -0
  34. package/dist/src/auth/providers.js +415 -0
  35. package/dist/src/auth/sso.js +87 -0
  36. package/dist/src/auth/store.js +424 -0
  37. package/dist/src/auth/types.js +5 -0
  38. package/dist/src/cli/index.js +8 -0
  39. package/dist/src/cli/init.js +1048 -0
  40. package/dist/src/cli/openapi-spec.js +346 -0
  41. package/dist/src/cli/run.js +505 -0
  42. package/dist/src/cli/serve-auth.js +56 -0
  43. package/dist/src/cli/serve.js +432 -0
  44. package/dist/src/cli/web.js +50 -0
  45. package/dist/src/cli.js +1574 -0
  46. package/dist/src/clients/core-engine-client.js +156 -0
  47. package/dist/src/clients/enterprise-client.js +246 -0
  48. package/dist/src/clients/generator-client.js +219 -0
  49. package/dist/src/clients/git-client.js +367 -0
  50. package/dist/src/clients/github-client.js +229 -0
  51. package/dist/src/clients/helm-client.js +299 -0
  52. package/dist/src/clients/index.js +18 -0
  53. package/dist/src/clients/k8s-client.js +270 -0
  54. package/dist/src/clients/llm-client.js +119 -0
  55. package/dist/src/clients/rest-client.js +104 -0
  56. package/dist/src/clients/service-discovery.js +35 -0
  57. package/dist/src/clients/terraform-client.js +302 -0
  58. package/dist/src/clients/tools-client.js +1227 -0
  59. package/dist/src/clients/ws-client.js +93 -0
  60. package/dist/src/commands/alias.js +91 -0
  61. package/dist/src/commands/analyze/index.js +313 -0
  62. package/dist/src/commands/apply/helm.js +375 -0
  63. package/dist/src/commands/apply/index.js +176 -0
  64. package/dist/src/commands/apply/k8s.js +350 -0
  65. package/dist/src/commands/apply/terraform.js +465 -0
  66. package/dist/src/commands/ask.js +137 -0
  67. package/dist/src/commands/audit/index.js +322 -0
  68. package/dist/src/commands/auth-cloud.js +345 -0
  69. package/dist/src/commands/auth-list.js +112 -0
  70. package/dist/src/commands/auth-profile.js +104 -0
  71. package/dist/src/commands/auth-refresh.js +161 -0
  72. package/dist/src/commands/auth-status.js +122 -0
  73. package/dist/src/commands/aws/ec2.js +402 -0
  74. package/dist/src/commands/aws/iam.js +304 -0
  75. package/dist/src/commands/aws/index.js +108 -0
  76. package/dist/src/commands/aws/lambda.js +317 -0
  77. package/dist/src/commands/aws/rds.js +345 -0
  78. package/dist/src/commands/aws/s3.js +346 -0
  79. package/dist/src/commands/aws/vpc.js +302 -0
  80. package/dist/src/commands/aws-discover.js +413 -0
  81. package/dist/src/commands/aws-terraform.js +618 -0
  82. package/dist/src/commands/azure/aks.js +305 -0
  83. package/dist/src/commands/azure/functions.js +200 -0
  84. package/dist/src/commands/azure/index.js +93 -0
  85. package/dist/src/commands/azure/storage.js +378 -0
  86. package/dist/src/commands/azure/vm.js +291 -0
  87. package/dist/src/commands/billing/index.js +224 -0
  88. package/dist/src/commands/chat.js +259 -0
  89. package/dist/src/commands/completions.js +255 -0
  90. package/dist/src/commands/config.js +291 -0
  91. package/dist/src/commands/cost/cloud-cost-estimator.js +211 -0
  92. package/dist/src/commands/cost/estimator.js +73 -0
  93. package/dist/src/commands/cost/index.js +625 -0
  94. package/dist/src/commands/cost/parsers/terraform.js +234 -0
  95. package/dist/src/commands/cost/parsers/types.js +4 -0
  96. package/dist/src/commands/cost/pricing/aws.js +501 -0
  97. package/dist/src/commands/cost/pricing/azure.js +462 -0
  98. package/dist/src/commands/cost/pricing/gcp.js +359 -0
  99. package/dist/src/commands/cost/pricing/index.js +24 -0
  100. package/dist/src/commands/demo.js +196 -0
  101. package/dist/src/commands/deploy.js +215 -0
  102. package/dist/src/commands/doctor.js +1291 -0
  103. package/dist/src/commands/drift/index.js +674 -0
  104. package/dist/src/commands/explain.js +235 -0
  105. package/dist/src/commands/export.js +120 -0
  106. package/dist/src/commands/feedback.js +319 -0
  107. package/dist/src/commands/fix.js +263 -0
  108. package/dist/src/commands/fs/index.js +338 -0
  109. package/dist/src/commands/gcp/compute.js +266 -0
  110. package/dist/src/commands/gcp/functions.js +221 -0
  111. package/dist/src/commands/gcp/gke.js +357 -0
  112. package/dist/src/commands/gcp/iam.js +295 -0
  113. package/dist/src/commands/gcp/index.js +105 -0
  114. package/dist/src/commands/gcp/storage.js +232 -0
  115. package/dist/src/commands/generate-helm.js +1026 -0
  116. package/dist/src/commands/generate-k8s.js +1263 -0
  117. package/dist/src/commands/generate-terraform.js +1058 -0
  118. package/dist/src/commands/gh/index.js +663 -0
  119. package/dist/src/commands/git/index.js +1208 -0
  120. package/dist/src/commands/helm/index.js +985 -0
  121. package/dist/src/commands/help.js +639 -0
  122. package/dist/src/commands/history.js +120 -0
  123. package/dist/src/commands/import.js +782 -0
  124. package/dist/src/commands/incident.js +144 -0
  125. package/dist/src/commands/index.js +109 -0
  126. package/dist/src/commands/init.js +955 -0
  127. package/dist/src/commands/k8s/index.js +979 -0
  128. package/dist/src/commands/login.js +588 -0
  129. package/dist/src/commands/logout.js +61 -0
  130. package/dist/src/commands/logs.js +160 -0
  131. package/dist/src/commands/onboarding.js +382 -0
  132. package/dist/src/commands/pipeline.js +153 -0
  133. package/dist/src/commands/plan/display.js +216 -0
  134. package/dist/src/commands/plan/index.js +525 -0
  135. package/dist/src/commands/plugin.js +325 -0
  136. package/dist/src/commands/preview.js +356 -0
  137. package/dist/src/commands/profile.js +297 -0
  138. package/dist/src/commands/questionnaire.js +1021 -0
  139. package/dist/src/commands/resume.js +35 -0
  140. package/dist/src/commands/rollback.js +259 -0
  141. package/dist/src/commands/rollout.js +74 -0
  142. package/dist/src/commands/runbook.js +307 -0
  143. package/dist/src/commands/schedule.js +202 -0
  144. package/dist/src/commands/status.js +213 -0
  145. package/dist/src/commands/team/index.js +309 -0
  146. package/dist/src/commands/team-context.js +200 -0
  147. package/dist/src/commands/template.js +204 -0
  148. package/dist/src/commands/tf/index.js +989 -0
  149. package/dist/src/commands/upgrade.js +515 -0
  150. package/dist/src/commands/usage/index.js +118 -0
  151. package/dist/src/commands/version.js +145 -0
  152. package/dist/src/commands/watch.js +127 -0
  153. package/dist/src/compat/index.js +2 -0
  154. package/dist/src/compat/runtime.js +10 -0
  155. package/dist/src/compat/sqlite.js +144 -0
  156. package/dist/src/config/index.js +6 -0
  157. package/dist/src/config/manager.js +469 -0
  158. package/dist/src/config/mode-store.js +57 -0
  159. package/dist/src/config/profiles.js +66 -0
  160. package/dist/src/config/safety-policy.js +251 -0
  161. package/dist/src/config/schema.js +107 -0
  162. package/dist/src/config/types.js +311 -0
  163. package/dist/src/config/workspace-state.js +38 -0
  164. package/dist/src/context/context-db.js +138 -0
  165. package/dist/src/demo/index.js +295 -0
  166. package/dist/src/demo/scenarios/full-journey.js +226 -0
  167. package/dist/src/demo/scenarios/getting-started.js +124 -0
  168. package/dist/src/demo/scenarios/helm-release.js +334 -0
  169. package/dist/src/demo/scenarios/k8s-deployment.js +190 -0
  170. package/dist/src/demo/scenarios/terraform-vpc.js +167 -0
  171. package/dist/src/demo/types.js +6 -0
  172. package/dist/src/engine/cost-estimator.js +334 -0
  173. package/dist/src/engine/diagram-generator.js +192 -0
  174. package/dist/src/engine/drift-detector.js +688 -0
  175. package/dist/src/engine/executor.js +832 -0
  176. package/dist/src/engine/index.js +39 -0
  177. package/dist/src/engine/orchestrator.js +436 -0
  178. package/dist/src/engine/planner.js +616 -0
  179. package/dist/src/engine/safety.js +609 -0
  180. package/dist/src/engine/verifier.js +664 -0
  181. package/dist/src/enterprise/audit.js +241 -0
  182. package/dist/src/enterprise/auth.js +189 -0
  183. package/dist/src/enterprise/billing.js +512 -0
  184. package/dist/src/enterprise/index.js +16 -0
  185. package/dist/src/enterprise/teams.js +315 -0
  186. package/dist/src/generator/best-practices.js +1375 -0
  187. package/dist/src/generator/helm.js +495 -0
  188. package/dist/src/generator/index.js +11 -0
  189. package/dist/src/generator/intent-parser.js +420 -0
  190. package/dist/src/generator/kubernetes.js +773 -0
  191. package/dist/src/generator/terraform.js +1472 -0
  192. package/dist/src/history/index.js +6 -0
  193. package/dist/src/history/manager.js +199 -0
  194. package/dist/src/history/types.js +6 -0
  195. package/dist/src/hooks/config.js +318 -0
  196. package/dist/src/hooks/engine.js +317 -0
  197. package/dist/src/hooks/index.js +2 -0
  198. package/dist/src/llm/auth-bridge.js +157 -0
  199. package/dist/src/llm/circuit-breaker.js +116 -0
  200. package/dist/src/llm/config-loader.js +172 -0
  201. package/dist/src/llm/cost-calculator.js +137 -0
  202. package/dist/src/llm/index.js +7 -0
  203. package/dist/src/llm/model-aliases.js +99 -0
  204. package/dist/src/llm/provider-registry.js +57 -0
  205. package/dist/src/llm/providers/anthropic.js +430 -0
  206. package/dist/src/llm/providers/bedrock.js +409 -0
  207. package/dist/src/llm/providers/google.js +344 -0
  208. package/dist/src/llm/providers/ollama.js +661 -0
  209. package/dist/src/llm/providers/openai-compatible.js +289 -0
  210. package/dist/src/llm/providers/openai.js +284 -0
  211. package/dist/src/llm/providers/openrouter.js +293 -0
  212. package/dist/src/llm/router.js +844 -0
  213. package/dist/src/llm/types.js +69 -0
  214. package/dist/src/lsp/client.js +239 -0
  215. package/dist/src/lsp/languages.js +95 -0
  216. package/dist/src/lsp/manager.js +243 -0
  217. package/dist/src/mcp/client.js +289 -0
  218. package/dist/src/mcp/index.js +5 -0
  219. package/dist/src/mcp/manager.js +113 -0
  220. package/dist/src/nimbus.js +212 -0
  221. package/dist/src/plugins/index.js +13 -0
  222. package/dist/src/plugins/loader.js +280 -0
  223. package/dist/src/plugins/manager.js +282 -0
  224. package/dist/src/plugins/types.js +23 -0
  225. package/dist/src/scanners/cicd-scanner.js +230 -0
  226. package/dist/src/scanners/cloud-scanner.js +415 -0
  227. package/dist/src/scanners/framework-scanner.js +430 -0
  228. package/dist/src/scanners/iac-scanner.js +350 -0
  229. package/dist/src/scanners/index.js +454 -0
  230. package/dist/src/scanners/language-scanner.js +258 -0
  231. package/dist/src/scanners/package-manager-scanner.js +252 -0
  232. package/dist/src/scanners/types.js +6 -0
  233. package/dist/src/sessions/manager.js +395 -0
  234. package/dist/src/sessions/types.js +4 -0
  235. package/dist/src/sharing/sync.js +238 -0
  236. package/dist/src/sharing/viewer.js +131 -0
  237. package/dist/src/snapshots/index.js +1 -0
  238. package/dist/src/snapshots/manager.js +432 -0
  239. package/dist/src/state/artifacts.js +94 -0
  240. package/dist/src/state/audit.js +73 -0
  241. package/dist/src/state/billing.js +126 -0
  242. package/dist/src/state/checkpoints.js +81 -0
  243. package/dist/src/state/config.js +58 -0
  244. package/dist/src/state/conversations.js +7 -0
  245. package/dist/src/state/credentials.js +96 -0
  246. package/dist/src/state/db.js +53 -0
  247. package/dist/src/state/index.js +23 -0
  248. package/dist/src/state/messages.js +76 -0
  249. package/dist/src/state/projects.js +92 -0
  250. package/dist/src/state/schema.js +233 -0
  251. package/dist/src/state/sessions.js +79 -0
  252. package/dist/src/state/teams.js +131 -0
  253. package/dist/src/telemetry.js +91 -0
  254. package/dist/src/tools/aws-ops.js +747 -0
  255. package/dist/src/tools/azure-ops.js +491 -0
  256. package/dist/src/tools/file-ops.js +451 -0
  257. package/dist/src/tools/gcp-ops.js +559 -0
  258. package/dist/src/tools/git-ops.js +557 -0
  259. package/dist/src/tools/github-ops.js +460 -0
  260. package/dist/src/tools/helm-ops.js +634 -0
  261. package/dist/src/tools/index.js +16 -0
  262. package/dist/src/tools/k8s-ops.js +579 -0
  263. package/dist/src/tools/schemas/converter.js +129 -0
  264. package/dist/src/tools/schemas/devops.js +3319 -0
  265. package/dist/src/tools/schemas/index.js +19 -0
  266. package/dist/src/tools/schemas/standard.js +966 -0
  267. package/dist/src/tools/schemas/types.js +409 -0
  268. package/dist/src/tools/spawn-exec.js +109 -0
  269. package/dist/src/tools/terraform-ops.js +627 -0
  270. package/dist/src/types/config.js +1 -0
  271. package/dist/src/types/drift.js +4 -0
  272. package/dist/src/types/enterprise.js +5 -0
  273. package/dist/src/types/index.js +14 -0
  274. package/dist/src/types/plan.js +1 -0
  275. package/dist/src/types/request.js +1 -0
  276. package/dist/src/types/response.js +1 -0
  277. package/dist/src/types/service.js +1 -0
  278. package/dist/src/ui/App.js +1672 -0
  279. package/dist/src/ui/DeployPreview.js +60 -0
  280. package/dist/src/ui/FileDiffModal.js +108 -0
  281. package/dist/src/ui/Header.js +46 -0
  282. package/dist/src/ui/HelpModal.js +9 -0
  283. package/dist/src/ui/InputBox.js +408 -0
  284. package/dist/src/ui/MessageList.js +795 -0
  285. package/dist/src/ui/PermissionPrompt.js +72 -0
  286. package/dist/src/ui/StatusBar.js +109 -0
  287. package/dist/src/ui/TerminalPane.js +31 -0
  288. package/dist/src/ui/ToolCallDisplay.js +303 -0
  289. package/dist/src/ui/TreePane.js +83 -0
  290. package/dist/src/ui/chat-ui.js +721 -0
  291. package/dist/src/ui/index.js +11 -0
  292. package/dist/src/ui/ink/index.js +1325 -0
  293. package/dist/src/ui/streaming.js +137 -0
  294. package/dist/src/ui/theme.js +78 -0
  295. package/dist/src/ui/types.js +7 -0
  296. package/dist/src/utils/analytics.js +61 -0
  297. package/dist/src/utils/cost-warning.js +25 -0
  298. package/dist/src/utils/env.js +42 -0
  299. package/dist/src/utils/errors.js +54 -0
  300. package/dist/src/utils/event-bus.js +22 -0
  301. package/dist/src/utils/index.js +16 -0
  302. package/dist/src/utils/logger.js +150 -0
  303. package/dist/src/utils/rate-limiter.js +90 -0
  304. package/dist/src/utils/service-auth.js +36 -0
  305. package/dist/src/utils/validation.js +39 -0
  306. package/dist/src/version.js +3 -0
  307. package/dist/src/watcher/index.js +192 -0
  308. package/dist/src/wizard/approval.js +275 -0
  309. package/dist/src/wizard/index.js +13 -0
  310. package/dist/src/wizard/prompts.js +273 -0
  311. package/dist/src/wizard/types.js +4 -0
  312. package/dist/src/wizard/ui.js +453 -0
  313. package/dist/src/wizard/wizard.js +227 -0
  314. package/package.json +31 -23
  315. package/src/__tests__/alias.test.ts +133 -0
  316. package/src/__tests__/app.test.ts +1 -1
  317. package/src/__tests__/audit.test.ts +1 -1
  318. package/src/__tests__/circuit-breaker.test.ts +1 -1
  319. package/src/__tests__/cli-run.test.ts +237 -1
  320. package/src/__tests__/compat-sqlite.test.ts +68 -0
  321. package/src/__tests__/context-manager.test.ts +131 -1
  322. package/src/__tests__/context.test.ts +1 -1
  323. package/src/__tests__/devops-terminal-gaps.test.ts +718 -0
  324. package/src/__tests__/doctor.test.ts +48 -0
  325. package/src/__tests__/enterprise.test.ts +1 -1
  326. package/src/__tests__/export.test.ts +236 -0
  327. package/src/__tests__/gap-11-18-20.test.ts +958 -0
  328. package/src/__tests__/generator.test.ts +1 -1
  329. package/src/__tests__/helm-streaming.test.ts +127 -0
  330. package/src/__tests__/hooks.test.ts +1 -1
  331. package/src/__tests__/incident.test.ts +179 -0
  332. package/src/__tests__/init.test.ts +55 -4
  333. package/src/__tests__/intent-parser.test.ts +1 -1
  334. package/src/__tests__/llm-router.test.ts +1 -1
  335. package/src/__tests__/logs.test.ts +107 -0
  336. package/src/__tests__/loop-errors.test.ts +244 -0
  337. package/src/__tests__/lsp.test.ts +1 -1
  338. package/src/__tests__/modes.test.ts +1 -1
  339. package/src/__tests__/perf-optimizations.test.ts +847 -0
  340. package/src/__tests__/permissions.test.ts +1 -1
  341. package/src/__tests__/pipeline.test.ts +50 -0
  342. package/src/__tests__/polish-phase3.test.ts +340 -0
  343. package/src/__tests__/profile.test.ts +237 -0
  344. package/src/__tests__/rollback.test.ts +83 -0
  345. package/src/__tests__/runbook.test.ts +219 -0
  346. package/src/__tests__/schedule.test.ts +206 -0
  347. package/src/__tests__/serve.test.ts +1 -1
  348. package/src/__tests__/sessions.test.ts +96 -1
  349. package/src/__tests__/sharing.test.ts +53 -1
  350. package/src/__tests__/snapshots.test.ts +1 -1
  351. package/src/__tests__/standalone-migration.test.ts +199 -0
  352. package/src/__tests__/state-db.test.ts +1 -1
  353. package/src/__tests__/status.test.ts +158 -0
  354. package/src/__tests__/stream-with-tools.test.ts +71 -25
  355. package/src/__tests__/subagents.test.ts +1 -1
  356. package/src/__tests__/system-prompt.test.ts +82 -3
  357. package/src/__tests__/terminal-gap-v2.test.ts +395 -0
  358. package/src/__tests__/terminal-parity.test.ts +393 -0
  359. package/src/__tests__/tf-apply.test.ts +187 -0
  360. package/src/__tests__/tool-converter.test.ts +1 -1
  361. package/src/__tests__/tool-schemas.test.ts +209 -4
  362. package/src/__tests__/tools.test.ts +4 -3
  363. package/src/__tests__/version-json.test.ts +184 -0
  364. package/src/__tests__/version.test.ts +1 -1
  365. package/src/__tests__/watch.test.ts +129 -0
  366. package/src/agent/compaction-agent.ts +40 -1
  367. package/src/agent/context-manager.ts +67 -3
  368. package/src/agent/deploy-preview.ts +62 -1
  369. package/src/agent/expand-files.ts +108 -0
  370. package/src/agent/loop.ts +1312 -31
  371. package/src/agent/permissions.ts +51 -4
  372. package/src/agent/system-prompt.ts +573 -19
  373. package/src/app.ts +58 -0
  374. package/src/audit/security-scanner.ts +45 -0
  375. package/src/auth/keychain.ts +82 -0
  376. package/src/auth/oauth.ts +15 -5
  377. package/src/cli/init.ts +378 -5
  378. package/src/cli/run.ts +407 -16
  379. package/src/cli/serve.ts +78 -1
  380. package/src/cli/web.ts +10 -6
  381. package/src/cli.ts +312 -1
  382. package/src/clients/service-discovery.ts +30 -25
  383. package/src/commands/alias.ts +100 -0
  384. package/src/commands/audit/index.ts +121 -2
  385. package/src/commands/auth-cloud.ts +113 -0
  386. package/src/commands/auth-refresh.ts +187 -0
  387. package/src/commands/aws-discover.ts +144 -251
  388. package/src/commands/aws-terraform.ts +68 -118
  389. package/src/commands/chat.ts +9 -3
  390. package/src/commands/completions.ts +268 -0
  391. package/src/commands/config.ts +26 -0
  392. package/src/commands/cost/index.ts +218 -2
  393. package/src/commands/deploy.ts +260 -0
  394. package/src/commands/doctor.ts +744 -152
  395. package/src/commands/drift/index.ts +371 -23
  396. package/src/commands/export.ts +146 -0
  397. package/src/commands/generate-k8s.ts +9 -61
  398. package/src/commands/generate-terraform.ts +191 -449
  399. package/src/commands/help.ts +212 -36
  400. package/src/commands/history.ts +8 -1
  401. package/src/commands/incident.ts +166 -0
  402. package/src/commands/init.ts +5 -0
  403. package/src/commands/login.ts +86 -1
  404. package/src/commands/logs.ts +167 -0
  405. package/src/commands/onboarding.ts +211 -34
  406. package/src/commands/pipeline.ts +186 -0
  407. package/src/commands/plugin.ts +398 -0
  408. package/src/commands/profile.ts +342 -0
  409. package/src/commands/questionnaire.ts +0 -98
  410. package/src/commands/resume.ts +26 -34
  411. package/src/commands/rollback.ts +315 -0
  412. package/src/commands/rollout.ts +88 -0
  413. package/src/commands/runbook.ts +346 -0
  414. package/src/commands/schedule.ts +236 -0
  415. package/src/commands/status.ts +252 -0
  416. package/src/commands/team-context.ts +220 -0
  417. package/src/commands/template.ts +58 -57
  418. package/src/commands/tf/index.ts +70 -11
  419. package/src/commands/upgrade.ts +57 -0
  420. package/src/commands/version.ts +54 -50
  421. package/src/commands/watch.ts +153 -0
  422. package/src/compat/runtime.ts +1 -1
  423. package/src/compat/sqlite.ts +75 -5
  424. package/src/config/mode-store.ts +62 -0
  425. package/src/config/profiles.ts +84 -0
  426. package/src/config/types.ts +83 -1
  427. package/src/config/workspace-state.ts +53 -0
  428. package/src/engine/cost-estimator.ts +52 -10
  429. package/src/engine/executor.ts +33 -2
  430. package/src/engine/planner.ts +68 -1
  431. package/src/generator/terraform.ts +8 -0
  432. package/src/history/manager.ts +2 -74
  433. package/src/hooks/engine.ts +5 -4
  434. package/src/llm/cost-calculator.ts +2 -2
  435. package/src/llm/providers/anthropic.ts +50 -21
  436. package/src/llm/router.ts +76 -7
  437. package/src/lsp/languages.ts +3 -0
  438. package/src/lsp/manager.ts +21 -5
  439. package/src/nimbus.ts +37 -18
  440. package/src/sessions/manager.ts +108 -1
  441. package/src/sharing/sync.ts +4 -0
  442. package/src/sharing/viewer.ts +66 -0
  443. package/src/tools/file-ops.ts +22 -0
  444. package/src/tools/schemas/devops.ts +3007 -117
  445. package/src/tools/schemas/standard.ts +5 -1
  446. package/src/tools/schemas/types.ts +31 -1
  447. package/src/tools/spawn-exec.ts +148 -0
  448. package/src/ui/App.tsx +1183 -66
  449. package/src/ui/DeployPreview.tsx +62 -57
  450. package/src/ui/FileDiffModal.tsx +162 -0
  451. package/src/ui/Header.tsx +87 -24
  452. package/src/ui/HelpModal.tsx +57 -0
  453. package/src/ui/InputBox.tsx +163 -10
  454. package/src/ui/MessageList.tsx +487 -40
  455. package/src/ui/PermissionPrompt.tsx +17 -5
  456. package/src/ui/StatusBar.tsx +122 -3
  457. package/src/ui/TerminalPane.tsx +84 -0
  458. package/src/ui/ToolCallDisplay.tsx +252 -18
  459. package/src/ui/TreePane.tsx +132 -0
  460. package/src/ui/chat-ui.ts +41 -44
  461. package/src/ui/ink/index.ts +771 -38
  462. package/src/ui/streaming.ts +1 -1
  463. package/src/ui/theme.ts +104 -0
  464. package/src/ui/types.ts +18 -0
  465. package/src/version.ts +1 -1
  466. package/src/watcher/index.ts +66 -15
  467. package/src/wizard/types.ts +1 -0
  468. package/src/wizard/ui.ts +1 -1
  469. package/tsconfig.json +2 -2
@@ -0,0 +1,252 @@
1
+ /**
2
+ * nimbus status — Infrastructure Status Dashboard
3
+ *
4
+ * Runs concurrent checks against common DevOps CLIs and shows a dashboard
5
+ * of the current cloud/infra context.
6
+ *
7
+ * G18: New command added to the gap fix plan.
8
+ *
9
+ * Usage:
10
+ * nimbus status
11
+ * nimbus status --json
12
+ */
13
+
14
+ import { ui } from '../wizard';
15
+
16
+ /** Options for the status command. */
17
+ export interface StatusOptions {
18
+ /** Output as JSON instead of table. */
19
+ json?: boolean;
20
+ /** L3: Show full snapshot including LLM config and active profile. */
21
+ verbose?: boolean;
22
+ }
23
+
24
+ interface StatusInfo {
25
+ k8sContext?: string;
26
+ tfWorkspace?: string;
27
+ awsAccount?: string;
28
+ awsRegion?: string;
29
+ gcpProject?: string;
30
+ lastDriftScan?: string;
31
+ // C2 enhancements
32
+ sessionCount?: number | string;
33
+ model?: string;
34
+ provider?: string;
35
+ nimbusMdSize?: number;
36
+ nimbusMdFound?: boolean;
37
+ dbSizeMB?: number;
38
+ // M1: Helm and pod health
39
+ helmFailedCount?: number;
40
+ helmTotalCount?: number;
41
+ unhealthyPodCount?: number;
42
+ errors: string[];
43
+ }
44
+
45
+ /**
46
+ * Run the nimbus status command.
47
+ * Checks k8s context, tf workspace, AWS identity, and GCP project concurrently.
48
+ */
49
+ export async function statusCommand(options: StatusOptions = {}): Promise<void> {
50
+ const { execFileSync } = await import('node:child_process');
51
+ const { existsSync, statSync, readFileSync } = await import('node:fs');
52
+ const { join } = await import('node:path');
53
+ const { homedir } = await import('node:os');
54
+
55
+ const info: StatusInfo = { errors: [] };
56
+
57
+ const run = (cmd: string, args: string[]): string | undefined => {
58
+ try {
59
+ return execFileSync(cmd, args, {
60
+ encoding: 'utf-8',
61
+ timeout: 8000,
62
+ stdio: ['pipe', 'pipe', 'pipe'],
63
+ }).trim();
64
+ } catch {
65
+ return undefined;
66
+ }
67
+ };
68
+
69
+ // Run all checks concurrently (M1: add helm + pod health)
70
+ const [k8sCtx, tfWs, awsIdentityRaw, gcpProject, helmListRaw, podUnhealthyRaw] = await Promise.allSettled([
71
+ Promise.resolve(run('kubectl', ['config', 'current-context'])),
72
+ Promise.resolve(run('terraform', ['workspace', 'show'])),
73
+ Promise.resolve(run('aws', ['sts', 'get-caller-identity', '--output', 'json'])),
74
+ Promise.resolve(run('gcloud', ['config', 'get-value', 'project'])),
75
+ Promise.resolve(run('helm', ['list', '-A', '--output', 'json'])),
76
+ Promise.resolve(run('kubectl', ['get', 'pods', '-A', '--field-selector=status.phase!=Running', '--no-headers'])),
77
+ ]);
78
+
79
+ if (k8sCtx.status === 'fulfilled' && k8sCtx.value) {
80
+ info.k8sContext = k8sCtx.value;
81
+ }
82
+ if (tfWs.status === 'fulfilled' && tfWs.value) {
83
+ info.tfWorkspace = tfWs.value;
84
+ }
85
+ if (awsIdentityRaw.status === 'fulfilled' && awsIdentityRaw.value) {
86
+ try {
87
+ const identity = JSON.parse(awsIdentityRaw.value);
88
+ info.awsAccount = identity.Account;
89
+ // Try to get region separately
90
+ const region = run('aws', ['configure', 'get', 'region']);
91
+ if (region) info.awsRegion = region;
92
+ } catch {
93
+ // Could not parse AWS identity
94
+ }
95
+ }
96
+ if (gcpProject.status === 'fulfilled' && gcpProject.value && gcpProject.value !== '(unset)') {
97
+ info.gcpProject = gcpProject.value;
98
+ }
99
+
100
+ // M1: Parse helm release health
101
+ if (helmListRaw.status === 'fulfilled' && helmListRaw.value) {
102
+ try {
103
+ const releases = JSON.parse(helmListRaw.value) as Array<{ status: string }>;
104
+ info.helmTotalCount = releases.length;
105
+ info.helmFailedCount = releases.filter(r => r.status !== 'deployed').length;
106
+ } catch { /* non-critical */ }
107
+ }
108
+
109
+ // M1: Parse unhealthy pod count
110
+ if (podUnhealthyRaw.status === 'fulfilled' && podUnhealthyRaw.value) {
111
+ const lines = podUnhealthyRaw.value.trim().split('\n').filter(Boolean);
112
+ info.unhealthyPodCount = lines.length;
113
+ }
114
+
115
+ // Check last drift scan from SQLite + session count
116
+ try {
117
+ const { getDb } = await import('../state/db');
118
+ const db = getDb();
119
+ const row = db.prepare(`
120
+ SELECT created_at FROM sessions
121
+ ORDER BY created_at DESC LIMIT 1
122
+ `).get() as { created_at?: string } | undefined;
123
+ if (row?.created_at) {
124
+ info.lastDriftScan = row.created_at;
125
+ }
126
+
127
+ // C2: Count sessions
128
+ const countRow = db.prepare('SELECT COUNT(*) as cnt FROM sessions').get() as { cnt?: number } | undefined;
129
+ info.sessionCount = countRow?.cnt ?? 0;
130
+ } catch {
131
+ // DB not available
132
+ info.sessionCount = 'N/A';
133
+ }
134
+
135
+ // C2: Read model and provider from ~/.nimbus/config.json
136
+ const configJsonPath = join(homedir(), '.nimbus', 'config.json');
137
+ try {
138
+ if (existsSync(configJsonPath)) {
139
+ const configRaw = readFileSync(configJsonPath, 'utf-8');
140
+ const config = JSON.parse(configRaw) as Record<string, unknown>;
141
+ info.model = typeof config.model === 'string' ? config.model : 'claude-sonnet-4-6';
142
+ info.provider = typeof config.provider === 'string' ? config.provider : 'anthropic';
143
+ } else {
144
+ info.model = 'claude-sonnet-4-6';
145
+ info.provider = 'anthropic';
146
+ }
147
+ } catch {
148
+ info.model = 'claude-sonnet-4-6';
149
+ info.provider = 'anthropic';
150
+ }
151
+
152
+ // C2: Check NIMBUS.md in cwd
153
+ const nimbusMdPaths = [
154
+ join(process.cwd(), 'NIMBUS.md'),
155
+ join(process.cwd(), '.nimbus', 'NIMBUS.md'),
156
+ ];
157
+ for (const p of nimbusMdPaths) {
158
+ try {
159
+ if (existsSync(p)) {
160
+ const stat = statSync(p);
161
+ info.nimbusMdFound = true;
162
+ info.nimbusMdSize = stat.size;
163
+ break;
164
+ }
165
+ } catch { /* skip */ }
166
+ }
167
+
168
+ // C2: Get DB file size
169
+ const dbPath = join(homedir(), '.nimbus', 'nimbus.db');
170
+ try {
171
+ if (existsSync(dbPath)) {
172
+ const stat = statSync(dbPath);
173
+ info.dbSizeMB = Math.round((stat.size / (1024 * 1024)) * 10) / 10;
174
+ }
175
+ } catch { /* skip */ }
176
+
177
+ if (options.json) {
178
+ console.log(JSON.stringify(info, null, 2));
179
+ return;
180
+ }
181
+
182
+ // Pretty-print dashboard
183
+ // Build session line
184
+ const sessionLine = typeof info.sessionCount === 'number'
185
+ ? `${info.sessionCount} sessions`
186
+ : 'N/A';
187
+
188
+ // Build NIMBUS.md line
189
+ const nimbusMdLine = info.nimbusMdFound && info.nimbusMdSize !== undefined
190
+ ? ` loaded (${info.nimbusMdSize >= 1024
191
+ ? `${(info.nimbusMdSize / 1024).toFixed(1)}KB`
192
+ : `${info.nimbusMdSize}B`})`
193
+ : ' not found';
194
+
195
+ // Build DB size line
196
+ const dbLine = info.dbSizeMB !== undefined
197
+ ? `~/.nimbus/nimbus.db (${info.dbSizeMB}MB)`
198
+ : '~/.nimbus/nimbus.db (not found)';
199
+
200
+ ui.newLine();
201
+ ui.box({
202
+ title: 'Nimbus Infrastructure Status',
203
+ content: [
204
+ '',
205
+ ` Session: ${sessionLine}`,
206
+ ` Model: ${info.model ?? 'claude-sonnet-4-6'}`,
207
+ ` Provider: ${info.provider ?? 'anthropic'} \u2713`,
208
+ '',
209
+ ' Infrastructure:',
210
+ info.k8sContext
211
+ ? ` Kubernetes: context=${info.k8sContext}`
212
+ : ' Kubernetes: (not configured)',
213
+ info.tfWorkspace
214
+ ? ` Terraform: workspace=${info.tfWorkspace} \u2713 initialized`
215
+ : ' Terraform: (not in a terraform directory)',
216
+ info.awsAccount
217
+ ? ` AWS: account=${info.awsAccount}${info.awsRegion ? ` region=${info.awsRegion}` : ''}`
218
+ : ' AWS: (not configured)',
219
+ info.gcpProject
220
+ ? ` GCP: project=${info.gcpProject}`
221
+ : '',
222
+ // M1: Helm release health
223
+ ...(info.helmTotalCount !== undefined
224
+ ? [
225
+ info.helmFailedCount && info.helmFailedCount > 0
226
+ ? ` [!] ${info.helmFailedCount} Helm release(s) in failed state (${info.helmTotalCount} total)`
227
+ : ` Helm: ${info.helmTotalCount} release(s) deployed`,
228
+ ]
229
+ : []),
230
+ // M1: Pod health warnings
231
+ ...(info.unhealthyPodCount !== undefined && info.unhealthyPodCount > 0
232
+ ? [` [!] ${info.unhealthyPodCount} pod(s) not running`]
233
+ : []),
234
+ '',
235
+ ` NIMBUS.md:${nimbusMdLine}`,
236
+ ` DB: ${dbLine}`,
237
+ '',
238
+ info.lastDriftScan
239
+ ? ` Last session: ${new Date(info.lastDriftScan).toLocaleString()}`
240
+ : ' Last session: (none)',
241
+ '',
242
+ ' Quick actions: nimbus plan | nimbus apply | nimbus logs',
243
+ '',
244
+ ].filter(line => line !== undefined) as string[],
245
+ style: 'rounded',
246
+ borderColor: 'cyan',
247
+ padding: 0,
248
+ });
249
+ ui.newLine();
250
+ }
251
+
252
+ export default statusCommand;
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Team NIMBUS.md Distribution (L6)
3
+ *
4
+ * Share project context files with team members.
5
+ *
6
+ * nimbus team-context push — share current NIMBUS.md via sharing service
7
+ * nimbus team-context pull <url> — download NIMBUS.md, show diff, prompt before overwrite
8
+ *
9
+ * Also supports NIMBUS_INSTRUCTIONS_URL env var: on startup, fetch that URL
10
+ * and use as NIMBUS.md if local file not present.
11
+ */
12
+
13
+ import * as fs from 'node:fs';
14
+ import * as path from 'node:path';
15
+ import { ui } from '../wizard/ui';
16
+
17
+ const NIMBUS_MD_PATHS = [
18
+ path.join(process.cwd(), 'NIMBUS.md'),
19
+ path.join(process.cwd(), '.nimbus', 'NIMBUS.md'),
20
+ ];
21
+
22
+ function findLocalNimbusMd(): string | null {
23
+ for (const p of NIMBUS_MD_PATHS) {
24
+ if (fs.existsSync(p)) return p;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function computeDiff(oldContent: string, newContent: string): string {
30
+ const oldLines = oldContent.split('\n');
31
+ const newLines = newContent.split('\n');
32
+ const lines: string[] = [];
33
+
34
+ const maxLen = Math.max(oldLines.length, newLines.length);
35
+ for (let i = 0; i < maxLen; i++) {
36
+ const old = oldLines[i];
37
+ const newLine = newLines[i];
38
+ if (old === undefined) {
39
+ lines.push(`+ ${newLine}`);
40
+ } else if (newLine === undefined) {
41
+ lines.push(`- ${old}`);
42
+ } else if (old !== newLine) {
43
+ lines.push(`- ${old}`);
44
+ lines.push(`+ ${newLine}`);
45
+ }
46
+ }
47
+ return lines.join('\n');
48
+ }
49
+
50
+ /** Path to the team.md file in the current project's .nimbus/ directory */
51
+ const TEAM_MD_PATH = path.join(process.cwd(), '.nimbus', 'team.md');
52
+
53
+ /** Regex to strip lines containing sensitive info */
54
+ const SENSITIVE_PATTERN = /SENSITIVE:/i;
55
+
56
+ /** Strip lines containing "SENSITIVE:" from NIMBUS.md before sharing */
57
+ function stripSensitiveLines(content: string): string {
58
+ return content
59
+ .split('\n')
60
+ .filter(line => !SENSITIVE_PATTERN.test(line))
61
+ .join('\n');
62
+ }
63
+
64
+ /**
65
+ * Merge non-duplicate sections from teamContent into localContent.
66
+ * Sections are identified by Markdown headings (## ...).
67
+ */
68
+ function mergeSections(localContent: string, teamContent: string): string {
69
+ // Split into sections by heading
70
+ const sectionRegex = /(?=^#{1,3} .+$)/m;
71
+ const localSections = localContent.split(sectionRegex);
72
+ const teamSections = teamContent.split(sectionRegex);
73
+
74
+ const localHeadings = new Set(
75
+ localSections
76
+ .map(s => s.match(/^(#{1,3} .+)$/m)?.[1]?.trim())
77
+ .filter(Boolean)
78
+ );
79
+
80
+ const newSections = teamSections.filter(s => {
81
+ const heading = s.match(/^(#{1,3} .+)$/m)?.[1]?.trim();
82
+ return heading && !localHeadings.has(heading);
83
+ });
84
+
85
+ if (newSections.length === 0) return localContent;
86
+
87
+ const merged = localContent.trimEnd() + '\n\n' + newSections.join('\n').trimStart();
88
+ return merged;
89
+ }
90
+
91
+ export async function teamContextCommand(subcommand: string, args: string[]): Promise<void> {
92
+ switch (subcommand) {
93
+ case 'push': {
94
+ // Git-based push: read NIMBUS.md, strip SENSITIVE lines, write to .nimbus/team.md
95
+ const localPath = findLocalNimbusMd();
96
+ if (!localPath) {
97
+ ui.warning('No NIMBUS.md found in current directory. Run `nimbus init` first.');
98
+ return;
99
+ }
100
+
101
+ const content = fs.readFileSync(localPath, 'utf-8');
102
+ const sanitized = stripSensitiveLines(content);
103
+
104
+ // Ensure .nimbus/ directory exists
105
+ const nimbusDir = path.dirname(TEAM_MD_PATH);
106
+ if (!fs.existsSync(nimbusDir)) {
107
+ fs.mkdirSync(nimbusDir, { recursive: true });
108
+ }
109
+
110
+ fs.writeFileSync(TEAM_MD_PATH, sanitized, 'utf-8');
111
+ ui.print(`${ui.color('✓', 'green')} Written sanitized context to ${TEAM_MD_PATH}`);
112
+ ui.dim(`Removed all lines containing "SENSITIVE:"`);
113
+ ui.newLine();
114
+
115
+ // Stage the file with git
116
+ try {
117
+ const { execFileSync } = await import('node:child_process');
118
+ execFileSync('git', ['add', TEAM_MD_PATH], {
119
+ encoding: 'utf-8',
120
+ stdio: ['pipe', 'pipe', 'pipe'],
121
+ timeout: 10_000,
122
+ });
123
+ ui.print(`${ui.color('✓', 'green')} Staged .nimbus/team.md with git`);
124
+ ui.print(' Next step: git commit -m "chore: update team context" && git push');
125
+ } catch {
126
+ ui.dim('(git stage skipped — not a git repo or git not in PATH)');
127
+ ui.print(' Copy .nimbus/team.md to your repository and commit/push it manually.');
128
+ }
129
+
130
+ ui.newLine();
131
+ ui.info('Note: Add .nimbus/team.md to version control (do NOT add .nimbus/team.md to .gitignore).');
132
+ ui.dim('Add NIMBUS.md to .gitignore if it contains sensitive local config.');
133
+ break;
134
+ }
135
+
136
+ case 'pull': {
137
+ // Git-based pull: read .nimbus/team.md from cwd, diff, merge
138
+ if (!fs.existsSync(TEAM_MD_PATH)) {
139
+ ui.error(`.nimbus/team.md not found in ${process.cwd()}`);
140
+ ui.dim('Run `git pull` to fetch the latest team context, then re-run this command.');
141
+ return;
142
+ }
143
+
144
+ const teamContent = fs.readFileSync(TEAM_MD_PATH, 'utf-8');
145
+ const targetPath = NIMBUS_MD_PATHS[0];
146
+ const existing = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, 'utf-8') : null;
147
+
148
+ if (existing) {
149
+ const diff = computeDiff(existing, teamContent);
150
+ if (!diff) {
151
+ ui.info('NIMBUS.md is already up to date with team.md.');
152
+ return;
153
+ }
154
+
155
+ ui.header('NIMBUS.md vs .nimbus/team.md diff');
156
+ for (const line of diff.split('\n')) {
157
+ if (line.startsWith('+')) {
158
+ ui.print(ui.color(line, 'green'));
159
+ } else if (line.startsWith('-')) {
160
+ ui.print(ui.color(line, 'red'));
161
+ } else {
162
+ ui.print(line);
163
+ }
164
+ }
165
+ ui.newLine();
166
+
167
+ // Prompt for confirmation
168
+ const { input: inputPrompt } = await import('../wizard/prompts');
169
+ const answer = await inputPrompt({
170
+ message: 'Merge new sections from team.md into NIMBUS.md? [y/N]',
171
+ defaultValue: 'N',
172
+ });
173
+ if (answer.toLowerCase() !== 'y') {
174
+ ui.info('Aborted — NIMBUS.md not changed.');
175
+ return;
176
+ }
177
+
178
+ // Merge: append non-duplicate sections
179
+ const merged = mergeSections(existing, teamContent);
180
+ fs.writeFileSync(targetPath, merged, 'utf-8');
181
+ ui.print(`${ui.color('✓', 'green')} Merged team context into ${targetPath}`);
182
+ } else {
183
+ // No local NIMBUS.md — use team.md as starting point
184
+ fs.writeFileSync(targetPath, teamContent, 'utf-8');
185
+ ui.print(`${ui.color('✓', 'green')} Created ${targetPath} from .nimbus/team.md`);
186
+ }
187
+
188
+ ui.newLine();
189
+ ui.dim('Note: Add NIMBUS.md to .gitignore to keep local config private.');
190
+ ui.dim('Add .nimbus/team.md to version control to share project context with team.');
191
+ break;
192
+ }
193
+
194
+ default:
195
+ ui.print('Usage: nimbus team-context <push|pull>');
196
+ ui.print('');
197
+ ui.print(' push Strip SENSITIVE: lines from NIMBUS.md, write to .nimbus/team.md, and git add');
198
+ ui.print(' pull Read .nimbus/team.md, diff against NIMBUS.md, merge on confirmation');
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Fetch NIMBUS.md from NIMBUS_INSTRUCTIONS_URL env var.
204
+ * Called on startup when no local NIMBUS.md exists.
205
+ */
206
+ export async function fetchRemoteNimbusMd(): Promise<string | null> {
207
+ const url = process.env.NIMBUS_INSTRUCTIONS_URL;
208
+ if (!url) return null;
209
+
210
+ const localPath = findLocalNimbusMd();
211
+ if (localPath) return null; // local file takes priority
212
+
213
+ try {
214
+ const response = await fetch(url, { signal: AbortSignal.timeout(10_000) });
215
+ if (!response.ok) return null;
216
+ return await response.text();
217
+ } catch {
218
+ return null;
219
+ }
220
+ }
@@ -4,11 +4,10 @@
4
4
  * CLI commands for managing infrastructure templates
5
5
  */
6
6
 
7
- import { RestClient } from '../clients';
7
+ import { getDb } from '../state/db';
8
+ import { randomUUID } from 'crypto';
8
9
  import { ui } from '../wizard/ui';
9
10
 
10
- const STATE_SERVICE_URL = process.env.STATE_SERVICE_URL || 'http://localhost:3004';
11
-
12
11
  export interface TemplateCommandOptions {
13
12
  type?: string;
14
13
  name?: string;
@@ -24,34 +23,35 @@ async function templateListCommand(options: TemplateCommandOptions = {}): Promis
24
23
  ui.startSpinner({ message: 'Fetching templates...' });
25
24
 
26
25
  try {
27
- const client = new RestClient(STATE_SERVICE_URL);
28
- const params = options.type ? `?type=${encodeURIComponent(options.type)}` : '';
29
- const result = await client.get<any>(`/api/state/templates${params}`);
30
-
31
- if (result.success && result.data) {
32
- const templates = Array.isArray(result.data) ? result.data : [];
33
- ui.stopSpinnerSuccess(`Found ${templates.length} template(s)`);
34
-
35
- if (templates.length > 0) {
36
- ui.table({
37
- columns: [
38
- { key: 'id', header: 'ID' },
39
- { key: 'name', header: 'Name' },
40
- { key: 'type', header: 'Type' },
41
- { key: 'createdAt', header: 'Created' },
42
- ],
43
- data: templates.map((t: any) => ({
44
- id: t.id?.substring(0, 8) || '-',
45
- name: t.name || '-',
46
- type: t.type || '-',
47
- createdAt: t.createdAt ? new Date(t.createdAt).toLocaleDateString() : '-',
48
- })),
49
- });
50
- } else {
51
- ui.info('No templates found. Use "nimbus template save" to create one.');
52
- }
26
+ const db = getDb();
27
+ const templates = db
28
+ .prepare(
29
+ 'SELECT * FROM templates WHERE (? IS NULL OR type=?) ORDER BY created_at DESC'
30
+ )
31
+ .all(options.type ?? null, options.type ?? null) as Array<Record<string, unknown>>;
32
+
33
+ ui.stopSpinnerSuccess(`Found ${templates.length} template(s)`);
34
+
35
+ if (templates.length > 0) {
36
+ ui.table({
37
+ columns: [
38
+ { key: 'id', header: 'ID' },
39
+ { key: 'name', header: 'Name' },
40
+ { key: 'type', header: 'Type' },
41
+ { key: 'createdAt', header: 'Created' },
42
+ ],
43
+ data: templates.map((t) => ({
44
+ id: typeof t.id === 'string' ? t.id.substring(0, 8) : '-',
45
+ name: typeof t.name === 'string' ? t.name : '-',
46
+ type: typeof t.type === 'string' ? t.type : '-',
47
+ createdAt:
48
+ typeof t.created_at === 'string'
49
+ ? new Date(t.created_at).toLocaleDateString()
50
+ : '-',
51
+ })),
52
+ });
53
53
  } else {
54
- ui.stopSpinnerFail('Failed to fetch templates');
54
+ ui.info('No templates found. Use "nimbus template save" to create one.');
55
55
  }
56
56
  } catch (error: any) {
57
57
  ui.stopSpinnerFail('Error fetching templates');
@@ -70,22 +70,31 @@ async function templateGetCommand(
70
70
  ui.startSpinner({ message: 'Fetching template...' });
71
71
 
72
72
  try {
73
- const client = new RestClient(STATE_SERVICE_URL);
74
- const result = await client.get<any>(`/api/state/templates/${encodeURIComponent(id)}`);
73
+ const db = getDb();
74
+ const template = db
75
+ .prepare('SELECT * FROM templates WHERE id LIKE ?')
76
+ .get(id + '%') as Record<string, unknown> | undefined;
75
77
 
76
- if (result.success && result.data) {
78
+ if (template) {
77
79
  ui.stopSpinnerSuccess('Template retrieved');
78
- const template = result.data;
79
80
 
80
81
  ui.print(` ${ui.color('Name:', 'cyan')} ${template.name || '-'}`);
81
82
  ui.print(` ${ui.color('Type:', 'cyan')} ${template.type || '-'}`);
82
83
  ui.print(` ${ui.color('ID:', 'cyan')} ${template.id || '-'}`);
83
- ui.print(` ${ui.color('Created:', 'cyan')} ${template.createdAt || '-'}`);
84
+ ui.print(` ${ui.color('Created:', 'cyan')} ${template.created_at || '-'}`);
85
+
86
+ let variables: Record<string, unknown> = {};
87
+ try {
88
+ variables =
89
+ typeof template.variables === 'string'
90
+ ? JSON.parse(template.variables)
91
+ : {};
92
+ } catch { /* ignore */ }
84
93
 
85
- if (template.variables && Object.keys(template.variables).length > 0) {
94
+ if (Object.keys(variables).length > 0) {
86
95
  ui.newLine();
87
96
  ui.print(` ${ui.color('Variables:', 'cyan')}`);
88
- for (const [key, val] of Object.entries(template.variables)) {
97
+ for (const [key, val] of Object.entries(variables)) {
89
98
  ui.print(` ${key}: ${JSON.stringify(val)}`);
90
99
  }
91
100
  }
@@ -134,22 +143,14 @@ async function templateSaveCommand(options: TemplateCommandOptions = {}): Promis
134
143
  ui.startSpinner({ message: 'Saving template...' });
135
144
 
136
145
  try {
137
- const client = new RestClient(STATE_SERVICE_URL);
138
- const result = await client.post<any>('/api/state/templates', {
139
- name: options.name,
140
- type: options.type || 'terraform',
141
- content,
142
- variables: {},
143
- });
144
-
145
- if (result.success) {
146
- ui.stopSpinnerSuccess(`Template "${options.name}" saved`);
147
- if (result.data?.id) {
148
- ui.info(`ID: ${result.data.id}`);
149
- }
150
- } else {
151
- ui.stopSpinnerFail('Failed to save template');
152
- }
146
+ const db = getDb();
147
+ const id = randomUUID();
148
+ db.prepare(
149
+ 'INSERT INTO templates (id,name,type,content,variables,created_at,updated_at) VALUES (?,?,?,?,?,datetime("now"),datetime("now"))'
150
+ ).run(id, options.name, options.type || 'terraform', content, '{}');
151
+
152
+ ui.stopSpinnerSuccess(`Template "${options.name}" saved`);
153
+ ui.info(`ID: ${id}`);
153
154
  } catch (error: any) {
154
155
  ui.stopSpinnerFail('Error saving template');
155
156
  ui.error(error.message);
@@ -164,13 +165,13 @@ async function templateDeleteCommand(id: string): Promise<void> {
164
165
  ui.startSpinner({ message: 'Deleting template...' });
165
166
 
166
167
  try {
167
- const client = new RestClient(STATE_SERVICE_URL);
168
- const result = await client.delete<any>(`/api/state/templates/${encodeURIComponent(id)}`);
168
+ const db = getDb();
169
+ const result = db.prepare('DELETE FROM templates WHERE id=?').run(id);
169
170
 
170
- if (result.success) {
171
+ if ((result as any).changes > 0) {
171
172
  ui.stopSpinnerSuccess('Template deleted');
172
173
  } else {
173
- ui.stopSpinnerFail('Failed to delete template');
174
+ ui.stopSpinnerFail('Template not found');
174
175
  }
175
176
  } catch (error: any) {
176
177
  ui.stopSpinnerFail('Error deleting template');