@chances-ai/engine 24.0.0

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 (389) hide show
  1. package/dist/agents/discover.d.ts +30 -0
  2. package/dist/agents/discover.d.ts.map +1 -0
  3. package/dist/agents/discover.js +183 -0
  4. package/dist/agents/discover.js.map +1 -0
  5. package/dist/agents/index.d.ts +20 -0
  6. package/dist/agents/index.d.ts.map +1 -0
  7. package/dist/agents/index.js +52 -0
  8. package/dist/agents/index.js.map +1 -0
  9. package/dist/agents/parse.d.ts +61 -0
  10. package/dist/agents/parse.d.ts.map +1 -0
  11. package/dist/agents/parse.js +527 -0
  12. package/dist/agents/parse.js.map +1 -0
  13. package/dist/agents/types.d.ts +52 -0
  14. package/dist/agents/types.d.ts.map +1 -0
  15. package/dist/agents/types.js +8 -0
  16. package/dist/agents/types.js.map +1 -0
  17. package/dist/ai/adapters/ai-sdk-stream.d.ts +19 -0
  18. package/dist/ai/adapters/ai-sdk-stream.d.ts.map +1 -0
  19. package/dist/ai/adapters/ai-sdk-stream.js +125 -0
  20. package/dist/ai/adapters/ai-sdk-stream.js.map +1 -0
  21. package/dist/ai/adapters/ai-sdk.d.ts +56 -0
  22. package/dist/ai/adapters/ai-sdk.d.ts.map +1 -0
  23. package/dist/ai/adapters/ai-sdk.js +112 -0
  24. package/dist/ai/adapters/ai-sdk.js.map +1 -0
  25. package/dist/ai/adapters/mock.d.ts +13 -0
  26. package/dist/ai/adapters/mock.d.ts.map +1 -0
  27. package/dist/ai/adapters/mock.js +54 -0
  28. package/dist/ai/adapters/mock.js.map +1 -0
  29. package/dist/ai/adapters/openai-compatible.d.ts +23 -0
  30. package/dist/ai/adapters/openai-compatible.d.ts.map +1 -0
  31. package/dist/ai/adapters/openai-compatible.js +45 -0
  32. package/dist/ai/adapters/openai-compatible.js.map +1 -0
  33. package/dist/ai/cost.d.ts +3 -0
  34. package/dist/ai/cost.d.ts.map +1 -0
  35. package/dist/ai/cost.js +5 -0
  36. package/dist/ai/cost.js.map +1 -0
  37. package/dist/ai/index.d.ts +12 -0
  38. package/dist/ai/index.d.ts.map +1 -0
  39. package/dist/ai/index.js +11 -0
  40. package/dist/ai/index.js.map +1 -0
  41. package/dist/ai/known-models.d.ts +20 -0
  42. package/dist/ai/known-models.d.ts.map +1 -0
  43. package/dist/ai/known-models.js +129 -0
  44. package/dist/ai/known-models.js.map +1 -0
  45. package/dist/ai/registry.d.ts +12 -0
  46. package/dist/ai/registry.d.ts.map +1 -0
  47. package/dist/ai/registry.js +24 -0
  48. package/dist/ai/registry.js.map +1 -0
  49. package/dist/ai/retry.d.ts +11 -0
  50. package/dist/ai/retry.d.ts.map +1 -0
  51. package/dist/ai/retry.js +14 -0
  52. package/dist/ai/retry.js.map +1 -0
  53. package/dist/ai/router.d.ts +25 -0
  54. package/dist/ai/router.d.ts.map +1 -0
  55. package/dist/ai/router.js +36 -0
  56. package/dist/ai/router.js.map +1 -0
  57. package/dist/ai/setup.d.ts +23 -0
  58. package/dist/ai/setup.d.ts.map +1 -0
  59. package/dist/ai/setup.js +47 -0
  60. package/dist/ai/setup.js.map +1 -0
  61. package/dist/ai/summarizer.d.ts +24 -0
  62. package/dist/ai/summarizer.d.ts.map +1 -0
  63. package/dist/ai/summarizer.js +56 -0
  64. package/dist/ai/summarizer.js.map +1 -0
  65. package/dist/ai/types.d.ts +83 -0
  66. package/dist/ai/types.d.ts.map +1 -0
  67. package/dist/ai/types.js +2 -0
  68. package/dist/ai/types.js.map +1 -0
  69. package/dist/core/compaction/circuit-breaker.d.ts +32 -0
  70. package/dist/core/compaction/circuit-breaker.d.ts.map +1 -0
  71. package/dist/core/compaction/circuit-breaker.js +42 -0
  72. package/dist/core/compaction/circuit-breaker.js.map +1 -0
  73. package/dist/core/compaction/compactor.d.ts +75 -0
  74. package/dist/core/compaction/compactor.d.ts.map +1 -0
  75. package/dist/core/compaction/compactor.js +261 -0
  76. package/dist/core/compaction/compactor.js.map +1 -0
  77. package/dist/core/compaction/estimate.d.ts +39 -0
  78. package/dist/core/compaction/estimate.d.ts.map +1 -0
  79. package/dist/core/compaction/estimate.js +74 -0
  80. package/dist/core/compaction/estimate.js.map +1 -0
  81. package/dist/core/compaction/index.d.ts +5 -0
  82. package/dist/core/compaction/index.d.ts.map +1 -0
  83. package/dist/core/compaction/index.js +5 -0
  84. package/dist/core/compaction/index.js.map +1 -0
  85. package/dist/core/compaction/prune.d.ts +43 -0
  86. package/dist/core/compaction/prune.d.ts.map +1 -0
  87. package/dist/core/compaction/prune.js +51 -0
  88. package/dist/core/compaction/prune.js.map +1 -0
  89. package/dist/core/engine.d.ts +268 -0
  90. package/dist/core/engine.d.ts.map +1 -0
  91. package/dist/core/engine.js +767 -0
  92. package/dist/core/engine.js.map +1 -0
  93. package/dist/core/index.d.ts +6 -0
  94. package/dist/core/index.d.ts.map +1 -0
  95. package/dist/core/index.js +6 -0
  96. package/dist/core/index.js.map +1 -0
  97. package/dist/core/task-tool.d.ts +175 -0
  98. package/dist/core/task-tool.d.ts.map +1 -0
  99. package/dist/core/task-tool.js +901 -0
  100. package/dist/core/task-tool.js.map +1 -0
  101. package/dist/core/workspace-query.d.ts +83 -0
  102. package/dist/core/workspace-query.d.ts.map +1 -0
  103. package/dist/core/workspace-query.js +217 -0
  104. package/dist/core/workspace-query.js.map +1 -0
  105. package/dist/core/worktree/active-marker.d.ts +31 -0
  106. package/dist/core/worktree/active-marker.d.ts.map +1 -0
  107. package/dist/core/worktree/active-marker.js +109 -0
  108. package/dist/core/worktree/active-marker.js.map +1 -0
  109. package/dist/core/worktree/create.d.ts +40 -0
  110. package/dist/core/worktree/create.d.ts.map +1 -0
  111. package/dist/core/worktree/create.js +121 -0
  112. package/dist/core/worktree/create.js.map +1 -0
  113. package/dist/core/worktree/errors.d.ts +7 -0
  114. package/dist/core/worktree/errors.d.ts.map +1 -0
  115. package/dist/core/worktree/errors.js +11 -0
  116. package/dist/core/worktree/errors.js.map +1 -0
  117. package/dist/core/worktree/gc.d.ts +39 -0
  118. package/dist/core/worktree/gc.d.ts.map +1 -0
  119. package/dist/core/worktree/gc.js +146 -0
  120. package/dist/core/worktree/gc.js.map +1 -0
  121. package/dist/core/worktree/git.d.ts +53 -0
  122. package/dist/core/worktree/git.d.ts.map +1 -0
  123. package/dist/core/worktree/git.js +166 -0
  124. package/dist/core/worktree/git.js.map +1 -0
  125. package/dist/core/worktree/index.d.ts +8 -0
  126. package/dist/core/worktree/index.d.ts.map +1 -0
  127. package/dist/core/worktree/index.js +8 -0
  128. package/dist/core/worktree/index.js.map +1 -0
  129. package/dist/core/worktree/paths.d.ts +26 -0
  130. package/dist/core/worktree/paths.d.ts.map +1 -0
  131. package/dist/core/worktree/paths.js +57 -0
  132. package/dist/core/worktree/paths.js.map +1 -0
  133. package/dist/core/worktree/slug.d.ts +6 -0
  134. package/dist/core/worktree/slug.d.ts.map +1 -0
  135. package/dist/core/worktree/slug.js +21 -0
  136. package/dist/core/worktree/slug.js.map +1 -0
  137. package/dist/local-vault/file-store.d.ts +64 -0
  138. package/dist/local-vault/file-store.d.ts.map +1 -0
  139. package/dist/local-vault/file-store.js +225 -0
  140. package/dist/local-vault/file-store.js.map +1 -0
  141. package/dist/local-vault/index.d.ts +57 -0
  142. package/dist/local-vault/index.d.ts.map +1 -0
  143. package/dist/local-vault/index.js +68 -0
  144. package/dist/local-vault/index.js.map +1 -0
  145. package/dist/local-vault/keychain.d.ts +58 -0
  146. package/dist/local-vault/keychain.d.ts.map +1 -0
  147. package/dist/local-vault/keychain.js +200 -0
  148. package/dist/local-vault/keychain.js.map +1 -0
  149. package/dist/local-vault/mutex.d.ts +20 -0
  150. package/dist/local-vault/mutex.d.ts.map +1 -0
  151. package/dist/local-vault/mutex.js +44 -0
  152. package/dist/local-vault/mutex.js.map +1 -0
  153. package/dist/local-vault/passphrase.d.ts +30 -0
  154. package/dist/local-vault/passphrase.d.ts.map +1 -0
  155. package/dist/local-vault/passphrase.js +72 -0
  156. package/dist/local-vault/passphrase.js.map +1 -0
  157. package/dist/lsp/config.d.ts +34 -0
  158. package/dist/lsp/config.d.ts.map +1 -0
  159. package/dist/lsp/config.js +68 -0
  160. package/dist/lsp/config.js.map +1 -0
  161. package/dist/lsp/detect.d.ts +7 -0
  162. package/dist/lsp/detect.d.ts.map +1 -0
  163. package/dist/lsp/detect.js +78 -0
  164. package/dist/lsp/detect.js.map +1 -0
  165. package/dist/lsp/errors.d.ts +11 -0
  166. package/dist/lsp/errors.d.ts.map +1 -0
  167. package/dist/lsp/errors.js +11 -0
  168. package/dist/lsp/errors.js.map +1 -0
  169. package/dist/lsp/formatters.d.ts +147 -0
  170. package/dist/lsp/formatters.d.ts.map +1 -0
  171. package/dist/lsp/formatters.js +259 -0
  172. package/dist/lsp/formatters.js.map +1 -0
  173. package/dist/lsp/index.d.ts +31 -0
  174. package/dist/lsp/index.d.ts.map +1 -0
  175. package/dist/lsp/index.js +31 -0
  176. package/dist/lsp/index.js.map +1 -0
  177. package/dist/lsp/instance.d.ts +72 -0
  178. package/dist/lsp/instance.d.ts.map +1 -0
  179. package/dist/lsp/instance.js +489 -0
  180. package/dist/lsp/instance.js.map +1 -0
  181. package/dist/lsp/lazy-load.d.ts +27 -0
  182. package/dist/lsp/lazy-load.d.ts.map +1 -0
  183. package/dist/lsp/lazy-load.js +57 -0
  184. package/dist/lsp/lazy-load.js.map +1 -0
  185. package/dist/lsp/manager.d.ts +59 -0
  186. package/dist/lsp/manager.d.ts.map +1 -0
  187. package/dist/lsp/manager.js +242 -0
  188. package/dist/lsp/manager.js.map +1 -0
  189. package/dist/lsp/ops.d.ts +13 -0
  190. package/dist/lsp/ops.d.ts.map +1 -0
  191. package/dist/lsp/ops.js +225 -0
  192. package/dist/lsp/ops.js.map +1 -0
  193. package/dist/lsp/rpc.d.ts +47 -0
  194. package/dist/lsp/rpc.d.ts.map +1 -0
  195. package/dist/lsp/rpc.js +134 -0
  196. package/dist/lsp/rpc.js.map +1 -0
  197. package/dist/lsp/safe-uri.d.ts +18 -0
  198. package/dist/lsp/safe-uri.d.ts.map +1 -0
  199. package/dist/lsp/safe-uri.js +96 -0
  200. package/dist/lsp/safe-uri.js.map +1 -0
  201. package/dist/lsp/types.d.ts +70 -0
  202. package/dist/lsp/types.d.ts.map +1 -0
  203. package/dist/lsp/types.js +16 -0
  204. package/dist/lsp/types.js.map +1 -0
  205. package/dist/mcp/bridge.d.ts +57 -0
  206. package/dist/mcp/bridge.d.ts.map +1 -0
  207. package/dist/mcp/bridge.js +98 -0
  208. package/dist/mcp/bridge.js.map +1 -0
  209. package/dist/mcp/category.d.ts +22 -0
  210. package/dist/mcp/category.d.ts.map +1 -0
  211. package/dist/mcp/category.js +11 -0
  212. package/dist/mcp/category.js.map +1 -0
  213. package/dist/mcp/client.d.ts +228 -0
  214. package/dist/mcp/client.d.ts.map +1 -0
  215. package/dist/mcp/client.js +352 -0
  216. package/dist/mcp/client.js.map +1 -0
  217. package/dist/mcp/content.d.ts +86 -0
  218. package/dist/mcp/content.d.ts.map +1 -0
  219. package/dist/mcp/content.js +147 -0
  220. package/dist/mcp/content.js.map +1 -0
  221. package/dist/mcp/env.d.ts +19 -0
  222. package/dist/mcp/env.d.ts.map +1 -0
  223. package/dist/mcp/env.js +50 -0
  224. package/dist/mcp/env.js.map +1 -0
  225. package/dist/mcp/host.d.ts +199 -0
  226. package/dist/mcp/host.d.ts.map +1 -0
  227. package/dist/mcp/host.js +530 -0
  228. package/dist/mcp/host.js.map +1 -0
  229. package/dist/mcp/index.d.ts +18 -0
  230. package/dist/mcp/index.d.ts.map +1 -0
  231. package/dist/mcp/index.js +17 -0
  232. package/dist/mcp/index.js.map +1 -0
  233. package/dist/mcp/load-mcp-host.d.ts +32 -0
  234. package/dist/mcp/load-mcp-host.d.ts.map +1 -0
  235. package/dist/mcp/load-mcp-host.js +49 -0
  236. package/dist/mcp/load-mcp-host.js.map +1 -0
  237. package/dist/mcp/oauth/callback-server.d.ts +73 -0
  238. package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
  239. package/dist/mcp/oauth/callback-server.js +280 -0
  240. package/dist/mcp/oauth/callback-server.js.map +1 -0
  241. package/dist/mcp/oauth/config-hash.d.ts +24 -0
  242. package/dist/mcp/oauth/config-hash.d.ts.map +1 -0
  243. package/dist/mcp/oauth/config-hash.js +55 -0
  244. package/dist/mcp/oauth/config-hash.js.map +1 -0
  245. package/dist/mcp/oauth/error-normalize.d.ts +39 -0
  246. package/dist/mcp/oauth/error-normalize.d.ts.map +1 -0
  247. package/dist/mcp/oauth/error-normalize.js +91 -0
  248. package/dist/mcp/oauth/error-normalize.js.map +1 -0
  249. package/dist/mcp/oauth/provider.d.ts +190 -0
  250. package/dist/mcp/oauth/provider.d.ts.map +1 -0
  251. package/dist/mcp/oauth/provider.js +305 -0
  252. package/dist/mcp/oauth/provider.js.map +1 -0
  253. package/dist/mcp/oauth/refresh-coalescer.d.ts +46 -0
  254. package/dist/mcp/oauth/refresh-coalescer.d.ts.map +1 -0
  255. package/dist/mcp/oauth/refresh-coalescer.js +77 -0
  256. package/dist/mcp/oauth/refresh-coalescer.js.map +1 -0
  257. package/dist/mcp/oauth/sdk-shapes.d.ts +77 -0
  258. package/dist/mcp/oauth/sdk-shapes.d.ts.map +1 -0
  259. package/dist/mcp/oauth/sdk-shapes.js +21 -0
  260. package/dist/mcp/oauth/sdk-shapes.js.map +1 -0
  261. package/dist/mcp/parse.d.ts +28 -0
  262. package/dist/mcp/parse.d.ts.map +1 -0
  263. package/dist/mcp/parse.js +209 -0
  264. package/dist/mcp/parse.js.map +1 -0
  265. package/dist/mcp/prompts.d.ts +31 -0
  266. package/dist/mcp/prompts.d.ts.map +1 -0
  267. package/dist/mcp/prompts.js +71 -0
  268. package/dist/mcp/prompts.js.map +1 -0
  269. package/dist/mcp/redact.d.ts +62 -0
  270. package/dist/mcp/redact.d.ts.map +1 -0
  271. package/dist/mcp/redact.js +87 -0
  272. package/dist/mcp/redact.js.map +1 -0
  273. package/dist/mcp/resources.d.ts +70 -0
  274. package/dist/mcp/resources.d.ts.map +1 -0
  275. package/dist/mcp/resources.js +170 -0
  276. package/dist/mcp/resources.js.map +1 -0
  277. package/dist/mcp/types.d.ts +123 -0
  278. package/dist/mcp/types.d.ts.map +1 -0
  279. package/dist/mcp/types.js +2 -0
  280. package/dist/mcp/types.js.map +1 -0
  281. package/dist/memory/frontmatter.d.ts +18 -0
  282. package/dist/memory/frontmatter.d.ts.map +1 -0
  283. package/dist/memory/frontmatter.js +81 -0
  284. package/dist/memory/frontmatter.js.map +1 -0
  285. package/dist/memory/index.d.ts +5 -0
  286. package/dist/memory/index.d.ts.map +1 -0
  287. package/dist/memory/index.js +5 -0
  288. package/dist/memory/index.js.map +1 -0
  289. package/dist/memory/store.d.ts +44 -0
  290. package/dist/memory/store.d.ts.map +1 -0
  291. package/dist/memory/store.js +237 -0
  292. package/dist/memory/store.js.map +1 -0
  293. package/dist/memory/tools.d.ts +11 -0
  294. package/dist/memory/tools.d.ts.map +1 -0
  295. package/dist/memory/tools.js +159 -0
  296. package/dist/memory/tools.js.map +1 -0
  297. package/dist/memory/types.d.ts +32 -0
  298. package/dist/memory/types.d.ts.map +1 -0
  299. package/dist/memory/types.js +20 -0
  300. package/dist/memory/types.js.map +1 -0
  301. package/dist/plugin-api/index.d.ts +167 -0
  302. package/dist/plugin-api/index.d.ts.map +1 -0
  303. package/dist/plugin-api/index.js +151 -0
  304. package/dist/plugin-api/index.js.map +1 -0
  305. package/dist/plugin-logger/index.d.ts +21 -0
  306. package/dist/plugin-logger/index.d.ts.map +1 -0
  307. package/dist/plugin-logger/index.js +59 -0
  308. package/dist/plugin-logger/index.js.map +1 -0
  309. package/dist/session/index.d.ts +125 -0
  310. package/dist/session/index.d.ts.map +1 -0
  311. package/dist/session/index.js +202 -0
  312. package/dist/session/index.js.map +1 -0
  313. package/dist/tools/approval.d.ts +33 -0
  314. package/dist/tools/approval.d.ts.map +1 -0
  315. package/dist/tools/approval.js +53 -0
  316. package/dist/tools/approval.js.map +1 -0
  317. package/dist/tools/builtins/_shared.d.ts +94 -0
  318. package/dist/tools/builtins/_shared.d.ts.map +1 -0
  319. package/dist/tools/builtins/_shared.js +246 -0
  320. package/dist/tools/builtins/_shared.js.map +1 -0
  321. package/dist/tools/builtins/ask-user-question.d.ts +27 -0
  322. package/dist/tools/builtins/ask-user-question.d.ts.map +1 -0
  323. package/dist/tools/builtins/ask-user-question.js +191 -0
  324. package/dist/tools/builtins/ask-user-question.js.map +1 -0
  325. package/dist/tools/builtins/bash.d.ts +3 -0
  326. package/dist/tools/builtins/bash.d.ts.map +1 -0
  327. package/dist/tools/builtins/bash.js +158 -0
  328. package/dist/tools/builtins/bash.js.map +1 -0
  329. package/dist/tools/builtins/diff.d.ts +3 -0
  330. package/dist/tools/builtins/diff.d.ts.map +1 -0
  331. package/dist/tools/builtins/diff.js +83 -0
  332. package/dist/tools/builtins/diff.js.map +1 -0
  333. package/dist/tools/builtins/edit.d.ts +3 -0
  334. package/dist/tools/builtins/edit.d.ts.map +1 -0
  335. package/dist/tools/builtins/edit.js +40 -0
  336. package/dist/tools/builtins/edit.js.map +1 -0
  337. package/dist/tools/builtins/glob.d.ts +3 -0
  338. package/dist/tools/builtins/glob.d.ts.map +1 -0
  339. package/dist/tools/builtins/glob.js +37 -0
  340. package/dist/tools/builtins/glob.js.map +1 -0
  341. package/dist/tools/builtins/grep.d.ts +3 -0
  342. package/dist/tools/builtins/grep.d.ts.map +1 -0
  343. package/dist/tools/builtins/grep.js +81 -0
  344. package/dist/tools/builtins/grep.js.map +1 -0
  345. package/dist/tools/builtins/lsp.d.ts +3 -0
  346. package/dist/tools/builtins/lsp.d.ts.map +1 -0
  347. package/dist/tools/builtins/lsp.js +102 -0
  348. package/dist/tools/builtins/lsp.js.map +1 -0
  349. package/dist/tools/builtins/pty.d.ts +64 -0
  350. package/dist/tools/builtins/pty.d.ts.map +1 -0
  351. package/dist/tools/builtins/pty.js +536 -0
  352. package/dist/tools/builtins/pty.js.map +1 -0
  353. package/dist/tools/builtins/read.d.ts +3 -0
  354. package/dist/tools/builtins/read.d.ts.map +1 -0
  355. package/dist/tools/builtins/read.js +18 -0
  356. package/dist/tools/builtins/read.js.map +1 -0
  357. package/dist/tools/builtins/web-fetch.d.ts +4 -0
  358. package/dist/tools/builtins/web-fetch.d.ts.map +1 -0
  359. package/dist/tools/builtins/web-fetch.js +353 -0
  360. package/dist/tools/builtins/web-fetch.js.map +1 -0
  361. package/dist/tools/builtins/write.d.ts +3 -0
  362. package/dist/tools/builtins/write.d.ts.map +1 -0
  363. package/dist/tools/builtins/write.js +48 -0
  364. package/dist/tools/builtins/write.js.map +1 -0
  365. package/dist/tools/builtins.d.ts +9 -0
  366. package/dist/tools/builtins.d.ts.map +1 -0
  367. package/dist/tools/builtins.js +29 -0
  368. package/dist/tools/builtins.js.map +1 -0
  369. package/dist/tools/diff.d.ts +18 -0
  370. package/dist/tools/diff.d.ts.map +1 -0
  371. package/dist/tools/diff.js +57 -0
  372. package/dist/tools/diff.js.map +1 -0
  373. package/dist/tools/index.d.ts +10 -0
  374. package/dist/tools/index.d.ts.map +1 -0
  375. package/dist/tools/index.js +9 -0
  376. package/dist/tools/index.js.map +1 -0
  377. package/dist/tools/permission.d.ts +120 -0
  378. package/dist/tools/permission.d.ts.map +1 -0
  379. package/dist/tools/permission.js +208 -0
  380. package/dist/tools/permission.js.map +1 -0
  381. package/dist/tools/registry.d.ts +12 -0
  382. package/dist/tools/registry.d.ts.map +1 -0
  383. package/dist/tools/registry.js +19 -0
  384. package/dist/tools/registry.js.map +1 -0
  385. package/dist/tools/types.d.ts +244 -0
  386. package/dist/tools/types.d.ts.map +1 -0
  387. package/dist/tools/types.js +15 -0
  388. package/dist/tools/types.js.map +1 -0
  389. package/package.json +109 -0
