@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,10 +1,10 @@
1
1
  /**
2
- * REPL slash command registry - Sprint α5.7, expanded α6.14 wave 2.
2
+ * REPL slash command registry - Sprint , expanded wave 2.
3
3
  *
4
4
  * The REPL input box surfaces a palette of slash commands the operator
5
5
  * can run from inside a persistent session. The wave-2 expansion (CEO
6
- * 2026-05-25) grows the surface from 6 to 20 commands so the `/help`
7
- * overlay matches the breadth Claude Code / Codex CLI operators expect.
6
+ *) grows the surface from 6 to 20 commands so the `/help`
7
+ * overlay matches the breadth the upstream tool / peer CLI operators expect.
8
8
  *
9
9
  * The registry is pure: each `parseSlashCommand` call returns a
10
10
  * `SlashCommandResult` describing what the REPL session should do next.
@@ -15,20 +15,21 @@
15
15
  *
16
16
  * Tiering (per CEO wave-2 spec):
17
17
  *
18
- * Tier 1 - wired against real state (3 + existing 6 = 9 wired):
19
- * brief, agents, stop, help, quit, web, clear, version, jobs.
18
+ * Tier 1 - wired against real state (3 + existing 6 = 9 wired):
19
+ * brief, agents, stop, help, quit, web, clear, version, jobs.
20
20
  *
21
- * Tier 2 - best-effort wiring against existing surfaces (3):
22
- * diff, cost, status.
21
+ * Tier 2 - best-effort wiring against existing surfaces (3):
22
+ * diff, cost, status.
23
23
  *
24
- * Tier 3 - deterministic stubs ("coming in αX.Y") (8):
25
- * compact, resume, memory, config, privacy, budget, mcp, undo.
24
+ * Tier 3 - deterministic stubs ("coming in αX.Y") (8):
25
+ * compact, resume, memory, config, privacy, budget, mcp, undo.
26
26
  *
27
27
  * Brand voice (brandbook §08): power words `brief / dispatch / stop /
28
28
  * agents / quit / shipped`. Tagline `Brief it. It ships.` reserved for
29
29
  * `/quit` confirmation and `/help` footer - never inline.
30
30
  */
31
31
  import { listRoles } from '../agents/registry.js';
32
+ import { PERMISSION_MODES, parsePermissionMode, } from '../permissions/index.js';
32
33
  /**
33
34
  * Deterministic stub copy returned by the Tier 3 commands. Spec'd
34
35
  * inline so the unit test can pin the exact text without poking at
@@ -38,44 +39,76 @@ import { listRoles } from '../agents/registry.js';
38
39
  * silently appear here with empty placeholders.
39
40
  */
40
41
  export const SLASH_STUB_MESSAGES = Object.freeze({
41
- compact: 'Manual context compaction lands in α6.5b.',
42
- memory: 'Session memory editor lands in α6.5b.',
43
- config: 'Run `pugi config list` from a fresh shell for the full surface; in-REPL editor lands in α6.5.',
42
+ // /compact graduated from stub. The session
43
+ // module now owns the summariser round-trip + boundary marker append
44
+ // via `dispatchCompact`. Keep the type record exhaustive so a future
45
+ // stub addition cannot silently overlap the wired set.
46
+ memory: 'Session memory editor lands in .',
47
+ config: 'Run `pugi config list` from a fresh shell for the full surface; in-REPL editor lands in .',
44
48
  // alpha 6.13: /privacy graduated from stub; nothing reads this at
45
49
  // runtime but the type record stays exhaustive.
46
50
  privacy: '',
47
- budget: 'Run `pugi budget` from a fresh shell; in-REPL summary lands in α6.5.',
48
- mcp: 'Run `pugi config mcp list` from a fresh shell; in-REPL palette lands in α6.5.',
49
- undo: 'Run `pugi undo` from a fresh shell; in-REPL undo lands in α6.5.',
51
+ budget: 'Run `pugi budget` from a fresh shell; in-REPL summary lands in .',
52
+ // β4 Sl7 : /mcp graduated from stub to a real handler
53
+ // that forwards to `runMcpCommand`. Stub message removed from the
54
+ // exhaustive record so the type narrows correctly.
55
+ //
56
+ // final : /undo graduated from stub to a real
57
+ // handler that forwards to `runUndoCommand` (Aider walk-back —
58
+ // single-step revert of the last mutating tool result, with
59
+ // mtime+hash external-modification gate). Stub message removed.
50
60
  });
