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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +55 -11
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/retro.js +210 -0
  11. package/dist/commands/smoke.js +133 -0
  12. package/dist/core/agent-progress/cleanup.js +134 -0
  13. package/dist/core/agent-progress/schema.js +144 -0
  14. package/dist/core/agent-progress/writer.js +101 -0
  15. package/dist/core/agents/adaptive-router.js +330 -0
  16. package/dist/core/agents/query-decomposer.js +297 -0
  17. package/dist/core/agents/registry.js +3 -3
  18. package/dist/core/approvals/shortcut-resolver.js +98 -0
  19. package/dist/core/artifact-chain/dispatcher.js +148 -0
  20. package/dist/core/artifact-chain/exporter.js +164 -0
  21. package/dist/core/artifact-chain/state.js +243 -0
  22. package/dist/core/artifact-chain/steps.js +169 -0
  23. package/dist/core/ask-user/question.js +92 -0
  24. package/dist/core/audit/audit-trail.js +275 -0
  25. package/dist/core/auth/ensure-authenticated.js +129 -0
  26. package/dist/core/auth/env-provider.js +238 -0
  27. package/dist/core/auto-open-browser.js +4 -4
  28. package/dist/core/auto-update/channels.js +122 -0
  29. package/dist/core/auto-update/checker.js +241 -0
  30. package/dist/core/auto-update/state.js +235 -0
  31. package/dist/core/bare-mode/index.js +107 -0
  32. package/dist/core/bash/redirect.js +281 -0
  33. package/dist/core/bash-classifier.js +436 -40
  34. package/dist/core/checkpoint/resumer.js +149 -0
  35. package/dist/core/checkpoint/rewinder.js +291 -0
  36. package/dist/core/checkpoints/shadow-git.js +670 -0
  37. package/dist/core/citations/parser.js +109 -0
  38. package/dist/core/classifier/yolo-classifier.js +88 -0
  39. package/dist/core/codegraph/db.js +506 -0
  40. package/dist/core/codegraph/decision-store.js +248 -0
  41. package/dist/core/codegraph/detect-repo.js +459 -0
  42. package/dist/core/codegraph/install.js +134 -0
  43. package/dist/core/codegraph/offer-hook.js +220 -0
  44. package/dist/core/codegraph/parser.js +598 -0
  45. package/dist/core/codegraph/queries/go.scm +57 -0
  46. package/dist/core/codegraph/queries/javascript.scm +56 -0
  47. package/dist/core/codegraph/queries/python.scm +55 -0
  48. package/dist/core/codegraph/queries/rust.scm +63 -0
  49. package/dist/core/codegraph/queries/typescript.scm +91 -0
  50. package/dist/core/codegraph/reindex.js +218 -0
  51. package/dist/core/codegraph/resolve-edges.js +107 -0
  52. package/dist/core/codegraph/types.js +34 -0
  53. package/dist/core/codegraph/watcher.js +440 -0
  54. package/dist/core/compact/auto-trigger.js +96 -0
  55. package/dist/core/compact/buffer-rewriter.js +115 -0
  56. package/dist/core/compact/summarizer.js +208 -0
  57. package/dist/core/compact/token-counter.js +108 -0
  58. package/dist/core/consensus/anvil-fanout.js +25 -25
  59. package/dist/core/consensus/diff-capture.js +121 -12
  60. package/dist/core/consensus/rubric.js +21 -21
  61. package/dist/core/context/builder.js +6 -6
  62. package/dist/core/context/compaction-events.js +8 -8
  63. package/dist/core/context/compaction.js +31 -31
  64. package/dist/core/context/index.js +15 -8
  65. package/dist/core/context/invariants.js +51 -51
  66. package/dist/core/context/markdown-loader.js +28 -10
  67. package/dist/core/context/markdown-traverse.js +255 -0
  68. package/dist/core/context/pugiignore.js +41 -41
  69. package/dist/core/context/repo-skeleton.js +37 -37
  70. package/dist/core/context/tool-eviction.js +55 -0
  71. package/dist/core/context/watcher.js +32 -32
  72. package/dist/core/context/working-set.js +23 -23
  73. package/dist/core/coordinator/agent-tools.js +77 -0
  74. package/dist/core/coordinator/agent-toolset.js +65 -0
  75. package/dist/core/coordinator/fsm.js +73 -0
  76. package/dist/core/coordinator/mode-fsm.js +70 -0
  77. package/dist/core/cost/rate-card.js +129 -0
  78. package/dist/core/cost/tracker.js +221 -0
  79. package/dist/core/credentials.js +13 -13
  80. package/dist/core/cron/scheduler.js +138 -0
  81. package/dist/core/denial-tracking/index.js +8 -0
  82. package/dist/core/denial-tracking/state.js +264 -0
  83. package/dist/core/diagnostics/probe-runner.js +93 -0
  84. package/dist/core/diagnostics/probes/api.js +46 -0
  85. package/dist/core/diagnostics/probes/auth.js +93 -0
  86. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  87. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  88. package/dist/core/diagnostics/probes/config.js +72 -0
  89. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  90. package/dist/core/diagnostics/probes/disk.js +81 -0
  91. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  92. package/dist/core/diagnostics/probes/git.js +65 -0
  93. package/dist/core/diagnostics/probes/hooks.js +118 -0
  94. package/dist/core/diagnostics/probes/mcp.js +75 -0
  95. package/dist/core/diagnostics/probes/node.js +59 -0
  96. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  97. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  98. package/dist/core/diagnostics/probes/sandbox.js +67 -0
  99. package/dist/core/diagnostics/probes/session.js +74 -0
  100. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  101. package/dist/core/diagnostics/probes/workspace.js +63 -0
  102. package/dist/core/diagnostics/types.js +70 -0
  103. package/dist/core/dispatch/cache-cleanup.js +197 -0
  104. package/dist/core/dispatch/cache-handoff.js +295 -0
  105. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  106. package/dist/core/edits/dispatch.js +333 -7
  107. package/dist/core/edits/format-detector.js +260 -0
  108. package/dist/core/edits/format-matrix.js +26 -0
  109. package/dist/core/edits/fuzzy-ladder.js +650 -0
  110. package/dist/core/edits/index.js +5 -1
  111. package/dist/core/edits/journal.js +199 -0
  112. package/dist/core/edits/layer-a-apply.js +15 -15
  113. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  114. package/dist/core/edits/layer-b-apply.js +9 -9
  115. package/dist/core/edits/layer-c-apply.js +6 -6
  116. package/dist/core/edits/layer-d-ast.js +557 -14
  117. package/dist/core/edits/marker-parser.js +12 -12
  118. package/dist/core/edits/security-gate.js +27 -27
  119. package/dist/core/edits/verify-hook.js +273 -0
  120. package/dist/core/edits/worktree.js +29 -29
  121. package/dist/core/engine/anvil-client.js +214 -26
  122. package/dist/core/engine/auto-compact.js +247 -0
  123. package/dist/core/engine/budgets.js +220 -0
  124. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  125. package/dist/core/engine/context-prefix.js +155 -0
  126. package/dist/core/engine/index.js +1 -1
  127. package/dist/core/engine/intensity.js +163 -0
  128. package/dist/core/engine/intent.js +260 -0
  129. package/dist/core/engine/native-pugi.js +1559 -227
  130. package/dist/core/engine/prompts.js +219 -19
  131. package/dist/core/engine/strip-internal-fields.js +124 -0
  132. package/dist/core/engine/tool-bridge.js +1887 -59
  133. package/dist/core/engine/verification-patterns.js +195 -0
  134. package/dist/core/eval/v1/ledger.js +83 -0
  135. package/dist/core/eval/v1/runner.js +280 -0
  136. package/dist/core/eval/v1/scoring.js +68 -0
  137. package/dist/core/eval/v1/task-loader.js +191 -0
  138. package/dist/core/eval/v1/types.js +14 -0
  139. package/dist/core/eval/v1/verifier.js +176 -0
  140. package/dist/core/eval/v1/yaml-parser.js +250 -0
  141. package/dist/core/evaluation/golden-dataset.js +293 -0
  142. package/dist/core/feedback/queue.js +177 -0
  143. package/dist/core/feedback/submitter.js +145 -0
  144. package/dist/core/file-cache.js +113 -1
  145. package/dist/core/flatten/flatten-repo.js +439 -0
  146. package/dist/core/format/osc8-link.js +28 -0
  147. package/dist/core/hook-chains.js +392 -0
  148. package/dist/core/hooks/citation-verify-hook.js +138 -0
  149. package/dist/core/hooks/citation-verify.js +112 -0
  150. package/dist/core/hooks/events.js +46 -0
  151. package/dist/core/hooks/index.js +15 -0
  152. package/dist/core/hooks/registry.js +216 -0
  153. package/dist/core/hooks/runner.js +236 -0
  154. package/dist/core/hooks/v2/event-emitter.js +115 -0
  155. package/dist/core/hooks/v2/executor.js +282 -0
  156. package/dist/core/hooks/v2/index.js +25 -0
  157. package/dist/core/hooks/v2/lifecycle.js +104 -0
  158. package/dist/core/hooks/v2/loader.js +216 -0
  159. package/dist/core/hooks/v2/matcher.js +125 -0
  160. package/dist/core/hooks/v2/trust.js +143 -0
  161. package/dist/core/hooks/v2/types.js +86 -0
  162. package/dist/core/hooks/worktree-events.js +158 -0
  163. package/dist/core/image/renderer.js +71 -0
  164. package/dist/core/init/detector.js +582 -0
  165. package/dist/core/init/template-renderer.js +242 -0
  166. package/dist/core/jobs/registry.js +18 -18
  167. package/dist/core/ledger/results-tsv.js +142 -0
  168. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  169. package/dist/core/lsp/cache.js +105 -0
  170. package/dist/core/lsp/client.js +551 -41
  171. package/dist/core/lsp/language-detect.js +66 -0
  172. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  173. package/dist/core/lsp/server-detect.js +173 -0
  174. package/dist/core/lsp/symbol-cache.js +162 -0
  175. package/dist/core/lsp/symbol-tools.js +664 -0
  176. package/dist/core/mcp/client.js +97 -28
  177. package/dist/core/mcp/http-server.js +553 -0
  178. package/dist/core/mcp/orchestrator-config.js +192 -0
  179. package/dist/core/mcp/orchestrator-tools.js +806 -0
  180. package/dist/core/mcp/permission.js +190 -0
  181. package/dist/core/mcp/registry.js +39 -17
  182. package/dist/core/mcp/server-tools.js +219 -0
  183. package/dist/core/mcp/server.js +397 -0
  184. package/dist/core/mcp/trust.js +10 -10
  185. package/dist/core/memory/dual-write.js +416 -0
  186. package/dist/core/memory/passive-extract.js +130 -0
  187. package/dist/core/memory/phase1-kinds.js +20 -0
  188. package/dist/core/memory/secret-scanner.js +304 -0
  189. package/dist/core/memory-sync/queue.js +170 -0
  190. package/dist/core/metrics/extract.js +113 -0
  191. package/dist/core/modes/roo-modes.js +68 -0
  192. package/dist/core/notes/notes-paths.js +113 -0
  193. package/dist/core/notes/notes-recorder.js +140 -0
  194. package/dist/core/notes/notes-writer.js +53 -0
  195. package/dist/core/notes/renderers.js +0 -0
  196. package/dist/core/notes/slug.js +105 -0
  197. package/dist/core/onboarding/ensure-initialized.js +133 -0
  198. package/dist/core/onboarding/marker.js +111 -0
  199. package/dist/core/onboarding/telemetry-state.js +108 -0
  200. package/dist/core/output-style/presets.js +176 -0
  201. package/dist/core/output-style/state.js +185 -0
  202. package/dist/core/path-security.js +287 -5
  203. package/dist/core/permission.js +82 -22
  204. package/dist/core/permissions/auto-classifier.js +124 -0
  205. package/dist/core/permissions/bash-parser.js +371 -0
  206. package/dist/core/permissions/circuit-breaker.js +83 -0
  207. package/dist/core/permissions/constrained-edit.js +91 -0
  208. package/dist/core/permissions/gate.js +278 -0
  209. package/dist/core/permissions/index.js +20 -0
  210. package/dist/core/permissions/mode.js +174 -0
  211. package/dist/core/permissions/network-egress.js +137 -0
  212. package/dist/core/permissions/state.js +241 -0
  213. package/dist/core/permissions/tool-class.js +107 -0
  214. package/dist/core/plan-mode/ui-state.js +51 -0
  215. package/dist/core/plans/plan-artifact.js +721 -0
  216. package/dist/core/policy-limits/etag-store.js +122 -0
  217. package/dist/core/prd-check/parser.js +215 -0
  218. package/dist/core/prd-check/reporter.js +127 -0
  219. package/dist/core/prd-check/session-review.js +557 -0
  220. package/dist/core/prd-check/verifiers.js +223 -0
  221. package/dist/core/prompt-cache/client-cache.js +99 -0
  222. package/dist/core/prompts/assembly.js +29 -0
  223. package/dist/core/prompts/registry.js +364 -0
  224. package/dist/core/pugi-gitignore.js +52 -0
  225. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  226. package/dist/core/pugi-md/context-injector.js +76 -0
  227. package/dist/core/pugi-md/walk-up.js +207 -0
  228. package/dist/core/python/uv-installer.js +270 -0
  229. package/dist/core/python/uv-resolver.js +83 -0
  230. package/dist/core/rate-limit/narrator.js +146 -0
  231. package/dist/core/recipes/cli-types.js +20 -0
  232. package/dist/core/recipes/loader.js +103 -0
  233. package/dist/core/recipes/runner.js +345 -0
  234. package/dist/core/recipes/schema.js +587 -0
  235. package/dist/core/release-notes/parser.js +241 -0
  236. package/dist/core/release-notes/state.js +116 -0
  237. package/dist/core/repl/ask.js +37 -37
  238. package/dist/core/repl/cancellation.js +26 -26
  239. package/dist/core/repl/cap-warning.js +4 -4
  240. package/dist/core/repl/clipboard-read.js +11 -11
  241. package/dist/core/repl/dispatch-fsm.js +12 -12
  242. package/dist/core/repl/engine-bridge.js +303 -0
  243. package/dist/core/repl/history-search.js +15 -15
  244. package/dist/core/repl/history.js +28 -18
  245. package/dist/core/repl/kill-ring.js +5 -5
  246. package/dist/core/repl/model-pricing.js +135 -0
  247. package/dist/core/repl/privacy-banner.js +22 -22
  248. package/dist/core/repl/session.js +2690 -229
  249. package/dist/core/repl/slash-commands.js +540 -41
  250. package/dist/core/repl/store/index.js +1 -1
  251. package/dist/core/repl/store/jsonl-log.js +22 -22
  252. package/dist/core/repl/store/lockfile.js +10 -10
  253. package/dist/core/repl/store/session-store.js +136 -107
  254. package/dist/core/repl/store/types.js +15 -15
  255. package/dist/core/repl/store/uuid-v7.js +12 -12
  256. package/dist/core/repl/tool-route.js +382 -0
  257. package/dist/core/repl/workspace-context.js +43 -21
  258. package/dist/core/repo-map/build.js +125 -0
  259. package/dist/core/repo-map/cache.js +185 -0
  260. package/dist/core/repo-map/extractor.js +254 -0
  261. package/dist/core/repo-map/formatter.js +145 -0
  262. package/dist/core/repo-map/page-rank.js +105 -0
  263. package/dist/core/repo-map/scanner.js +211 -0
  264. package/dist/core/retro/git-collector.js +251 -0
  265. package/dist/core/retro/health-card.js +25 -0
  266. package/dist/core/retro/metrics.js +342 -0
  267. package/dist/core/retro/narrative.js +249 -0
  268. package/dist/core/retro/plane-collector.js +274 -0
  269. package/dist/core/retro/pr-issue-link.js +65 -0
  270. package/dist/core/retro/types.js +16 -0
  271. package/dist/core/retry-budget/budget.js +284 -0
  272. package/dist/core/retry-budget/index.js +5 -0
  273. package/dist/core/retry-budget/retry-cap.js +74 -0
  274. package/dist/core/routing/lead-worker.js +43 -0
  275. package/dist/core/routing/pre-flight-estimator.js +108 -0
  276. package/dist/core/runs/run-tree.js +103 -0
  277. package/dist/core/sandboxing/adapter.js +43 -0
  278. package/dist/core/sandboxing/bubblewrap.js +209 -0
  279. package/dist/core/sandboxing/index.js +78 -0
  280. package/dist/core/sandboxing/none.js +19 -0
  281. package/dist/core/sandboxing/policy.js +97 -0
  282. package/dist/core/sandboxing/seatbelt.js +231 -0
  283. package/dist/core/security/injection-scanner.js +367 -0
  284. package/dist/core/security/output-filter.js +418 -0
  285. package/dist/core/session/env-file.js +105 -0
  286. package/dist/core/session/section-budgets.js +140 -0
  287. package/dist/core/session.js +119 -0
  288. package/dist/core/settings.js +402 -5
  289. package/dist/core/share/formatter.js +271 -0
  290. package/dist/core/share/redactor.js +221 -0
  291. package/dist/core/share/uploader.js +267 -0
  292. package/dist/core/skills/defaults.js +30 -30
  293. package/dist/core/skills/loader.js +22 -22
  294. package/dist/core/skills/sources.js +27 -27
  295. package/dist/core/smoke/headless-driver.js +174 -0
  296. package/dist/core/smoke/orchestrator.js +194 -0
  297. package/dist/core/smoke/runner.js +238 -0
  298. package/dist/core/smoke/scenario-parser.js +316 -0
  299. package/dist/core/statusline.js +99 -0
  300. package/dist/core/subagents/dispatcher-real.js +600 -0
  301. package/dist/core/subagents/dispatcher.js +146 -52
  302. package/dist/core/subagents/index.js +19 -6
  303. package/dist/core/subagents/isolation-matrix.js +213 -0
  304. package/dist/core/subagents/spawn.js +19 -4
  305. package/dist/core/telemetry/emitter.js +229 -0
  306. package/dist/core/telemetry/queue.js +251 -0
  307. package/dist/core/theme/context.js +91 -0
  308. package/dist/core/theme/presets.js +228 -0
  309. package/dist/core/theme/state.js +181 -0
  310. package/dist/core/todos/invariant.js +10 -0
  311. package/dist/core/todos/state.js +177 -0
  312. package/dist/core/tool-schema/compressor.js +89 -0
  313. package/dist/core/transport/version-interceptor.js +166 -0
  314. package/dist/core/trust.js +2 -2
  315. package/dist/core/tui/thinking-block.js +64 -0
  316. package/dist/core/vim/keymap.js +288 -0
  317. package/dist/core/vim/state.js +92 -0
  318. package/dist/core/watch-markers/marker-watcher.js +133 -0
  319. package/dist/core/worktree/include-parser.js +249 -0
  320. package/dist/core/worktree-manager/cleanup.js +123 -0
  321. package/dist/core/worktree-manager/manager.js +303 -0
  322. package/dist/index.js +36 -0
  323. package/dist/runtime/bootstrap.js +190 -0
  324. package/dist/runtime/cli.js +4403 -561
  325. package/dist/runtime/commands/agents.js +31 -31
  326. package/dist/runtime/commands/budget.js +5 -5
  327. package/dist/runtime/commands/cancel.js +231 -0
  328. package/dist/runtime/commands/chain.js +489 -0
  329. package/dist/runtime/commands/codegraph-status.js +227 -0
  330. package/dist/runtime/commands/compact.js +297 -0
  331. package/dist/runtime/commands/config.js +74 -40
  332. package/dist/runtime/commands/cost.js +199 -0
  333. package/dist/runtime/commands/delegate.js +27 -4
  334. package/dist/runtime/commands/dispatch.js +126 -0
  335. package/dist/runtime/commands/doctor.js +579 -0
  336. package/dist/runtime/commands/eval-v1.js +266 -0
  337. package/dist/runtime/commands/feedback.js +184 -0
  338. package/dist/runtime/commands/hooks.js +187 -0
  339. package/dist/runtime/commands/index-cmd.js +459 -0
  340. package/dist/runtime/commands/init.js +254 -0
  341. package/dist/runtime/commands/lsp.js +200 -38
  342. package/dist/runtime/commands/mcp.js +935 -0
  343. package/dist/runtime/commands/memory.js +582 -0
  344. package/dist/runtime/commands/model.js +237 -0
  345. package/dist/runtime/commands/onboarding.js +275 -0
  346. package/dist/runtime/commands/patch.js +12 -12
  347. package/dist/runtime/commands/permissions.js +112 -0
  348. package/dist/runtime/commands/plan.js +143 -0
  349. package/dist/runtime/commands/prd-check.js +285 -0
  350. package/dist/runtime/commands/privacy.js +17 -17
  351. package/dist/runtime/commands/recipe.js +325 -0
  352. package/dist/runtime/commands/redo-blob-store.js +92 -0
  353. package/dist/runtime/commands/redo.js +361 -0
  354. package/dist/runtime/commands/release-notes.js +229 -0
  355. package/dist/runtime/commands/repo-map.js +95 -0
  356. package/dist/runtime/commands/report.js +299 -0
  357. package/dist/runtime/commands/resume.js +118 -0
  358. package/dist/runtime/commands/review-consensus.js +68 -53
  359. package/dist/runtime/commands/rewind.js +333 -0
  360. package/dist/runtime/commands/roster.js +14 -14
  361. package/dist/runtime/commands/servers-cli.js +182 -0
  362. package/dist/runtime/commands/servers.js +236 -0
  363. package/dist/runtime/commands/sessions.js +163 -0
  364. package/dist/runtime/commands/share.js +316 -0
  365. package/dist/runtime/commands/skills.js +31 -31
  366. package/dist/runtime/commands/status.js +186 -0
  367. package/dist/runtime/commands/stickers.js +82 -0
  368. package/dist/runtime/commands/style.js +194 -0
  369. package/dist/runtime/commands/theme.js +196 -0
  370. package/dist/runtime/commands/undo.js +54 -22
  371. package/dist/runtime/commands/update.js +289 -0
  372. package/dist/runtime/commands/vim.js +140 -0
  373. package/dist/runtime/commands/worktree.js +8 -8
  374. package/dist/runtime/commands/worktrees.js +155 -0
  375. package/dist/runtime/deprecation-warning.js +69 -0
  376. package/dist/runtime/engine-exit-code.js +50 -0
  377. package/dist/runtime/headless-repl.js +195 -0
  378. package/dist/runtime/headless.js +548 -0
  379. package/dist/runtime/load-hooks-or-exit.js +71 -0
  380. package/dist/runtime/plan-decompose.js +22 -22
  381. package/dist/runtime/sigint-guard.js +272 -0
  382. package/dist/runtime/stream-renderer.js +195 -0
  383. package/dist/runtime/update-check.js +28 -28
  384. package/dist/runtime/version.js +65 -0
  385. package/dist/runtime/worktree-bootstrap.js +579 -0
  386. package/dist/skills/bundled/batch.js +617 -0
  387. package/dist/skills/bundled/index.js +45 -0
  388. package/dist/skills/bundled/loop.js +358 -0
  389. package/dist/skills/bundled/remember.js +383 -0
  390. package/dist/skills/bundled/simplify.js +289 -0
  391. package/dist/skills/bundled/skillify.js +373 -0
  392. package/dist/skills/bundled/stuck.js +558 -0
  393. package/dist/skills/bundled/verify.js +439 -0
  394. package/dist/testing/vcr.js +486 -0
  395. package/dist/tools/agent-tool.js +229 -0
  396. package/dist/tools/apply-patch.js +89 -28
  397. package/dist/tools/ask-user-question.js +337 -0
  398. package/dist/tools/ask-user.js +115 -0
  399. package/dist/tools/bash.js +811 -49
  400. package/dist/tools/brief.js +224 -0
  401. package/dist/tools/cron.js +433 -0
  402. package/dist/tools/enter-worktree.js +250 -0
  403. package/dist/tools/exit-worktree.js +147 -0
  404. package/dist/tools/file-tools.js +161 -44
  405. package/dist/tools/http-request.js +336 -0
  406. package/dist/tools/lsp-tools.js +377 -1
  407. package/dist/tools/mcp-tool.js +260 -0
  408. package/dist/tools/multi-edit.js +361 -0
  409. package/dist/tools/powershell.js +268 -0
  410. package/dist/tools/registry.js +120 -5
  411. package/dist/tools/server-tools.js +892 -0
  412. package/dist/tools/skill-tool.js +96 -0
  413. package/dist/tools/sleep.js +99 -0
  414. package/dist/tools/synthetic-output.js +133 -0
  415. package/dist/tools/tasks.js +208 -0
  416. package/dist/tools/todo-write.js +184 -0
  417. package/dist/tools/verify-plan-execution.js +295 -0
  418. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  419. package/dist/tools/web-fetch.js +195 -10
  420. package/dist/tools/web-search.js +458 -0
  421. package/dist/tui/agent-progress-card.js +111 -0
  422. package/dist/tui/agent-tree.js +22 -1
  423. package/dist/tui/ask-modal.js +14 -14
  424. package/dist/tui/ask-user-question-chips.js +315 -0
  425. package/dist/tui/ask-user-question-prompt.js +203 -0
  426. package/dist/tui/compact-banner.js +81 -0
  427. package/dist/tui/conversation-pane.js +85 -11
  428. package/dist/tui/cost-table.js +111 -0
  429. package/dist/tui/device-flow.js +2 -2
  430. package/dist/tui/doctor-table.js +46 -0
  431. package/dist/tui/feedback-prompt.js +156 -0
  432. package/dist/tui/input-box.js +247 -32
  433. package/dist/tui/login-picker.js +3 -3
  434. package/dist/tui/markdown-render.js +6 -6
  435. package/dist/tui/multi-file-diff-approval.js +375 -0
  436. package/dist/tui/onboarding-wizard.js +240 -0
  437. package/dist/tui/permissions-picker.js +86 -0
  438. package/dist/tui/render.js +36 -1
  439. package/dist/tui/repl-render.js +239 -25
  440. package/dist/tui/repl-splash-art.js +16 -16
  441. package/dist/tui/repl-splash-mascot.js +48 -24
  442. package/dist/tui/repl-splash.js +22 -22
  443. package/dist/tui/repl.js +125 -45
  444. package/dist/tui/slash-palette.js +6 -6
  445. package/dist/tui/splash.js +2 -2
  446. package/dist/tui/status-bar.js +109 -31
  447. package/dist/tui/status-table.js +7 -0
  448. package/dist/tui/stickers-art.js +136 -0
  449. package/dist/tui/style-table.js +28 -0
  450. package/dist/tui/theme-table.js +29 -0
  451. package/dist/tui/thinking-spinner.js +123 -0
  452. package/dist/tui/tool-stream-pane.js +53 -4
  453. package/dist/tui/update-banner.js +27 -2
  454. package/dist/tui/vim-input.js +267 -0
  455. package/dist/tui/welcome-banner.js +107 -0
  456. package/dist/tui/welcome-data.js +293 -0
  457. package/dist/tui/workspace-context.js +2 -2
  458. package/package.json +29 -6
  459. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  460. package/test/scenarios/compact-force.scenario.txt +12 -0
  461. package/test/scenarios/identity.scenario.txt +11 -0
  462. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  463. package/test/scenarios/walkback.scenario.txt +12 -0
  464. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Task loader for pugi-eval-v1.
