@getpaseo/server 0.1.59 → 0.1.61

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 (274) hide show
  1. package/dist/scripts/dev-runner.js +26 -7
  2. package/dist/scripts/dev-runner.js.map +1 -1
  3. package/dist/server/client/daemon-client-runtime-metrics.d.ts +39 -0
  4. package/dist/server/client/daemon-client-runtime-metrics.d.ts.map +1 -0
  5. package/dist/server/client/daemon-client-runtime-metrics.js +173 -0
  6. package/dist/server/client/daemon-client-runtime-metrics.js.map +1 -0
  7. package/dist/server/client/daemon-client.d.ts +58 -9
  8. package/dist/server/client/daemon-client.d.ts.map +1 -1
  9. package/dist/server/client/daemon-client.js +151 -10
  10. package/dist/server/client/daemon-client.js.map +1 -1
  11. package/dist/server/server/agent/agent-manager.d.ts +55 -48
  12. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  13. package/dist/server/server/agent/agent-manager.js +541 -331
  14. package/dist/server/server/agent/agent-manager.js.map +1 -1
  15. package/dist/server/server/agent/agent-metadata-generator.d.ts +3 -2
  16. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  17. package/dist/server/server/agent/agent-metadata-generator.js +31 -16
  18. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  19. package/dist/server/server/agent/agent-projections.d.ts +2 -1
  20. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  21. package/dist/server/server/agent/agent-projections.js +29 -1
  22. package/dist/server/server/agent/agent-projections.js.map +1 -1
  23. package/dist/server/server/agent/agent-sdk-types.d.ts +9 -5
  24. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  25. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  26. package/dist/server/server/agent/agent-storage.d.ts +76 -69
  27. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  28. package/dist/server/server/agent/agent-storage.js +13 -6
  29. package/dist/server/server/agent/agent-storage.js.map +1 -1
  30. package/dist/server/server/agent/agent-stream-coalescer.d.ts +41 -0
  31. package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -0
  32. package/dist/server/server/agent/agent-stream-coalescer.js +166 -0
  33. package/dist/server/server/agent/agent-stream-coalescer.js.map +1 -0
  34. package/dist/server/server/agent/agent-timeline-store-types.d.ts +54 -0
  35. package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -0
  36. package/dist/server/server/agent/agent-timeline-store-types.js +2 -0
  37. package/dist/server/server/agent/agent-timeline-store-types.js.map +1 -0
  38. package/dist/server/server/agent/agent-timeline-store.d.ts +32 -0
  39. package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -0
  40. package/dist/server/server/agent/agent-timeline-store.js +245 -0
  41. package/dist/server/server/agent/agent-timeline-store.js.map +1 -0
  42. package/dist/server/server/agent/mcp-server.d.ts +12 -1
  43. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  44. package/dist/server/server/agent/mcp-server.js +276 -65
  45. package/dist/server/server/agent/mcp-server.js.map +1 -1
  46. package/dist/server/server/agent/mcp-shared.d.ts +196 -152
  47. package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
  48. package/dist/server/server/agent/mcp-shared.js +40 -42
  49. package/dist/server/server/agent/mcp-shared.js.map +1 -1
  50. package/dist/server/server/agent/model-resolver.d.ts.map +1 -1
  51. package/dist/server/server/agent/model-resolver.js +3 -1
  52. package/dist/server/server/agent/model-resolver.js.map +1 -1
  53. package/dist/server/server/agent/prompt-attachments.d.ts +6 -0
  54. package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -0
  55. package/dist/server/server/agent/prompt-attachments.js +31 -0
  56. package/dist/server/server/agent/prompt-attachments.js.map +1 -0
  57. package/dist/server/server/agent/provider-launch-config.d.ts +78 -8
  58. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  59. package/dist/server/server/agent/provider-launch-config.js +35 -0
  60. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  61. package/dist/server/server/agent/provider-manifest.d.ts +1 -0
  62. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  63. package/dist/server/server/agent/provider-manifest.js +22 -1
  64. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  65. package/dist/server/server/agent/provider-registry.d.ts +5 -2
  66. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  67. package/dist/server/server/agent/provider-registry.js +61 -17
  68. package/dist/server/server/agent/provider-registry.js.map +1 -1
  69. package/dist/server/server/agent/provider-snapshot-manager.d.ts +17 -5
  70. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  71. package/dist/server/server/agent/provider-snapshot-manager.js +150 -61
  72. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  73. package/dist/server/server/agent/providers/acp-agent.d.ts +8 -4
  74. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  75. package/dist/server/server/agent/providers/acp-agent.js +73 -8
  76. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  77. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -2
  78. package/dist/server/server/agent/providers/claude-agent.d.ts +1 -1
  79. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  80. package/dist/server/server/agent/providers/claude-agent.js +8 -7
  81. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  82. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +37 -4
  83. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  84. package/dist/server/server/agent/providers/codex-app-server-agent.js +61 -31
  85. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  86. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
  87. package/dist/server/server/agent/providers/copilot-acp-agent.js +3 -2
  88. package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
  89. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +64 -0
  90. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -0
  91. package/dist/server/server/agent/providers/mock-load-test-agent.js +585 -0
  92. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -0
  93. package/dist/server/server/agent/providers/opencode-agent.d.ts +19 -4
  94. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  95. package/dist/server/server/agent/providers/opencode-agent.js +227 -118
  96. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  97. package/dist/server/server/agent/providers/pi-direct-agent.d.ts +69 -0
  98. package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -0
  99. package/dist/server/server/agent/providers/pi-direct-agent.js +1171 -0
  100. package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -0
  101. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +7 -4
  102. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  103. package/dist/server/server/agent-attention-policy.d.ts +13 -13
  104. package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
  105. package/dist/server/server/agent-attention-policy.js +20 -36
  106. package/dist/server/server/agent-attention-policy.js.map +1 -1
  107. package/dist/server/server/bootstrap.d.ts +6 -0
  108. package/dist/server/server/bootstrap.d.ts.map +1 -1
  109. package/dist/server/server/bootstrap.js +113 -11
  110. package/dist/server/server/bootstrap.js.map +1 -1
  111. package/dist/server/server/chat/chat-rpc-schemas.d.ts +44 -44
  112. package/dist/server/server/chat/chat-types.d.ts +6 -6
  113. package/dist/server/server/checkout-diff-manager.d.ts +0 -1
  114. package/dist/server/server/checkout-diff-manager.d.ts.map +1 -1
  115. package/dist/server/server/checkout-diff-manager.js +6 -4
  116. package/dist/server/server/checkout-diff-manager.js.map +1 -1
  117. package/dist/server/server/config.d.ts.map +1 -1
  118. package/dist/server/server/config.js +1 -0
  119. package/dist/server/server/config.js.map +1 -1
  120. package/dist/server/server/file-explorer/service.d.ts.map +1 -1
  121. package/dist/server/server/file-explorer/service.js +2 -1
  122. package/dist/server/server/file-explorer/service.js.map +1 -1
  123. package/dist/server/server/loop/rpc-schemas.d.ts +392 -392
  124. package/dist/server/server/loop-service.d.ts +52 -52
  125. package/dist/server/server/paseo-worktree-archive-service.d.ts +41 -0
  126. package/dist/server/server/paseo-worktree-archive-service.d.ts.map +1 -0
  127. package/dist/server/server/paseo-worktree-archive-service.js +137 -0
  128. package/dist/server/server/paseo-worktree-archive-service.js.map +1 -0
  129. package/dist/server/server/paseo-worktree-service.d.ts +24 -0
  130. package/dist/server/server/paseo-worktree-service.d.ts.map +1 -0
  131. package/dist/server/server/paseo-worktree-service.js +94 -0
  132. package/dist/server/server/paseo-worktree-service.js.map +1 -0
  133. package/dist/server/server/path-utils.d.ts +1 -0
  134. package/dist/server/server/path-utils.d.ts.map +1 -1
  135. package/dist/server/server/path-utils.js +9 -0
  136. package/dist/server/server/path-utils.js.map +1 -1
  137. package/dist/server/server/persisted-config.d.ts +195 -67
  138. package/dist/server/server/persisted-config.d.ts.map +1 -1
  139. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  140. package/dist/server/server/persistence-hooks.js +3 -0
  141. package/dist/server/server/persistence-hooks.js.map +1 -1
  142. package/dist/server/server/resolve-worktree-creation-intent.d.ts +30 -0
  143. package/dist/server/server/resolve-worktree-creation-intent.d.ts.map +1 -0
  144. package/dist/server/server/resolve-worktree-creation-intent.js +163 -0
  145. package/dist/server/server/resolve-worktree-creation-intent.js.map +1 -0
  146. package/dist/server/server/schedule/rpc-schemas.d.ts +192 -192
  147. package/dist/server/server/schedule/service.d.ts +1 -1
  148. package/dist/server/server/schedule/service.d.ts.map +1 -1
  149. package/dist/server/server/schedule/types.d.ts +44 -44
  150. package/dist/server/server/script-health-monitor.d.ts +39 -0
  151. package/dist/server/server/script-health-monitor.d.ts.map +1 -0
  152. package/dist/server/server/script-health-monitor.js +158 -0
  153. package/dist/server/server/script-health-monitor.js.map +1 -0
  154. package/dist/server/server/script-proxy.d.ts +40 -0
  155. package/dist/server/server/script-proxy.d.ts.map +1 -0
  156. package/dist/server/server/script-proxy.js +245 -0
  157. package/dist/server/server/script-proxy.js.map +1 -0
  158. package/dist/server/server/script-route-branch-handler.d.ts +10 -0
  159. package/dist/server/server/script-route-branch-handler.d.ts.map +1 -0
  160. package/dist/server/server/script-route-branch-handler.js +45 -0
  161. package/dist/server/server/script-route-branch-handler.js.map +1 -0
  162. package/dist/server/server/script-status-projection.d.ts +29 -0
  163. package/dist/server/server/script-status-projection.d.ts.map +1 -0
  164. package/dist/server/server/script-status-projection.js +133 -0
  165. package/dist/server/server/script-status-projection.js.map +1 -0
  166. package/dist/server/server/session.d.ts +77 -13
  167. package/dist/server/server/session.d.ts.map +1 -1
  168. package/dist/server/server/session.js +1290 -548
  169. package/dist/server/server/session.js.map +1 -1
  170. package/dist/server/server/websocket-server.d.ts +27 -3
  171. package/dist/server/server/websocket-server.d.ts.map +1 -1
  172. package/dist/server/server/websocket-server.js +112 -29
  173. package/dist/server/server/websocket-server.js.map +1 -1
  174. package/dist/server/server/workspace-archive-service.d.ts +8 -0
  175. package/dist/server/server/workspace-archive-service.d.ts.map +1 -0
  176. package/dist/server/server/workspace-archive-service.js +17 -0
  177. package/dist/server/server/workspace-archive-service.js.map +1 -0
  178. package/dist/server/server/workspace-git-metadata.d.ts +24 -0
  179. package/dist/server/server/workspace-git-metadata.d.ts.map +1 -0
  180. package/dist/server/server/workspace-git-metadata.js +78 -0
  181. package/dist/server/server/workspace-git-metadata.js.map +1 -0
  182. package/dist/server/server/workspace-git-service.d.ts +104 -5
  183. package/dist/server/server/workspace-git-service.d.ts.map +1 -1
  184. package/dist/server/server/workspace-git-service.js +442 -56
  185. package/dist/server/server/workspace-git-service.js.map +1 -1
  186. package/dist/server/server/workspace-reconciliation-service.d.ts +54 -0
  187. package/dist/server/server/workspace-reconciliation-service.d.ts.map +1 -0
  188. package/dist/server/server/workspace-reconciliation-service.js +176 -0
  189. package/dist/server/server/workspace-reconciliation-service.js.map +1 -0
  190. package/dist/server/server/workspace-registry-bootstrap.d.ts.map +1 -1
  191. package/dist/server/server/workspace-registry-bootstrap.js +4 -3
  192. package/dist/server/server/workspace-registry-bootstrap.js.map +1 -1
  193. package/dist/server/server/workspace-registry.d.ts +8 -8
  194. package/dist/server/server/workspace-registry.test-helpers.d.ts +37 -0
  195. package/dist/server/server/workspace-registry.test-helpers.d.ts.map +1 -0
  196. package/dist/server/server/workspace-registry.test-helpers.js +121 -0
  197. package/dist/server/server/workspace-registry.test-helpers.js.map +1 -0
  198. package/dist/server/server/workspace-script-runtime-store.d.ts +28 -0
  199. package/dist/server/server/workspace-script-runtime-store.d.ts.map +1 -0
  200. package/dist/server/server/workspace-script-runtime-store.js +78 -0
  201. package/dist/server/server/workspace-script-runtime-store.js.map +1 -0
  202. package/dist/server/server/workspace-service-env.d.ts +17 -0
  203. package/dist/server/server/workspace-service-env.d.ts.map +1 -0
  204. package/dist/server/server/workspace-service-env.js +80 -0
  205. package/dist/server/server/workspace-service-env.js.map +1 -0
  206. package/dist/server/server/workspace-service-port-registry.d.ts +19 -0
  207. package/dist/server/server/workspace-service-port-registry.d.ts.map +1 -0
  208. package/dist/server/server/workspace-service-port-registry.js +59 -0
  209. package/dist/server/server/workspace-service-port-registry.js.map +1 -0
  210. package/dist/server/server/worktree-bootstrap.d.ts +55 -10
  211. package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
  212. package/dist/server/server/worktree-bootstrap.js +290 -112
  213. package/dist/server/server/worktree-bootstrap.js.map +1 -1
  214. package/dist/server/server/worktree-core.d.ts +25 -0
  215. package/dist/server/server/worktree-core.d.ts.map +1 -0
  216. package/dist/server/server/worktree-core.js +75 -0
  217. package/dist/server/server/worktree-core.js.map +1 -0
  218. package/dist/server/server/worktree-errors.d.ts +12 -0
  219. package/dist/server/server/worktree-errors.d.ts.map +1 -0
  220. package/dist/server/server/worktree-errors.js +31 -0
  221. package/dist/server/server/worktree-errors.js.map +1 -0
  222. package/dist/server/server/worktree-session.d.ts +56 -70
  223. package/dist/server/server/worktree-session.d.ts.map +1 -1
  224. package/dist/server/server/worktree-session.js +176 -251
  225. package/dist/server/server/worktree-session.js.map +1 -1
  226. package/dist/server/services/github-service.d.ts +225 -0
  227. package/dist/server/services/github-service.d.ts.map +1 -0
  228. package/dist/server/services/github-service.js +1381 -0
  229. package/dist/server/services/github-service.js.map +1 -0
  230. package/dist/server/shared/messages.d.ts +29408 -12268
  231. package/dist/server/shared/messages.d.ts.map +1 -1
  232. package/dist/server/shared/messages.js +391 -65
  233. package/dist/server/shared/messages.js.map +1 -1
  234. package/dist/server/terminal/shell-integration/zsh/.zshenv +17 -0
  235. package/dist/server/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
  236. package/dist/server/terminal/terminal-manager.d.ts +9 -0
  237. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  238. package/dist/server/terminal/terminal-manager.js +27 -0
  239. package/dist/server/terminal/terminal-manager.js.map +1 -1
  240. package/dist/server/terminal/terminal-output-coalescer.d.ts +30 -0
  241. package/dist/server/terminal/terminal-output-coalescer.d.ts.map +1 -0
  242. package/dist/server/terminal/terminal-output-coalescer.js +55 -0
  243. package/dist/server/terminal/terminal-output-coalescer.js.map +1 -0
  244. package/dist/server/terminal/terminal.d.ts +32 -1
  245. package/dist/server/terminal/terminal.d.ts.map +1 -1
  246. package/dist/server/terminal/terminal.js +397 -17
  247. package/dist/server/terminal/terminal.js.map +1 -1
  248. package/dist/server/utils/checkout-git.d.ts +63 -10
  249. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  250. package/dist/server/utils/checkout-git.js +321 -229
  251. package/dist/server/utils/checkout-git.js.map +1 -1
  252. package/dist/server/utils/promise-timeout.d.ts +9 -0
  253. package/dist/server/utils/promise-timeout.d.ts.map +1 -0
  254. package/dist/server/utils/promise-timeout.js +25 -0
  255. package/dist/server/utils/promise-timeout.js.map +1 -0
  256. package/dist/server/utils/script-hostname.d.ts +8 -0
  257. package/dist/server/utils/script-hostname.d.ts.map +1 -0
  258. package/dist/server/utils/script-hostname.js +14 -0
  259. package/dist/server/utils/script-hostname.js.map +1 -0
  260. package/dist/server/utils/string-command-shell.d.ts +10 -0
  261. package/dist/server/utils/string-command-shell.d.ts.map +1 -0
  262. package/dist/server/utils/string-command-shell.js +21 -0
  263. package/dist/server/utils/string-command-shell.js.map +1 -0
  264. package/dist/server/utils/worktree.d.ts +54 -7
  265. package/dist/server/utils/worktree.d.ts.map +1 -1
  266. package/dist/server/utils/worktree.js +434 -129
  267. package/dist/server/utils/worktree.js.map +1 -1
  268. package/dist/src/terminal/shell-integration/zsh/.zshenv +17 -0
  269. package/dist/src/terminal/shell-integration/zsh/paseo-integration.zsh +32 -0
  270. package/package.json +11 -14
  271. package/dist/server/server/agent/providers/pi-acp-agent.d.ts +0 -28
  272. package/dist/server/server/agent/providers/pi-acp-agent.d.ts.map +0 -1
  273. package/dist/server/server/agent/providers/pi-acp-agent.js +0 -302
  274. package/dist/server/server/agent/providers/pi-acp-agent.js.map +0 -1
