commandmate 0.34.1 → 0.35.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 (311) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +176 -163
  3. package/.next/app-path-routes-manifest.json +27 -27
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +9 -9
  6. package/.next/required-server-files.json +1 -1
  7. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
  9. package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
  10. package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
  11. package/.next/server/app/api/app/update-check/route.js +1 -1
  12. package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
  14. package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
  16. package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/assistant/messages/route.js +1 -1
  18. package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
  19. package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/assistant/session/route.js +1 -1
  21. package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
  22. package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/assistant/start/route.js +1 -1
  24. package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
  25. package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/assistant/terminal/route.js +1 -1
  27. package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
  28. package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
  30. package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/capabilities/route.js +1 -1
  35. package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
  44. package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/hooks/claude-done/route.js +1 -1
  46. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  47. package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
  49. package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
  56. package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
  57. package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
  58. package/.next/server/app/api/relays/route.js +1 -1
  59. package/.next/server/app/api/relays/route.js.nft.json +1 -1
  60. package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
  62. package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  66. package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
  67. package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
  68. package/.next/server/app/api/repositories/clone/route.js +1 -1
  69. package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
  70. package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
  72. package/.next/server/app/api/repositories/restore/route.js +1 -1
  73. package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
  74. package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/repositories/route.js +1 -1
  76. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  77. package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
  78. package/.next/server/app/api/repositories/scan/route.js +1 -1
  79. package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
  80. package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/repositories/sync/route.js +1 -1
  82. package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
  83. package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
  86. package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
  88. package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
  92. package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  94. package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
  96. package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  103. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  104. package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
  106. package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
  108. package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
  109. package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  114. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  115. package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
  116. package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
  119. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
  120. package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
  123. package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
  126. package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
  128. package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
  141. package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
  143. package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
  146. package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
  147. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  148. package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
  149. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  150. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  151. package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
  153. package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
  155. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
  160. package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  162. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  163. package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  166. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  167. package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  169. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  170. package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
  171. package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
  173. package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
  175. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  176. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  177. package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
  179. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
  181. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
  183. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
  184. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
  185. package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
  186. package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
  187. package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
  188. package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
  189. package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
  190. package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
  191. package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
  192. package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
  193. package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
  194. package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
  195. package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
  196. package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
  197. package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
  198. package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
  199. package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
  200. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
  201. package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
  202. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
  203. package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
  204. package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
  205. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
  206. package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
  207. package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
  208. package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
  209. package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
  210. package/.next/server/app/api/worktrees/route.js +1 -1
  211. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  212. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  213. package/.next/server/app/chat/page.js +2 -2
  214. package/.next/server/app/chat/page.js.nft.json +1 -1
  215. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  216. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  217. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  218. package/.next/server/app/more/page.js +2 -2
  219. package/.next/server/app/more/page.js.nft.json +1 -1
  220. package/.next/server/app/more/page_client-reference-manifest.js +1 -1
  221. package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
  222. package/.next/server/app/page.js +1 -1
  223. package/.next/server/app/page.js.nft.json +1 -1
  224. package/.next/server/app/page_client-reference-manifest.js +1 -1
  225. package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
  226. package/.next/server/app/repositories/page.js +2 -2
  227. package/.next/server/app/repositories/page.js.nft.json +1 -1
  228. package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
  229. package/.next/server/app/review/page.js +1 -1
  230. package/.next/server/app/review/page.js.nft.json +1 -1
  231. package/.next/server/app/review/page_client-reference-manifest.js +1 -1
  232. package/.next/server/app/sessions/page.js +1 -1
  233. package/.next/server/app/sessions/page.js.nft.json +1 -1
  234. package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  235. package/.next/server/app/skills/[skillId]/page.js +2 -2
  236. package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
  237. package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
  238. package/.next/server/app/skills/installed/page.js +2 -2
  239. package/.next/server/app/skills/installed/page.js.nft.json +1 -1
  240. package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
  241. package/.next/server/app/skills/page.js +1 -1
  242. package/.next/server/app/skills/page.js.nft.json +1 -1
  243. package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  244. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  245. package/.next/server/app/worktrees/[id]/page.js +11 -11
  246. package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
  247. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  248. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  249. package/.next/server/app-paths-manifest.json +27 -27
  250. package/.next/server/chunks/1210.js +1 -1
  251. package/.next/server/chunks/{6286.js → 1467.js} +6 -6
  252. package/.next/server/chunks/1886.js +1 -1
  253. package/.next/server/chunks/2980.js +1 -1
  254. package/.next/server/chunks/3077.js +1 -1
  255. package/.next/server/chunks/3241.js +1 -1
  256. package/.next/server/chunks/3323.js +1 -1
  257. package/.next/server/chunks/434.js +1 -1
  258. package/.next/server/chunks/4443.js +1 -1
  259. package/.next/server/chunks/450.js +1 -1
  260. package/.next/server/chunks/6130.js +1 -1
  261. package/.next/server/chunks/650.js +1 -1
  262. package/.next/server/chunks/6677.js +3 -3
  263. package/.next/server/chunks/7075.js +1 -1
  264. package/.next/server/chunks/7506.js +1 -1
  265. package/.next/server/chunks/7905.js +1 -1
  266. package/.next/server/chunks/8728.js +8 -0
  267. package/.next/server/chunks/8918.js +5 -5
  268. package/.next/server/chunks/9123.js +2 -2
  269. package/.next/server/middleware-manifest.json +5 -5
  270. package/.next/server/pages/500.html +1 -1
  271. package/.next/server/server-reference-manifest.json +1 -1
  272. package/.next/static/chunks/2407-b9001cc6041876e1.js +1 -0
  273. package/.next/static/chunks/{4322-dfd5a7119b5ba4fc.js → 4322-59a7dbd77a7763cc.js} +1 -1
  274. package/.next/static/chunks/4388-410b1d8ed0d8d98b.js +1 -0
  275. package/.next/static/chunks/4718-56ebb6e55073ed5e.js +1 -0
  276. package/.next/static/chunks/{2652-18b8fea64413467e.js → 4809-145884b958e7d9a6.js} +1 -1
  277. package/.next/static/chunks/app/chat/{page-e60d07c4c79602f0.js → page-609c9a0f5ce44799.js} +1 -1
  278. package/.next/static/chunks/app/error-c1dc1038d0a84a33.js +1 -0
  279. package/.next/static/chunks/app/global-error-49f765ecb7fd6527.js +1 -0
  280. package/.next/static/chunks/app/{layout-4c2f21e3f653ee4c.js → layout-114dc6e93e332392.js} +1 -1
  281. package/.next/static/chunks/app/more/{page-fc94f86aa881489e.js → page-7c5380e4a7561935.js} +1 -1
  282. package/.next/static/chunks/app/{page-26a42881c8648cd3.js → page-a4f03c188610c6c8.js} +1 -1
  283. package/.next/static/chunks/app/repositories/{page-2576158c8420fda0.js → page-a15efb4ce1d1721e.js} +1 -1
  284. package/.next/static/chunks/app/review/{page-35b99baaf3c93b38.js → page-90ac618489676cbc.js} +1 -1
  285. package/.next/static/chunks/app/sessions/{page-a3ab6f122ec60d5a.js → page-6486d9a643c1dc6f.js} +1 -1
  286. package/.next/static/chunks/app/skills/[skillId]/{page-06799157652e8fa9.js → page-844872df04f52943.js} +1 -1
  287. package/.next/static/chunks/app/skills/installed/{page-2f3987e3a131c8d0.js → page-566dfde26d7e63ba.js} +1 -1
  288. package/.next/static/chunks/app/skills/{page-e70bfcf5ed4e4865.js → page-7859540e8092aace.js} +1 -1
  289. package/.next/static/chunks/app/worktrees/[id]/page-3a58ae66e203f935.js +1 -0
  290. package/.next/trace +14 -13
  291. package/dist/lib/detection/tools/verified-against.d.ts +20 -0
  292. package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
  293. package/dist/lib/detection/tools/verified-against.js +20 -0
  294. package/dist/server/src/config/websocket-config.js +67 -0
  295. package/dist/server/src/lib/auto-yes-poller.js +40 -7
  296. package/dist/server/src/lib/detection/selection-shape.js +361 -3
  297. package/dist/server/src/lib/detection/tools/command-code/detect.js +96 -0
  298. package/dist/server/src/lib/detection/tools/command-code/dialog.js +314 -0
  299. package/dist/server/src/lib/detection/tools/verified-against.js +20 -0
  300. package/dist/server/src/lib/polling/response-checker.js +48 -2
  301. package/dist/server/src/lib/ws-server.js +111 -1
  302. package/package.json +1 -1
  303. package/public/sw.js +12 -0
  304. package/.next/server/chunks/595.js +0 -8
  305. package/.next/static/chunks/4718-29cfcfb806724d9a.js +0 -1
  306. package/.next/static/chunks/8361-0142beb59be2abf3.js +0 -1
  307. package/.next/static/chunks/app/error-27ab04bce6545dae.js +0 -1
  308. package/.next/static/chunks/app/global-error-7b761b8e181ffc1f.js +0 -1
  309. package/.next/static/chunks/app/worktrees/[id]/page-60eab4e24c37cb77.js +0 -1
  310. /package/.next/static/{oDWX0JcwfhUYrMnnqDWVC → 0vChXhzA5YWj3f0D6A-Cn}/_buildManifest.js +0 -0
  311. /package/.next/static/{oDWX0JcwfhUYrMnnqDWVC → 0vChXhzA5YWj3f0D6A-Cn}/_ssgManifest.js +0 -0