3
+ *
4
+ * Reads `<root>/eval/v1/tasks/<NN>-<slug>.task.yml`, parses via the
5
+ * embedded minimal YAML parser, validates via Zod, then asserts the
6
+ * filename matches the parsed `id` so a refactor cannot silently
7
+ * decouple the basename from the schema id.
8
+ *
9
+ * The loader also exposes `loadTaskManifest` which is the spec-side
10
+ * gate that pins task content via sha256 and refuses mismatches (the
11
+ * frozen-benchmark invariant from backlog #120).
12
+ */
13
+ import { createHash } from 'node:crypto';
14
+ import { readdirSync, readFileSync } from 'node:fs';
15
+ import { basename, join, resolve } from 'node:path';
16
+ import { z } from 'zod';
17
+ import { parseTaskYaml } from './yaml-parser.js';
18
+ const TASK_ID_RE = /^\d{2}-[a-z][a-z0-9-]*$/;
19
+ const TASK_FILENAME_RE = /^(\d{2}-[a-z][a-z0-9-]*)\.task\.yml$/;
20
+ const verificationSchema = z.discriminatedUnion('kind', [
21
+ z
22
+ .object({ kind: z.literal('file_exists'), path: z.string().min(1) })
23
+ .strict(),
24
+ z
25
+ .object({
26
+ kind: z.literal('file_contains'),
27
+ path: z.string().min(1),
28
+ pattern: z.string().min(1),
29
+ mode: z.enum(['literal', 'regex']).optional(),
30
+ })
31
+ .strict(),
32
+ z
33
+ .object({
34
+ kind: z.literal('output_contains'),
35
+ pattern: z.string().min(1),
36
+ mode: z.enum(['literal', 'regex']).optional(),
37
+ })
38
+ .strict(),
39
+ z
40
+ .object({
41
+ kind: z.literal('command_exit_code'),
42
+ command: z.string().min(1),
43
+ expectedExitCode: z.number().int(),
44
+ timeoutMs: z.number().int().positive().optional(),
45
+ })
46
+ .strict(),
47
+ ]);
48
+ const taskSchema = z
49
+ .object({
50
+ id: z.string().regex(TASK_ID_RE, 'id must match <NN>-<slug>'),
51
+ difficulty: z.enum(['simple', 'medium', 'hard']),
52
+ intensity: z.enum(['quick', 'standard', 'deep', 'marathon']),
53
+ command: z.enum(['code', 'fix', 'explain', 'plan', 'build']),
54
+ brief: z.string().min(1),
55
+ fixture: z.record(z.string(), z.string()).optional(),
56
+ verification: z.array(verificationSchema).min(1),
57
+ maxTokens: z.number().int().positive(),
58
+ maxTurns: z.number().int().positive(),
59
+ timeoutMs: z.number().int().positive(),
60
+ })
61
+ .strict();
62
+ /**
63
+ * Default tasks directory relative to the @pugi/cli workspace root.
64
+ * Tests inject a different directory; production resolves it through
65
+ * `defaultTasksDir`.
66
+ */
67
+ export function defaultTasksDir(packageRoot) {
68
+ return resolve(packageRoot, 'eval', 'v1', 'tasks');
69
+ }
70
+ export function defaultManifestPath(packageRoot) {
71
+ return resolve(packageRoot, 'eval', 'v1', 'manifest.json');
72
+ }
73
+ export function defaultLedgerPath(packageRoot) {
74
+ return resolve(packageRoot, 'eval', 'v1', 'results.tsv');
75
+ }
76
+ export function listTaskFiles(tasksDir) {
77
+ let entries;
78
+ try {
79
+ entries = readdirSync(tasksDir);
80
+ }
81
+ catch (err) {
82
+ throw new Error(`eval-v1 tasks directory not found at ${tasksDir}: ${err.message}`);
83
+ }
84
+ const files = entries.filter((e) => TASK_FILENAME_RE.test(e)).sort();
85
+ return files.map((f) => join(tasksDir, f));
86
+ }
87
+ export function loadTaskFile(path) {
88
+ const raw = readFileSync(path, 'utf8');
89
+ const filenameMatch = TASK_FILENAME_RE.exec(basename(path));
90
+ if (!filenameMatch) {
91
+ throw new Error(`eval-v1: filename ${basename(path)} does not match <NN>-<slug>.task.yml`);
92
+ }
93
+ const expectedId = filenameMatch[1];
94
+ let parsed;
95
+ try {
96
+ parsed = parseTaskYaml(raw);
97
+ }
98
+ catch (err) {
99
+ throw new Error(`eval-v1 task ${basename(path)} failed YAML parse: ${err.message}`);
100
+ }
101
+ const result = taskSchema.safeParse(parsed);
102
+ if (!result.success) {
103
+ throw new Error(`eval-v1 task ${basename(path)} failed schema validation: ${result.error.message}`);
104
+ }
105
+ if (result.data.id !== expectedId) {
106
+ throw new Error(`eval-v1 task ${basename(path)} id field ${result.data.id} does not match filename ${expectedId}`);
107
+ }
108
+ return { path, raw, spec: result.data };
109
+ }
110
+ export function loadAllTasks(tasksDir) {
111
+ const files = listTaskFiles(tasksDir);
112
+ const loaded = files.map((f) => loadTaskFile(f));
113
+ // Detect duplicate ids that survived (cannot happen given filename
114
+ // regex but defensive against future refactors).
115
+ const seen = new Set();
116
+ for (const entry of loaded) {
117
+ if (seen.has(entry.spec.id)) {
118
+ throw new Error(`eval-v1: duplicate task id ${entry.spec.id}`);
119
+ }
120
+ seen.add(entry.spec.id);
121
+ }
122
+ return loaded;
123
+ }
124
+ export function manifestEntryFor(path, raw) {
125
+ const filenameMatch = TASK_FILENAME_RE.exec(basename(path));
126
+ if (!filenameMatch) {
127
+ throw new Error(`cannot derive manifest entry: filename ${basename(path)} does not match`);
128
+ }
129
+ const id = filenameMatch[1];
130
+ const sha = createHash('sha256').update(raw).digest('hex');
131
+ return { id, sha256: sha, byteLength: Buffer.byteLength(raw, 'utf8') };
132
+ }
133
+ export function readManifest(manifestPath) {
134
+ const raw = readFileSync(manifestPath, 'utf8');
135
+ const parsed = JSON.parse(raw);
136
+ const schema = z
137
+ .object({
138
+ schemaVersion: z.literal(1),
139
+ generatedAt: z.string(),
140
+ entries: z
141
+ .array(z
142
+ .object({
143
+ id: z.string().regex(TASK_ID_RE),
144
+ sha256: z.string().regex(/^[0-9a-f]{64}$/),
145
+ byteLength: z.number().int().nonnegative(),
146
+ })
147
+ .strict())
148
+ .min(1),
149
+ })
150
+ .strict();
151
+ const result = schema.safeParse(parsed);
152
+ if (!result.success) {
153
+ throw new Error(`eval-v1 manifest ${manifestPath} invalid: ${result.error.message}`);
154
+ }
155
+ return result.data;
156
+ }
157
+ /**
158
+ * Compare the on-disk task files against the committed manifest. Used
159
+ * by the meta-spec to enforce the frozen-benchmark invariant.
160
+ */
161
+ export function diffManifest(tasks, manifest) {
162
+ const reasons = [];
163
+ const computed = new Map();
164
+ for (const t of tasks) {
165
+ const entry = manifestEntryFor(t.path, t.raw);
166
+ computed.set(entry.id, entry);
167
+ }
168
+ const declared = new Map();
169
+ for (const e of manifest.entries)
170
+ declared.set(e.id, e);
171
+ for (const [id, entry] of computed) {
172
+ const decl = declared.get(id);
173
+ if (!decl) {
174
+ reasons.push(`task ${id} present on disk but missing from manifest`);
175
+ continue;
176
+ }
177
+ if (decl.sha256 !== entry.sha256) {
178
+ reasons.push(`task ${id} sha256 mismatch (disk=${entry.sha256.slice(0, 12)} manifest=${decl.sha256.slice(0, 12)})`);
179
+ }
180
+ if (decl.byteLength !== entry.byteLength) {
181
+ reasons.push(`task ${id} byteLength mismatch (disk=${entry.byteLength} manifest=${decl.byteLength})`);
182
+ }
183
+ }
184
+ for (const [id] of declared) {
185
+ if (!computed.has(id)) {
186
+ reasons.push(`task ${id} declared in manifest but missing on disk`);
187
+ }
188
+ }
189
+ return { ok: reasons.length === 0, reasons };
190
+ }
191
+ //# sourceMappingURL=task-loader.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * pugi-eval-v1 type definitions (backlog #120, Reviewer foundation).
3
+ *
4
+ * Frozen benchmark harness types. The shapes here are stable: every
5
+ * field added later must preserve backward compatibility with the v1
6
+ * `results.tsv` ledger columns and the v1 task YAML schema. Breaking
7
+ * changes ship as `eval-v2`.
8
+ *
9
+ * Why types live in `core/eval/v1/` and not next to the CLI command:
10
+ * the meta-spec, ledger, scoring, and verifier all consume them. CLI
11
+ * command modules stay thin wrappers per the project convention.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Verification check executor for pugi-eval-v1.
3
+ *
4
+ * Each check kind has a pure implementation. The runner calls
5
+ * `runVerifications` after the CLI subprocess exits, passing the
6
+ * post-run workspace root + the final stdout text the engine emitted.
7
+ *
8
+ * Path safety: `file_exists` and `file_contains` resolve the path
9
+ * relative to the workspace root and refuse anything that escapes the
10
+ * root (`..` traversal, absolute paths, symlinks). A task spec that
11
+ * tries to peek outside the workspace fails the check rather than
12
+ * leaking host state.
13
+ */
14
+ import { spawnSync } from 'node:child_process';
15
+ import { existsSync, readFileSync, realpathSync, statSync } from 'node:fs';
16
+ import { isAbsolute, relative, resolve } from 'node:path';
17
+ const DEFAULT_COMMAND_TIMEOUT_MS = 60_000;
18
+ function withinRoot(rootReal, candidateReal) {
19
+ if (candidateReal === rootReal)
20
+ return true;
21
+ const rel = relative(rootReal, candidateReal);
22
+ return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
23
+ }
24
+ /**
25
+ * Resolve `workspaceRoot` to its realpath once so symlinked tmp dirs
26
+ * (macOS `/tmp` -> `/private/tmp`) compare correctly against any
27
+ * resolved child path. Falls back к the raw root when the directory
28
+ * does not yet exist (defensive - production callers always create
29
+ * the tmp dir before invoking).
30
+ */
31
+ function realRoot(workspaceRoot) {
32
+ try {
33
+ return realpathSync(workspaceRoot);
34
+ }
35
+ catch {
36
+ return workspaceRoot;
37
+ }
38
+ }
39
+ function resolveWorkspacePath(workspaceRoot, path) {
40
+ if (isAbsolute(path)) {
41
+ return { ok: false, reason: `absolute paths refused (${path})` };
42
+ }
43
+ if (path.split(/[\\/]/).includes('..')) {
44
+ return { ok: false, reason: `path traversal refused (${path})` };
45
+ }
46
+ const root = realRoot(workspaceRoot);
47
+ const absolute = resolve(root, path);
48
+ if (!withinRoot(root, absolute)) {
49
+ return { ok: false, reason: `path escapes workspace root (${path})` };
50
+ }
51
+ // realpath check: if the file exists, the realpath must also be
52
+ // inside the workspace root - symlinks that point outside are a
53
+ // refusal even when the entry itself is inside.
54
+ if (existsSync(absolute)) {
55
+ try {
56
+ const real = realpathSync(absolute);
57
+ if (!withinRoot(root, real)) {
58
+ return {
59
+ ok: false,
60
+ reason: `symlink target escapes workspace root (${path})`,
61
+ };
62
+ }
63
+ }
64
+ catch {
65
+ // Stat failure during realpath: surface as missing rather than
66
+ // throwing - the file_exists check will record the failure.
67
+ }
68
+ }
69
+ return { ok: true, absolute };
70
+ }
71
+ function matches(haystack, pattern, mode) {
72
+ if (mode === 'regex') {
73
+ let re;
74
+ try {
75
+ re = new RegExp(pattern);
76
+ }
77
+ catch (err) {
78
+ return {
79
+ ok: false,
80
+ reason: `invalid regex ${pattern}: ${err.message}`,
81
+ };
82
+ }
83
+ return re.test(haystack)
84
+ ? { ok: true, reason: '' }
85
+ : { ok: false, reason: `regex ${pattern} did not match` };
86
+ }
87
+ return haystack.includes(pattern)
88
+ ? { ok: true, reason: '' }
89
+ : { ok: false, reason: `literal ${JSON.stringify(pattern)} not found` };
90
+ }
91
+ export function runVerification(check, ctx) {
92
+ switch (check.kind) {
93
+ case 'file_exists': {
94
+ const r = resolveWorkspacePath(ctx.workspaceRoot, check.path);
95
+ if (!r.ok)
96
+ return { kind: check.kind, passed: false, detail: r.reason };
97
+ try {
98
+ const stat = statSync(r.absolute);
99
+ if (!stat.isFile()) {
100
+ return {
101
+ kind: check.kind,
102
+ passed: false,
103
+ detail: `${check.path} exists but is not a regular file`,
104
+ };
105
+ }
106
+ return { kind: check.kind, passed: true, detail: '' };
107
+ }
108
+ catch {
109
+ return {
110
+ kind: check.kind,
111
+ passed: false,
112
+ detail: `${check.path} not found`,
113
+ };
114
+ }
115
+ }
116
+ case 'file_contains': {
117
+ const r = resolveWorkspacePath(ctx.workspaceRoot, check.path);
118
+ if (!r.ok)
119
+ return { kind: check.kind, passed: false, detail: r.reason };
120
+ let body;
121
+ try {
122
+ body = readFileSync(r.absolute, 'utf8');
123
+ }
124
+ catch {
125
+ return {
126
+ kind: check.kind,
127
+ passed: false,
128
+ detail: `${check.path} not readable`,
129
+ };
130
+ }
131
+ const m = matches(body, check.pattern, check.mode);
132
+ return {
133
+ kind: check.kind,
134
+ passed: m.ok,
135
+ detail: m.ok ? '' : `${check.path}: ${m.reason}`,
136
+ };
137
+ }
138
+ case 'output_contains': {
139
+ const m = matches(ctx.finalText, check.pattern, check.mode);
140
+ return {
141
+ kind: check.kind,
142
+ passed: m.ok,
143
+ detail: m.ok ? '' : `final output: ${m.reason}`,
144
+ };
145
+ }
146
+ case 'command_exit_code': {
147
+ const timeout = check.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
148
+ const result = spawnSync('bash', ['-lc', check.command], {
149
+ cwd: ctx.workspaceRoot,
150
+ timeout,
151
+ encoding: 'utf8',
152
+ env: { ...process.env, CI: '1' },
153
+ });
154
+ if (result.error) {
155
+ return {
156
+ kind: check.kind,
157
+ passed: false,
158
+ detail: `${check.command}: ${result.error.message}`,
159
+ };
160
+ }
161
+ const exit = result.status ?? -1;
162
+ if (exit !== check.expectedExitCode) {
163
+ return {
164
+ kind: check.kind,
165
+ passed: false,
166
+ detail: `${check.command}: exit ${exit} expected ${check.expectedExitCode}`,
167
+ };
168
+ }
169
+ return { kind: check.kind, passed: true, detail: '' };
170
+ }
171
+ }
172
+ }
173
+ export function runVerifications(checks, ctx) {
174
+ return checks.map((c) => runVerification(c, ctx));
175
+ }
176
+ //# sourceMappingURL=verifier.js.map
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Minimal YAML parser for pugi-eval-v1 task files.
3
+ *
4
+ * Same rationale as `core/recipes/schema.ts`: pulling in `js-yaml`
5
+ * is ~70 KB of installed weight for a schema we control. The shape
6
+ * here is intentionally narrow:
7
+ *
8
+ * - Top-level scalars (string / number / boolean).
9
+ * - Block sequences of scalars (e.g. plain string lists).
10
+ * - Block sequences of maps (verification entries).
11
+ * - Nested block maps (fixture file map).
12
+ * - Multi-line block scalars via `|` (literal) and `>` (folded).
13
+ * - `#` line comments.
14
+ *
15
+ * Unsupported YAML features: flow collections, anchors, tags,
16
+ * directives, document separators. The parser throws on anything
17
+ * outside the supported subset so a malformed task file fails fast
18
+ * with a clear line number.
19
+ *
20
+ * The parsed value is `unknown`; downstream loader code applies a Zod
21
+ * schema to narrow it. The parser deliberately does NOT do schema
22
+ * validation - it only enforces YAML well-formedness.
23
+ */
24
+ export class TaskYamlParseError extends Error {
25
+ line;
26
+ constructor(message, line) {
27
+ super(`task YAML parse error at line ${line}: ${message}`);
28
+ this.line = line;
29
+ this.name = 'TaskYamlParseError';
30
+ }
31
+ }
32
+ /**
33
+ * Tokenise the source into non-blank, non-comment lines + their
34
+ * indentation. Tab characters are rejected (YAML spec) so the indent
35
+ * count is unambiguous.
36
+ */
37
+ function tokenise(source) {
38
+ const physical = source.split(/\r?\n/);
39
+ const out = [];
40
+ for (let i = 0; i < physical.length; i += 1) {
41
+ const raw = physical[i] ?? '';
42
+ const lineNo = i + 1;
43
+ if (raw.includes('\t')) {
44
+ throw new TaskYamlParseError('tab characters are not allowed; use spaces for indentation', lineNo);
45
+ }
46
+ const stripped = raw.replace(/\s*#.*$/, '');
47
+ if (stripped.trim() === '')
48
+ continue;
49
+ const indent = stripped.length - stripped.replace(/^ +/, '').length;
50
+ out.push({ lineNo, indent, body: stripped.slice(indent) });
51
+ }
52
+ return out;
53
+ }
54
+ /**
55
+ * Convert a literal scalar (after the `:` or after a `-` marker) into
56
+ * a JS value. Recognises `true`/`false`, integers, floats, single-
57
+ * and double-quoted strings; everything else is returned verbatim as
58
+ * an unquoted string.
59
+ */
60
+ function coerceScalar(raw, lineNo) {
61
+ const trimmed = raw.trim();
62
+ if (trimmed === '')
63
+ return '';
64
+ if (trimmed === 'true')
65
+ return true;
66
+ if (trimmed === 'false')
67
+ return false;
68
+ if (trimmed === 'null' || trimmed === '~')
69
+ return '';
70
+ if (/^-?\d+$/.test(trimmed))
71
+ return Number(trimmed);
72
+ if (/^-?\d+\.\d+$/.test(trimmed))
73
+ return Number(trimmed);
74
+ const doubleQuoted = /^"((?:\\.|[^"\\])*)"$/.exec(trimmed);
75
+ if (doubleQuoted) {
76
+ // Decode common escape sequences. This subset matches what the
77
+ // task spec needs (newlines inside regex patterns, escaped
78
+ // backslashes).
79
+ return doubleQuoted[1].replace(/\\(.)/g, (_, c) => {
80
+ if (c === 'n')
81
+ return '\n';
82
+ if (c === 't')
83
+ return '\t';
84
+ if (c === 'r')
85
+ return '\r';
86
+ if (c === '"')
87
+ return '"';
88
+ if (c === '\\')
89
+ return '\\';
90
+ throw new TaskYamlParseError(`unknown escape \\${c} in double-quoted string`, lineNo);
91
+ });
92
+ }
93
+ const singleQuoted = /^'((?:''|[^'])*)'$/.exec(trimmed);
94
+ if (singleQuoted) {
95
+ return singleQuoted[1].replace(/''/g, "'");
96
+ }
97
+ return trimmed;
98
+ }
99
+ /**
100
+ * Block scalar reader for `|` (literal, preserves newlines) and `>`
101
+ * (folded, newlines collapse to spaces). Joins continuation lines
102
+ * whose indent is strictly greater than the parent map's indent.
103
+ */
104
+ function readBlockScalar(marker, parentIndent, lines, cursor) {
105
+ const collected = [];
106
+ let baseIndent = -1;
107
+ while (cursor.i < lines.length) {
108
+ const next = lines[cursor.i];
109
+ if (next.indent <= parentIndent)
110
+ break;
111
+ if (baseIndent < 0)
112
+ baseIndent = next.indent;
113
+ const slice = ' '.repeat(Math.max(0, next.indent - baseIndent)) + next.body;
114
+ collected.push(slice);
115
+ cursor.i += 1;
116
+ }
117
+ if (marker === '|')
118
+ return collected.join('\n');
119
+ return collected.join(' ');
120
+ }
121
+ /**
122
+ * Recursively parse a block beginning at `cursor.i` whose body lives
123
+ * strictly deeper than `parentIndent`. The return value is either a
124
+ * map (object) or a sequence (array); the caller decides which based
125
+ * on the first child line.
126
+ */
127
+ function parseBlock(lines, cursor, parentIndent) {
128
+ if (cursor.i >= lines.length)
129
+ return {};
130
+ const first = lines[cursor.i];
131
+ if (first.indent <= parentIndent)
132
+ return {};
133
+ const blockIndent = first.indent;
134
+ const isSequence = first.body.startsWith('- ') || first.body === '-';
135
+ if (isSequence) {
136
+ const arr = [];
137
+ while (cursor.i < lines.length) {
138
+ const line = lines[cursor.i];
139
+ if (line.indent < blockIndent)
140
+ break;
141
+ if (line.indent > blockIndent) {
142
+ throw new TaskYamlParseError(`unexpected indent ${line.indent} in sequence (expected ${blockIndent})`, line.lineNo);
143
+ }
144
+ if (!line.body.startsWith('-')) {
145
+ throw new TaskYamlParseError(`expected sequence marker '-' but found ${line.body.slice(0, 10)}`, line.lineNo);
146
+ }
147
+ const after = line.body.replace(/^-\s*/, '');
148
+ cursor.i += 1;
149
+ if (after === '') {
150
+ arr.push(parseBlock(lines, cursor, blockIndent));
151
+ continue;
152
+ }
153
+ // Inline `- key: value` first entry; treat the rest of the line
154
+ // as a single-key map entry, then continue collecting deeper
155
+ // siblings into the same map.
156
+ const colonIdx = after.indexOf(':');
157
+ if (colonIdx === -1) {
158
+ arr.push(coerceScalar(after, line.lineNo));
159
+ continue;
160
+ }
161
+ const key = after.slice(0, colonIdx).trim();
162
+ const valRaw = after.slice(colonIdx + 1).trim();
163
+ const entry = {};
164
+ if (valRaw === '' || valRaw === '|' || valRaw === '>') {
165
+ if (valRaw === '|' || valRaw === '>') {
166
+ entry[key] = readBlockScalar(valRaw, blockIndent, lines, cursor);
167
+ }
168
+ else {
169
+ entry[key] = parseBlock(lines, cursor, blockIndent);
170
+ }
171
+ }
172
+ else {
173
+ entry[key] = coerceScalar(valRaw, line.lineNo);
174
+ }
175
+ // Continue absorbing deeper siblings of this synthetic map.
176
+ while (cursor.i < lines.length) {
177
+ const sibling = lines[cursor.i];
178
+ // Sibling map keys live at the same indent as the inline
179
+ // `key: value` text (which is at `blockIndent + 2` relative to
180
+ // the `-` marker). Anything at blockIndent or shallower
181
+ // belongs to the parent.
182
+ const childIndent = blockIndent + 2;
183
+ if (sibling.indent < childIndent)
184
+ break;
185
+ if (sibling.indent > childIndent) {
186
+ throw new TaskYamlParseError(`unexpected indent ${sibling.indent} (expected ${childIndent})`, sibling.lineNo);
187
+ }
188
+ const sibColon = sibling.body.indexOf(':');
189
+ if (sibColon === -1) {
190
+ throw new TaskYamlParseError(`expected key:value in map but found ${sibling.body.slice(0, 20)}`, sibling.lineNo);
191
+ }
192
+ const sibKey = sibling.body.slice(0, sibColon).trim();
193
+ const sibVal = sibling.body.slice(sibColon + 1).trim();
194
+ cursor.i += 1;
195
+ if (sibVal === '' || sibVal === '|' || sibVal === '>') {
196
+ if (sibVal === '|' || sibVal === '>') {
197
+ entry[sibKey] = readBlockScalar(sibVal, childIndent, lines, cursor);
198
+ }
199
+ else {
200
+ entry[sibKey] = parseBlock(lines, cursor, childIndent);
201
+ }
202
+ }
203
+ else {
204
+ entry[sibKey] = coerceScalar(sibVal, sibling.lineNo);
205
+ }
206
+ }
207
+ arr.push(entry);
208
+ }
209
+ return arr;
210
+ }
211
+ const map = {};
212
+ while (cursor.i < lines.length) {
213
+ const line = lines[cursor.i];
214
+ if (line.indent < blockIndent)
215
+ break;
216
+ if (line.indent > blockIndent) {
217
+ throw new TaskYamlParseError(`unexpected indent ${line.indent} (expected ${blockIndent})`, line.lineNo);
218
+ }
219
+ const colonIdx = line.body.indexOf(':');
220
+ if (colonIdx === -1) {
221
+ throw new TaskYamlParseError(`expected key:value but found ${line.body.slice(0, 20)}`, line.lineNo);
222
+ }
223
+ const key = line.body.slice(0, colonIdx).trim();
224
+ const valRaw = line.body.slice(colonIdx + 1).trim();
225
+ cursor.i += 1;
226
+ if (valRaw === '') {
227
+ map[key] = parseBlock(lines, cursor, blockIndent);
228
+ continue;
229
+ }
230
+ if (valRaw === '|' || valRaw === '>') {
231
+ map[key] = readBlockScalar(valRaw, blockIndent, lines, cursor);
232
+ continue;
233
+ }
234
+ map[key] = coerceScalar(valRaw, line.lineNo);
235
+ }
236
+ return map;
237
+ }
238
+ export function parseTaskYaml(source) {
239
+ const lines = tokenise(source);
240
+ if (lines.length === 0)
241
+ return {};
242
+ const cursor = { i: 0 };
243
+ const result = parseBlock(lines, cursor, -1);
244
+ if (cursor.i !== lines.length) {
245
+ const stray = lines[cursor.i];
246
+ throw new TaskYamlParseError(`stray content after document body`, stray.lineNo);
247
+ }
248
+ return result;
249
+ }
250
+ //# sourceMappingURL=yaml-parser.js.map