commandmate 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +120 -120
  3. package/.next/app-path-routes-manifest.json +19 -19
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +12 -12
  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/app/update/route_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/app/update-check/route.js +1 -1
  10. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  12. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/auth/status/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_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/push/subscriptions/route.js +1 -1
  33. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.js +1 -1
  37. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/repositories/[id]/todos/route.js +1 -1
  39. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  56. package/.next/server/app/api/tasks/[taskId]/route.js +1 -1
  57. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  60. package/.next/server/app/api/todos/route.js +1 -1
  61. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/verification/runs/[runId]/route.js +1 -1
  63. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/verification/runs/route.js +1 -1
  65. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  66. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  67. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
  70. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +1 -1
  72. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
  75. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  76. package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +1 -1
  77. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  80. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/worktrees/[id]/git/branches/route.js +1 -1
  82. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/worktrees/[id]/git/commit/route.js +1 -1
  85. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/worktrees/[id]/git/diff/route.js +1 -1
  87. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/worktrees/[id]/git/log/route.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.js +1 -1
  94. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/worktrees/[id]/git/revert/route.js +1 -1
  96. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.js +1 -1
  98. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/worktrees/[id]/git/staged/route.js +1 -1
  101. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  104. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/git/stash/push/route.js +1 -1
  106. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/git/stash/route.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/worktrees/[id]/git/status/route.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/git/working-diff/route.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/logs/route.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/marp-render/route.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/schedules/active/route.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/schedules/route.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/skills/route.js +1 -1
  146. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
  148. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
  152. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/timers/history/route.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/todos/route.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
  167. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/worktrees/[id]/verify/runs/route.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  176. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  177. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  179. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  180. package/.next/server/app/page_client-reference-manifest.js +1 -1
  181. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  183. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  184. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  185. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  186. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  187. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  188. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  189. package/.next/server/app/worktrees/[id]/page.js +1 -1
  190. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  191. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  192. package/.next/server/app-paths-manifest.json +19 -19
  193. package/.next/server/chunks/1117.js +1 -1
  194. package/.next/server/chunks/3042.js +1 -1
  195. package/.next/server/chunks/3753.js +19 -23
  196. package/.next/server/chunks/5959.js +3 -3
  197. package/.next/server/chunks/6130.js +1 -1
  198. package/.next/server/chunks/6274.js +10 -9
  199. package/.next/server/chunks/6283.js +1 -1
  200. package/.next/server/chunks/8681.js +1 -1
  201. package/.next/server/chunks/8807.js +492 -485
  202. package/.next/server/chunks/8856.js +1 -1
  203. package/.next/server/middleware-manifest.json +5 -5
  204. package/.next/server/pages/500.html +1 -1
  205. package/.next/server/server-reference-manifest.json +1 -1
  206. package/.next/static/chunks/{6488-f4e447464c9e0e22.js → 6488-2c412c29258637b6.js} +1 -1
  207. package/.next/static/chunks/app/worktrees/[id]/{page-5ffc84094830babb.js → page-f6e09d25cd3b56f9.js} +1 -1
  208. package/.next/trace +12 -12
  209. package/dist/cli/commands/auto-yes.d.ts.map +1 -1
  210. package/dist/cli/commands/auto-yes.js +9 -2
  211. package/dist/cli/commands/capture.d.ts +1 -0
  212. package/dist/cli/commands/capture.d.ts.map +1 -1
  213. package/dist/cli/commands/capture.js +112 -2
  214. package/dist/cli/commands/instances.d.ts +24 -0
  215. package/dist/cli/commands/instances.d.ts.map +1 -1
  216. package/dist/cli/commands/instances.js +44 -0
  217. package/dist/cli/commands/respond.d.ts.map +1 -1
  218. package/dist/cli/commands/respond.js +10 -2
  219. package/dist/cli/commands/send.d.ts.map +1 -1
  220. package/dist/cli/commands/send.js +47 -19
  221. package/dist/cli/commands/wait.d.ts +4 -1
  222. package/dist/cli/commands/wait.d.ts.map +1 -1
  223. package/dist/cli/commands/wait.js +134 -10
  224. package/dist/cli/types/api-responses.d.ts +10 -0
  225. package/dist/cli/types/api-responses.d.ts.map +1 -1
  226. package/dist/cli/types/index.d.ts +16 -0
  227. package/dist/cli/types/index.d.ts.map +1 -1
  228. package/dist/cli/utils/pager.d.ts +28 -0
  229. package/dist/cli/utils/pager.d.ts.map +1 -0
  230. package/dist/cli/utils/pager.js +57 -0
  231. package/dist/cli/utils/verify-runner.d.ts +6 -0
  232. package/dist/cli/utils/verify-runner.d.ts.map +1 -1
  233. package/dist/cli/utils/verify-runner.js +1 -0
  234. package/dist/lib/detection/ansi.d.ts +30 -0
  235. package/dist/lib/detection/ansi.d.ts.map +1 -0
  236. package/dist/lib/detection/ansi.js +39 -0
  237. package/dist/lib/tmux/transcript-squeeze.d.ts +76 -0
  238. package/dist/lib/tmux/transcript-squeeze.d.ts.map +1 -0
  239. package/dist/lib/tmux/transcript-squeeze.js +96 -0
  240. package/dist/server/server.js +18 -0
  241. package/dist/server/src/config/tmux-pane-config.js +33 -1
  242. package/dist/server/src/lib/cli-tools/antigravity.js +2 -1
  243. package/dist/server/src/lib/cli-tools/codex.js +3 -2
  244. package/dist/server/src/lib/cli-tools/copilot.js +2 -2
  245. package/dist/server/src/lib/cli-tools/gemini.js +2 -2
  246. package/dist/server/src/lib/cli-tools/opencode.js +2 -2
  247. package/dist/server/src/lib/cli-tools/vibe-local.js +2 -2
  248. package/dist/server/src/lib/db/agent-instances-db.js +53 -0
  249. package/dist/server/src/lib/db/migrations/index.js +2 -0
  250. package/dist/server/src/lib/db/migrations/runner.js +1 -1
  251. package/dist/server/src/lib/db/migrations/v52-orphaned-worktree-children.js +76 -0
  252. package/dist/server/src/lib/db/migrations/worktree-child-tables.js +119 -0
  253. package/dist/server/src/lib/db/tasks-db.js +46 -1
  254. package/dist/server/src/lib/db/verification-db.js +21 -3
  255. package/dist/server/src/lib/db/worktree-db.js +65 -42
  256. package/dist/server/src/lib/detection/cli-patterns.js +24 -1
  257. package/dist/server/src/lib/detection/status-detector.js +52 -2
  258. package/dist/server/src/lib/session/claude-session.js +4 -2
  259. package/dist/server/src/lib/tmux/read-mode-pager.js +161 -0
  260. package/dist/server/src/lib/tmux/read-mode.js +327 -0
  261. package/dist/server/src/lib/tmux/tmux.js +43 -5
  262. package/package.json +1 -1
  263. /package/.next/static/{gR8Sh86RrTynJON8SWkWK → -E7I667pJDTBamyG8M_Z-}/_buildManifest.js +0 -0
  264. /package/.next/static/{gR8Sh86RrTynJON8SWkWK → -E7I667pJDTBamyG8M_Z-}/_ssgManifest.js +0 -0
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ /**
3
+ * Reading mode: `prefix+g` opens a squeezed transcript in a tmux popup (Issue #1623, 案A).
4
+ *
5
+ * Attaching to a CommandMate session shows nothing readable. The pane is pinned to
6
+ * 200x1000 (Issue #1163), the agent draws its composer at the bottom and its
7
+ * transcript at the top, and tmux follows the cursor — which sits at row 997.
8
+ * Measured on a live session: an attached 72-row client sees ZERO transcript rows.
9
+ * This module adds an on-demand way to read, WITHOUT touching the geometry that
10
+ * status detection, Auto-Yes and the response saver depend on.
11
+ *
12
+ * ## This module mutates state CommandMate does not own — read before changing it
13
+ *
14
+ * `bind-key` writes the tmux server's GLOBAL prefix key table. Every session on
15
+ * that server inherits it, including ones CommandMate knows nothing about. Four
16
+ * guards keep that intervention honest, and all four are load-bearing:
17
+ *
18
+ * 1. **Capability probe.** `display-popup` is tmux 3.2+. Measured on 3.5a:
19
+ * `tmux list-commands <unknown>` exits 0 with EMPTY stdout, so the exit code is
20
+ * useless as a probe — the presence of output is the signal. Older tmux gets
21
+ * no binding at all (a genuine no-op), and the CLI viewer is the fallback.
22
+ * 2. **Conflict check.** `list-keys -T prefix <key>` exits 1 with `unknown key`
23
+ * when the key is free and exits 0 printing the binding when it is taken. A key
24
+ * already bound to something that is not ours is left ALONE. The Issue's claim
25
+ * that `prefix+g` is unbound on stock tmux 3.5a is true here, but says nothing
26
+ * about a user's own `.tmux.conf`.
27
+ * 3. **Session-name guard.** The binding fires `if-shell -F '#{m:mcbd-*,...}'`, so
28
+ * pressing the key in a non-CommandMate session does nothing (measured: the
29
+ * format yields 1 for `mcbd-*` sessions and 0 for everything else).
30
+ * 4. **Opt-out that converges.** `CM_READ_MODE=off` does not merely skip
31
+ * installation — it REMOVES a binding a previous run installed. That is the
32
+ * uninstall path, and it is why shutdown does not unbind (see below).
33
+ *
34
+ * ## Why shutdown does not unbind
35
+ *
36
+ * CommandMate supports several servers at once (`commandmate start --issue N`),
37
+ * all sharing one tmux server. Unbinding when one of them stops would rip the key
38
+ * out from under the others, and the popup needs no CommandMate server to work —
39
+ * it only shells out to tmux. Removal is therefore an explicit user action
40
+ * (`CM_READ_MODE=off` + restart), not a shutdown side effect.
41
+ */
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.DEFAULT_READ_MODE_KEY = exports.MCBD_SESSION_PREFIX = void 0;
44
+ exports.getPagerScriptPath = getPagerScriptPath;
45
+ exports.isReadModeEnabled = isReadModeEnabled;
46
+ exports.resolveReadModeKey = resolveReadModeKey;
47
+ exports.quoteForTmuxCommand = quoteForTmuxCommand;
48
+ exports.buildBindKeyArgs = buildBindKeyArgs;
49
+ exports.supportsDisplayPopup = supportsDisplayPopup;
50
+ exports.readExistingBinding = readExistingBinding;
51
+ exports.materializePagerScript = materializePagerScript;
52
+ exports.buildUnbindKeyArgs = buildUnbindKeyArgs;
53
+ exports.reconcileReadModeBinding = reconcileReadModeBinding;
54
+ exports.initReadMode = initReadMode;
55
+ const child_process_1 = require("child_process");
56
+ const util_1 = require("util");
57
+ const os_1 = require("os");
58
+ const path_1 = require("path");
59
+ const fs_1 = require("fs");
60
+ const read_mode_pager_1 = require("./read-mode-pager");
61
+ const logger_1 = require("../logger");
62
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
63
+ const logger = (0, logger_1.createLogger)('tmux-read-mode');
64
+ /** tmux calls here are interactive-latency, not long-running. */
65
+ const TMUX_TIMEOUT = 5000;
66
+ /**
67
+ * Session-name prefix every CommandMate tmux session carries.
68
+ *
69
+ * Not imported from `cli-tools/base.ts` because it is a template literal there,
70
+ * not a constant. `tests/unit/lib/tmux/read-mode.test.ts` asserts that a real
71
+ * `getSessionName()` still starts with this, so a rename breaks the test instead
72
+ * of silently turning the guard into a permanent "never fire".
73
+ */
74
+ exports.MCBD_SESSION_PREFIX = 'mcbd-';
75
+ /** Key bound under the prefix table when `CM_READ_MODE_KEY` is unset. */
76
+ exports.DEFAULT_READ_MODE_KEY = 'g';
77
+ /**
78
+ * Accepted `CM_READ_MODE_KEY` values: a single alphanumeric, an F-key, either
79
+ * optionally with a `C-` / `M-` / `S-` modifier. Deliberately narrow — a typo
80
+ * should be rejected loudly rather than bound to something surprising in the
81
+ * user's global key table.
82
+ */
83
+ const READ_MODE_KEY_PATTERN = /^(?:[CMS]-)?(?:[A-Za-z0-9]|F(?:[1-9]|1[0-2]))$/;
84
+ /** Popup geometry. Large enough to read, small enough to still see it is a popup. */
85
+ const POPUP_WIDTH = '90%';
86
+ const POPUP_HEIGHT = '85%';
87
+ /**
88
+ * Absolute path of the materialized pager script.
89
+ *
90
+ * Always under `~/.commandmate/`, never the install directory and never the cwd:
91
+ * `bind-key` is server-global and long-lived, so the path it names has to outlive
92
+ * a `npx` cache eviction or a `cd`.
93
+ */
94
+ function getPagerScriptPath() {
95
+ return (0, path_1.join)((0, os_1.homedir)(), '.commandmate', 'bin', read_mode_pager_1.PAGER_SCRIPT_FILENAME);
96
+ }
97
+ /** True unless the user set `CM_READ_MODE=off` (or `0` / `false`). */
98
+ function isReadModeEnabled() {
99
+ const raw = (process.env.CM_READ_MODE ?? '').trim().toLowerCase();
100
+ return raw !== 'off' && raw !== '0' && raw !== 'false';
101
+ }
102
+ /**
103
+ * Resolve the configured key, or undefined when it is malformed.
104
+ * @returns The key to bind, or undefined if `CM_READ_MODE_KEY` fails validation
105
+ */
106
+ function resolveReadModeKey() {
107
+ const raw = (process.env.CM_READ_MODE_KEY ?? '').trim();
108
+ if (raw === '')
109
+ return exports.DEFAULT_READ_MODE_KEY;
110
+ return READ_MODE_KEY_PATTERN.test(raw) ? raw : undefined;
111
+ }
112
+ /**
113
+ * Quote a path for the shell-command tmux stores in a binding.
114
+ *
115
+ * tmux lexes the stored command itself before handing words to `/bin/sh`, so the
116
+ * quoting has to survive that pass. POSIX `'...'` with `'\''` for embedded quotes
117
+ * satisfies both. Returns undefined for a path that cannot be represented (a
118
+ * newline has no single-quoted form tmux will parse), which callers treat as
119
+ * "do not install".
120
+ */
121
+ function quoteForTmuxCommand(path) {
122
+ if (path.includes('\n') || path.includes('\r'))
123
+ return undefined;
124
+ return `'${path.replace(/'/g, "'\\''")}'`;
125
+ }
126
+ /**
127
+ * The exact `bind-key` argument vector for a given key and script path.
128
+ * Exported so tests assert the wire form rather than a paraphrase of it.
129
+ */
130
+ function buildBindKeyArgs(key, quotedScriptPath) {
131
+ return [
132
+ 'bind-key',
133
+ '-T',
134
+ 'prefix',
135
+ key,
136
+ 'if-shell',
137
+ '-F',
138
+ `#{m:${exports.MCBD_SESSION_PREFIX}*,#{session_name}}`,
139
+ `display-popup -w ${POPUP_WIDTH} -h ${POPUP_HEIGHT} -E ${quotedScriptPath}`,
140
+ ];
141
+ }
142
+ /**
143
+ * Whether this tmux understands `display-popup` (3.2+).
144
+ *
145
+ * Probes on OUTPUT, not exit status: measured on 3.5a, `list-commands` exits 0
146
+ * for an unknown command name and simply prints nothing.
147
+ */
148
+ async function supportsDisplayPopup() {
149
+ try {
150
+ const { stdout } = await execFileAsync('tmux', ['list-commands', 'display-popup'], {
151
+ timeout: TMUX_TIMEOUT,
152
+ });
153
+ return stdout.trim() !== '';
154
+ }
155
+ catch {
156
+ return false;
157
+ }
158
+ }
159
+ /**
160
+ * Current prefix-table binding for `key`, or undefined when the key is free.
161
+ *
162
+ * @param key - tmux key name
163
+ * @returns The `bind-key ...` line tmux reports, or undefined if unbound
164
+ */
165
+ async function readExistingBinding(key) {
166
+ try {
167
+ const { stdout } = await execFileAsync('tmux', ['list-keys', '-T', 'prefix', key], {
168
+ timeout: TMUX_TIMEOUT,
169
+ });
170
+ const line = stdout.trim();
171
+ return line === '' ? undefined : line;
172
+ }
173
+ catch {
174
+ // Exit 1 + `unknown key: <k>` on stderr is how tmux reports "not bound".
175
+ return undefined;
176
+ }
177
+ }
178
+ /**
179
+ * Write the pager script to {@link getPagerScriptPath}, if its content changed.
180
+ *
181
+ * Idempotent by content comparison so a restart does not churn the file's mtime,
182
+ * and re-executable after a `chmod` accident because the mode is re-applied.
183
+ *
184
+ * @returns The absolute path written
185
+ */
186
+ function materializePagerScript() {
187
+ const scriptPath = getPagerScriptPath();
188
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(scriptPath), { recursive: true });
189
+ let current;
190
+ if ((0, fs_1.existsSync)(scriptPath)) {
191
+ try {
192
+ current = (0, fs_1.readFileSync)(scriptPath, 'utf-8');
193
+ }
194
+ catch {
195
+ current = undefined;
196
+ }
197
+ }
198
+ if (current !== read_mode_pager_1.PAGER_SCRIPT) {
199
+ (0, fs_1.writeFileSync)(scriptPath, read_mode_pager_1.PAGER_SCRIPT, { mode: 0o755 });
200
+ }
201
+ (0, fs_1.chmodSync)(scriptPath, 0o755);
202
+ return scriptPath;
203
+ }
204
+ /**
205
+ * The exact argument vector that removes the binding again.
206
+ *
207
+ * Paired with {@link buildBindKeyArgs} and exported for the same reason: the
208
+ * install and uninstall paths are asserted against these builders rather than
209
+ * against re-typed literals, so a test cannot drift from what actually runs —
210
+ * and `tests/unit/config/tmux-live-test-safety.test.ts` keeps its meaning,
211
+ * because the mutating verbs appear in exactly one place, here in `src/`.
212
+ */
213
+ function buildUnbindKeyArgs(key) {
214
+ return ['unbind-key', '-T', 'prefix', key];
215
+ }
216
+ /**
217
+ * Remove the binding, but ONLY when it is still the one we installed.
218
+ *
219
+ * A user who rebound the key after us owns it now; clobbering that would be the
220
+ * same rudeness the conflict check exists to avoid.
221
+ */
222
+ async function removeOwnBinding(key, scriptPath) {
223
+ const existing = await readExistingBinding(key);
224
+ if (existing === undefined)
225
+ return 'not-installed';
226
+ if (!existing.includes(scriptPath))
227
+ return 'key-conflict';
228
+ await execFileAsync('tmux', buildUnbindKeyArgs(key), { timeout: TMUX_TIMEOUT });
229
+ return 'removed';
230
+ }
231
+ /**
232
+ * Reconcile the tmux prefix key table with the configured reading-mode state.
233
+ *
234
+ * Safe to call on every server start: installing twice is a no-op, and the
235
+ * disabled path removes what an earlier run installed. Never throws — reading
236
+ * mode is a convenience, and no failure here may keep the server from starting.
237
+ *
238
+ * @returns What was done, for logging and for tests
239
+ */
240
+ async function reconcileReadModeBinding() {
241
+ const key = resolveReadModeKey();
242
+ const scriptPath = getPagerScriptPath();
243
+ if (key === undefined) {
244
+ return {
245
+ installed: false,
246
+ outcome: 'invalid-key',
247
+ key: process.env.CM_READ_MODE_KEY ?? '',
248
+ scriptPath,
249
+ detail: 'CM_READ_MODE_KEY must be a single alphanumeric or F-key, optionally with a C-/M-/S- modifier',
250
+ };
251
+ }
252
+ try {
253
+ if (!isReadModeEnabled()) {
254
+ const outcome = await removeOwnBinding(key, scriptPath);
255
+ return { installed: false, outcome, key, scriptPath, detail: 'CM_READ_MODE=off' };
256
+ }
257
+ if (!(await supportsDisplayPopup())) {
258
+ return {
259
+ installed: false,
260
+ outcome: 'unsupported-tmux',
261
+ key,
262
+ scriptPath,
263
+ detail: 'display-popup requires tmux 3.2+; use `commandmate capture <id> --pane` instead',
264
+ };
265
+ }
266
+ const quoted = quoteForTmuxCommand(scriptPath);
267
+ if (quoted === undefined) {
268
+ return { installed: false, outcome: 'unsafe-script-path', key, scriptPath };
269
+ }
270
+ const existing = await readExistingBinding(key);
271
+ if (existing !== undefined && !existing.includes(scriptPath)) {
272
+ return {
273
+ installed: false,
274
+ outcome: 'key-conflict',
275
+ key,
276
+ scriptPath,
277
+ detail: `prefix+${key} is already bound: ${existing}`,
278
+ };
279
+ }
280
+ materializePagerScript();
281
+ await execFileAsync('tmux', buildBindKeyArgs(key, quoted), { timeout: TMUX_TIMEOUT });
282
+ return {
283
+ installed: true,
284
+ outcome: existing === undefined ? 'installed' : 'already-installed',
285
+ key,
286
+ scriptPath,
287
+ };
288
+ }
289
+ catch (error) {
290
+ return {
291
+ installed: false,
292
+ outcome: 'error',
293
+ key,
294
+ scriptPath,
295
+ detail: error instanceof Error ? error.message : String(error),
296
+ };
297
+ }
298
+ }
299
+ /**
300
+ * Startup entry point: reconcile and log the outcome once.
301
+ *
302
+ * `server.ts` must reach this through `await import()`, never a top-level static
303
+ * import — pulling module graphs into server.ts's eval-time graph perturbs Next's
304
+ * AsyncLocalStorage bootstrap under `tsx server.ts` and the first request that
305
+ * compiles middleware dies (see the Skill reconciler note in server.ts).
306
+ */
307
+ async function initReadMode() {
308
+ const status = await reconcileReadModeBinding();
309
+ switch (status.outcome) {
310
+ case 'installed':
311
+ case 'already-installed':
312
+ logger.info('read-mode:ready', { key: status.key, scriptPath: status.scriptPath });
313
+ break;
314
+ case 'removed':
315
+ case 'not-installed':
316
+ case 'disabled':
317
+ logger.info('read-mode:disabled', { key: status.key, outcome: status.outcome });
318
+ break;
319
+ default:
320
+ logger.warn('read-mode:not-installed', {
321
+ outcome: status.outcome,
322
+ key: status.key,
323
+ detail: status.detail,
324
+ });
325
+ }
326
+ return status;
327
+ }
@@ -198,7 +198,7 @@ async function listSessions() {
198
198
  * await createSession({
199
199
  * sessionName: 'my-session',
200
200
  * workingDirectory: '/path/to/project',
201
- * historyLimit: 50000,
201
+ * historyLimit: TMUX_HISTORY_LIMIT,
202
202
  * });
203
203
  * ```
204
204
  */
@@ -212,7 +212,7 @@ async function createSession(sessionNameOrOptions, cwd) {
212
212
  // Legacy signature
213
213
  sessionName = sessionNameOrOptions;
214
214
  workingDirectory = cwd;
215
- historyLimit = 50000;
215
+ historyLimit = tmux_pane_config_1.TMUX_HISTORY_LIMIT;
216
216
  windowWidth = tmux_pane_config_1.TUI_PANE_WIDTH;
217
217
  windowHeight = tmux_pane_config_1.TUI_PANE_HEIGHT;
218
218
  }
@@ -220,7 +220,7 @@ async function createSession(sessionNameOrOptions, cwd) {
220
220
  // New signature with options
221
221
  sessionName = sessionNameOrOptions.sessionName;
222
222
  workingDirectory = sessionNameOrOptions.workingDirectory;
223
- historyLimit = sessionNameOrOptions.historyLimit || 50000;
223
+ historyLimit = sessionNameOrOptions.historyLimit || tmux_pane_config_1.TMUX_HISTORY_LIMIT;
224
224
  windowWidth = sessionNameOrOptions.windowWidth || tmux_pane_config_1.TUI_PANE_WIDTH;
225
225
  windowHeight = sessionNameOrOptions.windowHeight || tmux_pane_config_1.TUI_PANE_HEIGHT;
226
226
  }
@@ -228,6 +228,40 @@ async function createSession(sessionNameOrOptions, cwd) {
228
228
  // Create session with explicit window size to avoid 80x24 default
229
229
  // This is critical for TUI tools (Copilot, OpenCode) that use alternate screen
230
230
  await execFileAsync('tmux', ['new-session', '-d', '-s', sessionName, '-c', workingDirectory, '-x', String(windowWidth), '-y', String(windowHeight)], { timeout: DEFAULT_TIMEOUT });
231
+ // Issue #1624: `history-limit` must be set BEFORE the pane that uses it exists.
232
+ //
233
+ // It is a session option, but a pane sizes its scrollback buffer ONCE, from
234
+ // the value in effect at pane-creation time. `new-session` above already
235
+ // created window 0 and its pane, so the old order (new-session → set-option)
236
+ // left every pane on tmux's built-in 2000 lines while `show-options` happily
237
+ // reported 50000 — the session option was set, and utterly inert. Measured on
238
+ // tmux 3.5a: pane `#{history_limit}` stayed 2000, and a live
239
+ // `mcbd-codex-*` session sat at 1977/2000 lines used, silently dropping the
240
+ // oldest transcript. `respawn-pane -k` does NOT fix it either (the pane reuses
241
+ // its existing buffer); only creating a NEW pane does.
242
+ await execFileAsync('tmux', ['set-option', '-t', exactTarget(sessionName), 'history-limit', String(historyLimit)], { timeout: DEFAULT_TIMEOUT });
243
+ // Rebuild window 0 IN PLACE so its pane is allocated against the option just
244
+ // set. `-k` replaces the existing window at index 0 rather than appending,
245
+ // which keeps `#{window_index}` at 0 and the session at exactly one window —
246
+ // no call site has to care that the window was recreated.
247
+ //
248
+ // `-c` is REQUIRED and not redundant: a bare `new-window` does NOT inherit the
249
+ // session's `-c` directory, it starts in the tmux CLIENT's cwd (verified: a
250
+ // session created with `-c /usr/local` produced a pane in the server process's
251
+ // cwd instead). Omitting it silently launches every agent in the wrong repo.
252
+ //
253
+ // Best-effort, matching the geometry step below: if this fails the session is
254
+ // still usable, just with tmux's default 2000-line scrollback.
255
+ try {
256
+ await execFileAsync('tmux', ['new-window', '-k', '-t', `${exactTarget(sessionName)}0`, '-c', workingDirectory], { timeout: DEFAULT_TIMEOUT });
257
+ }
258
+ catch (error) {
259
+ logger.warn('session-history-limit:window-rebuild-failed', {
260
+ sessionName,
261
+ historyLimit,
262
+ error: error instanceof Error ? error.message : String(error),
263
+ });
264
+ }
231
265
  // Issue #1163: Pin the pane to a fixed height so alternate-screen TUIs
232
266
  // (Claude/Codex/etc.) keep enough visible rows for capture-pane.
233
267
  //
@@ -239,9 +273,13 @@ async function createSession(sessionNameOrOptions, cwd) {
239
273
  // tracking (the global option is never touched), and an explicit
240
274
  // `resize-window` then locks in the intended geometry. Best-effort: a failure
241
275
  // here must not abort session creation (some environments restrict resize).
276
+ //
277
+ // Issue #1624: this MUST stay AFTER the window rebuild above. A window created
278
+ // by `new-window` does not inherit `window-size manual` from the window it
279
+ // replaced (verified: `show-window-options -v window-size` came back empty),
280
+ // so reconciling first would have the setting thrown away and leave the pane
281
+ // tracking the latest client again.
242
282
  await reconcileSessionGeometry(sessionName, { windowWidth, windowHeight });
243
- // Set history limit
244
- await execFileAsync('tmux', ['set-option', '-t', exactTarget(sessionName), 'history-limit', String(historyLimit)], { timeout: DEFAULT_TIMEOUT });
245
283
  }
246
284
  catch (error) {
247
285
  const errorMessage = error instanceof Error ? error.message : String(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commandmate",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "A local control plane for agent CLIs — orchestration and visibility for Claude Code, Codex, Gemini CLI, and more across Git worktrees",
5
5
  "keywords": [
6
6
  "claude-code",