@@ -109,6 +109,26 @@ export declare const ANTIGRAVITY_VERIFIED_AGAINST: {
109
109
  * tmux socket at the production geometry — the default pane size does not
110
110
  * reproduce the 200-column rules that fence the composer, and the status row
111
111
  * drops its `esc to interrupt` tail below 72 columns.
112
+ *
113
+ * ## Deliberately NOT advanced by Issues #2521 / #2522
114
+ *
115
+ * Both read the `AskUserQuestion` screen, and both did it from the anonymised
116
+ * capture in `tests/fixtures/command-code-askuserquestion-2521/` — whose rows
117
+ * 410-423 are verbatim from a live pane, but whose version is what the reporting
118
+ * session SAID it was (1.53.0) rather than something re-probed. #2522's own
119
+ * fixture directory is synthetic throughout, and the send mode
120
+ * (`submitMode: 'answer_only'`) was chosen from a STATIC reading of a locally
121
+ * installed 1.53.1 `QuestionPrompt` plus the safe-direction argument in
122
+ * `tools/command-code/dialog.ts` — not from a keystroke sent at a live pane.
123
+ *
124
+ * None of that is the measurement this stamp records. #2304 set the precedent in
125
+ * the other direction: seven frames re-captured live on 1.49.0 changed no rule,
126
+ * and the stamp still says 1.40.1 because that is the build the rules were READ
127
+ * OFF. Advancing it here on a reported version, a synthetic fixture and a
128
+ * package read off disk would make `getDetectorFreshness` claim a measurement
129
+ * nobody took. When a live 1.53.x session is probed — the question screen, the
130
+ * keys it accepts and what one of them advances — bump this to that exact
131
+ * version with its own `capturedAt`.
112
132
  */
113
133
  export declare const COMMAND_CODE_VERIFIED_AGAINST: {
114
134
  readonly version: "1.40.1";
@@ -1 +1 @@
1
- {"version":3,"file":"verified-against.d.ts","sourceRoot":"","sources":["../../../../src/lib/detection/tools/verified-against.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,iEAAiE;AACjE,eAAO,MAAM,uBAAuB;;;;CAI1B,CAAC;AAEX,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB;;;;CAI3B,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB;;;;CAI5B,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,4BAA4B;;;;CAI/B,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B;;;;CAIhC,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B;;;;CAI9B,CAAC;AAEX,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CASpF,CAAC"}
1
+ {"version":3,"file":"verified-against.d.ts","sourceRoot":"","sources":["../../../../src/lib/detection/tools/verified-against.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,iEAAiE;AACjE,eAAO,MAAM,uBAAuB;;;;CAI1B,CAAC;AAEX,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB;;;;CAI3B,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB;;;;CAI5B,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,4BAA4B;;;;CAI/B,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B;;;;CAIhC,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B;;;;CAI9B,CAAC;AAEX,iFAAiF;AACjF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CASpF,CAAC"}
@@ -112,6 +112,26 @@ exports.ANTIGRAVITY_VERIFIED_AGAINST = {
112
112
  * tmux socket at the production geometry — the default pane size does not
113
113
  * reproduce the 200-column rules that fence the composer, and the status row
114
114
  * drops its `esc to interrupt` tail below 72 columns.
115
+ *
116
+ * ## Deliberately NOT advanced by Issues #2521 / #2522
117
+ *
118
+ * Both read the `AskUserQuestion` screen, and both did it from the anonymised
119
+ * capture in `tests/fixtures/command-code-askuserquestion-2521/` — whose rows
120
+ * 410-423 are verbatim from a live pane, but whose version is what the reporting
121
+ * session SAID it was (1.53.0) rather than something re-probed. #2522's own
122
+ * fixture directory is synthetic throughout, and the send mode
123
+ * (`submitMode: 'answer_only'`) was chosen from a STATIC reading of a locally
124
+ * installed 1.53.1 `QuestionPrompt` plus the safe-direction argument in
125
+ * `tools/command-code/dialog.ts` — not from a keystroke sent at a live pane.
126
+ *
127
+ * None of that is the measurement this stamp records. #2304 set the precedent in
128
+ * the other direction: seven frames re-captured live on 1.49.0 changed no rule,
129
+ * and the stamp still says 1.40.1 because that is the build the rules were READ
130
+ * OFF. Advancing it here on a reported version, a synthetic fixture and a
131
+ * package read off disk would make `getDetectorFreshness` claim a measurement
132
+ * nobody took. When a live 1.53.x session is probed — the question screen, the
133
+ * keys it accepts and what one of them advances — bump this to that exact
134
+ * version with its own `capturedAt`.
115
135
  */
116
136
  exports.COMMAND_CODE_VERIFIED_AGAINST = {
117
137
  version: '1.40.1',
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * WebSocket heartbeat / reconnect constants shared by the server and the browser.
4
+ *
5
+ * Issue #2502: the socket had no liveness probe at all, so a half-open path — a
6
+ * phone that walked out of range, a laptop resuming from sleep, a Wi-Fi/LTE
7
+ * handover — stayed `connected` on both ends while every frame fell on the
8
+ * floor. TCP alone does not notice: without traffic neither side gets a RST,
9
+ * and `readyState` keeps answering OPEN. The only way out is to put bytes on
10
+ * the wire on a schedule and treat silence as a disconnect.
11
+ *
12
+ * Server and client read the same numbers from here because the two halves are
13
+ * a single protocol: the client's patience has to be a multiple of the server's
14
+ * beat, or a healthy connection gets killed between beats.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.WS_HALF_OPEN_CLOSE_CODE = exports.WS_RECONNECT_JITTER_RATIO = exports.WS_RECONNECT_MAX_DELAY_MS = exports.WS_RECONNECT_BASE_DELAY_MS = exports.WS_CLIENT_LIVENESS_TIMEOUT_MS = exports.WS_CLIENT_LIVENESS_CHECK_INTERVAL_MS = exports.WS_HEARTBEAT_MESSAGE_TYPE = exports.WS_SERVER_HEARTBEAT_INTERVAL_MS = void 0;
18
+ /**
19
+ * How often the server pings every client and emits an application-level beat.
20
+ *
21
+ * 30s is the `ws` library's own recipe cadence and stays well inside the idle
22
+ * timeouts of the proxies CommandMate is tunnelled through (Cloudflare Quick
23
+ * Tunnel, Tailscale Serve), which is a second reason to send it at all.
24
+ */
25
+ exports.WS_SERVER_HEARTBEAT_INTERVAL_MS = 30_000;
26
+ /**
27
+ * Server → client application-level beat (Issue #2502).
28
+ *
29
+ * A protocol-level ping is invisible to a browser: the WebSocket API answers
30
+ * pings automatically and gives page JavaScript no way to observe either the
31
+ * ping or its own pong. So the server sends this frame alongside the protocol
32
+ * ping — same cadence, same sweep — purely so the tab has something it CAN see.
33
+ * The client consumes it for liveness and does not forward it to listeners.
34
+ */
35
+ exports.WS_HEARTBEAT_MESSAGE_TYPE = 'heartbeat';
36
+ /** How often the client checks how long it has been since anything arrived. */
37
+ exports.WS_CLIENT_LIVENESS_CHECK_INTERVAL_MS = 5_000;
38
+ /**
39
+ * Silence, in ms, after which the client declares the socket half-open.
40
+ *
41
+ * 2.5x the server beat: one lost or late heartbeat must not tear down a healthy
42
+ * connection, but two in a row is not something a live path does. Anything
43
+ * inbound refreshes the clock — a broadcast, a terminal chunk, the beat — so a
44
+ * busy socket never gets near this.
45
+ */
46
+ exports.WS_CLIENT_LIVENESS_TIMEOUT_MS = 75_000;
47
+ /** Default base delay for the client's reconnect backoff. */
48
+ exports.WS_RECONNECT_BASE_DELAY_MS = 1_000;
49
+ /** Default ceiling for the client's reconnect backoff. */
50
+ exports.WS_RECONNECT_MAX_DELAY_MS = 30_000;
51
+ /**
52
+ * Fraction of the computed backoff the client randomises by, +/- (Issue #2502).
53
+ *
54
+ * Without it every tab that was connected to a restarting server retries on the
55
+ * identical 1s/2s/4s grid and the thundering herd lands on the first socket the
56
+ * new process opens. +/-20% is enough to smear the herd across the window while
57
+ * keeping the backoff's shape recognisable.
58
+ */
59
+ exports.WS_RECONNECT_JITTER_RATIO = 0.2;
60
+ /**
61
+ * Close code the client uses when it gives up on a silent socket.
62
+ *
63
+ * In the application-private 4000-4999 range (1000-2999 are reserved by the
64
+ * protocol and browsers throw on most of them), so a server-side close log can
65
+ * tell "the tab decided this path was dead" apart from an ordinary goodbye.
66
+ */
67
+ exports.WS_HALF_OPEN_CLOSE_CODE = 4001;
@@ -49,6 +49,7 @@ exports.clearAllPollerStates = clearAllPollerStates;
49
49
  exports.getLastServerResponseTimestamp = getLastServerResponseTimestamp;
50
50
  exports.isPollerActive = isPollerActive;
51
51
  exports.validatePollingContext = validatePollingContext;
52
+ exports.capturePollerFrame = capturePollerFrame;
52
53
  exports.captureAndCleanOutput = captureAndCleanOutput;
53
54
  exports.processStopConditionDelta = processStopConditionDelta;
54
55
  exports.detectAndRespondToPrompt = detectAndRespondToPrompt;
@@ -224,9 +225,28 @@ function validatePollingContext(compositeKey, pollerState) {
224
225
  }
225
226
  return 'valid';
226
227
  }
228
+ /**
229
+ * Capture tmux session output once, keeping both spellings (Issue #2522).
230
+ *
231
+ * @internal Exported for testing purposes only.
232
+ * @param worktreeId - Worktree identifier
233
+ * @param cliToolId - CLI tool type being polled
234
+ * @param captureLines - Optional number of lines to capture
235
+ * @param instanceId - Optional agent instance ID (Issue #896; defaults to primary session)
236
+ * @returns The tick's raw capture and its cleaned form
237
+ */
238
+ async function capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId) {
239
+ const lines = captureLines ?? auto_yes_config_1.FULL_CAPTURE_LINES;
240
+ const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, lines, instanceId);
241
+ return { raw: output, clean: (0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output)) };
242
+ }
227
243
  /**
228
244
  * Capture tmux session output and strip ANSI escape codes.
229
245
  *
246
+ * Kept as the cleaned half of {@link capturePollerFrame} so the many callers and
247
+ * tests that only ever wanted that string are untouched; `pollAutoYes` takes the
248
+ * pair. `stripBoxDrawing` still runs exactly once per capture either way.
249
+ *
230
250
  * @internal Exported for testing purposes only.
231
251
  * @param worktreeId - Worktree identifier
232
252
  * @param cliToolId - CLI tool type being polled
@@ -235,9 +255,7 @@ function validatePollingContext(compositeKey, pollerState) {
235
255
  * @returns Cleaned output string (ANSI stripped)
236
256
  */
237
257
  async function captureAndCleanOutput(worktreeId, cliToolId, captureLines, instanceId) {
238
- const lines = captureLines ?? auto_yes_config_1.FULL_CAPTURE_LINES;
239
- const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, lines, instanceId);
240
- return (0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output));
258
+ return (await capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId)).clean;
241
259
  }
