@pugi/cli 0.1.0-beta.1 → 0.1.0-beta.100

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 (448) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +53 -11
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/assets/pugi-mascot.ansi +15 -40
  6. package/assets/pugi-prozr2-mascot.ansi +9 -0
  7. package/bin/run.js +33 -1
  8. package/dist/commands/deploy.js +40 -40
  9. package/dist/commands/flatten.js +191 -0
  10. package/dist/commands/jobs-watch.js +201 -0
  11. package/dist/commands/jobs.js +42 -27
  12. package/dist/commands/retro.js +210 -0
  13. package/dist/commands/smoke.js +133 -0
  14. package/dist/core/agent-progress/cleanup.js +134 -0
  15. package/dist/core/agent-progress/schema.js +144 -0
  16. package/dist/core/agent-progress/writer.js +101 -0
  17. package/dist/core/agents/adaptive-router.js +330 -0
  18. package/dist/core/agents/query-decomposer.js +297 -0
  19. package/dist/core/agents/registry.js +3 -3
  20. package/dist/core/approvals/shortcut-resolver.js +98 -0
  21. package/dist/core/artifact-chain/dispatcher.js +148 -0
  22. package/dist/core/artifact-chain/exporter.js +164 -0
  23. package/dist/core/artifact-chain/state.js +243 -0
  24. package/dist/core/artifact-chain/steps.js +169 -0
  25. package/dist/core/ask-user/question.js +92 -0
  26. package/dist/core/audit/audit-trail.js +275 -0
  27. package/dist/core/auth/ensure-authenticated.js +129 -0
  28. package/dist/core/auth/env-provider.js +238 -0
  29. package/dist/core/auto-open-browser.js +4 -4
  30. package/dist/core/auto-update/channels.js +122 -0
  31. package/dist/core/auto-update/checker.js +241 -0
  32. package/dist/core/auto-update/state.js +235 -0
  33. package/dist/core/bare-mode/index.js +107 -0
  34. package/dist/core/bash/redirect.js +281 -0
  35. package/dist/core/bash-classifier.js +436 -40
  36. package/dist/core/checkpoint/resumer.js +149 -0
  37. package/dist/core/checkpoint/rewinder.js +291 -0
  38. package/dist/core/checkpoints/shadow-git.js +670 -0
  39. package/dist/core/citations/parser.js +109 -0
  40. package/dist/core/classifier/yolo-classifier.js +88 -0
  41. package/dist/core/codegraph/db.js +506 -0
  42. package/dist/core/codegraph/decision-store.js +248 -0
  43. package/dist/core/codegraph/detect-repo.js +459 -0
  44. package/dist/core/codegraph/install.js +134 -0
  45. package/dist/core/codegraph/offer-hook.js +220 -0
  46. package/dist/core/codegraph/parser.js +71 -0
  47. package/dist/core/codegraph/types.js +34 -0
  48. package/dist/core/compact/auto-trigger.js +96 -0
  49. package/dist/core/compact/buffer-rewriter.js +115 -0
  50. package/dist/core/compact/summarizer.js +208 -0
  51. package/dist/core/compact/token-counter.js +108 -0
  52. package/dist/core/consensus/anvil-fanout.js +25 -25
  53. package/dist/core/consensus/diff-capture.js +121 -12
  54. package/dist/core/consensus/rubric.js +21 -21
  55. package/dist/core/context/builder.js +6 -6
  56. package/dist/core/context/compaction-events.js +8 -8
  57. package/dist/core/context/compaction.js +31 -31
  58. package/dist/core/context/index.js +15 -8
  59. package/dist/core/context/invariants.js +51 -51
  60. package/dist/core/context/markdown-loader.js +28 -10
  61. package/dist/core/context/markdown-traverse.js +255 -0
  62. package/dist/core/context/pugiignore.js +41 -41
  63. package/dist/core/context/repo-skeleton.js +37 -37
  64. package/dist/core/context/tool-eviction.js +55 -0
  65. package/dist/core/context/watcher.js +32 -32
  66. package/dist/core/context/working-set.js +23 -23
  67. package/dist/core/coordinator/agent-tools.js +77 -0
  68. package/dist/core/coordinator/agent-toolset.js +65 -0
  69. package/dist/core/coordinator/fsm.js +73 -0
  70. package/dist/core/coordinator/mode-fsm.js +70 -0
  71. package/dist/core/cost/rate-card.js +129 -0
  72. package/dist/core/cost/tracker.js +221 -0
  73. package/dist/core/credentials.js +13 -13
  74. package/dist/core/cron/scheduler.js +138 -0
  75. package/dist/core/denial-tracking/index.js +8 -0
  76. package/dist/core/denial-tracking/state.js +264 -0
  77. package/dist/core/diagnostics/probe-runner.js +93 -0
  78. package/dist/core/diagnostics/probes/api.js +46 -0
  79. package/dist/core/diagnostics/probes/auth.js +93 -0
  80. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  81. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  82. package/dist/core/diagnostics/probes/config.js +72 -0
  83. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  84. package/dist/core/diagnostics/probes/disk.js +81 -0
  85. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  86. package/dist/core/diagnostics/probes/git.js +65 -0
  87. package/dist/core/diagnostics/probes/hooks.js +118 -0
  88. package/dist/core/diagnostics/probes/mcp.js +75 -0
  89. package/dist/core/diagnostics/probes/node.js +59 -0
  90. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  91. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  92. package/dist/core/diagnostics/probes/sandbox.js +72 -0
  93. package/dist/core/diagnostics/probes/session.js +74 -0
  94. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  95. package/dist/core/diagnostics/probes/workspace.js +63 -0
  96. package/dist/core/diagnostics/types.js +70 -0
  97. package/dist/core/dispatch/cache-cleanup.js +197 -0
  98. package/dist/core/dispatch/cache-handoff.js +295 -0
  99. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  100. package/dist/core/edits/dispatch.js +333 -7
  101. package/dist/core/edits/format-detector.js +260 -0
  102. package/dist/core/edits/format-matrix.js +26 -0
  103. package/dist/core/edits/fuzzy-ladder.js +650 -0
  104. package/dist/core/edits/index.js +5 -1
  105. package/dist/core/edits/journal.js +199 -0
  106. package/dist/core/edits/layer-a-apply.js +15 -15
  107. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  108. package/dist/core/edits/layer-b-apply.js +9 -9
  109. package/dist/core/edits/layer-c-apply.js +6 -6
  110. package/dist/core/edits/layer-d-ast.js +557 -14
  111. package/dist/core/edits/marker-parser.js +12 -12
  112. package/dist/core/edits/security-gate.js +27 -27
  113. package/dist/core/edits/verify-hook.js +273 -0
  114. package/dist/core/edits/worktree.js +322 -0
  115. package/dist/core/engine/anvil-client.js +214 -26
  116. package/dist/core/engine/auto-compact.js +247 -0
  117. package/dist/core/engine/budgets.js +220 -0
  118. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  119. package/dist/core/engine/context-prefix.js +155 -0
  120. package/dist/core/engine/index.js +1 -1
  121. package/dist/core/engine/intensity.js +163 -0
  122. package/dist/core/engine/intent.js +260 -0
  123. package/dist/core/engine/native-pugi.js +1559 -227
  124. package/dist/core/engine/prompts.js +192 -16
  125. package/dist/core/engine/strip-internal-fields.js +124 -0
  126. package/dist/core/engine/tool-bridge.js +1887 -59
  127. package/dist/core/engine/verification-patterns.js +195 -0
  128. package/dist/core/evaluation/golden-dataset.js +293 -0
  129. package/dist/core/feedback/queue.js +177 -0
  130. package/dist/core/feedback/submitter.js +145 -0
  131. package/dist/core/file-cache.js +113 -1
  132. package/dist/core/flatten/flatten-repo.js +439 -0
  133. package/dist/core/format/osc8-link.js +28 -0
  134. package/dist/core/hook-chains.js +392 -0
  135. package/dist/core/hooks/citation-verify-hook.js +138 -0
  136. package/dist/core/hooks/citation-verify.js +112 -0
  137. package/dist/core/hooks/events.js +46 -0
  138. package/dist/core/hooks/index.js +15 -0
  139. package/dist/core/hooks/registry.js +216 -0
  140. package/dist/core/hooks/runner.js +236 -0
  141. package/dist/core/hooks/v2/event-emitter.js +115 -0
  142. package/dist/core/hooks/v2/executor.js +282 -0
  143. package/dist/core/hooks/v2/index.js +25 -0
  144. package/dist/core/hooks/v2/lifecycle.js +104 -0
  145. package/dist/core/hooks/v2/loader.js +216 -0
  146. package/dist/core/hooks/v2/matcher.js +125 -0
  147. package/dist/core/hooks/v2/trust.js +143 -0
  148. package/dist/core/hooks/v2/types.js +86 -0
  149. package/dist/core/hooks/worktree-events.js +158 -0
  150. package/dist/core/image/renderer.js +71 -0
  151. package/dist/core/init/detector.js +582 -0
  152. package/dist/core/init/template-renderer.js +242 -0
  153. package/dist/core/jobs/registry.js +18 -18
  154. package/dist/core/ledger/results-tsv.js +142 -0
  155. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  156. package/dist/core/lsp/cache.js +105 -0
  157. package/dist/core/lsp/client.js +1229 -0
  158. package/dist/core/lsp/language-detect.js +66 -0
  159. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  160. package/dist/core/lsp/server-detect.js +173 -0
  161. package/dist/core/lsp/symbol-cache.js +162 -0
  162. package/dist/core/lsp/symbol-tools.js +664 -0
  163. package/dist/core/mcp/client.js +97 -28
  164. package/dist/core/mcp/http-server.js +553 -0
  165. package/dist/core/mcp/orchestrator-config.js +192 -0
  166. package/dist/core/mcp/orchestrator-tools.js +806 -0
  167. package/dist/core/mcp/permission.js +190 -0
  168. package/dist/core/mcp/registry.js +39 -17
  169. package/dist/core/mcp/server-tools.js +219 -0
  170. package/dist/core/mcp/server.js +397 -0
  171. package/dist/core/mcp/trust.js +10 -10
  172. package/dist/core/memory/dual-write.js +416 -0
  173. package/dist/core/memory/passive-extract.js +130 -0
  174. package/dist/core/memory/phase1-kinds.js +20 -0
  175. package/dist/core/memory/secret-scanner.js +304 -0
  176. package/dist/core/memory-sync/queue.js +170 -0
  177. package/dist/core/metrics/extract.js +113 -0
  178. package/dist/core/modes/roo-modes.js +68 -0
  179. package/dist/core/notes/notes-paths.js +113 -0
  180. package/dist/core/notes/notes-recorder.js +140 -0
  181. package/dist/core/notes/notes-writer.js +53 -0
  182. package/dist/core/notes/renderers.js +0 -0
  183. package/dist/core/notes/slug.js +105 -0
  184. package/dist/core/onboarding/ensure-initialized.js +133 -0
  185. package/dist/core/onboarding/marker.js +111 -0
  186. package/dist/core/onboarding/telemetry-state.js +108 -0
  187. package/dist/core/output-style/presets.js +176 -0
  188. package/dist/core/output-style/state.js +185 -0
  189. package/dist/core/path-security.js +287 -5
  190. package/dist/core/permission.js +82 -22
  191. package/dist/core/permissions/auto-classifier.js +124 -0
  192. package/dist/core/permissions/bash-parser.js +371 -0
  193. package/dist/core/permissions/circuit-breaker.js +83 -0
  194. package/dist/core/permissions/constrained-edit.js +91 -0
  195. package/dist/core/permissions/gate.js +278 -0
  196. package/dist/core/permissions/index.js +20 -0
  197. package/dist/core/permissions/mode.js +174 -0
  198. package/dist/core/permissions/network-egress.js +137 -0
  199. package/dist/core/permissions/state.js +241 -0
  200. package/dist/core/permissions/tool-class.js +107 -0
  201. package/dist/core/plan-mode/ui-state.js +51 -0
  202. package/dist/core/plans/plan-artifact.js +721 -0
  203. package/dist/core/policy-limits/etag-store.js +122 -0
  204. package/dist/core/prd-check/parser.js +215 -0
  205. package/dist/core/prd-check/reporter.js +127 -0
  206. package/dist/core/prd-check/session-review.js +557 -0
  207. package/dist/core/prd-check/verifiers.js +223 -0
  208. package/dist/core/prompt-cache/client-cache.js +99 -0
  209. package/dist/core/prompts/assembly.js +29 -0
  210. package/dist/core/prompts/registry.js +364 -0
  211. package/dist/core/pugi-gitignore.js +52 -0
  212. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  213. package/dist/core/pugi-md/context-injector.js +76 -0
  214. package/dist/core/pugi-md/walk-up.js +207 -0
  215. package/dist/core/python/uv-installer.js +270 -0
  216. package/dist/core/python/uv-resolver.js +83 -0
  217. package/dist/core/rate-limit/narrator.js +146 -0
  218. package/dist/core/recipes/cli-types.js +20 -0
  219. package/dist/core/recipes/loader.js +103 -0
  220. package/dist/core/recipes/runner.js +345 -0
  221. package/dist/core/recipes/schema.js +587 -0
  222. package/dist/core/release-notes/parser.js +241 -0
  223. package/dist/core/release-notes/state.js +116 -0
  224. package/dist/core/repl/ask.js +37 -37
  225. package/dist/core/repl/cancellation.js +26 -26
  226. package/dist/core/repl/cap-warning.js +4 -4
  227. package/dist/core/repl/clipboard-read.js +11 -11
  228. package/dist/core/repl/dispatch-fsm.js +12 -12
  229. package/dist/core/repl/engine-bridge.js +303 -0
  230. package/dist/core/repl/history-search.js +15 -15
  231. package/dist/core/repl/history.js +28 -18
  232. package/dist/core/repl/kill-ring.js +5 -5
  233. package/dist/core/repl/model-pricing.js +135 -0
  234. package/dist/core/repl/privacy-banner.js +22 -22
  235. package/dist/core/repl/session.js +2714 -228
  236. package/dist/core/repl/slash-commands.js +572 -40
  237. package/dist/core/repl/store/index.js +1 -1
  238. package/dist/core/repl/store/jsonl-log.js +22 -22
  239. package/dist/core/repl/store/lockfile.js +10 -10
  240. package/dist/core/repl/store/session-store.js +136 -107
  241. package/dist/core/repl/store/types.js +15 -15
  242. package/dist/core/repl/store/uuid-v7.js +12 -12
  243. package/dist/core/repl/tool-route.js +382 -0
  244. package/dist/core/repl/workspace-context.js +43 -21
  245. package/dist/core/repo-map/build.js +125 -0
  246. package/dist/core/repo-map/cache.js +185 -0
  247. package/dist/core/repo-map/extractor.js +254 -0
  248. package/dist/core/repo-map/formatter.js +145 -0
  249. package/dist/core/repo-map/page-rank.js +105 -0
  250. package/dist/core/repo-map/scanner.js +211 -0
  251. package/dist/core/retro/git-collector.js +251 -0
  252. package/dist/core/retro/health-card.js +25 -0
  253. package/dist/core/retro/metrics.js +342 -0
  254. package/dist/core/retro/narrative.js +249 -0
  255. package/dist/core/retro/plane-collector.js +274 -0
  256. package/dist/core/retro/pr-issue-link.js +65 -0
  257. package/dist/core/retro/types.js +16 -0
  258. package/dist/core/retry-budget/budget.js +284 -0
  259. package/dist/core/retry-budget/index.js +5 -0
  260. package/dist/core/retry-budget/retry-cap.js +74 -0
  261. package/dist/core/routing/lead-worker.js +43 -0
  262. package/dist/core/routing/pre-flight-estimator.js +108 -0
  263. package/dist/core/runs/run-tree.js +103 -0
  264. package/dist/core/sandboxing/adapter.js +29 -0
  265. package/dist/core/sandboxing/index.js +49 -0
  266. package/dist/core/sandboxing/none.js +19 -0
  267. package/dist/core/sandboxing/seatbelt.js +183 -0
  268. package/dist/core/security/injection-scanner.js +367 -0
  269. package/dist/core/security/output-filter.js +418 -0
  270. package/dist/core/session/env-file.js +105 -0
  271. package/dist/core/session/section-budgets.js +140 -0
  272. package/dist/core/session.js +119 -0
  273. package/dist/core/settings.js +378 -5
  274. package/dist/core/share/formatter.js +271 -0
  275. package/dist/core/share/redactor.js +221 -0
  276. package/dist/core/share/uploader.js +267 -0
  277. package/dist/core/skills/defaults.js +457 -0
  278. package/dist/core/skills/loader.js +22 -22
  279. package/dist/core/skills/sources.js +27 -27
  280. package/dist/core/smoke/headless-driver.js +174 -0
  281. package/dist/core/smoke/orchestrator.js +194 -0
  282. package/dist/core/smoke/runner.js +238 -0
  283. package/dist/core/smoke/scenario-parser.js +316 -0
  284. package/dist/core/statusline.js +99 -0
  285. package/dist/core/subagents/dispatcher-real.js +600 -0
  286. package/dist/core/subagents/dispatcher.js +146 -52
  287. package/dist/core/subagents/index.js +19 -6
  288. package/dist/core/subagents/isolation-matrix.js +213 -0
  289. package/dist/core/subagents/spawn.js +19 -4
  290. package/dist/core/telemetry/emitter.js +229 -0
  291. package/dist/core/telemetry/queue.js +251 -0
  292. package/dist/core/theme/context.js +91 -0
  293. package/dist/core/theme/presets.js +228 -0
  294. package/dist/core/theme/state.js +181 -0
  295. package/dist/core/todos/invariant.js +10 -0
  296. package/dist/core/todos/state.js +177 -0
  297. package/dist/core/tool-schema/compressor.js +89 -0
  298. package/dist/core/transport/version-interceptor.js +166 -0
  299. package/dist/core/trust.js +2 -2
  300. package/dist/core/tui/thinking-block.js +64 -0
  301. package/dist/core/vim/keymap.js +288 -0
  302. package/dist/core/vim/state.js +92 -0
  303. package/dist/core/watch-markers/marker-watcher.js +133 -0
  304. package/dist/core/worktree/include-parser.js +249 -0
  305. package/dist/core/worktree-manager/cleanup.js +123 -0
  306. package/dist/core/worktree-manager/manager.js +303 -0
  307. package/dist/index.js +36 -0
  308. package/dist/runtime/bootstrap.js +190 -0
  309. package/dist/runtime/cli.js +4536 -477
  310. package/dist/runtime/commands/agents.js +31 -31
  311. package/dist/runtime/commands/budget.js +5 -5
  312. package/dist/runtime/commands/cancel.js +231 -0
  313. package/dist/runtime/commands/chain.js +489 -0
  314. package/dist/runtime/commands/codegraph-status.js +227 -0
  315. package/dist/runtime/commands/compact.js +297 -0
  316. package/dist/runtime/commands/config.js +74 -40
  317. package/dist/runtime/commands/cost.js +199 -0
  318. package/dist/runtime/commands/delegate.js +312 -0
  319. package/dist/runtime/commands/dispatch.js +126 -0
  320. package/dist/runtime/commands/doctor.js +579 -0
  321. package/dist/runtime/commands/feedback.js +184 -0
  322. package/dist/runtime/commands/hooks.js +187 -0
  323. package/dist/runtime/commands/index-cmd.js +353 -0
  324. package/dist/runtime/commands/init.js +254 -0
  325. package/dist/runtime/commands/lsp.js +368 -0
  326. package/dist/runtime/commands/mcp.js +935 -0
  327. package/dist/runtime/commands/memory.js +582 -0
  328. package/dist/runtime/commands/model.js +237 -0
  329. package/dist/runtime/commands/onboarding.js +275 -0
  330. package/dist/runtime/commands/patch.js +128 -0
  331. package/dist/runtime/commands/permissions.js +112 -0
  332. package/dist/runtime/commands/plan.js +143 -0
  333. package/dist/runtime/commands/prd-check.js +285 -0
  334. package/dist/runtime/commands/privacy.js +17 -17
  335. package/dist/runtime/commands/recipe.js +325 -0
  336. package/dist/runtime/commands/redo-blob-store.js +92 -0
  337. package/dist/runtime/commands/redo.js +361 -0
  338. package/dist/runtime/commands/release-notes.js +229 -0
  339. package/dist/runtime/commands/repo-map.js +95 -0
  340. package/dist/runtime/commands/report.js +299 -0
  341. package/dist/runtime/commands/resume.js +118 -0
  342. package/dist/runtime/commands/review-consensus.js +68 -53
  343. package/dist/runtime/commands/rewind.js +333 -0
  344. package/dist/runtime/commands/roster.js +117 -0
  345. package/dist/runtime/commands/servers.js +236 -0
  346. package/dist/runtime/commands/sessions.js +163 -0
  347. package/dist/runtime/commands/share.js +316 -0
  348. package/dist/runtime/commands/skills.js +31 -31
  349. package/dist/runtime/commands/status.js +186 -0
  350. package/dist/runtime/commands/stickers.js +82 -0
  351. package/dist/runtime/commands/style.js +194 -0
  352. package/dist/runtime/commands/theme.js +196 -0
  353. package/dist/runtime/commands/undo.js +54 -22
  354. package/dist/runtime/commands/update.js +289 -0
  355. package/dist/runtime/commands/vim.js +140 -0
  356. package/dist/runtime/commands/worktree.js +177 -0
  357. package/dist/runtime/commands/worktrees.js +155 -0
  358. package/dist/runtime/deprecation-warning.js +69 -0
  359. package/dist/runtime/engine-exit-code.js +50 -0
  360. package/dist/runtime/headless-repl.js +195 -0
  361. package/dist/runtime/headless.js +548 -0
  362. package/dist/runtime/load-hooks-or-exit.js +71 -0
  363. package/dist/runtime/plan-decompose.js +531 -0
  364. package/dist/runtime/sigint-guard.js +272 -0
  365. package/dist/runtime/stream-renderer.js +195 -0
  366. package/dist/runtime/update-check.js +28 -28
  367. package/dist/runtime/version.js +65 -0
  368. package/dist/runtime/worktree-bootstrap.js +579 -0
  369. package/dist/skills/bundled/batch.js +617 -0
  370. package/dist/skills/bundled/index.js +45 -0
  371. package/dist/skills/bundled/loop.js +358 -0
  372. package/dist/skills/bundled/remember.js +383 -0
  373. package/dist/skills/bundled/simplify.js +289 -0
  374. package/dist/skills/bundled/skillify.js +373 -0
  375. package/dist/skills/bundled/stuck.js +558 -0
  376. package/dist/skills/bundled/verify.js +439 -0
  377. package/dist/testing/vcr.js +486 -0
  378. package/dist/tools/agent-tool.js +229 -0
  379. package/dist/tools/apply-patch.js +556 -0
  380. package/dist/tools/ask-user-question.js +337 -0
  381. package/dist/tools/ask-user.js +115 -0
  382. package/dist/tools/bash.js +624 -46
  383. package/dist/tools/brief.js +224 -0
  384. package/dist/tools/cron.js +433 -0
  385. package/dist/tools/enter-worktree.js +250 -0
  386. package/dist/tools/exit-worktree.js +147 -0
  387. package/dist/tools/file-tools.js +161 -44
  388. package/dist/tools/http-request.js +336 -0
  389. package/dist/tools/lsp-tools.js +565 -0
  390. package/dist/tools/mcp-tool.js +260 -0
  391. package/dist/tools/multi-edit.js +361 -0
  392. package/dist/tools/powershell.js +268 -0
  393. package/dist/tools/registry.js +142 -1
  394. package/dist/tools/server-tools.js +892 -0
  395. package/dist/tools/skill-tool.js +96 -0
  396. package/dist/tools/sleep.js +99 -0
  397. package/dist/tools/synthetic-output.js +133 -0
  398. package/dist/tools/tasks.js +208 -0
  399. package/dist/tools/todo-write.js +184 -0
  400. package/dist/tools/verify-plan-execution.js +295 -0
  401. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  402. package/dist/tools/web-fetch.js +195 -10
  403. package/dist/tools/web-search.js +458 -0
  404. package/dist/tui/agent-progress-card.js +111 -0
  405. package/dist/tui/agent-tree.js +22 -1
  406. package/dist/tui/ask-modal.js +14 -14
  407. package/dist/tui/ask-user-question-chips.js +315 -0
  408. package/dist/tui/ask-user-question-prompt.js +203 -0
  409. package/dist/tui/compact-banner.js +81 -0
  410. package/dist/tui/conversation-pane.js +85 -11
  411. package/dist/tui/cost-table.js +111 -0
  412. package/dist/tui/device-flow.js +2 -2
  413. package/dist/tui/doctor-table.js +46 -0
  414. package/dist/tui/feedback-prompt.js +156 -0
  415. package/dist/tui/input-box.js +247 -32
  416. package/dist/tui/login-picker.js +3 -3
  417. package/dist/tui/markdown-render.js +6 -6
  418. package/dist/tui/multi-file-diff-approval.js +375 -0
  419. package/dist/tui/onboarding-wizard.js +240 -0
  420. package/dist/tui/permissions-picker.js +86 -0
  421. package/dist/tui/render.js +36 -1
  422. package/dist/tui/repl-render.js +405 -32
  423. package/dist/tui/repl-splash-art.js +16 -16
  424. package/dist/tui/repl-splash-mascot.js +48 -24
  425. package/dist/tui/repl-splash.js +22 -22
  426. package/dist/tui/repl.js +136 -43
  427. package/dist/tui/slash-palette.js +6 -6
  428. package/dist/tui/splash.js +2 -2
  429. package/dist/tui/status-bar.js +109 -31
  430. package/dist/tui/status-table.js +7 -0
  431. package/dist/tui/stickers-art.js +136 -0
  432. package/dist/tui/style-table.js +28 -0
  433. package/dist/tui/theme-table.js +29 -0
  434. package/dist/tui/thinking-spinner.js +123 -0
  435. package/dist/tui/tool-stream-pane.js +53 -4
  436. package/dist/tui/update-banner.js +27 -2
  437. package/dist/tui/vim-input.js +267 -0
  438. package/dist/tui/welcome-banner.js +107 -0
  439. package/dist/tui/welcome-data.js +293 -0
  440. package/dist/tui/workspace-context.js +2 -2
  441. package/docs/examples/codegraph.mcp.json +10 -0
  442. package/package.json +25 -7
  443. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  444. package/test/scenarios/compact-force.scenario.txt +12 -0
  445. package/test/scenarios/identity.scenario.txt +11 -0
  446. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  447. package/test/scenarios/walkback.scenario.txt +12 -0
  448. package/dist/core/engine/compaction-hook.js +0 -154