@@ -1,20 +1,26 @@
1
- import { exec } from "child_process";
1
+ import { execFile, spawn } from "child_process";
2
2
  import { promisify } from "util";
3
3
  import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync } from "fs";
4
+ import { rm, stat } from "fs/promises";
4
5
  import { join, basename, dirname, resolve, sep } from "path";
5
6
  import net from "node:net";
6
7
  import { createHash } from "node:crypto";
7
- import { createNameId } from "mnemonic-id";
8
+ import * as pty from "node-pty";
9
+ import stripAnsi from "strip-ansi";
10
+ import { buildStringCommandShellInvocation } from "./string-command-shell.js";
8
11
  import { normalizeBaseRefName, readPaseoWorktreeMetadata, readPaseoWorktreeRuntimePort, writePaseoWorktreeMetadata, writePaseoWorktreeRuntimeMetadata, } from "./worktree-metadata.js";
9
12
  import { runGitCommand } from "./run-git-command.js";
10
- import { platformBash, spawnProcess } from "./spawn.js";
11
13
  import { resolvePaseoHome } from "../server/paseo-home.js";
14
+ import { ensureNodePtySpawnHelperExecutableForCurrentPlatform } from "../terminal/terminal.js";
12
15
  import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
13
- const execAsync = promisify(exec);
16
+ const execFileAsync = promisify(execFile);
14
17
  const READ_ONLY_GIT_ENV = {
15
18
  ...process.env,
16
19
  GIT_OPTIONAL_LOCKS: "0",
17
20
  };
