commandmate 0.41.1 → 0.42.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 (346) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +134 -134
  3. package/.next/app-path-routes-manifest.json +24 -24
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +9 -9
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/api/agents/update/route.js +1 -1
  9. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/agents/versions/route.js +1 -1
  11. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/app/update-check/route.js +1 -1
  15. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/capabilities/route.js +1 -1
  20. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  29. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  30. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  32. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  33. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/hooks/permission-request/route.js +1 -1
  35. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  36. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  43. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  44. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/relays/route.js +1 -1
  46. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  47. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  51. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  52. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/repositories/clone/route.js +1 -1
  54. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  55. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/repositories/restore/route.js +1 -1
  58. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  59. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/repositories/route.js +1 -1
  61. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  62. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/repositories/scan/route.js +1 -1
  64. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  65. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/repositories/sync/route.js +1 -1
  67. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  68. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/settings/default-agents/route.js +1 -1
  71. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  72. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
  81. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  90. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  93. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  96. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  102. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
  105. package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  135. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  138. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  141. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  153. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  157. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/route.js +103 -1
  159. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  160. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/send/route.js +103 -1
  166. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  167. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +1 -103
  169. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  170. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  177. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
  179. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  182. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  184. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -103
  186. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  187. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  190. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  191. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  193. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  197. package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  201. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  206. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/route.js +1 -1
  208. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  209. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  211. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  212. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  213. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  214. package/.next/server/app/page_client-reference-manifest.js +1 -1
  215. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  216. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  217. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  218. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  219. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  220. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  221. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  222. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  223. package/.next/server/app/worktrees/[id]/page.js +9 -9
  224. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  225. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  226. package/.next/server/app-paths-manifest.json +24 -24
  227. package/.next/server/chunks/1068.js +1 -0
  228. package/.next/server/chunks/1210.js +1 -1
  229. package/.next/server/chunks/1501.js +5 -5
  230. package/.next/server/chunks/1990.js +103 -0
  231. package/.next/server/chunks/2119.js +1 -1
  232. package/.next/server/chunks/2404.js +1 -1
  233. package/.next/server/chunks/2445.js +1 -1
  234. package/.next/server/chunks/3241.js +1 -1
  235. package/.next/server/chunks/3707.js +1 -1
  236. package/.next/server/chunks/3718.js +1 -0
  237. package/.next/server/chunks/434.js +1 -1
  238. package/.next/server/chunks/4443.js +1 -1
  239. package/.next/server/chunks/5164.js +1 -1
  240. package/.next/server/chunks/5775.js +1 -0
  241. package/.next/server/chunks/611.js +1 -0
  242. package/.next/server/chunks/6130.js +1 -1
  243. package/.next/server/chunks/650.js +1 -1
  244. package/.next/server/chunks/6781.js +1 -1
  245. package/.next/server/chunks/7905.js +1 -1
  246. package/.next/server/chunks/8581.js +1 -1
  247. package/.next/server/chunks/8583.js +1 -1
  248. package/.next/server/chunks/8673.js +2 -2
  249. package/.next/server/chunks/8728.js +3 -3
  250. package/.next/server/chunks/8791.js +1 -0
  251. package/.next/server/chunks/8800.js +1 -1
  252. package/.next/server/chunks/8918.js +1 -103
  253. package/.next/server/chunks/9130.js +2 -2
  254. package/.next/server/chunks/964.js +1 -1
  255. package/.next/server/chunks/9731.js +3 -3
  256. package/.next/server/chunks/9882.js +3 -3
  257. package/.next/server/middleware-manifest.json +5 -5
  258. package/.next/server/pages/500.html +1 -1
  259. package/.next/server/server-reference-manifest.json +1 -1
  260. package/.next/static/chunks/{2407-7bd75de2f6be3692.js → 2407-21fbf12d753b8d4d.js} +1 -1
  261. package/.next/static/chunks/7705-2a2936551b0cf9a3.js +1 -0
  262. package/.next/static/chunks/8165-934ee7ff3b4ddfd4.js +1 -0
  263. package/.next/static/chunks/app/{layout-51fa91ea833c97fc.js → layout-8ff4c7c08ef1f621.js} +1 -1
  264. package/.next/static/chunks/app/worktrees/[id]/page-3c946bd2875391fd.js +1 -0
  265. package/.next/static/css/{974314e796232f53.css → b3f433d52b8ed5c8.css} +1 -1
  266. package/.next/trace +13 -13
  267. package/dist/cli/commands/attach.d.ts +6 -4
  268. package/dist/cli/commands/attach.d.ts.map +1 -1
  269. package/dist/cli/commands/attach.js +36 -5
  270. package/dist/cli/commands/instances.d.ts.map +1 -1
  271. package/dist/cli/commands/instances.js +5 -0
  272. package/dist/cli/commands/ls.d.ts.map +1 -1
  273. package/dist/cli/commands/ls.js +19 -9
  274. package/dist/cli/commands/whoami.d.ts +9 -25
  275. package/dist/cli/commands/whoami.d.ts.map +1 -1
  276. package/dist/cli/commands/whoami.js +18 -42
  277. package/dist/cli/types/api-responses.d.ts +37 -0
  278. package/dist/cli/types/api-responses.d.ts.map +1 -1
  279. package/dist/lib/cli-tools/session-name.d.ts +72 -4
  280. package/dist/lib/cli-tools/session-name.d.ts.map +1 -1
  281. package/dist/lib/cli-tools/session-name.js +115 -12
  282. package/dist/lib/detection/tools/verified-against.d.ts +11 -2
  283. package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
  284. package/dist/lib/detection/tools/verified-against.js +11 -2
  285. package/dist/lib/session/tmux-session-surface.d.ts +9 -7
  286. package/dist/lib/session/tmux-session-surface.d.ts.map +1 -1
  287. package/dist/lib/session/tmux-session-surface.js +15 -8
  288. package/dist/lib/tmux/legacy-session-alias.d.ts +43 -0
  289. package/dist/lib/tmux/legacy-session-alias.d.ts.map +1 -0
  290. package/dist/lib/tmux/legacy-session-alias.js +64 -0
  291. package/dist/lib/verification/verify-config.d.ts +21 -0
  292. package/dist/lib/verification/verify-config.d.ts.map +1 -1
  293. package/dist/lib/verification/verify-config.js +54 -1
  294. package/dist/server/locales/en/worktree.json +60 -17
  295. package/dist/server/locales/ja/worktree.json +63 -20
  296. package/dist/server/server.js +29 -0
  297. package/dist/server/src/lib/auto-yes-poller.js +15 -0
  298. package/dist/server/src/lib/cli-tools/antigravity.js +1 -1
  299. package/dist/server/src/lib/cli-tools/base.js +10 -1
  300. package/dist/server/src/lib/cli-tools/codex.js +1 -1
  301. package/dist/server/src/lib/cli-tools/command-code.js +1 -1
  302. package/dist/server/src/lib/cli-tools/copilot.js +1 -1
  303. package/dist/server/src/lib/cli-tools/gemini.js +1 -1
  304. package/dist/server/src/lib/cli-tools/legacy-session-adoption.js +19 -0
  305. package/dist/server/src/lib/cli-tools/opencode.js +1 -1
  306. package/dist/server/src/lib/cli-tools/session-name.js +115 -12
  307. package/dist/server/src/lib/cli-tools/session-namespace.js +55 -0
  308. package/dist/server/src/lib/cli-tools/session-ownership.js +21 -0
  309. package/dist/server/src/lib/cli-tools/vibe-local.js +1 -1
  310. package/dist/server/src/lib/cli-tools/worktree-session-ownership.js +28 -0
  311. package/dist/server/src/lib/db/app-settings-db.js +29 -0
  312. package/dist/server/src/lib/detection/cli-patterns.js +51 -10
  313. package/dist/server/src/lib/detection/tools/codex/cli-patterns.js +8 -2
  314. package/dist/server/src/lib/detection/tools/codex/detect.js +8 -1
  315. package/dist/server/src/lib/detection/tools/codex/prompt.js +6 -2
  316. package/dist/server/src/lib/detection/tools/verified-against.js +11 -2
  317. package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +77 -2
  318. package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +77 -1
  319. package/dist/server/src/lib/realtime/terminal-broadcast.js +11 -0
  320. package/dist/server/src/lib/session/adopt-legacy-sessions.js +89 -0
  321. package/dist/server/src/lib/session/claude-session.js +13 -10
  322. package/dist/server/src/lib/session/current-output-builder.js +18 -0
  323. package/dist/server/src/lib/session/send-user-message.js +26 -0
  324. package/dist/server/src/lib/session/session-instance-targets.js +53 -0
  325. package/dist/server/src/lib/session/tmux-session-surface.js +15 -8
  326. package/dist/server/src/lib/session/worktree-session-reconcile.js +101 -56
  327. package/dist/server/src/lib/session-cleanup.js +33 -3
  328. package/dist/server/src/lib/tmux/legacy-session-alias.js +64 -0
  329. package/dist/server/src/lib/tmux/session-hook-scripts.js +12 -4
  330. package/dist/server/src/lib/tmux/session-ownership.js +174 -0
  331. package/dist/server/src/lib/tmux/tmux.js +13 -3
  332. package/dist/server/src/lib/verification/env-clean-gate.js +46 -14
  333. package/dist/server/src/lib/verification/gate-runner.js +1 -0
  334. package/dist/server/src/lib/verification/verify-config.js +54 -1
  335. package/dist/server/src/lib/ws-server.js +11 -0
  336. package/package.json +1 -1
  337. package/release-notes/0.42.0.json +62 -0
  338. package/release-notes/0.42.1.json +16 -0
  339. package/.next/server/chunks/3323.js +0 -1
  340. package/.next/server/chunks/7075.js +0 -1
  341. package/.next/server/chunks/9080.js +0 -1
  342. package/.next/static/chunks/7705-25076b57224da370.js +0 -1
  343. package/.next/static/chunks/8165-d3b7c8a7aa776bc7.js +0 -1
  344. package/.next/static/chunks/app/worktrees/[id]/page-1e6300dd50cd9a4d.js +0 -1
  345. /package/.next/static/{_fBjTkjoWfOqdjl2SeMIN → 3M_S0B7dhEDdVE2XSJz1Y}/_buildManifest.js +0 -0
  346. /package/.next/static/{_fBjTkjoWfOqdjl2SeMIN → 3M_S0B7dhEDdVE2XSJz1Y}/_ssgManifest.js +0 -0