@@ -1,29 +1,38 @@
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';
30
+ import { createEngineBridge } from '../core/repl/engine-bridge.js';
31
+ import { profileFor, resolveIntensity } from '../core/engine/intensity.js';
25
32
  import { SqliteSessionStore } from '../core/repl/store/index.js';
26
33
  import { slugForCwd } from '../core/repl/history.js';
34
+ import { loadSettings } from '../core/settings.js';
35
+ import { buildRuntimeConfig } from '@pugi/sdk';
27
36
  import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../core/context/index.js';
28
37
  /**
29
38
  * Mount the REPL and resolve when the user exits via Ctrl+C × 2 or
@@ -31,13 +40,86 @@ import { WorkingSet, buildRepoSkeleton, loadPugiIgnore, PugiWatcher, } from '../
31
40
  * `pugi resume <sessionId>` once that command exists).
32
41
  */
33
42
  export async function renderRepl(options) {
43
+ // beta.9 CEO dogfood: claim stdin raw mode + alt-screen
44
+ // BEFORE any async bootstrap step so keystrokes typed during the
45
+ // [launch -> Ink mount] window cannot echo into the terminal in
46
+ // cooked mode. Previously openLocalStore (SQLite open) +
47
+ // bootstrapContext (chokidar start) could take hundreds of ms to
48
+ // multiple seconds on a fresh install / large repo; during that
49
+ // window stdin stayed in cooked mode and the terminal echoed
50
+ // every typed character literally onto the screen below the
51
+ // pre-printed mascot/header. The visible result was the operator's
52
+ // "ssssss" landing on the rendered status-bar bottom row (CEO
53
+ // screenshot: beta.8 REPL bug 2).
54
+ //
55
+ // The claim is idempotent with Ink's own raw-mode enable: Ink
56
+ // ref-counts setRawMode calls, and Node's stdin.setRawMode is
57
+ // safe to call twice with the same value. The pre-Ink claim acts
58
+ // as a "raw-mode floor" - whatever Ink does after mount layers on
59
+ // top, and our finally{} restore drops the floor only after Ink
60
+ // has cleanly torn down (or never mounted on a bootstrap crash).
61
+ const bootstrap = claimTerminalForRepl();
62
+ // beta.13 auto-init wire (CEO dogfood): scaffold the
63
+ // `.pugi/` workspace silently on REPL boot so launching `pugi` in a
64
+ // fresh cwd no longer demands an explicit `pugi init` round-trip.
65
+ // Idempotent — every helper inside scaffoldPugiWorkspace is a
66
+ // `*_IfMissing` write, so re-running over an existing workspace is
67
+ // a no-op. Fail-safe: any FS / perms error never blocks REPL launch.
68
+ // Operator escape hatch: PUGI_NO_AUTO_INIT=1.
69
+ //
70
+ // Beta.13 P2 fix: gate the scaffold on project-root markers
71
+ // so launching `pugi` from `$HOME` / `/tmp` / arbitrary dirs does NOT
72
+ // sprinkle `.pugi/` directories all over the filesystem. The gate
73
+ // mirrors `isBoundWorkspace` from workspace-context.ts but also
74
+ // accepts non-JS roots (Cargo / pyproject / go.mod) because the CLI
75
+ // is language-agnostic and an operator working in a Rust repo deserves
76
+ // the same auto-init UX as a Node operator. Already-bound `.pugi/`
77
+ // dirs also opt back in so the scaffold can fill any missing
78
+ // sub-artifacts the operator deleted.
79
+ // `--bare` (PUGI_BARE=1) ALSO suppresses the
80
+ // auto-init scaffold. Bare mode is the deterministic "fresh install
81
+ // anywhere" path — no `.pugi/` writes, no PUGI.md scaffold, no
82
+ // settings.json seed. The pre-existing PUGI_NO_AUTO_INIT escape
83
+ // hatch stays — bare mode just unions with it.
84
+ const { isBareMode } = await import('../core/bare-mode/index.js');
85
+ if (process.env.PUGI_NO_AUTO_INIT !== '1' &&
86
+ !isBareMode() &&
87
+ isProjectRoot(process.cwd())) {
88
+ try {
89
+ const { scaffoldPugiWorkspace } = await import('../runtime/cli.js');
90
+ await scaffoldPugiWorkspace({
91
+ cwd: process.cwd(),
92
+ noDefaults: true,
93
+ log: () => {
94
+ /* silent — never leak scaffold progress into the REPL alt-screen */
95
+ },
96
+ });
97
+ }
98
+ catch (err) {
99
+ // Fail-safe: read-only FS or perms error never blocks REPL launch.
100
+ // Beta.13 P2 fix: bare-catch swallowed the diagnostic;
101
+ // surface it on stderr under PUGI_DEBUG=1 so operator-triage on
102
+ // "why isn't .pugi/ being created?" has a starting point without
103
+ // having to re-instrument the bootstrap.
104
+ if (process.env.PUGI_DEBUG === '1') {
105
+ const msg = err instanceof Error ? err.message : String(err);
106
+ process.stderr.write(`[pugi-debug] auto-init failed: ${msg}\n`);
107
+ }
108
+ }
109
+ }
34
110
  const transport = createProductionTransport();
35
- // Auto-bind the workspace context from process.cwd() so Mira knows
111
+ // Auto-bind the workspace context from process.cwd() so Pugi knows
36
112
  // which repo the operator launched the CLI in. The resolver is
37
113
  // best-effort — any FS error falls back to a basename-only summary,
38
- // never blocks REPL launch. Wave 4 fix 2026-05-25.
114
+ // never blocks REPL launch. fix.
39
115
  const workspace = options.workspace ?? resolveWorkspaceContext(process.cwd());
40
- // α6.4: open the local SessionStore for `/resume` persistence. The
116
+ // Beta.13 P1 fix: read `ui.cyberZoo` from
117
+ // `.pugi/settings.json` so the operator's splash posture flows to
118
+ // admin-api on session open. Without this, the renderer's `cyberZoo`
119
+ // parameter (added beta.13) was always defaulted to 'on' regardless
120
+ // of the operator's actual setting.
121
+ const cyberZoo = readCyberZooSetting(process.cwd());
122
+ // : open the local SessionStore for `/resume` persistence. The
41
123
  // store lives under `~/.pugi/projects/<slug>/`; failure is fail-safe
42
124
  // — we log a one-line warning to stderr and continue with the REPL
43
125
  // in memory-only mode. Lock-busy errors get the friendliest message
@@ -49,7 +131,7 @@ export async function renderRepl(options) {
49
131
  workspaceRoot: process.cwd(),
50
132
  resumeLocalSessionId: options.resumeLocalSessionId,
51
133
  });
