@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.88

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 (405) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/smoke.js +133 -0
  11. package/dist/core/agent-progress/cleanup.js +134 -0
  12. package/dist/core/agent-progress/schema.js +144 -0
  13. package/dist/core/agent-progress/writer.js +101 -0
  14. package/dist/core/agents/adaptive-router.js +330 -0
  15. package/dist/core/agents/query-decomposer.js +297 -0
  16. package/dist/core/agents/registry.js +3 -3
  17. package/dist/core/approvals/shortcut-resolver.js +98 -0
  18. package/dist/core/artifact-chain/dispatcher.js +148 -0
  19. package/dist/core/artifact-chain/exporter.js +164 -0
  20. package/dist/core/artifact-chain/state.js +243 -0
  21. package/dist/core/artifact-chain/steps.js +169 -0
  22. package/dist/core/ask-user/question.js +92 -0
  23. package/dist/core/audit/audit-trail.js +275 -0
  24. package/dist/core/auth/ensure-authenticated.js +129 -0
  25. package/dist/core/auth/env-provider.js +238 -0
  26. package/dist/core/auto-open-browser.js +4 -4
  27. package/dist/core/auto-update/channels.js +122 -0
  28. package/dist/core/auto-update/checker.js +241 -0
  29. package/dist/core/auto-update/state.js +235 -0
  30. package/dist/core/bare-mode/index.js +107 -0
  31. package/dist/core/bash/redirect.js +281 -0
  32. package/dist/core/bash-classifier.js +436 -40
  33. package/dist/core/checkpoint/resumer.js +149 -0
  34. package/dist/core/checkpoint/rewinder.js +291 -0
  35. package/dist/core/checkpoints/shadow-git.js +670 -0
  36. package/dist/core/citations/parser.js +109 -0
  37. package/dist/core/classifier/yolo-classifier.js +88 -0
  38. package/dist/core/codegraph/decision-store.js +248 -0
  39. package/dist/core/codegraph/detect-repo.js +459 -0
  40. package/dist/core/codegraph/install.js +134 -0
  41. package/dist/core/codegraph/offer-hook.js +220 -0
  42. package/dist/core/compact/auto-trigger.js +96 -0
  43. package/dist/core/compact/buffer-rewriter.js +115 -0
  44. package/dist/core/compact/summarizer.js +208 -0
  45. package/dist/core/compact/token-counter.js +108 -0
  46. package/dist/core/consensus/anvil-fanout.js +25 -25
  47. package/dist/core/consensus/diff-capture.js +121 -12
  48. package/dist/core/consensus/rubric.js +21 -21
  49. package/dist/core/context/builder.js +6 -6
  50. package/dist/core/context/compaction-events.js +8 -8
  51. package/dist/core/context/compaction.js +31 -31
  52. package/dist/core/context/index.js +15 -8
  53. package/dist/core/context/invariants.js +51 -51
  54. package/dist/core/context/markdown-loader.js +28 -10
  55. package/dist/core/context/markdown-traverse.js +255 -0
  56. package/dist/core/context/pugiignore.js +41 -41
  57. package/dist/core/context/repo-skeleton.js +37 -37
  58. package/dist/core/context/tool-eviction.js +55 -0
  59. package/dist/core/context/watcher.js +32 -32
  60. package/dist/core/context/working-set.js +23 -23
  61. package/dist/core/coordinator/agent-tools.js +77 -0
  62. package/dist/core/coordinator/agent-toolset.js +65 -0
  63. package/dist/core/coordinator/fsm.js +73 -0
  64. package/dist/core/coordinator/mode-fsm.js +70 -0
  65. package/dist/core/cost/rate-card.js +129 -0
  66. package/dist/core/cost/tracker.js +221 -0
  67. package/dist/core/credentials.js +12 -12
  68. package/dist/core/cron/scheduler.js +138 -0
  69. package/dist/core/denial-tracking/index.js +8 -0
  70. package/dist/core/denial-tracking/state.js +264 -0
  71. package/dist/core/diagnostics/probe-runner.js +93 -0
  72. package/dist/core/diagnostics/probes/api.js +46 -0
  73. package/dist/core/diagnostics/probes/auth.js +93 -0
  74. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  75. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  76. package/dist/core/diagnostics/probes/config.js +72 -0
  77. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  78. package/dist/core/diagnostics/probes/disk.js +81 -0
  79. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  80. package/dist/core/diagnostics/probes/git.js +65 -0
  81. package/dist/core/diagnostics/probes/hooks.js +118 -0
  82. package/dist/core/diagnostics/probes/mcp.js +75 -0
  83. package/dist/core/diagnostics/probes/node.js +59 -0
  84. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  85. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  86. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  87. package/dist/core/diagnostics/probes/session.js +74 -0
  88. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  89. package/dist/core/diagnostics/probes/workspace.js +63 -0
  90. package/dist/core/diagnostics/types.js +70 -0
  91. package/dist/core/dispatch/cache-cleanup.js +197 -0
  92. package/dist/core/dispatch/cache-handoff.js +295 -0
  93. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  94. package/dist/core/edits/dispatch.js +293 -7
  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 +3 -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 +322 -0
  108. package/dist/core/engine/anvil-client.js +151 -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 +134 -16
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1295 -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 +44 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +213 -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/image/renderer.js +71 -0
  141. package/dist/core/init/detector.js +582 -0
  142. package/dist/core/init/template-renderer.js +242 -0
  143. package/dist/core/jobs/registry.js +18 -18
  144. package/dist/core/ledger/results-tsv.js +142 -0
  145. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  146. package/dist/core/lsp/cache.js +105 -0
  147. package/dist/core/lsp/client.js +776 -0
  148. package/dist/core/lsp/language-detect.js +66 -0
  149. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  150. package/dist/core/lsp/symbol-tools.js +372 -0
  151. package/dist/core/mcp/client.js +97 -28
  152. package/dist/core/mcp/http-server.js +553 -0
  153. package/dist/core/mcp/orchestrator-tools.js +662 -0
  154. package/dist/core/mcp/permission.js +190 -0
  155. package/dist/core/mcp/registry.js +39 -17
  156. package/dist/core/mcp/server-tools.js +219 -0
  157. package/dist/core/mcp/server.js +397 -0
  158. package/dist/core/mcp/trust.js +10 -10
  159. package/dist/core/memory/dual-write.js +416 -0
  160. package/dist/core/memory/passive-extract.js +130 -0
  161. package/dist/core/memory/phase1-kinds.js +20 -0
  162. package/dist/core/memory/secret-scanner.js +304 -0
  163. package/dist/core/memory-sync/queue.js +170 -0
  164. package/dist/core/metrics/extract.js +113 -0
  165. package/dist/core/modes/roo-modes.js +68 -0
  166. package/dist/core/onboarding/ensure-initialized.js +133 -0
  167. package/dist/core/onboarding/marker.js +111 -0
  168. package/dist/core/onboarding/telemetry-state.js +108 -0
  169. package/dist/core/output-style/presets.js +176 -0
  170. package/dist/core/output-style/state.js +185 -0
  171. package/dist/core/path-security.js +287 -5
  172. package/dist/core/permission.js +82 -22
  173. package/dist/core/permissions/auto-classifier.js +124 -0
  174. package/dist/core/permissions/bash-parser.js +371 -0
  175. package/dist/core/permissions/circuit-breaker.js +83 -0
  176. package/dist/core/permissions/constrained-edit.js +91 -0
  177. package/dist/core/permissions/gate.js +278 -0
  178. package/dist/core/permissions/index.js +20 -0
  179. package/dist/core/permissions/mode.js +174 -0
  180. package/dist/core/permissions/network-egress.js +137 -0
  181. package/dist/core/permissions/state.js +241 -0
  182. package/dist/core/permissions/tool-class.js +93 -0
  183. package/dist/core/plan-mode/ui-state.js +51 -0
  184. package/dist/core/plans/plan-artifact.js +721 -0
  185. package/dist/core/policy-limits/etag-store.js +122 -0
  186. package/dist/core/prd-check/parser.js +215 -0
  187. package/dist/core/prd-check/reporter.js +127 -0
  188. package/dist/core/prd-check/session-review.js +557 -0
  189. package/dist/core/prd-check/verifiers.js +223 -0
  190. package/dist/core/prompt-cache/client-cache.js +99 -0
  191. package/dist/core/prompts/assembly.js +29 -0
  192. package/dist/core/prompts/registry.js +364 -0
  193. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  194. package/dist/core/pugi-md/context-injector.js +76 -0
  195. package/dist/core/pugi-md/walk-up.js +207 -0
  196. package/dist/core/python/uv-installer.js +270 -0
  197. package/dist/core/python/uv-resolver.js +83 -0
  198. package/dist/core/rate-limit/narrator.js +146 -0
  199. package/dist/core/recipes/cli-types.js +20 -0
  200. package/dist/core/recipes/loader.js +103 -0
  201. package/dist/core/recipes/runner.js +345 -0
  202. package/dist/core/recipes/schema.js +587 -0
  203. package/dist/core/release-notes/parser.js +241 -0
  204. package/dist/core/release-notes/state.js +116 -0
  205. package/dist/core/repl/ask.js +37 -37
  206. package/dist/core/repl/cancellation.js +26 -26
  207. package/dist/core/repl/cap-warning.js +4 -4
  208. package/dist/core/repl/clipboard-read.js +11 -11
  209. package/dist/core/repl/dispatch-fsm.js +12 -12
  210. package/dist/core/repl/history-search.js +15 -15
  211. package/dist/core/repl/history.js +28 -18
  212. package/dist/core/repl/kill-ring.js +5 -5
  213. package/dist/core/repl/model-pricing.js +135 -0
  214. package/dist/core/repl/privacy-banner.js +22 -22
  215. package/dist/core/repl/session.js +2157 -214
  216. package/dist/core/repl/slash-commands.js +533 -40
  217. package/dist/core/repl/store/index.js +1 -1
  218. package/dist/core/repl/store/jsonl-log.js +22 -22
  219. package/dist/core/repl/store/lockfile.js +10 -10
  220. package/dist/core/repl/store/session-store.js +136 -107
  221. package/dist/core/repl/store/types.js +15 -15
  222. package/dist/core/repl/store/uuid-v7.js +12 -12
  223. package/dist/core/repl/workspace-context.js +43 -21
  224. package/dist/core/repo-map/build.js +125 -0
  225. package/dist/core/repo-map/cache.js +185 -0
  226. package/dist/core/repo-map/extractor.js +254 -0
  227. package/dist/core/repo-map/formatter.js +145 -0
  228. package/dist/core/repo-map/page-rank.js +105 -0
  229. package/dist/core/repo-map/scanner.js +211 -0
  230. package/dist/core/retry-budget/budget.js +284 -0
  231. package/dist/core/retry-budget/index.js +5 -0
  232. package/dist/core/retry-budget/retry-cap.js +74 -0
  233. package/dist/core/routing/lead-worker.js +43 -0
  234. package/dist/core/routing/pre-flight-estimator.js +108 -0
  235. package/dist/core/runs/run-tree.js +103 -0
  236. package/dist/core/security/injection-scanner.js +367 -0
  237. package/dist/core/security/output-filter.js +418 -0
  238. package/dist/core/session/env-file.js +105 -0
  239. package/dist/core/session/section-budgets.js +140 -0
  240. package/dist/core/session.js +92 -0
  241. package/dist/core/settings.js +298 -5
  242. package/dist/core/share/formatter.js +271 -0
  243. package/dist/core/share/redactor.js +221 -0
  244. package/dist/core/share/uploader.js +267 -0
  245. package/dist/core/skills/defaults.js +457 -0
  246. package/dist/core/skills/loader.js +22 -22
  247. package/dist/core/skills/sources.js +27 -27
  248. package/dist/core/smoke/headless-driver.js +174 -0
  249. package/dist/core/smoke/orchestrator.js +194 -0
  250. package/dist/core/smoke/runner.js +238 -0
  251. package/dist/core/smoke/scenario-parser.js +316 -0
  252. package/dist/core/statusline.js +99 -0
  253. package/dist/core/subagents/dispatcher-real.js +600 -0
  254. package/dist/core/subagents/dispatcher.js +132 -43
  255. package/dist/core/subagents/index.js +19 -6
  256. package/dist/core/subagents/isolation-matrix.js +213 -0
  257. package/dist/core/subagents/spawn.js +19 -4
  258. package/dist/core/telemetry/emitter.js +229 -0
  259. package/dist/core/telemetry/queue.js +251 -0
  260. package/dist/core/theme/context.js +91 -0
  261. package/dist/core/theme/presets.js +228 -0
  262. package/dist/core/theme/state.js +181 -0
  263. package/dist/core/todos/invariant.js +10 -0
  264. package/dist/core/todos/state.js +177 -0
  265. package/dist/core/tool-schema/compressor.js +89 -0
  266. package/dist/core/transport/version-interceptor.js +166 -0
  267. package/dist/core/trust.js +2 -2
  268. package/dist/core/tui/thinking-block.js +64 -0
  269. package/dist/core/vim/keymap.js +288 -0
  270. package/dist/core/vim/state.js +92 -0
  271. package/dist/core/watch-markers/marker-watcher.js +133 -0
  272. package/dist/core/worktree-manager/cleanup.js +123 -0
  273. package/dist/core/worktree-manager/manager.js +303 -0
  274. package/dist/index.js +36 -0
  275. package/dist/runtime/bootstrap.js +190 -0
  276. package/dist/runtime/cli.js +4203 -493
  277. package/dist/runtime/commands/agents.js +30 -30
  278. package/dist/runtime/commands/budget.js +5 -5
  279. package/dist/runtime/commands/cancel.js +231 -0
  280. package/dist/runtime/commands/chain.js +489 -0
  281. package/dist/runtime/commands/codegraph-status.js +227 -0
  282. package/dist/runtime/commands/compact.js +297 -0
  283. package/dist/runtime/commands/config.js +73 -39
  284. package/dist/runtime/commands/cost.js +199 -0
  285. package/dist/runtime/commands/delegate.js +244 -13
  286. package/dist/runtime/commands/dispatch.js +126 -0
  287. package/dist/runtime/commands/doctor.js +579 -0
  288. package/dist/runtime/commands/feedback.js +184 -0
  289. package/dist/runtime/commands/hooks.js +184 -0
  290. package/dist/runtime/commands/init.js +254 -0
  291. package/dist/runtime/commands/lsp.js +368 -0
  292. package/dist/runtime/commands/mcp.js +879 -0
  293. package/dist/runtime/commands/memory.js +582 -0
  294. package/dist/runtime/commands/model.js +237 -0
  295. package/dist/runtime/commands/onboarding.js +275 -0
  296. package/dist/runtime/commands/patch.js +128 -0
  297. package/dist/runtime/commands/permissions.js +112 -0
  298. package/dist/runtime/commands/plan.js +143 -0
  299. package/dist/runtime/commands/prd-check.js +285 -0
  300. package/dist/runtime/commands/privacy.js +17 -17
  301. package/dist/runtime/commands/recipe.js +325 -0
  302. package/dist/runtime/commands/redo-blob-store.js +92 -0
  303. package/dist/runtime/commands/redo.js +361 -0
  304. package/dist/runtime/commands/release-notes.js +229 -0
  305. package/dist/runtime/commands/repo-map.js +95 -0
  306. package/dist/runtime/commands/report.js +299 -0
  307. package/dist/runtime/commands/resume.js +118 -0
  308. package/dist/runtime/commands/review-consensus.js +68 -53
  309. package/dist/runtime/commands/rewind.js +333 -0
  310. package/dist/runtime/commands/roster.js +14 -14
  311. package/dist/runtime/commands/sessions.js +163 -0
  312. package/dist/runtime/commands/share.js +316 -0
  313. package/dist/runtime/commands/skills.js +31 -31
  314. package/dist/runtime/commands/status.js +186 -0
  315. package/dist/runtime/commands/stickers.js +82 -0
  316. package/dist/runtime/commands/style.js +194 -0
  317. package/dist/runtime/commands/theme.js +196 -0
  318. package/dist/runtime/commands/undo.js +54 -22
  319. package/dist/runtime/commands/update.js +289 -0
  320. package/dist/runtime/commands/vim.js +140 -0
  321. package/dist/runtime/commands/worktree.js +177 -0
  322. package/dist/runtime/commands/worktrees.js +155 -0
  323. package/dist/runtime/headless-repl.js +195 -0
  324. package/dist/runtime/headless.js +543 -0
  325. package/dist/runtime/load-hooks-or-exit.js +71 -0
  326. package/dist/runtime/plan-decompose.js +531 -0
  327. package/dist/runtime/sigint-guard.js +272 -0
  328. package/dist/runtime/update-check.js +28 -28
  329. package/dist/runtime/version.js +65 -0
  330. package/dist/skills/bundled/batch.js +617 -0
  331. package/dist/skills/bundled/index.js +45 -0
  332. package/dist/skills/bundled/loop.js +358 -0
  333. package/dist/skills/bundled/remember.js +383 -0
  334. package/dist/skills/bundled/simplify.js +289 -0
  335. package/dist/skills/bundled/skillify.js +373 -0
  336. package/dist/skills/bundled/stuck.js +558 -0
  337. package/dist/skills/bundled/verify.js +439 -0
  338. package/dist/testing/vcr.js +486 -0
  339. package/dist/tools/agent-tool.js +229 -0
  340. package/dist/tools/apply-patch.js +556 -0
  341. package/dist/tools/ask-user-question.js +288 -0
  342. package/dist/tools/ask-user.js +115 -0
  343. package/dist/tools/bash.js +624 -46
  344. package/dist/tools/brief.js +224 -0
  345. package/dist/tools/enter-worktree.js +250 -0
  346. package/dist/tools/exit-worktree.js +147 -0
  347. package/dist/tools/file-tools.js +161 -44
  348. package/dist/tools/lsp-tools.js +189 -0
  349. package/dist/tools/mcp-tool.js +260 -0
  350. package/dist/tools/multi-edit.js +361 -0
  351. package/dist/tools/powershell.js +268 -0
  352. package/dist/tools/registry.js +85 -0
  353. package/dist/tools/skill-tool.js +96 -0
  354. package/dist/tools/sleep.js +99 -0
  355. package/dist/tools/synthetic-output.js +133 -0
  356. package/dist/tools/tasks.js +208 -0
  357. package/dist/tools/todo-write.js +184 -0
  358. package/dist/tools/verify-plan-execution.js +295 -0
  359. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  360. package/dist/tools/web-fetch.js +195 -10
  361. package/dist/tools/web-search.js +458 -0
  362. package/dist/tui/agent-progress-card.js +111 -0
  363. package/dist/tui/agent-tree.js +11 -1
  364. package/dist/tui/ask-modal.js +14 -14
  365. package/dist/tui/ask-user-question-chips.js +257 -0
  366. package/dist/tui/ask-user-question-prompt.js +203 -0
  367. package/dist/tui/compact-banner.js +81 -0
  368. package/dist/tui/conversation-pane.js +85 -11
  369. package/dist/tui/cost-table.js +111 -0
  370. package/dist/tui/device-flow.js +2 -2
  371. package/dist/tui/doctor-table.js +46 -0
  372. package/dist/tui/feedback-prompt.js +156 -0
  373. package/dist/tui/input-box.js +247 -32
  374. package/dist/tui/login-picker.js +3 -3
  375. package/dist/tui/markdown-render.js +6 -6
  376. package/dist/tui/onboarding-wizard.js +240 -0
  377. package/dist/tui/permissions-picker.js +86 -0
  378. package/dist/tui/render.js +35 -0
  379. package/dist/tui/repl-render.js +332 -54
  380. package/dist/tui/repl-splash-art.js +16 -16
  381. package/dist/tui/repl-splash-mascot.js +48 -24
  382. package/dist/tui/repl-splash.js +22 -22
  383. package/dist/tui/repl.js +124 -44
  384. package/dist/tui/slash-palette.js +6 -6
  385. package/dist/tui/splash.js +2 -2
  386. package/dist/tui/status-bar.js +109 -31
  387. package/dist/tui/status-table.js +7 -0
  388. package/dist/tui/stickers-art.js +136 -0
  389. package/dist/tui/style-table.js +28 -0
  390. package/dist/tui/theme-table.js +29 -0
  391. package/dist/tui/thinking-spinner.js +123 -0
  392. package/dist/tui/tool-stream-pane.js +53 -4
  393. package/dist/tui/update-banner.js +27 -2
  394. package/dist/tui/vim-input.js +267 -0
  395. package/dist/tui/welcome-banner.js +107 -0
  396. package/dist/tui/welcome-data.js +293 -0
  397. package/dist/tui/workspace-context.js +2 -2
  398. package/docs/examples/codegraph.mcp.json +10 -0
  399. package/package.json +25 -7
  400. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  401. package/test/scenarios/compact-force.scenario.txt +11 -0
  402. package/test/scenarios/identity.scenario.txt +11 -0
  403. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  404. package/test/scenarios/walkback.scenario.txt +12 -0
  405. package/dist/core/engine/compaction-hook.js +0 -154
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { MarkdownRender } from './markdown-render.js';
4
+ import { CompactBanner } from './compact-banner.js';
4
5
  const HUE_COLOR_BY_SLUG = {
5
- // Mira (Pug) - coordinator
6
+ // Pugi (Pug) - coordinator
6
7
  main: 'cyan',
7
8
  // Olivia (Honeybee) - release
8
9
  pm: 'yellow',
@@ -36,28 +37,101 @@ function PaneHeader({ count }) {
36
37
  function ConversationRow({ row, personaNames, }) {
37
38
  switch (row.source) {
38
39
  case 'operator':
39
- return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", children: '› ' }), _jsx(Text, { children: row.text })] }));
40
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "#3da9fc", children: '› ' }), _jsx(Text, { children: row.text })] }));
40
41
  case 'system':
