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

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 (409) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/LICENSE +1 -1
  3. package/assets/pugi-prozr2-mascot.ansi +9 -0
  4. package/bin/run.js +33 -1
  5. package/dist/commands/deploy.js +40 -40
  6. package/dist/commands/flatten.js +191 -0
  7. package/dist/commands/jobs-watch.js +201 -0
  8. package/dist/commands/jobs.js +42 -27
  9. package/dist/commands/smoke.js +133 -0
  10. package/dist/core/agent-progress/cleanup.js +134 -0
  11. package/dist/core/agent-progress/schema.js +144 -0
  12. package/dist/core/agent-progress/writer.js +101 -0
  13. package/dist/core/agents/adaptive-router.js +330 -0
  14. package/dist/core/agents/query-decomposer.js +297 -0
  15. package/dist/core/agents/registry.js +3 -3
  16. package/dist/core/approvals/shortcut-resolver.js +98 -0
  17. package/dist/core/artifact-chain/dispatcher.js +148 -0
  18. package/dist/core/artifact-chain/exporter.js +164 -0
  19. package/dist/core/artifact-chain/state.js +243 -0
  20. package/dist/core/artifact-chain/steps.js +169 -0
  21. package/dist/core/ask-user/question.js +92 -0
  22. package/dist/core/audit/audit-trail.js +275 -0
  23. package/dist/core/auth/ensure-authenticated.js +129 -0
  24. package/dist/core/auth/env-provider.js +238 -0
  25. package/dist/core/auto-open-browser.js +4 -4
  26. package/dist/core/auto-update/channels.js +122 -0
  27. package/dist/core/auto-update/checker.js +241 -0
  28. package/dist/core/auto-update/state.js +235 -0
  29. package/dist/core/bare-mode/index.js +107 -0
  30. package/dist/core/bash/redirect.js +281 -0
  31. package/dist/core/bash-classifier.js +436 -40
  32. package/dist/core/checkpoint/resumer.js +149 -0
  33. package/dist/core/checkpoint/rewinder.js +291 -0
  34. package/dist/core/checkpoints/shadow-git.js +670 -0
  35. package/dist/core/citations/parser.js +109 -0
  36. package/dist/core/classifier/yolo-classifier.js +88 -0
  37. package/dist/core/codegraph/decision-store.js +248 -0
  38. package/dist/core/codegraph/detect-repo.js +459 -0
  39. package/dist/core/codegraph/install.js +134 -0
  40. package/dist/core/codegraph/offer-hook.js +220 -0
  41. package/dist/core/compact/auto-trigger.js +96 -0
  42. package/dist/core/compact/buffer-rewriter.js +115 -0
  43. package/dist/core/compact/summarizer.js +208 -0
  44. package/dist/core/compact/token-counter.js +108 -0
  45. package/dist/core/consensus/anvil-fanout.js +25 -25
  46. package/dist/core/consensus/diff-capture.js +121 -12
  47. package/dist/core/consensus/rubric.js +21 -21
  48. package/dist/core/context/builder.js +6 -6
  49. package/dist/core/context/compaction-events.js +8 -8
  50. package/dist/core/context/compaction.js +31 -31
  51. package/dist/core/context/index.js +15 -8
  52. package/dist/core/context/invariants.js +51 -51
  53. package/dist/core/context/markdown-loader.js +28 -10
  54. package/dist/core/context/markdown-traverse.js +255 -0
  55. package/dist/core/context/pugiignore.js +41 -41
  56. package/dist/core/context/repo-skeleton.js +37 -37
  57. package/dist/core/context/tool-eviction.js +55 -0
  58. package/dist/core/context/watcher.js +32 -32
  59. package/dist/core/context/working-set.js +23 -23
  60. package/dist/core/coordinator/agent-tools.js +77 -0
  61. package/dist/core/coordinator/agent-toolset.js +65 -0
  62. package/dist/core/coordinator/fsm.js +73 -0
  63. package/dist/core/coordinator/mode-fsm.js +70 -0
  64. package/dist/core/cost/rate-card.js +129 -0
  65. package/dist/core/cost/tracker.js +221 -0
  66. package/dist/core/credentials.js +13 -13
  67. package/dist/core/cron/scheduler.js +138 -0
  68. package/dist/core/denial-tracking/index.js +8 -0
  69. package/dist/core/denial-tracking/state.js +264 -0
  70. package/dist/core/diagnostics/probe-runner.js +93 -0
  71. package/dist/core/diagnostics/probes/api.js +46 -0
  72. package/dist/core/diagnostics/probes/auth.js +93 -0
  73. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  74. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  75. package/dist/core/diagnostics/probes/config.js +72 -0
  76. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  77. package/dist/core/diagnostics/probes/disk.js +81 -0
  78. package/dist/core/diagnostics/probes/engine-live.js +46 -0
  79. package/dist/core/diagnostics/probes/git.js +65 -0
  80. package/dist/core/diagnostics/probes/hooks.js +118 -0
  81. package/dist/core/diagnostics/probes/mcp.js +75 -0
  82. package/dist/core/diagnostics/probes/node.js +59 -0
  83. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  84. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  85. package/dist/core/diagnostics/probes/sandbox.js +40 -0
  86. package/dist/core/diagnostics/probes/session.js +74 -0
  87. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  88. package/dist/core/diagnostics/probes/workspace.js +63 -0
  89. package/dist/core/diagnostics/types.js +70 -0
  90. package/dist/core/dispatch/cache-cleanup.js +197 -0
  91. package/dist/core/dispatch/cache-handoff.js +295 -0
  92. package/dist/core/edits/apply-patch-layer-e.js +189 -0
  93. package/dist/core/edits/dispatch.js +333 -7
  94. package/dist/core/edits/format-detector.js +260 -0
  95. package/dist/core/edits/format-matrix.js +26 -0
  96. package/dist/core/edits/fuzzy-ladder.js +650 -0
  97. package/dist/core/edits/index.js +5 -1
  98. package/dist/core/edits/journal.js +199 -0
  99. package/dist/core/edits/layer-a-apply.js +15 -15
  100. package/dist/core/edits/layer-a-fuzzy-apply.js +198 -0
  101. package/dist/core/edits/layer-b-apply.js +9 -9
  102. package/dist/core/edits/layer-c-apply.js +6 -6
  103. package/dist/core/edits/layer-d-ast.js +557 -14
  104. package/dist/core/edits/marker-parser.js +12 -12
  105. package/dist/core/edits/security-gate.js +27 -27
  106. package/dist/core/edits/verify-hook.js +273 -0
  107. package/dist/core/edits/worktree.js +29 -29
  108. package/dist/core/engine/anvil-client.js +214 -26
  109. package/dist/core/engine/auto-compact.js +179 -0
  110. package/dist/core/engine/budgets.js +186 -0
  111. package/dist/core/engine/context-prefix.js +155 -0
  112. package/dist/core/engine/index.js +1 -1
  113. package/dist/core/engine/intensity.js +158 -0
  114. package/dist/core/engine/intent.js +260 -0
  115. package/dist/core/engine/native-pugi.js +1295 -227
  116. package/dist/core/engine/prompts.js +129 -19
  117. package/dist/core/engine/strip-internal-fields.js +124 -0
  118. package/dist/core/engine/tool-bridge.js +1731 -59
  119. package/dist/core/evaluation/golden-dataset.js +293 -0
  120. package/dist/core/feedback/queue.js +177 -0
  121. package/dist/core/feedback/submitter.js +145 -0
  122. package/dist/core/file-cache.js +113 -1
  123. package/dist/core/flatten/flatten-repo.js +439 -0
  124. package/dist/core/format/osc8-link.js +28 -0
  125. package/dist/core/hook-chains.js +392 -0
  126. package/dist/core/hooks/citation-verify-hook.js +138 -0
  127. package/dist/core/hooks/citation-verify.js +112 -0
  128. package/dist/core/hooks/events.js +46 -0
  129. package/dist/core/hooks/index.js +15 -0
  130. package/dist/core/hooks/registry.js +216 -0
  131. package/dist/core/hooks/runner.js +236 -0
  132. package/dist/core/hooks/v2/event-emitter.js +115 -0
  133. package/dist/core/hooks/v2/executor.js +282 -0
  134. package/dist/core/hooks/v2/index.js +25 -0
  135. package/dist/core/hooks/v2/lifecycle.js +104 -0
  136. package/dist/core/hooks/v2/loader.js +216 -0
  137. package/dist/core/hooks/v2/matcher.js +125 -0
  138. package/dist/core/hooks/v2/trust.js +143 -0
  139. package/dist/core/hooks/v2/types.js +86 -0
  140. package/dist/core/hooks/worktree-events.js +158 -0
  141. package/dist/core/image/renderer.js +71 -0
  142. package/dist/core/init/detector.js +582 -0
  143. package/dist/core/init/template-renderer.js +242 -0
  144. package/dist/core/jobs/registry.js +18 -18
  145. package/dist/core/ledger/results-tsv.js +142 -0
  146. package/dist/core/log-discipline/stdout-redirect.js +51 -0
  147. package/dist/core/lsp/cache.js +105 -0
  148. package/dist/core/lsp/client.js +551 -41
  149. package/dist/core/lsp/language-detect.js +66 -0
  150. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  151. package/dist/core/lsp/server-detect.js +173 -0
  152. package/dist/core/lsp/symbol-cache.js +162 -0
  153. package/dist/core/lsp/symbol-tools.js +664 -0
  154. package/dist/core/mcp/client.js +97 -28
  155. package/dist/core/mcp/http-server.js +553 -0
  156. package/dist/core/mcp/orchestrator-tools.js +662 -0
  157. package/dist/core/mcp/permission.js +190 -0
  158. package/dist/core/mcp/registry.js +39 -17
  159. package/dist/core/mcp/server-tools.js +219 -0
  160. package/dist/core/mcp/server.js +397 -0
  161. package/dist/core/mcp/trust.js +10 -10
  162. package/dist/core/memory/dual-write.js +416 -0
  163. package/dist/core/memory/passive-extract.js +130 -0
  164. package/dist/core/memory/phase1-kinds.js +20 -0
  165. package/dist/core/memory/secret-scanner.js +304 -0
  166. package/dist/core/memory-sync/queue.js +170 -0
  167. package/dist/core/metrics/extract.js +113 -0
  168. package/dist/core/modes/roo-modes.js +68 -0
  169. package/dist/core/onboarding/ensure-initialized.js +133 -0
  170. package/dist/core/onboarding/marker.js +111 -0
  171. package/dist/core/onboarding/telemetry-state.js +108 -0
  172. package/dist/core/output-style/presets.js +176 -0
  173. package/dist/core/output-style/state.js +185 -0
  174. package/dist/core/path-security.js +287 -5
  175. package/dist/core/permission.js +82 -22
  176. package/dist/core/permissions/auto-classifier.js +124 -0
  177. package/dist/core/permissions/bash-parser.js +371 -0
  178. package/dist/core/permissions/circuit-breaker.js +83 -0
  179. package/dist/core/permissions/constrained-edit.js +91 -0
  180. package/dist/core/permissions/gate.js +278 -0
  181. package/dist/core/permissions/index.js +20 -0
  182. package/dist/core/permissions/mode.js +174 -0
  183. package/dist/core/permissions/network-egress.js +137 -0
  184. package/dist/core/permissions/state.js +241 -0
  185. package/dist/core/permissions/tool-class.js +93 -0
  186. package/dist/core/plan-mode/ui-state.js +51 -0
  187. package/dist/core/plans/plan-artifact.js +721 -0
  188. package/dist/core/policy-limits/etag-store.js +122 -0
  189. package/dist/core/prd-check/parser.js +215 -0
  190. package/dist/core/prd-check/reporter.js +127 -0
  191. package/dist/core/prd-check/session-review.js +557 -0
  192. package/dist/core/prd-check/verifiers.js +223 -0
  193. package/dist/core/prompt-cache/client-cache.js +99 -0
  194. package/dist/core/prompts/assembly.js +29 -0
  195. package/dist/core/prompts/registry.js +364 -0
  196. package/dist/core/pugi-md/cc-compat-rules.js +735 -0
  197. package/dist/core/pugi-md/context-injector.js +76 -0
  198. package/dist/core/pugi-md/walk-up.js +207 -0
  199. package/dist/core/python/uv-installer.js +270 -0
  200. package/dist/core/python/uv-resolver.js +83 -0
  201. package/dist/core/rate-limit/narrator.js +146 -0
  202. package/dist/core/recipes/cli-types.js +20 -0
  203. package/dist/core/recipes/loader.js +103 -0
  204. package/dist/core/recipes/runner.js +345 -0
  205. package/dist/core/recipes/schema.js +587 -0
  206. package/dist/core/release-notes/parser.js +241 -0
  207. package/dist/core/release-notes/state.js +116 -0
  208. package/dist/core/repl/ask.js +37 -37
  209. package/dist/core/repl/cancellation.js +26 -26
  210. package/dist/core/repl/cap-warning.js +4 -4
  211. package/dist/core/repl/clipboard-read.js +11 -11
  212. package/dist/core/repl/dispatch-fsm.js +12 -12
  213. package/dist/core/repl/history-search.js +15 -15
  214. package/dist/core/repl/history.js +28 -18
  215. package/dist/core/repl/kill-ring.js +5 -5
  216. package/dist/core/repl/model-pricing.js +135 -0
  217. package/dist/core/repl/privacy-banner.js +22 -22
  218. package/dist/core/repl/session.js +2148 -217
  219. package/dist/core/repl/slash-commands.js +501 -41
  220. package/dist/core/repl/store/index.js +1 -1
  221. package/dist/core/repl/store/jsonl-log.js +22 -22
  222. package/dist/core/repl/store/lockfile.js +10 -10
  223. package/dist/core/repl/store/session-store.js +136 -107
  224. package/dist/core/repl/store/types.js +15 -15
  225. package/dist/core/repl/store/uuid-v7.js +12 -12
  226. package/dist/core/repl/workspace-context.js +43 -21
  227. package/dist/core/repo-map/build.js +125 -0
  228. package/dist/core/repo-map/cache.js +185 -0
  229. package/dist/core/repo-map/extractor.js +254 -0
  230. package/dist/core/repo-map/formatter.js +145 -0
  231. package/dist/core/repo-map/page-rank.js +105 -0
  232. package/dist/core/repo-map/scanner.js +211 -0
  233. package/dist/core/retry-budget/budget.js +284 -0
  234. package/dist/core/retry-budget/index.js +5 -0
  235. package/dist/core/retry-budget/retry-cap.js +74 -0
  236. package/dist/core/routing/lead-worker.js +43 -0
  237. package/dist/core/routing/pre-flight-estimator.js +108 -0
  238. package/dist/core/runs/run-tree.js +103 -0
  239. package/dist/core/security/injection-scanner.js +367 -0
  240. package/dist/core/security/output-filter.js +418 -0
  241. package/dist/core/session/env-file.js +105 -0
  242. package/dist/core/session/section-budgets.js +140 -0
  243. package/dist/core/session.js +92 -0
  244. package/dist/core/settings.js +324 -5
  245. package/dist/core/share/formatter.js +271 -0
  246. package/dist/core/share/redactor.js +221 -0
  247. package/dist/core/share/uploader.js +267 -0
  248. package/dist/core/skills/defaults.js +30 -30
  249. package/dist/core/skills/loader.js +22 -22
  250. package/dist/core/skills/sources.js +27 -27
  251. package/dist/core/smoke/headless-driver.js +174 -0
  252. package/dist/core/smoke/orchestrator.js +194 -0
  253. package/dist/core/smoke/runner.js +238 -0
  254. package/dist/core/smoke/scenario-parser.js +316 -0
  255. package/dist/core/statusline.js +99 -0
  256. package/dist/core/subagents/dispatcher-real.js +600 -0
  257. package/dist/core/subagents/dispatcher.js +132 -43
  258. package/dist/core/subagents/index.js +19 -6
  259. package/dist/core/subagents/isolation-matrix.js +213 -0
  260. package/dist/core/subagents/spawn.js +19 -4
  261. package/dist/core/telemetry/emitter.js +229 -0
  262. package/dist/core/telemetry/queue.js +251 -0
  263. package/dist/core/theme/context.js +91 -0
  264. package/dist/core/theme/presets.js +228 -0
  265. package/dist/core/theme/state.js +181 -0
  266. package/dist/core/todos/invariant.js +10 -0
  267. package/dist/core/todos/state.js +177 -0
  268. package/dist/core/tool-schema/compressor.js +89 -0
  269. package/dist/core/transport/version-interceptor.js +166 -0
  270. package/dist/core/trust.js +2 -2
  271. package/dist/core/tui/thinking-block.js +64 -0
  272. package/dist/core/vim/keymap.js +288 -0
  273. package/dist/core/vim/state.js +92 -0
  274. package/dist/core/watch-markers/marker-watcher.js +133 -0
  275. package/dist/core/worktree/include-parser.js +249 -0
  276. package/dist/core/worktree-manager/cleanup.js +123 -0
  277. package/dist/core/worktree-manager/manager.js +303 -0
  278. package/dist/index.js +36 -0
  279. package/dist/runtime/bootstrap.js +190 -0
  280. package/dist/runtime/cli.js +4185 -549
  281. package/dist/runtime/commands/agents.js +31 -31
  282. package/dist/runtime/commands/budget.js +5 -5
  283. package/dist/runtime/commands/cancel.js +231 -0
  284. package/dist/runtime/commands/chain.js +489 -0
  285. package/dist/runtime/commands/codegraph-status.js +227 -0
  286. package/dist/runtime/commands/compact.js +297 -0
  287. package/dist/runtime/commands/config.js +73 -39
  288. package/dist/runtime/commands/cost.js +199 -0
  289. package/dist/runtime/commands/delegate.js +27 -4
  290. package/dist/runtime/commands/dispatch.js +126 -0
  291. package/dist/runtime/commands/doctor.js +579 -0
  292. package/dist/runtime/commands/feedback.js +184 -0
  293. package/dist/runtime/commands/hooks.js +187 -0
  294. package/dist/runtime/commands/init.js +254 -0
  295. package/dist/runtime/commands/lsp.js +200 -38
  296. package/dist/runtime/commands/mcp.js +879 -0
  297. package/dist/runtime/commands/memory.js +582 -0
  298. package/dist/runtime/commands/model.js +237 -0
  299. package/dist/runtime/commands/onboarding.js +275 -0
  300. package/dist/runtime/commands/patch.js +12 -12
  301. package/dist/runtime/commands/permissions.js +112 -0
  302. package/dist/runtime/commands/plan.js +143 -0
  303. package/dist/runtime/commands/prd-check.js +285 -0
  304. package/dist/runtime/commands/privacy.js +17 -17
  305. package/dist/runtime/commands/recipe.js +325 -0
  306. package/dist/runtime/commands/redo-blob-store.js +92 -0
  307. package/dist/runtime/commands/redo.js +361 -0
  308. package/dist/runtime/commands/release-notes.js +229 -0
  309. package/dist/runtime/commands/repo-map.js +95 -0
  310. package/dist/runtime/commands/report.js +299 -0
  311. package/dist/runtime/commands/resume.js +118 -0
  312. package/dist/runtime/commands/review-consensus.js +68 -53
  313. package/dist/runtime/commands/rewind.js +333 -0
  314. package/dist/runtime/commands/roster.js +14 -14
  315. package/dist/runtime/commands/sessions.js +163 -0
  316. package/dist/runtime/commands/share.js +316 -0
  317. package/dist/runtime/commands/skills.js +31 -31
  318. package/dist/runtime/commands/status.js +186 -0
  319. package/dist/runtime/commands/stickers.js +82 -0
  320. package/dist/runtime/commands/style.js +194 -0
  321. package/dist/runtime/commands/theme.js +196 -0
  322. package/dist/runtime/commands/undo.js +54 -22
  323. package/dist/runtime/commands/update.js +289 -0
  324. package/dist/runtime/commands/vim.js +140 -0
  325. package/dist/runtime/commands/worktree.js +8 -8
  326. package/dist/runtime/commands/worktrees.js +155 -0
  327. package/dist/runtime/headless-repl.js +195 -0
  328. package/dist/runtime/headless.js +543 -0
  329. package/dist/runtime/load-hooks-or-exit.js +71 -0
  330. package/dist/runtime/plan-decompose.js +22 -22
  331. package/dist/runtime/sigint-guard.js +272 -0
  332. package/dist/runtime/update-check.js +28 -28
  333. package/dist/runtime/version.js +65 -0
  334. package/dist/runtime/worktree-bootstrap.js +579 -0
  335. package/dist/skills/bundled/batch.js +617 -0
  336. package/dist/skills/bundled/index.js +45 -0
  337. package/dist/skills/bundled/loop.js +358 -0
  338. package/dist/skills/bundled/remember.js +383 -0
  339. package/dist/skills/bundled/simplify.js +289 -0
  340. package/dist/skills/bundled/skillify.js +373 -0
  341. package/dist/skills/bundled/stuck.js +558 -0
  342. package/dist/skills/bundled/verify.js +439 -0
  343. package/dist/testing/vcr.js +486 -0
  344. package/dist/tools/agent-tool.js +229 -0
  345. package/dist/tools/apply-patch.js +89 -28
  346. package/dist/tools/ask-user-question.js +337 -0
  347. package/dist/tools/ask-user.js +115 -0
  348. package/dist/tools/bash.js +624 -46
  349. package/dist/tools/brief.js +224 -0
  350. package/dist/tools/enter-worktree.js +250 -0
  351. package/dist/tools/exit-worktree.js +147 -0
  352. package/dist/tools/file-tools.js +161 -44
  353. package/dist/tools/lsp-tools.js +377 -1
  354. package/dist/tools/mcp-tool.js +260 -0
  355. package/dist/tools/multi-edit.js +361 -0
  356. package/dist/tools/powershell.js +268 -0
  357. package/dist/tools/registry.js +86 -4
  358. package/dist/tools/skill-tool.js +96 -0
  359. package/dist/tools/sleep.js +99 -0
  360. package/dist/tools/synthetic-output.js +133 -0
  361. package/dist/tools/tasks.js +208 -0
  362. package/dist/tools/todo-write.js +184 -0
  363. package/dist/tools/verify-plan-execution.js +295 -0
  364. package/dist/tools/web-fetch-injection-scanner.js +207 -0
  365. package/dist/tools/web-fetch.js +195 -10
  366. package/dist/tools/web-search.js +458 -0
  367. package/dist/tui/agent-progress-card.js +111 -0
  368. package/dist/tui/agent-tree.js +11 -1
  369. package/dist/tui/ask-modal.js +14 -14
  370. package/dist/tui/ask-user-question-chips.js +315 -0
  371. package/dist/tui/ask-user-question-prompt.js +203 -0
  372. package/dist/tui/compact-banner.js +81 -0
  373. package/dist/tui/conversation-pane.js +85 -11
  374. package/dist/tui/cost-table.js +111 -0
  375. package/dist/tui/device-flow.js +2 -2
  376. package/dist/tui/doctor-table.js +46 -0
  377. package/dist/tui/feedback-prompt.js +156 -0
  378. package/dist/tui/input-box.js +247 -32
  379. package/dist/tui/login-picker.js +3 -3
  380. package/dist/tui/markdown-render.js +6 -6
  381. package/dist/tui/onboarding-wizard.js +240 -0
  382. package/dist/tui/permissions-picker.js +86 -0
  383. package/dist/tui/render.js +36 -1
  384. package/dist/tui/repl-render.js +176 -25
  385. package/dist/tui/repl-splash-art.js +16 -16
  386. package/dist/tui/repl-splash-mascot.js +48 -24
  387. package/dist/tui/repl-splash.js +22 -22
  388. package/dist/tui/repl.js +125 -45
  389. package/dist/tui/slash-palette.js +6 -6
  390. package/dist/tui/splash.js +2 -2
  391. package/dist/tui/status-bar.js +109 -31
  392. package/dist/tui/status-table.js +7 -0
  393. package/dist/tui/stickers-art.js +136 -0
  394. package/dist/tui/style-table.js +28 -0
  395. package/dist/tui/theme-table.js +29 -0
  396. package/dist/tui/thinking-spinner.js +123 -0
  397. package/dist/tui/tool-stream-pane.js +53 -4
  398. package/dist/tui/update-banner.js +27 -2
  399. package/dist/tui/vim-input.js +267 -0
  400. package/dist/tui/welcome-banner.js +107 -0
  401. package/dist/tui/welcome-data.js +293 -0
  402. package/dist/tui/workspace-context.js +2 -2
  403. package/package.json +31 -16
  404. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  405. package/test/scenarios/compact-force.scenario.txt +12 -0
  406. package/test/scenarios/identity.scenario.txt +12 -0
  407. package/test/scenarios/persona-handoff.scenario.txt +12 -0
  408. package/test/scenarios/walkback.scenario.txt +12 -0
  409. package/dist/core/engine/compaction-hook.js +0 -154