52
- // α6.5 three-tier context bootstrap. The skeleton + working set
134
+ // three-tier context bootstrap. The skeleton + working set
53
135
  // + watcher are local-first and best-effort: every step is wrapped
54
136
  // in try/catch so an unreadable workspace never blocks REPL launch.
55
137
  // Opt-out via PUGI_DISABLE_CONTEXT=1 for hermetic test runs.
@@ -57,13 +139,63 @@ export async function renderRepl(options) {
57
139
  cwd: process.cwd(),
58
140
  env: process.env,
59
141
  });
142
+ // PUGI-538c () -- wire the production engine bridge so
143
+ // `<pugi-tool-route>` envelopes emitted by Pugi's coordinator
144
+ // actually drive a local `NativePugiEngineAdapter` run instead of
145
+ // surfacing "Engine bridge not configured" on the system line. The
146
+ // factory closure resolves `cwd` per invocation so an operator who
147
+ // `cd`-ed mid-session writes files into the new directory (matches
148
+ // `pugi code` direct-path behaviour).
149
+ //
150
+ // The bridge runtime config reuses the REPL transport credentials so
151
+ // a single token authenticates both the coordinator brief (via
152
+ // admin-api /api/pugi/sessions) AND the bridged engine call (via
153
+ // /api/pugi/engine). No new credential surface, no double login.
154
+ //
155
+ // Fail-safe construction: `buildRuntimeConfig` validates apiUrl via
156
+ // `z.string().url()` and throws on a malformed value. We must not
157
+ // crash REPL launch on that path -- every other bootstrap step in
158
+ // this file (auto-init, openLocalStore, bootstrapContext, watcher)
159
+ // is fail-safe. On construction failure we surface a one-line stderr
160
+ // diagnostic under PUGI_DEBUG=1 and pass `engineBridge: undefined`
161
+ // so the REPL launches; the operator sees the legacy "Engine bridge
162
+ // not configured" system line on the first routed brief, which is
163
+ // the same UX as a pre-PUGI-538c CLI build.
164
+ let engineBridge;
165
+ try {
166
+ // PR A (2026-06-05): resolve intensity profile once at bridge
167
+ // construction time. The bridge stays pure and re-uses the resolved
168
+ // profile across every routed brief. `resolveIntensity` reads env
169
+ // (`PUGI_INTENSITY`) и settings.json; the REPL launch lifecycle is
170
+ // the natural binding point. A future `/intensity` REPL command
171
+ // will rebuild the bridge to swap the profile mid-session.
172
+ const intensityLevel = resolveIntensity({});
173
+ const intensityProfile = profileFor(intensityLevel);
174
+ engineBridge = createEngineBridge({
175
+ config: buildRuntimeConfig({
176
+ apiUrl: options.apiUrl,
177
+ apiKey: options.apiKey,
178
+ }),
179
+ cwd: () => process.cwd(),
180
+ intensityProfile,
181
+ });
182
+ }
183
+ catch (err) {
184
+ if (process.env.PUGI_DEBUG === '1') {
185
+ const msg = err instanceof Error ? err.message : String(err);
186
+ process.stderr.write(`[pugi-debug] engine bridge bootstrap failed: ${msg}\n`);
187
+ }
188
+ engineBridge = undefined;
189
+ }
60
190
  const session = new ReplSession({
61
191
  apiUrl: options.apiUrl,
62
192
  apiKey: options.apiKey,
63
193
  workspaceLabel: options.workspaceLabel,
64
194
  cliVersion: options.cliVersion,
65
195
  transport,
196
+ ...(engineBridge !== undefined ? { engineBridge } : {}),
66
197
  workspace,
198
+ cyberZoo,
67
199
  store,
68
200
  localSessionId: openedSessionId,
69
201
  repoSkeleton: skeleton,
@@ -86,26 +218,84 @@ export async function renderRepl(options) {
86
218
  // Kick off the connect; the Repl renders the connecting state until
87
219
  // the session pushes `connection: 'on_watch'` from the SSE onOpen.
88
220
  void session.start();
89
- // α6.14.2 wave 5: paint the chafa-baked brand-pug ANSI render to
90
- // stdout BEFORE Ink mounts. Ink's layout engine would mis-measure
91
- // the truecolor escape sequences, so the pug must land verbatim.
92
- // The flag is passed into <Repl /> so the splash component knows to
93
- // skip its own hand-crafted PUG_MASCOT column otherwise the
94
- // operator sees both the chafa pug AND the ASCII fallback stacked.
95
- // When skipSplash is true (operator opted out via --no-splash), we
96
- // suppress the pre-print too so the boot stays silent.
221
+ // beta.9: drain any keystrokes that landed in stdin between the
222
+ // pre-Ink raw-mode claim and now. Without this, the queued bytes
223
+ // would feed Ink's first useInput tick as a flood of "stale"
224
+ // characters once the InputBox mounts - the operator would see
225
+ // their pre-typed input materialise in the prompt as if they had
226
+ // typed it after the REPL became interactive. Idempotent: no-op
227
+ // when stdin is not a TTY or no bytes were buffered.
228
+ drainBufferedStdin(process.stdin);
229
+ // wave 5: paint the chafa-baked brand-pug ANSI render to
230
+ // stdout BEFORE Ink mounts (but AFTER alt-screen enter). Ink's
231
+ // layout engine would mis-measure the truecolor escape sequences,
232
+ // so the pug must land verbatim. The flag is passed into <Repl />
233
+ // so the splash component knows to skip its own hand-crafted
234
+ // PUG_MASCOT column - otherwise the operator sees both the chafa
235
+ // pug AND the ASCII fallback stacked. When skipSplash is true
236
+ // (operator opted out via --no-splash), we suppress the pre-print
237
+ // too so the boot stays silent.
97
238
  const mascotPrePrinted = options.skipSplash === true ? false : printPugMascotPreInk(process.stdout);
98
- const instance = render(React.createElement(Repl, {
239
+ // resolve the active theme ONCE at mount
240
+ // and wrap `<Repl />` in `<ThemeProvider>` so every Ink consumer
241
+ // (`<Header>`, `<DoctorTable>`, `<StyleTable>`, `<ThemeTable>`,
242
+ // …) picks up the same color tokens. The provider is stable for
243
+ // the lifetime of the REPL — operator `/theme <name>` writes to
244
+ // disk + appends a system line, and the next `pugi` launch re-
245
+ // mounts with the new slug. Re-mounting mid-session would race
246
+ // against Ink's raw-mode handler so we deliberately keep the
247
+ // session-lifetime contract instead of polling the config file.
248
+ const resolvedTheme = resolveTheme({
249
+ workspaceRoot: process.cwd(),
250
+ env: process.env,
251
+ });
252
+ // CEO P0 #2 : collect welcome banner data BEFORE Ink
253
+ // mounts so the banner paints on the first frame instead of swapping
254
+ // in mid-render. The collector swallows every IO error so а missing
255
+ // CHANGELOG / unreadable credential / malformed settings never
256
+ // blocks the boot.
257
+ let welcomeData;
258
+ if (options.skipSplash !== true) {
259
+ try {
260
+ welcomeData = collectWelcomeData({
261
+ cliVersion: options.cliVersion,
262
+ cwd: process.cwd(),
263
+ });
264
+ }
265
+ catch {
266
+ welcomeData = undefined;
267
+ }
268
+ }
269
+ const instance = render(React.createElement(ThemeProvider, { slug: resolvedTheme.slug }, React.createElement(Repl, {
99
270
  session,
100
271
  updateBanner: options.updateBanner ?? null,
101
272
  skipSplash: options.skipSplash === true,
102
273
  hideToolStream: options.hideToolStream === true,
103
274
  mascotPrePrinted,
104
- }));
275
+ welcomeData,
276
+ autoInitStatus: options.autoInitStatus ?? null,
277
+ })), {
278
+ // PUGI-534 — Ink kills the process on the first raw Ctrl+C by
279
+ // default, which beat the InputBox useInput double-tap timer to
280
+ // the punch (operator reported single ^C exits the REPL). Disable
281
+ // Ink's built-in handler so the InputBox `lastCtrlCAt` window owns
282
+ // the exit gesture: first press → cancel + toast, second press
283
+ // within 1s → `handleExit()` → `useApp().exit()`. SIGINT handlers
284
+ // on the process object (sigint-guard + per-engine-task in
285
+ // runtime/cli.ts) still fire as defence-in-depth on the rare
286
+ // non-raw fallback path.
287
+ exitOnCtrlC: false,
288
+ });
289
+ // Make sure we leave the alt screen on abrupt exits too. Without
290
+ // this the operator's shell stays "frozen" on the Pugi splash.
291
+ process.once('exit', bootstrap.restore);
292
+ process.once('SIGINT', bootstrap.restore);
293
+ process.once('SIGTERM', bootstrap.restore);
105
294
  try {
106
295
  await instance.waitUntilExit();
107
296
  }
108
297
  finally {
298
+ bootstrap.restore();
109
299
  session.close();
110
300
  if (store) {
111
301
  try {
@@ -125,6 +315,138 @@ export async function renderRepl(options) {
125
315
  }
126
316
  }
127
317
  }
318
+ export function claimTerminalForRepl(stdin = process.stdin, stdout = process.stdout) {
319
+ const isStdoutTty = stdout.isTTY === true;
320
+ const isStdinTty = stdin.isTTY === true && typeof stdin.setRawMode === 'function';
321
+ let altScreenEntered = false;
322
+ if (isStdoutTty) {
323
+ try {
324
+ stdout.write('\x1b[?1049h');
325
+ stdout.write('\x1b[H');
326
+ altScreenEntered = true;
327
+ }
328
+ catch {
329
+ /* terminal already detached */
330
+ }
331
+ }
332
+ let rawModeClaimed = false;
333
+ if (isStdinTty) {
334
+ try {
335
+ stdin.setEncoding('utf8');
336
+ stdin.setRawMode(true);
337
+ // Resume so the kernel actually delivers bytes to Node's event
338
+ // loop. Without resume, raw mode is set but data does not flow
339
+ // until something else (e.g. Ink) attaches a 'data' listener.
340
+ stdin.resume();
341
+ rawModeClaimed = true;
342
+ }
343
+ catch {
344
+ /* raw mode unsupported - the operator's shell still works */
345
+ }
346
+ }
347
+ let restored = false;
348
+ const restore = () => {
349
+ if (restored)
350
+ return;
351
+ restored = true;
352
+ if (rawModeClaimed && isStdinTty) {
353
+ try {
354
+ stdin.setRawMode(false);
355
+ }
356
+ catch {
357
+ /* terminal already detached */
358
+ }
359
+ }
360
+ if (altScreenEntered) {
361
+ try {
362
+ stdout.write('\x1b[?1049l');
363
+ }
364
+ catch {
365
+ /* shutdown race - terminal already detached */
366
+ }
367
+ }
368
+ };
369
+ return { altScreenEntered, rawModeClaimed, restore };
370
+ }
371
+ /**
372
+ * Read and discard any bytes buffered in stdin between
373
+ * `claimTerminalForRepl()` and the Ink mount. Returns the number of
374
+ * bytes drained so tests can assert the behaviour without intercepting
375
+ * the side effect.
376
+ *
377
+ * `stdin.read()` is a no-op when no data is buffered, so this is safe
378
+ * to call whether or not the operator actually typed during bootstrap.
379
+ * Wrapped in try/catch because a closed / piped stdin will throw on
380
+ * read in some Node versions.
381
+ */
382
+ export function drainBufferedStdin(stdin = process.stdin) {
383
+ if (stdin.isTTY !== true)
384
+ return 0;
385
+ try {
386
+ let bytesDrained = 0;
387
+ // Loop until read() returns null - readable streams may chunk
388
+ // buffered bytes across multiple read() calls when the operator
389
+ // typed faster than the kernel could deliver to Node's loop.
390
+ for (;;) {
391
+ const chunk = stdin.read();
392
+ if (chunk === null)
393
+ return bytesDrained;
394
+ bytesDrained += typeof chunk === 'string' ? chunk.length : chunk.byteLength;
395
+ }
396
+ }
397
+ catch {
398
+ return 0;
399
+ }
400
+ }
401
+ /**
402
+ * Project-root probe — beta.13 P2 fix.
403
+ *
404
+ * Beta.13 auto-init was unconditional and silently created `.pugi/` in
405
+ * every cwd the REPL was launched from, including `$HOME` and `/tmp`.
406
+ * Operators who ran `pugi` to ask a quick question outside of any
407
+ * project ended up with stray `.pugi/` directories polluting their
408
+ * filesystem. The gate looks for any of six project-root markers
409
+ * before scaffolding:
410
+ *
411
+ * - `package.json` — JS / TS workspaces
412
+ * - `.git` — any cloned repo regardless of language
413
+ * - `.pugi` — already-bound Pugi workspace (re-scaffold
414
+ * fills any missing artifacts the operator
415
+ * deleted, idempotent over existing files)
416
+ * - `Cargo.toml` — Rust crates
417
+ * - `pyproject.toml` — Python projects (PEP 518)
418
+ * - `go.mod` — Go modules
419
+ *
420
+ * The probe is six cheap `existsSync` calls; the cost is negligible
421
+ * compared with the alt-screen + Ink mount that follows. Exported so a
422
+ * future unit spec can lock the contract.
423
+ */
424
+ export function isProjectRoot(cwd) {
425
+ // ESM static imports — `require()` is not defined in a `"type": "module"`
426
+ // bundle and would throw `ReferenceError: require is not defined` the
427
+ // moment the REPL bootstrap calls this gate. Beta.16 P0 fix.
428
+ return (existsSync(resolve(cwd, 'package.json')) ||
429
+ existsSync(resolve(cwd, '.git')) ||
430
+ existsSync(resolve(cwd, '.pugi')) ||
431
+ existsSync(resolve(cwd, 'Cargo.toml')) ||
432
+ existsSync(resolve(cwd, 'pyproject.toml')) ||
433
+ existsSync(resolve(cwd, 'go.mod')));
434
+ }
435
+ /**
436
+ * Read the operator's cyber-zoo posture from `.pugi/settings.json`.
437
+ * Best-effort: when the file is missing / malformed, fall through to
438
+ * the historical 'on' default so the REPL never refuses to launch on
439
+ * a settings error. Beta.13 P1 fix.
440
+ */
441
+ function readCyberZooSetting(cwd) {
442
+ try {
443
+ const settings = loadSettings(cwd);
444
+ return settings.ui?.cyberZoo ?? 'on';
445
+ }
446
+ catch {
447
+ return 'on';
448
+ }
449
+ }
128
450
  /**
129
451
  * Open the local SessionStore for the REPL bootstrap. Returns
130
452
  * `{ store: null, openedSessionId: undefined }` on any error so the
@@ -162,11 +484,11 @@ async function openLocalStore(input) {
162
484
  }
163
485
  }
164
486
  /**
165
- * Bootstrap the α6.5 three-tier context primitives:
487
+ * Bootstrap the three-tier context primitives:
166
488
  *
167
- * - Tier 0: `RepoSkeleton` (~5KB ASCII tree + meta) for prompt injection.
168
- * - Tier 1: `WorkingSet` LRU bounded at 50 entries.
169
- * - Filewatch: chokidar started against cwd, ignore-filtered.
489
+ * - Tier 0: `RepoSkeleton` (~5KB ASCII tree + meta) for prompt injection.
490
+ * - Tier 1: `WorkingSet` LRU bounded at 50 entries.
491
+ * - Filewatch: chokidar started against cwd, ignore-filtered.
170
492
  *
171
493
  * The bootstrap is fail-safe: every primitive is wrapped so the REPL
172
494
  * still launches when (e.g.) chokidar refuses to start on a
@@ -214,15 +536,22 @@ async function bootstrapContext(input) {
214
536
  return { skeleton, workingSet, watcher };
215
537
  }
216
538
  /* ------------------------------------------------------------------ */
217
- /* Production transport */
539
+ /* Production transport */
218
540
  /* ------------------------------------------------------------------ */
219
- function createProductionTransport() {
541
+ export function createProductionTransport() {
220
542
  return {
221
- async createSession({ apiUrl, apiKey, workspace }) {
543
+ async createSession({ apiUrl, apiKey, workspace, cyberZoo }) {
222
544
  // Forward the workspace bundle in the POST body so admin-api can
223
- // surface `<workspace-context>` in Mira's prompt. Older admin-api
545
+ // surface `<workspace-context>` in Pugi's prompt. Older admin-api
224
546
  // builds ignore unknown fields, so this stays forward-compatible.
225
- // Wave 4 fix 2026-05-25.
547
+ // fix.
548
+ //
549
+ // Beta.13 P1 fix: also forward `cyberZoo` so admin-api
550
+ // can render Pugi's `<cyber-zoo>` marker matching the operator's
551
+ // `.pugi/settings.json::ui.cyberZoo` toggle instead of the
552
+ // historical 'on' default. Only included on the wire when set
553
+ // explicitly so a missing setting still survives older admin-api
554
+ // builds that do not declare the DTO field.
226
555
  const body = {};
227
556
  if (workspace?.workspaceCwd)
228
557
  body.workspaceCwd = workspace.workspaceCwd;
@@ -230,6 +559,8 @@ function createProductionTransport() {
230
559
  body.workspaceSlug = workspace.workspaceSlug;
231
560
  if (workspace?.workspaceSummary)
232
561
  body.workspaceSummary = workspace.workspaceSummary;
562
+ if (cyberZoo === 'on' || cyberZoo === 'off')
563
+ body.cyberZoo = cyberZoo;
233
564
  const response = await fetch(joinUrl(apiUrl, '/api/pugi/sessions'), {
234
565
  method: 'POST',
235
566
  headers: jsonHeaders(apiKey),
@@ -275,6 +606,31 @@ function createProductionTransport() {
275
606
  if (lastEventId) {
276
607
  headers['Last-Event-ID'] = lastEventId;
277
608
  }
609
+ // beta.9 CEO dogfood: hard timeout on the SSE
610
+ // handshake so a CDN/proxy that buffers the response (or an
611
+ // admin-api that accepted the route but never flushed headers)
612
+ // cannot freeze the REPL in `connecting` forever. The 5s budget
613
+ // is generous - admin-api routinely responds in <500ms when
614
+ // healthy - but tight enough that an operator who launched
615
+ // `pugi` and is staring at the screen will see the status flip
616
+ // to `reconnecting` instead of an indefinite hang. The
617
+ // AbortController bound to the fetch aborts the in-flight
618
+ // request when the timer fires, which surfaces as an
619
+ // `AbortError` and routes through the existing onError handler
620
+ // (which calls scheduleReconnect via the session). The timer
621
+ // is cleared the moment onOpen fires so a slow-but-eventually-
622
+ // successful handshake still works.
623
+ const handshakeDeadlineMs = 5_000;
624
+ const handshakeTimer = setTimeout(() => {
625
+ controller.abort();
626
+ // onError is called from the catch block below (the abort
627
+ // synthesises an AbortError that consumeSseStream / fetch
628
+ // will throw). No explicit onError call here - we let the
629
+ // catch path normalise the error message so the operator
630
+ // sees the consistent "SSE handshake timed out (5s)" prose
631
+ // through the same plumbing that surfaces every other
632
+ // transport failure.
633
+ }, handshakeDeadlineMs);
278
634
  void (async () => {
279
635
  try {
280
636
  const response = await fetch(url, {
@@ -288,6 +644,9 @@ function createProductionTransport() {
288
644
  if (!response.body) {
289
645
  throw new Error('SSE response has no body');
290
646
  }
647
+ // Handshake survived; cancel the deadline so a slow
648
+ // first-event stream does not get aborted later.
649
+ clearTimeout(handshakeTimer);
291
650
  onOpen();
292
651
  await consumeSseStream(response.body, onEvent);
293
652
  // Server closed the stream cleanly. Treat as an error so
@@ -297,19 +656,33 @@ function createProductionTransport() {
297
656
  onError(new Error('SSE stream ended'));
298
657
  }
299
658
  catch (error) {
300
- if (controller.signal.aborted)
659
+ clearTimeout(handshakeTimer);
660
+ if (controller.signal.aborted) {
661
+ // Distinguish operator-driven close (session.close())
662
+ // from the handshake-deadline abort. The session sets a
663
+ // `closed` flag before calling controller.abort(); the
664
+ // handshake-deadline abort fires while the session is
665
+ // still expecting onOpen. We cannot read session state
666
+ // from here, so we surface a single error class with a
667
+ // clear message - the session-side onError handler
668
+ // already short-circuits when `closed=true`.
669
+ onError(new Error(`SSE handshake timed out after ${handshakeDeadlineMs}ms`));
301
670
  return;
671
+ }
302
672
  onError(error instanceof Error ? error : new Error(String(error)));
303
673
  }
304
674
  })();
305
675
  return {
306
- close: () => controller.abort(),
676
+ close: () => {
677
+ clearTimeout(handshakeTimer);
678
+ controller.abort();
679
+ },
307
680
  };
308
681
  },
309
682
  };
310
683
  }
311
684
  /* ------------------------------------------------------------------ */
312
- /* SSE parser */
685
+ /* SSE parser */
313
686
  /* ------------------------------------------------------------------ */
314
687
  /**
315
688
  * Minimal SSE parser. Reads a UTF-8 stream of `id:` / `event:` / `data:`
@@ -373,7 +746,7 @@ async function consumeSseStream(body, onEvent) {
373
746
  }
374
747
  }
375
748
  /* ------------------------------------------------------------------ */
376
- /* Small helpers */
749
+ /* Small helpers */
377
750
  /* ------------------------------------------------------------------ */
378
751
  function jsonHeaders(apiKey) {
379
752
  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.