41
42
  return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: '· ' }), _jsx(Text, { dimColor: true, children: row.text })] }));
43
+ case 'compact-boundary': {
44
+ // L29 : structured render. When the row carries the
45
+ // `compaction` payload, drive the Ink banner directly so the
46
+ // operator sees a width-aware gray separator with the canonical
47
+ // label. Fallback: a legacy boundary missing the structured
48
+ // payload (older replay events, hand-built rows) renders through
49
+ // the dim text path so the operator still sees the marker.
50
+ if (row.compaction) {
51
+ return (_jsx(CompactBanner, { turnsBefore: row.compaction.turnsBefore, trigger: row.compaction.trigger, summaryTokenCount: row.compaction.summaryTokenCount, turnsAgo: row.compaction.turnsAgo, summaryAtEpochMs: row.timestampEpochMs, columns: process.stdout?.columns }));
52
+ }
53
+ return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: row.text }) }));
54
+ }
42
55
  case 'persona': {
43
56
  const slug = row.personaSlug ?? '';
44
57
  const color = HUE_COLOR_BY_SLUG[slug] ?? 'white';
45
58
  const displayName = personaNames?.get(slug) ?? slug;
46
- // α6.12: persona bodies travel through MarkdownRender so code
47
- // fences, headings, and inline accents land correctly. A row that
48
- // carries no Markdown syntax renders as plain text under the same
49
- // path (the parser falls through to a single paragraph span), so
50
- // there is no regression for the simple "Mira shipped." baseline.
51
- const containsMarkdown = looksLikeMarkdown(row.text);
52
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: color, bold: true, children: `▸ ${displayName} ` }), containsMarkdown ? null : _jsx(Text, { children: row.text })] }), containsMarkdown ? (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownRender, { source: row.text }) })) : null] }));
59
+ // CEO live dogfood: a body that started with a
60
+ // Cyrillic letter (e.g. "Принял.") rendered as "PugiПринял." on
61
+ // the same line because the persona label sat at zero margin and
62
+ // the body fell flush behind it. The label is bold-coloured chrome,
63
+ // not text the operator is meant to read as a sentence stem.
64
+ // Split body to its own row and indent two columns so the
65
+ // transcript reads:
66
+ //
67
+ // ▸ Pugi
68
+ // Принял.
69
+ //
70
+ // matching the the upstream tool / Codex / Gemini baseline visually +
71
+ // preventing the "PugiПринял" glue regardless of the body's
72
+ // leading character.
73
+ //
74
+ // The render also strips a leading identity-intro phrase as a
75
+ // defense-in-depth complement to the backend output gate — when
76
+ // turnIndex > 1, the operator must NOT see "Я Pugi - твой
77
+ // инженерный напарник." opening every reply. This is belt-and-
78
+ // braces; the prompt + output-gate already block it upstream.
79
+ const stripped = stripLeadingIdentityIntro(row.text);
80
+ const containsMarkdown = looksLikeMarkdown(stripped);
81
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsx(Text, { color: color, bold: true, children: `▸ ${displayName}` }) }), containsMarkdown ? (_jsx(Box, { marginLeft: 2, children: _jsx(MarkdownRender, { source: stripped }) })) : (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: stripped }) }))] }));
53
82
  }
