@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
package/src/cli/init.ts CHANGED
@@ -68,6 +68,11 @@ export interface InitOptions {
68
68
  force?: boolean;
69
69
  /** Suppress all console output */
70
70
  quiet?: boolean;
71
+ /**
72
+ * Merge mode (M2): append a ## Local Overrides section to an existing
73
+ * NIMBUS.md and create .nimbus/local.md instead of overwriting the file.
74
+ */
75
+ merge?: boolean;
71
76
  }
72
77
 
73
78
  /** Value returned by {@link runInit} on success */
@@ -345,6 +350,167 @@ export function detectCloudProviders(dir: string): CloudProvider[] {
345
350
  return Array.from(found);
346
351
  }
347
352
 
353
+ /** Live infrastructure context gathered by querying actual CLI tools */
354
+ export interface InfraContext {
355
+ terraformWorkspace?: string;
356
+ terraformBackend?: string;
357
+ kubectlContext?: string;
358
+ kubectlClusters?: string[];
359
+ helmReleases?: string[];
360
+ awsAccount?: string;
361
+ awsRegion?: string;
362
+ /** G13: Named AWS profiles from ~/.aws/config (max 10) */
363
+ awsProfiles?: Array<{ profile: string; account?: string; region?: string }>;
364
+ gcpProject?: string;
365
+ /** G16: Number of Kubernetes namespaces in the current cluster */
366
+ k8sNamespaceCount?: number;
367
+ /** G16: Number of Kubernetes deployments across all namespaces */
368
+ k8sDeploymentCount?: number;
369
+ /** CI/CD pipeline system detected in the project directory (G9). */
370
+ cicdPipeline?: string;
371
+ }
372
+
373
+ /**
374
+ * Discover live infrastructure context by querying CLI tools.
375
+ * All queries are best-effort with short timeouts.
376
+ */
377
+ export async function discoverInfraContext(dir: string): Promise<InfraContext> {
378
+ const { execFileSync } = await import('node:child_process');
379
+ const ctx: InfraContext = {};
380
+
381
+ // Terraform workspace (only if .terraform exists)
382
+ if (exists(path.join(dir, '.terraform'))) {
383
+ try {
384
+ ctx.terraformWorkspace = execFileSync('terraform', ['workspace', 'show'], {
385
+ cwd: dir, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
386
+ }).trim();
387
+ } catch { /* ignore */ }
388
+ }
389
+
390
+ // kubectl context
391
+ try {
392
+ ctx.kubectlContext = execFileSync('kubectl', ['config', 'current-context'], {
393
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
394
+ }).trim();
395
+ const clustersOut = execFileSync('kubectl', ['config', 'get-clusters'], {
396
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
397
+ });
398
+ ctx.kubectlClusters = clustersOut.split('\n').slice(1).map(s => s.trim()).filter(Boolean);
399
+ } catch { /* ignore */ }
400
+
401
+ // Helm releases
402
+ try {
403
+ const helmOut = execFileSync('helm', ['list', '-A', '--output=json'], {
404
+ encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'],
405
+ });
406
+ const releases = JSON.parse(helmOut || '[]') as Array<{ name: string; namespace: string }>;
407
+ ctx.helmReleases = releases.map(r => `${r.name} (${r.namespace})`);
408
+ } catch { /* ignore */ }
409
+
410
+ // AWS account + region
411
+ try {
412
+ const awsIdOut = execFileSync('aws', ['sts', 'get-caller-identity', '--output=json'], {
413
+ encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'],
414
+ });
415
+ const awsId = JSON.parse(awsIdOut);
416
+ ctx.awsAccount = awsId.Account;
417
+ } catch { /* ignore */ }
418
+
419
+ try {
420
+ ctx.awsRegion = execFileSync('aws', ['configure', 'get', 'region'], {
421
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
422
+ }).trim() || process.env.AWS_DEFAULT_REGION;
423
+ } catch {
424
+ ctx.awsRegion = process.env.AWS_DEFAULT_REGION;
425
+ }
426
+
427
+ // G13: Discover all AWS profiles from ~/.aws/config
428
+ try {
429
+ const { readFileSync } = await import('node:fs');
430
+ const { homedir } = await import('node:os');
431
+ const awsConfigPath = path.join(homedir(), '.aws', 'config');
432
+ if (exists(awsConfigPath)) {
433
+ const awsConfig = readFileSync(awsConfigPath, 'utf-8');
434
+ const profiles: Array<{ profile: string; account?: string; region?: string }> = [];
435
+ const profileRegex = /^\[(?:profile\s+)?(\S+)\]/gm;
436
+ let match;
437
+ while ((match = profileRegex.exec(awsConfig)) !== null) {
438
+ const profileName = match[1];
439
+ if (profileName === 'default') continue; // handled separately
440
+ // Extract region from the profile block
441
+ const blockStart = match.index + match[0].length;
442
+ const nextBlock = awsConfig.indexOf('\n[', blockStart);
443
+ const block = awsConfig.slice(blockStart, nextBlock === -1 ? undefined : nextBlock);
444
+ const regionMatch = block.match(/^\s*region\s*=\s*(.+)$/m);
445
+ profiles.push({ profile: profileName, region: regionMatch?.[1]?.trim() });
446
+ }
447
+ if (profiles.length > 0) ctx.awsProfiles = profiles.slice(0, 10); // max 10 profiles
448
+ }
449
+ } catch { /* ignore */ }
450
+
451
+ // GCP project
452
+ try {
453
+ const proj = execFileSync('gcloud', ['config', 'get-value', 'project'], {
454
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
455
+ }).trim();
456
+ if (proj && proj !== '(unset)') ctx.gcpProject = proj;
457
+ } catch { /* ignore */ }
458
+
459
+ // G16: Count K8s namespaces and deployments
460
+ if (ctx.kubectlContext) {
461
+ try {
462
+ const nsOut = execFileSync('kubectl', ['get', 'namespaces', '--no-headers'], {
463
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
464
+ });
465
+ const nsCount = nsOut.trim().split('\n').filter(Boolean).length;
466
+ ctx.k8sNamespaceCount = nsCount;
467
+ } catch { /* ignore */ }
468
+ try {
469
+ const deplOut = execFileSync('kubectl', ['get', 'deployments', '-A', '--no-headers'], {
470
+ encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'],
471
+ });
472
+ const deplCount = deplOut.trim().split('\n').filter(Boolean).length;
473
+ ctx.k8sDeploymentCount = deplCount;
474
+ } catch { /* ignore */ }
475
+ }
476
+
477
+ // CI/CD pipeline detection (G9)
478
+ const cicdFiles: Array<[string, string]> = [
479
+ ['.github/workflows', 'GitHub Actions'],
480
+ ['.gitlab-ci.yml', 'GitLab CI'],
481
+ ['.circleci', 'CircleCI'],
482
+ ['Jenkinsfile', 'Jenkins'],
483
+ ['.buildkite', 'Buildkite'],
484
+ ['azure-pipelines.yml', 'Azure Pipelines'],
485
+ ];
486
+ for (const [file, name] of cicdFiles) {
487
+ if (exists(path.join(dir, file))) {
488
+ ctx.cicdPipeline = name;
489
+ break;
490
+ }
491
+ }
492
+
493
+ return ctx;
494
+ }
495
+
496
+ /**
497
+ * Format an InfraContext into a compact single-line summary for injection
498
+ * into agent messages (Gaps 7 & 10).
499
+ */
500
+ export function formatInfraContext(ctx: InfraContext): string {
501
+ const parts: string[] = [];
502
+ if (ctx.terraformWorkspace) parts.push(`tf-workspace: ${ctx.terraformWorkspace}`);
503
+ if (ctx.kubectlContext) parts.push(`k8s-context: ${ctx.kubectlContext}`);
504
+ if (ctx.helmReleases && ctx.helmReleases.length > 0) {
505
+ parts.push(`helm-releases: ${ctx.helmReleases.slice(0, 3).join(', ')}${ctx.helmReleases.length > 3 ? ` +${ctx.helmReleases.length - 3} more` : ''}`);
506
+ }
507
+ if (ctx.awsAccount) parts.push(`aws-account: ${ctx.awsAccount}`);
508
+ if (ctx.awsRegion) parts.push(`aws-region: ${ctx.awsRegion}`);
509
+ if (ctx.gcpProject) parts.push(`gcp-project: ${ctx.gcpProject}`);
510
+ if (ctx.cicdPipeline) parts.push(`cicd: ${ctx.cicdPipeline}`);
511
+ return parts.length > 0 ? parts.join(' | ') : 'no infra context detected';
512
+ }
513
+
348
514
  /**
349
515
  * Detect which Node.js package manager is used in `dir`.
350
516
  *
@@ -563,7 +729,7 @@ export function detectProject(dir: string): ProjectDetection {
563
729
  * The generated markdown serves as both human-readable documentation
564
730
  * and machine-readable project metadata for the Nimbus agent.
565
731
  */
