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

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 (445) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +53 -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 +71 -0
  45. package/dist/core/codegraph/types.js +34 -0
  46. package/dist/core/compact/auto-trigger.js +96 -0
  47. package/dist/core/compact/buffer-rewriter.js +115 -0
  48. package/dist/core/compact/summarizer.js +208 -0
  49. package/dist/core/compact/token-counter.js +108 -0
  50. package/dist/core/consensus/anvil-fanout.js +25 -25
  51. package/dist/core/consensus/diff-capture.js +121 -12
  52. package/dist/core/consensus/rubric.js +21 -21
  53. package/dist/core/context/builder.js +6 -6
  54. package/dist/core/context/compaction-events.js +8 -8
  55. package/dist/core/context/compaction.js +31 -31
  56. package/dist/core/context/index.js +15 -8
  57. package/dist/core/context/invariants.js +51 -51
  58. package/dist/core/context/markdown-loader.js +28 -10
  59. package/dist/core/context/markdown-traverse.js +255 -0
  60. package/dist/core/context/pugiignore.js +41 -41
  61. package/dist/core/context/repo-skeleton.js +37 -37
  62. package/dist/core/context/tool-eviction.js +55 -0
  63. package/dist/core/context/watcher.js +32 -32
  64. package/dist/core/context/working-set.js +23 -23
  65. package/dist/core/coordinator/agent-tools.js +77 -0
  66. package/dist/core/coordinator/agent-toolset.js +65 -0
  67. package/dist/core/coordinator/fsm.js +73 -0
  68. package/dist/core/coordinator/mode-fsm.js +70 -0
  69. package/dist/core/cost/rate-card.js +129 -0
  70. package/dist/core/cost/tracker.js +221 -0
  71. package/dist/core/credentials.js +13 -13
  72. package/dist/core/cron/scheduler.js +138 -0
  73. package/dist/core/denial-tracking/index.js +8 -0
  74. package/dist/core/denial-tracking/state.js +264 -0
  75. package/dist/core/diagnostics/probe-runner.js +93 -0
  76. package/dist/core/diagnostics/probes/api.js +46 -0
  77. package/dist/core/diagnostics/probes/auth.js +93 -0
  78. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  79. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  80. package/dist/core/diagnostics/probes/config.js +72 -0
  81. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  82. package/dist/core/diagnostics/probes/disk.js +81 -0
  83. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  84. package/dist/core/diagnostics/probes/git.js +65 -0
  85. package/dist/core/diagnostics/probes/hooks.js +118 -0
  86. package/dist/core/diagnostics/probes/mcp.js +75 -0
  87. package/dist/core/diagnostics/probes/node.js +59 -0
  88. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  89. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  90. package/dist/core/diagnostics/probes/sandbox.js +72 -0
  91. package/dist/core/diagnostics/probes/session.js +74 -0
  92. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  93. package/dist/core/diagnostics/probes/workspace.js +63 -0
  94. package/dist/core/diagnostics/types.js +70 -0
  95. package/dist/core/dispatch/cache-cleanup.js +197 -0
  96. package/dist/core/dispatch/cache-handoff.js +295 -0
  97. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  98. package/dist/core/edits/dispatch.js +333 -7
  99. package/dist/core/edits/format-detector.js +260 -0
  100. package/dist/core/edits/format-matrix.js +26 -0
  101. package/dist/core/edits/fuzzy-ladder.js +650 -0
  102. package/dist/core/edits/index.js +5 -1
  103. package/dist/core/edits/journal.js +199 -0
  104. package/dist/core/edits/layer-a-apply.js +15 -15
  105. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  106. package/dist/core/edits/layer-b-apply.js +9 -9
  107. package/dist/core/edits/layer-c-apply.js +6 -6
  108. package/dist/core/edits/layer-d-ast.js +557 -14
  109. package/dist/core/edits/marker-parser.js +12 -12
  110. package/dist/core/edits/security-gate.js +27 -27
  111. package/dist/core/edits/verify-hook.js +273 -0
  112. package/dist/core/edits/worktree.js +29 -29
  113. package/dist/core/engine/anvil-client.js +214 -26
  114. package/dist/core/engine/auto-compact.js +247 -0
  115. package/dist/core/engine/budgets.js +220 -0
  116. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  117. package/dist/core/engine/context-prefix.js +155 -0
  118. package/dist/core/engine/index.js +1 -1
  119. package/dist/core/engine/intensity.js +163 -0
  120. package/dist/core/engine/intent.js +260 -0
  121. package/dist/core/engine/native-pugi.js +1559 -227
  122. package/dist/core/engine/prompts.js +187 -19
  123. package/dist/core/engine/strip-internal-fields.js +124 -0
  124. package/dist/core/engine/tool-bridge.js +1887 -59
  125. package/dist/core/engine/verification-patterns.js +195 -0
  126. package/dist/core/evaluation/golden-dataset.js +293 -0
  127. package/dist/core/feedback/queue.js +177 -0
  128. package/dist/core/feedback/submitter.js +145 -0
  129. package/dist/core/file-cache.js +113 -1
  130. package/dist/core/flatten/flatten-repo.js +439 -0
  131. package/dist/core/format/osc8-link.js +28 -0
  132. package/dist/core/hook-chains.js +392 -0
  133. package/dist/core/hooks/citation-verify-hook.js +138 -0
  134. package/dist/core/hooks/citation-verify.js +112 -0
  135. package/dist/core/hooks/events.js +46 -0
  136. package/dist/core/hooks/index.js +15 -0
  137. package/dist/core/hooks/registry.js +216 -0
  138. package/dist/core/hooks/runner.js +236 -0
  139. package/dist/core/hooks/v2/event-emitter.js +115 -0
  140. package/dist/core/hooks/v2/executor.js +282 -0
  141. package/dist/core/hooks/v2/index.js +25 -0
  142. package/dist/core/hooks/v2/lifecycle.js +104 -0
  143. package/dist/core/hooks/v2/loader.js +216 -0
  144. package/dist/core/hooks/v2/matcher.js +125 -0
  145. package/dist/core/hooks/v2/trust.js +143 -0
  146. package/dist/core/hooks/v2/types.js +86 -0
  147. package/dist/core/hooks/worktree-events.js +158 -0
  148. package/dist/core/image/renderer.js +71 -0
  149. package/dist/core/init/detector.js +582 -0
  150. package/dist/core/init/template-renderer.js +242 -0
  151. package/dist/core/jobs/registry.js +18 -18
  152. package/dist/core/ledger/results-tsv.js +142 -0
  153. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  154. package/dist/core/lsp/cache.js +105 -0
  155. package/dist/core/lsp/client.js +551 -41
  156. package/dist/core/lsp/language-detect.js +66 -0
  157. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  158. package/dist/core/lsp/server-detect.js +173 -0
  159. package/dist/core/lsp/symbol-cache.js +162 -0
  160. package/dist/core/lsp/symbol-tools.js +664 -0
  161. package/dist/core/mcp/client.js +97 -28
  162. package/dist/core/mcp/http-server.js +553 -0
  163. package/dist/core/mcp/orchestrator-config.js +192 -0
  164. package/dist/core/mcp/orchestrator-tools.js +806 -0
  165. package/dist/core/mcp/permission.js +190 -0
  166. package/dist/core/mcp/registry.js +39 -17
  167. package/dist/core/mcp/server-tools.js +219 -0
  168. package/dist/core/mcp/server.js +397 -0
  169. package/dist/core/mcp/trust.js +10 -10
  170. package/dist/core/memory/dual-write.js +416 -0
  171. package/dist/core/memory/passive-extract.js +130 -0
  172. package/dist/core/memory/phase1-kinds.js +20 -0
  173. package/dist/core/memory/secret-scanner.js +304 -0
  174. package/dist/core/memory-sync/queue.js +170 -0
  175. package/dist/core/metrics/extract.js +113 -0
  176. package/dist/core/modes/roo-modes.js +68 -0
  177. package/dist/core/notes/notes-paths.js +113 -0
  178. package/dist/core/notes/notes-recorder.js +140 -0
  179. package/dist/core/notes/notes-writer.js +53 -0
  180. package/dist/core/notes/renderers.js +0 -0
  181. package/dist/core/notes/slug.js +105 -0
  182. package/dist/core/onboarding/ensure-initialized.js +133 -0
  183. package/dist/core/onboarding/marker.js +111 -0
  184. package/dist/core/onboarding/telemetry-state.js +108 -0
  185. package/dist/core/output-style/presets.js +176 -0
  186. package/dist/core/output-style/state.js +185 -0
  187. package/dist/core/path-security.js +287 -5
  188. package/dist/core/permission.js +82 -22
  189. package/dist/core/permissions/auto-classifier.js +124 -0
  190. package/dist/core/permissions/bash-parser.js +371 -0
  191. package/dist/core/permissions/circuit-breaker.js +83 -0
  192. package/dist/core/permissions/constrained-edit.js +91 -0
  193. package/dist/core/permissions/gate.js +278 -0
  194. package/dist/core/permissions/index.js +20 -0
  195. package/dist/core/permissions/mode.js +174 -0
  196. package/dist/core/permissions/network-egress.js +137 -0
  197. package/dist/core/permissions/state.js +241 -0
  198. package/dist/core/permissions/tool-class.js +107 -0
  199. package/dist/core/plan-mode/ui-state.js +51 -0
  200. package/dist/core/plans/plan-artifact.js +721 -0
  201. package/dist/core/policy-limits/etag-store.js +122 -0
  202. package/dist/core/prd-check/parser.js +215 -0
  203. package/dist/core/prd-check/reporter.js +127 -0
  204. package/dist/core/prd-check/session-review.js +557 -0
  205. package/dist/core/prd-check/verifiers.js +223 -0
  206. package/dist/core/prompt-cache/client-cache.js +99 -0
  207. package/dist/core/prompts/assembly.js +29 -0
  208. package/dist/core/prompts/registry.js +364 -0
  209. package/dist/core/pugi-gitignore.js +52 -0
  210. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  211. package/dist/core/pugi-md/context-injector.js +76 -0
  212. package/dist/core/pugi-md/walk-up.js +207 -0
  213. package/dist/core/python/uv-installer.js +270 -0
  214. package/dist/core/python/uv-resolver.js +83 -0
  215. package/dist/core/rate-limit/narrator.js +146 -0
  216. package/dist/core/recipes/cli-types.js +20 -0
  217. package/dist/core/recipes/loader.js +103 -0
  218. package/dist/core/recipes/runner.js +345 -0
  219. package/dist/core/recipes/schema.js +587 -0
  220. package/dist/core/release-notes/parser.js +241 -0
  221. package/dist/core/release-notes/state.js +116 -0
  222. package/dist/core/repl/ask.js +37 -37
  223. package/dist/core/repl/cancellation.js +26 -26
  224. package/dist/core/repl/cap-warning.js +4 -4
  225. package/dist/core/repl/clipboard-read.js +11 -11
  226. package/dist/core/repl/dispatch-fsm.js +12 -12
  227. package/dist/core/repl/engine-bridge.js +303 -0
  228. package/dist/core/repl/history-search.js +15 -15
  229. package/dist/core/repl/history.js +28 -18
  230. package/dist/core/repl/kill-ring.js +5 -5
  231. package/dist/core/repl/model-pricing.js +135 -0
  232. package/dist/core/repl/privacy-banner.js +22 -22
  233. package/dist/core/repl/session.js +2690 -229
  234. package/dist/core/repl/slash-commands.js +540 -41
  235. package/dist/core/repl/store/index.js +1 -1
  236. package/dist/core/repl/store/jsonl-log.js +22 -22
  237. package/dist/core/repl/store/lockfile.js +10 -10
  238. package/dist/core/repl/store/session-store.js +136 -107
  239. package/dist/core/repl/store/types.js +15 -15
  240. package/dist/core/repl/store/uuid-v7.js +12 -12
  241. package/dist/core/repl/tool-route.js +382 -0
  242. package/dist/core/repl/workspace-context.js +43 -21
  243. package/dist/core/repo-map/build.js +125 -0
  244. package/dist/core/repo-map/cache.js +185 -0
  245. package/dist/core/repo-map/extractor.js +254 -0
  246. package/dist/core/repo-map/formatter.js +145 -0
  247. package/dist/core/repo-map/page-rank.js +105 -0
  248. package/dist/core/repo-map/scanner.js +211 -0
  249. package/dist/core/retro/git-collector.js +251 -0
  250. package/dist/core/retro/health-card.js +25 -0
  251. package/dist/core/retro/metrics.js +342 -0
  252. package/dist/core/retro/narrative.js +249 -0
  253. package/dist/core/retro/plane-collector.js +274 -0
  254. package/dist/core/retro/pr-issue-link.js +65 -0
  255. package/dist/core/retro/types.js +16 -0
  256. package/dist/core/retry-budget/budget.js +284 -0
  257. package/dist/core/retry-budget/index.js +5 -0
  258. package/dist/core/retry-budget/retry-cap.js +74 -0
  259. package/dist/core/routing/lead-worker.js +43 -0
  260. package/dist/core/routing/pre-flight-estimator.js +108 -0
  261. package/dist/core/runs/run-tree.js +103 -0
  262. package/dist/core/sandboxing/adapter.js +29 -0
  263. package/dist/core/sandboxing/index.js +49 -0
  264. package/dist/core/sandboxing/none.js +19 -0
  265. package/dist/core/sandboxing/seatbelt.js +183 -0
  266. package/dist/core/security/injection-scanner.js +367 -0
  267. package/dist/core/security/output-filter.js +418 -0
  268. package/dist/core/session/env-file.js +105 -0
  269. package/dist/core/session/section-budgets.js +140 -0
  270. package/dist/core/session.js +119 -0
  271. package/dist/core/settings.js +378 -5
  272. package/dist/core/share/formatter.js +271 -0
  273. package/dist/core/share/redactor.js +221 -0
  274. package/dist/core/share/uploader.js +267 -0
  275. package/dist/core/skills/defaults.js +30 -30
  276. package/dist/core/skills/loader.js +22 -22
  277. package/dist/core/skills/sources.js +27 -27
  278. package/dist/core/smoke/headless-driver.js +174 -0
  279. package/dist/core/smoke/orchestrator.js +194 -0
  280. package/dist/core/smoke/runner.js +238 -0
  281. package/dist/core/smoke/scenario-parser.js +316 -0
  282. package/dist/core/statusline.js +99 -0
  283. package/dist/core/subagents/dispatcher-real.js +600 -0
  284. package/dist/core/subagents/dispatcher.js +146 -52
  285. package/dist/core/subagents/index.js +19 -6
  286. package/dist/core/subagents/isolation-matrix.js +213 -0
  287. package/dist/core/subagents/spawn.js +19 -4
  288. package/dist/core/telemetry/emitter.js +229 -0
  289. package/dist/core/telemetry/queue.js +251 -0
  290. package/dist/core/theme/context.js +91 -0
  291. package/dist/core/theme/presets.js +228 -0
  292. package/dist/core/theme/state.js +181 -0
  293. package/dist/core/todos/invariant.js +10 -0
  294. package/dist/core/todos/state.js +177 -0
  295. package/dist/core/tool-schema/compressor.js +89 -0
  296. package/dist/core/transport/version-interceptor.js +166 -0
  297. package/dist/core/trust.js +2 -2
  298. package/dist/core/tui/thinking-block.js +64 -0
  299. package/dist/core/vim/keymap.js +288 -0
  300. package/dist/core/vim/state.js +92 -0
  301. package/dist/core/watch-markers/marker-watcher.js +133 -0
  302. package/dist/core/worktree/include-parser.js +249 -0
  303. package/dist/core/worktree-manager/cleanup.js +123 -0
  304. package/dist/core/worktree-manager/manager.js +303 -0
  305. package/dist/index.js +36 -0
  306. package/dist/runtime/bootstrap.js +190 -0
  307. package/dist/runtime/cli.js +4345 -561
  308. package/dist/runtime/commands/agents.js +31 -31
  309. package/dist/runtime/commands/budget.js +5 -5
  310. package/dist/runtime/commands/cancel.js +231 -0
  311. package/dist/runtime/commands/chain.js +489 -0
  312. package/dist/runtime/commands/codegraph-status.js +227 -0
  313. package/dist/runtime/commands/compact.js +297 -0
  314. package/dist/runtime/commands/config.js +74 -40
  315. package/dist/runtime/commands/cost.js +199 -0
  316. package/dist/runtime/commands/delegate.js +27 -4
  317. package/dist/runtime/commands/dispatch.js +126 -0
  318. package/dist/runtime/commands/doctor.js +579 -0
  319. package/dist/runtime/commands/feedback.js +184 -0
  320. package/dist/runtime/commands/hooks.js +187 -0
  321. package/dist/runtime/commands/index-cmd.js +353 -0
  322. package/dist/runtime/commands/init.js +254 -0
  323. package/dist/runtime/commands/lsp.js +200 -38
  324. package/dist/runtime/commands/mcp.js +935 -0
  325. package/dist/runtime/commands/memory.js +582 -0
  326. package/dist/runtime/commands/model.js +237 -0
  327. package/dist/runtime/commands/onboarding.js +275 -0
  328. package/dist/runtime/commands/patch.js +12 -12
  329. package/dist/runtime/commands/permissions.js +112 -0
  330. package/dist/runtime/commands/plan.js +143 -0
  331. package/dist/runtime/commands/prd-check.js +285 -0
  332. package/dist/runtime/commands/privacy.js +17 -17
  333. package/dist/runtime/commands/recipe.js +325 -0
  334. package/dist/runtime/commands/redo-blob-store.js +92 -0
  335. package/dist/runtime/commands/redo.js +361 -0
  336. package/dist/runtime/commands/release-notes.js +229 -0
  337. package/dist/runtime/commands/repo-map.js +95 -0
  338. package/dist/runtime/commands/report.js +299 -0
  339. package/dist/runtime/commands/resume.js +118 -0
  340. package/dist/runtime/commands/review-consensus.js +68 -53
  341. package/dist/runtime/commands/rewind.js +333 -0
  342. package/dist/runtime/commands/roster.js +14 -14
  343. package/dist/runtime/commands/servers.js +236 -0
  344. package/dist/runtime/commands/sessions.js +163 -0
  345. package/dist/runtime/commands/share.js +316 -0
  346. package/dist/runtime/commands/skills.js +31 -31
  347. package/dist/runtime/commands/status.js +186 -0
  348. package/dist/runtime/commands/stickers.js +82 -0
  349. package/dist/runtime/commands/style.js +194 -0
  350. package/dist/runtime/commands/theme.js +196 -0
  351. package/dist/runtime/commands/undo.js +54 -22
  352. package/dist/runtime/commands/update.js +289 -0
  353. package/dist/runtime/commands/vim.js +140 -0
  354. package/dist/runtime/commands/worktree.js +8 -8
  355. package/dist/runtime/commands/worktrees.js +155 -0
  356. package/dist/runtime/deprecation-warning.js +69 -0
  357. package/dist/runtime/engine-exit-code.js +50 -0
  358. package/dist/runtime/headless-repl.js +195 -0
  359. package/dist/runtime/headless.js +548 -0
  360. package/dist/runtime/load-hooks-or-exit.js +71 -0
  361. package/dist/runtime/plan-decompose.js +22 -22
  362. package/dist/runtime/sigint-guard.js +272 -0
  363. package/dist/runtime/stream-renderer.js +195 -0
  364. package/dist/runtime/update-check.js +28 -28
  365. package/dist/runtime/version.js +65 -0
  366. package/dist/runtime/worktree-bootstrap.js +579 -0
  367. package/dist/skills/bundled/batch.js +617 -0
  368. package/dist/skills/bundled/index.js +45 -0
  369. package/dist/skills/bundled/loop.js +358 -0
  370. package/dist/skills/bundled/remember.js +383 -0
  371. package/dist/skills/bundled/simplify.js +289 -0
  372. package/dist/skills/bundled/skillify.js +373 -0
  373. package/dist/skills/bundled/stuck.js +558 -0
  374. package/dist/skills/bundled/verify.js +439 -0
  375. package/dist/testing/vcr.js +486 -0
  376. package/dist/tools/agent-tool.js +229 -0
  377. package/dist/tools/apply-patch.js +89 -28
  378. package/dist/tools/ask-user-question.js +337 -0
  379. package/dist/tools/ask-user.js +115 -0
  380. package/dist/tools/bash.js +624 -46
  381. package/dist/tools/brief.js +224 -0
  382. package/dist/tools/cron.js +433 -0
  383. package/dist/tools/enter-worktree.js +250 -0
  384. package/dist/tools/exit-worktree.js +147 -0
  385. package/dist/tools/file-tools.js +161 -44
  386. package/dist/tools/http-request.js +336 -0
  387. package/dist/tools/lsp-tools.js +377 -1
  388. package/dist/tools/mcp-tool.js +260 -0
  389. package/dist/tools/multi-edit.js +361 -0
  390. package/dist/tools/powershell.js +268 -0
  391. package/dist/tools/registry.js +120 -5
  392. package/dist/tools/server-tools.js +892 -0
  393. package/dist/tools/skill-tool.js +96 -0
  394. package/dist/tools/sleep.js +99 -0
  395. package/dist/tools/synthetic-output.js +133 -0
  396. package/dist/tools/tasks.js +208 -0
  397. package/dist/tools/todo-write.js +184 -0
  398. package/dist/tools/verify-plan-execution.js +295 -0
  399. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  400. package/dist/tools/web-fetch.js +195 -10
  401. package/dist/tools/web-search.js +458 -0
  402. package/dist/tui/agent-progress-card.js +111 -0
  403. package/dist/tui/agent-tree.js +22 -1
  404. package/dist/tui/ask-modal.js +14 -14
  405. package/dist/tui/ask-user-question-chips.js +315 -0
  406. package/dist/tui/ask-user-question-prompt.js +203 -0
  407. package/dist/tui/compact-banner.js +81 -0
  408. package/dist/tui/conversation-pane.js +85 -11
  409. package/dist/tui/cost-table.js +111 -0
  410. package/dist/tui/device-flow.js +2 -2
  411. package/dist/tui/doctor-table.js +46 -0
  412. package/dist/tui/feedback-prompt.js +156 -0
  413. package/dist/tui/input-box.js +247 -32
  414. package/dist/tui/login-picker.js +3 -3
  415. package/dist/tui/markdown-render.js +6 -6
  416. package/dist/tui/multi-file-diff-approval.js +375 -0
  417. package/dist/tui/onboarding-wizard.js +240 -0
  418. package/dist/tui/permissions-picker.js +86 -0
  419. package/dist/tui/render.js +36 -1
  420. package/dist/tui/repl-render.js +239 -25
  421. package/dist/tui/repl-splash-art.js +16 -16
  422. package/dist/tui/repl-splash-mascot.js +48 -24
  423. package/dist/tui/repl-splash.js +22 -22
  424. package/dist/tui/repl.js +125 -45
  425. package/dist/tui/slash-palette.js +6 -6
  426. package/dist/tui/splash.js +2 -2
  427. package/dist/tui/status-bar.js +109 -31
  428. package/dist/tui/status-table.js +7 -0
  429. package/dist/tui/stickers-art.js +136 -0
  430. package/dist/tui/style-table.js +28 -0
  431. package/dist/tui/theme-table.js +29 -0
  432. package/dist/tui/thinking-spinner.js +123 -0
  433. package/dist/tui/tool-stream-pane.js +53 -4
  434. package/dist/tui/update-banner.js +27 -2
  435. package/dist/tui/vim-input.js +267 -0
  436. package/dist/tui/welcome-banner.js +107 -0
  437. package/dist/tui/welcome-data.js +293 -0
  438. package/dist/tui/workspace-context.js +2 -2
  439. package/package.json +21 -5
  440. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  441. package/test/scenarios/compact-force.scenario.txt +12 -0
  442. package/test/scenarios/identity.scenario.txt +11 -0
  443. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  444. package/test/scenarios/walkback.scenario.txt +12 -0
  445. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,65 @@