@@ -74,11 +74,15 @@ exports.reconcileWorktreeSessions = reconcileWorktreeSessions;
74
74
  exports.reconcileWorktreeSessionsFromAliases = reconcileWorktreeSessionsFromAliases;
75
75
  const manager_1 = require("../../lib/cli-tools/manager");
76
76
  const types_1 = require("../../lib/cli-tools/types");
77
- const validation_1 = require("../../lib/cli-tools/validation");
78
77
  const agent_instances_db_1 = require("../../lib/db/agent-instances-db");
78
+ const validation_1 = require("../../lib/cli-tools/validation");
79
+ const session_name_1 = require("../../lib/cli-tools/session-name");
80
+ const session_namespace_1 = require("../../lib/cli-tools/session-namespace");
81
+ const session_instance_targets_1 = require("./session-instance-targets");
79
82
  const worktree_alias_db_1 = require("../../lib/db/worktree-alias-db");
80
83
  const path_validator_1 = require("../../lib/security/path-validator");
81
84
  const tmux_1 = require("../../lib/tmux/tmux");
85
+ const session_ownership_1 = require("../../lib/tmux/session-ownership");
82
86
  const control_mode_tmux_transport_1 = require("../../lib/tmux/control-mode-tmux-transport");
83
87
  const auto_yes_manager_1 = require("../../lib/polling/auto-yes-manager");
