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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (445) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +53 -11
  4. package/assets/pugi-prozr2-mascot.ansi +9 -0
  5. package/bin/run.js +33 -1
  6. package/dist/commands/deploy.js +40 -40
  7. package/dist/commands/flatten.js +191 -0
  8. package/dist/commands/jobs-watch.js +201 -0
  9. package/dist/commands/jobs.js +42 -27
  10. package/dist/commands/retro.js +210 -0
  11. package/dist/commands/smoke.js +133 -0
  12. package/dist/core/agent-progress/cleanup.js +134 -0
  13. package/dist/core/agent-progress/schema.js +144 -0
  14. package/dist/core/agent-progress/writer.js +101 -0
  15. package/dist/core/agents/adaptive-router.js +330 -0
  16. package/dist/core/agents/query-decomposer.js +297 -0
  17. package/dist/core/agents/registry.js +3 -3
  18. package/dist/core/approvals/shortcut-resolver.js +98 -0
  19. package/dist/core/artifact-chain/dispatcher.js +148 -0
  20. package/dist/core/artifact-chain/exporter.js +164 -0
  21. package/dist/core/artifact-chain/state.js +243 -0
  22. package/dist/core/artifact-chain/steps.js +169 -0
  23. package/dist/core/ask-user/question.js +92 -0
  24. package/dist/core/audit/audit-trail.js +275 -0
  25. package/dist/core/auth/ensure-authenticated.js +129 -0
  26. package/dist/core/auth/env-provider.js +238 -0
  27. package/dist/core/auto-open-browser.js +4 -4
  28. package/dist/core/auto-update/channels.js +122 -0
  29. package/dist/core/auto-update/checker.js +241 -0
  30. package/dist/core/auto-update/state.js +235 -0
  31. package/dist/core/bare-mode/index.js +107 -0
  32. package/dist/core/bash/redirect.js +281 -0
  33. package/dist/core/bash-classifier.js +436 -40
  34. package/dist/core/checkpoint/resumer.js +149 -0
  35. package/dist/core/checkpoint/rewinder.js +291 -0
  36. package/dist/core/checkpoints/shadow-git.js +670 -0
  37. package/dist/core/citations/parser.js +109 -0
  38. package/dist/core/classifier/yolo-classifier.js +88 -0
  39. package/dist/core/codegraph/db.js +506 -0
  40. package/dist/core/codegraph/decision-store.js +248 -0
  41. package/dist/core/codegraph/detect-repo.js +459 -0
  42. package/dist/core/codegraph/install.js +134 -0
  43. package/dist/core/codegraph/offer-hook.js +220 -0
  44. package/dist/core/codegraph/parser.js +71 -0
  45. package/dist/core/codegraph/types.js +34 -0
  46. package/dist/core/compact/auto-trigger.js +96 -0
  47. package/dist/core/compact/buffer-rewriter.js +115 -0
  48. package/dist/core/compact/summarizer.js +208 -0
  49. package/dist/core/compact/token-counter.js +108 -0
  50. package/dist/core/consensus/anvil-fanout.js +25 -25
  51. package/dist/core/consensus/diff-capture.js +121 -12
  52. package/dist/core/consensus/rubric.js +21 -21
  53. package/dist/core/context/builder.js +6 -6
  54. package/dist/core/context/compaction-events.js +8 -8
  55. package/dist/core/context/compaction.js +31 -31
  56. package/dist/core/context/index.js +15 -8
  57. package/dist/core/context/invariants.js +51 -51
  58. package/dist/core/context/markdown-loader.js +28 -10
  59. package/dist/core/context/markdown-traverse.js +255 -0
  60. package/dist/core/context/pugiignore.js +41 -41
  61. package/dist/core/context/repo-skeleton.js +37 -37
  62. package/dist/core/context/tool-eviction.js +55 -0
  63. package/dist/core/context/watcher.js +32 -32
  64. package/dist/core/context/working-set.js +23 -23
  65. package/dist/core/coordinator/agent-tools.js +77 -0
  66. package/dist/core/coordinator/agent-toolset.js +65 -0
  67. package/dist/core/coordinator/fsm.js +73 -0
  68. package/dist/core/coordinator/mode-fsm.js +70 -0
  69. package/dist/core/cost/rate-card.js +129 -0
  70. package/dist/core/cost/tracker.js +221 -0
  71. package/dist/core/credentials.js +13 -13
  72. package/dist/core/cron/scheduler.js +138 -0
  73. package/dist/core/denial-tracking/index.js +8 -0
  74. package/dist/core/denial-tracking/state.js +264 -0
  75. package/dist/core/diagnostics/probe-runner.js +93 -0
  76. package/dist/core/diagnostics/probes/api.js +46 -0
  77. package/dist/core/diagnostics/probes/auth.js +93 -0
  78. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  79. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  80. package/dist/core/diagnostics/probes/config.js +72 -0
  81. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  82. package/dist/core/diagnostics/probes/disk.js +81 -0
  83. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  84. package/dist/core/diagnostics/probes/git.js +65 -0
  85. package/dist/core/diagnostics/probes/hooks.js +118 -0
  86. package/dist/core/diagnostics/probes/mcp.js +75 -0
  87. package/dist/core/diagnostics/probes/node.js +59 -0
  88. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  89. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  90. package/dist/core/diagnostics/probes/sandbox.js +72 -0
  91. package/dist/core/diagnostics/probes/session.js +74 -0
  92. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  93. package/dist/core/diagnostics/probes/workspace.js +63 -0
  94. package/dist/core/diagnostics/types.js +70 -0
  95. package/dist/core/dispatch/cache-cleanup.js +197 -0
  96. package/dist/core/dispatch/cache-handoff.js +295 -0
  97. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  98. package/dist/core/edits/dispatch.js +333 -7
  99. package/dist/core/edits/format-detector.js +260 -0
  100. package/dist/core/edits/format-matrix.js +26 -0
  101. package/dist/core/edits/fuzzy-ladder.js +650 -0
  102. package/dist/core/edits/index.js +5 -1
  103. package/dist/core/edits/journal.js +199 -0
  104. package/dist/core/edits/layer-a-apply.js +15 -15
  105. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  106. package/dist/core/edits/layer-b-apply.js +9 -9
  107. package/dist/core/edits/layer-c-apply.js +6 -6
  108. package/dist/core/edits/layer-d-ast.js +557 -14
  109. package/dist/core/edits/marker-parser.js +12 -12
  110. package/dist/core/edits/security-gate.js +27 -27
  111. package/dist/core/edits/verify-hook.js +273 -0
  112. package/dist/core/edits/worktree.js +29 -29
  113. package/dist/core/engine/anvil-client.js +214 -26
  114. package/dist/core/engine/auto-compact.js +247 -0
  115. package/dist/core/engine/budgets.js +220 -0
  116. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  117. package/dist/core/engine/context-prefix.js +155 -0
  118. package/dist/core/engine/index.js +1 -1
  119. package/dist/core/engine/intensity.js +163 -0
  120. package/dist/core/engine/intent.js +260 -0
  121. package/dist/core/engine/native-pugi.js +1559 -227
  122. package/dist/core/engine/prompts.js +187 -19
  123. package/dist/core/engine/strip-internal-fields.js +124 -0
  124. package/dist/core/engine/tool-bridge.js +1887 -59
  125. package/dist/core/engine/verification-patterns.js +195 -0
  126. package/dist/core/evaluation/golden-dataset.js +293 -0
  127. package/dist/core/feedback/queue.js +177 -0
  128. package/dist/core/feedback/submitter.js +145 -0
  129. package/dist/core/file-cache.js +113 -1
  130. package/dist/core/flatten/flatten-repo.js +439 -0
  131. package/dist/core/format/osc8-link.js +28 -0
  132. package/dist/core/hook-chains.js +392 -0
  133. package/dist/core/hooks/citation-verify-hook.js +138 -0
  134. package/dist/core/hooks/citation-verify.js +112 -0
  135. package/dist/core/hooks/events.js +46 -0
  136. package/dist/core/hooks/index.js +15 -0
  137. package/dist/core/hooks/registry.js +216 -0
  138. package/dist/core/hooks/runner.js +236 -0
  139. package/dist/core/hooks/v2/event-emitter.js +115 -0
  140. package/dist/core/hooks/v2/executor.js +282 -0
  141. package/dist/core/hooks/v2/index.js +25 -0
  142. package/dist/core/hooks/v2/lifecycle.js +104 -0
  143. package/dist/core/hooks/v2/loader.js +216 -0
  144. package/dist/core/hooks/v2/matcher.js +125 -0
  145. package/dist/core/hooks/v2/trust.js +143 -0
  146. package/dist/core/hooks/v2/types.js +86 -0
  147. package/dist/core/hooks/worktree-events.js +158 -0
  148. package/dist/core/image/renderer.js +71 -0
  149. package/dist/core/init/detector.js +582 -0
  150. package/dist/core/init/template-renderer.js +242 -0
  151. package/dist/core/jobs/registry.js +18 -18
  152. package/dist/core/ledger/results-tsv.js +142 -0
  153. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  154. package/dist/core/lsp/cache.js +105 -0
  155. package/dist/core/lsp/client.js +551 -41
  156. package/dist/core/lsp/language-detect.js +66 -0
  157. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  158. package/dist/core/lsp/server-detect.js +173 -0
  159. package/dist/core/lsp/symbol-cache.js +162 -0
  160. package/dist/core/lsp/symbol-tools.js +664 -0
  161. package/dist/core/mcp/client.js +97 -28
  162. package/dist/core/mcp/http-server.js +553 -0
  163. package/dist/core/mcp/orchestrator-config.js +192 -0
  164. package/dist/core/mcp/orchestrator-tools.js +806 -0
  165. package/dist/core/mcp/permission.js +190 -0
  166. package/dist/core/mcp/registry.js +39 -17
  167. package/dist/core/mcp/server-tools.js +219 -0
  168. package/dist/core/mcp/server.js +397 -0
  169. package/dist/core/mcp/trust.js +10 -10
  170. package/dist/core/memory/dual-write.js +416 -0
  171. package/dist/core/memory/passive-extract.js +130 -0
  172. package/dist/core/memory/phase1-kinds.js +20 -0
  173. package/dist/core/memory/secret-scanner.js +304 -0
  174. package/dist/core/memory-sync/queue.js +170 -0
  175. package/dist/core/metrics/extract.js +113 -0
  176. package/dist/core/modes/roo-modes.js +68 -0
  177. package/dist/core/notes/notes-paths.js +113 -0
  178. package/dist/core/notes/notes-recorder.js +140 -0
  179. package/dist/core/notes/notes-writer.js +53 -0
  180. package/dist/core/notes/renderers.js +0 -0
  181. package/dist/core/notes/slug.js +105 -0
  182. package/dist/core/onboarding/ensure-initialized.js +133 -0
  183. package/dist/core/onboarding/marker.js +111 -0
  184. package/dist/core/onboarding/telemetry-state.js +108 -0
  185. package/dist/core/output-style/presets.js +176 -0
  186. package/dist/core/output-style/state.js +185 -0
  187. package/dist/core/path-security.js +287 -5
  188. package/dist/core/permission.js +82 -22
  189. package/dist/core/permissions/auto-classifier.js +124 -0
  190. package/dist/core/permissions/bash-parser.js +371 -0
  191. package/dist/core/permissions/circuit-breaker.js +83 -0
  192. package/dist/core/permissions/constrained-edit.js +91 -0
  193. package/dist/core/permissions/gate.js +278 -0
  194. package/dist/core/permissions/index.js +20 -0
  195. package/dist/core/permissions/mode.js +174 -0
  196. package/dist/core/permissions/network-egress.js +137 -0
  197. package/dist/core/permissions/state.js +241 -0
  198. package/dist/core/permissions/tool-class.js +107 -0
  199. package/dist/core/plan-mode/ui-state.js +51 -0
  200. package/dist/core/plans/plan-artifact.js +721 -0
  201. package/dist/core/policy-limits/etag-store.js +122 -0
  202. package/dist/core/prd-check/parser.js +215 -0
  203. package/dist/core/prd-check/reporter.js +127 -0
  204. package/dist/core/prd-check/session-review.js +557 -0
  205. package/dist/core/prd-check/verifiers.js +223 -0
  206. package/dist/core/prompt-cache/client-cache.js +99 -0
  207. package/dist/core/prompts/assembly.js +29 -0
  208. package/dist/core/prompts/registry.js +364 -0
  209. package/dist/core/pugi-gitignore.js +52 -0
  210. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  211. package/dist/core/pugi-md/context-injector.js +76 -0
  212. package/dist/core/pugi-md/walk-up.js +207 -0
  213. package/dist/core/python/uv-installer.js +270 -0
  214. package/dist/core/python/uv-resolver.js +83 -0
  215. package/dist/core/rate-limit/narrator.js +146 -0
  216. package/dist/core/recipes/cli-types.js +20 -0
  217. package/dist/core/recipes/loader.js +103 -0
  218. package/dist/core/recipes/runner.js +345 -0
  219. package/dist/core/recipes/schema.js +587 -0
  220. package/dist/core/release-notes/parser.js +241 -0
  221. package/dist/core/release-notes/state.js +116 -0
  222. package/dist/core/repl/ask.js +37 -37
  223. package/dist/core/repl/cancellation.js +26 -26
  224. package/dist/core/repl/cap-warning.js +4 -4
  225. package/dist/core/repl/clipboard-read.js +11 -11
  226. package/dist/core/repl/dispatch-fsm.js +12 -12
  227. package/dist/core/repl/engine-bridge.js +303 -0
  228. package/dist/core/repl/history-search.js +15 -15
  229. package/dist/core/repl/history.js +28 -18
  230. package/dist/core/repl/kill-ring.js +5 -5
  231. package/dist/core/repl/model-pricing.js +135 -0
  232. package/dist/core/repl/privacy-banner.js +22 -22
  233. package/dist/core/repl/session.js +2690 -229
  234. package/dist/core/repl/slash-commands.js +540 -41
  235. package/dist/core/repl/store/index.js +1 -1
  236. package/dist/core/repl/store/jsonl-log.js +22 -22
  237. package/dist/core/repl/store/lockfile.js +10 -10
  238. package/dist/core/repl/store/session-store.js +136 -107
  239. package/dist/core/repl/store/types.js +15 -15
  240. package/dist/core/repl/store/uuid-v7.js +12 -12
  241. package/dist/core/repl/tool-route.js +382 -0
  242. package/dist/core/repl/workspace-context.js +43 -21
  243. package/dist/core/repo-map/build.js +125 -0
  244. package/dist/core/repo-map/cache.js +185 -0
  245. package/dist/core/repo-map/extractor.js +254 -0
  246. package/dist/core/repo-map/formatter.js +145 -0
  247. package/dist/core/repo-map/page-rank.js +105 -0
  248. package/dist/core/repo-map/scanner.js +211 -0
  249. package/dist/core/retro/git-collector.js +251 -0
  250. package/dist/core/retro/health-card.js +25 -0
  251. package/dist/core/retro/metrics.js +342 -0
  252. package/dist/core/retro/narrative.js +249 -0
  253. package/dist/core/retro/plane-collector.js +274 -0
  254. package/dist/core/retro/pr-issue-link.js +65 -0
  255. package/dist/core/retro/types.js +16 -0
  256. package/dist/core/retry-budget/budget.js +284 -0
  257. package/dist/core/retry-budget/index.js +5 -0
  258. package/dist/core/retry-budget/retry-cap.js +74 -0
  259. package/dist/core/routing/lead-worker.js +43 -0
  260. package/dist/core/routing/pre-flight-estimator.js +108 -0
  261. package/dist/core/runs/run-tree.js +103 -0
  262. package/dist/core/sandboxing/adapter.js +29 -0
  263. package/dist/core/sandboxing/index.js +49 -0
  264. package/dist/core/sandboxing/none.js +19 -0
  265. package/dist/core/sandboxing/seatbelt.js +183 -0
  266. package/dist/core/security/injection-scanner.js +367 -0
  267. package/dist/core/security/output-filter.js +418 -0
  268. package/dist/core/session/env-file.js +105 -0
  269. package/dist/core/session/section-budgets.js +140 -0
  270. package/dist/core/session.js +119 -0
  271. package/dist/core/settings.js +378 -5
  272. package/dist/core/share/formatter.js +271 -0
  273. package/dist/core/share/redactor.js +221 -0
  274. package/dist/core/share/uploader.js +267 -0
  275. package/dist/core/skills/defaults.js +30 -30
  276. package/dist/core/skills/loader.js +22 -22
  277. package/dist/core/skills/sources.js +27 -27
  278. package/dist/core/smoke/headless-driver.js +174 -0
  279. package/dist/core/smoke/orchestrator.js +194 -0
  280. package/dist/core/smoke/runner.js +238 -0
  281. package/dist/core/smoke/scenario-parser.js +316 -0
  282. package/dist/core/statusline.js +99 -0
  283. package/dist/core/subagents/dispatcher-real.js +600 -0
  284. package/dist/core/subagents/dispatcher.js +146 -52
  285. package/dist/core/subagents/index.js +19 -6
  286. package/dist/core/subagents/isolation-matrix.js +213 -0
  287. package/dist/core/subagents/spawn.js +19 -4
  288. package/dist/core/telemetry/emitter.js +229 -0
  289. package/dist/core/telemetry/queue.js +251 -0
  290. package/dist/core/theme/context.js +91 -0
  291. package/dist/core/theme/presets.js +228 -0
  292. package/dist/core/theme/state.js +181 -0
  293. package/dist/core/todos/invariant.js +10 -0
  294. package/dist/core/todos/state.js +177 -0
  295. package/dist/core/tool-schema/compressor.js +89 -0
  296. package/dist/core/transport/version-interceptor.js +166 -0
  297. package/dist/core/trust.js +2 -2
  298. package/dist/core/tui/thinking-block.js +64 -0
  299. package/dist/core/vim/keymap.js +288 -0
  300. package/dist/core/vim/state.js +92 -0
  301. package/dist/core/watch-markers/marker-watcher.js +133 -0
  302. package/dist/core/worktree/include-parser.js +249 -0
  303. package/dist/core/worktree-manager/cleanup.js +123 -0
  304. package/dist/core/worktree-manager/manager.js +303 -0
  305. package/dist/index.js +36 -0
  306. package/dist/runtime/bootstrap.js +190 -0
  307. package/dist/runtime/cli.js +4345 -561
  308. package/dist/runtime/commands/agents.js +31 -31
  309. package/dist/runtime/commands/budget.js +5 -5
  310. package/dist/runtime/commands/cancel.js +231 -0
  311. package/dist/runtime/commands/chain.js +489 -0
  312. package/dist/runtime/commands/codegraph-status.js +227 -0
  313. package/dist/runtime/commands/compact.js +297 -0
  314. package/dist/runtime/commands/config.js +74 -40
  315. package/dist/runtime/commands/cost.js +199 -0
  316. package/dist/runtime/commands/delegate.js +27 -4
  317. package/dist/runtime/commands/dispatch.js +126 -0
  318. package/dist/runtime/commands/doctor.js +579 -0
  319. package/dist/runtime/commands/feedback.js +184 -0
  320. package/dist/runtime/commands/hooks.js +187 -0
  321. package/dist/runtime/commands/index-cmd.js +353 -0
  322. package/dist/runtime/commands/init.js +254 -0
  323. package/dist/runtime/commands/lsp.js +200 -38
  324. package/dist/runtime/commands/mcp.js +935 -0
  325. package/dist/runtime/commands/memory.js +582 -0
  326. package/dist/runtime/commands/model.js +237 -0
  327. package/dist/runtime/commands/onboarding.js +275 -0
  328. package/dist/runtime/commands/patch.js +12 -12
  329. package/dist/runtime/commands/permissions.js +112 -0
  330. package/dist/runtime/commands/plan.js +143 -0
  331. package/dist/runtime/commands/prd-check.js +285 -0
  332. package/dist/runtime/commands/privacy.js +17 -17
  333. package/dist/runtime/commands/recipe.js +325 -0
  334. package/dist/runtime/commands/redo-blob-store.js +92 -0
  335. package/dist/runtime/commands/redo.js +361 -0
  336. package/dist/runtime/commands/release-notes.js +229 -0
  337. package/dist/runtime/commands/repo-map.js +95 -0
  338. package/dist/runtime/commands/report.js +299 -0
  339. package/dist/runtime/commands/resume.js +118 -0
  340. package/dist/runtime/commands/review-consensus.js +68 -53
  341. package/dist/runtime/commands/rewind.js +333 -0
  342. package/dist/runtime/commands/roster.js +14 -14
  343. package/dist/runtime/commands/servers.js +236 -0
  344. package/dist/runtime/commands/sessions.js +163 -0
  345. package/dist/runtime/commands/share.js +316 -0
  346. package/dist/runtime/commands/skills.js +31 -31
  347. package/dist/runtime/commands/status.js +186 -0
  348. package/dist/runtime/commands/stickers.js +82 -0
  349. package/dist/runtime/commands/style.js +194 -0
  350. package/dist/runtime/commands/theme.js +196 -0
  351. package/dist/runtime/commands/undo.js +54 -22
  352. package/dist/runtime/commands/update.js +289 -0
  353. package/dist/runtime/commands/vim.js +140 -0
  354. package/dist/runtime/commands/worktree.js +8 -8
  355. package/dist/runtime/commands/worktrees.js +155 -0
  356. package/dist/runtime/deprecation-warning.js +69 -0
  357. package/dist/runtime/engine-exit-code.js +50 -0
  358. package/dist/runtime/headless-repl.js +195 -0
  359. package/dist/runtime/headless.js +548 -0
  360. package/dist/runtime/load-hooks-or-exit.js +71 -0
  361. package/dist/runtime/plan-decompose.js +22 -22
  362. package/dist/runtime/sigint-guard.js +272 -0
  363. package/dist/runtime/stream-renderer.js +195 -0
  364. package/dist/runtime/update-check.js +28 -28
  365. package/dist/runtime/version.js +65 -0
  366. package/dist/runtime/worktree-bootstrap.js +579 -0
  367. package/dist/skills/bundled/batch.js +617 -0
  368. package/dist/skills/bundled/index.js +45 -0
  369. package/dist/skills/bundled/loop.js +358 -0
  370. package/dist/skills/bundled/remember.js +383 -0
  371. package/dist/skills/bundled/simplify.js +289 -0
  372. package/dist/skills/bundled/skillify.js +373 -0
  373. package/dist/skills/bundled/stuck.js +558 -0
  374. package/dist/skills/bundled/verify.js +439 -0
  375. package/dist/testing/vcr.js +486 -0
  376. package/dist/tools/agent-tool.js +229 -0
  377. package/dist/tools/apply-patch.js +89 -28
  378. package/dist/tools/ask-user-question.js +337 -0
  379. package/dist/tools/ask-user.js +115 -0
  380. package/dist/tools/bash.js +624 -46
  381. package/dist/tools/brief.js +224 -0
  382. package/dist/tools/cron.js +433 -0
  383. package/dist/tools/enter-worktree.js +250 -0
  384. package/dist/tools/exit-worktree.js +147 -0
  385. package/dist/tools/file-tools.js +161 -44
  386. package/dist/tools/http-request.js +336 -0
  387. package/dist/tools/lsp-tools.js +377 -1
  388. package/dist/tools/mcp-tool.js +260 -0
  389. package/dist/tools/multi-edit.js +361 -0
  390. package/dist/tools/powershell.js +268 -0
  391. package/dist/tools/registry.js +120 -5
  392. package/dist/tools/server-tools.js +892 -0
  393. package/dist/tools/skill-tool.js +96 -0
  394. package/dist/tools/sleep.js +99 -0
  395. package/dist/tools/synthetic-output.js +133 -0
  396. package/dist/tools/tasks.js +208 -0
  397. package/dist/tools/todo-write.js +184 -0
  398. package/dist/tools/verify-plan-execution.js +295 -0
  399. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  400. package/dist/tools/web-fetch.js +195 -10
  401. package/dist/tools/web-search.js +458 -0
  402. package/dist/tui/agent-progress-card.js +111 -0
  403. package/dist/tui/agent-tree.js +22 -1
  404. package/dist/tui/ask-modal.js +14 -14
  405. package/dist/tui/ask-user-question-chips.js +315 -0
  406. package/dist/tui/ask-user-question-prompt.js +203 -0
  407. package/dist/tui/compact-banner.js +81 -0
  408. package/dist/tui/conversation-pane.js +85 -11
  409. package/dist/tui/cost-table.js +111 -0
  410. package/dist/tui/device-flow.js +2 -2
  411. package/dist/tui/doctor-table.js +46 -0
  412. package/dist/tui/feedback-prompt.js +156 -0
  413. package/dist/tui/input-box.js +247 -32
  414. package/dist/tui/login-picker.js +3 -3
  415. package/dist/tui/markdown-render.js +6 -6
  416. package/dist/tui/multi-file-diff-approval.js +375 -0
  417. package/dist/tui/onboarding-wizard.js +240 -0
  418. package/dist/tui/permissions-picker.js +86 -0
  419. package/dist/tui/render.js +36 -1
  420. package/dist/tui/repl-render.js +239 -25
  421. package/dist/tui/repl-splash-art.js +16 -16
  422. package/dist/tui/repl-splash-mascot.js +48 -24
  423. package/dist/tui/repl-splash.js +22 -22
  424. package/dist/tui/repl.js +125 -45
  425. package/dist/tui/slash-palette.js +6 -6
  426. package/dist/tui/splash.js +2 -2
  427. package/dist/tui/status-bar.js +109 -31
  428. package/dist/tui/status-table.js +7 -0
  429. package/dist/tui/stickers-art.js +136 -0
  430. package/dist/tui/style-table.js +28 -0
  431. package/dist/tui/theme-table.js +29 -0
  432. package/dist/tui/thinking-spinner.js +123 -0
  433. package/dist/tui/tool-stream-pane.js +53 -4
  434. package/dist/tui/update-banner.js +27 -2
  435. package/dist/tui/vim-input.js +267 -0
  436. package/dist/tui/welcome-banner.js +107 -0
  437. package/dist/tui/welcome-data.js +293 -0
  438. package/dist/tui/workspace-context.js +2 -2
  439. package/package.json +21 -5
  440. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  441. package/test/scenarios/compact-force.scenario.txt +12 -0
  442. package/test/scenarios/identity.scenario.txt +11 -0
  443. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  444. package/test/scenarios/walkback.scenario.txt +12 -0
  445. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,382 @@
