@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,558 @@
1
+ /**
2
+ * `pugi stuck` — non-invasive self-diagnosis for a Pugi process that
3
+ * appears wedged. Bundled skill, batch 1 (backlog, inspired by
4
+ * the upstream tool's bundled-skills pattern surfaced in leak intel).
5
+ *
6
+ * # What this does (and does not)
7
+ *
8
+ * The operator runs `pugi stuck` when the foreground Pugi seems frozen
9
+ * (engine spinner, REPL silent, edit hangs). The skill walks the
10
+ * process tree once, gathers vitals on every peer Pugi process, flags
11
+ * the ones that match a fault heuristic, optionally dumps a kernel
12
+ * stack via `sample(1)` on macOS, and posts the redacted snapshot to a
13
+ * feedback webhook if one is configured.
14
+ *
15
+ * **It NEVER signals, kills, or otherwise mutates a process.** The
16
+ * exit code is purely informational: a non-zero return means "at least
17
+ * one peer looked suspicious, here is what we saw". The operator
18
+ * decides what to do — kill, attach a debugger, file a bug, wait.
19
+ *
20
+ * # Fault heuristic (peer is suspect when ANY holds)
21
+ *
22
+ * - sustained CPU >= CPU_THRESHOLD across the snapshot window
23
+ * - resident set size (RSS) >= RSS_THRESHOLD_MB
24
+ * - process state is `D` (uninterruptible I/O wait)
25
+ * - process state is `T` (stopped, e.g. Ctrl+Z)
26
+ * - process state is `Z` (zombie / exited but unreaped)
27
+ *
28
+ * # Output
29
+ *
30
+ * Human-readable table by default; machine-readable JSON envelope when
31
+ * `--json` is set. Both surfaces describe the same payload. The JSON
32
+ * envelope is the source of truth for the feedback webhook.
33
+ *
34
+ * # Provenance
35
+ *
36
+ * Inspired by the the upstream tool bundled-skills pattern (intel from
37
+ * leak-research memos, clean-room TS). No upstream code reused.
38
+ */
39
+ import { execFileSync, spawnSync } from 'node:child_process';
40
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
41
+ import { homedir, platform } from 'node:os';
42
+ import { join, resolve } from 'node:path';
43
+ /**
44
+ * CPU usage (percent) at which a peer Pugi is flagged. Single-core
45
+ * tight loops sit at ~100%; the threshold trips well before that to
46
+ * surface near-pegged processes. Operator-overridable via flag.
47
+ */
48
+ const DEFAULT_CPU_THRESHOLD = 85;
49
+ /**
50
+ * Resident set size (MB) at which a peer Pugi is flagged. Engine + REPL
51
+ * + Ink should stay well under 1 GB in normal operation. 4 GB indicates
52
+ * a runaway model context or a leaking subagent.
53
+ */
54
+ const DEFAULT_RSS_THRESHOLD_MB = 4096;
55
+ /**
56
+ * Process states that are inherently suspicious regardless of CPU / RSS.
57
+ * D — uninterruptible sleep (often disk / network hang)
58
+ * T — stopped (Ctrl+Z, debugger detached without continue)
59
+ * Z — zombie (exit not reaped, parent leaking)
60
+ */
61
+ const STUCK_STATES = new Set(['D', 'T', 'Z']);
62
+ function parseFlags(args) {
63
+ const flags = {
64
+ json: false,
65
+ cpuThreshold: DEFAULT_CPU_THRESHOLD,
66
+ rssThresholdMb: DEFAULT_RSS_THRESHOLD_MB,
67
+ sampleStack: true,
68
+ };
69
+ for (let i = 0; i < args.length; i += 1) {
70
+ const arg = args[i];
71
+ if (arg === undefined)
72
+ continue;
73
+ if (arg === '--json') {
74
+ flags.json = true;
75
+ continue;
76
+ }
77
+ if (arg === '--no-sample') {
78
+ flags.sampleStack = false;
79
+ continue;
80
+ }
81
+ if (arg === '--cpu') {
82
+ const next = args[i + 1];
83
+ if (next === undefined)
84
+ return { flags, error: '--cpu requires a value' };
85
+ const parsed = Number.parseFloat(next);
86
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
87
+ return { flags, error: '--cpu must be a number 0..100' };
88
+ }
89
+ flags.cpuThreshold = parsed;
90
+ i += 1;
91
+ continue;
92
+ }
93
+ if (arg === '--rss') {
94
+ const next = args[i + 1];
95
+ if (next === undefined)
96
+ return { flags, error: '--rss requires a value' };
97
+ const parsed = Number.parseInt(next, 10);
98
+ if (!Number.isFinite(parsed) || parsed <= 0) {
99
+ return { flags, error: '--rss must be a positive integer (MB)' };
100
+ }
101
+ flags.rssThresholdMb = parsed;
102
+ i += 1;
103
+ continue;
104
+ }
105
+ if (arg === '--help' || arg === '-h') {
106
+ return { flags, error: 'help' };
107
+ }
108
+ return { flags, error: `unknown argument: ${arg}` };
109
+ }
110
+ return { flags, error: null };
111
+ }
112
+ /**
113
+ * One row out of `ps -axo pid=,ppid=,%cpu=,rss=,state=,command=`. Parsed
114
+ * defensively because BSD ps and GNU ps disagree on whitespace + the
115
+ * trailing command can contain spaces (we slurp the remainder into the
116
+ * `command` field rather than splitting on spaces blindly).
117
+ */
118
+ function parsePsLine(line) {
119
+ const trimmed = line.trim();
120
+ if (!trimmed)
121
+ return null;
122
+ const head = trimmed.split(/\s+/, 5);
123
+ if (head.length < 5)
124
+ return null;
125
+ const [pidStr, ppidStr, cpuStr, rssStr, stateRaw] = head;
126
+ if (pidStr === undefined ||
127
+ ppidStr === undefined ||
128
+ cpuStr === undefined ||
129
+ rssStr === undefined ||
130
+ stateRaw === undefined) {
131
+ return null;
132
+ }
133
+ const pid = Number.parseInt(pidStr, 10);
134
+ const parentPid = Number.parseInt(ppidStr, 10);
135
+ const cpuPercent = Number.parseFloat(cpuStr);
136
+ const rssKb = Number.parseInt(rssStr, 10);
137
+ if (!Number.isFinite(pid) ||
138
+ !Number.isFinite(parentPid) ||
139
+ !Number.isFinite(cpuPercent) ||
140
+ !Number.isFinite(rssKb)) {
141
+ return null;
142
+ }
143
+ // Re-walk the line to recover the command tail without losing
144
+ // embedded spaces. We skip the 5 leading whitespace-delimited fields.
145
+ let cursor = 0;
146
+ for (let field = 0; field < 5; field += 1) {
147
+ // skip any leading whitespace
148
+ while (cursor < trimmed.length && /\s/.test(trimmed.charAt(cursor)))
149
+ cursor += 1;
150
+ // skip the field itself
151
+ while (cursor < trimmed.length && !/\s/.test(trimmed.charAt(cursor)))
152
+ cursor += 1;
153
+ }
154
+ const command = trimmed.slice(cursor).trim();
155
+ // First character of the BSD `state` field is the canonical state
156
+ // (e.g. `S+`, `R<`, `D+`). Normalise to single letter.
157
+ const state = stateRaw.charAt(0) || '?';
158
+ return { pid, parentPid, cpuPercent, rssKb, state, command };
159
+ }
160
+ /**
161
+ * Snapshot the whole process table once via `ps -axo`. Pure read, no
162
+ * mutation. Returns an empty list if `ps` is unavailable (extremely
163
+ * locked-down container) — the caller surfaces an empty snapshot
164
+ * rather than crashing.
165
+ */
166
+ export function readProcessTable() {
167
+ try {
168
+ const stdout = execFileSync('ps', ['-axo', 'pid=,ppid=,%cpu=,rss=,state=,command='], {
169
+ encoding: 'utf8',
170
+ maxBuffer: 16 * 1024 * 1024,
171
+ // 5 s is plenty even on a loaded host; we never want `pugi
172
+ // stuck` itself to wedge waiting for ps.
173
+ timeout: 5000,
174
+ });
175
+ const rows = [];
176
+ for (const line of stdout.split('\n')) {
177
+ const parsed = parsePsLine(line);
178
+ if (parsed !== null)
179
+ rows.push(parsed);
180
+ }
181
+ return rows;
182
+ }
183
+ catch {
184
+ return [];
185
+ }
186
+ }
187
+ /**
188
+ * Identify peer Pugi processes. A row is a "peer" when its command
189
+ * line looks like a `pugi` invocation — node + pugi entrypoint, or the
190
+ * bin shim, or a tsx-driven dev run. The current process is excluded
191
+ * so `pugi stuck` does not diagnose itself.
192
+ */
193
+ function isPugiPeer(row, selfPid) {
194
+ if (row.pid === selfPid)
195
+ return false;
196
+ const cmd = row.command.toLowerCase();
197
+ // The bin shim path: `…/.npm/_npx/…/bin/pugi` or `node …/bin/pugi`.
198
+ if (/\bpugi\b/.test(cmd) && /(\bnode\b|\bbin\/pugi\b|\.npm\/|\.pnpm\/|tsx\b)/.test(cmd)) {
199
+ return true;
200
+ }
201
+ // Direct invocation of the compiled bundle inside dist/.
202
+ if (/pugi-cli\/(dist|bin)\//.test(cmd))
203
+ return true;
204
+ return false;
205
+ }
206
+ /**
207
+ * Walk the ps table and collect every Pugi peer with the heuristic
208
+ * verdict attached. We deliberately keep the verdict logic outside of
209
+ * any side-effects so tests can exercise the boundary by feeding a
210
+ * synthetic ps table.
211
+ */
212
+ export function classifyPeers(rows, selfPid, thresholds) {
213
+ const peers = [];
214
+ for (const row of rows) {
215
+ if (!isPugiPeer(row, selfPid))
216
+ continue;
217
+ const rssMb = Math.round(row.rssKb / 1024);
218
+ const reasons = [];
219
+ if (row.cpuPercent >= thresholds.cpuPercent) {
220
+ reasons.push(`cpu ${row.cpuPercent.toFixed(1)}% >= ${thresholds.cpuPercent}%`);
221
+ }
222
+ if (rssMb >= thresholds.rssMb) {
223
+ reasons.push(`rss ${rssMb} MB >= ${thresholds.rssMb} MB`);
224
+ }
225
+ if (STUCK_STATES.has(row.state)) {
226
+ reasons.push(`state ${row.state} (${describeState(row.state)})`);
227
+ }
228
+ peers.push({
229
+ pid: row.pid,
230
+ parentPid: row.parentPid,
231
+ cpuPercent: row.cpuPercent,
232
+ rssMb,
233
+ state: row.state,
234
+ command: row.command,
235
+ suspect: reasons.length > 0,
236
+ reasons,
237
+ });
238
+ }
239
+ return peers;
240
+ }
241
+ function describeState(letter) {
242
+ switch (letter) {
243
+ case 'D':
244
+ return 'uninterruptible I/O wait';
245
+ case 'T':
246
+ return 'stopped';
247
+ case 'Z':
248
+ return 'zombie';
249
+ case 'R':
250
+ return 'runnable';
251
+ case 'S':
252
+ return 'sleeping';
253
+ default:
254
+ return 'unknown';
255
+ }
256
+ }
257
+ /**
258
+ * Walk the ps table for child processes of a peer Pugi (git, node,
259
+ * shell helpers). A child is flagged when it is in a suspicious state
260
+ * OR pegged on CPU. Children are not subject to the RSS threshold
261
+ * because git/sh legitimately stay tiny.
262
+ */
263
+ export function classifyChildren(rows, peerPids, thresholds) {
264
+ const peerSet = new Set(peerPids);
265
+ const out = [];
266
+ for (const row of rows) {
267
+ if (!peerSet.has(row.parentPid))
268
+ continue;
269
+ const suspicious = STUCK_STATES.has(row.state) || row.cpuPercent >= thresholds.cpuPercent;
270
+ if (!suspicious)
271
+ continue;
272
+ out.push({
273
+ pid: row.pid,
274
+ parentPid: row.parentPid,
275
+ state: row.state,
276
+ command: row.command,
277
+ cpuPercent: row.cpuPercent,
278
+ });
279
+ }
280
+ return out;
281
+ }
282
+ /**
283
+ * Read the tail of `~/.pugi/debug/` log files, if any. The directory
284
+ * is optional — most operators do not have debug logging enabled.
285
+ * We sort newest-first by mtime and slurp the last few lines of the
286
+ * top file so the snapshot carries enough context without dragging in
287
+ * megabytes of history.
288
+ */
289
+ export function readDebugTail(home, maxLines = 25) {
290
+ const dir = join(home, '.pugi', 'debug');
291
+ if (!existsSync(dir))
292
+ return [];
293
+ let entries;
294
+ try {
295
+ entries = readdirSync(dir)
296
+ .map((name) => ({ path: join(dir, name), mtimeMs: 0 }))
297
+ .map((entry) => {
298
+ try {
299
+ const st = statSync(entry.path);
300
+ return { path: entry.path, mtimeMs: st.mtimeMs };
301
+ }
302
+ catch {
303
+ return entry;
304
+ }
305
+ })
306
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
307
+ }
308
+ catch {
309
+ return [];
310
+ }
311
+ const top = entries[0];
312
+ if (top === undefined)
313
+ return [];
314
+ try {
315
+ const raw = readFileSync(top.path, 'utf8');
316
+ const lines = raw.split('\n').filter((l) => l !== '');
317
+ return lines.slice(Math.max(0, lines.length - maxLines));
318
+ }
319
+ catch {
320
+ return [];
321
+ }
322
+ }
323
+ /**
324
+ * Capture a 1-second `sample <pid>` stack dump on macOS. Returns the
325
+ * raw textual output (already truncated by sample itself to a
326
+ * reasonable size). Returns null on any failure — sample is best-effort
327
+ * diagnostic, never blocking.
328
+ */
329
+ export function captureSampleStack(pid) {
330
+ if (platform() !== 'darwin')
331
+ return null;
332
+ try {
333
+ const result = spawnSync('sample', [String(pid), '1'], {
334
+ encoding: 'utf8',
335
+ timeout: 5000,
336
+ maxBuffer: 8 * 1024 * 1024,
337
+ });
338
+ if (result.status !== 0)
339
+ return null;
340
+ return result.stdout.trim();
341
+ }
342
+ catch {
343
+ return null;
344
+ }
345
+ }
346
+ /**
347
+ * Post the snapshot JSON to a feedback webhook. The webhook is
348
+ * configured via `PUGI_FEEDBACK_WEBHOOK` env var; if absent we record
349
+ * `not-configured` and skip. We deliberately use a short fetch timeout
350
+ * because `pugi stuck` is run when the operator already suspects
351
+ * something is wrong — blocking on a slow webhook defeats the purpose.
352
+ */
353
+ /**
354
+ * Webhook URL validation guard — P1 fix from triple-review PR.
355
+ *
356
+ * `PUGI_FEEDBACK_WEBHOOK` is operator-set, but a malicious shell-rc
357
+ * injection could redirect snapshot payloads to an attacker URL —
358
+ * SSRF + exfiltration of process telemetry (peer command-lines,
359
+ * debug-log tail, sample stack with symbols/paths). Enforce:
360
+ * 1. HTTPS only — reject http/file/data/javascript/etc.
361
+ * 2. No private/loopback hostnames — reject localhost, 127.x,
362
+ * 10.x, 172.16-31.x, 192.168.x, *.local, link-local.
363
+ * Returns null when URL is invalid OR safe-to-use otherwise.
364
+ */
365
+ function validateWebhookUrl(raw) {
366
+ let parsed;
367
+ try {
368
+ parsed = new URL(raw);
369
+ }
370
+ catch {
371
+ return null;
372
+ }
373
+ if (parsed.protocol !== 'https:')
374
+ return null;
375
+ const host = parsed.hostname.toLowerCase();
376
+ if (host === 'localhost' ||
377
+ host === '0.0.0.0' ||
378
+ host.endsWith('.local') ||
379
+ host.startsWith('127.') ||
380
+ host.startsWith('10.') ||
381
+ host.startsWith('192.168.') ||
382
+ /^172\.(1[6-9]|2[0-9]|3[01])\./.test(host) ||
383
+ /^169\.254\./.test(host) // link-local
384
+ ) {
385
+ return null;
386
+ }
387
+ return parsed;
388
+ }
389
+ export async function postSnapshotToWebhook(snapshot, url) {
390
+ if (!url || url.trim() === '')
391
+ return 'not-configured';
392
+ const safe = validateWebhookUrl(url.trim());
393
+ if (!safe)
394
+ return 'invalid-url';
395
+ try {
396
+ const controller = new AbortController();
397
+ const timer = setTimeout(() => controller.abort(), 3000);
398
+ try {
399
+ const response = await fetch(safe.toString(), {
400
+ method: 'POST',
401
+ headers: { 'content-type': 'application/json' },
402
+ body: JSON.stringify({ kind: 'pugi-stuck', snapshot }),
403
+ signal: controller.signal,
404
+ });
405
+ return response.ok ? 'ok' : 'failed';
406
+ }
407
+ finally {
408
+ clearTimeout(timer);
409
+ }
410
+ }
411
+ catch {
412
+ return 'failed';
413
+ }
414
+ }
415
+ function renderHumanReport(snapshot) {
416
+ const lines = [];
417
+ lines.push(`pugi stuck — diagnostic snapshot @ ${snapshot.capturedAt}`);
418
+ lines.push(`platform: ${snapshot.platform} selfPid: ${snapshot.selfPid}`);
419
+ lines.push(`thresholds: cpu>=${snapshot.thresholds.cpuPercent}% rss>=${snapshot.thresholds.rssMb} MB`);
420
+ lines.push('');
421
+ if (snapshot.peers.length === 0) {
422
+ lines.push('No peer Pugi processes detected.');
423
+ }
424
+ else {
425
+ lines.push(`Peer Pugi processes (${snapshot.peers.length}):`);
426
+ for (const peer of snapshot.peers) {
427
+ const tag = peer.suspect ? '[SUSPECT]' : '[ok]';
428
+ lines.push(` ${tag} pid=${peer.pid} ppid=${peer.parentPid} cpu=${peer.cpuPercent.toFixed(1)}% rss=${peer.rssMb}MB state=${peer.state}`);
429
+ lines.push(` ${peer.command}`);
430
+ for (const reason of peer.reasons) {
431
+ lines.push(` reason: ${reason}`);
432
+ }
433
+ }
434
+ }
435
+ if (snapshot.suspectChildren.length > 0) {
436
+ lines.push('');
437
+ lines.push(`Suspect child processes (${snapshot.suspectChildren.length}):`);
438
+ for (const child of snapshot.suspectChildren) {
439
+ lines.push(` pid=${child.pid} ppid=${child.parentPid} cpu=${child.cpuPercent.toFixed(1)}% state=${child.state}`);
440
+ lines.push(` ${child.command}`);
441
+ }
442
+ }
443
+ if (snapshot.debugLogTail.length > 0) {
444
+ lines.push('');
445
+ lines.push(`Tail of ~/.pugi/debug/ (${snapshot.debugLogTail.length} lines):`);
446
+ for (const line of snapshot.debugLogTail)
447
+ lines.push(` ${line}`);
448
+ }
449
+ if (snapshot.sampleDump !== null) {
450
+ lines.push('');
451
+ lines.push('sample(1) stack (truncated):');
452
+ const dumpLines = snapshot.sampleDump.split('\n').slice(0, 40);
453
+ for (const line of dumpLines)
454
+ lines.push(` ${line}`);
455
+ }
456
+ lines.push('');
457
+ lines.push(`webhook: ${snapshot.webhookPosted}`);
458
+ lines.push('');
459
+ lines.push('Diagnostic only — no process was signalled or killed.');
460
+ return lines.join('\n');
461
+ }
462
+ /**
463
+ * Public entrypoint. The CLI dispatcher wraps this. Pure async function
464
+ * + injected `writeOutput` so tests can capture both surfaces without
465
+ * touching stdout.
466
+ */
467
+ export async function runStuckCommand(args, ctx) {
468
+ const { flags, error } = parseFlags(args);
469
+ if (error === 'help') {
470
+ ctx.writeOutput({ ok: true, command: 'stuck', usage: STUCK_USAGE }, STUCK_USAGE);
471
+ return { snapshot: emptySnapshot(ctx), exitCode: 0 };
472
+ }
473
+ if (error !== null) {
474
+ ctx.writeOutput({ ok: false, command: 'stuck', error }, `pugi stuck: ${error}`);
475
+ return { exitCode: 2 };
476
+ }
477
+ const home = ctx.env.PUGI_HOME ?? homedir();
478
+ const rows = readProcessTable();
479
+ const peers = classifyPeers(rows, process.pid, {
480
+ cpuPercent: flags.cpuThreshold,
481
+ rssMb: flags.rssThresholdMb,
482
+ });
483
+ const peerPids = peers.map((p) => p.pid);
484
+ const suspectChildren = classifyChildren(rows, peerPids, {
485
+ cpuPercent: flags.cpuThreshold,
486
+ });
487
+ const debugLogTail = readDebugTail(home);
488
+ const mostSuspect = peers
489
+ .filter((p) => p.suspect)
490
+ .sort((a, b) => b.cpuPercent - a.cpuPercent)[0];
491
+ const sampleDump = flags.sampleStack && mostSuspect !== undefined
492
+ ? captureSampleStack(mostSuspect.pid)
493
+ : null;
494
+ const draftSnapshot = {
495
+ capturedAt: ctx.now().toISOString(),
496
+ platform: platform(),
497
+ selfPid: process.pid,
498
+ thresholds: {
499
+ cpuPercent: flags.cpuThreshold,
500
+ rssMb: flags.rssThresholdMb,
501
+ },
502
+ peers,
503
+ suspectChildren,
504
+ debugLogTail,
505
+ sampleDump,
506
+ webhookPosted: 'skipped',
507
+ };
508
+ const webhookUrl = ctx.env.PUGI_FEEDBACK_WEBHOOK;
509
+ const webhookPosted = await postSnapshotToWebhook(draftSnapshot, webhookUrl);
510
+ const snapshot = { ...draftSnapshot, webhookPosted };
511
+ const human = renderHumanReport(snapshot);
512
+ ctx.writeOutput({ ok: true, command: 'stuck', snapshot }, human);
513
+ const anySuspect = snapshot.peers.some((p) => p.suspect) || snapshot.suspectChildren.length > 0;
514
+ return { snapshot, exitCode: anySuspect ? 1 : 0 };
515
+ }
516
+ function emptySnapshot(ctx) {
517
+ return {
518
+ capturedAt: ctx.now().toISOString(),
519
+ platform: platform(),
520
+ selfPid: process.pid,
521
+ thresholds: {
522
+ cpuPercent: DEFAULT_CPU_THRESHOLD,
523
+ rssMb: DEFAULT_RSS_THRESHOLD_MB,
524
+ },
525
+ peers: [],
526
+ suspectChildren: [],
527
+ debugLogTail: [],
528
+ sampleDump: null,
529
+ webhookPosted: 'skipped',
530
+ };
531
+ }
532
+ const STUCK_USAGE = [
533
+ 'pugi stuck — non-invasive self-diagnosis for a wedged Pugi process.',
534
+ '',
535
+ 'Usage:',
536
+ ' pugi stuck [--json] [--cpu <pct>] [--rss <mb>] [--no-sample]',
537
+ '',
538
+ 'Flags:',
539
+ ' --json Emit a JSON envelope instead of human text.',
540
+ ' --cpu <pct> CPU % at which a peer is flagged (default 85).',
541
+ ' --rss <mb> RSS MB at which a peer is flagged (default 4096).',
542
+ ' --no-sample Skip the macOS sample(1) stack dump.',
543
+ '',
544
+ 'Env:',
545
+ ' PUGI_FEEDBACK_WEBHOOK POST snapshot JSON to this URL (best-effort).',
546
+ ' PUGI_HOME Override the ~/.pugi/debug log root.',
547
+ '',
548
+ 'Diagnostic only — NEVER signals or kills any process.',
549
+ ].join('\n');
550
+ /**
551
+ * Public helper for tests + the dispatcher: assert the textual `resolve`
552
+ * import is still in the module graph so node resolution does not strip
553
+ * the side-effect-free path import. Some bundlers tree-shake otherwise.
554
+ */
555
+ export function __debugResolveSelfPath(workspaceRoot) {
556
+ return resolve(workspaceRoot, '.pugi', 'debug');
557
+ }
558
+ //# sourceMappingURL=stuck.js.map