@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,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * PermissionPrompt Component
4
+ *
5
+ * Modal-style overlay that asks the user to approve or reject a tool
6
+ * invocation before it executes. Displays the tool name, its input
7
+ * parameters, and a risk-level indicator.
8
+ *
9
+ * Keyboard shortcuts:
10
+ * a - Approve this invocation
11
+ * r - Reject this invocation
12
+ * A - Approve all invocations for this tool
13
+ * s - Approve for the remainder of this session
14
+ */
15
+ import { useState } from 'react';
16
+ import { Box, Text, useInput } from 'ink';
17
+ /** Map risk levels to display colours. */
18
+ const RISK_COLORS = {
19
+ low: 'green',
20
+ medium: 'yellow',
21
+ high: 'red',
22
+ critical: 'magenta',
23
+ };
24
+ /** H4: Human-readable descriptions of each risk tier. */
25
+ const RISK_DESCRIPTIONS = {
26
+ low: 'Read-only — safe to auto-approve',
27
+ medium: 'Modifies local files — review before approving',
28
+ high: 'System or cloud operation — approve with caution',
29
+ critical: 'Destructive or irreversible — explicit confirmation required',
30
+ };
31
+ /**
32
+ * Format tool input into a list of truncated key=value lines.
33
+ */
34
+ function formatInput(input, maxEntries = 6) {
35
+ return Object.entries(input)
36
+ .slice(0, maxEntries)
37
+ .map(([key, value]) => {
38
+ const str = typeof value === 'string' ? value : JSON.stringify(value);
39
+ const truncated = str.length > 80 ? `${str.slice(0, 77)}...` : str;
40
+ return `${key}: ${truncated}`;
41
+ });
42
+ }
43
+ /**
44
+ * PermissionPrompt renders a bordered box with tool information and waits for
45
+ * a single keypress to determine the user's decision.
46
+ */
47
+ export function PermissionPrompt({ toolName, toolInput, riskLevel, onDecide, }) {
48
+ const [pressed, setPressed] = useState(null);
49
+ useInput(input => {
50
+ switch (input) {
51
+ case 'a':
52
+ setPressed('a');
53
+ onDecide('approve');
54
+ break;
55
+ case 'r':
56
+ setPressed('r');
57
+ onDecide('reject');
58
+ break;
59
+ case 'A':
60
+ setPressed('A');
61
+ onDecide('approve_all');
62
+ break;
63
+ case 's':
64
+ setPressed('s');
65
+ onDecide('session');
66
+ break;
67
+ }
68
+ });
69
+ const inputLines = formatInput(toolInput);
70
+ const riskColor = RISK_COLORS[riskLevel];
71
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "double", borderColor: riskColor, paddingX: 1, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "yellow", children: "Permission Required" }) }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Tool: " }), _jsx(Text, { bold: true, children: toolName })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Risk: " }), _jsx(Text, { bold: true, color: riskColor, children: riskLevel.toUpperCase() })] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { dimColor: true, children: RISK_DESCRIPTIONS[riskLevel] }) }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "Parameters:" }), inputLines.map((line, idx) => (_jsxs(Text, { children: [" ", line] }, idx)))] }), _jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, inverse: pressed === 'a', children: "[a]" }), _jsx(Text, { children: "=approve " }), _jsx(Text, { color: "red", bold: true, inverse: pressed === 'r', children: "[r]" }), _jsx(Text, { children: "=reject " }), _jsx(Text, { color: "cyan", bold: true, inverse: pressed === 'A', children: "[A]" }), _jsx(Text, { children: "=approve-all-session " }), _jsx(Text, { color: "blue", bold: true, inverse: pressed === 's', children: "[s]" }), _jsx(Text, { children: "=approve-this-tool-session" })] })] }));
72
+ }
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * StatusBar Component
4
+ *
5
+ * A single-line footer that shows the active agent mode, token usage with a
6
+ * percentage bar, estimated cost, snapshot count, and elapsed processing time.
7
+ *
8
+ * Layout:
9
+ * [Plan] [Build] [Deploy] | Tokens: 45.2k/200k (22%) | Cost: $0.03 | Snapshots: 3 (undo) | tf:default | k8s:prod-cluster | delta:+$1.20 | 12s
10
+ *
11
+ * Token percentage colour:
12
+ * green < 50%
13
+ * yellow 50-80%
14
+ * red > 80%
15
+ */
16
+ import { useState, useEffect, useRef } from 'react';
17
+ import { Box, Text } from 'ink';
18
+ /** All modes in display order. */
19
+ const MODES = ['plan', 'build', 'deploy'];
20
+ /**
21
+ * Format a token count into a human-readable string (e.g. 45200 -> "45.2k").
22
+ */
23
+ function formatTokens(count) {
24
+ if (count >= 1_000_000) {
25
+ return `${(count / 1_000_000).toFixed(1)}M`;
26
+ }
27
+ if (count >= 1_000) {
28
+ return `${(count / 1_000).toFixed(1)}k`;
29
+ }
30
+ return String(count);
31
+ }
32
+ /**
33
+ * Pick the colour for the token percentage indicator.
34
+ */
35
+ function tokenColor(pct) {
36
+ if (pct > 80) {
37
+ return 'red';
38
+ }
39
+ if (pct >= 50) {
40
+ return 'yellow';
41
+ }
42
+ return 'green';
43
+ }
44
+ /**
45
+ * Determine if an environment name is production-like (G1).
46
+ */
47
+ function isProdEnvironment(name) {
48
+ return /prod|production|live/i.test(name);
49
+ }
50
+ /**
51
+ * A single mode badge. The active mode is rendered with inverse styling.
52
+ */
53
+ function ModeBadge({ mode, active }) {
54
+ const label = ` ${mode.charAt(0).toUpperCase() + mode.slice(1)} `;
55
+ if (active) {
56
+ return (_jsx(Text, { bold: true, inverse: true, children: label }));
57
+ }
58
+ return _jsx(Text, { dimColor: true, children: label });
59
+ }
60
+ /**
61
+ * StatusBar renders the bottom status line of the TUI.
62
+ */
63
+ export function StatusBar({ session, isProcessing = false, processingStartTime = null, inputLineCount = 1, showScrollHint = false, copyToast = '', showStreamingHint = false, modeToast, searchQuery, searchResultCount, }) {
64
+ const pct = session.maxTokens > 0 ? Math.round((session.tokenCount / session.maxTokens) * 100) : 0;
65
+ const pctColor = tokenColor(pct);
66
+ const costStr = session.costUSD < 0.01 && session.costUSD > 0
67
+ ? `$${session.costUSD.toFixed(4)}`
68
+ : `$${session.costUSD.toFixed(2)}`;
69
+ // Elapsed time counter
70
+ const [elapsedSeconds, setElapsedSeconds] = useState(0);
71
+ const intervalRef = useRef(null);
72
+ useEffect(() => {
73
+ if (isProcessing && processingStartTime) {
74
+ // Immediately calculate current elapsed
75
+ setElapsedSeconds(Math.floor((Date.now() - processingStartTime) / 1000));
76
+ intervalRef.current = setInterval(() => {
77
+ setElapsedSeconds(Math.floor((Date.now() - processingStartTime) / 1000));
78
+ }, 1000);
79
+ return () => {
80
+ if (intervalRef.current) {
81
+ clearInterval(intervalRef.current);
82
+ }
83
+ };
84
+ }
85
+ else {
86
+ setElapsedSeconds(0);
87
+ if (intervalRef.current) {
88
+ clearInterval(intervalRef.current);
89
+ intervalRef.current = null;
90
+ }
91
+ }
92
+ }, [isProcessing, processingStartTime]);
93
+ // Snapshot display: show undo hint when snapshots exist (G19)
94
+ const snapshotDisplay = session.snapshotCount > 0
95
+ ? `${session.snapshotCount} (↶ undo)`
96
+ : String(session.snapshotCount);
97
+ // Infra context colors (G1)
98
+ const tfColor = session.terraformWorkspace && isProdEnvironment(session.terraformWorkspace)
99
+ ? 'yellow'
100
+ : 'green';
101
+ const k8sColor = session.kubectlContext && isProdEnvironment(session.kubectlContext)
102
+ ? 'yellow'
103
+ : 'green';
104
+ // L5: Build a compact visual progress bar for context budget
105
+ const BAR_WIDTH = 8;
106
+ const filledBars = Math.round((pct / 100) * BAR_WIDTH);
107
+ const progressBar = '█'.repeat(filledBars) + '░'.repeat(BAR_WIDTH - filledBars);
108
+ return (_jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, justifyContent: "space-between", width: "100%", children: [_jsx(Box, { children: MODES.map(m => (_jsx(ModeBadge, { mode: m, active: m === session.mode }, m))) }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Ctx: " }), _jsx(Text, { color: pctColor, children: progressBar }), _jsx(Text, { dimColor: true, children: " " }), _jsxs(Text, { color: pctColor, children: [formatTokens(session.tokenCount), "/", formatTokens(session.maxTokens), " (", pct, "%)"] }), _jsx(Text, { dimColor: true, children: " | Cost: " }), _jsx(Text, { children: costStr }), _jsx(Text, { dimColor: true, children: " | Snapshots: " }), _jsx(Text, { children: snapshotDisplay }), session.terraformWorkspace && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: tfColor, children: ["tf:", session.terraformWorkspace] })] })), session.kubectlContext && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: k8sColor, children: ["k8s:", session.kubectlContext] })] })), session.infraCostDelta && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: "green", children: ["delta:", session.infraCostDelta] })] })), isProcessing && elapsedSeconds > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: "cyan", children: [elapsedSeconds, "s"] })] })), inputLineCount > 1 && !isProcessing && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: "cyan", children: [inputLineCount, " lines"] })] })), _jsx(Text, { dimColor: true, children: " | Tab" }), _jsx(Text, { dimColor: true, children: ":mode " }), _jsx(Text, { dimColor: true, children: "Esc" }), _jsx(Text, { dimColor: true, children: ":cancel " }), _jsx(Text, { dimColor: true, children: "^C" }), _jsx(Text, { dimColor: true, children: ":exit" }), !isProcessing && inputLineCount <= 1 && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { dimColor: true, children: "F1:help /tree /terminal" }), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { dimColor: true, children: "? help" })] })), showScrollHint && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { color: "cyan", children: "\u2191\u2193 scroll | G bottom" })] })), showStreamingHint && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { color: "cyan", children: "Esc:stop stream" })] })), copyToast && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { color: "green", children: copyToast })] })), modeToast && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsx(Text, { color: "cyan", italic: true, children: modeToast })] })), searchQuery && searchResultCount !== undefined && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: " | " }), _jsxs(Text, { color: "yellow", children: ["Search: \"", searchQuery, "\" \u2014 ", searchResultCount, " results"] })] }))] })] }));
109
+ }
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ /**
4
+ * A scrollable pane showing the last tool outputs as a rolling buffer.
5
+ */
6
+ export function TerminalPane({ toolCalls, maxLines = 20 }) {
7
+ // Collect lines from all tool calls (completed + running with streaming output)
8
+ const outputLines = [];
9
+ for (const tc of toolCalls) {
10
+ if (tc.status === 'running') {
11
+ // Show live streaming output for in-progress tool calls (Gap 1)
12
+ const liveOutput = tc.streamingOutput ?? '(waiting for output...)';
13
+ const lines = liveOutput.split('\n').filter(l => l.length > 0);
14
+ for (const line of lines) {
15
+ outputLines.push({ text: line, isError: false, toolName: tc.name, live: true });
16
+ }
17
+ continue;
18
+ }
19
+ if (tc.status !== 'completed' && tc.status !== 'failed')
20
+ continue;
21
+ const output = tc.result?.output ?? '';
22
+ const isError = tc.result?.isError ?? false;
23
+ const lines = output.split('\n').filter(l => l.length > 0);
24
+ for (const line of lines) {
25
+ outputLines.push({ text: line, isError, toolName: tc.name });
26
+ }
27
+ }
28
+ // Show only the last maxLines lines
29
+ const visible = outputLines.slice(-maxLines);
30
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, overflow: "hidden", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Terminal Output" }), _jsx(Text, { dimColor: true, children: " (read-only \u00B7 /terminal to toggle)" })] }), visible.length === 0 ? (_jsx(Text, { dimColor: true, italic: true, children: "No tool output yet." })) : (visible.map((line, i) => (_jsxs(Text, { color: line.isError ? 'red' : line.live ? 'cyan' : undefined, dimColor: !line.isError && !line.live, wrap: "truncate", children: [line.live ? '[>] ' : '', line.text] }, i))))] }));
31
+ }
@@ -0,0 +1,303 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * ToolCallDisplay Component
4
+ *
5
+ * Renders one or more tool invocations inline with the conversation. Each tool
6
+ * call is shown in a bordered box with a header containing the tool name and
7
+ * status indicator. While a tool is running the box shows a Spinner; on
8
+ * completion or failure it shows a condensed result summary.
9
+ *
10
+ * Specialised renderers exist for common tools:
11
+ * - read_file: filename + optional line range
12
+ * - edit_file: unified diff with context lines, red/green colouring
13
+ * - bash: command + expandable/collapsible output
14
+ * - terraform: resource table
15
+ *
16
+ * All other tools fall through to a generic key/value display.
17
+ */
18
+ import { useState, useEffect } from 'react';
19
+ import { Box, Text, useInput } from 'ink';
20
+ import Spinner from 'ink-spinner';
21
+ import { parseTerraformPlanOutput } from '../agent/deploy-preview';
22
+ /** Maximum number of output lines shown for bash tool results. */
23
+ const MAX_BASH_OUTPUT_LINES = 50;
24
+ /** Lines shown in collapsed view. */
25
+ const COLLAPSED_LINES = 20;
26
+ /* ---------------------------------------------------------------------------
27
+ * Status badge
28
+ * -------------------------------------------------------------------------*/
29
+ /** Long-running tools that get a context hint about expected duration. */
30
+ const LONG_RUNNING_TOOLS = new Set([
31
+ 'terraform', 'terraform_plan_analyze', 'deploy_preview', 'drift_detect',
32
+ 'helm', 'k8s_rbac', 'cfn', 'gitops',
33
+ ]);
34
+ function StatusBadge({ status, startTime }) {
35
+ const [elapsed, setElapsed] = useState(0);
36
+ useEffect(() => {
37
+ if (status !== 'running' || !startTime)
38
+ return;
39
+ const initial = Math.floor((Date.now() - startTime) / 1000);
40
+ setElapsed(initial);
41
+ const id = setInterval(() => {
42
+ setElapsed(Math.floor((Date.now() - startTime) / 1000));
43
+ }, 1000);
44
+ return () => clearInterval(id);
45
+ }, [status, startTime]);
46
+ switch (status) {
47
+ case 'pending':
48
+ return _jsx(Text, { dimColor: true, children: "[pending]" });
49
+ case 'running':
50
+ return (_jsxs(Text, { color: "cyan", children: [_jsx(Spinner, { type: "dots" }), startTime && elapsed > 0 ? ` ${elapsed}s` : ''] }));
51
+ case 'completed':
52
+ return _jsx(Text, { color: "green", children: "[done]" });
53
+ case 'failed':
54
+ return _jsx(Text, { color: "red", children: "[failed]" });
55
+ }
56
+ }
57
+ /* ---------------------------------------------------------------------------
58
+ * Per-tool body renderers
59
+ * -------------------------------------------------------------------------*/
60
+ function ReadFileBody({ input, result, }) {
61
+ const filePath = String(input.file_path ?? input.path ?? '');
62
+ const startLine = input.start_line;
63
+ const endLine = input.end_line;
64
+ const rangeLabel = startLine != null
65
+ ? endLine != null
66
+ ? ` (lines ${startLine}-${endLine})`
67
+ : ` (from line ${startLine})`
68
+ : '';
69
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "file: " }), _jsx(Text, { color: "cyan", children: filePath }), _jsx(Text, { dimColor: true, children: rangeLabel })] }), result && !result.isError && (_jsxs(Text, { dimColor: true, children: [result.output.split('\n').length, " lines read"] })), result && result.isError && _jsx(Text, { color: "red", children: result.output })] }));
70
+ }
71
+ /**
72
+ * Compute a minimal unified diff between old and new text with context lines.
73
+ */
74
+ function computeDiff(oldStr, newStr) {
75
+ const oldLines = oldStr.split('\n');
76
+ const newLines = newStr.split('\n');
77
+ const elements = [];
78
+ // Find common prefix
79
+ let prefixLen = 0;
80
+ while (prefixLen < oldLines.length &&
81
+ prefixLen < newLines.length &&
82
+ oldLines[prefixLen] === newLines[prefixLen]) {
83
+ prefixLen++;
84
+ }
85
+ // Find common suffix (from the end, but not overlapping with prefix)
86
+ let suffixLen = 0;
87
+ while (suffixLen < oldLines.length - prefixLen &&
88
+ suffixLen < newLines.length - prefixLen &&
89
+ oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]) {
90
+ suffixLen++;
91
+ }
92
+ // Context: show up to 2 lines before the diff
93
+ const contextStart = Math.max(0, prefixLen - 2);
94
+ for (let i = contextStart; i < prefixLen; i++) {
95
+ elements.push(_jsxs(Text, { dimColor: true, children: [' ', oldLines[i]] }, `ctx-pre-${i}`));
96
+ }
97
+ // Removed lines (from old)
98
+ const oldDiffEnd = oldLines.length - suffixLen;
99
+ for (let i = prefixLen; i < oldDiffEnd; i++) {
100
+ elements.push(_jsxs(Text, { color: "red", children: ["- ", oldLines[i]] }, `rm-${i}`));
101
+ }
102
+ // Added lines (from new)
103
+ const newDiffEnd = newLines.length - suffixLen;
104
+ for (let i = prefixLen; i < newDiffEnd; i++) {
105
+ elements.push(_jsxs(Text, { color: "green", children: ["+ ", newLines[i]] }, `add-${i}`));
106
+ }
107
+ // Context: show up to 2 lines after the diff
108
+ const contextEnd = Math.min(oldLines.length, oldDiffEnd + 2);
109
+ for (let i = oldDiffEnd; i < contextEnd; i++) {
110
+ elements.push(_jsxs(Text, { dimColor: true, children: [' ', oldLines[i]] }, `ctx-post-${i}`));
111
+ }
112
+ return elements;
113
+ }
114
+ function EditFileBody({ input, result, }) {
115
+ const filePath = String(input.file_path ?? input.path ?? '');
116
+ const oldStr = String(input.old_string ?? '');
117
+ const newStr = String(input.new_string ?? '');
118
+ const replaceAll = input.replace_all === true;
119
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "file: " }), _jsx(Text, { color: "cyan", children: filePath }), replaceAll && _jsx(Text, { dimColor: true, children: " (replace all)" })] }), oldStr && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: computeDiff(oldStr, newStr) })), result && result.isError && _jsx(Text, { color: "red", children: result.output })] }));
120
+ }
121
+ function BashBody({ input, result, }) {
122
+ const command = String(input.command ?? '');
123
+ const [expanded, setExpanded] = useState(false);
124
+ useInput((_input, key) => {
125
+ if (_input === 'e' && !key.ctrl && !key.meta) {
126
+ setExpanded(prev => !prev);
127
+ }
128
+ });
129
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "$ " }), _jsx(Text, { bold: true, children: command })] }), result && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: (() => {
130
+ const lines = result.output.split('\n');
131
+ const showLimit = expanded ? MAX_BASH_OUTPUT_LINES : COLLAPSED_LINES;
132
+ const truncated = lines.length > showLimit;
133
+ const visible = truncated ? lines.slice(0, showLimit) : lines;
134
+ return (_jsxs(_Fragment, { children: [visible.map((line, i) => (_jsx(Text, { color: result.isError ? 'red' : undefined, dimColor: !result.isError, children: line }, i))), truncated && (_jsxs(Text, { dimColor: true, italic: true, children: ["... ", lines.length - showLimit, " more lines", ' ', expanded ? "(press 'e' to collapse)" : "(press 'e' to expand)"] })), !truncated && lines.length > COLLAPSED_LINES && expanded && (_jsx(Text, { dimColor: true, italic: true, children: "(press 'e' to collapse)" }))] }));
135
+ })() }))] }));
136
+ }
137
+ function TerraformBody({ input, result, }) {
138
+ const action = String(input.action ?? input.command ?? input.subcommand ?? 'plan');
139
+ // Gap 8: Show structured plan summary when output contains plan data
140
+ const isPlan = action === 'plan' || (result?.output ?? '').includes('Plan:');
141
+ const changes = isPlan && result && !result.isError
142
+ ? parseTerraformPlanOutput(result.output)
143
+ : [];
144
+ const creates = changes.filter(c => c.action === 'create').length;
145
+ const updates = changes.filter(c => c.action === 'update').length;
146
+ const destroys = changes.filter(c => c.action === 'destroy').length;
147
+ const replaces = changes.filter(c => c.action === 'replace').length;
148
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "terraform " }), _jsx(Text, { bold: true, children: action })] }), isPlan && changes.length > 0 && result && !result.isError && (_jsxs(Box, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, children: "Plan Summary" }), _jsxs(Box, { children: [creates > 0 && _jsxs(Text, { color: "green", children: ["+", creates, " create "] }), updates > 0 && _jsxs(Text, { color: "yellow", children: ["~", updates, " change "] }), destroys > 0 && _jsxs(Text, { color: "red", children: ["-", destroys, " destroy "] }), replaces > 0 && _jsxs(Text, { color: "magenta", children: ["\u00B1", replaces, " replace "] }), creates === 0 && updates === 0 && destroys === 0 && replaces === 0 && (_jsx(Text, { dimColor: true, children: "No changes" }))] }), changes.slice(0, 10).map((c, i) => {
149
+ const icon = c.action === 'create' ? '+' : c.action === 'destroy' ? '-' : c.action === 'replace' ? '±' : '~';
150
+ const color = c.action === 'create' ? 'green' : c.action === 'destroy' ? 'red' : c.action === 'replace' ? 'magenta' : 'yellow';
151
+ return (_jsxs(Text, { color: color, children: [icon, " ", c.resource] }, i));
152
+ }), changes.length > 10 && _jsxs(Text, { dimColor: true, children: ["... and ", changes.length - 10, " more"] })] })), !(isPlan && changes.length > 0) && result && !result.isError && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: (() => {
153
+ const lines = result.output.split('\n');
154
+ const MAX_LINES = 200;
155
+ const displayLines = lines.slice(0, MAX_LINES);
156
+ return (_jsxs(_Fragment, { children: [displayLines.map((line, i) => {
157
+ let color;
158
+ if (line.startsWith('+') || line.includes('will be created')) {
159
+ color = 'green';
160
+ }
161
+ else if (line.startsWith('-') || line.includes('will be destroyed')) {
162
+ color = 'red';
163
+ }
164
+ else if (line.startsWith('~') || line.includes('will be updated')) {
165
+ color = 'yellow';
166
+ }
167
+ return (_jsx(Text, { color: color, dimColor: !color, children: line }, i));
168
+ }), lines.length > MAX_LINES && (_jsxs(Text, { dimColor: true, children: ["... ", lines.length - MAX_LINES, " more lines (full output saved to tool history)"] }))] }));
169
+ })() })), result && result.isError && _jsx(Text, { color: "red", children: result.output })] }));
170
+ }
171
+ /** G12: Kubectl output renderer with status colorization */
172
+ function KubectlBody({ input, result, }) {
173
+ const action = String(input.action ?? input.command ?? 'get');
174
+ const colorizeKubectlLine = (line, i) => {
175
+ // Status coloring for kubectl get output
176
+ if (/\bRunning\b/.test(line)) {
177
+ return _jsx(Text, { color: "green", children: line }, i);
178
+ }
179
+ if (/\b(Pending|ContainerCreating|Init:|PodInitializing)\b/.test(line)) {
180
+ return _jsx(Text, { color: "yellow", children: line }, i);
181
+ }
182
+ if (/\b(CrashLoopBackOff|Error|Failed|OOMKilled|ImagePullBackOff|ErrImagePull)\b/.test(line)) {
183
+ return _jsx(Text, { color: "red", children: line }, i);
184
+ }
185
+ if (/\bCompleted\b/.test(line)) {
186
+ return _jsx(Text, { color: "green", dimColor: true, children: line }, i);
187
+ }
188
+ if (/\bTerminating\b/.test(line)) {
189
+ return _jsx(Text, { color: "yellow", dimColor: true, children: line }, i);
190
+ }
191
+ return _jsx(Text, { dimColor: true, children: line }, i);
192
+ };
193
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "kubectl " }), _jsx(Text, { bold: true, children: action })] }), result && !result.isError && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: result.output.split('\n').slice(0, 80).map((line, i) => colorizeKubectlLine(line, i)) })), result && result.isError && _jsx(Text, { color: "red", children: result.output })] }));
194
+ }
195
+ /** M2: Docker build progress renderer */
196
+ function DockerBuildBody({ input, result, streamingOutput, }) {
197
+ const action = String(input.action ?? '');
198
+ if (action !== 'build') {
199
+ // Non-build actions: show raw output
200
+ if (result && !result.isError) {
201
+ return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: result.output.split('\n').slice(0, 20).map((line, i) => (_jsx(Text, { dimColor: true, children: line }, i))) }));
202
+ }
203
+ if (result?.isError)
204
+ return _jsx(Text, { color: "red", children: result.output });
205
+ return null;
206
+ }
207
+ // Parse step progress from streaming output or result
208
+ const outputText = streamingOutput || result?.output || '';
209
+ const stepMatch = outputText.match(/Step\s+(\d+)\/(\d+)/gi);
210
+ const lastStep = stepMatch ? stepMatch[stepMatch.length - 1] : null;
211
+ const totalSteps = lastStep ? parseInt(lastStep.match(/\/(\d+)/)[1]) : 0;
212
+ const currentStep = lastStep ? parseInt(lastStep.match(/Step\s+(\d+)/i)[1]) : 0;
213
+ const succeeded = outputText.includes('Successfully built') || outputText.includes('Successfully tagged');
214
+ const failed = result?.isError || false;
215
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [totalSteps > 0 && (_jsx(Box, { children: _jsxs(Text, { color: succeeded ? 'green' : failed ? 'red' : 'cyan', children: [succeeded ? '[ok] ' : failed ? '[xx] ' : '[..] ', "[", currentStep, "/", totalSteps, " steps]"] }) })), outputText.split('\n').filter(l => l.trim()).slice(-5).map((line, i) => {
216
+ const isStep = /^Step\s+\d+\/\d+/i.test(line.trim());
217
+ const isSuccess = /Successfully/i.test(line);
218
+ const isError = /error/i.test(line);
219
+ return (_jsx(Text, { color: isSuccess ? 'green' : isError ? 'red' : isStep ? 'cyan' : undefined, dimColor: !isStep && !isSuccess && !isError, children: line }, i));
220
+ }), result?.isError && _jsx(Text, { color: "red", children: result.output })] }));
221
+ }
222
+ function GenericBody({ input, result, }) {
223
+ const entries = Object.entries(input).slice(0, 6);
224
+ const omitted = Object.keys(input).length - entries.length;
225
+ return (_jsxs(Box, { flexDirection: "column", children: [entries.map(([key, value]) => {
226
+ const str = String(value);
227
+ const truncated = str.length > 120;
228
+ return (_jsxs(Text, { children: [_jsxs(Text, { dimColor: true, children: [key, ": "] }), _jsx(Text, { children: truncated ? `${str.slice(0, 120)}...` : str })] }, key));
229
+ }), omitted > 0 && (_jsxs(Text, { dimColor: true, italic: true, children: ["... ", omitted, " more fields"] })), result && result.isError && _jsx(Text, { color: "red", children: result.output }), result && !result.isError && (_jsx(Text, { dimColor: true, children: result.output.length > 120 ? `${result.output.slice(0, 120)}...` : result.output }))] }));
230
+ }
231
+ /* ---------------------------------------------------------------------------
232
+ * Single tool call box
233
+ * -------------------------------------------------------------------------*/
234
+ function ToolCallBox({ toolCall, expanded }) {
235
+ const durationLabel = toolCall.duration != null ? ` (${toolCall.duration}ms)` : '';
236
+ const isLongRunning = LONG_RUNNING_TOOLS.has(toolCall.name.toLowerCase());
237
+ // Choose specialised body renderer based on tool name
238
+ const renderBody = () => {
239
+ if (!expanded && toolCall.status === 'completed') {
240
+ return (_jsx(Text, { dimColor: true, children: toolCall.result
241
+ ? toolCall.result.isError
242
+ ? toolCall.result.output.slice(0, 80)
243
+ : 'completed'
244
+ : 'completed' }));
245
+ }
246
+ const name = toolCall.name.toLowerCase();
247
+ const props = { input: toolCall.input, result: toolCall.result };
248
+ if (name === 'read_file' || name === 'read') {
249
+ return _jsx(ReadFileBody, { ...props });
250
+ }
251
+ if (name === 'edit_file' || name === 'edit') {
252
+ return _jsx(EditFileBody, { ...props });
253
+ }
254
+ if (name === 'bash' || name === 'execute' || name === 'run_command') {
255
+ return _jsx(BashBody, { ...props });
256
+ }
257
+ if (name.startsWith('terraform') || name === 'tf_plan' || name === 'tf_apply') {
258
+ return _jsx(TerraformBody, { ...props });
259
+ }
260
+ if (name === 'kubectl' || name === 'k8s') {
261
+ return _jsx(KubectlBody, { ...props });
262
+ }
263
+ if (name === 'docker') {
264
+ return _jsx(DockerBuildBody, { input: toolCall.input, result: toolCall.result, streamingOutput: toolCall.streamingOutput });
265
+ }
266
+ return _jsx(GenericBody, { ...props });
267
+ };
268
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: toolCall.status === 'failed' ? 'red' : 'gray', paddingX: 1, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(StatusBadge, { status: toolCall.status, startTime: toolCall.startTime }), _jsxs(Text, { bold: true, children: [" ", toolCall.name] }), _jsx(Text, { dimColor: true, children: durationLabel }), toolCall.status === 'completed' && toolCall.duration != null && toolCall.duration > 5000 && (_jsxs(Text, { dimColor: true, children: [" [", (toolCall.duration / 1000).toFixed(1), "s]"] })), toolCall.status === 'running' && toolCall.name === 'logs' && (_jsx(Text, { color: "cyan", children: " \u25CF LIVE" }))] }), toolCall.status === 'running' && isLongRunning && (_jsx(Text, { dimColor: true, italic: true, children: "This may take several minutes for large infrastructure changes." })), toolCall.status === 'running' && toolCall.streamingOutput && toolCall.streamingOutput.trim() && (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 2, children: [_jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, children: "[LIVE] " }), _jsx(Text, { children: '─'.repeat(34) })] }), (() => {
269
+ const allLines = toolCall.streamingOutput.split('\n');
270
+ const isTerraformOrKubectl = toolCall.name === 'terraform' || toolCall.name === 'kubectl' || toolCall.name === 'logs';
271
+ // M1: Increased streaming window — 60 lines for terraform/kubectl/logs, 40 for others
272
+ const windowSize = isTerraformOrKubectl ? 60 : 40;
273
+ const visibleLines = allLines.slice(-windowSize);
274
+ // Pad to minimum 4 lines so the live area is always visible
275
+ while (visibleLines.length < 4)
276
+ visibleLines.push('');
277
+ const hiddenCount = Math.max(0, allLines.length - windowSize);
278
+ return (_jsxs(_Fragment, { children: [hiddenCount > 0 && (_jsxs(Text, { dimColor: true, children: ["... ", hiddenCount, " earlier lines"] })), visibleLines.map((line, i) => {
279
+ // M2: Color terraform/kubectl streaming output lines
280
+ let lineColor;
281
+ if (line.match(/^\s*\+/) || line.includes('will be created') || line.includes(' created'))
282
+ lineColor = 'green';
283
+ else if (line.match(/^\s*-/) || line.includes('will be destroyed') || line.includes(' destroyed'))
284
+ lineColor = 'red';
285
+ else if (line.match(/^\s*~/) || line.includes('will be updated') || line.includes(' modified'))
286
+ lineColor = 'yellow';
287
+ return _jsx(Text, { color: lineColor ?? 'gray', dimColor: !lineColor, children: line }, i);
288
+ })] }));
289
+ })()] })), _jsx(Box, { marginTop: 1, children: renderBody() })] }));
290
+ }
291
+ /* ---------------------------------------------------------------------------
292
+ * Public component
293
+ * -------------------------------------------------------------------------*/
294
+ /**
295
+ * ToolCallDisplay renders a list of tool invocations. When `expanded` is
296
+ * false, completed calls are collapsed to a single summary line.
297
+ */
298
+ export function ToolCallDisplay({ toolCalls, expanded = true }) {
299
+ if (toolCalls.length === 0) {
300
+ return null;
301
+ }
302
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: toolCalls.map(tc => (_jsx(ToolCallBox, { toolCall: tc, expanded: expanded }, tc.id))) }));
303
+ }
@@ -0,0 +1,83 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * TreePane Component (L1)
4
+ *
5
+ * Collapsible file tree sidebar using box-drawing characters.
6
+ * Shows 2 levels deep by default; toggle via /tree slash command.
7
+ */
8
+ import { useState, useEffect } from 'react';
9
+ import { Box, Text, useInput } from 'ink';
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ function buildTree(dir, depth, maxDepth) {
13
+ if (depth > maxDepth)
14
+ return [];
15
+ try {
16
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
17
+ return entries
18
+ .filter(e => !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== 'dist')
19
+ .slice(0, 30) // limit entries per level
20
+ .map(e => {
21
+ const fullPath = path.join(dir, e.name);
22
+ const node = { name: e.name, fullPath, isDir: e.isDirectory() };
23
+ if (e.isDirectory() && depth < maxDepth) {
24
+ node.children = buildTree(fullPath, depth + 1, maxDepth);
25
+ }
26
+ return node;
27
+ });
28
+ }
29
+ catch {
30
+ return [];
31
+ }
32
+ }
33
+ function renderTree(nodes, prefix, selectedIdx, flatList, recentPaths) {
34
+ const elements = [];
35
+ nodes.forEach((node, i) => {
36
+ const isLast = i === nodes.length - 1;
37
+ const connector = isLast ? '└─' : '├─';
38
+ const childPrefix = prefix + (isLast ? ' ' : '│ ');
39
+ const globalIdx = flatList.indexOf(node);
40
+ const isSelected = globalIdx === selectedIdx;
41
+ const isRecent = recentPaths.has(node.fullPath);
42
+ elements.push(_jsxs(Text, { color: isSelected ? 'cyan' : isRecent ? 'yellow' : undefined, bold: isSelected, children: [prefix, connector, " ", node.isDir ? '[/] ' : '', node.name] }, node.fullPath));
43
+ if (node.children && node.children.length > 0) {
44
+ elements.push(...renderTree(node.children, childPrefix, selectedIdx, flatList, recentPaths));
45
+ }
46
+ });
47
+ return elements;
48
+ }
49
+ function flattenTree(nodes) {
50
+ const result = [];
51
+ for (const node of nodes) {
52
+ result.push(node);
53
+ if (node.children) {
54
+ result.push(...flattenTree(node.children));
55
+ }
56
+ }
57
+ return result;
58
+ }
59
+ export function TreePane({ cwd = process.cwd(), recentFiles = [], onSelectFile }) {
60
+ const [tree, setTree] = useState([]);
61
+ const [selectedIdx, setSelectedIdx] = useState(0);
62
+ const recentPaths = new Set(recentFiles);
63
+ useEffect(() => {
64
+ const nodes = buildTree(cwd, 0, 2);
65
+ setTree(nodes);
66
+ }, [cwd]);
67
+ const flat = flattenTree(tree);
68
+ useInput((input, key) => {
69
+ if (key.upArrow) {
70
+ setSelectedIdx(prev => Math.max(0, prev - 1));
71
+ }
72
+ else if (key.downArrow) {
73
+ setSelectedIdx(prev => Math.min(flat.length - 1, prev + 1));
74
+ }
75
+ else if (key.return && flat[selectedIdx]) {
76
+ const node = flat[selectedIdx];
77
+ if (!node.isDir && onSelectFile) {
78
+ onSelectFile(node.fullPath);
79
+ }
80
+ }
81
+ });
82
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexShrink: 0, overflow: "hidden", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Files" }), _jsx(Text, { dimColor: true, children: " (\u2191\u2193 navigate \u00B7 Enter to @ref \u00B7 /tree to toggle)" })] }), _jsxs(Text, { bold: true, dimColor: true, children: [path.basename(cwd), "/"] }), renderTree(tree, '', selectedIdx, flat, recentPaths)] }));
83
+ }