@pugi/cli 0.1.0-beta.4 → 0.1.0-beta.40

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 (249) hide show
  1. package/THIRD_PARTY_NOTICES.md +40 -0
  2. package/assets/pugi-mascot.ansi +15 -25
  3. package/bin/run.js +33 -1
  4. package/dist/commands/jobs-watch.js +201 -0
  5. package/dist/commands/jobs.js +15 -0
  6. package/dist/commands/smoke.js +133 -0
  7. package/dist/core/agent-progress/cleanup.js +134 -0
  8. package/dist/core/agent-progress/schema.js +144 -0
  9. package/dist/core/agent-progress/writer.js +101 -0
  10. package/dist/core/artifact-chain/dispatcher.js +148 -0
  11. package/dist/core/artifact-chain/exporter.js +164 -0
  12. package/dist/core/artifact-chain/state.js +243 -0
  13. package/dist/core/artifact-chain/steps.js +169 -0
  14. package/dist/core/auth/ensure-authenticated.js +129 -0
  15. package/dist/core/auth/env-provider.js +238 -0
  16. package/dist/core/auto-update/channels.js +122 -0
  17. package/dist/core/auto-update/checker.js +241 -0
  18. package/dist/core/auto-update/state.js +235 -0
  19. package/dist/core/bare-mode/index.js +107 -0
  20. package/dist/core/bash-classifier.js +108 -1
  21. package/dist/core/checkpoint/resumer.js +149 -0
  22. package/dist/core/checkpoint/rewinder.js +291 -0
  23. package/dist/core/codegraph/decision-store.js +248 -0
  24. package/dist/core/codegraph/detect-repo.js +459 -0
  25. package/dist/core/codegraph/install.js +134 -0
  26. package/dist/core/codegraph/offer-hook.js +220 -0
  27. package/dist/core/compact/auto-trigger.js +96 -0
  28. package/dist/core/compact/buffer-rewriter.js +115 -0
  29. package/dist/core/compact/summarizer.js +208 -0
  30. package/dist/core/compact/token-counter.js +108 -0
  31. package/dist/core/consensus/diff-capture.js +73 -0
  32. package/dist/core/context/index.js +7 -0
  33. package/dist/core/context/markdown-traverse.js +255 -0
  34. package/dist/core/cost/rate-card.js +129 -0
  35. package/dist/core/cost/tracker.js +221 -0
  36. package/dist/core/denial-tracking/index.js +8 -0
  37. package/dist/core/denial-tracking/state.js +264 -0
  38. package/dist/core/diagnostics/probe-runner.js +93 -0
  39. package/dist/core/diagnostics/probes/api.js +46 -0
  40. package/dist/core/diagnostics/probes/auth.js +86 -0
  41. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  42. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  43. package/dist/core/diagnostics/probes/config.js +72 -0
  44. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  45. package/dist/core/diagnostics/probes/disk.js +81 -0
  46. package/dist/core/diagnostics/probes/git.js +65 -0
  47. package/dist/core/diagnostics/probes/mcp.js +75 -0
  48. package/dist/core/diagnostics/probes/node.js +59 -0
  49. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  50. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  51. package/dist/core/diagnostics/probes/session.js +74 -0
  52. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  53. package/dist/core/diagnostics/probes/workspace.js +63 -0
  54. package/dist/core/diagnostics/types.js +70 -0
  55. package/dist/core/dispatch/cache-cleanup.js +197 -0
  56. package/dist/core/dispatch/cache-handoff.js +295 -0
  57. package/dist/core/edits/dispatch.js +218 -2
  58. package/dist/core/edits/journal.js +199 -0
  59. package/dist/core/edits/layer-d-ast.js +557 -14
  60. package/dist/core/edits/verify-hook.js +273 -0
  61. package/dist/core/edits/worktree.js +322 -0
  62. package/dist/core/engine/anvil-client.js +115 -5
  63. package/dist/core/engine/budgets.js +98 -0
  64. package/dist/core/engine/context-prefix.js +155 -0
  65. package/dist/core/engine/intent.js +260 -0
  66. package/dist/core/engine/native-pugi.js +860 -211
  67. package/dist/core/engine/prompts.js +88 -2
  68. package/dist/core/engine/strip-internal-fields.js +124 -0
  69. package/dist/core/engine/tool-bridge.js +992 -36
  70. package/dist/core/feedback/queue.js +177 -0
  71. package/dist/core/feedback/submitter.js +145 -0
  72. package/dist/core/file-cache.js +113 -1
  73. package/dist/core/hooks/events.js +44 -0
  74. package/dist/core/hooks/index.js +15 -0
  75. package/dist/core/hooks/registry.js +213 -0
  76. package/dist/core/hooks/runner.js +236 -0
  77. package/dist/core/hooks/v2/event-emitter.js +115 -0
  78. package/dist/core/hooks/v2/executor.js +282 -0
  79. package/dist/core/hooks/v2/index.js +25 -0
  80. package/dist/core/hooks/v2/lifecycle.js +104 -0
  81. package/dist/core/hooks/v2/loader.js +216 -0
  82. package/dist/core/hooks/v2/matcher.js +125 -0
  83. package/dist/core/hooks/v2/trust.js +143 -0
  84. package/dist/core/hooks/v2/types.js +86 -0
  85. package/dist/core/lsp/cache.js +105 -0
  86. package/dist/core/lsp/client.js +776 -0
  87. package/dist/core/lsp/language-detect.js +66 -0
  88. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  89. package/dist/core/mcp/client.js +75 -6
  90. package/dist/core/mcp/http-server.js +553 -0
  91. package/dist/core/mcp/orchestrator-tools.js +662 -0
  92. package/dist/core/mcp/permission.js +190 -0
  93. package/dist/core/mcp/registry.js +24 -2
  94. package/dist/core/mcp/server-tools.js +219 -0
  95. package/dist/core/mcp/server.js +397 -0
  96. package/dist/core/memory/dual-write.js +416 -0
  97. package/dist/core/memory/phase1-kinds.js +20 -0
  98. package/dist/core/memory-sync/queue.js +158 -0
  99. package/dist/core/onboarding/ensure-initialized.js +133 -0
  100. package/dist/core/onboarding/marker.js +111 -0
  101. package/dist/core/onboarding/telemetry-state.js +108 -0
  102. package/dist/core/output-style/presets.js +176 -0
  103. package/dist/core/output-style/state.js +185 -0
  104. package/dist/core/permissions/auto-classifier.js +124 -0
  105. package/dist/core/permissions/circuit-breaker.js +83 -0
  106. package/dist/core/permissions/gate.js +278 -0
  107. package/dist/core/permissions/index.js +20 -0
  108. package/dist/core/permissions/mode.js +174 -0
  109. package/dist/core/permissions/state.js +241 -0
  110. package/dist/core/permissions/tool-class.js +93 -0
  111. package/dist/core/prd-check/parser.js +215 -0
  112. package/dist/core/prd-check/reporter.js +127 -0
  113. package/dist/core/prd-check/session-review.js +557 -0
  114. package/dist/core/prd-check/verifiers.js +223 -0
  115. package/dist/core/pugi-md/context-injector.js +76 -0
  116. package/dist/core/pugi-md/walk-up.js +207 -0
  117. package/dist/core/release-notes/parser.js +241 -0
  118. package/dist/core/release-notes/state.js +116 -0
  119. package/dist/core/repl/history.js +11 -1
  120. package/dist/core/repl/model-pricing.js +135 -0
  121. package/dist/core/repl/session.js +1899 -38
  122. package/dist/core/repl/slash-commands.js +406 -21
  123. package/dist/core/repl/store/session-store.js +31 -2
  124. package/dist/core/repl/workspace-context.js +22 -0
  125. package/dist/core/repo-map/build.js +125 -0
  126. package/dist/core/repo-map/cache.js +185 -0
  127. package/dist/core/repo-map/extractor.js +254 -0
  128. package/dist/core/repo-map/formatter.js +145 -0
  129. package/dist/core/repo-map/scanner.js +211 -0
  130. package/dist/core/retry-budget/budget.js +284 -0
  131. package/dist/core/retry-budget/index.js +5 -0
  132. package/dist/core/session.js +92 -0
  133. package/dist/core/settings.js +80 -0
  134. package/dist/core/share/formatter.js +271 -0
  135. package/dist/core/share/redactor.js +221 -0
  136. package/dist/core/share/uploader.js +267 -0
  137. package/dist/core/skills/defaults.js +457 -0
  138. package/dist/core/smoke/headless-driver.js +174 -0
  139. package/dist/core/smoke/orchestrator.js +194 -0
  140. package/dist/core/smoke/runner.js +238 -0
  141. package/dist/core/smoke/scenario-parser.js +316 -0
  142. package/dist/core/subagents/dispatcher-real.js +600 -0
  143. package/dist/core/subagents/dispatcher.js +113 -24
  144. package/dist/core/subagents/index.js +18 -5
  145. package/dist/core/subagents/isolation-matrix.js +213 -0
  146. package/dist/core/subagents/spawn.js +19 -4
  147. package/dist/core/telemetry/emitter.js +229 -0
  148. package/dist/core/telemetry/queue.js +251 -0
  149. package/dist/core/theme/context.js +91 -0
  150. package/dist/core/theme/presets.js +228 -0
  151. package/dist/core/theme/state.js +181 -0
  152. package/dist/core/todos/invariant.js +10 -0
  153. package/dist/core/todos/state.js +177 -0
  154. package/dist/core/transport/version-interceptor.js +166 -0
  155. package/dist/core/vim/keymap.js +288 -0
  156. package/dist/core/vim/state.js +92 -0
  157. package/dist/index.js +28 -0
  158. package/dist/runtime/bootstrap.js +190 -0
  159. package/dist/runtime/cli.js +3073 -321
  160. package/dist/runtime/commands/cancel.js +231 -0
  161. package/dist/runtime/commands/chain.js +489 -0
  162. package/dist/runtime/commands/codegraph-status.js +227 -0
  163. package/dist/runtime/commands/compact.js +297 -0
  164. package/dist/runtime/commands/cost.js +199 -0
  165. package/dist/runtime/commands/delegate.js +242 -11
  166. package/dist/runtime/commands/dispatch.js +126 -0
  167. package/dist/runtime/commands/doctor.js +390 -0
  168. package/dist/runtime/commands/feedback.js +184 -0
  169. package/dist/runtime/commands/hooks.js +184 -0
  170. package/dist/runtime/commands/lsp.js +368 -0
  171. package/dist/runtime/commands/mcp.js +879 -0
  172. package/dist/runtime/commands/memory.js +508 -0
  173. package/dist/runtime/commands/model.js +237 -0
  174. package/dist/runtime/commands/onboarding.js +275 -0
  175. package/dist/runtime/commands/patch.js +128 -0
  176. package/dist/runtime/commands/permissions.js +112 -0
  177. package/dist/runtime/commands/plan.js +143 -0
  178. package/dist/runtime/commands/prd-check.js +285 -0
  179. package/dist/runtime/commands/redo-blob-store.js +92 -0
  180. package/dist/runtime/commands/redo.js +361 -0
  181. package/dist/runtime/commands/release-notes.js +229 -0
  182. package/dist/runtime/commands/repo-map.js +95 -0
  183. package/dist/runtime/commands/report.js +299 -0
  184. package/dist/runtime/commands/resume.js +118 -0
  185. package/dist/runtime/commands/review-consensus.js +17 -2
  186. package/dist/runtime/commands/rewind.js +333 -0
  187. package/dist/runtime/commands/sessions.js +163 -0
  188. package/dist/runtime/commands/share.js +316 -0
  189. package/dist/runtime/commands/status.js +186 -0
  190. package/dist/runtime/commands/stickers.js +82 -0
  191. package/dist/runtime/commands/style.js +194 -0
  192. package/dist/runtime/commands/theme.js +196 -0
  193. package/dist/runtime/commands/undo.js +32 -0
  194. package/dist/runtime/commands/update.js +289 -0
  195. package/dist/runtime/commands/vim.js +140 -0
  196. package/dist/runtime/commands/worktree.js +177 -0
  197. package/dist/runtime/headless-repl.js +195 -0
  198. package/dist/runtime/headless.js +543 -0
  199. package/dist/runtime/load-hooks-or-exit.js +71 -0
  200. package/dist/runtime/plan-decompose.js +531 -0
  201. package/dist/runtime/version.js +65 -0
  202. package/dist/tools/agent-tool.js +229 -0
  203. package/dist/tools/apply-patch.js +556 -0
  204. package/dist/tools/ask-user-question.js +213 -0
  205. package/dist/tools/ask-user.js +115 -0
  206. package/dist/tools/file-tools.js +85 -14
  207. package/dist/tools/lsp-tools.js +189 -0
  208. package/dist/tools/mcp-tool.js +260 -0
  209. package/dist/tools/multi-edit.js +361 -0
  210. package/dist/tools/registry.js +46 -0
  211. package/dist/tools/skill-tool.js +96 -0
  212. package/dist/tools/tasks.js +208 -0
  213. package/dist/tools/todo-write.js +184 -0
  214. package/dist/tools/web-fetch.js +147 -2
  215. package/dist/tools/web-search.js +458 -0
  216. package/dist/tui/agent-progress-card.js +111 -0
  217. package/dist/tui/agent-tree.js +10 -0
  218. package/dist/tui/ask-modal.js +2 -2
  219. package/dist/tui/ask-user-question-prompt.js +192 -0
  220. package/dist/tui/compact-banner.js +81 -0
  221. package/dist/tui/conversation-pane.js +82 -8
  222. package/dist/tui/cost-table.js +111 -0
  223. package/dist/tui/doctor-table.js +46 -0
  224. package/dist/tui/feedback-prompt.js +156 -0
  225. package/dist/tui/input-box.js +69 -2
  226. package/dist/tui/markdown-render.js +4 -4
  227. package/dist/tui/onboarding-wizard.js +240 -0
  228. package/dist/tui/permissions-picker.js +86 -0
  229. package/dist/tui/render.js +35 -0
  230. package/dist/tui/repl-render.js +303 -13
  231. package/dist/tui/repl-splash.js +2 -2
  232. package/dist/tui/repl.js +72 -14
  233. package/dist/tui/splash.js +1 -1
  234. package/dist/tui/status-bar.js +94 -16
  235. package/dist/tui/status-table.js +7 -0
  236. package/dist/tui/stickers-art.js +136 -0
  237. package/dist/tui/style-table.js +28 -0
  238. package/dist/tui/theme-table.js +29 -0
  239. package/dist/tui/tool-stream-pane.js +52 -3
  240. package/dist/tui/update-banner.js +20 -2
  241. package/dist/tui/vim-input.js +267 -0
  242. package/docs/examples/codegraph.mcp.json +10 -0
  243. package/package.json +12 -6
  244. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  245. package/test/scenarios/compact-force.scenario.txt +11 -0
  246. package/test/scenarios/identity.scenario.txt +11 -0
  247. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  248. package/test/scenarios/walkback.scenario.txt +12 -0
  249. package/dist/core/engine/compaction-hook.js +0 -154