@@ -0,0 +1,121 @@
1
+ import { mkdir, stat } from "node:fs/promises";
2
+ import { ensureChancesExcludeEntry, findCanonicalRoot, gitBranchDelete, gitWorktreeAdd, gitWorktreeRemove, } from "./git.js";
3
+ import { branchNameFor, isAgentSlug, newAgentSlug } from "./slug.js";
4
+ import { writeActiveMarker } from "./active-marker.js";
5
+ import { worktreePathFor, worktreesDir } from "./paths.js";
6
+ import { WorktreeError } from "./errors.js";
7
+ /**
8
+ * Creates a fresh `.chances/worktrees/<slug>` worktree for an isolated
9
+ * subagent. Per Round-1:
10
+ *
11
+ * - AbortSignal-aware: cancellation kills the in-flight `git worktree add`
12
+ * and runs idempotent cleanup before rethrowing (MUST-FIX #3).
13
+ * - Slug-collision retry up to `maxSlugAttempts` (NICE #2).
14
+ * - Canonical-root resolution so `chances`-from-inside-a-user-worktree
15
+ * doesn't nest a new worktree under the user's (SHOULD-FIX #3).
16
+ * - `.git/info/exclude` ensured for `.chances/worktrees/` so the parent's
17
+ * `git status` stays clean (SHOULD-FIX #1).
18
+ * - Writes the `.chances-active` heartbeat marker so cross-process GC can
19
+ * detect a live owner (MUST-FIX #4 + SHOULD-FIX #5).
20
+ */
21
+ export async function createAgentWorktree(cwd, opts = {}) {
22
+ const { signal } = opts;
23
+ const maxAttempts = Math.max(1, opts.maxSlugAttempts ?? 5);
24
+ if (signal?.aborted) {
25
+ throw new WorktreeError("CREATE_FAILED", "cancelled before start");
26
+ }
27
+ const canonicalRoot = await findCanonicalRoot(cwd, signal);
28
+ await ensureChancesExcludeEntry(canonicalRoot);
29
+ await mkdir(worktreesDir(canonicalRoot), { recursive: true });
30
+ let lastErr;
31
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
32
+ if (signal?.aborted) {
33
+ throw new WorktreeError("CREATE_FAILED", "cancelled mid-create");
34
+ }
35
+ const slug = newAgentSlug();
36
+ if (!isAgentSlug(slug)) {
37
+ // Defensive: random hex generation should always satisfy the regex,
38
+ // but if a future change loosens entropy we want to fail loud rather
39
+ // than push a malformed branch into git.
40
+ throw new WorktreeError("CREATE_FAILED", `generated slug rejected: ${slug}`);
41
+ }
42
+ const branch = branchNameFor(slug);
43
+ const path = worktreePathFor(canonicalRoot, slug);
44
+ // Slug-collision pre-check: if the path already exists, regenerate.
45
+ if (await pathExists(path)) {
46
+ lastErr = new WorktreeError("CREATE_FAILED", `path collision: ${path}`);
47
+ continue;
48
+ }
49
+ try {
50
+ await gitWorktreeAdd(canonicalRoot, path, branch, signal);
51
+ await writeActiveMarker(path);
52
+ return {
53
+ canonicalRoot,
54
+ path,
55
+ branch,
56
+ slug,
57
+ cleanup: makeCleanup(canonicalRoot, path, branch),
58
+ };
59
+ }
60
+ catch (e) {
61
+ lastErr = e;
62
+ // Best-effort cleanup in case `git worktree add` left a partial
63
+ // directory or branch behind. Errors here are swallowed — we're
64
+ // already on the failure path and the next stale-GC pass will mop
65
+ // up. We do NOT re-check `signal` here because cleanup is the
66
+ // promise we made to the caller; honouring it dominates the abort.
67
+ try {
68
+ await gitWorktreeRemove(canonicalRoot, path);
69
+ }
70
+ catch {
71
+ /* ignore */
72
+ }
73
+ try {
74
+ await gitBranchDelete(canonicalRoot, branch);
75
+ }
76
+ catch {
77
+ /* ignore */
78
+ }
79
+ // If the error looks like a transient collision, retry. Otherwise
80
+ // propagate immediately to avoid masking real failures (e.g.
81
+ // permission denied on `.chances/`).
82
+ if (e instanceof WorktreeError && /collision|already exists/i.test(e.message)) {
83
+ continue;
84
+ }
85
+ throw e;
86
+ }
87
+ }
88
+ throw new WorktreeError("SLUG_EXHAUSTED", `could not create a unique worktree slug after ${maxAttempts} attempts`, lastErr);
89
+ }
90
+ function makeCleanup(canonicalRoot, path, branch) {
91
+ let done = false;
92
+ return async () => {
93
+ if (done)
94
+ return;
95
+ done = true;
96
+ try {
97
+ await gitWorktreeRemove(canonicalRoot, path);
98
+ }
99
+ catch {
100
+ /* best-effort; stale-GC handles the rest */
101
+ }
102
+ try {
103
+ await gitBranchDelete(canonicalRoot, branch);
104
+ }
105
+ catch {
106
+ /* best-effort */
107
+ }
108
+ };
109
+ }
110
+ async function pathExists(p) {
111
+ try {
112
+ await stat(p);
113
+ return true;
114
+ }
115
+ catch (e) {
116
+ if (e.code === "ENOENT")
117
+ return false;
118
+ return true;
119
+ }
120
+ }
121
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/core/worktree/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,GAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA4B5C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAW,EACX,OAAmC,EAAE;IAErC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,CAAC;IAE3D,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,yBAAyB,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9D,IAAI,OAAgB,CAAC;IACrB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,oEAAoE;YACpE,qEAAqE;YACrE,yCAAyC;YACzC,MAAM,IAAI,aAAa,CAAC,eAAe,EAAE,4BAA4B,IAAI,EAAE,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,eAAe,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAElD,oEAAoE;QACpE,IAAI,MAAM,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,OAAO,GAAG,IAAI,aAAa,CAAC,eAAe,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC;YACxE,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YAC1D,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC9B,OAAO;gBACL,aAAa;gBACb,IAAI;gBACJ,MAAM;gBACN,IAAI;gBACJ,OAAO,EAAE,WAAW,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC;aAClD,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,CAAC,CAAC;YACZ,gEAAgE;YAChE,gEAAgE;YAChE,kEAAkE;YAClE,8DAA8D;YAC9D,mEAAmE;YACnE,IAAI,CAAC;gBACH,MAAM,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;YACD,kEAAkE;YAClE,6DAA6D;YAC7D,qCAAqC;YACrC,IAAI,CAAC,YAAY,aAAa,IAAI,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9E,SAAS;YACX,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC;IAED,MAAM,IAAI,aAAa,CACrB,gBAAgB,EAChB,iDAAiD,WAAW,WAAW,EACvE,OAAO,CACR,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAClB,aAAqB,EACrB,IAAY,EACZ,MAAc;IAEd,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,OAAO,KAAK,IAAI,EAAE;QAChB,IAAI,IAAI;YAAE,OAAO;QACjB,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC;YACH,MAAM,iBAAiB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;QACD,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,CAAS;IACjC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAK,CAA2B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type WorktreeErrorCode = "NOT_A_GIT_REPO" | "GIT_CLI_MISSING" | "CREATE_FAILED" | "REMOVE_FAILED" | "GC_FAILED" | "SLUG_EXHAUSTED" | "NESTED_RESOLVE_FAILED";
2
+ export declare class WorktreeError extends Error {
3
+ readonly code: WorktreeErrorCode;
4
+ readonly cause?: unknown | undefined;
5
+ constructor(code: WorktreeErrorCode, message: string, cause?: unknown | undefined);
6
+ }
7
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/worktree/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GACzB,gBAAgB,GAChB,iBAAiB,GACjB,eAAe,GACf,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,uBAAuB,CAAC;AAE5B,qBAAa,aAAc,SAAQ,KAAK;IAEpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB;aAEd,KAAK,CAAC,EAAE,OAAO;gBAFxB,IAAI,EAAE,iBAAiB,EAChC,OAAO,EAAE,MAAM,EACG,KAAK,CAAC,EAAE,OAAO,YAAA;CAKpC"}
@@ -0,0 +1,11 @@
1
+ export class WorktreeError extends Error {
2
+ code;
3
+ cause;
4
+ constructor(code, message, cause) {
5
+ super(`${code}: ${message}`);
6
+ this.code = code;
7
+ this.cause = cause;
8
+ this.name = "WorktreeError";
9
+ }
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/worktree/errors.ts"],"names":[],"mappings":"AASA,MAAM,OAAO,aAAc,SAAQ,KAAK;IAE3B;IAES;IAHpB,YACW,IAAuB,EAChC,OAAe,EACG,KAAe;QAEjC,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAJpB,SAAI,GAAJ,IAAI,CAAmB;QAEd,UAAK,GAAL,KAAK,CAAU;QAGjC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,39 @@
1
+ export interface GcOptions {
2
+ /** Worktrees older than this are eligible. Default 30, matches
3
+ * claude-code. */
4
+ cutoffDays?: number;
5
+ /** Heartbeat freshness window. If the active marker's `lastHeartbeat`
6
+ * is within this window (and the PID is alive), the worktree is
7
+ * conservatively retained. Default 600 s = 10 min. */
8
+ heartbeatStaleSeconds?: number;
9
+ /** Upper bound on the number of entries the GC scans in one pass.
10
+ * Matches 3.3's catalog cap pattern — bounds the cost so a pathological
11
+ * `.chances/worktrees/` (thousands of leaked dirs) can't blow the
12
+ * cold-start budget. Default 256. */
13
+ maxEntries?: number;
14
+ /** Cap on the wallclock the GC pass may spend before giving up the
15
+ * rest of the entries (the remaining cleanup falls to the next boot).
16
+ * Default 2 000 ms. */
17
+ maxWallclockMs?: number;
18
+ /** Injected `now` for tests. */
19
+ now?: () => number;
20
+ }
21
+ export interface GcResult {
22
+ scanned: number;
23
+ removed: string[];
24
+ skipped: {
25
+ path: string;
26
+ reason: string;
27
+ }[];
28
+ }
29
+ /** Best-effort sweep of stale agent worktrees under
30
+ * `<canonicalRoot>/.chances/worktrees/`. Fail-closed: any error on a
31
+ * single entry skips that entry rather than deleting on uncertainty.
32
+ * Returns a summary so the boot path can log a one-line "removed N
33
+ * stale worktrees" without spamming. */
34
+ export declare function cleanupStaleWorktrees(canonicalRoot: string, opts?: GcOptions): Promise<GcResult>;
35
+ /** Active-marker path expose for tests/diagnostics — kept here so the
36
+ * gc module's surface stays self-contained without re-exporting from
37
+ * `paths.ts`. */
38
+ export { activeMarkerPathFor } from "./paths.js";
39
+ //# sourceMappingURL=gc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../../src/core/worktree/gc.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,SAAS;IACxB;sBACkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;0DAEsD;IACtD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;yCAGqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;2BAEuB;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7C;AAED;;;;wCAIwC;AACxC,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CA0EnB;AA+ED;;iBAEiB;AACjB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,146 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { gitBranchDelete, gitDiffShortstat, gitStatusEmpty, gitUnmergedCommitCount, gitWorktreeRemove, } from "./git.js";
4
+ import { branchNameFor, isAgentSlug } from "./slug.js";
5
+ import { worktreesDir } from "./paths.js";
6
+ import { isProcessAlive, readActiveMarker } from "./active-marker.js";
7
+ /** Best-effort sweep of stale agent worktrees under
8
+ * `<canonicalRoot>/.chances/worktrees/`. Fail-closed: any error on a
9
+ * single entry skips that entry rather than deleting on uncertainty.
10
+ * Returns a summary so the boot path can log a one-line "removed N
11
+ * stale worktrees" without spamming. */
12
+ export async function cleanupStaleWorktrees(canonicalRoot, opts = {}) {
13
+ const cutoffDays = opts.cutoffDays ?? 30;
14
+ const heartbeatStaleSeconds = opts.heartbeatStaleSeconds ?? 600;
15
+ const maxEntries = Math.max(1, opts.maxEntries ?? 256);
16
+ const maxWallclockMs = Math.max(100, opts.maxWallclockMs ?? 2_000);
17
+ const now = opts.now ?? Date.now;
18
+ const startedAt = now();
19
+ const cutoffMs = cutoffDays * 24 * 60 * 60 * 1000;
20
+ const heartbeatStaleMs = heartbeatStaleSeconds * 1000;
21
+ const dir = worktreesDir(canonicalRoot);
22
+ let entries;
23
+ try {
24
+ entries = await readdir(dir);
25
+ }
26
+ catch (e) {
27
+ if (e.code === "ENOENT") {
28
+ return { scanned: 0, removed: [], skipped: [] };
29
+ }
30
+ return { scanned: 0, removed: [], skipped: [{ path: dir, reason: "readdir failed" }] };
31
+ }
32
+ const result = { scanned: 0, removed: [], skipped: [] };
33
+ for (const name of entries.sort()) {
34
+ if (result.scanned >= maxEntries) {
35
+ result.skipped.push({ path: name, reason: "max-entries cap reached" });
36
+ continue;
37
+ }
38
+ if (now() - startedAt > maxWallclockMs) {
39
+ result.skipped.push({ path: name, reason: "wallclock cap reached" });
40
+ break;
41
+ }
42
+ result.scanned++;
43
+ if (!isAgentSlug(name)) {
44
+ result.skipped.push({ path: name, reason: "slug shape mismatch" });
45
+ continue;
46
+ }
47
+ const worktreePath = join(dir, name);
48
+ try {
49
+ const verdict = await evaluateEntry(canonicalRoot, worktreePath, name, {
50
+ now: now(),
51
+ cutoffMs,
52
+ heartbeatStaleMs,
53
+ });
54
+ if (verdict === "delete") {
55
+ try {
56
+ await gitWorktreeRemove(canonicalRoot, worktreePath);
57
+ }
58
+ catch (e) {
59
+ // (Round-2 SHOULD-FIX #1) Real removal failures must NOT be
60
+ // reported as successful removal. Skip this entry; the user
61
+ // can investigate / next pass will retry.
62
+ result.skipped.push({
63
+ path: name,
64
+ reason: `remove failed: ${e.message || String(e)}`,
65
+ });
66
+ continue;
67
+ }
68
+ // Branch deletion is best-effort — the worktree directory is
69
+ // already gone so leaving an orphan branch is benign.
70
+ await gitBranchDelete(canonicalRoot, branchNameFor(name));
71
+ result.removed.push(name);
72
+ }
73
+ else {
74
+ result.skipped.push({ path: name, reason: verdict });
75
+ }
76
+ }
77
+ catch (e) {
78
+ // Fail-closed: ANY git error on a probe skips this entry.
79
+ result.skipped.push({
80
+ path: name,
81
+ reason: `error: ${e.message || String(e)}`,
82
+ });
83
+ }
84
+ }
85
+ return result;
86
+ }
87
+ async function evaluateEntry(canonicalRoot, worktreePath, slug, ctx) {
88
+ // (1) `.git` pointer file exists — if not, this is a stale leftover
89
+ // dir that git no longer tracks; let regular FS cleanup handle it
90
+ // (deleted via `git worktree remove --force` against the path is
91
+ // typically a no-op, but we keep that path off the happy path so the
92
+ // operator can investigate orphans).
93
+ const dotGitPath = join(worktreePath, ".git");
94
+ let dotGitStat;
95
+ try {
96
+ dotGitStat = await stat(dotGitPath);
97
+ }
98
+ catch {
99
+ return "missing-dotgit-pointer";
100
+ }
101
+ // (2) Age gate: created < cutoffDays ago → kept. Use the `.git`
102
+ // pointer's mtime (claude-code pattern) since the worktree dir itself
103
+ // can be touched by tools.
104
+ const ageMs = ctx.now - dotGitStat.mtimeMs;
105
+ if (ageMs < ctx.cutoffMs)
106
+ return "too-recent";
107
+ // (3) Active marker liveness check. Marker missing → eligible
108
+ // (continues to dirty/unmerged probes). Marker present + fresh
109
+ // heartbeat + alive PID → conservatively retained.
110
+ const marker = await readActiveMarker(worktreePath);
111
+ if (marker) {
112
+ const heartbeatAge = ctx.now - marker.lastHeartbeat;
113
+ if (heartbeatAge < ctx.heartbeatStaleMs) {
114
+ // Marker is fresh — owner is almost certainly alive, hands off.
115
+ return "active-marker-fresh";
116
+ }
117
+ if (isProcessAlive(marker.pid, marker.hostname)) {
118
+ // Heartbeat is stale but the PID is alive (or remote-host); the
119
+ // owner may be paused (debugger, SIGSTOP) or just slow. Stay
120
+ // conservative.
121
+ return "active-marker-live-pid";
122
+ }
123
+ // Marker is stale AND owner is dead → fall through to git probes.
124
+ }
125
+ // (4) Worktree must be clean (no untracked, no staged, no modified).
126
+ const clean = await gitStatusEmpty(worktreePath);
127
+ if (!clean)
128
+ return "git-dirty";
129
+ // (5) Branch must have no commits unreachable from HEAD.
130
+ const unmerged = await gitUnmergedCommitCount(canonicalRoot, branchNameFor(slug));
131
+ if (unmerged > 0)
132
+ return "unmerged-commits";
133
+ // (6) Extra defensive belt: a `--shortstat` diff against HEAD that's
134
+ // non-empty also keeps it. `gitStatusEmpty` already covers this, but
135
+ // catching the second observable is cheap and matches claude-code's
136
+ // belt-and-suspenders pattern.
137
+ const shortstat = await gitDiffShortstat(worktreePath);
138
+ if (shortstat.length > 0)
139
+ return "git-dirty";
140
+ return "delete";
141
+ }
142
+ /** Active-marker path expose for tests/diagnostics — kept here so the
143
+ * gc module's surface stays self-contained without re-exporting from
144
+ * `paths.ts`. */
145
+ export { activeMarkerPathFor } from "./paths.js";
146
+ //# sourceMappingURL=gc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc.js","sourceRoot":"","sources":["../../../src/core/worktree/gc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ export interface GitRunOptions {
2
+ cwd?: string;
3
+ signal?: AbortSignal;
4
+ /** When true, a non-zero exit is returned as `{ ok: false, ... }`
5
+ * instead of throwing. Callers that care about specific codes (e.g.
6
+ * `git status` returning 0/non-zero meaningfully) opt into this. */
7
+ allowNonZero?: boolean;
8
+ }
9
+ export interface GitResult {
10
+ ok: boolean;
11
+ stdout: string;
12
+ stderr: string;
13
+ code: number | null;
14
+ }
15
+ /** Spawn `git ARGS...` with stdout/stderr captured. Resolves with the result;
16
+ * throws WorktreeError on signal failures or when the git binary is missing.
17
+ * Cancellation via AbortSignal kills the child with SIGTERM. */
18
+ export declare function runGit(args: string[], opts?: GitRunOptions): Promise<GitResult>;
19
+ /** Resolves the canonical git repo root for `cwd`. When inside a nested
20
+ * worktree, `--show-superproject-working-tree` returns the parent superproject;
21
+ * we fall back to `--show-toplevel` for regular repos. (Round-1 SHOULD-FIX #3.) */
22
+ export declare function findCanonicalRoot(cwd: string, signal?: AbortSignal): Promise<string>;
23
+ /** `git worktree add -b <branch> <path> HEAD` against `canonicalRoot`. */
24
+ export declare function gitWorktreeAdd(canonicalRoot: string, path: string, branch: string, signal?: AbortSignal): Promise<void>;
25
+ /** `git worktree remove --force <path>` then `git worktree prune`.
26
+ *
27
+ * Idempotency: "worktree is not registered" / "no such directory"
28
+ * exits are treated as success (the post-condition "the path is not
29
+ * registered as a worktree" already holds). REAL failures (permission
30
+ * denied, locked, FS error) throw `REMOVE_FAILED` so callers can
31
+ * distinguish "removed" from "could not remove" — Round-2 SHOULD-FIX
32
+ * #1.
33
+ */
34
+ export declare function gitWorktreeRemove(canonicalRoot: string, path: string, signal?: AbortSignal): Promise<void>;
35
+ /** `git branch -D <branch>`. Best-effort; returns silently if the branch
36
+ * doesn't exist. */
37
+ export declare function gitBranchDelete(canonicalRoot: string, branch: string, signal?: AbortSignal): Promise<void>;
38
+ /** `git status --porcelain` parser: returns true when the worktree has no
39
+ * untracked, modified, or staged files. */
40
+ export declare function gitStatusEmpty(worktreePath: string, signal?: AbortSignal): Promise<boolean>;
41
+ /** `git rev-list <branch> ^HEAD --count`. Number of commits on `branch` not
42
+ * reachable from `HEAD`. Used by GC to detect worktrees holding unmerged
43
+ * work. */
44
+ export declare function gitUnmergedCommitCount(canonicalRoot: string, branch: string, signal?: AbortSignal): Promise<number>;
45
+ /** `git diff --shortstat HEAD` invoked inside the worktree. Returns the
46
+ * trimmed shortstat ("3 files changed, 47 insertions(+), 12 deletions(-)")
47
+ * or the empty string when the worktree matches HEAD. */
48
+ export declare function gitDiffShortstat(worktreePath: string, signal?: AbortSignal): Promise<string>;
49
+ /** Ensures `.git/info/exclude` contains `.chances/worktrees/`. Idempotent. We
50
+ * append a labelled section so a re-run doesn't duplicate the entry, and we
51
+ * never touch the user's tracked `.gitignore`. (Round-1 SHOULD-FIX #1.) */
52
+ export declare function ensureChancesExcludeEntry(canonicalRoot: string): Promise<void>;
53
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../src/core/worktree/git.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;wEAEoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;gEAEgE;AAChE,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,SAAS,CAAC,CA+BzF;AAED;;mFAEmF;AACnF,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB1F;AAED,0EAA0E;AAC1E,wBAAsB,cAAc,CAClC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;oBACoB;AACpB,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;2CAC2C;AAC3C,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CASjG;AAED;;WAEW;AACX,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;yDAEyD;AACzD,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAQlG;AAED;;2EAE2E;AAC3E,wBAAsB,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBpF"}
@@ -0,0 +1,166 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { WorktreeError } from "./errors.js";
5
+ /** Spawn `git ARGS...` with stdout/stderr captured. Resolves with the result;
6
+ * throws WorktreeError on signal failures or when the git binary is missing.
7
+ * Cancellation via AbortSignal kills the child with SIGTERM. */
8
+ export async function runGit(args, opts = {}) {
9
+ return new Promise((resolve, reject) => {
10
+ const child = spawn("git", args, {
11
+ cwd: opts.cwd,
12
+ signal: opts.signal,
13
+ stdio: ["ignore", "pipe", "pipe"],
14
+ });
15
+ let stdout = "";
16
+ let stderr = "";
17
+ child.stdout?.on("data", (b) => (stdout += b.toString("utf8")));
18
+ child.stderr?.on("data", (b) => (stderr += b.toString("utf8")));
19
+ child.on("error", (e) => {
20
+ if (e.code === "ENOENT") {
21
+ reject(new WorktreeError("GIT_CLI_MISSING", "git binary not found on PATH", e));
22
+ return;
23
+ }
24
+ if (e.name === "AbortError") {
25
+ reject(new WorktreeError("CREATE_FAILED", `git ${args[0]} aborted`, e));
26
+ return;
27
+ }
28
+ reject(new WorktreeError("CREATE_FAILED", `git ${args[0]} spawn failed: ${e.message}`, e));
29
+ });
30
+ child.on("close", (code) => {
31
+ const ok = code === 0;
32
+ if (!ok && !opts.allowNonZero) {
33
+ resolve({ ok, stdout, stderr, code });
34
+ return;
35
+ }
36
+ resolve({ ok, stdout, stderr, code });
37
+ });
38
+ });
39
+ }
40
+ /** Resolves the canonical git repo root for `cwd`. When inside a nested
41
+ * worktree, `--show-superproject-working-tree` returns the parent superproject;
42
+ * we fall back to `--show-toplevel` for regular repos. (Round-1 SHOULD-FIX #3.) */
43
+ export async function findCanonicalRoot(cwd, signal) {
44
+ const superRes = await runGit(["rev-parse", "--show-superproject-working-tree"], {
45
+ cwd,
46
+ signal,
47
+ allowNonZero: true,
48
+ });
49
+ if (superRes.ok) {
50
+ const trimmed = superRes.stdout.trim();
51
+ if (trimmed.length > 0)
52
+ return trimmed;
53
+ }
54
+ const topRes = await runGit(["rev-parse", "--show-toplevel"], {
55
+ cwd,
56
+ signal,
57
+ allowNonZero: true,
58
+ });
59
+ if (!topRes.ok) {
60
+ throw new WorktreeError("NOT_A_GIT_REPO", `not a git repository (or any parent): ${cwd}${topRes.stderr.trim() ? " — " + topRes.stderr.trim() : ""}`);
61
+ }
62
+ return topRes.stdout.trim();
63
+ }
64
+ /** `git worktree add -b <branch> <path> HEAD` against `canonicalRoot`. */
65
+ export async function gitWorktreeAdd(canonicalRoot, path, branch, signal) {
66
+ const res = await runGit(["worktree", "add", "-b", branch, path, "HEAD"], {
67
+ cwd: canonicalRoot,
68
+ signal,
69
+ allowNonZero: true,
70
+ });
71
+ if (!res.ok) {
72
+ throw new WorktreeError("CREATE_FAILED", `git worktree add failed${res.stderr.trim() ? ": " + res.stderr.trim() : ""}`);
73
+ }
74
+ }
75
+ /** `git worktree remove --force <path>` then `git worktree prune`.
76
+ *
77
+ * Idempotency: "worktree is not registered" / "no such directory"
78
+ * exits are treated as success (the post-condition "the path is not
79
+ * registered as a worktree" already holds). REAL failures (permission
80
+ * denied, locked, FS error) throw `REMOVE_FAILED` so callers can
81
+ * distinguish "removed" from "could not remove" — Round-2 SHOULD-FIX
82
+ * #1.
83
+ */
84
+ export async function gitWorktreeRemove(canonicalRoot, path, signal) {
85
+ const res = await runGit(["worktree", "remove", "--force", path], {
86
+ cwd: canonicalRoot,
87
+ signal,
88
+ allowNonZero: true,
89
+ });
90
+ if (!res.ok) {
91
+ const err = res.stderr.toLowerCase();
92
+ const idempotentMiss = /is not a working tree/.test(err) ||
93
+ /no such (file or directory|working tree)/.test(err) ||
94
+ /is not a working tree/.test(err) ||
95
+ /not a working tree/.test(err);
96
+ if (!idempotentMiss) {
97
+ throw new WorktreeError("REMOVE_FAILED", `git worktree remove failed${res.stderr.trim() ? ": " + res.stderr.trim() : ""}`);
98
+ }
99
+ }
100
+ await runGit(["worktree", "prune"], { cwd: canonicalRoot, signal, allowNonZero: true });
101
+ }
102
+ /** `git branch -D <branch>`. Best-effort; returns silently if the branch
103
+ * doesn't exist. */
104
+ export async function gitBranchDelete(canonicalRoot, branch, signal) {
105
+ await runGit(["branch", "-D", branch], { cwd: canonicalRoot, signal, allowNonZero: true });
106
+ }
107
+ /** `git status --porcelain` parser: returns true when the worktree has no
108
+ * untracked, modified, or staged files. */
109
+ export async function gitStatusEmpty(worktreePath, signal) {
110
+ const res = await runGit(["status", "--porcelain"], { cwd: worktreePath, signal });
111
+ if (!res.ok) {
112
+ throw new WorktreeError("GC_FAILED", `git status failed at ${worktreePath}${res.stderr.trim() ? ": " + res.stderr.trim() : ""}`);
113
+ }
114
+ return res.stdout.trim().length === 0;
115
+ }
116
+ /** `git rev-list <branch> ^HEAD --count`. Number of commits on `branch` not
117
+ * reachable from `HEAD`. Used by GC to detect worktrees holding unmerged
118
+ * work. */
119
+ export async function gitUnmergedCommitCount(canonicalRoot, branch, signal) {
120
+ const res = await runGit(["rev-list", "--count", branch, "^HEAD"], {
121
+ cwd: canonicalRoot,
122
+ signal,
123
+ allowNonZero: true,
124
+ });
125
+ if (!res.ok) {
126
+ throw new WorktreeError("GC_FAILED", `git rev-list failed for ${branch}${res.stderr.trim() ? ": " + res.stderr.trim() : ""}`);
127
+ }
128
+ const n = parseInt(res.stdout.trim(), 10);
129
+ return Number.isFinite(n) ? n : 0;
130
+ }
131
+ /** `git diff --shortstat HEAD` invoked inside the worktree. Returns the
132
+ * trimmed shortstat ("3 files changed, 47 insertions(+), 12 deletions(-)")
133
+ * or the empty string when the worktree matches HEAD. */
134
+ export async function gitDiffShortstat(worktreePath, signal) {
135
+ const res = await runGit(["diff", "--shortstat", "HEAD"], {
136
+ cwd: worktreePath,
137
+ signal,
138
+ allowNonZero: true,
139
+ });
140
+ if (!res.ok)
141
+ return "";
142
+ return res.stdout.trim();
143
+ }
144
+ /** Ensures `.git/info/exclude` contains `.chances/worktrees/`. Idempotent. We
145
+ * append a labelled section so a re-run doesn't duplicate the entry, and we
146
+ * never touch the user's tracked `.gitignore`. (Round-1 SHOULD-FIX #1.) */
147
+ export async function ensureChancesExcludeEntry(canonicalRoot) {
148
+ const excludePath = join(canonicalRoot, ".git", "info", "exclude");
149
+ let existing = "";
150
+ try {
151
+ existing = await readFile(excludePath, "utf8");
152
+ }
153
+ catch (e) {
154
+ const err = e;
155
+ if (err.code !== "ENOENT")
156
+ throw err;
157
+ }
158
+ if (existing.includes(".chances/worktrees/"))
159
+ return;
160
+ await mkdir(dirname(excludePath), { recursive: true });
161
+ const block = (existing.endsWith("\n") || existing.length === 0 ? "" : "\n") +
162
+ "# chances-cli — agent worktrees (auto-managed, see docs/4.1-design.md)\n" +
163
+ ".chances/worktrees/\n";
164
+ await writeFile(excludePath, existing + block, "utf8");
165
+ }
166
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/core/worktree/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { createAgentWorktree, type AgentWorktreeHandle, type CreateAgentWorktreeOptions, } from "./create.js";
2
+ export { cleanupStaleWorktrees, type GcOptions, type GcResult, } from "./gc.js";
3
+ export { WorktreeError, type WorktreeErrorCode } from "./errors.js";
4
+ export { refreshActiveMarker } from "./active-marker.js";
5
+ export { gitDiffShortstat, findCanonicalRoot } from "./git.js";
6
+ export { worktreePathFor, worktreesDir, activeMarkerPathFor } from "./paths.js";
7
+ export { branchNameFor, isAgentSlug, newAgentSlug } from "./slug.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/worktree/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,EACrB,KAAK,SAAS,EACd,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { createAgentWorktree, } from "./create.js";
2
+ export { cleanupStaleWorktrees, } from "./gc.js";
3
+ export { WorktreeError } from "./errors.js";
4
+ export { refreshActiveMarker } from "./active-marker.js";
5
+ export { gitDiffShortstat, findCanonicalRoot } from "./git.js";
6
+ export { worktreePathFor, worktreesDir, activeMarkerPathFor } from "./paths.js";
7
+ export { branchNameFor, isAgentSlug, newAgentSlug } from "./slug.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/worktree/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAGpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,qBAAqB,GAGtB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,aAAa,EAA0B,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}