@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,86 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ import { PERMISSION_MODES, PERMISSION_MODE_GLOSS, } from '../core/permissions/index.js';
5
+ /**
6
+ * Build the rendered rows from the canonical mode list. We map the
7
+ * 1-line gloss → row hint so `/permissions` and the picker stay in
8
+ * sync — a change to `PERMISSION_MODE_GLOSS` shows up here too.
9
+ */
10
+ const ITEMS = PERMISSION_MODES.map((mode) => ({
11
+ mode,
12
+ title: titleCase(mode),
13
+ hint: PERMISSION_MODE_GLOSS[mode],
14
+ }));
15
+ function titleCase(mode) {
16
+ return mode.charAt(0).toUpperCase() + mode.slice(1);
17
+ }
18
+ /**
19
+ * Resolve the initial cursor — prefer the explicit `initialIndex`
20
+ * (spec / test) when provided, otherwise highlight the row matching
21
+ * the active mode so the operator opens the picker with their
22
+ * current selection focused.
23
+ */
24
+ function resolveInitialIndex(currentMode, override) {
25
+ if (typeof override === 'number') {
26
+ return Math.min(Math.max(override, 0), ITEMS.length - 1);
27
+ }
28
+ const idx = ITEMS.findIndex((item) => item.mode === currentMode);
29
+ return idx >= 0 ? idx : 0;
30
+ }
31
+ export function PermissionsPicker(props) {
32
+ const [index, setIndex] = useState(resolveInitialIndex(props.currentMode, props.initialIndex));
33
+ useInput((input, key) => {
34
+ if (key.upArrow || input === 'k') {
35
+ setIndex((current) => (current === 0 ? ITEMS.length - 1 : current - 1));
36
+ return;
37
+ }
38
+ if (key.downArrow || input === 'j') {
39
+ setIndex((current) => (current === ITEMS.length - 1 ? 0 : current + 1));
40
+ return;
41
+ }
42
+ if (key.return) {
43
+ const selected = ITEMS[index];
44
+ if (selected)
45
+ props.onSelect(selected.mode);
46
+ return;
47
+ }
48
+ if (key.escape || input === 'q') {
49
+ props.onCancel();
50
+ return;
51
+ }
52
+ // : number shortcuts mirror the canonical 6-mode order.
53
+ // Operators coming from the upstream tool reach the same mode with the
54
+ // same digit (1=default, 2=acceptEdits, …, 6=bypassPermissions).
55
+ if (input === '1')
56
+ props.onSelect('default');
57
+ if (input === '2')
58
+ props.onSelect('acceptEdits');
59
+ if (input === '3')
60
+ props.onSelect('plan');
61
+ if (input === '4')
62
+ props.onSelect('auto');
63
+ if (input === '5')
64
+ props.onSelect('dontAsk');
65
+ if (input === '6')
66
+ props.onSelect('bypassPermissions');
67
+ });
68
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Permission mode" }), _jsx(Text, { dimColor: true, children: ` (current: ${props.currentMode} — ${props.sourceLabel})` })] }), props.firstRun ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "First time? Mode = Ask \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E. Use /permissions \u043A change later." }) })) : null, _jsx(Box, { marginTop: 1, flexDirection: "column", children: ITEMS.map((item, itemIndex) => {
69
+ const isSelected = itemIndex === index;
70
+ const isCurrent = item.mode === props.currentMode;
71
+ return (_jsx(PickerRow, { isSelected: isSelected, isCurrent: isCurrent, title: item.title, hint: item.hint }, item.mode));
72
+ }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: '↑/↓ select Enter confirm Esc cancel' }) })] }));
73
+ }
74
+ function PickerRow({ isSelected, isCurrent, title, hint, }) {
75
+ // Arrow glyph + padded title so highlighted and dim rows share
76
+ // column alignment. A trailing ` ●` marks the currently-effective
77
+ // mode (separate from cursor focus) so an operator instantly sees
78
+ // which row is "what I have now" vs "what I'm hovering".
79
+ const indicator = isSelected ? '▸ ' : ' ';
80
+ // : longest title is `BypassPermissions` (17 chars). Bump the
81
+ // pad column так the gloss column stays aligned across all 6 rows.
82
+ const padded = title.padEnd(18, ' ');
83
+ const currentMarker = isCurrent ? ' ●' : ' ';
84
+ return (_jsxs(Text, { children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, bold: isSelected, children: [indicator, padded] }), _jsx(Text, { color: isCurrent ? 'green' : undefined, children: currentMarker }), _jsx(Text, { dimColor: true, children: ` ${hint}` })] }));
85
+ }
86
+ //# sourceMappingURL=permissions-picker.js.map
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { render } from 'ink';
3
3
  import { DeviceFlow } from './device-flow.js';
