@pugi/cli 0.1.0-beta.9 → 0.1.0-beta.91

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 (411) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +3 -3
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +13 -13
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +333 -7
  94. package/dist/core/edits/format-detector.js +260 -0
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +5 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +29 -29
  108. package/dist/core/engine/anvil-client.js +214 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +129 -19
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1792 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +46 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +216 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/hooks/worktree-events.js +158 -0
  141. package/dist/core/image/renderer.js +71 -0
  142. package/dist/core/init/detector.js +582 -0
  143. package/dist/core/init/template-renderer.js +242 -0
  144. package/dist/core/jobs/registry.js +18 -18
  145. package/dist/core/ledger/results-tsv.js +142 -0
  146. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  147. package/dist/core/lsp/cache.js +105 -0
  148. package/dist/core/lsp/client.js +551 -41
  149. package/dist/core/lsp/language-detect.js +66 -0
  150. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  151. package/dist/core/lsp/server-detect.js +173 -0
  152. package/dist/core/lsp/symbol-cache.js +162 -0
  153. package/dist/core/lsp/symbol-tools.js +664 -0
  154. package/dist/core/mcp/client.js +97 -28
  155. package/dist/core/mcp/http-server.js +553 -0
  156. package/dist/core/mcp/orchestrator-tools.js +662 -0
  157. package/dist/core/mcp/permission.js +190 -0
  158. package/dist/core/mcp/registry.js +39 -17
  159. package/dist/core/mcp/server-tools.js +219 -0
  160. package/dist/core/mcp/server.js +397 -0
  161. package/dist/core/mcp/trust.js +10 -10
  162. package/dist/core/memory/dual-write.js +416 -0
  163. package/dist/core/memory/passive-extract.js +130 -0
  164. package/dist/core/memory/phase1-kinds.js +20 -0
  165. package/dist/core/memory/secret-scanner.js +304 -0
  166. package/dist/core/memory-sync/queue.js +170 -0
  167. package/dist/core/metrics/extract.js +113 -0
  168. package/dist/core/modes/roo-modes.js +68 -0
  169. package/dist/core/onboarding/ensure-initialized.js +133 -0
  170. package/dist/core/onboarding/marker.js +111 -0
  171. package/dist/core/onboarding/telemetry-state.js +108 -0
  172. package/dist/core/output-style/presets.js +176 -0
  173. package/dist/core/output-style/state.js +185 -0
  174. package/dist/core/path-security.js +287 -5
  175. package/dist/core/permission.js +82 -22
  176. package/dist/core/permissions/auto-classifier.js +124 -0
  177. package/dist/core/permissions/bash-parser.js +371 -0
  178. package/dist/core/permissions/circuit-breaker.js +83 -0
  179. package/dist/core/permissions/constrained-edit.js +91 -0
  180. package/dist/core/permissions/gate.js +278 -0
  181. package/dist/core/permissions/index.js +20 -0
  182. package/dist/core/permissions/mode.js +174 -0
  183. package/dist/core/permissions/network-egress.js +137 -0
  184. package/dist/core/permissions/state.js +241 -0
  185. package/dist/core/permissions/tool-class.js +93 -0
  186. package/dist/core/plan-mode/ui-state.js +51 -0
  187. package/dist/core/plans/plan-artifact.js +721 -0
  188. package/dist/core/policy-limits/etag-store.js +122 -0
  189. package/dist/core/prd-check/parser.js +215 -0
  190. package/dist/core/prd-check/reporter.js +127 -0
  191. package/dist/core/prd-check/session-review.js +557 -0
  192. package/dist/core/prd-check/verifiers.js +223 -0
  193. package/dist/core/prompt-cache/client-cache.js +99 -0
  194. package/dist/core/prompts/assembly.js +29 -0
  195. package/dist/core/prompts/registry.js +364 -0
  196. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  197. package/dist/core/pugi-md/context-injector.js +76 -0
  198. package/dist/core/pugi-md/walk-up.js +207 -0
  199. package/dist/core/python/uv-installer.js +270 -0
  200. package/dist/core/python/uv-resolver.js +83 -0
  201. package/dist/core/rate-limit/narrator.js +146 -0
  202. package/dist/core/recipes/cli-types.js +20 -0
  203. package/dist/core/recipes/loader.js +103 -0
  204. package/dist/core/recipes/runner.js +345 -0
  205. package/dist/core/recipes/schema.js +587 -0
  206. package/dist/core/release-notes/parser.js +241 -0
  207. package/dist/core/release-notes/state.js +116 -0
  208. package/dist/core/repl/ask.js +37 -37
  209. package/dist/core/repl/cancellation.js +26 -26
  210. package/dist/core/repl/cap-warning.js +4 -4
  211. package/dist/core/repl/clipboard-read.js +11 -11
  212. package/dist/core/repl/dispatch-fsm.js +12 -12
  213. package/dist/core/repl/history-search.js +15 -15
  214. package/dist/core/repl/history.js +28 -18
  215. package/dist/core/repl/kill-ring.js +5 -5
  216. package/dist/core/repl/model-pricing.js +135 -0
  217. package/dist/core/repl/privacy-banner.js +22 -22
  218. package/dist/core/repl/session.js +2148 -217
  219. package/dist/core/repl/slash-commands.js +501 -41
  220. package/dist/core/repl/store/index.js +1 -1
  221. package/dist/core/repl/store/jsonl-log.js +22 -22
  222. package/dist/core/repl/store/lockfile.js +10 -10
  223. package/dist/core/repl/store/session-store.js +136 -107
  224. package/dist/core/repl/store/types.js +15 -15
  225. package/dist/core/repl/store/uuid-v7.js +12 -12
  226. package/dist/core/repl/workspace-context.js +43 -21
  227. package/dist/core/repo-map/build.js +125 -0
  228. package/dist/core/repo-map/cache.js +185 -0
  229. package/dist/core/repo-map/extractor.js +254 -0
  230. package/dist/core/repo-map/formatter.js +145 -0
  231. package/dist/core/repo-map/page-rank.js +105 -0
  232. package/dist/core/repo-map/scanner.js +211 -0
  233. package/dist/core/retry-budget/budget.js +284 -0
  234. package/dist/core/retry-budget/index.js +5 -0
  235. package/dist/core/retry-budget/retry-cap.js +74 -0
  236. package/dist/core/routing/lead-worker.js +43 -0
  237. package/dist/core/routing/pre-flight-estimator.js +108 -0
  238. package/dist/core/runs/run-tree.js +103 -0
  239. package/dist/core/security/injection-scanner.js +367 -0
  240. package/dist/core/security/output-filter.js +418 -0
  241. package/dist/core/session/env-file.js +105 -0
  242. package/dist/core/session/section-budgets.js +140 -0
  243. package/dist/core/session.js +92 -0
  244. package/dist/core/settings.js +324 -5
  245. package/dist/core/share/formatter.js +271 -0
  246. package/dist/core/share/redactor.js +221 -0
  247. package/dist/core/share/uploader.js +267 -0
  248. package/dist/core/skills/defaults.js +30 -30
  249. package/dist/core/skills/loader.js +22 -22
  250. package/dist/core/skills/sources.js +27 -27
  251. package/dist/core/smoke/headless-driver.js +174 -0
  252. package/dist/core/smoke/orchestrator.js +194 -0
  253. package/dist/core/smoke/runner.js +238 -0
  254. package/dist/core/smoke/scenario-parser.js +316 -0
  255. package/dist/core/statusline.js +99 -0
  256. package/dist/core/subagents/dispatcher-real.js +600 -0
  257. package/dist/core/subagents/dispatcher.js +132 -43
  258. package/dist/core/subagents/index.js +19 -6
  259. package/dist/core/subagents/isolation-matrix.js +213 -0
  260. package/dist/core/subagents/spawn.js +19 -4
  261. package/dist/core/telemetry/emitter.js +229 -0
  262. package/dist/core/telemetry/queue.js +251 -0
  263. package/dist/core/theme/context.js +91 -0
  264. package/dist/core/theme/presets.js +228 -0
  265. package/dist/core/theme/state.js +181 -0
  266. package/dist/core/todos/invariant.js +10 -0
  267. package/dist/core/todos/state.js +177 -0
  268. package/dist/core/tool-schema/compressor.js +89 -0
  269. package/dist/core/transport/version-interceptor.js +166 -0
  270. package/dist/core/trust.js +2 -2
  271. package/dist/core/tui/thinking-block.js +64 -0
  272. package/dist/core/vim/keymap.js +288 -0
  273. package/dist/core/vim/state.js +92 -0
  274. package/dist/core/watch-markers/marker-watcher.js +133 -0
  275. package/dist/core/worktree/include-parser.js +249 -0
  276. package/dist/core/worktree-manager/cleanup.js +123 -0
  277. package/dist/core/worktree-manager/manager.js +303 -0
  278. package/dist/index.js +36 -0
  279. package/dist/runtime/bootstrap.js +190 -0
  280. package/dist/runtime/cli.js +4185 -549
  281. package/dist/runtime/commands/agents.js +31 -31
  282. package/dist/runtime/commands/budget.js +5 -5
  283. package/dist/runtime/commands/cancel.js +231 -0
  284. package/dist/runtime/commands/chain.js +489 -0
  285. package/dist/runtime/commands/codegraph-status.js +227 -0
  286. package/dist/runtime/commands/compact.js +297 -0
  287. package/dist/runtime/commands/config.js +73 -39
  288. package/dist/runtime/commands/cost.js +199 -0
  289. package/dist/runtime/commands/delegate.js +27 -4
  290. package/dist/runtime/commands/dispatch.js +126 -0
  291. package/dist/runtime/commands/doctor.js +579 -0
  292. package/dist/runtime/commands/feedback.js +184 -0
  293. package/dist/runtime/commands/hooks.js +187 -0
  294. package/dist/runtime/commands/init.js +254 -0
  295. package/dist/runtime/commands/lsp.js +200 -38
  296. package/dist/runtime/commands/mcp.js +879 -0
  297. package/dist/runtime/commands/memory.js +582 -0
  298. package/dist/runtime/commands/model.js +237 -0
  299. package/dist/runtime/commands/onboarding.js +275 -0
  300. package/dist/runtime/commands/patch.js +12 -12
  301. package/dist/runtime/commands/permissions.js +112 -0
  302. package/dist/runtime/commands/plan.js +143 -0
  303. package/dist/runtime/commands/prd-check.js +285 -0
  304. package/dist/runtime/commands/privacy.js +17 -17
  305. package/dist/runtime/commands/recipe.js +325 -0
  306. package/dist/runtime/commands/redo-blob-store.js +92 -0
  307. package/dist/runtime/commands/redo.js +361 -0
  308. package/dist/runtime/commands/release-notes.js +229 -0
  309. package/dist/runtime/commands/repo-map.js +95 -0
  310. package/dist/runtime/commands/report.js +299 -0
  311. package/dist/runtime/commands/resume.js +118 -0
  312. package/dist/runtime/commands/review-consensus.js +68 -53
  313. package/dist/runtime/commands/rewind.js +333 -0
  314. package/dist/runtime/commands/roster.js +14 -14
  315. package/dist/runtime/commands/sessions.js +163 -0
  316. package/dist/runtime/commands/share.js +316 -0
  317. package/dist/runtime/commands/skills.js +31 -31
  318. package/dist/runtime/commands/status.js +186 -0
  319. package/dist/runtime/commands/stickers.js +82 -0
  320. package/dist/runtime/commands/style.js +194 -0
  321. package/dist/runtime/commands/theme.js +196 -0
  322. package/dist/runtime/commands/undo.js +54 -22
  323. package/dist/runtime/commands/update.js +289 -0
  324. package/dist/runtime/commands/vim.js +140 -0
  325. package/dist/runtime/commands/worktree.js +8 -8
  326. package/dist/runtime/commands/worktrees.js +155 -0
  327. package/dist/runtime/headless-repl.js +195 -0
  328. package/dist/runtime/headless.js +543 -0
  329. package/dist/runtime/load-hooks-or-exit.js +71 -0
  330. package/dist/runtime/plan-decompose.js +22 -22
  331. package/dist/runtime/sigint-guard.js +272 -0
  332. package/dist/runtime/update-check.js +28 -28
  333. package/dist/runtime/version.js +65 -0
  334. package/dist/runtime/worktree-bootstrap.js +579 -0
  335. package/dist/skills/bundled/batch.js +617 -0
  336. package/dist/skills/bundled/index.js +45 -0
  337. package/dist/skills/bundled/loop.js +358 -0
  338. package/dist/skills/bundled/remember.js +383 -0
  339. package/dist/skills/bundled/simplify.js +289 -0
  340. package/dist/skills/bundled/skillify.js +373 -0
  341. package/dist/skills/bundled/stuck.js +558 -0
  342. package/dist/skills/bundled/verify.js +439 -0
  343. package/dist/testing/vcr.js +486 -0
  344. package/dist/tools/agent-tool.js +229 -0
  345. package/dist/tools/apply-patch.js +89 -28
  346. package/dist/tools/ask-user-question.js +337 -0
  347. package/dist/tools/ask-user.js +115 -0
  348. package/dist/tools/bash.js +624 -46
  349. package/dist/tools/brief.js +224 -0
  350. package/dist/tools/cron.js +433 -0
  351. package/dist/tools/enter-worktree.js +250 -0
  352. package/dist/tools/exit-worktree.js +147 -0
  353. package/dist/tools/file-tools.js +161 -44
  354. package/dist/tools/lsp-tools.js +377 -1
  355. package/dist/tools/mcp-tool.js +260 -0
  356. package/dist/tools/multi-edit.js +361 -0
  357. package/dist/tools/powershell.js +268 -0
  358. package/dist/tools/registry.js +99 -4
  359. package/dist/tools/skill-tool.js +96 -0
  360. package/dist/tools/sleep.js +99 -0
  361. package/dist/tools/synthetic-output.js +133 -0
  362. package/dist/tools/tasks.js +208 -0
  363. package/dist/tools/todo-write.js +184 -0
  364. package/dist/tools/verify-plan-execution.js +295 -0
  365. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  366. package/dist/tools/web-fetch.js +195 -10
  367. package/dist/tools/web-search.js +458 -0
  368. package/dist/tui/agent-progress-card.js +111 -0
  369. package/dist/tui/agent-tree.js +11 -1
  370. package/dist/tui/ask-modal.js +14 -14
  371. package/dist/tui/ask-user-question-chips.js +315 -0
  372. package/dist/tui/ask-user-question-prompt.js +203 -0
  373. package/dist/tui/compact-banner.js +81 -0
  374. package/dist/tui/conversation-pane.js +85 -11
  375. package/dist/tui/cost-table.js +111 -0
  376. package/dist/tui/device-flow.js +2 -2
  377. package/dist/tui/doctor-table.js +46 -0
  378. package/dist/tui/feedback-prompt.js +156 -0
  379. package/dist/tui/input-box.js +247 -32
  380. package/dist/tui/login-picker.js +3 -3
  381. package/dist/tui/markdown-render.js +6 -6
  382. package/dist/tui/multi-file-diff-approval.js +375 -0
  383. package/dist/tui/onboarding-wizard.js +240 -0
  384. package/dist/tui/permissions-picker.js +86 -0
  385. package/dist/tui/render.js +36 -1
  386. package/dist/tui/repl-render.js +176 -25
  387. package/dist/tui/repl-splash-art.js +16 -16
  388. package/dist/tui/repl-splash-mascot.js +48 -24
  389. package/dist/tui/repl-splash.js +22 -22
  390. package/dist/tui/repl.js +125 -45
  391. package/dist/tui/slash-palette.js +6 -6
  392. package/dist/tui/splash.js +2 -2
  393. package/dist/tui/status-bar.js +109 -31
  394. package/dist/tui/status-table.js +7 -0
  395. package/dist/tui/stickers-art.js +136 -0
  396. package/dist/tui/style-table.js +28 -0
  397. package/dist/tui/theme-table.js +29 -0
  398. package/dist/tui/thinking-spinner.js +123 -0
  399. package/dist/tui/tool-stream-pane.js +53 -4
  400. package/dist/tui/update-banner.js +27 -2
  401. package/dist/tui/vim-input.js +267 -0
  402. package/dist/tui/welcome-banner.js +107 -0
  403. package/dist/tui/welcome-data.js +293 -0
  404. package/dist/tui/workspace-context.js +2 -2
  405. package/package.json +31 -16
  406. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  407. package/test/scenarios/compact-force.scenario.txt +12 -0
  408. package/test/scenarios/identity.scenario.txt +12 -0
  409. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  410. package/test/scenarios/walkback.scenario.txt +12 -0
  411. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Artifact chain exporter — Pugi .
