@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.88

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 (405) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/smoke.js +133 -0
  11. package/dist/core/agent-progress/cleanup.js +134 -0
  12. package/dist/core/agent-progress/schema.js +144 -0
  13. package/dist/core/agent-progress/writer.js +101 -0
  14. package/dist/core/agents/adaptive-router.js +330 -0
  15. package/dist/core/agents/query-decomposer.js +297 -0
  16. package/dist/core/agents/registry.js +3 -3
  17. package/dist/core/approvals/shortcut-resolver.js +98 -0
  18. package/dist/core/artifact-chain/dispatcher.js +148 -0
  19. package/dist/core/artifact-chain/exporter.js +164 -0
  20. package/dist/core/artifact-chain/state.js +243 -0
  21. package/dist/core/artifact-chain/steps.js +169 -0
  22. package/dist/core/ask-user/question.js +92 -0
  23. package/dist/core/audit/audit-trail.js +275 -0
  24. package/dist/core/auth/ensure-authenticated.js +129 -0
  25. package/dist/core/auth/env-provider.js +238 -0
  26. package/dist/core/auto-open-browser.js +4 -4
  27. package/dist/core/auto-update/channels.js +122 -0
  28. package/dist/core/auto-update/checker.js +241 -0
  29. package/dist/core/auto-update/state.js +235 -0
  30. package/dist/core/bare-mode/index.js +107 -0
  31. package/dist/core/bash/redirect.js +281 -0
  32. package/dist/core/bash-classifier.js +436 -40
  33. package/dist/core/checkpoint/resumer.js +149 -0
  34. package/dist/core/checkpoint/rewinder.js +291 -0
  35. package/dist/core/checkpoints/shadow-git.js +670 -0
  36. package/dist/core/citations/parser.js +109 -0
  37. package/dist/core/classifier/yolo-classifier.js +88 -0
  38. package/dist/core/codegraph/decision-store.js +248 -0
  39. package/dist/core/codegraph/detect-repo.js +459 -0
  40. package/dist/core/codegraph/install.js +134 -0
  41. package/dist/core/codegraph/offer-hook.js +220 -0
  42. package/dist/core/compact/auto-trigger.js +96 -0
  43. package/dist/core/compact/buffer-rewriter.js +115 -0
  44. package/dist/core/compact/summarizer.js +208 -0
  45. package/dist/core/compact/token-counter.js +108 -0
  46. package/dist/core/consensus/anvil-fanout.js +25 -25
  47. package/dist/core/consensus/diff-capture.js +121 -12
  48. package/dist/core/consensus/rubric.js +21 -21
  49. package/dist/core/context/builder.js +6 -6
  50. package/dist/core/context/compaction-events.js +8 -8
  51. package/dist/core/context/compaction.js +31 -31
  52. package/dist/core/context/index.js +15 -8
  53. package/dist/core/context/invariants.js +51 -51
  54. package/dist/core/context/markdown-loader.js +28 -10
  55. package/dist/core/context/markdown-traverse.js +255 -0
  56. package/dist/core/context/pugiignore.js +41 -41
  57. package/dist/core/context/repo-skeleton.js +37 -37
  58. package/dist/core/context/tool-eviction.js +55 -0
  59. package/dist/core/context/watcher.js +32 -32
  60. package/dist/core/context/working-set.js +23 -23
  61. package/dist/core/coordinator/agent-tools.js +77 -0
  62. package/dist/core/coordinator/agent-toolset.js +65 -0
  63. package/dist/core/coordinator/fsm.js +73 -0
  64. package/dist/core/coordinator/mode-fsm.js +70 -0
  65. package/dist/core/cost/rate-card.js +129 -0
  66. package/dist/core/cost/tracker.js +221 -0
  67. package/dist/core/credentials.js +12 -12
  68. package/dist/core/cron/scheduler.js +138 -0
  69. package/dist/core/denial-tracking/index.js +8 -0
  70. package/dist/core/denial-tracking/state.js +264 -0
  71. package/dist/core/diagnostics/probe-runner.js +93 -0
  72. package/dist/core/diagnostics/probes/api.js +46 -0
  73. package/dist/core/diagnostics/probes/auth.js +93 -0
  74. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  75. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  76. package/dist/core/diagnostics/probes/config.js +72 -0
  77. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  78. package/dist/core/diagnostics/probes/disk.js +81 -0
  79. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  80. package/dist/core/diagnostics/probes/git.js +65 -0
  81. package/dist/core/diagnostics/probes/hooks.js +118 -0
  82. package/dist/core/diagnostics/probes/mcp.js +75 -0
  83. package/dist/core/diagnostics/probes/node.js +59 -0
  84. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  85. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  86. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  87. package/dist/core/diagnostics/probes/session.js +74 -0
  88. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  89. package/dist/core/diagnostics/probes/workspace.js +63 -0
  90. package/dist/core/diagnostics/types.js +70 -0
  91. package/dist/core/dispatch/cache-cleanup.js +197 -0
  92. package/dist/core/dispatch/cache-handoff.js +295 -0
  93. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  94. package/dist/core/edits/dispatch.js +293 -7
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +3 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +322 -0
  108. package/dist/core/engine/anvil-client.js +151 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +134 -16
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1295 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +44 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +213 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/image/renderer.js +71 -0
  141. package/dist/core/init/detector.js +582 -0
  142. package/dist/core/init/template-renderer.js +242 -0
  143. package/dist/core/jobs/registry.js +18 -18
  144. package/dist/core/ledger/results-tsv.js +142 -0
  145. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  146. package/dist/core/lsp/cache.js +105 -0
  147. package/dist/core/lsp/client.js +776 -0
  148. package/dist/core/lsp/language-detect.js +66 -0
  149. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  150. package/dist/core/lsp/symbol-tools.js +372 -0
  151. package/dist/core/mcp/client.js +97 -28
  152. package/dist/core/mcp/http-server.js +553 -0
  153. package/dist/core/mcp/orchestrator-tools.js +662 -0
  154. package/dist/core/mcp/permission.js +190 -0
  155. package/dist/core/mcp/registry.js +39 -17
  156. package/dist/core/mcp/server-tools.js +219 -0
  157. package/dist/core/mcp/server.js +397 -0
  158. package/dist/core/mcp/trust.js +10 -10
  159. package/dist/core/memory/dual-write.js +416 -0
  160. package/dist/core/memory/passive-extract.js +130 -0
  161. package/dist/core/memory/phase1-kinds.js +20 -0
  162. package/dist/core/memory/secret-scanner.js +304 -0
  163. package/dist/core/memory-sync/queue.js +170 -0
  164. package/dist/core/metrics/extract.js +113 -0
  165. package/dist/core/modes/roo-modes.js +68 -0
  166. package/dist/core/onboarding/ensure-initialized.js +133 -0
  167. package/dist/core/onboarding/marker.js +111 -0
  168. package/dist/core/onboarding/telemetry-state.js +108 -0
  169. package/dist/core/output-style/presets.js +176 -0
  170. package/dist/core/output-style/state.js +185 -0
  171. package/dist/core/path-security.js +287 -5
  172. package/dist/core/permission.js +82 -22
  173. package/dist/core/permissions/auto-classifier.js +124 -0
  174. package/dist/core/permissions/bash-parser.js +371 -0
  175. package/dist/core/permissions/circuit-breaker.js +83 -0
  176. package/dist/core/permissions/constrained-edit.js +91 -0
  177. package/dist/core/permissions/gate.js +278 -0
  178. package/dist/core/permissions/index.js +20 -0
  179. package/dist/core/permissions/mode.js +174 -0
  180. package/dist/core/permissions/network-egress.js +137 -0
  181. package/dist/core/permissions/state.js +241 -0
  182. package/dist/core/permissions/tool-class.js +93 -0
  183. package/dist/core/plan-mode/ui-state.js +51 -0
  184. package/dist/core/plans/plan-artifact.js +721 -0
  185. package/dist/core/policy-limits/etag-store.js +122 -0
  186. package/dist/core/prd-check/parser.js +215 -0
  187. package/dist/core/prd-check/reporter.js +127 -0
  188. package/dist/core/prd-check/session-review.js +557 -0
  189. package/dist/core/prd-check/verifiers.js +223 -0
  190. package/dist/core/prompt-cache/client-cache.js +99 -0
  191. package/dist/core/prompts/assembly.js +29 -0
  192. package/dist/core/prompts/registry.js +364 -0
  193. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  194. package/dist/core/pugi-md/context-injector.js +76 -0
  195. package/dist/core/pugi-md/walk-up.js +207 -0
  196. package/dist/core/python/uv-installer.js +270 -0
  197. package/dist/core/python/uv-resolver.js +83 -0
  198. package/dist/core/rate-limit/narrator.js +146 -0
  199. package/dist/core/recipes/cli-types.js +20 -0
  200. package/dist/core/recipes/loader.js +103 -0
  201. package/dist/core/recipes/runner.js +345 -0
  202. package/dist/core/recipes/schema.js +587 -0
  203. package/dist/core/release-notes/parser.js +241 -0
  204. package/dist/core/release-notes/state.js +116 -0
  205. package/dist/core/repl/ask.js +37 -37
  206. package/dist/core/repl/cancellation.js +26 -26
  207. package/dist/core/repl/cap-warning.js +4 -4
  208. package/dist/core/repl/clipboard-read.js +11 -11
  209. package/dist/core/repl/dispatch-fsm.js +12 -12
  210. package/dist/core/repl/history-search.js +15 -15
  211. package/dist/core/repl/history.js +28 -18
  212. package/dist/core/repl/kill-ring.js +5 -5
  213. package/dist/core/repl/model-pricing.js +135 -0
  214. package/dist/core/repl/privacy-banner.js +22 -22
  215. package/dist/core/repl/session.js +2157 -214
  216. package/dist/core/repl/slash-commands.js +533 -40
  217. package/dist/core/repl/store/index.js +1 -1
  218. package/dist/core/repl/store/jsonl-log.js +22 -22
  219. package/dist/core/repl/store/lockfile.js +10 -10
  220. package/dist/core/repl/store/session-store.js +136 -107
  221. package/dist/core/repl/store/types.js +15 -15
  222. package/dist/core/repl/store/uuid-v7.js +12 -12
  223. package/dist/core/repl/workspace-context.js +43 -21
  224. package/dist/core/repo-map/build.js +125 -0
  225. package/dist/core/repo-map/cache.js +185 -0
  226. package/dist/core/repo-map/extractor.js +254 -0
  227. package/dist/core/repo-map/formatter.js +145 -0
  228. package/dist/core/repo-map/page-rank.js +105 -0
  229. package/dist/core/repo-map/scanner.js +211 -0
  230. package/dist/core/retry-budget/budget.js +284 -0
  231. package/dist/core/retry-budget/index.js +5 -0
  232. package/dist/core/retry-budget/retry-cap.js +74 -0
  233. package/dist/core/routing/lead-worker.js +43 -0
  234. package/dist/core/routing/pre-flight-estimator.js +108 -0
  235. package/dist/core/runs/run-tree.js +103 -0
  236. package/dist/core/security/injection-scanner.js +367 -0
  237. package/dist/core/security/output-filter.js +418 -0
  238. package/dist/core/session/env-file.js +105 -0
  239. package/dist/core/session/section-budgets.js +140 -0
  240. package/dist/core/session.js +92 -0
  241. package/dist/core/settings.js +298 -5
  242. package/dist/core/share/formatter.js +271 -0
  243. package/dist/core/share/redactor.js +221 -0
  244. package/dist/core/share/uploader.js +267 -0
  245. package/dist/core/skills/defaults.js +457 -0
  246. package/dist/core/skills/loader.js +22 -22
  247. package/dist/core/skills/sources.js +27 -27
  248. package/dist/core/smoke/headless-driver.js +174 -0
  249. package/dist/core/smoke/orchestrator.js +194 -0
  250. package/dist/core/smoke/runner.js +238 -0
  251. package/dist/core/smoke/scenario-parser.js +316 -0
  252. package/dist/core/statusline.js +99 -0
  253. package/dist/core/subagents/dispatcher-real.js +600 -0
  254. package/dist/core/subagents/dispatcher.js +132 -43
  255. package/dist/core/subagents/index.js +19 -6
  256. package/dist/core/subagents/isolation-matrix.js +213 -0
  257. package/dist/core/subagents/spawn.js +19 -4
  258. package/dist/core/telemetry/emitter.js +229 -0
  259. package/dist/core/telemetry/queue.js +251 -0
  260. package/dist/core/theme/context.js +91 -0
  261. package/dist/core/theme/presets.js +228 -0
  262. package/dist/core/theme/state.js +181 -0
  263. package/dist/core/todos/invariant.js +10 -0
  264. package/dist/core/todos/state.js +177 -0
  265. package/dist/core/tool-schema/compressor.js +89 -0
  266. package/dist/core/transport/version-interceptor.js +166 -0
  267. package/dist/core/trust.js +2 -2
  268. package/dist/core/tui/thinking-block.js +64 -0
  269. package/dist/core/vim/keymap.js +288 -0
  270. package/dist/core/vim/state.js +92 -0
  271. package/dist/core/watch-markers/marker-watcher.js +133 -0
  272. package/dist/core/worktree-manager/cleanup.js +123 -0
  273. package/dist/core/worktree-manager/manager.js +303 -0
  274. package/dist/index.js +36 -0
  275. package/dist/runtime/bootstrap.js +190 -0
  276. package/dist/runtime/cli.js +4203 -493
  277. package/dist/runtime/commands/agents.js +30 -30
  278. package/dist/runtime/commands/budget.js +5 -5
  279. package/dist/runtime/commands/cancel.js +231 -0
  280. package/dist/runtime/commands/chain.js +489 -0
  281. package/dist/runtime/commands/codegraph-status.js +227 -0
  282. package/dist/runtime/commands/compact.js +297 -0
  283. package/dist/runtime/commands/config.js +73 -39
  284. package/dist/runtime/commands/cost.js +199 -0
  285. package/dist/runtime/commands/delegate.js +244 -13
  286. package/dist/runtime/commands/dispatch.js +126 -0
  287. package/dist/runtime/commands/doctor.js +579 -0
  288. package/dist/runtime/commands/feedback.js +184 -0
  289. package/dist/runtime/commands/hooks.js +184 -0
  290. package/dist/runtime/commands/init.js +254 -0
  291. package/dist/runtime/commands/lsp.js +368 -0
  292. package/dist/runtime/commands/mcp.js +879 -0
  293. package/dist/runtime/commands/memory.js +582 -0
  294. package/dist/runtime/commands/model.js +237 -0
  295. package/dist/runtime/commands/onboarding.js +275 -0
  296. package/dist/runtime/commands/patch.js +128 -0
  297. package/dist/runtime/commands/permissions.js +112 -0
  298. package/dist/runtime/commands/plan.js +143 -0
  299. package/dist/runtime/commands/prd-check.js +285 -0
  300. package/dist/runtime/commands/privacy.js +17 -17
  301. package/dist/runtime/commands/recipe.js +325 -0
  302. package/dist/runtime/commands/redo-blob-store.js +92 -0
  303. package/dist/runtime/commands/redo.js +361 -0
  304. package/dist/runtime/commands/release-notes.js +229 -0
  305. package/dist/runtime/commands/repo-map.js +95 -0
  306. package/dist/runtime/commands/report.js +299 -0
  307. package/dist/runtime/commands/resume.js +118 -0
  308. package/dist/runtime/commands/review-consensus.js +68 -53
  309. package/dist/runtime/commands/rewind.js +333 -0
  310. package/dist/runtime/commands/roster.js +14 -14
  311. package/dist/runtime/commands/sessions.js +163 -0
  312. package/dist/runtime/commands/share.js +316 -0
  313. package/dist/runtime/commands/skills.js +31 -31
  314. package/dist/runtime/commands/status.js +186 -0
  315. package/dist/runtime/commands/stickers.js +82 -0
  316. package/dist/runtime/commands/style.js +194 -0
  317. package/dist/runtime/commands/theme.js +196 -0
  318. package/dist/runtime/commands/undo.js +54 -22
  319. package/dist/runtime/commands/update.js +289 -0
  320. package/dist/runtime/commands/vim.js +140 -0
  321. package/dist/runtime/commands/worktree.js +177 -0
  322. package/dist/runtime/commands/worktrees.js +155 -0
  323. package/dist/runtime/headless-repl.js +195 -0
  324. package/dist/runtime/headless.js +543 -0
  325. package/dist/runtime/load-hooks-or-exit.js +71 -0
  326. package/dist/runtime/plan-decompose.js +531 -0
  327. package/dist/runtime/sigint-guard.js +272 -0
  328. package/dist/runtime/update-check.js +28 -28
  329. package/dist/runtime/version.js +65 -0
  330. package/dist/skills/bundled/batch.js +617 -0
  331. package/dist/skills/bundled/index.js +45 -0
  332. package/dist/skills/bundled/loop.js +358 -0
  333. package/dist/skills/bundled/remember.js +383 -0
  334. package/dist/skills/bundled/simplify.js +289 -0
  335. package/dist/skills/bundled/skillify.js +373 -0
  336. package/dist/skills/bundled/stuck.js +558 -0
  337. package/dist/skills/bundled/verify.js +439 -0
  338. package/dist/testing/vcr.js +486 -0
  339. package/dist/tools/agent-tool.js +229 -0
  340. package/dist/tools/apply-patch.js +556 -0
  341. package/dist/tools/ask-user-question.js +288 -0
  342. package/dist/tools/ask-user.js +115 -0
  343. package/dist/tools/bash.js +624 -46
  344. package/dist/tools/brief.js +224 -0
  345. package/dist/tools/enter-worktree.js +250 -0
  346. package/dist/tools/exit-worktree.js +147 -0
  347. package/dist/tools/file-tools.js +161 -44
  348. package/dist/tools/lsp-tools.js +189 -0
  349. package/dist/tools/mcp-tool.js +260 -0
  350. package/dist/tools/multi-edit.js +361 -0
  351. package/dist/tools/powershell.js +268 -0
  352. package/dist/tools/registry.js +85 -0
  353. package/dist/tools/skill-tool.js +96 -0
  354. package/dist/tools/sleep.js +99 -0
  355. package/dist/tools/synthetic-output.js +133 -0
  356. package/dist/tools/tasks.js +208 -0
  357. package/dist/tools/todo-write.js +184 -0
  358. package/dist/tools/verify-plan-execution.js +295 -0
  359. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  360. package/dist/tools/web-fetch.js +195 -10
  361. package/dist/tools/web-search.js +458 -0
  362. package/dist/tui/agent-progress-card.js +111 -0
  363. package/dist/tui/agent-tree.js +11 -1
  364. package/dist/tui/ask-modal.js +14 -14
  365. package/dist/tui/ask-user-question-chips.js +257 -0
  366. package/dist/tui/ask-user-question-prompt.js +203 -0
  367. package/dist/tui/compact-banner.js +81 -0
  368. package/dist/tui/conversation-pane.js +85 -11
  369. package/dist/tui/cost-table.js +111 -0
  370. package/dist/tui/device-flow.js +2 -2
  371. package/dist/tui/doctor-table.js +46 -0
  372. package/dist/tui/feedback-prompt.js +156 -0
  373. package/dist/tui/input-box.js +247 -32
  374. package/dist/tui/login-picker.js +3 -3
  375. package/dist/tui/markdown-render.js +6 -6
  376. package/dist/tui/onboarding-wizard.js +240 -0
  377. package/dist/tui/permissions-picker.js +86 -0
  378. package/dist/tui/render.js +35 -0
  379. package/dist/tui/repl-render.js +332 -54
  380. package/dist/tui/repl-splash-art.js +16 -16
  381. package/dist/tui/repl-splash-mascot.js +48 -24
  382. package/dist/tui/repl-splash.js +22 -22
  383. package/dist/tui/repl.js +124 -44
  384. package/dist/tui/slash-palette.js +6 -6
  385. package/dist/tui/splash.js +2 -2
  386. package/dist/tui/status-bar.js +109 -31
  387. package/dist/tui/status-table.js +7 -0
  388. package/dist/tui/stickers-art.js +136 -0
  389. package/dist/tui/style-table.js +28 -0
  390. package/dist/tui/theme-table.js +29 -0
  391. package/dist/tui/thinking-spinner.js +123 -0
  392. package/dist/tui/tool-stream-pane.js +53 -4
  393. package/dist/tui/update-banner.js +27 -2
  394. package/dist/tui/vim-input.js +267 -0
  395. package/dist/tui/welcome-banner.js +107 -0
  396. package/dist/tui/welcome-data.js +293 -0
  397. package/dist/tui/workspace-context.js +2 -2
  398. package/docs/examples/codegraph.mcp.json +10 -0
  399. package/package.json +25 -7
  400. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  401. package/test/scenarios/compact-force.scenario.txt +11 -0
  402. package/test/scenarios/identity.scenario.txt +11 -0
  403. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  404. package/test/scenarios/walkback.scenario.txt +12 -0
  405. package/dist/core/engine/compaction-hook.js +0 -154