1
+ /**
2
+ * PUGI-538b () — `<pugi-tool-route>` envelope parser.
3
+ *
4
+ * The admin-api Pugi coordinator persona emits an
5
+ * `<pugi-tool-route command="code|fix|build" persona="<slug>" brief="…"/>`
6
+ * envelope on her turn when the operator's brief requires workspace
7
+ * tool use (write/edit/read a file, run a script, install a package,
8
+ * build, test). The CLI consumer parses the envelope, strips it from
9
+ * operator-visible detail, and routes the brief through the local
10
+ * `NativePugiEngineAdapter` → `runEngineLoop` → `POST /api/pugi/engine`
11
+ * flow that already drives `pugi code/fix/build` end-to-end with real
12
+ * tool calls and atomic file writes.
13
+ *
14
+ * Without this envelope the REPL chat path is a single-shot text
15
+ * streamer: customer says "make tic-tac-toe", Pugi responds with a
16
+ * bash heredoc dump as prose, no file is written. (PUGI-538a)
17
+ * lays out the architecture; this parser is the CLI half of the bridge.
18
+ *
19
+ * # Grammar (self-closing form is preferred; paired form also accepted)
20
+ *
21
+ * <pugi-tool-route command="code" persona="dev" brief="One sentence…"/>
22
+ * <pugi-tool-route command="fix" persona="dev" brief="…"></pugi-tool-route>
23
+ *
24
+ * Attributes:
25
+ * - command (required) — exactly one of `code`, `fix`, `build`.
26
+ * - persona (optional) — Tier-1 persona slug hint; defaults to `dev`
27
+ * when omitted. The value `main` is rejected (the REPL
28
+ * coordinator IS Pugi already, routing back to herself
29
+ * would not change behaviour).
30
+ * - brief (required) — single-sentence operational brief,
31
+ * <= 400 chars after entity decoding.
32
+ *
33
+ * # Why a hand-rolled parser, not a generic XML library
34
+ *
35
+ * Same rationale as `ask.ts`: generic XML libraries (sax,
36
+ * fast-xml-parser, xmldoc) carry a large attack surface (external
37
+ * entity expansion, recursive blowup on malformed input, sloppy
38
+ * attribute handling) and require ~50 KB of runtime dependency. The
39
+ * persona-side grammar here is one tag with a closed three-attribute
40
+ * set, so a bounded tokenizer is both safer and smaller. The defences
41
+ * mirror ask.ts: closed entity allowlist, control-char strip, refusal
42
+ * on raw `&` outside `&amp;` / `&lt;` / `&gt;` / `&quot;` / `&apos;`,
43
+ * refusal on nested tags inside attribute blob, hard span cap.
44
+ *
45
+ * # Buffering across streaming chunks
46
+ *
47
+ * The session calls `extractToolRouteTags(buffer)` on the accumulated
48
+ * `agent.step.detail` body. If the close tag (or self-close `/>`) has
49
+ * not arrived yet, the parser returns `{ tags: [], pendingOpenTag: true }`
50
+ * and the session waits for more chunks. This mirrors the
51
+ * `extractAskTags` / `extractPlanReviewTags` shape so `session.ts`
52
+ * routes the three envelope families through one buffer-and-strip
53
+ * machinery.
54
+ */
55
+ /* ------------------------------------------------------------------ */
56
+ /* Bounded constants */
57
+ /* ------------------------------------------------------------------ */
58
+ /** Hard cap on the brief length after entity decoding. */
59
+ export const TOOL_ROUTE_MAX_BRIEF_LEN = 400;
60
+ /** Hard cap on the persona slug length. */
61
+ export const TOOL_ROUTE_MAX_PERSONA_LEN = 32;
62
+ /** Hard cap on the entire tag span. Long enough for full attrs + a
63
+ * worst-case 400-char brief plus closing form; defends against runaway
64
+ * payloads. */
65
+ const TAG_MAX_SPAN_BYTES = 4 * 1024;
66
+ /** Tag families exhaustively enumerated for the closed-attribute gate. */
67
+ const ALLOWED_ATTRS = new Set([
68
+ 'command',
69
+ 'persona',
70
+ 'brief',
71
+ ]);
72
+ /** Accepted `command` literals — locked to the engine adapter contract. */
73
+ const ALLOWED_COMMANDS = new Set(['code', 'fix', 'build']);
74
+ /** Refused personas. `main` would route Pugi back to herself; the engine
75
+ * adapter rejects the slug anyway, so we filter at parse time for clarity. */
76
+ const REFUSED_PERSONAS = new Set(['main', 'pugi']);
77
+ /** Default persona slug when the attribute is omitted. Matches the
78
+ * ENVELOPES_BODY prompt copy ("Defaults to 'dev' when omitted"). */
79
+ const DEFAULT_PERSONA = 'dev';
80
+ /* ------------------------------------------------------------------ */
81
+ /* Public extraction API */
82
+ /* ------------------------------------------------------------------ */
83
+ /**
84
+ * Find every well-formed `<pugi-tool-route>` envelope in `body`.
85
+ * Malformed envelopes are dropped and surfaced via `hadMalformedTag`
86
+ * so the session can log a warning. Streaming-incomplete envelopes
87
+ * (open observed, close/self-close not yet arrived) are withheld from
88
+ * `cleaned` so the raw envelope cannot leak into the transcript if the
89
+ * stream pauses mid-tag.
90
+ */
91
+ export function extractToolRouteTags(body) {
92
+ const tags = [];
93
+ const segments = [];
94
+ let cursor = 0;
95
+ let pendingOpenTag = false;
96
+ let hadMalformedTag = false;
97
+ // Hard cap on tags per buffer so a hostile (or accidental) flood
98
+ // does not pin the parser. 16 is generous — a real session never
99
+ // has more than one envelope queued per turn.
100
+ const MAX_TAGS_PER_BUFFER = 16;
101
+ // Belt-and-braces safety counter — the body length bounds the
102
+ // iteration count strictly, but a pathological input could otherwise
103
+ // loop until the per-buffer cap fires.
104
+ let safetyIterations = body.length + 16;
105
+ const OPEN_PREFIX = '<pugi-tool-route';
106
+ const PAIR_CLOSE = '</pugi-tool-route>';
107
+ while (cursor < body.length && tags.length < MAX_TAGS_PER_BUFFER) {
108
+ if (safetyIterations-- <= 0)
109
+ break;
110
+ const openIndex = body.indexOf(OPEN_PREFIX, cursor);
111
+ if (openIndex === -1) {
112
+ // No more envelopes. Flush the rest of the body as cleaned prose.
113
+ segments.push(body.slice(cursor));
114
+ cursor = body.length;
115
+ break;
116
+ }
117
+ // Push everything before the opening tag as cleaned prose.
118
+ segments.push(body.slice(cursor, openIndex));
119
+ // Locate the end of the opening tag. The envelope may be self-
120
+ // closing (`/>`) or paired (`>` then `</pugi-tool-route>`).
121
+ // Search for the FIRST unquoted `>` after the OPEN_PREFIX position
122
+ // so a quoted attribute value with an embedded `>` (escaped as
123
+ // `&gt;` per the grammar; raw `>` is rejected below) cannot
124
+ // confuse the boundary scan.
125
+ const openEnd = findUnquotedGt(body, openIndex + OPEN_PREFIX.length);
126
+ if (openEnd === -1) {
127
+ // Open seen, no terminator yet. Withhold from `cleaned` so the
128
+ // raw envelope cannot leak. Same posture as ask.ts.
129
+ pendingOpenTag = true;
130
+ cursor = body.length;
131
+ break;
132
+ }
133
+ const selfClosed = body[openEnd - 1] === '/';
134
+ let tagEnd;
135
+ if (selfClosed) {
136
+ tagEnd = openEnd + 1; // include the closing `>`
137
+ }
138
+ else {
139
+ // Paired form — look for `</pugi-tool-route>`.
140
+ const closeAt = body.indexOf(PAIR_CLOSE, openEnd + 1);
141
+ if (closeAt === -1) {
142
+ pendingOpenTag = true;
143
+ cursor = body.length;
144
+ break;
145
+ }
146
+ tagEnd = closeAt + PAIR_CLOSE.length;
147
+ }
148
+ const span = body.slice(openIndex, tagEnd);
149
+ if (span.length > TAG_MAX_SPAN_BYTES) {
150
+ hadMalformedTag = true;
151
+ cursor = tagEnd;
152
+ continue;
153
+ }
154
+ // Reject nested envelopes — the generic "find next close" would
155
+ // otherwise pair an outer open with an inner close.
156
+ const innerOpen = span.indexOf(OPEN_PREFIX, OPEN_PREFIX.length);
157
+ if (innerOpen !== -1) {
158
+ hadMalformedTag = true;
159
+ cursor = tagEnd;
160
+ continue;
161
+ }
162
+ // Slice the attribute blob between OPEN_PREFIX and the closing
163
+ // `>` (excluding the trailing `/` on the self-closed form).
164
+ const attrBlobEnd = selfClosed ? openEnd - 1 : openEnd;
165
+ const attrBlob = body.slice(openIndex + OPEN_PREFIX.length, attrBlobEnd);
166
+ const parsed = parseToolRouteInner(attrBlob, {
167
+ start: openIndex,
168
+ end: tagEnd,
169
+ });
170
+ if (parsed === null) {
171
+ hadMalformedTag = true;
172
+ cursor = tagEnd;
173
+ continue;
174
+ }
175
+ tags.push(parsed);
176
+ cursor = tagEnd;
177
+ }
178
+ return {
179
+ tags,
180
+ cleaned: segments.join('').replace(/\s+\n/g, '\n').trimEnd(),
181
+ pendingOpenTag,
182
+ hadMalformedTag,
183
+ };
184
+ }
185
+ /* ------------------------------------------------------------------ */
186
+ /* Inner parser */
187
+ /* ------------------------------------------------------------------ */
188
+ function parseToolRouteInner(attrBlob, span) {
189
+ // Reject CDATA, comments, processing instructions, DOCTYPE inside
190
+ // the attribute blob — none of those appear in the legal grammar.
191
+ if (/<!--|<!\[|<\?|<!DOCTYPE/i.test(attrBlob))
192
+ return null;
193
+ // Reject raw `&` not in a known entity (decoded entities are allowed
194
+ // via decodeEntities below).
195
+ if (containsRawAmpersand(attrBlob))
196
+ return null;
197
+ // Reject raw `<` / `>` inside the attribute blob — they should always
198
+ // be escaped as `&lt;` / `&gt;`. The presence of a raw bracket is a
199
+ // sign of either accidental sloppy output or an injection attempt.
200
+ if (/[<>]/.test(attrBlob))
201
+ return null;
202
+ const attrs = parseAttrBlob(attrBlob);
203
+ if (attrs === null)
204
+ return null;
205
+ // Closed-attribute gate — refuse anything outside the allowlist.
206
+ for (const key of Object.keys(attrs)) {
207
+ if (!ALLOWED_ATTRS.has(key))
208
+ return null;
209
+ }
210
+ const command = attrs['command'];
211
+ if (typeof command !== 'string' || command.length === 0)
212
+ return null;
213
+ if (!ALLOWED_COMMANDS.has(command))
214
+ return null;
215
+ const briefRaw = attrs['brief'];
216
+ if (typeof briefRaw !== 'string')
217
+ return null;
218
+ const brief = briefRaw.trim();
219
+ if (brief.length === 0 || brief.length > TOOL_ROUTE_MAX_BRIEF_LEN)
220
+ return null;
221
+ // Persona is optional → default to `dev`. Any explicit non-empty
222
+ // value runs through the slug shape check + refused-persona gate.
223
+ const personaRaw = attrs['persona'];
224
+ let persona = DEFAULT_PERSONA;
225
+ if (typeof personaRaw === 'string' && personaRaw.length > 0) {
226
+ const trimmed = personaRaw.trim();
227
+ if (trimmed.length === 0 || trimmed.length > TOOL_ROUTE_MAX_PERSONA_LEN)
228
+ return null;
229
+ // Slug shape: lowercase ASCII letters + digits + hyphens, must
230
+ // start with a letter. Same shape as the `<pugi-delegate>` slug
231
+ // gate in admin-api so a future engine adapter can reuse the slug
232
+ // verbatim without re-validating.
233
+ if (!/^[a-z][a-z0-9-]*$/.test(trimmed))
234
+ return null;
235
+ if (REFUSED_PERSONAS.has(trimmed))
236
+ return null;
237
+ persona = trimmed;
238
+ }
239
+ const signature = signatureForToolRoute(command, persona, brief);
240
+ return {
241
+ command: command,
242
+ persona,
243
+ brief,
244
+ signature,
245
+ start: span.start,
246
+ end: span.end,
247
+ };
248
+ }
249
+ /**
250
+ * Parse `key="value"` / `key='value'` pairs out of an attribute blob.
251
+ * Returns null on any malformed attribute (unterminated quote, raw
252
+ * entity outside the allowlist, etc). Mirrors the bounded tokenizer
253
+ * in ask.ts so the parsing posture is uniform across envelope families.
254
+ */
255
+ function parseAttrBlob(blob) {
256
+ const trimmed = blob.trim();
257
+ if (trimmed.length === 0)
258
+ return {};
259
+ const result = {};
260
+ let cursor = 0;
261
+ const maxIterations = trimmed.length + 4;
262
+ let iterations = 0;
263
+ while (cursor < trimmed.length && iterations++ < maxIterations) {
264
+ while (cursor < trimmed.length && /\s/.test(trimmed[cursor] ?? ''))
265
+ cursor += 1;
266
+ if (cursor >= trimmed.length)
267
+ break;
268
+ const nameStart = cursor;
269
+ // Attribute names are lowercase letters; allow `-` for consistency
270
+ // with the rest of the persona grammar even though our allowlist is
271
+ // hyphen-free. Mismatch is caught by the ALLOWED_ATTRS gate.
272
+ while (cursor < trimmed.length &&
273
+ /[a-z-]/.test(trimmed[cursor] ?? '')) {
274
+ cursor += 1;
275
+ }
276
+ if (cursor === nameStart)
277
+ return null;
278
+ const name = trimmed.slice(nameStart, cursor);
279
+ if (trimmed[cursor] !== '=')
280
+ return null;
281
+ cursor += 1;
282
+ const quote = trimmed[cursor];
283
+ if (quote !== '"' && quote !== "'")
284
+ return null;
285
+ cursor += 1;
286
+ const valueStart = cursor;
287
+ const valueEnd = trimmed.indexOf(quote, valueStart);
288
+ if (valueEnd === -1)
289
+ return null;
290
+ const rawValue = trimmed.slice(valueStart, valueEnd);
291
+ // Raw `&` outside a known entity is malformed; raw angle brackets
292
+ // are forbidden inside quoted values (must be escaped). The closed
293
+ // entity allowlist + control-char strip mirrors ask.ts.
294
+ if (containsRawAmpersand(rawValue))
295
+ return null;
296
+ if (/[<>]/.test(rawValue))
297
+ return null;
298
+ result[name] = stripControlChars(decodeEntities(rawValue));
299
+ cursor = valueEnd + 1;
300
+ }
301
+ return result;
302
+ }
303
+ /* ------------------------------------------------------------------ */
304
+ /* Streaming helper — find an unquoted `>` after `from` */
305
+ /* ------------------------------------------------------------------ */
306
+ /**
307
+ * Walk `body` from index `from` looking for the first `>` that is NOT
308
+ * inside a quoted attribute value. Returns the index of the `>` or -1
309
+ * when no such position is found.
310
+ *
311
+ * Quote tracking is necessary because a `brief="…contains > as &gt;…"`
312
+ * value should never confuse the boundary scan. The grammar already
313
+ * forbids raw `>` inside quoted values (escape as `&gt;`) — this is
314
+ * belt-and-braces against a sloppy model emission.
315
+ */
316
+ function findUnquotedGt(body, from) {
317
+ let quote = null;
318
+ for (let i = from; i < body.length; i += 1) {
319
+ const ch = body[i];
320
+ if (quote !== null) {
321
+ if (ch === quote)
322
+ quote = null;
323
+ continue;
324
+ }
325
+ if (ch === '"' || ch === "'") {
326
+ quote = ch;
327
+ continue;
328
+ }
329
+ if (ch === '>')
330
+ return i;
331
+ }
332
+ return -1;
333
+ }
334
+ /* ------------------------------------------------------------------ */
335
+ /* Entity decoding + amp safety + control strip */
336
+ /* ------------------------------------------------------------------ */
337
+ const ENTITY_MAP = Object.freeze({
338
+ amp: '&',
339
+ lt: '<',
340
+ gt: '>',
341
+ quot: '"',
342
+ apos: "'",
343
+ });
344
+ function decodeEntities(input) {
345
+ return input.replace(/&([a-z]+);/g, (whole, name) => {
346
+ const decoded = ENTITY_MAP[name];
347
+ return decoded === undefined ? whole : decoded;
348
+ });
349
+ }
350
+ function stripControlChars(input) {
351
+ // eslint-disable-next-line no-control-regex -- deliberately matching control range
352
+ return input.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\x80-\x9f]/g, '');
353
+ }
354
+ function containsRawAmpersand(input) {
355
+ for (let i = 0; i < input.length; i += 1) {
356
+ if (input[i] !== '&')
357
+ continue;
358
+ const semi = input.indexOf(';', i + 1);
359
+ if (semi === -1)
360
+ return true;
361
+ const name = input.slice(i + 1, semi);
362
+ if (!Object.prototype.hasOwnProperty.call(ENTITY_MAP, name))
363
+ return true;
364
+ i = semi;
365
+ }
366
+ return false;
367
+ }
368
+ /* ------------------------------------------------------------------ */
369
+ /* Signature */
370
+ /* ------------------------------------------------------------------ */
371
+ /**
372
+ * Stable dedupe signature for a tool-route tag. Exported so future
373
+ * synthesisers (slash command, local fallback) can produce signatures
374
+ * that collision-match parser-produced ones — without a single source
375
+ * of truth a local synth could share a signature with a persona-emitted
376
+ * envelope and the rolling-seen set would suppress one of them.
377
+ */
378
+ export function signatureForToolRoute(command, persona, brief) {
379
+ const raw = `${command}::${persona.toLowerCase()}::${brief.trim().toLowerCase()}`;
380
+ return Buffer.from(raw, 'utf8').toString('base64');
381
+ }
382
+ //# sourceMappingURL=tool-route.js.map
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Workspace context resolver — Sprint α6.14 wave 4.
2
+ * Workspace context resolver — Sprint wave 4.
3
3
  *