51
61
  export const SLASH_COMMAND_HELP = Object.freeze([
52
62
  // Workforce dispatch
53
63
  { name: 'brief', args: '<text>', gloss: 'Dispatch a brief to the workforce', group: 'Workforce dispatch' },
54
64
  { name: 'agents', args: '', gloss: 'List the on-watch agent roster', group: 'Workforce dispatch' },
65
+ { name: 'delegate', args: '<slug> <brief>', gloss: 'Dispatch a brief to one Tier 1 specialist ', group: 'Workforce dispatch' },
55
66
  { name: 'stop', args: '<persona>', gloss: 'Stop one agent by persona slug', group: 'Workforce dispatch' },
56
- { name: 'jobs', args: '', gloss: 'List background jobs', group: 'Workforce dispatch' },
57
- { name: 'ask', args: '<question>', gloss: 'Surface a yes/no modal locally (α6.3 forcing question)', group: 'Workforce dispatch' },
67
+ { name: 'jobs', args: '[--watch]', gloss: 'List background jobs + agent-progress; --watch mounts the live Ink TUI', group: 'Workforce dispatch' },
68
+ { name: 'cancel', args: '[<id> | all]', gloss: 'Halt active dispatch by id ', group: 'Workforce dispatch' },
69
+ { name: 'ask', args: '<question>', gloss: 'Surface a yes/no modal locally ', group: 'Workforce dispatch' },
58
70
  // Session
59
71
  { name: 'clear', args: '', gloss: 'Clear conversation pane', group: 'Session' },
60
72
  { name: 'resume', args: '', gloss: 'Pick a stored session to restore', group: 'Session' },
61
73
  { name: 'context', args: '', gloss: 'Show three-tier context summary (Tier 0 skeleton + Tier 1 working set)', group: 'Session' },
62
- { name: 'compact', args: '', gloss: 'Manual context compaction (α6.5b)', group: 'Session', stub: true },
63
- { name: 'memory', args: '', gloss: 'Session memory editor (α6.5b)', group: 'Session', stub: true },
74
+ { name: 'compact', args: '[--force]', gloss: 'Summarise older turns into a boundary marker . --force bypasses the noop-empty guard', group: 'Session' },
75
+ { name: 'rewind', args: '[N | --to <id>]', gloss: 'Roll the conversation back to a checkpoint ', group: 'Session' },
76
+ { name: 'memory', args: '', gloss: 'Session memory editor ', group: 'Session', stub: true },
77
+ { name: 'init', args: '', gloss: 'Scaffold .pugi/ in the current workspace (β1 Sl11)', group: 'Session' },
64
78
  // Pugi tools
65
79
  { name: 'web', args: '<url>', gloss: 'Fetch a URL into context', group: 'Pugi tools' },
66
80
  { name: 'diff', args: '', gloss: 'Show pending diff', group: 'Pugi tools' },
67
- { name: 'cost', args: '', gloss: 'Token usage + budget', group: 'Pugi tools' },
68
- { name: 'status', args: '', gloss: 'Backend + tenant status', group: 'Pugi tools' },
81
+ { name: 'cost', args: '', gloss: 'Session token + USD totals + last 5 turn breakdown', group: 'Pugi tools' },
82
+ { name: 'quota', args: '', gloss: 'Plan tier + monthly usage caps (sync / review / engine)', group: 'Pugi tools' },
83
+ { name: 'status', args: '', gloss: 'Session snapshot — id · cwd · mode · tokens · dispatches · auth', group: 'Pugi tools' },
69
84
  { name: 'consensus', args: '[ref]', gloss: '3-model consensus review (codex · claude · deepseek)', group: 'Pugi tools' },
85
+ { name: 'repo-map', args: '[refresh]', gloss: 'AST-light symbol summary of the workspace ', group: 'Pugi tools' },
86
+ { name: 'codegraph-status', args: '[--install|--reindex|--offer]', gloss: 'Codegraph MCP — install state, index age, symbol count, refresh CTA ', group: 'Pugi tools' },
70
87
  // Settings
71
88
  { name: 'config', args: '', gloss: 'Show config', group: 'Settings', stub: true },
72
89
  { name: 'privacy', args: '', gloss: 'Show privacy mode + contract', group: 'Settings' },
90
+ { name: 'permissions', args: '[mode] [--persist]', gloss: 'Show or flip permission mode (default / acceptEdits / plan / auto / dontAsk / bypassPermissions) (also: /plan, Shift+Tab cycle)', group: 'Settings' },
91
+ { name: 'plan', args: '[--back | --persist] [<prompt>]', gloss: 'Switch to plan mode (read-only). Same as /permissions plan, slicker UX.', group: 'Settings' },
92
+ { name: 'model', args: '[<slug>]', gloss: 'Show or select the active model. Bare /model lists tier-gated options', group: 'Settings' },
73
93
  { name: 'budget', args: '', gloss: 'Show usage budget', group: 'Settings', stub: true },
74
- { name: 'mcp', args: '', gloss: 'List MCP servers', group: 'Settings', stub: true },
75
- { name: 'undo', args: '', gloss: 'Undo last write', group: 'Settings', stub: true },
94
+ { name: 'mcp', args: '[sub]', gloss: 'MCP servers — list / trust / deny / install / serve / perms', group: 'Settings' },
95
+ { name: 'style', args: '[name] [--persist|--reset|--list]', gloss: 'Output-style preset (default / terse / explanatory / russian-formal / casual)', group: 'Settings' },
96
+ { name: 'theme', args: '[name] [--persist|--reset|--list]', gloss: 'TUI color palette (default / dark / light / colorblind)', group: 'Settings' },
97
+ { name: 'onboarding', args: '[--reset|--non-interactive]', gloss: 'First-run wizard — auth / mode / style / MCP / telemetry ', group: 'Settings' },
98
+ { name: 'vim', args: '[on|off|status]', gloss: 'Toggle vim-style modal editing in the input buffer ', group: 'Settings' },
99
+ { name: 'undo', args: '', gloss: 'Revert the last successful write / edit / multi_edit (Aider walk-back, )', group: 'Settings' },
100
+ { name: 'redo', args: '', gloss: 'Reapply the most recent /undo (LIFO stack, cleanup)', group: 'Settings' },
76
101
  // Meta
77
102
  { name: 'help', args: '', gloss: 'Show this help overlay', group: 'Meta' },
78
103
  { name: 'version', args: '', gloss: 'Show CLI version', group: 'Meta' },
104
+ { name: 'doctor', args: '', gloss: 'Environment health report (auth · API · Node · disk · MCP · …)', group: 'Meta' },
105
+ { name: 'prd-check', args: '<prd-path | --all | --session> [--json]', gloss: 'Verify PRD against code (default) or session work (--session, final)', group: 'Meta' },
106
+ { name: 'chain', args: '<new|status|next|show|export|list> [...args]', gloss: 'Artifact chain — PRD → ADR → mindmap → ER → sequence → tests → code ', group: 'Meta' },
107
+ { name: 'stickers', args: '', gloss: 'show Pugi brand stickers (gimmick)', group: 'Meta' },
108
+ { name: 'feedback', args: '', gloss: 'file a bug / feature / general comment without leaving the REPL', group: 'Meta' },
109
+ { name: 'share', args: '[--gist|--pugi] [--redact] [--preview]', gloss: 'Export session transcript to gist / pugi.io ', group: 'Meta' },
110
+ { name: 'release-notes', args: '[--reset]', gloss: 'Show changelog diff since last upgrade ', group: 'Meta' },
111
+ { name: 'update', args: '[--check|--apply [--yes]] [--channel <name>]', gloss: 'Check for / apply CLI update on stable / beta / canary ', group: 'Meta' },
79
112
  { name: 'quit', args: '', gloss: 'Exit the REPL', group: 'Meta' },
80
113
  ]);
