@pugi/cli 0.1.0-beta.1 → 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 (448) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/README.md +53 -11
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/assets/pugi-mascot.ansi +15 -40
  6. package/assets/pugi-prozr2-mascot.ansi +9 -0
  7. package/bin/run.js +33 -1
  8. package/dist/commands/deploy.js +40 -40
  9. package/dist/commands/flatten.js +191 -0
  10. package/dist/commands/jobs-watch.js +201 -0
  11. package/dist/commands/jobs.js +42 -27
  12. package/dist/commands/retro.js +210 -0
  13. package/dist/commands/smoke.js +133 -0
  14. package/dist/core/agent-progress/cleanup.js +134 -0
  15. package/dist/core/agent-progress/schema.js +144 -0
  16. package/dist/core/agent-progress/writer.js +101 -0
  17. package/dist/core/agents/adaptive-router.js +330 -0
  18. package/dist/core/agents/query-decomposer.js +297 -0
  19. package/dist/core/agents/registry.js +3 -3
  20. package/dist/core/approvals/shortcut-resolver.js +98 -0
  21. package/dist/core/artifact-chain/dispatcher.js +148 -0
  22. package/dist/core/artifact-chain/exporter.js +164 -0
  23. package/dist/core/artifact-chain/state.js +243 -0
  24. package/dist/core/artifact-chain/steps.js +169 -0
  25. package/dist/core/ask-user/question.js +92 -0
  26. package/dist/core/audit/audit-trail.js +275 -0
  27. package/dist/core/auth/ensure-authenticated.js +129 -0
  28. package/dist/core/auth/env-provider.js +238 -0
  29. package/dist/core/auto-open-browser.js +4 -4
  30. package/dist/core/auto-update/channels.js +122 -0
  31. package/dist/core/auto-update/checker.js +241 -0
  32. package/dist/core/auto-update/state.js +235 -0
  33. package/dist/core/bare-mode/index.js +107 -0
  34. package/dist/core/bash/redirect.js +281 -0
  35. package/dist/core/bash-classifier.js +436 -40
  36. package/dist/core/checkpoint/resumer.js +149 -0
  37. package/dist/core/checkpoint/rewinder.js +291 -0
  38. package/dist/core/checkpoints/shadow-git.js +670 -0
  39. package/dist/core/citations/parser.js +109 -0
  40. package/dist/core/classifier/yolo-classifier.js +88 -0
  41. package/dist/core/codegraph/db.js +506 -0
  42. package/dist/core/codegraph/decision-store.js +248 -0
  43. package/dist/core/codegraph/detect-repo.js +459 -0
  44. package/dist/core/codegraph/install.js +134 -0
  45. package/dist/core/codegraph/offer-hook.js +220 -0
  46. package/dist/core/codegraph/parser.js +71 -0
  47. package/dist/core/codegraph/types.js +34 -0
  48. package/dist/core/compact/auto-trigger.js +96 -0
  49. package/dist/core/compact/buffer-rewriter.js +115 -0
  50. package/dist/core/compact/summarizer.js +208 -0
  51. package/dist/core/compact/token-counter.js +108 -0
  52. package/dist/core/consensus/anvil-fanout.js +25 -25
  53. package/dist/core/consensus/diff-capture.js +121 -12
  54. package/dist/core/consensus/rubric.js +21 -21
  55. package/dist/core/context/builder.js +6 -6
  56. package/dist/core/context/compaction-events.js +8 -8
  57. package/dist/core/context/compaction.js +31 -31
  58. package/dist/core/context/index.js +15 -8
  59. package/dist/core/context/invariants.js +51 -51
  60. package/dist/core/context/markdown-loader.js +28 -10
  61. package/dist/core/context/markdown-traverse.js +255 -0
  62. package/dist/core/context/pugiignore.js +41 -41
  63. package/dist/core/context/repo-skeleton.js +37 -37
  64. package/dist/core/context/tool-eviction.js +55 -0
  65. package/dist/core/context/watcher.js +32 -32
  66. package/dist/core/context/working-set.js +23 -23
  67. package/dist/core/coordinator/agent-tools.js +77 -0
  68. package/dist/core/coordinator/agent-toolset.js +65 -0
  69. package/dist/core/coordinator/fsm.js +73 -0
  70. package/dist/core/coordinator/mode-fsm.js +70 -0
  71. package/dist/core/cost/rate-card.js +129 -0
  72. package/dist/core/cost/tracker.js +221 -0
  73. package/dist/core/credentials.js +13 -13
  74. package/dist/core/cron/scheduler.js +138 -0
  75. package/dist/core/denial-tracking/index.js +8 -0
  76. package/dist/core/denial-tracking/state.js +264 -0
  77. package/dist/core/diagnostics/probe-runner.js +93 -0
  78. package/dist/core/diagnostics/probes/api.js +46 -0
  79. package/dist/core/diagnostics/probes/auth.js +93 -0
  80. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  81. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  82. package/dist/core/diagnostics/probes/config.js +72 -0
  83. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  84. package/dist/core/diagnostics/probes/disk.js +81 -0
  85. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  86. package/dist/core/diagnostics/probes/git.js +65 -0
  87. package/dist/core/diagnostics/probes/hooks.js +118 -0
  88. package/dist/core/diagnostics/probes/mcp.js +75 -0
  89. package/dist/core/diagnostics/probes/node.js +59 -0
  90. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  91. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  92. package/dist/core/diagnostics/probes/sandbox.js +72 -0
  93. package/dist/core/diagnostics/probes/session.js +74 -0
  94. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  95. package/dist/core/diagnostics/probes/workspace.js +63 -0
  96. package/dist/core/diagnostics/types.js +70 -0
  97. package/dist/core/dispatch/cache-cleanup.js +197 -0
  98. package/dist/core/dispatch/cache-handoff.js +295 -0
  99. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  100. package/dist/core/edits/dispatch.js +333 -7
  101. package/dist/core/edits/format-detector.js +260 -0
  102. package/dist/core/edits/format-matrix.js +26 -0
  103. package/dist/core/edits/fuzzy-ladder.js +650 -0
  104. package/dist/core/edits/index.js +5 -1
  105. package/dist/core/edits/journal.js +199 -0
  106. package/dist/core/edits/layer-a-apply.js +15 -15
  107. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  108. package/dist/core/edits/layer-b-apply.js +9 -9
  109. package/dist/core/edits/layer-c-apply.js +6 -6
  110. package/dist/core/edits/layer-d-ast.js +557 -14
  111. package/dist/core/edits/marker-parser.js +12 -12
  112. package/dist/core/edits/security-gate.js +27 -27
  113. package/dist/core/edits/verify-hook.js +273 -0
  114. package/dist/core/edits/worktree.js +322 -0
  115. package/dist/core/engine/anvil-client.js +214 -26
  116. package/dist/core/engine/auto-compact.js +247 -0
  117. package/dist/core/engine/budgets.js +220 -0
  118. package/dist/core/engine/compact-llm-summarizer.js +124 -0
  119. package/dist/core/engine/context-prefix.js +155 -0
  120. package/dist/core/engine/index.js +1 -1
  121. package/dist/core/engine/intensity.js +163 -0
  122. package/dist/core/engine/intent.js +260 -0
  123. package/dist/core/engine/native-pugi.js +1559 -227
  124. package/dist/core/engine/prompts.js +192 -16
  125. package/dist/core/engine/strip-internal-fields.js +124 -0
  126. package/dist/core/engine/tool-bridge.js +1887 -59
  127. package/dist/core/engine/verification-patterns.js +195 -0
  128. package/dist/core/evaluation/golden-dataset.js +293 -0
  129. package/dist/core/feedback/queue.js +177 -0
  130. package/dist/core/feedback/submitter.js +145 -0
  131. package/dist/core/file-cache.js +113 -1
  132. package/dist/core/flatten/flatten-repo.js +439 -0
  133. package/dist/core/format/osc8-link.js +28 -0
  134. package/dist/core/hook-chains.js +392 -0
  135. package/dist/core/hooks/citation-verify-hook.js +138 -0
  136. package/dist/core/hooks/citation-verify.js +112 -0
  137. package/dist/core/hooks/events.js +46 -0
  138. package/dist/core/hooks/index.js +15 -0
  139. package/dist/core/hooks/registry.js +216 -0
  140. package/dist/core/hooks/runner.js +236 -0
  141. package/dist/core/hooks/v2/event-emitter.js +115 -0
  142. package/dist/core/hooks/v2/executor.js +282 -0
  143. package/dist/core/hooks/v2/index.js +25 -0
  144. package/dist/core/hooks/v2/lifecycle.js +104 -0
  145. package/dist/core/hooks/v2/loader.js +216 -0
  146. package/dist/core/hooks/v2/matcher.js +125 -0
  147. package/dist/core/hooks/v2/trust.js +143 -0
  148. package/dist/core/hooks/v2/types.js +86 -0
  149. package/dist/core/hooks/worktree-events.js +158 -0
  150. package/dist/core/image/renderer.js +71 -0
  151. package/dist/core/init/detector.js +582 -0
  152. package/dist/core/init/template-renderer.js +242 -0
  153. package/dist/core/jobs/registry.js +18 -18
  154. package/dist/core/ledger/results-tsv.js +142 -0
  155. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  156. package/dist/core/lsp/cache.js +105 -0
  157. package/dist/core/lsp/client.js +1229 -0
  158. package/dist/core/lsp/language-detect.js +66 -0
  159. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  160. package/dist/core/lsp/server-detect.js +173 -0
  161. package/dist/core/lsp/symbol-cache.js +162 -0
  162. package/dist/core/lsp/symbol-tools.js +664 -0
  163. package/dist/core/mcp/client.js +97 -28
  164. package/dist/core/mcp/http-server.js +553 -0
  165. package/dist/core/mcp/orchestrator-config.js +192 -0
  166. package/dist/core/mcp/orchestrator-tools.js +806 -0
  167. package/dist/core/mcp/permission.js +190 -0
  168. package/dist/core/mcp/registry.js +39 -17
  169. package/dist/core/mcp/server-tools.js +219 -0
  170. package/dist/core/mcp/server.js +397 -0
  171. package/dist/core/mcp/trust.js +10 -10
  172. package/dist/core/memory/dual-write.js +416 -0
  173. package/dist/core/memory/passive-extract.js +130 -0
  174. package/dist/core/memory/phase1-kinds.js +20 -0
  175. package/dist/core/memory/secret-scanner.js +304 -0
  176. package/dist/core/memory-sync/queue.js +170 -0
  177. package/dist/core/metrics/extract.js +113 -0
  178. package/dist/core/modes/roo-modes.js +68 -0
  179. package/dist/core/notes/notes-paths.js +113 -0
  180. package/dist/core/notes/notes-recorder.js +140 -0
  181. package/dist/core/notes/notes-writer.js +53 -0
  182. package/dist/core/notes/renderers.js +0 -0
  183. package/dist/core/notes/slug.js +105 -0
  184. package/dist/core/onboarding/ensure-initialized.js +133 -0
  185. package/dist/core/onboarding/marker.js +111 -0
  186. package/dist/core/onboarding/telemetry-state.js +108 -0
  187. package/dist/core/output-style/presets.js +176 -0
  188. package/dist/core/output-style/state.js +185 -0
  189. package/dist/core/path-security.js +287 -5
  190. package/dist/core/permission.js +82 -22
  191. package/dist/core/permissions/auto-classifier.js +124 -0
  192. package/dist/core/permissions/bash-parser.js +371 -0
  193. package/dist/core/permissions/circuit-breaker.js +83 -0
  194. package/dist/core/permissions/constrained-edit.js +91 -0
  195. package/dist/core/permissions/gate.js +278 -0
  196. package/dist/core/permissions/index.js +20 -0
  197. package/dist/core/permissions/mode.js +174 -0
  198. package/dist/core/permissions/network-egress.js +137 -0
  199. package/dist/core/permissions/state.js +241 -0
  200. package/dist/core/permissions/tool-class.js +107 -0
  201. package/dist/core/plan-mode/ui-state.js +51 -0
  202. package/dist/core/plans/plan-artifact.js +721 -0
  203. package/dist/core/policy-limits/etag-store.js +122 -0
  204. package/dist/core/prd-check/parser.js +215 -0
  205. package/dist/core/prd-check/reporter.js +127 -0
  206. package/dist/core/prd-check/session-review.js +557 -0
  207. package/dist/core/prd-check/verifiers.js +223 -0
  208. package/dist/core/prompt-cache/client-cache.js +99 -0
  209. package/dist/core/prompts/assembly.js +29 -0
  210. package/dist/core/prompts/registry.js +364 -0
  211. package/dist/core/pugi-gitignore.js +52 -0
  212. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  213. package/dist/core/pugi-md/context-injector.js +76 -0
  214. package/dist/core/pugi-md/walk-up.js +207 -0
  215. package/dist/core/python/uv-installer.js +270 -0
  216. package/dist/core/python/uv-resolver.js +83 -0
  217. package/dist/core/rate-limit/narrator.js +146 -0
  218. package/dist/core/recipes/cli-types.js +20 -0
  219. package/dist/core/recipes/loader.js +103 -0
  220. package/dist/core/recipes/runner.js +345 -0
  221. package/dist/core/recipes/schema.js +587 -0
  222. package/dist/core/release-notes/parser.js +241 -0
  223. package/dist/core/release-notes/state.js +116 -0
  224. package/dist/core/repl/ask.js +37 -37
  225. package/dist/core/repl/cancellation.js +26 -26
  226. package/dist/core/repl/cap-warning.js +4 -4
  227. package/dist/core/repl/clipboard-read.js +11 -11
  228. package/dist/core/repl/dispatch-fsm.js +12 -12
  229. package/dist/core/repl/engine-bridge.js +303 -0
  230. package/dist/core/repl/history-search.js +15 -15
  231. package/dist/core/repl/history.js +28 -18
  232. package/dist/core/repl/kill-ring.js +5 -5
  233. package/dist/core/repl/model-pricing.js +135 -0
  234. package/dist/core/repl/privacy-banner.js +22 -22
  235. package/dist/core/repl/session.js +2714 -228
  236. package/dist/core/repl/slash-commands.js +572 -40
  237. package/dist/core/repl/store/index.js +1 -1
  238. package/dist/core/repl/store/jsonl-log.js +22 -22
  239. package/dist/core/repl/store/lockfile.js +10 -10
  240. package/dist/core/repl/store/session-store.js +136 -107
  241. package/dist/core/repl/store/types.js +15 -15
  242. package/dist/core/repl/store/uuid-v7.js +12 -12
  243. package/dist/core/repl/tool-route.js +382 -0
  244. package/dist/core/repl/workspace-context.js +43 -21
  245. package/dist/core/repo-map/build.js +125 -0
  246. package/dist/core/repo-map/cache.js +185 -0
  247. package/dist/core/repo-map/extractor.js +254 -0
  248. package/dist/core/repo-map/formatter.js +145 -0
  249. package/dist/core/repo-map/page-rank.js +105 -0
  250. package/dist/core/repo-map/scanner.js +211 -0
  251. package/dist/core/retro/git-collector.js +251 -0
  252. package/dist/core/retro/health-card.js +25 -0
  253. package/dist/core/retro/metrics.js +342 -0
  254. package/dist/core/retro/narrative.js +249 -0
  255. package/dist/core/retro/plane-collector.js +274 -0
  256. package/dist/core/retro/pr-issue-link.js +65 -0
  257. package/dist/core/retro/types.js +16 -0
  258. package/dist/core/retry-budget/budget.js +284 -0
  259. package/dist/core/retry-budget/index.js +5 -0
  260. package/dist/core/retry-budget/retry-cap.js +74 -0
  261. package/dist/core/routing/lead-worker.js +43 -0
  262. package/dist/core/routing/pre-flight-estimator.js +108 -0
  263. package/dist/core/runs/run-tree.js +103 -0
  264. package/dist/core/sandboxing/adapter.js +29 -0
  265. package/dist/core/sandboxing/index.js +49 -0
  266. package/dist/core/sandboxing/none.js +19 -0
  267. package/dist/core/sandboxing/seatbelt.js +183 -0
  268. package/dist/core/security/injection-scanner.js +367 -0
  269. package/dist/core/security/output-filter.js +418 -0
  270. package/dist/core/session/env-file.js +105 -0
  271. package/dist/core/session/section-budgets.js +140 -0
  272. package/dist/core/session.js +119 -0
  273. package/dist/core/settings.js +378 -5
  274. package/dist/core/share/formatter.js +271 -0
  275. package/dist/core/share/redactor.js +221 -0
  276. package/dist/core/share/uploader.js +267 -0
  277. package/dist/core/skills/defaults.js +457 -0
  278. package/dist/core/skills/loader.js +22 -22
  279. package/dist/core/skills/sources.js +27 -27
  280. package/dist/core/smoke/headless-driver.js +174 -0
  281. package/dist/core/smoke/orchestrator.js +194 -0
  282. package/dist/core/smoke/runner.js +238 -0
  283. package/dist/core/smoke/scenario-parser.js +316 -0
  284. package/dist/core/statusline.js +99 -0
  285. package/dist/core/subagents/dispatcher-real.js +600 -0
  286. package/dist/core/subagents/dispatcher.js +146 -52
  287. package/dist/core/subagents/index.js +19 -6
  288. package/dist/core/subagents/isolation-matrix.js +213 -0
  289. package/dist/core/subagents/spawn.js +19 -4
  290. package/dist/core/telemetry/emitter.js +229 -0
  291. package/dist/core/telemetry/queue.js +251 -0
  292. package/dist/core/theme/context.js +91 -0
  293. package/dist/core/theme/presets.js +228 -0
  294. package/dist/core/theme/state.js +181 -0
  295. package/dist/core/todos/invariant.js +10 -0
  296. package/dist/core/todos/state.js +177 -0
  297. package/dist/core/tool-schema/compressor.js +89 -0
  298. package/dist/core/transport/version-interceptor.js +166 -0
  299. package/dist/core/trust.js +2 -2
  300. package/dist/core/tui/thinking-block.js +64 -0
  301. package/dist/core/vim/keymap.js +288 -0
  302. package/dist/core/vim/state.js +92 -0
  303. package/dist/core/watch-markers/marker-watcher.js +133 -0
  304. package/dist/core/worktree/include-parser.js +249 -0
  305. package/dist/core/worktree-manager/cleanup.js +123 -0
  306. package/dist/core/worktree-manager/manager.js +303 -0
  307. package/dist/index.js +36 -0
  308. package/dist/runtime/bootstrap.js +190 -0
  309. package/dist/runtime/cli.js +4536 -477
  310. package/dist/runtime/commands/agents.js +31 -31
  311. package/dist/runtime/commands/budget.js +5 -5
  312. package/dist/runtime/commands/cancel.js +231 -0
  313. package/dist/runtime/commands/chain.js +489 -0
  314. package/dist/runtime/commands/codegraph-status.js +227 -0
  315. package/dist/runtime/commands/compact.js +297 -0
  316. package/dist/runtime/commands/config.js +74 -40
  317. package/dist/runtime/commands/cost.js +199 -0
  318. package/dist/runtime/commands/delegate.js +312 -0
  319. package/dist/runtime/commands/dispatch.js +126 -0
  320. package/dist/runtime/commands/doctor.js +579 -0
  321. package/dist/runtime/commands/feedback.js +184 -0
  322. package/dist/runtime/commands/hooks.js +187 -0
  323. package/dist/runtime/commands/index-cmd.js +353 -0
  324. package/dist/runtime/commands/init.js +254 -0
  325. package/dist/runtime/commands/lsp.js +368 -0
  326. package/dist/runtime/commands/mcp.js +935 -0
  327. package/dist/runtime/commands/memory.js +582 -0
  328. package/dist/runtime/commands/model.js +237 -0
  329. package/dist/runtime/commands/onboarding.js +275 -0
  330. package/dist/runtime/commands/patch.js +128 -0
  331. package/dist/runtime/commands/permissions.js +112 -0
  332. package/dist/runtime/commands/plan.js +143 -0
  333. package/dist/runtime/commands/prd-check.js +285 -0
  334. package/dist/runtime/commands/privacy.js +17 -17
  335. package/dist/runtime/commands/recipe.js +325 -0
  336. package/dist/runtime/commands/redo-blob-store.js +92 -0
  337. package/dist/runtime/commands/redo.js +361 -0
  338. package/dist/runtime/commands/release-notes.js +229 -0
  339. package/dist/runtime/commands/repo-map.js +95 -0
  340. package/dist/runtime/commands/report.js +299 -0
  341. package/dist/runtime/commands/resume.js +118 -0
  342. package/dist/runtime/commands/review-consensus.js +68 -53
  343. package/dist/runtime/commands/rewind.js +333 -0
  344. package/dist/runtime/commands/roster.js +117 -0
  345. package/dist/runtime/commands/servers.js +236 -0
  346. package/dist/runtime/commands/sessions.js +163 -0
  347. package/dist/runtime/commands/share.js +316 -0
  348. package/dist/runtime/commands/skills.js +31 -31
  349. package/dist/runtime/commands/status.js +186 -0
  350. package/dist/runtime/commands/stickers.js +82 -0
  351. package/dist/runtime/commands/style.js +194 -0
  352. package/dist/runtime/commands/theme.js +196 -0
  353. package/dist/runtime/commands/undo.js +54 -22
  354. package/dist/runtime/commands/update.js +289 -0
  355. package/dist/runtime/commands/vim.js +140 -0
  356. package/dist/runtime/commands/worktree.js +177 -0
  357. package/dist/runtime/commands/worktrees.js +155 -0
  358. package/dist/runtime/deprecation-warning.js +69 -0
  359. package/dist/runtime/engine-exit-code.js +50 -0
  360. package/dist/runtime/headless-repl.js +195 -0
  361. package/dist/runtime/headless.js +548 -0
  362. package/dist/runtime/load-hooks-or-exit.js +71 -0
  363. package/dist/runtime/plan-decompose.js +531 -0
  364. package/dist/runtime/sigint-guard.js +272 -0
  365. package/dist/runtime/stream-renderer.js +195 -0
  366. package/dist/runtime/update-check.js +28 -28
  367. package/dist/runtime/version.js +65 -0
  368. package/dist/runtime/worktree-bootstrap.js +579 -0
  369. package/dist/skills/bundled/batch.js +617 -0
  370. package/dist/skills/bundled/index.js +45 -0
  371. package/dist/skills/bundled/loop.js +358 -0
  372. package/dist/skills/bundled/remember.js +383 -0
  373. package/dist/skills/bundled/simplify.js +289 -0
  374. package/dist/skills/bundled/skillify.js +373 -0
  375. package/dist/skills/bundled/stuck.js +558 -0
  376. package/dist/skills/bundled/verify.js +439 -0
  377. package/dist/testing/vcr.js +486 -0
  378. package/dist/tools/agent-tool.js +229 -0
  379. package/dist/tools/apply-patch.js +556 -0
  380. package/dist/tools/ask-user-question.js +337 -0
  381. package/dist/tools/ask-user.js +115 -0
  382. package/dist/tools/bash.js +624 -46
  383. package/dist/tools/brief.js +224 -0
  384. package/dist/tools/cron.js +433 -0
  385. package/dist/tools/enter-worktree.js +250 -0
  386. package/dist/tools/exit-worktree.js +147 -0
  387. package/dist/tools/file-tools.js +161 -44
  388. package/dist/tools/http-request.js +336 -0
  389. package/dist/tools/lsp-tools.js +565 -0
  390. package/dist/tools/mcp-tool.js +260 -0
  391. package/dist/tools/multi-edit.js +361 -0
  392. package/dist/tools/powershell.js +268 -0
  393. package/dist/tools/registry.js +142 -1
  394. package/dist/tools/server-tools.js +892 -0
  395. package/dist/tools/skill-tool.js +96 -0
  396. package/dist/tools/sleep.js +99 -0
  397. package/dist/tools/synthetic-output.js +133 -0
  398. package/dist/tools/tasks.js +208 -0
  399. package/dist/tools/todo-write.js +184 -0
  400. package/dist/tools/verify-plan-execution.js +295 -0
  401. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  402. package/dist/tools/web-fetch.js +195 -10
  403. package/dist/tools/web-search.js +458 -0
  404. package/dist/tui/agent-progress-card.js +111 -0
  405. package/dist/tui/agent-tree.js +22 -1
  406. package/dist/tui/ask-modal.js +14 -14
  407. package/dist/tui/ask-user-question-chips.js +315 -0
  408. package/dist/tui/ask-user-question-prompt.js +203 -0
  409. package/dist/tui/compact-banner.js +81 -0
  410. package/dist/tui/conversation-pane.js +85 -11
  411. package/dist/tui/cost-table.js +111 -0
  412. package/dist/tui/device-flow.js +2 -2
  413. package/dist/tui/doctor-table.js +46 -0
  414. package/dist/tui/feedback-prompt.js +156 -0
  415. package/dist/tui/input-box.js +247 -32
  416. package/dist/tui/login-picker.js +3 -3
  417. package/dist/tui/markdown-render.js +6 -6
  418. package/dist/tui/multi-file-diff-approval.js +375 -0
  419. package/dist/tui/onboarding-wizard.js +240 -0
  420. package/dist/tui/permissions-picker.js +86 -0
  421. package/dist/tui/render.js +36 -1
  422. package/dist/tui/repl-render.js +405 -32
  423. package/dist/tui/repl-splash-art.js +16 -16
  424. package/dist/tui/repl-splash-mascot.js +48 -24
  425. package/dist/tui/repl-splash.js +22 -22
  426. package/dist/tui/repl.js +136 -43
  427. package/dist/tui/slash-palette.js +6 -6
  428. package/dist/tui/splash.js +2 -2
  429. package/dist/tui/status-bar.js +109 -31
  430. package/dist/tui/status-table.js +7 -0
  431. package/dist/tui/stickers-art.js +136 -0
  432. package/dist/tui/style-table.js +28 -0
  433. package/dist/tui/theme-table.js +29 -0
  434. package/dist/tui/thinking-spinner.js +123 -0
  435. package/dist/tui/tool-stream-pane.js +53 -4
  436. package/dist/tui/update-banner.js +27 -2
  437. package/dist/tui/vim-input.js +267 -0
  438. package/dist/tui/welcome-banner.js +107 -0
  439. package/dist/tui/welcome-data.js +293 -0
  440. package/dist/tui/workspace-context.js +2 -2
  441. package/docs/examples/codegraph.mcp.json +10 -0
  442. package/package.json +25 -7
  443. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  444. package/test/scenarios/compact-force.scenario.txt +12 -0
  445. package/test/scenarios/identity.scenario.txt +11 -0
  446. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  447. package/test/scenarios/walkback.scenario.txt +12 -0
  448. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,177 @@
