@pugi/cli 0.1.0-beta.7 → 0.1.0-beta.87

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 (402) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/THIRD_PARTY_NOTICES.md +40 -0
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +2 -2
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +12 -12
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +293 -7
  94. package/dist/core/edits/format-matrix.js +26 -0
  95. package/dist/core/edits/fuzzy-ladder.js +650 -0
  96. package/dist/core/edits/index.js +3 -1
  97. package/dist/core/edits/journal.js +199 -0
  98. package/dist/core/edits/layer-a-apply.js +15 -15
  99. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  100. package/dist/core/edits/layer-b-apply.js +9 -9
  101. package/dist/core/edits/layer-c-apply.js +6 -6
  102. package/dist/core/edits/layer-d-ast.js +557 -14
  103. package/dist/core/edits/marker-parser.js +12 -12
  104. package/dist/core/edits/security-gate.js +27 -27
  105. package/dist/core/edits/verify-hook.js +273 -0
  106. package/dist/core/edits/worktree.js +322 -0
  107. package/dist/core/engine/anvil-client.js +140 -26
  108. package/dist/core/engine/auto-compact.js +179 -0
  109. package/dist/core/engine/budgets.js +186 -0
  110. package/dist/core/engine/context-prefix.js +155 -0
  111. package/dist/core/engine/index.js +1 -1
  112. package/dist/core/engine/intensity.js +158 -0
  113. package/dist/core/engine/intent.js +260 -0
  114. package/dist/core/engine/native-pugi.js +1295 -227
  115. package/dist/core/engine/prompts.js +134 -16
  116. package/dist/core/engine/strip-internal-fields.js +124 -0
  117. package/dist/core/engine/tool-bridge.js +1295 -59
  118. package/dist/core/evaluation/golden-dataset.js +293 -0
  119. package/dist/core/feedback/queue.js +177 -0
  120. package/dist/core/feedback/submitter.js +145 -0
  121. package/dist/core/file-cache.js +113 -1
  122. package/dist/core/flatten/flatten-repo.js +439 -0
  123. package/dist/core/format/osc8-link.js +28 -0
  124. package/dist/core/hook-chains.js +392 -0
  125. package/dist/core/hooks/citation-verify-hook.js +138 -0
  126. package/dist/core/hooks/citation-verify.js +112 -0
  127. package/dist/core/hooks/events.js +44 -0
  128. package/dist/core/hooks/index.js +15 -0
  129. package/dist/core/hooks/registry.js +213 -0
  130. package/dist/core/hooks/runner.js +236 -0
  131. package/dist/core/hooks/v2/event-emitter.js +115 -0
  132. package/dist/core/hooks/v2/executor.js +282 -0
  133. package/dist/core/hooks/v2/index.js +25 -0
  134. package/dist/core/hooks/v2/lifecycle.js +104 -0
  135. package/dist/core/hooks/v2/loader.js +216 -0
  136. package/dist/core/hooks/v2/matcher.js +125 -0
  137. package/dist/core/hooks/v2/trust.js +143 -0
  138. package/dist/core/hooks/v2/types.js +86 -0
  139. package/dist/core/image/renderer.js +71 -0
  140. package/dist/core/init/detector.js +582 -0
  141. package/dist/core/init/template-renderer.js +242 -0
  142. package/dist/core/jobs/registry.js +18 -18
  143. package/dist/core/ledger/results-tsv.js +142 -0
  144. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  145. package/dist/core/lsp/cache.js +105 -0
  146. package/dist/core/lsp/client.js +776 -0
  147. package/dist/core/lsp/language-detect.js +66 -0
  148. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  149. package/dist/core/lsp/symbol-tools.js +372 -0
  150. package/dist/core/mcp/client.js +97 -28
  151. package/dist/core/mcp/http-server.js +553 -0
  152. package/dist/core/mcp/orchestrator-tools.js +662 -0
  153. package/dist/core/mcp/permission.js +190 -0
  154. package/dist/core/mcp/registry.js +39 -17
  155. package/dist/core/mcp/server-tools.js +219 -0
  156. package/dist/core/mcp/server.js +397 -0
  157. package/dist/core/mcp/trust.js +10 -10
  158. package/dist/core/memory/dual-write.js +416 -0
  159. package/dist/core/memory/passive-extract.js +130 -0
  160. package/dist/core/memory/phase1-kinds.js +20 -0
  161. package/dist/core/memory/secret-scanner.js +304 -0
  162. package/dist/core/memory-sync/queue.js +170 -0
  163. package/dist/core/metrics/extract.js +113 -0
  164. package/dist/core/modes/roo-modes.js +68 -0
  165. package/dist/core/onboarding/ensure-initialized.js +133 -0
  166. package/dist/core/onboarding/marker.js +111 -0
  167. package/dist/core/onboarding/telemetry-state.js +108 -0
  168. package/dist/core/output-style/presets.js +176 -0
  169. package/dist/core/output-style/state.js +185 -0
  170. package/dist/core/path-security.js +287 -5
  171. package/dist/core/permission.js +82 -22
  172. package/dist/core/permissions/auto-classifier.js +124 -0
  173. package/dist/core/permissions/bash-parser.js +371 -0
  174. package/dist/core/permissions/circuit-breaker.js +83 -0
  175. package/dist/core/permissions/constrained-edit.js +91 -0
  176. package/dist/core/permissions/gate.js +278 -0
  177. package/dist/core/permissions/index.js +20 -0
  178. package/dist/core/permissions/mode.js +174 -0
  179. package/dist/core/permissions/network-egress.js +137 -0
  180. package/dist/core/permissions/state.js +241 -0
  181. package/dist/core/permissions/tool-class.js +93 -0
  182. package/dist/core/plan-mode/ui-state.js +51 -0
  183. package/dist/core/plans/plan-artifact.js +721 -0
  184. package/dist/core/policy-limits/etag-store.js +122 -0
  185. package/dist/core/prd-check/parser.js +215 -0
  186. package/dist/core/prd-check/reporter.js +127 -0
  187. package/dist/core/prd-check/session-review.js +557 -0
  188. package/dist/core/prd-check/verifiers.js +223 -0
  189. package/dist/core/prompt-cache/client-cache.js +99 -0
  190. package/dist/core/prompts/assembly.js +29 -0
  191. package/dist/core/prompts/registry.js +364 -0
  192. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  193. package/dist/core/pugi-md/context-injector.js +76 -0
  194. package/dist/core/pugi-md/walk-up.js +207 -0
  195. package/dist/core/python/uv-installer.js +270 -0
  196. package/dist/core/python/uv-resolver.js +83 -0
  197. package/dist/core/rate-limit/narrator.js +146 -0
  198. package/dist/core/recipes/cli-types.js +20 -0
  199. package/dist/core/recipes/loader.js +103 -0
  200. package/dist/core/recipes/runner.js +345 -0
  201. package/dist/core/recipes/schema.js +587 -0
  202. package/dist/core/release-notes/parser.js +241 -0
  203. package/dist/core/release-notes/state.js +116 -0
  204. package/dist/core/repl/ask.js +37 -37
  205. package/dist/core/repl/cancellation.js +26 -26
  206. package/dist/core/repl/cap-warning.js +4 -4
  207. package/dist/core/repl/clipboard-read.js +11 -11
  208. package/dist/core/repl/dispatch-fsm.js +12 -12
  209. package/dist/core/repl/history-search.js +15 -15
  210. package/dist/core/repl/history.js +28 -18
  211. package/dist/core/repl/kill-ring.js +5 -5
  212. package/dist/core/repl/model-pricing.js +135 -0
  213. package/dist/core/repl/privacy-banner.js +22 -22
  214. package/dist/core/repl/session.js +2157 -214
  215. package/dist/core/repl/slash-commands.js +533 -40
  216. package/dist/core/repl/store/index.js +1 -1
  217. package/dist/core/repl/store/jsonl-log.js +22 -22
  218. package/dist/core/repl/store/lockfile.js +10 -10
  219. package/dist/core/repl/store/session-store.js +136 -107
  220. package/dist/core/repl/store/types.js +15 -15
  221. package/dist/core/repl/store/uuid-v7.js +12 -12
  222. package/dist/core/repl/workspace-context.js +43 -21
  223. package/dist/core/repo-map/build.js +125 -0
  224. package/dist/core/repo-map/cache.js +185 -0
  225. package/dist/core/repo-map/extractor.js +254 -0
  226. package/dist/core/repo-map/formatter.js +145 -0
  227. package/dist/core/repo-map/page-rank.js +105 -0
  228. package/dist/core/repo-map/scanner.js +211 -0
  229. package/dist/core/retry-budget/budget.js +284 -0
  230. package/dist/core/retry-budget/index.js +5 -0
  231. package/dist/core/retry-budget/retry-cap.js +74 -0
  232. package/dist/core/routing/lead-worker.js +43 -0
  233. package/dist/core/routing/pre-flight-estimator.js +108 -0
  234. package/dist/core/runs/run-tree.js +103 -0
  235. package/dist/core/security/injection-scanner.js +367 -0
  236. package/dist/core/security/output-filter.js +418 -0
  237. package/dist/core/session/env-file.js +105 -0
  238. package/dist/core/session/section-budgets.js +140 -0
  239. package/dist/core/session.js +92 -0
  240. package/dist/core/settings.js +286 -5
  241. package/dist/core/share/formatter.js +271 -0
  242. package/dist/core/share/redactor.js +221 -0
  243. package/dist/core/share/uploader.js +267 -0
  244. package/dist/core/skills/defaults.js +457 -0
  245. package/dist/core/skills/loader.js +22 -22
  246. package/dist/core/skills/sources.js +27 -27
  247. package/dist/core/smoke/headless-driver.js +174 -0
  248. package/dist/core/smoke/orchestrator.js +194 -0
  249. package/dist/core/smoke/runner.js +238 -0
  250. package/dist/core/smoke/scenario-parser.js +316 -0
  251. package/dist/core/statusline.js +99 -0
  252. package/dist/core/subagents/dispatcher-real.js +600 -0
  253. package/dist/core/subagents/dispatcher.js +132 -43
  254. package/dist/core/subagents/index.js +19 -6
  255. package/dist/core/subagents/isolation-matrix.js +213 -0
  256. package/dist/core/subagents/spawn.js +19 -4
  257. package/dist/core/telemetry/emitter.js +229 -0
  258. package/dist/core/telemetry/queue.js +251 -0
  259. package/dist/core/theme/context.js +91 -0
  260. package/dist/core/theme/presets.js +228 -0
  261. package/dist/core/theme/state.js +181 -0
  262. package/dist/core/todos/invariant.js +10 -0
  263. package/dist/core/todos/state.js +177 -0
  264. package/dist/core/tool-schema/compressor.js +89 -0
  265. package/dist/core/transport/version-interceptor.js +166 -0
  266. package/dist/core/trust.js +2 -2
  267. package/dist/core/tui/thinking-block.js +64 -0
  268. package/dist/core/vim/keymap.js +288 -0
  269. package/dist/core/vim/state.js +92 -0
  270. package/dist/core/watch-markers/marker-watcher.js +133 -0
  271. package/dist/core/worktree-manager/cleanup.js +123 -0
  272. package/dist/core/worktree-manager/manager.js +303 -0
  273. package/dist/index.js +28 -0
  274. package/dist/runtime/bootstrap.js +190 -0
  275. package/dist/runtime/cli.js +4162 -488
  276. package/dist/runtime/commands/agents.js +30 -30
  277. package/dist/runtime/commands/budget.js +5 -5
  278. package/dist/runtime/commands/cancel.js +231 -0
  279. package/dist/runtime/commands/chain.js +489 -0
  280. package/dist/runtime/commands/codegraph-status.js +227 -0
  281. package/dist/runtime/commands/compact.js +297 -0
  282. package/dist/runtime/commands/config.js +32 -32
  283. package/dist/runtime/commands/cost.js +199 -0
  284. package/dist/runtime/commands/delegate.js +244 -13
  285. package/dist/runtime/commands/dispatch.js +126 -0
  286. package/dist/runtime/commands/doctor.js +579 -0
  287. package/dist/runtime/commands/feedback.js +184 -0
  288. package/dist/runtime/commands/hooks.js +184 -0
  289. package/dist/runtime/commands/init.js +254 -0
  290. package/dist/runtime/commands/lsp.js +368 -0
  291. package/dist/runtime/commands/mcp.js +879 -0
  292. package/dist/runtime/commands/memory.js +582 -0
  293. package/dist/runtime/commands/model.js +237 -0
  294. package/dist/runtime/commands/onboarding.js +275 -0
  295. package/dist/runtime/commands/patch.js +128 -0
  296. package/dist/runtime/commands/permissions.js +112 -0
  297. package/dist/runtime/commands/plan.js +143 -0
  298. package/dist/runtime/commands/prd-check.js +285 -0
  299. package/dist/runtime/commands/privacy.js +17 -17
  300. package/dist/runtime/commands/recipe.js +325 -0
  301. package/dist/runtime/commands/redo-blob-store.js +92 -0
  302. package/dist/runtime/commands/redo.js +361 -0
  303. package/dist/runtime/commands/release-notes.js +229 -0
  304. package/dist/runtime/commands/repo-map.js +95 -0
  305. package/dist/runtime/commands/report.js +299 -0
  306. package/dist/runtime/commands/resume.js +118 -0
  307. package/dist/runtime/commands/review-consensus.js +68 -53
  308. package/dist/runtime/commands/rewind.js +333 -0
  309. package/dist/runtime/commands/roster.js +14 -14
  310. package/dist/runtime/commands/sessions.js +163 -0
  311. package/dist/runtime/commands/share.js +316 -0
  312. package/dist/runtime/commands/skills.js +31 -31
  313. package/dist/runtime/commands/status.js +186 -0
  314. package/dist/runtime/commands/stickers.js +82 -0
  315. package/dist/runtime/commands/style.js +194 -0
  316. package/dist/runtime/commands/theme.js +196 -0
  317. package/dist/runtime/commands/undo.js +54 -22
  318. package/dist/runtime/commands/update.js +289 -0
  319. package/dist/runtime/commands/vim.js +140 -0
  320. package/dist/runtime/commands/worktree.js +177 -0
  321. package/dist/runtime/commands/worktrees.js +155 -0
  322. package/dist/runtime/headless-repl.js +195 -0
  323. package/dist/runtime/headless.js +543 -0
  324. package/dist/runtime/load-hooks-or-exit.js +71 -0
  325. package/dist/runtime/plan-decompose.js +531 -0
  326. package/dist/runtime/update-check.js +28 -28
  327. package/dist/runtime/version.js +65 -0
  328. package/dist/skills/bundled/batch.js +617 -0
  329. package/dist/skills/bundled/index.js +45 -0
  330. package/dist/skills/bundled/loop.js +358 -0
  331. package/dist/skills/bundled/remember.js +383 -0
  332. package/dist/skills/bundled/simplify.js +289 -0
  333. package/dist/skills/bundled/skillify.js +373 -0
  334. package/dist/skills/bundled/stuck.js +558 -0
  335. package/dist/skills/bundled/verify.js +439 -0
  336. package/dist/testing/vcr.js +486 -0
  337. package/dist/tools/agent-tool.js +229 -0
  338. package/dist/tools/apply-patch.js +556 -0
  339. package/dist/tools/ask-user-question.js +222 -0
  340. package/dist/tools/ask-user.js +115 -0
  341. package/dist/tools/bash.js +623 -45
  342. package/dist/tools/brief.js +224 -0
  343. package/dist/tools/enter-worktree.js +250 -0
  344. package/dist/tools/exit-worktree.js +147 -0
  345. package/dist/tools/file-tools.js +161 -44
  346. package/dist/tools/lsp-tools.js +189 -0
  347. package/dist/tools/mcp-tool.js +260 -0
  348. package/dist/tools/multi-edit.js +361 -0
  349. package/dist/tools/powershell.js +268 -0
  350. package/dist/tools/registry.js +85 -0
  351. package/dist/tools/skill-tool.js +96 -0
  352. package/dist/tools/sleep.js +99 -0
  353. package/dist/tools/synthetic-output.js +133 -0
  354. package/dist/tools/tasks.js +208 -0
  355. package/dist/tools/todo-write.js +184 -0
  356. package/dist/tools/verify-plan-execution.js +295 -0
  357. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  358. package/dist/tools/web-fetch.js +195 -10
  359. package/dist/tools/web-search.js +458 -0
  360. package/dist/tui/agent-progress-card.js +111 -0
  361. package/dist/tui/agent-tree.js +11 -1
  362. package/dist/tui/ask-modal.js +14 -14
  363. package/dist/tui/ask-user-question-prompt.js +203 -0
  364. package/dist/tui/compact-banner.js +81 -0
  365. package/dist/tui/conversation-pane.js +85 -11
  366. package/dist/tui/cost-table.js +111 -0
  367. package/dist/tui/device-flow.js +2 -2
  368. package/dist/tui/doctor-table.js +46 -0
  369. package/dist/tui/feedback-prompt.js +156 -0
  370. package/dist/tui/input-box.js +247 -32
  371. package/dist/tui/login-picker.js +3 -3
  372. package/dist/tui/markdown-render.js +6 -6
  373. package/dist/tui/onboarding-wizard.js +240 -0
  374. package/dist/tui/permissions-picker.js +86 -0
  375. package/dist/tui/render.js +35 -0
  376. package/dist/tui/repl-render.js +332 -54
  377. package/dist/tui/repl-splash-art.js +16 -16
  378. package/dist/tui/repl-splash-mascot.js +48 -24
  379. package/dist/tui/repl-splash.js +22 -22
  380. package/dist/tui/repl.js +124 -44
  381. package/dist/tui/slash-palette.js +6 -6
  382. package/dist/tui/splash.js +2 -2
  383. package/dist/tui/status-bar.js +109 -31
  384. package/dist/tui/status-table.js +7 -0
  385. package/dist/tui/stickers-art.js +136 -0
  386. package/dist/tui/style-table.js +28 -0
  387. package/dist/tui/theme-table.js +29 -0
  388. package/dist/tui/thinking-spinner.js +123 -0
  389. package/dist/tui/tool-stream-pane.js +53 -4
  390. package/dist/tui/update-banner.js +27 -2
  391. package/dist/tui/vim-input.js +267 -0
  392. package/dist/tui/welcome-banner.js +107 -0
  393. package/dist/tui/welcome-data.js +293 -0
  394. package/dist/tui/workspace-context.js +2 -2
  395. package/docs/examples/codegraph.mcp.json +10 -0
  396. package/package.json +23 -6
  397. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  398. package/test/scenarios/compact-force.scenario.txt +11 -0
  399. package/test/scenarios/identity.scenario.txt +11 -0
  400. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  401. package/test/scenarios/walkback.scenario.txt +12 -0
  402. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Watch-mode inline marker scanner — Aider AI?/AI! pattern.