84
88
  const response_poller_1 = require("../../lib/polling/response-poller");
@@ -131,44 +135,16 @@ function normalizeRenames(renames) {
131
135
  * The roster is read for BOTH IDs because the caller may reconcile either side
132
136
  * of the DB move: after `migrateWorktreeIdPreservingChildren` the rows sit under
133
137
  * `newId`, but a caller reconciling ahead of the move still finds them under
134
- * `oldId`. The primary instance of every CLI tool is always included — a
135
- * worktree that predates the roster (#1000) has no `agent_instances` rows at all
136
- * yet can absolutely have a running `mcbd-claude-<id>` session.
138
+ * `oldId`.
137
139
  */
138
- function collectInstanceTargets(db, rename) {
139
- const seen = new Set();
140
- const targets = [];
141
- const add = (cliToolId, instanceId) => {
142
- const key = `${cliToolId}|${instanceId}`;
143
- if (seen.has(key))
144
- return;
145
- seen.add(key);
146
- targets.push({ cliToolId, instanceId });
147
- };
148
- for (const cliToolId of types_1.CLI_TOOL_IDS)
149
- add(cliToolId, cliToolId);
150
- for (const worktreeId of [rename.newId, rename.oldId]) {
151
- let instances;
152
- try {
153
- instances = (0, agent_instances_db_1.getAgentInstances)(db, worktreeId);
154
- }
155
- catch {
156
- // No agent_instances table (older database) — the primary instances above
157
- // already cover the pre-roster shape.
158
- continue;
159
- }
160
- for (const instance of instances) {
161
- if ((0, types_1.isCliToolType)(instance.cliTool))
162
- add(instance.cliTool, instance.id);
163
- }
164
- }
165
- return targets;
140
+ function collectRenameInstanceTargets(db, rename) {
141
+ return (0, session_instance_targets_1.collectInstanceTargets)(db, [rename.newId, rename.oldId]);
166
142
  }
167
143
  /** Build the (old session name → new session name) plan for one ID move. */
168
144
  function planSessionRenames(db, rename, errors) {
169
145
  const manager = manager_1.CLIToolManager.getInstance();
170
146
  const plans = [];
171
- for (const { cliToolId, instanceId } of collectInstanceTargets(db, rename)) {
147
+ for (const { cliToolId, instanceId } of collectRenameInstanceTargets(db, rename)) {
172
148
  try {
173
149
  const tool = manager.getTool(cliToolId);
174
150
  const oldName = tool.getSessionName(rename.oldId, instanceId);
@@ -243,27 +219,33 @@ function collectKnownWorktreeIds(db, renames) {
243
219
  * @param knownIds - Every worktree ID the database can vouch for
244
220
  * @returns The attribution, or `null` when nothing in `knownIds` explains the name
245
221
  */
246
- function attributeSessionName(name, knownIds) {
247
- for (const cliToolId of types_1.CLI_TOOL_IDS) {
248
- const prefix = `mcbd-${cliToolId}-`;
249
- if (!name.startsWith(prefix))
250
- continue;
251
- // No CLI tool id is a prefix of another, so at most one branch is entered
252
- // and the remainder is unambiguously `<id>[-<suffix>]`.
253
- const rest = name.slice(prefix.length);
254
- let candidate = rest;
255
- for (;;) {
256
- if (knownIds.has(candidate)) {
257
- const suffix = candidate.length === rest.length ? undefined : rest.slice(candidate.length + 1);
258
- return { cliToolId, worktreeId: candidate, suffix: suffix || undefined };
259
- }
260
- const cut = candidate.lastIndexOf('-');
261
- if (cut <= 0)
262
- return null;
263
- candidate = candidate.slice(0, cut);
222
+ function attributeSessionName(name, knownIds, namespace = (0, session_namespace_1.getSessionNamespace)()) {
223
+ // Issue #2866: legacy names and names in this server's namespace only. A name
224
+ // in another namespace is another server's session, whatever ID it carries.
225
+ const parsed = (0, session_name_1.parseSessionName)(name);
226
+ if (!parsed)
227
+ return null;
228
+ if (parsed.namespace !== null && parsed.namespace !== namespace)
229
+ return null;
230
+ // No CLI tool id is a prefix of another, so the remainder is unambiguously
231
+ // `<id>[-<suffix>]`.
232
+ const { cliToolId, rest } = parsed;
233
+ let candidate = rest;
234
+ for (;;) {
235
+ if (knownIds.has(candidate)) {
236
+ const suffix = candidate.length === rest.length ? undefined : rest.slice(candidate.length + 1);
237
+ return { cliToolId, worktreeId: candidate, suffix: suffix || undefined };
264
238
  }
239
+ const cut = candidate.lastIndexOf('-');
240
+ if (cut <= 0)
241
+ return null;
242
+ candidate = candidate.slice(0, cut);
265
243
  }
266
- return null;
244
+ }
245
+ /** Whether a live name is another server's namespaced session (Issue #2866). */
246
+ function isOtherNamespaceSession(name, namespace) {
247
+ const parsed = (0, session_name_1.parseSessionName)(name);
248
+ return parsed !== null && parsed.namespace !== null && parsed.namespace !== namespace;
267
249
  }
268
250
  /**
269
251
  * Read the live session list the other way round: attribute each name to a
@@ -277,10 +259,14 @@ function attributeSessionName(name, knownIds) {
277
259
  function discoverSessionRenames(renames, liveNames, knownIds, result) {
278
260
  const byOldId = new Map(renames.map((rename) => [rename.oldId, rename]));
279
261
  const plans = [];
262
+ const namespace = (0, session_namespace_1.getSessionNamespace)();
280
263
  for (const name of liveNames) {
281
264
  if (!name.startsWith('mcbd-'))
282
265
  continue;
283
- const attributed = attributeSessionName(name, knownIds);
266
+ // Issue #2866: another server's namespace — neither attributed nor reported.
267
+ if (isOtherNamespaceSession(name, namespace))
268
+ continue;
269
+ const attributed = attributeSessionName(name, knownIds, namespace);
284
270
  if (!attributed) {
285
271
  result.unaccountedSessions.push(name);
286
272
  continue;
@@ -291,7 +277,8 @@ function discoverSessionRenames(renames, liveNames, knownIds, result) {
291
277
  try {
292
278
  const tool = manager_1.CLIToolManager.getInstance().getTool(attributed.cliToolId);
293
279
  // Rebuilt through getSessionName rather than by string surgery so the
294
- // suffix convention stays owned by one place (`cli-tools/base.ts`).
280
+ // suffix convention stays owned by one place (`cli-tools/session-name.ts`).
281
+ // A legacy-form source lands on the current form (Issue #2866).
295
282
  const instanceId = attributed.suffix
296
283
  ? (0, types_1.buildInstanceId)(attributed.cliToolId, attributed.suffix)
297
284
  : attributed.cliToolId;
@@ -322,6 +309,58 @@ function mergeSessionPlans(predicted, discovered) {
322
309
  }
323
310
  return Array.from(byOldName.values());
324
311
  }
312
+ /**
313
+ * The path of a worktree row, or null. Queried directly for the same reason
314
+ * {@link collectKnownWorktreeIds} is (suites mock `worktree-db`).
315
+ */
316
+ function readWorktreePath(db, worktreeId) {
317
+ try {
318
+ const row = db.prepare('SELECT path FROM worktrees WHERE id = ?').get(worktreeId);
319
+ return row?.path ?? null;
320
+ }
321
+ catch {
322
+ return null;
323
+ }
324
+ }
325
+ /**
326
+ * Drop the plans whose live source session another CommandMate server created
327
+ * (Issue #2865).
328
+ *
329
+ * Session names carry no server identity, so a live `mcbd-claude-<old>` may be
330
+ * a different server's session that merely shares the worktree ID. Only a
331
+ * session whose `#{session_path}` is the worktree's own directory is renamed;
332
+ * the rest are reported in `skippedSessions`. The directory comes from the
333
+ * rename target's row, falling back to the source's row when the pass runs
334
+ * ahead of the DB move. A plan whose worktree has no row at all cannot be
335
+ * vouched for and is skipped too (fail safe).
336
+ */
337
+ function excludeForeignSessionPlans(db, plans, live, livePaths, deps, result) {
338
+ const owned = [];
339
+ for (const plan of plans) {
340
+ // Not live: renameSessionsTwoStage drops it without counting it.
341
+ if (!live.has(plan.oldName)) {
342
+ owned.push(plan);
343
+ continue;
344
+ }
345
+ const worktreePath = readWorktreePath(db, plan.rename.newId) ?? readWorktreePath(db, plan.rename.oldId);
346
+ const sessionPath = livePaths.get(plan.oldName) ?? null;
347
+ if (worktreePath !== null && deps.isSessionOwnedBy(sessionPath, worktreePath)) {
348
+ owned.push(plan);
349
+ continue;
350
+ }
351
+ result.skippedSessions.push({
352
+ oldName: plan.oldName,
353
+ newName: plan.newName,
354
+ reason: 'session owned by another server',
355
+ });
356
+ logger.warn('reconcile:foreign-session-skipped', {
357
+ sessionName: plan.oldName,
358
+ sessionPath,
359
+ worktreePath,
360
+ });
361
+ }
362
+ return owned;
363
+ }
325
364
  /** Mint a session name that is free both on the server and within this pass. */
326
365
  function allocateTempName(taken, ordinal) {
327
366
  let candidate = `${TEMP_SESSION_PREFIX}${ordinal}`;
@@ -501,13 +540,18 @@ async function reconcileWorktreeSessions(db, renamesOrOldId, newIdOrOptions, may
501
540
  const deps = {
502
541
  listSessions: options?.tmux?.listSessions ?? tmux_1.listSessions,
503
542
  renameSession: options?.tmux?.renameSession ?? tmux_1.renameSession,
543
+ isSessionOwnedBy: options?.tmux?.isSessionOwnedBy ?? (0, session_ownership_1.createCachedOwnershipMatcher)(),
504
544
  };
505
545
  // Listed once, up front: both the prediction pass and the attribution pass
506
546
  // need it, and it is what makes a startup with nothing stale cost exactly one
507
547
  // `tmux list-sessions`.
508
548
  let live = null;
549
+ const livePaths = new Map();
509
550
  try {
510
- live = new Set((await deps.listSessions()).map((session) => session.name));
551
+ const sessions = await deps.listSessions();
552
+ live = new Set(sessions.map((session) => session.name));
553
+ for (const session of sessions)
554
+ livePaths.set(session.name, session.path);
511
555
  }
512
556
  catch (error) {
513
557
  result.errors.push(`list tmux sessions: ${error instanceof Error ? error.message : String(error)}`);
@@ -527,7 +571,8 @@ async function reconcileWorktreeSessions(db, renamesOrOldId, newIdOrOptions, may
527
571
  else
528
572
  result.planSources.discovered++;
529
573
  }
530
- await renameSessionsTwoStage(plans, live, deps, result);
574
+ const ownedPlans = excludeForeignSessionPlans(db, plans, live, livePaths, deps, result);
575
+ await renameSessionsTwoStage(ownedPlans, live, deps, result);
531
576
  }
532
577
  // Runtime state moves after the sessions: a poller that ticks in between
533
578
  // fails one capture against a name that is briefly gone, which is a logged
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.cleanupWorktreeSessions = cleanupWorktreeSessions;
13
13
  exports.cleanupMultipleWorktrees = cleanupMultipleWorktrees;
14
14
  exports.killWorktreeSession = killWorktreeSession;
15
+ exports.createOwnedSessionKiller = createOwnedSessionKiller;
15
16
  exports.cleanupGlobalSessions = cleanupGlobalSessions;
16
17
  exports.syncWorktreesAndCleanup = syncWorktreesAndCleanup;
17
18
  const response_poller_1 = require("./polling/response-poller");
@@ -27,6 +28,8 @@ const logger_1 = require("../lib/logger");
27
28
  const manager_1 = require("./cli-tools/manager");
28
29
  const tmux_1 = require("./tmux/tmux");
29
30
  const worktrees_1 = require("./git/worktrees");
31
+ const worktree_db_1 = require("./db/worktree-db");
32
+ const session_ownership_1 = require("./cli-tools/session-ownership");
30
33
  const logger = (0, logger_1.createLogger)('session-cleanup');
31
34
  /**
32
35
  * Clean up all CLI tool sessions and pollers for a single worktree
@@ -164,26 +167,50 @@ async function cleanupMultipleWorktrees(worktreeIds, killSessionFn) {
164
167
  * MF-C01: getTool() throws Error if tool not found, wrapped in try-catch
165
168
  * SF-004: isRunning() correctly awaited
166
169
  *
170
+ * Issue #2865: only a session created in `worktreePath` is killed. A
171
+ * same-named session another CommandMate server created (the worktree ID is
172
+ * derived from the directory name, so two servers can share it) is left alone
173
+ * and `false` is returned.
174
+ *
167
175
  * @param worktreeId - Worktree ID
168
176
  * @param cliToolId - CLI tool type
177
+ * @param worktreePath - The worktree's directory, read from its row BEFORE the row is deleted
169
178
  * @returns true if session was killed, false otherwise
170
179
  */
171
- async function killWorktreeSession(worktreeId, cliToolId) {
180
+ async function killWorktreeSession(worktreeId, cliToolId, worktreePath) {
172
181
  try {
173
182
  const manager = manager_1.CLIToolManager.getInstance();
174
183
  const tool = manager.getTool(cliToolId); // throws Error if not found
175
184
  if (!await tool.isRunning(worktreeId))
176
185
  return false; // SF-004: await
186
+ const sessionName = tool.getSessionName(worktreeId);
187
+ const ownership = await (0, session_ownership_1.checkSessionOwnership)(sessionName, worktreePath);
188
+ if (ownership.verdict !== 'owned')
189
+ return false;
177
190
  // Issue #565: Clear prompt dedup cache on session kill
178
191
  const pollerKey = `${worktreeId}:${cliToolId}`;
179
192
  (0, response_poller_1.clearPromptHashCache)(pollerKey);
180
- const sessionName = tool.getSessionName(worktreeId);
181
193
  return (0, tmux_1.killSession)(sessionName);
182
194
  }
183
195
  catch {
184
196
  return false;
185
197
  }
186
198
  }
199
+ /**
200
+ * A {@link KillSessionFn} that kills through {@link killWorktreeSession} with
201
+ * each worktree's pre-deletion path (Issue #2865). A worktree with no known
202
+ * path cannot be vouched for, so nothing is killed for it.
203
+ *
204
+ * @param pathById - worktree ID -> path, captured before the rows are deleted
205
+ */
206
+ function createOwnedSessionKiller(pathById) {
207
+ return async (worktreeId, cliToolId) => {
208
+ const worktreePath = pathById.get(worktreeId);
209
+ if (worktreePath === undefined)
210
+ return false;
211
+ return killWorktreeSession(worktreeId, cliToolId, worktreePath);
212
+ };
213
+ }
187
214
  /**
188
215
  * Clean up all global assistant sessions.
189
216
  * Issue #649: Kill any orphaned mcbd-{cli_tool_id}-__global__ tmux sessions
@@ -233,6 +260,9 @@ async function cleanupGlobalSessions() {
233
260
  * @returns SyncResult and sanitized cleanupWarnings
234
261
  */
235
262
  async function syncWorktreesAndCleanup(db, worktrees) {
263
+ // Issue #2865: the rows sync deletes are gone afterwards, and their paths are
264
+ // what decides which sessions are this server's to kill.
265
+ const pathById = new Map((0, worktree_db_1.getAllWorktreePathIds)(db).map((row) => [row.id, row.path]));
236
266
  const syncResult = (0, worktrees_1.syncWorktreesToDB)(db, worktrees);
237
267
  // Issue #649: Clean up orphaned global assistant sessions
238
268
  try {
@@ -244,7 +274,7 @@ async function syncWorktreesAndCleanup(db, worktrees) {
244
274
  let cleanupWarnings = [];
245
275
  if (syncResult.deletedIds.length > 0) {
246
276
  try {
247
- const cleanupResult = await cleanupMultipleWorktrees(syncResult.deletedIds, killWorktreeSession);
277
+ const cleanupResult = await cleanupMultipleWorktrees(syncResult.deletedIds, createOwnedSessionKiller(pathById));
248
278
  if (cleanupResult.warnings.length > 0) {
249
279
  // SEC-MF-001: Log detailed warnings server-side only
250
280
  logger.warn('sync:cleanup-warnings', { warnings: cleanupResult.warnings });
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Legacy tmux session adoption table (Issue #2866).
4
+ *
5
+ * Once the server has a session-name namespace, a session is named
6
+ * `mcbd-{ns}-{cli}-{worktreeId}[-{suffix}]`. Sessions the same server started
7
+ * before that (`mcbd-{cli}-{worktreeId}[-{suffix}]`) are NOT renamed — a rename
8
+ * would lose them for an older CommandMate running beside this one, or after a
9
+ * downgrade. Instead `adoptLegacySessions` records "the new-format name X is
10
+ * served by the legacy session Y", and `resolveSessionName` returns Y for X
11
+ * while the entry lives.
12
+ *
13
+ * An entry is dropped when its legacy session is killed or found gone
14
+ * (`tmux.ts` `killSession` / `hasSession`), so the next start uses the
15
+ * new-format name.
16
+ *
17
+ * Pure (no `@/` alias, no child_process): `cli-tools/session-name.ts` reads it,
18
+ * and that module is part of the CLI bundle (`tsconfig.cli.json` has no paths).
19
+ *
20
+ * Kept on `globalThis` rather than in module scope: under `next start` this
21
+ * module is evaluated once in the custom server's graph and again in each Next
22
+ * route bundle (see `tui-accumulator.ts`, Issue #2223), and the table is filled
23
+ * by the server at startup but read by the routes.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.registerLegacyAlias = registerLegacyAlias;
27
+ exports.lookupLegacyAlias = lookupLegacyAlias;
28
+ exports.dropLegacyAliasByLegacyName = dropLegacyAliasByLegacyName;
29
+ exports.clearLegacyAliasesForTests = clearLegacyAliasesForTests;
30
+ function aliases() {
31
+ return (globalThis.__cmLegacySessionAliases ??= new Map());
32
+ }
33
+ /**
34
+ * Serve the new-format session name `newName` with the existing legacy session
35
+ * `legacyName`.
36
+ */
37
+ function registerLegacyAlias(newName, legacyName) {
38
+ aliases().set(newName, legacyName);
39
+ }
40
+ /** The legacy session name adopted for `newName`, if any. */
41
+ function lookupLegacyAlias(newName) {
42
+ return aliases().get(newName);
43
+ }
44
+ /**
45
+ * Forget every adoption served by the legacy session `legacyName`. A no-op for
46
+ * a name that is not an adopted legacy session.
47
+ *
48
+ * @returns true when an entry was removed
49
+ */
50
+ function dropLegacyAliasByLegacyName(legacyName) {
51
+ const table = aliases();
52
+ let dropped = false;
53
+ for (const [newName, legacy] of table) {
54
+ if (legacy === legacyName) {
55
+ table.delete(newName);
56
+ dropped = true;
57
+ }
58
+ }
59
+ return dropped;
60
+ }
61
+ /** Test-only: forget every adoption. */
62
+ function clearLegacyAliasesForTests() {
63
+ aliases().clear();
64
+ }
@@ -114,12 +114,20 @@ tmux set-option -u -t "=$SESSION:" @cm_delegated 2>/dev/null || true
114
114
  * `CM_LIVE_ATTACH_HOOK=on`.
115
115
  *
116
116
  * claude-only by session-name prefix, and that is not a shortcut. The session
117
- * name is `mcbd-<tool>-<worktree>`, no other CLI tool id begins with `claude`,
118
- * and the script has nothing but the name to go on — a hook fires with tmux
119
- * formats, not with CommandMate's roster. Widening it means widening
117
+ * name is `mcbd-<ns>-<tool>-<worktree>` or the legacy `mcbd-<tool>-<worktree>`
118
+ * (Issue #2866), no other CLI tool id begins with `claude`, and the script has
119
+ * nothing but the name to go on — a hook fires with tmux formats, not with
120
+ * CommandMate's roster. The namespace is matched as exactly eight hex digits
121
+ * (Issue #2867), not `*`: `mcbd-*-claude-*` would also match a legacy codex
122
+ * session of a worktree whose id starts with `claude-`. Widening it means widening
120
123
  * `LIVE_ATTACH_TOOLS` too, and that needs the per-tool re-measurement
121
124
  * Issue #2317 puts out of scope.
122
125
  */
126
+ /**
127
+ * A session-name namespace as a shell `case` glob: eight hex digits, the shape
128
+ * `SESSION_NAMESPACE_PATTERN` accepts (Issue #2866 / #2867).
129
+ */
130
+ const NAMESPACE_GLOB = '[0-9a-f]'.repeat(8);
123
131
  exports.LIVE_DELEGATE_SCRIPT = `#!/bin/sh
124
132
  # CommandMate live-attach delegate (Issue #2317) — generated file, edits are overwritten.
125
133
  #
@@ -141,7 +149,7 @@ SESSION="\${1:-}"
141
149
  CONTROL_MODE="\${2:-0}"
142
150
 
143
151
  case "$SESSION" in
144
- ${tmux_session_surface_1.MCBD_SESSION_PREFIX}claude-*) ;;
152
+ ${tmux_session_surface_1.MCBD_SESSION_PREFIX}claude-*|${tmux_session_surface_1.MCBD_SESSION_PREFIX}${NAMESPACE_GLOB}-claude-*) ;;
145
153
  *) exit 0 ;;
146
154
  esac
147
155
 
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * tmux session ownership (Issue #2865)
4
+ *
5
+ * Session names (`mcbd-{cli}-{worktreeId}`) carry nothing that identifies the
6
+ * CommandMate server that created them, and worktree IDs are derived from the
7
+ * directory name — so two servers with different DBs and different worktree
8
+ * paths can resolve the SAME session name on the shared default tmux socket.
9
+ * Deciding "is it running?" by name alone then shows, types into, kills and
10
+ * adopts the OTHER server's session.
11
+ *
12
+ * CommandMate always creates sessions with `tmux new-session -c <worktree.path>`,
13
+ * so `#{session_path}` records which worktree a session belongs to. A session is
14
+ * "owned" only when the realpath of its `#{session_path}` equals the realpath of
15
+ * the worktree's path; anything else under the same name is "foreign" and is
16
+ * excluded from display, input, stop and reuse.
17
+ *
18
+ * A worktree whose directory has since moved therefore sees its old session as
19
+ * foreign. That is intentional (fail safe).
20
+ */
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.ForeignSessionError = exports.FOREIGN_SESSION_ERROR_CODE = void 0;
26
+ exports.normalizeOwnershipPath = normalizeOwnershipPath;
27
+ exports.isSessionPathOwnedBy = isSessionPathOwnedBy;
28
+ exports.createCachedOwnershipMatcher = createCachedOwnershipMatcher;
29
+ exports.reportForeignSession = reportForeignSession;
30
+ exports.resetForeignSessionWarningsForTesting = resetForeignSessionWarningsForTesting;
31
+ exports.checkSessionOwnership = checkSessionOwnership;
32
+ exports.assertSessionNotForeign = assertSessionNotForeign;
33
+ exports.foreignSessionErrorBody = foreignSessionErrorBody;
34
+ exports.ownedSessionNameSet = ownedSessionNameSet;
35
+ const fs_1 = __importDefault(require("fs"));
36
+ const path_1 = __importDefault(require("path"));
37
+ const logger_1 = require("../../lib/logger");
38
+ const tmux_1 = require("./tmux");
39
+ const logger = (0, logger_1.createLogger)('session-ownership');
40
+ exports.FOREIGN_SESSION_ERROR_CODE = 'session_owned_by_other_server';
41
+ /**
42
+ * Thrown when a code path would adopt / drive a session another server owns.
43
+ */
44
+ class ForeignSessionError extends Error {
45
+ sessionName;
46
+ sessionPath;
47
+ worktreePath;
48
+ code = exports.FOREIGN_SESSION_ERROR_CODE;
49
+ constructor(sessionName, sessionPath, worktreePath) {
50
+ super(`tmux session "${sessionName}" belongs to another server ` +
51
+ `(session_path=${sessionPath ?? 'unknown'}, worktree=${worktreePath})`);
52
+ this.sessionName = sessionName;
53
+ this.sessionPath = sessionPath;
54
+ this.worktreePath = worktreePath;
55
+ this.name = 'ForeignSessionError';
56
+ }
57
+ }
58
+ exports.ForeignSessionError = ForeignSessionError;
59
+ /**
60
+ * Normalize a path for ownership comparison: realpath when it resolves,
61
+ * otherwise `path.resolve()`; trailing `/` removed; case preserved.
62
+ */
63
+ function normalizeOwnershipPath(p) {
64
+ let resolved;
65
+ try {
66
+ resolved = fs_1.default.realpathSync.native(p);
67
+ }
68
+ catch {
69
+ resolved = path_1.default.resolve(p);
70
+ }
71
+ if (resolved.length > 1) {
72
+ resolved = resolved.replace(/\/+$/, '');
73
+ }
74
+ return resolved === '' ? '/' : resolved;
75
+ }
76
+ /**
77
+ * Pure comparison: is a session created in `sessionPath` owned by the worktree
78
+ * at `worktreePath`? `null` (tmux could not say) is never owned.
79
+ *
80
+ * @param normalize - Path normalizer; {@link createCachedOwnershipMatcher}
81
+ * passes a memoized one. Defaults to {@link normalizeOwnershipPath}.
82
+ */
83
+ function isSessionPathOwnedBy(sessionPath, worktreePath, normalize = normalizeOwnershipPath) {
84
+ if (sessionPath === null || sessionPath === '')
85
+ return false;
86
+ return normalize(sessionPath) === normalize(worktreePath);
87
+ }
88
+ /**
89
+ * Build an `isSessionPathOwnedBy` that memoizes realpath per input path. Meant
90
+ * to live for one request (e.g. the batch status listing), so a moved/created
91
+ * directory is picked up on the next request.
92
+ */
93
+ function createCachedOwnershipMatcher() {
94
+ const cache = new Map();
95
+ const norm = (p) => {
96
+ let v = cache.get(p);
97
+ if (v === undefined) {
98
+ v = normalizeOwnershipPath(p);
99
+ cache.set(p, v);
100
+ }
101
+ return v;
102
+ };
103
+ return (sessionPath, worktreePath) => isSessionPathOwnedBy(sessionPath, worktreePath, norm);
104
+ }
105
+ const warnedForeignSessions = new Set();
106
+ /**
107
+ * Log `session:foreign-detected` once per session name for the process lifetime.
108
+ */
109
+ function reportForeignSession(sessionName, sessionPath, worktreePath) {
110
+ if (warnedForeignSessions.has(sessionName))
111
+ return;
112
+ warnedForeignSessions.add(sessionName);
113
+ logger.warn('session:foreign-detected', { sessionName, sessionPath, worktreePath });
114
+ }
115
+ /** Test-only: forget which foreign sessions were already reported. */
116
+ function resetForeignSessionWarningsForTesting() {
117
+ warnedForeignSessions.clear();
118
+ }
119
+ /**
120
+ * Decide whether `sessionName` is absent, owned by the worktree at
121
+ * `worktreePath`, or foreign. A session that exists but whose
122
+ * `#{session_path}` cannot be read is treated as foreign (fail safe).
123
+ */
124
+ async function checkSessionOwnership(sessionName, worktreePath) {
125
+ if (!(await (0, tmux_1.hasSession)(sessionName))) {
126
+ return { verdict: 'absent', sessionPath: null };
127
+ }
128
+ const sessionPath = await (0, tmux_1.getSessionWorkingDirectory)(sessionName);
129
+ if (sessionPath !== null && isSessionPathOwnedBy(sessionPath, worktreePath)) {
130
+ return { verdict: 'owned', sessionPath };
131
+ }
132
+ reportForeignSession(sessionName, sessionPath, worktreePath);
133
+ return { verdict: 'foreign', sessionPath };
134
+ }
135
+ /**
136
+ * Throw `ForeignSessionError` when `sessionName` is foreign; otherwise return
137
+ * the ownership verdict (`owned` or `absent`).
138
+ */
139
+ async function assertSessionNotForeign(sessionName, worktreePath) {
140
+ const ownership = await checkSessionOwnership(sessionName, worktreePath);
141
+ if (ownership.verdict === 'foreign') {
142
+ throw new ForeignSessionError(sessionName, ownership.sessionPath, worktreePath);
143
+ }
144
+ return ownership;
145
+ }
146
+ /**
147
+ * JSON body for the HTTP 409 an API route returns for a foreign session.
148
+ */
149
+ function foreignSessionErrorBody(sessionName, sessionPath) {
150
+ return {
151
+ error: `tmux session "${sessionName}" belongs to another CommandMate server`,
152
+ code: exports.FOREIGN_SESSION_ERROR_CODE,
153
+ sessionName,
154
+ sessionPath,
155
+ };
156
+ }
157
+ /**
158
+ * From one batch `listSessions()` result, the names of the sessions the worktree
159
+ * at `worktreePath` owns. Feed this — not the raw name set — to status detection
160
+ * so a same-named foreign session never reads as "running". Other worktrees'
161
+ * sessions drop out too, which is harmless: status detection only looks up the
162
+ * worktree's own session names.
163
+ *
164
+ * @param matcher - Pass one `createCachedOwnershipMatcher()` across all worktrees
165
+ * of a request so each realpath is resolved once.
166
+ */
167
+ function ownedSessionNameSet(tmuxSessions, worktreePath, matcher = createCachedOwnershipMatcher()) {
168
+ const owned = new Set();
169
+ for (const session of tmuxSessions) {
170
+ if (matcher(session.path ?? null, worktreePath))
171
+ owned.add(session.name);
172
+ }
173
+ return owned;
174
+ }
@@ -30,6 +30,7 @@ const child_process_1 = require("child_process");
30
30
  const util_1 = require("util");
31
31
  const tmux_capture_cache_1 = require("./tmux-capture-cache");
32
32
  const geometry_delegation_1 = require("./geometry-delegation");
33
+ const legacy_session_alias_1 = require("./legacy-session-alias");
33
34
  const validation_1 = require("../../lib/cli-tools/validation");
34
35
  const tmux_pane_config_1 = require("../../config/tmux-pane-config");
35
36
  const logger_1 = require("../../lib/logger");
@@ -181,6 +182,9 @@ async function hasSession(sessionName) {
181
182
  }
182
183
  catch {
183
184
  // tmux has-session returns non-zero exit code if session doesn't exist
185
+ // Issue #2866: an adopted legacy session that is gone no longer serves its
186
+ // new-format name, so the next start uses the new-format name.
187
+ (0, legacy_session_alias_1.dropLegacyAliasByLegacyName)(sessionName);
184
188
  return false;
185
189
  }
186
190
  }
@@ -225,19 +229,23 @@ async function getSessionWorkingDirectory(sessionName) {
225
229
  */
226
230
  async function listSessions() {
227
231
  try {
228
- const { stdout } = await execFileAsync('tmux', ['list-sessions', '-F', '#{session_name}|#{session_windows}|#{session_attached}'], { timeout: DEFAULT_TIMEOUT });
232
+ // Issue #2865: `#{session_path}` is the LAST column and tab-separated, so a
233
+ // path containing `|` or spaces survives intact (everything after the third
234
+ // tab is the path).
235
+ const { stdout } = await execFileAsync('tmux', ['list-sessions', '-F', '#{session_name}\t#{session_windows}\t#{session_attached}\t#{session_path}'], { timeout: DEFAULT_TIMEOUT });
229
236
  if (!stdout || stdout.trim() === '') {
230
237
  return [];
231
238
  }
232
239
  return stdout
233
- .trim()
234
240
  .split('\n')
241
+ .filter(line => line.trim() !== '')
235
242
  .map(line => {
236
- const [name, windows, attached] = line.split('|');
243
+ const [name, windows, attached, ...rest] = line.split('\t');
237
244
  return {
238
245
  name,
239
246
  windows: parseInt(windows, 10) || 0,
240
247
  attached: attached === '1',
248
+ path: rest.join('\t'),
241
249
  };
242
250
  });
243
251
  }
@@ -628,6 +636,8 @@ async function killSession(sessionName) {
628
636
  await execFileAsync('tmux', ['kill-session', '-t', exactTarget(sessionName)], {
629
637
  timeout: DEFAULT_TIMEOUT,
630
638
  });
639
+ // Issue #2866: see hasSession.
640
+ (0, legacy_session_alias_1.dropLegacyAliasByLegacyName)(sessionName);
631
641
  return true;
632
642
  }
633
643
  catch (error) {