@vantran-se/openchamber-web 2.1.1 → 2.2.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 (442) hide show
  1. package/README.md +32 -24
  2. package/bin/cli.js +1 -3
  3. package/bin/cli.test.js +40 -16
  4. package/bin/lib/DOCUMENTATION.md +6 -5
  5. package/bin/lib/cli-args.js +6 -1
  6. package/bin/lib/cli-args.test.js +34 -0
  7. package/bin/lib/cli-lifecycle.js +0 -1
  8. package/bin/lib/cli-network.js +8 -2
  9. package/bin/lib/cli-process.js +1 -1
  10. package/bin/lib/cli-settings-accessors.test.js +21 -13
  11. package/bin/lib/cli-startup.js +17 -11
  12. package/bin/lib/cli-startup.test.js +92 -2
  13. package/bin/lib/commands-connect-url.js +8 -1
  14. package/bin/lib/commands-schedule.js +8 -2
  15. package/bin/lib/commands-serve.js +15 -21
  16. package/bin/lib/commands-update.js +12 -24
  17. package/bin/lib/commands-update.test.js +14 -46
  18. package/dist/assets/DiffView-Cv9MSXl1.js +4 -0
  19. package/dist/assets/FilePreviewCommentMenu-D4kLseHj.js +4 -0
  20. package/dist/assets/FilesView-BVoD8I2x.js +14 -0
  21. package/dist/assets/{GitView-DzuzhIPx.js → GitView-Bzb8p-ew.js} +4 -4
  22. package/dist/assets/GuestSurfacePane-Crdbvsxo.js +1 -0
  23. package/dist/assets/InProgressOperationBanner-CwMjNQgk.js +2 -0
  24. package/dist/assets/{JsonTreeView-B_EPRjzy.js → JsonTreeView-DRKHZfsn.js} +1 -1
  25. package/dist/assets/MarkdownImageGallery-DZtqT1Zn.js +1 -0
  26. package/dist/assets/MarkdownRendererImpl-CqG7HCNd.js +10 -0
  27. package/dist/assets/OnboardingScreen-DMIxwYK5.js +1 -0
  28. package/dist/assets/{PatchDiff-6yh1csza.js → PatchDiff-CUcCg1jk.js} +1 -1
  29. package/dist/assets/PierreDiffViewer-CnnR-dnD.js +228 -0
  30. package/dist/assets/PlanView-RCBadcY7.js +5 -0
  31. package/dist/assets/SettingsView-CjBjZFRO.js +30 -0
  32. package/dist/assets/SettingsWindow-DyM7QuhI.js +1 -0
  33. package/dist/assets/SubmoduleDiffSummary-BWIfgMED.js +1 -0
  34. package/dist/assets/ToolOutputDialog-Q0P3FuAi.js +15 -0
  35. package/dist/assets/ToolPartDiffPreview-p7qR9FpU.js +8 -0
  36. package/dist/assets/{VirtualizedFileDiff-BTJVRkyo.js → VirtualizedFileDiff-CFpjL8Ns.js} +3 -3
  37. package/dist/assets/{Virtualizer-xCEYz34p.js → Virtualizer-C0IsclZn.js} +1 -1
  38. package/dist/assets/WalkthroughView-DI31zD02.js +4 -0
  39. package/dist/assets/appThemeRegistry-B9gkFq9i.js +1 -0
  40. package/dist/assets/{computeVirtualFileMetrics-CmPe29DM.js → computeVirtualFileMetrics-C2WM6JC2.js} +2 -2
  41. package/dist/assets/de-YKfk5MmC.js +14 -0
  42. package/dist/assets/es-_gwT0kr-.js +20 -0
  43. package/dist/assets/fileTreeStatus-D438yi8M.js +1 -0
  44. package/dist/assets/fr-DqzvXmIm.js +18 -0
  45. package/dist/assets/index-BLaHKdtx.js +1 -0
  46. package/dist/assets/index-BkMXRrVQ.css +1 -0
  47. package/dist/assets/index-DxNa7xYB.js +65 -0
  48. package/dist/assets/initializeSharedPreferences-Bck5EAWP.js +1 -0
  49. package/dist/assets/{isDiffPlainText-CQmDCnEv.js → isDiffPlainText-Dgbw8VKk.js} +1 -1
  50. package/dist/assets/{isFilePlainText-CYBUzW4I.js → isFilePlainText-_ZS5UYRj.js} +1 -1
  51. package/dist/assets/ja-B-GFYhQE.js +20 -0
  52. package/dist/assets/ko-mzksfqDC.js +20 -0
  53. package/dist/assets/main-C5Xx8dn_.js +2 -0
  54. package/dist/assets/main-CfBSQM1H.js +990 -0
  55. package/dist/assets/markdownCore-BmWwW1ll.js +7 -0
  56. package/dist/assets/miniChat-mnKwy4vQ.js +2 -0
  57. package/dist/assets/mobile-DeEQd0wZ.js +2 -0
  58. package/dist/assets/nl-CWQr6NSq.js +20 -0
  59. package/dist/assets/{parseDiffFromFile-Cy0PNxun.js → parseDiffFromFile-BUgn-T9p.js} +2 -2
  60. package/dist/assets/patchFileDiff-Dt8LhgBh.js +13 -0
  61. package/dist/assets/pl-UmRcDUX2.js +20 -0
  62. package/dist/assets/pt-BR-BSLybvVF.js +20 -0
  63. package/dist/assets/relocateRestoredSession-B5E9xb3T.js +1 -0
  64. package/dist/assets/renderElectronMiniChatApp-C0a9A5_J.js +2 -0
  65. package/dist/assets/renderMobileApp-BNncg3Pk.js +25 -0
  66. package/dist/assets/runtimeEndpointReset-CZ5aafpl.js +62 -0
  67. package/dist/assets/{selectionMarkdown-TkYk5h9l.js → selectionMarkdown-CUN3wUld.js} +9 -9
  68. package/dist/assets/{sessionOwnership-Bs1xbRfB.js → sessionOwnership-BSuxCEhb.js} +1 -1
  69. package/dist/assets/shikiHighlight-DHDx-6eM.js +1 -0
  70. package/dist/assets/space-apply-DM_2pRW7.js +1 -0
  71. package/dist/assets/space-close-Bpomt8z0.js +1 -0
  72. package/dist/assets/{surface-B6n7wJtt.js → surface-C-jQaGnL.js} +1 -1
  73. package/dist/assets/{textMateThemeFromAppTheme-CDlKffWz.js → textMateThemeFromAppTheme-DlsTphbY.js} +1 -1
  74. package/dist/assets/tr-gaTrX_HK.js +11 -0
  75. package/dist/assets/uk-CnJF6ykl.js +20 -0
  76. package/dist/assets/{useAppFontEffects-DCqCVGlT.js → useAppFontEffects-DGlvOqP_.js} +194 -233
  77. package/dist/assets/useInlineCommentController-kMN4Wg_D.js +2 -0
  78. package/dist/assets/{useRootScrollLock-BowwS8kq.js → useRootScrollLock-DMPpvpNP.js} +2 -2
  79. package/dist/assets/{vendor-base-ui-DX96QhTP.js → vendor-base-ui-Det7U81h.js} +1 -1
  80. package/dist/assets/{vendor-codemirror-autocomplete-DW1FQz9_.js → vendor-codemirror-autocomplete-BSHQHbKu.js} +1 -1
  81. package/dist/assets/{vendor-codemirror-commands-DpmhalwN.js → vendor-codemirror-commands-C4G_dPWe.js} +1 -1
  82. package/dist/assets/{vendor-codemirror-lang-angular-BqExbN1M.js → vendor-codemirror-lang-angular-B6LbBGHB.js} +1 -1
  83. package/dist/assets/{vendor-codemirror-lang-cpp-DZA36HfS.js → vendor-codemirror-lang-cpp-BWWXBx7W.js} +1 -1
  84. package/dist/assets/{vendor-codemirror-lang-css-B07blnnZ.js → vendor-codemirror-lang-css-Cayfb29q.js} +1 -1
  85. package/dist/assets/{vendor-codemirror-lang-go-BM8K9D1-.js → vendor-codemirror-lang-go-BoKlCNKw.js} +1 -1
  86. package/dist/assets/{vendor-codemirror-lang-html-DaH9adfE.js → vendor-codemirror-lang-html-BQWerHsJ.js} +1 -1
  87. package/dist/assets/{vendor-codemirror-lang-java-CsdC0viz.js → vendor-codemirror-lang-java-DWX7Fq_K.js} +1 -1
  88. package/dist/assets/{vendor-codemirror-lang-javascript-CpGYXGuo.js → vendor-codemirror-lang-javascript-DbpEcZM0.js} +2 -2
  89. package/dist/assets/{vendor-codemirror-lang-jinja-BBeGzXw5.js → vendor-codemirror-lang-jinja-D1JIJRA7.js} +1 -1
  90. package/dist/assets/{vendor-codemirror-lang-json-58T70Hqs.js → vendor-codemirror-lang-json-BzgckzmB.js} +1 -1
  91. package/dist/assets/{vendor-codemirror-lang-less-BI-xCA59.js → vendor-codemirror-lang-less-B9EpDXFi.js} +1 -1
  92. package/dist/assets/{vendor-codemirror-lang-liquid-zEcMa-zy.js → vendor-codemirror-lang-liquid-9U7kRbSf.js} +1 -1
  93. package/dist/assets/{vendor-codemirror-lang-markdown-BLKnlLP4.js → vendor-codemirror-lang-markdown-D2TEBIvZ.js} +1 -1
  94. package/dist/assets/{vendor-codemirror-lang-php-9f35M83-.js → vendor-codemirror-lang-php-BT6sDVzE.js} +1 -1
  95. package/dist/assets/vendor-codemirror-lang-python-jAMtYiA9.js +3 -0
  96. package/dist/assets/{vendor-codemirror-lang-rust-MNDL_jZQ.js → vendor-codemirror-lang-rust-CVO56EiL.js} +1 -1
  97. package/dist/assets/{vendor-codemirror-lang-sass-p9epsSNm.js → vendor-codemirror-lang-sass-DZlOoUWu.js} +1 -1
  98. package/dist/assets/{vendor-codemirror-lang-sql-Bqzh0EXX.js → vendor-codemirror-lang-sql-fZ6t1DPw.js} +1 -1
  99. package/dist/assets/{vendor-codemirror-lang-vue-BnNUWV-p.js → vendor-codemirror-lang-vue-DxbqrLiE.js} +1 -1
  100. package/dist/assets/{vendor-codemirror-lang-wast-C2a-oLmk.js → vendor-codemirror-lang-wast-DyU1Gt-I.js} +1 -1
  101. package/dist/assets/vendor-codemirror-lang-xml-C9a_K5_p.js +1 -0
  102. package/dist/assets/{vendor-codemirror-lang-yaml-C0sjswB0.js → vendor-codemirror-lang-yaml-Cptq8l0e.js} +1 -1
  103. package/dist/assets/{vendor-codemirror-language-ByBn5zIW.js → vendor-codemirror-language-DufXoQvE.js} +2 -2
  104. package/dist/assets/vendor-codemirror-language-data-CtwSWKtX.js +2 -0
  105. package/dist/assets/{vendor-codemirror-search-ib-sHD5W.js → vendor-codemirror-search-CvXM_pZg.js} +1 -1
  106. package/dist/assets/{vendor-codemirror-state-G3UuR-be.js → vendor-codemirror-state-Dek9sMtw.js} +1 -1
  107. package/dist/assets/{vendor-codemirror-view-Bniqp7p5.js → vendor-codemirror-view-1e0FQ6Wn.js} +1 -1
  108. package/dist/assets/vendor-framer-motion-DxDVhOLc.js +9 -0
  109. package/dist/assets/vendor-katex-CuQvj9-L.js +257 -0
  110. package/dist/assets/vendor-katex-DdZNUVNG.css +1 -0
  111. package/dist/assets/{vendor-motion-dom-DcCgeG69.js → vendor-motion-dom-HXPOMxb7.js} +1 -1
  112. package/dist/assets/{vendor-opencode-client-DHzhmLwV.js → vendor-opencode-client-BmFcUkbQ.js} +1 -1
  113. package/dist/assets/{vendor-replit-codemirror-vim-DkzqxN2H.js → vendor-replit-codemirror-vim-BlJRn-cW.js} +2 -2
  114. package/dist/assets/{vendor-tanstack-react-virtual-H_JmXOFV.js → vendor-tanstack-react-virtual-CvuLsk7D.js} +1 -1
  115. package/dist/assets/{vendor-tanstack-virtual-core-Dxc8hSTB.js → vendor-tanstack-virtual-core-DPaNcJcT.js} +1 -1
  116. package/dist/assets/zh-CN-t2vigSB1.js +20 -0
  117. package/dist/assets/zh-TW-jAgqBokB.js +20 -0
  118. package/dist/index.html +6 -6
  119. package/dist/mini-chat.html +6 -6
  120. package/dist/mobile.html +6 -6
  121. package/package.json +8 -5
  122. package/server/index.js +80 -36
  123. package/server/lib/agent-memory/actions.js +1 -6
  124. package/server/lib/agent-memory/routes.js +1 -2
  125. package/server/lib/agent-memory/runtime.js +1 -8
  126. package/server/lib/agent-tool/DOCUMENTATION.md +7 -3
  127. package/server/lib/agent-tool/runtime.js +3 -6
  128. package/server/lib/agent-tool/runtime.test.js +0 -2
  129. package/server/lib/browser-control/DOCUMENTATION.md +6 -0
  130. package/server/lib/changelog/update-notes.js +1 -1
  131. package/server/lib/context-obligatory/runtime.js +1 -2
  132. package/server/lib/dev-tunnel/DOCUMENTATION.md +8 -0
  133. package/server/lib/dev-tunnel/client.js +21 -10
  134. package/server/lib/dev-tunnel/tunnel.test.js +34 -0
  135. package/server/lib/dispatch-results/runtime.js +1 -1
  136. package/server/lib/enterprise-mode.d.ts +1 -0
  137. package/server/lib/enterprise-mode.js +13 -2
  138. package/server/lib/enterprise-mode.test.js +21 -0
  139. package/server/lib/event-stream/DOCUMENTATION.md +2 -2
  140. package/server/lib/event-stream/index.js +1 -3
  141. package/server/lib/event-stream/translate-v2.js +1 -9
  142. package/server/lib/event-stream/translate-v2.test.js +0 -11
  143. package/server/lib/fs/DOCUMENTATION.md +5 -2
  144. package/server/lib/fs/routes.js +62 -15
  145. package/server/lib/fs/routes.test.js +278 -48
  146. package/server/lib/fs/workspace-file-names.js +104 -0
  147. package/server/lib/fs/workspace-file-names.test.js +62 -0
  148. package/server/lib/git/DOCUMENTATION.md +17 -5
  149. package/server/lib/git/contributor-provenance-storage.js +9 -6
  150. package/server/lib/git/contributor-provenance-storage.test.js +29 -0
  151. package/server/lib/git/credential-broker.js +1 -1
  152. package/server/lib/git/credential-resolver.js +1 -4
  153. package/server/lib/git/credentials.js +1 -0
  154. package/server/lib/git/discovery-endpoint.js +1 -1
  155. package/server/lib/git/identity-provisioning.js +2 -1
  156. package/server/lib/git/identity-storage.js +3 -4
  157. package/server/lib/git/lfs-discovery.js +14 -14
  158. package/server/lib/git/lfs-discovery.test.js +16 -6
  159. package/server/lib/git/network-operation-plan.js +50 -62
  160. package/server/lib/git/network-operation-plan.test.js +103 -1
  161. package/server/lib/git/network-operation-registry.js +4 -6
  162. package/server/lib/git/network-operation-storage.js +4 -6
  163. package/server/lib/git/network-operations.js +12 -4
  164. package/server/lib/git/network-operations.test.js +19 -6
  165. package/server/lib/git/redaction.js +2 -1
  166. package/server/lib/git/repository-credential-runtime.js +5 -3
  167. package/server/lib/git/repository-credential-runtime.test.js +22 -0
  168. package/server/lib/git/routes.js +31 -137
  169. package/server/lib/git/routes.test.js +2 -2
  170. package/server/lib/git/service.js +143 -27
  171. package/server/lib/git/service.test.js +215 -1
  172. package/server/lib/git/ssh-credential-storage.js +1 -4
  173. package/server/lib/git/submodule-discovery.js +1 -1
  174. package/server/lib/git/worktree-bootstrap-storage.js +3 -4
  175. package/server/lib/git/worktree-bootstrap-storage.test.js +15 -0
  176. package/server/lib/git/worktree-creation.js +194 -0
  177. package/server/lib/git/worktree-creation.test.js +155 -0
  178. package/server/lib/github/DOCUMENTATION.md +11 -6
  179. package/server/lib/github/auth.js +2 -4
  180. package/server/lib/github/device-flow.js +2 -2
  181. package/server/lib/github/gh-cli-credential.js +1 -0
  182. package/server/lib/github/octokit.js +31 -0
  183. package/server/lib/github/pr-status.js +35 -18
  184. package/server/lib/github/pr-status.test.js +271 -3
  185. package/server/lib/github/reference-search.js +102 -13
  186. package/server/lib/github/reference-search.test.js +48 -7
  187. package/server/lib/github/routes.auth.test.js +18 -0
  188. package/server/lib/github/routes.js +360 -6
  189. package/server/lib/github/routes.mutations.test.js +239 -0
  190. package/server/lib/github/routes.test.js +134 -2
  191. package/server/lib/gitlab/DOCUMENTATION.md +5 -1
  192. package/server/lib/gitlab/auth-storage.test.js +1 -0
  193. package/server/lib/gitlab/mappers.js +30 -1
  194. package/server/lib/gitlab/resources.js +230 -6
  195. package/server/lib/gitlab/resources.test.js +179 -0
  196. package/server/lib/gitlab/routes.js +200 -6
  197. package/server/lib/gitlab/routes.test.js +186 -2
  198. package/server/lib/gitlab/validation.js +1 -5
  199. package/server/lib/guests/DOCUMENTATION.md +1 -1
  200. package/server/lib/guests/oauth.js +2 -2
  201. package/server/lib/guests/service.js +1 -1
  202. package/server/lib/guests/sockets.js +1 -1
  203. package/server/lib/injected-env.d.ts +8 -0
  204. package/server/lib/injected-env.js +50 -0
  205. package/server/lib/injected-env.test.js +56 -0
  206. package/server/lib/linear/DOCUMENTATION.md +5 -6
  207. package/server/lib/linear/issues.js +62 -4
  208. package/server/lib/linear/issues.test.js +68 -0
  209. package/server/lib/linear/oauth.js +5 -5
  210. package/server/lib/linear/parse.js +2 -9
  211. package/server/lib/message-queue/DOCUMENTATION.md +23 -1
  212. package/server/lib/message-queue/runtime.js +85 -12
  213. package/server/lib/message-queue/runtime.test.js +172 -2
  214. package/server/lib/notifications/apns-runtime.js +5 -6
  215. package/server/lib/notifications/apns-runtime.test.js +20 -4
  216. package/server/lib/openchamber-control/DOCUMENTATION.md +19 -1
  217. package/server/lib/openchamber-control/actions.js +2 -1
  218. package/server/lib/openchamber-control/service.js +119 -20
  219. package/server/lib/openchamber-control/service.test.js +166 -0
  220. package/server/lib/openchamber-sessions/archive-store.js +1 -6
  221. package/server/lib/openchamber-sessions/routes.js +47 -12
  222. package/server/lib/openchamber-sessions/routes.test.js +65 -2
  223. package/server/lib/openchamber-sessions/session-metadata-store.js +1 -6
  224. package/server/lib/opencode/DOCUMENTATION.md +22 -8
  225. package/server/lib/opencode/agents.js +62 -0
  226. package/server/lib/opencode/auth-state-runtime.js +3 -1
  227. package/server/lib/opencode/auth.d.ts +1 -1
  228. package/server/lib/opencode/auth.js +8 -0
  229. package/server/lib/opencode/auth.test.js +31 -0
  230. package/server/lib/opencode/config-entity-routes.js +16 -0
  231. package/server/lib/opencode/config-entity-v2.test.js +16 -1
  232. package/server/lib/opencode/config-paths.test.js +2 -2
  233. package/server/lib/opencode/config-v2.js +2 -4
  234. package/server/lib/opencode/feature-routes-runtime.js +89 -49
  235. package/server/lib/opencode/lifecycle.js +41 -21
  236. package/server/lib/opencode/lifecycle.test.js +50 -2
  237. package/server/lib/opencode/model-discovery.js +1 -1
  238. package/server/lib/opencode/model-discovery.test.js +17 -0
  239. package/server/lib/opencode/models-metadata.js +0 -2
  240. package/server/lib/opencode/openchamber-routes.test.js +3 -3
  241. package/server/lib/opencode/plugins.js +6 -9
  242. package/server/lib/opencode/provider-source-route.test.js +101 -3
  243. package/server/lib/opencode/providers-disabled.test.js +59 -0
  244. package/server/lib/opencode/providers.js +34 -0
  245. package/server/lib/opencode/proxy.js +11 -3
  246. package/server/lib/opencode/routes.js +40 -2
  247. package/server/lib/opencode/settings-helpers.js +27 -1
  248. package/server/lib/opencode/settings-helpers.test.js +19 -7
  249. package/server/lib/opencode/settings-registry.json +27 -16
  250. package/server/lib/opencode/settings-runtime.test.js +18 -0
  251. package/server/lib/opencode/shared-service-runtime.js +23 -104
  252. package/server/lib/opencode/shared.js +77 -46
  253. package/server/lib/opencode/shared.test.js +12 -12
  254. package/server/lib/opencode/shutdown-runtime.test.js +2 -3
  255. package/server/lib/opencode/skill-routes.js +123 -38
  256. package/server/lib/opencode/skill-routes.test.js +201 -0
  257. package/server/lib/opencode/skills.js +81 -34
  258. package/server/lib/opencode/skills.test.js +176 -21
  259. package/server/lib/opencode/startup-pipeline-runtime.js +6 -3
  260. package/server/lib/opencode/startup-pipeline-runtime.test.js +2 -1
  261. package/server/lib/opencode/watcher.test.js +0 -54
  262. package/server/lib/outbound-proxy.js +52 -0
  263. package/server/lib/outbound-proxy.test.js +55 -0
  264. package/server/lib/package-manager.js +23 -17
  265. package/server/lib/package-manager.test.js +5 -18
  266. package/server/lib/permission-auto-accept/DOCUMENTATION.md +1 -1
  267. package/server/lib/permission-auto-accept/runtime.js +19 -47
  268. package/server/lib/permission-auto-accept/runtime.test.js +2 -2
  269. package/server/lib/project-context/DOCUMENTATION.md +7 -1
  270. package/server/lib/project-context/routes.js +1 -2
  271. package/server/lib/project-context/runtime.js +12 -14
  272. package/server/lib/project-context/runtime.test.js +78 -2
  273. package/server/lib/projects/project-config.js +30 -15
  274. package/server/lib/projects/project-config.test.js +30 -0
  275. package/server/lib/projects/project-setup.js +1 -2
  276. package/server/lib/projects/routes.js +1 -2
  277. package/server/lib/quota/DOCUMENTATION.md +35 -7
  278. package/server/lib/quota/index.js +1 -0
  279. package/server/lib/quota/providers/claude/index.js +1 -1
  280. package/server/lib/quota/providers/cline-pass.js +1 -1
  281. package/server/lib/quota/providers/exe-dev.js +1 -1
  282. package/server/lib/quota/providers/google/index.js +1 -1
  283. package/server/lib/quota/providers/hyper.js +1 -1
  284. package/server/lib/quota/providers/kimi.js +116 -36
  285. package/server/lib/quota/providers/kimi.test.js +128 -3
  286. package/server/lib/quota/providers/opencode-go.js +144 -21
  287. package/server/lib/quota/providers/opencode-go.test.js +229 -6
  288. package/server/lib/quota/providers/openrouter.js +1 -1
  289. package/server/lib/quota/providers/zenmux.js +1 -1
  290. package/server/lib/quota/utils/auth.js +1 -0
  291. package/server/lib/quota/utils/transformers.js +2 -6
  292. package/server/lib/relay/DOCUMENTATION.md +18 -3
  293. package/server/lib/relay/e2ee.js +7 -7
  294. package/server/lib/relay/host-client.js +84 -13
  295. package/server/lib/relay/host-client.test.js +201 -1
  296. package/server/lib/relay/identity.js +6 -29
  297. package/server/lib/relay/identity.test.js +18 -28
  298. package/server/lib/relay/key-store.js +141 -0
  299. package/server/lib/relay/key-store.test.js +138 -0
  300. package/server/lib/relay/service.js +8 -8
  301. package/server/lib/relay/service.test.js +10 -5
  302. package/server/lib/relay/signing-key.js +13 -36
  303. package/server/lib/relay/tunnel-codec.js +10 -10
  304. package/server/lib/route-module-exports.test.js +22 -0
  305. package/server/lib/routing/DOCUMENTATION.md +23 -3
  306. package/server/lib/routing/cache.js +87 -0
  307. package/server/lib/routing/defaults.js +11 -0
  308. package/server/lib/routing/history.js +12 -4
  309. package/server/lib/routing/runtime.js +39 -3
  310. package/server/lib/routing/runtime.test.js +81 -2
  311. package/server/lib/routing/store.js +12 -1
  312. package/server/lib/routing/store.test.js +13 -0
  313. package/server/lib/scheduled-tasks/DOCUMENTATION.md +59 -1
  314. package/server/lib/scheduled-tasks/existing-session.js +119 -0
  315. package/server/lib/scheduled-tasks/existing-session.test.js +350 -0
  316. package/server/lib/scheduled-tasks/loops.js +1 -8
  317. package/server/lib/scheduled-tasks/routes.js +8 -8
  318. package/server/lib/scheduled-tasks/runtime.js +103 -13
  319. package/server/lib/scheduled-tasks/runtime.test.js +48 -0
  320. package/server/lib/scheduled-tasks/service.js +33 -11
  321. package/server/lib/scheduled-tasks/service.test.js +128 -2
  322. package/server/lib/scheduled-tasks/session-defaults.js +27 -0
  323. package/server/lib/scheduled-tasks/session-defaults.test.js +19 -0
  324. package/server/lib/session-assist/context.js +1 -0
  325. package/server/lib/session-folders/routes.js +2 -2
  326. package/server/lib/session-goal/objectives.js +1 -1
  327. package/server/lib/session-knowledge/routes.js +2 -2
  328. package/server/lib/session-knowledge/runtime.js +1 -2
  329. package/server/lib/session-work/DOCUMENTATION.md +39 -14
  330. package/server/lib/session-work/questions.js +31 -2
  331. package/server/lib/session-work/runtime.js +22 -13
  332. package/server/lib/session-work/runtime.test.js +43 -26
  333. package/server/lib/session-work/state.js +10 -4
  334. package/server/lib/session-work/state.test.js +8 -1
  335. package/server/lib/shared/DOCUMENTATION.md +89 -0
  336. package/server/lib/shared/guards.js +53 -0
  337. package/server/lib/shared/guards.test.js +112 -0
  338. package/server/lib/skills-catalog/DOCUMENTATION.md +10 -8
  339. package/server/lib/skills-catalog/cache.js +3 -2
  340. package/server/lib/skills-catalog/curated-sources.js +5 -1
  341. package/server/lib/skills-catalog/git.js +107 -20
  342. package/server/lib/skills-catalog/git.test.js +78 -0
  343. package/server/lib/skills-catalog/install.js +21 -12
  344. package/server/lib/skills-catalog/scan.js +25 -15
  345. package/server/lib/skills-catalog/source.js +26 -1
  346. package/server/lib/skills-catalog/source.test.js +27 -0
  347. package/server/lib/source-control/DOCUMENTATION.md +2 -1
  348. package/server/lib/source-control/audit-storage.js +7 -6
  349. package/server/lib/source-control/audit-storage.test.js +11 -0
  350. package/server/lib/source-control/binding-service.js +2 -5
  351. package/server/lib/source-control/change-request-worktree.js +90 -0
  352. package/server/lib/source-control/change-request-worktree.test.js +121 -0
  353. package/server/lib/source-control/mutation-executor.js +1 -1
  354. package/server/lib/source-control/mutation-storage.js +11 -5
  355. package/server/lib/source-control/provider-instance.js +1 -2
  356. package/server/lib/source-control/repository-identity.js +1 -1
  357. package/server/lib/source-control/routes.js +14 -2
  358. package/server/lib/spaces/DOCUMENTATION.md +42 -17
  359. package/server/lib/spaces/command-stream.test.js +15 -0
  360. package/server/lib/spaces/dispatcher.js +7 -0
  361. package/server/lib/spaces/dispatcher.test.js +17 -0
  362. package/server/lib/spaces/hardening.js +2 -2
  363. package/server/lib/spaces/hardening.test.js +1 -1
  364. package/server/lib/spaces/host.js +2 -0
  365. package/server/lib/spaces/journey.js +66 -5
  366. package/server/lib/spaces/journey.test.js +95 -4
  367. package/server/lib/spaces/layout.js +1 -1
  368. package/server/lib/spaces/places/docker-disk.js +38 -14
  369. package/server/lib/spaces/places/docker-disk.test.js +80 -1
  370. package/server/lib/spaces/places/docker.js +35 -11
  371. package/server/lib/spaces/places/fake-docker.js +22 -3
  372. package/server/lib/spaces/places/memory-place.js +3 -0
  373. package/server/lib/spaces/places/packed.docker.live.test.js +1 -1
  374. package/server/lib/spaces/routes.js +5 -0
  375. package/server/lib/spaces/routes.test.js +4 -2
  376. package/server/lib/spaces/run-command.js +9 -2
  377. package/server/lib/spaces/tools.js +4 -5
  378. package/server/lib/spaces/tools.test.js +2 -2
  379. package/server/lib/terminal/runtime.js +18 -6
  380. package/server/lib/terminal/runtime.test.js +66 -0
  381. package/server/lib/tts/index.js +3 -0
  382. package/server/lib/tts/service.js +4 -2
  383. package/server/lib/walkthrough/DOCUMENTATION.md +3 -1
  384. package/server/lib/walkthrough/index.js +2 -3
  385. package/server/lib/walkthrough/pr-source.test.js +22 -0
  386. package/server/lib/walkthrough/sources.js +6 -1
  387. package/server/opencode-proxy.test.js +127 -0
  388. package/bin/lib/commands-startup.test.js +0 -15
  389. package/dist/assets/CodeMirrorCommentWidgets-CVDSjcrP.js +0 -1
  390. package/dist/assets/DiffView-Ct7vL3fD.js +0 -4
  391. package/dist/assets/FilesView-DT0uEYgT.js +0 -17
  392. package/dist/assets/GuestSurfacePane-DVvk1VPu.js +0 -1
  393. package/dist/assets/LinearIssuesView-C10MwA9h.js +0 -1
  394. package/dist/assets/MarkdownImageGallery-BbN_K_3d.js +0 -1
  395. package/dist/assets/MarkdownRendererImpl-BLt2Tkk9.js +0 -10
  396. package/dist/assets/OnboardingScreen-Bgp5A3WB.js +0 -1
  397. package/dist/assets/PierreDiffViewer-CWJLj-S-.js +0 -210
  398. package/dist/assets/PlanView-DsRWnsOI.js +0 -5
  399. package/dist/assets/PublishDialog-CCeKloRu.js +0 -2
  400. package/dist/assets/SettingsView-BuPPsNPp.js +0 -30
  401. package/dist/assets/SettingsWindow-CM5fL8Ot.js +0 -1
  402. package/dist/assets/SubmoduleDiffSummary-CfsMcfiQ.js +0 -1
  403. package/dist/assets/ToolOutputDialog-mZrfSofz.js +0 -15
  404. package/dist/assets/ToolPartDiffPreview-BUGCRYEL.js +0 -8
  405. package/dist/assets/WalkthroughView-ClBo7PPd.js +0 -4
  406. package/dist/assets/appThemeRegistry-CxzMxR-H.js +0 -1
  407. package/dist/assets/de-CBla4hjd.js +0 -14
  408. package/dist/assets/es-BmnZtxTD.js +0 -20
  409. package/dist/assets/fileTreeStatus-BSHFIGEg.js +0 -1
  410. package/dist/assets/fr-OVKIBUZO.js +0 -18
  411. package/dist/assets/index-CKCFKmjF.js +0 -1
  412. package/dist/assets/index-Dbx4V4GN.css +0 -1
  413. package/dist/assets/index-vQ1Pg1cr.js +0 -61
  414. package/dist/assets/ja-6v-yr-gE.js +0 -20
  415. package/dist/assets/ko-I_ZLvgQ9.js +0 -20
  416. package/dist/assets/main-CmO4dRtR.js +0 -2
  417. package/dist/assets/main-b9vqhlYM.js +0 -999
  418. package/dist/assets/markdownCore-BPEZRQfP.js +0 -7
  419. package/dist/assets/miniChat-B5Oi7B-6.js +0 -2
  420. package/dist/assets/mobile-CVtShif9.js +0 -2
  421. package/dist/assets/nl-DcvIJf46.js +0 -20
  422. package/dist/assets/patchFileDiff-CW3wCH1O.js +0 -13
  423. package/dist/assets/pl-JxCUl0sd.js +0 -20
  424. package/dist/assets/pt-BR-L89mjja2.js +0 -20
  425. package/dist/assets/relocateRestoredSession-DYh_9oBa.js +0 -1
  426. package/dist/assets/renderElectronMiniChatApp-BD-1JHSg.js +0 -2
  427. package/dist/assets/renderMobileApp-7s5InXt1.js +0 -25
  428. package/dist/assets/runtimeEndpointReset-DqLJC6L1.js +0 -53
  429. package/dist/assets/shikiHighlight-CXgZnvHh.js +0 -1
  430. package/dist/assets/space-apply-1trGVIl8.js +0 -1
  431. package/dist/assets/space-close-y7j-NuBv.js +0 -1
  432. package/dist/assets/tr-ka5J1flt.js +0 -11
  433. package/dist/assets/uk-PyGe6Q_u.js +0 -20
  434. package/dist/assets/useInlineCommentController-CrjSGCpi.js +0 -2
  435. package/dist/assets/vendor-codemirror-lang-python-B7ClrMIT.js +0 -3
  436. package/dist/assets/vendor-codemirror-lang-xml-C8tGnS64.js +0 -1
  437. package/dist/assets/vendor-codemirror-language-data-q2bKO1EM.js +0 -2
  438. package/dist/assets/vendor-framer-motion-Bq3iTZ6r.js +0 -1
  439. package/dist/assets/vendor-katex-CHD9BdDO.js +0 -257
  440. package/dist/assets/vendor-katex-Cp579UoJ.css +0 -1
  441. package/dist/assets/zh-CN-B0P2tybJ.js +0 -20
  442. package/dist/assets/zh-TW-Bs9C5cN8.js +0 -20