@@ -12,29 +12,29 @@ import { validateHostnameForFetch } from '../../tools/web-fetch.js';
12
12
  *
13
13
  * Supported source schemes:
14
14
  *
15
- * 1. `gh:owner/repo[/subdir][@ref]`
16
- * → `gh:anthropics/skills/python-coding-standards@main`
17
- * Fetches the GitHub tarball via the public codeload endpoint,
18
- * extracts the requested subtree.
15
+ * 1. `gh:owner/repo[/subdir][@ref]`
16
+ * → `gh:anthropics/skills/python-coding-standards@main`
17
+ * Fetches the GitHub tarball via the public codeload endpoint,
18
+ * extracts the requested subtree.
19
19
  *
20
- * 2. `https://github.com/<owner>/<repo>/tree/<ref>/<subdir>` (or `/blob/`)
21
- * Normalised to the gh: form above.
20
+ * 2. `https://github.com/<owner>/<repo>/tree/<ref>/<subdir>` (or `/blob/`)
21
+ * Normalised to the gh: form above.
22
22
  *
23
- * 3. `anthropic:<slug>` — convenience alias for
24
- * `gh:anthropics/skills/<slug>@main`. Hard-coded base; the only
25
- * reason this exists is so operators can copy a slug from the
26
- * Anthropic docs without remembering the org name.
23
+ * 3. `anthropic:<slug>` — convenience alias for
24
+ * `gh:anthropics/skills/<slug>@main`. Hard-coded base; the only
25
+ * reason this exists is so operators can copy a slug from the
26
+ * Anthropic docs without remembering the org name.
27
27
  *
