@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
@@ -0,0 +1,186 @@
1
+ /**
2
+ * `pugi status` — concise session-state probe ().
3
+ *
4
+ * Different from `pugi doctor` (environment health). The status
5
+ * command answers "what is this Pugi session doing right now?" —
6
+ * session id + age, cwd, permission mode, CLI version, token
7
+ * usage, dispatch counts, last command, compact boundaries, auth
8
+ * identity.
9
+ *
10
+ * # Module contract
11
+ *
12
+ * - This file owns the WIRING from CLI flags + ambient process
13
+ * state к the snapshot collector. The data collector itself
14
+ * lives in `core/diagnostics/probes/status-snapshot.ts` and
15
+ * has zero coupling к the CLI dispatch surface.
16
+ *
17
+ * - `runStatusCommand` is the single entry point. Both the
18
+ * top-level `pugi status` handler in `runtime/cli.ts` AND the
19
+ * in-REPL `/status` slash command call it. The function
20
+ * returns the `StatusSnapshot` so the REPL slash handler can
21
+ * mount the Ink renderer without re-collecting fields.
22
+ *
23
+ * - The credential resolver is captured behind a function so the
24
+ * spec can stub it without monkey-patching `core/credentials.ts`.
25
+ *
26
+ * - The permission state module is dynamic-imported with a
27
+ * try/catch so this command lands cleanly before the L6
28
+ * permission-mode work — when the module is absent the field
29
+ * degrades к "unknown" instead of crashing the snapshot.
30
+ *
31
+ * - Exit code is 0 unless the snapshot itself throws. The
32
+ * command is intentionally informational — even a fully
33
+ * "unavailable" snapshot (everything degraded к sentinels)
34
+ * exits 0 so monitoring scripts can rely on a stable contract.
35
+ */
36
+ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
37
+ import { homedir } from 'node:os';
38
+ import { resolveActiveCredential } from '../../core/credentials.js';
39
+ import { PUGI_CLI_VERSION } from '../version.js';
40
+ import { collectStatusSnapshot, } from '../../core/diagnostics/probes/status-snapshot.js';
41
+ /**
42
+ * Default production filesystem stubs. Wraps node:fs so the
43
+ * snapshot collector can stay sync + structurally testable.
44
+ */
45
+ const DEFAULT_FS = {
46
+ existsSync,
47
+ statSync,
48
+ readdirSync: (p) => readdirSync(p),
49
+ readFileSync: (p, encoding) => readFileSync(p, encoding),
50
+ };
51
+ /**
52
+ * Default credential resolver. Wraps `resolveActiveCredential` so
53
+ * the snapshot only sees the trimmed `ResolvedCredentialSummary`
54
+ * shape and не the full store record.
55
+ */
56
+ function defaultResolveCredential(env, home) {
57
+ const cred = resolveActiveCredential(env, home);
58
+ if (!cred)
59
+ return null;
60
+ // Tier is не yet recorded в the credentials file — set к null
61
+ // until a future sprint surfaces `tier` on the stored record.
62
+ // The renderer hides the parenthetical when tier is null.
63
+ return {
64
+ apiUrl: cred.apiUrl,
65
+ label: cred.label ?? null,
66
+ tier: null,
67
+ identity: cred.label ?? null,
68
+ };
69
+ }
70
+ /**
71
+ * Default permission-mode loader. Dynamic-imports
72
+ * `core/permissions/state.ts` (L6 in the leak-parity roadmap);
73
+ * returns null when the module is absent OR malformed, which the
74
+ * snapshot field degrades к the "unknown" sentinel.
75
+ */
76
+ async function defaultResolvePermissionMode() {
77
+ try {
78
+ const mod = (await import('../../core/permissions/state.js').catch(() => null));
79
+ if (!mod || typeof mod.getCurrentPermissionMode !== 'function') {
80
+ return null;
81
+ }
82
+ const mode = mod.getCurrentPermissionMode();
83
+ return typeof mode === 'string' && mode.length > 0 ? mode : null;
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Collect the snapshot + emit the output via the supplied
91
+ * writeOutput sink. Returns the snapshot so REPL callers can
92
+ * route it к the Ink renderer instead of the plain-text fallback.
93
+ */
94
+ export async function runStatusCommand(ctx) {
95
+ // Resolve the permission mode upfront — the snapshot is sync but
96
+ // the L6 loader is async (dynamic import). We materialise the
97
+ // value here and hand the snapshot a sync getter.
98
+ const permissionMode = ctx.resolvePermissionMode === undefined
99
+ ? await defaultResolvePermissionMode()
100
+ : null;
101
+ const deps = {
102
+ cwd: ctx.cwd,
103
+ home: ctx.home,
104
+ cliVersion: PUGI_CLI_VERSION,
105
+ now: ctx.now ?? Date.now,
106
+ liveSessionId: ctx.liveSessionId ?? null,
107
+ sessionStartedAtEpochMs: ctx.sessionStartedAtEpochMs ?? null,
108
+ liveTokensUsed: ctx.liveTokensUsed ?? null,
109
+ lastCommand: ctx.lastCommand ?? null,
110
+ lastCommandAtEpochMs: ctx.lastCommandAtEpochMs ?? null,
111
+ liveApiUrl: ctx.liveApiUrl ?? null,
112
+ workspaceLabel: ctx.workspaceLabel ?? null,
113
+ fs: ctx.fs ?? DEFAULT_FS,
114
+ resolveCredential: ctx.resolveCredential ?? (() => defaultResolveCredential(ctx.env, ctx.home)),
115
+ resolvePermissionMode: ctx.resolvePermissionMode ?? (() => permissionMode),
116
+ };
117
+ let snapshot;
118
+ try {
119
+ snapshot = collectStatusSnapshot(deps);
120
+ }
121
+ catch (error) {
122
+ // Defensive: the snapshot collector is fail-soft per field, but
123
+ // a structural crash (e.g. a hostile env that throws on
124
+ // `process.version` access) must не bring down `pugi status`.
125
+ // We synthesise a minimal envelope and surface the error в the
126
+ // text output so the operator can file a bug.
127
+ const message = error instanceof Error ? error.message : String(error);
128
+ snapshot = {
129
+ command: 'status',
130
+ fields: [
131
+ {
132
+ key: 'session',
133
+ label: 'Session',
134
+ value: 'unknown',
135
+ available: false,
136
+ note: `snapshot collector crashed: ${message}`,
137
+ },
138
+ ],
139
+ meta: {
140
+ cliVersion: PUGI_CLI_VERSION,
141
+ nodeVersion: process.version,
142
+ cwd: ctx.cwd,
143
+ capturedAt: new Date((ctx.now ?? Date.now)()).toISOString(),
144
+ },
145
+ };
146
+ }
147
+ const text = renderStatusTable(snapshot);
148
+ ctx.writeOutput(snapshot, text);
149
+ // Always exit 0 — `pugi status` is informational, never a gate.
150
+ process.exitCode = 0;
151
+ return snapshot;
152
+ }
153
+ /**
154
+ * Plain-text table renderer. Lays out the snapshot as a two-column
155
+ * (LABEL / VALUE) table with the brand-voice header `Pugi status`.
156
+ * Matches the column-light pattern used by `renderDoctorTable` so
157
+ * narrow terminals stay legible without a layout library.
158
+ */
159
+ export function renderStatusTable(snapshot) {
160
+ // Row syntax: `${Label}: ${value}` with exactly ONE space after the
161
+ // colon, then the value verbatim. The colon doubles as a visual
162
+ // anchor and the REPL spec assertions match on the single-space
163
+ // form (`Backend: https://api.pugi.io`) — multi-space column
164
+ // padding broke `.includes('Backend: https://...')` substring
165
+ // checks. Operators who want a column layout can run the Ink
166
+ // renderer (`<StatusTable>`); the plain-text fallback stays
167
+ // narrow-terminal friendly without padding columns.
168
+ const lines = [];
169
+ lines.push('Pugi status');
170
+ lines.push('═'.repeat(50));
171
+ for (const field of snapshot.fields) {
172
+ lines.push(`${field.label}: ${field.value}`);
173
+ }
174
+ lines.push('');
175
+ lines.push(`CLI ${snapshot.meta.cliVersion} Node ${snapshot.meta.nodeVersion} cwd ${snapshot.meta.cwd}`);
176
+ return lines.join('\n');
177
+ }
178
+ /**
179
+ * Default home dir resolver. Centralised so the CLI handler can
180
+ * call `runStatusCommand` without re-importing `os.homedir`
181
+ * everywhere.
182
+ */
183
+ export function defaultStatusHome() {
184
+ return homedir();
185
+ }
186
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `pugi stickers` — brand-personality gimmick command ().
3
+ *
4
+ * Parity command with the upstream tool's `/stickers` easter egg. Claude
5
+ * ships physical stickers OR shows ASCII brand art; Pugi's variant
6
+ * focuses на the ASCII branch — picks one of the curated pug-face
7
+ * variants at random and footers it with a rotating brand quote.
8
+ *
9
+ * # Module contract
10
+ *
11
+ * - This file owns the WIRING from CLI flags + ambient state to the
12
+ * art + quote pickers. The corpus + pure renderers live in
13
+ * `tui/stickers-art.tsx` and have zero coupling к the CLI dispatch
14
+ * surface.
15
+ *
16
+ * - `runStickersCommand` is the single entry point. Both the top-
17
+ * level `pugi stickers` handler в `runtime/cli.ts` AND the in-REPL
18
+ * `/stickers` slash command call it. The function returns the
19
+ * resolved `StickersResult` so the slash dispatcher can route the
20
+ * output к the REPL's system pane without re-picking the art.
21
+ *
22
+ * - Exit code is ALWAYS 0. The command is a brand surface — never a
23
+ * gate. Even when the corpus is somehow exhausted (impossible
24
+ * today, the lists are frozen), the handler synthesises a fallback
25
+ * line and exits 0 instead of crashing.
26
+ *
27
+ * - The random source is injected so the spec can pin the chosen
28
+ * art + quote without monkey-patching `Math.random`.
29
+ */
30
+ import { PUG_QUOTES, PUG_STICKERS, pickArtVariant, pickQuote, renderPugStickersText, } from '../../tui/stickers-art.js';
31
+ /**
32
+ * Pick + render the sticker, hand it к the output sink, exit 0.
33
+ * Returns the picked result so the REPL slash handler can mount the
34
+ * Ink renderer without re-picking.
35
+ */
36
+ export function runStickersCommand(ctx) {
37
+ const rng = ctx.rng ?? Math.random;
38
+ let art;
39
+ let quote;
40
+ try {
41
+ art = pickArtVariant(rng);
42
+ quote = pickQuote(rng);
43
+ }
44
+ catch {
45
+ // Defensive: the pickers are pure + the corpus is frozen, so this
46
+ // path is unreachable in production. If a future refactor swaps
47
+ // the corpus к an empty array we still want к exit 0 with a
48
+ // deterministic fallback so monitoring scripts that probe
49
+ // `pugi stickers` do not flap.
50
+ art = {
51
+ id: 'fallback',
52
+ caption: 'fallback',
53
+ art: '/\\___/\\\n( o o )\n( =^= )',
54
+ };
55
+ quote = 'Pugi: your engineering co-pilot.';
56
+ }
57
+ const text = renderPugStickersText(art, quote);
58
+ const result = {
59
+ command: 'stickers',
60
+ art,
61
+ quote,
62
+ text,
63
+ meta: {
64
+ artVariants: PUG_STICKERS.length,
65
+ quotes: PUG_QUOTES.length,
66
+ },
67
+ };
68
+ ctx.writeOutput(result, text);
69
+ // Brand surface — never a gate. The caller may have set a different
70
+ // exit code (e.g. dispatch loop reused this handler for an inline
71
+ // brand banner); we explicitly stamp 0 so `pugi stickers && echo ok`
72
+ // stays predictable.
73
+ process.exitCode = 0;
74
+ // Silence the unused-import warning in the rare case `ctx.asciiOnly`
75
+ // is added in a future surface without a switch. Today the flag is
76
+ // honoured by the CLI handler choosing the writeOutput sink — the
77
+ // result.text is the same regardless because the boxed renderer
78
+ // never lands в the plain stdout sink.
79
+ void ctx.asciiOnly;
80
+ return result;
81
+ }
82
+ //# sourceMappingURL=stickers.js.map
@@ -0,0 +1,194 @@
1
+ /**
2
+ * — `pugi style` top-level command + REPL slash
3
+ * companion.
4
+ *
5
+ * Operator surface:
6
+ *
7
+ * pugi style Show active preset + table.
8
+ * pugi style <name> Switch workspace preset (current cwd).
9
+ * pugi style <name> --persist Switch + also write user default.
10
+ * pugi style --reset Clear workspace override → back to default.
11
+ * pugi style --reset --user Also clear the user default.
12
+ * pugi style --list Print the catalogue (no flip).
13
+ * pugi style --json Structured envelope variant.
14
+ *
15
+ * The same runner powers `/style` from inside the REPL. The REPL
16
+ * dispatcher (see `core/repl/session.ts`) routes through here so the
17
+ * two surfaces stay single-sourced — operators trained on one read
18
+ * the same payload + table on the other.
19
+ *
20
+ * Exit codes:
21
+ * 0 — show / switch / reset all succeed
22
+ * 1 — unknown preset slug (returned BEFORE any write)
23
+ * 2 — conflicting flags (e.g. `--reset` with a positional slug)
24
+ *
25
+ * The exit codes are surfaced through `process.exitCode` by the
26
+ * dispatcher in `cli.ts` — this module returns a structured payload
27
+ * + writes via the injected `writeOutput`. Throwing is reserved for
28
+ * truly unexpected errors (fs permissions etc.); the spec hooks the
29
+ * happy + sad paths through `writeOutput` shape, not via try/catch
30
+ * on the throw.
31
+ */
32
+ import { DEFAULT_OUTPUT_STYLE, isOutputStyleSlug, OUTPUT_STYLE_SLUGS, renderStyleTable, } from '../../core/output-style/presets.js';
33
+ import { clearUserOutputStyle, clearWorkspaceOutputStyle, resolveOutputStyle, setUserOutputStyle, setWorkspaceOutputStyle, } from '../../core/output-style/state.js';
34
+ /**
35
+ * Entry point. Parses `args`, applies the operation, emits the
36
+ * payload + text via `ctx.writeOutput`, and returns the exit code the
37
+ * dispatcher should hand back to the shell.
38
+ */
39
+ export async function runStyleCommand(args, ctx) {
40
+ const flags = parseFlags(args);
41
+ // Reset path
42
+ if (flags.reset) {
43
+ if (flags.slug !== null) {
44
+ const payload = buildPayload({
45
+ status: 'invalid_flags',
46
+ ctx,
47
+ message: '/style --reset cannot be combined with a preset name. Use one or the other.',
48
+ });
49
+ ctx.writeOutput(payload, payload.message);
50
+ return 2;
51
+ }
52
+ if (flags.persist) {
53
+ const payload = buildPayload({
54
+ status: 'invalid_flags',
55
+ ctx,
56
+ message: '/style --reset cannot be combined with --persist. Use --reset --user to also clear the user default.',
57
+ });
58
+ ctx.writeOutput(payload, payload.message);
59
+ return 2;
60
+ }
61
+ clearWorkspaceOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
62
+ if (flags.user) {
63
+ clearUserOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
64
+ }
65
+ const payload = buildPayload({
66
+ status: 'reset',
67
+ ctx,
68
+ message: flags.user
69
+ ? `Cleared workspace + user output style. Active: ${DEFAULT_OUTPUT_STYLE} (default).`
70
+ : `Cleared workspace output style. Active: ${describeActive(ctx)}.`,
71
+ });
72
+ ctx.writeOutput(payload, payload.message);
73
+ return 0;
74
+ }
75
+ // List path
76
+ if (flags.list && flags.slug === null) {
77
+ const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
78
+ const payload = buildPayload({
79
+ status: 'listed',
80
+ ctx,
81
+ message: renderStyleTable(resolved.slug),
82
+ });
83
+ ctx.writeOutput(payload, payload.message);
84
+ return 0;
85
+ }
86
+ // Switch path
87
+ if (flags.slug !== null) {
88
+ if (!isOutputStyleSlug(flags.slug)) {
89
+ const payload = buildPayload({
90
+ status: 'invalid_slug',
91
+ ctx,
92
+ attemptedSlug: flags.slug,
93
+ message: `Unknown style "${flags.slug}". Try one of: ${OUTPUT_STYLE_SLUGS.join(', ')}.`,
94
+ });
95
+ ctx.writeOutput(payload, payload.message);
96
+ return 1;
97
+ }
98
+ const before = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
99
+ setWorkspaceOutputStyle(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
100
+ if (flags.persist) {
101
+ setUserOutputStyle(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
102
+ }
103
+ const tail = flags.persist ? ' (workspace + user default)' : ' (workspace)';
104
+ const payload = buildPayload({
105
+ status: 'switched',
106
+ ctx,
107
+ previous: before.slug,
108
+ persistedToUser: flags.persist,
109
+ message: `Output style → ${flags.slug}${tail}. Was: ${before.slug} (${before.source}).`,
110
+ });
111
+ ctx.writeOutput(payload, payload.message);
112
+ return 0;
113
+ }
114
+ // Show path (no args)
115
+ const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
116
+ const banner = `Active output style: ${resolved.slug} (${resolved.source})`;
117
+ const table = renderStyleTable(resolved.slug);
118
+ const payload = buildPayload({
119
+ status: 'show',
120
+ ctx,
121
+ message: `${banner}\n\n${table}`,
122
+ });
123
+ ctx.writeOutput(payload, payload.message);
124
+ return 0;
125
+ }
126
+ function describeActive(ctx) {
127
+ const resolved = resolveOutputStyle({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
128
+ return `${resolved.slug} (${resolved.source})`;
129
+ }
130
+ function buildPayload(args) {
131
+ const resolved = resolveOutputStyle({ workspaceRoot: args.ctx.workspaceRoot, env: args.ctx.env });
132
+ const payload = {
133
+ command: 'style',
134
+ status: args.status,
135
+ active: resolved.slug,
136
+ source: resolved.source,
137
+ presets: OUTPUT_STYLE_SLUGS,
138
+ message: args.message,
139
+ };
140
+ if (args.previous !== undefined)
141
+ payload.previous = args.previous;
142
+ if (args.persistedToUser !== undefined)
143
+ payload.persistedToUser = args.persistedToUser;
144
+ if (args.attemptedSlug !== undefined)
145
+ payload.attemptedSlug = args.attemptedSlug;
146
+ return payload;
147
+ }
148
+ function parseFlags(args) {
149
+ const flags = {
150
+ slug: null,
151
+ persist: false,
152
+ reset: false,
153
+ user: false,
154
+ list: false,
155
+ };
156
+ for (const arg of args) {
157
+ if (arg === '--persist')
158
+ flags.persist = true;
159
+ else if (arg === '--reset')
160
+ flags.reset = true;
161
+ else if (arg === '--user')
162
+ flags.user = true;
163
+ else if (arg === '--list')
164
+ flags.list = true;
165
+ else if (arg.startsWith('-')) {
166
+ // Unknown flag — keep simple parser. Treat as positional so the
167
+ // downstream isOutputStyleSlug check rejects it with a clear
168
+ // "unknown style" message rather than swallowing silently.
169
+ if (flags.slug === null)
170
+ flags.slug = arg;
171
+ }
172
+ else if (flags.slug === null) {
173
+ flags.slug = arg;
174
+ }
175
+ }
176
+ // Normalise the slug to lowercase so `pugi style TERSE` works the
177
+ // same as `pugi style terse`. The catalogue is lowercase-only by
178
+ // contract; this keeps operators from tripping on shift-key habits.
179
+ if (flags.slug !== null)
180
+ flags.slug = flags.slug.toLowerCase();
181
+ return flags;
182
+ }
183
+ /**
184
+ * Re-export for the slash-command dispatcher in `core/repl/session.ts`
185
+ * so it can render a compiled prompt block when the operator runs
186
+ * `/style --preview` (follow-up surface; current slash returns the
187
+ * runner's standard payload).
188
+ *
189
+ * Kept here so the runtime module is the single import point for
190
+ * style-related surfaces; consumers should NOT reach into
191
+ * `core/output-style/*` directly.
192
+ */
193
+ export { OUTPUT_STYLES as OUTPUT_STYLE_CATALOGUE, } from '../../core/output-style/presets.js';
194
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1,196 @@
1
+ /**
2
+ * — `pugi theme` top-level command + REPL slash
3
+ * companion.
4
+ *
5
+ * Operator surface:
6
+ *
7
+ * pugi theme Show active theme + table.
8
+ * pugi theme <name> Switch workspace theme (current cwd).
9
+ * pugi theme <name> --persist Switch + also write user default.
10
+ * pugi theme --reset Clear workspace override → back to default.
11
+ * pugi theme --reset --user Also clear the user default.
12
+ * pugi theme --list Print the catalogue (no flip).
13
+ * pugi theme --json Structured envelope variant.
14
+ *
15
+ * The same runner powers `/theme` from inside the REPL. The REPL
16
+ * dispatcher (see `core/repl/session.ts`) routes through here so the
17
+ * two surfaces stay single-sourced — operators trained on one read
18
+ * the same payload + table on the other. Matches the
19
+ * `/style` runner exactly so the two settings surfaces are
20
+ * paste-comparable for the operator + grep-comparable for future
21
+ * maintenance.
22
+ *
23
+ * Exit codes:
24
+ * 0 — show / switch / reset all succeed
25
+ * 1 — unknown preset slug (returned BEFORE any write)
26
+ * 2 — conflicting flags (e.g. `--reset` with a positional slug)
27
+ *
28
+ * The exit codes are surfaced through `process.exitCode` by the
29
+ * dispatcher in `cli.ts` — this module returns a structured payload
30
+ * + writes via the injected `writeOutput`. Throwing is reserved for
31
+ * truly unexpected errors (fs permissions etc.); the spec hooks the
32
+ * happy + sad paths through `writeOutput` shape, not via try/catch
33
+ * on the throw.
34
+ */
35
+ import { DEFAULT_THEME, isThemeSlug, renderThemeTable, THEME_SLUGS, } from '../../core/theme/presets.js';
36
+ import { clearUserTheme, clearWorkspaceTheme, resolveTheme, setUserTheme, setWorkspaceTheme, } from '../../core/theme/state.js';
37
+ /**
38
+ * Entry point. Parses `args`, applies the operation, emits the
39
+ * payload + text via `ctx.writeOutput`, and returns the exit code the
40
+ * dispatcher should hand back to the shell.
41
+ */
42
+ export async function runThemeCommand(args, ctx) {
43
+ const flags = parseFlags(args);
44
+ // Reset path
45
+ if (flags.reset) {
46
+ if (flags.slug !== null) {
47
+ const payload = buildPayload({
48
+ status: 'invalid_flags',
49
+ ctx,
50
+ message: '/theme --reset cannot be combined with a preset name. Use one or the other.',
51
+ });
52
+ ctx.writeOutput(payload, payload.message);
53
+ return 2;
54
+ }
55
+ if (flags.persist) {
56
+ const payload = buildPayload({
57
+ status: 'invalid_flags',
58
+ ctx,
59
+ message: '/theme --reset cannot be combined with --persist. Use --reset --user to also clear the user default.',
60
+ });
61
+ ctx.writeOutput(payload, payload.message);
62
+ return 2;
63
+ }
64
+ clearWorkspaceTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
65
+ if (flags.user) {
66
+ clearUserTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
67
+ }
68
+ const payload = buildPayload({
69
+ status: 'reset',
70
+ ctx,
71
+ message: flags.user
72
+ ? `Cleared workspace + user theme. Active: ${DEFAULT_THEME} (default).`
73
+ : `Cleared workspace theme. Active: ${describeActive(ctx)}.`,
74
+ });
75
+ ctx.writeOutput(payload, payload.message);
76
+ return 0;
77
+ }
78
+ // List path
79
+ if (flags.list && flags.slug === null) {
80
+ const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
81
+ const payload = buildPayload({
82
+ status: 'listed',
83
+ ctx,
84
+ message: renderThemeTable(resolved.slug),
85
+ });
86
+ ctx.writeOutput(payload, payload.message);
87
+ return 0;
88
+ }
89
+ // Switch path
90
+ if (flags.slug !== null) {
91
+ if (!isThemeSlug(flags.slug)) {
92
+ const payload = buildPayload({
93
+ status: 'invalid_slug',
94
+ ctx,
95
+ attemptedSlug: flags.slug,
96
+ message: `Unknown theme "${flags.slug}". Try one of: ${THEME_SLUGS.join(', ')}.`,
97
+ });
98
+ ctx.writeOutput(payload, payload.message);
99
+ return 1;
100
+ }
101
+ const before = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
102
+ setWorkspaceTheme(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
103
+ if (flags.persist) {
104
+ setUserTheme(flags.slug, { workspaceRoot: ctx.workspaceRoot, env: ctx.env });
105
+ }
106
+ const tail = flags.persist ? ' (workspace + user default)' : ' (workspace)';
107
+ const payload = buildPayload({
108
+ status: 'switched',
109
+ ctx,
110
+ previous: before.slug,
111
+ persistedToUser: flags.persist,
112
+ message: `Theme → ${flags.slug}${tail}. Was: ${before.slug} (${before.source}).`,
113
+ });
114
+ ctx.writeOutput(payload, payload.message);
115
+ return 0;
116
+ }
117
+ // Show path (no args)
118
+ const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
119
+ const banner = `Active theme: ${resolved.slug} (${resolved.source})`;
120
+ const table = renderThemeTable(resolved.slug);
121
+ const payload = buildPayload({
122
+ status: 'show',
123
+ ctx,
124
+ message: `${banner}\n\n${table}`,
125
+ });
126
+ ctx.writeOutput(payload, payload.message);
127
+ return 0;
128
+ }
129
+ function describeActive(ctx) {
130
+ const resolved = resolveTheme({ workspaceRoot: ctx.workspaceRoot, env: ctx.env });
131
+ return `${resolved.slug} (${resolved.source})`;
132
+ }
133
+ function buildPayload(args) {
134
+ const resolved = resolveTheme({ workspaceRoot: args.ctx.workspaceRoot, env: args.ctx.env });
135
+ const payload = {
136
+ command: 'theme',
137
+ status: args.status,
138
+ active: resolved.slug,
139
+ source: resolved.source,
140
+ presets: THEME_SLUGS,
141
+ message: args.message,
142
+ };
143
+ if (args.previous !== undefined)
144
+ payload.previous = args.previous;
145
+ if (args.persistedToUser !== undefined)
146
+ payload.persistedToUser = args.persistedToUser;
147
+ if (args.attemptedSlug !== undefined)
148
+ payload.attemptedSlug = args.attemptedSlug;
149
+ return payload;
150
+ }
151
+ function parseFlags(args) {
152
+ const flags = {
153
+ slug: null,
154
+ persist: false,
155
+ reset: false,
156
+ user: false,
157
+ list: false,
158
+ };
159
+ for (const arg of args) {
160
+ if (arg === '--persist')
161
+ flags.persist = true;
162
+ else if (arg === '--reset')
163
+ flags.reset = true;
164
+ else if (arg === '--user')
165
+ flags.user = true;
166
+ else if (arg === '--list')
167
+ flags.list = true;
168
+ else if (arg.startsWith('-')) {
169
+ // Unknown flag — keep simple parser. Treat as positional so the
170
+ // downstream isThemeSlug check rejects it with a clear "unknown
171
+ // theme" message rather than swallowing silently. Mirrors the
172
+ // L18 style runner's behaviour for grep-parity.
173
+ if (flags.slug === null)
174
+ flags.slug = arg;
175
+ }
176
+ else if (flags.slug === null) {
177
+ flags.slug = arg;
178
+ }
179
+ }
180
+ // Normalise the slug to lowercase so `pugi theme DARK` works the
181
+ // same as `pugi theme dark`. The catalogue is lowercase-only by
182
+ // contract; this keeps operators from tripping on shift-key habits.
183
+ if (flags.slug !== null)
184
+ flags.slug = flags.slug.toLowerCase();
185
+ return flags;
186
+ }
187
+ /**
188
+ * Re-export for the slash-command dispatcher in
189
+ * `core/repl/session.ts` so it can render a preview block if a
190
+ * future surface (`/theme --preview`) lands. Kept here so the
191
+ * runtime module is the single import point for theme-related
192
+ * surfaces; consumers should NOT reach into `core/theme/*` directly
193
+ * unless they are Ink components that need the React context.
194
+ */
195
+ export { THEMES as THEME_CATALOGUE, } from '../../core/theme/presets.js';
196
+ //# sourceMappingURL=theme.js.map