1
+ /**
2
+ * GIT probe — verifies git is on PATH AND the current cwd is inside a
3
+ * git work tree. Reports the short HEAD sha + repo root for context.
4
+ *
5
+ * Pugi treats the workspace as a git project (worktree isolation,
6
+ * /codex review, /triple-review and the entire agent loop assume
7
+ * `git diff origin/main..HEAD` is meaningful). A workspace that is
8
+ * not a git work tree still works for read-only commands but the
9
+ * doctor surface flags this so the operator knows where the limits
10
+ * are.
11
+ */
12
+ export function probeGit(ctx, deps) {
13
+ let version;
14
+ try {
15
+ version = deps.resolveVersion();
16
+ }
17
+ catch (error) {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ return {
20
+ name: 'GIT',
21
+ status: 'warn',
22
+ detail: 'git not on PATH',
23
+ remediation: `Install git (error: ${message})`,
24
+ };
25
+ }
26
+ let inWorkTree = false;
27
+ try {
28
+ inWorkTree = deps.isInWorkTree(ctx.cwd);
29
+ }
30
+ catch {
31
+ inWorkTree = false;
32
+ }
33
+ if (!inWorkTree) {
34
+ return {
35
+ name: 'GIT',
36
+ status: 'warn',
37
+ detail: `${version} (cwd is not a git work tree)`,
38
+ remediation: 'Run `git init` to enable diff-based commands',
39
+ };
40
+ }
41
+ const sha = (() => {
42
+ try {
43
+ return deps.resolveHeadSha(ctx.cwd);
44
+ }
45
+ catch {
46
+ return null;
47
+ }
48
+ })();
49
+ const root = (() => {
50
+ try {
51
+ return deps.resolveRoot(ctx.cwd);
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ })();
57
+ const shaSuffix = sha ? ` @ ${sha.slice(0, 8)}` : '';
58
+ const rootSuffix = root ? ` (${root})` : '';
59
+ return {
60
+ name: 'GIT',
61
+ status: 'ok',
62
+ detail: `${version}${shaSuffix}${rootSuffix}`,
63
+ };
64
+ }
65
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1,118 @@
1
+ /**
2
+ * HOOKS probe — verifies `.pugi/hooks-mvp.json` and `.pugi/hook-chains.json`
3
+ * exist + parse + carry their declared shape. Absence is OK (most workspaces
4
+ * don't ship hooks); presence с invalid JSON is a deployment-blocking error
5
+ * the operator wants surfaced before a tool dispatch fires a malformed hook
6
+ * and the model sees a confusing failure.
7
+ *
8
+ * Validation tier:
9
+ * 1. JSON.parse — catches typos / trailing commas / bad escapes.
10
+ * 2. Top-level shape sniff — `hooks-mvp.json` MUST be an object с a
11
+ * `hooks` array property; `hook-chains.json` MUST be an object
12
+ * с string-keyed event names mapping to arrays.
13
+ * 3. Per-entry require-fields sniff — minimal так что probe stays cheap.
14
+ *
15
+ * Out of scope: full Zod schema validation. That lives in the hook loader
16
+ * itself (apps/pugi-cli/src/core/hooks/v2/loader.ts). The probe is a
17
+ * fast sanity check; the loader produces the canonical error message
18
+ * when a hook actually fires.
19
+ */
20
+ function loadHookFile(fs, path) {
21
+ if (!fs.existsSync(path))
22
+ return null;
23
+ try {
24
+ const raw = fs.readFileSync(path, 'utf8');
25
+ return { path, parsed: JSON.parse(raw) };
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ return { parseError: `${path}: ${message}` };
30
+ }
31
+ }
32
+ function validateMvpShape(parsed) {
33
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
34
+ return 'top-level value must be an object';
35
+ }
36
+ const hooks = parsed['hooks'];
37
+ if (hooks !== undefined && !Array.isArray(hooks)) {
38
+ return '`hooks` property must be an array when present';
39
+ }
40
+ return null;
41
+ }
42
+ function validateChainsShape(parsed) {
43
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
44
+ return 'top-level value must be an object';
45
+ }
46
+ for (const [event, body] of Object.entries(parsed)) {
47
+ if (!Array.isArray(body)) {
48
+ return `event "${event}": value must be an array of hook entries`;
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+ export function probeHooks(ctx, fs) {
54
+ const mvpPath = `${ctx.cwd}/.pugi/hooks-mvp.json`;
55
+ const chainsPath = `${ctx.cwd}/.pugi/hook-chains.json`;
56
+ const mvp = loadHookFile(fs, mvpPath);
57
+ const chains = loadHookFile(fs, chainsPath);
58
+ const issues = [];
59
+ let mvpCount = 0;
60
+ let chainEvents = 0;
61
+ if (mvp !== null) {
62
+ if ('parseError' in mvp) {
63
+ issues.push(`hooks-mvp.json parse failed: ${mvp.parseError}`);
64
+ }
65
+ else {
66
+ const shapeIssue = validateMvpShape(mvp.parsed);
67
+ if (shapeIssue) {
68
+ issues.push(`hooks-mvp.json: ${shapeIssue}`);
69
+ }
70
+ else {
71
+ const hooksArr = mvp.parsed['hooks'];
72
+ mvpCount = Array.isArray(hooksArr) ? hooksArr.length : 0;
73
+ }
74
+ }
75
+ }
76
+ if (chains !== null) {
77
+ if ('parseError' in chains) {
78
+ issues.push(`hook-chains.json parse failed: ${chains.parseError}`);
79
+ }
80
+ else {
81
+ const shapeIssue = validateChainsShape(chains.parsed);
82
+ if (shapeIssue) {
83
+ issues.push(`hook-chains.json: ${shapeIssue}`);
84
+ }
85
+ else {
86
+ chainEvents = Object.keys(chains.parsed).length;
87
+ }
88
+ }
89
+ }
90
+ if (issues.length > 0) {
91
+ return {
92
+ name: 'HOOKS',
93
+ status: 'error',
94
+ detail: issues.join('; '),
95
+ remediation: 'Fix the JSON syntax / shape, or delete the file if hooks are not in use. The hook loader surfaces the canonical error when a hook actually fires; this probe catches the problem before the first dispatch.',
96
+ };
97
+ }
98
+ if (mvp === null && chains === null) {
99
+ return {
100
+ name: 'HOOKS',
101
+ status: 'skipped',
102
+ detail: 'no hook config files in .pugi/ (workspace has not opted into hooks)',
103
+ };
104
+ }
105
+ const parts = [];
106
+ if (mvp !== null && !('parseError' in mvp)) {
107
+ parts.push(`hooks-mvp.json OK (${mvpCount} entr${mvpCount === 1 ? 'y' : 'ies'})`);
108
+ }
109
+ if (chains !== null && !('parseError' in chains)) {
110
+ parts.push(`hook-chains.json OK (${chainEvents} event${chainEvents === 1 ? '' : 's'})`);
111
+ }
112
+ return {
113
+ name: 'HOOKS',
114
+ status: 'ok',
115
+ detail: parts.join('; '),
116
+ };
117
+ }
118
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * MCP probe — reports the configured Model Context Protocol servers
3
+ * along with their trust + connection state.
4
+ *
5
+ * Sibling L13 (MCP server config) ships its own command surface; this
6
+ * probe consumes the existing `loadMcpRegistry` helper в a graceful
7
+ * try/catch so an unconfigured workspace (no `.pugi/mcp.json` OR an
8
+ * empty `servers: {}` map) lands as `ok` with detail "no servers
9
+ * configured" rather than a noisy failure row.
10
+ *
11
+ * Failure modes:
12
+ * - registry helper throws → `warn` with the error message;
13
+ * the table renderer surfaces the remediation hint;
14
+ * - one or more servers report `lastError` → `warn` summarising the
15
+ * count;
16
+ * - all configured servers connected cleanly → `ok` listing them.
17
+ */
18
+ export async function probeMcp(ctx, deps) {
19
+ let registry;
20
+ try {
21
+ // `connect: false` keeps the probe cheap — we only need the
22
+ // declared config + trust ledger entries, not live subprocess
23
+ // handshakes. The doctor sweep budget shouldn't be eaten by
24
+ // server-spawn latency.
25
+ registry = await deps.loadRegistry(ctx.cwd, { connect: false });
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ return {
30
+ name: 'MCP SERVERS',
31
+ status: 'warn',
32
+ detail: 'MCP registry not loadable (config or schema error)',
33
+ remediation: `Inspect .pugi/mcp.json: ${message}`,
34
+ };
35
+ }
36
+ const servers = Array.from(registry.servers.values());
37
+ // Best-effort shutdown — even with `connect: false` we still own the
38
+ // registry handle. Swallow errors so a clean-up failure never
39
+ // poisons the probe verdict.
40
+ await registry.shutdown().catch(() => { });
41
+ if (servers.length === 0) {
42
+ return {
43
+ name: 'MCP SERVERS',
44
+ status: 'ok',
45
+ detail: 'No MCP servers configured',
46
+ };
47
+ }
48
+ const labels = servers.map((server) => `${server.name}:${server.trust}`);
49
+ const trusted = servers.filter((server) => server.trust === 'trusted');
50
+ const pending = servers.filter((server) => server.trust === 'pending');
51
+ const denied = servers.filter((server) => server.trust === 'denied');
52
+ const failing = servers.filter((server) => typeof server.lastError === 'string' && server.lastError.length > 0);
53
+ if (failing.length > 0) {
54
+ return {
55
+ name: 'MCP SERVERS',
56
+ status: 'warn',
57
+ detail: `${failing.length}/${servers.length} server(s) reporting errors: ${labels.join(', ')}`,
58
+ remediation: `Inspect: \`pugi mcp list\``,
59
+ };
60
+ }
61
+ if (pending.length > 0) {
62
+ return {
63
+ name: 'MCP SERVERS',
64
+ status: 'warn',
65
+ detail: `${pending.length} pending trust decision(s): ${labels.join(', ')}`,
66
+ remediation: 'Run `pugi mcp trust <name>` or `pugi mcp deny <name>`',
67
+ };
68
+ }
69
+ return {
70
+ name: 'MCP SERVERS',
71
+ status: 'ok',
72
+ detail: `${trusted.length} trusted, ${denied.length} denied (${labels.join(', ')})`,
73
+ };
74
+ }
75
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * NODE probe — verifies the running Node major version meets the
3
+ * `engines.node` floor declared in @pugi/cli's package.json.
4
+ *
5
+ * We bake the floor in as a constant rather than reading package.json
6
+ * at runtime because the published .tgz strips the file from a location
7
+ * the compiled bundle can reach (`--resolveJsonModule` is off for the
8
+ * CLI build). The lockstep is enforced by
9
+ * `scripts/check-version-lockstep.sh` in the publish pipeline.
10
+ */
11
+ /**
12
+ * Minimum supported Node major. Mirrors `engines.node` in
13
+ * apps/pugi-cli/package.json (`">=22.5.0"`).
14
+ */
15
+ export const MIN_NODE_MAJOR = 22;
16
+ export const MIN_NODE_MINOR = 5;
17
+ /**
18
+ * Parse a Node version string of the form `v<major>.<minor>.<patch>`.
19
+ * Returns null when the input doesn't match — the caller treats null
20
+ * as an error condition.
21
+ */
22
+ export function parseNodeVersion(version) {
23
+ const match = /^v(\d+)\.(\d+)\./.exec(version);
24
+ if (!match)
25
+ return null;
26
+ const major = Number(match[1]);
27
+ const minor = Number(match[2]);
28
+ if (!Number.isFinite(major) || !Number.isFinite(minor))
29
+ return null;
30
+ return { major, minor };
31
+ }
32
+ export function probeNode(input) {
33
+ const parsed = parseNodeVersion(input.version);
34
+ if (!parsed) {
35
+ return {
36
+ name: 'NODE',
37
+ status: 'error',
38
+ detail: `Unparseable Node version "${input.version}"`,
39
+ remediation: `Install Node >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0 (current binary returns a non-semver version string)`,
40
+ };
41
+ }
42
+ const { major, minor } = parsed;
43
+ const passes = major > MIN_NODE_MAJOR ||
44
+ (major === MIN_NODE_MAJOR && minor >= MIN_NODE_MINOR);
45
+ if (passes) {
46
+ return {
47
+ name: 'NODE',
48
+ status: 'ok',
49
+ detail: `${input.version} (>= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0 required)`,
50
+ };
51
+ }
52
+ return {
53
+ name: 'NODE',
54
+ status: 'error',
55
+ detail: `${input.version} below floor >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.0`,
56
+ remediation: `Upgrade Node: \`nvm install ${MIN_NODE_MAJOR}\` or download from nodejs.org`,
57
+ };
58
+ }
59
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * PNPM probe — verifies pnpm is on PATH and reports its version. The
3
+ * customer-facing CLI doesn't strictly require pnpm at runtime
4
+ * (`@pugi/cli` is published as a regular npm package), but a missing
5
+ * pnpm means `pugi code` cannot run `pnpm test` / `pnpm typecheck`
6
+ * gates the agent loop emits. Surfacing this early prevents the
7
+ * agent from issuing a meaningless `command not found: pnpm` error
8
+ * three turns into a session.
9
+ */
10
+ export function probePnpm(deps) {
11
+ try {
12
+ const version = deps.resolveVersion();
13
+ if (!version) {
14
+ return {
15
+ name: 'PNPM',
16
+ status: 'warn',
17
+ detail: 'pnpm reported empty version string',
18
+ };
19
+ }
20
+ return {
21
+ name: 'PNPM',
22
+ status: 'ok',
23
+ detail: `pnpm ${version}`,
24
+ };
25
+ }
26
+ catch (error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ return {
29
+ name: 'PNPM',
30
+ status: 'warn',
31
+ detail: 'pnpm not on PATH — agent quality gates will be skipped',
32
+ remediation: `Install pnpm: \`npm i -g pnpm\` (error: ${message})`,
33
+ };
34
+ }
35
+ }
36
+ //# sourceMappingURL=pnpm.js.map
@@ -0,0 +1,89 @@
1
+ /**
2
+ * PUGI.md hierarchy probe — .
3
+ *
4
+ * Surfaces how many ambient `PUGI.md` / `CLAUDE.md` files were
5
+ * discovered by the cwd → homedir walk-up. Operators triaging "why
6
+ * is the model not following my project conventions" can run
7
+ * `pugi doctor` and immediately see whether the hierarchy walk
8
+ * loaded the file they expected.
9
+ *
10
+ * Status semantics:
11
+ * - `skipped` when bare mode is active (the walk is deliberately
12
+ * disabled). The row still renders so the JSON schema stays
13
+ * stable for downstream consumers.
14
+ * - `skipped` when zero files were found. This is the default
15
+ * state on a clean machine and is NOT an error — most operators
16
+ * do not maintain a `~/PUGI.md`.
17
+ * - `ok` when one or more files were discovered. The detail names
18
+ * the closest file and the total count.
19
+ *
20
+ * Side effects:
21
+ * - One filesystem walk from cwd to homedir (bounded by the walker's
22
+ * depth cap). Each level performs at most 2 `existsSync` calls.
23
+ * Cost is single-digit ms even on cold cache; well inside the
24
+ * doctor probe wall-clock budget.
25
+ *
26
+ * Wired into `buildDefaultProbes` in `runtime/commands/doctor.ts`.
27
+ */
28
+ import { isBareMode } from '../../bare-mode/index.js';
29
+ import { walkUpPugiMd } from '../../pugi-md/walk-up.js';
30
+ export const PUGI_MD_DOCTOR_LABEL = 'PUGI.md HIERARCHY';
31
+ /** Detail string emitted when bare mode disables the walk. */
32
+ export const PUGI_MD_BARE_SKIP_DETAIL = 'skipped (--bare)';
33
+ /** Detail string emitted when the walk ran but found nothing. */
34
+ export const PUGI_MD_EMPTY_DETAIL = 'no PUGI.md / CLAUDE.md found in cwd → homedir';
35
+ export function probePugiMdHierarchy(input = {}) {
36
+ const env = input.env ?? process.env;
37
+ if (isBareMode(env)) {
38
+ return {
39
+ name: PUGI_MD_DOCTOR_LABEL,
40
+ status: 'skipped',
41
+ detail: PUGI_MD_BARE_SKIP_DETAIL,
42
+ };
43
+ }
44
+ const cwd = input.cwd ?? process.cwd();
45
+ const walk = input.walkImpl ?? ((c, o) => walkUpPugiMd(c, o));
46
+ let files;
47
+ try {
48
+ files = walk(cwd, input.homedir !== undefined ? { homedir: input.homedir } : {});
49
+ }
50
+ catch {
51
+ // Defensive: walker is wrapped in per-file try/catch already, so
52
+ // a throw here means a programmer error (bad input). Degrade to
53
+ // a `warn` row rather than crashing the doctor sweep.
54
+ return {
55
+ name: PUGI_MD_DOCTOR_LABEL,
56
+ status: 'warn',
57
+ detail: 'walk-up failed (see logs)',
58
+ };
59
+ }
60
+ if (files.length === 0) {
61
+ return {
62
+ name: PUGI_MD_DOCTOR_LABEL,
63
+ status: 'skipped',
64
+ detail: PUGI_MD_EMPTY_DETAIL,
65
+ };
66
+ }
67
+ // `files` is shallow-to-deep; the first entry is the closest to cwd.
68
+ // Operators reading the table care most about that one — it is the
69
+ // file that will most directly influence model behaviour. The
70
+ // additional count gives a quick "is the homedir / parent picked up
71
+ // too?" signal without listing every path.
72
+ const closest = files[0];
73
+ // closest is guaranteed non-undefined: files.length > 0 enforced
74
+ // by the early return above.
75
+ if (!closest) {
76
+ return {
77
+ name: PUGI_MD_DOCTOR_LABEL,
78
+ status: 'skipped',
79
+ detail: PUGI_MD_EMPTY_DETAIL,
80
+ };
81
+ }
82
+ const suffix = files.length === 1 ? '' : ` (+${files.length - 1} more)`;
83
+ return {
84
+ name: PUGI_MD_DOCTOR_LABEL,
85
+ status: 'ok',
86
+ detail: `${files.length} file${files.length === 1 ? '' : 's'}: ${closest.path}${suffix}`,
87
+ };
88
+ }
89
+ //# sourceMappingURL=pugi-md.js.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * SANDBOX probe — surfaces the current OS-level sandbox posture (
3
+ * Trust Sprint item 6: macOS Seatbelt adapter wired; Linux Landlock
4
+ * and Docker variants still backlog).
5
+ *
6
+ * Sources `bash.sandbox` from `.pugi/settings.json`, defaults to
7
+ * `none`. When set to `macOS-seatbelt` the probe verifies the OS
8
+ * primitive is callable and reports `ok` (armed) or `error`
9
+ * (configured-but-unavailable). When set to `none` the probe reports
10
+ * `warn` with the operator-readable reason "policy 'none' selected".
11
+ */
12
+ import { homedir } from 'node:os';
13
+ import { loadSettings } from '../../settings.js';
14
+ import { probeSandbox as probeSandboxAdapter } from '../../sandboxing/index.js';
15
+ export function probeSandbox(ctx) {
16
+ const settings = loadSettings(ctx.cwd);
17
+ const configured = (settings.bash?.sandbox ?? 'none');
18
+ const home = ctx.home || homedir();
19
+ const extraWritePaths = [`${home}/.pugi`];
20
+ try {
21
+ const state = probeSandboxAdapter({
22
+ mode: configured,
23
+ workspaceRoot: ctx.cwd,
24
+ extraWritePaths,
25
+ });
26
+ if (state.armed) {
27
+ // Discipline-gap honesty (Trust Sprint thesis): the adapter
28
+ // probes ok, but spawn-wrap is NOT yet wired into the bash
29
+ // runner (that file is owned by another agent on PUGI-VERIFY-
30
+ // GATE). Reporting status=ok would overstate the posture — an
31
+ // operator reading 'armed' would assume their bash calls were
32
+ // jailed when they still run with full process privileges. We
33
+ // surface 'warn' with a precise reason instead and flip к 'ok'
34
+ // when the runner indirection lands.
35
+ return {
36
+ name: 'SANDBOX',
37
+ status: 'warn',
38
+ detail: `configured (mode=${state.mode}) but spawn-wrap not yet wired — bash dispatches still run with full process privileges. ` +
39
+ `Adapter posture: ${state.details.join('; ')}`,
40
+ remediation: 'The seatbelt adapter is in-tree and exercised by tests; the bash runner indirection that consumes it lands in a follow-up. ' +
41
+ 'Bash classifier denylist + permission FSM remain in force in the meantime.',
42
+ };
43
+ }
44
+ // Not armed — distinguish "operator chose none" from "configured
45
+ // mode failed". The latter is an error; the former is a documented
46
+ // posture and stays a warning.
47
+ if (state.mode === 'none') {
48
+ return {
49
+ name: 'SANDBOX',
50
+ status: 'warn',
51
+ detail: `not armed: ${state.reason ?? 'mode none'}`,
52
+ remediation: 'Set `bash.sandbox = "macOS-seatbelt"` in .pugi/settings.json on macOS to enable workspace-scoped write isolation. ' +
53
+ 'Bash classifier denylist + permission FSM still apply.',
54
+ };
55
+ }
56
+ return {
57
+ name: 'SANDBOX',
58
+ status: 'error',
59
+ detail: `configured mode "${state.mode}" failed to arm: ${state.reason ?? 'unknown'}`,
60
+ remediation: 'Set `bash.sandbox` to a supported mode for this platform or remove the key to fall back to "none".',
61
+ };
62
+ }
63
+ catch (err) {
64
+ return {
65
+ name: 'SANDBOX',
66
+ status: 'error',
67
+ detail: `sandbox probe threw: ${err.message}`,
68
+ remediation: 'Remove the bash.sandbox key from .pugi/settings.json or set it to "none".',
69
+ };
70
+ }
71
+ }
72
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * SESSION probe — reports the active session id + age when the doctor
3
+ * runs from inside the REPL OR finds a recent NDJSON session log in
4
+ * the workspace.
5
+ *
6
+ * The CLI command path has no live session context (each `pugi <cmd>`
7
+ * invocation is a fresh process), so we read `.pugi/events.jsonl` if
8
+ * present and report the most-recent event's age + total line count.
9
+ * Inside the REPL we pass an explicit `sessionId` so the probe
10
+ * surfaces the live state without re-reading disk.
11
+ *
12
+ * Absence of `.pugi/events.jsonl` is `skipped`, not an error — the
13
+ * operator may simply be running `pugi doctor` in a workspace that
14
+ * has not yet seen a dispatch.
15
+ */
16
+ export function probeSession(ctx, fs, deps) {
17
+ if (deps.liveSessionId) {
18
+ return {
19
+ name: 'SESSION',
20
+ status: 'ok',
21
+ detail: `session=${deps.liveSessionId} (live, REPL active)`,
22
+ };
23
+ }
24
+ const eventsPath = `${ctx.cwd}/.pugi/events.jsonl`;
25
+ if (!fs.existsSync(eventsPath)) {
26
+ return {
27
+ name: 'SESSION',
28
+ status: 'skipped',
29
+ detail: 'No prior dispatch logged in this workspace',
30
+ };
31
+ }
32
+ let stats;
33
+ try {
34
+ stats = fs.statSync(eventsPath);
35
+ }
36
+ catch (error) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ return {
39
+ name: 'SESSION',
40
+ status: 'warn',
41
+ detail: `.pugi/events.jsonl present but unreadable`,
42
+ remediation: `Inspect: ${message}`,
43
+ };
44
+ }
45
+ const ageMs = Math.max(0, deps.now() - stats.mtimeMs);
46
+ const ageLabel = formatAge(ageMs);
47
+ // Counting lines is cheap on a small NDJSON file; a "huge" Pugi
48
+ // session is single-digit MB. We avoid loading binary blobs by
49
+ // simply walking the buffer count.
50
+ let lineCount = 0;
51
+ try {
52
+ const raw = fs.readFileSync(eventsPath, 'utf8');
53
+ lineCount = raw.split('\n').filter((line) => line.trim().length > 0).length;
54
+ }
55
+ catch {
56
+ lineCount = -1;
57
+ }
58
+ const linePart = lineCount >= 0 ? `, ${lineCount} event(s)` : '';
59
+ return {
60
+ name: 'SESSION',
61
+ status: 'ok',
62
+ detail: `last event ${ageLabel} ago${linePart}`,
63
+ };
64
+ }
65
+ export function formatAge(ms) {
66
+ if (ms < 60_000)
67
+ return `${Math.round(ms / 1000)}s`;
68
+ if (ms < 3_600_000)
69
+ return `${Math.round(ms / 60_000)}m`;
70
+ if (ms < 86_400_000)
71
+ return `${Math.round(ms / 3_600_000)}h`;
72
+ return `${Math.round(ms / 86_400_000)}d`;
73
+ }
74
+ //# sourceMappingURL=session.js.map