@pugi/cli 0.1.0-beta.8 → 0.1.0-beta.88

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 (405) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  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/smoke.js +133 -0
  11. package/dist/core/agent-progress/cleanup.js +134 -0
  12. package/dist/core/agent-progress/schema.js +144 -0
  13. package/dist/core/agent-progress/writer.js +101 -0
  14. package/dist/core/agents/adaptive-router.js +330 -0
  15. package/dist/core/agents/query-decomposer.js +297 -0
  16. package/dist/core/agents/registry.js +3 -3
  17. package/dist/core/approvals/shortcut-resolver.js +98 -0
  18. package/dist/core/artifact-chain/dispatcher.js +148 -0
  19. package/dist/core/artifact-chain/exporter.js +164 -0
  20. package/dist/core/artifact-chain/state.js +243 -0
  21. package/dist/core/artifact-chain/steps.js +169 -0
  22. package/dist/core/ask-user/question.js +92 -0
  23. package/dist/core/audit/audit-trail.js +275 -0
  24. package/dist/core/auth/ensure-authenticated.js +129 -0
  25. package/dist/core/auth/env-provider.js +238 -0
  26. package/dist/core/auto-open-browser.js +4 -4
  27. package/dist/core/auto-update/channels.js +122 -0
  28. package/dist/core/auto-update/checker.js +241 -0
  29. package/dist/core/auto-update/state.js +235 -0
  30. package/dist/core/bare-mode/index.js +107 -0
  31. package/dist/core/bash/redirect.js +281 -0
  32. package/dist/core/bash-classifier.js +436 -40
  33. package/dist/core/checkpoint/resumer.js +149 -0
  34. package/dist/core/checkpoint/rewinder.js +291 -0
  35. package/dist/core/checkpoints/shadow-git.js +670 -0
  36. package/dist/core/citations/parser.js +109 -0
  37. package/dist/core/classifier/yolo-classifier.js +88 -0
  38. package/dist/core/codegraph/decision-store.js +248 -0
  39. package/dist/core/codegraph/detect-repo.js +459 -0
  40. package/dist/core/codegraph/install.js +134 -0
  41. package/dist/core/codegraph/offer-hook.js +220 -0
  42. package/dist/core/compact/auto-trigger.js +96 -0
  43. package/dist/core/compact/buffer-rewriter.js +115 -0
  44. package/dist/core/compact/summarizer.js +208 -0
  45. package/dist/core/compact/token-counter.js +108 -0
  46. package/dist/core/consensus/anvil-fanout.js +25 -25
  47. package/dist/core/consensus/diff-capture.js +121 -12
  48. package/dist/core/consensus/rubric.js +21 -21
  49. package/dist/core/context/builder.js +6 -6
  50. package/dist/core/context/compaction-events.js +8 -8
  51. package/dist/core/context/compaction.js +31 -31
  52. package/dist/core/context/index.js +15 -8
  53. package/dist/core/context/invariants.js +51 -51
  54. package/dist/core/context/markdown-loader.js +28 -10
  55. package/dist/core/context/markdown-traverse.js +255 -0
  56. package/dist/core/context/pugiignore.js +41 -41
  57. package/dist/core/context/repo-skeleton.js +37 -37
  58. package/dist/core/context/tool-eviction.js +55 -0
  59. package/dist/core/context/watcher.js +32 -32
  60. package/dist/core/context/working-set.js +23 -23
  61. package/dist/core/coordinator/agent-tools.js +77 -0
  62. package/dist/core/coordinator/agent-toolset.js +65 -0
  63. package/dist/core/coordinator/fsm.js +73 -0
  64. package/dist/core/coordinator/mode-fsm.js +70 -0
  65. package/dist/core/cost/rate-card.js +129 -0
  66. package/dist/core/cost/tracker.js +221 -0
  67. package/dist/core/credentials.js +12 -12
  68. package/dist/core/cron/scheduler.js +138 -0
  69. package/dist/core/denial-tracking/index.js +8 -0
  70. package/dist/core/denial-tracking/state.js +264 -0
  71. package/dist/core/diagnostics/probe-runner.js +93 -0
  72. package/dist/core/diagnostics/probes/api.js +46 -0
  73. package/dist/core/diagnostics/probes/auth.js +93 -0
  74. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  75. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  76. package/dist/core/diagnostics/probes/config.js +72 -0
  77. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  78. package/dist/core/diagnostics/probes/disk.js +81 -0
  79. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  80. package/dist/core/diagnostics/probes/git.js +65 -0
  81. package/dist/core/diagnostics/probes/hooks.js +118 -0
  82. package/dist/core/diagnostics/probes/mcp.js +75 -0
  83. package/dist/core/diagnostics/probes/node.js +59 -0
  84. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  85. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  86. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  87. package/dist/core/diagnostics/probes/session.js +74 -0
  88. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  89. package/dist/core/diagnostics/probes/workspace.js +63 -0
  90. package/dist/core/diagnostics/types.js +70 -0
  91. package/dist/core/dispatch/cache-cleanup.js +197 -0
  92. package/dist/core/dispatch/cache-handoff.js +295 -0
  93. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  94. package/dist/core/edits/dispatch.js +293 -7
  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 +3 -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 +322 -0
  108. package/dist/core/engine/anvil-client.js +151 -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 +134 -16
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1295 -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 +44 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +213 -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/image/renderer.js +71 -0
  141. package/dist/core/init/detector.js +582 -0
  142. package/dist/core/init/template-renderer.js +242 -0
  143. package/dist/core/jobs/registry.js +18 -18
  144. package/dist/core/ledger/results-tsv.js +142 -0
  145. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  146. package/dist/core/lsp/cache.js +105 -0
  147. package/dist/core/lsp/client.js +776 -0
  148. package/dist/core/lsp/language-detect.js +66 -0
  149. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  150. package/dist/core/lsp/symbol-tools.js +372 -0
  151. package/dist/core/mcp/client.js +97 -28
  152. package/dist/core/mcp/http-server.js +553 -0
  153. package/dist/core/mcp/orchestrator-tools.js +662 -0
  154. package/dist/core/mcp/permission.js +190 -0
  155. package/dist/core/mcp/registry.js +39 -17
  156. package/dist/core/mcp/server-tools.js +219 -0
  157. package/dist/core/mcp/server.js +397 -0
  158. package/dist/core/mcp/trust.js +10 -10
  159. package/dist/core/memory/dual-write.js +416 -0
  160. package/dist/core/memory/passive-extract.js +130 -0
  161. package/dist/core/memory/phase1-kinds.js +20 -0
  162. package/dist/core/memory/secret-scanner.js +304 -0
  163. package/dist/core/memory-sync/queue.js +170 -0
  164. package/dist/core/metrics/extract.js +113 -0
  165. package/dist/core/modes/roo-modes.js +68 -0
  166. package/dist/core/onboarding/ensure-initialized.js +133 -0
  167. package/dist/core/onboarding/marker.js +111 -0
  168. package/dist/core/onboarding/telemetry-state.js +108 -0
  169. package/dist/core/output-style/presets.js +176 -0
  170. package/dist/core/output-style/state.js +185 -0
  171. package/dist/core/path-security.js +287 -5
  172. package/dist/core/permission.js +82 -22
  173. package/dist/core/permissions/auto-classifier.js +124 -0
  174. package/dist/core/permissions/bash-parser.js +371 -0
  175. package/dist/core/permissions/circuit-breaker.js +83 -0
  176. package/dist/core/permissions/constrained-edit.js +91 -0
  177. package/dist/core/permissions/gate.js +278 -0
  178. package/dist/core/permissions/index.js +20 -0
  179. package/dist/core/permissions/mode.js +174 -0
  180. package/dist/core/permissions/network-egress.js +137 -0
  181. package/dist/core/permissions/state.js +241 -0
  182. package/dist/core/permissions/tool-class.js +93 -0
  183. package/dist/core/plan-mode/ui-state.js +51 -0
  184. package/dist/core/plans/plan-artifact.js +721 -0
  185. package/dist/core/policy-limits/etag-store.js +122 -0
  186. package/dist/core/prd-check/parser.js +215 -0
  187. package/dist/core/prd-check/reporter.js +127 -0
  188. package/dist/core/prd-check/session-review.js +557 -0
  189. package/dist/core/prd-check/verifiers.js +223 -0
  190. package/dist/core/prompt-cache/client-cache.js +99 -0
  191. package/dist/core/prompts/assembly.js +29 -0
  192. package/dist/core/prompts/registry.js +364 -0
  193. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  194. package/dist/core/pugi-md/context-injector.js +76 -0
  195. package/dist/core/pugi-md/walk-up.js +207 -0
  196. package/dist/core/python/uv-installer.js +270 -0
  197. package/dist/core/python/uv-resolver.js +83 -0
  198. package/dist/core/rate-limit/narrator.js +146 -0
  199. package/dist/core/recipes/cli-types.js +20 -0
  200. package/dist/core/recipes/loader.js +103 -0
  201. package/dist/core/recipes/runner.js +345 -0
  202. package/dist/core/recipes/schema.js +587 -0
  203. package/dist/core/release-notes/parser.js +241 -0
  204. package/dist/core/release-notes/state.js +116 -0
  205. package/dist/core/repl/ask.js +37 -37
  206. package/dist/core/repl/cancellation.js +26 -26
  207. package/dist/core/repl/cap-warning.js +4 -4
  208. package/dist/core/repl/clipboard-read.js +11 -11
  209. package/dist/core/repl/dispatch-fsm.js +12 -12
  210. package/dist/core/repl/history-search.js +15 -15
  211. package/dist/core/repl/history.js +28 -18
  212. package/dist/core/repl/kill-ring.js +5 -5
  213. package/dist/core/repl/model-pricing.js +135 -0
  214. package/dist/core/repl/privacy-banner.js +22 -22
  215. package/dist/core/repl/session.js +2157 -214
  216. package/dist/core/repl/slash-commands.js +533 -40
  217. package/dist/core/repl/store/index.js +1 -1
  218. package/dist/core/repl/store/jsonl-log.js +22 -22
  219. package/dist/core/repl/store/lockfile.js +10 -10
  220. package/dist/core/repl/store/session-store.js +136 -107
  221. package/dist/core/repl/store/types.js +15 -15
  222. package/dist/core/repl/store/uuid-v7.js +12 -12
  223. package/dist/core/repl/workspace-context.js +43 -21
  224. package/dist/core/repo-map/build.js +125 -0
  225. package/dist/core/repo-map/cache.js +185 -0
  226. package/dist/core/repo-map/extractor.js +254 -0
  227. package/dist/core/repo-map/formatter.js +145 -0
  228. package/dist/core/repo-map/page-rank.js +105 -0
  229. package/dist/core/repo-map/scanner.js +211 -0
  230. package/dist/core/retry-budget/budget.js +284 -0
  231. package/dist/core/retry-budget/index.js +5 -0
  232. package/dist/core/retry-budget/retry-cap.js +74 -0
  233. package/dist/core/routing/lead-worker.js +43 -0
  234. package/dist/core/routing/pre-flight-estimator.js +108 -0
  235. package/dist/core/runs/run-tree.js +103 -0
  236. package/dist/core/security/injection-scanner.js +367 -0
  237. package/dist/core/security/output-filter.js +418 -0
  238. package/dist/core/session/env-file.js +105 -0
  239. package/dist/core/session/section-budgets.js +140 -0
  240. package/dist/core/session.js +92 -0
  241. package/dist/core/settings.js +298 -5
  242. package/dist/core/share/formatter.js +271 -0
  243. package/dist/core/share/redactor.js +221 -0
  244. package/dist/core/share/uploader.js +267 -0
  245. package/dist/core/skills/defaults.js +457 -0
  246. package/dist/core/skills/loader.js +22 -22
  247. package/dist/core/skills/sources.js +27 -27
  248. package/dist/core/smoke/headless-driver.js +174 -0
  249. package/dist/core/smoke/orchestrator.js +194 -0
  250. package/dist/core/smoke/runner.js +238 -0
  251. package/dist/core/smoke/scenario-parser.js +316 -0
  252. package/dist/core/statusline.js +99 -0
  253. package/dist/core/subagents/dispatcher-real.js +600 -0
  254. package/dist/core/subagents/dispatcher.js +132 -43
  255. package/dist/core/subagents/index.js +19 -6
  256. package/dist/core/subagents/isolation-matrix.js +213 -0
  257. package/dist/core/subagents/spawn.js +19 -4
  258. package/dist/core/telemetry/emitter.js +229 -0
  259. package/dist/core/telemetry/queue.js +251 -0
  260. package/dist/core/theme/context.js +91 -0
  261. package/dist/core/theme/presets.js +228 -0
  262. package/dist/core/theme/state.js +181 -0
  263. package/dist/core/todos/invariant.js +10 -0
  264. package/dist/core/todos/state.js +177 -0
  265. package/dist/core/tool-schema/compressor.js +89 -0
  266. package/dist/core/transport/version-interceptor.js +166 -0
  267. package/dist/core/trust.js +2 -2
  268. package/dist/core/tui/thinking-block.js +64 -0
  269. package/dist/core/vim/keymap.js +288 -0
  270. package/dist/core/vim/state.js +92 -0
  271. package/dist/core/watch-markers/marker-watcher.js +133 -0
  272. package/dist/core/worktree-manager/cleanup.js +123 -0
  273. package/dist/core/worktree-manager/manager.js +303 -0
  274. package/dist/index.js +36 -0
  275. package/dist/runtime/bootstrap.js +190 -0
  276. package/dist/runtime/cli.js +4203 -493
  277. package/dist/runtime/commands/agents.js +30 -30
  278. package/dist/runtime/commands/budget.js +5 -5
  279. package/dist/runtime/commands/cancel.js +231 -0
  280. package/dist/runtime/commands/chain.js +489 -0
  281. package/dist/runtime/commands/codegraph-status.js +227 -0
  282. package/dist/runtime/commands/compact.js +297 -0
  283. package/dist/runtime/commands/config.js +73 -39
  284. package/dist/runtime/commands/cost.js +199 -0
  285. package/dist/runtime/commands/delegate.js +244 -13
  286. package/dist/runtime/commands/dispatch.js +126 -0
  287. package/dist/runtime/commands/doctor.js +579 -0
  288. package/dist/runtime/commands/feedback.js +184 -0
  289. package/dist/runtime/commands/hooks.js +184 -0
  290. package/dist/runtime/commands/init.js +254 -0
  291. package/dist/runtime/commands/lsp.js +368 -0
  292. package/dist/runtime/commands/mcp.js +879 -0
  293. package/dist/runtime/commands/memory.js +582 -0
  294. package/dist/runtime/commands/model.js +237 -0
  295. package/dist/runtime/commands/onboarding.js +275 -0
  296. package/dist/runtime/commands/patch.js +128 -0
  297. package/dist/runtime/commands/permissions.js +112 -0
  298. package/dist/runtime/commands/plan.js +143 -0
  299. package/dist/runtime/commands/prd-check.js +285 -0
  300. package/dist/runtime/commands/privacy.js +17 -17
  301. package/dist/runtime/commands/recipe.js +325 -0
  302. package/dist/runtime/commands/redo-blob-store.js +92 -0
  303. package/dist/runtime/commands/redo.js +361 -0
  304. package/dist/runtime/commands/release-notes.js +229 -0
  305. package/dist/runtime/commands/repo-map.js +95 -0
  306. package/dist/runtime/commands/report.js +299 -0
  307. package/dist/runtime/commands/resume.js +118 -0
  308. package/dist/runtime/commands/review-consensus.js +68 -53
  309. package/dist/runtime/commands/rewind.js +333 -0
  310. package/dist/runtime/commands/roster.js +14 -14
  311. package/dist/runtime/commands/sessions.js +163 -0
  312. package/dist/runtime/commands/share.js +316 -0
  313. package/dist/runtime/commands/skills.js +31 -31
  314. package/dist/runtime/commands/status.js +186 -0
  315. package/dist/runtime/commands/stickers.js +82 -0
  316. package/dist/runtime/commands/style.js +194 -0
  317. package/dist/runtime/commands/theme.js +196 -0
  318. package/dist/runtime/commands/undo.js +54 -22
  319. package/dist/runtime/commands/update.js +289 -0
  320. package/dist/runtime/commands/vim.js +140 -0
  321. package/dist/runtime/commands/worktree.js +177 -0
  322. package/dist/runtime/commands/worktrees.js +155 -0
  323. package/dist/runtime/headless-repl.js +195 -0
  324. package/dist/runtime/headless.js +543 -0
  325. package/dist/runtime/load-hooks-or-exit.js +71 -0
  326. package/dist/runtime/plan-decompose.js +531 -0
  327. package/dist/runtime/sigint-guard.js +272 -0
  328. package/dist/runtime/update-check.js +28 -28
  329. package/dist/runtime/version.js +65 -0
  330. package/dist/skills/bundled/batch.js +617 -0
  331. package/dist/skills/bundled/index.js +45 -0
  332. package/dist/skills/bundled/loop.js +358 -0
  333. package/dist/skills/bundled/remember.js +383 -0
  334. package/dist/skills/bundled/simplify.js +289 -0
  335. package/dist/skills/bundled/skillify.js +373 -0
  336. package/dist/skills/bundled/stuck.js +558 -0
  337. package/dist/skills/bundled/verify.js +439 -0
  338. package/dist/testing/vcr.js +486 -0
  339. package/dist/tools/agent-tool.js +229 -0
  340. package/dist/tools/apply-patch.js +556 -0
  341. package/dist/tools/ask-user-question.js +288 -0
  342. package/dist/tools/ask-user.js +115 -0
  343. package/dist/tools/bash.js +624 -46
  344. package/dist/tools/brief.js +224 -0
  345. package/dist/tools/enter-worktree.js +250 -0
  346. package/dist/tools/exit-worktree.js +147 -0
  347. package/dist/tools/file-tools.js +161 -44
  348. package/dist/tools/lsp-tools.js +189 -0
  349. package/dist/tools/mcp-tool.js +260 -0
  350. package/dist/tools/multi-edit.js +361 -0
  351. package/dist/tools/powershell.js +268 -0
  352. package/dist/tools/registry.js +85 -0
  353. package/dist/tools/skill-tool.js +96 -0
  354. package/dist/tools/sleep.js +99 -0
  355. package/dist/tools/synthetic-output.js +133 -0
  356. package/dist/tools/tasks.js +208 -0
  357. package/dist/tools/todo-write.js +184 -0
  358. package/dist/tools/verify-plan-execution.js +295 -0
  359. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  360. package/dist/tools/web-fetch.js +195 -10
  361. package/dist/tools/web-search.js +458 -0
  362. package/dist/tui/agent-progress-card.js +111 -0
  363. package/dist/tui/agent-tree.js +11 -1
  364. package/dist/tui/ask-modal.js +14 -14
  365. package/dist/tui/ask-user-question-chips.js +257 -0
  366. package/dist/tui/ask-user-question-prompt.js +203 -0
  367. package/dist/tui/compact-banner.js +81 -0
  368. package/dist/tui/conversation-pane.js +85 -11
  369. package/dist/tui/cost-table.js +111 -0
  370. package/dist/tui/device-flow.js +2 -2
  371. package/dist/tui/doctor-table.js +46 -0
  372. package/dist/tui/feedback-prompt.js +156 -0
  373. package/dist/tui/input-box.js +247 -32
  374. package/dist/tui/login-picker.js +3 -3
  375. package/dist/tui/markdown-render.js +6 -6
  376. package/dist/tui/onboarding-wizard.js +240 -0
  377. package/dist/tui/permissions-picker.js +86 -0
  378. package/dist/tui/render.js +35 -0
  379. package/dist/tui/repl-render.js +332 -54
  380. package/dist/tui/repl-splash-art.js +16 -16
  381. package/dist/tui/repl-splash-mascot.js +48 -24
  382. package/dist/tui/repl-splash.js +22 -22
  383. package/dist/tui/repl.js +124 -44
  384. package/dist/tui/slash-palette.js +6 -6
  385. package/dist/tui/splash.js +2 -2
  386. package/dist/tui/status-bar.js +109 -31
  387. package/dist/tui/status-table.js +7 -0
  388. package/dist/tui/stickers-art.js +136 -0
  389. package/dist/tui/style-table.js +28 -0
  390. package/dist/tui/theme-table.js +29 -0
  391. package/dist/tui/thinking-spinner.js +123 -0
  392. package/dist/tui/tool-stream-pane.js +53 -4
  393. package/dist/tui/update-banner.js +27 -2
  394. package/dist/tui/vim-input.js +267 -0
  395. package/dist/tui/welcome-banner.js +107 -0
  396. package/dist/tui/welcome-data.js +293 -0
  397. package/dist/tui/workspace-context.js +2 -2
  398. package/docs/examples/codegraph.mcp.json +10 -0
  399. package/package.json +25 -7
  400. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  401. package/test/scenarios/compact-force.scenario.txt +11 -0
  402. package/test/scenarios/identity.scenario.txt +11 -0
  403. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  404. package/test/scenarios/walkback.scenario.txt +12 -0
  405. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Rate-limit narrator — branded copy + actionable next step.