566
- export function generateNimbusMd(detection: ProjectDetection, _dir: string): string {
732
+ export function generateNimbusMd(detection: ProjectDetection, _dir: string, infraCtx?: InfraContext): string {
567
733
  const lines: string[] = [];
568
734
 
569
735
  // --- Header ---
@@ -597,6 +763,57 @@ export function generateNimbusMd(detection: ProjectDetection, _dir: string): str
597
763
  if (detection.cloudProviders.length > 0) {
598
764
  lines.push(`- **Cloud Providers:** ${detection.cloudProviders.join(', ')}`);
599
765
  }
766
+ if (infraCtx) {
767
+ if (infraCtx.terraformWorkspace) {
768
+ lines.push(`- **Terraform Workspace:** ${infraCtx.terraformWorkspace}`);
769
+ }
770
+ if (infraCtx.kubectlContext) {
771
+ lines.push(`- **Kubernetes Context:** ${infraCtx.kubectlContext}`);
772
+ }
773
+ if (infraCtx.kubectlClusters && infraCtx.kubectlClusters.length > 0) {
774
+ lines.push(`- **Kubernetes Clusters:** ${infraCtx.kubectlClusters.join(', ')}`);
775
+ }
776
+ if (infraCtx.helmReleases && infraCtx.helmReleases.length > 0) {
777
+ lines.push(`- **Helm Releases:** ${infraCtx.helmReleases.slice(0, 5).join(', ')}${infraCtx.helmReleases.length > 5 ? ` (+${infraCtx.helmReleases.length - 5} more)` : ''}`);
778
+ }
779
+ if (infraCtx.awsAccount) {
780
+ lines.push(`- **AWS Account:** ${infraCtx.awsAccount}${infraCtx.awsRegion ? ` (${infraCtx.awsRegion})` : ''}`);
781
+ }
782
+ // G13: List named AWS profiles
783
+ if (infraCtx.awsProfiles && infraCtx.awsProfiles.length > 0) {
784
+ lines.push(`- **AWS Profiles:** ${infraCtx.awsProfiles.map(p => p.profile).join(', ')}`);
785
+ }
786
+ if (infraCtx.gcpProject) {
787
+ lines.push(`- **GCP Project:** ${infraCtx.gcpProject}`);
788
+ }
789
+ // G16: K8s namespace and deployment counts
790
+ if (infraCtx.k8sNamespaceCount !== undefined) {
791
+ lines.push(`- **K8s Namespaces:** ${infraCtx.k8sNamespaceCount}`);
792
+ }
793
+ if (infraCtx.k8sDeploymentCount !== undefined) {
794
+ lines.push(`- **K8s Deployments:** ${infraCtx.k8sDeploymentCount}`);
795
+ }
796
+ // G9: CI/CD pipeline
797
+ if (infraCtx.cicdPipeline) {
798
+ lines.push(`- **CI/CD:** ${infraCtx.cicdPipeline}`);
799
+ }
800
+ }
801
+ lines.push('');
802
+ }
803
+
804
+ // --- CI/CD (M4) ---
805
+ if (infraCtx?.cicdPipeline) {
806
+ lines.push('## CI/CD');
807
+ lines.push('');
808
+ const pipelineDir: Record<string, string> = {
809
+ 'GitHub Actions': '.github/workflows/',
810
+ 'GitLab CI': '.gitlab-ci.yml',
811
+ 'CircleCI': '.circleci/',
812
+ 'Jenkins': 'Jenkinsfile',
813
+ };
814
+ const pipelinePath = pipelineDir[infraCtx.cicdPipeline] ?? '';
815
+ lines.push(`Pipeline: ${infraCtx.cicdPipeline}${pipelinePath ? ` (${pipelinePath})` : ''}`);
816
+ lines.push('Convention: Always run `terraform plan` in CI before apply. Apply only on main branch merge.');
600
817
  lines.push('');
601
818
  }
602
819
 
@@ -613,6 +830,16 @@ export function generateNimbusMd(detection: ProjectDetection, _dir: string): str
613
830
  lines.push('');
614
831
  }
