@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91

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 (411) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +3 -3
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +13 -13
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +333 -7
  94. package/dist/core/edits/format-detector.js +260 -0
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +5 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +29 -29
  108. package/dist/core/engine/anvil-client.js +214 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +129 -19
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1792 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +46 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +216 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/hooks/worktree-events.js +158 -0
  141. package/dist/core/image/renderer.js +71 -0
  142. package/dist/core/init/detector.js +582 -0
  143. package/dist/core/init/template-renderer.js +242 -0
  144. package/dist/core/jobs/registry.js +18 -18
  145. package/dist/core/ledger/results-tsv.js +142 -0
  146. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  147. package/dist/core/lsp/cache.js +105 -0
  148. package/dist/core/lsp/client.js +551 -41
  149. package/dist/core/lsp/language-detect.js +66 -0
  150. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  151. package/dist/core/lsp/server-detect.js +173 -0
  152. package/dist/core/lsp/symbol-cache.js +162 -0
  153. package/dist/core/lsp/symbol-tools.js +664 -0
  154. package/dist/core/mcp/client.js +97 -28
  155. package/dist/core/mcp/http-server.js +553 -0
  156. package/dist/core/mcp/orchestrator-tools.js +662 -0
  157. package/dist/core/mcp/permission.js +190 -0
  158. package/dist/core/mcp/registry.js +39 -17
  159. package/dist/core/mcp/server-tools.js +219 -0
  160. package/dist/core/mcp/server.js +397 -0
  161. package/dist/core/mcp/trust.js +10 -10
  162. package/dist/core/memory/dual-write.js +416 -0
  163. package/dist/core/memory/passive-extract.js +130 -0
  164. package/dist/core/memory/phase1-kinds.js +20 -0
  165. package/dist/core/memory/secret-scanner.js +304 -0
  166. package/dist/core/memory-sync/queue.js +170 -0
  167. package/dist/core/metrics/extract.js +113 -0
  168. package/dist/core/modes/roo-modes.js +68 -0
  169. package/dist/core/onboarding/ensure-initialized.js +133 -0
  170. package/dist/core/onboarding/marker.js +111 -0
  171. package/dist/core/onboarding/telemetry-state.js +108 -0
  172. package/dist/core/output-style/presets.js +176 -0
  173. package/dist/core/output-style/state.js +185 -0
  174. package/dist/core/path-security.js +287 -5
  175. package/dist/core/permission.js +82 -22
  176. package/dist/core/permissions/auto-classifier.js +124 -0
  177. package/dist/core/permissions/bash-parser.js +371 -0
  178. package/dist/core/permissions/circuit-breaker.js +83 -0
  179. package/dist/core/permissions/constrained-edit.js +91 -0
  180. package/dist/core/permissions/gate.js +278 -0
  181. package/dist/core/permissions/index.js +20 -0
  182. package/dist/core/permissions/mode.js +174 -0
  183. package/dist/core/permissions/network-egress.js +137 -0
  184. package/dist/core/permissions/state.js +241 -0
  185. package/dist/core/permissions/tool-class.js +93 -0
  186. package/dist/core/plan-mode/ui-state.js +51 -0
  187. package/dist/core/plans/plan-artifact.js +721 -0
  188. package/dist/core/policy-limits/etag-store.js +122 -0
  189. package/dist/core/prd-check/parser.js +215 -0
  190. package/dist/core/prd-check/reporter.js +127 -0
  191. package/dist/core/prd-check/session-review.js +557 -0
  192. package/dist/core/prd-check/verifiers.js +223 -0
  193. package/dist/core/prompt-cache/client-cache.js +99 -0
  194. package/dist/core/prompts/assembly.js +29 -0
  195. package/dist/core/prompts/registry.js +364 -0
  196. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  197. package/dist/core/pugi-md/context-injector.js +76 -0
  198. package/dist/core/pugi-md/walk-up.js +207 -0
  199. package/dist/core/python/uv-installer.js +270 -0
  200. package/dist/core/python/uv-resolver.js +83 -0
  201. package/dist/core/rate-limit/narrator.js +146 -0
  202. package/dist/core/recipes/cli-types.js +20 -0
  203. package/dist/core/recipes/loader.js +103 -0
  204. package/dist/core/recipes/runner.js +345 -0
  205. package/dist/core/recipes/schema.js +587 -0
  206. package/dist/core/release-notes/parser.js +241 -0
  207. package/dist/core/release-notes/state.js +116 -0
  208. package/dist/core/repl/ask.js +37 -37
  209. package/dist/core/repl/cancellation.js +26 -26
  210. package/dist/core/repl/cap-warning.js +4 -4
  211. package/dist/core/repl/clipboard-read.js +11 -11
  212. package/dist/core/repl/dispatch-fsm.js +12 -12
  213. package/dist/core/repl/history-search.js +15 -15
  214. package/dist/core/repl/history.js +28 -18
  215. package/dist/core/repl/kill-ring.js +5 -5
  216. package/dist/core/repl/model-pricing.js +135 -0
  217. package/dist/core/repl/privacy-banner.js +22 -22
  218. package/dist/core/repl/session.js +2148 -217
  219. package/dist/core/repl/slash-commands.js +501 -41
  220. package/dist/core/repl/store/index.js +1 -1
  221. package/dist/core/repl/store/jsonl-log.js +22 -22
  222. package/dist/core/repl/store/lockfile.js +10 -10
  223. package/dist/core/repl/store/session-store.js +136 -107
  224. package/dist/core/repl/store/types.js +15 -15
  225. package/dist/core/repl/store/uuid-v7.js +12 -12
  226. package/dist/core/repl/workspace-context.js +43 -21
  227. package/dist/core/repo-map/build.js +125 -0
  228. package/dist/core/repo-map/cache.js +185 -0
  229. package/dist/core/repo-map/extractor.js +254 -0
  230. package/dist/core/repo-map/formatter.js +145 -0
  231. package/dist/core/repo-map/page-rank.js +105 -0
  232. package/dist/core/repo-map/scanner.js +211 -0
  233. package/dist/core/retry-budget/budget.js +284 -0
  234. package/dist/core/retry-budget/index.js +5 -0
  235. package/dist/core/retry-budget/retry-cap.js +74 -0
  236. package/dist/core/routing/lead-worker.js +43 -0
  237. package/dist/core/routing/pre-flight-estimator.js +108 -0
  238. package/dist/core/runs/run-tree.js +103 -0
  239. package/dist/core/security/injection-scanner.js +367 -0
  240. package/dist/core/security/output-filter.js +418 -0
  241. package/dist/core/session/env-file.js +105 -0
  242. package/dist/core/session/section-budgets.js +140 -0
  243. package/dist/core/session.js +92 -0
  244. package/dist/core/settings.js +324 -5
  245. package/dist/core/share/formatter.js +271 -0
  246. package/dist/core/share/redactor.js +221 -0
  247. package/dist/core/share/uploader.js +267 -0
  248. package/dist/core/skills/defaults.js +30 -30
  249. package/dist/core/skills/loader.js +22 -22
  250. package/dist/core/skills/sources.js +27 -27
  251. package/dist/core/smoke/headless-driver.js +174 -0
  252. package/dist/core/smoke/orchestrator.js +194 -0
  253. package/dist/core/smoke/runner.js +238 -0
  254. package/dist/core/smoke/scenario-parser.js +316 -0
  255. package/dist/core/statusline.js +99 -0
  256. package/dist/core/subagents/dispatcher-real.js +600 -0
  257. package/dist/core/subagents/dispatcher.js +132 -43
  258. package/dist/core/subagents/index.js +19 -6
  259. package/dist/core/subagents/isolation-matrix.js +213 -0
  260. package/dist/core/subagents/spawn.js +19 -4
  261. package/dist/core/telemetry/emitter.js +229 -0
  262. package/dist/core/telemetry/queue.js +251 -0
  263. package/dist/core/theme/context.js +91 -0
  264. package/dist/core/theme/presets.js +228 -0
  265. package/dist/core/theme/state.js +181 -0
  266. package/dist/core/todos/invariant.js +10 -0
  267. package/dist/core/todos/state.js +177 -0
  268. package/dist/core/tool-schema/compressor.js +89 -0
  269. package/dist/core/transport/version-interceptor.js +166 -0
  270. package/dist/core/trust.js +2 -2
  271. package/dist/core/tui/thinking-block.js +64 -0
  272. package/dist/core/vim/keymap.js +288 -0
  273. package/dist/core/vim/state.js +92 -0
  274. package/dist/core/watch-markers/marker-watcher.js +133 -0
  275. package/dist/core/worktree/include-parser.js +249 -0
  276. package/dist/core/worktree-manager/cleanup.js +123 -0
  277. package/dist/core/worktree-manager/manager.js +303 -0
  278. package/dist/index.js +36 -0
  279. package/dist/runtime/bootstrap.js +190 -0
  280. package/dist/runtime/cli.js +4185 -549
  281. package/dist/runtime/commands/agents.js +31 -31
  282. package/dist/runtime/commands/budget.js +5 -5
  283. package/dist/runtime/commands/cancel.js +231 -0
  284. package/dist/runtime/commands/chain.js +489 -0
  285. package/dist/runtime/commands/codegraph-status.js +227 -0
  286. package/dist/runtime/commands/compact.js +297 -0
  287. package/dist/runtime/commands/config.js +73 -39
  288. package/dist/runtime/commands/cost.js +199 -0
  289. package/dist/runtime/commands/delegate.js +27 -4
  290. package/dist/runtime/commands/dispatch.js +126 -0
  291. package/dist/runtime/commands/doctor.js +579 -0
  292. package/dist/runtime/commands/feedback.js +184 -0
  293. package/dist/runtime/commands/hooks.js +187 -0
  294. package/dist/runtime/commands/init.js +254 -0
  295. package/dist/runtime/commands/lsp.js +200 -38
  296. package/dist/runtime/commands/mcp.js +879 -0
  297. package/dist/runtime/commands/memory.js +582 -0
  298. package/dist/runtime/commands/model.js +237 -0
  299. package/dist/runtime/commands/onboarding.js +275 -0
  300. package/dist/runtime/commands/patch.js +12 -12
  301. package/dist/runtime/commands/permissions.js +112 -0
  302. package/dist/runtime/commands/plan.js +143 -0
  303. package/dist/runtime/commands/prd-check.js +285 -0
  304. package/dist/runtime/commands/privacy.js +17 -17
  305. package/dist/runtime/commands/recipe.js +325 -0
  306. package/dist/runtime/commands/redo-blob-store.js +92 -0
  307. package/dist/runtime/commands/redo.js +361 -0
  308. package/dist/runtime/commands/release-notes.js +229 -0
  309. package/dist/runtime/commands/repo-map.js +95 -0
  310. package/dist/runtime/commands/report.js +299 -0
  311. package/dist/runtime/commands/resume.js +118 -0
  312. package/dist/runtime/commands/review-consensus.js +68 -53
  313. package/dist/runtime/commands/rewind.js +333 -0
  314. package/dist/runtime/commands/roster.js +14 -14
  315. package/dist/runtime/commands/sessions.js +163 -0
  316. package/dist/runtime/commands/share.js +316 -0
  317. package/dist/runtime/commands/skills.js +31 -31
  318. package/dist/runtime/commands/status.js +186 -0
  319. package/dist/runtime/commands/stickers.js +82 -0
  320. package/dist/runtime/commands/style.js +194 -0
  321. package/dist/runtime/commands/theme.js +196 -0
  322. package/dist/runtime/commands/undo.js +54 -22
  323. package/dist/runtime/commands/update.js +289 -0
  324. package/dist/runtime/commands/vim.js +140 -0
  325. package/dist/runtime/commands/worktree.js +8 -8
  326. package/dist/runtime/commands/worktrees.js +155 -0
  327. package/dist/runtime/headless-repl.js +195 -0
  328. package/dist/runtime/headless.js +543 -0
  329. package/dist/runtime/load-hooks-or-exit.js +71 -0
  330. package/dist/runtime/plan-decompose.js +22 -22
  331. package/dist/runtime/sigint-guard.js +272 -0
  332. package/dist/runtime/update-check.js +28 -28
  333. package/dist/runtime/version.js +65 -0
  334. package/dist/runtime/worktree-bootstrap.js +579 -0
  335. package/dist/skills/bundled/batch.js +617 -0
  336. package/dist/skills/bundled/index.js +45 -0
  337. package/dist/skills/bundled/loop.js +358 -0
  338. package/dist/skills/bundled/remember.js +383 -0
  339. package/dist/skills/bundled/simplify.js +289 -0
  340. package/dist/skills/bundled/skillify.js +373 -0
  341. package/dist/skills/bundled/stuck.js +558 -0
  342. package/dist/skills/bundled/verify.js +439 -0
  343. package/dist/testing/vcr.js +486 -0
  344. package/dist/tools/agent-tool.js +229 -0
  345. package/dist/tools/apply-patch.js +89 -28
  346. package/dist/tools/ask-user-question.js +337 -0
  347. package/dist/tools/ask-user.js +115 -0
  348. package/dist/tools/bash.js +624 -46
  349. package/dist/tools/brief.js +224 -0
  350. package/dist/tools/cron.js +433 -0
  351. package/dist/tools/enter-worktree.js +250 -0
  352. package/dist/tools/exit-worktree.js +147 -0
  353. package/dist/tools/file-tools.js +161 -44
  354. package/dist/tools/lsp-tools.js +377 -1
  355. package/dist/tools/mcp-tool.js +260 -0
  356. package/dist/tools/multi-edit.js +361 -0
  357. package/dist/tools/powershell.js +268 -0
  358. package/dist/tools/registry.js +99 -4
  359. package/dist/tools/skill-tool.js +96 -0
  360. package/dist/tools/sleep.js +99 -0
  361. package/dist/tools/synthetic-output.js +133 -0
  362. package/dist/tools/tasks.js +208 -0
  363. package/dist/tools/todo-write.js +184 -0
  364. package/dist/tools/verify-plan-execution.js +295 -0
  365. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  366. package/dist/tools/web-fetch.js +195 -10
  367. package/dist/tools/web-search.js +458 -0
  368. package/dist/tui/agent-progress-card.js +111 -0
  369. package/dist/tui/agent-tree.js +11 -1
  370. package/dist/tui/ask-modal.js +14 -14
  371. package/dist/tui/ask-user-question-chips.js +315 -0
  372. package/dist/tui/ask-user-question-prompt.js +203 -0
  373. package/dist/tui/compact-banner.js +81 -0
  374. package/dist/tui/conversation-pane.js +85 -11
  375. package/dist/tui/cost-table.js +111 -0
  376. package/dist/tui/device-flow.js +2 -2
  377. package/dist/tui/doctor-table.js +46 -0
  378. package/dist/tui/feedback-prompt.js +156 -0
  379. package/dist/tui/input-box.js +247 -32
  380. package/dist/tui/login-picker.js +3 -3
  381. package/dist/tui/markdown-render.js +6 -6
  382. package/dist/tui/multi-file-diff-approval.js +375 -0
  383. package/dist/tui/onboarding-wizard.js +240 -0
  384. package/dist/tui/permissions-picker.js +86 -0
  385. package/dist/tui/render.js +36 -1
  386. package/dist/tui/repl-render.js +176 -25
  387. package/dist/tui/repl-splash-art.js +16 -16
  388. package/dist/tui/repl-splash-mascot.js +48 -24
  389. package/dist/tui/repl-splash.js +22 -22
  390. package/dist/tui/repl.js +125 -45
  391. package/dist/tui/slash-palette.js +6 -6
  392. package/dist/tui/splash.js +2 -2
  393. package/dist/tui/status-bar.js +109 -31
  394. package/dist/tui/status-table.js +7 -0
  395. package/dist/tui/stickers-art.js +136 -0
  396. package/dist/tui/style-table.js +28 -0
  397. package/dist/tui/theme-table.js +29 -0
  398. package/dist/tui/thinking-spinner.js +123 -0
  399. package/dist/tui/tool-stream-pane.js +53 -4
  400. package/dist/tui/update-banner.js +27 -2
  401. package/dist/tui/vim-input.js +267 -0
  402. package/dist/tui/welcome-banner.js +107 -0
  403. package/dist/tui/welcome-data.js +293 -0
  404. package/dist/tui/workspace-context.js +2 -2
  405. package/package.json +31 -16
  406. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  407. package/test/scenarios/compact-force.scenario.txt +12 -0
  408. package/test/scenarios/identity.scenario.txt +12 -0
  409. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  410. package/test/scenarios/walkback.scenario.txt +12 -0
  411. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,486 @@