4
4
  import { LoginPicker } from './login-picker.js';
5
+ import { PermissionsPicker } from './permissions-picker.js';
5
6
  import { Splash } from './splash.js';
6
7
  import { collectSplashData } from './splash-data.js';
7
8
  /**
@@ -25,7 +26,7 @@ export async function renderSplash(cliVersion) {
25
26
  * Sentinel thrown when the user dismisses the login picker via Esc
26
27
  * or `q`. The CLI dispatcher catches it, prints a one-line abort
27
28
  * message, and exits 130 (the standard exit code for SIGINT-style
28
- * user cancellations — matches gh CLI, codex, claude-code).
29
+ * user cancellations — matches gh CLI and peer CLIs).
29
30
  */
30
31
  export class LoginCancelledError extends Error {
31
32
  constructor() {
@@ -66,6 +67,40 @@ export function renderLoginPicker(apiUrl) {
66
67
  }));
67
68
  });
68
69
  }
70
+ /**
71
+ * Sentinel thrown when the operator dismisses the permissions picker
72
+ * via Esc / q. Mirrors `LoginCancelledError` — the host catches and
73
+ * prints a one-line abort message; no mode flip lands.
74
+ */
75
+ export class PermissionsPickerCancelledError extends Error {
76
+ constructor() {
77
+ super('Permissions picker cancelled');
78
+ this.name = 'PermissionsPickerCancelledError';
79
+ }
80
+ }
81
+ export function renderPermissionsPicker(options) {
82
+ return new Promise((resolveMode, rejectMode) => {
83
+ let settled = false;
84
+ const finish = (cb) => {
85
+ if (settled)
86
+ return;
87
+ settled = true;
88
+ instance.unmount();
89
+ setImmediate(cb);
90
+ };
91
+ const instance = render(React.createElement(PermissionsPicker, {
92
+ currentMode: options.currentMode,
93
+ sourceLabel: options.sourceLabel,
94
+ firstRun: options.firstRun ?? false,
95
+ onSelect: (mode) => {
96
+ finish(() => resolveMode(mode));
97
+ },
98
+ onCancel: () => {
99
+ finish(() => rejectMode(new PermissionsPickerCancelledError()));
100
+ },
101
+ }));
102
+ });
103
+ }
69
104
  /**
70
105
  * Mount `<DeviceFlow />` on a TTY and return a handle the host uses to
71
106
  * drive the frame. Mirrors `renderLoginPicker`'s lifecycle: we
@@ -1,29 +1,35 @@
1
1
  /**
2
- * Production REPL mount + transport - Sprint α5.7.
2
+ * Production REPL mount + transport - Sprint .
3
3
  *
4
4
  * Owns the Ink mount lifecycle for `<Repl />` and wires the real
5
5
  * fetch + SSE transport. The CLI dispatcher in `runtime/cli.ts` calls
6
6
  * `renderRepl` on the bare-`pugi` path when stdin / stdout are TTYs.
7
7
  *
8
8
  * The transport speaks to admin-api:
9
- * POST /api/pugi/sessions → { sessionId }
10
- * POST /api/pugi/sessions/:id/brief → { dispatchId }
11
- * POST /api/pugi/sessions/:id/stop → { stopped }
12
- * GET /api/pugi/sessions/:id/stream → text/event-stream
9
+ * POST /api/pugi/sessions → { sessionId }
10
+ * POST /api/pugi/sessions/:id/brief → { dispatchId }
11
+ * POST /api/pugi/sessions/:id/stop → { stopped }
12
+ * GET /api/pugi/sessions/:id/stream → text/event-stream
13
13
  *
14
14
  * SSE is parsed client-side from a streaming fetch response - Node 22
15
15
  * native fetch returns a WHATWG `ReadableStream` which we feed through
16
16
  * a tiny `event:`/`data:`/`id:` parser. This keeps the dependency
17
17
  * graph at zero new packages.
18
18
  */
19
+ import { existsSync } from 'node:fs';
20
+ import { resolve } from 'node:path';
19
21
  import React from 'react';
20
22
  import { render } from 'ink';
21
23
  import { Repl } from './repl.js';
22
24
  import { printPugMascotPreInk } from './repl-splash-mascot.js';
25
+ import { collectWelcomeData } from './welcome-data.js';
26
+ import { ThemeProvider } from '../core/theme/context.js';
27
+ import { resolveTheme } from '../core/theme/state.js';
23
28
  import { ReplSession, } from '../core/repl/session.js';
24
29
  import { resolveWorkspaceContext } from '../core/repl/workspace-context.js';
25
30
  import { SqliteSessionStore } from '../core/repl/store/index.js';
26
31
  import { slugForCwd } from '../core/repl/history.js';
32
+ import { loadSettings } from '../core/settings.js';
27
33
  import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../core/context/index.js';