4
4
  * Reads the operator's cwd and synthesises the workspace bundle the CLI
5
- * forwards to admin-api on POST /api/pugi/sessions. Mira's prompt v1.1
5
+ * forwards to admin-api on POST /api/pugi/sessions. Pugi's prompt v1.1
6
6
  * consumes the bundle so "what repo is this?" / "а изучи репо…" answers
7
7
  * from the live cwd instead of bouncing back "репо не привязано" (CEO
8
- * dogfood 2026-05-25).
8
+ * dogfood).
9
9
  *
10
10
  * Three fields:
11
11
  *
12
- * - `workspaceCwd` — absolute path the CLI was launched from.
13
- * - `workspaceSlug` — a stable short identifier (slugForCwd).
14
- * - `workspaceSummary` — first ~200 chars of `.pugi/PUGI.md` if the
15
- * repo has one, else the directory basename.
12
+ * - `workspaceCwd` — absolute path the CLI was launched from.
13
+ * - `workspaceSlug` — a stable short identifier (slugForCwd).
14
+ * - `workspaceSummary` — first ~200 chars of `.pugi/PUGI.md` if the
15
+ * repo has one, else the directory basename.
16
16
  *
17
17
  * The helper is pure-ish (reads the filesystem but does not mutate it)
18
18
  * so the production caller in `runtime/cli.ts` can call it eagerly at