@@ -461,8 +461,15 @@ const toSimpleGitEnv = (env) => {
461
461
  // Transport configuration is owned by repository bindings and the credential
462
462
  // broker, so no caller needs simple-git's unsafe SSH-command or
463
463
  // credential-helper escapes any more.
464
- const createGit = async (directory, { stallTimeoutMs = 0 } = {}) => {
464
+ const createGit = async (directory, { stallTimeoutMs = 0, optionalLocks = true } = {}) => {
465
465
  const env = await buildGitEnv(directory);
466
+ // `git status` holds `.git/index.lock` while it refreshes the index, and a
467
+ // `git commit` the user runs in a terminal at that moment fails with
468
+ // "index.lock: File exists" (#2229). Read-only callers opt out of that
469
+ // refresh; commands that write the index still take the lock.
470
+ if (!optionalLocks) {
471
+ env.GIT_OPTIONAL_LOCKS = '0';
472
+ }
466
473
  const spawnOptions = { windowsHide: true };
467
474
  // simple-git's block timeout kills the process once it has produced no
468
475
  // output for this long. Opt-in per caller: a background read must never hold
@@ -878,6 +885,14 @@ const parseWorktreePorcelain = (raw) => {
878
885
  continue;
879
886
  }
880
887
 
888
+ // A bare repository lists itself as a worktree with a `bare` attribute
889
+ // and no HEAD or branch; without a working tree it is not a checkout a
890
+ // session can run in.
891
+ if (line === 'bare') {
892
+ current.bare = true;
893
+ continue;
894
+ }
895
+
881
896
  // git marks a worktree whose directory is gone (deleted outside git) as
882
897
  // prunable; it stays registered until `git worktree prune`. The sidebar
883
898
  // needs that distinction: the directory is missing, but the sessions that
@@ -1465,12 +1480,15 @@ export async function resolveRepositoryGitPaths(directory) {
1465
1480
  * `git remote get-url [--push]` reports them: the first URL of several, and a
1466
1481
  * remote with no URL read as a URL equal to its name, as Git does. Lines may
1467
1482
  * end in CRLF (Git for Windows); a kept `\r` would match no line and read
1468
- * every remote as URL-less.
1483
+ * every remote as URL-less. Anything after the URL-kind marker — Git 2.54+
1484
+ * appends the partial-clone filter there as `[blob:none]` — is decoration
1485
+ * about the remote, never part of the URL, and is ignored whether or not its
1486
+ * shape is recognized.
1469
1487
  */
1470
1488
  export function parseRemoteListing(names, listing) {
1471
1489
  const urls = new Map();
1472
1490
  for (const line of String(listing || '').split(/\r?\n/)) {
1473
- const match = line.match(/^([^\t]+)\t(.*) \((fetch|push)\)$/);
1491
+ const match = line.match(/^([^\t]+)\t(.*) \((fetch|push)\)/);
1474
1492
  if (!match) continue;
1475
1493
  const entry = urls.get(match[1]) ?? {};
1476
1494
  if (entry[match[3]] === undefined) entry[match[3]] = match[2];
@@ -1518,6 +1536,7 @@ export async function resolveWorktreeTopLevel(directory) {
1518
1536
  return { root: root || directory };
1519
1537
  }
1520
1538
 
1539
+ /** @public */
1521
1540
  export async function getCommitSummaries(directory, shas) {
1522
1541
  const commits = Array.isArray(shas)
1523
1542
  ? shas.map((sha) => String(sha || '').trim()).filter(Boolean)
@@ -1629,6 +1648,7 @@ const ensureLocalIntegrateBranch = async (repoRoot, candidate) => {
1629
1648
  return raw;
1630
1649
  };
1631
1650
 
1651
+ /** @public */
1632
1652
  export async function computeIntegratePlan(input = {}) {
1633
1653
  const repoRoot = normalizeIntegratePath(input.repoRoot, 'repoRoot');
1634
1654
  const sourceBranch = normalizeIntegrateBranch(input.sourceBranch, 'sourceBranch');
@@ -1682,6 +1702,7 @@ const maybeFastForwardIntegrateUpstream = async (tmpDir) => {
1682
1702
  }
1683
1703
  };
1684
1704
 
1705
+ /** @public */
1685
1706
  export async function getIntegrateConflictDetails(tmpDir) {
1686
1707
  const target = normalizeIntegratePath(tmpDir, 'tempWorktreePath');
1687
1708
  const [status, unmerged, diff, meta, patch] = await Promise.all([
@@ -1701,6 +1722,7 @@ export async function getIntegrateConflictDetails(tmpDir) {
1701
1722
  };
1702
1723
  }
1703
1724
 
1725
+ /** @public */
1704
1726
  export async function isCherryPickInProgress(tmpDir) {
1705
1727
  const target = normalizeIntegratePath(tmpDir, 'tempWorktreePath');
1706
1728
  const head = await runGitCommand(target, ['rev-parse', '--verify', '--quiet', 'CHERRY_PICK_HEAD']);
@@ -1752,6 +1774,7 @@ const normalizeIntegrateState = (state = {}) => ({
1752
1774
  currentCommit: normalizeIntegrateSha(state.currentCommit),
1753
1775
  });
1754
1776
 
1777
+ /** @public */
1755
1778
  export async function integrateWorktreeCommits(inputPlan = {}) {
1756
1779
  const plan = await normalizeIntegratePlan(inputPlan);
1757
1780
  if (plan.commits.length === 0) {
@@ -1815,6 +1838,7 @@ export async function integrateWorktreeCommits(inputPlan = {}) {
1815
1838
  }
1816
1839
  }
1817
1840
 
1841
+ /** @public */
1818
1842
  export async function abortIntegrate(stateInput = {}) {
1819
1843
  const state = normalizeIntegrateState(stateInput);
1820
1844
  await runGitCommand(state.tempWorktreePath, ['cherry-pick', '--abort']).catch(() => undefined);
@@ -1822,6 +1846,7 @@ export async function abortIntegrate(stateInput = {}) {
1822
1846
  return { success: true };
1823
1847
  }
1824
1848
 
1849
+ /** @public */
1825
1850
  export async function continueIntegrate(stateInput = {}) {
1826
1851
  const state = normalizeIntegrateState(stateInput);
1827
1852
  const cont = await runGitCommand(state.tempWorktreePath, ['cherry-pick', '--continue']);
@@ -1906,12 +1931,13 @@ const resolveWorktreeProjectContext = async (directory, options = {}) => {
1906
1931
  throw new Error('Directory is required');
1907
1932
  }
1908
1933
 
1909
- const topResult = await runGitCommandOrThrow(
1910
- directoryPath,
1911
- ['rev-parse', '--show-toplevel'],
1912
- 'Failed to resolve git top-level directory'
1913
- );
1914
- const sandbox = path.resolve(directoryPath, normalizeGitOutputPath(topResult.stdout.trim()));
1934
+ const topResult = await runGitCommand(directoryPath, ['rev-parse', '--show-toplevel']);
1935
+ // A bare repository has no work tree, so `--show-toplevel` fails there; the
1936
+ // bare directory itself still answers the object-level commands worktree
1937
+ // creation runs against it.
1938
+ const sandbox = topResult.success
1939
+ ? path.resolve(directoryPath, normalizeGitOutputPath(topResult.stdout.trim()))
1940
+ : directoryPath;
1915
1941
 
1916
1942
  const commonResult = await runGitCommandOrThrow(
1917
1943
  sandbox,
@@ -1919,7 +1945,10 @@ const resolveWorktreeProjectContext = async (directory, options = {}) => {
1919
1945
  'Failed to resolve git common directory'
1920
1946
  );
1921
1947
  const commonDir = path.resolve(sandbox, normalizeGitOutputPath(commonResult.stdout.trim()));
1922
- const primaryWorktree = path.dirname(commonDir);
1948
+ // `<root>/.git` resolves to the primary checkout; any other layout (a bare
1949
+ // repository, a separate git dir) has no primary worktree, and the sandbox
1950
+ // directory is the closest thing to one.
1951
+ const primaryWorktree = derivePrimaryWorktreeRootFromGitDir(commonDir) || sandbox;
1923
1952
  const projectID = await ensureOpenCodeProjectId(primaryWorktree);
1924
1953
  // OpenCode's `worktree.directory` is read from the canonical checkout so a
1925
1954
  // linked worktree still sees the project's saved configuration. When unset,
@@ -2893,6 +2922,33 @@ export async function isAncestorOfHead(directory, sha) {
2893
2922
  return result.success;
2894
2923
  }
2895
2924
 
2925
+ const PATCH_EQUIVALENCE_TIMEOUT_MS = 5_000;
2926
+
2927
+ // Whether a commit has an equivalent patch reachable from the checked-out HEAD.
2928
+ // Missing objects and ambiguous prefixes cannot prove it.
2929
+ export async function isPatchEquivalentOfHead(directory, sha) {
2930
+ const normalizedDirectory = normalizeDirectoryPath(directory);
2931
+ const normalizedSha = String(sha ?? '').trim();
2932
+ if (!normalizedDirectory || !/^[0-9a-f]{7,64}$/i.test(normalizedSha)) {
2933
+ return false;
2934
+ }
2935
+
2936
+ const cherry = await runGitCommand(
2937
+ normalizedDirectory,
2938
+ ['cherry', 'HEAD', normalizedSha, `${normalizedSha}^`],
2939
+ { timeoutMs: PATCH_EQUIVALENCE_TIMEOUT_MS },
2940
+ );
2941
+ if (!cherry.success) {
2942
+ return false;
2943
+ }
2944
+
2945
+ const expectedPrefix = normalizedSha.toLowerCase();
2946
+ return cherry.stdout.split(/\r?\n/).some((line) => {
2947
+ const match = line.match(/^-\s+([0-9a-f]{40,64})$/i);
2948
+ return Boolean(match?.[1]) && match[1].toLowerCase().startsWith(expectedPrefix);
2949
+ });
2950
+ }
2951
+
2896
2952
  async function readStatus(normalizedDirectory, lightMode) {
2897
2953
  try {
2898
2954
  // Prefer an explicit non-repo check before simple-git status so a missing
@@ -2903,6 +2959,7 @@ async function readStatus(normalizedDirectory, lightMode) {
2903
2959
 
2904
2960
  const { directoryPath, repoRoot, git } = await createRepositoryGitContext(normalizedDirectory, {
2905
2961
  stallTimeoutMs: GIT_STATUS_STALL_TIMEOUT_MS,
2962
+ optionalLocks: false,
2906
2963
  });
2907
2964
 
2908
2965
  // `-unormal` lists a directory with no tracked files as one `dir/` entry
@@ -2916,12 +2973,15 @@ async function readStatus(normalizedDirectory, lightMode) {
2916
2973
  // Light mode: skip numstat + new-file line counting for faster response.
2917
2974
  // Staged (`--cached`: HEAD -> index) and working (`--numstat`: index -> worktree)
2918
2975
  // stay in separate maps. A partially staged file has an entry in both, and the
2919
- // UI shows each row's own scope instead of a combined total.
2976
+ // UI shows each row's own scope instead of a combined total. The working
2977
+ // side uses `diff-files`: `git diff` rewrites a stale index under
2978
+ // `index.lock` even with GIT_OPTIONAL_LOCKS=0. `-M` keeps the rename
2979
+ // detection `git diff` does by default.
2920
2980
  const [stagedStatsRaw, workingStatsRaw] = lightMode
2921
2981
  ? ['', '']
2922
2982
  : await Promise.all([
2923
2983
  git.raw(['diff', '--cached', '--numstat']).catch(() => ''),
2924
- git.raw(['diff', '--numstat']).catch(() => ''),
2984
+ git.raw(['diff-files', '-M', '--numstat']).catch(() => ''),
2925
2985
  ]);
2926
2986
 
2927
2987
  const stagedDiffStats = {};
@@ -3955,7 +4015,7 @@ export async function applyHunk(directory, filePath, options = {}) {
3955
4015
  });
3956
4016
  }
3957
4017
 
3958
- export async function collectDiffs(directory, files = []) {
4018
+ async function collectDiffs(directory, files = []) {
3959
4019
  const results = [];
3960
4020
  for (const filePath of files) {
3961
4021
  try {
@@ -3970,6 +4030,7 @@ export async function collectDiffs(directory, files = []) {
3970
4030
  return results;
3971
4031
  }
3972
4032
 
4033
+ /** @public */
3973
4034
  export async function listStashes(directory) {
3974
4035
  const { git } = await createRepositoryGitContext(directory);
3975
4036
  const output = await git.raw(['stash', 'list', '--format=%gd%x1f%gs%x1f%cr%x1f%H']);
@@ -3984,6 +4045,7 @@ export async function listStashes(directory) {
3984
4045
  .filter((entry) => entry.ref);
3985
4046
  }
3986
4047
 
4048
+ /** @public */
3987
4049
  export async function countStashFiles(directory, refs = []) {
3988
4050
  const { git } = await createRepositoryGitContext(directory);
3989
4051
  const uniqueRefs = Array.from(new Set((Array.isArray(refs) ? refs : []).map((ref) => String(ref || '').trim()).filter(Boolean)));
@@ -4007,6 +4069,7 @@ export async function countStashFiles(directory, refs = []) {
4007
4069
  await Promise.all(Array.from({ length: Math.min(concurrency, uniqueRefs.length) }, () => worker()));
4008
4070
  return counts;
4009
4071
  }
4072
+ /** @public */
4010
4073
  export async function stashPush(directory, options = {}) {
4011
4074
  const { git } = await createRepositoryGitContext(directory);
4012
4075
  const message = typeof options.message === 'string' && options.message.trim()
@@ -4021,6 +4084,7 @@ export async function stashPush(directory, options = {}) {
4021
4084
  };
4022
4085
  }
4023
4086
 
4087
+ /** @public */
4024
4088
  export async function stashApply(directory, options = {}) {
4025
4089
  const { git } = await createRepositoryGitContext(directory);
4026
4090
  const ref = typeof options.ref === 'string' && options.ref.trim() ? options.ref.trim() : 'stash@{0}';
@@ -4033,6 +4097,7 @@ export async function stashApply(directory, options = {}) {
4033
4097
  return { success: true, ref };
4034
4098
  }
4035
4099
 
4100
+ /** @public */
4036
4101
  export async function stashDrop(directory, options = {}) {
4037
4102
  const { git } = await createRepositoryGitContext(directory);
4038
4103
  const ref = typeof options.ref === 'string' && options.ref.trim() ? options.ref.trim() : 'stash@{0}';
@@ -4040,6 +4105,7 @@ export async function stashDrop(directory, options = {}) {
4040
4105
  return { success: true, ref };
4041
4106
  }
4042
4107
 
4108
+ /** @public */
4043
4109
  export async function stashPop(directory, options = {}) {
4044
4110
  const ref = typeof options.ref === 'string' && options.ref.trim() ? options.ref.trim() : 'stash@{0}';
4045
4111
  await stashApply(directory, { ref });
@@ -4047,7 +4113,7 @@ export async function stashPop(directory, options = {}) {
4047
4113
  return { success: true, ref };
4048
4114
  }
4049
4115
 
4050
- export async function stageFile(directory, filePath) {
4116
+ async function stageFile(directory, filePath) {
4051
4117
  await stageFiles(directory, [filePath]);
4052
4118
  }
4053
4119
 
@@ -4095,7 +4161,7 @@ export async function stageFiles(directory, paths) {
4095
4161
  });
4096
4162
  }
4097
4163
 
4098
- export async function unstageFile(directory, filePath) {
4164
+ async function unstageFile(directory, filePath) {
4099
4165
  await unstageFiles(directory, [filePath]);
4100
4166
  }
4101
4167
 
@@ -4431,6 +4497,7 @@ async function filterActiveRemoteBranches(git, directory, remoteBranches) {
4431
4497
  }
4432
4498
  }
4433
4499
 
4500
+ /** @public */
4434
4501
  export async function createBranch(directory, branchName, options = {}) {
4435
4502
  const { git } = await createRepositoryGitContext(directory);
4436
4503
 
@@ -4617,26 +4684,46 @@ export async function resetToCommit(directory, hash, mode, force = false) {
4617
4684
  }
4618
4685
  }
4619
4686
 
4687
+ // `git worktree list` answers from any directory of a repository, but the
4688
+ // repository root resolution behind it (`--show-toplevel`) has no answer in a
4689
+ // bare repository: "this operation must be run in a work tree". A bare
4690
+ // checkout used as the project directory would otherwise read as a failure
4691
+ // and show no worktrees at all, so fall back to listing from the directory
4692
+ // itself when it is still a git directory.
4693
+ const resolveWorktreeListingDirectory = async (directoryPath) => {
4694
+ try {
4695
+ const directoryGit = await createGit(directoryPath);
4696
+ return await resolveGitRepositoryRoot(directoryPath, directoryGit);
4697
+ } catch (error) {
4698
+ const probe = await runGitCommand(directoryPath, ['rev-parse', '--git-dir']);
4699
+ if (probe.success) {
4700
+ return directoryPath;
4701
+ }
4702
+ throw error;
4703
+ }
4704
+ };
4705
+
4620
4706
  export async function getWorktrees(directory) {
4621
4707
  const directoryPath = normalizeDirectoryPath(directory);
4622
4708
  if (!directoryPath || !fs.existsSync(directoryPath)) {
4623
4709
  return [];
4624
4710
  }
4625
4711
  try {
4626
- const directoryGit = await createGit(directoryPath);
4627
- const repoRoot = await resolveGitRepositoryRoot(directoryPath, directoryGit);
4712
+ const listingDirectory = await resolveWorktreeListingDirectory(directoryPath);
4628
4713
  const result = await runGitCommandOrThrow(
4629
- repoRoot,
4714
+ listingDirectory,
4630
4715
  ['worktree', 'list', '--porcelain'],
4631
4716
  'Failed to list git worktrees'
4632
4717
  );
4633
- return parseWorktreePorcelain(result.stdout).map((entry) => ({
4634
- head: entry.head || '',
4635
- name: path.basename(entry.worktree || ''),
4636
- branch: entry.branch || '',
4637
- path: entry.worktree,
4638
- prunable: entry.prunable === true,
4639
- }));
4718
+ return parseWorktreePorcelain(result.stdout)
4719
+ .filter((entry) => entry.bare !== true)
4720
+ .map((entry) => ({
4721
+ head: entry.head || '',
4722
+ name: path.basename(entry.worktree || ''),
4723
+ branch: entry.branch || '',
4724
+ path: entry.worktree,
4725
+ prunable: entry.prunable === true,
4726
+ }));
4640
4727
  } catch (error) {
4641
4728
  // Worktrees are an optional feature. When the caller passes a directory
4642
4729
  // that is not inside any git repository (for example, the managed
@@ -5951,6 +6038,7 @@ export async function snapshotWorktree(directory, input = {}) {
5951
6038
  }
5952
6039
  }
5953
6040
 
6041
+ /** @public */
5954
6042
  export async function deleteBranch(directory, branch, options = {}) {
5955
6043
  const { git } = await createRepositoryGitContext(directory);
5956
6044
 
@@ -6173,6 +6261,7 @@ export async function getLog(directory, options = {}) {
6173
6261
  }
6174
6262
  }
6175
6263
 
6264
+ /** @public */
6176
6265
  export async function isLinkedWorktree(directory) {
6177
6266
  const git = await createGit(directory);
6178
6267
  try {
@@ -6187,6 +6276,7 @@ export async function isLinkedWorktree(directory) {
6187
6276
  }
6188
6277
  }
6189
6278
 
6279
+ /** @public */
6190
6280
  export async function validateWorktreeDirectory(directory, worktreeRoot) {
6191
6281
  const directoryPath = normalizeDirectoryPath(directory);
6192
6282
  const rootPath = normalizeDirectoryPath(worktreeRoot);
@@ -6223,6 +6313,7 @@ export async function validateWorktreeDirectory(directory, worktreeRoot) {
6223
6313
  };
6224
6314
  }
6225
6315
 
6316
+ /** @public */
6226
6317
  export async function canonicalizeWorktreeState(directory) {
6227
6318
  const directoryPath = normalizeDirectoryPath(directory);
6228
6319
 
@@ -6384,6 +6475,7 @@ export async function getCommitFiles(directory, commitHash) {
6384
6475
  return { files };
6385
6476
  }
6386
6477
 
6478
+ /** @public */
6387
6479
  export async function renameBranch(directory, oldName, newName) {
6388
6480
  const { git, repoRoot } = await createRepositoryGitContext(directory);
6389
6481
 
@@ -6400,9 +6492,30 @@ export async function renameBranch(directory, oldName, newName) {
6400
6492
  .then((value) => String(value || '').trim())
6401
6493
  .catch(() => '');
6402
6494
 
6403
- // Use git branch -m command to rename the branch
6495
+ // git refuses a taken name with a bare "already exists"; say where the
6496
+ // name is in use so a worktree rename that collides is understandable.
6497
+ const worktrees = await listWorktreeEntries(repoRoot).catch(() => []);
6498
+ const takenBy = worktrees.find((entry) => entry.branch === normalizedNewName);
6499
+ if (takenBy) {
6500
+ const error = new Error(`Branch ${normalizedNewName} is already checked out in ${takenBy.worktree}`);
6501
+ error.statusCode = 409;
6502
+ throw error;
6503
+ }
6504
+ const existing = await runGitCommand(repoRoot, ['show-ref', '--verify', '--quiet', `refs/heads/${normalizedNewName}`]);
6505
+ if (existing.success) {
6506
+ const error = new Error(`A branch named ${normalizedNewName} already exists`);
6507
+ error.statusCode = 409;
6508
+ throw error;
6509
+ }
6510
+
6404
6511
  await git.raw(['branch', '-m', oldName, newName]);
6405
6512
 
6513
+ // A worktree's branch lives in a file the topology watcher does not see,
6514
+ // so tell the sidebar directly when the renamed branch is checked out.
6515
+ if (worktrees.some((entry) => entry.branch === normalizedOldName)) {
6516
+ await publishWorktreeTopologyChange(repoRoot);
6517
+ }
6518
+
6406
6519
  if (previousRemote && previousMerge && normalizedNewName) {
6407
6520
  const previousMergeBranch = cleanBranchName(previousMerge);
6408
6521
  const nextMergeBranch =
@@ -6426,7 +6539,7 @@ export async function renameBranch(directory, oldName, newName) {
6426
6539
 
6427
6540
  return { success: true, branch: newName };
6428
6541
  } catch (error) {
6429
- console.error('Failed to rename branch:', error);
6542
+ if (error.statusCode !== 409) console.error('Failed to rename branch:', error);
6430
6543
  throw error;
6431
6544
  }
6432
6545
  }
@@ -6507,6 +6620,7 @@ export async function rebase(directory, options = {}) {
6507
6620
  }
6508
6621
  }
6509
6622
 
6623
+ /** @public */
6510
6624
  export async function abortRebase(directory) {
6511
6625
  const { git } = await createRepositoryGitContext(directory);
6512
6626
 
@@ -6555,6 +6669,7 @@ export async function merge(directory, options = {}) {
6555
6669
  }
6556
6670
  }
6557
6671
 
6672
+ /** @public */
6558
6673
  export async function abortMerge(directory) {
6559
6674
  const { git } = await createRepositoryGitContext(directory);
6560
6675
 
@@ -6712,6 +6827,7 @@ export async function getConflictDetails(directory) {
6712
6827
  }
6713
6828
  }
6714
6829
 
6830
+ /** @public */
6715
6831
  export async function getCommitFileDiff(directory, hash, filePath, isBinary) {
6716
6832
  if (!directory || !hash || !filePath) {
6717
6833
  throw new Error('directory, hash, and path are required for getCommitFileDiff');