borgmcp 1.1.14 → 2.0.1

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 (459) hide show
  1. package/CONTRIBUTING.md +15 -0
  2. package/LICENSE +201 -33
  3. package/NOTICE +2 -0
  4. package/README.md +86 -37
  5. package/SECURITY.md +7 -0
  6. package/dist/agent-runtime.d.ts.map +1 -0
  7. package/dist/agent-runtime.js +63 -1
  8. package/dist/agent-runtime.js.map +1 -0
  9. package/dist/assimilate-cmd.d.ts +96 -17
  10. package/dist/assimilate-cmd.d.ts.map +1 -0
  11. package/dist/assimilate-cmd.js +1396 -48
  12. package/dist/assimilate-cmd.js.map +1 -0
  13. package/dist/assimilate-deps.d.ts.map +1 -0
  14. package/dist/assimilate-deps.js +384 -3
  15. package/dist/assimilate-deps.js.map +1 -0
  16. package/dist/assimilate-guard.d.ts +5 -7
  17. package/dist/assimilate-guard.d.ts.map +1 -0
  18. package/dist/assimilate-guard.js +60 -2
  19. package/dist/assimilate-guard.js.map +1 -0
  20. package/dist/assimilate-welcome.d.ts +1 -1
  21. package/dist/assimilate-welcome.d.ts.map +1 -0
  22. package/dist/assimilate-welcome.js +32 -2
  23. package/dist/assimilate-welcome.js.map +1 -0
  24. package/dist/auth-env.d.ts +7 -50
  25. package/dist/auth-env.d.ts.map +1 -0
  26. package/dist/auth-env.js +22 -1
  27. package/dist/auth-env.js.map +1 -0
  28. package/dist/backends/launch-all-pastelist.d.ts.map +1 -0
  29. package/dist/backends/launch-all-pastelist.js +11 -4
  30. package/dist/backends/launch-all-pastelist.js.map +1 -0
  31. package/dist/backends/launch-all-tmux.d.ts.map +1 -0
  32. package/dist/backends/launch-all-tmux.js +49 -1
  33. package/dist/backends/launch-all-tmux.js.map +1 -0
  34. package/dist/backends/launch-all-windows.d.ts.map +1 -0
  35. package/dist/backends/launch-all-windows.js +86 -12
  36. package/dist/backends/launch-all-windows.js.map +1 -0
  37. package/dist/bare-launch-menu.d.ts.map +1 -0
  38. package/dist/bare-launch-menu.js +83 -6
  39. package/dist/bare-launch-menu.js.map +1 -0
  40. package/dist/claude-launch-args.d.ts.map +1 -0
  41. package/dist/claude-launch-args.js +34 -1
  42. package/dist/claude-launch-args.js.map +1 -0
  43. package/dist/claude.d.ts.map +1 -0
  44. package/dist/claude.js +486 -14
  45. package/dist/claude.js.map +1 -0
  46. package/dist/cleanup-cmd.d.ts +5 -5
  47. package/dist/cleanup-cmd.d.ts.map +1 -0
  48. package/dist/cleanup-cmd.js +453 -14
  49. package/dist/cleanup-cmd.js.map +1 -0
  50. package/dist/clear-rewake-core.d.ts.map +1 -0
  51. package/dist/clear-rewake-core.js +23 -2
  52. package/dist/clear-rewake-core.js.map +1 -0
  53. package/dist/clear-rewake.d.ts.map +1 -0
  54. package/dist/clear-rewake.js +19 -1
  55. package/dist/clear-rewake.js.map +1 -0
  56. package/dist/cli-help.d.ts +5 -0
  57. package/dist/cli-help.d.ts.map +1 -0
  58. package/dist/cli-help.js +104 -56
  59. package/dist/cli-help.js.map +1 -0
  60. package/dist/cli-platform.d.ts.map +1 -0
  61. package/dist/cli-platform.js +106 -4
  62. package/dist/cli-platform.js.map +1 -0
  63. package/dist/cli-tool-approval.d.ts +84 -0
  64. package/dist/cli-tool-approval.d.ts.map +1 -0
  65. package/dist/cli-tool-approval.js +583 -0
  66. package/dist/cli-tool-approval.js.map +1 -0
  67. package/dist/codex-app-server.d.ts.map +1 -0
  68. package/dist/codex-app-server.js +228 -4
  69. package/dist/codex-app-server.js.map +1 -0
  70. package/dist/codex-app-wake.d.ts.map +1 -0
  71. package/dist/codex-app-wake.js +432 -2
  72. package/dist/codex-app-wake.js.map +1 -0
  73. package/dist/codex-launch.d.ts.map +1 -0
  74. package/dist/codex-launch.js +102 -2
  75. package/dist/codex-launch.js.map +1 -0
  76. package/dist/codex-remote.d.ts +6 -1
  77. package/dist/codex-remote.d.ts.map +1 -0
  78. package/dist/codex-remote.js +394 -1
  79. package/dist/codex-remote.js.map +1 -0
  80. package/dist/codex-wake-resolve.d.ts.map +1 -0
  81. package/dist/codex-wake-resolve.js +127 -1
  82. package/dist/codex-wake-resolve.js.map +1 -0
  83. package/dist/config-utils.d.ts +2 -2
  84. package/dist/config-utils.d.ts.map +1 -0
  85. package/dist/config-utils.js +766 -3
  86. package/dist/config-utils.js.map +1 -0
  87. package/dist/config.d.ts +98 -61
  88. package/dist/config.d.ts.map +1 -0
  89. package/dist/config.js +426 -1
  90. package/dist/config.js.map +1 -0
  91. package/dist/console-prefix.d.ts.map +1 -0
  92. package/dist/console-prefix.js +96 -1
  93. package/dist/console-prefix.js.map +1 -0
  94. package/dist/cube-name.d.ts.map +1 -0
  95. package/dist/cube-name.js +67 -1
  96. package/dist/cube-name.js.map +1 -0
  97. package/dist/cubes.d.ts +126 -18
  98. package/dist/cubes.d.ts.map +1 -0
  99. package/dist/cubes.js +412 -4
  100. package/dist/cubes.js.map +1 -0
  101. package/dist/debug.d.ts.map +1 -0
  102. package/dist/debug.js +71 -1
  103. package/dist/debug.js.map +1 -0
  104. package/dist/direct-log.d.ts.map +1 -0
  105. package/dist/direct-log.js +11 -1
  106. package/dist/direct-log.js.map +1 -0
  107. package/dist/docs-sections.d.ts +11 -10
  108. package/dist/docs-sections.d.ts.map +1 -0
  109. package/dist/docs-sections.js +113 -3
  110. package/dist/docs-sections.js.map +1 -0
  111. package/dist/drone-lifecycle.d.ts +5 -21
  112. package/dist/drone-lifecycle.d.ts.map +1 -0
  113. package/dist/drone-lifecycle.js +57 -8
  114. package/dist/drone-lifecycle.js.map +1 -0
  115. package/dist/drone-management.d.ts +29 -0
  116. package/dist/drone-management.d.ts.map +1 -0
  117. package/dist/drone-management.js +109 -0
  118. package/dist/drone-management.js.map +1 -0
  119. package/dist/ensure-mcp-config.d.ts.map +1 -0
  120. package/dist/ensure-mcp-config.js +36 -1
  121. package/dist/ensure-mcp-config.js.map +1 -0
  122. package/dist/evict-drone.d.ts +10 -2
  123. package/dist/evict-drone.d.ts.map +1 -0
  124. package/dist/evict-drone.js +60 -1
  125. package/dist/evict-drone.js.map +1 -0
  126. package/dist/gc-orphan-inboxes.d.ts.map +1 -0
  127. package/dist/gc-orphan-inboxes.js +177 -1
  128. package/dist/gc-orphan-inboxes.js.map +1 -0
  129. package/dist/inbox-monitor.d.ts +1 -1
  130. package/dist/inbox-monitor.d.ts.map +1 -0
  131. package/dist/inbox-monitor.js +856 -3
  132. package/dist/inbox-monitor.js.map +1 -0
  133. package/dist/index.d.ts +6 -4
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +1034 -37
  136. package/dist/index.js.map +1 -0
  137. package/dist/launch-all-cmd.d.ts.map +1 -0
  138. package/dist/launch-all-cmd.js +377 -37
  139. package/dist/launch-all-cmd.js.map +1 -0
  140. package/dist/launch-all-command.d.ts.map +1 -0
  141. package/dist/launch-all-command.js +24 -1
  142. package/dist/launch-all-command.js.map +1 -0
  143. package/dist/launch-all-deps.d.ts +3 -8
  144. package/dist/launch-all-deps.d.ts.map +1 -0
  145. package/dist/launch-all-deps.js +99 -1
  146. package/dist/launch-all-deps.js.map +1 -0
  147. package/dist/launch-all-discovery.d.ts +1 -0
  148. package/dist/launch-all-discovery.d.ts.map +1 -0
  149. package/dist/launch-all-discovery.js +90 -4
  150. package/dist/launch-all-discovery.js.map +1 -0
  151. package/dist/launch-all-locks.d.ts.map +1 -0
  152. package/dist/launch-all-locks.js +54 -1
  153. package/dist/launch-all-locks.js.map +1 -0
  154. package/dist/launch-gate.d.ts +2 -2
  155. package/dist/launch-gate.d.ts.map +1 -0
  156. package/dist/launch-gate.js +73 -1
  157. package/dist/launch-gate.js.map +1 -0
  158. package/dist/lifecycle-log-guard.d.ts.map +1 -0
  159. package/dist/lifecycle-log-guard.js +93 -2
  160. package/dist/lifecycle-log-guard.js.map +1 -0
  161. package/dist/list-roles-render.d.ts.map +1 -0
  162. package/dist/list-roles-render.js +40 -6
  163. package/dist/list-roles-render.js.map +1 -0
  164. package/dist/local-log-routing.d.ts +19 -0
  165. package/dist/local-log-routing.d.ts.map +1 -0
  166. package/dist/local-log-routing.js +67 -0
  167. package/dist/local-log-routing.js.map +1 -0
  168. package/dist/local-manage-tool-result.d.ts +8 -0
  169. package/dist/local-manage-tool-result.d.ts.map +1 -0
  170. package/dist/local-manage-tool-result.js +10 -0
  171. package/dist/local-manage-tool-result.js.map +1 -0
  172. package/dist/local-server-cursor.d.ts +34 -0
  173. package/dist/local-server-cursor.d.ts.map +1 -0
  174. package/dist/local-server-cursor.js +151 -0
  175. package/dist/local-server-cursor.js.map +1 -0
  176. package/dist/log-audit.d.ts.map +1 -0
  177. package/dist/log-audit.js +194 -3
  178. package/dist/log-audit.js.map +1 -0
  179. package/dist/log-stream.d.ts +31 -18
  180. package/dist/log-stream.d.ts.map +1 -0
  181. package/dist/log-stream.js +1214 -12
  182. package/dist/log-stream.js.map +1 -0
  183. package/dist/model-presets.d.ts.map +1 -0
  184. package/dist/model-presets.js +19 -1
  185. package/dist/model-presets.js.map +1 -0
  186. package/dist/name-validator.d.ts.map +1 -0
  187. package/dist/name-validator.js +22 -1
  188. package/dist/name-validator.js.map +1 -0
  189. package/dist/opencode-drone.d.ts.map +1 -0
  190. package/dist/opencode-drone.js +438 -4
  191. package/dist/opencode-drone.js.map +1 -0
  192. package/dist/opencode-plugin.d.ts +1 -1
  193. package/dist/opencode-plugin.d.ts.map +1 -0
  194. package/dist/opencode-plugin.js +29 -4
  195. package/dist/opencode-plugin.js.map +1 -0
  196. package/dist/parse-assimilate-args.d.ts +1 -1
  197. package/dist/parse-assimilate-args.d.ts.map +1 -0
  198. package/dist/parse-assimilate-args.js +125 -1
  199. package/dist/parse-assimilate-args.js.map +1 -0
  200. package/dist/parse-launch-all-args.d.ts.map +1 -0
  201. package/dist/parse-launch-all-args.js +70 -1
  202. package/dist/parse-launch-all-args.js.map +1 -0
  203. package/dist/postinstall-banner.d.ts.map +1 -0
  204. package/dist/postinstall-banner.js +27 -2
  205. package/dist/postinstall-banner.js.map +1 -0
  206. package/dist/postinstall.d.ts.map +1 -0
  207. package/dist/postinstall.js +32 -5
  208. package/dist/postinstall.js.map +1 -0
  209. package/dist/readiness-probe.d.ts.map +1 -0
  210. package/dist/readiness-probe.js +9 -1
  211. package/dist/readiness-probe.js.map +1 -0
  212. package/dist/regen-format.d.ts.map +1 -0
  213. package/dist/regen-format.js +545 -13
  214. package/dist/regen-format.js.map +1 -0
  215. package/dist/regen.d.ts.map +1 -0
  216. package/dist/regen.js +141 -5
  217. package/dist/regen.js.map +1 -0
  218. package/dist/remote-client.d.ts +65 -175
  219. package/dist/remote-client.d.ts.map +1 -0
  220. package/dist/remote-client.js +942 -1
  221. package/dist/remote-client.js.map +1 -0
  222. package/dist/reset-local-seat-cmd.d.ts +50 -0
  223. package/dist/reset-local-seat-cmd.d.ts.map +1 -0
  224. package/dist/reset-local-seat-cmd.js +169 -0
  225. package/dist/reset-local-seat-cmd.js.map +1 -0
  226. package/dist/role-match.d.ts.map +1 -0
  227. package/dist/role-match.js +23 -1
  228. package/dist/role-match.js.map +1 -0
  229. package/dist/role-resolver.d.ts.map +1 -0
  230. package/dist/role-resolver.js +60 -1
  231. package/dist/role-resolver.js.map +1 -0
  232. package/dist/roster-render.d.ts.map +1 -0
  233. package/dist/roster-render.js +121 -3
  234. package/dist/roster-render.js.map +1 -0
  235. package/dist/seat-probe.d.ts +32 -13
  236. package/dist/seat-probe.d.ts.map +1 -0
  237. package/dist/seat-probe.js +78 -1
  238. package/dist/seat-probe.js.map +1 -0
  239. package/dist/seat-store.d.ts +76 -0
  240. package/dist/seat-store.d.ts.map +1 -0
  241. package/dist/seat-store.js +309 -0
  242. package/dist/seat-store.js.map +1 -0
  243. package/dist/seats.d.ts +303 -0
  244. package/dist/seats.d.ts.map +1 -0
  245. package/dist/seats.js +551 -0
  246. package/dist/seats.js.map +1 -0
  247. package/dist/self-path.d.ts +27 -0
  248. package/dist/self-path.d.ts.map +1 -0
  249. package/dist/self-path.js +43 -0
  250. package/dist/self-path.js.map +1 -0
  251. package/dist/server-endpoint.d.ts +9 -0
  252. package/dist/server-endpoint.d.ts.map +1 -0
  253. package/dist/server-endpoint.js +38 -0
  254. package/dist/server-endpoint.js.map +1 -0
  255. package/dist/server-errors.d.ts +50 -0
  256. package/dist/server-errors.d.ts.map +1 -0
  257. package/dist/server-errors.js +79 -0
  258. package/dist/server-errors.js.map +1 -0
  259. package/dist/server-handshake.d.ts +197 -0
  260. package/dist/server-handshake.d.ts.map +1 -0
  261. package/dist/server-handshake.js +505 -0
  262. package/dist/server-handshake.js.map +1 -0
  263. package/dist/server-response.d.ts +3 -0
  264. package/dist/server-response.d.ts.map +1 -0
  265. package/dist/server-response.js +67 -0
  266. package/dist/server-response.js.map +1 -0
  267. package/dist/server-trust.d.ts +14 -0
  268. package/dist/server-trust.d.ts.map +1 -0
  269. package/dist/server-trust.js +225 -0
  270. package/dist/server-trust.js.map +1 -0
  271. package/dist/setup-confirm.d.ts +3 -3
  272. package/dist/setup-confirm.d.ts.map +1 -0
  273. package/dist/setup-confirm.js +126 -2
  274. package/dist/setup-confirm.js.map +1 -0
  275. package/dist/setup.d.ts +5 -3
  276. package/dist/setup.d.ts.map +1 -0
  277. package/dist/setup.js +169 -46
  278. package/dist/setup.js.map +1 -0
  279. package/dist/shell-escape.d.ts.map +1 -0
  280. package/dist/shell-escape.js +22 -1
  281. package/dist/shell-escape.js.map +1 -0
  282. package/dist/spawn.d.ts +2 -2
  283. package/dist/spawn.d.ts.map +1 -0
  284. package/dist/spawn.js +29 -10
  285. package/dist/spawn.js.map +1 -0
  286. package/dist/startup-services.d.ts +0 -1
  287. package/dist/startup-services.d.ts.map +1 -0
  288. package/dist/startup-services.js +26 -1
  289. package/dist/startup-services.js.map +1 -0
  290. package/dist/stream-owner.d.ts +6 -2
  291. package/dist/stream-owner.d.ts.map +1 -0
  292. package/dist/stream-owner.js +547 -2
  293. package/dist/stream-owner.js.map +1 -0
  294. package/dist/stream-status.d.ts +1 -1
  295. package/dist/stream-status.d.ts.map +1 -0
  296. package/dist/stream-status.js +268 -3
  297. package/dist/stream-status.js.map +1 -0
  298. package/dist/sync-roles-render.d.ts.map +1 -0
  299. package/dist/sync-roles-render.js +118 -3
  300. package/dist/sync-roles-render.js.map +1 -0
  301. package/dist/sync.d.ts +1 -1
  302. package/dist/sync.d.ts.map +1 -0
  303. package/dist/sync.js +286 -22
  304. package/dist/sync.js.map +1 -0
  305. package/dist/terminal-title.d.ts.map +1 -0
  306. package/dist/terminal-title.js +68 -1
  307. package/dist/terminal-title.js.map +1 -0
  308. package/dist/token-store.d.ts +17 -111
  309. package/dist/token-store.d.ts.map +1 -0
  310. package/dist/token-store.js +81 -1
  311. package/dist/token-store.js.map +1 -0
  312. package/dist/tool-manifest.d.ts +4 -8
  313. package/dist/tool-manifest.d.ts.map +1 -0
  314. package/dist/tool-manifest.js +554 -1
  315. package/dist/tool-manifest.js.map +1 -0
  316. package/dist/tool-scope.d.ts +8 -12
  317. package/dist/tool-scope.d.ts.map +1 -0
  318. package/dist/tool-scope.js +97 -1
  319. package/dist/tool-scope.js.map +1 -0
  320. package/dist/types.d.ts +1 -5
  321. package/dist/types.d.ts.map +1 -0
  322. package/dist/types.js +5 -0
  323. package/dist/types.js.map +1 -0
  324. package/dist/unknown-subcommand.d.ts +1 -1
  325. package/dist/unknown-subcommand.d.ts.map +1 -0
  326. package/dist/unknown-subcommand.js +37 -1
  327. package/dist/unknown-subcommand.js.map +1 -0
  328. package/dist/version.d.ts +1 -1
  329. package/dist/version.d.ts.map +1 -0
  330. package/dist/version.js +78 -2
  331. package/dist/version.js.map +1 -0
  332. package/dist/working-repo.d.ts.map +1 -0
  333. package/dist/working-repo.js +81 -1
  334. package/dist/working-repo.js.map +1 -0
  335. package/dist/worktree-lifecycle.d.ts.map +1 -0
  336. package/dist/worktree-lifecycle.js +205 -2
  337. package/dist/worktree-lifecycle.js.map +1 -0
  338. package/docs/EXTRACTION_PROVENANCE.md +40 -0
  339. package/docs/LOCAL_SERVER.md +103 -0
  340. package/docs/RELEASING.md +120 -0
  341. package/package.json +52 -34
  342. package/src/agent-runtime.ts +69 -0
  343. package/src/assimilate-cmd.ts +1864 -0
  344. package/src/assimilate-deps.ts +444 -0
  345. package/src/assimilate-guard.ts +84 -0
  346. package/src/assimilate-welcome.ts +39 -0
  347. package/src/auth-env.ts +21 -0
  348. package/src/backends/launch-all-pastelist.ts +18 -0
  349. package/src/backends/launch-all-tmux.ts +75 -0
  350. package/src/backends/launch-all-windows.ts +110 -0
  351. package/src/bare-launch-menu.ts +148 -0
  352. package/src/claude-launch-args.ts +35 -0
  353. package/src/claude.ts +561 -0
  354. package/src/cleanup-cmd.ts +581 -0
  355. package/src/clear-rewake-core.ts +34 -0
  356. package/src/clear-rewake.ts +18 -0
  357. package/src/cli-help.ts +116 -0
  358. package/src/cli-platform.ts +127 -0
  359. package/src/cli-tool-approval.ts +716 -0
  360. package/src/codex-app-server.ts +238 -0
  361. package/src/codex-app-wake.ts +512 -0
  362. package/src/codex-launch.ts +140 -0
  363. package/src/codex-remote.ts +500 -0
  364. package/src/codex-wake-resolve.ts +155 -0
  365. package/src/config-utils.ts +811 -0
  366. package/src/config.ts +595 -0
  367. package/src/console-prefix.ts +99 -0
  368. package/src/cube-name.ts +69 -0
  369. package/src/cubes.ts +583 -0
  370. package/src/debug.ts +73 -0
  371. package/src/direct-log.ts +9 -0
  372. package/src/docs-sections.ts +129 -0
  373. package/src/drone-lifecycle.ts +62 -0
  374. package/src/drone-management.ts +162 -0
  375. package/src/ensure-mcp-config.ts +54 -0
  376. package/src/evict-drone.ts +82 -0
  377. package/src/gc-orphan-inboxes.ts +229 -0
  378. package/src/inbox-monitor.ts +1005 -0
  379. package/src/index.ts +1201 -0
  380. package/src/launch-all-cmd.ts +450 -0
  381. package/src/launch-all-command.ts +30 -0
  382. package/src/launch-all-deps.ts +188 -0
  383. package/src/launch-all-discovery.ts +118 -0
  384. package/src/launch-all-locks.ts +74 -0
  385. package/src/launch-gate.ts +80 -0
  386. package/src/lifecycle-log-guard.ts +144 -0
  387. package/src/list-roles-render.ts +52 -0
  388. package/src/local-log-routing.ts +106 -0
  389. package/src/local-manage-tool-result.ts +10 -0
  390. package/src/local-server-cursor.ts +188 -0
  391. package/src/log-audit.ts +207 -0
  392. package/src/log-stream.ts +1505 -0
  393. package/src/model-presets.ts +22 -0
  394. package/src/name-validator.ts +23 -0
  395. package/src/opencode-drone.ts +528 -0
  396. package/src/opencode-plugin.ts +38 -0
  397. package/src/parse-assimilate-args.ts +125 -0
  398. package/src/parse-launch-all-args.ts +100 -0
  399. package/src/postinstall-banner.ts +37 -0
  400. package/src/postinstall.ts +36 -0
  401. package/src/readiness-probe.ts +12 -0
  402. package/src/regen-format.ts +651 -0
  403. package/src/regen.ts +150 -0
  404. package/src/remote-client.ts +1379 -0
  405. package/src/reset-local-seat-cmd.ts +227 -0
  406. package/src/role-match.ts +29 -0
  407. package/src/role-resolver.ts +93 -0
  408. package/src/roster-render.ts +196 -0
  409. package/src/seat-probe.ts +115 -0
  410. package/src/seat-store.ts +333 -0
  411. package/src/seats.ts +741 -0
  412. package/src/self-path.ts +51 -0
  413. package/src/server-endpoint.ts +38 -0
  414. package/src/server-errors.ts +93 -0
  415. package/src/server-handshake.ts +783 -0
  416. package/src/server-response.ts +76 -0
  417. package/src/server-trust.ts +245 -0
  418. package/src/setup-confirm.ts +171 -0
  419. package/src/setup.ts +186 -0
  420. package/src/shell-escape.ts +22 -0
  421. package/src/spawn.ts +31 -0
  422. package/src/startup-services.ts +36 -0
  423. package/src/stream-owner.ts +686 -0
  424. package/src/stream-status.ts +337 -0
  425. package/src/sync-roles-render.ts +163 -0
  426. package/src/sync.ts +363 -0
  427. package/src/terminal-title.ts +76 -0
  428. package/src/token-store.ts +102 -0
  429. package/src/tool-manifest.ts +610 -0
  430. package/src/tool-scope.ts +110 -0
  431. package/src/types.ts +5 -0
  432. package/src/unknown-subcommand.ts +35 -0
  433. package/src/version.ts +82 -0
  434. package/src/working-repo.ts +99 -0
  435. package/src/worktree-lifecycle.ts +254 -0
  436. package/dist/auth-recovery.d.ts +0 -33
  437. package/dist/auth-recovery.js +0 -1
  438. package/dist/auth.d.ts +0 -103
  439. package/dist/auth.js +0 -39
  440. package/dist/device-auth.d.ts +0 -75
  441. package/dist/device-auth.js +0 -1
  442. package/dist/drone-address.d.ts +0 -26
  443. package/dist/drone-address.js +0 -1
  444. package/dist/get-started.d.ts +0 -20
  445. package/dist/get-started.js +0 -2
  446. package/dist/health-beat.d.ts +0 -134
  447. package/dist/health-beat.js +0 -1
  448. package/dist/role-section.d.ts +0 -110
  449. package/dist/role-section.js +0 -8
  450. package/dist/setup-action.d.ts +0 -20
  451. package/dist/setup-action.js +0 -1
  452. package/dist/stale-version-check.d.ts +0 -40
  453. package/dist/stale-version-check.js +0 -1
  454. package/dist/subscription-retry.d.ts +0 -40
  455. package/dist/subscription-retry.js +0 -1
  456. package/dist/templates.d.ts +0 -107
  457. package/dist/templates.js +0 -359
  458. package/dist/token-crypto.d.ts +0 -50
  459. package/dist/token-crypto.js +0 -1