@@ -27,16 +27,26 @@
27
27
  import { existsSync, readFileSync, statSync } from 'node:fs';
28
28
  import { basename, resolve as resolvePath } from 'node:path';
29
29
  import { slugForCwd } from './history.js';
30
+ import { isBareMode } from '../bare-mode/index.js';
31
+ /**
32
+ * Workspace summary shown when the operator launched with `--bare` (or
33
+ * `PUGI_BARE=1`). bare mode disables project auto-discovery
34
+ * across the CLI, so we never read `.pugi/PUGI.md` and never advertise
35
+ * a real workspace label to admin-api. Explicit string so the splash +
36
+ * status bar agree, and so operators triaging "why is Pugi ignoring
37
+ * my repo" see a clear cause.
38
+ */
39
+ export const BARE_MODE_WORKSPACE_LABEL = '(bare mode - auto-discovery disabled)';
30
40
  /** Cap on the PUGI.md head we forward. Mirrors the admin-api clamp. */
31
41
  const PUGI_MD_HEAD_LIMIT = 200;
32
42
  /**
33
43
  * Workspace label shown when the cwd has no project markers (no .git,
34
- * no package.json, no PUGI.md). Per CEO 2026-05-25 dogfood, the
44
+ * no package.json, no PUGI.md). Per CEO dogfood, the
35
45
  * previous behaviour leaked the parent directory basename (e.g.
36
46
  * `codeforge-io`) into the splash as if it were a real workspace,
37
- * confusing Mira/Pugi about what repo she was looking at. The
47
+ * confusing Pugi/Pugi about what repo she was looking at. The
38
48
  * unbound label is a single explicit string so the splash + status bar
39
- * read the same warning. (α6.14.2 wave 5.)
49
+ * read the same warning.
40
50
  */
