@pugi/cli 0.1.0-beta.10 → 0.1.0-beta.101

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 (464) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +55 -11
  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/retro.js +210 -0
  11. package/dist/commands/smoke.js +133 -0
  12. package/dist/core/agent-progress/cleanup.js +134 -0
  13. package/dist/core/agent-progress/schema.js +144 -0
  14. package/dist/core/agent-progress/writer.js +101 -0
  15. package/dist/core/agents/adaptive-router.js +330 -0
  16. package/dist/core/agents/query-decomposer.js +297 -0
  17. package/dist/core/agents/registry.js +3 -3
  18. package/dist/core/approvals/shortcut-resolver.js +98 -0
  19. package/dist/core/artifact-chain/dispatcher.js +148 -0
  20. package/dist/core/artifact-chain/exporter.js +164 -0
  21. package/dist/core/artifact-chain/state.js +243 -0
  22. package/dist/core/artifact-chain/steps.js +169 -0
  23. package/dist/core/ask-user/question.js +92 -0
  24. package/dist/core/audit/audit-trail.js +275 -0
  25. package/dist/core/auth/ensure-authenticated.js +129 -0
  26. package/dist/core/auth/env-provider.js +238 -0
  27. package/dist/core/auto-open-browser.js +4 -4
  28. package/dist/core/auto-update/channels.js +122 -0
  29. package/dist/core/auto-update/checker.js +241 -0
  30. package/dist/core/auto-update/state.js +235 -0
  31. package/dist/core/bare-mode/index.js +107 -0
  32. package/dist/core/bash/redirect.js +281 -0
  33. package/dist/core/bash-classifier.js +436 -40
  34. package/dist/core/checkpoint/resumer.js +149 -0
  35. package/dist/core/checkpoint/rewinder.js +291 -0
  36. package/dist/core/checkpoints/shadow-git.js +670 -0
  37. package/dist/core/citations/parser.js +109 -0
  38. package/dist/core/classifier/yolo-classifier.js +88 -0
  39. package/dist/core/codegraph/db.js +506 -0
  40. package/dist/core/codegraph/decision-store.js +248 -0
  41. package/dist/core/codegraph/detect-repo.js +459 -0
  42. package/dist/core/codegraph/install.js +134 -0
  43. package/dist/core/codegraph/offer-hook.js +220 -0
  44. package/dist/core/codegraph/parser.js +598 -0
  45. package/dist/core/codegraph/queries/go.scm +57 -0
  46. package/dist/core/codegraph/queries/javascript.scm +56 -0
  47. package/dist/core/codegraph/queries/python.scm +55 -0
  48. package/dist/core/codegraph/queries/rust.scm +63 -0
  49. package/dist/core/codegraph/queries/typescript.scm +91 -0
  50. package/dist/core/codegraph/reindex.js +218 -0
  51. package/dist/core/codegraph/resolve-edges.js +107 -0
  52. package/dist/core/codegraph/types.js +34 -0
  53. package/dist/core/codegraph/watcher.js +440 -0
  54. package/dist/core/compact/auto-trigger.js +96 -0
  55. package/dist/core/compact/buffer-rewriter.js +115 -0
  56. package/dist/core/compact/summarizer.js +208 -0
  57. package/dist/core/compact/token-counter.js +108 -0
  58. package/dist/core/consensus/anvil-fanout.js +25 -25
  59. package/dist/core/consensus/diff-capture.js +121 -12
  60. package/dist/core/consensus/rubric.js +21 -21
  61. package/dist/core/context/builder.js +6 -6
  62. package/dist/core/context/compaction-events.js +8 -8
  63. package/dist/core/context/compaction.js +31 -31
  64. package/dist/core/context/index.js +15 -8
  65. package/dist/core/context/invariants.js +51 -51
  66. package/dist/core/context/markdown-loader.js +28 -10
  67. package/dist/core/context/markdown-traverse.js +255 -0
  68. package/dist/core/context/pugiignore.js +41 -41
  69. package/dist/core/context/repo-skeleton.js +37 -37
  70. package/dist/core/context/tool-eviction.js +55 -0
  71. package/dist/core/context/watcher.js +32 -32
  72. package/dist/core/context/working-set.js +23 -23
  73. package/dist/core/coordinator/agent-tools.js +77 -0
  74. package/dist/core/coordinator/agent-toolset.js +65 -0
  75. package/dist/core/coordinator/fsm.js +73 -0
  76. package/dist/core/coordinator/mode-fsm.js +70 -0
  77. package/dist/core/cost/rate-card.js +129 -0
  78. package/dist/core/cost/tracker.js +221 -0
  79. package/dist/core/credentials.js +13 -13
  80. package/dist/core/cron/scheduler.js +138 -0
  81. package/dist/core/denial-tracking/index.js +8 -0
  82. package/dist/core/denial-tracking/state.js +264 -0
  83. package/dist/core/diagnostics/probe-runner.js +93 -0
  84. package/dist/core/diagnostics/probes/api.js +46 -0
  85. package/dist/core/diagnostics/probes/auth.js +93 -0
  86. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  87. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  88. package/dist/core/diagnostics/probes/config.js +72 -0
  89. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  90. package/dist/core/diagnostics/probes/disk.js +81 -0
  91. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  92. package/dist/core/diagnostics/probes/git.js +65 -0
  93. package/dist/core/diagnostics/probes/hooks.js +118 -0
  94. package/dist/core/diagnostics/probes/mcp.js +75 -0
  95. package/dist/core/diagnostics/probes/node.js +59 -0
  96. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  97. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  98. package/dist/core/diagnostics/probes/sandbox.js +67 -0
  99. package/dist/core/diagnostics/probes/session.js +74 -0
  100. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  101. package/dist/core/diagnostics/probes/workspace.js +63 -0
  102. package/dist/core/diagnostics/types.js +70 -0
  103. package/dist/core/dispatch/cache-cleanup.js +197 -0
  104. package/dist/core/dispatch/cache-handoff.js +295 -0
  105. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  106. package/dist/core/edits/dispatch.js +333 -7
  107. package/dist/core/edits/format-detector.js +260 -0
  108. package/dist/core/edits/format-matrix.js +26 -0
  109. package/dist/core/edits/fuzzy-ladder.js +650 -0
  110. package/dist/core/edits/index.js +5 -1
  111. package/dist/core/edits/journal.js +199 -0
  112. package/dist/core/edits/layer-a-apply.js +15 -15
  113. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  114. package/dist/core/edits/layer-b-apply.js +9 -9
  115. package/dist/core/edits/layer-c-apply.js +6 -6
  116. package/dist/core/edits/layer-d-ast.js +557 -14
  117. package/dist/core/edits/marker-parser.js +12 -12
  118. package/dist/core/edits/security-gate.js +27 -27
  119. package/dist/core/edits/verify-hook.js +273 -0
  120. package/dist/core/edits/worktree.js +29 -29
  121. package/dist/core/engine/anvil-client.js +214 -26
  122. package/dist/core/engine/auto-compact.js +247 -0
  123. package/dist/core/engine/budgets.js +220 -0
  124. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  125. package/dist/core/engine/context-prefix.js +155 -0
  126. package/dist/core/engine/index.js +1 -1
  127. package/dist/core/engine/intensity.js +163 -0
  128. package/dist/core/engine/intent.js +260 -0
  129. package/dist/core/engine/native-pugi.js +1559 -227
  130. package/dist/core/engine/prompts.js +219 -19
  131. package/dist/core/engine/strip-internal-fields.js +124 -0
  132. package/dist/core/engine/tool-bridge.js +1887 -59
  133. package/dist/core/engine/verification-patterns.js +195 -0
  134. package/dist/core/eval/v1/ledger.js +83 -0
  135. package/dist/core/eval/v1/runner.js +280 -0
  136. package/dist/core/eval/v1/scoring.js +68 -0
  137. package/dist/core/eval/v1/task-loader.js +191 -0
  138. package/dist/core/eval/v1/types.js +14 -0
  139. package/dist/core/eval/v1/verifier.js +176 -0
  140. package/dist/core/eval/v1/yaml-parser.js +250 -0
  141. package/dist/core/evaluation/golden-dataset.js +293 -0
  142. package/dist/core/feedback/queue.js +177 -0
  143. package/dist/core/feedback/submitter.js +145 -0
  144. package/dist/core/file-cache.js +113 -1
  145. package/dist/core/flatten/flatten-repo.js +439 -0
  146. package/dist/core/format/osc8-link.js +28 -0
  147. package/dist/core/hook-chains.js +392 -0
  148. package/dist/core/hooks/citation-verify-hook.js +138 -0
  149. package/dist/core/hooks/citation-verify.js +112 -0
  150. package/dist/core/hooks/events.js +46 -0
  151. package/dist/core/hooks/index.js +15 -0
  152. package/dist/core/hooks/registry.js +216 -0
  153. package/dist/core/hooks/runner.js +236 -0
  154. package/dist/core/hooks/v2/event-emitter.js +115 -0
  155. package/dist/core/hooks/v2/executor.js +282 -0
  156. package/dist/core/hooks/v2/index.js +25 -0
  157. package/dist/core/hooks/v2/lifecycle.js +104 -0
  158. package/dist/core/hooks/v2/loader.js +216 -0
  159. package/dist/core/hooks/v2/matcher.js +125 -0
  160. package/dist/core/hooks/v2/trust.js +143 -0
  161. package/dist/core/hooks/v2/types.js +86 -0
  162. package/dist/core/hooks/worktree-events.js +158 -0
  163. package/dist/core/image/renderer.js +71 -0
  164. package/dist/core/init/detector.js +582 -0
  165. package/dist/core/init/template-renderer.js +242 -0
  166. package/dist/core/jobs/registry.js +18 -18
  167. package/dist/core/ledger/results-tsv.js +142 -0
  168. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  169. package/dist/core/lsp/cache.js +105 -0
  170. package/dist/core/lsp/client.js +551 -41
  171. package/dist/core/lsp/language-detect.js +66 -0
  172. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  173. package/dist/core/lsp/server-detect.js +173 -0
  174. package/dist/core/lsp/symbol-cache.js +162 -0
  175. package/dist/core/lsp/symbol-tools.js +664 -0
  176. package/dist/core/mcp/client.js +97 -28
  177. package/dist/core/mcp/http-server.js +553 -0
  178. package/dist/core/mcp/orchestrator-config.js +192 -0
  179. package/dist/core/mcp/orchestrator-tools.js +806 -0
  180. package/dist/core/mcp/permission.js +190 -0
  181. package/dist/core/mcp/registry.js +39 -17
  182. package/dist/core/mcp/server-tools.js +219 -0
  183. package/dist/core/mcp/server.js +397 -0
  184. package/dist/core/mcp/trust.js +10 -10
  185. package/dist/core/memory/dual-write.js +416 -0
  186. package/dist/core/memory/passive-extract.js +130 -0
  187. package/dist/core/memory/phase1-kinds.js +20 -0
  188. package/dist/core/memory/secret-scanner.js +304 -0
  189. package/dist/core/memory-sync/queue.js +170 -0
  190. package/dist/core/metrics/extract.js +113 -0
  191. package/dist/core/modes/roo-modes.js +68 -0
  192. package/dist/core/notes/notes-paths.js +113 -0
  193. package/dist/core/notes/notes-recorder.js +140 -0
  194. package/dist/core/notes/notes-writer.js +53 -0
  195. package/dist/core/notes/renderers.js +0 -0
  196. package/dist/core/notes/slug.js +105 -0
  197. package/dist/core/onboarding/ensure-initialized.js +133 -0
  198. package/dist/core/onboarding/marker.js +111 -0
  199. package/dist/core/onboarding/telemetry-state.js +108 -0
  200. package/dist/core/output-style/presets.js +176 -0
  201. package/dist/core/output-style/state.js +185 -0
  202. package/dist/core/path-security.js +287 -5
  203. package/dist/core/permission.js +82 -22
  204. package/dist/core/permissions/auto-classifier.js +124 -0
  205. package/dist/core/permissions/bash-parser.js +371 -0
  206. package/dist/core/permissions/circuit-breaker.js +83 -0
  207. package/dist/core/permissions/constrained-edit.js +91 -0
  208. package/dist/core/permissions/gate.js +278 -0
  209. package/dist/core/permissions/index.js +20 -0
  210. package/dist/core/permissions/mode.js +174 -0
  211. package/dist/core/permissions/network-egress.js +137 -0
  212. package/dist/core/permissions/state.js +241 -0
  213. package/dist/core/permissions/tool-class.js +107 -0
  214. package/dist/core/plan-mode/ui-state.js +51 -0
  215. package/dist/core/plans/plan-artifact.js +721 -0
  216. package/dist/core/policy-limits/etag-store.js +122 -0
  217. package/dist/core/prd-check/parser.js +215 -0
  218. package/dist/core/prd-check/reporter.js +127 -0
  219. package/dist/core/prd-check/session-review.js +557 -0
  220. package/dist/core/prd-check/verifiers.js +223 -0
  221. package/dist/core/prompt-cache/client-cache.js +99 -0
  222. package/dist/core/prompts/assembly.js +29 -0
  223. package/dist/core/prompts/registry.js +364 -0
  224. package/dist/core/pugi-gitignore.js +52 -0
  225. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  226. package/dist/core/pugi-md/context-injector.js +76 -0
  227. package/dist/core/pugi-md/walk-up.js +207 -0
  228. package/dist/core/python/uv-installer.js +270 -0
  229. package/dist/core/python/uv-resolver.js +83 -0
  230. package/dist/core/rate-limit/narrator.js +146 -0
  231. package/dist/core/recipes/cli-types.js +20 -0
  232. package/dist/core/recipes/loader.js +103 -0
  233. package/dist/core/recipes/runner.js +345 -0
  234. package/dist/core/recipes/schema.js +587 -0
  235. package/dist/core/release-notes/parser.js +241 -0
  236. package/dist/core/release-notes/state.js +116 -0
  237. package/dist/core/repl/ask.js +37 -37
  238. package/dist/core/repl/cancellation.js +26 -26
  239. package/dist/core/repl/cap-warning.js +4 -4
  240. package/dist/core/repl/clipboard-read.js +11 -11
  241. package/dist/core/repl/dispatch-fsm.js +12 -12
  242. package/dist/core/repl/engine-bridge.js +303 -0
  243. package/dist/core/repl/history-search.js +15 -15
  244. package/dist/core/repl/history.js +28 -18
  245. package/dist/core/repl/kill-ring.js +5 -5
  246. package/dist/core/repl/model-pricing.js +135 -0
  247. package/dist/core/repl/privacy-banner.js +22 -22
  248. package/dist/core/repl/session.js +2690 -229
  249. package/dist/core/repl/slash-commands.js +540 -41
  250. package/dist/core/repl/store/index.js +1 -1
  251. package/dist/core/repl/store/jsonl-log.js +22 -22
  252. package/dist/core/repl/store/lockfile.js +10 -10
  253. package/dist/core/repl/store/session-store.js +136 -107
  254. package/dist/core/repl/store/types.js +15 -15
  255. package/dist/core/repl/store/uuid-v7.js +12 -12
  256. package/dist/core/repl/tool-route.js +382 -0
  257. package/dist/core/repl/workspace-context.js +43 -21
  258. package/dist/core/repo-map/build.js +125 -0
  259. package/dist/core/repo-map/cache.js +185 -0
  260. package/dist/core/repo-map/extractor.js +254 -0
  261. package/dist/core/repo-map/formatter.js +145 -0
  262. package/dist/core/repo-map/page-rank.js +105 -0
  263. package/dist/core/repo-map/scanner.js +211 -0
  264. package/dist/core/retro/git-collector.js +251 -0
  265. package/dist/core/retro/health-card.js +25 -0
  266. package/dist/core/retro/metrics.js +342 -0
  267. package/dist/core/retro/narrative.js +249 -0
  268. package/dist/core/retro/plane-collector.js +274 -0
  269. package/dist/core/retro/pr-issue-link.js +65 -0
  270. package/dist/core/retro/types.js +16 -0
  271. package/dist/core/retry-budget/budget.js +284 -0
  272. package/dist/core/retry-budget/index.js +5 -0
  273. package/dist/core/retry-budget/retry-cap.js +74 -0
  274. package/dist/core/routing/lead-worker.js +43 -0
  275. package/dist/core/routing/pre-flight-estimator.js +108 -0
  276. package/dist/core/runs/run-tree.js +103 -0
  277. package/dist/core/sandboxing/adapter.js +43 -0
  278. package/dist/core/sandboxing/bubblewrap.js +209 -0
  279. package/dist/core/sandboxing/index.js +78 -0
  280. package/dist/core/sandboxing/none.js +19 -0
  281. package/dist/core/sandboxing/policy.js +97 -0
  282. package/dist/core/sandboxing/seatbelt.js +231 -0
  283. package/dist/core/security/injection-scanner.js +367 -0
  284. package/dist/core/security/output-filter.js +418 -0
  285. package/dist/core/session/env-file.js +105 -0
  286. package/dist/core/session/section-budgets.js +140 -0
  287. package/dist/core/session.js +119 -0
  288. package/dist/core/settings.js +402 -5
  289. package/dist/core/share/formatter.js +271 -0
  290. package/dist/core/share/redactor.js +221 -0
  291. package/dist/core/share/uploader.js +267 -0
  292. package/dist/core/skills/defaults.js +30 -30
  293. package/dist/core/skills/loader.js +22 -22
  294. package/dist/core/skills/sources.js +27 -27
  295. package/dist/core/smoke/headless-driver.js +174 -0
  296. package/dist/core/smoke/orchestrator.js +194 -0
  297. package/dist/core/smoke/runner.js +238 -0
  298. package/dist/core/smoke/scenario-parser.js +316 -0
  299. package/dist/core/statusline.js +99 -0
  300. package/dist/core/subagents/dispatcher-real.js +600 -0
  301. package/dist/core/subagents/dispatcher.js +146 -52
  302. package/dist/core/subagents/index.js +19 -6
  303. package/dist/core/subagents/isolation-matrix.js +213 -0
  304. package/dist/core/subagents/spawn.js +19 -4
  305. package/dist/core/telemetry/emitter.js +229 -0
  306. package/dist/core/telemetry/queue.js +251 -0
  307. package/dist/core/theme/context.js +91 -0
  308. package/dist/core/theme/presets.js +228 -0
  309. package/dist/core/theme/state.js +181 -0
  310. package/dist/core/todos/invariant.js +10 -0
  311. package/dist/core/todos/state.js +177 -0
  312. package/dist/core/tool-schema/compressor.js +89 -0
  313. package/dist/core/transport/version-interceptor.js +166 -0
  314. package/dist/core/trust.js +2 -2
  315. package/dist/core/tui/thinking-block.js +64 -0
  316. package/dist/core/vim/keymap.js +288 -0
  317. package/dist/core/vim/state.js +92 -0
  318. package/dist/core/watch-markers/marker-watcher.js +133 -0
  319. package/dist/core/worktree/include-parser.js +249 -0
  320. package/dist/core/worktree-manager/cleanup.js +123 -0
  321. package/dist/core/worktree-manager/manager.js +303 -0
  322. package/dist/index.js +36 -0
  323. package/dist/runtime/bootstrap.js +190 -0
  324. package/dist/runtime/cli.js +4403 -561
  325. package/dist/runtime/commands/agents.js +31 -31
  326. package/dist/runtime/commands/budget.js +5 -5
  327. package/dist/runtime/commands/cancel.js +231 -0
  328. package/dist/runtime/commands/chain.js +489 -0
  329. package/dist/runtime/commands/codegraph-status.js +227 -0
  330. package/dist/runtime/commands/compact.js +297 -0
  331. package/dist/runtime/commands/config.js +74 -40
  332. package/dist/runtime/commands/cost.js +199 -0
  333. package/dist/runtime/commands/delegate.js +27 -4
  334. package/dist/runtime/commands/dispatch.js +126 -0
  335. package/dist/runtime/commands/doctor.js +579 -0
  336. package/dist/runtime/commands/eval-v1.js +266 -0
  337. package/dist/runtime/commands/feedback.js +184 -0
  338. package/dist/runtime/commands/hooks.js +187 -0
  339. package/dist/runtime/commands/index-cmd.js +459 -0
  340. package/dist/runtime/commands/init.js +254 -0
  341. package/dist/runtime/commands/lsp.js +200 -38
  342. package/dist/runtime/commands/mcp.js +935 -0
  343. package/dist/runtime/commands/memory.js +582 -0
  344. package/dist/runtime/commands/model.js +237 -0
  345. package/dist/runtime/commands/onboarding.js +275 -0
  346. package/dist/runtime/commands/patch.js +12 -12
  347. package/dist/runtime/commands/permissions.js +112 -0
  348. package/dist/runtime/commands/plan.js +143 -0
  349. package/dist/runtime/commands/prd-check.js +285 -0
  350. package/dist/runtime/commands/privacy.js +17 -17
  351. package/dist/runtime/commands/recipe.js +325 -0
  352. package/dist/runtime/commands/redo-blob-store.js +92 -0
  353. package/dist/runtime/commands/redo.js +361 -0
  354. package/dist/runtime/commands/release-notes.js +229 -0
  355. package/dist/runtime/commands/repo-map.js +95 -0
  356. package/dist/runtime/commands/report.js +299 -0
  357. package/dist/runtime/commands/resume.js +118 -0
  358. package/dist/runtime/commands/review-consensus.js +68 -53
  359. package/dist/runtime/commands/rewind.js +333 -0
  360. package/dist/runtime/commands/roster.js +14 -14
  361. package/dist/runtime/commands/servers-cli.js +182 -0
  362. package/dist/runtime/commands/servers.js +236 -0
  363. package/dist/runtime/commands/sessions.js +163 -0
  364. package/dist/runtime/commands/share.js +316 -0
  365. package/dist/runtime/commands/skills.js +31 -31
  366. package/dist/runtime/commands/status.js +186 -0
  367. package/dist/runtime/commands/stickers.js +82 -0
  368. package/dist/runtime/commands/style.js +194 -0
  369. package/dist/runtime/commands/theme.js +196 -0
  370. package/dist/runtime/commands/undo.js +54 -22
  371. package/dist/runtime/commands/update.js +289 -0
  372. package/dist/runtime/commands/vim.js +140 -0
  373. package/dist/runtime/commands/worktree.js +8 -8
  374. package/dist/runtime/commands/worktrees.js +155 -0
  375. package/dist/runtime/deprecation-warning.js +69 -0
  376. package/dist/runtime/engine-exit-code.js +50 -0
  377. package/dist/runtime/headless-repl.js +195 -0
  378. package/dist/runtime/headless.js +548 -0
  379. package/dist/runtime/load-hooks-or-exit.js +71 -0
  380. package/dist/runtime/plan-decompose.js +22 -22
  381. package/dist/runtime/sigint-guard.js +272 -0
  382. package/dist/runtime/stream-renderer.js +195 -0
  383. package/dist/runtime/update-check.js +28 -28
  384. package/dist/runtime/version.js +65 -0
  385. package/dist/runtime/worktree-bootstrap.js +579 -0
  386. package/dist/skills/bundled/batch.js +617 -0
  387. package/dist/skills/bundled/index.js +45 -0
  388. package/dist/skills/bundled/loop.js +358 -0
  389. package/dist/skills/bundled/remember.js +383 -0
  390. package/dist/skills/bundled/simplify.js +289 -0
  391. package/dist/skills/bundled/skillify.js +373 -0
  392. package/dist/skills/bundled/stuck.js +558 -0
  393. package/dist/skills/bundled/verify.js +439 -0
  394. package/dist/testing/vcr.js +486 -0
  395. package/dist/tools/agent-tool.js +229 -0
  396. package/dist/tools/apply-patch.js +89 -28
  397. package/dist/tools/ask-user-question.js +337 -0
  398. package/dist/tools/ask-user.js +115 -0
  399. package/dist/tools/bash.js +811 -49
  400. package/dist/tools/brief.js +224 -0
  401. package/dist/tools/cron.js +433 -0
  402. package/dist/tools/enter-worktree.js +250 -0
  403. package/dist/tools/exit-worktree.js +147 -0
  404. package/dist/tools/file-tools.js +161 -44
  405. package/dist/tools/http-request.js +336 -0
  406. package/dist/tools/lsp-tools.js +377 -1
  407. package/dist/tools/mcp-tool.js +260 -0
  408. package/dist/tools/multi-edit.js +361 -0
  409. package/dist/tools/powershell.js +268 -0
  410. package/dist/tools/registry.js +120 -5
  411. package/dist/tools/server-tools.js +892 -0
  412. package/dist/tools/skill-tool.js +96 -0
  413. package/dist/tools/sleep.js +99 -0
  414. package/dist/tools/synthetic-output.js +133 -0
  415. package/dist/tools/tasks.js +208 -0
  416. package/dist/tools/todo-write.js +184 -0
  417. package/dist/tools/verify-plan-execution.js +295 -0
  418. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  419. package/dist/tools/web-fetch.js +195 -10
  420. package/dist/tools/web-search.js +458 -0
  421. package/dist/tui/agent-progress-card.js +111 -0
  422. package/dist/tui/agent-tree.js +22 -1
  423. package/dist/tui/ask-modal.js +14 -14
  424. package/dist/tui/ask-user-question-chips.js +315 -0
  425. package/dist/tui/ask-user-question-prompt.js +203 -0
  426. package/dist/tui/compact-banner.js +81 -0
  427. package/dist/tui/conversation-pane.js +85 -11
  428. package/dist/tui/cost-table.js +111 -0
  429. package/dist/tui/device-flow.js +2 -2
  430. package/dist/tui/doctor-table.js +46 -0
  431. package/dist/tui/feedback-prompt.js +156 -0
  432. package/dist/tui/input-box.js +247 -32
  433. package/dist/tui/login-picker.js +3 -3
  434. package/dist/tui/markdown-render.js +6 -6
  435. package/dist/tui/multi-file-diff-approval.js +375 -0
  436. package/dist/tui/onboarding-wizard.js +240 -0
  437. package/dist/tui/permissions-picker.js +86 -0
  438. package/dist/tui/render.js +36 -1
  439. package/dist/tui/repl-render.js +239 -25
  440. package/dist/tui/repl-splash-art.js +16 -16
  441. package/dist/tui/repl-splash-mascot.js +48 -24
  442. package/dist/tui/repl-splash.js +22 -22
  443. package/dist/tui/repl.js +125 -45
  444. package/dist/tui/slash-palette.js +6 -6
  445. package/dist/tui/splash.js +2 -2
  446. package/dist/tui/status-bar.js +109 -31
  447. package/dist/tui/status-table.js +7 -0
  448. package/dist/tui/stickers-art.js +136 -0
  449. package/dist/tui/style-table.js +28 -0
  450. package/dist/tui/theme-table.js +29 -0
  451. package/dist/tui/thinking-spinner.js +123 -0
  452. package/dist/tui/tool-stream-pane.js +53 -4
  453. package/dist/tui/update-banner.js +27 -2
  454. package/dist/tui/vim-input.js +267 -0
  455. package/dist/tui/welcome-banner.js +107 -0
  456. package/dist/tui/welcome-data.js +293 -0
  457. package/dist/tui/workspace-context.js +2 -2
  458. package/package.json +29 -6
  459. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  460. package/test/scenarios/compact-force.scenario.txt +12 -0
  461. package/test/scenarios/identity.scenario.txt +11 -0
  462. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  463. package/test/scenarios/walkback.scenario.txt +12 -0
  464. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Pugi local symbol index - chokidar file watcher (PR L2).
