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
@@ -10,6 +10,11 @@
10
10
  * leaves a `running` row behind, which is recoverable evidence, whereas a
11
11
  * single write-on-completion would leave nothing at all.
12
12
  *
13
+ * The timestamps describe the *execution*, not the writes (Issue #1625): a
14
+ * caller that measured a gate closes the row with the window it measured, so
15
+ * `finished_at - started_at` equals `duration_ms` instead of the gap between
16
+ * two adjacent INSERT/UPDATE statements.
17
+ *
13
18
  * The finish* functions throw when the target row does not exist. A silent
14
19
  * no-op would let a caller believe it recorded a verdict it never recorded —
15
20
  * exactly the failure this table exists to make impossible.
@@ -64,6 +69,9 @@ function mapGateRow(row) {
64
69
  logTail: row.log_tail,
65
70
  startedAt: new Date(row.started_at),
66
71
  finishedAt: row.finished_at === null ? null : new Date(row.finished_at),
72
+ timingsMeasured: row.finished_at !== null &&
73
+ row.duration_ms !== null &&
74
+ row.finished_at - row.started_at === row.duration_ms,
67
75
  };
68
76
  }
69
77
  /** Open a run in `running` state. */
@@ -113,15 +121,25 @@ function createGateResult(db, runId, input) {
113
121
  }
114
122
  return mapGateRow(row);
115
123
  }