54
83
  }
55
84
  }
85
+ /**
86
+ * Strip a leading identity-intro line ("I'm Pugi — your engineering
87
+ * copilot..." / "Я Pugi — твой инженерный напарник...") from the
88
+ * transcript body as a defense-in-depth complement to the backend
89
+ * output gate. The prompt + gate already block this upstream; the
90
+ * render guards against the rare case where a legacy model release
91
+ * sneaks the intro past both layers.
92
+ *
93
+ * The strip is intentionally conservative — we only remove the
94
+ * canonical phrasings the prompt teaches Pugi to emit. Any other
95
+ * prose containing the word "Pugi" passes through unchanged.
96
+ *
97
+ * Exported for spec.
98
+ */
99
+ export function stripLeadingIdentityIntro(text) {
100
+ if (!text || text.length === 0)
101
+ return text;
102
+ // Try each canonical intro shape in turn (RU + EN). We strip only
103
+ // the LEADING occurrence; a mid-body mention is intentional citation
104
+ // (e.g. "the Pugi codename..." in an explainer turn).
105
+ const introPatterns = [
106
+ // RU canonical — both masculine ("напарник/напарника/напарнику") and
107
+ // feminine ("напарница/напарницей/напарнице") declensions land on this
108
+ // strip. The prior pattern `напарни[ккк][аеу]?` was a character-class
109
+ // typo (three `к`s collapse to one) and accepted no feminine form, so
110
+ // a Pugi reply opening "Я Pugi — твоя инженерная напарница…" leaked
111
+ // past the strip. P1 reviewer fix PR .
112
+ /^(?:Я\s+Pugi\s*[—–-]\s*тв(?:ой|ё|оя)\s+инженерн[аыяий][хйеомя]*\s+напарни(?:к[аеу]?|ц(?:ей|а|ы|е|у))[.,!]?\s*)/u,
113
+ /^(?:Я\s+Pugi\s*[—–-]\s*координатор[.,!]?\s*)/u,
114
+ // EN canonical
115
+ /^(?:I'?m\s+Pugi\s*[—–-]\s*your\s+engineering\s+copilot[.,!]?\s*)/iu,
116
+ /^(?:Pugi\s+here[.,!]?\s*)/iu,
117
+ /^(?:This\s+is\s+Pugi[.,!]?\s*)/iu,
118
+ ];
119
+ let out = text;
120
+ for (const re of introPatterns) {
121
+ out = out.replace(re, '');
122
+ }
123
+ // If the body had ONLY the intro phrase, return the original — never
124
+ // hand the operator an empty bubble. The output gate would have
125
+ // logged the verbosity hit; the operator sees the unmodified text.
126
+ if (out.trim().length === 0)
127
+ return text;
128
+ return out;
129
+ }
56
130
  /**
57
131
  * Cheap heuristic for "this transcript row will benefit from Markdown
58
132
  * rendering". We only pay the parser cost when the row plausibly
59
133
  * contains a code fence, heading, list item, or inline accent. A bare
60
- * "Mira shipped." line takes the legacy fast path.
134
+ * "Pugi shipped." line takes the legacy fast path.
61
135
  *
62
136
  * The probe is intentionally generous - false positives just route
63
137
  * through the parser, which renders plain text identically.
@@ -67,7 +141,7 @@ function looksLikeMarkdown(text) {
67
141
  return false;
68
142
  if (text.includes('```'))
69
143
  return true;
70
- // Codex P2 PR #369: intro-plus-list shape ("Summary:\n- bullet")
144
+ // Codex P2 PR: intro-plus-list shape ("Summary:\n- bullet")
71
145
  // must route through renderer. Scan EVERY line, not just the first.
72
146
  const lines = text.split('\n');
73
147
  for (const raw of lines) {
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { estimateUsd, formatTokensWithCommas, formatUsd, rateFor } from '../core/cost/rate-card.js';
4
+ /**
5
+ * Column widths chosen to match the L19 spec output. The model column is
6
+ * the widest because slug strings like `qwen3-coder-480b-instruct-fp8`
7
+ * run 31 chars. We pad/truncate inside the renderer so a TUI on a 80-col
8
+ * terminal does not wrap mid-table.
9
+ */
10
+ const COL_MODEL = 34;
11
+ const COL_IN = 8;
12
+ const COL_OUT = 9;
13
+ const COL_USD = 10;
14
+ /**
15
+ * Render one cost report. Stateless — re-rendering with the same view
16
+ * produces the same output by construction. Tests assert against
17
+ * `lastFrame()` from `ink-testing-library`.
18
+ */
19
+ export function CostTable({ view }) {
20
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: view.heading }), _jsx(Text, { children: "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550" }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [pad('MODEL', COL_MODEL), padLeft('IN_TOK', COL_IN), padLeft('OUT_TOK', COL_OUT), padLeft('$ EST', COL_USD)] }), view.rows.length === 0 ? (_jsx(Text, { dimColor: true, children: "No calls recorded yet \u2014 brief a persona to charge the meter." })) : (view.rows.map((row) => (_jsxs(Text, { children: [pad(row.model, COL_MODEL), padLeft(formatTokensWithCommas(row.inputTokens), COL_IN), padLeft(formatTokensWithCommas(row.outputTokens), COL_OUT), padLeft(formatUsd(row.usd), COL_USD), row.note ? ` (${row.note})` : ''] }, row.model)))), _jsx(Text, { children: " " }), _jsxs(Text, { children: ["Total tokens: ", formatTokensWithCommas(view.totalInputTokens + view.totalOutputTokens), ' (in: ', formatTokensWithCommas(view.totalInputTokens), ', out: ', formatTokensWithCommas(view.totalOutputTokens), ')'] }), _jsxs(Text, { children: ["Total dollar estimate: ", formatUsd(view.totalUsd)] }), view.tier ? _jsxs(Text, { children: ["Tier: ", view.tier.tier, view.tier.quotaLine ? ` (${view.tier.quotaLine})` : ''] }) : null] }));
21
+ }
22
+ /**
23
+ * Build a `CostView` from a `SessionAggregate`. The function lives here
24
+ * (next to the renderer) so a future caller — the REPL slash, the CLI
25
+ * command, a JSON-only path — uses the same view-model shape and the
26
+ * same row-sort rule.
27
+ */
28
+ export function buildCostView(input) {
29
+ const rows = [];
30
+ for (const [model, entry] of Object.entries(input.aggregate.models)) {
31
+ const usd = estimateUsd(model, entry.input, entry.output);
32
+ const rate = rateFor(model);
33
+ rows.push({
34
+ model,
35
+ inputTokens: entry.input,
36
+ outputTokens: entry.output,
37
+ usd,
38
+ note: rate.note,
39
+ });
40
+ }
41
+ // Sort by USD descending first (most-expensive-first, matches the L19
42
+ // sample output where the Claude row leads). Ties break by total
43
+ // tokens so two free open-weight rows order deterministically.
44
+ rows.sort((a, b) => {
45
+ if (b.usd !== a.usd)
46
+ return b.usd - a.usd;
47
+ return (b.inputTokens + b.outputTokens) - (a.inputTokens + a.outputTokens);
48
+ });
49
+ let totalIn = 0;
50
+ let totalOut = 0;
51
+ let totalUsd = 0;
52
+ for (const row of rows) {
53
+ totalIn += row.inputTokens;
54
+ totalOut += row.outputTokens;
55
+ totalUsd += row.usd;
56
+ }
57
+ return {
58
+ heading: input.heading,
59
+ rows,
60
+ totalInputTokens: totalIn,
61
+ totalOutputTokens: totalOut,
62
+ totalUsd,
63
+ tier: input.tier,
64
+ };
65
+ }
66
+ /**
67
+ * Plain-string renderer for non-TTY / `--json` callers. Produces the
68
+ * same table the Ink component would render — no ANSI / no color so it
69
+ * pipes cleanly into `less` or a JSON tool.
70
+ */
71
+ export function renderCostTableText(view) {
72
+ const lines = [];
73
+ lines.push(view.heading);
74
+ lines.push('════════════════════════════════════════════════');
75
+ lines.push('');
76
+ lines.push(pad('MODEL', COL_MODEL) +
77
+ padLeft('IN_TOK', COL_IN) +
78
+ padLeft('OUT_TOK', COL_OUT) +
79
+ padLeft('$ EST', COL_USD));
80
+ if (view.rows.length === 0) {
81
+ lines.push('No calls recorded yet — brief a persona to charge the meter.');
82
+ }
83
+ else {
84
+ for (const row of view.rows) {
85
+ const noteSuffix = row.note ? ` (${row.note})` : '';
86
+ lines.push(pad(row.model, COL_MODEL) +
87
+ padLeft(formatTokensWithCommas(row.inputTokens), COL_IN) +
88
+ padLeft(formatTokensWithCommas(row.outputTokens), COL_OUT) +
89
+ padLeft(formatUsd(row.usd), COL_USD) +
90
+ noteSuffix);
91
+ }
92
+ }
93
+ lines.push('');
94
+ lines.push(`Total tokens: ${formatTokensWithCommas(view.totalInputTokens + view.totalOutputTokens)} (in: ${formatTokensWithCommas(view.totalInputTokens)}, out: ${formatTokensWithCommas(view.totalOutputTokens)})`);
95
+ lines.push(`Total dollar estimate: ${formatUsd(view.totalUsd)}`);
96
+ if (view.tier) {
97
+ lines.push(`Tier: ${view.tier.tier}${view.tier.quotaLine ? ` (${view.tier.quotaLine})` : ''}`);
98
+ }
99
+ return lines.join('\n');
100
+ }
101
+ function pad(value, width) {
102
+ if (value.length >= width)
103
+ return value.slice(0, Math.max(0, width - 1)) + ' ';
104
+ return value + ' '.repeat(width - value.length);
105
+ }
106
+ function padLeft(value, width) {
107
+ if (value.length >= width)
108
+ return value.slice(0, width);
109
+ return ' '.repeat(width - value.length) + value;
110
+ }
111
+ //# sourceMappingURL=cost-table.js.map
@@ -5,7 +5,7 @@ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧',
5
5
  const ACCENT_CYAN = 'cyan';