81
114
  /**
@@ -92,20 +125,88 @@ export const SLASH_COMMAND_GROUPS = Object.freeze([
92
125
  /**
93
126
  * Parse one line of input from the REPL. The contract:
94
127
  *
95
- * - Empty / whitespace-only input returns `noop` with the original
96
- * text so the REPL can ignore it without printing anything.
97
- * - Input that does not start with `/` is treated as an implicit
98
- * `/brief <text>` - the most-common operator action.
99
- * - `/<name> [args]` resolves the name against the registry; unknown
100
- * names return `error` so the REPL can render a one-line tip
101
- * instead of silently dropping the input.
102
- * - Tier 3 stubs return `{ kind: 'stub', name, message }` so the REPL
103
- * can render the deterministic "coming in αX.Y" copy uniformly.
128
+ * - Empty / whitespace-only input returns `noop` with the original
129
+ * text so the REPL can ignore it without printing anything.
130
+ * - Input that does not start with `/` is treated as an implicit
131
+ * `/brief <text>` - the most-common operator action.
132
+ * - `/<name> [args]` resolves the name against the registry; unknown
133
+ * names return `error` so the REPL can render a one-line tip
134
+ * instead of silently dropping the input.
135
+ * - Tier 3 stubs return `{ kind: 'stub', name, message }` so the REPL
136
+ * can render the deterministic "coming in αX.Y" copy uniformly.
104
137
  *
105
138
  * The function never throws. Bad input maps to a structured result the
106
139
  * REPL can render - the alternative (throwing from a keystroke handler)
107
140
  * would unmount Ink mid-frame.
108
141
  */