@@ -0,0 +1,579 @@
1
+ /**
2
+ * `pugi --worktree` bootstrap - PUGI-487.
3
+ *
4
+ * User-facing parity surface for parallel-agent isolation patterns
5
+ * seen across competitor AI coding CLIs. The flag creates an isolated
6
+ * git worktree at `.claude/worktrees/<name>/` on a new branch
7
+ * `worktree-<name>` and starts the REPL inside it. Operators can run
8
+ * several Pugi sessions in parallel without their filesystems
9
+ * colliding.
10
+ *
11
+ * Distinct namespace from the existing dispatcher worktree manager:
12
+ *
13
+ * - This module owns `.claude/worktrees/<name>/` (USER-facing,
14
+ * short kebab names like `feat-x` or auto-generated `bright-fox`).
15
+ * - `core/worktree-manager/` owns `.pugi/worktrees/<agent-id>/`
16
+ * (DISPATCHER-facing, agent-id keyed).
17
+ *
18
+ * Public surface:
19
+ *
20
+ * - `bootstrapWorktree(options)` - validate inputs, run hooks,
21
+ * create the worktree, copy `.worktreeinclude` matches, return
22
+ * a typed envelope ready for the CLI to chdir + REPL.
23
+ * - `generateWhimsicalName()` - adjective + animal pair for the
24
+ * auto-named case (`pugi --worktree` with no name).
25
+ * - `runUserWorktreeCleanup(options)` - the daily sweep that removes
26
+ * user-facing worktrees > N days old with no uncommitted state.
27
+ *
28
+ * Brand voice: ASCII only, no emoji, no banned words.
29
+ */
30
+ import { spawnSync } from 'node:child_process';
31
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, } from 'node:fs';
32
+ import { dirname, isAbsolute, posix, resolve, sep } from 'node:path';
33
+ import { loadHooksConfig } from '../core/hooks/registry.js';
34
+ import { fireWorktreeHooks, } from '../core/hooks/worktree-events.js';
35
+ import { collectWorktreeIncludeFiles, loadWorktreeInclude, } from '../core/worktree/include-parser.js';
36
+ import { isTrustedWorkspace } from '../core/trust.js';
37
+ /** Canonical directory prefix for user-facing worktrees. */
38
+ export const USER_WORKTREES_PREFIX = '.claude/worktrees';
39
+ /**
40
+ * Branch prefix for user-facing worktree branches. The full name is
41
+ * `worktree-<name>` so the operator can quickly see which branches in
42
+ * `git branch` came from `pugi --worktree`.
43
+ */
44
+ export const USER_WORKTREE_BRANCH_PREFIX = 'worktree-';
45
+ /** Strict slug regex for the operator-supplied name. */
46
+ export const WORKTREE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
47
+ /** Strict regex for the PR-shorthand sugar. */
48
+ export const PR_SHORTHAND_PATTERN = /^#(\d{1,8})$/;
49
+ /**
50
+ * The main entry point invoked by `runtime/cli.ts` when `--worktree`
51
+ * is set on the bare invocation. Returns the typed envelope so the
52
+ * CLI layer can decide whether to chdir + mount the REPL or surface
53
+ * an error.
54
+ */
55
+ export async function bootstrapWorktree(options) {
56
+ const runGit = options.runGit ?? defaultGitRunner;
57
+ const repoRoot = resolve(options.repoRoot);
58
+ // Step 1: confirm we are actually in a git repo. The standard
59
+ // `rev-parse --git-dir` probe surfaces the most useful error when
60
+ // the operator runs `pugi --worktree` in `$HOME` by mistake.
61
+ const probe = runGit(['rev-parse', '--git-dir'], repoRoot);
62
+ if (probe.status !== 0) {
63
+ return failure({
64
+ name: '',
65
+ branch: '',
66
+ directory: '',
67
+ baseRef: options.baseRef ?? 'fresh',
68
+ resolvedBase: '',
69
+ reason: 'not_a_git_repo',
70
+ message: `not a git repository: ${repoRoot}`,
71
+ });
72
+ }
73
+ // Step 2: trust gate. The default-restricted policy (see
74
+ // `core/trust.ts`) refuses to load workspace-scoped hooks unless the
75
+ // operator has explicitly trusted the directory. Worktrees inherit
76
+ // their parent's trust state so a `pugi init`-trusted root carries
77
+ // through to every child created via this flag.
78
+ const trustGate = options.isTrusted ?? isTrustedWorkspace;
79
+ const skipTrust = options.skipTrustGate === true ||
80
+ process.env.PUGI_SKIP_WORKTREE_TRUST === '1';
81
+ if (!skipTrust) {
82
+ const trusted = await trustGate(repoRoot);
83
+ if (!trusted) {
84
+ return failure({
85
+ name: '',
86
+ branch: '',
87
+ directory: '',
88
+ baseRef: options.baseRef ?? 'fresh',
89
+ resolvedBase: '',
90
+ reason: 'untrusted_workspace',
91
+ message: `workspace ${repoRoot} is not trusted. Run \`pugi init\` or \`pugi config trust .\` first.`,
92
+ });
93
+ }
94
+ }
95
+ // Step 3: resolve the requested name. Three shapes:
96
+ // - PR shorthand `#1234`
97
+ // - operator-supplied slug
98
+ // - undefined => auto-generate
99
+ const nameResolution = resolveName(options.nameArg, options.generateName);
100
+ if (typeof nameResolution === 'string') {
101
+ return failure({
102
+ name: typeof options.nameArg === 'string' ? options.nameArg : '',
103
+ branch: '',
104
+ directory: '',
105
+ baseRef: options.baseRef ?? 'fresh',
106
+ resolvedBase: '',
107
+ reason: 'invalid_name',
108
+ message: nameResolution,
109
+ });
110
+ }
111
+ const { name, branch, prNumber } = nameResolution;
112
+ const baseRefMode = options.baseRef ?? 'fresh';
113
+ // Step 4: compute the target directory + resolved base ref.
114
+ const defaultDirectory = resolve(repoRoot, USER_WORKTREES_PREFIX, name);
115
+ if (existsSync(defaultDirectory)) {
116
+ return failure({
117
+ name,
118
+ branch,
119
+ directory: defaultDirectory,
120
+ baseRef: baseRefMode,
121
+ resolvedBase: '',
122
+ reason: 'directory_exists',
123
+ message: `worktree already exists at ${defaultDirectory}`,
124
+ });
125
+ }
126
+ const baseSha = resolveBaseSha(runGit, repoRoot, baseRefMode, prNumber);
127
+ if (typeof baseSha === 'string' && baseSha.startsWith('ERR:')) {
128
+ return failure({
129
+ name,
130
+ branch,
131
+ directory: defaultDirectory,
132
+ baseRef: baseRefMode,
133
+ resolvedBase: '',
134
+ reason: prNumber !== undefined ? 'pr_fetch_failed' : 'git_command_failed',
135
+ message: baseSha.slice('ERR:'.length),
136
+ });
137
+ }
138
+ // Step 5: fire the WorktreeCreate hook. Hooks may veto via non-zero
139
+ // exit (with `blocking: true`) or override the directory location.
140
+ const hooks = (options.loadHooks ?? loadHooksConfig)();
141
+ const createPayload = {
142
+ event: 'WorktreeCreate',
143
+ name,
144
+ defaultDirectory,
145
+ baseRef: typeof baseSha === 'string' ? baseSha : '',
146
+ repoRoot,
147
+ };
148
+ const createOutcome = fireWorktreeHooks(hooks, 'WorktreeCreate', createPayload);
149
+ if (createOutcome.anyBlocked) {
150
+ return failure({
151
+ name,
152
+ branch,
153
+ directory: defaultDirectory,
154
+ baseRef: baseRefMode,
155
+ resolvedBase: typeof baseSha === 'string' ? baseSha : '',
156
+ reason: 'hook_blocked',
157
+ message: `WorktreeCreate hook blocked the operation`,
158
+ });
159
+ }
160
+ // Override resolution: a hook stdout `{ "directory": "<abs-path>" }`
161
+ // wins ONLY when the path is inside the repo root. We refuse
162
+ // out-of-repo overrides defensively because the cleanup sweep would
163
+ // never reach them.
164
+ let directory = defaultDirectory;
165
+ if (createOutcome.directoryOverride) {
166
+ const override = resolve(repoRoot, createOutcome.directoryOverride);
167
+ if (override.startsWith(repoRoot + sep)) {
168
+ directory = override;
169
+ }
170
+ }
171
+ // Step 6: git worktree add. We resolve the base SHA above so the
172
+ // branch ref does not depend on a runtime-mutable `origin/HEAD` at
173
+ // creation time.
174
+ mkdirSync(dirname(directory), { recursive: true });
175
+ const addArgs = [
176
+ 'worktree',
177
+ 'add',
178
+ '-b',
179
+ branch,
180
+ '--',
181
+ directory,
182
+ typeof baseSha === 'string' ? baseSha : 'HEAD',
183
+ ];
184
+ const add = runGit(addArgs, repoRoot);
185
+ if (add.status !== 0) {
186
+ return failure({
187
+ name,
188
+ branch,
189
+ directory,
190
+ baseRef: baseRefMode,
191
+ resolvedBase: typeof baseSha === 'string' ? baseSha : '',
192
+ reason: 'git_command_failed',
193
+ message: (add.stderr || add.stdout || `exit=${add.status}`).trim(),
194
+ });
195
+ }
196
+ // Step 7: copy `.worktreeinclude` matches (e.g. .env). Tracked files
197
+ // are never duplicated - see the include-parser doc-block.
198
+ const includeResult = loadWorktreeInclude(repoRoot);
199
+ const collector = options.collectIncludes ?? collectWorktreeIncludeFiles;
200
+ const copyFile = options.copyFile ?? defaultCopyFile;
201
+ const copyOpts = {};
202
+ const matches = collector(repoRoot, includeResult.rules, copyOpts);
203
+ const copiedFiles = [];
204
+ for (const rel of matches) {
205
+ const src = resolve(repoRoot, rel);
206
+ const dest = resolve(directory, rel);
207
+ try {
208
+ mkdirSync(dirname(dest), { recursive: true });
209
+ copyFile(src, dest);
210
+ copiedFiles.push(rel);
211
+ }
212
+ catch {
213
+ // Best-effort copy. A single failure (e.g. permission denied on
214
+ // one entry) should not abort the bootstrap - surface via the
215
+ // envelope and let the operator decide.
216
+ }
217
+ }
218
+ return {
219
+ ok: true,
220
+ name,
221
+ branch,
222
+ directory,
223
+ baseRef: baseRefMode,
224
+ resolvedBase: typeof baseSha === 'string' ? baseSha : '',
225
+ copiedFiles,
226
+ };
227
+ }
228
+ /**
229
+ * Parse the operator-supplied name argument. Returns a `ResolvedName`
230
+ * on success or a human-readable error string on rejection. Exposed
231
+ * for spec coverage.
232
+ */
233
+ export function resolveName(nameArg, generator) {
234
+ if (nameArg === undefined || nameArg.trim().length === 0) {
235
+ const generated = (generator ?? generateWhimsicalName)();
236
+ if (!WORKTREE_NAME_PATTERN.test(generated)) {
237
+ return `auto-generated name "${generated}" failed validation`;
238
+ }
239
+ return {
240
+ name: generated,
241
+ branch: `${USER_WORKTREE_BRANCH_PREFIX}${generated}`,
242
+ };
243
+ }
244
+ const trimmed = nameArg.trim();
245
+ const prMatch = PR_SHORTHAND_PATTERN.exec(trimmed);
246
+ if (prMatch && typeof prMatch[1] === 'string') {
247
+ const n = Number(prMatch[1]);
248
+ if (!Number.isInteger(n) || n <= 0) {
249
+ return `invalid PR number: ${trimmed}`;
250
+ }
251
+ return {
252
+ name: `pr-${n}`,
253
+ branch: `${USER_WORKTREE_BRANCH_PREFIX}pr-${n}`,
254
+ prNumber: n,
255
+ };
256
+ }
257
+ if (!WORKTREE_NAME_PATTERN.test(trimmed)) {
258
+ return `name must match ${WORKTREE_NAME_PATTERN}`;
259
+ }
260
+ return {
261
+ name: trimmed,
262
+ branch: `${USER_WORKTREE_BRANCH_PREFIX}${trimmed}`,
263
+ };
264
+ }
265
+ /* ------------------------------------------------------------------ */
266
+ /* Base ref resolution */
267
+ /* ------------------------------------------------------------------ */
268
+ /**
269
+ * Resolve the SHA the new branch should point at. PR-shorthand wins
270
+ * regardless of `baseRef` mode (the operator's clear intent). For the
271
+ * `fresh` mode we resolve `origin/<default-branch>` and fall back to
272
+ * local HEAD when no remote is configured. The `head` mode always
273
+ * carries local HEAD.
274
+ *
275
+ * Returns the resolved SHA on success or an `ERR:<detail>` string on
276
+ * failure so the caller can decide whether to surface
277
+ * `git_command_failed` or `pr_fetch_failed`.
278
+ */
279
+ function resolveBaseSha(runGit, repoRoot, baseRef, prNumber) {
280
+ if (prNumber !== undefined) {
281
+ // `pull/N/head` is the GitHub convention. Fetch the PR head into
282
+ // the local refs cache before resolving so the worktree-add can
283
+ // find it.
284
+ const ref = `pull/${prNumber}/head`;
285
+ const fetch = runGit(['fetch', 'origin', ref], repoRoot);
286
+ if (fetch.status !== 0) {
287
+ return `ERR:failed to fetch ${ref} from origin: ${(fetch.stderr || fetch.stdout).trim()}`;
288
+ }
289
+ const sha = runGit(['rev-parse', 'FETCH_HEAD'], repoRoot);
290
+ if (sha.status !== 0) {
291
+ return `ERR:failed to resolve FETCH_HEAD after fetching ${ref}`;
292
+ }
293
+ return sha.stdout.trim();
294
+ }
295
+ if (baseRef === 'head') {
296
+ const sha = runGit(['rev-parse', 'HEAD'], repoRoot);
297
+ if (sha.status !== 0) {
298
+ return `ERR:failed to resolve HEAD: ${(sha.stderr || sha.stdout).trim()}`;
299
+ }
300
+ return sha.stdout.trim();
301
+ }
302
+ // baseRef === 'fresh': try origin/<default-branch>, fall back to HEAD.
303
+ const remoteBranch = detectOriginHead(runGit, repoRoot);
304
+ if (remoteBranch) {
305
+ const sha = runGit(['rev-parse', remoteBranch], repoRoot);
306
+ if (sha.status === 0) {
307
+ return sha.stdout.trim();
308
+ }
309
+ }
310
+ const headSha = runGit(['rev-parse', 'HEAD'], repoRoot);
311
+ if (headSha.status !== 0) {
312
+ return `ERR:failed to resolve any base ref (no origin/HEAD and no local HEAD)`;
313
+ }
314
+ return headSha.stdout.trim();
315
+ }
316
+ function detectOriginHead(runGit, repoRoot) {
317
+ const symRef = runGit(['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'], repoRoot);
318
+ if (symRef.status === 0) {
319
+ const value = symRef.stdout.trim();
320
+ if (value.length > 0)
321
+ return value;
322
+ }
323
+ // Common default fallbacks. Try `origin/main`, then `origin/master`.
324
+ for (const candidate of ['origin/main', 'origin/master']) {
325
+ const verify = runGit(['rev-parse', '--verify', candidate], repoRoot);
326
+ if (verify.status === 0)
327
+ return candidate;
328
+ }
329
+ return undefined;
330
+ }
331
+ /* ------------------------------------------------------------------ */
332
+ /* Whimsical name generator */
333
+ /* ------------------------------------------------------------------ */
334
+ const WHIMSICAL_ADJECTIVES = [
335
+ 'bright',
336
+ 'brave',
337
+ 'calm',
338
+ 'clever',
339
+ 'eager',
340
+ 'fancy',
341
+ 'gentle',
342
+ 'happy',
343
+ 'jolly',
344
+ 'keen',
345
+ 'lucky',
346
+ 'mighty',
347
+ 'noble',
348
+ 'quick',
349
+ 'royal',
350
+ 'silent',
351
+ 'swift',
352
+ 'tidy',
353
+ 'vivid',
354
+ 'witty',
355
+ ];
356
+ const WHIMSICAL_VERBS = [
357
+ 'running',
358
+ 'sleeping',
359
+ 'flying',
360
+ 'leaping',
361
+ 'roaring',
362
+ 'singing',
363
+ 'climbing',
364
+ 'gliding',
365
+ 'dancing',
366
+ 'soaring',
367
+ ];
368
+ const WHIMSICAL_ANIMALS = [
369
+ 'fox',
370
+ 'otter',
371
+ 'lynx',
372
+ 'puma',
373
+ 'eagle',
374
+ 'hawk',
375
+ 'whale',
376
+ 'dolphin',
377
+ 'badger',
378
+ 'panda',
379
+ 'tiger',
380
+ 'wolf',
381
+ 'bear',
382
+ 'koala',
383
+ 'raven',
384
+ 'falcon',
385
+ 'crow',
386
+ 'sparrow',
387
+ ];
388
+ /**
389
+ * Three-word adjective-verb-animal slug for the auto-name case.
390
+ * Uses `Math.random` because there is no security boundary at the
391
+ * naming layer - the WORKTREE_NAME_PATTERN regex below validates the
392
+ * shape after generation.
393
+ */
394
+ export function generateWhimsicalName() {
395
+ const adj = pick(WHIMSICAL_ADJECTIVES);
396
+ const verb = pick(WHIMSICAL_VERBS);
397
+ const animal = pick(WHIMSICAL_ANIMALS);
398
+ return `${adj}-${verb}-${animal}`;
399
+ }
400
+ function pick(items) {
401
+ if (items.length === 0) {
402
+ throw new Error('cannot pick from empty list');
403
+ }
404
+ const idx = Math.floor(Math.random() * items.length);
405
+ // Bounded by the modulo above so the result is always defined.
406
+ return items[idx];
407
+ }
408
+ /* ------------------------------------------------------------------ */
409
+ /* Helpers */
410
+ /* ------------------------------------------------------------------ */
411
+ function defaultGitRunner(argv, cwd) {
412
+ const result = spawnSync('git', [...argv], {
413
+ cwd,
414
+ encoding: 'utf8',
415
+ shell: false,
416
+ timeout: 60_000,
417
+ maxBuffer: 16 * 1024 * 1024,
418
+ });
419
+ return {
420
+ status: result.status,
421
+ stdout: typeof result.stdout === 'string' ? result.stdout : '',
422
+ stderr: typeof result.stderr === 'string' ? result.stderr : '',
423
+ };
424
+ }
425
+ function defaultCopyFile(src, dest) {
426
+ copyFileSync(src, dest);
427
+ }
428
+ function failure(args) {
429
+ return {
430
+ ok: false,
431
+ name: args.name,
432
+ branch: args.branch,
433
+ directory: args.directory,
434
+ baseRef: args.baseRef,
435
+ resolvedBase: args.resolvedBase,
436
+ copiedFiles: [],
437
+ reason: args.reason,
438
+ message: args.message,
439
+ };
440
+ }
441
+ /**
442
+ * The daily cleanup sweep. Walks `.claude/worktrees/`, computes each
443
+ * entry's age (mtime), and removes ones older than `periodDays` that
444
+ * have no uncommitted/untracked/unpushed state. Crashed-looking trees
445
+ * with local work are preserved by design.
446
+ */
447
+ export function runUserWorktreeCleanup(options) {
448
+ const repoRoot = resolve(options.repoRoot);
449
+ const now = options.now ?? Date.now;
450
+ const runGit = options.runGit ?? defaultGitRunner;
451
+ const root = resolve(repoRoot, USER_WORKTREES_PREFIX);
452
+ const lister = options.listWorktreeDir ?? defaultListWorktreeDir;
453
+ const remover = options.removeWorktree ?? defaultRemoveWorktree;
454
+ const hooks = (options.loadHooks ?? loadHooksConfig)();
455
+ if (!existsSync(root)) {
456
+ return { scanned: 0, removed: [], preserved: [] };
457
+ }
458
+ const entries = lister(root);
459
+ const removed = [];
460
+ const preserved = [];
461
+ const periodMs = options.periodDays * 24 * 60 * 60 * 1000;
462
+ for (const name of entries) {
463
+ if (!WORKTREE_NAME_PATTERN.test(name))
464
+ continue;
465
+ const dir = resolve(root, name);
466
+ let mtimeMs;
467
+ try {
468
+ mtimeMs = statSync(dir).mtimeMs;
469
+ }
470
+ catch {
471
+ continue;
472
+ }
473
+ const ageMs = now() - mtimeMs;
474
+ const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
475
+ if (ageMs < periodMs) {
476
+ preserved.push({ name, directory: dir, ageDays, removed: false, preservedReason: 'too-young' });
477
+ continue;
478
+ }
479
+ if (hasUncommittedChanges(runGit, dir)) {
480
+ preserved.push({
481
+ name,
482
+ directory: dir,
483
+ ageDays,
484
+ removed: false,
485
+ preservedReason: 'has-uncommitted-changes',
486
+ });
487
+ continue;
488
+ }
489
+ if (hasUntrackedFiles(runGit, dir)) {
490
+ preserved.push({
491
+ name,
492
+ directory: dir,
493
+ ageDays,
494
+ removed: false,
495
+ preservedReason: 'has-untracked-files',
496
+ });
497
+ continue;
498
+ }
499
+ if (hasUnpushedCommits(runGit, dir)) {
500
+ preserved.push({
501
+ name,
502
+ directory: dir,
503
+ ageDays,
504
+ removed: false,
505
+ preservedReason: 'has-unpushed-commits',
506
+ });
507
+ continue;
508
+ }
509
+ // Fire WorktreeRemove hook before pulling the trigger.
510
+ const removePayload = {
511
+ event: 'WorktreeRemove',
512
+ name,
513
+ directory: dir,
514
+ reason: 'auto-cleanup',
515
+ repoRoot,
516
+ };
517
+ const outcome = fireWorktreeHooks(hooks, 'WorktreeRemove', removePayload);
518
+ if (outcome.anyBlocked) {
519
+ preserved.push({
520
+ name,
521
+ directory: dir,
522
+ ageDays,
523
+ removed: false,
524
+ preservedReason: 'hook-blocked',
525
+ });
526
+ continue;
527
+ }
528
+ if (options.dryRun) {
529
+ removed.push({ name, directory: dir, ageDays, removed: false });
530
+ continue;
531
+ }
532
+ const ok = remover(dir, runGit, repoRoot);
533
+ removed.push({ name, directory: dir, ageDays, removed: ok });
534
+ }
535
+ return {
536
+ scanned: entries.length,
537
+ removed,
538
+ preserved,
539
+ };
540
+ }
541
+ function defaultListWorktreeDir(dir) {
542
+ try {
543
+ return readdirSync(dir);
544
+ }
545
+ catch {
546
+ return [];
547
+ }
548
+ }
549
+ function defaultRemoveWorktree(path, runGit, repoRoot) {
550
+ const result = runGit(['worktree', 'remove', '--force', path], repoRoot);
551
+ return result.status === 0;
552
+ }
553
+ function hasUncommittedChanges(runGit, dir) {
554
+ // `git diff --quiet` only inspects the working tree against the
555
+ // index, so a `git add`-staged but uncommitted hunk passes that
556
+ // check. Catch the staged case explicitly via `--cached` so the
557
+ // cleanup sweep never drops a worktree with staged work.
558
+ const unstaged = runGit(['diff', '--quiet'], dir);
559
+ if (unstaged.status !== 0)
560
+ return true;
561
+ const staged = runGit(['diff', '--cached', '--quiet'], dir);
562
+ return staged.status !== 0;
563
+ }
564
+ function hasUntrackedFiles(runGit, dir) {
565
+ const r = runGit(['ls-files', '--others', '--exclude-standard'], dir);
566
+ return r.status === 0 && r.stdout.trim().length > 0;
567
+ }
568
+ function hasUnpushedCommits(runGit, dir) {
569
+ const r = runGit(['log', '--branches', '--not', '--remotes', '--oneline'], dir);
570
+ return r.status === 0 && r.stdout.trim().length > 0;
571
+ }
572
+ /** Re-export for symmetry with the dispatcher manager. */
573
+ export function userWorktreePathFor(repoRoot, name) {
574
+ // Posix-join the prefix so the slash separators stay correct on
575
+ // every host, then resolve to absolute on the local FS.
576
+ const rel = posix.join(USER_WORKTREES_PREFIX, name);
577
+ return isAbsolute(rel) ? rel : resolve(repoRoot, rel);
578
+ }
579
+ //# sourceMappingURL=worktree-bootstrap.js.map