28
34
  /**
29
35
  * Mount the REPL and resolve when the user exits via Ctrl+C × 2 or
@@ -31,7 +37,7 @@ import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../
31
37
  * `pugi resume <sessionId>` once that command exists).
32
38
  */
33
39
  export async function renderRepl(options) {
34
- // beta.9 CEO dogfood 2026-05-26: claim stdin raw mode + alt-screen
40
+ // beta.9 CEO dogfood: claim stdin raw mode + alt-screen
35
41
  // BEFORE any async bootstrap step so keystrokes typed during the
36
42
  // [launch -> Ink mount] window cannot echo into the terminal in
37
43
  // cooked mode. Previously openLocalStore (SQLite open) +
@@ -41,7 +47,7 @@ export async function renderRepl(options) {
41
47
  // every typed character literally onto the screen below the
42
48
  // pre-printed mascot/header. The visible result was the operator's
43
49
  // "ssssss" landing on the rendered status-bar bottom row (CEO
44
- // screenshot 2026-05-26: beta.8 REPL bug 2).
50
+ // screenshot: beta.8 REPL bug 2).
45
51
  //
46
52
  // The claim is idempotent with Ink's own raw-mode enable: Ink
47
53
  // ref-counts setRawMode calls, and Node's stdin.setRawMode is
@@ -50,13 +56,67 @@ export async function renderRepl(options) {
50
56
  // top, and our finally{} restore drops the floor only after Ink
51
57
  // has cleanly torn down (or never mounted on a bootstrap crash).
52
58
  const bootstrap = claimTerminalForRepl();
59
+ // beta.13 auto-init wire (CEO dogfood): scaffold the
60
+ // `.pugi/` workspace silently on REPL boot so launching `pugi` in a
61
+ // fresh cwd no longer demands an explicit `pugi init` round-trip.
62
+ // Idempotent — every helper inside scaffoldPugiWorkspace is a
63
+ // `*_IfMissing` write, so re-running over an existing workspace is
64
+ // a no-op. Fail-safe: any FS / perms error never blocks REPL launch.
65
+ // Operator escape hatch: PUGI_NO_AUTO_INIT=1.
66
+ //
67
+ // Beta.13 P2 fix: gate the scaffold on project-root markers
68
+ // so launching `pugi` from `$HOME` / `/tmp` / arbitrary dirs does NOT
69
+ // sprinkle `.pugi/` directories all over the filesystem. The gate
70
+ // mirrors `isBoundWorkspace` from workspace-context.ts but also
71
+ // accepts non-JS roots (Cargo / pyproject / go.mod) because the CLI
72
+ // is language-agnostic and an operator working in a Rust repo deserves
73
+ // the same auto-init UX as a Node operator. Already-bound `.pugi/`
74
+ // dirs also opt back in so the scaffold can fill any missing
75
+ // sub-artifacts the operator deleted.
76
+ // `--bare` (PUGI_BARE=1) ALSO suppresses the
77
+ // auto-init scaffold. Bare mode is the deterministic "fresh install
78
+ // anywhere" path — no `.pugi/` writes, no PUGI.md scaffold, no
79
+ // settings.json seed. The pre-existing PUGI_NO_AUTO_INIT escape
80
+ // hatch stays — bare mode just unions with it.
81
+ const { isBareMode } = await import('../core/bare-mode/index.js');
82
+ if (process.env.PUGI_NO_AUTO_INIT !== '1' &&
83
+ !isBareMode() &&
84
+ isProjectRoot(process.cwd())) {
85
+ try {
86
+ const { scaffoldPugiWorkspace } = await import('../runtime/cli.js');
87
+ await scaffoldPugiWorkspace({
88
+ cwd: process.cwd(),
89
+ noDefaults: true,
90
+ log: () => {
91
+ /* silent — never leak scaffold progress into the REPL alt-screen */
92
+ },
93
+ });
94
+ }
95
+ catch (err) {
96
+ // Fail-safe: read-only FS or perms error never blocks REPL launch.
97
+ // Beta.13 P2 fix: bare-catch swallowed the diagnostic;
98
+ // surface it on stderr under PUGI_DEBUG=1 so operator-triage on
99
+ // "why isn't .pugi/ being created?" has a starting point without
100
+ // having to re-instrument the bootstrap.
101
+ if (process.env.PUGI_DEBUG === '1') {
102
+ const msg = err instanceof Error ? err.message : String(err);
103
+ process.stderr.write(`[pugi-debug] auto-init failed: ${msg}\n`);
104
+ }
105
+ }
106
+ }
53
107
  const transport = createProductionTransport();
54
- // Auto-bind the workspace context from process.cwd() so Mira knows
108
+ // Auto-bind the workspace context from process.cwd() so Pugi knows
55
109
  // which repo the operator launched the CLI in. The resolver is
56
110
  // best-effort — any FS error falls back to a basename-only summary,
57
- // never blocks REPL launch. Wave 4 fix 2026-05-25.
111
+ // never blocks REPL launch. fix.
58
112
  const workspace = options.workspace ?? resolveWorkspaceContext(process.cwd());
59
- // α6.4: open the local SessionStore for `/resume` persistence. The
113
+ // Beta.13 P1 fix: read `ui.cyberZoo` from
114
+ // `.pugi/settings.json` so the operator's splash posture flows to
115
+ // admin-api on session open. Without this, the renderer's `cyberZoo`
116
+ // parameter (added beta.13) was always defaulted to 'on' regardless
117
+ // of the operator's actual setting.
118
+ const cyberZoo = readCyberZooSetting(process.cwd());
119
+ // : open the local SessionStore for `/resume` persistence. The
60
120
  // store lives under `~/.pugi/projects/<slug>/`; failure is fail-safe
61
121
  // — we log a one-line warning to stderr and continue with the REPL
62
122
  // in memory-only mode. Lock-busy errors get the friendliest message
@@ -68,7 +128,7 @@ export async function renderRepl(options) {
68
128
  workspaceRoot: process.cwd(),
69
129
  resumeLocalSessionId: options.resumeLocalSessionId,
70
130
  });
71
- // α6.5 three-tier context bootstrap. The skeleton + working set
131
+ // three-tier context bootstrap. The skeleton + working set
72
132
  // + watcher are local-first and best-effort: every step is wrapped
73
133
  // in try/catch so an unreadable workspace never blocks REPL launch.
74
134
  // Opt-out via PUGI_DISABLE_CONTEXT=1 for hermetic test runs.
@@ -83,6 +143,7 @@ export async function renderRepl(options) {
83
143
  cliVersion: options.cliVersion,
84
144
  transport,
85
145
  workspace,
146
+ cyberZoo,
86
147
  store,
87
148
  localSessionId: openedSessionId,
88
149
  repoSkeleton: skeleton,
@@ -113,7 +174,7 @@ export async function renderRepl(options) {
113
174
  // typed it after the REPL became interactive. Idempotent: no-op
114
175
  // when stdin is not a TTY or no bytes were buffered.
115
176
  drainBufferedStdin(process.stdin);
116
- // α6.14.2 wave 5: paint the chafa-baked brand-pug ANSI render to
177
+ // wave 5: paint the chafa-baked brand-pug ANSI render to
117
178
  // stdout BEFORE Ink mounts (but AFTER alt-screen enter). Ink's
118
179
  // layout engine would mis-measure the truecolor escape sequences,
119
180
  // so the pug must land verbatim. The flag is passed into <Repl />
@@ -123,13 +184,45 @@ export async function renderRepl(options) {
123
184
  // (operator opted out via --no-splash), we suppress the pre-print
124
185
  // too so the boot stays silent.
125
186
  const mascotPrePrinted = options.skipSplash === true ? false : printPugMascotPreInk(process.stdout);
126
- const instance = render(React.createElement(Repl, {
187
+ // resolve the active theme ONCE at mount
188
+ // and wrap `<Repl />` in `<ThemeProvider>` so every Ink consumer
189
+ // (`<Header>`, `<DoctorTable>`, `<StyleTable>`, `<ThemeTable>`,
190
+ // …) picks up the same color tokens. The provider is stable for
191
+ // the lifetime of the REPL — operator `/theme <name>` writes to
192
+ // disk + appends a system line, and the next `pugi` launch re-
193
+ // mounts with the new slug. Re-mounting mid-session would race
194
+ // against Ink's raw-mode handler so we deliberately keep the
195
+ // session-lifetime contract instead of polling the config file.
196
+ const resolvedTheme = resolveTheme({
197
+ workspaceRoot: process.cwd(),
198
+ env: process.env,
199
+ });
200
+ // CEO P0 #2 : collect welcome banner data BEFORE Ink
201
+ // mounts so the banner paints on the first frame instead of swapping
202
+ // in mid-render. The collector swallows every IO error so а missing
203
+ // CHANGELOG / unreadable credential / malformed settings never
204
+ // blocks the boot.
205
+ let welcomeData;
206
+ if (options.skipSplash !== true) {
207
+ try {
208
+ welcomeData = collectWelcomeData({
209
+ cliVersion: options.cliVersion,
210
+ cwd: process.cwd(),
211
+ });
212
+ }
213
+ catch {
214
+ welcomeData = undefined;
215
+ }
216
+ }
217
+ const instance = render(React.createElement(ThemeProvider, { slug: resolvedTheme.slug }, React.createElement(Repl, {
127
218
  session,
128
219
  updateBanner: options.updateBanner ?? null,
129
220
  skipSplash: options.skipSplash === true,
130
221
  hideToolStream: options.hideToolStream === true,
131
222
  mascotPrePrinted,
132
- }));
223
+ welcomeData,
224
+ autoInitStatus: options.autoInitStatus ?? null,
225
+ })));
133
226
  // Make sure we leave the alt screen on abrupt exits too. Without
134
227
  // this the operator's shell stays "frozen" on the Pugi splash.
135
228
  process.once('exit', bootstrap.restore);
@@ -242,6 +335,55 @@ export function drainBufferedStdin(stdin = process.stdin) {
242
335
  return 0;
243
336
  }
244
337
  }
338
+ /**
339
+ * Project-root probe — beta.13 P2 fix.
340
+ *
341
+ * Beta.13 auto-init was unconditional and silently created `.pugi/` in
342
+ * every cwd the REPL was launched from, including `$HOME` and `/tmp`.
343
+ * Operators who ran `pugi` to ask a quick question outside of any
344
+ * project ended up with stray `.pugi/` directories polluting their
345
+ * filesystem. The gate looks for any of six project-root markers
346
+ * before scaffolding:
347
+ *
348
+ * - `package.json` — JS / TS workspaces
349
+ * - `.git` — any cloned repo regardless of language
350
+ * - `.pugi` — already-bound Pugi workspace (re-scaffold
351
+ * fills any missing artifacts the operator
352
+ * deleted, idempotent over existing files)
353
+ * - `Cargo.toml` — Rust crates
354
+ * - `pyproject.toml` — Python projects (PEP 518)
355
+ * - `go.mod` — Go modules
356
+ *
357
+ * The probe is six cheap `existsSync` calls; the cost is negligible
358
+ * compared with the alt-screen + Ink mount that follows. Exported so a
359
+ * future unit spec can lock the contract.
360
+ */
361
+ export function isProjectRoot(cwd) {
362
+ // ESM static imports — `require()` is not defined in a `"type": "module"`
363
+ // bundle and would throw `ReferenceError: require is not defined` the
364
+ // moment the REPL bootstrap calls this gate. Beta.16 P0 fix.
365
+ return (existsSync(resolve(cwd, 'package.json')) ||
366
+ existsSync(resolve(cwd, '.git')) ||
367
+ existsSync(resolve(cwd, '.pugi')) ||
368
+ existsSync(resolve(cwd, 'Cargo.toml')) ||
369
+ existsSync(resolve(cwd, 'pyproject.toml')) ||
370
+ existsSync(resolve(cwd, 'go.mod')));
371
+ }
372
+ /**
373
+ * Read the operator's cyber-zoo posture from `.pugi/settings.json`.
374
+ * Best-effort: when the file is missing / malformed, fall through to
375
+ * the historical 'on' default so the REPL never refuses to launch on
376
+ * a settings error. Beta.13 P1 fix.
377
+ */
378
+ function readCyberZooSetting(cwd) {
379
+ try {
380
+ const settings = loadSettings(cwd);
381
+ return settings.ui?.cyberZoo ?? 'on';
382
+ }
383
+ catch {
384
+ return 'on';
385
+ }
386
+ }
245
387
  /**
246
388
  * Open the local SessionStore for the REPL bootstrap. Returns
247
389
  * `{ store: null, openedSessionId: undefined }` on any error so the
@@ -279,11 +421,11 @@ async function openLocalStore(input) {
279
421
  }
280
422
  }
281
423
  /**
282
- * Bootstrap the α6.5 three-tier context primitives:
424
+ * Bootstrap the three-tier context primitives:
283
425
  *
284
- * - Tier 0: `RepoSkeleton` (~5KB ASCII tree + meta) for prompt injection.
285
- * - Tier 1: `WorkingSet` LRU bounded at 50 entries.
286
- * - Filewatch: chokidar started against cwd, ignore-filtered.
426
+ * - Tier 0: `RepoSkeleton` (~5KB ASCII tree + meta) for prompt injection.
427
+ * - Tier 1: `WorkingSet` LRU bounded at 50 entries.
428
+ * - Filewatch: chokidar started against cwd, ignore-filtered.
287
429
  *
288
430
  * The bootstrap is fail-safe: every primitive is wrapped so the REPL
289
431
  * still launches when (e.g.) chokidar refuses to start on a
@@ -331,15 +473,22 @@ async function bootstrapContext(input) {
331
473
  return { skeleton, workingSet, watcher };
332
474
  }
333
475
  /* ------------------------------------------------------------------ */
334
- /* Production transport */
476
+ /* Production transport */
335
477
  /* ------------------------------------------------------------------ */
336
478
  export function createProductionTransport() {
337
479
  return {
338
- async createSession({ apiUrl, apiKey, workspace }) {
480
+ async createSession({ apiUrl, apiKey, workspace, cyberZoo }) {
339
481
  // Forward the workspace bundle in the POST body so admin-api can
340
- // surface `<workspace-context>` in Mira's prompt. Older admin-api
482
+ // surface `<workspace-context>` in Pugi's prompt. Older admin-api
341
483
  // builds ignore unknown fields, so this stays forward-compatible.
342
- // Wave 4 fix 2026-05-25.
484
+ // fix.
485
+ //
486
+ // Beta.13 P1 fix: also forward `cyberZoo` so admin-api
487
+ // can render Pugi's `<cyber-zoo>` marker matching the operator's
488
+ // `.pugi/settings.json::ui.cyberZoo` toggle instead of the
489
+ // historical 'on' default. Only included on the wire when set
490
+ // explicitly so a missing setting still survives older admin-api
491
+ // builds that do not declare the DTO field.
343
492
  const body = {};
344
493
  if (workspace?.workspaceCwd)
345
494
  body.workspaceCwd = workspace.workspaceCwd;
@@ -347,6 +496,8 @@ export function createProductionTransport() {
347
496
  body.workspaceSlug = workspace.workspaceSlug;
348
497
  if (workspace?.workspaceSummary)
349
498
  body.workspaceSummary = workspace.workspaceSummary;
499
+ if (cyberZoo === 'on' || cyberZoo === 'off')
500
+ body.cyberZoo = cyberZoo;
350
501
  const response = await fetch(joinUrl(apiUrl, '/api/pugi/sessions'), {
351
502
  method: 'POST',
352
503
  headers: jsonHeaders(apiKey),
@@ -392,7 +543,7 @@ export function createProductionTransport() {
392
543
  if (lastEventId) {
393
544
  headers['Last-Event-ID'] = lastEventId;
394
545
  }
395
- // beta.9 CEO dogfood 2026-05-26: hard timeout on the SSE
546
+ // beta.9 CEO dogfood: hard timeout on the SSE
396
547
  // handshake so a CDN/proxy that buffers the response (or an
397
548
  // admin-api that accepted the route but never flushed headers)
398
549
  // cannot freeze the REPL in `connecting` forever. The 5s budget
@@ -468,7 +619,7 @@ export function createProductionTransport() {
468
619
  };
469
620
  }
470
621
  /* ------------------------------------------------------------------ */
471
- /* SSE parser */
622
+ /* SSE parser */
472
623
  /* ------------------------------------------------------------------ */
473
624
  /**
474
625
  * Minimal SSE parser. Reads a UTF-8 stream of `id:` / `event:` / `data:`
@@ -532,7 +683,7 @@ async function consumeSseStream(body, onEvent) {
532
683
  }
533
684
  }
534
685
  /* ------------------------------------------------------------------ */
535
- /* Small helpers */
686
+ /* Small helpers */
536
687
  /* ------------------------------------------------------------------ */
537
688
  function jsonHeaders(apiKey) {
538
689
  return {
@@ -1,9 +1,9 @@
1
1
  /**
2
- * ASCII pug mascot for the REPL boot splash (α6.14 wave 3).
2
+ * ASCII pug mascot for the REPL boot splash .
3
3
  *
4
4
  * Hand-crafted at 9 rows × 20 columns to read as a pug at a single
5
5
  * glance — references the cyber-zoo hero glyph in
6
- * `apps/clawhost-web/public/brand/hero-pug.png`: blocky pug face with
6
+ * `apps/console-web/public/brand/hero-pug.png`: blocky pug face with
7
7
  * angular ear flaps on either side of the head, forehead crease,
8
8
  * angular cyan eyes (`◉`), smushed snout, undershot jaw, and a small
9
9
  * cyan circuit chip (`▐■▌`) on the lower-right cheek.
@@ -15,30 +15,30 @@
15
15
  * columns cyan (#3DA9FC, brandbook §05).
16
16
  *
17
17
  * Convention:
18
- * - PUG_MASCOT[i] = one row of the silhouette
19
- * - PUG_MASCOT_CYAN_MASK[i] = parallel boolean array, true => that
20
- * column renders cyan instead of gray
18
+ * - PUG_MASCOT[i] = one row of the silhouette
19
+ * - PUG_MASCOT_CYAN_MASK[i] = parallel boolean array, true => that
20
+ * column renders cyan instead of gray
21
21
  *
22
22
  * Both arrays MUST stay the same length and each mask row MUST be the
23
23
  * same length as the corresponding art row. A unit test enforces this.
24
24
  */
25
25
  /* eslint-disable no-irregular-whitespace */
26
26
  export const PUG_MASCOT = [
27
- ' ▄▀▀▀▄▄▄▀▀▀▄ ',
28
- ' █▄▄ ▄▄█ ',
29
- ' ▀▄▄▄▄▄▀ ',
30
- ' ',
31
- ' ▀▄ ▀█▀ ▄▀ ',
32
- ' █▀▀▀▀▀█ ',
33
- ' █▒▒▒▒▒█ ▐■▌ ',
34
- ' ▀▄▄▄▀ ',
35
- ' ',
27
+ ' ▄▀▀▀▄▄▄▀▀▀▄ ',
28
+ ' █▄▄ ▄▄█ ',
29
+ ' ▀▄▄▄▄▄▀ ',
30
+ ' ',
31
+ ' ▀▄ ▀█▀ ▄▀ ',
32
+ ' █▀▀▀▀▀█ ',
33
+ ' █▒▒▒▒▒█ ▐■▌ ',
34
+ ' ▀▄▄▄▀ ',
35
+ ' ',
36
36
  ];
37
37
  /**
38
38
  * Cyan accents are derived from the source characters so the art file
39
39
  * stays the single source of truth. Two glyph classes get colored:
40
- * - `◉` -> the two cyan eyes on row 3
41
- * - `▐■▌` -> the cyan chip cluster on row 6 (right cheek)
40
+ * - `◉` -> the two cyan eyes on row 3
41
+ * - `▐■▌` -> the cyan chip cluster on row 6 (right cheek)
42
42
  *
43
43
  * Everything else renders gray. The derivation runs at module load,
44
44
  * which keeps the mask trivially auditable from the source array.
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Chafa-validated brand-pug ANSI loader (α6.14.4 wave 6, mascot regen).
2
+ * Chafa-validated brand-pug ANSI loader .
3
3
  *
4
- * CEO dogfood 2026-05-25 (first pass, α6.14.2 wave 5): the hand-crafted
4
+ * CEO dogfood (first pass, wave 5): the hand-crafted
5
5
  * 9-row ASCII pug in `repl-splash-art.ts` reads as "точно не похожа" —
6
6
  * too abstract to carry the brand at boot. This module loads a pre-baked
7
7
  * truecolor ANSI render of the canonical hero-pug PNG (cyber-zoo pug
8
8
  * face with cyan eyes + circuit + chip) so the splash matches the brand
9
9
  * glyph the operator already sees on pugi.io.
10
10
  *
11
- * CEO dogfood 2026-05-25 (α6.14.4): the first chafa bake at 32x16 still
11
+ * CEO dogfood : the first chafa bake at 32x16 still
12
12
  * read as "monitor on stand", not pug — too few rows to resolve the
13
13
  * snout / eyes / wrinkles. The vertical resolution was the bottleneck:
14
14
  * 16 char rows ≈ 16 pixel rows with the block symbol set. The fresh
@@ -21,9 +21,9 @@
21
21
  * gates at 100KB so we stay well under cap.
22
22
  *
23
23
  * Generation (operator-side, one-shot):
24
- * chafa --size 80x40 --symbols=vhalf --colors=full \
25
- * apps/clawhost-web/public/brand/hero-pug.png \
26
- * > apps/pugi-cli/assets/pugi-mascot.ansi
24
+ * chafa --size 80x40 --symbols=vhalf --colors=full \
25
+ * apps/console-web/public/brand/hero-pug.png \
26
+ * > apps/pugi-cli/assets/pugi-mascot.ansi
27
27
  *
28
28
  * The output is committed verbatim to the repo and shipped inside the
29
29
  * `@pugi/cli` npm tarball under `assets/pugi-mascot.ansi` (the
@@ -33,7 +33,7 @@
33
33
  * corruption, dev cwd drift), the splash falls back to the hand-crafted
34
34
  * `PUG_MASCOT` art so the boot never crashes.
35
35
  *
36
- * The pre-Ink write convention mirrors the Claude Code Chrome plugin
36
+ * The pre-Ink write convention mirrors the the upstream tool Chrome plugin
37
37
  * splash pattern: raw bytes go to `process.stdout` BEFORE the Ink
38
38
  * render mount, so the terminal interprets the truecolor escapes
39
39
  * directly instead of Ink trying to layout-engine over them.
@@ -48,9 +48,21 @@ import { fileURLToPath } from 'node:url';
48
48
  * — two directory hops up from this file. In a local `pnpm dev`
49
49
  * checkout the structure is the same (`src/tui/` ⇒ `../../assets/`)
50
50
  * because tsx re-resolves the same relative tree.
51
+ *
52
+ * CEO P0 #2 — banner mascot bake. The prozr2 portrait is
53
+ * the canonical brand-pug glyph from `apps/console-web/public/brand/
54
+ * Pugi-prozr2.png`, baked к а 16x8 vhalf truecolor render. We prefer
55
+ * the prozr2 bake when it ships alongside the CLI (small — ~900 bytes,
56
+ * shaped for the compact welcome-banner left column) and fall back к
57
+ * the legacy 40KB `pugi-mascot.ansi` (hero-pug 80x40) when prozr2 is
58
+ * missing — preserves the splash-mascot install surface for any
59
+ * tarball that predates the bake.
51
60
  */
52
61
  export function pugMascotAssetPath() {
53
62
  const here = dirname(fileURLToPath(import.meta.url));
63
+ const prozr2 = resolvePath(here, '..', '..', 'assets', 'pugi-prozr2-mascot.ansi');
64
+ if (existsSync(prozr2))
65
+ return prozr2;
54
66
  return resolvePath(here, '..', '..', 'assets', 'pugi-mascot.ansi');
55
67
  }
56
68
  /**
@@ -83,27 +95,39 @@ export function loadPugMascotAnsi() {
83
95
  if (!raw || raw.length === 0)
84
96
  return null;
85
97
  // 1. Drop OSC sequences. Two terminator forms:
86
- // ESC ] ... BEL (0x1b 0x5d ... 0x07)
87
- // ESC ] ... ESC \ (0x1b 0x5d ... 0x1b 0x5c, the ST form)
88
- // A truecolor splash never needs OSC (those are for window title,
89
- // icon, clipboard, hyperlinks, color-palette change). Drop them
90
- // so a corrupted asset cannot rename the operator's terminal tab
91
- // or smuggle a hyperlink into the splash region.
92
- // 2. Drop CSI ? <mode> [hl] for mouse-tracking and screen-buffer
93
- // switch modes (1000, 1001, 1002, 1003, 1004, 1005, 1006, 1015,
94
- // 1049, 47, 1047, 1048). These would either start swallowing
95
- // mouse input or flip the terminal into the alternate screen.
98
+ // ESC ] ... BEL (0x1b 0x5d ... 0x07)
99
+ // ESC ] ... ESC \ (0x1b 0x5d ... 0x1b 0x5c, the ST form)
100
+ // A truecolor splash never needs OSC (those are for window title,
101
+ // icon, clipboard, hyperlinks, color-palette change). Drop them
102
+ // so a corrupted asset cannot rename the operator's terminal tab
103
+ // or smuggle a hyperlink into the splash region.
104
+ // 2. Drop ALL CSI ? <numbers and semicolons> [lh] (DEC private-mode
105
+ // set / reset). The legitimate chafa output for a splash is
106
+ // truecolor SGR (`CSI 38;2;R;G;B m`) plus cursor-positioning
107
+ // no private-mode toggle ever appears там legitimately. A
108
+ // permissive deny-all pattern covers every disruptive private
109
+ // mode in one regex:
110
+ // - cursor visibility (25)
111
+ // - alt-screen buffer (47, 1047, 1048, 1049)
112
+ // - mouse tracking (1000, 1001, 1002, 1003, 1004, 1005, 1006, 1015)
113
+ // - bracketed paste (2004)
114
+ // - focus reporting (1004)
115
+ // - multi-mode forms (e.g. `CSI ? 47 ; 1049 h` — legal per
116
+ // xterm ctlseqs but missed by the previous single-mode regex)
117
+ // - any future private mode a corrupt asset might emit
118
+ // Allowlisting the modes the splash needs is impossible because
119
+ // the splash needs ZERO of them — chafa renders glyph-by-glyph,
120
+ // not via private-mode toggles. A pure deny-all is strictly
121
+ // safer than enumerating known-bad modes one-by-one.
96
122
  // 3. Drop CSI 6 n (cursor-position report). Would inject a fake
97
- // CPR into the operator's stdin stream.
123
+ // CPR into the operator's stdin stream.
98
124
  // 4. Drop CSI [23]J / CSI [23]K (full screen / line clear). A
99
- // chafa render uses cursor-positioning per row, not bulk
100
- // erases; bulk clears would wipe whatever the operator already
101
- // had on screen above the splash.
102
- // The cursor-hide/show wrappers (CSI ? 25 [lh]) are handled by
103
- // the same CSI-?-mode pattern as the mouse / alt-screen modes.
125
+ // chafa render uses cursor-positioning per row, not bulk
126
+ // erases; bulk clears would wipe whatever the operator already
127
+ // had on screen above the splash.
104
128
  const stripped = raw
105
129
  .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
106
- .replace(/\x1b\[\?(?:25|47|1000|1001|1002|1003|1004|1005|1006|1015|1047|1048|1049)[lh]/g, '')
130
+ .replace(/\x1b\[\?[0-9;]+[lh]/g, '')
107
131
  .replace(/\x1b\[6n/g, '')
108
132
  .replace(/\x1b\[[23]?[JK]/g, '');
109
133
  if (stripped.trim().length === 0)