@@ -0,0 +1,110 @@
1
+ /**
2
+ * gh#899: role-scope the NATIVE MCP tool surface to cut the per-call tool tax
3
+ * for simpler models. This is a token-surface UX / context optimization ONLY —
4
+ * it carries ZERO authorization semantics. Server enforcement uses the selected
5
+ * local client's live per-cube `read|write|manage` grant, independent of which
6
+ * tool schemas are pre-loaded. Reaching a management tool through the dispatcher
7
+ * bypasses no server check because it uses the identical CallTool path.
8
+ * Never treat this filter as a security boundary.
9
+ *
10
+ * Deferred (filtered-out) tools are never lost — they remain reachable through
11
+ * the always-present dispatcher (`borg_tool` / `borg_describe-tool`), so a
12
+ * scoped role can never dead-end.
13
+ */
14
+
15
+ /** Always-present escape hatch — reaches any borg tool regardless of scope. */
16
+ export const DISPATCHER_TOOLS = ['borg_tool', 'borg_describe-tool'] as const;
17
+
18
+ /** Every role needs these; pre-loaded natively for all roles. */
19
+ export const UNIVERSAL_TOOLS = [
20
+ 'borg_regen',
21
+ 'borg_log',
22
+ 'borg_read-log',
23
+ 'borg_roster',
24
+ 'borg_stream-status',
25
+ 'borg_whoami',
26
+ 'borg_ack',
27
+ 'borg_version',
28
+ 'borg_cube',
29
+ 'borg_role',
30
+ 'borg_role-rationale',
31
+ 'borg_assimilate',
32
+ 'borg_playbook', // gh#912: on-demand operating-playbook chapter — every role needs it
33
+ 'borg_docs', // gh#docs-site B: in-product docs lookup — every role can answer "how does borgmcp work"
34
+ ...DISPATCHER_TOOLS,
35
+ ] as const;
36
+
37
+ /** Cube/role/drone management — native for management seats, deferred for workers. */
38
+ export const MANAGEMENT_TOOLS = [
39
+ 'borg_create-cube',
40
+ 'borg_update-cube',
41
+ 'borg_delete-cube',
42
+ 'borg_create-role',
43
+ 'borg_update-role',
44
+ 'borg_delete-role',
45
+ 'borg_patch-role-section',
46
+ 'borg_patch-taxonomy-class',
47
+ 'borg_reassign-drone',
48
+ 'borg_evict-drone',
49
+ 'borg_sync-roles',
50
+ 'borg_apply-template',
51
+ 'borg_list-cubes',
52
+ 'borg_list-drones',
53
+ 'borg_list-roles',
54
+ 'borg_list-templates',
55
+ 'borg_remove-decision',
56
+ ] as const;
57
+
58
+ /**
59
+ * Highest-stakes subset of the management set — filtered from a worker's native
60
+ * surface for context economy only. NOT an auth list: server access is enforced
61
+ * by live per-client cube grants (`read|write|manage`) and is independent
62
+ * of this client-side UX filter. Role labels never grant server permission.
63
+ * Named here only to focus reviewer attention.
64
+ */
65
+ export const AUTH_SENSITIVE_TOOLS = [
66
+ 'borg_evict-drone',
67
+ 'borg_delete-cube',
68
+ 'borg_delete-role',
69
+ 'borg_reassign-drone',
70
+ ] as const;
71
+
72
+ export interface RoleScope {
73
+ /** The drone's role name, as persisted at assimilate. Absent → role unknown. */
74
+ roleName?: string | null;
75
+ roleClass?: 'queen' | 'worker' | null;
76
+ isHumanSeat?: boolean | null;
77
+ }
78
+
79
+ /** Management seats (human-seat roles + queen-class) get the full surface. */
80
+ export function isManagementSeat(scope: RoleScope): boolean {
81
+ return scope.isHumanSeat === true || scope.roleClass === 'queen';
82
+ }
83
+
84
+ /**
85
+ * Names to HIDE from the native surface for this scope. Empty (hide nothing)
86
+ * when the role is unknown (no roleName — old cubes.json entry or pre-assimilate)
87
+ * or the caller is a management seat. Only a known worker role defers the
88
+ * management + billing sets. Fail-safe: anything not explicitly in a deferred
89
+ * set stays native.
90
+ */
91
+ export function deferredToolNames(scope: RoleScope | null): Set<string> {
92
+ if (!scope || !scope.roleName) return new Set(); // role unknown → full set
93
+ if (isManagementSeat(scope)) return new Set(); // management seat → full set
94
+ return new Set<string>([...MANAGEMENT_TOOLS]); // worker → defer
95
+ }
96
+
97
+ /**
98
+ * Filter a tool-definition array down to the native surface for this scope.
99
+ * Tools whose name is in the deferred set are removed; everything else
100
+ * (universal, dispatcher, unmapped) stays. The deferred tools remain reachable
101
+ * via the dispatcher.
102
+ */
103
+ export function filterToolsForRole<T extends { name: string }>(
104
+ tools: T[],
105
+ scope: RoleScope | null
106
+ ): T[] {
107
+ const hidden = deferredToolNames(scope);
108
+ if (hidden.size === 0) return tools;
109
+ return tools.filter((t) => !hidden.has(t.name));
110
+ }
package/src/types.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Type definitions for Borg MCP Client
3
+ */
4
+
5
+ export {};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * gh#911: top-level CLI footgun guard.
3
+ *
4
+ * `borg <unknown>` (e.g. `borg evict-drone X`) used to fall through the
5
+ * subcommand router, get ignored by parseCliFlag (which validates only
6
+ * `--cli`), and silently LAUNCH a Claude Code session with the typo'd word as
7
+ * its prompt — surprising + wasteful, and the trailing args were dropped.
8
+ *
9
+ * This pure predicate lets claude.ts reject an unknown NON-FLAG argv[2] before
10
+ * the default launch. Bare `borg` (no argv[2]) and recognized flags
11
+ * (`--cli`, `--remote`, agent passthrough, …) still fall through to launch.
12
+ */
13
+
14
+ /** The subcommands the claude.ts router dispatches on (lines 107-176). */
15
+ export const KNOWN_SUBCOMMANDS = [
16
+ 'setup',
17
+ 'assimilate',
18
+ 'reset-local-seat',
19
+ 'spawn',
20
+ 'sync',
21
+ 'cleanup',
22
+ 'launch-all',
23
+ ] as const;
24
+
25
+ /**
26
+ * Returns the offending command string when argv[2] is an unknown non-flag
27
+ * positional (caller should error + exit 1), or null when it's bare `borg`,
28
+ * a flag, or a known subcommand (caller falls through to existing handling).
29
+ */
30
+ export function unknownSubcommand(argv2: string | undefined): string | null {
31
+ if (argv2 === undefined) return null; // bare `borg`
32
+ if (argv2.startsWith('-')) return null; // a flag → handled downstream
33
+ if ((KNOWN_SUBCOMMANDS as readonly string[]).includes(argv2)) return null;
34
+ return argv2; // unknown non-flag positional → footgun
35
+ }
package/src/version.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Runtime version reader.
3
+ *
4
+ * Single source of truth for the borgmcp client version — read at runtime
5
+ * from `package.json` relative to `import.meta.url`, NOT hardcoded.
6
+ *
7
+ * Consumers:
8
+ * - `index.ts` — passed into the MCP `Server({ name, version })`
9
+ * constructor so Claude Code's `/mcp` view shows the real version
10
+ * instead of the long-standing hardcoded "0.1.0".
11
+ * - `claude.ts` / `setup.ts` / `regen.ts` / `log-audit.ts` — each
12
+ * binary supports a `--version` flag that prints `borgmcp X.Y.Z`
13
+ * and exits 0 before any side-effecting work begins.
14
+ *
15
+ * Implementation notes:
16
+ * - Uses `readFileSync` on the resolved path relative to this module's
17
+ * `import.meta.url`. The compiled `dist/version.js` sits one level
18
+ * above `package.json` at the package root, so `../package.json` is
19
+ * the relative resolution. This works under both `node dist/...`
20
+ * and `npm run start`; the path resolution is independent of CWD.
21
+ * - Result is cached at module-eval time. The package.json is part of
22
+ * the published tarball and immutable for any given install.
23
+ * - Falls back to `'unknown'` if the read fails (corrupted install,
24
+ * someone deleted package.json, etc.) — never throws, so a fresh
25
+ * `--version` invocation can't kill a CLI launch.
26
+ */
27
+
28
+ import { readFileSync } from 'node:fs';
29
+ import { fileURLToPath } from 'node:url';
30
+ import { dirname, join } from 'node:path';
31
+
32
+ function readPackageVersion(): string {
33
+ try {
34
+ const here = fileURLToPath(import.meta.url);
35
+ const pkgPath = join(dirname(here), '..', 'package.json');
36
+ const raw = readFileSync(pkgPath, 'utf-8');
37
+ const parsed = JSON.parse(raw);
38
+ if (typeof parsed.version === 'string' && parsed.version.length > 0) {
39
+ return parsed.version;
40
+ }
41
+ return 'unknown';
42
+ } catch {
43
+ return 'unknown';
44
+ }
45
+ }
46
+
47
+ const VERSION = readPackageVersion();
48
+
49
+ /**
50
+ * Return the installed borgmcp version (the same string as
51
+ * `client/package.json`'s `version` field). Cached at module load.
52
+ */
53
+ export function getPackageVersion(): string {
54
+ return VERSION;
55
+ }
56
+
57
+ /**
58
+ * Standard `--version` handler — call near the top of any CLI entry
59
+ * point. If `process.argv` contains `--version` or `-v`, prints
60
+ * `borgmcp X.Y.Z` to stdout and exits 0. Otherwise returns silently
61
+ * so the caller can continue with normal CLI work.
62
+ *
63
+ * Examples:
64
+ * - `borg --version` → "borgmcp 0.6.0"
65
+ * - `borg-mcp -v` → "borgmcp 0.6.0"
66
+ * - `borg assimilate` → continues to assimilation flow
67
+ * - `borg-setup` → continues to interactive setup wizard
68
+ */
69
+ /**
70
+ * gh#285: read the on-disk package.json version fresh (not cached).
71
+ * Used by the regen handler to detect post-upgrade version mismatch.
72
+ */
73
+ export function getOnDiskVersion(): string {
74
+ return readPackageVersion();
75
+ }
76
+
77
+ export function handleVersionFlag(): void {
78
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
79
+ process.stdout.write(`borgmcp ${VERSION}\n`);
80
+ process.exit(0);
81
+ }
82
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Resolve the repository identity for the directory hosting this MCP child.
3
+ *
4
+ * The report carries only a canonical repository identity (`host/org/repo`)
5
+ * and its derived name. Local filesystem paths and raw remote URLs are never
6
+ * included in lifecycle metadata.
7
+ */
8
+
9
+ import { spawnSync } from 'node:child_process';
10
+ export interface WorkingRepo {
11
+ name: string | null;
12
+ /** Canonical host/path identity, never a raw Git remote URL. */
13
+ origin: string | null;
14
+ }
15
+
16
+ export interface WorkingRepoDeps {
17
+ runGit?: (
18
+ cwd: string,
19
+ args: string[]
20
+ ) => { status: number | null; stdout?: string | null };
21
+ }
22
+
23
+ function defaultRunGit(cwd: string, args: string[]) {
24
+ const result = spawnSync('git', args, { cwd, encoding: 'utf-8' });
25
+ return { status: result.status, stdout: result.stdout };
26
+ }
27
+
28
+ function trimmed(value: string | null | undefined): string | null {
29
+ const normalized = value?.trim();
30
+ return normalized ? normalized : null;
31
+ }
32
+
33
+ function nameFromIdentity(identity: string): string | null {
34
+ const lastPathSegment = identity.replace(/\/$/, '').split('/').pop();
35
+ const name = lastPathSegment?.replace(/\.git$/i, '').trim();
36
+ return name || null;
37
+ }
38
+
39
+ /**
40
+ * Convert a Git remote to a non-secret `host/org/repo` identity.
41
+ *
42
+ * URL userinfo, query strings, fragments, scheme, and SCP-style user prefixes
43
+ * are deliberately discarded. Inputs that cannot identify a host and path are
44
+ * treated as unreportable rather than forwarded verbatim.
45
+ */
46
+ export function canonicalizeWorkingRepoIdentity(origin: string): string | null {
47
+ const raw = origin.trim();
48
+ if (!raw) return null;
49
+ // Remote clients send this canonical form on subsequent lifecycle calls.
50
+ const canonical = raw.match(/^([A-Za-z0-9.-]+)\/([^?#\s]+)$/);
51
+ if (canonical) {
52
+ const host = canonical[1].toLowerCase();
53
+ const path = canonical[2].replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
54
+ return host && path ? `${host}/${path}` : null;
55
+ }
56
+ try {
57
+ const url = new URL(origin);
58
+ if (!['http:', 'https:', 'ssh:', 'git:'].includes(url.protocol)) return null;
59
+ const path = url.pathname.replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
60
+ return url.hostname && path ? `${url.hostname.toLowerCase()}/${path}` : null;
61
+ } catch {
62
+ // SCP-style SSH remote: discard its optional user prefix and URL-like
63
+ // query/fragment suffix before accepting only host + repository path.
64
+ const match = raw.match(/^(?:[^@\s/:]+@)?([A-Za-z0-9.-]+):\/?([^?#\s]+)(?:[?#].*)?$/);
65
+ if (match) {
66
+ const host = match[1].toLowerCase();
67
+ const path = match[2].replace(/^\/+|\/+$/g, '').replace(/\.git$/i, '');
68
+ return host && path ? `${host}/${path}` : null;
69
+ }
70
+ return null;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Return a reportable identity for the caller's current directory.
76
+ *
77
+ * `git rev-parse --show-toplevel` supports both ordinary clones and linked
78
+ * worktrees. Any git failure reports no repository identity instead of
79
+ * disclosing the caller's local filesystem layout.
80
+ */
81
+ export function resolveWorkingRepo(
82
+ cwd: string = process.cwd(),
83
+ deps: WorkingRepoDeps = {}
84
+ ): WorkingRepo {
85
+ const runGit = deps.runGit ?? defaultRunGit;
86
+ const rootResult = runGit(cwd, ['rev-parse', '--show-toplevel']);
87
+ const root = rootResult.status === 0 ? trimmed(rootResult.stdout) : null;
88
+ if (!root) {
89
+ return { name: null, origin: null };
90
+ }
91
+
92
+ const originResult = runGit(cwd, ['config', '--get', 'remote.origin.url']);
93
+ const originRaw = originResult.status === 0 ? trimmed(originResult.stdout) : null;
94
+ const origin = originRaw ? canonicalizeWorkingRepoIdentity(originRaw) : null;
95
+ return {
96
+ name: origin ? nameFromIdentity(origin) : null,
97
+ origin,
98
+ };
99
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * gh#33 — worktree lifecycle as product behavior.
3
+ *
4
+ * Pure git-decision helpers behind an injected `runSync` seam (matching
5
+ * the `AssimilateDeps.runSync` shape), so every branch is unit-testable
6
+ * without a live repo. This module DECIDES + emits git command sequences;
7
+ * it never launches agents and never touches the cube API.
8
+ *
9
+ * Design spec: docs/superpowers/specs/2026-05-29-worktree-lifecycle-design.md
10
+ * Q-resolutions baked in (SPEC-APPROVED 3a80412d):
11
+ * Q1 branch naming — `wt-<suffix>` prefix-stripped, full-basename fallback.
12
+ * Q2 idle-sync — ff-only, clean-gated; never merge/rebase; never over dirty.
13
+ * Q3 post-merge — auto-return to wt-<basename>; ANNOUNCE the prunable
14
+ * merged branch, prune only when explicitly requested.
15
+ * Q4 uniform — no primary-worktree carve-out; main is never a working branch.
16
+ */
17
+
18
+ import { join } from 'node:path';
19
+
20
+ /** Injected subprocess runner — matches AssimilateDeps.runSync. */
21
+ export type RunSync = (
22
+ cmd: string,
23
+ args: string[],
24
+ cwd?: string
25
+ ) => { status: number | null; stdout: string; stderr: string };
26
+
27
+ /**
28
+ * Per-worktree branch name (Q1). Strips the repo basename prefix from the
29
+ * worktree dir basename for readability (`borg-mcp-codex-builder` ->
30
+ * `wt-codex-builder`); falls back to the full dir basename when there is
31
+ * no shared prefix (`myrepo-feature` under repo `otherrepo` ->
32
+ * `wt-myrepo-feature`).
33
+ */
34
+ export function perWorktreeBranchName(worktreeBasename: string, repoBasename: string): string {
35
+ const prefix = `${repoBasename}-`;
36
+ const suffix = worktreeBasename.startsWith(prefix)
37
+ ? worktreeBasename.slice(prefix.length)
38
+ : worktreeBasename;
39
+ return `wt-${suffix}`;
40
+ }
41
+
42
+ /**
43
+ * gh#556 Part 1 — the home for NEW drone worktrees: `<homeDir>/.borg/worktrees`.
44
+ * (`~/.borg` is the established borg home — it already holds the encrypted
45
+ * credentials file, see config.ts.)
46
+ */
47
+ export function worktreesHome(homeDir: string): string {
48
+ return join(homeDir, '.borg', 'worktrees');
49
+ }
50
+
51
+ /**
52
+ * gh#556 Part 1 — where a NEW drone worktree lives:
53
+ * `<homeDir>/.borg/worktrees/<repoBase>/<suffix>` (collision variant `<suffix>-<n>`
54
+ * for n>=2; the caller loops n until the path is free).
55
+ *
56
+ * Pure (homeDir injected) so the path scheme + collision dedup + containment are
57
+ * unit-testable without touching $HOME or spawning git.
58
+ *
59
+ * Path-safety / no-traversal: `suffix` is validated upstream BEFORE it reaches here —
60
+ * `--worktree` via validateName (NAME_RE excludes `.`/`/`) or the role default via
61
+ * roleSlug (strips everything but `[a-z0-9-]`); `repoBase` is a single `basename(...)`
62
+ * component. So the result is always CONTAINED under `worktreesHome(homeDir)`.
63
+ * As defense-in-depth this throws on an EMPTY suffix — an empty leaf would let
64
+ * `join` collapse the path up to the repo-level dir (the degenerate-path bug); the
65
+ * caller also guards empty before calling, fail-loud.
66
+ */
67
+ export function computeWorktreePath(
68
+ homeDir: string,
69
+ repoBase: string,
70
+ suffix: string,
71
+ n?: number
72
+ ): string {
73
+ if (suffix.length === 0) {
74
+ throw new Error('computeWorktreePath: suffix must be non-empty (empty leaf would collapse the path to the repo-level dir)');
75
+ }
76
+ const leaf = n !== undefined && n >= 2 ? `${suffix}-${n}` : suffix;
77
+ return join(worktreesHome(homeDir), repoBase, leaf);
78
+ }
79
+
80
+ /** True iff the working tree is clean (`git status --porcelain` empty). */
81
+ export function isCleanTree(runSync: RunSync, cwd: string): boolean {
82
+ const r = runSync('git', ['status', '--porcelain'], cwd);
83
+ return r.status === 0 && r.stdout.trim() === '';
84
+ }
85
+
86
+ export interface DirtyClassification {
87
+ staged: string[];
88
+ unstaged: string[];
89
+ untracked: string[];
90
+ /** Subset that are local-only config (e.g. `.claude/...`) — likely safe to set aside. */
91
+ localConfig: string[];
92
+ }
93
+
94
+ const LOCAL_CONFIG_RE = /^\.claude\//;
95
+
96
+ /**
97
+ * Classify a dirty tree into staged / unstaged / untracked buckets, and
98
+ * flag local-config files separately. The STAGED bucket is load-bearing:
99
+ * the live UNBLOCK case (b15894be) had a *staged* leftover diff that
100
+ * blocked `pull --ff-only`, which an unstaged-only check would miss.
101
+ */
102
+ export function classifyDirty(runSync: RunSync, cwd: string): DirtyClassification {
103
+ const r = runSync('git', ['status', '--porcelain'], cwd);
104
+ const out: DirtyClassification = { staged: [], unstaged: [], untracked: [], localConfig: [] };
105
+ if (r.status !== 0) return out;
106
+ for (const line of r.stdout.split('\n')) {
107
+ if (!line.trim()) continue;
108
+ const path = line.slice(3);
109
+ if (line.startsWith('??')) {
110
+ out.untracked.push(path);
111
+ } else {
112
+ const x = line[0]; // staged (index) column
113
+ const y = line[1]; // unstaged (work-tree) column
114
+ if (x !== ' ' && x !== '?') out.staged.push(path);
115
+ if (y !== ' ' && y !== '?') out.unstaged.push(path);
116
+ }
117
+ if (LOCAL_CONFIG_RE.test(path)) out.localConfig.push(path);
118
+ }
119
+ return out;
120
+ }
121
+
122
+ /** True iff `branch` is an ancestor of `ref` — i.e. a clean fast-forward target. */
123
+ export function isFastForward(runSync: RunSync, cwd: string, branch: string, ref: string): boolean {
124
+ return runSync('git', ['merge-base', '--is-ancestor', branch, ref], cwd).status === 0;
125
+ }
126
+
127
+ /** True iff `branch`'s tip is an ancestor of `ref` — i.e. fully merged into it. */
128
+ export function isMerged(runSync: RunSync, cwd: string, branch: string, ref: string): boolean {
129
+ return runSync('git', ['merge-base', '--is-ancestor', branch, ref], cwd).status === 0;
130
+ }
131
+
132
+ export interface SyncResult {
133
+ action: 'fast-forwarded' | 'already-current' | 'skipped-dirty' | 'skipped-diverged';
134
+ message?: string;
135
+ }
136
+
137
+ /**
138
+ * Idle-sync the current per-worktree branch to `ref` (Q2). NEVER discards
139
+ * work: dirty -> skipped-dirty (no mutation). Only fast-forwards (no
140
+ * merge/rebase): diverged -> skipped-diverged. The caller fetches first.
141
+ *
142
+ * `already-current` when the branch tip already equals `ref` (the common
143
+ * no-op case on every launch).
144
+ */
145
+ export function syncWorktree(runSync: RunSync, cwd: string, branch: string, ref: string): SyncResult {
146
+ if (!isCleanTree(runSync, cwd)) {
147
+ return {
148
+ action: 'skipped-dirty',
149
+ message: 'uncommitted changes present; sync skipped (nothing discarded)',
150
+ };
151
+ }
152
+ if (!isFastForward(runSync, cwd, branch, ref)) {
153
+ return {
154
+ action: 'skipped-diverged',
155
+ message: `${branch} has diverged from ${ref}; resolve manually (no auto-merge/rebase)`,
156
+ };
157
+ }
158
+ // Already at ref? merge --ff-only is a no-op but we report it distinctly
159
+ // so callers can stay quiet on the common case.
160
+ const ahead = runSync('git', ['rev-list', '--count', `${branch}..${ref}`], cwd);
161
+ if (ahead.status === 0 && ahead.stdout.trim() === '0') {
162
+ return { action: 'already-current' };
163
+ }
164
+ const ff = runSync('git', ['merge', '--ff-only', ref], cwd);
165
+ if (ff.status !== 0) {
166
+ return { action: 'skipped-diverged', message: 'ff-only merge unexpectedly failed' };
167
+ }
168
+ return { action: 'fast-forwarded' };
169
+ }
170
+
171
+ export interface AdoptResult {
172
+ action: 'adopted' | 'blocked-unmerged' | 'blocked-target-unmerged' | 'skipped-dirty';
173
+ message?: string;
174
+ }
175
+
176
+ /** True iff a local branch named `branch` already exists. */
177
+ export function localBranchExists(runSync: RunSync, cwd: string, branch: string): boolean {
178
+ return runSync('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], cwd).status === 0;
179
+ }
180
+
181
+ /**
182
+ * Migration (Q4/Q5/§4.5): bring a detached/stale worktree onto
183
+ * `wt-<basename>` at `ref`. Idempotent: re-running on an already-adopted
184
+ * clean worktree is a lossless reset to `ref`. Never discards:
185
+ * - dirty work tree -> skipped-dirty (surface)
186
+ * - current HEAD unmerged -> blocked-unmerged (surface)
187
+ * - TARGET `branch` exists with commits not on `ref` -> blocked-target-
188
+ * unmerged (surface). This is load-bearing: the switch uses `-C`
189
+ * (force-create/reset), which would ORPHAN commits on a pre-existing
190
+ * `wt-` branch. The HEAD-merged check alone misses this when the
191
+ * target branch != HEAD (e.g. on `main` while a prior `wt-x` holds
192
+ * committed-but-unmerged work). gh#33 CR-v2 blocker 078d1630.
193
+ */
194
+ export function adoptWorktree(runSync: RunSync, cwd: string, branch: string, ref: string): AdoptResult {
195
+ if (!isCleanTree(runSync, cwd)) {
196
+ return {
197
+ action: 'skipped-dirty',
198
+ message: 'uncommitted changes present; not switching (nothing discarded)',
199
+ };
200
+ }
201
+ if (!isMerged(runSync, cwd, 'HEAD', ref)) {
202
+ return {
203
+ action: 'blocked-unmerged',
204
+ message: `current HEAD has commits not on ${ref}; commit/push or set aside before adopting`,
205
+ };
206
+ }
207
+ // Guard the TARGET branch ref before the `switch -C` force-reset. If
208
+ // `branch` already exists and is NOT an ancestor of `ref`, it carries
209
+ // committed-unmerged work that `-C` would discard — block instead.
210
+ // (Absent, or an ancestor of `ref` = a clean reset target → proceed.)
211
+ if (localBranchExists(runSync, cwd, branch) && !isMerged(runSync, cwd, branch, ref)) {
212
+ return {
213
+ action: 'blocked-target-unmerged',
214
+ message: `branch ${branch} exists with commits not on ${ref}; resolve before adopting (a force-switch would discard them)`,
215
+ };
216
+ }
217
+ runSync('git', ['switch', '-C', branch, ref], cwd);
218
+ return { action: 'adopted' };
219
+ }
220
+
221
+ export interface CleanupResult {
222
+ action: 'pruned' | 'announced' | 'not-merged';
223
+ /** The feature branch this result concerns (for the announce message). */
224
+ branch?: string;
225
+ message?: string;
226
+ }
227
+
228
+ /**
229
+ * Post-merge cleanup (Q3): when `feature` is fully merged into `ref`,
230
+ * either ANNOUNCE it as prunable (default) or actually prune it with the
231
+ * safe `git branch -d` (which itself refuses to delete an unmerged
232
+ * branch — defense in depth against a stale local ref). Unmerged ->
233
+ * not-merged (never touched).
234
+ */
235
+ export function cleanupMerged(
236
+ runSync: RunSync,
237
+ cwd: string,
238
+ feature: string,
239
+ ref: string,
240
+ opts: { prune: boolean } = { prune: false }
241
+ ): CleanupResult {
242
+ if (!isMerged(runSync, cwd, feature, ref)) {
243
+ return { action: 'not-merged', branch: feature };
244
+ }
245
+ if (!opts.prune) {
246
+ return {
247
+ action: 'announced',
248
+ branch: feature,
249
+ message: `${feature} is merged into ${ref} and can be pruned: \`git branch -d ${feature}\` (or re-run with --prune)`,
250
+ };
251
+ }
252
+ runSync('git', ['branch', '-d', feature], cwd);
253
+ return { action: 'pruned', branch: feature };
254
+ }
@@ -1,33 +0,0 @@
1
- /**
2
- * gh#780 companion fix: classify auth-class failures into the RIGHT
3
- * recovery advice for an in-session agent.
4
- *
5
- * Root-cause context: the pre-gh#780 funnel answered every auth failure by
6
- * pointing the user at `borg assimilate` (the wrong remedy; gh#794 now also
7
- * differentiates a dead saved login from never-signed-in, both → `borg setup`).
8
- * An in-session agent's only reachable assimilate is the borg_assimilate MCP tool, which
9
- * minted a brand-new drones row — so each auth blip spawned an orphan seat
10
- * (the gh#780 class). Neither failure mode is fixable by assimilating:
11
- * assimilate rides the same broken Bearer token.
12
- *
13
- * - REVOKED / re-consent required (RefreshTokenInvalidError surfaced as
14
- * "Authentication required/expired"): only `borg setup` in a terminal
15
- * re-consents.
16
- * - TRANSIENT refresh failure (RefreshTransientError, "Failed to
17
- * refresh"): keychain intact by design (gh#34) — the next call
18
- * retries; waiting IS the recovery.
19
- */
20
- interface AuthErrorLike {
21
- name?: string;
22
- message?: string;
23
- }
24
- /**
25
- * Returns the agent-facing recovery message for an auth-class error, or
26
- * null when the error is not auth-related (caller falls through to its
27
- * generic error rendering). Transient classification wins over the generic
28
- * auth-text match — a transient error's message may also mention
29
- * "Authentication".
30
- */
31
- export declare function authRecoveryMessage(error: AuthErrorLike): string | null;
32
- export {};
33
- //# sourceMappingURL=auth-recovery.d.ts.map
@@ -1 +0,0 @@
1
- function t(n){const e=n.message??"";return n.name==="RefreshTransientError"||e.includes("Failed to refresh")?"\u25FC Transient auth-refresh failure (network/Google hiccup). Your session is intact and auth self-recovers \u2014 retry the tool call in a moment. Do NOT re-assimilate: borg_assimilate cannot fix auth and would mint a duplicate drone seat.":e.includes("Authentication required")||e.includes("Authentication expired")?"\u25FC Authentication expired \u2014 re-consent needed. Run `borg setup` in a terminal to sign in again. Do NOT re-assimilate: borg_assimilate cannot fix auth and would mint a duplicate drone seat.":null}export{t as authRecoveryMessage};