1
+ /**
2
+ * `pugi worktree <op>` — Phase 1.
3
+ *
4
+ * Manual control over the scratch worktree primitive. Three subcommands:
5
+ *
6
+ * pugi worktree create [branch] # spawns `.pugi/worktrees/<uuid>`
7
+ * pugi worktree promote <path> # applies the worktree's diff back to cwd
8
+ * pugi worktree drop <path> # removes the worktree (idempotent)
9
+ *
10
+ * Output: human-readable by default, structured JSON under --json so
11
+ * scripted callers can chain (`pugi worktree create --json | jq .path`).
12
+ *
13
+ * The same primitives are used by the `pugi build` and
14
+ * `pugi review --consensus` paths internally; this surface is the
15
+ * operator escape hatch for debugging / manual experimentation.
16
+ *
17
+ * Brand voice: ASCII only, no emoji, no banned words.
18
+ */
19
+ import { spawnSync } from 'node:child_process';
20
+ import { resolve, sep } from 'node:path';
21
+ import { createWorktree, dropWorktree, promoteWorktree } from '../../core/edits/worktree.js';
22
+ /**
23
+ * R1 fix (2026-05-26, PR r1, P2 #10): operator-facing path
24
+ * validation. The core `promoteWorktree` / `dropWorktree` primitives
25
+ * already gate their inputs, but mirroring the check at the CLI surface
26
+ * gives the operator a clean error message before we even attempt the
27
+ * git invocation (which would otherwise leak `git rev-parse HEAD` stderr).
28
+ */
29
+ function isUnderScratchRoot(cwd, candidate) {
30
+ const scratchRoot = resolve(cwd, '.pugi', 'worktrees');
31
+ const abs = resolve(cwd, candidate);
32
+ return abs.startsWith(scratchRoot + sep) && abs !== scratchRoot;
33
+ }
34
+ export async function runWorktreeCommand(args, opts) {
35
+ const [op, ...rest] = args;
36
+ if (!op)
37
+ return usage();
38
+ if (op === 'create') {
39
+ const branch = rest[0];
40
+ const result = createWorktree({
41
+ cwd: opts.cwd,
42
+ ...(branch ? { branch } : {}),
43
+ });
44
+ if (!result.ok) {
45
+ return {
46
+ ok: false,
47
+ text: opts.json
48
+ ? JSON.stringify(result, null, 2)
49
+ : `worktree create failed: ${result.reason}: ${result.detail}`,
50
+ exitCode: 1,
51
+ };
52
+ }
53
+ const handle = result.value;
54
+ return {
55
+ ok: true,
56
+ text: opts.json
57
+ ? JSON.stringify({ path: handle.path, baseSha: handle.baseSha }, null, 2)
58
+ : `worktree created: ${handle.path}\nbase: ${handle.baseSha}`,
59
+ exitCode: 0,
60
+ };
61
+ }
62
+ if (op === 'promote') {
63
+ const worktreePath = rest[0];
64
+ if (!worktreePath) {
65
+ return {
66
+ ok: false,
67
+ text: 'Usage: pugi worktree promote <path>',
68
+ exitCode: 2,
69
+ };
70
+ }
71
+ if (!isUnderScratchRoot(opts.cwd, worktreePath)) {
72
+ return {
73
+ ok: false,
74
+ text: opts.json
75
+ ? JSON.stringify({
76
+ ok: false,
77
+ reason: 'invalid_worktree_path',
78
+ detail: `worktree path must live under <cwd>/.pugi/worktrees/`,
79
+ }, null, 2)
80
+ : `promote failed: invalid_worktree_path: ${worktreePath} is not under .pugi/worktrees/`,
81
+ exitCode: 3,
82
+ };
83
+ }
84
+ // Resolve the worktree path's base SHA from its own git HEAD so
85
+ // the operator never has to remember it after `worktree create`.
86
+ const abs = resolve(opts.cwd, worktreePath);
87
+ const head = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: abs, encoding: 'utf8' });
88
+ if (head.status !== 0) {
89
+ return {
90
+ ok: false,
91
+ text: `cannot read HEAD of ${abs}: ${head.stderr.trim() || 'git rev-parse failed'}`,
92
+ exitCode: 1,
93
+ };
94
+ }
95
+ const baseSha = head.stdout.trim();
96
+ const result = promoteWorktree({
97
+ cwd: opts.cwd,
98
+ worktreePath: abs,
99
+ baseSha,
100
+ ...(opts.dryRun ? { dryRun: true } : {}),
101
+ });
102
+ if (!result.ok) {
103
+ return {
104
+ ok: false,
105
+ text: opts.json
106
+ ? JSON.stringify(result, null, 2)
107
+ : `promote failed: ${result.reason}: ${result.detail}`,
108
+ exitCode: result.reason === 'protected_file_in_worktree' ? 3 : 1,
109
+ };
110
+ }
111
+ const prefix = opts.dryRun ? 'dry-run: would promote' : 'promoted';
112
+ return {
113
+ ok: true,
114
+ text: opts.json
115
+ ? JSON.stringify({ filesChanged: result.value.filesChanged, dryRun: opts.dryRun ?? false }, null, 2)
116
+ : `${prefix} ${result.value.filesChanged} files from ${abs}`,
117
+ exitCode: 0,
118
+ };
119
+ }
120
+ if (op === 'drop') {
121
+ const worktreePath = rest[0];
122
+ if (!worktreePath) {
123
+ return {
124
+ ok: false,
125
+ text: 'Usage: pugi worktree drop <path>',
126
+ exitCode: 2,
127
+ };
128
+ }
129
+ if (!isUnderScratchRoot(opts.cwd, worktreePath)) {
130
+ return {
131
+ ok: false,
132
+ text: opts.json
133
+ ? JSON.stringify({
134
+ ok: false,
135
+ reason: 'invalid_worktree_path',
136
+ detail: `worktree path must live under <cwd>/.pugi/worktrees/`,
137
+ }, null, 2)
138
+ : `drop failed: invalid_worktree_path: ${worktreePath} is not under .pugi/worktrees/`,
139
+ exitCode: 3,
140
+ };
141
+ }
142
+ const abs = resolve(opts.cwd, worktreePath);
143
+ const result = dropWorktree(abs, opts.cwd);
144
+ if (!result.ok) {
145
+ return {
146
+ ok: false,
147
+ text: opts.json
148
+ ? JSON.stringify(result, null, 2)
149
+ : `drop failed: ${result.reason}: ${result.detail}`,
150
+ exitCode: result.reason === 'invalid_worktree_path' ? 3 : 1,
151
+ };
152
+ }
153
+ return {
154
+ ok: true,
155
+ text: opts.json
156
+ ? JSON.stringify({ dropped: abs }, null, 2)
157
+ : `worktree dropped: ${abs}`,
158
+ exitCode: 0,
159
+ };
160
+ }
161
+ return {
162
+ ok: false,
163
+ text: `unknown worktree operation: ${op}. Supported: create, promote, drop`,
164
+ exitCode: 2,
165
+ };
166
+ }
167
+ function usage() {
168
+ return {
169
+ ok: false,
170
+ text: 'Usage: pugi worktree <op>\n' +
171
+ ' pugi worktree create [branch]\n' +
172
+ ' pugi worktree promote <path>\n' +
173
+ ' pugi worktree drop <path>',
174
+ exitCode: 2,
175
+ };
176
+ }
177
+ //# sourceMappingURL=worktree.js.map
@@ -0,0 +1,155 @@
1
+ /**
2
+ * `pugi worktrees <op>` — .
3
+ *
4
+ * Plural surface for the agent-bound worktree manager. Distinct from the
5
+ * singular `pugi worktree <op>` which manages UUID-keyed
6
+ * scratch worktrees for the manual `create / promote / drop` flow.
7
+ *
8
+ * pugi worktrees list # show active agent worktrees
9
+ * pugi worktrees cleanup <agent-id> # remove one
10
+ * pugi worktrees cleanup --all-stale # sweep orphans + completed/failed
11
+ * pugi worktrees cleanup --all-stale --dry-run
12
+ *
13
+ * Output: human-readable by default, NDJSON envelope under --json.
14
+ *
15
+ * Brand voice: ASCII only, no emoji, no banned words.
16
+ */
17
+ import { runStaleCleanup } from '../../core/worktree-manager/cleanup.js';
18
+ import { WorktreeManager } from '../../core/worktree-manager/manager.js';
19
+ export async function runWorktreesCommand(args, opts) {
20
+ const [op, ...rest] = args;
21
+ if (!op)
22
+ return usage();
23
+ if (op === 'list') {
24
+ return runList(opts);
25
+ }
26
+ if (op === 'cleanup') {
27
+ return runCleanup(rest, opts);
28
+ }
29
+ return {
30
+ ok: false,
31
+ text: `unknown worktrees operation: ${op}. Supported: list, cleanup`,
32
+ exitCode: 2,
33
+ };
34
+ }
35
+ function runList(opts) {
36
+ const manager = new WorktreeManager({ cwd: opts.cwd });
37
+ const result = manager.list();
38
+ if (!result.ok) {
39
+ return {
40
+ ok: false,
41
+ text: opts.json
42
+ ? JSON.stringify(result, null, 2)
43
+ : `worktrees list failed: ${result.reason}: ${result.detail}`,
44
+ exitCode: 1,
45
+ };
46
+ }
47
+ const rows = result.value;
48
+ if (opts.json) {
49
+ return {
50
+ ok: true,
51
+ text: JSON.stringify({ worktrees: rows }, null, 2),
52
+ exitCode: 0,
53
+ };
54
+ }
55
+ if (rows.length === 0) {
56
+ return { ok: true, text: 'no agent worktrees', exitCode: 0 };
57
+ }
58
+ const lines = [];
59
+ lines.push('AGENT ID BRANCH GIT PATH');
60
+ for (const row of rows) {
61
+ lines.push(`${pad(row.agentId, 28)}${pad(row.branch, 36)}${pad(row.gitTracked ? 'yes' : 'no', 8)}${row.path}`);
62
+ }
63
+ return { ok: true, text: lines.join('\n'), exitCode: 0 };
64
+ }
65
+ function runCleanup(args, opts) {
66
+ // --all-stale sweeps every classified stale entry. Otherwise the next
67
+ // positional arg is the agent id to remove.
68
+ const allStale = args.includes('--all-stale');
69
+ if (allStale) {
70
+ const report = runStaleCleanup({ cwd: opts.cwd }, opts.dryRun ? { dryRun: true } : {});
71
+ return formatStaleReport(report, opts);
72
+ }
73
+ const agentId = args.find((a) => !a.startsWith('--'));
74
+ if (!agentId) {
75
+ return {
76
+ ok: false,
77
+ text: 'Usage: pugi worktrees cleanup <agent-id> | --all-stale [--dry-run]',
78
+ exitCode: 2,
79
+ };
80
+ }
81
+ const manager = new WorktreeManager({ cwd: opts.cwd });
82
+ const result = manager.cleanup(agentId);
83
+ if (!result.ok) {
84
+ // Missing-worktree is idempotent: surface the structured note but
85
+ // return exit 0 so a double-cleanup in a teardown hook never trips
86
+ // CI. Every other failure mode (invalid id, git failure) stays
87
+ // exit 1.
88
+ return {
89
+ ok: false,
90
+ text: opts.json
91
+ ? JSON.stringify(result, null, 2)
92
+ : `cleanup failed: ${result.reason}: ${result.detail}`,
93
+ exitCode: result.reason === 'worktree_missing' ? 0 : 1,
94
+ };
95
+ }
96
+ return {
97
+ ok: true,
98
+ text: opts.json
99
+ ? JSON.stringify({ removed: result.value.removed }, null, 2)
100
+ : `worktree removed: ${result.value.removed}`,
101
+ exitCode: 0,
102
+ };
103
+ }
104
+ function formatStaleReport(report, opts) {
105
+ if (opts.json) {
106
+ return {
107
+ ok: report.errors.length === 0,
108
+ text: JSON.stringify(report, null, 2),
109
+ exitCode: report.errors.length === 0 ? 0 : 1,
110
+ };
111
+ }
112
+ const lines = [];
113
+ lines.push(`scanned: ${report.scanned} worktree(s)`);
114
+ if (report.removed.length > 0) {
115
+ lines.push(`removed: ${report.removed.length}`);
116
+ for (const r of report.removed)
117
+ lines.push(` - ${r}`);
118
+ }
119
+ if (report.preserved.length > 0) {
120
+ lines.push(`preserved: ${report.preserved.length}`);
121
+ for (const p of report.preserved)
122
+ lines.push(` - ${p.agentId} (${p.reason})`);
123
+ }
124
+ if (opts.dryRun) {
125
+ lines.push('dry-run: classifications only, nothing removed');
126
+ for (const c of report.classified)
127
+ lines.push(` - ${c.agentId}: ${c.class}`);
128
+ }
129
+ if (report.errors.length > 0) {
130
+ lines.push(`errors: ${report.errors.length}`);
131
+ for (const e of report.errors)
132
+ lines.push(` - ${e.agentId}: ${e.detail}`);
133
+ }
134
+ return {
135
+ ok: report.errors.length === 0,
136
+ text: lines.join('\n'),
137
+ exitCode: report.errors.length === 0 ? 0 : 1,
138
+ };
139
+ }
140
+ function usage() {
141
+ return {
142
+ ok: false,
143
+ text: 'Usage: pugi worktrees <op>\n' +
144
+ ' pugi worktrees list\n' +
145
+ ' pugi worktrees cleanup <agent-id>\n' +
146
+ ' pugi worktrees cleanup --all-stale [--dry-run]',
147
+ exitCode: 2,
148
+ };
149
+ }
150
+ function pad(s, n) {
151
+ if (s.length >= n)
152
+ return `${s.slice(0, n - 1)} `;
153
+ return s + ' '.repeat(n - s.length);
154
+ }
155
+ //# sourceMappingURL=worktrees.js.map
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Centralised deprecation warning helper (Trust Sprint item 7).
3
+ *
4
+ * Pugi CLI is in 0.x and we are introducing the first generation of
5
+ * collapsed env vars. Operators with existing scripts need a clear
6
+ * forward path without breakage. Pattern:
7
+ *
8
+ * warnDeprecation('PUGI_OLD_FLAG', 'PUGI_NEW_FLAG=1', 'rationale...');
9
+ *
10
+ * Behaviour:
11
+ * - Emits ONE stderr line per (old, new) pair per process. Repeat
12
+ * calls for the same pair are suppressed so the operator does not
13
+ * see the same noise across many tool invocations in a session.
14
+ * - Silenced entirely when `PUGI_SUPPRESS_DEPRECATION_WARNINGS=1` is
15
+ * set (CI escape hatch; documented in operator-deploy-checklist).
16
+ * - Silenced in test runs via the standard `NODE_ENV=test` /
17
+ * `PUGI_TEST_MODE=1` checks so spec output stays clean.
18
+ *
19
+ * Brand rule: no AI attribution, no em dashes. Copy stays short and
20
+ * actionable so the operator can paste the new form into their script
21
+ * in one read.
22
+ */
23
+ const warnedPairs = new Set();
24
+ /**
25
+ * Reset the warned-once memo. Tests call this between cases so the
26
+ * one-warning-per-pair guard does not bleed across specs.
27
+ */
28
+ export function resetDeprecationWarningsForTest() {
29
+ warnedPairs.clear();
30
+ }
31
+ /**
32
+ * Emit a single deprecation warning to stderr.
33
+ *
34
+ * @param oldName literal name of the deprecated env/flag the operator
35
+ * is using (e.g. 'PUGI_MCP_EXEC_ENABLED').
36
+ * @param newName literal name of the replacement env/flag, including
37
+ * the form an operator should type (e.g.
38
+ * 'PUGI_MCP_ORCHESTRATOR=1').
39
+ * @param hint optional one-line rationale appended after the
40
+ * redirect. Keep under 80 chars; multi-line wrapping
41
+ * is the operator's terminal job, not ours.
42
+ */
43
+ export function warnDeprecation(oldName, newName, hint) {
44
+ if (isSilenced())
45
+ return;
46
+ const key = `${oldName}->${newName}`;
47
+ if (warnedPairs.has(key))
48
+ return;
49
+ warnedPairs.add(key);
50
+ const baseLine = `pugi: deprecation: ${oldName} is deprecated; prefer ${newName}.`;
51
+ const line = hint && hint.length > 0 ? `${baseLine} ${hint}` : baseLine;
52
+ process.stderr.write(`${line}\n`);
53
+ }
54
+ /**
55
+ * Internal helper. Lifted so tests can mock or override.
56
+ */
57
+ function isSilenced() {
58
+ const env = process.env;
59
+ if (env.PUGI_SUPPRESS_DEPRECATION_WARNINGS === '1')
60
+ return true;
61
+ if (env.PUGI_TEST_MODE === '1')
62
+ return true;
63
+ // NODE_ENV=test is set by `node --test`'s harness and by the
64
+ // pugi-cli vitest workflows.
65
+ if (env.NODE_ENV === 'test')
66
+ return true;
67
+ return false;
68
+ }
69
+ //# sourceMappingURL=deprecation-warning.js.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * engine-exit-code - Phase 1 verification gate invariant (PUGI-299).
3
+ *
4
+ * The runtime resolves the CLI exit code from the engine outcome via a
5
+ * deterministic ladder. Extracted from `cli.ts` so the invariant is
6
+ * exercisable from a focused spec without mounting the 9700-LOC entry
7
+ * module.
8
+ *
9
+ * Contract (in priority order):
10
+ * 1. `status === 'needs_verification'` → exit 2
11
+ * 2. `unverifiedReason === 'verification_command_failed'` → exit 1
12
+ * 3. `verificationFailures` is a non-empty array → exit 1
13
+ * 4. `verified === false && status === 'done'` → exit 2
14
+ * 5. `status === 'done'` → exit 0
15
+ * 6. `status === 'failed'` → exit 8
16
+ * 7. `status === 'blocked'` (catch-all) → exit 9
17
+ *
18
+ * The rule fires defensively: rules 3 + 4 cover producer bugs where a
19
+ * future engine adapter forgets to flip `unverifiedReason` or
20
+ * downgrade the status to `needs_verification`. Today
21
+ * `computeVerificationOutcome` always synthesises one of the two,
22
+ * but the invariant locks the contract.
23
+ */
24
+ export const ENGINE_EXIT_CODES = {
25
+ done: 0,
26
+ failed: 8,
27
+ blocked: 9,
28
+ engine_unavailable: 1,
29
+ needs_verification: 2,
30
+ };
31
+ export function resolveEngineExitCode(result) {
32
+ if (result.status === 'needs_verification') {
33
+ return ENGINE_EXIT_CODES.needs_verification;
34
+ }
35
+ if (result.unverifiedReason === 'verification_command_failed') {
36
+ return 1;
37
+ }
38
+ if (Array.isArray(result.verificationFailures) && result.verificationFailures.length > 0) {
39
+ return 1;
40
+ }
41
+ if (result.verified === false && result.status === 'done') {
42
+ return ENGINE_EXIT_CODES.needs_verification;
43
+ }
44
+ if (result.status === 'done')
45
+ return ENGINE_EXIT_CODES.done;
46
+ if (result.status === 'failed')
47
+ return ENGINE_EXIT_CODES.failed;
48
+ return ENGINE_EXIT_CODES.blocked;
49
+ }
50
+ //# sourceMappingURL=engine-exit-code.js.map
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Headless REPL — `pugi --headless` .
3
+ *
4
+ * Long-form rationale lives in the spec PR; the short version: every
5
+ * CLI publish gets manually smoke-tested today (CEO directive
6
+ * `feedback_live_console_test_every_publish`) and that toil must be
7
+ * automated. `pugi --headless` is the I/O surface that lets a scripted
8
+ * harness drive Pugi multi-turn — one stdin line in, one or more JSON
9
+ * envelopes out on stdout, exit when stdin closes. This is the
10
+ * machine-facing peer to the human-facing Ink REPL.
11
+ *
12
+ * Envelope shape (Phase 1):
13
+ *
14
+ * { "kind": "user-turn" | "persona-turn" | "tool-call" | "error"
15
+ * | "session-start" | "session-end" | "system",
16
+ * "body": "<string>",
17
+ * "ts": <epoch ms> }
18
+ *
19
+ * One JSON object per line. Stdout stays pure envelopes; stderr gets
20
+ * any human-readable trace. Discipline mirrors the existing
21
+ * `runHeadlessPrint` in `headless.ts`.
22
+ *
23
+ * Phase 1 engine wiring — the headless REPL emits the user-turn
24
+ * envelope verbatim, then asks the engine adapter for ONE turn, then
25
+ * emits the persona-turn / tool-call envelopes. Multi-turn state is
26
+ * accumulated in a single in-process session so consecutive lines see
27
+ * the same persona history. When a credential is absent (a common CI
28
+ * state) we fall through to a deterministic stub responder so the
29
+ * smoke harness can still exercise the I/O contract WITHOUT requiring
30
+ * an api.pugi.io reachability dependency.
31
+ */
32
+ import { createInterface } from 'node:readline';
33
+ import { resolve as resolvePath } from 'node:path';
34
+ import { resolveActiveCredential } from '../core/credentials.js';
35
+ /**
36
+ * Run the headless REPL loop. Returns the desired process exit code:
37
+ *
38
+ * 0 Stdin closed cleanly after at least one successful turn.
39
+ * 0 Stdin closed with no input (empty pipe — harmless, exit clean).
40
+ * 1 Fatal error from the turn handler.
41
+ *
42
+ * The caller (cli.ts) sets `process.exitCode`; we never call
43
+ * `process.exit` so an embedded driver can reuse the function.
44
+ */
45
+ export async function runHeadlessRepl(opts) {
46
+ const cwd = resolvePath(opts.cwd);
47
+ const stdoutWrite = opts.stdoutWrite ?? ((chunk) => process.stdout.write(chunk));
48
+ const stderrWrite = opts.stderrWrite ?? ((chunk) => process.stderr.write(chunk));
49
+ const now = opts.now ?? Date.now;
50
+ const turnHandler = opts.turnHandler ?? buildDefaultTurnHandler();
51
+ const stdin = opts.stdin ?? process.stdin;
52
+ emit(stdoutWrite, {
53
+ kind: 'session-start',
54
+ body: JSON.stringify({ cwd, cliVersion: 'phase1' }),
55
+ ts: now(),
56
+ });
57
+ const rl = createInterface({
58
+ input: stdin,
59
+ crlfDelay: Infinity,
60
+ terminal: false,
61
+ });
62
+ let turnIndex = 0;
63
+ let fatal = false;
64
+ for await (const rawLine of rl) {
65
+ const line = rawLine.replace(/\r$/, '');
66
+ if (line.length === 0)
67
+ continue;
68
+ emit(stdoutWrite, {
69
+ kind: 'user-turn',
70
+ body: line,
71
+ ts: now(),
72
+ });
73
+ try {
74
+ const out = await turnHandler({ line, turnIndex, cwd });
75
+ for (const env of out) {
76
+ emit(stdoutWrite, { kind: env.kind, body: env.body, ts: now() });
77
+ }
78
+ }
79
+ catch (error) {
80
+ const message = error instanceof Error ? error.message : String(error);
81
+ emit(stdoutWrite, {
82
+ kind: 'error',
83
+ body: JSON.stringify({ message }),
84
+ ts: now(),
85
+ });
86
+ stderrWrite(`pugi --headless: turn handler threw: ${message}\n`);
87
+ fatal = true;
88
+ break;
89
+ }
90
+ turnIndex += 1;
91
+ }
92
+ emit(stdoutWrite, {
93
+ kind: 'session-end',
94
+ body: JSON.stringify({ turns: turnIndex }),
95
+ ts: now(),
96
+ });
97
+ return fatal ? 1 : 0;
98
+ }
99
+ /**
100
+ * Emit a single envelope to stdout. Always terminates with `\n` so a
101
+ * line-buffered reader (the smoke runner, jq, etc.) sees each
102
+ * envelope atomically.
103
+ */
104
+ function emit(write, envelope) {
105
+ write(`${JSON.stringify(envelope)}\n`);
106
+ }
107
+ /**
108
+ * Build the default turn handler. When a Pugi credential is resolvable
109
+ * we'll plumb to the engine adapter in Phase 2; for Phase 1 the
110
+ * default responder is a deterministic stub. Smoke scenarios that
111
+ * exercise the engine path use a Phase-2-only flag (`PUGI_HEADLESS_LIVE=1`)
112
+ * to opt into the real engine; the default keeps CI offline-safe.
113
+ */
114
+ function buildDefaultTurnHandler() {
115
+ const credential = resolveActiveCredential();
116
+ if (credential && process.env.PUGI_HEADLESS_LIVE === '1') {
117
+ // Phase 2: route through `NativePugiEngineAdapter` here. Left as a
118
+ // stub so the Phase 1 PR stays narrowly scoped to the I/O surface.
119
+ // The smoke corpus does NOT exercise this branch in Phase 1.
120
+ return stubResponder('live engine path not yet wired (Phase 2)');
121
+ }
122
+ return stubResponder('pugi headless stub: no credential or PUGI_HEADLESS_LIVE!=1; echoing input');
123
+ }
124
+ /**
125
+ * Tiny deterministic responder used by Phase 1 smoke tests. Echoes the
126
+ * input back as a `persona-turn` envelope with a stable prefix so
127
+ * scenarios can author assertions against it. The contract is:
128
+ *
129
+ * - Input "ты кто?" → persona-turn "Pugi: ты кто? (stub)"
130
+ * - Input "create FILE with content X"
131
+ * → tool-call Write file=FILE +
132
+ * persona-turn "Pugi: wrote FILE"
133
+ * - Any other input → persona-turn "Pugi: ...(stub)"
134
+ *
135
+ * Real engine routing is Phase 2. The stub exists to validate the
136
+ * envelope contract end-to-end in CI without depending on api.pugi.io.
137
+ */
138
+ function stubResponder(banner) {
139
+ return async (input) => {
140
+ const text = input.line.trim();
141
+ const envelopes = [];
142
+ const createMatch = /^create\s+(\S+)\s+with\s+content\s+['"]([^'"]+)['"]\s*$/i.exec(text);
143
+ if (createMatch) {
144
+ const file = createMatch[1] ?? 'unknown';
145
+ const body = createMatch[2] ?? '';
146
+ const { writeFileSync, mkdirSync } = await import('node:fs');
147
+ const { dirname } = await import('node:path');
148
+ const absolute = resolvePath(input.cwd, file);
149
+ try {
150
+ mkdirSync(dirname(absolute), { recursive: true });
151
+ writeFileSync(absolute, body, 'utf8');
152
+ }
153
+ catch (error) {
154
+ envelopes.push({
155
+ kind: 'error',
156
+ body: JSON.stringify({
157
+ message: `write failed: ${error.message}`,
158
+ }),
159
+ });
160
+ return envelopes;
161
+ }
162
+ envelopes.push({
163
+ kind: 'tool-call',
164
+ body: JSON.stringify({
165
+ tool: 'Write',
166
+ args: { file, content: body },
167
+ }),
168
+ });
169
+ envelopes.push({
170
+ kind: 'persona-turn',
171
+ body: `Pugi: wrote ${file}`,
172
+ });
173
+ return envelopes;
174
+ }
175
+ if (/^ты\s+кто/i.test(text) || /^who\s+are\s+you/i.test(text)) {
176
+ envelopes.push({
177
+ kind: 'persona-turn',
178
+ body: 'Pugi: я Pugi, твой co-pilot. (Пуджи, stub)',
179
+ });
180
+ return envelopes;
181
+ }
182
+ envelopes.push({
183
+ kind: 'persona-turn',
184
+ body: `Pugi: ${text} (stub)`,
185
+ });
186
+ // One-time banner on the first turn so operators see why the stub
187
+ // is firing. Suppressed on subsequent turns to keep the envelope
188
+ // stream noise-free.
189
+ if (input.turnIndex === 0) {
190
+ envelopes.push({ kind: 'system', body: banner });
191
+ }
192
+ return envelopes;
193
+ };
194
+ }
195
+ //# sourceMappingURL=headless-repl.js.map