116
- /** Close a gate result with its outcome, stamping `finished_at`. */
124
+ /**
125
+ * Close a gate result with its outcome and the interval it occupied.
126
+ *
127
+ * `started_at` is rewritten only when the caller declares an
128
+ * {@link FinishGateResultPatch.executionWindow}: the value written when the row
129
+ * was opened is a placeholder for "this gate was entered", and the measured
130
+ * start is a few milliseconds later — after the insert this call is closing.
131
+ * Leaving the placeholder would put that gap inside the reported window.
132
+ */
117
133
  function finishGateResult(db, gateResultId, patch) {
118
134
  const info = db
119
135
  .prepare(`
120
136
  UPDATE verification_gate_results
121
- SET status = ?, exit_code = ?, duration_ms = ?, log_tail = ?, finished_at = ?
137
+ SET status = ?, exit_code = ?, duration_ms = ?, log_tail = ?,
138
+ started_at = COALESCE(?, started_at),
139
+ finished_at = ?
122
140
  WHERE id = ?
123
141
  `)
124
- .run(patch.status, patch.exitCode ?? null, patch.durationMs ?? null, patch.logTail ?? null, Date.now(), gateResultId);
142
+ .run(patch.status, patch.exitCode ?? null, patch.durationMs ?? null, patch.logTail ?? null, patch.executionWindow?.startedAt ?? null, patch.executionWindow?.finishedAt ?? Date.now(), gateResultId);
125
143
  if (info.changes === 0) {
126
144
  throw new Error(`Verification gate result ${gateResultId} not found`);
127
145
  }
@@ -28,6 +28,7 @@ exports.deleteRepositoryWorktrees = deleteRepositoryWorktrees;
28
28
  exports.deleteWorktreesByIds = deleteWorktreesByIds;
29
29
  const selected_agents_validator_1 = require("../../lib/selected-agents-validator");
30
30
  const chat_db_1 = require("./chat-db");
31
+ const worktree_child_tables_1 = require("./migrations/worktree-child-tables");
31
32
  /**
32
33
  * Get latest user message per CLI tool for multiple worktrees (batch query)
33
34
  * Optimized to avoid N+1 query problem
@@ -269,30 +270,24 @@ function upsertWorktree(db, worktree) {
269
270
  run();
270
271
  }
271
272
  /**
272
- * Discover every table that has a foreign key referencing `worktrees(id)`,
273
- * along with the referencing column. Derived dynamically from the live schema
274
- * so it never drifts out of sync with future migrations that add child tables.
273
+ * Delete every child row pointing at any of `worktreeIds`.
275
274
  *
276
- * Issue #1151.
275
+ * Deliberately covers *all* deletable child tables, not just the FK-less ones:
276
+ * doing so makes deletion correct even when `PRAGMA foreign_keys` is off, and
277
+ * the extra statements against CASCADE tables remove rows that would have been
278
+ * removed anyway. Append-only ledgers are skipped (see getWriteGuardedTables).
279
+ * Must run before the parent row is deleted (Issue #1621).
277
280
  */
278
- function getWorktreeChildTables(db) {
279
- const tables = db
280
- .prepare("SELECT name FROM sqlite_master WHERE type = 'table'")
281
- .all();
282
- const children = [];
283
- for (const { name } of tables) {
284
- if (name === 'worktrees')
281
+ function deleteWorktreeChildRows(db, worktreeIds, children = (0, worktree_child_tables_1.getWorktreeChildTables)(db)) {
282
+ if (worktreeIds.length === 0)
283
+ return;
284
+ const { noDelete } = (0, worktree_child_tables_1.getWriteGuardedTables)(db);
285
+ const placeholders = worktreeIds.map(() => '?').join(',');
286
+ for (const { table, column } of children) {
287
+ if (noDelete.has(table))
285
288
  continue;
286
- // PRAGMA identifiers cannot be bound; `name` comes from sqlite_master, not
287
- // user input, so string interpolation here is safe.
288
- const fks = db.prepare(`PRAGMA foreign_key_list("${name}")`).all();
289
- for (const fk of fks) {
290
- if (fk.table === 'worktrees' && (fk.to === 'id' || fk.to === null)) {
291
- children.push({ table: name, column: fk.from });
292
- }
293
- }
289
+ db.prepare(`DELETE FROM "${table}" WHERE "${column}" IN (${placeholders})`).run(...worktreeIds);
294
290
  }
295
- return children;
296
291
  }
297
292
  /**
298
293
  * Rename a worktree row's primary key from `oldId` to `newId`, re-pointing every
@@ -300,6 +295,18 @@ function getWorktreeChildTables(db) {
300
295
  * same-directory branch switch preserve chat history and all related data
301
296
  * instead of destroying it (Issue #1151).
302
297
  *
298
+ * "Every child table" means every table carrying a worktree reference, not just
299
+ * the ones that declare a foreign key: `tasks`, `verification_runs` and
300
+ * `skill_operations` do not, and used to be left addressing an ID that no
301
+ * longer resolves (Issue #1621).
302
+ *
303
+ * Append-only ledgers are the one exception, and the schema states it itself —
304
+ * `skill_operations` aborts any UPDATE (#1234). An immutable audit row records
305
+ * what happened under the identity in force at the time, and rewriting it is
306
+ * precisely what its trigger exists to prevent; attempting it does not fail
307
+ * quietly either, it aborts the enclosing transaction and takes the rename with
308
+ * it (measured). See getWriteGuardedTables.
309
+ *
303
310
  * Must be called inside a transaction. `PRAGMA defer_foreign_keys` defers FK
304
311
  * enforcement to COMMIT, letting us repoint the parent PK and its children in
305
312
  * any order without needing `ON UPDATE CASCADE` on every constraint.
@@ -314,15 +321,20 @@ function migrateWorktreeIdPreservingChildren(db, oldId, newId) {
314
321
  // independently. Resets automatically at the end of the transaction.
315
322
  db.pragma('defer_foreign_keys = ON');
316
323
  const newExists = db.prepare('SELECT 1 FROM worktrees WHERE id = ?').get(newId);
317
- const children = getWorktreeChildTables(db);
324
+ const { noUpdate } = (0, worktree_child_tables_1.getWriteGuardedTables)(db);
325
+ const allChildren = (0, worktree_child_tables_1.getWorktreeChildTables)(db);
326
+ const children = allChildren.filter(({ table }) => !noUpdate.has(table));
318
327
  if (newExists) {
319
328
  // Collision guard. `path` is UNIQUE so two rows for the same directory
320
329
  // should never coexist, but if the DB is already inconsistent keep the
321
- // destination row's data and fold in the source's non-conflicting children;
322
- // conflicting leftovers are removed by CASCADE when the old row is deleted.
330
+ // destination row's data and fold in the source's non-conflicting children.
323
331
  for (const { table, column } of children) {
324
332
  db.prepare(`UPDATE OR IGNORE "${table}" SET "${column}" = ? WHERE "${column}" = ?`).run(newId, oldId);
325
333
  }
334
+ // Conflicting leftovers are dropped explicitly rather than left to CASCADE:
335
+ // tables without a foreign key never cascade, so relying on the parent
336
+ // delete alone would strand their rows on a dead ID (#1621).
337
+ deleteWorktreeChildRows(db, [oldId], allChildren);
326
338
  db.prepare('DELETE FROM worktrees WHERE id = ?').run(oldId);
327
339
  return;
328
340
  }
@@ -523,29 +535,38 @@ function getWorktreesByRepository(db, repositoryPath) {
523
535
  }
524
536
  /**
525
537
  * Delete all worktrees for a given repository path
526
- * Related data is automatically deleted via ON DELETE CASCADE in every table
527
- * holding a foreign key to worktrees(id) chat_messages, session_states,
528
- * worktree_memos, worktree_todos, agent_instances, scheduled_executions,
529
- * execution_logs, timer_messages and skill_installations (#1430). The live list
530
- * is whatever `PRAGMA foreign_key_list` reports; see getWorktreeChildTables.
538
+ * Child rows are deleted explicitly before the parent, for every table
539
+ * getWorktreeChildTables reports. ON DELETE CASCADE covers only the tables that
540
+ * declare a foreign key; `tasks`, `verification_runs` and `skill_operations`
541
+ * hold a `worktree_id` with no constraint and would otherwise survive their
542
+ * worktree as orphans (#1621). Deleting `verification_runs` still cascades to
543
+ * `verification_gate_results`, which does declare its foreign key.
531
544
  *
532
545
  * @param db - Database instance
533
546
  * @param repositoryPath - Path of the repository to delete
534
547
  * @returns Object containing the count of deleted worktrees
535
548
  */
536
549
  function deleteRepositoryWorktrees(db, repositoryPath) {
537
- const stmt = db.prepare(`
538
- DELETE FROM worktrees WHERE repository_path = ?
539
- `);
540
- const result = stmt.run(repositoryPath);
541
- return { deletedCount: result.changes };
550
+ const run = db.transaction(() => {
551
+ const ids = db
552
+ .prepare('SELECT id FROM worktrees WHERE repository_path = ?')
553
+ .all(repositoryPath)
554
+ .map((row) => row.id)
555
+ .filter((id) => id !== null);
556
+ deleteWorktreeChildRows(db, ids);
557
+ return db
558
+ .prepare('DELETE FROM worktrees WHERE repository_path = ?')
559
+ .run(repositoryPath).changes;
560
+ });
561
+ return { deletedCount: run() };
542
562
  }
543
563
  /**
544
564
  * Delete worktrees by their IDs
545
- * Related data is automatically deleted via ON DELETE CASCADE in every table
546
- * holding a foreign key to worktrees(id) — including skill_installations, whose
547
- * rows used to survive the worktree and make a re-created worktree at the same
548
- * path un-installable (#1430). See getWorktreeChildTables for the live list.
565
+ * Child rows are deleted explicitly before the parent, for every table
566
+ * getWorktreeChildTables reports — including skill_installations, whose rows
567
+ * used to survive the worktree and make a re-created worktree at the same path
568
+ * un-installable (#1430), and the constraint-less `tasks` / `verification_runs`
569
+ * / `skill_operations`, which no CASCADE can reach (#1621).
549
570
  *
550
571
  * @param db - Database instance
551
572
  * @param worktreeIds - Array of worktree IDs to delete
@@ -556,9 +577,11 @@ function deleteWorktreesByIds(db, worktreeIds) {
556
577
  return { deletedCount: 0 };
557
578
  }
558
579
  const placeholders = worktreeIds.map(() => '?').join(',');
559
- const stmt = db.prepare(`
560
- DELETE FROM worktrees WHERE id IN (${placeholders})
561
- `);
562
- const result = stmt.run(...worktreeIds);
563
- return { deletedCount: result.changes };
580
+ const run = db.transaction(() => {
581
+ deleteWorktreeChildRows(db, worktreeIds);
582
+ return db
583
+ .prepare(`DELETE FROM worktrees WHERE id IN (${placeholders})`)
584
+ .run(...worktreeIds).changes;
585
+ });
586
+ return { deletedCount: run() };
564
587
  }
@@ -4,7 +4,7 @@
4
4
  * Shared between response-poller.ts and API routes
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_SELECTION_LIST_PATTERN = exports.COPILOT_SEPARATOR_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_DIALOG_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
7
+ exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_SELECTION_LIST_PATTERN = exports.COPILOT_SEPARATOR_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_APPROVAL_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_DIALOG_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
8
8
  exports.findClaudeChromeStart = findClaudeChromeStart;
9
9
  exports.isCodexPromptReady = isCodexPromptReady;
10
10
  exports.getCodexActiveDialog = getCodexActiveDialog;
@@ -295,6 +295,29 @@ exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
295
295
  * a numbered prompt (PromptPanel with buttons).
296
296
  */
297
297
  exports.CODEX_SELECTION_LIST_PATTERN = /press\s+enter\s+to\s+(?:confirm|select)/i;
298
+ /**
299
+ * Codex CLI approval-request footer pattern (Issue #1628).
300
+ *
301
+ * Codex renders an approval request ("Would you like to run the following
302
+ * command?" / "Would you like to make the following edits?") with the SAME
303
+ * "Press enter to confirm" footer as a `/model`-style menu, which is why
304
+ * CODEX_SELECTION_LIST_PATTERN swallows it. The two differ in the escape verb:
305
+ * an approval request can be *cancelled* (it is the agent asking the human for
306
+ * permission), a menu can only be *gone back* from.
307
+ *
308
+ * Measured on codex-cli 0.146.0 (five consecutive live approval frames captured
309
+ * from a real session, plus two live `/model` picker frames):
310
+ * - approval : "Press enter to confirm or esc to cancel"
311
+ * - /model : "Press enter to confirm or esc to go back"
312
+ * - /model : "Press enter to select reasoning effort, or esc to dismiss."
313
+ *
314
+ * Used only as one of two OR'd approval signals (see isCodexApprovalRequest in
315
+ * status-detector.ts); the other is an interrogative question line, so a future
316
+ * rewording of either signal alone does not reopen Issue #1628.
317
+ *
318
+ * No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
319
+ */
320
+ exports.CODEX_APPROVAL_FOOTER_PATTERN = /esc\s+to\s+cancel/i;
298
321
  /**
299
322
  * Codex CLI pager / edit-previous (transcript) mode footer pattern (Issue #1017)
300
323
  *
@@ -100,7 +100,8 @@ const CODEX_NUMBERED_OPTION_PATTERN = /^\s*[❯›●]?\s*\d{1,2}[.)]\s/;
100
100
  * already-answered prompt left in the 50-line scan window rather than an active one.
101
101
  *
102
102
  * Codex keeps the answered "1. Yes / 2. No" block + "press number to confirm" footer in
103
- * its transcript (session historyLimit 50000) instead of repainting it away like Claude,
103
+ * its transcript (it draws on the normal screen, so tmux retains a scrollback for it —
104
+ * TMUX_HISTORY_LIMIT lines deep) instead of repainting it away like Claude,
104
105
  * so detectPrompt() keeps matching it and detectSessionStatus() reports `waiting` even
105
106
  * after the user answered and Codex resumed — the sidebar status dot then stays orange
106
107
  * forever (the reported bug).
@@ -147,6 +148,39 @@ function isCodexStalePrompt(contentLines) {
147
148
  }
148
149
  return lastPromptIdx >= 0 && lastThinkingIdx > lastPromptIdx;
149
150
  }
151
+ /**
152
+ * Issue #1628: decide whether a Codex frame that matched CODEX_SELECTION_LIST_PATTERN
153
+ * is the agent ASKING FOR APPROVAL rather than the user browsing a menu.
154
+ *
155
+ * Why this exists: Codex renders both with a "Press enter to confirm" footer, so the
156
+ * priority-0.8 selection-list branch (added for `/model` in Issue #622) also swallowed
157
+ * every approval request and returned `hasActivePrompt: false`. `isPromptWaiting` is the
158
+ * only blocked-on-a-human signal the current-output payload carries, so `commandmate wait
159
+ * --on-prompt agent` could never raise exit 10 for a Codex worker sitting on
160
+ * "Would you like to run the following command?" — it polled until the timeout while the
161
+ * agent was stopped. Auto-Yes was unaffected because it calls detectPrompt() directly,
162
+ * which parses these frames correctly; only the status-detector layer lost them.
163
+ *
164
+ * Two OR'd signals, both measured against live codex-cli 0.146.0 captures
165
+ * (5 approval frames from one real session + 2 `/model` picker frames):
166
+ * 1. an interrogative question line directly above the options
167
+ * ("Would you like to run the following command?" → the extracted question ends
168
+ * with "?"; a picker's ends with its description, e.g. "…browse all models.")
169
+ * 2. the approval escape verb ("esc to cancel" vs a menu's "esc to go back" /
170
+ * "esc to dismiss")
171
+ * Either alone covers every measured approval frame, so a rewording of one does not
172
+ * reopen the bug.
173
+ *
174
+ * Gated on `promptDetection.isPrompt` so a frame detectPrompt() could not parse into
175
+ * options (e.g. the unnumbered "Select a model" list of Issue #619) can never be
176
+ * promoted to an active prompt.
177
+ */
178
+ function isCodexApprovalRequest(promptDetection, selectionWindow) {
179
+ if (!promptDetection.isPrompt)
180
+ return false;
181
+ const question = promptDetection.promptData?.question?.trim() ?? '';
182
+ return question.endsWith('?') || cli_patterns_1.CODEX_APPROVAL_FOOTER_PATTERN.test(selectionWindow);
183
+ }
150
184
  /**
151
185
  * Detect session status with confidence level
152
186
  *
@@ -286,6 +320,21 @@ function detectSessionStatus(output, cliToolId, lastOutputTimestamp) {
286
320
  if (cli_patterns_1.CODEX_SELECTION_LIST_PATTERN.test(codexSelectionWindow)) {
287
321
  const codexPromptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
288
322
  const codexPromptDetection = (0, prompt_detector_1.detectPrompt)((0, cli_patterns_1.stripBoxDrawing)(cleanOutput), codexPromptOptions);
323
+ // Issue #1628: an approval request wears the same footer as a menu but is the
324
+ // agent blocked on the human, so it must surface as an active prompt (exit 10
325
+ // for `wait`, PromptPanel in the UI) instead of a navigable list. The #1160
326
+ // staleness guard still applies: an ALREADY-ANSWERED approval whose footer is
327
+ // still inside the window, with Codex running below it, is dead scrollback.
328
+ if (isCodexApprovalRequest(codexPromptDetection, codexSelectionWindow) &&
329
+ !isCodexStalePrompt(contentLines)) {
330
+ return {
331
+ status: 'waiting',
332
+ confidence: 'high',
333
+ reason: exports.STATUS_REASON.PROMPT_DETECTED,
334
+ hasActivePrompt: true,
335
+ promptDetection: codexPromptDetection,
336
+ };
337
+ }
289
338
  return {
290
339
  status: 'waiting',
291
340
  confidence: 'high',
@@ -333,7 +382,8 @@ function detectSessionStatus(output, cliToolId, lastOutputTimestamp) {
333
382
  if (promptDetection.isPrompt) {
334
383
  // Issue #1160: Codex keeps an ANSWERED approval / numbered-choice block
335
384
  // ("1. Yes / 2. No" + "press number to confirm" footer) in its transcript
336
- // (historyLimit 50000) instead of repainting it away like Claude. detectPrompt's
385
+ // (non-alternate-screen, so tmux keeps TMUX_HISTORY_LIMIT lines of scrollback for
386
+ // it) instead of repainting it away like Claude. detectPrompt's
337
387
  // 50-line window then keeps matching that dead prompt, so this priority-1 branch
338
388
  // returns `waiting` even after the user answered and Codex resumed — the sidebar
339
389
  // status dot stays orange forever (the reported bug). Guard with a position check:
@@ -502,11 +502,13 @@ async function startClaudeSession(options) {
502
502
  // (SF-S2-004: Explicit fall-through instead of hidden re-entry)
503
503
  }
504
504
  try {
505
- // Create tmux session with large history buffer for Claude output
505
+ // Create tmux session. Scrollback depth comes from the shared
506
+ // TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
507
+ // (Claude itself renders in the alternate screen and keeps history_size at 0,
508
+ // so the limit is inert for this tool; it still applies to the bare shell.)
506
509
  await (0, tmux_1.createSession)({
507
510
  sessionName,
508
511
  workingDirectory: worktreePath,
509
- historyLimit: 50000,
510
512
  });
511
513
  // SF-S2-003: Sanitize environment after createSession, before launching Claude CLI
512
514
  await (0, session_key_sender_1.sanitizeSessionEnvironment)(sessionName);
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * The shell pager that `prefix+g` opens inside a tmux popup (Issue #1623, 案A).
4
+ *
5
+ * ## Why the script is embedded here instead of shipped as a file
6
+ *
7
+ * `package.json`'s `files` list publishes `bin/`, `dist/`, `.next/`, `public/`,
8
+ * `scripts/hooks/` and `.env.example` — NOT `scripts/`. A path into the repo
9
+ * therefore does not exist in a global install, and an `npx` install lives in a
10
+ * cache directory npm may garbage-collect out from under a long-lived `bind-key`.
11
+ * Embedding the text and materializing it into `~/.commandmate/bin/` gives the
12
+ * binding one absolute path that is stable across global / local / npx installs.
13
+ *
14
+ * ## Why the squeeze is reimplemented in awk
15
+ *
16
+ * The popup must not depend on the CommandMate server, a node binary, or a built
17
+ * `dist/` — it only has tmux and a shell. awk is present everywhere those are.
18
+ * The duplication is not left to trust: `tests/unit/lib/tmux/transcript-squeeze.test.ts`
19
+ * runs THIS awk program over real captures and asserts byte equality with
20
+ * `squeezeTranscript()`, so the two cannot drift apart silently.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.PAGER_SCRIPT = exports.PAGER_SCRIPT_FILENAME = exports.SQUEEZE_AWK_PROGRAM = exports.SQUEEZE_AWK_LOCALE = void 0;
24
+ /**
25
+ * Locale the squeeze awk program MUST run under.
26
+ *
27
+ * Not cosmetic — the program is byte-oriented by construction, and `%c` is the
28
+ * one place where awk implementations disagree about what a "character" is:
29
+ *
30
+ * | awk | `sprintf("%c", 194)` |
31
+ * |-------------------------|----------------------|
32
+ * | mawk, BWK awk | byte `0xC2` |
33
+ * | gawk, `LC_ALL=C` | byte `0xC2` |
34
+ * | gawk, `*.UTF-8` locale | U+00C2 -> `C3 82` |
35
+ *
36
+ * `NBSP` below is assembled from bytes `C2 A0`, so under gawk in a UTF-8 locale
37
+ * it becomes a four-byte string that can never match a real NBSP — NBSP-only
38
+ * rows stop counting as blank and the frame comes back unsqueezed. That is not
39
+ * hypothetical: it is exactly how this shipped broken, green on macOS (BWK awk)
40
+ * and red on `ubuntu-latest`, whose `/usr/bin/awk` is gawk (Debian ranks the
41
+ * gawk alternative above mawk) under a UTF-8 locale.
42
+ *
43
+ * Pinning the locale — rather than making the regex match both encodings — is
44
+ * deliberate: an alternation covering the character form
45
+ * (`sprintf("%c%c",194,160) "|" sprintf("%c",160)`) fixes gawk but makes BWK awk
46
+ * abort with `multibyte conversion failure` in a UTF-8 locale. Measured on all
47
+ * three implementations, `LC_ALL=C` is the only setting where every one of them
48
+ * agrees, and it is byte-transparent: the same 1000-row fixture comes out with
49
+ * an identical sha256 from BWK awk, mawk and gawk.
50
+ *
51
+ * Scoped to the awk process alone, so `less` and tmux keep the user's locale and
52
+ * still render UTF-8 box drawing.
53
+ */
54
+ exports.SQUEEZE_AWK_LOCALE = 'C';
55
+ /**
56
+ * awk implementation of {@link import('./transcript-squeeze').squeezeTranscript}.
57
+ *
58
+ * Mirrors the TypeScript rules exactly:
59
+ * - `ANSI` is built from `sprintf("%c")` rather than a regex literal because
60
+ * `\033` / `\a` escapes inside a regex literal are not portable across awk
61
+ * implementations (macOS ships BWK awk, Linux usually gawk/mawk).
62
+ * - NBSP (UTF-8 `C2 A0`) is folded to a space before trimming, because
63
+ * JavaScript's `.trim()` treats U+00A0 as whitespace and awk's `[ \t]` does not.
64
+ * Without this the two implementations disagree on NBSP-only rows. This is the
65
+ * line that requires {@link SQUEEZE_AWK_LOCALE} — read that doc before editing.
66
+ * - Leading/trailing blank runs are dropped, runs of 1-2 kept verbatim, runs of
67
+ * 3+ collapsed to a single row carrying the run's ANSI sequences.
68
+ */
69
+ exports.SQUEEZE_AWK_PROGRAM = `BEGIN {
70
+ ESC = sprintf("%c", 27); BEL = sprintf("%c", 7); NBSP = sprintf("%c%c", 194, 160)
71
+ ANSI = ESC "\\\\[[0-9;]*[a-zA-Z]|" ESC "\\\\][^" BEL "]*" BEL "|\\\\[[0-9;]*m"
72
+ n = 0
73
+ }
74
+ { line[++n] = $0 }
75
+ END {
76
+ for (i = 1; i <= n; i++) {
77
+ s = line[i]; gsub(ANSI, "", s); gsub(NBSP, " ", s)
78
+ gsub(/^[ \\t\\r\\f\\v]+|[ \\t\\r\\f\\v]+$/, "", s)
79
+ blank[i] = (s == "")
80
+ }
81
+ seen = 0; i = 1
82
+ while (i <= n) {
83
+ if (!blank[i]) { print line[i]; seen = 1; i++; continue }
84
+ j = i; while (j <= n && blank[j]) j++
85
+ if (seen && j <= n) {
86
+ if (j - i <= 2) { for (k = i; k < j; k++) print line[k] }
87
+ else {
88
+ joined = ""; for (k = i; k < j; k++) joined = joined line[k]
89
+ keep = ""
90
+ while (match(joined, ANSI)) {
91
+ keep = keep substr(joined, RSTART, RLENGTH); joined = substr(joined, RSTART + RLENGTH)
92
+ }
93
+ print keep
94
+ }
95
+ }
96
+ i = j
97
+ }
98
+ }`;
99
+ /** Basename of the materialized script. */
100
+ exports.PAGER_SCRIPT_FILENAME = 'cm-read-pane.sh';
101
+ /**
102
+ * Full text of the pager script.
103
+ *
104
+ * Session resolution is done INSIDE the script on purpose. `display-popup` does
105
+ * NOT expand tmux formats in its shell-command (measured on 3.5a: a literal
106
+ * `#{session_name}` reaches the shell unexpanded), so the session cannot be baked
107
+ * into the binding. `tmux display-message -p` inside the popup returns the correct
108
+ * session and client, which is what makes one global binding serve every session.
109
+ *
110
+ * `=NAME:` is the exact-match target form required by Issue #1156 — a bare name
111
+ * prefix-matches and would show the `-2` instance's pane while the primary is
112
+ * stopped.
113
+ */
114
+ exports.PAGER_SCRIPT = `#!/bin/sh
115
+ # CommandMate reading mode (Issue #1623) — generated file, edits are overwritten.
116
+ #
117
+ # Renders the transcript of a CommandMate tmux session with layout-only blank rows
118
+ # squeezed away, so a pager lands on the composer instead of hundreds of rows of
119
+ # padding. Invoked by \`prefix+g\` through \`display-popup -E\`; also runnable by hand:
120
+ #
121
+ # sh ~/.commandmate/bin/${exports.PAGER_SCRIPT_FILENAME} mcbd-claude-my-worktree
122
+ #
123
+ # The popup is a SNAPSHOT. Press the key again to refresh it.
124
+ set -u
125
+
126
+ SESSION="\${1:-}"
127
+ if [ -z "$SESSION" ]; then
128
+ SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null) || SESSION=""
129
+ fi
130
+ if [ -z "$SESSION" ]; then
131
+ echo "cm-read-pane: no tmux session given and none could be resolved" >&2
132
+ exit 1
133
+ fi
134
+
135
+ LINES_BACK="\${CM_READ_LINES:-1000}"
136
+ case "$LINES_BACK" in
137
+ ''|*[!0-9]*) LINES_BACK=1000 ;;
138
+ esac
139
+
140
+ if [ -n "\${CM_READ_PAGER:-}" ]; then
141
+ PAGER_CMD="$CM_READ_PAGER"
142
+ elif command -v less >/dev/null 2>&1; then
143
+ PAGER_CMD="less -R +G"
144
+ else
145
+ PAGER_CMD="cat"
146
+ fi
147
+
148
+ # LC_ALL is pinned for awk ONLY (not for less, which still renders UTF-8 box
149
+ # drawing in the user's locale). Under gawk in a UTF-8 locale, sprintf("%c",194)
150
+ # yields U+00C2 rather than the byte 0xC2 and the NBSP fold below silently stops
151
+ # matching. See SQUEEZE_AWK_LOCALE in read-mode-pager.ts.
152
+ tmux capture-pane -pe -t "=$SESSION:" -S "-$LINES_BACK" -E - \\
153
+ | LC_ALL=${exports.SQUEEZE_AWK_LOCALE} awk '${exports.SQUEEZE_AWK_PROGRAM}' \\
154
+ | $PAGER_CMD
155
+
156
+ # Without a pager the popup would close before anything could be read.
157
+ if [ "$PAGER_CMD" = "cat" ]; then
158
+ printf '\\n[CommandMate] end of transcript — press Enter to close.' >&2
159
+ read -r _unused || true
160
+ fi
161
+ `;