3
+ *
4
+ * Upstream providers (Anthropic, OpenAI, DeepSeek, Cerebras) emit
5
+ * shaped error payloads when a tenant hits a quota / RPM / TPM gate.
6
+ * Default error text is generic ("Rate limit exceeded") и hostile к
7
+ * customers who don't know what to do next. This module normalises
8
+ * those signals into a Pugi-flavoured narration that says:
9
+ *
10
+ * 1. What hit the wall (token quota / request rate / concurrent jobs)
11
+ * 2. When to retry
12
+ * 3. What the operator can do RIGHT NOW (switch tier, wait, cancel)
13
+ *
14
+ * Pure mapping function — no I/O, no globals. Caller (engine / TUI)
15
+ * decides where the narration lands.
16
+ */
17
+ export function narrateRateLimit(signal) {
18
+ const retryAfterSec = signal.retryAfterSec ?? -1;
19
+ const provider = signal.provider ?? 'upstream provider';
20
+ switch (signal.kind) {
21
+ case 'token-budget':
22
+ return {
23
+ headline: `Token budget exhausted на ${provider}`,
24
+ body: formatTokenBudgetBody(signal),
25
+ nextStep: retryAfterSec > 0
26
+ ? `Wait ${formatDuration(retryAfterSec)} or upgrade tier с pugi tier upgrade.`
27
+ : 'Upgrade tier с pugi tier upgrade or wait for window reset.',
28
+ retryAfterSec,
29
+ };
30
+ case 'request-rate':
31
+ return {
32
+ headline: `Too many requests к ${provider}`,
33
+ body: formatRateBody(signal),
34
+ nextStep: retryAfterSec > 0
35
+ ? `Pugi retries automatically in ${formatDuration(retryAfterSec)}.`
36
+ : 'Pugi retries automatically. Press Ctrl+C to cancel.',
37
+ retryAfterSec,
38
+ };
39
+ case 'concurrent-jobs':
40
+ return {
41
+ headline: `Concurrent job limit reached (${provider})`,
42
+ body: `Your tier permits up к ${signal.limit ?? 'N'} parallel jobs. Pugi queues the rest.`,
43
+ nextStep: 'Wait for a job to finish or upgrade tier для more parallelism.',
44
+ retryAfterSec,
45
+ };
46
+ case 'provider-quota':
47
+ return {
48
+ headline: `${provider} monthly quota exhausted`,
49
+ body: formatProviderQuotaBody(signal),
50
+ nextStep: 'Switch model tier или wait until next billing window.',
51
+ retryAfterSec,
52
+ };
53
+ case 'unknown':
54
+ default:
55
+ return {
56
+ headline: `Rate limit hit на ${provider}`,
57
+ body: signal.message?.trim().slice(0, 240) ?? 'Upstream returned a rate-limit signal без structured details.',
58
+ nextStep: retryAfterSec > 0
59
+ ? `Pugi retries в ${formatDuration(retryAfterSec)}.`
60
+ : 'Pugi will retry shortly. Press Ctrl+C to cancel.',
61
+ retryAfterSec,
62
+ };
63
+ }
64
+ }
65
+ function formatTokenBudgetBody(signal) {
66
+ if (signal.used !== undefined && signal.limit !== undefined && signal.window) {
67
+ const pct = Math.min(100, Math.round((signal.used / signal.limit) * 100));
68
+ return `Used ${signal.used.toLocaleString('en-US')} of ${signal.limit.toLocaleString('en-US')} tokens this ${signal.window} (${pct}%).`;
69
+ }
70
+ return signal.message?.trim().slice(0, 240) ?? 'Token budget for this window has been exhausted.';
71
+ }
72
+ function formatRateBody(signal) {
73
+ if (signal.limit !== undefined && signal.window) {
74
+ return `Provider cap is ${signal.limit} requests per ${signal.window}.`;
75
+ }
76
+ return signal.message?.trim().slice(0, 240) ?? 'Upstream is throttling the request burst.';
77
+ }
78
+ function formatProviderQuotaBody(signal) {
79
+ if (signal.message)
80
+ return signal.message.trim().slice(0, 240);
81
+ return 'Provider monthly billing window is exhausted. Pugi cannot route к this model until reset.';
82
+ }
83
+ function formatDuration(seconds) {
84
+ if (seconds < 60)
85
+ return `${seconds}s`;
86
+ if (seconds < 3600) {
87
+ const m = Math.floor(seconds / 60);
88
+ const s = seconds % 60;
89
+ return s > 0 ? `${m}m ${s}s` : `${m}m`;
90
+ }
91
+ const h = Math.floor(seconds / 3600);
92
+ const m = Math.floor((seconds % 3600) / 60);
93
+ return m > 0 ? `${h}h ${m}m` : `${h}h`;
94
+ }
95
+ /**
96
+ * Classify a raw error payload to one of the known kinds. Conservative —
97
+ * defaults к 'unknown' rather than misclassifying. Caller feeds the
98
+ * result back через narrateRateLimit().
99
+ */
100
+ export function classifyRateLimit(raw) {
101
+ const message = (raw.message ?? '').toLowerCase();
102
+ const retryAfterSec = parseRetryAfter(raw.retryAfter ?? raw.headers?.['retry-after']);
103
+ let kind = 'unknown';
104
+ if (raw.status === 429) {
105
+ if (/token|tpm|budget|quota/.test(message))
106
+ kind = 'token-budget';
107
+ else if (/rpm|requests? per|too many requests?/.test(message))
108
+ kind = 'request-rate';
109
+ else if (/concurrent|parallel/.test(message))
110
+ kind = 'concurrent-jobs';
111
+ else
112
+ kind = 'request-rate';
113
+ }
114
+ else if (raw.status === 402) {
115
+ kind = 'provider-quota';
116
+ }
117
+ else if (/rate.?limit/.test(message)) {
118
+ kind = 'request-rate';
119
+ }
120
+ else if (/quota/.test(message)) {
121
+ kind = 'provider-quota';
122
+ }
123
+ const signal = { kind };
124
+ if (raw.message !== undefined)
125
+ signal.message = raw.message;
126
+ if (retryAfterSec >= 0)
127
+ signal.retryAfterSec = retryAfterSec;
128
+ return signal;
129
+ }
130
+ function parseRetryAfter(value) {
131
+ if (value === undefined)
132
+ return -1;
133
+ if (typeof value === 'number')
134
+ return Math.max(0, Math.floor(value));
135
+ const trimmed = value.trim();
136
+ if (/^\d+$/.test(trimmed))
137
+ return Math.max(0, parseInt(trimmed, 10));
138
+ // HTTP-date form: Retry-After: Wed, 21 Oct 2026 07:28:00 GMT
139
+ const t = Date.parse(trimmed);
140
+ if (Number.isFinite(t)) {
141
+ const delta = Math.floor((t - Date.now()) / 1000);
142
+ return Math.max(0, delta);
143
+ }
144
+ return -1;
145
+ }
146
+ //# sourceMappingURL=narrator.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Recipe — saved parameterized multi-step workflows (Pugi backlog #62).
3
+ *
4
+ * Recipes are DIFFERENT from Skills (markdown LLM hints). Recipes are a
5
+ * deterministic parameterized command sequence the operator runs
6
+ * verbatim — Goose pattern. Each step's command is run through
7
+ * `/bin/sh -c` (POSIX), with parameter substitution done at the
8
+ * argv-quoting layer (NOT shell-eval) so a value like
9
+ * `foo;rm -rf /` is passed as a literal single string and NOT split
10
+ * into shell tokens.
11
+ *
12
+ * On-disk layout:
13
+ * .pugi/recipes/<id>.yml repo-local (committed to project)
14
+ * ~/.pugi/recipes/<id>.yml global (per-operator)
15
+ *
16
+ * Repo wins on id conflict — operators can shadow a global recipe by
17
+ * dropping a same-id file in `.pugi/recipes/`.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=cli-types.js.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Recipe loader — walks both global (~/.pugi/recipes/) and repo-local
3
+ * (.pugi/recipes/) directories, parses each `.yml`/`.yaml` file, dedups
4
+ * on recipe id with repo-wins semantics. Parse errors are collected
5
+ * and returned alongside the success map so `recipe list` can surface
6
+ * them as soft warnings without aborting.
7
+ */
8
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { join, resolve } from 'node:path';
11
+ import { parseRecipeFile } from './schema.js';
12
+ export function globalRecipesDir(home) {
13
+ return join(home ?? homedir(), '.pugi', 'recipes');
14
+ }
15
+ export function repoRecipesDir(workspaceRoot) {
16
+ return join(workspaceRoot, '.pugi', 'recipes');
17
+ }
18
+ export function loadRecipes(opts) {
19
+ const globalDir = globalRecipesDir(opts.home);
20
+ const repoDir = repoRecipesDir(opts.workspaceRoot);
21
+ const errors = [];
22
+ const recipes = new Map();
23
+ // Load global first. Repo overlays on top, so repo wins on conflict.
24
+ for (const entry of safeScan(globalDir)) {
25
+ const result = loadFile(entry, 'global');
26
+ if (result.ok) {
27
+ recipes.set(result.recipe.id, result.recipe);
28
+ }
29
+ else {
30
+ errors.push(result.error);
31
+ }
32
+ }
33
+ for (const entry of safeScan(repoDir)) {
34
+ const result = loadFile(entry, 'repo');
35
+ if (result.ok) {
36
+ recipes.set(result.recipe.id, result.recipe);
37
+ }
38
+ else {
39
+ errors.push(result.error);
40
+ }
41
+ }
42
+ return { recipes, errors, globalDir, repoDir };
43
+ }
44
+ function safeScan(dir) {
45
+ if (!existsSync(dir))
46
+ return [];
47
+ let entries;
48
+ try {
49
+ entries = readdirSync(dir);
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ const files = [];
55
+ for (const entry of entries) {
56
+ const full = resolve(dir, entry);
57
+ let info;
58
+ try {
59
+ info = statSync(full);
60
+ }
61
+ catch {
62
+ continue;
63
+ }
64
+ if (!info.isFile())
65
+ continue;
66
+ if (!/\.(yml|yaml)$/.test(entry))
67
+ continue;
68
+ files.push(full);
69
+ }
70
+ // Stable order so list output is deterministic across machines.
71
+ files.sort();
72
+ return files;
73
+ }
74
+ function loadFile(path, origin) {
75
+ const filename = path.split('/').pop() ?? path;
76
+ const id = filename.replace(/\.(yml|yaml)$/, '');
77
+ let body;
78
+ try {
79
+ body = readFileSync(path, 'utf8');
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ return { ok: false, error: { source: path, id, error: `read failed: ${message}` } };
84
+ }
85
+ const parsed = parseRecipeFile(body, { id, source: path, origin });
86
+ if (!parsed.ok) {
87
+ return { ok: false, error: { source: path, id, error: parsed.error } };
88
+ }
89
+ return { ok: true, recipe: parsed.recipe };
90
+ }
91
+ export function resolveRecipe(id, loaded) {
92
+ const direct = loaded.get(id);
93
+ if (direct)
94
+ return { ok: true, recipe: direct };
95
+ const ids = Array.from(loaded.keys());
96
+ const near = ids.find((known) => known.startsWith(id) || id.startsWith(known));
97
+ const suggestion = near ? ` Did you mean '${near}'?` : '';
98
+ return {
99
+ ok: false,
100
+ error: `recipe '${id}' not found.${suggestion}`,
101
+ };
102
+ }
103
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1,345 @@
1
+ /**
2
+ * Recipe runner — execute a parameterized recipe step-by-step.
3
+ *
4
+ * # Security model — parameter substitution
5
+ *
6
+ * `${params.X}` markers in `run:` templates are substituted with
7
+ * shell-quoted values BEFORE the command is handed to `/bin/sh -c`.
8
+ * The substitution wraps every value in single quotes, doubling any
9
+ * embedded single quotes per POSIX:
10
+ *
11
+ * value `foo;rm -rf /`
12
+ * -> `'foo;rm -rf /'`
13
+ * value `it's`
14
+ * -> `'it'\''s'`
15
+ *
16
+ * The result is ONE shell token. Metacharacters (`;`, `&&`, `|`,
17
+ * `$()`, backticks, redirects, newlines) are literal inside the
18
+ * quoted segment — the shell never re-parses them. This is the same
19
+ * pattern `printf %q` produces and the one Goose / `shell-quote`
20
+ * recommend.
21
+ *
22
+ * # Permission gate
23
+ *
24
+ * Each step's command is routed through `evaluateBashPermission` with
25
+ * the operator's current settings.permissions.mode. A `deny` decision
26
+ * halts the run. `allow` / `ask` both proceed — this is an operator-
27
+ * initiated context (no interactive prompt is possible from a recipe
28
+ * run), so an "ask" result is treated as "allow" for the duration of
29
+ * the recipe.
30
+ *
31
+ * # Halt vs continueOnError
32
+ *
33
+ * A non-zero exit halts the run unless the step declares
34
+ * `continueOnError: true`, in which case the next step runs anyway
35
+ * and the failing step is recorded with status `failed`.
36
+ */
37
+ import { spawn } from 'node:child_process';
38
+ import { compileWhenExpression } from './schema.js';
39
+ import { evaluateBashPermission, } from '../permission.js';
40
+ const PARAM_REF_RE = /\$\{params\.([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
41
+ export function resolveParams(recipe, supplied) {
42
+ const out = {};
43
+ for (const spec of recipe.params) {
44
+ const raw = supplied[spec.name];
45
+ if (raw === undefined) {
46
+ if (spec.default !== undefined) {
47
+ out[spec.name] = spec.default;
48
+ continue;
49
+ }
50
+ if (spec.required) {
51
+ return {
52
+ ok: false,
53
+ error: `missing required param '${spec.name}' (--${spec.name}=<value>)`,
54
+ };
55
+ }
56
+ // Unspecified optional param without a default: skip — when a
57
+ // step references it, the substitution layer will report a
58
+ // clean error so the operator knows which step needs it.
59
+ continue;
60
+ }
61
+ const coerced = coerceParam(raw, spec.type);
62
+ if (!coerced.ok) {
63
+ return {
64
+ ok: false,
65
+ error: `param '${spec.name}': ${coerced.error}`,
66
+ };
67
+ }
68
+ out[spec.name] = coerced.value;
69
+ }
70
+ // Reject unknown params so a `--typo=foo` doesn't silently disappear.
71
+ for (const key of Object.keys(supplied)) {
72
+ if (!recipe.params.some((p) => p.name === key)) {
73
+ return {
74
+ ok: false,
75
+ error: `unknown param '${key}' (recipe declares: ${recipe.params.map((p) => p.name).join(', ') || 'none'})`,
76
+ };
77
+ }
78
+ }
79
+ return { ok: true, params: out };
80
+ }
81
+ function coerceParam(raw, type) {
82
+ switch (type) {
83
+ case 'string':
84
+ if (typeof raw === 'string')
85
+ return { ok: true, value: raw };
86
+ if (typeof raw === 'number' || typeof raw === 'boolean') {
87
+ return { ok: true, value: String(raw) };
88
+ }
89
+ return { ok: false, error: `expected string, got ${typeof raw}` };
90
+ case 'boolean':
91
+ if (typeof raw === 'boolean')
92
+ return { ok: true, value: raw };
93
+ if (raw === 'true')
94
+ return { ok: true, value: true };
95
+ if (raw === 'false')
96
+ return { ok: true, value: false };
97
+ return {
98
+ ok: false,
99
+ error: `expected boolean ('true' | 'false'), got '${String(raw)}'`,
100
+ };
101
+ case 'number': {
102
+ if (typeof raw === 'number')
103
+ return { ok: true, value: raw };
104
+ if (typeof raw === 'string') {
105
+ const parsed = Number(raw);
106
+ if (Number.isFinite(parsed))
107
+ return { ok: true, value: parsed };
108
+ }
109
+ return { ok: false, error: `expected number, got '${String(raw)}'` };
110
+ }
111
+ }
112
+ }
113
+ /**
114
+ * Quote a value into a single POSIX-shell-safe token.
115
+ *
116
+ * foo -> 'foo'
117
+ * foo;rm -rf / -> 'foo;rm -rf /'
118
+ * it's -> 'it'\''s'
119
+ *
120
+ * The wrapping single quotes mean nothing inside them is interpreted
121
+ * by the shell — that is the entire point of this layer. Public so
122
+ * test code can assert the contract directly.
123
+ */
124
+ export function shellQuote(value) {
125
+ const str = String(value);
126
+ // Empty string still needs visible quotes so the shell sees a token.
127
+ if (str === '')
128
+ return "''";
129
+ // Pure safe ASCII can pass through unquoted as a micro-optimization;
130
+ // we deliberately DO NOT take that shortcut. Always quote so the
131
+ // contract holds regardless of value content.
132
+ const escaped = str.replace(/'/g, "'\\''");
133
+ return `'${escaped}'`;
134
+ }
135
+ /**
136
+ * Substitute `${params.X}` markers in a template. Each value is
137
+ * shell-quoted. Returns a clean error when a referenced param is
138
+ * absent from the resolved map (optional param without a default and
139
+ * the operator did not pass it).
140
+ *
141
+ * Public for direct injection-test coverage.
142
+ */
143
+ export function substituteCommand(template, params) {
144
+ let missing = null;
145
+ const out = template.replace(PARAM_REF_RE, (_match, rawName) => {
146
+ if (!(rawName in params)) {
147
+ missing = rawName;
148
+ return '';
149
+ }
150
+ const value = params[rawName];
151
+ if (value === undefined) {
152
+ missing = rawName;
153
+ return '';
154
+ }
155
+ return shellQuote(value);
156
+ });
157
+ if (missing !== null) {
158
+ return {
159
+ ok: false,
160
+ error: `step references '\${params.${missing}}' but it is unset (declare a default or pass --${missing})`,
161
+ };
162
+ }
163
+ return { ok: true, command: out };
164
+ }
165
+ const defaultExec = (command, cwd) => new Promise((resolveExec) => {
166
+ const child = spawn('/bin/sh', ['-c', command], {
167
+ cwd,
168
+ stdio: ['ignore', 'pipe', 'pipe'],
169
+ env: process.env,
170
+ });
171
+ const stdoutChunks = [];
172
+ const stderrChunks = [];
173
+ child.stdout.on('data', (chunk) => stdoutChunks.push(chunk));
174
+ child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
175
+ child.on('error', (error) => {
176
+ resolveExec({
177
+ stdout: Buffer.concat(stdoutChunks).toString('utf8'),
178
+ stderr: Buffer.concat(stderrChunks).toString('utf8') +
179
+ `\nspawn error: ${error.message}`,
180
+ exitCode: 126,
181
+ });
182
+ });
183
+ child.on('exit', (code, signal) => {
184
+ const exit = typeof code === 'number'
185
+ ? code
186
+ : signal !== null
187
+ ? 128 + (signalNumber(signal) ?? 0)
188
+ : 0;
189
+ resolveExec({
190
+ stdout: Buffer.concat(stdoutChunks).toString('utf8'),
191
+ stderr: Buffer.concat(stderrChunks).toString('utf8'),
192
+ exitCode: exit,
193
+ });
194
+ });
195
+ });
196
+ function signalNumber(signal) {
197
+ switch (signal) {
198
+ case 'SIGTERM':
199
+ return 15;
200
+ case 'SIGKILL':
201
+ return 9;
202
+ case 'SIGINT':
203
+ return 2;
204
+ default:
205
+ return null;
206
+ }
207
+ }
208
+ /**
209
+ * Execute a recipe step-by-step. Returns the aggregated `RecipeRunResult`
210
+ * with one `StepResult` per declared step (skipped steps still produce
211
+ * an entry so the operator can see why each step did or didn't run).
212
+ */
213
+ export async function runRecipe(opts) {
214
+ const exec = opts.exec ?? defaultExec;
215
+ const now = opts.now ?? Date.now;
216
+ const source = opts.source ?? 'human';
217
+ const additionalDirs = opts.additionalDirectories ?? [];
218
+ const steps = [];
219
+ let aggregate = 'success';
220
+ let haltAfter = false;
221
+ let totalDuration = 0;
222
+ for (const step of opts.recipe.steps) {
223
+ if (haltAfter) {
224
+ steps.push({
225
+ name: step.name,
226
+ command: null,
227
+ status: 'aborted',
228
+ exitCode: null,
229
+ stdout: '',
230
+ stderr: '',
231
+ durationMs: 0,
232
+ denyReason: '',
233
+ });
234
+ continue;
235
+ }
236
+ // when: gate — skip pre-substitution so a templated command never
237
+ // hits the shell when its `when` says no.
238
+ if (step.when !== undefined) {
239
+ const compiled = compileWhenExpression(step.when, opts.recipe.params);
240
+ if (!compiled.ok) {
241
+ steps.push({
242
+ name: step.name,
243
+ command: null,
244
+ status: 'failed',
245
+ exitCode: null,
246
+ stdout: '',
247
+ stderr: `when-clause invalid: ${compiled.error}`,
248
+ durationMs: 0,
249
+ denyReason: '',
250
+ });
251
+ aggregate = 'halted';
252
+ haltAfter = true;
253
+ continue;
254
+ }
255
+ const allowed = compiled.evaluate(opts.params);
256
+ if (!allowed) {
257
+ steps.push({
258
+ name: step.name,
259
+ command: null,
260
+ status: 'skipped',
261
+ exitCode: null,
262
+ stdout: '',
263
+ stderr: '',
264
+ durationMs: 0,
265
+ denyReason: '',
266
+ });
267
+ continue;
268
+ }
269
+ }
270
+ const substituted = substituteCommand(step.run, opts.params);
271
+ if (!substituted.ok) {
272
+ steps.push({
273
+ name: step.name,
274
+ command: null,
275
+ status: 'failed',
276
+ exitCode: null,
277
+ stdout: '',
278
+ stderr: substituted.error,
279
+ durationMs: 0,
280
+ denyReason: '',
281
+ });
282
+ if (!step.continueOnError) {
283
+ aggregate = 'halted';
284
+ haltAfter = true;
285
+ }
286
+ else {
287
+ aggregate = aggregate === 'success' ? 'failed' : aggregate;
288
+ }
289
+ continue;
290
+ }
291
+ const command = substituted.command;
292
+ const decision = evaluateBashPermission(command, opts.permissionMode, {
293
+ workspaceRoot: opts.workspaceRoot,
294
+ additionalDirectories: [...additionalDirs],
295
+ source,
296
+ });
297
+ if (decision.decision === 'deny') {
298
+ steps.push({
299
+ name: step.name,
300
+ command,
301
+ status: 'denied',
302
+ exitCode: null,
303
+ stdout: '',
304
+ stderr: decision.reason,
305
+ durationMs: 0,
306
+ denyReason: decision.reason,
307
+ });
308
+ aggregate = 'denied';
309
+ haltAfter = true;
310
+ continue;
311
+ }
312
+ const startedAt = now();
313
+ const result = await exec(command, opts.workspaceRoot);
314
+ const durationMs = Math.max(0, now() - startedAt);
315
+ totalDuration += durationMs;
316
+ const status = result.exitCode === 0 ? 'success' : 'failed';
317
+ steps.push({
318
+ name: step.name,
319
+ command,
320
+ status,
321
+ exitCode: result.exitCode,
322
+ stdout: result.stdout,
323
+ stderr: result.stderr,
324
+ durationMs,
325
+ denyReason: '',
326
+ });
327
+ if (status === 'failed') {
328
+ if (step.continueOnError) {
329
+ aggregate = aggregate === 'success' ? 'failed' : aggregate;
330
+ }
331
+ else {
332
+ aggregate = 'halted';
333
+ haltAfter = true;
334
+ }
335
+ }
336
+ }
337
+ return {
338
+ id: opts.recipe.id,
339
+ params: opts.params,
340
+ steps,
341
+ status: aggregate,
342
+ durationMs: totalDuration,
343
+ };
344
+ }
345
+ //# sourceMappingURL=runner.js.map