28
- * 4. `npm:<package>` — fetches a tarball from the npm registry,
29
- * extracts, looks for `SKILL.md` at the package root.
28
+ * 4. `npm:<package>` — fetches a tarball from the npm registry,
29
+ * extracts, looks for `SKILL.md` at the package root.
30
30
  *
31
- * 5. Local path — `./relative` or `/abs/path`. Copied to tmp so the
32
- * caller can mutate the original without affecting install.
31
+ * 5. Local path — `./relative` or `/abs/path`. Copied to tmp so the
32
+ * caller can mutate the original without affecting install.
33
33
  *
34
- * 6. Catalog name — bare slug, queried against
35
- * `https://catalog.pugi.dev/api/skills/<name>`. Returns a 404 →
36
- * we surface a hint pointing at the `gh:anthropics/skills/<name>`
37
- * form rather than crashing.
34
+ * 6. Catalog name — bare slug, queried against
35
+ * `https://catalog.pugi.dev/api/skills/<name>`. Returns a 404 →
36
+ * we surface a hint pointing at the `gh:anthropics/skills/<name>`
37
+ * form rather than crashing.
38
38
  *
39
39
  * Every resolver writes the payload into a fresh temp dir under
40
40
  * `/tmp/pugi-skill-XXXXXX/` (caller cleans up after install completes).