3
+ *
4
+ * Watches a directory tree via chokidar (already в deps — used by
5
+ * VS Code и most JS file-watchers). When a file changes, scans the
6
+ * delta для inline markers и emits structured events. Pugi-flavoured
7
+ * marker pair:
8
+ *
9
+ * // PUGI? → operator asks Pugi a question right here
10
+ * // PUGI! → operator instructs Pugi to act right here
11
+ *
12
+ * Also accepts Aider-compatible `// AI?` / `// AI!` for muscle-memory
13
+ * parity. Block comments and `# PUGI?` (Python / shell) are recognised
14
+ * too — language is detected from extension, NOT parsed properly. The
15
+ * goal is opportunistic ergonomics, not a precise parser.
16
+ *
17
+ * No tool dispatch happens here — this module emits events; downstream
18
+ * code (engine integration follow-up) decides what to do.
19
+ */
20
+ import chokidar from 'chokidar';
21
+ import { readFile } from 'node:fs/promises';
22
+ import { EventEmitter } from 'node:events';
23
+ import path from 'node:path';
24
+ const DEFAULT_IGNORED = [
25
+ '**/node_modules/**',
26
+ '**/.git/**',
27
+ '**/dist/**',
28
+ '**/build/**',
29
+ '**/coverage/**',
30
+ '**/.next/**',
31
+ '**/.pugi/**',
32
+ ];
33
+ const DEFAULT_MAX_FILE_BYTES = 256 * 1024;
34
+ // Recognises `// PUGI?`, `# PUGI!`, `/* AI? ... */`, `<!-- PUGI! ... -->` and equivalents.
35
+ // Capture group 1 = comment prefix, 2 = tag (PUGI | AI), 3 = symbol (? | !), 4 = trailing text.
36
+ const MARKER_RE = /(?:\/\/|#|\/\*|<!--)\s*(PUGI|AI)([?!])\s*([^\n*-]*?)(?:\*\/|-->|$)/g;
37
+ export class MarkerWatcher extends EventEmitter {
38
+ options;
39
+ watcher = null;
40
+ maxFileBytes;
41
+ constructor(options = {}) {
42
+ super();
43
+ this.options = options;
44
+ this.maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
45
+ }
46
+ /**
47
+ * Start watching `patterns`. Multiple calls without close() throw —
48
+ * one instance, one active watcher.
49
+ */
50
+ start(patterns) {
51
+ if (this.watcher) {
52
+ throw new Error('MarkerWatcher already started — call close() first');
53
+ }
54
+ const ignored = [...(this.options.ignored ?? DEFAULT_IGNORED)];
55
+ const opts = {
56
+ ignored,
57
+ ignoreInitial: this.options.ignoreInitial ?? true,
58
+ persistent: true,
59
+ };
60
+ if (this.options.cwd !== undefined) {
61
+ opts.cwd = this.options.cwd;
62
+ }
63
+ this.watcher = chokidar.watch(patterns, opts);
64
+ this.watcher.on('add', p => void this.scan(p));
65
+ this.watcher.on('change', p => void this.scan(p));
66
+ this.watcher.on('error', err => this.emit('error', err));
67
+ }
68
+ /** Stop watching и release file handles. Safe to call multiple times. */
69
+ async close() {
70
+ if (!this.watcher)
71
+ return;
72
+ await this.watcher.close();
73
+ this.watcher = null;
74
+ }
75
+ /**
76
+ * Scan a single file synchronously — bypasses the watcher entirely.
77
+ * Useful для one-shot scans / tests / initial-load passes.
78
+ */
79
+ async scanOnce(file) {
80
+ return this.collectFromFile(file);
81
+ }
82
+ async scan(file) {
83
+ const hits = await this.collectFromFile(file);
84
+ for (const hit of hits) {
85
+ this.emit('marker', hit);
86
+ }
87
+ }
88
+ async collectFromFile(file) {
89
+ let content;
90
+ // chokidar emits paths relative to its `cwd` option when set. Resolve
91
+ // so readFile doesn't fall back to the process CWD и miss the file.
92
+ const resolved = this.options.cwd && !path.isAbsolute(file)
93
+ ? path.join(this.options.cwd, file)
94
+ : file;
95
+ try {
96
+ const buf = await readFile(resolved);
97
+ if (buf.byteLength > this.maxFileBytes)
98
+ return [];
99
+ content = buf.toString('utf8');
100
+ }
101
+ catch (err) {
102
+ // File deleted between event and read, permissions, etc.
103
+ this.emit('error', err);
104
+ return [];
105
+ }
106
+ const hits = [];
107
+ const lines = content.split('\n');
108
+ for (let i = 0; i < lines.length; i += 1) {
109
+ const lineText = lines[i] ?? '';
110
+ MARKER_RE.lastIndex = 0;
111
+ let match;
112
+ while ((match = MARKER_RE.exec(lineText)) !== null) {
113
+ const tag = match[1] ?? 'PUGI';
114
+ const symbol = match[2] ?? '?';
115
+ const text = (match[3] ?? '').trim();
116
+ hits.push({
117
+ file,
118
+ line: i + 1,
119
+ kind: symbol === '!' ? 'instruction' : 'question',
120
+ tag,
121
+ text,
122
+ });
123
+ }
124
+ }
125
+ return hits;
126
+ }
127
+ }
128
+ /** Standalone scan helper для callers that don't want a long-lived watcher. */
129
+ export async function scanFileForMarkers(file, options = {}) {
130
+ const watcher = new MarkerWatcher({ maxFileBytes: options.maxFileBytes });
131
+ return watcher.scanOnce(file);
132
+ }
133
+ //# sourceMappingURL=marker-watcher.js.map
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Stale-worktree sweep — .
3
+ *
4
+ * Pairs with `WorktreeManager` to find worktrees whose agent has either:
5
+ *
6
+ * 1. No corresponding progress JSON under `.pugi/agent-progress/` (the
7
+ * writer never wrote one, OR the file was archived away), OR
8
+ * 2. A progress JSON whose `status` is `completed` or `failed` (the
9
+ * agent finished — the worktree is no longer load-bearing).
10
+ *
11
+ * Crashed agents (status `running` with a stale lastUpdate) are NOT
12
+ * swept; the leak-research contract says we LEAVE worktrees of crashed
13
+ * agents in place for forensics. The operator can `pugi worktrees
14
+ * cleanup <id>` manually after the post-mortem.
15
+ *
16
+ * Implementation notes:
17
+ *
18
+ * - The cleanup is read-then-write: list the manager state, classify
19
+ * each entry, then call `manager.cleanup()` per stale id. Errors per
20
+ * entry never abort the loop — they accumulate in the report.
21
+ * - The progress directory we look at is the SAME `.pugi/agent-progress/`
22
+ * root the writer uses (resolved via the writer's env-aware helper).
23
+ * - `runStaleCleanup` is the only public entry point. The internal
24
+ * classifier is exported as `classifyStale` for spec coverage.
25
+ *
26
+ * Brand voice: ASCII only, no emoji, no banned words.
27
+ */
28
+ import { existsSync, readFileSync } from 'node:fs';
29
+ import { join } from 'node:path';
30
+ import { resolveProgressDir } from '../agent-progress/writer.js';
31
+ import { validateAgentProgress } from '../agent-progress/schema.js';
32
+ import { WorktreeManager, } from './manager.js';
33
+ /**
34
+ * Classify a single worktree listing against the on-disk progress JSON.
35
+ * Pure-ish — only reads from the progress file when one exists. Exposed
36
+ * for the spec; production callers go through `runStaleCleanup`.
37
+ */
38
+ export function classifyStale(listing, progressDir, options = {}) {
39
+ const grace = options.crashedGraceMs ?? 30 * 60 * 1000;
40
+ const now = options.now ?? Date.now;
41
+ const progressPath = join(progressDir, `${listing.agentId}.json`);
42
+ if (!existsSync(progressPath)) {
43
+ return 'orphan_no_progress';
44
+ }
45
+ let raw;
46
+ try {
47
+ raw = readFileSync(progressPath, 'utf8');
48
+ }
49
+ catch {
50
+ // Unreadable progress file — treat as orphan so the operator can
51
+ // see it in the cleanup report. We never act on it silently.
52
+ return 'orphan_no_progress';
53
+ }
54
+ let parsed;
55
+ try {
56
+ parsed = JSON.parse(raw);
57
+ }
58
+ catch {
59
+ return 'orphan_no_progress';
60
+ }
61
+ const validation = validateAgentProgress(parsed);
62
+ if (!validation.ok)
63
+ return 'orphan_no_progress';
64
+ const progress = validation.value;
65
+ if (progress.status === 'completed')
66
+ return 'stale_completed';
67
+ if (progress.status === 'failed')
68
+ return 'stale_failed';
69
+ // status === 'running' — check for crashed (stale lastUpdate)
70
+ const lastTs = Date.parse(progress.lastUpdate);
71
+ if (Number.isFinite(lastTs) && now() - lastTs > grace) {
72
+ return 'crashed_running';
73
+ }
74
+ return 'active';
75
+ }
76
+ /**
77
+ * Run a single stale-worktree sweep. Removes only `stale_completed`,
78
+ * `stale_failed`, and `orphan_no_progress` entries. Crashed-running
79
+ * entries are LEFT IN PLACE per the L23 forensics contract, but they
80
+ * appear in the report so the operator can act on them manually.
81
+ */
82
+ export function runStaleCleanup(managerOpts, options = {}) {
83
+ const manager = new WorktreeManager(managerOpts);
84
+ const report = {
85
+ scanned: 0,
86
+ classified: [],
87
+ removed: [],
88
+ preserved: [],
89
+ errors: [],
90
+ };
91
+ const listResult = manager.list();
92
+ if (!listResult.ok) {
93
+ report.errors.push({ agentId: '', detail: `list failed: ${listResult.detail}` });
94
+ return report;
95
+ }
96
+ const progressDir = options.progressDir ?? resolveProgressDir();
97
+ const listings = listResult.value;
98
+ report.scanned = listings.length;
99
+ for (const entry of listings) {
100
+ const cls = classifyStale(entry, progressDir, options);
101
+ report.classified.push({ agentId: entry.agentId, path: entry.path, class: cls });
102
+ if (cls === 'active' || cls === 'crashed_running') {
103
+ report.preserved.push({ agentId: entry.agentId, reason: cls });
104
+ continue;
105
+ }
106
+ if (options.dryRun) {
107
+ // Dry-run reports the classification only — no cleanup, no remove.
108
+ continue;
109
+ }
110
+ const cleanup = manager.cleanup(entry.agentId);
111
+ if (cleanup.ok) {
112
+ report.removed.push(entry.path);
113
+ }
114
+ else {
115
+ report.errors.push({
116
+ agentId: entry.agentId,
117
+ detail: `${cleanup.reason}: ${cleanup.detail}`,
118
+ });
119
+ }
120
+ }
121
+ return report;
122
+ }
123
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1,303 @@
1
+ /**
2
+ * WorktreeManager — .
3
+ *
4
+ * the upstream tool spawns a separate git worktree per dispatched sub-agent so
5
+ * parallel agents never collide on the filesystem. This module is Pugi's
6
+ * parity surface: it binds a worktree to an `agentId` (the same kebab-case
7
+ * id the agent-progress JSON uses) and exposes a tiny CRUD-shaped API
8
+ * the dispatcher can call before/after spawning a sub-persona.
9
+ *
10
+ * Differences from the existing `core/edits/worktree.ts` primitive:
11
+ *
12
+ * - The edits primitive keys worktrees by UUID and serves the manual
13
+ * `pugi worktree create/promote/drop` operator surface (one-shot
14
+ * scratch trees that get promoted back to the operator's tree).
15
+ * - This manager keys worktrees by AGENT ID so the dispatcher (and any
16
+ * observer like `pugi worktrees list`) can correlate a worktree
17
+ * with the in-flight agent driving it.
18
+ *
19
+ * The two namespaces coexist safely because the directory shapes differ:
20
+ *
21
+ * - manager: `<cwd>/.pugi/worktrees/<agent-id>/` (kebab-case)
22
+ * - primitive `<cwd>/.pugi/worktrees/<uuid>/` (dashed hex)
23
+ *
24
+ * The manager refuses to operate on a path whose basename does not match
25
+ * the agent-id regex `[a-zA-Z0-9_-]+` and does NOT collide with the UUID
26
+ * shape (`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`).
27
+ *
28
+ * Concurrency:
29
+ *
30
+ * - `create(agentId)` is mutex'd per agent-id in process so two
31
+ * concurrent dispatch hooks racing on the same id resolve to the
32
+ * same handle (the second call awaits the first).
33
+ * - At the filesystem level the worktree dir's existence is the lock:
34
+ * if a stale dir for an agent-id survives from a previous process
35
+ * `create` returns `already_exists` so the caller can decide to
36
+ * reuse-or-cleanup explicitly (avoids `git worktree add` racing on
37
+ * the same path which surfaces a noisy git error).
38
+ *
39
+ * Brand voice: ASCII only, no emoji, no banned words.
40
+ */
41
+ import { spawnSync } from 'node:child_process';
42
+ import { existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
43
+ import { resolve, sep } from 'node:path';
44
+ /**
45
+ * Filename-safe + dispatcher-safe agent id.
46
+ *
47
+ * Mirrors the regex on `AgentProgress.agentId` so an id that can write a
48
+ * progress file is exactly the id this manager will accept. The UUID
49
+ * exclusion below makes sure a caller never accidentally collides with
50
+ * the existing UUID-based scratch worktrees managed by
51
+ * `core/edits/worktree.ts`.
52
+ */
53
+ const AGENT_ID_RE = /^[a-zA-Z0-9_-]+$/;
54
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
55
+ /** Validate an agent id. Exported for spec coverage + caller pre-checks. */
56
+ export function isValidAgentId(id) {
57
+ if (typeof id !== 'string' || id.length === 0 || id.length > 128)
58
+ return false;
59
+ if (!AGENT_ID_RE.test(id))
60
+ return false;
61
+ if (UUID_RE.test(id))
62
+ return false;
63
+ // Disallow `.` / `..` and the archive subdir name reserved by
64
+ // agent-progress cleanup so list/scan never crosses paths.
65
+ if (id === '.' || id === '..' || id === 'archive')
66
+ return false;
67
+ return true;
68
+ }
69
+ /**
70
+ * Build the absolute path for an agent's worktree without touching the
71
+ * filesystem. Exposed so the dispatcher can compute a `cwd` for the
72
+ * spawned child without invoking the manager (useful in dry-run paths).
73
+ */
74
+ export function worktreePathFor(cwd, agentId) {
75
+ return resolve(cwd, '.pugi', 'worktrees', agentId);
76
+ }
77
+ /** Shape an agent-id + slug into the canonical branch name. */
78
+ export function branchNameFor(agentId, slug) {
79
+ const clean = (slug ?? '')
80
+ .toLowerCase()
81
+ .replace(/[^a-z0-9-]+/g, '-')
82
+ .replace(/^-+|-+$/g, '')
83
+ .slice(0, 24);
84
+ return clean.length > 0 ? `agent/${agentId}-${clean}` : `agent/${agentId}`;
85
+ }
86
+ /**
87
+ * Class wrapping git worktree operations for a single workspace root.
88
+ * Stateless across instances except for the per-agent-id mutex map.
89
+ */
90
+ export class WorktreeManager {
91
+ cwd;
92
+ spawnGit;
93
+ inFlight = new Map();
94
+ constructor(options) {
95
+ this.cwd = options.cwd;
96
+ this.spawnGit = options.spawnGit ?? defaultSpawnGit;
97
+ }
98
+ /**
99
+ * Create a worktree bound to `agentId`. Idempotent under concurrent
100
+ * callers: the second concurrent `create(<same id>)` awaits the first.
101
+ * A subsequent `create()` after a process restart on an EXISTING dir
102
+ * returns `already_exists` so the caller can choose to cleanup-then-
103
+ * recreate explicitly.
104
+ */
105
+ async create(agentId, options = {}) {
106
+ if (!isValidAgentId(agentId)) {
107
+ return { ok: false, reason: 'invalid_agent_id', detail: `agent id ${String(agentId)} does not match ${AGENT_ID_RE} or collides with UUID shape` };
108
+ }
109
+ const pending = this.inFlight.get(agentId);
110
+ if (pending)
111
+ return pending;
112
+ const promise = this.createUnlocked(agentId, options);
113
+ this.inFlight.set(agentId, promise);
114
+ try {
115
+ return await promise;
116
+ }
117
+ finally {
118
+ this.inFlight.delete(agentId);
119
+ }
120
+ }
121
+ async createUnlocked(agentId, options) {
122
+ const gitDir = this.spawnGit(['rev-parse', '--git-dir'], this.cwd);
123
+ if (gitDir.status !== 0) {
124
+ return { ok: false, reason: 'not_a_git_repo', detail: `not a git repo: ${this.cwd}` };
125
+ }
126
+ const target = worktreePathFor(this.cwd, agentId);
127
+ if (existsSync(target)) {
128
+ return {
129
+ ok: false,
130
+ reason: 'already_exists',
131
+ detail: `worktree already exists at ${target}; call cleanup(${agentId}) first if reuse intended`,
132
+ };
133
+ }
134
+ const baseRef = options.baseRef ?? 'HEAD';
135
+ const baseShaResult = this.spawnGit(['rev-parse', baseRef], this.cwd);
136
+ if (baseShaResult.status !== 0) {
137
+ return {
138
+ ok: false,
139
+ reason: 'git_command_failed',
140
+ detail: `cannot resolve base ref ${baseRef}: ${baseShaResult.stderr}`,
141
+ };
142
+ }
143
+ const baseSha = baseShaResult.stdout.trim();
144
+ const branch = branchNameFor(agentId, options.slug);
145
+ // Ensure the parent exists before git creates the leaf. git worktree
146
+ // add will create the leaf itself; pre-creating .pugi/worktrees/ keeps
147
+ // the failure mode local to git (we never have to mkdir the leaf).
148
+ const worktreesRoot = resolve(this.cwd, '.pugi', 'worktrees');
149
+ mkdirSync(worktreesRoot, { recursive: true });
150
+ // `-b <branch> <path> <base>` creates a NEW branch off `base`,
151
+ // checked out at `path`. We deliberately do NOT use `--detach` here
152
+ // (unlike the scratch primitive) so the branch survives across
153
+ // process restarts and the operator can `git push agent/<id>-<slug>`
154
+ // if they want to inspect the child's work outside the worktree.
155
+ const create = this.spawnGit(['worktree', 'add', '-b', branch, target, baseSha], this.cwd);
156
+ if (create.status !== 0) {
157
+ return {
158
+ ok: false,
159
+ reason: 'git_command_failed',
160
+ detail: `git worktree add failed: ${create.stderr || create.stdout}`,
161
+ };
162
+ }
163
+ return {
164
+ ok: true,
165
+ value: { agentId, path: target, branch, baseSha },
166
+ };
167
+ }
168
+ /**
169
+ * Remove the worktree bound to `agentId`. Idempotent: a missing path
170
+ * surfaces `worktree_missing` rather than throwing, so a double-call
171
+ * during dispatch teardown never crashes the parent.
172
+ */
173
+ cleanup(agentId) {
174
+ if (!isValidAgentId(agentId)) {
175
+ return { ok: false, reason: 'invalid_agent_id', detail: `agent id ${String(agentId)} is invalid` };
176
+ }
177
+ const target = worktreePathFor(this.cwd, agentId);
178
+ // Guard: never let cleanup touch a path that escapes the worktrees
179
+ // root (defensive — the worktreePathFor builder is already bounded
180
+ // because the agent id regex rejects '..', but containment check
181
+ // is cheap and gives us a second line of defense).
182
+ const root = resolve(this.cwd, '.pugi', 'worktrees');
183
+ if (!target.startsWith(root + sep)) {
184
+ return { ok: false, reason: 'invalid_agent_id', detail: `resolved path ${target} escapes ${root}` };
185
+ }
186
+ if (!existsSync(target)) {
187
+ // Best-effort prune so a metadata-only orphan goes away too.
188
+ this.spawnGit(['worktree', 'prune'], this.cwd);
189
+ return { ok: false, reason: 'worktree_missing', detail: `no worktree at ${target}` };
190
+ }
191
+ const remove = this.spawnGit(['worktree', 'remove', '--force', target], this.cwd);
192
+ if (remove.status !== 0) {
193
+ return {
194
+ ok: false,
195
+ reason: 'git_command_failed',
196
+ detail: `git worktree remove failed: ${remove.stderr || remove.stdout}`,
197
+ };
198
+ }
199
+ return { ok: true, value: { removed: target } };
200
+ }
201
+ /**
202
+ * Enumerate worktrees the manager knows about. Read-only and never
203
+ * touches git metadata; the optional `gitTracked` flag on each entry
204
+ * surfaces the cross-check against `git worktree list --porcelain`.
205
+ */
206
+ list() {
207
+ const root = resolve(this.cwd, '.pugi', 'worktrees');
208
+ if (!existsSync(root)) {
209
+ return { ok: true, value: [] };
210
+ }
211
+ let entries;
212
+ try {
213
+ entries = readdirSync(root);
214
+ }
215
+ catch (err) {
216
+ return {
217
+ ok: false,
218
+ reason: 'git_command_failed',
219
+ detail: `readdir ${root} failed: ${err.message}`,
220
+ };
221
+ }
222
+ const tracked = this.collectGitTrackedWorktrees();
223
+ const out = [];
224
+ for (const name of entries) {
225
+ if (!isValidAgentId(name))
226
+ continue;
227
+ const path = resolve(root, name);
228
+ let isDir = false;
229
+ try {
230
+ isDir = statSync(path).isDirectory();
231
+ }
232
+ catch {
233
+ continue;
234
+ }
235
+ if (!isDir)
236
+ continue;
237
+ const meta = tracked.get(path);
238
+ const branch = meta?.branch ?? branchNameFor(name);
239
+ const baseSha = meta?.head ?? '';
240
+ out.push({
241
+ agentId: name,
242
+ path,
243
+ branch,
244
+ baseSha,
245
+ gitTracked: meta !== undefined,
246
+ });
247
+ }
248
+ return { ok: true, value: out };
249
+ }
250
+ /**
251
+ * Parse `git worktree list --porcelain` into a `path → {branch, head}`
252
+ * map. Errors degrade to an empty map — `list()` still surfaces the
253
+ * directory entries; the caller sees `gitTracked: false` rows and can
254
+ * decide.
255
+ */
256
+ collectGitTrackedWorktrees() {
257
+ const out = new Map();
258
+ const list = this.spawnGit(['worktree', 'list', '--porcelain'], this.cwd);
259
+ if (list.status !== 0)
260
+ return out;
261
+ let currentPath = null;
262
+ let currentHead = '';
263
+ let currentBranch = '';
264
+ const commit = () => {
265
+ if (currentPath) {
266
+ out.set(currentPath, { branch: currentBranch, head: currentHead });
267
+ }
268
+ currentPath = null;
269
+ currentHead = '';
270
+ currentBranch = '';
271
+ };
272
+ for (const raw of list.stdout.split('\n')) {
273
+ const line = raw.trimEnd();
274
+ if (line.length === 0) {
275
+ commit();
276
+ continue;
277
+ }
278
+ if (line.startsWith('worktree ')) {
279
+ // New record — flush the previous one if any.
280
+ commit();
281
+ currentPath = line.slice('worktree '.length);
282
+ }
283
+ else if (line.startsWith('HEAD ')) {
284
+ currentHead = line.slice('HEAD '.length);
285
+ }
286
+ else if (line.startsWith('branch ')) {
287
+ // `branch refs/heads/agent/foo` → `agent/foo`
288
+ const ref = line.slice('branch '.length);
289
+ currentBranch = ref.replace(/^refs\/heads\//, '');
290
+ }
291
+ }
292
+ commit();
293
+ return out;
294
+ }
295
+ }
296
+ function defaultSpawnGit(args, cwd) {
297
+ return spawnSync('git', args, {
298
+ cwd,
299
+ encoding: 'utf8',
300
+ maxBuffer: 16 * 1024 * 1024,
301
+ });
302
+ }
303
+ //# sourceMappingURL=manager.js.map
package/dist/index.js CHANGED
@@ -1,6 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
  import { runCli } from './runtime/cli.js';
3
+ import { PugiCliUpgradeRequiredError } from './core/transport/version-interceptor.js';
3
4
  runCli(process.argv.slice(2)).catch((error) => {
5
+ // PR-CLI-SERVER-VERSION-HANDSHAKE . When the admin-api returns
6
+ // 426 Upgrade Required, the engine transport throws a typed
7
+ // PugiCliUpgradeRequiredError. Render an operator-friendly banner
8
+ // (vs. the bland `pugi: <message>` default) so the upgrade command
9
+ // is obvious + copy-pasteable. Plain console.error rather than Ink
10
+ // here because the error may surface during one-shot commands where
11
+ // no Ink renderer is mounted (REPL paths catch via runtime/cli.ts).
12
+ if (error instanceof PugiCliUpgradeRequiredError) {
13
+ const lines = [
14
+ '',
15
+ 'Pugi CLI upgrade required',
16
+ '',
17
+ ` Your installed version: ${error.installedVersion}`,
18
+ ` Server requires minimum: ${error.minClientVersion}`,
19
+ ` Latest recommended: ${error.recommendedVersion}`,
20
+ '',
21
+ ` Upgrade: ${error.upgradeCommand}`,
22
+ ` Docs: ${error.upgradeUrl}`,
23
+ '',
24
+ ' Until you upgrade, the server will reject your requests.',
25
+ ' This protects you from silent protocol-drift bugs.',
26
+ '',
27
+ ];
28
+ console.error(lines.join('\n'));
29
+ process.exitCode = 1;
30
+ return;
31
+ }
4
32
  const message = error instanceof Error ? error.message : String(error);
5
33
  console.error(`pugi: ${message}`);
6
34
  process.exitCode = 1;