21
+ export function isServiceScript(config) {
22
+ return "type" in config && config.type === "service";
23
+ }
18
24
  export class WorktreeSetupError extends Error {
19
25
  constructor(message, results) {
20
26
  super(message);
@@ -29,6 +35,21 @@ export class WorktreeTeardownError extends Error {
29
35
  this.results = results;
30
36
  }
31
37
  }
38
+ export class BranchAlreadyCheckedOutError extends Error {
39
+ constructor(branchName) {
40
+ super(`Branch already checked out: ${branchName}`);
41
+ this.name = "BranchAlreadyCheckedOutError";
42
+ this.branchName = branchName;
43
+ }
44
+ }
45
+ export class UnknownBranchError extends Error {
46
+ constructor(params) {
47
+ super(`Unknown branch: ${params.branchName}`);
48
+ this.name = "UnknownBranchError";
49
+ this.branchName = params.branchName;
50
+ this.cwd = params.cwd;
51
+ }
52
+ }
32
53
  function readPaseoConfig(repoRoot) {
33
54
  const paseoConfigPath = join(repoRoot, "paseo.json");
34
55
  if (!existsSync(paseoConfigPath)) {
@@ -85,13 +106,89 @@ export function getWorktreeTerminalSpecs(repoRoot) {
85
106
  }
86
107
  return specs;
87
108
  }
109
+ export function getScriptConfigs(repoRoot) {
110
+ const config = readPaseoConfig(repoRoot);
111
+ const scripts = config?.scripts;
112
+ if (!scripts || typeof scripts !== "object") {
113
+ return new Map();
114
+ }
115
+ const result = new Map();
116
+ for (const [name, entry] of Object.entries(scripts)) {
117
+ if (!entry || typeof entry !== "object") {
118
+ continue;
119
+ }
120
+ const rawCommand = entry.command;
121
+ if (typeof rawCommand !== "string") {
122
+ continue;
123
+ }
124
+ const command = rawCommand.trim();
125
+ if (!command) {
126
+ continue;
127
+ }
128
+ const scriptConfig = entry.type === "service"
129
+ ? {
130
+ type: "service",
131
+ command,
132
+ }
133
+ : { command };
134
+ if (isServiceScript(scriptConfig) &&
135
+ typeof entry.port === "number" &&
136
+ Number.isFinite(entry.port)) {
137
+ scriptConfig.port = entry.port;
138
+ }
139
+ result.set(name, scriptConfig);
140
+ }
141
+ return result;
142
+ }
143
+ export function processCarriageReturns(text) {
144
+ if (!text.includes("\r")) {
145
+ return text;
146
+ }
147
+ const output = [];
148
+ let line = [];
149
+ let cursor = 0;
150
+ const flushLine = () => {
151
+ output.push(line.join(""));
152
+ line = [];
153
+ cursor = 0;
154
+ };
155
+ for (let index = 0; index < text.length; index += 1) {
156
+ const char = text[index];
157
+ if (char === "\r") {
158
+ if (text[index + 1] === "\n") {
159
+ flushLine();
160
+ output.push("\n");
161
+ index += 1;
162
+ continue;
163
+ }
164
+ cursor = 0;
165
+ continue;
166
+ }
167
+ if (char === "\n") {
168
+ flushLine();
169
+ output.push("\n");
170
+ continue;
171
+ }
172
+ if (cursor < line.length) {
173
+ line[cursor] = char;
174
+ }
175
+ else {
176
+ line.push(char);
177
+ }
178
+ cursor += 1;
179
+ }
180
+ if (line.length > 0) {
181
+ output.push(line.join(""));
182
+ }
183
+ return output.join("");
184
+ }
88
185
  async function execSetupCommand(command, options) {
89
186
  const startedAt = Date.now();
187
+ const shellInvocation = buildStringCommandShellInvocation({ command });
90
188
  try {
91
- const { stdout, stderr } = await execAsync(command, {
189
+ const { stdout, stderr } = await execFileAsync(shellInvocation.shell, shellInvocation.args, {
92
190
  cwd: options.cwd,
93
191
  env: options.env,
94
- ...(process.platform === "win32" ? {} : { shell: "/bin/bash" }),
95
192
  });
96
193
  return {
97
194
  command,
@@ -119,6 +216,27 @@ async function execSetupCommandStreamed(options) {
119
216
  const stdoutChunks = [];
120
217
  const stderrChunks = [];
121
218
  let settled = false;
219
+ const emitOutput = (stream, chunk) => {
220
+ const text = stripAnsi(chunk);
221
+ if (!text) {
222
+ return;
223
+ }
224
+ if (stream === "stdout") {
225
+ stdoutChunks.push(text);
226
+ }
227
+ else {
228
+ stderrChunks.push(text);
229
+ }
230
+ options.onEvent?.({
231
+ type: "output",
232
+ index: options.index,
233
+ total: options.total,
234
+ command: options.command,
235
+ cwd: options.cwd,
236
+ stream,
237
+ chunk: text,
238
+ });
239
+ };
122
240
  const finish = (exitCode) => {
123
241
  if (settled) {
124
242
  return;
@@ -152,45 +270,48 @@ async function execSetupCommandStreamed(options) {
152
270
  command: options.command,
153
271
  cwd: options.cwd,
154
272
  });
155
- const shell = platformBash();
156
- const child = spawnProcess(shell.command, [...shell.flag, options.command], {
157
- cwd: options.cwd,
158
- env: options.env,
159
- stdio: ["ignore", "pipe", "pipe"],
160
- });
161
- child.stdout?.on("data", (chunk) => {
162
- const text = chunk.toString();
163
- stdoutChunks.push(text);
164
- options.onEvent?.({
165
- type: "output",
166
- index: options.index,
167
- total: options.total,
168
- command: options.command,
273
+ const spawnWithPipes = () => {
274
+ const shellInvocation = buildStringCommandShellInvocation({ command: options.command });
275
+ const child = spawn(shellInvocation.shell, shellInvocation.args, {
169
276
  cwd: options.cwd,
170
- stream: "stdout",
171
- chunk: text,
277
+ env: options.env,
278
+ stdio: ["ignore", "pipe", "pipe"],
172
279
  });
173
- });
174
- child.stderr?.on("data", (chunk) => {
175
- const text = chunk.toString();
176
- stderrChunks.push(text);
177
- options.onEvent?.({
178
- type: "output",
179
- index: options.index,
180
- total: options.total,
181
- command: options.command,
280
+ child.stdout?.on("data", (chunk) => {
281
+ emitOutput("stdout", chunk.toString());
282
+ });
283
+ child.stderr?.on("data", (chunk) => {
284
+ emitOutput("stderr", chunk.toString());
285
+ });
286
+ child.on("error", (error) => {
287
+ emitOutput("stderr", error instanceof Error ? error.message : String(error));
288
+ finish(null);
289
+ });
290
+ child.on("close", (code) => {
291
+ finish(typeof code === "number" ? code : null);
292
+ });
293
+ };
294
+ try {
295
+ ensureNodePtySpawnHelperExecutableForCurrentPlatform();
296
+ const shellInvocation = buildStringCommandShellInvocation({ command: options.command });
297
+ const terminal = pty.spawn(shellInvocation.shell, shellInvocation.args, {
182
298
  cwd: options.cwd,
183
- stream: "stderr",
184
- chunk: text,
299
+ env: options.env,
300
+ name: "xterm-color",
301
+ cols: 120,
302
+ rows: 30,
185
303
  });
186
- });
187
- child.on("error", (error) => {
188
- stderrChunks.push(error instanceof Error ? error.message : String(error));
189
- finish(null);
190
- });
191
- child.on("close", (code) => {
192
- finish(typeof code === "number" ? code : null);
193
- });
304
+ terminal.onData((data) => {
305
+ emitOutput("stdout", data);
306
+ });
307
+ terminal.onExit(({ exitCode }) => {
308
+ finish(typeof exitCode === "number" ? exitCode : null);
309
+ });
310
+ }
311
+ catch (error) {
312
+ emitOutput("stderr", error instanceof Error ? error.message : String(error));
313
+ spawnWithPipes();
314
+ }
194
315
  });
195
316
  }
196
317
  async function getAvailablePort() {
@@ -456,9 +577,6 @@ export function slugify(input) {
456
577
  }
457
578
  return truncated.replace(/-+$/, "");
458
579
  }
459
- function generateWorktreeSlug() {
460
- return createNameId();
461
- }
462
580
  const WORKTREE_PROJECT_HASH_LENGTH = 8;
463
581
  function deriveShortAlphanumericHash(value) {
464
582
  const digest = createHash("sha256").update(value).digest();
@@ -503,36 +621,44 @@ function resolveRepoRootFromGitCommonDir(commonDir) {
503
621
  : normalizedCommonDir;
504
622
  }
505
623
  export async function isPaseoOwnedWorktreeCwd(cwd, options) {
506
- let gitCommonDir;
624
+ const resolvedCwd = normalizePathForOwnership(cwd);
625
+ // repoRoot is best-effort: git may be unreachable from the worktree (e.g. a
626
+ // previous archive attempt removed the admin dir before the working tree
627
+ // could be fully cleaned up). We still want to allow archiving in that case.
628
+ let repoRoot;
507
629
  try {
508
- gitCommonDir = await getGitCommonDir(cwd);
630
+ const gitCommonDir = await getGitCommonDir(cwd);
631
+ repoRoot = resolveRepoRootFromGitCommonDir(gitCommonDir);
509
632
  }
510
633
  catch {
634
+ // ignore
635
+ }
636
+ const paseoHome = options?.paseoHome ? resolve(options.paseoHome) : resolvePaseoHome();
637
+ const paseoWorktreesPrefix = normalizePathForOwnership(join(paseoHome, "worktrees")) + sep;
638
+ // Ownership is defined by the path living under $PASEO_HOME/worktrees/<hash>/<slug>[/...].
639
+ // The <hash>/<slug> prefix is Paseo-private — nothing else writes there — so the
640
+ // path shape alone is sufficient proof of ownership, even when git has already
641
+ // forgotten about the worktree.
642
+ if (!resolvedCwd.startsWith(paseoWorktreesPrefix)) {
511
643
  return {
512
644
  allowed: false,
513
- worktreePath: normalizePathForOwnership(cwd),
645
+ ...(repoRoot !== undefined ? { repoRoot } : {}),
646
+ worktreePath: resolvedCwd,
514
647
  };
515
648
  }
516
- const repoRoot = resolveRepoRootFromGitCommonDir(gitCommonDir);
517
- const worktreesRoot = await getPaseoWorktreesRoot(cwd, options?.paseoHome);
518
- const resolvedRoot = normalizePathForOwnership(worktreesRoot) + sep;
519
- const resolvedCwd = normalizePathForOwnership(cwd);
520
- if (!resolvedCwd.startsWith(resolvedRoot)) {
649
+ const relative = resolvedCwd.slice(paseoWorktreesPrefix.length);
650
+ const parts = relative.split(sep).filter((part) => part.length > 0);
651
+ if (parts.length < 2) {
521
652
  return {
522
653
  allowed: false,
523
- repoRoot,
524
- worktreeRoot: worktreesRoot,
654
+ ...(repoRoot !== undefined ? { repoRoot } : {}),
525
655
  worktreePath: resolvedCwd,
526
656
  };
527
657
  }
528
- const worktrees = await listPaseoWorktrees({ cwd, paseoHome: options?.paseoHome });
529
- const allowed = worktrees.some((entry) => {
530
- const worktreePath = resolve(entry.path);
531
- return resolvedCwd === worktreePath || resolvedCwd.startsWith(worktreePath + sep);
532
- });
658
+ const worktreesRoot = join(paseoHome, "worktrees", parts[0]);
533
659
  return {
534
- allowed,
535
- repoRoot,
660
+ allowed: true,
661
+ ...(repoRoot !== undefined ? { repoRoot } : {}),
536
662
  worktreeRoot: worktreesRoot,
537
663
  worktreePath: resolvedCwd,
538
664
  };
@@ -596,6 +722,29 @@ export async function listPaseoWorktrees({ cwd, paseoHome, }) {
596
722
  createdAt: resolveWorktreeCreatedAtIso(entry.path),
597
723
  }));
598
724
  }
725
+ export async function resolveExistingWorktreeForSlug({ slug, repoRoot, paseoHome, }) {
726
+ const worktrees = await listPaseoWorktrees({
727
+ cwd: repoRoot,
728
+ paseoHome,
729
+ });
730
+ const slugSuffix = `${sep}${slug}`;
731
+ const existingWorktree = worktrees.find((worktree) => worktree.path.endsWith(slugSuffix));
732
+ if (!existingWorktree) {
733
+ return null;
734
+ }
735
+ const { stdout } = await runGitCommand(["branch", "--show-current"], {
736
+ cwd: existingWorktree.path,
737
+ env: READ_ONLY_GIT_ENV,
738
+ });
739
+ const branchName = stdout.trim();
740
+ if (!branchName) {
741
+ throw new Error(`Unable to resolve branch for existing worktree: ${existingWorktree.path}`);
742
+ }
743
+ return {
744
+ branchName,
745
+ worktreePath: existingWorktree.path,
746
+ };
747
+ }
599
748
  export async function resolvePaseoWorktreeRootForCwd(cwd, options) {
600
749
  let gitCommonDir;
601
750
  try {
@@ -638,119 +787,275 @@ export async function resolvePaseoWorktreeRootForCwd(cwd, options) {
638
787
  worktreePath: match.path,
639
788
  };
640
789
  }
641
- export async function deletePaseoWorktree({ cwd, worktreePath, worktreeSlug, paseoHome, }) {
790
+ export async function deletePaseoWorktree({ cwd, worktreePath, worktreeSlug, worktreesRoot, paseoHome, }) {
642
791
  if (!worktreePath && !worktreeSlug) {
643
792
  throw new Error("worktreePath or worktreeSlug is required");
644
793
  }
645
- const worktreesRoot = await getPaseoWorktreesRoot(cwd, paseoHome);
646
- const resolvedRoot = normalizePathForOwnership(worktreesRoot) + sep;
647
- const requestedPath = worktreePath ?? join(worktreesRoot, worktreeSlug);
794
+ // Resolve the worktrees-root. With a repo cwd we hash it the normal way; if
795
+ // git has forgotten about the worktree we expect the caller to hand us the
796
+ // path-derived worktreesRoot from the ownership check.
797
+ let resolvedWorktreesRoot;
798
+ if (worktreesRoot) {
799
+ resolvedWorktreesRoot = worktreesRoot;
800
+ }
801
+ else if (cwd) {
802
+ resolvedWorktreesRoot = await getPaseoWorktreesRoot(cwd, paseoHome);
803
+ }
804
+ else {
805
+ throw new Error("cwd or worktreesRoot is required to delete a Paseo worktree");
806
+ }
807
+ const resolvedRoot = normalizePathForOwnership(resolvedWorktreesRoot) + sep;
808
+ const requestedPath = worktreePath ?? join(resolvedWorktreesRoot, worktreeSlug);
648
809
  const resolvedRequested = normalizePathForOwnership(requestedPath);
649
810
  const resolvedWorktree = (await resolvePaseoWorktreeRootForCwd(requestedPath, { paseoHome }))?.worktreePath ??
650
811
  resolvedRequested;
651
812
  if (!resolvedWorktree.startsWith(resolvedRoot)) {
652
813
  throw new Error("Refusing to delete non-Paseo worktree");
653
814
  }
654
- await runWorktreeTeardownCommands({
655
- worktreePath: resolvedWorktree,
656
- });
657
- await runGitCommand(["worktree", "remove", resolvedWorktree, "--force"], {
658
- cwd,
659
- timeout: 120000,
660
- });
661
- if (existsSync(resolvedWorktree)) {
662
- rmSync(resolvedWorktree, { recursive: true, force: true });
815
+ if (await pathExists(resolvedWorktree)) {
816
+ await runWorktreeTeardownCommands({
817
+ worktreePath: resolvedWorktree,
818
+ });
819
+ }
820
+ if (cwd) {
821
+ try {
822
+ await runGitCommand(["worktree", "remove", resolvedWorktree, "--force"], {
823
+ cwd,
824
+ timeout: 120000,
825
+ });
826
+ }
827
+ catch {
828
+ // `git worktree remove` fails if the admin dir is already gone (e.g. a
829
+ // prior archive attempt removed it before the working tree could be
830
+ // fully cleaned up), or if the repo root has moved. Fall through to the
831
+ // rm retry loop below so the operation stays idempotent.
832
+ }
833
+ }
834
+ await removeDirectoryWithRetries(resolvedWorktree);
835
+ if (cwd) {
836
+ try {
837
+ await runGitCommand(["worktree", "prune"], { cwd, timeout: 30000 });
838
+ }
839
+ catch {
840
+ // not critical; git will prune lazily
841
+ }
842
+ }
843
+ }
844
+ async function pathExists(path) {
845
+ try {
846
+ await stat(path);
847
+ return true;
848
+ }
849
+ catch (error) {
850
+ if (error.code === "ENOENT") {
851
+ return false;
852
+ }
853
+ throw error;
854
+ }
855
+ }
856
+ async function removeDirectoryWithRetries(path) {
857
+ if (!(await pathExists(path))) {
858
+ return;
859
+ }
860
+ const delaysMs = [0, 100, 300, 700, 1500];
861
+ let lastError = null;
862
+ for (const delay of delaysMs) {
863
+ if (delay > 0) {
864
+ await new Promise((resolve) => setTimeout(resolve, delay));
865
+ }
866
+ try {
867
+ await rm(path, { recursive: true, force: true });
868
+ if (!(await pathExists(path))) {
869
+ return;
870
+ }
871
+ lastError = new Error(`Directory still present after rm: ${path}`);
872
+ }
873
+ catch (error) {
874
+ lastError = error;
875
+ }
876
+ }
877
+ if (await pathExists(path)) {
878
+ throw lastError instanceof Error
879
+ ? lastError
880
+ : new Error(`Failed to remove worktree directory: ${path}`);
663
881
  }
664
882
  }
665
883
  /**
666
884
  * Create a git worktree with proper naming conventions
667
885
  */
668
- export async function createWorktree({ branchName, cwd, baseBranch, worktreeSlug, runSetup = true, paseoHome, }) {
669
- // Validate branch name
886
+ export const createWorktree = async ({ cwd, source, worktreeSlug, runSetup, paseoHome, }) => {
887
+ const sourcePlan = await resolveWorktreeSourcePlan({ cwd, source, desiredSlug: worktreeSlug });
888
+ let worktreePath = join(await getPaseoWorktreesRoot(cwd, paseoHome), worktreeSlug);
889
+ mkdirSync(dirname(worktreePath), { recursive: true });
890
+ // Also handle worktree path collision
891
+ let finalWorktreePath = worktreePath;
892
+ let pathSuffix = 1;
893
+ while (existsSync(finalWorktreePath)) {
894
+ finalWorktreePath = `${worktreePath}-${pathSuffix}`;
895
+ pathSuffix++;
896
+ }
897
+ // Primitive owner for `git worktree add`; callers route through createWorktreeCore.
898
+ await runGitCommand(["worktree", "add", finalWorktreePath, ...sourcePlan.addArguments], {
899
+ cwd,
900
+ timeout: 120000,
901
+ });
902
+ worktreePath = normalizePathForOwnership(finalWorktreePath);
903
+ if (sourcePlan.pushRemote) {
904
+ await configureWorktreePushRemote({
905
+ cwd,
906
+ branchName: sourcePlan.branchName,
907
+ remote: sourcePlan.pushRemote,
908
+ });
909
+ }
910
+ writePaseoWorktreeMetadata(worktreePath, { baseRefName: sourcePlan.metadataBaseRefName });
911
+ if (runSetup) {
912
+ await runWorktreeSetupCommands({
913
+ worktreePath,
914
+ branchName: sourcePlan.branchName,
915
+ cleanupOnFailure: true,
916
+ });
917
+ }
918
+ return {
919
+ branchName: sourcePlan.branchName,
920
+ worktreePath,
921
+ };
922
+ };
923
+ async function resolveWorktreeSourcePlan({ cwd, source, desiredSlug, }) {
924
+ switch (source.kind) {
925
+ case "branch-off": {
926
+ const branchName = source.newBranchName;
927
+ validateWorktreeBranchName(branchName);
928
+ const normalizedBaseBranch = normalizeRequiredBaseBranch(source.baseBranch);
929
+ const resolvedBaseBranch = await resolveBaseBranchForWorktree(cwd, normalizedBaseBranch);
930
+ const branchExists = await localBranchExists(cwd, branchName);
931
+ const base = branchExists ? branchName : resolvedBaseBranch;
932
+ const candidateBranch = branchExists ? desiredSlug : branchName;
933
+ const newBranchName = await resolveUniqueLocalBranchName(cwd, candidateBranch);
934
+ return {
935
+ branchName: newBranchName,
936
+ metadataBaseRefName: normalizedBaseBranch,
937
+ addArguments: ["-b", newBranchName, base],
938
+ };
939
+ }
940
+ case "checkout-branch": {
941
+ validateWorktreeBranchName(source.branchName);
942
+ if (!(await localBranchExists(cwd, source.branchName))) {
943
+ try {
944
+ await runGitCommand(["fetch", "origin", `${source.branchName}:${source.branchName}`], {
945
+ cwd,
946
+ timeout: 120000,
947
+ });
948
+ }
949
+ catch {
950
+ throw new UnknownBranchError({ branchName: source.branchName, cwd });
951
+ }
952
+ }
953
+ if (await isBranchCheckedOut(cwd, source.branchName)) {
954
+ throw new BranchAlreadyCheckedOutError(source.branchName);
955
+ }
956
+ return {
957
+ branchName: source.branchName,
958
+ metadataBaseRefName: source.branchName,
959
+ addArguments: [source.branchName],
960
+ };
961
+ }
962
+ case "checkout-github-pr": {
963
+ const localBranchCandidate = source.localBranchName ?? source.headRef;
964
+ validateWorktreeBranchName(localBranchCandidate);
965
+ const localBranchName = await resolveUniqueLocalBranchName(cwd, localBranchCandidate);
966
+ const normalizedBaseRefName = normalizeRequiredBaseBranch(source.baseRefName);
967
+ await runGitCommand([
968
+ "fetch",
969
+ "origin",
970
+ `refs/pull/${source.githubPrNumber}/head:refs/heads/${localBranchName}`,
971
+ "--force",
972
+ ], {
973
+ cwd,
974
+ timeout: 120000,
975
+ });
976
+ return {
977
+ branchName: localBranchName,
978
+ metadataBaseRefName: normalizedBaseRefName,
979
+ addArguments: [localBranchName],
980
+ ...(source.pushRemoteUrl
981
+ ? {
982
+ pushRemote: {
983
+ name: `paseo-pr-${source.githubPrNumber}`,
984
+ url: source.pushRemoteUrl,
985
+ headRef: source.headRef,
986
+ },
987
+ }
988
+ : {}),
989
+ };
990
+ }
991
+ }
992
+ }
993
+ async function configureWorktreePushRemote(options) {
994
+ await runGitCommand(["config", `remote.${options.remote.name}.url`, options.remote.url], {
995
+ cwd: options.cwd,
996
+ });
997
+ await runGitCommand(["config", `remote.${options.remote.name}.push`, `HEAD:refs/heads/${options.remote.headRef}`], { cwd: options.cwd });
998
+ await runGitCommand(["config", `branch.${options.branchName}.remote`, options.remote.name], {
999
+ cwd: options.cwd,
1000
+ });
1001
+ await runGitCommand(["config", `branch.${options.branchName}.merge`, `refs/heads/${options.remote.headRef}`], { cwd: options.cwd });
1002
+ }
1003
+ function validateWorktreeBranchName(branchName) {
670
1004
  const validation = validateBranchSlug(branchName);
671
1005
  if (!validation.valid) {
672
1006
  throw new Error(`Invalid branch name: ${validation.error}`);
673
1007
  }
674
- const normalizedBaseBranch = baseBranch ? normalizeBaseRefName(baseBranch) : "";
1008
+ }
1009
+ function normalizeRequiredBaseBranch(baseBranch) {
1010
+ const normalizedBaseBranch = normalizeBaseRefName(baseBranch);
675
1011
  if (!normalizedBaseBranch) {
676
1012
  throw new Error("Base branch is required when creating a Paseo worktree");
677
1013
  }
678
1014
  if (normalizedBaseBranch === "HEAD") {
679
1015
  throw new Error("Base branch cannot be HEAD when creating a Paseo worktree");
680
1016
  }
681
- // Resolve the base branch - prefer origin/{branch}, then fall back to local
682
- let resolvedBaseBranch = normalizedBaseBranch;
1017
+ return normalizedBaseBranch;
1018
+ }
1019
+ async function resolveBaseBranchForWorktree(cwd, normalizedBaseBranch) {
683
1020
  try {
684
1021
  await runGitCommand(["rev-parse", "--verify", `origin/${normalizedBaseBranch}`], { cwd });
685
- resolvedBaseBranch = `origin/${normalizedBaseBranch}`;
1022
+ return `origin/${normalizedBaseBranch}`;
686
1023
  }
687
1024
  catch {
688
1025
  try {
689
1026
  await runGitCommand(["rev-parse", "--verify", normalizedBaseBranch], { cwd });
1027
+ return normalizedBaseBranch;
690
1028
  }
691
1029
  catch {
692
1030
  throw new Error(`Base branch not found: ${normalizedBaseBranch}`);
693
1031
  }
694
1032
  }
695
- let worktreePath;
696
- const desiredSlug = worktreeSlug || generateWorktreeSlug();
697
- worktreePath = join(await getPaseoWorktreesRoot(cwd, paseoHome), desiredSlug);
698
- mkdirSync(dirname(worktreePath), { recursive: true });
699
- // Check if branch already exists
700
- let branchExists = false;
1033
+ }
1034
+ async function localBranchExists(cwd, branchName) {
701
1035
  try {
702
1036
  await runGitCommand(["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`], {
703
1037
  cwd,
704
1038
  });
705
- branchExists = true;
1039
+ return true;
706
1040
  }
707
1041
  catch {
708
- branchExists = false;
709
- }
710
- // Always create a new branch for the worktree
711
- // If branchName already exists, use it as base and create worktree-slug as branch name
712
- // If branchName doesn't exist, create it from baseBranch (resolved to remote if needed)
713
- const base = branchExists ? branchName : resolvedBaseBranch;
714
- const candidateBranch = branchExists ? desiredSlug : branchName;
715
- // Find unique branch name if collision
1042
+ return false;
1043
+ }
1044
+ }
1045
+ async function resolveUniqueLocalBranchName(cwd, candidateBranch) {
716
1046
  let newBranchName = candidateBranch;
717
1047
  let suffix = 1;
718
- while (true) {
719
- try {
720
- await runGitCommand(["show-ref", "--verify", "--quiet", `refs/heads/${newBranchName}`], {
721
- cwd,
722
- });
723
- // Branch exists, try with suffix
724
- newBranchName = `${candidateBranch}-${suffix}`;
725
- suffix++;
726
- }
727
- catch {
728
- break;
729
- }
1048
+ while (await localBranchExists(cwd, newBranchName)) {
1049
+ newBranchName = `${candidateBranch}-${suffix}`;
1050
+ suffix++;
730
1051
  }
731
- // Also handle worktree path collision
732
- let finalWorktreePath = worktreePath;
733
- let pathSuffix = 1;
734
- while (existsSync(finalWorktreePath)) {
735
- finalWorktreePath = `${worktreePath}-${pathSuffix}`;
736
- pathSuffix++;
737
- }
738
- await runGitCommand(["worktree", "add", finalWorktreePath, "-b", newBranchName, base], {
1052
+ return newBranchName;
1053
+ }
1054
+ async function isBranchCheckedOut(cwd, branchName) {
1055
+ const { stdout } = await runGitCommand(["worktree", "list", "--porcelain"], {
739
1056
  cwd,
740
- timeout: 120000,
1057
+ env: READ_ONLY_GIT_ENV,
741
1058
  });
742
- worktreePath = normalizePathForOwnership(finalWorktreePath);
743
- writePaseoWorktreeMetadata(worktreePath, { baseRefName: normalizedBaseBranch });
744
- if (runSetup) {
745
- await runWorktreeSetupCommands({
746
- worktreePath,
747
- branchName: newBranchName,
748
- cleanupOnFailure: true,
749
- });
750
- }
751
- return {
752
- branchName: newBranchName,
753
- worktreePath,
754
- };
1059
+ return parseWorktreeList(stdout).some((entry) => entry.branchName === branchName);
755
1060
  }
756
1061
  //# sourceMappingURL=worktree.js.map