615
832
 
833
+ // --- Environments (GAP-22) ---
834
+ lines.push('## Environments');
835
+ lines.push('');
836
+ lines.push('| Name | Terraform Workspace | Kubernetes Context | Protected |');
837
+ lines.push('|------|--------------------|--------------------|-----------|');
838
+ lines.push('| dev | dev | | false |');
839
+ lines.push('| staging | staging | | false |');
840
+ lines.push('| prod | prod | | true |');
841
+ lines.push('');
842
+
616
843
  // --- Safety Rules ---
617
844
  lines.push('## Safety Rules');
618
845
  lines.push('');
@@ -623,12 +850,52 @@ export function generateNimbusMd(detection: ProjectDetection, _dir: string): str
623
850
  lines.push('- Never store secrets in source control');
624
851
  lines.push('');
625
852
 
853
+ // --- Guardrails (G5): DevOps-specific rules when infra is detected ---
854
+ const hasInfra = detection.infraTypes.length > 0 || detection.cloudProviders.length > 0;
855
+ if (hasInfra) {
856
+ lines.push('## Guardrails');
857
+ lines.push('');
858
+ lines.push('- Never run `terraform destroy` without explicit confirmation');
859
+ lines.push('- Protected Kubernetes namespaces: `production`, `kube-system`, `monitoring`');
860
+ lines.push('- Always show terraform plan before apply');
861
+ lines.push('- Confirm target cloud account and region before resource creation');
862
+ lines.push('- Protected environments: any workspace/namespace containing `prod`, `prd`, `production`');
863
+ lines.push('');
864
+ }
865
+
866
+ // --- Forbidden placeholder (G5) ---
867
+ lines.push('## Forbidden');
868
+ lines.push('');
869
+ lines.push('<!-- List operations Nimbus must never perform in this project -->');
870
+ lines.push('<!-- Example: - Never destroy the production database -->');
871
+ lines.push('');
872
+
626
873
  // --- Custom Instructions ---