1
+ /**
2
+ * VCR — Pugi CLI test helper that records `fetch` interactions to a JSON
3
+ * cassette and replays them deterministically. independent implementation re-implementation
4
+ * of the external `vcr.ts` pattern; Node built-ins only (`node:fs`,
5
+ * `node:crypto`, `node:path`), no new dependencies.
6
+ *
7
+ * # Why this exists
8
+ *
9
+ * Hand-stubbed `globalThis.fetch =` blocks are spread across the CLI
10
+ * test corpus (`anvil-client-e2-contract.spec.ts`, `submit-delegate.spec.ts`,
11
+ * `cost-quota-slash.spec.ts`, `repl-render-transport.spec.ts`). They
12
+ * work but the response bodies are inlined as JSON literals — drifts
13
+ * between the real admin-api wire shape and the test fixture are
14
+ * caught only when production breaks.
15
+ *
16
+ * VCR shifts the source-of-truth: a real run records once to a cassette,
17
+ * subsequent runs replay deterministically. Operators can re-record by
18
+ * deleting the cassette file (auto-mode flips back to record).
19
+ *
20
+ * # Lifecycle
21
+ *
22
+ * const tape = vcr({ path: '...', mode: 'auto' });
23
+ * const restore = tape.install();
24
+ * try {
25
+ * await codeUnderTestThatCallsFetch();
26
+ * await tape.flush(); // no-op in replay mode
27
+ * } finally {
28
+ * restore();
29
+ * }
30
+ *
31
+ * # Matching strategy
32
+ *
33
+ * `method + url + sha256(canonical-body)`. Bodies are normalised when
34
+ * the request advertises `content-type: application/json` so timestamp
35
+ * / uuid wobble inside a JSON envelope does NOT cause a cassette miss.
36
+ *
37
+ * # Security
38
+ *
39
+ * Sensitive headers (`authorization`, `x-pugi-bearer`, `x-api-key`,
40
+ * `cookie`) are redacted at write time so a cassette accidentally
41
+ * committed never leaks a real bearer token. Redaction is one-way —
42
+ * the cassette retains `[REDACTED]` and replay matches on the original
43
+ * scrubbed value (replay does not re-hash the headers; only the body
44
+ * participates in the match key, which keeps record/replay symmetric).
45
+ *
46
+ * # Atomicity
47
+ *
48
+ * Cassette writes go `tmp + rename` so a concurrent reader never sees
49
+ * a half-written JSON document (mirrors the agent-progress writer
50
+ * pattern in `src/core/agent-progress/writer.ts`).
51
+ */
52
+ import { createHash } from 'node:crypto';
53
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
54
+ import { dirname } from 'node:path';
55
+ /** Cassette schema version. Bump on breaking changes to the JSON shape. */
56
+ export const VCR_CASSETTE_VERSION = 1;
57
+ /** Headers that are scrubbed to `[REDACTED]` at cassette-write time. */
58
+ export const VCR_SENSITIVE_HEADERS = Object.freeze([
59
+ 'authorization',
60
+ 'x-pugi-bearer',
61
+ 'x-api-key',
62
+ 'cookie',
63
+ ]);
64
+ /** Placeholder written in place of a sensitive header value. */
65
+ export const VCR_REDACTED = '[REDACTED]';
66
+ /**
67
+ * Error thrown when replay mode looks up a request that is NOT in the
68
+ * cassette. The message lists the cassette's recorded URLs so the
69
+ * operator immediately sees whether the test drifted or the cassette
70
+ * is stale.
71
+ */
72
+ export class VcrCassetteMiss extends Error {
73
+ request;
74
+ recordedUrls;
75
+ constructor(request, recordedUrls) {
76
+ const recordedList = recordedUrls.length
77
+ ? recordedUrls.map((u) => ` - ${u}`).join('\n')
78
+ : ' (empty cassette)';
79
+ super(`VCR cassette miss: ${request.method} ${request.url}\n` +
80
+ `Recorded URLs:\n${recordedList}\n` +
81
+ 'Re-record by deleting the cassette and running the test, ' +
82
+ 'or check whether the request shape drifted.');
83
+ this.name = 'VcrCassetteMiss';
84
+ this.request = request;
85
+ this.recordedUrls = recordedUrls;
86
+ }
87
+ }
88
+ /**
89
+ * Error thrown when a cassette file on disk advertises a `version`
90
+ * that this VCR build does not understand. Loud failure is the right
91
+ * call — silent best-effort would replay against a shape the test
92
+ * expects to be a different one.
93
+ */
94
+ export class VcrCassetteVersionMismatch extends Error {
95
+ cassettePath;
96
+ foundVersion;
97
+ expectedVersion;
98
+ constructor(cassettePath, foundVersion) {
99
+ super(`VCR cassette ${cassettePath} advertises version ${foundVersion}; ` +
100
+ `this build expects version ${VCR_CASSETTE_VERSION}. ` +
101
+ 'Delete the cassette and re-record, or upgrade the helper.');
102
+ this.name = 'VcrCassetteVersionMismatch';
103
+ this.cassettePath = cassettePath;
104
+ this.foundVersion = foundVersion;
105
+ this.expectedVersion = VCR_CASSETTE_VERSION;
106
+ }
107
+ }
108
+ /* ------------------------------------------------------------------ */
109
+ /* Internal helpers */
110
+ /* ------------------------------------------------------------------ */
111
+ /**
112
+ * Canonicalise a JSON body string so two semantically-equal payloads
113
+ * with different key order hash identically. Non-JSON bodies pass
114
+ * through unchanged.
115
+ *
116
+ * The contract intentionally does NOT walk arrays or recursively
117
+ * normalise nested objects beyond their key order — that is a future
118
+ * refinement if cassettes start to miss on collection re-orderings.
119
+ */
120
+ function canonicaliseBodyForHash(body, contentType) {
121
+ if (body === undefined || body === '')
122
+ return '';
123
+ const isJson = typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
124
+ if (!isJson)
125
+ return body;
126
+ try {
127
+ const parsed = JSON.parse(body);
128
+ return canonicalStringify(parsed);
129
+ }
130
+ catch {
131
+ // Body advertised JSON but was not parseable — fall back to the raw
132
+ // string so the test still produces a stable hash and the operator
133
+ // sees the broken body during diagnostics.
134
+ return body;
135
+ }
136
+ }
137
+ /**
138
+ * Stable JSON.stringify with sorted object keys. Arrays preserve order
139
+ * (order is semantically meaningful in the wire shape).
140
+ */
141
+ function canonicalStringify(value) {
142
+ if (value === null || typeof value !== 'object') {
143
+ return JSON.stringify(value);
144
+ }
145
+ if (Array.isArray(value)) {
146
+ return `[${value.map((v) => canonicalStringify(v)).join(',')}]`;
147
+ }
148
+ const entries = Object.entries(value)
149
+ .filter(([, v]) => v !== undefined)
150
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
151
+ const body = entries
152
+ .map(([k, v]) => `${JSON.stringify(k)}:${canonicalStringify(v)}`)
153
+ .join(',');
154
+ return `{${body}}`;
155
+ }
156
+ /**
157
+ * Normalise a Headers / plain-object header bag into a lowercased
158
+ * record. The cassette stores lowercase keys exclusively so a runtime
159
+ * fetch that sends `Authorization` matches a recorded `authorization`
160
+ * fixture without ambiguity.
161
+ */
162
+ function normaliseHeaders(input) {
163
+ const out = {};
164
+ if (!input)
165
+ return out;
166
+ if (input instanceof Headers) {
167
+ input.forEach((value, key) => {
168
+ out[key.toLowerCase()] = value;
169
+ });
170
+ return out;
171
+ }
172
+ if (Array.isArray(input)) {
173
+ for (const entry of input) {
174
+ if (Array.isArray(entry) && entry.length === 2) {
175
+ const [k, v] = entry;
176
+ if (typeof k === 'string' && typeof v === 'string') {
177
+ out[k.toLowerCase()] = v;
178
+ }
179
+ }
180
+ }
181
+ return out;
182
+ }
183
+ if (typeof input === 'object') {
184
+ for (const [k, v] of Object.entries(input)) {
185
+ if (typeof v === 'string') {
186
+ out[k.toLowerCase()] = v;
187
+ }
188
+ else if (typeof v === 'number' || typeof v === 'boolean') {
189
+ out[k.toLowerCase()] = String(v);
190
+ }
191
+ }
192
+ }
193
+ return out;
194
+ }
195
+ /**
196
+ * Redact sensitive headers in-place by replacing their values with
197
+ * `[REDACTED]`. The exported list is canonical lowercase; the headers
198
+ * argument is also normalised to lowercase keys before this is called.
199
+ */
200
+ function redactHeaders(headers) {
201
+ const out = { ...headers };
202
+ for (const key of VCR_SENSITIVE_HEADERS) {
203
+ if (key in out) {
204
+ out[key] = VCR_REDACTED;
205
+ }
206
+ }
207
+ return out;
208
+ }
209
+ /**
210
+ * Resolve `fetch` arguments into the canonical request shape used by
211
+ * the matcher and cassette writer.
212
+ */
213
+ function buildRequest(input, init) {
214
+ let method = 'GET';
215
+ let url;
216
+ let headers = {};
217
+ let body;
218
+ if (typeof input === 'string') {
219
+ url = input;
220
+ }
221
+ else if (input instanceof URL) {
222
+ url = input.toString();
223
+ }
224
+ else if (typeof input === 'object' && input !== null && 'url' in input) {
225
+ // Request object
226
+ const req = input;
227
+ url = req.url;
228
+ method = req.method;
229
+ headers = normaliseHeaders(req.headers);
230
+ }
231
+ else {
232
+ url = String(input);
233
+ }
234
+ if (init) {
235
+ if (typeof init.method === 'string')
236
+ method = init.method;
237
+ if (init.headers !== undefined) {
238
+ headers = { ...headers, ...normaliseHeaders(init.headers) };
239
+ }
240
+ if (typeof init.body === 'string') {
241
+ body = init.body;
242
+ }
243
+ else if (init.body instanceof URLSearchParams) {
244
+ body = init.body.toString();
245
+ }
246
+ else if (init.body !== undefined && init.body !== null) {
247
+ // ArrayBuffer / Blob / ReadableStream are not supported in VCR
248
+ // record mode — fall back to JSON.stringify so the matcher key
249
+ // is at least stable. The recorded body still surfaces in the
250
+ // cassette so the operator can see what landed.
251
+ try {
252
+ body = JSON.stringify(init.body);
253
+ }
254
+ catch {
255
+ body = String(init.body);
256
+ }
257
+ }
258
+ }
259
+ return {
260
+ method: method.toUpperCase(),
261
+ url,
262
+ headers,
263
+ body,
264
+ };
265
+ }
266
+ /**
267
+ * Compute the match key for an interaction. The cassette key uses
268
+ * sha256 because collision probability across a test suite is
269
+ * astronomically low, and the hex is short enough to log.
270
+ */
271
+ function matchKey(request) {
272
+ const contentType = request.headers['content-type'];
273
+ const canonicalBody = canonicaliseBodyForHash(request.body, contentType);
274
+ const hash = createHash('sha256')
275
+ .update(`${request.method}\n${request.url}\n${canonicalBody}`)
276
+ .digest('hex');
277
+ return hash;
278
+ }
279
+ function loadCassette(path) {
280
+ if (!existsSync(path))
281
+ return [];
282
+ const raw = readFileSync(path, 'utf8');
283
+ if (raw.trim() === '')
284
+ return [];
285
+ const parsed = JSON.parse(raw);
286
+ if (typeof parsed !== 'object' ||
287
+ parsed === null ||
288
+ !('version' in parsed) ||
289
+ !('interactions' in parsed)) {
290
+ throw new Error(`VCR cassette ${path} has invalid shape (missing version / interactions)`);
291
+ }
292
+ const cast = parsed;
293
+ if (typeof cast.version !== 'number' || cast.version !== VCR_CASSETTE_VERSION) {
294
+ throw new VcrCassetteVersionMismatch(path, typeof cast.version === 'number' ? cast.version : -1);
295
+ }
296
+ if (!Array.isArray(cast.interactions)) {
297
+ throw new Error(`VCR cassette ${path}: interactions must be an array`);
298
+ }
299
+ return cast.interactions;
300
+ }
301
+ function writeCassetteAtomic(path, interactions) {
302
+ const parent = dirname(path);
303
+ if (!existsSync(parent)) {
304
+ mkdirSync(parent, { recursive: true });
305
+ }
306
+ const file = {
307
+ version: VCR_CASSETTE_VERSION,
308
+ interactions: [...interactions],
309
+ };
310
+ const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
311
+ const body = `${JSON.stringify(file, null, 2)}\n`;
312
+ try {
313
+ writeFileSync(tmpPath, body, 'utf8');
314
+ renameSync(tmpPath, path);
315
+ }
316
+ catch (err) {
317
+ try {
318
+ rmSync(tmpPath, { force: true });
319
+ }
320
+ catch {
321
+ // best-effort
322
+ }
323
+ throw err;
324
+ }
325
+ }
326
+ /* ------------------------------------------------------------------ */
327
+ /* Public factory */
328
+ /* ------------------------------------------------------------------ */
329
+ /** Internal guard — only one VCR can be installed on globalThis at a time. */
330
+ let installed = false;
331
+ export function vcr(cassette) {
332
+ // Snapshot the recorded interactions at construction time so the
333
+ // matcher does not re-read the cassette on every fetch call.
334
+ //
335
+ // In explicit `mode: 'record'` we swallow VcrCassetteVersionMismatch
336
+ // and treat the cassette as empty — the whole point of explicit
337
+ // record mode is to nuke and rewrite, so refusing to construct on a
338
+ // stale on-disk version would brick the re-record workflow.
339
+ // Triple-review Claude P1,.
340
+ let recordedFromDisk;
341
+ try {
342
+ recordedFromDisk = loadCassette(cassette.path);
343
+ }
344
+ catch (err) {
345
+ if (cassette.mode === 'record' && err instanceof VcrCassetteVersionMismatch) {
346
+ recordedFromDisk = [];
347
+ }
348
+ else {
349
+ throw err;
350
+ }
351
+ }
352
+ const newlyRecorded = [];
353
+ const effectiveMode = cassette.mode === 'auto'
354
+ ? recordedFromDisk.length > 0 || existsSync(cassette.path)
355
+ ? 'replay'
356
+ : 'record'
357
+ : cassette.mode;
358
+ // Precompute the match index for replay mode so lookup is O(1).
359
+ const replayIndex = new Map();
360
+ if (effectiveMode === 'replay') {
361
+ for (const interaction of recordedFromDisk) {
362
+ replayIndex.set(matchKey(interaction.request), interaction);
363
+ }
364
+ }
365
+ let originalFetch;
366
+ function buildResponse(interaction) {
367
+ const headers = new Headers();
368
+ for (const [k, v] of Object.entries(interaction.response.headers)) {
369
+ headers.set(k, v);
370
+ }
371
+ return new Response(interaction.response.body, {
372
+ status: interaction.response.status,
373
+ headers,
374
+ });
375
+ }
376
+ async function interceptor(input, init) {
377
+ const request = buildRequest(input, init);
378
+ const key = matchKey(request);
379
+ if (effectiveMode === 'replay') {
380
+ const hit = replayIndex.get(key);
381
+ if (!hit) {
382
+ const recordedUrls = recordedFromDisk.map((r) => `${r.request.method} ${r.request.url}`);
383
+ throw new VcrCassetteMiss(request, recordedUrls);
384
+ }
385
+ return buildResponse(hit);
386
+ }
387
+ // Record mode: pass through to the real network, then store.
388
+ if (!originalFetch) {
389
+ // Defensive — should not happen because install() captures it.
390
+ throw new Error('VCR record mode requires a captured original fetch but found none');
391
+ }
392
+ const liveResponse = await originalFetch(input, init);
393
+ // Clone before draining the body so the caller still gets a
394
+ // readable Response with its own untouched stream.
395
+ const cloned = liveResponse.clone();
396
+ const responseBody = await cloned.text();
397
+ const responseHeaders = {};
398
+ liveResponse.headers.forEach((value, key) => {
399
+ responseHeaders[key.toLowerCase()] = value;
400
+ });
401
+ const recordedRequestHeaders = redactHeaders(request.headers);
402
+ const interaction = {
403
+ request: {
404
+ method: request.method,
405
+ url: request.url,
406
+ headers: recordedRequestHeaders,
407
+ body: request.body,
408
+ },
409
+ response: {
410
+ status: liveResponse.status,
411
+ headers: responseHeaders,
412
+ body: responseBody,
413
+ },
414
+ recordedAt: new Date().toISOString(),
415
+ };
416
+ newlyRecorded.push(interaction);
417
+ return liveResponse;
418
+ }
419
+ return {
420
+ install() {
421
+ if (installed) {
422
+ throw new Error('VCR is already installed on globalThis.fetch. ' +
423
+ 'Restore the previous tape before installing a new one.');
424
+ }
425
+ originalFetch = globalThis.fetch;
426
+ installed = true;
427
+ globalThis.fetch = interceptor;
428
+ return () => {
429
+ // Idempotent restore — calling twice is safe.
430
+ if (!installed)
431
+ return;
432
+ if (originalFetch) {
433
+ globalThis.fetch = originalFetch;
434
+ }
435
+ installed = false;
436
+ };
437
+ },
438
+ recordedCount() {
439
+ return newlyRecorded.length;
440
+ },
441
+ async flush() {
442
+ if (effectiveMode === 'replay')
443
+ return;
444
+ if (newlyRecorded.length === 0)
445
+ return;
446
+ // Merge any pre-existing on-disk interactions (e.g. auto-mode
447
+ // started fresh but a parallel process raced us) with the newly
448
+ // recorded ones. On-disk wins for duplicate keys so a manual
449
+ // edit to the cassette is not stomped.
450
+ const merged = [];
451
+ const seen = new Set();
452
+ const existing = loadCassette(cassette.path);
453
+ for (const interaction of existing) {
454
+ const k = matchKey(interaction.request);
455
+ seen.add(k);
456
+ merged.push(interaction);
457
+ }
458
+ for (const interaction of newlyRecorded) {
459
+ const k = matchKey(interaction.request);
460
+ if (seen.has(k))
461
+ continue;
462
+ seen.add(k);
463
+ merged.push(interaction);
464
+ }
465
+ writeCassetteAtomic(cassette.path, merged);
466
+ },
467
+ };
468
+ }
469
+ /**
470
+ * Test-only escape hatch: forcibly clear the install latch. Use from
471
+ * an `afterEach` if a test crashed mid-install before its restore
472
+ * handle could run. NEVER call from production code.
473
+ */
474
+ export function __resetVcrInstallLatchForTests() {
475
+ installed = false;
476
+ }
477
+ /**
478
+ * Build a synthetic cassette file on disk. Useful in tests that want
479
+ * to exercise replay-mode against an inline fixture (cassettes are
480
+ * gitignored so a checked-in fixture is not the right pattern). Writes
481
+ * atomically using the same tmp+rename path as the record-mode flush.
482
+ */
483
+ export function writeCassetteFixtureForTests(path, interactions) {
484
+ writeCassetteAtomic(path, interactions);
485
+ }
486
+ //# sourceMappingURL=vcr.js.map