41
51
  export const UNBOUND_WORKSPACE_LABEL = '(not bound - run /init OR cd into project)';
42
52
  /**
@@ -48,9 +58,21 @@ export const UNBOUND_WORKSPACE_LABEL = '(not bound - run /init OR cd into projec
48
58
  export function resolveWorkspaceContext(cwd) {
49
59
  const normalised = resolvePath(cwd);
50
60
  const slug = slugForCwd(normalised);
51
- // α6.14.2 wave 5: when the cwd has no project markers, prefer the
61
+ // `--bare` short-circuits BEFORE any PUGI.md
62
+ // / project-marker reads so the resolver never advertises a real
63
+ // workspace summary to admin-api. The cwd + slug still travel for
64
+ // telemetry, but the model + Pugi treat the session as if launched
65
+ // from a fresh, unbound directory.
66
+ if (isBareMode()) {
67
+ return {
68
+ workspaceCwd: normalised,
69
+ workspaceSlug: slug,
70
+ workspaceSummary: BARE_MODE_WORKSPACE_LABEL,
71
+ };
72
+ }
73
+ // wave 5: when the cwd has no project markers, prefer the
52
74
  // explicit "not bound" summary so admin-api's prompt builder knows
53
- // not to fabricate a workspace context for Mira/Pugi. The cwd +
75
+ // not to fabricate a workspace context for Pugi/Pugi. The cwd +
54
76
  // slug still travel so the server can record where the operator
55
77
  // launched from for telemetry, but the summary no longer leaks a
56
78
  // stray parent-dir basename as if it were a real workspace.
@@ -69,18 +91,18 @@ export function resolveWorkspaceContext(cwd) {
69
91
  * operator wandered into. The probe is intentionally cheap — three
70
92
  * `existsSync` calls — and the order matches the brand convention:
71
93
  *
72
- * 1. `.git` — any clone of a real repo
73
- * 2. `package.json` — JS/TS workspace root
74
- * 3. `PUGI.md` — a Pugi-initialised workspace (root or
75
- * `.pugi/PUGI.md`)
94
+ * 1. `.git` — any clone of a real repo
95
+ * 2. `package.json` — JS/TS workspace root
96
+ * 3. `PUGI.md` — a Pugi-initialised workspace (root or
97
+ * `.pugi/PUGI.md`)
76
98
  *
77
99
  * Hitting any one of these counts the cwd as "bound" — the operator
78
100
  * intentionally landed in a project. Hitting none means they ran
79
101
  * `pugi` from `$HOME` or from the parent of a checkout; in that case
80
102
  * the splash surfaces an explicit "not bound" label instead of leaking
81
103
  * the parent-dir basename as if it were a workspace. The check
82
- * mirrors the Claude Code "no CLAUDE.md → silent context" rule —
83
- * never fake-bind. (α6.14.2 wave 5 — CEO dogfood fix.)
104
+ * mirrors the the upstream tool "no CLAUDE.md → silent context" rule —
105
+ * never fake-bind.
84
106
  */
