@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,201 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `pugi jobs --watch` Ink TUI — live progress .
4
+ *
5
+ * Tails `~/.pugi/agent-progress/*.json` via chokidar and re-renders a
6
+ * grid of <AgentProgressCard> components. Mirrors the the upstream tool
7
+ * `/compact` layout pattern — one card per agent, sorted by status
8
+ * (running → completed → failed) then by lastUpdate descending so the
9
+ * freshest activity floats к the top.
10
+ *
11
+ * Lifecycle:
12
+ * 1. Resolve dir via resolveProgressDir() (env-aware).
13
+ * 2. Initial scan with readdirSync — populate the map.
14
+ * 3. chokidar.watch(dir, { ignoreInitial: true }) for add/change/unlink.
15
+ * 4. setInterval(1000) tick re-renders to keep elapsed labels live
16
+ * WITHOUT re-reading files.
17
+ * 5. SIGINT (Ctrl+C) triggers `app.unmount()` + process.exit(0).
18
+ *
19
+ * Layout: single column under 100 cols, two columns at 100+ cols
20
+ * (Ink's flexbox computes the responsive split automatically — we just
21
+ * set `flexDirection="row" flexWrap="wrap"` and bound each card к 50%
22
+ * width when stdout is wide enough).
23
+ *
24
+ * The TUI is opt-in (operator runs `pugi jobs --watch`); the bare
25
+ * `pugi jobs` continues to print the legacy job-registry table.
26
+ */
27
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
28
+ import { join } from 'node:path';
29
+ import chokidar from 'chokidar';
30
+ import { useEffect, useMemo, useState } from 'react';
31
+ import { Box, Text, render, useApp, useInput, useStdout } from 'ink';
32
+ import { resolveProgressDir } from '../core/agent-progress/writer.js';
33
+ import { runCleanup } from '../core/agent-progress/cleanup.js';
34
+ import { validateAgentProgress, } from '../core/agent-progress/schema.js';
35
+ import { AgentProgressCard } from '../tui/agent-progress-card.js';
36
+ /** Cleanup pass cadence inside the watcher. Cheap (readdir on a small
37
+ * dir); 60s is the operator-tested sweet spot — fast enough to feel
38
+ * live, slow enough к never burn CPU. */
39
+ const CLEANUP_INTERVAL_MS = 60_000;
40
+ const STATUS_RANK = {
41
+ running: 0,
42
+ completed: 1,
43
+ failed: 2,
44
+ };
45
+ /**
46
+ * Read + validate one progress file. Returns undefined on any failure
47
+ * — the watcher prefers к skip a malformed entry rather than crash.
48
+ * Exported for the spec.
49
+ */
50
+ export function readProgressFile(path) {
51
+ if (!existsSync(path))
52
+ return undefined;
53
+ try {
54
+ const body = readFileSync(path, 'utf8');
55
+ const parsed = JSON.parse(body);
56
+ const result = validateAgentProgress(parsed);
57
+ return result.ok ? result.value : undefined;
58
+ }
59
+ catch {
60
+ return undefined;
61
+ }
62
+ }
63
+ /**
64
+ * Sort progress entries for stable, operator-friendly display:
65
+ * 1. Running first.
66
+ * 2. Then by lastUpdate (most recent first).
67
+ * 3. Tiebreak on agentId for snapshot stability.
68
+ */
69
+ export function sortProgressEntries(entries) {
70
+ return [...entries].sort((a, b) => {
71
+ const rank = STATUS_RANK[a.status] - STATUS_RANK[b.status];
72
+ if (rank !== 0)
73
+ return rank;
74
+ const tsDiff = Date.parse(b.lastUpdate) - Date.parse(a.lastUpdate);
75
+ if (tsDiff !== 0)
76
+ return tsDiff;
77
+ return a.agentId.localeCompare(b.agentId);
78
+ });
79
+ }
80
+ export function JobsWatch(props) {
81
+ const { dir, nowEpochMs, staticMode = false } = props;
82
+ const resolvedDir = useMemo(() => resolveProgressDir(dir), [dir]);
83
+ const [agents, setAgents] = useState(() => initialScan(resolvedDir));
84
+ const [tick, setTick] = useState(0);
85
+ const { exit } = useApp();
86
+ const { stdout } = useStdout();
87
+ const cols = stdout?.columns ?? 80;
88
+ const twoColumns = cols >= 100;
89
+ useInput((_input, key) => {
90
+ if (key.escape || key.ctrl) {
91
+ // Ink's useInput passes ctrl=true for Ctrl+<x>; we exit on Ctrl+C
92
+ // (key.ctrl alone is too broad — actual key char arrives in `input`).
93
+ // Practically, raw mode passes Ctrl+C as SIGINT to the process so
94
+ // this is a belt-and-suspenders catch.
95
+ if (key.escape)
96
+ exit();
97
+ }
98
+ });
99
+ useEffect(() => {
100
+ if (staticMode)
101
+ return;
102
+ let watcher;
103
+ try {
104
+ watcher = chokidar.watch(resolvedDir, {
105
+ ignoreInitial: true,
106
+ depth: 0,
107
+ // The agent writer uses atomic rename — chokidar reports `add`
108
+ // when the final filename appears, never the *.tmp-* intermediate.
109
+ ignored: /\.tmp-/,
110
+ });
111
+ const onChange = (path) => {
112
+ if (!/\.json$/.test(path))
113
+ return;
114
+ const progress = readProgressFile(path);
115
+ if (!progress)
116
+ return;
117
+ setAgents((prev) => ({ ...prev, [progress.agentId]: progress }));
118
+ };
119
+ const onRemove = (path) => {
120
+ const match = /([^/\\]+)\.json$/.exec(path);
121
+ if (!match)
122
+ return;
123
+ const id = match[1];
124
+ if (!id)
125
+ return;
126
+ setAgents((prev) => {
127
+ if (!(id in prev))
128
+ return prev;
129
+ const next = { ...prev };
130
+ delete next[id];
131
+ return next;
132
+ });
133
+ };
134
+ watcher.on('add', onChange);
135
+ watcher.on('change', onChange);
136
+ watcher.on('unlink', onRemove);
137
+ }
138
+ catch {
139
+ // Watcher failures degrade gracefully — initial scan still rendered.
140
+ }
141
+ const interval = setInterval(() => setTick((t) => t + 1), 1000);
142
+ // Auto-cleanup completed/failed entries older than the TTL. The
143
+ // sweep moves them к `<dir>/archive/` so operators can still
144
+ // forensic; the watcher's chokidar `unlink` handler drops them
145
+ // from the live grid as the rename fires.
146
+ const cleanupInterval = setInterval(() => {
147
+ try {
148
+ runCleanup({ dir: resolvedDir });
149
+ }
150
+ catch {
151
+ // best-effort housekeeping; never crash the TUI
152
+ }
153
+ }, CLEANUP_INTERVAL_MS);
154
+ return () => {
155
+ clearInterval(interval);
156
+ clearInterval(cleanupInterval);
157
+ if (watcher) {
158
+ void watcher.close();
159
+ }
160
+ };
161
+ }, [resolvedDir, staticMode]);
162
+ const sorted = useMemo(() => sortProgressEntries(Object.values(agents)), [agents]);
163
+ const effectiveNow = nowEpochMs ?? Date.now() + tick * 0; // tick triggers re-render only
164
+ if (sorted.length === 0) {
165
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Text, { dimColor: true, children: ["No agents broadcasting progress yet. Watching", ' ', _jsx(Text, { bold: true, children: resolvedDir }), " for `<id>.json` files\u2026"] }), _jsx(Text, { dimColor: true, children: "(Press Esc / Ctrl+C \u043A exit.)" })] }));
166
+ }
167
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, children: "pugi jobs --watch" }), _jsxs(Text, { dimColor: true, children: [' ', "\u00B7 ", sorted.length, " ", sorted.length === 1 ? 'agent' : 'agents', " \u00B7", ' ', resolvedDir] })] }), _jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: sorted.map((progress) => (_jsx(Box, { width: twoColumns ? '50%' : '100%', flexDirection: "column", children: _jsx(AgentProgressCard, { progress: progress, nowEpochMs: effectiveNow }) }, progress.agentId))) }), _jsx(Text, { dimColor: true, children: "(Esc / Ctrl+C \u043A exit.)" })] }));
168
+ }
169
+ function initialScan(dir) {
170
+ const map = {};
171
+ if (!existsSync(dir))
172
+ return map;
173
+ let entries;
174
+ try {
175
+ entries = readdirSync(dir);
176
+ }
177
+ catch {
178
+ return map;
179
+ }
180
+ for (const name of entries) {
181
+ if (!name.endsWith('.json'))
182
+ continue;
183
+ if (/\.tmp-/.test(name))
184
+ continue;
185
+ const progress = readProgressFile(join(dir, name));
186
+ if (progress)
187
+ map[progress.agentId] = progress;
188
+ }
189
+ return map;
190
+ }
191
+ /**
192
+ * Entry-point for `pugi jobs --watch`. Mounts the Ink app and returns
193
+ * a Promise that resolves when the user exits (Ctrl+C / Esc).
194
+ */
195
+ export async function runJobsWatch(options = {}) {
196
+ return new Promise((resolve) => {
197
+ const app = render(_jsx(JobsWatch, { dir: options.dir }), { exitOnCtrlC: true });
198
+ void app.waitUntilExit().then(() => resolve(0));
199
+ });
200
+ }
201
+ //# sourceMappingURL=jobs-watch.js.map
@@ -1,21 +1,22 @@
1
1
  /**
2
- * `pugi jobs` command surface — Sprint α5.9 (ADR-0056 PR-PUGI-CLI-M1-GAP-J).
2
+ * `pugi jobs` command surface — Sprint .
3
3
  *
4
4
  * Subcommands:
5
- * pugi jobs list — table of all tracked jobs (or JSON envelope)
6
- * pugi jobs status <id> — full record + tail of overflow artifact
7
- * pugi jobs tail <id> — stream the overflow artifact
8
- * pugi jobs kill <id> — SIGTERM then SIGKILL after a grace
9
- * pugi jobs kill --all — kill every running job in this session
5
+ * pugi jobs list — table of all tracked jobs (or JSON envelope)
6
+ * pugi jobs status <id> — full record + tail of overflow artifact
7
+ * pugi jobs tail <id> — stream the overflow artifact
8
+ * pugi jobs kill <id> — SIGTERM then SIGKILL after a grace
9
+ * pugi jobs kill --all — kill every running job in this session
10
10
  *
11
11
  * Power-word voice rules (per Pugi brand guide):
12
- * - status names render as "on watch" (running), "shipped" (finished),
13
- * "stood down" (killed), "blocked" (failed), "lost" (abandoned).
14
- * - JSON envelopes keep the machine-friendly enum so consumers do
15
- * not have to map back.
12
+ * - status names render as "on watch" (running), "shipped" (finished),
13
+ * "stood down" (killed), "blocked" (failed), "lost" (abandoned).
14
+ * - JSON envelopes keep the machine-friendly enum so consumers do
15
+ * not have to map back.
16
16
  */