627
874
  lines.push('## Custom Instructions');
628
875
  lines.push('');
629
876
  lines.push('<!-- Add project-specific instructions for the Nimbus agent here -->');
630
877
  lines.push('');
631
878
 
879
+ // --- G18: Runbooks ---
880
+ const runbookDirs = ['docs/runbooks', 'runbooks', '.github/runbooks', '.nimbus/runbooks'];
881
+ const foundRunbooks: string[] = [];
882
+ for (const dir of runbookDirs) {
883
+ const fullPath = path.join(_dir, dir);
884
+ if (fs.existsSync(fullPath)) {
885
+ try {
886
+ const files = fs.readdirSync(fullPath).filter(f => /\.(md|yaml|yml)$/.test(f));
887
+ foundRunbooks.push(...files.map(f => `- ${dir}/${f}`));
888
+ } catch { /* non-critical */ }
889
+ }
890
+ }
891
+ const runbookSection = foundRunbooks.length > 0
892
+ ? foundRunbooks.join('\n') + '\n\nRefer to these runbooks for incident response and operational procedures.'
893
+ : '<!-- Add runbook references, e.g.:\n- docs/runbooks/cert-rotation.md -->';
894
+ lines.push('## Runbooks');
895
+ lines.push('');
896
+ lines.push(runbookSection);
897
+ lines.push('');
898
+
632
899
  return lines.join('\n');
633
900
  }
634
901
 