85
107
  export function isBoundWorkspace(cwd) {
86
108
  const normalised = resolvePath(cwd);
@@ -105,7 +127,7 @@ export function isBoundWorkspace(cwd) {
105
127
  * understands no real workspace was detected. The label is the only
106
128
  * string the splash and status bar agree on, so we centralise the
107
129
  * decision here instead of re-deriving in two places.
108
- * (α6.14.2 wave 5.)
130
+ *
109
131
  */
110
132
  export function resolveWorkspaceLabel(cwd) {
111
133
  if (!isBoundWorkspace(cwd))
@@ -119,7 +141,7 @@ export function resolveWorkspaceLabel(cwd) {
119
141
  /**
120
142
  * Read the first ~200 chars of `.pugi/PUGI.md` if the file exists. The
121
143
  * project's own description is the highest-signal one-line summary we
122
- * can hand to Mira — `pugi init` writes it on workspace creation, and
144
+ * can hand to Pugi — `pugi init` writes it on workspace creation, and
123
145
  * the operator may have edited it since.
124
146
  *
125
147
  * Returns null on any FS error so the caller falls back to the
@@ -169,7 +191,7 @@ export function summariseMarkdown(raw) {
169
191
  }
170
192
  /**
171
193
  * Drop a YAML front-matter block (`---\n…\n---`) from the head of a
172
- * Markdown file. Mira does not need to see the metadata; the prose body
194
+ * Markdown file. Pugi does not need to see the metadata; the prose body
173
195
  * carries the project description.
174
196
  */
175
197
  function stripFrontmatter(raw) {
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Repo-map build orchestrator — .
3
+ *
4
+ * Single entry point that the CLI command + the engine boot path both
5
+ * call. Wires the scanner → extractor → cache → formatter pipeline
6
+ * together and surfaces a structured result the caller can render or
7
+ * inject without knowing the inner module shapes.
8
+ *
9
+ * The orchestrator is split out от cache.ts and the command handler
10
+ * so:
11
+ *
12
+ * 1. The CLI command + the engine system-prompt injector share one
13
+ * code path. Drift between the two would silently change what
14
+ * the model sees vs. what the operator sees.
15
+ *
16
+ * 2. The spec can exercise the full pipeline against a temp dir
17
+ * without mounting Ink or the engine.
18
+ *
19
+ * Pure-ish: reads from disk (the source files + the cache), but never
20
+ * mutates anything outside `.pugi/repo-map.json` and never logs. The
21
+ * caller decides whether к persist the cache (`writeCache: true`) or
22
+ * к compute the map в-memory (`writeCache: false` — useful for
23
+ * non-interactive `--json` invocations on read-only fs).
24
+ */
25
+ import { readFileSync } from 'node:fs';
26
+ import { loadPugiIgnore } from '../context/pugiignore.js';
27
+ import { defaultCachePath, diffCacheAgainstScan, mergeCache, readRepoMapCache, writeRepoMapCache, } from './cache.js';
28
+ import { extractFromFile } from './extractor.js';
29
+ import { scanRepoForMap } from './scanner.js';
30
+ import { formatRepoMap } from './formatter.js';
31
+ /**
32
+ * Run the full pipeline. Returns a structured verdict; never throws.
33
+ * The 'too-large' branch fires when the workspace exceeds the file
34
+ * cap — callers surface a hint к the operator ("repo too large for
35
+ * inline map — try .pugiignore") and skip injection.
36
+ */
37
+ export function buildRepoMap(options) {
38
+ const root = options.root;
39
+ const refresh = options.refresh === true;
40
+ const writeCache = options.writeCache !== false;
41
+ const cachePath = options.cachePath ?? defaultCachePath(root);
42
+ const readFile = options.readFile ?? ((path) => readFileSync(path, 'utf8'));
43
+ const ignore = loadPugiIgnore(root);
44
+ const scan = scanRepoForMap({ root, ignore });
45
+ if (!scan.ok) {
46
+ return {
47
+ ok: false,
48
+ root,
49
+ reason: scan.skipped.reason,
50
+ walked: scan.skipped.walked,
51
+ };
52
+ }
53
+ const prior = refresh ? null : readCacheOrNull(cachePath);
54
+ const diff = diffCacheAgainstScan(prior, scan.files);
55
+ const freshExtracts = new Map();
56
+ for (const file of diff.toRebuild) {
57
+ let source;
58
+ try {
59
+ source = readFile(file.absPath);
60
+ }
61
+ catch {
62
+ // File vanished или became unreadable mid-build — skip. The
63
+ // cache layer will just not have an entry for it; next refresh
64
+ // picks it up if it reappears.
65
+ continue;
66
+ }
67
+ freshExtracts.set(file.relPath, extractFromFile(file, source));
68
+ }
69
+ const cache = mergeCache({
70
+ root,
71
+ prior,
72
+ scanned: scan.files,
73
+ freshExtracts,
74
+ });
75
+ let cacheWritten = false;
76
+ if (writeCache) {
77
+ const writeResult = writeRepoMapCache(cachePath, cache);
78
+ cacheWritten = writeResult.ok;
79
+ }
80
+ // Surface the extracts в the same order the scanner produced (sorted
81
+ // by POSIX path) so callers iterating the result render deterministic
82
+ // output. The formatter does its own priority sort, so a different
83
+ // order here would only affect callers that iterate manually.
84
+ const extracts = [];
85
+ for (const file of scan.files) {
86
+ const entry = cache.entries[file.relPath];
87
+ if (entry)
88
+ extracts.push(entry.extract);
89
+ }
90
+ return {
91
+ ok: true,
92
+ root,
93
+ cache,
94
+ extracts,
95
+ scanStats: scan.stats,
96
+ diffStats: {
97
+ rebuilt: diff.toRebuild.length,
98
+ reused: diff.reuse.length,
99
+ dropped: diff.toDrop.length,
100
+ },
101
+ cachePath,
102
+ cacheWritten,
103
+ };
104
+ }
105
+ /**
106
+ * Convenience wrapper: build + format в one call. The engine boot
107
+ * path uses this so it does not have к know the formatter shape.
108
+ */
109
+ export function buildAndFormatRepoMap(options) {
110
+ const build = buildRepoMap(options);
111
+ if (!build.ok)
112
+ return { build };
113
+ const format = formatRepoMap(build.extracts, {
114
+ maxBytes: options.formatBytesCap,
115
+ omitHeader: options.omitHeader,
116
+ });
117
+ return { build, format };
118
+ }
119
+ function readCacheOrNull(path) {
120
+ const verdict = readRepoMapCache(path);
121
+ if (verdict.ok)
122
+ return verdict.cache;
123
+ return null;
124
+ }
125
+ //# sourceMappingURL=build.js.map