3
+ *
4
+ * Live auto-sync layer на top of the parser (PR L1) и DB (PR L0). When
5
+ * the operator runs `pugi index --watch`, this module:
6
+ *
7
+ * 1. Spins up a chokidar watcher rooted at `workspaceRoot`.
8
+ * 2. Filters events to the v1 parseable extensions
9
+ * (`PARSEABLE_EXTENSIONS` from parser.ts).
10
+ * 3. Coalesces bursts via a 2-second debounce (configurable; tests
11
+ * pass `debounceMs: 50` to keep specs fast).
12
+ * 4. On flush: per file -> `deleteFile` cascade -> `parseFile` ->
13
+ * `insertSymbols` -> resolve `pendingEdges` -> `insertEdges`.
14
+ * 5. On `unlink`: `deleteFile` only (cascade removes symbols + edges
15
+ * via the schema's FK ON DELETE CASCADE).
16
+ *
17
+ * Architectural decisions:
18
+ *
19
+ * - **No nested transactions.** `deleteFile` / `insertSymbols` /
20
+ * `insertEdges` each open + commit their own BEGIN block. We call
21
+ * them sequentially and rely on per-call atomicity. A higher-level
22
+ * SAVEPOINT wrapper was considered и rejected: chokidar batches are
23
+ * small (typically 1-5 files) and the per-file work is already
24
+ * transactional, so the extra level adds complexity without a
25
+ * customer-visible win.
26
+ *
27
+ * - **Two-step edge resolution.** `parseFile` returns `pendingEdges`
28
+ * keyed by name + scope. We resolve them after `insertSymbols`
29
+ * populates ids, using the shared `resolvePendingEdges` helper.
30
+ * Cross-file targets are looked up via a SQL `SELECT id FROM
31
+ * symbols WHERE name = ?` closure - the live DB IS the cross-file
32
+ * map for the watcher. Reindex builds an in-memory map because it
33
+ * sees every file in one pass; the watcher sees files one delta at
34
+ * a time.
35
+ *
36
+ * - **Path normalization.** Chokidar emits absolute or cwd-relative
37
+ * paths depending on options. We always work in workspace-relative
38
+ * POSIX form (`path.relative(root, abs).split(sep).join('/')`)
39
+ * because that is what `reindex.ts` writes to `Symbol.file` and
40
+ * `files.path`. Mismatched separators on Windows would break the
41
+ * FK + the `deleteFile` lookup.
42
+ *
43
+ * - **Exclude list parity.** We use the EXACT same `PRUNE_DIRS` as
44
+ * `reindex.ts` (extended slightly with the v1 extensions the spec
45
+ * pinned). Divergence between the walker and the watcher would
46
+ * silently produce different indexes depending on entry point.
47
+ *
48
+ * - **In-flight queue at close.** SIGINT / SIGTERM trigger
49
+ * `close()`. If a debounce timer is still pending, we flush it
50
+ * synchronously before tearing down chokidar - dropping a pending
51
+ * edit would leave the index out of sync until the next manual
52
+ * `pugi index` run.
53
+ *
54
+ * - **Initial scan is the operator's job.** `ignoreInitial: true`
55
+ * means we do NOT re-index the workspace on startup. The CLI
56
+ * command runs `reindexWorkspace` first (covered by PR L1) и THEN
57
+ * starts the watcher; this avoids a double-pass over the
58
+ * workspace.
59
+ */
60
+ import { existsSync } from 'node:fs';
61
+ import { readFile } from 'node:fs/promises';
62
+ import { createHash } from 'node:crypto';
63
+ import { relative, resolve, sep, extname } from 'node:path';
64
+ import chokidar from 'chokidar';
65
+ import { deleteFile, insertEdges, insertSymbols, upsertFile, } from './db.js';
66
+ import { PARSEABLE_EXTENSIONS, parseFile } from './parser.js';
67
+ import { buildPerFileNameMap, resolvePendingEdges, } from './resolve-edges.js';
68
+ /**
69
+ * Directories pruned from the watch. EXACT mirror of
70
+ * `reindex.ts:PRUNE_DIRS` so the walker и watcher agree on what
71
+ * "workspace" means. Touching this list requires touching both
72
+ * sites.
73
+ */
74
+ const PRUNE_DIRS = Object.freeze([
75
+ 'node_modules',
76
+ '.git',
77
+ 'dist',
78
+ 'build',
79
+ '.next',
80
+ '.turbo',
81
+ '.cache',
82
+ 'coverage',
83
+ '.pugi',
84
+ ]);
85
+ /** Default debounce window between burst-of-events and flush. */
86
+ const DEFAULT_DEBOUNCE_MS = 2000;
87
+ /**
88
+ * Start a chokidar watcher rooted at `workspaceRoot` against the
89
+ * caller-owned IndexDB. Returns a handle the caller invokes к stop the
90
+ * watcher cleanly.
91
+ *
92
+ * The function is sync - the watcher is "started" the moment chokidar
93
+ * exists, even if it has не yet emitted `ready`. The first `add`/
94
+ * `change` events flow into the debounce queue immediately.
95
+ */
96
+ export function startWatcher(opts) {
97
+ const root = resolve(opts.workspaceRoot);
98
+ if (!existsSync(root)) {
99
+ throw new Error(`startWatcher: workspaceRoot does not exist: ${root}`);
100
+ }
101
+ const debounceMs = Math.max(0, opts.debounceMs ?? DEFAULT_DEBOUNCE_MS);
102
+ const writeStderr = opts.writeStderr ?? ((text) => void process.stderr.write(text));
103
+ const watchFn = opts.chokidarWatchFn ?? chokidar.watch;
104
+ const quiet = opts.quiet === true;
105
+ const stats = {
106
+ added: 0,
107
+ changed: 0,
108
+ removed: 0,
109
+ reparsed: 0,
110
+ edgesInserted: 0,
111
+ edgesOrphaned: 0,
112
+ parseErrors: 0,
113
+ watcherErrors: 0,
114
+ };
115
+ /** Workspace-relative POSIX path. */
116
+ const toRelPosix = (abs) => {
117
+ const rel = relative(root, abs);
118
+ if (rel.length === 0 || rel.startsWith('..'))
119
+ return '';
120
+ return rel.split(sep).join('/');
121
+ };
122
+ /**
123
+ * Defense in depth: chokidar matches on directory globs, but a
124
+ * watched-parent-dir event still might fire для excluded subpaths
125
+ * on some platforms. Re-check the relative path against the prune
126
+ * list before doing any work.
127
+ */
128
+ const isExcluded = (relPosix) => {
129
+ if (relPosix.length === 0)
130
+ return true;
131
+ const parts = relPosix.split('/');
132
+ for (const p of parts) {
133
+ if (PRUNE_DIRS.includes(p))
134
+ return true;
135
+ }
136
+ return false;
137
+ };
138
+ const isParseable = (relPosix) => {
139
+ const ext = extname(relPosix).toLowerCase();
140
+ return PARSEABLE_EXTENSIONS.includes(ext);
141
+ };
142
+ // ---- queue + debounce -------------------------------------------------
143
+ /** Files queued для re-parse on the next flush. */
144
+ const dirty = new Set();
145
+ /** Files queued для unlink-cascade on the next flush. */
146
+ const removed = new Set();
147
+ let timer = null;
148
+ /** Tracks the in-progress flush so close() can await it. */
149
+ let flushing = null;
150
+ /** Once closed, ignore further events. */
151
+ let closed = false;
152
+ const armDebounce = () => {
153
+ if (closed)
154
+ return;
155
+ if (timer)
156
+ clearTimeout(timer);
157
+ timer = setTimeout(() => {
158
+ timer = null;
159
+ void flush();
160
+ }, debounceMs);
161
+ };
162
+ /**
163
+ * Drain the queue. Sequential per-file work - the parser is async,
164
+ * SQLite handles are sync but already wrapped in per-call BEGIN/
165
+ * COMMIT inside db.ts, so concurrency would only add lock contention
166
+ * without throughput gain on a single-process watcher.
167
+ */
168
+ const flush = async () => {
169
+ // If a flush is already in-flight, queue behind it. We serialize
170
+ // so the dirty Set drained by one flush never collides with one
171
+ // mutated by another.
172
+ if (flushing) {
173
+ await flushing;
174
+ // Nothing new since the previous flush picked it up? Bail.
175
+ if (dirty.size === 0 && removed.size === 0)
176
+ return;
177
+ }
178
+ flushing = doFlush().finally(() => {
179
+ flushing = null;
180
+ });
181
+ return flushing;
182
+ };
183
+ const doFlush = async () => {
184
+ const snapshotDirty = Array.from(dirty);
185
+ dirty.clear();
186
+ const snapshotRemoved = Array.from(removed);
187
+ removed.clear();
188
+ // Process removals first - if a file was deleted and re-added in
189
+ // the same window we want the add half to write fresh rows.
190
+ for (const relPath of snapshotRemoved) {
191
+ try {
192
+ deleteFile(opts.db, relPath);
193
+ }
194
+ catch (err) {
195
+ const message = err instanceof Error ? err.message : String(err);
196
+ if (!quiet) {
197
+ writeStderr(`pugi index --watch: deleteFile(${relPath}) failed: ${message}\n`);
198
+ }
199
+ }
200
+ }
201
+ for (const relPath of snapshotDirty) {
202
+ // Drop файлы that were also removed in this batch - the unlink
203
+ // above already wiped them.
204
+ if (snapshotRemoved.includes(relPath))
205
+ continue;
206
+ const abs = resolve(root, relPath);
207
+ let bytes;
208
+ try {
209
+ bytes = await readFile(abs, 'utf8');
210
+ }
211
+ catch {
212
+ // Disappeared between event и flush - treat as unlink.
213
+ try {
214
+ deleteFile(opts.db, relPath);
215
+ }
216
+ catch (err) {
217
+ const message = err instanceof Error ? err.message : String(err);
218
+ if (!quiet) {
219
+ writeStderr(`pugi index --watch: post-disappear deleteFile(${relPath}) failed: ${message}\n`);
220
+ }
221
+ }
222
+ continue;
223
+ }
224
+ let result;
225
+ try {
226
+ result = await parseFile(abs, {
227
+ relPath,
228
+ quiet: true,
229
+ sourceOverride: bytes,
230
+ });
231
+ }
232
+ catch (err) {
233
+ stats.parseErrors += 1;
234
+ if (!quiet) {
235
+ const message = err instanceof Error ? err.message : String(err);
236
+ writeStderr(`pugi index --watch: parse(${relPath}) failed: ${message}\n`);
237
+ }
238
+ continue;
239
+ }
240
+ // Clear prior rows for this file before re-inserting. Cheap
241
+ // даже if the file is new - the lookup hits the `idx_symbols_file`
242
+ // index и returns zero rows.
243
+ try {
244
+ deleteFile(opts.db, relPath);
245
+ }
246
+ catch (err) {
247
+ const message = err instanceof Error ? err.message : String(err);
248
+ if (!quiet) {
249
+ writeStderr(`pugi index --watch: deleteFile(${relPath}) failed: ${message}\n`);
250
+ }
251
+ continue;
252
+ }
253
+ if (result.symbols.length === 0) {
254
+ // Record the file fingerprint so a future "did this change?"
255
+ // query can short-circuit. Only when we got далеко enough to
256
+ // recognise the language - skipping unsupported extensions
257
+ // earlier means we never see them here.
258
+ if (result.language !== null) {
259
+ try {
260
+ upsertFile(opts.db, {
261
+ path: relPath,
262
+ sha256: sha256(bytes),
263
+ lastIndexedAt: new Date().toISOString(),
264
+ symbolCount: 0,
265
+ });
266
+ }
267
+ catch (err) {
268
+ const message = err instanceof Error ? err.message : String(err);
269
+ if (!quiet) {
270
+ writeStderr(`pugi index --watch: upsertFile(${relPath}) failed: ${message}\n`);
271
+ }
272
+ }
273
+ }
274
+ stats.reparsed += 1;
275
+ continue;
276
+ }
277
+ let ids;
278
+ try {
279
+ ids = insertSymbols(opts.db, result.symbols);
280
+ }
281
+ catch (err) {
282
+ const message = err instanceof Error ? err.message : String(err);
283
+ if (!quiet) {
284
+ writeStderr(`pugi index --watch: insertSymbols(${relPath}) failed: ${message}\n`);
285
+ }
286
+ continue;
287
+ }
288
+ const perFile = buildPerFileNameMap(result.symbols, ids);
289
+ const { resolved, orphaned } = resolvePendingEdges(result.pendingEdges, perFile, (name) => crossFileLookup(opts.db, name));
290
+ stats.edgesOrphaned += orphaned;
291
+ if (resolved.length > 0) {
292
+ try {
293
+ insertEdges(opts.db, resolved);
294
+ stats.edgesInserted += resolved.length;
295
+ }
296
+ catch (err) {
297
+ const message = err instanceof Error ? err.message : String(err);
298
+ if (!quiet) {
299
+ writeStderr(`pugi index --watch: insertEdges(${relPath}) failed: ${message}\n`);
300
+ }
301
+ }
302
+ }
303
+ try {
304
+ upsertFile(opts.db, {
305
+ path: relPath,
306
+ sha256: sha256(bytes),
307
+ lastIndexedAt: new Date().toISOString(),
308
+ symbolCount: ids.length,
309
+ });
310
+ }
311
+ catch (err) {
312
+ const message = err instanceof Error ? err.message : String(err);
313
+ if (!quiet) {
314
+ writeStderr(`pugi index --watch: upsertFile(${relPath}) failed: ${message}\n`);
315
+ }
316
+ }
317
+ stats.reparsed += 1;
318
+ }
319
+ };
320
+ // ---- chokidar wiring --------------------------------------------------
321
+ // We watch the workspace root recursively and let our own filter
322
+ // drop unparseable extensions + excluded directories. Chokidar's
323
+ // built-in `ignored` accepts glob/regex/function; we use a function
324
+ // form so we can re-use the prune list verbatim instead of
325
+ // duplicating it as a glob array.
326
+ const ignored = (p) => {
327
+ // chokidar may pass absolute or workspace-relative paths depending
328
+ // on platform и `cwd` option; normalize then reuse the predicate.
329
+ const abs = resolve(p);
330
+ const rel = toRelPosix(abs);
331
+ if (rel.length === 0)
332
+ return false; // root itself - keep watching
333
+ return isExcluded(rel);
334
+ };
335
+ const watcher = watchFn(root, {
336
+ ignored,
337
+ ignoreInitial: true,
338
+ persistent: true,
339
+ followSymlinks: false,
340
+ awaitWriteFinish: {
341
+ stabilityThreshold: Math.min(50, Math.max(10, Math.floor(debounceMs / 4))),
342
+ pollInterval: 10,
343
+ },
344
+ });
345
+ const onChange = (kind, abs) => {
346
+ if (closed)
347
+ return;
348
+ const rel = toRelPosix(abs);
349
+ if (isExcluded(rel) || !isParseable(rel))
350
+ return;
351
+ if (kind === 'add')
352
+ stats.added += 1;
353
+ else
354
+ stats.changed += 1;
355
+ dirty.add(rel);
356
+ armDebounce();
357
+ };
358
+ const onUnlink = (abs) => {
359
+ if (closed)
360
+ return;
361
+ const rel = toRelPosix(abs);
362
+ if (isExcluded(rel) || !isParseable(rel))
363
+ return;
364
+ stats.removed += 1;
365
+ // If the file is also pending как dirty, drop the dirty marker -
366
+ // the unlink wins.
367
+ dirty.delete(rel);
368
+ removed.add(rel);
369
+ armDebounce();
370
+ };
371
+ watcher.on('add', (p) => onChange('add', p));
372
+ watcher.on('change', (p) => onChange('change', p));
373
+ watcher.on('unlink', (p) => onUnlink(p));
374
+ watcher.on('error', (err) => {
375
+ stats.watcherErrors += 1;
376
+ if (!quiet) {
377
+ const message = err instanceof Error ? err.message : String(err);
378
+ writeStderr(`pugi index --watch: chokidar error: ${message}\n`);
379
+ }
380
+ });
381
+ const close = async () => {
382
+ if (closed)
383
+ return;
384
+ closed = true;
385
+ if (timer) {
386
+ clearTimeout(timer);
387
+ timer = null;
388
+ }
389
+ // Drain any pending events before closing the watcher.
390
+ if (dirty.size > 0 || removed.size > 0) {
391
+ try {
392
+ await flush();
393
+ }
394
+ catch (err) {
395
+ if (!quiet) {
396
+ const message = err instanceof Error ? err.message : String(err);
397
+ writeStderr(`pugi index --watch: close-time flush failed: ${message}\n`);
398
+ }
399
+ }
400
+ }
401
+ // Wait for any in-progress flush even if the queue was empty.
402
+ if (flushing) {
403
+ try {
404
+ await flushing;
405
+ }
406
+ catch {
407
+ /* already surfaced via writeStderr above */
408
+ }
409
+ }
410
+ await watcher.close();
411
+ };
412
+ const flushNow = async () => {
413
+ if (timer) {
414
+ clearTimeout(timer);
415
+ timer = null;
416
+ }
417
+ await flush();
418
+ };
419
+ return {
420
+ close,
421
+ flushNow,
422
+ stats: () => ({ ...stats }),
423
+ };
424
+ }
425
+ /**
426
+ * Cross-file target resolver backed by the live `symbols` table.
427
+ * Returns the first matching row id - deterministic stability via the
428
+ * autoincrement order, mirrors `reindex.ts`'s "first-write-wins"
429
+ * convention. `null` when no symbol carries the requested name.
430
+ */
431
+ function crossFileLookup(db, name) {
432
+ const row = db.conn
433
+ .prepare('SELECT id FROM symbols WHERE name = ? ORDER BY id ASC LIMIT 1')
434
+ .get(name);
435
+ return row?.id ?? null;
436
+ }
437
+ function sha256(s) {
438
+ return createHash('sha256').update(s).digest('hex');
439
+ }
440
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Auto-compact threshold gate.
3
+ *
4
+ * Decides whether the conversation buffer has crossed the threshold
5
+ * percent of the active model's context window. The check runs after
6
+ * every operator/persona turn before the NEXT operator input lands so
7
+ * the compaction completes BEFORE the model would have rejected the
8
+ * request with a context-overflow error.
9
+ *
10
+ * Design choices:
11
+ *
12
+ * - Pure function. The caller passes (tokenCount, windowSize, env).
13
+ * The gate returns a verdict; the session module owns the side
14
+ * effect of invoking the summariser. Pure-function shape keeps the
15
+ * spec exhaustive and the call site readable.
16
+ *
17
+ * - Hysteresis: once a compaction lands, the marker resets the
18
+ * baseline token count to "summary + tail" — the gate looks at the
19
+ * POST-marker tokens only. This is enforced upstream by the caller
20
+ * passing the post-marker count; the gate itself has no memory.
21
+ *
22
+ * - Two env knobs:
23
+ * PUGI_AUTOCOMPACT_DISABLED=1 — kill switch
24
+ * PUGI_AUTOCOMPACT_THRESHOLD=N — float in (0, 1] (default 0.75)
25
+ * Anything outside (0, 1] is rejected and the gate falls back to
26
+ * the default. Bad input never crashes the REPL.
27
+ */
28
+ /** Default trip point as a fraction of the context window. */
29
+ export const DEFAULT_THRESHOLD = 0.75;
30
+ /**
31
+ * Decide whether to fire `/compact` automatically. Pure; safe to call
32
+ * after every turn.
33
+ */
34
+ export function evaluateAutoCompact(input) {
35
+ const env = input.env ?? process.env;
36
+ const threshold = resolveThreshold(env);
37
+ if (input.windowSize <= 0 || !Number.isFinite(input.windowSize)) {
38
+ return {
39
+ kind: 'skip',
40
+ reason: 'invalid-window',
41
+ tokenCount: input.tokenCount,
42
+ windowSize: input.windowSize,
43
+ threshold,
44
+ pressure: 0,
45
+ };
46
+ }
47
+ if (env['PUGI_AUTOCOMPACT_DISABLED'] === '1') {
48
+ return {
49
+ kind: 'skip',
50
+ reason: 'disabled',
51
+ tokenCount: input.tokenCount,
52
+ windowSize: input.windowSize,
53
+ threshold,
54
+ pressure: roundPressure(input.tokenCount / input.windowSize),
55
+ };
56
+ }
57
+ const pressure = roundPressure(input.tokenCount / input.windowSize);
58
+ if (pressure >= threshold) {
59
+ return {
60
+ kind: 'fire',
61
+ tokenCount: input.tokenCount,
62
+ windowSize: input.windowSize,
63
+ threshold,
64
+ pressure,
65
+ };
66
+ }
67
+ return {
68
+ kind: 'skip',
69
+ reason: 'below-threshold',
70
+ tokenCount: input.tokenCount,
71
+ windowSize: input.windowSize,
72
+ threshold,
73
+ pressure,
74
+ };
75
+ }
76
+ /**
77
+ * Resolve the threshold from env, clamping to the (0, 1] open-closed
78
+ * interval. Bad input silently falls back to DEFAULT_THRESHOLD so the
79
+ * REPL never crashes on a malformed environment variable.
80
+ */
81
+ function resolveThreshold(env) {
82
+ const raw = env['PUGI_AUTOCOMPACT_THRESHOLD'];
83
+ if (!raw)
84
+ return DEFAULT_THRESHOLD;
85
+ const parsed = Number.parseFloat(raw);
86
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 1) {
87
+ return DEFAULT_THRESHOLD;
88
+ }
89
+ return parsed;
90
+ }
91
+ function roundPressure(raw) {
92
+ if (!Number.isFinite(raw) || raw < 0)
93
+ return 0;
94
+ return Math.round(raw * 1000) / 1000;
95
+ }
96
+ //# sourceMappingURL=auto-trigger.js.map
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Type guard: discriminate a SessionEvent against the `compaction`
3
+ * kind. Used by replay code so the boundary marker drives a different
4
+ * code path than `user`/`persona`/`system` transcript rows.
5
+ */
6
+ export function isCompactBoundary(event) {
7
+ if (event.kind !== 'compaction')
8
+ return false;
9
+ const p = event.payload;
10
+ if (p === null || typeof p !== 'object')
11
+ return false;
12
+ if (p.version !== 1)
13
+ return false;
14
+ if (p.trigger !== 'manual' && p.trigger !== 'auto')
15
+ return false;
16
+ if (typeof p.summary !== 'string' || p.summary.length === 0)
17
+ return false;
18
+ if (typeof p.summaryTokenCount !== 'number')
19
+ return false;
20
+ if (typeof p.summaryTurnsBefore !== 'number')
21
+ return false;
22
+ if (typeof p.keptTailTurns !== 'number')
23
+ return false;
24
+ if (typeof p.coversUntilOffset !== 'number')
25
+ return false;
26
+ return true;
27
+ }
28
+ /**
29
+ * Append one `compaction` boundary marker to the SessionStore. Returns
30
+ * the SessionEvent we wrote so the caller can echo it into the in-
31
+ * memory transcript without a re-read. Throws on store error so the
32
+ * caller surfaces the failure inline.
33
+ */
34
+ export async function appendCompactBoundary(input) {
35
+ const ts = (input.now ?? (() => Date.now()))();
36
+ const payload = {
37
+ version: 1,
38
+ trigger: input.trigger,
39
+ summary: input.summary,
40
+ summaryTokenCount: input.summaryTokenCount,
41
+ summaryTurnsBefore: input.summaryTurnsBefore,
42
+ keptTailTurns: input.keptTailTurns,
43
+ coversUntilOffset: input.coversUntilOffset,
44
+ };
45
+ const event = {
46
+ t: ts,
47
+ kind: 'compaction',
48
+ payload,
49
+ };
50
+ await input.store.appendEvent(event);
51
+ return event;
52
+ }
53
+ /**
54
+ * Apply replay masking to a chronological event list. Given the full
55
+ * ordered events.jsonl content, return only the events the caller
56
+ * should render: every event AFTER the latest `compaction` boundary,
57
+ * plus the boundary itself (so the renderer can show the banner +
58
+ * summary), and the K kept-tail events that landed BEFORE the boundary
59
+ * but were preserved per the marker's `keptTailTurns`.
60
+ *
61
+ * Mask logic:
62
+ * 1. Walk events. Find the LATEST boundary by offset.
63
+ * 2. Index 0 .. coversUntilOffset-1 are masked, EXCEPT the last
64
+ * `keptTailTurns` of that range (which are the verbatim tail).
65
+ * 3. The boundary event itself + everything after it stays.
66
+ *
67
+ * Why we expose this here (and not in session.ts): keeping the mask
68
+ * logic next to the writer means the wire format is owned by one
69
+ * module. session.ts depends on this; this depends on nothing in
70
+ * session.ts. Unidirectional.
71
+ */
72
+ export function applyCompactMask(events) {
73
+ // Find latest compaction event.
74
+ let latestIdx = -1;
75
+ let latestPayload = null;
76
+ for (let i = events.length - 1; i >= 0; i -= 1) {
77
+ const ev = events[i];
78
+ if (isCompactBoundary(ev)) {
79
+ latestIdx = i;
80
+ latestPayload = ev.payload;
81
+ break;
82
+ }
83
+ }
84
+ if (latestIdx === -1 || latestPayload === null) {
85
+ return events;
86
+ }
87
+ // `coversUntilOffset` is the count of events that existed in the
88
+ // store immediately before the marker append. Events 0 ..
89
+ // coversUntilOffset-1 are summarised; events keptTailTurns of them
90
+ // are surfaced anyway as the verbatim tail.
91
+ const cap = Math.max(0, Math.min(latestPayload.coversUntilOffset, latestIdx));
92
+ const tailKeepCount = Math.max(0, Math.min(latestPayload.keptTailTurns, cap));
93
+ // Take the LAST tailKeepCount events from the masked range, but only
94
+ // those that represent renderable turns (user/persona/system).
95
+ // Boundary markers and tool stream events are NOT counted as turns
96
+ // for the tail-keep window — using them would let the keptTailTurns
97
+ // budget be consumed by infra events and the operator would lose
98
+ // the last K real turns. The spec is about "last K human-visible
99
+ // turns", not "last K events".
100
+ const tailSlice = [];
101
+ for (let i = cap - 1; i >= 0 && tailSlice.length < tailKeepCount; i -= 1) {
102
+ const ev = events[i];
103
+ if (ev.kind === 'user' || ev.kind === 'persona' || ev.kind === 'system') {
104
+ tailSlice.push(ev);
105
+ }
106
+ }
107
+ tailSlice.reverse();
108
+ // After the marker: everything that landed AFTER the boundary
109
+ // append. These are post-compaction events the user has not yet
110
+ // seen folded into a summary; they pass through verbatim.
111
+ const afterMarker = events.slice(latestIdx + 1);
112
+ const markerEvent = events[latestIdx];
113
+ return [...tailSlice, markerEvent, ...afterMarker];
114
+ }
115
+ //# sourceMappingURL=buffer-rewriter.js.map