@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,383 @@
1
+ /**
2
+ * `pugi remember` — proposal-first memory curator.
3
+ *
4
+ * Bundled skill, batch 1 (backlog). Today `pugi memory write` (and
5
+ * the auto-extractor that fires from MEMORY: markers in chat) writes
6
+ * silently — operators occasionally find unwanted facts persisted with
7
+ * no chance to veto. This skill flips the surface: it inspects recent
8
+ * memory candidates, classifies each as project / personal / temp, and
9
+ * presents EVERY proposed write to the operator BEFORE persisting.
10
+ * Nothing is queued or sent to the admin-api until the operator
11
+ * approves it.
12
+ *
13
+ * # Three classification buckets
14
+ *
15
+ * - project — codebase / ADR / architecture / convention.
16
+ * Targets `kind` = `architecture` | `pattern` | `bug`.
17
+ * - personal — operator-specific preference, workflow, identity.
18
+ * Targets `kind` = `preference` | `workflow` | `fact`.
19
+ * - temp — short-lived note, scheduled for `forgetAfter`.
20
+ * Pugi heuristic: anything that looks transient
21
+ * (timestamps, log paste, single-session note).
22
+ *
23
+ * # Surface
24
+ *
25
+ * Two flows:
26
+ *
27
+ * 1. **Auto-classify** an inline list of candidate strings (CLI args
28
+ * or `--input <path>`). For each one Pugi emits a structured
29
+ * proposal. Operator answers `y`/`n`/`s` (yes / no / skip) per
30
+ * proposal. `y` enqueues via the existing `enqueueMemoryOp` API.
31
+ *
32
+ * 2. **Review queue** (`pugi remember review`) — walks any pending
33
+ * memory queue lines and lets the operator drop entries before
34
+ * `pugi memory sync` ships them.
35
+ *
36
+ * # Opt-in
37
+ *
38
+ * `pugi memory write …` keeps its current behaviour (immediate enqueue,
39
+ * no prompt). The proposal-first mode requires either:
40
+ *
41
+ * - explicit `pugi remember <text>...` invocation, OR
42
+ * - `pugi memory write --curate` flag (added in this batch; the
43
+ * curator runs in proposal-first mode).
44
+ *
45
+ * # Provenance
46
+ *
47
+ * Inspired by the the upstream tool bundled-skills pattern (intel from
48
+ * leak-research memos, clean-room TS). No upstream code reused.
49
+ */
50
+ import { readFileSync } from 'node:fs';
51
+ import { PERSONA_MEMORY_KINDS, enqueueMemoryOp, } from '../../core/memory-sync/queue.js';
52
+ const DEFAULT_PERSONA = 'mira';
53
+ function parseFlags(args) {
54
+ const flags = {
55
+ json: false,
56
+ personaSlug: DEFAULT_PERSONA,
57
+ inputPath: null,
58
+ };
59
+ const positionals = [];
60
+ for (let i = 0; i < args.length; i += 1) {
61
+ const arg = args[i];
62
+ if (arg === undefined)
63
+ continue;
64
+ if (arg === '--json') {
65
+ flags.json = true;
66
+ continue;
67
+ }
68
+ if (arg === '--persona') {
69
+ const next = args[i + 1];
70
+ if (next === undefined)
71
+ return { flags, positionals, error: '--persona requires a value' };
72
+ flags.personaSlug = next;
73
+ i += 1;
74
+ continue;
75
+ }
76
+ if (arg === '--input') {
77
+ const next = args[i + 1];
78
+ if (next === undefined)
79
+ return { flags, positionals, error: '--input requires a path' };
80
+ flags.inputPath = next;
81
+ i += 1;
82
+ continue;
83
+ }
84
+ if (arg === '--help' || arg === '-h') {
85
+ return { flags, positionals, error: 'help' };
86
+ }
87
+ if (arg.startsWith('--')) {
88
+ return { flags, positionals, error: `unknown flag: ${arg}` };
89
+ }
90
+ positionals.push(arg);
91
+ }
92
+ return { flags, positionals, error: null };
93
+ }
94
+ /**
95
+ * Single-line classifier. The signal is intentionally crude (regex +
96
+ * keyword bags) so the behaviour is deterministic and explainable —
97
+ * the operator approves each proposal anyway. Edge cases land in
98
+ * `personal` by default because a stray project fact persisting under
99
+ * the operator's profile is less corrosive than a stray personal note
100
+ * leaking into a project's memory.
101
+ *
102
+ * Public for direct test coverage.
103
+ */
104
+ export function classifyMemoryCandidate(raw, now) {
105
+ const trimmed = raw.trim();
106
+ if (trimmed === '')
107
+ return null;
108
+ if (trimmed.length > 4000)
109
+ return null;
110
+ const lower = trimmed.toLowerCase();
111
+ const projectSignals = [
112
+ /\badr-?\d+\b/,
113
+ /\bmigration\b/,
114
+ /\bschema\b/,
115
+ /\bendpoint\b/,
116
+ /\barchitecture\b/,
117
+ /\bconvention\b/,
118
+ /\bbug\b/,
119
+ /\bbroken\b/,
120
+ /\bpattern\b/,
121
+ /\bworkspace\b/,
122
+ /\bpackage\b/,
123
+ /\bmodule\b/,
124
+ ];
125
+ const personalSignals = [
126
+ /\bi (prefer|like|hate|always|never)\b/,
127
+ /\bmy (workflow|setup|laptop|terminal|editor)\b/,
128
+ /\bin russian\b/,
129
+ /\bin ukrainian\b/,
130
+ /\bdo not ask\b/,
131
+ /\bdon't ask\b/,
132
+ ];
133
+ const tempSignals = [
134
+ /\btoday\b/,
135
+ /\btomorrow\b/,
136
+ /\bnow\b/,
137
+ /\btemporary\b/,
138
+ /\bfor now\b/,
139
+ /\bremind me\b/,
140
+ /\bthis session\b/,
141
+ ];
142
+ const matchedProject = projectSignals.some((rx) => rx.test(lower));
143
+ const matchedPersonal = personalSignals.some((rx) => rx.test(lower));
144
+ const matchedTemp = tempSignals.some((rx) => rx.test(lower));
145
+ let bucket;
146
+ let rationale;
147
+ let kind;
148
+ let forgetAfter;
149
+ if (matchedTemp) {
150
+ bucket = 'temp';
151
+ rationale = 'looks transient (timing keyword)';
152
+ kind = 'fact';
153
+ forgetAfter = new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString();
154
+ }
155
+ else if (matchedProject && !matchedPersonal) {
156
+ bucket = 'project';
157
+ rationale = 'codebase / architecture keyword present';
158
+ if (/\bbug\b|\bbroken\b/.test(lower)) {
159
+ kind = 'bug';
160
+ }
161
+ else if (/\bpattern\b/.test(lower)) {
162
+ kind = 'pattern';
163
+ }
164
+ else {
165
+ kind = 'architecture';
166
+ }
167
+ forgetAfter = null;
168
+ }
169
+ else if (matchedPersonal && !matchedProject) {
170
+ bucket = 'personal';
171
+ rationale = 'operator preference / workflow keyword present';
172
+ if (/\bworkflow\b|\bsetup\b/.test(lower)) {
173
+ kind = 'workflow';
174
+ }
175
+ else if (/prefer|like|hate|always|never/.test(lower)) {
176
+ kind = 'preference';
177
+ }
178
+ else {
179
+ kind = 'fact';
180
+ }
181
+ forgetAfter = null;
182
+ }
183
+ else if (matchedProject && matchedPersonal) {
184
+ bucket = 'personal';
185
+ rationale = 'mixed signals; defaulting to personal (safer)';
186
+ kind = 'preference';
187
+ forgetAfter = null;
188
+ }
189
+ else {
190
+ bucket = 'personal';
191
+ rationale = 'no strong signal; defaulting to personal';
192
+ kind = 'fact';
193
+ forgetAfter = null;
194
+ }
195
+ return {
196
+ bucket,
197
+ personaSlug: DEFAULT_PERSONA,
198
+ kind,
199
+ content: trimmed,
200
+ forgetAfter,
201
+ rationale,
202
+ };
203
+ }
204
+ /**
205
+ * Default decide hook — reject everything. Encodes the safety invariant
206
+ * that NOTHING is written when the runtime forgets to pass a real
207
+ * approval gate. Tests assert this behaviour explicitly.
208
+ */
209
+ async function defaultDecide() {
210
+ return 'reject';
211
+ }
212
+ /**
213
+ * Default persistence sink — append to the on-disk pending-write queue
214
+ * via the existing `enqueueMemoryOp` API. `pugi memory sync` is what
215
+ * actually ships these to the admin-api; the curator only enqueues.
216
+ */
217
+ async function defaultPersist(proposal) {
218
+ enqueueMemoryOp({
219
+ op: 'write',
220
+ personaSlug: proposal.personaSlug,
221
+ kind: proposal.kind,
222
+ content: proposal.content,
223
+ forgetAfter: proposal.forgetAfter,
224
+ });
225
+ }
226
+ function loadCandidates(positionals, inputPath) {
227
+ const inline = positionals.length > 0 ? [positionals.join(' ')] : [];
228
+ if (inputPath === null)
229
+ return inline;
230
+ try {
231
+ const raw = readFileSync(inputPath, 'utf8');
232
+ const lines = raw
233
+ .split('\n')
234
+ .map((l) => l.trim())
235
+ .filter((l) => l !== '' && !l.startsWith('#'));
236
+ return [...inline, ...lines];
237
+ }
238
+ catch {
239
+ return inline;
240
+ }
241
+ }
242
+ function renderHumanReport(result) {
243
+ const lines = [];
244
+ lines.push(`pugi remember — proposal-first memory curator`);
245
+ lines.push(`proposals: ${result.proposals.length} accepted: ${result.accepted} rejected: ${result.rejected} skipped: ${result.skipped}`);
246
+ if (result.decisions.length === 0) {
247
+ lines.push('');
248
+ lines.push('No proposals to review.');
249
+ return lines.join('\n');
250
+ }
251
+ for (const entry of result.decisions) {
252
+ const tag = entry.decision === 'accept' ? '[ACCEPTED]' : entry.decision === 'reject' ? '[REJECTED]' : '[SKIPPED]';
253
+ lines.push('');
254
+ lines.push(`${tag} (${entry.proposal.bucket} / ${entry.proposal.kind})`);
255
+ lines.push(` ${entry.proposal.content}`);
256
+ lines.push(` rationale: ${entry.proposal.rationale}`);
257
+ if (entry.proposal.forgetAfter !== null) {
258
+ lines.push(` forget-after: ${entry.proposal.forgetAfter}`);
259
+ }
260
+ }
261
+ return lines.join('\n');
262
+ }
263
+ export async function runRememberCommand(args, ctx) {
264
+ const { flags, positionals, error } = parseFlags(args);
265
+ if (error === 'help') {
266
+ ctx.writeOutput({ ok: true, command: 'remember', usage: REMEMBER_USAGE }, REMEMBER_USAGE);
267
+ return {
268
+ proposals: [],
269
+ decisions: [],
270
+ accepted: 0,
271
+ rejected: 0,
272
+ skipped: 0,
273
+ exitCode: 0,
274
+ };
275
+ }
276
+ if (error !== null) {
277
+ ctx.writeOutput({ ok: false, command: 'remember', error }, `pugi remember: ${error}`);
278
+ return {
279
+ proposals: [],
280
+ decisions: [],
281
+ accepted: 0,
282
+ rejected: 0,
283
+ skipped: 0,
284
+ exitCode: 2,
285
+ };
286
+ }
287
+ const candidates = loadCandidates(positionals, flags.inputPath);
288
+ const proposals = [];
289
+ for (const cand of candidates) {
290
+ const proposal = classifyMemoryCandidate(cand, ctx.now());
291
+ if (proposal === null)
292
+ continue;
293
+ // Carry the operator-overridden persona slug through. The classifier
294
+ // hard-codes the default so callers can swap it here without
295
+ // touching the heuristic.
296
+ proposals.push({ ...proposal, personaSlug: flags.personaSlug });
297
+ }
298
+ // Guard rail: the schema enum is the source of truth for kinds. If
299
+ // anyone widens the classifier without widening PERSONA_MEMORY_KINDS,
300
+ // we surface that drift instead of writing an unknown kind to disk.
301
+ for (const proposal of proposals) {
302
+ if (!PERSONA_MEMORY_KINDS.includes(proposal.kind)) {
303
+ ctx.writeOutput({
304
+ ok: false,
305
+ command: 'remember',
306
+ error: `internal: classifier produced unknown kind '${proposal.kind}'`,
307
+ }, `pugi remember: internal classifier drift on '${proposal.kind}'.`);
308
+ return {
309
+ proposals,
310
+ decisions: [],
311
+ accepted: 0,
312
+ rejected: 0,
313
+ skipped: 0,
314
+ exitCode: 2,
315
+ };
316
+ }
317
+ }
318
+ const decide = ctx.decide ?? defaultDecide;
319
+ const persist = ctx.persist ?? defaultPersist;
320
+ const decisions = [];
321
+ let accepted = 0;
322
+ let rejected = 0;
323
+ let skipped = 0;
324
+ for (const proposal of proposals) {
325
+ const decision = await decide(proposal);
326
+ decisions.push({ proposal, decision });
327
+ if (decision === 'accept') {
328
+ try {
329
+ await persist(proposal);
330
+ accepted += 1;
331
+ }
332
+ catch (err) {
333
+ // Backlog: the memory-queue secret scanner can refuse a
334
+ // persist call even after operator approval (defense-in-depth).
335
+ // Surface the rejection as a `reject` outcome so the operator
336
+ // sees what was blocked without crashing the curator loop.
337
+ const message = err instanceof Error ? err.message : String(err);
338
+ ctx.writeOutput({
339
+ ok: false,
340
+ command: 'remember',
341
+ error: 'persist_blocked',
342
+ proposal,
343
+ message,
344
+ }, `pugi remember: persist blocked — ${message}`);
345
+ decisions[decisions.length - 1] = { proposal, decision: 'reject' };
346
+ rejected += 1;
347
+ }
348
+ }
349
+ else if (decision === 'reject') {
350
+ rejected += 1;
351
+ }
352
+ else {
353
+ skipped += 1;
354
+ }
355
+ }
356
+ const result = {
357
+ proposals,
358
+ decisions,
359
+ accepted,
360
+ rejected,
361
+ skipped,
362
+ exitCode: 0,
363
+ };
364
+ ctx.writeOutput({ ok: true, command: 'remember', result }, renderHumanReport(result));
365
+ return result;
366
+ }
367
+ const REMEMBER_USAGE = [
368
+ 'pugi remember — proposal-first memory curator.',
369
+ '',
370
+ 'Usage:',
371
+ ' pugi remember [--json] [--persona <slug>] [--input <path>] <text>...',
372
+ '',
373
+ 'Flags:',
374
+ ' --json Emit a JSON envelope instead of human text.',
375
+ ' --persona <slug> Persona slug to attribute the memory to (default: mira).',
376
+ ' --input <path> Read newline-separated candidates from a file.',
377
+ '',
378
+ 'Every proposal is shown to the operator BEFORE persisting; nothing is',
379
+ 'written without explicit approval. Unlike `pugi memory write` (silent),',
380
+ 'this surface is curate-first and exists to keep automatic memory drift',
381
+ 'in check.',
382
+ ].join('\n');
383
+ //# sourceMappingURL=remember.js.map
@@ -0,0 +1,289 @@
1
+ /**
2
+ * `pugi simplify` — local 3-agent parallel code review + auto-fix loop.
3
+ *
4
+ * Bundled skill, batch 1 (backlog). Distinct from `/triple-review`,
5
+ * which is the pre-push consensus gate; `simplify` is the inner-loop
6
+ * companion that examines the current `git diff` from three orthogonal
7
+ * dimensions (reuse, quality, efficiency), aggregates findings, and
8
+ * hands the merged review to `pugi fix` so the operator does not need
9
+ * to scroll through three separate reports + paste suggestions back
10
+ * into the engine by hand.
11
+ *
12
+ * # Three review dimensions
13
+ *
14
+ * 1. reuse — does the diff duplicate logic that already exists in
15
+ * the codebase? Prefer extraction + import over copy.
16
+ * 2. quality — does the diff respect Pugi conventions: strict TS,
17
+ * no `any`, naming, error handling, test coverage?
18
+ * 3. efficiency — does the diff introduce avoidable allocations, sync
19
+ * I/O in hot paths, or N+1 patterns?
20
+ *
21
+ * # Flow
22
+ *
23
+ * 1. Capture `git diff` against the configured base ref (default
24
+ * `origin/main`).
25
+ * 2. Fan out to three parallel review prompts. Each prompt is sent
26
+ * through the provided `reviewerInvoker` injection point — the
27
+ * real CLI wires this to `pugi fix --review-only --persona <slug>`,
28
+ * tests inject a stub.
29
+ * 3. Collect findings. Each finding carries a dimension tag + severity
30
+ * ([P1] / [P2] / [P3]) + free-form text.
31
+ * 4. Optionally hand the aggregated findings to `pugi fix` to apply
32
+ * fixes automatically. `--review-only` skips the fix step and just
33
+ * prints the merged report.
34
+ *
35
+ * # Non-goals
36
+ *
37
+ * - **Not** a pre-push gate. Use `/triple-review` for that — it spans
38
+ * three model families across paid OAuth subscriptions and is the
39
+ * production-grade gate.
40
+ * - **Not** a new dispatcher. The fix step delegates to the existing
41
+ * `pugi fix` engine task; this skill only shapes inputs + outputs.
42
+ *
43
+ * # Provenance
44
+ *
45
+ * Inspired by the the upstream tool bundled-skills pattern (intel from
46
+ * leak-research memos, clean-room TS). No upstream code reused.
47
+ */
48
+ import { execFileSync } from 'node:child_process';
49
+ const DEFAULT_BASE_REF = 'origin/main';
50
+ function parseFlags(args) {
51
+ const flags = {
52
+ json: false,
53
+ reviewOnly: false,
54
+ baseRef: DEFAULT_BASE_REF,
55
+ sequential: false,
56
+ };
57
+ for (let i = 0; i < args.length; i += 1) {
58
+ const arg = args[i];
59
+ if (arg === undefined)
60
+ continue;
61
+ if (arg === '--json') {
62
+ flags.json = true;
63
+ continue;
64
+ }
65
+ if (arg === '--review-only') {
66
+ flags.reviewOnly = true;
67
+ continue;
68
+ }
69
+ if (arg === '--sequential') {
70
+ flags.sequential = true;
71
+ continue;
72
+ }
73
+ if (arg === '--base') {
74
+ const next = args[i + 1];
75
+ if (next === undefined)
76
+ return { flags, error: '--base requires a ref' };
77
+ flags.baseRef = next;
78
+ i += 1;
79
+ continue;
80
+ }
81
+ if (arg === '--help' || arg === '-h') {
82
+ return { flags, error: 'help' };
83
+ }
84
+ return { flags, error: `unknown argument: ${arg}` };
85
+ }
86
+ return { flags, error: null };
87
+ }
88
+ /**
89
+ * Default `git diff` capture. Lives separate from the runner so tests
90
+ * can stub by injecting `captureDiff`.
91
+ */
92
+ export function defaultCaptureDiff(baseRef, cwd) {
93
+ try {
94
+ return execFileSync('git', ['diff', `${baseRef}...HEAD`, '--unified=3'], {
95
+ cwd,
96
+ encoding: 'utf8',
97
+ maxBuffer: 32 * 1024 * 1024,
98
+ timeout: 10_000,
99
+ });
100
+ }
101
+ catch {
102
+ return '';
103
+ }
104
+ }
105
+ const DIMENSION_PROMPTS = {
106
+ reuse: [
107
+ 'You are reviewing a git diff for code reuse opportunities.',
108
+ 'For every block that duplicates existing utilities in the workspace,',
109
+ 'or that re-implements a helper that should be extracted, emit a',
110
+ 'finding line. Format each finding as:',
111
+ ' [P1|P2|P3] <relative/path>: <one-sentence summary>',
112
+ 'Severity rubric:',
113
+ ' P1 — clear duplicate of an in-repo helper.',
114
+ ' P2 — copy-paste of a similar block that should be extracted.',
115
+ ' P3 — opportunity worth noting but not blocking.',
116
+ ].join('\n'),
117
+ quality: [
118
+ 'You are reviewing a git diff for code quality vs. Pugi conventions:',
119
+ 'strict TypeScript (no `any`, no `// @ts-ignore`), explicit error handling,',
120
+ 'naming consistency, test coverage for new code paths.',
121
+ 'For every violation, emit:',
122
+ ' [P1|P2|P3] <relative/path>: <one-sentence summary>',
123
+ 'Severity rubric:',
124
+ ' P1 — strict-mode violation or missing critical error path.',
125
+ ' P2 — convention drift (naming, return shape, missing test).',
126
+ ' P3 — style nit worth surfacing.',
127
+ ].join('\n'),
128
+ efficiency: [
129
+ 'You are reviewing a git diff for runtime efficiency.',
130
+ 'Flag avoidable allocations, sync I/O in hot paths, N+1 query patterns,',
131
+ 'unbounded recursion, or accidental quadratic loops.',
132
+ 'For every issue, emit:',
133
+ ' [P1|P2|P3] <relative/path>: <one-sentence summary>',
134
+ 'Severity rubric:',
135
+ ' P1 — algorithmic regression or sync I/O in a hot loop.',
136
+ ' P2 — avoidable allocation or per-row query.',
137
+ ' P3 — minor optimisation worth tracking.',
138
+ ].join('\n'),
139
+ };
140
+ const FINDING_LINE = /^\[(P1|P2|P3)\]\s+([^:]+):\s+(.+)$/;
141
+ /**
142
+ * Parse a free-form reviewer response into structured findings. Lines
143
+ * that don't match the expected `[P1] file: summary` shape are ignored
144
+ * so a reviewer can emit free-form commentary alongside its findings
145
+ * without polluting the aggregator. Exposed for test coverage of the
146
+ * grammar.
147
+ */
148
+ export function parseReviewerFindings(dimension, raw) {
149
+ const findings = [];
150
+ for (const line of raw.split('\n')) {
151
+ const trimmed = line.trim();
152
+ if (!trimmed.startsWith('[P'))
153
+ continue;
154
+ const match = FINDING_LINE.exec(trimmed);
155
+ if (match === null)
156
+ continue;
157
+ const [, severity, file, summary] = match;
158
+ if (severity === undefined || file === undefined || summary === undefined)
159
+ continue;
160
+ findings.push({
161
+ dimension,
162
+ severity: severity,
163
+ file: file.trim() === '-' ? null : file.trim(),
164
+ summary: summary.trim(),
165
+ });
166
+ }
167
+ return findings;
168
+ }
169
+ /**
170
+ * Run the three dimensions in parallel (or sequentially when
171
+ * `--sequential` is set) and aggregate. The aggregator is the only
172
+ * piece of "real" work in this skill; everything else is plumbing.
173
+ */
174
+ export async function aggregateReviewerResults(diff, baseRef, invoker, options = { sequential: false }) {
175
+ const dimensions = ['reuse', 'quality', 'efficiency'];
176
+ const runOne = async (dimension) => {
177
+ const prompt = DIMENSION_PROMPTS[dimension];
178
+ const raw = await invoker({ dimension, prompt, diff, baseRef });
179
+ const findings = parseReviewerFindings(dimension, raw);
180
+ return { dimension, findings, raw };
181
+ };
182
+ const results = [];
183
+ if (options.sequential) {
184
+ for (const dim of dimensions) {
185
+ results.push(await runOne(dim));
186
+ }
187
+ }
188
+ else {
189
+ const settled = await Promise.all(dimensions.map(runOne));
190
+ results.push(...settled);
191
+ }
192
+ const flat = [];
193
+ const summary = { P1: 0, P2: 0, P3: 0 };
194
+ for (const r of results) {
195
+ for (const f of r.findings) {
196
+ flat.push(f);
197
+ summary[f.severity] += 1;
198
+ }
199
+ }
200
+ return {
201
+ diffBytes: Buffer.byteLength(diff, 'utf8'),
202
+ baseRef,
203
+ dimensions: results,
204
+ findings: flat,
205
+ summaryBySeverity: summary,
206
+ };
207
+ }
208
+ function renderHumanReport(report, applied) {
209
+ const lines = [];
210
+ lines.push(`pugi simplify — 3-agent review @ base=${report.baseRef}`);
211
+ lines.push(`diff: ${report.diffBytes} bytes`);
212
+ lines.push(`findings: P1=${report.summaryBySeverity.P1} P2=${report.summaryBySeverity.P2} P3=${report.summaryBySeverity.P3}`);
213
+ lines.push('');
214
+ for (const reviewer of report.dimensions) {
215
+ lines.push(`-- ${reviewer.dimension} (${reviewer.findings.length} findings) --`);
216
+ if (reviewer.findings.length === 0) {
217
+ lines.push(' (clean)');
218
+ }
219
+ else {
220
+ for (const finding of reviewer.findings) {
221
+ lines.push(` [${finding.severity}] ${finding.file ?? '-'}: ${finding.summary}`);
222
+ }
223
+ }
224
+ }
225
+ if (applied !== null) {
226
+ lines.push('');
227
+ lines.push(`pugi fix: applied=${applied.applied} skipped=${applied.skipped}`);
228
+ }
229
+ return lines.join('\n');
230
+ }
231
+ export async function runSimplifyCommand(args, ctx) {
232
+ const { flags, error } = parseFlags(args);
233
+ if (error === 'help') {
234
+ ctx.writeOutput({ ok: true, command: 'simplify', usage: SIMPLIFY_USAGE }, SIMPLIFY_USAGE);
235
+ return {
236
+ report: emptyReport(flags.baseRef),
237
+ applied: null,
238
+ exitCode: 0,
239
+ };
240
+ }
241
+ if (error !== null) {
242
+ ctx.writeOutput({ ok: false, command: 'simplify', error }, `pugi simplify: ${error}`);
243
+ return { report: emptyReport(flags.baseRef), applied: null, exitCode: 2 };
244
+ }
245
+ const capture = ctx.captureDiff ?? defaultCaptureDiff;
246
+ const diff = capture(flags.baseRef, ctx.cwd);
247
+ if (diff.trim() === '') {
248
+ const empty = emptyReport(flags.baseRef);
249
+ ctx.writeOutput({
250
+ ok: true,
251
+ command: 'simplify',
252
+ report: empty,
253
+ note: `No diff against ${flags.baseRef}.`,
254
+ }, `pugi simplify: no diff against ${flags.baseRef}. Nothing to review.`);
255
+ return { report: empty, applied: null, exitCode: 0 };
256
+ }
257
+ const report = await aggregateReviewerResults(diff, flags.baseRef, ctx.reviewerInvoker, { sequential: flags.sequential });
258
+ let applied = null;
259
+ if (!flags.reviewOnly && ctx.fixer !== undefined && report.findings.length > 0) {
260
+ applied = await ctx.fixer(report.findings);
261
+ }
262
+ ctx.writeOutput({ ok: true, command: 'simplify', report, applied }, renderHumanReport(report, applied));
263
+ const exitCode = report.findings.length === 0 ? 0 : 1;
264
+ return { report, applied, exitCode };
265
+ }
266
+ function emptyReport(baseRef) {
267
+ return {
268
+ diffBytes: 0,
269
+ baseRef,
270
+ dimensions: [],
271
+ findings: [],
272
+ summaryBySeverity: { P1: 0, P2: 0, P3: 0 },
273
+ };
274
+ }
275
+ const SIMPLIFY_USAGE = [
276
+ 'pugi simplify — local 3-agent diff review (reuse / quality / efficiency).',
277
+ '',
278
+ 'Usage:',
279
+ ' pugi simplify [--json] [--review-only] [--sequential] [--base <ref>]',
280
+ '',
281
+ 'Flags:',
282
+ ' --json Emit a JSON envelope instead of human text.',
283
+ ' --review-only Skip the auto-fix step; print the merged report only.',
284
+ ' --sequential Run the three reviewers in series (low-quota mode).',
285
+ ' --base <ref> Diff base ref (default: origin/main).',
286
+ '',
287
+ 'Not a pre-push gate — for that, use /triple-review.',
288
+ ].join('\n');
289
+ //# sourceMappingURL=simplify.js.map