3
+ *
4
+ * Bundles the seven step artifacts under `.pugi/chains/<id>/` into a
5
+ * single markdown report (default) or a deterministic JSON envelope
6
+ * the operator can feed into a future zip bundler. The exporter is
7
+ * read-only — it never mutates state.
8
+ *
9
+ * Why markdown instead of an actual `.zip`: the CLI ships zero binary
10
+ * dependencies (HARD rule for `@pugi/cli` install footprint — the
11
+ * 4.4MB bundled-zip toolchains would land the install at ~20MB).
12
+ * Operators that want a true zip pipe `pugi chain export` through
13
+ * `zip -j chain.zip` or similar; the markdown form covers 95% of the
14
+ * "share with my CTO" workflow without the dep.
15
+ *
16
+ * Module contract:
17
+ *
18
+ * - `renderMarkdownReport` is pure. Pass it a snapshot + a map of
19
+ * filename → contents and it returns the rendered report. The
20
+ * CLI handler does the disk read; the spec drives the renderer
21
+ * with synthetic content so it never touches the filesystem.
22
+ *
23
+ * - `exportChain` does the disk dance — reads every artifact that
24
+ * exists, skips missing ones (a partially-finished chain still
25
+ * exports cleanly), and returns the report + the list of
26
+ * skipped steps so the renderer can warn the operator.
27
+ */
28
+ import { existsSync, readFileSync } from 'node:fs';
29
+ import { join } from 'node:path';
30
+ import { chainDir, readChain } from './state.js';
31
+ import { CHAIN_STEPS } from './steps.js';
32
+ /**
33
+ * Render the markdown report. The format is intentionally stable so
34
+ * downstream tools can grep it; do NOT reformat without bumping the
35
+ * report version comment in the header.
36
+ *
37
+ * Report layout:
38
+ *
39
+ * # Pugi artifact chain: <chain id>
40
+ *
41
+ * - **Intent**: ...
42
+ * - **Created**: ...
43
+ * - **Finalised**: ... (or "in progress")
44
+ *
45
+ * ## 1. PRD — Olivia (PM)
46
+ * <contents>
47
+ *
48
+ * ## 2. ADR — Marcus (CTO)
49
+ * <contents>
50
+ *
51
+ * ... (one section per step)
52
+ */
53
+ export function renderMarkdownReport(envelope) {
54
+ const lines = [];
55
+ lines.push(`# Pugi artifact chain: ${envelope.chainId}`);
56
+ lines.push('');
57
+ lines.push(`- **Intent**: ${envelope.intent}`);
58
+ lines.push(`- **Created**: ${envelope.createdAt}`);
59
+ lines.push(`- **Finalised**: ${envelope.finalisedAt ?? 'in progress'}`);
60
+ if (envelope.missingSteps.length > 0) {
61
+ lines.push(`- **Missing steps**: ${envelope.missingSteps.join(', ')}`);
62
+ }
63
+ lines.push('');
64
+ for (const entry of envelope.artifacts) {
65
+ lines.push(`## ${entry.step.ordinal}. ${entry.step.id.toUpperCase()} — ${entry.step.personaLabel}`);
66
+ lines.push('');
67
+ if (entry.contents === null) {
68
+ lines.push(`_artifact not yet produced (${entry.step.artifactFilename})_`);
69
+ }
70
+ else {
71
+ const trimmed = entry.contents.trimEnd();
72
+ lines.push(trimmed.length === 0 ? '_empty artifact_' : trimmed);
73
+ }
74
+ lines.push('');
75
+ }
76
+ return lines.join('\n');
77
+ }
78
+ /**
79
+ * Render a deterministic JSON envelope. Same shape as
80
+ * `ChainExportEnvelope` minus the descriptor object (we serialise the
81
+ * step id + ordinal + persona so the consumer does not need access to
82
+ * the in-process descriptor table).
83
+ */
84
+ export function renderJsonReport(envelope) {
85
+ const serialisable = {
86
+ chainId: envelope.chainId,
87
+ intent: envelope.intent,
88
+ createdAt: envelope.createdAt,
89
+ finalisedAt: envelope.finalisedAt,
90
+ missingSteps: envelope.missingSteps,
91
+ artifacts: envelope.artifacts.map((entry) => ({
92
+ stepId: entry.step.id,
93
+ ordinal: entry.step.ordinal,
94
+ persona: entry.step.persona,
95
+ personaLabel: entry.step.personaLabel,
96
+ filename: entry.step.artifactFilename,
97
+ bytes: entry.bytes,
98
+ contents: entry.contents,
99
+ })),
100
+ };
101
+ return `${JSON.stringify(serialisable, null, 2)}\n`;
102
+ }
103
+ /**
104
+ * Read the chain + every artifact on disk, build the envelope, and
105
+ * render either markdown OR JSON. Returns BOTH the envelope and the
106
+ * rendered string so callers can route to stdout, a file, or both.
107
+ *
108
+ * Non-throwing per the artifact-chain module contract — every failure
109
+ * mode (chain not found, malformed state) returns a structured
110
+ * `ok: false` result so callers can map to an exit code without a
111
+ * try/catch wrapper.
112
+ */
113
+ export function exportChain(options) {
114
+ const state = readChain(options.workspaceCwd, options.chainId);
115
+ if (!state) {
116
+ return {
117
+ ok: false,
118
+ error: `chain ${options.chainId} not found`,
119
+ reason: 'chain_not_found',
120
+ };
121
+ }
122
+ const envelope = buildEnvelope(state, options.workspaceCwd);
123
+ const format = options.format ?? 'markdown';
124
+ const rendered = format === 'json' ? renderJsonReport(envelope) : renderMarkdownReport(envelope);
125
+ return { ok: true, envelope, rendered };
126
+ }
127
+ /**
128
+ * Build the export envelope from a chain snapshot + the workspace
129
+ * directory. Exported so the spec can drive the renderer against a
130
+ * fixture state without an explicit disk read.
131
+ */
132
+ export function buildEnvelope(state, workspaceCwd, options = {}) {
133
+ const dir = chainDir(workspaceCwd, state.id);
134
+ const readArtifact = options.readArtifact ?? defaultReadArtifact;
135
+ const artifacts = [];
136
+ const missing = [];
137
+ for (const step of CHAIN_STEPS) {
138
+ const path = join(dir, step.artifactFilename);
139
+ const contents = readArtifact(path);
140
+ if (contents === null) {
141
+ missing.push(step.id);
142
+ }
143
+ artifacts.push({
144
+ step,
145
+ contents,
146
+ bytes: contents === null ? 0 : Buffer.byteLength(contents, 'utf8'),
147
+ });
148
+ }
149
+ const finalisedAt = state.nextStep === null ? state.updatedAt : null;
150
+ return {
151
+ chainId: state.id,
152
+ intent: state.intent,
153
+ createdAt: state.createdAt,
154
+ finalisedAt,
155
+ artifacts,
156
+ missingSteps: missing,
157
+ };
158
+ }
159
+ function defaultReadArtifact(path) {
160
+ if (!existsSync(path))
161
+ return null;
162
+ return readFileSync(path, 'utf8');
163
+ }
164
+ //# sourceMappingURL=exporter.js.map
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Artifact chain state machine — Pugi .
3
+ *
4
+ * Owns the on-disk persisted state for a single chain
5
+ * (`.pugi/chains/<chain-id>/chain.json`). A chain is a deterministic
6
+ * 7-step pipeline: each step is `pending` → `dispatched` → `complete`
7
+ * (terminal). The machine never auto-advances; operator review between
8
+ * steps is the gate. This file is pure with respect to the network and
9
+ * stays small enough to import from the REPL hot path without dragging
10
+ * the dispatcher graph along.
11
+ *
12
+ * Concurrency: the CLI is single-process per invocation; the on-disk
13
+ * format tolerates two simultaneous readers (snapshot is atomic via
14
+ * write-then-rename) but writers MUST take the per-chain directory as
15
+ * an implicit lock. Multi-writer races are out-of-scope — the operator
16
+ * runs one `pugi chain next` at a time.
17
+ *
18
+ * Backwards-compat: `schemaVersion` is pinned. Older chains with no
19
+ * version field default to `1`. Bump + add migration logic when the
20
+ * persisted shape changes.
21
+ */
22
+ import { mkdirSync, readFileSync, writeFileSync, existsSync, renameSync, readdirSync } from 'node:fs';
23
+ import { join, resolve } from 'node:path';
24
+ import { randomBytes } from 'node:crypto';
25
+ import { CHAIN_STEPS, CHAIN_STEP_IDS, CHAIN_STEP_COUNT, findStep, } from './steps.js';
26
+ /**
27
+ * Generate a new chain id. Format: `chn_<8 lowercase hex>`. Short
28
+ * enough to fit on one terminal column; collision probability is fine
29
+ * for the operator-scale workload (chains per workspace per session).
30
+ */
31
+ export function generateChainId(rng = () => randomBytes(4)) {
32
+ return `chn_${rng().toString('hex')}`;
33
+ }
34
+ /**
35
+ * Resolve the directory that holds a single chain. Encapsulates the
36
+ * `.pugi/chains/<id>/` convention so every caller agrees.
37
+ */
38
+ export function chainDir(workspaceCwd, chainId) {
39
+ return resolve(workspaceCwd, '.pugi', 'chains', chainId);
40
+ }
41
+ /**
42
+ * Path to the chain's state JSON. Atomic writes go through a sibling
43
+ * `chain.json.tmp` + rename so a crashed CLI cannot leave a truncated
44
+ * state file behind.
45
+ */
46
+ export function chainStatePath(workspaceCwd, chainId) {
47
+ return join(chainDir(workspaceCwd, chainId), 'chain.json');
48
+ }
49
+ /**
50
+ * Initialise on-disk state for a fresh chain. Returns the persisted
51
+ * snapshot. Throws when the chain id already exists on disk so the
52
+ * caller is forced to handle the collision explicitly.
53
+ */
54
+ export function createChain(workspaceCwd, intent, options = {}) {
55
+ const now = options.now ?? (() => new Date());
56
+ const id = options.chainId ?? generateChainId();
57
+ const dir = chainDir(workspaceCwd, id);
58
+ if (existsSync(dir)) {
59
+ throw new Error(`chain ${id} already exists at ${dir}`);
60
+ }
61
+ mkdirSync(dir, { recursive: true });
62
+ const createdAt = now().toISOString();
63
+ const trimmedIntent = intent.trim();
64
+ if (trimmedIntent.length === 0) {
65
+ throw new Error('chain intent must be non-empty');
66
+ }
67
+ const steps = CHAIN_STEPS.map((descriptor) => ({
68
+ id: descriptor.id,
69
+ status: 'pending',
70
+ updatedAt: createdAt,
71
+ dispatchId: null,
72
+ errorMessage: null,
73
+ }));
74
+ const state = {
75
+ schemaVersion: 1,
76
+ id,
77
+ intent: trimmedIntent,
78
+ createdAt,
79
+ updatedAt: createdAt,
80
+ nextStep: CHAIN_STEP_IDS[0] ?? null,
81
+ steps,
82
+ };
83
+ writeStateAtomic(workspaceCwd, state);
84
+ return Object.freeze(state);
85
+ }
86
+ /**
87
+ * Read the persisted state for a chain. Returns `null` when the chain
88
+ * does not exist on disk — callers render a friendly "no such chain"
89
+ * message instead of throwing.
90
+ */
91
+ export function readChain(workspaceCwd, chainId) {
92
+ const path = chainStatePath(workspaceCwd, chainId);
93
+ if (!existsSync(path))
94
+ return null;
95
+ const raw = readFileSync(path, 'utf8');
96
+ const parsed = JSON.parse(raw);
97
+ if (parsed.schemaVersion !== 1 || typeof parsed.id !== 'string') {
98
+ throw new Error(`chain ${chainId} state is malformed (schemaVersion mismatch)`);
99
+ }
100
+ // Migrate any missing optional fields to defaults so a partially-
101
+ // hand-edited state file still loads.
102
+ const steps = Array.isArray(parsed.steps) ? parsed.steps : [];
103
+ if (steps.length !== CHAIN_STEP_COUNT) {
104
+ throw new Error(`chain ${chainId} state has ${steps.length} steps; expected ${CHAIN_STEP_COUNT}`);
105
+ }
106
+ return Object.freeze({
107
+ schemaVersion: 1,
108
+ id: parsed.id,
109
+ intent: parsed.intent ?? '',
110
+ createdAt: parsed.createdAt ?? new Date(0).toISOString(),
111
+ updatedAt: parsed.updatedAt ?? new Date(0).toISOString(),
112
+ nextStep: parsed.nextStep ?? null,
113
+ steps: steps.map((s) => ({
114
+ id: s.id,
115
+ status: (s.status ?? 'pending'),
116
+ updatedAt: s.updatedAt ?? parsed.updatedAt ?? new Date(0).toISOString(),
117
+ dispatchId: s.dispatchId ?? null,
118
+ errorMessage: s.errorMessage ?? null,
119
+ })),
120
+ });
121
+ }
122
+ /**
123
+ * List every chain id present under `.pugi/chains/`. Returns an empty
124
+ * array when the directory does not exist — operators rarely call this
125
+ * before `pugi chain new`.
126
+ */
127
+ export function listChainIds(workspaceCwd) {
128
+ const root = join(workspaceCwd, '.pugi', 'chains');
129
+ if (!existsSync(root))
130
+ return [];
131
+ return readdirSync(root, { withFileTypes: true })
132
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('chn_'))
133
+ .map((entry) => entry.name)
134
+ .sort();
135
+ }
136
+ /**
137
+ * Mark a step as dispatched. Idempotent re-dispatch is allowed (the
138
+ * operator may re-run a step after a transient failure); the previous
139
+ * dispatchId is overwritten.
140
+ */
141
+ export function markDispatched(workspaceCwd, chainId, stepId, dispatchId, options = {}) {
142
+ return transition(workspaceCwd, chainId, options.now ?? (() => new Date()), (state) => {
143
+ const step = state.steps.find((s) => s.id === stepId);
144
+ if (!step) {
145
+ throw new Error(`chain ${chainId}: unknown step ${stepId}`);
146
+ }
147
+ if (step.status === 'complete') {
148
+ throw new Error(`chain ${chainId}: step ${stepId} already complete`);
149
+ }
150
+ step.status = 'dispatched';
151
+ step.dispatchId = dispatchId;
152
+ step.errorMessage = null;
153
+ });
154
+ }
155
+ /**
156
+ * Mark a step as complete. Operator approves between steps so this is
157
+ * the final gate — when the operator runs `pugi chain next` AFTER the
158
+ * artifact lands, the dispatcher first flips the current step to
159
+ * complete then advances the cursor.
160
+ */
161
+ export function markComplete(workspaceCwd, chainId, stepId, options = {}) {
162
+ return transition(workspaceCwd, chainId, options.now ?? (() => new Date()), (state) => {
163
+ const step = state.steps.find((s) => s.id === stepId);
164
+ if (!step) {
165
+ throw new Error(`chain ${chainId}: unknown step ${stepId}`);
166
+ }
167
+ step.status = 'complete';
168
+ step.errorMessage = null;
169
+ state.nextStep = firstPendingStep(state.steps);
170
+ });
171
+ }
172
+ /**
173
+ * Capture an error against the current step without flipping it to
174
+ * complete. The step stays in `dispatched` so the operator can re-run.
175
+ */
176
+ export function markError(workspaceCwd, chainId, stepId, errorMessage, options = {}) {
177
+ return transition(workspaceCwd, chainId, options.now ?? (() => new Date()), (state) => {
178
+ const step = state.steps.find((s) => s.id === stepId);
179
+ if (!step) {
180
+ throw new Error(`chain ${chainId}: unknown step ${stepId}`);
181
+ }
182
+ step.errorMessage = errorMessage;
183
+ });
184
+ }
185
+ /**
186
+ * Compute the next pending step. Returns `null` when every step is
187
+ * complete (chain finalised).
188
+ */
189
+ export function firstPendingStep(steps) {
190
+ for (const id of CHAIN_STEP_IDS) {
191
+ const record = steps.find((s) => s.id === id);
192
+ if (!record || record.status !== 'complete')
193
+ return id;
194
+ }
195
+ return null;
196
+ }
197
+ /**
198
+ * Resolve the descriptor for the current cursor. Returns `null` when
199
+ * the chain is finalised. Convenience wrapper so renderers do not need
200
+ * to handle the lookup themselves.
201
+ */
202
+ export function currentStepDescriptor(state) {
203
+ if (!state.nextStep)
204
+ return null;
205
+ return findStep(state.nextStep) ?? null;
206
+ }
207
+ /* ------------------------------------------------------------------ */
208
+ /* Internal: transactional mutate helper */
209
+ /* ------------------------------------------------------------------ */
210
+ function transition(workspaceCwd, chainId, now, mutate) {
211
+ const current = readChain(workspaceCwd, chainId);
212
+ if (!current) {
213
+ throw new Error(`chain ${chainId} not found at ${chainStatePath(workspaceCwd, chainId)}`);
214
+ }
215
+ // Deep clone so the frozen snapshot is never mutated.
216
+ const draft = JSON.parse(JSON.stringify(current));
217
+ mutate(draft);
218
+ draft.updatedAt = now().toISOString();
219
+ // Update step-level timestamp for whichever step the mutation
220
+ // touched. We diff status / dispatchId / errorMessage against the
221
+ // previous snapshot — any change bumps the step's updatedAt.
222
+ for (const draftStep of draft.steps) {
223
+ const previousStep = current.steps.find((s) => s.id === draftStep.id);
224
+ if (!previousStep)
225
+ continue;
226
+ if (draftStep.status !== previousStep.status ||
227
+ draftStep.dispatchId !== previousStep.dispatchId ||
228
+ draftStep.errorMessage !== previousStep.errorMessage) {
229
+ draftStep.updatedAt = draft.updatedAt;
230
+ }
231
+ }
232
+ writeStateAtomic(workspaceCwd, draft);
233
+ return Object.freeze(draft);
234
+ }
235
+ function writeStateAtomic(workspaceCwd, state) {
236
+ const dir = chainDir(workspaceCwd, state.id);
237
+ mkdirSync(dir, { recursive: true });
238
+ const finalPath = chainStatePath(workspaceCwd, state.id);
239
+ const tmpPath = `${finalPath}.tmp`;
240
+ writeFileSync(tmpPath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
241
+ renameSync(tmpPath, finalPath);
242
+ }
243
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Artifact chain step definitions — Pugi .
3
+ *
4
+ * The chain encodes Pugi's moat: an operator drops one high-level intent
5
+ * (`pugi chain new "<intent>"`) and the CLI walks a deterministic 7-step
6
+ * pipeline that produces verifiable artifacts at each stage. Every step
7
+ * dispatches to a specialist persona via the existing `pugi delegate`
8
+ * surface so the same Tier 1 roster powers both ad-hoc dispatch and the
9
+ * artifact chain.
10
+ *
11
+ * The pipeline:
12
+ *
13
+ * 1. prd — Olivia (PM): structured PRD (goals / users /
14
+ * acceptance criteria / scope)
15
+ * 2. adr — Marcus (CTO): architectural decision record
16
+ * against the team ADR template
17
+ * 3. mindmap — Marcus (CTO): mermaid mindmap of the solution
18
+ * surface (modules + boundaries)
19
+ * 4. er — Hiroshi (Lead Dev):entity-relationship mermaid for
20
+ * the data model the design implies
21
+ * 5. sequence — Hiroshi (Lead Dev):mermaid sequence diagram covering
22
+ * the critical happy + sad paths
23
+ * 6. tests — Vera (QA): spec scaffolding mapped from the
24
+ * PRD acceptance criteria
25
+ * 7. code — Hiroshi (Lead Dev):implementation diff against the
26
+ * scaffolded specs
27
+ *
28
+ * Persona slugs are lowercase ASCII to satisfy the server-side delegate
29
+ * grammar (`^[a-z]+$`, mirrors `PUGI_DELEGATE_REGEX` in
30
+ * `apps/admin-api/src/pugi/sessions.controller.ts`). The chain never
31
+ * invents new personas — it only orchestrates the existing roster.
32
+ *
33
+ * Module contract:
34
+ *
35
+ * - This file is PURE data. No fs, no network, no side effects.
36
+ * Importing it from a hot path (REPL keystroke handler) is safe.
37
+ * - The step ORDER is authoritative; downstream consumers MUST iterate
38
+ * `CHAIN_STEPS` instead of hand-rolling their own arrays so future
39
+ * re-ordering lands in exactly one place.
40
+ * - Personas are looked up by slug at dispatch time so a roster change
41
+ * does not silently break the chain — the dispatcher surfaces an
42
+ * `unknown_persona` outcome the operator can see.
43
+ */
44
+ /**
45
+ * Ordered, frozen table of the seven steps. The chain state machine
46
+ * advances through this array exactly once; re-ordering OR insertion
47
+ * is a breaking change for any chain currently on disk.
48
+ */
49
+ export const CHAIN_STEPS = Object.freeze([
50
+ {
51
+ id: 'prd',
52
+ ordinal: 1,
53
+ persona: 'olivia',
54
+ personaLabel: 'Olivia (PM)',
55
+ artifactFilename: 'PRD.md',
56
+ briefTemplate: 'Produce a structured PRD for chain {{chainId}}. ' +
57
+ 'Operator intent: "{{intent}}". ' +
58
+ 'Cover: goals, target users, success metrics, ' +
59
+ 'numbered acceptance criteria (## Acceptance Criteria), scope + non-scope, ' +
60
+ 'risks. Output markdown ready for prd-check ingestion.',
61
+ gloss: 'Structured PRD — goals, users, acceptance criteria, scope',
62
+ },
63
+ {
64
+ id: 'adr',
65
+ ordinal: 2,
66
+ persona: 'marcus',
67
+ personaLabel: 'Marcus (CTO)',
68
+ artifactFilename: 'ADR.md',
69
+ briefTemplate: 'Produce an architectural decision record for chain {{chainId}}. ' +
70
+ 'Read the PRD at .pugi/chains/{{chainId}}/PRD.md verbatim. ' +
71
+ 'Follow the team ADR template: Status, Context, Decision, ' +
72
+ 'Consequences, Alternatives Considered. ' +
73
+ 'Be explicit about boundary changes the decision implies.',
74
+ gloss: 'Architectural decision record per team ADR template',
75
+ },
76
+ {
77
+ id: 'mindmap',
78
+ ordinal: 3,
79
+ persona: 'marcus',
80
+ personaLabel: 'Marcus (CTO)',
81
+ artifactFilename: 'MINDMAP.mmd',
82
+ briefTemplate: 'Produce a mermaid mindmap of the solution surface for chain {{chainId}}. ' +
83
+ 'Read PRD + ADR at .pugi/chains/{{chainId}}/PRD.md and ADR.md. ' +
84
+ 'Root node = the feature name; first-level branches = subsystems; ' +
85
+ 'leaves = concrete modules / endpoints / tables. ' +
86
+ 'Output ONE mermaid `mindmap` fenced block — no prose.',
87
+ gloss: 'Mermaid mindmap — solution surface (subsystems + modules)',
88
+ },
89
+ {
90
+ id: 'er',
91
+ ordinal: 4,
92
+ persona: 'hiroshi',
93
+ personaLabel: 'Hiroshi (Lead Dev)',
94
+ artifactFilename: 'ER.mmd',
95
+ briefTemplate: 'Produce a mermaid entity-relationship diagram for chain {{chainId}}. ' +
96
+ 'Source: PRD + ADR + MINDMAP under .pugi/chains/{{chainId}}/. ' +
97
+ 'Use `erDiagram` syntax. Cover every persisted entity the ' +
98
+ 'design implies; mark PK / FK relationships explicitly.',
99
+ gloss: 'Mermaid ER diagram — persisted entities + relationships',
100
+ },
101
+ {
102
+ id: 'sequence',
103
+ ordinal: 5,
104
+ persona: 'hiroshi',
105
+ personaLabel: 'Hiroshi (Lead Dev)',
106
+ artifactFilename: 'SEQUENCE.mmd',
107
+ briefTemplate: 'Produce mermaid sequence diagrams for chain {{chainId}}. ' +
108
+ 'Cover the critical happy path + at least one sad path. ' +
109
+ 'Source: PRD acceptance criteria + ER diagram. ' +
110
+ 'Use `sequenceDiagram` syntax; one diagram per fenced block.',
111
+ gloss: 'Mermaid sequence diagrams — happy + sad path flows',
112
+ },
113
+ {
114
+ id: 'tests',
115
+ ordinal: 6,
116
+ persona: 'vera',
117
+ personaLabel: 'Vera (QA)',
118
+ artifactFilename: 'TESTS.md',
119
+ briefTemplate: 'Produce spec scaffolding for chain {{chainId}}. ' +
120
+ 'Map every numbered PRD acceptance criterion (under ## Acceptance Criteria) ' +
121
+ 'to one or more concrete test descriptions using node:test + node:assert. ' +
122
+ 'Format: criterion-id → test file path → `it(...)` blocks. ' +
123
+ 'Output markdown only — no executable code.',
124
+ gloss: 'Spec scaffolding — PRD criteria mapped to test descriptions',
125
+ },
126
+ {
127
+ id: 'code',
128
+ ordinal: 7,
129
+ persona: 'hiroshi',
130
+ personaLabel: 'Hiroshi (Lead Dev)',
131
+ artifactFilename: 'CODE.md',
132
+ briefTemplate: 'Produce an implementation plan + diff references for chain {{chainId}}. ' +
133
+ 'Read PRD, ADR, MINDMAP, ER, SEQUENCE, TESTS under .pugi/chains/{{chainId}}/. ' +
134
+ 'Output: file-by-file changes (path, change kind, summary), ' +
135
+ 'with each change tied back to a PRD criterion id. ' +
136
+ 'NO inline patches — patches land via `pugi patch apply` after operator review.',
137
+ gloss: 'Implementation plan — file changes mapped to PRD criteria',
138
+ },
139
+ ]);
140
+ /**
141
+ * Resolve a step descriptor by id. Returns `undefined` for unknown ids
142
+ * so callers can surface a structured error instead of panicking.
143
+ */
144
+ export function findStep(id) {
145
+ return CHAIN_STEPS.find((step) => step.id === id);
146
+ }
147
+ /**
148
+ * Index of step ids in chain order. Exported so the state machine can
149
+ * answer "what is the next step after X?" without re-scanning the
150
+ * table on every transition.
151
+ */
152
+ export const CHAIN_STEP_IDS = Object.freeze(CHAIN_STEPS.map((s) => s.id));
153
+ /**
154
+ * Total number of steps in the chain. Hard-coded constant exported so
155
+ * downstream renderers can size their progress bars without iterating.
156
+ */
157
+ export const CHAIN_STEP_COUNT = CHAIN_STEPS.length;
158
+ /**
159
+ * Interpolate the brief template with chain context. The template
160
+ * grammar is intentionally minimal — only `{{chainId}}` and `{{intent}}`
161
+ * are honoured. Unknown placeholders are left verbatim so a template
162
+ * typo surfaces in the dispatched brief instead of silently dropping.
163
+ */
164
+ export function renderBrief(template, ctx) {
165
+ return template
166
+ .replace(/\{\{chainId\}\}/g, ctx.chainId)
167
+ .replace(/\{\{intent\}\}/g, ctx.intent);
168
+ }
169
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1,92 @@
1
+ const MIN_QUESTIONS = 1;
2
+ const MAX_QUESTIONS = 4;
3
+ const MIN_OPTIONS = 2;
4
+ const MAX_OPTIONS = 4;
5
+ const MAX_HEADER_LENGTH = 12;
6
+ export function validateBatch(batch) {
7
+ const errors = [];
8
+ if (!Array.isArray(batch.questions)) {
9
+ errors.push({
10
+ field: 'questions',
11
+ message: 'questions must be an array',
12
+ });
13
+ return errors;
14
+ }
15
+ if (batch.questions.length < MIN_QUESTIONS || batch.questions.length > MAX_QUESTIONS) {
16
+ errors.push({
17
+ field: 'questions',
18
+ message: 'questions must contain 1 to 4 items',
19
+ });
20
+ }
21
+ for (const [questionIndex, question] of batch.questions.entries()) {
22
+ const questionPath = `questions[${questionIndex}]`;
23
+ if (typeof question.question !== 'string' || !question.question.endsWith('?')) {
24
+ errors.push({
25
+ field: `${questionPath}.question`,
26
+ message: 'question must end with ?',
27
+ });
28
+ }
29
+ if (typeof question.header !== 'string' || question.header.length < 1 || question.header.length > MAX_HEADER_LENGTH) {
30
+ errors.push({
31
+ field: `${questionPath}.header`,
32
+ message: 'header must be 1 to 12 characters',
33
+ });
34
+ }
35
+ if (typeof question.multiSelect !== 'boolean') {
36
+ errors.push({
37
+ field: `${questionPath}.multiSelect`,
38
+ message: 'multiSelect must be a boolean',
39
+ });
40
+ }
41
+ if (!Array.isArray(question.options)) {
42
+ errors.push({
43
+ field: `${questionPath}.options`,
44
+ message: 'options must be an array',
45
+ });
46
+ continue;
47
+ }
48
+ if (question.options.length < MIN_OPTIONS || question.options.length > MAX_OPTIONS) {
49
+ errors.push({
50
+ field: `${questionPath}.options`,
51
+ message: 'options must contain 2 to 4 items',
52
+ });
53
+ }
54
+ for (const [optionIndex, option] of question.options.entries()) {
55
+ const optionPath = `${questionPath}.options[${optionIndex}]`;
56
+ if (typeof option.label !== 'string' || option.label.length < 1) {
57
+ errors.push({
58
+ field: `${optionPath}.label`,
59
+ message: 'label must be a non-empty string',
60
+ });
61
+ }
62
+ if (typeof option.description !== 'string' || option.description.length < 1) {
63
+ errors.push({
64
+ field: `${optionPath}.description`,
65
+ message: 'description must be a non-empty string',
66
+ });
67
+ }
68
+ if (option.preview !== undefined && (typeof option.preview !== 'string' || option.preview.length < 1)) {
69
+ errors.push({
70
+ field: `${optionPath}.preview`,
71
+ message: 'preview must be a non-empty string when set',
72
+ });
73
+ }
74
+ if (option.recommended !== undefined && typeof option.recommended !== 'boolean') {
75
+ errors.push({
76
+ field: `${optionPath}.recommended`,
77
+ message: 'recommended must be a boolean when set',
78
+ });
79
+ }
80
+ }
81
+ }
82
+ return errors;
83
+ }
84
+ export function formatHeader(header) {
85
+ if (header.length <= MAX_HEADER_LENGTH)
86
+ return header;
87
+ return `${header.slice(0, MAX_HEADER_LENGTH - 3)}...`;
88
+ }
89
+ export function findRecommended(options) {
90
+ return options.find(option => option.recommended === true) ?? null;
91
+ }
92
+ //# sourceMappingURL=question.js.map