6
6
  const ACCENT_RED = 'red';
7
7
  /**
8
- * P2-1 (triple-review 2026-05-24): both `userCode` and
8
+ * P2-1 (triple-review): both `userCode` and
9
9
  * `verificationUrl` originate from a server response and would render
10
10
  * verbatim into Ink `<Text>`. A hostile or compromised server could
11
11
  * embed ANSI escape sequences (clear screen, cursor moves, hyperlink
@@ -108,7 +108,7 @@ export function DeviceFlow(props) {
108
108
  * cabinet page.
109
109
  */
110
110
  function CodeChip({ code }) {
111
- return (_jsx(Box, { marginTop: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: ACCENT_CYAN, children: ` ${code} ` }) }));
111
+ return (_jsx(Box, { marginTop: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: ACCENT_CYAN, children: ` ${code} ` }) }));
112
112
  }
113
113
  function BrowserHintRow({ opened, url, copied, }) {
114
114
  if (copied) {
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { useTheme } from '../core/theme/context.js';
4
+ /**
5
+ * status colors flow through the active theme
6
+ * instead of being hard-coded Ink color names. The `colorblind`
7
+ * preset re-maps OK/WARN/ERROR to a blue-yellow-magenta axis so
8
+ * deuteranopia operators can still distinguish the three states; the
9
+ * `default` / `dark` / `light` themes route to green/yellow/red
10
+ * variants tuned for each background. `skipped` keeps a muted color
11
+ * (theme-controlled muted token) so dimmed rows still pick up the
12
+ * theme tint.
13
+ */
14
+ function buildStatusColor(theme) {
15
+ return {
16
+ ok: theme.success,
17
+ warn: theme.warning,
18
+ error: theme.error,
19
+ skipped: theme.muted,
20
+ };
21
+ }
22
+ function buildOverallColor(theme) {
23
+ return {
24
+ healthy: theme.success,
25
+ warning: theme.warning,
26
+ error: theme.error,
27
+ };
28
+ }
29
+ export function DoctorTable({ envelope }) {
30
+ const theme = useTheme();
31
+ const statusColor = buildStatusColor(theme);
32
+ const overallColor = buildOverallColor(theme);
33
+ const nameWidth = Math.max('NAME'.length, ...envelope.probes.map((row) => row.name.length));
34
+ const statusWidth = Math.max('STATUS'.length, ...envelope.probes.map((row) => row.status.length));
35
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Pugi Doctor" }) }), envelope.probes.map((row) => (_jsx(DoctorRow, { row: row, nameWidth: nameWidth, statusWidth: statusWidth, statusColor: statusColor }, row.name))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [envelope.counts.error, " error(s), ", envelope.counts.warn, " warning(s), ", envelope.counts.ok, " ok,", ' ', envelope.counts.skipped, " skipped. Overall:", ' ', _jsx(Text, { color: overallColor[envelope.overall], bold: true, children: envelope.overall.toUpperCase() })] }) }), _jsx(Box, { children: _jsxs(Text, { dimColor: true, children: ["CLI ", envelope.meta.cliVersion, " Node ", envelope.meta.nodeVersion, " cwd ", envelope.meta.cwd] }) })] }));
36
+ }
37
+ function DoctorRow({ row, nameWidth, statusWidth, statusColor }) {
38
+ const namePart = row.name.padEnd(nameWidth, ' ');
39
+ const statusPart = row.status.toUpperCase().padEnd(statusWidth, ' ');
40
+ const latencyPart = typeof row.latencyMs === 'number' ? ` (${row.latencyMs}ms)` : '';
41
+ const showRemediation = typeof row.remediation === 'string' &&
42
+ row.remediation.length > 0 &&
43
+ (row.status === 'warn' || row.status === 'error');
44
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { children: [namePart, " "] }), _jsx(Text, { color: statusColor[row.status], bold: true, children: statusPart }), _jsxs(Text, { children: [' ', row.detail, latencyPart] })] }), showRemediation && (_jsx(Box, { marginLeft: nameWidth + statusWidth + 4, children: _jsxs(Text, { dimColor: true, children: ["\u2192 ", row.remediation] }) }))] }));
45
+ }
46
+ //# sourceMappingURL=doctor-table.js.map
@@ -0,0 +1,156 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `/feedback` interactive prompt — .
4
+ *
5
+ * Five-step wizard mounted from both the top-level `pugi feedback`
6
+ * shell handler and the in-REPL `/feedback` slash. Steps:
7
+ *
8
+ * 1. category — bug / feature / general / praise (numeric pick 1-4)
9
+ * 2. rating — 1-5 stars (numeric pick 1-5)
10
+ * 3. comment — free-text, multi-line, Ctrl-D submits, Esc cancels
11
+ * 4. context — y/n include last 5 turns (redacted)? default no
12
+ * 5. confirm — y/n submit?
13
+ *
14
+ * Brand voice gate: ASCII glyphs only, no em-dashes, no banned brand
15
+ * words. Copy is intentionally power-word neutral so the future i18n
16
+ * landing localises cleanly.
17
+ *
18
+ * The component is PURE in the Ink sense — props in, one terminal
19
+ * `onResolve` event out. The caller owns the submit + queue logic.
20
+ *
21
+ * The verdict the operator submits is:
22
+ * - `{ cancelled: true }` when the operator presses Esc at any step
23
+ * - `{ cancelled: false, draft }` when the wizard completes
24
+ *
25
+ * `draft` is the assembled `FeedbackDraft` — NOT yet a full envelope
26
+ * (the caller still injects `ts` + `cliVersion` + maybe `tier`).
27
+ */
28
+ import { useState } from 'react';
29
+ import { Box, Text, render, useApp, useInput } from 'ink';
30
+ const CATEGORIES = [
31
+ { value: 'bug', label: 'bug', gloss: 'something broke or behaves unexpectedly' },
32
+ { value: 'feature', label: 'feature', gloss: 'request a new capability' },
33
+ { value: 'general', label: 'general', gloss: 'observation, idea, comment' },
34
+ { value: 'praise', label: 'praise', gloss: 'positive note for the team' },
35
+ ];
36
+ export function FeedbackPrompt(props) {
37
+ const [step, setStep] = useState('category');
38
+ const [category, setCategory] = useState(null);
39
+ const [rating, setRating] = useState(null);
40
+ const [commentBuffer, setCommentBuffer] = useState('');
41
+ const [includeContext, setIncludeContext] = useState(false);
42
+ useInput((input, key) => {
43
+ if (key.escape) {
44
+ props.onResolve({ cancelled: true });
45
+ return;
46
+ }
47
+ if (step === 'category') {
48
+ const n = Number.parseInt(input, 10);
49
+ if (!Number.isNaN(n) && n >= 1 && n <= CATEGORIES.length) {
50
+ const picked = CATEGORIES[n - 1];
51
+ if (picked) {
52
+ setCategory(picked.value);
53
+ setStep('rating');
54
+ }
55
+ }
56
+ return;
57
+ }
58
+ if (step === 'rating') {
59
+ const n = Number.parseInt(input, 10);
60
+ if (!Number.isNaN(n) && n >= 1 && n <= 5) {
61
+ setRating(n);
62
+ setStep('comment');
63
+ }
64
+ return;
65
+ }
66
+ if (step === 'comment') {
67
+ // Ctrl-D submits the comment (even when empty — rating-only
68
+ // feedback is allowed). Enter inserts a newline so the
69
+ // operator can write multi-line bug repros without the wizard
70
+ // ending the buffer prematurely.
71
+ if (key.ctrl && (input === 'd' || input === '')) {
72
+ setStep('context');
73
+ return;
74
+ }
75
+ if (key.return) {
76
+ setCommentBuffer((prev) => prev + '\n');
77
+ return;
78
+ }
79
+ if (key.backspace || key.delete) {
80
+ setCommentBuffer((prev) => prev.slice(0, -1));
81
+ return;
82
+ }
83
+ // Printable characters land in the buffer. We accept anything
84
+ // non-empty + non-control. Ink's useInput sometimes delivers
85
+ // multi-char paste bursts as one `input` — we append the whole
86
+ // burst so paste lands intact.
87
+ if (input && !key.ctrl && !key.meta) {
88
+ setCommentBuffer((prev) => prev + input);
89
+ }
90
+ return;
91
+ }
92
+ if (step === 'context') {
93
+ if (input === 'y' || input === 'Y') {
94
+ setIncludeContext(true);
95
+ setStep('confirm');
96
+ return;
97
+ }
98
+ if (input === 'n' || input === 'N' || key.return) {
99
+ // Default no — Enter at the context prompt picks "no" so
100
+ // the operator can blast through with all defaults.
101
+ setIncludeContext(false);
102
+ setStep('confirm');
103
+ return;
104
+ }
105
+ return;
106
+ }
107
+ if (step === 'confirm') {
108
+ if (input === 'y' || input === 'Y' || key.return) {
109
+ if (category != null && rating != null) {
110
+ props.onResolve({
111
+ cancelled: false,
112
+ draft: {
113
+ category,
114
+ rating,
115
+ comment: commentBuffer,
116
+ includeSessionContext: includeContext,
117
+ },
118
+ });
119
+ }
120
+ return;
121
+ }
122
+ if (input === 'n' || input === 'N') {
123
+ props.onResolve({ cancelled: true });
124
+ return;
125
+ }
126
+ return;
127
+ }
128
+ }, { isActive: !props.inert });
129
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Pugi /feedback" }), _jsx(Text, { children: " \u2014 share what you saw. Esc cancels at any step." })] }), step === 'category' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "1. Category" }), CATEGORIES.map((c, idx) => (_jsx(Text, { children: ` ${idx + 1}. ${c.label.padEnd(8)} ${c.gloss}` }, c.value))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-4." }) })] })), step === 'rating' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "2. Rating" }), _jsx(Text, { children: ` 1=poor, 5=excellent. Picked category: ${category ?? '?'}` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Pick 1-5." }) })] })), step === 'comment' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "3. Comment" }), _jsx(Text, { dimColor: true, children: "Multi-line. Enter inserts a newline. Ctrl-D submits." }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: commentBuffer.length === 0 ? (_jsx(Text, { dimColor: true, children: "(empty \u2014 Ctrl-D submits without a comment)" })) : (commentBuffer.split('\n').map((line, idx) => (_jsx(Text, { children: `> ${line}` }, idx)))) })] })), step === 'context' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "4. Include session context?" }), _jsx(Text, { children: ' Last 5 turns, redacted (tokens / *_KEY=* values stripped).' }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default n (Enter)." }) })] })), step === 'confirm' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "5. Submit?" }), _jsx(Text, { children: ` category=${category ?? '?'} rating=${rating ?? '?'} context=${includeContext ? 'yes' : 'no'}` }), _jsx(Text, { children: ` comment=${commentBuffer.length} chars` }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "y / n \u2014 default y (Enter)." }) })] }))] }));
130
+ }
131
+ export async function renderFeedbackPrompt() {
132
+ let resolveOuter;
133
+ const outerPromise = new Promise((resolve) => {
134
+ resolveOuter = resolve;
135
+ });
136
+ function App() {
137
+ const { exit } = useApp();
138
+ return (_jsx(FeedbackPrompt, { onResolve: (verdict) => {
139
+ resolveOuter(verdict);
140
+ // Mirror renderAskCli: tiny delay so Ink flushes unmount
141
+ // before the caller prints the toast line.
142
+ setTimeout(() => exit(), 16);
143
+ } }));
144
+ }
145
+ const instance = render(_jsx(App, {}));
146
+ const verdict = await outerPromise;
147
+ try {
148
+ await instance.waitUntilExit();
149
+ }
150
+ catch {
151
+ // Ink can throw when exit() races with a re-render — the verdict
152
+ // is already captured so we ignore.
153
+ }
154
+ return verdict;
155
+ }
156
+ //# sourceMappingURL=feedback-prompt.js.map