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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +176 -163
- package/.next/app-path-routes-manifest.json +27 -27
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +1 -1
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page.js +2 -2
- package/.next/server/app/chat/page.js.nft.json +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page.js +2 -2
- package/.next/server/app/more/page.js.nft.json +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +2 -2
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +2 -2
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +1 -1
- package/.next/server/app/skills/page.js.nft.json +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +11 -11
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +27 -27
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/{6286.js → 1467.js} +6 -6
- package/.next/server/chunks/1886.js +1 -1
- package/.next/server/chunks/2980.js +1 -1
- package/.next/server/chunks/3077.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3323.js +1 -1
- package/.next/server/chunks/434.js +1 -1
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/450.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/650.js +1 -1
- package/.next/server/chunks/6677.js +3 -3
- package/.next/server/chunks/7075.js +1 -1
- package/.next/server/chunks/7506.js +1 -1
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8728.js +8 -0
- package/.next/server/chunks/8918.js +5 -5
- package/.next/server/chunks/9123.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/2407-b9001cc6041876e1.js +1 -0
- package/.next/static/chunks/{4322-dfd5a7119b5ba4fc.js → 4322-59a7dbd77a7763cc.js} +1 -1
- package/.next/static/chunks/4388-410b1d8ed0d8d98b.js +1 -0
- package/.next/static/chunks/4718-56ebb6e55073ed5e.js +1 -0
- package/.next/static/chunks/{2652-18b8fea64413467e.js → 4809-145884b958e7d9a6.js} +1 -1
- package/.next/static/chunks/app/chat/{page-e60d07c4c79602f0.js → page-609c9a0f5ce44799.js} +1 -1
- package/.next/static/chunks/app/error-c1dc1038d0a84a33.js +1 -0
- package/.next/static/chunks/app/global-error-49f765ecb7fd6527.js +1 -0
- package/.next/static/chunks/app/{layout-4c2f21e3f653ee4c.js → layout-114dc6e93e332392.js} +1 -1
- package/.next/static/chunks/app/more/{page-fc94f86aa881489e.js → page-7c5380e4a7561935.js} +1 -1
- package/.next/static/chunks/app/{page-26a42881c8648cd3.js → page-a4f03c188610c6c8.js} +1 -1
- package/.next/static/chunks/app/repositories/{page-2576158c8420fda0.js → page-a15efb4ce1d1721e.js} +1 -1
- package/.next/static/chunks/app/review/{page-35b99baaf3c93b38.js → page-90ac618489676cbc.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-a3ab6f122ec60d5a.js → page-6486d9a643c1dc6f.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/{page-06799157652e8fa9.js → page-844872df04f52943.js} +1 -1
- package/.next/static/chunks/app/skills/installed/{page-2f3987e3a131c8d0.js → page-566dfde26d7e63ba.js} +1 -1
- package/.next/static/chunks/app/skills/{page-e70bfcf5ed4e4865.js → page-7859540e8092aace.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3a58ae66e203f935.js +1 -0
- package/.next/trace +14 -13
- package/dist/lib/detection/tools/verified-against.d.ts +20 -0
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.js +20 -0
- package/dist/server/src/config/websocket-config.js +67 -0
- package/dist/server/src/lib/auto-yes-poller.js +40 -7
- package/dist/server/src/lib/detection/selection-shape.js +361 -3
- package/dist/server/src/lib/detection/tools/command-code/detect.js +96 -0
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +314 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +20 -0
- package/dist/server/src/lib/polling/response-checker.js +48 -2
- package/dist/server/src/lib/ws-server.js +111 -1
- package/package.json +1 -1
- package/public/sw.js +12 -0
- package/.next/server/chunks/595.js +0 -8
- package/.next/static/chunks/4718-29cfcfb806724d9a.js +0 -1
- package/.next/static/chunks/8361-0142beb59be2abf3.js +0 -1
- package/.next/static/chunks/app/error-27ab04bce6545dae.js +0 -1
- package/.next/static/chunks/app/global-error-7b761b8e181ffc1f.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-60eab4e24c37cb77.js +0 -1
- /package/.next/static/{oDWX0JcwfhUYrMnnqDWVC → 0vChXhzA5YWj3f0D6A-Cn}/_buildManifest.js +0 -0
- /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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
318
|
-
|
|
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)
|