commandmate 0.32.0 → 0.32.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 (209) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +187 -187
  3. package/.next/app-path-routes-manifest.json +36 -36
  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_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  22. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  28. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/hooks/agent-event/route.js +1 -1
  31. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/hooks/claude-done/route.js +2 -2
  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_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
  64. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  70. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
  136. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  156. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  157. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  159. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  160. package/.next/server/app/page_client-reference-manifest.js +1 -1
  161. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  162. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  163. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  164. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  165. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  166. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  167. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  168. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  169. package/.next/server/app/worktrees/[id]/page.js +1 -1
  170. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  171. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  172. package/.next/server/app-paths-manifest.json +36 -36
  173. package/.next/server/chunks/1210.js +1 -1
  174. package/.next/server/chunks/5043.js +1 -1
  175. package/.next/server/chunks/5164.js +1 -1
  176. package/.next/server/chunks/6491.js +2 -2
  177. package/.next/server/chunks/6838.js +1 -1
  178. package/.next/server/chunks/7875.js +2 -2
  179. package/.next/server/chunks/8918.js +3 -3
  180. package/.next/server/chunks/9130.js +1 -1
  181. package/.next/server/middleware-manifest.json +5 -5
  182. package/.next/server/pages/500.html +1 -1
  183. package/.next/server/server-reference-manifest.json +1 -1
  184. package/.next/static/chunks/{4718-0053d42fb635e700.js → 4718-9d63107c612a065d.js} +1 -1
  185. package/.next/static/chunks/app/worktrees/[id]/{page-06e1ff5ce2d126fd.js → page-3d82a9355d5612d9.js} +1 -1
  186. package/.next/trace +13 -13
  187. package/README.md +6 -2
  188. package/dist/cli/commands/instances.d.ts.map +1 -1
  189. package/dist/cli/commands/instances.js +44 -15
  190. package/dist/cli/commands/peers.d.ts.map +1 -1
  191. package/dist/cli/commands/peers.js +12 -0
  192. package/dist/cli/commands/whoami.d.ts +67 -0
  193. package/dist/cli/commands/whoami.d.ts.map +1 -1
  194. package/dist/cli/commands/whoami.js +106 -0
  195. package/dist/cli/utils/api-client.d.ts +30 -1
  196. package/dist/cli/utils/api-client.d.ts.map +1 -1
  197. package/dist/cli/utils/api-client.js +28 -5
  198. package/dist/server/src/lib/detection/cli-patterns.js +206 -2
  199. package/dist/server/src/lib/hooks/agent-event-service.js +27 -5
  200. package/dist/server/src/lib/hooks/sources/codex/history.js +63 -3
  201. package/dist/server/src/lib/hooks/sources/codex/transcript.js +53 -1
  202. package/dist/server/src/lib/hooks/sources/launch-command.js +112 -15
  203. package/dist/server/src/lib/hooks/stop-history-capture.js +252 -50
  204. package/dist/server/src/lib/polling/response-checker.js +129 -5
  205. package/dist/server/src/lib/polling/response-dedup.js +136 -0
  206. package/dist/server/src/lib/relay/relay-delivery.js +160 -6
  207. package/package.json +1 -1
  208. /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → Lno4w_zWXdGdlfXg06Vp8}/_buildManifest.js +0 -0
  209. /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → Lno4w_zWXdGdlfXg06Vp8}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  **[commandmate website →](https://kewton.github.io/CommandMate/)**
13
13
 
14
14
  <p align="center">
15
- <img src="./docs/images/demo-desktop.gif" width="600" alt="A task contract is sent, the verification gates run, and the exit code returns RESULT passed" />
15
+ <img src="./docs/images/demo-hero.en.gif" width="600" alt="Switch repos from the tab bar, delegate to another agent from the chat, open the file it names, and do the same from a phone" />
16
16
  </p>
17
17
 
18
18
  > **From vibe coding to Vibe Engineering.**
@@ -30,8 +30,12 @@ npx commandmate@latest
30
30
  CommandMate adds the machinery — a contract before the work, verification gates after it, Skills that carry the method — on top of the agent CLIs you already use.
31
31
  It does not replace tmux, Git worktrees, your terminal, or your agent CLI. It puts a frame around them, so the work arrives verified instead of merely finished.
32
32
 
33
+ ## Verified, not vibe-checked
34
+
35
+ Gates you declared decide whether the work is done, and the exit code is the verdict.
36
+
33
37
  <p align="center">
34
- <img src="./docs/images/demo-mobile.gif" width="300" alt="A waiting agent reaches you, and the prompt is answered from a phone" />
38
+ <img src="./docs/images/demo-contract-verify.gif" width="600" alt="A task contract is sent, the verification gates run, and the exit code returns RESULT passed" />
35
39
  </p>
36
40
 
37
41
  Works on desktop and mobile — monitor and steer sessions from any browser, including your phone.
@@ -1 +1 @@
1
- {"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,SAAS,EAAkD,MAAM,qBAAqB,CAAC;AA8HhG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG7B;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAE6D,CAAC;AAErG,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAE0B,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAmCjC;AA2ND,wBAAgB,sBAAsB,IAAI,OAAO,CAqEhD"}
1
+ {"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,SAAS,EAAkD,MAAM,qBAAqB,CAAC;AAkIhG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG7B;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAE6D,CAAC;AAErG,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAE0B,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAmCjC;AAyPD,wBAAgB,sBAAsB,IAAI,OAAO,CAgFhD"}
@@ -22,6 +22,10 @@ const command_helpers_1 = require("../utils/command-helpers");
22
22
  const cli_tool_ids_1 = require("../config/cli-tool-ids");
23
23
  const agent_instances_1 = require("../utils/agent-instances");
24
24
  const session_target_1 = require("../utils/session-target");
25
+ // Issue #2404: "the worktree is not on this server" is a different failure from
26
+ // "the worktree id is wrong", and only the session's own identity can tell them
27
+ // apart.
28
+ const whoami_1 = require("./whoami");
25
29
  // Issue #2317: the tmux session name each instance runs in, so `commandmate
26
30
  // attach` / `tmux attach` need no hand-assembly of `mcbd-<tool>-<wt>[-<suffix>]`.
27
31
  const session_name_1 = require("../../lib/cli-tools/session-name");
@@ -243,8 +247,7 @@ async function fetchOpencodeSessions(client, worktreeId, instances) {
243
247
  * Probes GET .../current-output?cliTool=&instance= per instance (same
244
248
  * endpoint capture.ts uses) since the roster itself carries no session state.
245
249
  */
246
- async function listInstances(worktreeId, options) {
247
- const client = new api_client_1.ApiClient({ token: options.token });
250
+ async function listInstances(client, worktreeId, options) {
248
251
  const instances = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
249
252
  // Issue #2038: one extra request for the whole worktree, and only when there
250
253
  // is an opencode instance to ask about — the endpoint is opencode-only and a
@@ -286,7 +289,7 @@ async function listInstances(worktreeId, options) {
286
289
  /**
287
290
  * Add action: append a new instance to the roster (PATCH full replacement).
288
291
  */
289
- async function addInstance(worktreeId, options) {
292
+ async function addInstance(client, worktreeId, options) {
290
293
  if (!options.agent) {
291
294
  console.error('Error: add requires --agent <tool>.');
292
295
  process.exit(types_1.ExitCode.CONFIG_ERROR);
@@ -303,7 +306,6 @@ async function addInstance(worktreeId, options) {
303
306
  console.error(`Error: --alias exceeds ${agent_instances_1.MAX_AGENT_ALIAS_LENGTH} characters.`);
304
307
  process.exit(types_1.ExitCode.CONFIG_ERROR);
305
308
  }
306
- const client = new api_client_1.ApiClient({ token: options.token });
307
309
  const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
308
310
  if (existing.length >= agent_instances_1.MAX_AGENT_INSTANCES) {
309
311
  console.error(`Error: worktree already has the maximum of ${agent_instances_1.MAX_AGENT_INSTANCES} agent instances.`);
@@ -332,12 +334,11 @@ async function addInstance(worktreeId, options) {
332
334
  * Remove action: drop an instance from the roster. Kills the session first
333
335
  * (when --kill) so the server can still resolve its CLI tool from the roster.
334
336
  */
335
- async function removeInstance(worktreeId, instanceId, options) {
337
+ async function removeInstance(client, worktreeId, instanceId, options) {
336
338
  if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
337
339
  console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
338
340
  process.exit(types_1.ExitCode.CONFIG_ERROR);
339
341
  }
340
- const client = new api_client_1.ApiClient({ token: options.token });
341
342
  const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
342
343
  if (!existing.some(inst => inst.id === instanceId)) {
343
344
  console.error(`Error: instance '${instanceId}' not found in roster.`);
@@ -363,7 +364,7 @@ async function removeInstance(worktreeId, instanceId, options) {
363
364
  /**
364
365
  * Alias action: rename an existing instance's display label.
365
366
  */
366
- async function renameInstance(worktreeId, instanceId, alias, options) {
367
+ async function renameInstance(client, worktreeId, instanceId, alias) {
367
368
  if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
368
369
  console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
369
370
  process.exit(types_1.ExitCode.CONFIG_ERROR);
@@ -372,7 +373,6 @@ async function renameInstance(worktreeId, instanceId, alias, options) {
372
373
  console.error(`Error: alias must be 1-${agent_instances_1.MAX_AGENT_ALIAS_LENGTH} characters.`);
373
374
  process.exit(types_1.ExitCode.CONFIG_ERROR);
374
375
  }
375
- const client = new api_client_1.ApiClient({ token: options.token });
376
376
  const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
377
377
  const index = existing.findIndex(inst => inst.id === instanceId);
378
378
  if (index === -1) {
@@ -388,15 +388,33 @@ async function renameInstance(worktreeId, instanceId, alias, options) {
388
388
  * The server resolves the backing CLI tool from the roster via the `instance`
389
389
  * query param (kill-session/route.ts), so no extra lookup is needed here.
390
390
  */
391
- async function killInstance(worktreeId, instanceId, options) {
391
+ async function killInstance(client, worktreeId, instanceId) {
392
392
  if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
393
393
  console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
394
394
  process.exit(types_1.ExitCode.CONFIG_ERROR);
395
395
  }
396
- const client = new api_client_1.ApiClient({ token: options.token });
397
396
  await client.post(`/api/worktrees/${worktreeId}/kill-session?instance=${encodeURIComponent(instanceId)}`);
398
397
  console.error(`Session killed: ${instanceId}`);
399
398
  }
399
+ /**
400
+ * Print the "you are on another server" hint when that is what a 404 means
401
+ * (Issue #2404).
402
+ *
403
+ * Runs only on a failure path, so the extra `tmux display-message` +
404
+ * `GET /api/worktrees` it costs buys the difference between a correct diagnosis
405
+ * and a wrong one. Never throws: {@link describeServerMismatch} swallows its own
406
+ * failures, and a diagnosis must not replace the error being diagnosed.
407
+ *
408
+ * @param client - The client whose request failed
409
+ * @param error - The caught error, diagnosed only when it is a 404
410
+ */
411
+ async function warnIfDifferentServer(client, error) {
412
+ if (!(error instanceof api_client_1.ApiError) || error.statusCode !== 404)
413
+ return;
414
+ const mismatch = await (0, whoami_1.describeServerMismatch)(client);
415
+ if (mismatch)
416
+ console.error((0, whoami_1.formatServerMismatchHint)(mismatch));
417
+ }
400
418
  function createInstancesCommand() {
401
419
  const cmd = new commander_1.Command('instances');
402
420
  cmd
@@ -411,6 +429,10 @@ function createInstancesCommand() {
411
429
  .option('--kill', 'Also kill the running session when removing an instance')
412
430
  .option('--token <token>', command_helpers_1.TOKEN_WARNING)
413
431
  .action(async (worktreeId, action, rest, options) => {
432
+ // One client for the whole invocation (Issue #2404): every action built
433
+ // its own identical one, and the failure path below has to report the URL
434
+ // the request actually went to — a second construction is a second answer.
435
+ const client = new api_client_1.ApiClient({ token: options.token });
414
436
  try {
415
437
  if (!(0, api_client_1.isValidWorktreeId)(worktreeId)) {
416
438
  console.error('Error: Invalid worktree ID format.');
@@ -418,10 +440,10 @@ function createInstancesCommand() {
418
440
  }
419
441
  switch (action ?? 'list') {
420
442
  case 'list':
421
- await listInstances(worktreeId, options);
443
+ await listInstances(client, worktreeId, options);
422
444
  break;
423
445
  case 'add':
424
- await addInstance(worktreeId, options);
446
+ await addInstance(client, worktreeId, options);
425
447
  break;
426
448
  case 'remove': {
427
449
  const [instanceId] = rest;
@@ -429,7 +451,7 @@ function createInstancesCommand() {
429
451
  console.error('Error: remove requires an <instance-id> argument.');
430
452
  process.exit(types_1.ExitCode.CONFIG_ERROR);
431
453
  }
432
- await removeInstance(worktreeId, instanceId, options);
454
+ await removeInstance(client, worktreeId, instanceId, options);
433
455
  break;
434
456
  }
435
457
  case 'alias': {
@@ -439,7 +461,7 @@ function createInstancesCommand() {
439
461
  console.error('Error: alias requires <instance-id> and <new-alias> arguments.');
440
462
  process.exit(types_1.ExitCode.CONFIG_ERROR);
441
463
  }
442
- await renameInstance(worktreeId, instanceId, alias, options);
464
+ await renameInstance(client, worktreeId, instanceId, alias);
443
465
  break;
444
466
  }
445
467
  case 'kill': {
@@ -448,7 +470,7 @@ function createInstancesCommand() {
448
470
  console.error('Error: kill requires an <instance-id> argument.');
449
471
  process.exit(types_1.ExitCode.CONFIG_ERROR);
450
472
  }
451
- await killInstance(worktreeId, instanceId, options);
473
+ await killInstance(client, worktreeId, instanceId);
452
474
  break;
453
475
  }
454
476
  default:
@@ -457,6 +479,13 @@ function createInstancesCommand() {
457
479
  }
458
480
  }
459
481
  catch (error) {
482
+ // Issue #2404: a 404 here says "no such worktree ON THIS SERVER", and
483
+ // `Resource not found. Check the worktree ID.` reads as "you typed it
484
+ // wrong". When the caller's own session belongs to a worktree this
485
+ // server does not list, the id was never the problem — say so before
486
+ // handleCommandError() exits, so nobody re-checks a correct id and then
487
+ // delegates to whatever this server does have.
488
+ await warnIfDifferentServer(client, error);
460
489
  (0, command_helpers_1.handleCommandError)(error);
461
490
  }
462
491
  });
@@ -1 +1 @@
1
- {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/peers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAiB,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAIhG,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB;;;;;;;;GAQG;AACH,UAAU,gBAAiB,SAAQ,YAAY;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,kCAAkC;AAClC,UAAU,OAAO;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAuED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,EAAE,CAuBX;AAoBD,wBAAgB,kBAAkB,IAAI,OAAO,CAkD5C"}
1
+ {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/peers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAiB,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAIhG,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB;;;;;;;;GAQG;AACH,UAAU,gBAAiB,SAAQ,YAAY;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,kCAAkC;AAClC,UAAU,OAAO;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAuED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,EAAE,CAuBX;AAoBD,wBAAgB,kBAAkB,IAAI,OAAO,CAkE5C"}
@@ -175,13 +175,25 @@ function createPeersCommand() {
175
175
  // authority on the one string the caller pastes into a shell.
176
176
  const binary = (0, command_reference_1.resolveCommandMateBinary)();
177
177
  const rows = buildPeerRows(worktrees, identity, binary);
178
+ // Issue #2404: the listing above is empty precisely when the caller's own
179
+ // worktree is absent from `worktrees` — which is what "you are on another
180
+ // server" looks like from here, and which `No peer sessions found.` reads
181
+ // as "you have no siblings". The list is already in hand, so the check is
182
+ // free and applies whichever source named the worktree.
183
+ const mismatch = (0, whoami_1.detectServerMismatch)(identity, new Set(worktrees.map((wt) => wt.id)), client.serverUrl);
184
+ if (mismatch)
185
+ console.error((0, whoami_1.formatServerMismatchWarning)(mismatch));
178
186
  if (options.json) {
179
187
  console.log(JSON.stringify({
188
+ // `self` is unchanged: it answers "who is asking", and the server is
189
+ // not part of that. The connection target is its own top-level field.
180
190
  self: {
181
191
  worktreeId: identity.worktreeId,
182
192
  instanceId: identity.instanceId,
183
193
  cliToolId: identity.cliToolId,
184
194
  },
195
+ serverUrl: client.serverUrl,
196
+ ...(mismatch ? { serverMismatch: true } : {}),
185
197
  peers: rows,
186
198
  }, null, 2));
187
199
  return;
@@ -57,6 +57,29 @@ export interface SessionIdentity {
57
57
  source: IdentitySource;
58
58
  /** The tmux session, when the identity came from (or could be checked against) one. */
59
59
  sessionName: string | null;
60
+ /**
61
+ * Every worktree id the server answered with while this identity was resolved,
62
+ * or null/undefined when no list was read (Issue #2404).
63
+ *
64
+ * Carried rather than discarded because it is the whole evidence for
65
+ * {@link detectServerMismatch}: the tmux path already fetches it to break the
66
+ * `-<n>` ambiguity, so "is my own worktree on the server I am dialling?" costs
67
+ * no second request. Absent on the env path, which never asks — and that is
68
+ * the reason a `source: 'env'` identity never reports a mismatch here.
69
+ */
70
+ knownWorktreeIds?: Set<string> | null;
71
+ }
72
+ /**
73
+ * The caller is on a session one server started and is talking to another
74
+ * (Issue #2404).
75
+ */
76
+ export interface ServerMismatch {
77
+ /** The worktree this session belongs to, as {@link resolveSessionIdentity} read it. */
78
+ worktreeId: string;
79
+ /** The server this CLI is dialling — the one that does NOT list that worktree. */
80
+ serverUrl: string;
81
+ /** Where the worktree id came from, so the warning can say how it is known. */
82
+ source: IdentitySource;
60
83
  }
61
84
  /**
62
85
  * The tmux session this process is running inside, or null.
@@ -95,6 +118,50 @@ export declare function parseSessionName(sessionName: string): ParsedSessionName
95
118
  * @returns The identity, or null when nothing establishes one
96
119
  */
97
120
  export declare function resolveSessionIdentity(client: ApiClient, env?: Readonly<Record<string, string | undefined>>): Promise<SessionIdentity | null>;
121
+ /**
122
+ * Whether the caller's own worktree is missing from the server it is dialling.
123
+ *
124
+ * The contradiction this names was, until now, unnameable: `whoami` answers
125
+ * from the tmux session name and therefore succeeds, while `ls` / `instances` /
126
+ * `peers` answer from a *different* server's ledger and therefore say the
127
+ * worktree does not exist. Both are correct and they disagree, and an agent
128
+ * reading them concluded the reasonable, wrong thing — "I must not be in the
129
+ * ledger" — and delegated its work into an unrelated worktree on the other
130
+ * server (#2403). Exit codes said nothing: `ask` returned 0.
131
+ *
132
+ * Deliberately NOT gated on {@link IdentitySource}. The gate is having a list at
133
+ * all: the env path never fetches one, so it passes null and cannot flag, while
134
+ * `peers` — which has just fetched the whole list for its own listing — can
135
+ * check an env-derived identity for free. A missing list is never a mismatch:
136
+ * a stopped server must not be reported as the wrong server.
137
+ *
138
+ * @param identity - Who the caller is
139
+ * @param knownWorktreeIds - Worktree ids from the server, or null when unread
140
+ * @param serverUrl - The URL those ids came from ({@link ApiClient.serverUrl})
141
+ * @returns The mismatch, or null when there is nothing to report
142
+ */
143
+ export declare function detectServerMismatch(identity: SessionIdentity, knownWorktreeIds: Set<string> | null | undefined, serverUrl: string): ServerMismatch | null;
144
+ /**
145
+ * The multi-line warning printed when {@link detectServerMismatch} fires.
146
+ *
147
+ * Says what is wrong, why it is not a typo, and what it costs to ignore —
148
+ * because the failure it describes is silent by construction: every command
149
+ * involved keeps exiting 0 while answering about the wrong machine's worktrees.
150
+ */
151
+ export declare function formatServerMismatchWarning(mismatch: ServerMismatch): string;
152
+ /** The one-line form, for a command that has already failed for another reason. */
153
+ export declare function formatServerMismatchHint(mismatch: ServerMismatch): string;
154
+ /**
155
+ * Resolve the caller's identity and report a mismatch, or null.
156
+ *
157
+ * For commands that only need this on a failure path: it costs a `tmux
158
+ * display-message` and one `GET /api/worktrees`, and it is called after the
159
+ * request that already failed. Never throws — a diagnosis that fails must not
160
+ * replace the error it was diagnosing.
161
+ *
162
+ * @param client - The client whose 404 is being explained
163
+ */
164
+ export declare function describeServerMismatch(client: ApiClient): Promise<ServerMismatch | null>;
98
165
  /**
99
166
  * The roster row for this identity, or null.
100
167
  *
@@ -1 +1 @@
1
- {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAsC,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKhD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAUhC,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,cAAc,CAAC;AAEpD,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAcD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,IAAI,CASf;AAED,gFAAgF;AAChF,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAa9E;AAkDD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAoCjC;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAO/B;AAED,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,QAMmC,CAAC;AAEvE,wBAAgB,mBAAmB,IAAI,OAAO,CA0C7C"}
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAsC,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKhD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAUhC,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,cAAc,CAAC;AAEpD,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,MAAM,EAAE,cAAc,CAAC;CACxB;AAcD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,IAAI,CASf;AAED,gFAAgF;AAChF,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAa9E;AAkDD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAqCjC;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EACzB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,EAChD,SAAS,EAAE,MAAM,GAChB,cAAc,GAAG,IAAI,CAIvB;AASD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAS5E;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAMzE;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAY9F;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAO/B;AAED,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,QAMmC,CAAC;AAEvE,wBAAgB,mBAAmB,IAAI,OAAO,CA6D7C"}
@@ -38,6 +38,10 @@ exports.NOT_IN_SESSION_MESSAGE = exports.NOT_IN_SESSION = void 0;
38
38
  exports.readTmuxSessionName = readTmuxSessionName;
39
39
  exports.parseSessionName = parseSessionName;
40
40
  exports.resolveSessionIdentity = resolveSessionIdentity;
41
+ exports.detectServerMismatch = detectServerMismatch;
42
+ exports.formatServerMismatchWarning = formatServerMismatchWarning;
43
+ exports.formatServerMismatchHint = formatServerMismatchHint;
44
+ exports.describeServerMismatch = describeServerMismatch;
41
45
  exports.readOwnRosterEntry = readOwnRosterEntry;
42
46
  exports.createWhoamiCommand = createWhoamiCommand;
43
47
  const child_process_1 = require("child_process");
@@ -205,8 +209,95 @@ async function resolveSessionIdentity(client, env = process.env) {
205
209
  cliToolId: parsed.cliToolId,
206
210
  source: 'tmux-session',
207
211
  sessionName,
212
+ knownWorktreeIds: known,
208
213
  };
209
214
  }
215
+ // ===========================================================================
216
+ // "Am I even talking to the server that started me?" (Issue #2404)
217
+ // ===========================================================================
218
+ /**
219
+ * Whether the caller's own worktree is missing from the server it is dialling.
220
+ *
221
+ * The contradiction this names was, until now, unnameable: `whoami` answers
222
+ * from the tmux session name and therefore succeeds, while `ls` / `instances` /
223
+ * `peers` answer from a *different* server's ledger and therefore say the
224
+ * worktree does not exist. Both are correct and they disagree, and an agent
225
+ * reading them concluded the reasonable, wrong thing — "I must not be in the
226
+ * ledger" — and delegated its work into an unrelated worktree on the other
227
+ * server (#2403). Exit codes said nothing: `ask` returned 0.
228
+ *
229
+ * Deliberately NOT gated on {@link IdentitySource}. The gate is having a list at
230
+ * all: the env path never fetches one, so it passes null and cannot flag, while
231
+ * `peers` — which has just fetched the whole list for its own listing — can
232
+ * check an env-derived identity for free. A missing list is never a mismatch:
233
+ * a stopped server must not be reported as the wrong server.
234
+ *
235
+ * @param identity - Who the caller is
236
+ * @param knownWorktreeIds - Worktree ids from the server, or null when unread
237
+ * @param serverUrl - The URL those ids came from ({@link ApiClient.serverUrl})
238
+ * @returns The mismatch, or null when there is nothing to report
239
+ */
240
+ function detectServerMismatch(identity, knownWorktreeIds, serverUrl) {
241
+ if (!knownWorktreeIds)
242
+ return null;
243
+ if (knownWorktreeIds.has(identity.worktreeId))
244
+ return null;
245
+ return { worktreeId: identity.worktreeId, serverUrl, source: identity.source };
246
+ }
247
+ /** How the worktree id was established, as a phrase for the warning. */
248
+ function describeSource(source) {
249
+ return source === 'tmux-session'
250
+ ? 'read from the tmux session name'
251
+ : 'read from the CM_* variables on this session\'s launch line';
252
+ }
253
+ /**
254
+ * The multi-line warning printed when {@link detectServerMismatch} fires.
255
+ *
256
+ * Says what is wrong, why it is not a typo, and what it costs to ignore —
257
+ * because the failure it describes is silent by construction: every command
258
+ * involved keeps exiting 0 while answering about the wrong machine's worktrees.
259
+ */
260
+ function formatServerMismatchWarning(mismatch) {
261
+ return [
262
+ `Warning: this session is worktree '${mismatch.worktreeId}' (${describeSource(mismatch.source)}), `
263
+ + `but the server this CLI is talking to (${mismatch.serverUrl}) does not list it.`,
264
+ ' You are almost certainly connected to a DIFFERENT CommandMate server than the one that started this session.',
265
+ ' Everything that reads the server ledger — ls / instances / peers / ask — is answering about that other server,',
266
+ ' so anything delegated from here can land in an unrelated worktree while still exiting 0.',
267
+ ' Aim the CLI at the right server (CM_PORT=<port> commandmate …) or check ~/.commandmate/.env, then re-run.',
268
+ ].join('\n');
269
+ }
270
+ /** The one-line form, for a command that has already failed for another reason. */
271
+ function formatServerMismatchHint(mismatch) {
272
+ return (`Hint: this session is worktree '${mismatch.worktreeId}' (${describeSource(mismatch.source)}), `
273
+ + `which the server at ${mismatch.serverUrl} does not list — this CLI is probably connected to a `
274
+ + 'different CommandMate server than the one that started this session, so the id is not the problem.');
275
+ }
276
+ /**
277
+ * Resolve the caller's identity and report a mismatch, or null.
278
+ *
279
+ * For commands that only need this on a failure path: it costs a `tmux
280
+ * display-message` and one `GET /api/worktrees`, and it is called after the
281
+ * request that already failed. Never throws — a diagnosis that fails must not
282
+ * replace the error it was diagnosing.
283
+ *
284
+ * @param client - The client whose 404 is being explained
285
+ */
286
+ async function describeServerMismatch(client) {
287
+ try {
288
+ const identity = await resolveSessionIdentity(client);
289
+ if (!identity)
290
+ return null;
291
+ // The env path carries no list, so ask for one here: this runs only after a
292
+ // command has already failed, where one extra request buys the difference
293
+ // between "check the worktree ID" and the actual cause.
294
+ const known = identity.knownWorktreeIds ?? (await readWorktreeIds(client));
295
+ return detectServerMismatch(identity, known, client.serverUrl);
296
+ }
297
+ catch {
298
+ return null;
299
+ }
300
+ }
210
301
  /**
211
302
  * The roster row for this identity, or null.
212
303
  *
@@ -245,6 +336,14 @@ function createWhoamiCommand() {
245
336
  return;
246
337
  }
247
338
  const roster = await readOwnRosterEntry(client, identity);
339
+ // Issue #2404: no extra request — `resolveSessionIdentity` already read
340
+ // the list on the tmux path, which is the path that can be wrong.
341
+ const mismatch = detectServerMismatch(identity, identity.knownWorktreeIds, client.serverUrl);
342
+ // stderr in both modes: it must not corrupt `--json` stdout, and a skill
343
+ // that reports "I asked <url>" needs to see it whichever mode it ran in.
344
+ // Exit code stays 0 — whoami still answered the question it was asked.
345
+ if (mismatch)
346
+ console.error(formatServerMismatchWarning(mismatch));
248
347
  if (options.json) {
249
348
  console.log(JSON.stringify({
250
349
  worktreeId: identity.worktreeId,
@@ -253,6 +352,12 @@ function createWhoamiCommand() {
253
352
  alias: roster?.alias ?? null,
254
353
  source: identity.source,
255
354
  sessionName: identity.sessionName,
355
+ // Always present: "where did this answer come from" is half of what
356
+ // makes the answer checkable (Issue #2404).
357
+ serverUrl: client.serverUrl,
358
+ // Absent, not false, when everything agrees: a reader tests for the
359
+ // flag, and an ordinary session's output carries no trace of it.
360
+ ...(mismatch ? { serverMismatch: true } : {}),
256
361
  }, null, 2));
257
362
  return;
258
363
  }
@@ -262,6 +367,7 @@ function createWhoamiCommand() {
262
367
  console.log(`alias: ${roster?.alias ?? '-'}`);
263
368
  console.log(`source: ${identity.source}`);
264
369
  console.log(`tmux: ${identity.sessionName ?? '-'}`);
370
+ console.log(`server: ${client.serverUrl}`);
265
371
  }
266
372
  catch (error) {
267
373
  (0, command_helpers_1.handleCommandError)(error);
@@ -38,6 +38,22 @@ export interface ApiErrorResult {
38
38
  message: string;
39
39
  exitCode: number;
40
40
  }
41
+ /**
42
+ * What this function knows about the request, beyond its status (Issue #2404).
43
+ *
44
+ * `handleApiError` is a pure classifier over `(status, payload)` and has no way
45
+ * to know which server answered — which is exactly the fact a 404 needs. The
46
+ * caller's `Resource not found. Check the worktree ID.` sent an agent looking
47
+ * for a typo in an id that was correct: the CLI was dialling a *different*
48
+ * CommandMate server than the one that had started its session, and the id it
49
+ * was asking about lived on the other one. Optional so the two callers that
50
+ * classify without a client ({@link ApiClient} aside, `server-capabilities.ts`)
51
+ * keep compiling and keep their wording.
52
+ */
53
+ export interface ApiErrorContext {
54
+ /** The base URL the failing request was actually sent to. */
55
+ serverUrl?: string;
56
+ }
41
57
  /**
42
58
  * Classify API errors into user-friendly messages and exit codes.
43
59
  * [IA3-09] Covers: ECONNREFUSED, 400, 401/403, 404, 429, 500, timeout
@@ -47,9 +63,12 @@ export interface ApiErrorResult {
47
63
  * @param payload - Parsed error body, when the response carried one (Issue #1637).
48
64
  * Used for 5xx only: the 4xx messages below are already specific, and are
49
65
  * pinned by tests as the CLI's own wording.
66
+ * @param context - Which server answered, when the caller knows (Issue #2404).
67
+ * Read by the 404 branch only; the other messages do not send anyone looking
68
+ * in the wrong place.
50
69
  * @returns User-friendly error message and exit code
51
70
  */
52
- export declare function handleApiError(error: unknown, status?: number, payload?: ApiErrorPayload): ApiErrorResult;
71
+ export declare function handleApiError(error: unknown, status?: number, payload?: ApiErrorPayload, context?: ApiErrorContext): ApiErrorResult;
53
72
  /**
54
73
  * API Client for CLI HTTP communication.
55
74
  * Uses Node.js built-in fetch. Handles auth headers and error mapping.
@@ -61,6 +80,16 @@ export declare class ApiClient {
61
80
  baseUrl?: string;
62
81
  token?: string;
63
82
  });
83
+ /**
84
+ * The base URL this client actually dials (Issue #2404).
85
+ *
86
+ * Resolved once in the constructor through the precedence in
87
+ * `loadClientEnv()` (`--base-url` > exported `CM_PORT` > `~/.commandmate/.env`
88
+ * > 3000), which is why it is read from here rather than re-derived by each
89
+ * caller: a second derivation is a second answer, and "which server did this
90
+ * command talk to" only helps if it is the one the request went to.
91
+ */
92
+ get serverUrl(): string;
64
93
  private getHeaders;
65
94
  /**
66
95
  * GET without any status handling, for callers that must read the raw response.
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAK3C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAaD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CASjF;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CAyEhB;AAeD;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAC,CAAS;gBAEX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAmB1D,OAAO,CAAC,UAAU;IAUlB;;;;;;;;;;;;;;OAcG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1F;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBtC;;;OAGG;IACG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BvD;;;;OAIG;IACG,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAyBzD;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA0BD;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;aAGf,QAAQ,EAAE,MAAM;aAChB,UAAU,CAAC,EAAE,MAAM;IACnC,gEAAgE;aAChD,OAAO,CAAC,EAAE,eAAe;gBAJzC,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,YAAA;IACnC,gEAAgE;IAChD,OAAO,CAAC,EAAE,eAAe,YAAA;IAM3C,uEAAuE;IACvE,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;CACF;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EACvC,OAAO,EAAE,MAAM,GACd,CAAC,CAiBH;AAOD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CASvF;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CASxE"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAK3C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAaD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CASjF;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CA6EhB;AAeD;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAC,CAAS;gBAEX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAmB1D;;;;;;;;OAQG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,OAAO,CAAC,UAAU;IAUlB;;;;;;;;;;;;;;OAcG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1F;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBtC;;;OAGG;IACG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BvD;;;;OAIG;IACG,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAyBzD;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA0BD;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;aAGf,QAAQ,EAAE,MAAM;aAChB,UAAU,CAAC,EAAE,MAAM;IACnC,gEAAgE;aAChD,OAAO,CAAC,EAAE,eAAe;gBAJzC,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,YAAA;IACnC,gEAAgE;IAChD,OAAO,CAAC,EAAE,eAAe,YAAA;IAM3C,uEAAuE;IACvE,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;CACF;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EACvC,OAAO,EAAE,MAAM,GACd,CAAC,CAiBH;AAOD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CASvF;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CASxE"}
@@ -87,6 +87,10 @@ function serverErrorDetail(payload) {
87
87
  ? `${detail.slice(0, MAX_SERVER_ERROR_DETAIL)}…`
88
88
  : detail;
89
89
  }
90
+ /** Append `(server: <url>)` when the caller named one. */
91
+ function withServerUrl(message, context) {
92
+ return context?.serverUrl ? `${message} (server: ${context.serverUrl})` : message;
93
+ }
90
94
  /**
91
95
  * Classify API errors into user-friendly messages and exit codes.
92
96
  * [IA3-09] Covers: ECONNREFUSED, 400, 401/403, 404, 429, 500, timeout
@@ -96,9 +100,12 @@ function serverErrorDetail(payload) {
96
100
  * @param payload - Parsed error body, when the response carried one (Issue #1637).
97
101
  * Used for 5xx only: the 4xx messages below are already specific, and are
98
102
  * pinned by tests as the CLI's own wording.
103
+ * @param context - Which server answered, when the caller knows (Issue #2404).
104
+ * Read by the 404 branch only; the other messages do not send anyone looking
105
+ * in the wrong place.
99
106
  * @returns User-friendly error message and exit code
100
107
  */
101
- function handleApiError(error, status, payload) {
108
+ function handleApiError(error, status, payload, context) {
102
109
  // HTTP status-based errors
103
110
  if (status !== undefined) {
104
111
  switch (status) {
@@ -114,8 +121,12 @@ function handleApiError(error, status, payload) {
114
121
  exitCode: types_1.ExitCode.CONFIG_ERROR,
115
122
  };
116
123
  case 404:
124
+ // Issue #2404: the sentence stays, so everything matching on it keeps
125
+ // matching; the URL is appended because "check the worktree ID" is
126
+ // advice about the wrong thing whenever the id is fine and the SERVER
127
+ // is the one that is wrong.
117
128
  return {
118
- message: 'Resource not found. Check the worktree ID.',
129
+ message: withServerUrl('Resource not found. Check the worktree ID.', context),
119
130
  exitCode: types_1.ExitCode.UNEXPECTED_ERROR,
120
131
  };
121
132
  case 429:
@@ -205,6 +216,18 @@ class ApiClient {
205
216
  console.error('Warning: Connecting to remote server over HTTP. Auth token will be sent in plaintext. Use HTTPS for non-localhost connections.');
206
217
  }
207
218
  }
219
+ /**
220
+ * The base URL this client actually dials (Issue #2404).
221
+ *
222
+ * Resolved once in the constructor through the precedence in
223
+ * `loadClientEnv()` (`--base-url` > exported `CM_PORT` > `~/.commandmate/.env`
224
+ * > 3000), which is why it is read from here rather than re-derived by each
225
+ * caller: a second derivation is a second answer, and "which server did this
226
+ * command talk to" only helps if it is the one the request went to.
227
+ */
228
+ get serverUrl() {
229
+ return this.baseUrl;
230
+ }
208
231
  getHeaders() {
209
232
  const headers = {
210
233
  'Content-Type': 'application/json',
@@ -250,7 +273,7 @@ class ApiClient {
250
273
  // Issue #1637: read the body first — handleApiError needs it to surface
251
274
  // the server's reason for a 5xx instead of "check the logs".
252
275
  const payload = await readErrorPayload(response);
253
- const errResult = handleApiError(null, response.status, payload);
276
+ const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
254
277
  throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
255
278
  }
256
279
  return (await response.json());
@@ -277,7 +300,7 @@ class ApiClient {
277
300
  // Issue #1637: read the body first — handleApiError needs it to surface
278
301
  // the server's reason for a 5xx instead of "check the logs".
279
302
  const payload = await readErrorPayload(response);
280
- const errResult = handleApiError(null, response.status, payload);
303
+ const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
281
304
  throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
282
305
  }
283
306
  // Handle 204 No Content
@@ -309,7 +332,7 @@ class ApiClient {
309
332
  // Issue #1637: read the body first — handleApiError needs it to surface
310
333
  // the server's reason for a 5xx instead of "check the logs".
311
334
  const payload = await readErrorPayload(response);
312
- const errResult = handleApiError(null, response.status, payload);
335
+ const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
313
336
  throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
314
337
  }
315
338
  const text = await response.text();