242
260
  /**
243
261
  * Process stop condition check using delta-based approach.
@@ -276,9 +294,12 @@ function processStopConditionDelta(compositeKey, pollerState, cleanOutput) {
276
294
  * @param cleanOutput - ANSI-stripped terminal output
277
295
  * @param precomputedLines - Optional pre-split lines to reuse
278
296
  * @param instanceId - Optional agent instance ID (Issue #896; defaults to primary session)
297
+ * @param rawOutput - The SAME tick's capture with its box drawing intact
298
+ * (Issue #2522). Omitting it costs Command Code's `AskUserQuestion` and
299
+ * nothing else; see {@link capturePollerFrame}.
279
300
  * @returns 'responded' | 'no_prompt' | 'duplicate' | 'no_answer' | 'error'
280
301
  */
281
- async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId) {
302
+ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId, rawOutput) {
282
303
  const compositeKey = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
283
304
  try {
284
305
  // 1. Detect prompt.
@@ -301,7 +322,15 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
301
322
  // `cleanOutput` is handed over already stripped, and the clean-frame entry
302
323
  // is the one that does not strip again (`stripBoxDrawing` is not
303
324
  // idempotent), so this poller and the response poller judge identical text.
304
- const promptDetection = (0, response_checker_1.detectPromptOnCleanFrame)(cleanOutput, cliToolId, precomputedLines);
325
+ //
326
+ // Issue #2522 adds the fourth argument, and it is a WIRING change rather
327
+ // than a reader one: Command Code's `AskUserQuestion` is anchored on the
328
+ // rule row `captureAndCleanOutput` blanks, so teaching
329
+ // `detectPromptOnCleanFrame` to read it changed nothing on this path until
330
+ // the same tick's raw capture reached it. `precomputedLines` stays the split
331
+ // of `cleanOutput` — the stop-condition delta and every other tool's reading
332
+ // are measured on that string and must not move.
333
+ const promptDetection = (0, response_checker_1.detectPromptOnCleanFrame)(cleanOutput, cliToolId, precomputedLines, rawOutput);
305
334
  if (!promptDetection.isPrompt || !promptDetection.promptData) {
306
335
  pollerState.lastAnsweredPromptKey = null;
307
336
  pollerState.lastAnsweredAt = null;
@@ -519,14 +548,18 @@ async function pollAutoYes(worktreeId, cliToolId, instanceId) {
519
548
  const captureLines = autoYesState?.stopPattern
520
549
  ? auto_yes_config_1.FULL_CAPTURE_LINES
521
550
  : auto_yes_config_1.REDUCED_CAPTURE_LINES;
522
- const cleanOutput = await captureAndCleanOutput(worktreeId, cliToolId, captureLines, instanceId);
551
+ // Issue #2522: one capture, both spellings. The cleaned string drives every
552
+ // step below exactly as it did; the raw one is carried to the prompt reading
553
+ // so a dialog anchored on box drawing is not invisible to the one path that
554
+ // can answer it without a human.
555
+ const { raw: rawOutput, clean: cleanOutput } = await capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId);
523
556
  const lines = cleanOutput.split('\n');
524
557
  // 3. Stop condition delta check (Issue #314)
525
558
  if (processStopConditionDelta(compositeKey, pollerState, cleanOutput)) {
526
559
  return;
527
560
  }
528
561
  // 4. Detect and respond to prompt
529
- const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId);
562
+ const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId, rawOutput);
530
563
  if (result === 'responded') {
531
564
  scheduleNextPoll(worktreeId, cliToolId, instanceId, auto_yes_state_1.COOLDOWN_INTERVAL_MS);
532
565
  return;
@@ -66,6 +66,8 @@
66
66
  Object.defineProperty(exports, "__esModule", { value: true });
67
67
  exports.DISMISSABLE_PANEL_FOOTER_PATTERN = exports.COMMAND_CODE_SELECTION_LIST_FOOTER = exports.SET_AS_DEFAULT_FOOTER_PATTERN = exports.SESSION_SCOPE_FOOTER_PATTERN = exports.MAX_OPTION_NUMBER = exports.SELECTION_SHAPE_TAIL_LINE_COUNT = void 0;
68
68
  exports.hasDismissablePanelFooter = hasDismissablePanelFooter;
69
+ exports.readCommandCodeQuestionRegion = readCommandCodeQuestionRegion;
70
+ exports.hasCommandCodeQuestionChrome = hasCommandCodeQuestionChrome;
69
71
  exports.extractCommandCodeSelectionListFrame = extractCommandCodeSelectionListFrame;
70
72
  exports.readSelectionListShape = readSelectionListShape;
71
73
  exports.shouldOfferOptionNumbers = shouldOfferOptionNumbers;
@@ -249,6 +251,344 @@ const COMMAND_CODE_RULE_ROW_PATTERN = /^\u2500+$/;
249
251
  * `tests/fixtures/`.
250
252
  */
251
253
  const COMMAND_CODE_RULE_MIN_COLUMNS = 40;
254
+ /**
255
+ * The cursor glyph Command Code draws against the highlighted option.
256
+ *
257
+ * U+276F, measured on the capture Issue #2521 was raised from. Only this one,
258
+ * and not codex's `›` or gemini's `●`: {@link readCommandCodeQuestionRegion}
259
+ * counts cursors to decide whether the region holds ONE live dialog, and a
260
+ * wider alphabet would start counting glyphs that are not cursors at all.
261
+ */
262
+ const COMMAND_CODE_CURSOR_GLYPH = '❯';
263
+ /**
264
+ * The markers Command Code paints on a question screen's tab strip.
265
+ *
266
+ * Measured row: `● Dispatch | ◯ Review` — a filled disc on the tab that has the
267
+ * screen and a hollow ring on the one that does not. The names are NOT part of
268
+ * the reading (a different question draws different tabs); what is read is the
269
+ * strip's structure, which is what {@link isCommandCodeQuestionTabRow} states.
270
+ *
271
+ * Two families rather than one alphabet, because "one of these is filled and one
272
+ * of these is not" is the whole signal. A list of bullets (`● item one`) carries
273
+ * the first family and nothing else, and a bullet list is the thing this
274
+ * reading must never mistake for a dialog.
275
+ */
276
+ const COMMAND_CODE_TAB_SELECTED_MARKERS = '●◉⦿';
277
+ /** The hollow half of {@link COMMAND_CODE_TAB_SELECTED_MARKERS}'s pair. */
278
+ const COMMAND_CODE_TAB_UNSELECTED_MARKERS = '◯○◌⚪';
279
+ /** One `<marker> <label>` cell of the tab strip. */
280
+ const COMMAND_CODE_TAB_SEGMENT_PATTERN = new RegExp(`^\\s*([${COMMAND_CODE_TAB_SELECTED_MARKERS}${COMMAND_CODE_TAB_UNSELECTED_MARKERS}])\\s+\\S`);
281
+ /**
282
+ * Is this row Command Code's question-screen tab strip?
283
+ *
284
+ * Three conditions, and each one is a bullet list this predicate has to refuse:
285
+ *
286
+ * - **two or more `|`-separated cells.** `● Dispatch` on its own is a bullet;
287
+ * `● Dispatch | ◯ Review` is a strip. The separator is the cheapest part of
288
+ * the structure and the one prose never has in this position;
289
+ * - **every cell is `<marker> <label>`.** A row that is a strip for its first
290
+ * cell and prose for its second is prose;
291
+ * - **at least one filled marker and at least one hollow one.** A tab strip
292
+ * says which tab has the screen. A row of identical bullets does not, and
293
+ * `● one | ● two` is exactly the list-with-a-pipe this rules out.
294
+ */
295
+ function isCommandCodeQuestionTabRow(line) {
296
+ const segments = line.split('|');
297
+ if (segments.length < 2)
298
+ return false;
299
+ let selected = 0;
300
+ let unselected = 0;
301
+ for (const segment of segments) {
302
+ const match = COMMAND_CODE_TAB_SEGMENT_PATTERN.exec(segment);
303
+ if (match === null)
304
+ return false;
305
+ if (COMMAND_CODE_TAB_SELECTED_MARKERS.includes(match[1]))
306
+ selected += 1;
307
+ else
308
+ unselected += 1;
309
+ }
310
+ return selected > 0 && unselected > 0;
311
+ }
312
+ /**
313
+ * Rows that mean the region spans more than the one dialog on screen now.
314
+ *
315
+ * Issue #2521's condition 4: the tab strip and its options have to be the
316
+ * SAME screen, with no other turn's generating or completion UI between them.
317
+ * Structurally that is mostly settled by taking the LAST rule row as the top
318
+ * edge — a new turn repaints the composer, and the composer's own lower rule
319
+ * then becomes that edge — so this pattern is the belt to that braces, for a
320
+ * frame captured mid-repaint.
321
+ *
322
+ * Two rows, both restated here rather than imported: `cli-patterns` owns
323
+ * Command Code's busy and marker vocabularies and pulls the logger and the tool
324
+ * registry in with them, and this module is the browser-safe leaf the chat
325
+ * surface imports (see the module docblock).
326
+ *
327
+ * - `esc to interrupt`, the tail Command Code appends to every status row
328
+ * (`COMMAND_CODE_INTERRUPT_HINT_PATTERN`). A turn is in flight;
329
+ * - a row that OPENS with U+273B and a word — `✻ Thought for 1 second`,
330
+ * `✻ Worked for 4s`. Matched by shape rather than by the two English verbs,
331
+ * so a reworded or localised marker still ends the region.
332
+ */
333
+ const COMMAND_CODE_QUESTION_REGION_REJECT_PATTERN = /\besc\s+to\s+interrupt\b|^[^\S\n]*✻[^\S\n]+\S/im;
334
+ /**
335
+ * Where a footer-less Command Code question screen sits on a pane (Issue #2521).
336
+ *
337
+ * ## The frame this exists for
338
+ *
339
+ * Command Code's `AskUserQuestion` draws no hint-bar footer. It draws a rule,
340
+ * a tab strip, the question, and the options — and when an option's description
341
+ * WRAPS, the continuation row begins with a single space:
342
+ *
343
+ * ────────────────────────────… (200 columns of U+2500)
344
+ * ● Dispatch | ◯ Review
345
+ *
346
+ * Approve proceeding from the plan into worktree creation and dispatch?
347
+ *
348
+ * ❯ 1. Prepare worktrees + dispatch (Recommended)
349
+ * I create the worktrees and pause for you to
350
+ * answer).
351
+ * 2. Worktrees only, then pause
352
+ * …
353
+ *
354
+ * Measured 2026-09-12 on a pane reported as Command Code 1.53.0 at the
355
+ * production 200x1000; the anonymised capture is
356
+ * `tests/fixtures/command-code-askuserquestion-2521/`. Nothing read it:
357
+ * `detectPrompt` stops its scan at ` answer).` and never reaches option 1, and
358
+ * the selection-list branch below wants a footer this screen does not draw. So
359
+ * the frame fell through to the generic composer check — which matches, because
360
+ * `COMMAND_CODE_PROMPT_PATTERN` is `^❯(\s*$|\s+\S)` and `❯ 1. Prepare …` is a
361
+ * `❯` followed by a space and a glyph — and the pane was published as
362
+ * `ready` / `input_prompt`. `wait` read that as a finished turn and exited 0 on
363
+ * a session that was asking a question (Issue #2521's "偽完了").
364
+ *
365
+ * ## What this function is NOT
366
+ *
367
+ * It is not a general reading of footer-less dialogs, and it deliberately does
368
+ * not make `optionCount` into a dialog test. {@link readSelectionListShape}
369
+ * counts the numbers in the tail of ANY frame, an ordinary numbered answer
370
+ * included, and the whole point of the conditions below is that a number list
371
+ * on its own proves nothing. Five things have to line up:
372
+ *
373
+ * 1. the LAST rule row that qualifies as the seam ({@link
374
+ * COMMAND_CODE_RULE_ROW_PATTERN} at {@link COMMAND_CODE_RULE_MIN_COLUMNS} or
375
+ * wider) is the top edge, and the last row carrying content is the bottom.
376
+ * Taking the LAST one is what keeps an older dialog higher up the pane out:
377
+ * a new turn repaints the composer, whose own rules sit below the old
378
+ * dialog, and the region is then the composer's hint row;
379
+ * 2. the first row under that rule is a tab strip
380
+ * ({@link isCommandCodeQuestionTabRow}) and a non-blank question body
381
+ * follows it before the first option;
382
+ * 3. the options are a STRICT run `1.` … `N.`, 2 ≤ N ≤ {@link
383
+ * MAX_OPTION_NUMBER}. A gap, a repeat or a list that starts at `2` is not
384
+ * this screen;
385
+ * 4. exactly one {@link COMMAND_CODE_CURSOR_GLYPH} in the region, and it is on
386
+ * an option row. Two cursors means two screens — typically a composer's own
387
+ * `❯` under the dialog — and none means the highlight is elsewhere;
388
+ * 5. no filter box, no `/model` footer, no dismiss-only footer and no other
389
+ * turn's UI in the region ({@link FILTER_INPUT_PATTERN},
390
+ * {@link COMMAND_CODE_SELECTION_LIST_FOOTER},
391
+ * {@link DISMISSABLE_PANEL_FOOTER_PATTERN},
392
+ * {@link COMMAND_CODE_QUESTION_REGION_REJECT_PATTERN}). `Type something...`
393
+ * is an OPTION on this screen, not a filter, and the filter pattern is
394
+ * narrow enough to tell them apart.
395
+ *
396
+ * `null` for anything else, which leaves every existing verdict and every
397
+ * existing crop exactly as they were.
398
+ *
399
+ * ## Tool-id-free and browser-safe, on purpose
400
+ *
401
+ * Three call sites read the same answer: the command-code detector's
402
+ * `afterPrompt`, {@link extractCommandCodeSelectionListFrame} (which runs for
403
+ * every CLI, with no tool id in hand) and `ChatSurface`, a client component.
404
+ * So this takes a raw frame and nothing else, and imports nothing but
405
+ * `./ansi` — the same trade the rest of this module makes.
406
+ *
407
+ * ## The one spelling it cannot answer on
408
+ *
409
+ * A capture that has been through `stripBoxDrawing` — which blanks a pure-U+2500
410
+ * row, so condition 1 finds no seam and this returns `null`. Same limitation
411
+ * `extractOpenCodeModalOverlayFrame` records for the same reason. Issue #2522
412
+ * did not weaken the reading to work around it: the three consumers that clean
413
+ * their frames (the response poller, Auto-Yes, `/prompt-response`) now keep the
414
+ * same tick's RAW capture and hand that here, so both spellings of one capture
415
+ * exist and the right one is passed.
416
+ *
417
+ * ## Issue #2522: the structural half is shared
418
+ *
419
+ * Conditions 1, 2, 3-minus-strictness and 5 moved into
420
+ * {@link scanCommandCodeQuestionScreen}, which {@link hasCommandCodeQuestionChrome}
421
+ * also reads. This function is exactly that scan plus the two conditions that
422
+ * are about the LIST — a strict run and a single cursor — so every verdict it
423
+ * gave before is the verdict it gives now.
424
+ *
425
+ * @param frame - a raw `capture-pane -p -e` frame, ANSI intact (CRLF tolerated)
426
+ * @returns the region's line indices into the LF-normalised frame, or `null`
427
+ */
428
+ function readCommandCodeQuestionRegion(frame) {
429
+ const screen = scanCommandCodeQuestionScreen(frame);
430
+ if (screen === null)
431
+ return null;
432
+ // 3 (continued). A STRICT run. The numbers were collected in the order they
433
+ // are drawn and are compared against the run, rather than restarted on every
434
+ // fresh `1` the way `countTrailingOptionRun` does: that function is reading a
435
+ // tail that may hold two lists, and this one is asserting that the region
436
+ // holds exactly one.
437
+ if (!screen.numbers.every((value, index) => value === index + 1))
438
+ return null;
439
+ // 4 (continued). Exactly ONE cursor. Two means two screens — typically a
440
+ // composer's own `❯` under the dialog, or a frame caught mid-repaint.
441
+ if (screen.cursorCount !== 1)
442
+ return null;
443
+ return {
444
+ ruleLineIndex: screen.ruleLineIndex,
445
+ firstLineIndex: screen.firstLineIndex,
446
+ lastLineIndex: screen.lastLineIndex,
447
+ tabLineIndex: screen.tabLineIndex,
448
+ cursorLineIndex: screen.cursorLineIndex,
449
+ optionCount: screen.numbers.length,
450
+ };
451
+ }
452
+ /**
453
+ * Is Command Code's question CHROME on this frame, whatever its numbering says?
454
+ * (Issue #2522)
455
+ *
456
+ * The same five conditions {@link readCommandCodeQuestionRegion} applies, minus
457
+ * the two that are about the LIST rather than about the screen: the run need not
458
+ * be a strict `1.`…`N.`, and there may be more than one cursor on it.
459
+ *
460
+ * ## Why the weaker reading is worth having
461
+ *
462
+ * Issue #2522 確定仕様 B needs three answers where #2521 had two. "Not this
463
+ * screen" keeps every existing verdict; "read in full" produces an answerable
464
+ * prompt; and **"this screen, unreadable"** — a gap in the numbering, a repeated
465
+ * row, a list that starts at `2`, a second cursor from a half-finished repaint —
466
+ * has to reach the manual-operation fallback rather than the `ready` the generic
467
+ * composer check would otherwise publish off the dialog's own `❯` row. That is
468
+ * the very failure both Issues exist for: `wait` exiting 0 on a pane that is
469
+ * asking a human a question.
470
+ *
471
+ * It is deliberately NOT a relaxation of the strict reading. Nothing that was
472
+ * `null` becomes a REGION, so no partial option list is produced anywhere and no
473
+ * existing crop, card or prompt payload changes. What this adds is the ability
474
+ * to say "declined for a numbering reason" out loud.
475
+ *
476
+ * ## Why it is still narrow
477
+ *
478
+ * Every structural condition stays: the last qualifying rule row, a genuine tab
479
+ * strip as the first row under it ({@link isCommandCodeQuestionTabRow} — one
480
+ * filled marker, one hollow, `|`-separated), a non-blank question body before
481
+ * the first option, at least two numbered rows, at least one `❯` ON one of them,
482
+ * and none of the pickers, panels or other-turn UI the other branches own. An
483
+ * assistant answering in a numbered list carries none of that, and a frame with
484
+ * one option or with the `❯` on a composer row is still `false` — the two cases
485
+ * #2521 recorded as "the highlight is elsewhere".
486
+ *
487
+ * @param frame - a raw `capture-pane -p -e` frame, ANSI intact (CRLF tolerated)
488
+ */
489
+ function hasCommandCodeQuestionChrome(frame) {
490
+ return scanCommandCodeQuestionScreen(frame) !== null;
491
+ }
492
+ /**
493
+ * The structural half of the question-screen reading, shared by the strict
494
+ * {@link readCommandCodeQuestionRegion} and the weaker
495
+ * {@link hasCommandCodeQuestionChrome} (Issue #2522 split them apart).
496
+ *
497
+ * Written once rather than twice on purpose: the conditions below are what make
498
+ * the reading safe for every other CLI's frames, and a second copy of them is a
499
+ * second thing to forget to narrow.
500
+ */
501
+ function scanCommandCodeQuestionScreen(frame) {
502
+ if (!frame)
503
+ return null;
504
+ // Stripped per line off the same split, so every index below is valid against
505
+ // the ANSI-bearing rows the cropper slices.
506
+ const lines = frame.replace(/\r\n/g, '\n').split('\n').map(ansi_1.stripAnsi);
507
+ let last = lines.length - 1;
508
+ while (last >= 0 && lines[last].trim() === '')
509
+ last -= 1;
510
+ // A region needs a rule above it, so the last content row cannot be row 0.
511
+ if (last < 1)
512
+ return null;
513
+ let ruleLineIndex = -1;
514
+ for (let i = last - 1; i >= 0; i -= 1) {
515
+ const row = lines[i].trim();
516
+ if (row.length < COMMAND_CODE_RULE_MIN_COLUMNS)
517
+ continue;
518
+ if (!COMMAND_CODE_RULE_ROW_PATTERN.test(row))
519
+ continue;
520
+ ruleLineIndex = i;
521
+ break;
522
+ }
523
+ if (ruleLineIndex < 0)
524
+ return null;
525
+ const region = lines.slice(ruleLineIndex + 1, last + 1);
526
+ const body = region.join('\n');
527
+ // 5. The screens this reading must not take over.
528
+ if (FILTER_INPUT_PATTERN.test(body))
529
+ return null;
530
+ if (exports.COMMAND_CODE_SELECTION_LIST_FOOTER.test(body))
531
+ return null;
532
+ if (exports.DISMISSABLE_PANEL_FOOTER_PATTERN.test(body))
533
+ return null;
534
+ if (COMMAND_CODE_QUESTION_REGION_REJECT_PATTERN.test(body))
535
+ return null;
536
+ // 2. The tab strip opens the region.
537
+ const tabOffset = region.findIndex((line) => line.trim() !== '');
538
+ if (tabOffset < 0)
539
+ return null;
540
+ if (!isCommandCodeQuestionTabRow(region[tabOffset]))
541
+ return null;
542
+ // 3. Numbered rows under it.
543
+ const numbers = [];
544
+ let firstOptionOffset = -1;
545
+ for (let i = tabOffset + 1; i < region.length; i += 1) {
546
+ const match = OPTION_ROW_PATTERN.exec(region[i]);
547
+ if (match === null)
548
+ continue;
549
+ if (firstOptionOffset < 0)
550
+ firstOptionOffset = i;
551
+ numbers.push(Number(match[1]));
552
+ }
553
+ // Two is the floor: one numbered row under a tab strip is not a choice. The
554
+ // ceiling is {@link MAX_OPTION_NUMBER} and it is {@link OPTION_ROW_PATTERN}'s
555
+ // rather than a length check — that pattern captures a SINGLE digit, so a
556
+ // `10.` row is not read as an option at all and an eleven-option screen
557
+ // reports its first nine. Recognising such a screen and under-counting it is
558
+ // the right failure: declining it would put the pane back on the `ready` that
559
+ // made `wait` exit 0. (Issue #2522's reader cross-checks the count against its
560
+ // own block reading and declines to ANSWER such a screen; what it must not do
561
+ // is hand the pane back to the composer check.)
562
+ if (numbers.length < 2)
563
+ return null;
564
+ // 2 (continued). The question itself. A tab strip sitting straight on top of
565
+ // its options is some other screen.
566
+ if (!region.slice(tabOffset + 1, firstOptionOffset).some((line) => line.trim() !== '')) {
567
+ return null;
568
+ }
569
+ // 4. A cursor, on an option row.
570
+ let cursorCount = 0;
571
+ let cursorOffset = -1;
572
+ for (let i = 0; i < region.length; i += 1) {
573
+ const inRow = region[i].split(COMMAND_CODE_CURSOR_GLYPH).length - 1;
574
+ if (inRow === 0)
575
+ continue;
576
+ cursorCount += inRow;
577
+ if (OPTION_ROW_PATTERN.test(region[i]))
578
+ cursorOffset = i;
579
+ }
580
+ if (cursorCount === 0 || cursorOffset < 0)
581
+ return null;
582
+ return {
583
+ ruleLineIndex,
584
+ firstLineIndex: ruleLineIndex + 1,
585
+ lastLineIndex: last,
586
+ tabLineIndex: ruleLineIndex + 1 + tabOffset,
587
+ numbers,
588
+ cursorCount,
589
+ cursorLineIndex: ruleLineIndex + 1 + cursorOffset,
590
+ };
591
+ }
252
592
  /**
253
593
  * The rows of a Command Code dialog, cut out of the pane it is painted on
254
594
  * (Issue #2326).
@@ -290,7 +630,11 @@ const COMMAND_CODE_RULE_MIN_COLUMNS = 40;
290
630
  * - **no footer** — the picker was closed between the flags being read and the
291
631
  * pane being captured, or the frame is some other dialog. Cropping on a
292
632
  * guess would blank the card, which Issue #2326 calls out as worse than
293
- * showing too much;
633
+ * showing too much. Issue #2521 carved ONE screen out of this: a frame that
634
+ * {@link readCommandCodeQuestionRegion} positively recognises is cropped to
635
+ * that region, because `AskUserQuestion` draws no footer at all and its card
636
+ * was otherwise the whole pane. The carve-out is a positive reading, not a
637
+ * relaxation — everything it declines still lands here;
294
638
  * - **a footer but no rule above it** — copilot's `/model` footer is
295
639
  * `↑/↓ to navigate · … · enter to select · esc to cancel` and matches the
296
640
  * same pattern, but copilot draws its picker in a corner-bordered box, so no
@@ -314,8 +658,22 @@ function extractCommandCodeSelectionListFrame(frame) {
314
658
  break;
315
659
  }
316
660
  }
317
- if (footer < 0)
318
- return null;
661
+ // Issue #2521: no footer is no longer unconditionally "do not crop". One
662
+ // screen draws none — `AskUserQuestion` — and it is the screen whose
663
+ // uncropped card was 409 rows of transcript with the question below the fold.
664
+ // The reading is the narrow one, so every other footer-less frame (a closed
665
+ // picker, a permission dialog, another CLI's overlay) still takes the `null`
666
+ // path and is returned untouched. Checked only when the footer is absent, so
667
+ // a picker's own crop is decided exactly as #2326 left it.
668
+ if (footer < 0) {
669
+ const region = readCommandCodeQuestionRegion(frame);
670
+ if (region === null)
671
+ return null;
672
+ // The rule row itself is the transcript's boundary and not the dialog's
673
+ // first row — the same edge the footer path takes — and `lastLineIndex` is
674
+ // the last row with content, so the pane's trailing padding is left out.
675
+ return lines.slice(region.firstLineIndex, region.lastLineIndex + 1).join('\n');
676
+ }
319
677
  for (let i = footer - 1; i >= 0; i -= 1) {
320
678
  const row = (0, ansi_1.stripAnsi)(lines[i]).trim();
321
679
  if (row.length < COMMAND_CODE_RULE_MIN_COLUMNS)