@@ -721,6 +988,7 @@ export async function runInit(options?: InitOptions): Promise<InitResult> {
721
988
  const dir = path.resolve(options?.cwd ?? process.cwd());
722
989
  const force = options?.force ?? false;
723
990
  const quiet = options?.quiet ?? false;
991
+ const merge = options?.merge ?? false;
724
992
 
725
993
  const log = (msg: string): void => {
726
994
  if (!quiet) {
@@ -728,6 +996,31 @@ export async function runInit(options?: InitOptions): Promise<InitResult> {
728
996
  }
729
997
  };
730
998
 
999
+ // ---- M2: --merge mode — append Local Overrides section, don't overwrite ----
1000
+ const nimbusmdPathEarly = path.join(dir, 'NIMBUS.md');
1001
+ if (merge && exists(nimbusmdPathEarly)) {
1002
+ const content = fs.readFileSync(nimbusmdPathEarly, 'utf-8');
1003
+ const filesCreated: string[] = [];
1004
+
1005
+ if (!content.includes('## Local Overrides')) {
1006
+ fs.appendFileSync(nimbusmdPathEarly, '\n\n## Local Overrides\n\n<!-- Personal additions -->\n', 'utf-8');
1007
+ log(' Appended ## Local Overrides section to NIMBUS.md');
1008
+ } else {
1009
+ log(' NIMBUS.md already has a ## Local Overrides section');
1010
+ }
1011
+
1012
+ const localMdPath = path.join(dir, '.nimbus', 'local.md');
1013
+ if (!exists(localMdPath)) {
1014
+ fs.mkdirSync(path.join(dir, '.nimbus'), { recursive: true });
1015
+ fs.writeFileSync(localMdPath, '# Local Overrides\n\n<!-- Personal notes and overrides that are not committed -->\n', 'utf-8');
1016
+ log(' Created .nimbus/local.md for personal overrides');
1017
+ filesCreated.push(localMdPath);
1018
+ }
1019
+
1020
+ const detection = detectProject(dir);
1021
+ return { detection, filesCreated, nimbusmdPath: nimbusmdPathEarly };
1022
+ }
1023
+
731
1024
  // ---- Step 1: Detect project characteristics ----
732
1025
  log('Detecting project...');
733
1026
  const detection = detectProject(dir);
@@ -746,10 +1039,62 @@ export async function runInit(options?: InitOptions): Promise<InitResult> {
746
1039
  log(` Test framework: ${detection.testFramework}`);
747
1040
  }
748
1041
 
749
- // ---- Step 2: Check for existing NIMBUS.md ----
1042
+ // ---- Step 2: Check for existing NIMBUS.md and show diff (L8) ----
750
1043
  const nimbusmdPath = path.join(dir, 'NIMBUS.md');
751
- if (exists(nimbusmdPath) && !force) {
752
- throw new Error('NIMBUS.md already exists. Use --force to overwrite.');
1044
+ if (exists(nimbusmdPath) && !force && !quiet) {
1045
+ // Generate the new content first for diffing
1046
+ const infraCtxForDiff = await discoverInfraContext(dir).catch(() => undefined);
1047
+ const newContent = generateNimbusMd(detection, dir, infraCtxForDiff);
1048
+ const existingContent = readText(nimbusmdPath);
1049
+
1050
+ if (newContent === existingContent) {
1051
+ log('NIMBUS.md is already up to date.');
1052
+ return { detection, filesCreated: [], nimbusmdPath };
1053
+ }
1054
+
1055
+ // Show line-level diff
1056
+ const oldLines = existingContent.split('\n');
1057
+ const newLines = newContent.split('\n');
1058
+ const diffLines: string[] = [];
1059
+ const maxLen = Math.max(oldLines.length, newLines.length);
1060
+ for (let idx = 0; idx < maxLen; idx++) {
1061
+ const o = oldLines[idx];
1062
+ const n = newLines[idx];
1063
+ if (o === undefined) diffLines.push(`+ ${n}`);
1064
+ else if (n === undefined) diffLines.push(`- ${o}`);
1065
+ else if (o !== n) { diffLines.push(`- ${o}`); diffLines.push(`+ ${n}`); }
1066
+ }
1067
+
1068
+ if (diffLines.length > 0) {
1069
+ log('\nNIMBUS.md changes:');
1070
+ for (const dl of diffLines.slice(0, 50)) {
1071
+ if (dl.startsWith('+')) log(` \x1b[32m${dl}\x1b[0m`);
1072
+ else if (dl.startsWith('-')) log(` \x1b[31m${dl}\x1b[0m`);
1073
+ else log(` ${dl}`);
1074
+ }
1075
+ if (diffLines.length > 50) log(` ... and ${diffLines.length - 50} more changes`);
1076
+ log('');
1077
+ log('Apply these changes? [y/N]');
1078
+
1079
+ // Synchronous readline for non-quiet mode
1080
+ const answer = await new Promise<string>(resolve => {
1081
+ const { createInterface } = require('readline') as typeof import('readline');
1082
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1083
+ rl.question('', (ans: string) => { rl.close(); resolve(ans.trim()); });
1084
+ });
1085
+
1086
+ if (answer.toLowerCase() !== 'y') {
1087
+ log('Aborted — NIMBUS.md not changed.');
1088
+ return { detection, filesCreated: [], nimbusmdPath };
1089
+ }
1090
+
1091
+ fs.writeFileSync(nimbusmdPath, newContent, 'utf-8');
1092
+ log(' Updated NIMBUS.md');
1093
+ return { detection, filesCreated: [nimbusmdPath], nimbusmdPath };
1094
+ }
1095
+ } else if (exists(nimbusmdPath) && !force) {
1096
+ // quiet mode — skip without prompting
1097
+ return { detection, filesCreated: [], nimbusmdPath };
753
1098
  }
754
1099
 
755
1100
  // ---- Step 3: Create .nimbus/ directory structure ----
@@ -827,7 +1172,25 @@ export async function runInit(options?: InitOptions): Promise<InitResult> {
827
1172
  }
828
1173
 
829
1174
  // ---- Step 7: Generate and write NIMBUS.md ----
830
- const nimbusmdContent = generateNimbusMd(detection, dir);
1175
+ // L8: Monorepo detection — scan immediate subdirs for terraform roots
1176
+ let monorepoSection = '';
1177
+ try {
1178
+ const subdirs = fs.readdirSync(dir, { withFileTypes: true })
1179
+ .filter(e => e.isDirectory() && !e.name.startsWith('.') && !['node_modules', '.git'].includes(e.name))
1180
+ .map(e => e.name);
1181
+ const tfRoots: string[] = [];
1182
+ for (const sub of subdirs.slice(0, 20)) {
1183
+ const subPath = path.join(dir, sub);
1184
+ const hasTf = fs.readdirSync(subPath).some(f => f.endsWith('.tf'));
1185
+ if (hasTf) tfRoots.push(sub);
1186
+ }
1187
+ if (tfRoots.length > 1) {
1188
+ monorepoSection = `\n## Terraform Modules (Monorepo)\n\nThis is a monorepo with multiple Terraform roots:\n${tfRoots.map(r => `- \`./${r}/\``).join('\n')}\n\nTo target a specific root, \`cd\` into the directory or specify the path.\n`;
1189
+ log(` Detected ${tfRoots.length} Terraform roots (monorepo)`);
1190
+ }
1191
+ } catch { /* non-critical */ }
1192
+
1193
+ const nimbusmdContent = generateNimbusMd(detection, dir) + monorepoSection;
831
1194
  fs.writeFileSync(nimbusmdPath, nimbusmdContent, 'utf-8');
832
1195
  filesCreated.push(nimbusmdPath);
833
1196
  log(' Created NIMBUS.md');
@@ -846,6 +1209,16 @@ export async function runInit(options?: InitOptions): Promise<InitResult> {
846
1209
  log('Nimbus project initialized successfully.');
847
1210
  log('Edit NIMBUS.md to customise agent behaviour.');
848
1211
 
1212
+ // M5: Print next steps after generating NIMBUS.md
1213
+ if (!quiet) {
1214
+ log('');
1215
+ log('\x1b[36mNext steps:\x1b[0m');
1216
+ log(' nimbus plan Preview infrastructure changes');
1217
+ log(' nimbus doctor Check your DevOps toolchain');
1218
+ log(' nimbus status Live infrastructure health dashboard');
1219
+ log(' nimbus Open the interactive DevOps agent');
1220
+ }
1221
+
849
1222
  return {
850
1223
  detection,
851
1224
  filesCreated,