17
17
  import { existsSync, readFileSync } from 'node:fs';
18
18
  import { formatDuration, getJobRegistry, relativeAge, } from '../core/jobs/registry.js';
19
+ import { runJobsWatch } from './jobs-watch.js';
19
20
  const HUMAN_STATUS = {
20
21
  running: 'on watch',
21
22
  finished: 'shipped',
@@ -24,6 +25,19 @@ const HUMAN_STATUS = {
24
25
  abandoned: 'lost',
25
26
  };
26
27
  export async function runJobsCommand(args, flags, io, sessionId) {
28
+ // live progress : `pugi jobs --watch` mounts the live
29
+ // agent-progress TUI. The flag may sit anywhere in the arg list
30
+ // (`pugi jobs --watch` or `pugi jobs watch`) — both forms route к
31
+ // the watcher. JSON mode is incompatible (it would never return); we
32
+ // bail with a helpful error instead of silently swallowing the flag.
33
+ const wantsWatch = args.includes('--watch') || args[0] === 'watch';
34
+ if (wantsWatch) {
35
+ if (flags.json) {
36
+ io.writeError('pugi jobs --watch is interactive; --json is not supported.');
37
+ return 2;
38
+ }
39
+ return runJobsWatch();
40
+ }
27
41
  const sub = args[0] ?? 'list';
28
42
  switch (sub) {
29
43
  case 'list':
@@ -43,11 +57,12 @@ export async function runJobsCommand(args, flags, io, sessionId) {
43
57
  function usage() {
44
58
  return [
45
59
  'Usage:',
46
- ' pugi jobs list [--json] Table of background jobs.',
47
- ' pugi jobs status <id> [--json] Full record + tail of artifact.',
48
- ' pugi jobs tail <id> Stream the captured artifact.',
49
- ' pugi jobs kill <id> [--json] SIGTERM, escalate to SIGKILL.',
50
- ' pugi jobs kill --all [--json] Stand down every running job.',
60
+ ' pugi jobs list [--json] Table of background jobs.',
61
+ ' pugi jobs status <id> [--json] Full record + tail of artifact.',
62
+ ' pugi jobs tail <id> Stream the captured artifact.',
63
+ ' pugi jobs kill <id> [--json] SIGTERM, escalate to SIGKILL.',
64
+ ' pugi jobs kill --all [--json] Stand down every running job.',
65
+ ' pugi jobs --watch Live Ink TUI of agent progress.',
51
66
  ].join('\n');
52
67
  }
53
68
  async function runList(flags, io) {
@@ -91,20 +106,20 @@ async function runStatus(id, flags, io) {
91
106
  }
92
107
  const lines = [
93
108
  `Job ${entry.id}`,
94
- ` PID: ${entry.pid}`,
95
- ` Command: ${entry.command}`,
96
- ` Class: ${entry.bashClass}`,
97
- ` Status: ${HUMAN_STATUS[entry.status]} (${entry.status})`,
98
- ` CWD: ${entry.cwd}`,
99
- ` Started: ${entry.startedAt} (${relativeAge(entry.startedAt)} ago)`,
100
- ` Duration: ${formatDuration(entry.startedAt, entry.finishedAt)}`,
109
+ ` PID: ${entry.pid}`,
110
+ ` Command: ${entry.command}`,
111
+ ` Class: ${entry.bashClass}`,
112
+ ` Status: ${HUMAN_STATUS[entry.status]} (${entry.status})`,
113
+ ` CWD: ${entry.cwd}`,
114
+ ` Started: ${entry.startedAt} (${relativeAge(entry.startedAt)} ago)`,
115
+ ` Duration: ${formatDuration(entry.startedAt, entry.finishedAt)}`,
101
116
  ];
102
117
  if (entry.finishedAt)
103
- lines.push(` Finished: ${entry.finishedAt}`);
118
+ lines.push(` Finished: ${entry.finishedAt}`);
104
119
  if (entry.exitCode !== undefined)
105
- lines.push(` Exit code: ${entry.exitCode}`);
120
+ lines.push(` Exit code: ${entry.exitCode}`);
106
121
  if (entry.outputArtifactRef)
107
- lines.push(` Artifact: ${entry.outputArtifactRef}`);
122
+ lines.push(` Artifact: ${entry.outputArtifactRef}`);
108
123
  if (tail) {
109
124
  lines.push('', '--- output tail ---', tail);
110
125
  }
@@ -159,7 +174,7 @@ async function runKill(args, flags, io, sessionId) {
159
174
  }
160
175
  else {
161
176
  for (const result of results) {
162
- io.write(` ${result.id} ${result.killed ? 'stood down' : 'noop'} (${result.method})\n`);
177
+ io.write(` ${result.id} ${result.killed ? 'stood down' : 'noop'} (${result.method})\n`);
163
178
  }
164
179
  }
165
180
  return 0;
@@ -233,7 +248,7 @@ function renderTable(entries) {
233
248
  return rows
234
249
  .map((row) => row
235
250
  .map((cell, i) => cell.padEnd(widths[i] ?? cell.length))
236
- .join(' ')
251
+ .join(' ')
237
252
  .trimEnd())
238
253
  .join('\n');
239
254
  }
@@ -0,0 +1,210 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text, render } from 'ink';
3
+ import { collectGitContext, countCommitsAheadOfBase, } from '../core/retro/git-collector.js';
4
+ import { ensurePugiGitIgnore } from '../core/pugi-gitignore.js';
5
+ import { computeMetrics } from '../core/retro/metrics.js';
6
+ import { persistRetro } from '../core/retro/narrative.js';
7
+ import { collectPlaneSlice, postRetroToPlane, resolvePlaneConfig, } from '../core/retro/plane-collector.js';
8
+ import { enrichLinks } from '../core/retro/pr-issue-link.js';
9
+ import { computeHealthCard } from '../core/retro/health-card.js';
10
+ /** Parse `7d` | `14d` | `30d` | `24h` into a duration in days
11
+ * (fractional for sub-day windows). Defaults to 7 days when omitted.
12
+ */
13
+ function parseDurationToken(token) {
14
+ if (!token)
15
+ return undefined;
16
+ const match = /^(\d+)(h|d)$/.exec(token);
17
+ if (!match)
18
+ return undefined;
19
+ const value = Number.parseInt(match[1] ?? '0', 10);
20
+ const unit = match[2];
21
+ if (!Number.isFinite(value) || value <= 0)
22
+ return undefined;
23
+ const days = unit === 'h' ? value / 24 : value;
24
+ return { days, label: token };
25
+ }
26
+ function buildWindow(durationDays, label, now) {
27
+ const until = now;
28
+ const sinceMs = until.getTime() - durationDays * 24 * 60 * 60 * 1000;
29
+ const since = new Date(sinceMs);
30
+ // Midnight-align the lower bound to keep `--since` deterministic per day.
31
+ since.setHours(0, 0, 0, 0);
32
+ return { since, until, label, days: Math.max(1, Math.round(durationDays)) };
33
+ }
34
+ function buildPriorWindow(current) {
35
+ const until = new Date(current.since.getTime());
36
+ const sinceMs = until.getTime() - current.days * 24 * 60 * 60 * 1000;
37
+ const since = new Date(sinceMs);
38
+ since.setHours(0, 0, 0, 0);
39
+ return { since, until, label: `prior ${current.label}`, days: current.days };
40
+ }
41
+ function parseRetroArgs(rawArgs, now) {
42
+ const args = [...rawArgs];
43
+ const postPlane = args.includes('--post-plane');
44
+ const enrichPlane = args.includes('--plane') || postPlane;
45
+ const positional = args.filter((a) => !a.startsWith('-'));
46
+ let compare = false;
47
+ let durationToken;
48
+ if (positional[0] === 'compare') {
49
+ compare = true;
50
+ durationToken = positional[1];
51
+ }
52
+ else {
53
+ durationToken = positional[0];
54
+ }
55
+ const parsed = parseDurationToken(durationToken) ?? { days: 7, label: '7d' };
56
+ return {
57
+ window: buildWindow(parsed.days, parsed.label, now),
58
+ compare,
59
+ enrichPlane,
60
+ postPlane,
61
+ };
62
+ }
63
+ function SummaryCard(props) {
64
+ const { persisted, metrics, plane, planePostUrl } = props;
65
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderRight: false, borderTop: false, borderBottom: false, paddingLeft: 1, children: [_jsxs(Text, { bold: true, children: ["pugi retro \u00B7 ", metrics.window.label] }), _jsxs(Text, { dimColor: true, children: ["Branch ", metrics.branch.current, " over ", metrics.branch.base] }), _jsxs(Text, { children: [metrics.commits.total, " commits \u00B7 +", metrics.loc.insertions, " / -", metrics.loc.deletions, " LOC \u00B7 ", metrics.activeDays, " active days"] }), _jsxs(Text, { children: ["Focus ", metrics.focus.score, "% on ", metrics.focus.topDir ?? 'n/a', " \u00B7 Streak ", metrics.streak.personalDays, "d personal / ", metrics.streak.teamDays, "d team"] }), metrics.shipOfTheWeek ? (_jsxs(Text, { children: ["Ship of the week: ", metrics.shipOfTheWeek.subject.slice(0, 60)] })) : null, plane ? (_jsxs(Text, { children: ["Plane: closed ", plane.closedIssues.length, " \u00B7 created ", plane.createdIssues.length, " \u00B7 oversized modules ", plane.oversizedModules.length] })) : null, _jsxs(Text, { dimColor: true, children: ["Markdown: ", persisted.markdownPath] }), _jsxs(Text, { dimColor: true, children: ["JSON: ", persisted.jsonPath] }), planePostUrl ? _jsxs(Text, { children: ["Posted to Plane: ", planePostUrl] }) : null] }));
66
+ }
67
+ function renderSummary(props) {
68
+ const app = render(_jsx(SummaryCard, { ...props }));
69
+ app.unmount();
70
+ }
71
+ export async function runRetroCommand(ctx) {
72
+ const now = ctx.now ?? new Date();
73
+ const parsed = parseRetroArgs(ctx.args, now);
74
+ const gitCtx = await collectGitContext({ cwd: ctx.cwd, window: parsed.window });
75
+ if (!gitCtx.hasGit) {
76
+ const msg = 'pugi retro: not a git workspace - initialise git or cd into one.';
77
+ if (ctx.flags.json) {
78
+ ctx.io.write(`${JSON.stringify({ ok: false, error: 'no_git_workspace' })}\n`);
79
+ }
80
+ else {
81
+ ctx.io.writeError(msg);
82
+ }
83
+ return 2;
84
+ }
85
+ // Triple-review P1.2 (): before we write anything
86
+ // under `.pugi/retros/`, guarantee `.gitignore` covers `.pugi/`. Without
87
+ // this, the first customer run of `pugi retro` in a fresh repo would
88
+ // leave retros (and any future `.pugi/settings.json` secret store)
89
+ // tracked by git on the next `git add -A`. Idempotent.
90
+ //
91
+ // Round 2 P1 (2026-06-04): surface failure к stderr — silent catch
92
+ // defeats the gate's purpose. If `.gitignore` is read-only or perms
93
+ // refuse, the operator must know retros may be tracked by git.
94
+ const gitIgnoreCreated = [];
95
+ const gitIgnoreSkipped = [];
96
+ try {
97
+ ensurePugiGitIgnore(ctx.cwd, gitIgnoreCreated, gitIgnoreSkipped);
98
+ }
99
+ catch (err) {
100
+ const reason = err instanceof Error ? err.message : String(err);
101
+ ctx.io.writeError(`pugi retro: could not update .gitignore (${reason}). ` +
102
+ `Manually add ".pugi/" to .gitignore so retros are not tracked.`);
103
+ }
104
+ const toBaseHeadCount = await countCommitsAheadOfBase(ctx.cwd, gitCtx.baseBranch, parsed.window.since);
105
+ const metrics = computeMetrics({
106
+ window: parsed.window,
107
+ currentBranch: gitCtx.currentBranch,
108
+ baseBranch: gitCtx.baseBranch,
109
+ toBaseHeadCount,
110
+ currentUserName: gitCtx.userName,
111
+ currentUserEmail: gitCtx.userEmail,
112
+ commits: gitCtx.commits,
113
+ });
114
+ let compare;
115
+ if (parsed.compare) {
116
+ const priorWindow = buildPriorWindow(parsed.window);
117
+ const priorCtx = await collectGitContext({ cwd: ctx.cwd, window: priorWindow });
118
+ const priorAhead = await countCommitsAheadOfBase(ctx.cwd, gitCtx.baseBranch, priorWindow.since);
119
+ const priorMetrics = computeMetrics({
120
+ window: priorWindow,
121
+ currentBranch: gitCtx.currentBranch,
122
+ baseBranch: gitCtx.baseBranch,
123
+ toBaseHeadCount: priorAhead,
124
+ currentUserName: gitCtx.userName,
125
+ currentUserEmail: gitCtx.userEmail,
126
+ commits: priorCtx.commits,
127
+ });
128
+ compare = { current: metrics, prior: priorMetrics };
129
+ }
130
+ let plane;
131
+ let planeUnavailableReason;
132
+ if (parsed.enrichPlane) {
133
+ const cfgResult = resolvePlaneConfig(ctx.cwd);
134
+ if (!cfgResult.ok) {
135
+ planeUnavailableReason = cfgResult.reason;
136
+ }
137
+ else {
138
+ try {
139
+ const slice = await collectPlaneSlice({
140
+ config: cfgResult.config,
141
+ since: parsed.window.since,
142
+ });
143
+ const links = enrichLinks(gitCtx.commits, slice.closedIssues.concat(slice.createdIssues));
144
+ const health = computeHealthCard(slice.modules);
145
+ plane = {
146
+ ...slice,
147
+ prToIssueLinks: links,
148
+ oversizedModules: health.oversized,
149
+ };
150
+ }
151
+ catch (err) {
152
+ planeUnavailableReason = err instanceof Error ? err.message : String(err);
153
+ }
154
+ }
155
+ }
156
+ const persisted = persistRetro({
157
+ root: ctx.cwd,
158
+ metrics,
159
+ plane,
160
+ compare,
161
+ now,
162
+ });
163
+ let planePostUrl;
164
+ if (parsed.postPlane) {
165
+ if (!plane) {
166
+ ctx.io.writeError(`pugi retro --post-plane: Plane unavailable (${planeUnavailableReason ?? 'unknown'}).`);
167
+ }
168
+ else {
169
+ const cfgResult = resolvePlaneConfig(ctx.cwd);
170
+ if (cfgResult.ok) {
171
+ try {
172
+ const result = await postRetroToPlane({
173
+ config: cfgResult.config,
174
+ markdown: persisted.markdown,
175
+ sequence: persisted.sequence,
176
+ dateLabel: persisted.dateLabel,
177
+ });
178
+ planePostUrl = result.url;
179
+ if (result.alreadyExists) {
180
+ ctx.io.write(`pugi retro: already exists at ${result.url}\n`);
181
+ }
182
+ }
183
+ catch (err) {
184
+ const msg = err instanceof Error ? err.message : String(err);
185
+ ctx.io.writeError(`pugi retro --post-plane failed: ${msg}`);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ if (ctx.flags.json) {
191
+ ctx.io.write(`${JSON.stringify({
192
+ ok: true,
193
+ markdownPath: persisted.markdownPath,
194
+ jsonPath: persisted.jsonPath,
195
+ sequence: persisted.sequence,
196
+ metrics,
197
+ plane: plane ?? null,
198
+ planePostUrl: planePostUrl ?? null,
199
+ planeUnavailableReason: planeUnavailableReason ?? null,
200
+ }, null, 2)}\n`);
201
+ }
202
+ else {
203
+ renderSummary({ persisted, metrics, plane, planePostUrl });
204
+ if (planeUnavailableReason && !plane) {
205
+ ctx.io.writeError(`pugi retro: Plane integration unavailable (${planeUnavailableReason}).`);
206
+ }
207
+ }
208
+ return 0;
209
+ }
210
+ //# sourceMappingURL=retro.js.map
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `pugi smoke` — runs the bundled scenario corpus through the headless
3
+ * harness and reports pass/fail per scenario (
4
+ *).
5
+ *
6
+ * The CLI surface lives here rather than in `runtime/cli.ts` so the
7
+ * dispatch surface stays focused on argv routing. This module owns:
8
+ *
9
+ * - Resolving the scenarios directory (default
10
+ * `<cli-root>/test/scenarios/` when bundled, configurable via
11
+ * `--scenarios-dir`).
12
+ * - Selecting the `pugi` binary the headless driver should spawn.
13
+ * Local development: `node <cli-root>/bin/run.js`. CI / published
14
+ * usage: the `pugi` on PATH.
15
+ * - Forwarding the orchestrator output to the unified
16
+ * `writeOutput` writer so `--json` mode works without a second
17
+ * code path.
18
+ *
19
+ * Phase 1 deliberately ships ONE flag (`--filter`) and one option
20
+ * (`--scenarios-dir`); the rest of the surface comes in Phase 2 once
21
+ * the engine path is wired and we know which scenarios actually need
22
+ * per-run plumbing (timeouts, fixture credentials, hermetic stubs).
23
+ */
24
+ import { existsSync } from 'node:fs';
25
+ import { dirname, resolve } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import { runSmoke, renderReportText, } from '../core/smoke/orchestrator.js';
28
+ import { runHeadlessScenario } from '../core/smoke/headless-driver.js';
29
+ /**
30
+ * Entry point invoked by `runtime/cli.ts::dispatchSmoke`. Returns the
31
+ * desired process exit code so the dispatcher can set
32
+ * `process.exitCode` without a second round-trip.
33
+ */
34
+ export async function runSmokeCommand(ctx) {
35
+ // Parse the (small) command-local argv. We only honor flags this
36
+ // command actually consumes; unknown args produce a usage error so
37
+ // typos surface immediately.
38
+ let scenariosDirOverride = ctx.scenariosDir ?? undefined;
39
+ let filter = ctx.filter ?? '';
40
+ for (let i = 0; i < ctx.args.length; i += 1) {
41
+ const arg = ctx.args[i] ?? '';
42
+ if (arg === '--filter') {
43
+ const next = ctx.args[i + 1];
44
+ if (!next || next.startsWith('--')) {
45
+ ctx.writeOutput({ ok: false, error: '--filter requires a pattern' }, 'pugi smoke: --filter requires a pattern (substring or *-glob)');
46
+ return 2;
47
+ }
48
+ filter = next;
49
+ i += 1;
50
+ }
51
+ else if (arg.startsWith('--filter=')) {
52
+ filter = arg.slice('--filter='.length);
53
+ }
54
+ else if (arg === '--scenarios-dir') {
55
+ const next = ctx.args[i + 1];
56
+ if (!next || next.startsWith('--')) {
57
+ ctx.writeOutput({ ok: false, error: '--scenarios-dir requires a path' }, 'pugi smoke: --scenarios-dir requires a path');
58
+ return 2;
59
+ }
60
+ scenariosDirOverride = next;
61
+ i += 1;
62
+ }
63
+ else if (arg.startsWith('--scenarios-dir=')) {
64
+ scenariosDirOverride = arg.slice('--scenarios-dir='.length);
65
+ }
66
+ else if (arg === '--help' || arg === '-h') {
67
+ ctx.writeOutput({
68
+ ok: true,
69
+ usage: 'pugi smoke [--filter <pattern>] [--scenarios-dir <path>]',
70
+ }, [
71
+ 'pugi smoke — run the bundled scenario corpus headlessly.',
72
+ '',
73
+ 'Flags:',
74
+ ' --filter <pattern> Run a subset (substring or *-glob match on scenario id).',
75
+ ' --scenarios-dir <path> Override the scenarios directory (default: bundled corpus).',
76
+ ].join('\n'));
77
+ return 0;
78
+ }
79
+ else {
80
+ ctx.writeOutput({ ok: false, error: `unknown arg: ${arg}` }, `pugi smoke: unknown arg ${arg}`);
81
+ return 2;
82
+ }
83
+ }
84
+ const scenariosDir = scenariosDirOverride ?? resolveBundledScenariosDir();
85
+ if (!existsSync(scenariosDir)) {
86
+ ctx.writeOutput({ ok: false, error: `scenarios dir not found: ${scenariosDir}` }, `pugi smoke: scenarios dir not found: ${scenariosDir}`);
87
+ return 2;
88
+ }
89
+ const pugiBin = ctx.pugiBin ?? process.env.PUGI_SMOKE_BIN ?? 'pugi';
90
+ const log = ctx.log ?? ((line) => process.stderr.write(`${line}\n`));
91
+ const report = await runSmoke({
92
+ scenariosDir,
93
+ filter,
94
+ executor: (scenario) => runHeadlessScenario(scenario, { pugiBin }),
95
+ log,
96
+ });
97
+ if (ctx.json) {
98
+ ctx.writeOutput(report, renderReportText(report));
99
+ }
100
+ else {
101
+ process.stdout.write(`${renderReportText(report)}\n`);
102
+ }
103
+ return report.exitCode;
104
+ }
105
+ /**
106
+ * Resolve the scenarios directory that ships alongside the CLI.
107
+ *
108
+ * Both the dev source layout (`<cli-root>/src/commands/smoke.ts`) and
109
+ * the built output layout (`<cli-root>/dist/commands/smoke.js`) land
110
+ * on the same `../../test/scenarios` path relative to this file.
111
+ * The bundled `npm i -g @pugi/cli` install replicates that structure
112
+ * by shipping `test/scenarios` (glob `**\/*.scenario.txt`) via the
113
+ * `package.json` `files` field — so the single resolved path works in
114
+ * dev, in `tsx` runs, and in published installs without a config knob.
115
+ *
116
+ * If a future restructure ever bundles scenarios at
117
+ * `<cli-root>/dist/scenarios/` instead, add that to the fallback chain
118
+ * here AND mirror the path in `package.json` `files`. Until then we
119
+ * keep the resolver intentionally one-line.
120
+ */
121
+ export function resolveBundledScenariosDir() {
122
+ const here = dirname(fileURLToPath(import.meta.url));
123
+ // Works for both src/commands/smoke.ts and dist/commands/smoke.js —
124
+ // both live two directories below the cli root.
125
+ const bundled = resolve(here, '..', '..', 'test', 'scenarios');
126
+ if (existsSync(bundled))
127
+ return bundled;
128
+ // Last resort: return the expected path so the orchestrator surfaces
129
+ // a clean "scenarios dir not found" diagnostic at the call site
130
+ // rather than the resolver swallowing it silently.
131
+ return bundled;
132
+ }
133
+ //# sourceMappingURL=smoke.js.map