@@ -29,6 +29,7 @@
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,15 +39,24 @@ 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
+ // Leak L8 (2026-05-27): /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.
42
46
  memory: 'Session memory editor lands in α6.5b.',
43
47
  config: 'Run `pugi config list` from a fresh shell for the full surface; in-REPL editor lands in α6.5.',
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
51
  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.',
52
+ // β4 Sl7 (2026-05-26): /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
+ // Wave 6 final (2026-05-27): /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
@@ -54,29 +64,51 @@ export const SLASH_COMMAND_HELP = Object.freeze([
54
64
  { name: 'agents', args: '', gloss: 'List the on-watch agent roster', group: 'Workforce dispatch' },
55
65
  { name: 'delegate', args: '<slug> <brief>', gloss: 'Dispatch a brief to one Tier 1 specialist (α7.5)', group: 'Workforce dispatch' },
56
66
  { name: 'stop', args: '<persona>', gloss: 'Stop one agent by persona slug', group: 'Workforce dispatch' },
57
- { name: 'jobs', args: '', gloss: 'List background jobs', 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 (Wave 6)', group: 'Workforce dispatch' },
58
69
  { name: 'ask', args: '<question>', gloss: 'Surface a yes/no modal locally (α6.3 forcing question)', group: 'Workforce dispatch' },
59
70
  // Session
60
71
  { name: 'clear', args: '', gloss: 'Clear conversation pane', group: 'Session' },
61
72
  { name: 'resume', args: '', gloss: 'Pick a stored session to restore', group: 'Session' },
62
73
  { name: 'context', args: '', gloss: 'Show three-tier context summary (Tier 0 skeleton + Tier 1 working set)', group: 'Session' },
63
- { name: 'compact', args: '', gloss: 'Manual context compaction (α6.5b)', group: 'Session', stub: true },
74
+ { name: 'compact', args: '[--force]', gloss: 'Summarise older turns into a boundary marker (leak L8). --force bypasses the noop-empty guard', group: 'Session' },
75
+ { name: 'rewind', args: '[N | --to <id>]', gloss: 'Roll the conversation back to a checkpoint (leak L9)', group: 'Session' },
64
76
  { name: 'memory', args: '', gloss: 'Session memory editor (α6.5b)', group: 'Session', stub: true },
77
+ { name: 'init', args: '', gloss: 'Scaffold .pugi/ in the current workspace (β1 Sl11)', group: 'Session' },
65
78
  // Pugi tools
66
79
  { name: 'web', args: '<url>', gloss: 'Fetch a URL into context', group: 'Pugi tools' },
67
80
  { name: 'diff', args: '', gloss: 'Show pending diff', group: 'Pugi tools' },
68
- { name: 'cost', args: '', gloss: 'Token usage + budget', group: 'Pugi tools' },
69
- { 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' },
70
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 (leak L28)', group: 'Pugi tools' },
86
+ { name: 'codegraph-status', args: '[--install|--reindex|--offer]', gloss: 'Codegraph MCP — install state, index age, symbol count, refresh CTA (Wave 6 BT 9 P2)', group: 'Pugi tools' },
71
87
  // Settings
72
88
  { name: 'config', args: '', gloss: 'Show config', group: 'Settings', stub: true },
73
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' },
74
93
  { name: 'budget', args: '', gloss: 'Show usage budget', group: 'Settings', stub: true },
75
- { name: 'mcp', args: '', gloss: 'List MCP servers', group: 'Settings', stub: true },
76
- { 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 (leak L25)', group: 'Settings' },
98
+ { name: 'vim', args: '[on|off|status]', gloss: 'Toggle vim-style modal editing in the input buffer (leak L26)', group: 'Settings' },
99
+ { name: 'undo', args: '', gloss: 'Revert the last successful write / edit / multi_edit (Aider walk-back, Wave 6)', group: 'Settings' },
100
+ { name: 'redo', args: '', gloss: 'Reapply the most recent /undo (LIFO stack, Wave 6 cleanup)', group: 'Settings' },
77
101
  // Meta
78
102
  { name: 'help', args: '', gloss: 'Show this help overlay', group: 'Meta' },
79
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, Wave 6 final)', group: 'Meta' },
106
+ { name: 'chain', args: '<new|status|next|show|export|list> [...args]', gloss: 'Artifact chain — PRD → ADR → mindmap → ER → sequence → tests → code (Wave 6)', 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 (leak L20)', group: 'Meta' },
110
+ { name: 'release-notes', args: '[--reset]', gloss: 'Show changelog diff since last upgrade (leak L24)', group: 'Meta' },
111
+ { name: 'update', args: '[--check|--apply [--yes]] [--channel <name>]', gloss: 'Check for / apply CLI update on stable / beta / canary (leak L27)', group: 'Meta' },
80
112
  { name: 'quit', args: '', gloss: 'Exit the REPL', group: 'Meta' },
81
113
  ]);
82
114
  /**
@@ -142,19 +174,22 @@ export function parseSlashCommand(input) {
142
174
  // server-side persona registry enforces; anything else surfaces
143
175
  // as a usage error so the operator sees the typo before the
144
176
  // round-trip.
145
- const space = tail.indexOf(' ');
146
- if (space === -1 || space === 0) {
177
+ const innerSpace = tail.indexOf(' ');
178
+ if (innerSpace === -1 || innerSpace === 0) {
147
179
  return {
148
180
  kind: 'error',
149
181
  message: 'Usage: /delegate <slug> <one-sentence brief>',
150
182
  };
151
183
  }
152
- const persona = tail.slice(0, space).toLowerCase();
153
- const brief = tail.slice(space + 1).trim();
154
- if (!/^[a-z_]+$/.test(persona)) {
184
+ const persona = tail.slice(0, innerSpace).toLowerCase();
185
+ const brief = tail.slice(innerSpace + 1).trim();
186
+ // Pattern intentionally mirrors server-side PUGI_DELEGATE_REGEX in
187
+ // sessions.controller.ts (^[a-z]+$). Keeping them lockstep means
188
+ // the REPL surfaces typos locally instead of round-tripping a 4xx.
189
+ if (!/^[a-z]+$/.test(persona)) {
155
190
  return {
156
191
  kind: 'error',
157
- message: `/delegate slug must be lowercase ASCII; got '${persona}'`,
192
+ message: `/delegate slug must be lowercase ASCII (a-z only); got '${persona}'`,
158
193
  };
159
194
  }
160
195
  if (brief.length === 0) {
@@ -200,7 +235,48 @@ export function parseSlashCommand(input) {
200
235
  return { kind: 'version' };
201
236
  }
202
237
  case 'jobs': {
203
- return { kind: 'jobs' };
238
+ // Wave 6 cleanup (2026-05-27): tokenise the tail so the slash
239
+ // can route `--watch` к the live Ink TUI (same renderer as
240
+ // `pugi jobs --watch`). Unknown tokens fall through silently —
241
+ // the slash surface is intentionally minimal vs. the shell
242
+ // command (which supports list/status/tail/kill subcommands
243
+ // through `runJobsCommand`).
244
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
245
+ const watch = tokens.includes('--watch') || tokens.includes('-w') || tokens[0] === 'watch';
246
+ return { kind: 'jobs', watch };
247
+ }
248
+ case 'cancel':
249
+ case 'halt': {
250
+ // Wave 6 small-CC-parity batch (2026-05-27):
251
+ //
252
+ // /cancel -> list active dispatches
253
+ // /cancel all -> halt every running dispatch
254
+ // /cancel <id> -> halt one (id may be a prefix; runner
255
+ // does startsWith lookup)
256
+ //
257
+ // The `halt` alias matches operator muscle memory from systemd /
258
+ // brand voice (`stop` is already taken by /stop <persona>; cancel
259
+ // is dispatch-id-keyed, stop is persona-keyed). Unknown extra
260
+ // tokens are tolerated — the runner reads only the first.
261
+ const trimmedTail = tail.trim();
262
+ if (trimmedTail.length === 0) {
263
+ return { kind: 'cancel', mode: 'list', dispatchId: '' };
264
+ }
265
+ const firstToken = trimmedTail.split(/\s+/)[0].toLowerCase();
266
+ if (firstToken === 'all' || firstToken === '*') {
267
+ return { kind: 'cancel', mode: 'all', dispatchId: 'all' };
268
+ }
269
+ // Defensive: dispatch ids are filename-safe per the
270
+ // `validateAgentProgress` agentId regex (`[a-zA-Z0-9_-]+`).
271
+ // Reject anything outside that range with a usage tip so the
272
+ // operator sees the typo before the round-trip.
273
+ if (!/^[A-Za-z0-9_-]+$/.test(firstToken)) {
274
+ return {
275
+ kind: 'error',
276
+ message: `/cancel: invalid dispatch id '${firstToken}'. Use letters / digits / '-' / '_' only.`,
277
+ };
278
+ }
279
+ return { kind: 'cancel', mode: 'one', dispatchId: firstToken };
204
280
  }
205
281
  case 'ask': {
206
282
  if (tail.length === 0) {
@@ -211,9 +287,19 @@ export function parseSlashCommand(input) {
211
287
  case 'diff': {
212
288
  return { kind: 'diff' };
213
289
  }
214
- case 'cost': {
290
+ case 'cost':
291
+ case 'usage': {
292
+ // L19 (2026-05-27): `/usage` is an alias of `/cost` per the cost-
293
+ // command spec. The previous mapping routed `/usage` to the
294
+ // network-backed `/quota` surface, but operators trained on Claude
295
+ // Code expect `/usage` to surface the per-model token breakdown
296
+ // (same shape as `/cost`). `/quota` remains the canonical name
297
+ // for the tier + monthly-cap fetch.
215
298
  return { kind: 'cost' };
216
299
  }
300
+ case 'quota': {
301
+ return { kind: 'quota' };
302
+ }
217
303
  case 'status': {
218
304
  return { kind: 'status' };
219
305
  }
@@ -245,12 +331,311 @@ export function parseSlashCommand(input) {
245
331
  // device flow + audit identity are wired correctly).
246
332
  return { kind: 'privacy' };
247
333
  }
248
- case 'compact':
334
+ case 'permissions':
335
+ case 'perms': {
336
+ // Wave 7: `/permissions [mode] [--persist] [--confirm]`.
337
+ //
338
+ // Argument grammar (single line, no quoting):
339
+ // /permissions -> show + table
340
+ // /permissions default|acceptEdits|plan|auto|dontAsk -> flip mode
341
+ // /permissions bypassPermissions --confirm -> flip to
342
+ // bypassPermissions (refused
343
+ // без --confirm — safety)
344
+ // /permissions <mode> --persist -> also write to ~/.pugi/config.json
345
+ //
346
+ // α6 aliases (`ask`, `allow`, `bypass`) are accepted и mapped to
347
+ // their Wave 7 canonical names via `parsePermissionMode`.
348
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
349
+ if (tokens.length === 0) {
350
+ return { kind: 'permissions', persist: false, confirmBypass: false };
351
+ }
352
+ const headRaw = tokens[0] ?? '';
353
+ const mode = parsePermissionMode(headRaw);
354
+ if (!mode) {
355
+ const modeList = [...PERMISSION_MODES].join('|');
356
+ return {
357
+ kind: 'error',
358
+ message: `Usage: /permissions [${modeList}] [--persist] [--confirm]; unknown mode '${headRaw}'`,
359
+ };
360
+ }
361
+ const flags = tokens.slice(1);
362
+ let persist = false;
363
+ let confirmBypass = false;
364
+ for (const flag of flags) {
365
+ if (flag === '--persist') {
366
+ persist = true;
367
+ }
368
+ else if (flag === '--confirm') {
369
+ confirmBypass = true;
370
+ }
371
+ else {
372
+ return {
373
+ kind: 'error',
374
+ message: `/permissions: unknown flag '${flag}' (allowed: --persist, --confirm)`,
375
+ };
376
+ }
377
+ }
378
+ return { kind: 'permissions', mode, persist, confirmBypass };
379
+ }
380
+ case 'init': {
381
+ // β1 Sl11: surface the init flow inside the REPL. Tail args
382
+ // are ignored — the init handler is parameterless today; `pugi
383
+ // init --no-defaults` is the CLI surface for skipping bundled
384
+ // skills.
385
+ return { kind: 'init' };
386
+ }
387
+ case 'plan': {
388
+ // Leak L7: `/plan [--back | --persist] [<prompt>]`.
389
+ //
390
+ // Argument grammar (single line, no quoting):
391
+ // /plan -> enter plan mode + banner
392
+ // /plan --back -> restore previous mode
393
+ // /plan --persist -> enter + write global config
394
+ // /plan <prompt...> -> enter + run one-shot engine
395
+ // /plan --auto-back <prompt...> -> enter + run + restore mode
396
+ //
397
+ // The parser pulls the flags off the head of the tail; whatever
398
+ // remains is the prompt. `--back` + a non-empty prompt and
399
+ // `--back` + `--auto-back` are both refused as `error` because
400
+ // they conflict at the verb level.
401
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
402
+ let back = false;
403
+ let persist = false;
404
+ let autoBack = false;
405
+ const promptTokens = [];
406
+ for (const token of tokens) {
407
+ if (token === '--back') {
408
+ back = true;
409
+ }
410
+ else if (token === '--persist') {
411
+ persist = true;
412
+ }
413
+ else if (token === '--auto-back') {
414
+ autoBack = true;
415
+ }
416
+ else {
417
+ promptTokens.push(token);
418
+ }
419
+ }
420
+ const prompt = promptTokens.join(' ');
421
+ if (back && prompt.length > 0) {
422
+ return {
423
+ kind: 'error',
424
+ message: '/plan --back does not accept a prompt; revert first, then dispatch.',
425
+ };
426
+ }
427
+ if (back && autoBack) {
428
+ return {
429
+ kind: 'error',
430
+ message: '/plan --back and --auto-back cannot be combined.',
431
+ };
432
+ }
433
+ return { kind: 'plan', back, persist, autoBack, prompt };
434
+ }
435
+ case 'model': {
436
+ // Wave 6 BT 8 (Claude Code parity): `/model [<slug>]`. Bare form
437
+ // prints the tier-gated model menu + current selection; with a
438
+ // slug it flips workspace selection. Slug grammar (loose): alnum
439
+ // + dash + dot + slash. Anything outside that range becomes an
440
+ // error verdict so the operator sees a clear message instead of a
441
+ // silent no-op. Whitespace inside the tail = multiple tokens = we
442
+ // take the first; the help gloss documents single-slug usage.
443
+ const trimmedTail = tail.trim();
444
+ if (trimmedTail.length === 0) {
445
+ return { kind: 'model', slug: undefined };
446
+ }
447
+ const firstToken = trimmedTail.split(/\s+/)[0] ?? '';
448
+ if (!/^[A-Za-z0-9][A-Za-z0-9._\-\/]{0,63}$/.test(firstToken)) {
449
+ return {
450
+ kind: 'error',
451
+ message: `/model: invalid slug '${firstToken}'. Use letters / digits / '-' / '.' / '/' only.`,
452
+ };
453
+ }
454
+ return { kind: 'model', slug: firstToken };
455
+ }
456
+ case 'mcp': {
457
+ // β4 Sl7: tokenize the tail. Empty tail -> `list` (matches CLI).
458
+ // Quoting / shell-escapes are NOT supported — the slash surface is
459
+ // intentionally simple; complex installs (env vars, multi-word
460
+ // args) go through `pugi mcp install` from a fresh shell.
461
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
462
+ return { kind: 'mcp', args: tokens };
463
+ }
464
+ case 'style':
465
+ case 'output-style': {
466
+ // Leak L18 (2026-05-27): forward the tokenized tail unchanged so
467
+ // the slash + top-level CLI surfaces share one parser inside
468
+ // `runStyleCommand`. Quoting / multi-word args are not used (the
469
+ // preset slugs are single tokens by contract).
470
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
471
+ return { kind: 'style', args: tokens };
472
+ }
473
+ case 'theme':
474
+ case 'palette':
475
+ case 'colors': {
476
+ // Leak L30 (2026-05-27): forward the tokenized tail unchanged so
477
+ // the slash + top-level `pugi theme` surfaces share one parser
478
+ // inside `runThemeCommand`. Aliases `/palette` and `/colors`
479
+ // exist because the leak-landscape audit found operators reach
480
+ // for either word interchangeably — same surface, same handler.
481
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
482
+ return { kind: 'theme', args: tokens };
483
+ }
484
+ case 'onboarding':
485
+ case 'onboard':
486
+ case 'setup': {
487
+ // Leak L25 (2026-05-27): forward the tokenized tail unchanged.
488
+ // The slash always routes through the non-interactive snapshot
489
+ // path (the REPL already owns the Ink tree); the runner picks
490
+ // it up from `ctx.interactive = false` in the session
491
+ // dispatcher.
492
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
493
+ return { kind: 'onboarding', args: tokens };
494
+ }
495
+ case 'vim': {
496
+ // Leak L26 (2026-05-27): forward the tokenized tail unchanged so
497
+ // the slash + top-level CLI surfaces share one parser inside
498
+ // `runVimCommand`. Subcommands are single tokens (on / off /
499
+ // status); a bare `/vim` toggles.
500
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
501
+ return { kind: 'vim', args: tokens };
502
+ }
503
+ case 'doctor':
504
+ case 'health': {
505
+ // L17 (2026-05-27): run the probe sweep inline. Tail is ignored —
506
+ // the doctor command has no operator-facing arguments (every
507
+ // probe runs unconditionally; per-probe disable lives on the CLI
508
+ // shell surface, not the slash one).
509
+ return { kind: 'doctor' };
510
+ }
511
+ case 'prd-check':
512
+ case 'prdcheck': {
513
+ // Wave 6 (2026-05-27): tokenise the tail and forward verbatim
514
+ // so the slash + shell surfaces share one `parsePrdCheckArgs`.
515
+ // Supports `<prd-path>`, `--all`, and `--json`.
516
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
517
+ return { kind: 'prd-check', args: tokens };
518
+ }
519
+ case 'chain': {
520
+ // Wave 6 (2026-05-27): forward the tokenized argv to
521
+ // `runChainCommand` via the session module. Subcommands are
522
+ // single tokens (new / status / next / show / export / list);
523
+ // the `new` subcommand accepts the intent as the joined tail so
524
+ // operators can write `/chain new add auth flow` без quoting.
525
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
526
+ return { kind: 'chain', args: tokens };
527
+ }
528
+ case 'codegraph-status':
529
+ case 'codegraph': {
530
+ // Wave 6 BT 9 Phase 2 (2026-05-27): forward the tokenized argv
531
+ // to `runCodegraphStatusCommand`. Flags handled by the runner:
532
+ // --install — merge codegraph into .pugi/mcp.json (accept)
533
+ // --reindex — stamp lastIndexedAt + hint runtime to refresh
534
+ // --offer — surface the install prompt even after a decline
535
+ // `/codegraph` is the short alias; same handler.
536
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
537
+ return { kind: 'codegraph-status', args: tokens };
538
+ }
539
+ case 'compact': {
540
+ // Leak L8 (2026-05-27): graduated from stub. The session module
541
+ // owns the summariser round-trip. Wave 6 BT 8: `--force` overrides
542
+ // the noop-empty guard. Unknown flags fall through silently per
543
+ // the existing tail-tolerance behaviour (operators wanting a
544
+ // per-session compact run `pugi compact --session <id>` from a
545
+ // fresh shell).
546
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
547
+ const force = tokens.some((t) => t === '--force' || t === '-f');
548
+ return { kind: 'compact', force };
549
+ }
550
+ case 'rewind': {
551
+ // Leak L9 (2026-05-27): `/rewind [N | --to <id>]`. Tokenize the
552
+ // tail unchanged so `runRewindCommand` (in `runtime/commands/
553
+ // rewind.ts`) handles every mode (picker / turns / to-event)
554
+ // through one parser. The slash + top-level CLI surfaces stay
555
+ // single-sourced — same separation as `/compact`.
556
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
557
+ return { kind: 'rewind', args: tokens };
558
+ }
559
+ case 'stickers': {
560
+ // Leak L33 (2026-05-27): brand-personality gimmick. Tail args
561
+ // are ignored — the surface is intentionally parameterless. The
562
+ // session module delegates to the shared `runStickersCommand`
563
+ // so the slash + top-level paths stay single-sourced.
564
+ return { kind: 'stickers' };
565
+ }
566
+ case 'feedback': {
567
+ // Leak L21 (2026-05-27): in-CLI feedback collector. The wizard
568
+ // collects category/rating/comment/context/confirm interactively
569
+ // so the slash surface is parameterless. Tail args are reserved
570
+ // for a future `--message=...` quick-path; today they are
571
+ // accepted but ignored so the operator-level UX matches
572
+ // Claude Code's `/feedback`.
573
+ return { kind: 'feedback' };
574
+ }
575
+ case 'share': {
576
+ // Leak L20 (2026-05-27): forward the tokenized arg list verbatim
577
+ // so the session module (which owns the network + readline
578
+ // affordances) can hand them to runShareCommand. Defaults: no
579
+ // tokens means "auto-pick target + prompt for confirmation".
580
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
581
+ return { kind: 'share', args: tokens };
582
+ }
583
+ case 'repo-map':
584
+ case 'repomap': {
585
+ // Leak L28 (2026-05-27): build + show the AST-light symbol
586
+ // summary. Accepts `refresh` as a positional или `--refresh`
587
+ // flag so muscle memory from both shells lands the same way.
588
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
589
+ const refresh = tokens.includes('--refresh') || tokens.includes('refresh') || tokens.includes('-r');
590
+ return { kind: 'repo-map', refresh };
591
+ }
592
+ case 'release-notes':
593
+ case 'releasenotes':
594
+ case 'changelog': {
595
+ // Leak L24 (2026-05-27): changelog diff between last-seen +
596
+ // installed CLI version. Tail args are tokenized so `--reset`
597
+ // can flip the marker-clear bit; no other flags are honoured —
598
+ // the surface mirrors the CLI top-level's intentional minimalism.
599
+ // `changelog` alias matches operator muscle memory from npm /
600
+ // cargo / brew, all of which ship `changelog` subcommands.
601
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
602
+ const reset = tokens.includes('--reset') || tokens.includes('-r');
603
+ return { kind: 'release-notes', reset };
604
+ }
605
+ case 'update': {
606
+ // Leak L27 (2026-05-27): forward the tokenized argv to the
607
+ // session module which delegates to `runUpdateCommand`. The
608
+ // dispatcher owns argv validation (unknown channel / flag) so
609
+ // the slash parser stays as thin as the rest of the surface.
610
+ // The slash form does NOT support `--apply` because spawning
611
+ // `npm install -g` from inside a running REPL session would
612
+ // corrupt the operator's running binary — the dispatcher treats
613
+ // `--apply` from a slash as a non-interactive offer (probe +
614
+ // install command, no shell-out). Top-level `pugi update --apply`
615
+ // remains the recommended path for the actual install.
616
+ const tokens = tail.length === 0 ? [] : tail.split(/\s+/).filter((s) => s.length > 0);
617
+ return { kind: 'update', args: tokens };
618
+ }
619
+ case 'undo': {
620
+ // Wave 6 final (2026-05-27): graduated from stub. Tail args are
621
+ // ignored — `runUndoCommand` is parameterless (single-step revert
622
+ // of the most recent successful mutating tool result). Multiple
623
+ // undos = stack of single-step undos. `/redo` is the counterpart
624
+ // (Wave 6 cleanup) — operators ping-pong через undo/redo on the
625
+ // event-log stack without re-running the underlying tool.
626
+ return { kind: 'undo' };
627
+ }
628
+ case 'redo': {
629
+ // Wave 6 cleanup (2026-05-27): counterpart к /undo. Tail args
630
+ // are ignored — `runRedoCommand` is parameterless. Each /redo
631
+ // pops one entry from the LIFO undo stack (the runner tracks
632
+ // which undos have already been consumed by previous redos so
633
+ // double-/redo is a noop, not a double-write).
634
+ return { kind: 'redo' };
635
+ }
249
636
  case 'memory':
250
637
  case 'config':
251
- case 'budget':
252
- case 'mcp':
253
- case 'undo': {
638
+ case 'budget': {
254
639
  const stubName = name;
255
640
  return {
256
641
  kind: 'stub',
@@ -361,7 +361,7 @@ export class SqliteSessionStore {
361
361
  // which maps to SQLITE_OPEN_READONLY. The option form is the
362
362
  // documented API; the file-URI form (file:...?mode=ro) also works.
363
363
  const db = new DatabaseSync(dbPath, { readOnly: true });
364
- return new SqliteSessionStoreReadOnlyView(db);
364
+ return new SqliteSessionStoreReadOnlyView(db, projectStoreDir);
365
365
  }
366
366
  /* ------------------------------------------------------------ */
367
367
  /* Internals */
@@ -584,8 +584,37 @@ export class SqliteSessionStore {
584
584
  */
585
585
  export class SqliteSessionStoreReadOnlyView {
586
586
  db;
587
- constructor(db) {
587
+ projectStoreDir;
588
+ constructor(db,
589
+ /**
590
+ * Project store directory — required for the JSONL event read path.
591
+ * L9 (2026-05-27): `/rewind` + `/resume` need to walk events from
592
+ * inside the read-only view so the rewind picker + resume preview
593
+ * never take the writer lockfile.
594
+ */
595
+ projectStoreDir) {
588
596
  this.db = db;
597
+ this.projectStoreDir = projectStoreDir;
598
+ }
599
+ /**
600
+ * Read every event for a session via the durable JSONL log. The
601
+ * SQLite cache is NOT used here — JSONL is the source of truth and
602
+ * the cache only holds counters. The walk stitches across rotation
603
+ * files (`events.<n>.jsonl`) in the same order `JsonlEventLog.read`
604
+ * uses inside the writer path so consumers see one consistent stream
605
+ * whether they came in via the writer store OR the read-only view.
606
+ */
607
+ async events(sessionId, opts) {
608
+ const sessionDir = resolve(this.projectStoreDir, 'sessions', sessionId);
609
+ if (!existsSync(sessionDir))
610
+ return [];
611
+ const log = new JsonlEventLog({ sessionDir });
612
+ try {
613
+ return log.read(opts);
614
+ }
615
+ finally {
616
+ log.close();
617
+ }
589
618
  }
590
619
  async list(opts) {
591
620
  const limit = clampLimit(opts?.limit ?? DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT);
@@ -27,6 +27,16 @@
27
27
  import { existsSync, readFileSync, statSync } from 'node:fs';
28
28
  import { basename, resolve as resolvePath } from 'node:path';
29
29
  import { slugForCwd } from './history.js';
30
+ import { isBareMode } from '../bare-mode/index.js';
31
+ /**
32
+ * Workspace summary shown when the operator launched with `--bare` (or
33
+ * `PUGI_BARE=1`). Leak L22: bare mode disables project auto-discovery
34
+ * across the CLI, so we never read `.pugi/PUGI.md` and never advertise
35
+ * a real workspace label to admin-api. Explicit string so the splash +
36
+ * status bar agree, and so operators triaging "why is Mira ignoring
37
+ * my repo" see a clear cause.
38
+ */
39
+ export const BARE_MODE_WORKSPACE_LABEL = '(bare mode - auto-discovery disabled)';
30
40
  /** Cap on the PUGI.md head we forward. Mirrors the admin-api clamp. */
31
41
  const PUGI_MD_HEAD_LIMIT = 200;
32
42
  /**
@@ -48,6 +58,18 @@ export const UNBOUND_WORKSPACE_LABEL = '(not bound - run /init OR cd into projec
48
58
  export function resolveWorkspaceContext(cwd) {
49
59
  const normalised = resolvePath(cwd);
50
60
  const slug = slugForCwd(normalised);
61
+ // Leak L22 (2026-05-27): `--bare` short-circuits BEFORE any PUGI.md
62
+ // / project-marker reads so the resolver never advertises a real
63
+ // workspace summary to admin-api. The cwd + slug still travel for
64
+ // telemetry, but the model + Mira treat the session as if launched
65
+ // from a fresh, unbound directory.
66
+ if (isBareMode()) {
67
+ return {
68
+ workspaceCwd: normalised,
69
+ workspaceSlug: slug,
70
+ workspaceSummary: BARE_MODE_WORKSPACE_LABEL,
71
+ };
72
+ }
51
73
  // α6.14.2 wave 5: when the cwd has no project markers, prefer the
52
74
  // explicit "not bound" summary so admin-api's prompt builder knows
53
75
  // not to fabricate a workspace context for Mira/Pugi. The cwd +