142
+ /**
143
+ * Pugi backlog : parse the artifact-store subcommands
144
+ * of `/plan`. Kept module-local because the legacy mode-toggle parser
145
+ * already owns the `case 'plan':` branch — this helper is the
146
+ * dispatch tail when the first arg is `show / list / diff / prune`.
147
+ *
148
+ * Argument grammar:
149
+ * /plan show <planId>
150
+ * /plan list [<taskId>]
151
+ * /plan diff <planId> [<otherId>]
152
+ * /plan prune [<maxAgeDays>]
153
+ *
154
+ * Validation:
155
+ * - `planId` is required for show + diff; surfaced as a structured
156
+ * error verdict when missing so the REPL renders one line of
157
+ * guidance instead of falling through to the mode-toggle path.
158
+ * - `maxAgeDays` is parsed as a positive integer; missing leaves the
159
+ * core module's default in place. Negative / NaN inputs become
160
+ * errors so the operator does not silently wipe their plan store.
161
+ */
162
+ function parsePlanArtifactSubcommand(op, rest) {
163
+ if (op === 'show') {
164
+ const planId = rest[0];
165
+ if (!planId) {
166
+ return {
167
+ kind: 'error',
168
+ message: '/plan show <plan-id> — plan-id is required.',
169
+ };
170
+ }
171
+ return { kind: 'plan-artifact', sub: { op: 'show', planId } };
172
+ }
173
+ if (op === 'list') {
174
+ const taskId = rest[0];
175
+ return {
176
+ kind: 'plan-artifact',
177
+ sub: taskId ? { op: 'list', taskId } : { op: 'list' },
178
+ };
179
+ }
180
+ if (op === 'diff') {
181
+ const planId = rest[0];
182
+ if (!planId) {
183
+ return {
184
+ kind: 'error',
185
+ message: '/plan diff <plan-id> [<other-id>] — plan-id is required.',
186
+ };
187
+ }
188
+ const otherId = rest[1];
189
+ return {
190
+ kind: 'plan-artifact',
191
+ sub: otherId
192
+ ? { op: 'diff', planId, otherId }
193
+ : { op: 'diff', planId },
194
+ };
195
+ }
196
+ // prune
197
+ const raw = rest[0];
198
+ if (raw === undefined) {
199
+ return { kind: 'plan-artifact', sub: { op: 'prune' } };
200
+ }
201
+ const parsed = Number(raw);
202
+ if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
203
+ return {
204
+ kind: 'error',
205
+ message: `/plan prune [<days>] — expected positive integer, got '${raw}'.`,
206
+ };
207
+ }
208
+ return { kind: 'plan-artifact', sub: { op: 'prune', maxAgeDays: parsed } };
209
+ }
109
210
  export function parseSlashCommand(input) {
110
211
  const trimmed = input.trim();
111
212
  if (trimmed.length === 0) {
@@ -135,6 +236,38 @@ export function parseSlashCommand(input) {
135
236
  case 'roster': {
136
237
  return { kind: 'roster' };
137
238
  }
239
+ case 'delegate': {
240
+ // tail must start with the persona slug followed by the brief.
241
+ // Slug accepts only the closed-set lowercase ASCII pattern the
242
+ // server-side persona registry enforces; anything else surfaces
243
+ // as a usage error so the operator sees the typo before the
244
+ // round-trip.
245
+ const innerSpace = tail.indexOf(' ');
246
+ if (innerSpace === -1 || innerSpace === 0) {
247
+ return {
248
+ kind: 'error',
249
+ message: 'Usage: /delegate <slug> <one-sentence brief>',
250
+ };
251
+ }
252
+ const persona = tail.slice(0, innerSpace).toLowerCase();
253
+ const brief = tail.slice(innerSpace + 1).trim();
254
+ // Pattern intentionally mirrors server-side PUGI_DELEGATE_REGEX in
255
+ // sessions.controller.ts (^[a-z]+$). Keeping them lockstep means
256
+ // the REPL surfaces typos locally instead of round-tripping a 4xx.
257
+ if (!/^[a-z]+$/.test(persona)) {
258
+ return {
259
+ kind: 'error',
260
+ message: `/delegate slug must be lowercase ASCII (a-z only); got '${persona}'`,
261
+ };
262
+ }
263
+ if (brief.length === 0) {
264
+ return {
265
+ kind: 'error',
266
+ message: 'Usage: /delegate <slug> <one-sentence brief>',
267
+ };
268
+ }
269
+ return { kind: 'delegate', persona, brief };
270
+ }
138
271
  case 'stop':
139
272
  case 'kill': {
140
273
  if (tail.length === 0) {
@@ -170,7 +303,87 @@ export function parseSlashCommand(input) {
170
303
  return { kind: 'version' };
171
304
  }
172
305
  case 'jobs': {
173
- return { kind: 'jobs' };
306
+ // cleanup : tokenise the tail so the slash
307
+ // can route `--watch` к the live Ink TUI (same renderer as
308
+ // `pugi jobs --watch`). Unknown tokens fall through silently —
309
+ // the slash surface is intentionally minimal vs. the shell
310
+ // command (which supports list/status/tail/kill subcommands
311
+ // through `runJobsCommand`).
312
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
313
+ const watch = tokens.includes('--watch') || tokens.includes('-w') || tokens[0] === 'watch';
314
+ return { kind: 'jobs', watch };
315
+ }
316
+ case 'cancel':
317
+ case 'halt': {
318
+ // small-CC-parity batch :
319
+ //
320
+ // /cancel -> list active dispatches
321
+ // /cancel all -> halt every running dispatch
322
+ // /cancel <id> -> halt one (id may be a prefix; runner
323
+ // does startsWith lookup)
324
+ //
325
+ // The `halt` alias matches operator muscle memory from systemd /
326
+ // brand voice (`stop` is already taken by /stop <persona>; cancel
327
+ // is dispatch-id-keyed, stop is persona-keyed). Unknown extra
328
+ // tokens are tolerated — the runner reads only the first.
329
+ const trimmedTail = tail.trim();
330
+ if (trimmedTail.length === 0) {
331
+ return { kind: 'cancel', mode: 'list', dispatchId: '' };
332
+ }
333
+ const firstToken = trimmedTail.split(/\s+/)[0].toLowerCase();
334
+ if (firstToken === 'all' || firstToken === '*') {
335
+ return { kind: 'cancel', mode: 'all', dispatchId: 'all' };
336
+ }
337
+ // Defensive: dispatch ids are filename-safe per the
338
+ // `validateAgentProgress` agentId regex (`[a-zA-Z0-9_-]+`).
339
+ // Reject anything outside that range with a usage tip so the
340
+ // operator sees the typo before the round-trip.
341
+ if (!/^[A-Za-z0-9_-]+$/.test(firstToken)) {
342
+ return {
343
+ kind: 'error',
344
+ message: `/cancel: invalid dispatch id '${firstToken}'. Use letters / digits / '-' / '_' only.`,
345
+ };
346
+ }
347
+ return { kind: 'cancel', mode: 'one', dispatchId: firstToken };
348
+ }
349
+ case 'servers': {
350
+ // PR H (2026-06-05): operator-facing server kill. Customer
351
+ // can't terminate `server_start`-launched processes from inside
352
+ // the REPL — `/stop <persona>` halts a persona agent, not the
353
+ // server. Surface the existing `.pugi/runs/srv-*/server.json`
354
+ // meta as a first-class list + dispatch к `dispatchServerStop`.
355
+ //
356
+ // /servers → list
357
+ // /servers stop <runId|pid|all>→ kill one or all
358
+ const trimmed = tail.trim();
359
+ if (trimmed.length === 0) {
360
+ return { kind: 'servers', mode: 'list', target: '' };
361
+ }
362
+ const tokens = trimmed.split(/\s+/);
363
+ const verb = tokens[0].toLowerCase();
364
+ if (verb !== 'stop' && verb !== 'kill') {
365
+ return {
366
+ kind: 'error',
367
+ message: `Usage: /servers [stop <runId|pid|all>]`,
368
+ };
369
+ }
370
+ const target = tokens[1]?.toLowerCase() ?? '';
371
+ if (target.length === 0) {
372
+ return {
373
+ kind: 'error',
374
+ message: `Usage: /servers stop <runId|pid|all>`,
375
+ };
376
+ }
377
+ // Defensive: validate target shape. Run ids look like `srv-<uuid>`,
378
+ // pids are positive integers, `all` is the literal token. Reject
379
+ // anything else with the usage tip.
380
+ if (target !== 'all' && !/^srv-[\w-]+$/.test(target) && !/^\d+$/.test(target)) {
381
+ return {
382
+ kind: 'error',
383
+ message: `/servers stop: target must be 'all', a run id (srv-...), or a numeric pid; got '${target}'.`,
384
+ };
385
+ }
386
+ return { kind: 'servers', mode: 'stop', target };
174
387
  }
175
388
  case 'ask': {
176
389
  if (tail.length === 0) {
@@ -181,9 +394,19 @@ export function parseSlashCommand(input) {
181
394
  case 'diff': {
182
395
  return { kind: 'diff' };
183
396
  }
184
- case 'cost': {
397
+ case 'cost':
398
+ case 'usage': {
399
+ // L19 : `/usage` is an alias of `/cost` per the cost-
400
+ // command spec. The previous mapping routed `/usage` to the
401
+ // network-backed `/quota` surface, but operators trained on Claude
402
+ // Code expect `/usage` to surface the per-model token breakdown
403
+ // (same shape as `/cost`). `/quota` remains the canonical name
404
+ // for the tier + monthly-cap fetch.
185
405
  return { kind: 'cost' };
186
406
  }
407
+ case 'quota': {
408
+ return { kind: 'quota' };
409
+ }
187
410
  case 'status': {
188
411
  return { kind: 'status' };
189
412
  }
@@ -195,14 +418,14 @@ export function parseSlashCommand(input) {
195
418
  return { kind: 'consensus', ref: tail };
196
419
  }
197
420
  case 'resume': {
198
- // α6.4: wired against the local SessionStore. The REPL session
421
+ // : wired against the local SessionStore. The REPL session
199
422
  // owns the picker UI (Ink select over the 10 most recent rows)
200
423
  // so the slash-command layer stays UI-agnostic.
201
424
  return { kind: 'resume' };
202
425
  }
203
426
  case 'context':
204
427
  case 'ctx': {
205
- // α6.5: surface Tier 0 + Tier 1 status. The session module
428
+ // : surface Tier 0 + Tier 1 status. The session module
206
429
  // renders the summary as system lines so the operator can see
207
430
  // skeleton size + working-set utilisation at a glance.
208
431
  return { kind: 'context' };
@@ -215,12 +438,321 @@ export function parseSlashCommand(input) {
215
438
  // device flow + audit identity are wired correctly).
216
439
  return { kind: 'privacy' };
217
440
  }
218
- case 'compact':
441
+ case 'permissions':
442
+ case 'perms': {
443
+ // : `/permissions [mode] [--persist] [--confirm]`.
444
+ //
445
+ // Argument grammar (single line, no quoting):
446
+ // /permissions -> show + table
447
+ // /permissions default|acceptEdits|plan|auto|dontAsk -> flip mode
448
+ // /permissions bypassPermissions --confirm -> flip to
449
+ // bypassPermissions (refused
450
+ // без --confirm — safety)
451
+ // /permissions <mode> --persist -> also write to ~/.pugi/config.json
452
+ //
453
+ // aliases (`ask`, `allow`, `bypass`) are accepted и mapped to
454
+ // their canonical names via `parsePermissionMode`.
455
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
456
+ if (tokens.length === 0) {
457
+ return { kind: 'permissions', persist: false, confirmBypass: false };
458
+ }
459
+ const headRaw = tokens[0] ?? '';
460
+ const mode = parsePermissionMode(headRaw);
461
+ if (!mode) {
462
+ const modeList = [...PERMISSION_MODES].join('|');
463
+ return {
464
+ kind: 'error',
465
+ message: `Usage: /permissions [${modeList}] [--persist] [--confirm]; unknown mode '${headRaw}'`,
466
+ };
467
+ }
468
+ const flags = tokens.slice(1);
469
+ let persist = false;
470
+ let confirmBypass = false;
471
+ for (const flag of flags) {
472
+ if (flag === '--persist') {
473
+ persist = true;
474
+ }
475
+ else if (flag === '--confirm') {
476
+ confirmBypass = true;
477
+ }
478
+ else {
479
+ return {
480
+ kind: 'error',
481
+ message: `/permissions: unknown flag '${flag}' (allowed: --persist, --confirm)`,
482
+ };
483
+ }
484
+ }
485
+ return { kind: 'permissions', mode, persist, confirmBypass };
486
+ }
487
+ case 'init': {
488
+ // β1 Sl11: surface the init flow inside the REPL. Tail args
489
+ // are ignored — the init handler is parameterless today; `pugi
490
+ // init --no-defaults` is the CLI surface for skipping bundled
491
+ // skills.
492
+ return { kind: 'init' };
493
+ }
494
+ case 'plan': {
495
+ // `/plan [--back | --persist] [<prompt>]`.
496
+ //
497
+ // Argument grammar (single line, no quoting):
498
+ // /plan -> enter plan mode + banner
499
+ // /plan --back -> restore previous mode
500
+ // /plan --persist -> enter + write global config
501
+ // /plan <prompt...> -> enter + run one-shot engine
502
+ // /plan --auto-back <prompt...> -> enter + run + restore mode
503
+ //
504
+ // The parser pulls the flags off the head of the tail; whatever
505
+ // remains is the prompt. `--back` + a non-empty prompt and
506
+ // `--back` + `--auto-back` are both refused as `error` because
507
+ // they conflict at the verb level.
508
+ //
509
+ // Pugi backlog : when the FIRST token of the
510
+ // tail is one of the artifact subcommands (`show / list / diff /
511
+ // prune`), the parser hands off to the plan-as-FILE surface
512
+ // instead of the mode-toggle. This keeps both feature sets under
513
+ // one `/plan` namespace without overloading the verb table.
514
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
515
+ const head = tokens[0];
516
+ if (head === 'show' || head === 'list' || head === 'diff' || head === 'prune') {
517
+ return parsePlanArtifactSubcommand(head, tokens.slice(1));
518
+ }
519
+ let back = false;
520
+ let persist = false;
521
+ let autoBack = false;
522
+ const promptTokens = [];
523
+ for (const token of tokens) {
524
+ if (token === '--back') {
525
+ back = true;
526
+ }
527
+ else if (token === '--persist') {
528
+ persist = true;
529
+ }
530
+ else if (token === '--auto-back') {
531
+ autoBack = true;
532
+ }
533
+ else {
534
+ promptTokens.push(token);
535
+ }
536
+ }
537
+ const prompt = promptTokens.join(' ');
538
+ if (back && prompt.length > 0) {
539
+ return {
540
+ kind: 'error',
541
+ message: '/plan --back does not accept a prompt; revert first, then dispatch.',
542
+ };
543
+ }
544
+ if (back && autoBack) {
545
+ return {
546
+ kind: 'error',
547
+ message: '/plan --back and --auto-back cannot be combined.',
548
+ };
549
+ }
550
+ return { kind: 'plan', back, persist, autoBack, prompt };
551
+ }
552
+ case 'model': {
553
+ // BT 8 (the upstream tool parity): `/model [<slug>]`. Bare form
554
+ // prints the tier-gated model menu + current selection; with a
555
+ // slug it flips workspace selection. Slug grammar (loose): alnum
556
+ // + dash + dot + slash. Anything outside that range becomes an
557
+ // error verdict so the operator sees a clear message instead of a
558
+ // silent no-op. Whitespace inside the tail = multiple tokens = we
559
+ // take the first; the help gloss documents single-slug usage.
560
+ const trimmedTail = tail.trim();
561
+ if (trimmedTail.length === 0) {
562
+ return { kind: 'model', slug: undefined };
563
+ }
564
+ const firstToken = trimmedTail.split(/\s+/)[0] ?? '';
565
+ if (!/^[A-Za-z0-9][A-Za-z0-9._\-\/]{0,63}$/.test(firstToken)) {
566
+ return {
567
+ kind: 'error',
568
+ message: `/model: invalid slug '${firstToken}'. Use letters / digits / '-' / '.' / '/' only.`,
569
+ };
570
+ }
571
+ return { kind: 'model', slug: firstToken };
572
+ }
573
+ case 'mcp': {
574
+ // β4 Sl7: tokenize the tail. Empty tail -> `list` (matches CLI).
575
+ // Quoting / shell-escapes are NOT supported — the slash surface is
576
+ // intentionally simple; complex installs (env vars, multi-word
577
+ // args) go through `pugi mcp install` from a fresh shell.
578
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
579
+ return { kind: 'mcp', args: tokens };
580
+ }
581
+ case 'style':
582
+ case 'output-style': {
583
+ // forward the tokenized tail unchanged so
584
+ // the slash + top-level CLI surfaces share one parser inside
585
+ // `runStyleCommand`. Quoting / multi-word args are not used (the
586
+ // preset slugs are single tokens by contract).
587
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
588
+ return { kind: 'style', args: tokens };
589
+ }
590
+ case 'theme':
591
+ case 'palette':
592
+ case 'colors': {
593
+ // forward the tokenized tail unchanged so
594
+ // the slash + top-level `pugi theme` surfaces share one parser
595
+ // inside `runThemeCommand`. Aliases `/palette` and `/colors`
596
+ // exist because the leak-landscape audit found operators reach
597
+ // for either word interchangeably — same surface, same handler.
598
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
599
+ return { kind: 'theme', args: tokens };
600
+ }
601
+ case 'onboarding':
602
+ case 'onboard':
603
+ case 'setup': {
604
+ // forward the tokenized tail unchanged.
605
+ // The slash always routes through the non-interactive snapshot
606
+ // path (the REPL already owns the Ink tree); the runner picks
607
+ // it up from `ctx.interactive = false` in the session
608
+ // dispatcher.
609
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
610
+ return { kind: 'onboarding', args: tokens };
611
+ }
612
+ case 'vim': {
613
+ // forward the tokenized tail unchanged so
614
+ // the slash + top-level CLI surfaces share one parser inside
615
+ // `runVimCommand`. Subcommands are single tokens (on / off /
616
+ // status); a bare `/vim` toggles.
617
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
618
+ return { kind: 'vim', args: tokens };
619
+ }
620
+ case 'doctor':
621
+ case 'health': {
622
+ // L17 : run the probe sweep inline. Tail is ignored —
623
+ // the doctor command has no operator-facing arguments (every
624
+ // probe runs unconditionally; per-probe disable lives on the CLI
625
+ // shell surface, not the slash one).
626
+ return { kind: 'doctor' };
627
+ }
628
+ case 'prd-check':
629
+ case 'prdcheck': {
630
+ // : tokenise the tail and forward verbatim
631
+ // so the slash + shell surfaces share one `parsePrdCheckArgs`.
632
+ // Supports `<prd-path>`, `--all`, and `--json`.
633
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
634
+ return { kind: 'prd-check', args: tokens };
635
+ }
636
+ case 'chain': {
637
+ // : forward the tokenized argv to
638
+ // `runChainCommand` via the session module. Subcommands are
639
+ // single tokens (new / status / next / show / export / list);
640
+ // the `new` subcommand accepts the intent as the joined tail so
641
+ // operators can write `/chain new add auth flow` без quoting.
642
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
643
+ return { kind: 'chain', args: tokens };
644
+ }
645
+ case 'codegraph-status':
646
+ case 'codegraph': {
647
+ // BT 9 Phase 2 : forward the tokenized argv
648
+ // to `runCodegraphStatusCommand`. Flags handled by the runner:
649
+ // --install — merge codegraph into .pugi/mcp.json (accept)
650
+ // --reindex — stamp lastIndexedAt + hint runtime to refresh
651
+ // --offer — surface the install prompt even after a decline
652
+ // `/codegraph` is the short alias; same handler.
653
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
654
+ return { kind: 'codegraph-status', args: tokens };
655
+ }
656
+ case 'compact': {
657
+ // graduated from stub. The session module
658
+ // owns the summariser round-trip. BT 8: `--force` overrides
659
+ // the noop-empty guard. Unknown flags fall through silently per
660
+ // the existing tail-tolerance behaviour (operators wanting a
661
+ // per-session compact run `pugi compact --session <id>` from a
662
+ // fresh shell).
663
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
664
+ const force = tokens.some((t) => t === '--force' || t === '-f');
665
+ return { kind: 'compact', force };
666
+ }
667
+ case 'rewind': {
668
+ // `/rewind [N | --to <id>]`. Tokenize the
669
+ // tail unchanged so `runRewindCommand` (in `runtime/commands/
670
+ // rewind.ts`) handles every mode (picker / turns / to-event)
671
+ // through one parser. The slash + top-level CLI surfaces stay
672
+ // single-sourced — same separation as `/compact`.
673
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
674
+ return { kind: 'rewind', args: tokens };
675
+ }
676
+ case 'stickers': {
677
+ // brand-personality gimmick. Tail args
678
+ // are ignored — the surface is intentionally parameterless. The
679
+ // session module delegates to the shared `runStickersCommand`
680
+ // so the slash + top-level paths stay single-sourced.
681
+ return { kind: 'stickers' };
682
+ }
683
+ case 'feedback': {
684
+ // in-CLI feedback collector. The wizard
685
+ // collects category/rating/comment/context/confirm interactively
686
+ // so the slash surface is parameterless. Tail args are reserved
687
+ // for a future `--message=...` quick-path; today they are
688
+ // accepted but ignored so the operator-level UX matches
689
+ // the upstream tool's `/feedback`.
690
+ return { kind: 'feedback' };
691
+ }
692
+ case 'share': {
693
+ // forward the tokenized arg list verbatim
694
+ // so the session module (which owns the network + readline
695
+ // affordances) can hand them to runShareCommand. Defaults: no
696
+ // tokens means "auto-pick target + prompt for confirmation".
697
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
698
+ return { kind: 'share', args: tokens };
699
+ }
700
+ case 'repo-map':
701
+ case 'repomap': {
702
+ // build + show the AST-light symbol
703
+ // summary. Accepts `refresh` as a positional или `--refresh`
704
+ // flag so muscle memory from both shells lands the same way.
705
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
706
+ const refresh = tokens.includes('--refresh') || tokens.includes('refresh') || tokens.includes('-r');
707
+ return { kind: 'repo-map', refresh };
708
+ }
709
+ case 'release-notes':
710
+ case 'releasenotes':
711
+ case 'changelog': {
712
+ // changelog diff between last-seen +
713
+ // installed CLI version. Tail args are tokenized so `--reset`
714
+ // can flip the marker-clear bit; no other flags are honoured —
715
+ // the surface mirrors the CLI top-level's intentional minimalism.
716
+ // `changelog` alias matches operator muscle memory from npm /
717
+ // cargo / brew, all of which ship `changelog` subcommands.
718
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
719
+ const reset = tokens.includes('--reset') || tokens.includes('-r');
720
+ return { kind: 'release-notes', reset };
721
+ }
722
+ case 'update': {
723
+ // forward the tokenized argv to the
724
+ // session module which delegates to `runUpdateCommand`. The
725
+ // dispatcher owns argv validation (unknown channel / flag) so
726
+ // the slash parser stays as thin as the rest of the surface.
727
+ // The slash form does NOT support `--apply` because spawning
728
+ // `npm install -g` from inside a running REPL session would
729
+ // corrupt the operator's running binary — the dispatcher treats
730
+ // `--apply` from a slash as a non-interactive offer (probe +
731
+ // install command, no shell-out). Top-level `pugi update --apply`
732
+ // remains the recommended path for the actual install.
733
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
734
+ return { kind: 'update', args: tokens };
735
+ }
736
+ case 'undo': {
737
+ // final : graduated from stub. Tail args are
738
+ // ignored — `runUndoCommand` is parameterless (single-step revert
739
+ // of the most recent successful mutating tool result). Multiple
740
+ // undos = stack of single-step undos. `/redo` is the counterpart
741
+ // — operators ping-pong через undo/redo on the
742
+ // event-log stack without re-running the underlying tool.
743
+ return { kind: 'undo' };
744
+ }
745
+ case 'redo': {
746
+ // cleanup : counterpart к /undo. Tail args
747
+ // are ignored — `runRedoCommand` is parameterless. Each /redo
748
+ // pops one entry from the LIFO undo stack (the runner tracks
749
+ // which undos have already been consumed by previous redos so
750
+ // double-/redo is a noop, not a double-write).
751
+ return { kind: 'redo' };
752
+ }
219
753
  case 'memory':
220
754
  case 'config':
221
- case 'budget':
222
- case 'mcp':
223
- case 'undo': {
755
+ case 'budget': {
224
756
  const stubName = name;
225
757
  return {
226
758
  kind: 'stub',