@@ -58,7 +58,7 @@ export async function fetchSource(source) {
58
58
  throw new Error(`SOURCE_PARSE: anthropic: source needs a bare slug (got "${source}"). Example: anthropic:algorithmic-art`);
59
59
  }
60
60
  // Real layout is `anthropics/skills` repo → `skills/<slug>/SKILL.md`.
61
- // Verified 2026-05-25 against the live repo tarball.
61
+ // Verified against the live repo tarball.
62
62
  return fetchGitHub(`${ANTHROPIC_REPO}/skills/${slug}@main`.slice(3));
63
63
  }
64
64
  if (source.startsWith('npm:')) {
@@ -113,7 +113,7 @@ async function fetchGitHub(raw) {
113
113
  const spec = parseGithubSpec(raw);
114
114
  // Use codeload.github.com — the public tarball endpoint requires no
115
115
  // auth for public repos and returns a single .tar.gz of the requested
116
- // ref's tree. Private repos are out of scope for α7.0.
116
+ // ref's tree. Private repos are out of scope for .
117
117
  const tarUrl = `https://codeload.github.com/${spec.owner}/${spec.repo}/tar.gz/${spec.ref}`;
118
118
  const tmpRoot = mkdtempSync(join(tmpdir(), 'pugi-skill-gh-'));
119
119
  const tarPath = join(tmpRoot, 'payload.tar.gz');
@@ -268,9 +268,9 @@ async function requestFollow(url) {
268
268
  // future: webhook delivery). Drift between two copies of that block
269
269
  // list would be a real footgun — the SSRF cheat-sheet covers ~10
270
270
  // ranges and missing one (e.g. SIIT/NAT64) is exactly the class of
271
- // bug Codex caught in PR #349.
271
+ // bug Codex caught in PR.
272
272
  // Initial scheme — locked for entire redirect chain. Codex P2 review
273
- // (PR #362 v2): an HTTPS source that 302s к public http:// URL would
273
+ // (PR v2): an HTTPS source that 302s к public http:// URL would
274
274
  // otherwise be fetched cleartext, MITM tampers payload. Stay TLS.
275
275
  const initialScheme = new URL(currentUrl).protocol;
276
276
  await guardOutboundUrl(currentUrl, 'initial request', initialScheme);
@@ -321,7 +321,7 @@ async function guardOutboundUrl(rawUrl, label, initialScheme) {
321
321
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
322
322
  throw new Error(`SOURCE_SSRF: ${label} uses unsupported scheme ${parsed.protocol} (only http/https).`);
323
323
  }
324
- // Codex P2 PR #362 v2: HTTPS source MUST NOT downgrade к HTTP across
324
+ // Codex P2 PR v2: HTTPS source MUST NOT downgrade к HTTP across
325
325
  // redirect chain — would let MITM tamper с payload after the initial
326
326
  // TLS hop. Once we started TLS, stay TLS.
327
327
  if (initialScheme === 'https:' && parsed.protocol === 'http:') {
@@ -405,20 +405,20 @@ async function extractTarball(tarPath, destDir) {
405
405
  // violations and throw AFTER extraction completes (see below).
406
406
  filter: (path, entry) => {
407
407
  // 1. Block any symlink or hardlink — these are the tar-slip
408
- // vectors. A symlink to ../../home/user/.ssh + a follow-up
409
- // write to that symlink would exfil secrets.
408
+ // vectors. A symlink to ../../home/user/.ssh + a follow-up
409
+ // write to that symlink would exfil secrets.
410
410
  if (entry.type === 'SymbolicLink' || entry.type === 'Link') {
411
411
  violations.push(`SOURCE_TAR_SYMLINK: tarball contains ${entry.type} entry (${path} → ${entry.linkpath ?? '?'}). Refusing extraction.`);
412
412
  return false;
413
413
  }
414
414
  // 2. Block absolute paths — `tar` strips the leading "/" in
415
- // permissive mode and writes anyway. We refuse such entries.
415
+ // permissive mode and writes anyway. We refuse such entries.
416
416
  if (path.startsWith('/')) {
417
417
  violations.push(`SOURCE_TAR_ABSOLUTE: tarball entry has absolute path: ${path}`);
418
418
  return false;
419
419
  }
420
420
  // 3. Block parent-traversal segments. `..` as a path segment
421
- // cannot be present in any legitimate skill/agent payload.
421
+ // cannot be present in any legitimate skill/agent payload.
422
422
  const segments = path.split(/[\\/]+/);
423
423
  if (segments.includes('..')) {
424
424
  violations.push(`SOURCE_TAR_TRAVERSAL: tarball entry has parent-traversal segment: ${path}`);
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Headless subprocess driver for the smoke harness (Phase 1).
3
+ *
4
+ * Spawns `pugi --headless`, feeds the scenario's user-input lines one
5
+ * at a time, and collects every JSON envelope written to stdout. The
6
+ * driver is the bridge between the in-process scenario parser and the
7
+ * real CLI binary — running scenarios via subprocess is what lets
8
+ * `pugi smoke` validate the AS-PUBLISHED CLI, not just the in-process
9
+ * code path.
10
+ *
11
+ * Phase 1 invariants:
12
+ *
13
+ * - One scenario = one subprocess. We do NOT reuse processes across
14
+ * scenarios; each run starts in a fresh temp workspace so file
15
+ * assertions are deterministic.
16
+ *
17
+ * - Stdin is fed line-by-line on a timer (default 200ms gap). We
18
+ * could fire all lines at once, but spacing them out gives the
19
+ * engine a chance to emit `persona-turn` envelopes between user
20
+ * turns. Mirrors real-operator typing cadence — feeds a more
21
+ * realistic test signal than a burst write.
22
+ *
23
+ * - Hard timeout per scenario (default 30s). The driver kills the
24
+ * subprocess on timeout and surfaces a clean executor-error so the
25
+ * report still shows the row instead of hanging CI.
26
+ *
27
+ * - Stderr is captured but NOT parsed. We forward it to the optional
28
+ * `onStderr` sink so the operator can see what the headless CLI
29
+ * wrote to stderr (banners, MCP load notices) — handy for
30
+ * debugging a failing scenario locally.
31
+ */
32
+ import { spawn } from 'node:child_process';
33
+ import { mkdtempSync, rmSync } from 'node:fs';
34
+ import { tmpdir } from 'node:os';
35
+ import { resolve } from 'node:path';
36
+ /**
37
+ * Run a parsed scenario through the headless CLI. Returns the captured
38
+ * envelope stream + the workspace root the process ran in so the
39
+ * runner can resolve `EXPECT_FILE` paths.
40
+ */
41
+ export async function runHeadlessScenario(scenario, opts = {}) {
42
+ const pugiBin = opts.pugiBin ?? 'pugi';
43
+ const extraArgs = opts.extraArgs ?? [];
44
+ const timeoutMs = opts.timeoutMs ?? 30_000;
45
+ const lineGapMs = opts.lineGapMs ?? 200;
46
+ const onStderr = opts.onStderr ?? noopChunk;
47
+ const spawner = opts.spawner ?? defaultSpawner;
48
+ const workspaceRoot = mkdtempSync(resolve(tmpdir(), 'pugi-smoke-'));
49
+ let cleanedUp = false;
50
+ const args = ['--headless', ...extraArgs];
51
+ const child = spawner(pugiBin, args, {
52
+ cwd: workspaceRoot,
53
+ env: { ...process.env, PUGI_HEADLESS: '1' },
54
+ });
55
+ const envelopes = [];
56
+ let stdoutBuffer = '';
57
+ child.stdout.setEncoding('utf8');
58
+ child.stdout.on('data', (chunk) => {
59
+ stdoutBuffer += chunk;
60
+ let newlineIndex = stdoutBuffer.indexOf('\n');
61
+ while (newlineIndex !== -1) {
62
+ const line = stdoutBuffer.slice(0, newlineIndex);
63
+ stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
64
+ const parsed = parseEnvelopeLine(line);
65
+ if (parsed)
66
+ envelopes.push(parsed);
67
+ newlineIndex = stdoutBuffer.indexOf('\n');
68
+ }
69
+ });
70
+ child.stderr.setEncoding('utf8');
71
+ child.stderr.on('data', (chunk) => onStderr(chunk));
72
+ const userInputs = scenario.steps
73
+ .filter((s) => s.kind === 'user-input')
74
+ .map((s) => s.body);
75
+ // Feed inputs serially with a small gap. We don't await the engine's
76
+ // response — Phase 1 evaluates the WHOLE envelope stream after the
77
+ // process closes, so the gap is purely to give the engine room to
78
+ // emit between turns. A future phase can swap this for a barrier
79
+ // ("wait for `persona-turn` before sending the next `user-turn`")
80
+ // once envelope ordering is sealed.
81
+ const feedPromise = (async () => {
82
+ for (const line of userInputs) {
83
+ child.stdin.write(`${line}\n`);
84
+ await delay(lineGapMs);
85
+ }
86
+ child.stdin.end();
87
+ })();
88
+ const timeoutHandle = setTimeout(() => {
89
+ child.kill('SIGTERM');
90
+ }, timeoutMs);
91
+ // Don't keep node alive purely for the timeout — the spawned child
92
+ // already holds the loop via its IPC pipe.
93
+ timeoutHandle.unref?.();
94
+ await new Promise((resolvePromise) => {
95
+ child.on('close', () => resolvePromise());
96
+ });
97
+ clearTimeout(timeoutHandle);
98
+ // Flush any tail bytes that did not end with a newline.
99
+ if (stdoutBuffer.length > 0) {
100
+ const parsed = parseEnvelopeLine(stdoutBuffer);
101
+ if (parsed)
102
+ envelopes.push(parsed);
103
+ stdoutBuffer = '';
104
+ }
105
+ await feedPromise.catch(() => {
106
+ /* feed promise rejects when the child closes early — ignore */
107
+ });
108
+ // Cleanup the temp workspace lazily — the orchestrator may want to
109
+ // inspect it after the run. Callers that need a fresh dir per scenario
110
+ // should pass a custom workspaceRoot in a future phase; for now the
111
+ // runner resolves EXPECT_FILE paths against this directory and the
112
+ // OS reaps the tmp tree on reboot.
113
+ // (Explicit cleanup helper exposed for tests that want determinism.)
114
+ void cleanedUp;
115
+ return { envelopes, workspaceRoot };
116
+ }
117
+ /**
118
+ * Tear down a workspace created by `runHeadlessScenario`. Exposed
119
+ * separately because the orchestrator wants the directory to survive
120
+ * the run for EXPECT_FILE evaluation.
121
+ */
122
+ export function cleanupWorkspace(workspaceRoot) {
123
+ try {
124
+ rmSync(workspaceRoot, { recursive: true, force: true });
125
+ }
126
+ catch {
127
+ /* best-effort */
128
+ }
129
+ }
130
+ /**
131
+ * Parse one line of headless stdout into an envelope. Returns null
132
+ * when the line is empty or unparseable — non-fatal so the runner can
133
+ * tolerate stray banner output.
134
+ */
135
+ export function parseEnvelopeLine(raw) {
136
+ const line = raw.trim();
137
+ if (line.length === 0)
138
+ return null;
139
+ if (!line.startsWith('{'))
140
+ return null;
141
+ try {
142
+ const parsed = JSON.parse(line);
143
+ if (typeof parsed['kind'] !== 'string')
144
+ return null;
145
+ if (typeof parsed['body'] !== 'string')
146
+ return null;
147
+ const ts = typeof parsed['ts'] === 'number'
148
+ ? parsed['ts']
149
+ : Number(parsed['ts'] ?? Date.now());
150
+ return {
151
+ kind: parsed['kind'],
152
+ body: parsed['body'],
153
+ ts: Number.isFinite(ts) ? ts : Date.now(),
154
+ };
155
+ }
156
+ catch {
157
+ return null;
158
+ }
159
+ }
160
+ function defaultSpawner(bin, args, options) {
161
+ return spawn(bin, args, {
162
+ cwd: options.cwd,
163
+ env: options.env,
164
+ stdio: ['pipe', 'pipe', 'pipe'],
165
+ });
166
+ }
167
+ function delay(ms) {
168
+ return new Promise((resolve) => {
169
+ const handle = setTimeout(resolve, ms);
170
+ handle.unref?.();
171
+ });
172
+ }
173
+ function noopChunk(_chunk) { }
174
+ //# sourceMappingURL=headless-driver.js.map
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Smoke orchestrator — glues the scenario parser, the headless
3
+ * subprocess driver, and the runner into a single "load → run → report"
4
+ * pipeline. The CLI surface (`pugi smoke`) and the standalone script
5
+ * (`scripts/run-scenarios.ts`) both call into this module so the two
6
+ * entry points share one code path.
7
+ *
8
+ * Phase 1 boundary — `runSmoke` is responsible for:
9
+ *
10
+ * 1. Discovering scenario files under `scenariosDir` (glob match on
11
+ * `*.scenario.txt`).
12
+ * 2. Parsing each file via `parseScenario`. Parse errors are surfaced
13
+ * via the report but do not stop the run — every scenario gets a
14
+ * chance to fail with a clean diagnostic.
15
+ * 3. Driving each scenario through the headless executor (the
16
+ * executor is injected so tests can swap it for a deterministic
17
+ * stub; production wires `runHeadlessScenario` from
18
+ * `headless-driver.ts`).
19
+ * 4. Filtering by `--filter <pattern>` (compiles to fnmatch-lite).
20
+ * 5. Computing pass/fail/summary numbers.
21
+ *
22
+ * The orchestrator is intentionally synchronous (apart from the
23
+ * per-scenario `await`) — running scenarios in parallel is a Phase 2
24
+ * concern. The corpus is small and sequential output is easier to read.
25
+ */
26
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
27
+ import { resolve } from 'node:path';
28
+ import { parseScenario, } from './scenario-parser.js';
29
+ import { runScenario, } from './runner.js';
30
+ /**
31
+ * Top-level smoke entry. Returns the report so the CLI can pretty-print
32
+ * it AND set `process.exitCode` deterministically.
33
+ */
34
+ export async function runSmoke(opts) {
35
+ const log = opts.log ?? noopLog;
36
+ const now = opts.now ?? Date.now;
37
+ const allScenarios = loadScenariosFromDir(opts.scenariosDir);
38
+ const visible = opts.filter && opts.filter.length > 0
39
+ ? filterByPattern(allScenarios, opts.filter)
40
+ : allScenarios;
41
+ const results = [];
42
+ let passed = 0;
43
+ let failed = 0;
44
+ for (const item of visible) {
45
+ log(`pugi smoke: running ${item.scenario.id}`);
46
+ if (item.parseErrors.length > 0) {
47
+ results.push({
48
+ id: item.scenario.id,
49
+ filePath: item.scenario.filePath,
50
+ status: 'parse-error',
51
+ durationMs: 0,
52
+ assertionCount: 0,
53
+ failures: [],
54
+ parseErrors: item.parseErrors,
55
+ });
56
+ failed += 1;
57
+ continue;
58
+ }
59
+ let envelopes = [];
60
+ let workspaceRoot = '.';
61
+ try {
62
+ const out = await opts.executor(item.scenario);
63
+ envelopes = out.envelopes;
64
+ workspaceRoot = out.workspaceRoot;
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ results.push({
69
+ id: item.scenario.id,
70
+ filePath: item.scenario.filePath,
71
+ status: 'executor-error',
72
+ durationMs: 0,
73
+ assertionCount: 0,
74
+ failures: [],
75
+ executorError: message,
76
+ });
77
+ failed += 1;
78
+ continue;
79
+ }
80
+ const result = runScenario({
81
+ scenario: item.scenario,
82
+ envelopes,
83
+ workspaceRoot,
84
+ now,
85
+ });
86
+ results.push({
87
+ id: result.id,
88
+ filePath: item.scenario.filePath,
89
+ status: result.passed ? 'passed' : 'failed',
90
+ durationMs: result.durationMs,
91
+ assertionCount: result.assertionCount,
92
+ failures: result.failures,
93
+ });
94
+ if (result.passed)
95
+ passed += 1;
96
+ else
97
+ failed += 1;
98
+ }
99
+ const total = visible.length;
100
+ const skipped = allScenarios.length - visible.length;
101
+ const exitCode = failed === 0 ? 0 : 1;
102
+ return { total, passed, failed, skipped, results, exitCode };
103
+ }
104
+ /**
105
+ * Walk `dir` for `*.scenario.txt` files (non-recursive). Returns each
106
+ * file's parsed scenario + collected parse errors so the orchestrator
107
+ * can surface malformed files as failed runs rather than skipping them.
108
+ */
109
+ export function loadScenariosFromDir(dir) {
110
+ let names = [];
111
+ try {
112
+ names = readdirSync(dir);
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ const out = [];
118
+ for (const name of names) {
119
+ if (!name.endsWith('.scenario.txt'))
120
+ continue;
121
+ const filePath = resolve(dir, name);
122
+ let stat;
123
+ try {
124
+ stat = statSync(filePath);
125
+ }
126
+ catch {
127
+ continue;
128
+ }
129
+ if (!stat.isFile())
130
+ continue;
131
+ const body = readFileSync(filePath, 'utf8');
132
+ const parsed = parseScenario(filePath, body);
133
+ if (parsed.scenario) {
134
+ out.push({ scenario: parsed.scenario, parseErrors: parsed.errors });
135
+ }
136
+ }
137
+ // Stable sort by id so report output is deterministic across
138
+ // filesystems with different readdir order.
139
+ out.sort((a, b) => a.scenario.id.localeCompare(b.scenario.id));
140
+ return out;
141
+ }
142
+ /**
143
+ * Render a SmokeReport into a multi-line human-readable string. Kept
144
+ * separate from `runSmoke` so the CLI can pick its own format (text vs
145
+ * JSON). The default text format mirrors `node:test`'s tap-lite output:
146
+ *
147
+ * ok 1 - identity (12ms)
148
+ * not ok 2 - codegen-create-file (8ms)
149
+ * line 5: EXPECT failed — no envelope matched ...
150
+ *
151
+ * pugi smoke: 1 passed, 1 failed
152
+ */
153
+ export function renderReportText(report) {
154
+ const lines = [];
155
+ for (let i = 0; i < report.results.length; i += 1) {
156
+ const r = report.results[i];
157
+ if (!r)
158
+ continue;
159
+ const ordinal = i + 1;
160
+ if (r.status === 'passed') {
161
+ lines.push(`ok ${ordinal} - ${r.id} (${r.durationMs}ms)`);
162
+ }
163
+ else if (r.status === 'failed') {
164
+ lines.push(`not ok ${ordinal} - ${r.id} (${r.durationMs}ms)`);
165
+ for (const f of r.failures) {
166
+ lines.push(` line ${f.line}: ${f.message}`);
167
+ }
168
+ }
169
+ else if (r.status === 'parse-error') {
170
+ lines.push(`not ok ${ordinal} - ${r.id} (parse error)`);
171
+ for (const e of r.parseErrors ?? []) {
172
+ lines.push(` line ${e.line}: ${e.message}`);
173
+ }
174
+ }
175
+ else if (r.status === 'executor-error') {
176
+ lines.push(`not ok ${ordinal} - ${r.id} (executor error)`);
177
+ lines.push(` ${r.executorError ?? 'unknown executor failure'}`);
178
+ }
179
+ }
180
+ lines.push('');
181
+ const skippedSuffix = report.skipped > 0 ? `, ${report.skipped} skipped` : '';
182
+ lines.push(`pugi smoke: ${report.passed} passed, ${report.failed} failed${skippedSuffix}`);
183
+ return lines.join('\n');
184
+ }
185
+ function filterByPattern(scenarios, pattern) {
186
+ if (!pattern.includes('*')) {
187
+ return scenarios.filter((s) => s.scenario.id.includes(pattern));
188
+ }
189
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
190
+ const re = new RegExp(`^${escaped.replace(/\*/g, '.*')}$`);
191
+ return scenarios.filter((s) => re.test(s.scenario.id));
192
+ }
193
+ function noopLog(_line) { }
194
+ //# sourceMappingURL=orchestrator.js.map