commandmate 0.44.1 → 0.44.3
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 +169 -169
- package/.next/app-path-routes-manifest.json +35 -35
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +0 -6
- 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.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.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route.js +2 -2
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route.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.js +1 -1
- package/.next/server/app/api/daily-summary/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/daily-summary/status/route.js.nft.json +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.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.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 +2 -2
- 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 +2 -2
- 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 +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.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.js +2 -2
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route.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.js +1 -1
- package/.next/server/app/api/repositories/[id]/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 +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.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.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.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.js +2 -2
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route.js +2 -2
- package/.next/server/app/api/templates/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 +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.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.js +3 -3
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js.nft.json +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]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/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.js +2 -2
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js.nft.json +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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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 +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 +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.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.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.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.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.js +1 -2
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js.nft.json +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.js +1 -2
- package/.next/server/app/api/worktrees/[id]/opencode/share/route.js.nft.json +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.js +2 -2
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js.nft.json +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.js +2 -2
- package/.next/server/app/api/worktrees/[id]/schedules/active/route.js.nft.json +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.js +3 -3
- package/.next/server/app/api/worktrees/[id]/schedules/route.js.nft.json +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 +3 -3
- 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.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.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.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.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 +2 -2
- 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.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.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.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.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.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/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_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +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 +35 -35
- package/.next/server/chunks/1191.js +2 -2
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/1559.js +2 -0
- package/.next/server/chunks/1789.js +1 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2460.js +1 -0
- package/.next/server/chunks/2710.js +1 -1
- package/.next/server/chunks/2908.js +1 -0
- package/.next/server/chunks/2980.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3480.js +1 -1
- package/.next/server/chunks/3718.js +1 -1
- package/.next/server/chunks/4207.js +2 -2
- package/.next/server/chunks/450.js +1 -1
- package/.next/server/chunks/4672.js +5 -0
- package/.next/server/chunks/{9353.js → 4718.js} +2 -2
- package/.next/server/chunks/4837.js +14 -0
- package/.next/server/chunks/5048.js +1 -1
- package/.next/server/chunks/5209.js +1 -0
- package/.next/server/chunks/5595.js +1 -0
- package/.next/server/chunks/5625.js +25 -0
- package/.next/server/chunks/5845.js +1 -0
- package/.next/server/chunks/611.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/6793.js +3 -3
- package/.next/server/chunks/7491.js +2 -2
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/850.js +1 -0
- package/.next/server/chunks/8543.js +1 -1
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8800.js +1 -1
- package/.next/server/chunks/8918.js +7 -7
- package/.next/server/chunks/9130.js +1 -5
- package/.next/server/chunks/9390.js +2 -3
- package/.next/server/chunks/9584.js +1 -1
- package/.next/server/chunks/9627.js +6 -6
- package/.next/server/chunks/9731.js +3 -3
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{1642-fb2e176aaadbd137.js → 1642-767aed8e7c61ef7f.js} +1 -1
- package/.next/static/chunks/{2407-f1f258d4a1d92620.js → 2407-06d35bb1aa02de80.js} +1 -1
- package/.next/static/chunks/{2619-04bc32f026a0d946.js → 2619-a305ad4810f1393a.js} +1 -1
- package/.next/static/chunks/4727-5d695d71e3fdf97b.js +1 -0
- package/.next/static/chunks/{8165-3853772f6ae1299b.js → 8165-3525fb42f1b4976e.js} +1 -1
- package/.next/static/chunks/app/{layout-da66300cc20244ed.js → layout-0d1750b833f5cc3e.js} +1 -1
- package/.next/static/chunks/app/sessions/page-2af68c63211c751f.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-e36898c3a80e7302.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-373ea37b16d930c7.js → page-c601f3b3ece6557e.js} +1 -1
- package/.next/static/css/057f64a65726c439.css +1 -0
- package/.next/trace +13 -13
- package/dist/cli/commands/ask.d.ts.map +1 -1
- package/dist/cli/commands/ask.js +120 -119
- package/dist/cli/commands/attach.d.ts +94 -0
- package/dist/cli/commands/attach.d.ts.map +1 -1
- package/dist/cli/commands/attach.js +145 -7
- package/dist/cli/commands/auto-yes.js +1 -1
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +10 -13
- package/dist/cli/commands/command-target.d.ts +23 -0
- package/dist/cli/commands/command-target.d.ts.map +1 -0
- package/dist/cli/commands/command-target.js +25 -0
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +41 -2
- package/dist/cli/commands/message-dispatch.d.ts +36 -0
- package/dist/cli/commands/message-dispatch.d.ts.map +1 -0
- package/dist/cli/commands/message-dispatch.js +63 -0
- package/dist/cli/commands/reply.d.ts.map +1 -1
- package/dist/cli/commands/reply.js +2 -5
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +3 -6
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +149 -146
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +608 -416
- package/dist/cli/types/api-responses.d.ts +174 -27
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/api-responses.js +4 -2
- package/dist/cli/utils/api-client.d.ts +25 -5
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +87 -55
- package/dist/cli/utils/server-url.d.ts +1 -0
- package/dist/cli/utils/server-url.d.ts.map +1 -1
- package/dist/cli/utils/server-url.js +7 -0
- package/dist/config/uat-isolation.d.ts +140 -0
- package/dist/config/uat-isolation.d.ts.map +1 -0
- package/dist/config/uat-isolation.js +246 -0
- package/dist/lib/global-state.d.ts +12 -0
- package/dist/lib/global-state.d.ts.map +1 -0
- package/dist/lib/global-state.js +16 -0
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +33 -0
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -0
- package/dist/lib/polling/auto-yes-suppression-reason.js +2 -0
- package/dist/lib/session/prompt-view.d.ts +228 -0
- package/dist/lib/session/prompt-view.d.ts.map +1 -0
- package/dist/lib/session/prompt-view.js +290 -0
- package/dist/lib/session/status-evidence-type.d.ts +16 -0
- package/dist/lib/session/status-evidence-type.d.ts.map +1 -0
- package/dist/lib/session/status-evidence-type.js +2 -0
- package/dist/lib/tmux/legacy-session-alias.d.ts.map +1 -1
- package/dist/lib/tmux/legacy-session-alias.js +2 -1
- package/dist/server/locales/en/worktree.json +3 -1
- package/dist/server/locales/ja/worktree.json +3 -1
- package/dist/server/server.js +5 -3
- package/dist/server/src/cli/types/api-responses.js +4 -2
- package/dist/server/src/config/cli-tool-timing-config.js +1 -6
- package/dist/server/src/config/uat-isolation.js +246 -0
- package/dist/server/src/lib/assistant-response-saver.js +62 -3
- package/dist/server/src/lib/auto-yes-lifecycle.js +139 -0
- package/dist/server/src/lib/auto-yes-poller.js +465 -160
- package/dist/server/src/lib/auto-yes-state.js +2 -2
- package/dist/server/src/lib/cli-tools/antigravity.js +39 -80
- package/dist/server/src/lib/cli-tools/base.js +151 -0
- package/dist/server/src/lib/cli-tools/codex.js +44 -80
- package/dist/server/src/lib/cli-tools/command-code.js +24 -56
- package/dist/server/src/lib/cli-tools/copilot.js +25 -67
- package/dist/server/src/lib/cli-tools/direct-input.js +8 -0
- package/dist/server/src/lib/cli-tools/gemini.js +23 -73
- package/dist/server/src/lib/cli-tools/opencode-pane-width.js +53 -0
- package/dist/server/src/lib/cli-tools/opencode-v2.js +9 -7
- package/dist/server/src/lib/cli-tools/opencode.js +21 -85
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +127 -4
- package/dist/server/src/lib/cli-tools/vibe-local.js +21 -66
- package/dist/server/src/lib/detection/cli-patterns.js +374 -3047
- package/dist/server/src/lib/detection/idle-evidence-observation.js +2 -2
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +7 -32
- package/dist/server/src/lib/detection/prompt-detector.js +2 -35
- package/dist/server/src/lib/detection/selection-shape-patterns.js +69 -0
- package/dist/server/src/lib/detection/selection-shape.js +32 -520
- package/dist/server/src/lib/detection/shared/pasted-text.js +41 -0
- package/dist/server/src/lib/detection/shared/shell-pane-command.js +16 -0
- package/dist/server/src/lib/detection/shared/strip-box-drawing.js +25 -0
- package/dist/server/src/lib/detection/status-detector.js +1 -1
- package/dist/server/src/lib/detection/tools/antigravity/detect.js +12 -60
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +2 -15
- package/dist/server/src/lib/detection/tools/antigravity/live-region.js +39 -0
- package/dist/server/src/lib/detection/tools/antigravity/patterns.js +286 -0
- package/dist/server/src/lib/detection/tools/claude/detect.js +11 -17
- package/dist/server/src/lib/detection/tools/claude/live-region.js +26 -0
- package/dist/server/src/lib/detection/tools/claude/patterns.js +264 -4
- package/dist/server/src/lib/detection/tools/claude/prompt.js +2 -10
- package/dist/server/src/lib/detection/tools/codex/cli-patterns.js +1 -1
- package/dist/server/src/lib/detection/tools/codex/detect.js +71 -130
- package/dist/server/src/lib/detection/tools/codex/live-region.js +104 -0
- package/dist/server/src/lib/detection/tools/codex/patterns.js +855 -0
- package/dist/server/src/lib/detection/tools/codex/prompt.js +2 -10
- package/dist/server/src/lib/detection/tools/command-code/detect.js +19 -50
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +2 -18
- package/dist/server/src/lib/detection/tools/command-code/live-region.js +36 -0
- package/dist/server/src/lib/detection/tools/command-code/patterns.js +234 -0
- package/dist/server/src/lib/detection/tools/command-code/selection-shape.js +461 -0
- package/dist/server/src/lib/detection/tools/context-above.js +20 -0
- package/dist/server/src/lib/detection/tools/copilot/detect.js +13 -35
- package/dist/server/src/lib/detection/tools/copilot/live-region.js +52 -0
- package/dist/server/src/lib/detection/tools/copilot/patterns.js +640 -0
- package/dist/server/src/lib/detection/tools/copilot/prompt.js +2 -10
- package/dist/server/src/lib/detection/tools/frame.js +36 -2
- package/dist/server/src/lib/detection/tools/gemini/patterns.js +29 -0
- package/dist/server/src/lib/detection/tools/live-region-specs.js +31 -0
- package/dist/server/src/lib/detection/tools/live-region.js +204 -0
- package/dist/server/src/lib/detection/tools/opencode/detect.js +11 -49
- package/dist/server/src/lib/detection/tools/opencode/live-region.js +24 -0
- package/dist/server/src/lib/detection/tools/opencode/patterns.js +508 -0
- package/dist/server/src/lib/detection/tools/opencode-gutter-live-region.js +72 -0
- package/dist/server/src/lib/detection/tools/opencode-v2/detect.js +10 -42
- package/dist/server/src/lib/detection/tools/opencode-v2/live-region.js +24 -0
- package/dist/server/src/lib/detection/tools/opencode-v2/patterns.js +63 -0
- package/dist/server/src/lib/detection/tools/run-detection.js +31 -50
- package/dist/server/src/lib/detection/tools/verdicts.js +59 -0
- package/dist/server/src/lib/detection/tools/vibe-local/patterns.js +20 -0
- package/dist/server/src/lib/detection/truncate-raw-content.js +38 -0
- package/dist/server/src/lib/detection/turn-abandoned.js +66 -0
- package/dist/server/src/lib/detection/unclassified-frame-tracker.js +2 -2
- package/dist/server/src/lib/global-state.js +16 -0
- package/dist/server/src/lib/hooks/agent-session-telemetry.js +4 -6
- package/dist/server/src/lib/hooks/permission-decision-state.js +2 -2
- package/dist/server/src/lib/hooks/sources/antigravity/history.js +11 -76
- package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +63 -1
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +16 -0
- package/dist/server/src/lib/hooks/sources/antigravity/transcript.js +3 -12
- package/dist/server/src/lib/hooks/sources/claude/history.js +17 -156
- package/dist/server/src/lib/hooks/sources/claude/queued-notice.js +53 -0
- package/dist/server/src/lib/hooks/sources/claude/source.js +33 -3
- package/dist/server/src/lib/hooks/sources/claude/transcript.js +3 -13
- package/dist/server/src/lib/hooks/sources/codex/history.js +11 -74
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +147 -11
- package/dist/server/src/lib/hooks/sources/codex/relay-install.js +64 -21
- package/dist/server/src/lib/hooks/sources/codex/transcript.js +5 -15
- package/dist/server/src/lib/hooks/sources/command-code/history.js +20 -121
- package/dist/server/src/lib/hooks/sources/command-code/transcript.js +3 -13
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +161 -31
- package/dist/server/src/lib/hooks/sources/define-source.js +40 -55
- package/dist/server/src/lib/hooks/sources/event-mapper.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/diff.js +3 -4
- package/dist/server/src/lib/hooks/sources/opencode/history.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/launch-settings.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/payloads.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/ports.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/subscription.js +4 -3
- package/dist/server/src/lib/hooks/sources/opencode/transcript.js +3 -16
- package/dist/server/src/lib/hooks/sources/opencode-v2/history.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode-v2/mappers.js +5 -4
- package/dist/server/src/lib/hooks/sources/opencode-v2/ports.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode-v2/subscription.js +3 -7
- package/dist/server/src/lib/hooks/sources/opencode-v2/usage.js +2 -1
- package/dist/server/src/lib/hooks/sources/pending-decisions.js +2 -1
- package/dist/server/src/lib/hooks/sources/registry.js +3 -2
- package/dist/server/src/lib/hooks/sources/transcript-history.js +262 -0
- package/dist/server/src/lib/hooks/sources/turn-body.js +31 -0
- package/dist/server/src/lib/hooks/tool-input-normalization-state.js +2 -2
- package/dist/server/src/lib/polling/antigravity-permission-receipts.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +16 -4
- package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +142 -0
- package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +187 -0
- package/dist/server/src/lib/polling/auto-yes-resolver.js +9 -3
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +2 -0
- package/dist/server/src/lib/polling/auto-yes-suppression-state.js +2 -2
- package/dist/server/src/lib/polling/pending-scraped-response.js +190 -0
- package/dist/server/src/lib/polling/prompt-dedup-state.js +2 -2
- package/dist/server/src/lib/polling/prompt-dedup.js +2 -2
- package/dist/server/src/lib/polling/response-checker-dialog-gate.js +95 -0
- package/dist/server/src/lib/polling/response-checker-extraction-result.js +50 -0
- package/dist/server/src/lib/polling/response-checker-extraction-steps.js +670 -0
- package/dist/server/src/lib/polling/response-checker.js +666 -1162
- package/dist/server/src/lib/polling/response-dedup.js +3 -4
- package/dist/server/src/lib/polling/response-poller-constants.js +17 -0
- package/dist/server/src/lib/polling/response-poller-core.js +54 -20
- package/dist/server/src/lib/polling/startup-screen.js +142 -0
- package/dist/server/src/lib/polling/structured-history-gate.js +3 -2
- package/dist/server/src/lib/prompt-answer-semantic.js +178 -0
- package/dist/server/src/lib/prompt-answer-sender.js +6 -5
- package/dist/server/src/lib/push/failure-episode-state.js +2 -2
- package/dist/server/src/lib/push/notification-dedup.js +3 -4
- package/dist/server/src/lib/push/prompt-card-state.js +2 -2
- package/dist/server/src/lib/push/waiting-push-notifier.js +3 -4
- package/dist/server/src/lib/realtime/publisher-registry.js +3 -2
- package/dist/server/src/lib/realtime/terminal-broadcast.js +38 -2
- package/dist/server/src/lib/realtime/terminal-session-ownership.js +45 -0
- package/dist/server/src/lib/relay/relay-delivery.js +6 -2
- package/dist/server/src/lib/relay/relay-readiness.js +38 -1
- package/dist/server/src/lib/relay/relay-triggers.js +9 -0
- package/dist/server/src/lib/resource-cleanup.js +6 -4
- package/dist/server/src/lib/response-cleaner.js +24 -1
- package/dist/server/src/lib/response-extractor.js +16 -0
- package/dist/server/src/lib/schedule-manager.js +8 -10
- package/dist/server/src/lib/session/agent-event-ask-user-question.js +168 -0
- package/dist/server/src/lib/session/agent-event-awaiting-instruction.js +104 -0
- package/dist/server/src/lib/session/agent-event-dedup.js +539 -0
- package/dist/server/src/lib/session/agent-event-model.js +562 -0
- package/dist/server/src/lib/session/agent-event-record.js +10 -0
- package/dist/server/src/lib/session/agent-event-state.js +106 -1854
- package/dist/server/src/lib/session/agent-event-structured-state.js +109 -0
- package/dist/server/src/lib/session/agent-event-turn.js +811 -0
- package/dist/server/src/lib/session/agent-session-lifecycle.js +11 -2
- package/dist/server/src/lib/session/chat-turn-progress.js +322 -0
- package/dist/server/src/lib/session/claude-executor.js +45 -5
- package/dist/server/src/lib/session/claude-session.js +21 -65
- package/dist/server/src/lib/session/cli-session.js +17 -0
- package/dist/server/src/lib/session/current-output-builder.js +454 -1103
- package/dist/server/src/lib/session/current-output-history-writers.js +255 -0
- package/dist/server/src/lib/session/current-output-types.js +9 -0
- package/dist/server/src/lib/session/detection-divergence.js +55 -0
- package/dist/server/src/lib/session/global-session-constants.js +2 -13
- package/dist/server/src/lib/session/hook-turn-hold.js +102 -0
- package/dist/server/src/lib/session/layer-disagreement.js +2 -2
- package/dist/server/src/lib/session/opencode-session-store.js +2 -1
- package/dist/server/src/lib/session/prompt-view.js +290 -0
- package/dist/server/src/lib/session/prompt-waiting-composition.js +35 -0
- package/dist/server/src/lib/session/provisional-turn.js +3 -1
- package/dist/server/src/lib/session/send-user-message.js +1 -1
- package/dist/server/src/lib/session/session-starting-state.js +4 -6
- package/dist/server/src/lib/session/status-evidence-type.js +2 -0
- package/dist/server/src/lib/session/status-evidence.js +2 -2
- package/dist/server/src/lib/session/structured-status-merge.js +335 -0
- package/dist/server/src/lib/session/waiting-episode-state.js +3 -4
- package/dist/server/src/lib/session/waiting-kind.js +3 -0
- package/dist/server/src/lib/session-cleanup.js +6 -8
- package/dist/server/src/lib/skills/install-plan.js +2 -1
- package/dist/server/src/lib/skills/snapshot-store.js +2 -2
- package/dist/server/src/lib/skills/uninstall-plan.js +2 -2
- package/dist/server/src/lib/timer-manager.js +6 -8
- package/dist/server/src/lib/tmux/geometry-delegation.js +3 -4
- package/dist/server/src/lib/tmux/legacy-session-alias.js +2 -1
- package/dist/server/src/lib/tmux/session-hooks.js +2 -1
- package/dist/server/src/lib/tmux/session-status-options.js +3 -3
- package/dist/server/src/lib/tmux/tmux-capture-cache.js +3 -2
- package/dist/server/src/lib/tmux/tmux.js +33 -59
- package/dist/server/src/lib/tui-accumulator.js +2 -2
- package/dist/server/src/lib/version-checker.js +6 -6
- package/dist/server/src/lib/ws-server.js +47 -11
- package/docs/en/user-guide/webapp-guide.md +27 -3
- package/package.json +1 -1
- package/release-notes/0.44.2.json +33 -0
- package/release-notes/0.44.3.json +82 -0
- package/scripts/hooks/cmate-agent-event.sh +18 -0
- package/.next/server/chunks/1068.js +0 -1
- package/.next/server/chunks/434.js +0 -1
- package/.next/server/chunks/5775.js +0 -1
- package/.next/server/chunks/6474.js +0 -14
- package/.next/server/chunks/650.js +0 -1
- package/.next/server/chunks/6661.js +0 -1
- package/.next/server/chunks/8583.js +0 -1
- package/.next/server/chunks/964.js +0 -1
- package/.next/server/chunks/967.js +0 -1
- package/.next/server/chunks/9882.js +0 -25
- package/.next/static/chunks/591-308072f285c34787.js +0 -1
- package/.next/static/chunks/app/sessions/page-049e06335187aa91.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-040c5f0f585e6812.js +0 -1
- package/.next/static/css/38070d879efa1371.css +0 -1
- package/dist/server/src/lib/cli-tools/index.js +0 -41
- package/dist/server/src/lib/polling/global-session-poller.js +0 -140
- /package/.next/static/{57lOiz8zSEHdw97XIm0af → tWwHQzmvc-rv9KKUtDWfQ}/_buildManifest.js +0 -0
- /package/.next/static/{57lOiz8zSEHdw97XIm0af → tWwHQzmvc-rv9KKUtDWfQ}/_ssgManifest.js +0 -0
|
@@ -4,1638 +4,156 @@
|
|
|
4
4
|
* Shared between response-poller.ts and API routes
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
10
|
-
exports.findClaudeChromeStart = findClaudeChromeStart;
|
|
11
|
-
exports.resolveClaudeTrustDialogKeys = resolveClaudeTrustDialogKeys;
|
|
12
|
-
exports.isClaudeTrustDialogOpen = isClaudeTrustDialogOpen;
|
|
13
|
-
exports.isShellPaneCommand = isShellPaneCommand;
|
|
14
|
-
exports.isCodexPromptReady = isCodexPromptReady;
|
|
15
|
-
exports.getCodexActiveDialog = getCodexActiveDialog;
|
|
16
|
-
exports.getCodexLifecycleDialog = getCodexLifecycleDialog;
|
|
17
|
-
exports.findCodexChromeStart = findCodexChromeStart;
|
|
18
|
-
exports.findCodexUserEchoIndex = findCodexUserEchoIndex;
|
|
19
|
-
exports.findOpenCodeChromeStart = findOpenCodeChromeStart;
|
|
20
|
-
exports.findOpenCodeUserEchoEnd = findOpenCodeUserEchoEnd;
|
|
21
|
-
exports.findOpenCodeComposerRows = findOpenCodeComposerRows;
|
|
22
|
-
exports.stripOpenCodeGutter = stripOpenCodeGutter;
|
|
23
|
-
exports.isOpencodeV2ComposerVisible = isOpencodeV2ComposerVisible;
|
|
24
|
-
exports.readCopilotStatusBar = readCopilotStatusBar;
|
|
25
|
-
exports.findCopilotChromeStart = findCopilotChromeStart;
|
|
26
|
-
exports.isCopilotSelectionFrame = isCopilotSelectionFrame;
|
|
27
|
-
exports.isCopilotFolderTrustDialog = isCopilotFolderTrustDialog;
|
|
28
|
-
exports.locateAntigravityDialogRegion = locateAntigravityDialogRegion;
|
|
29
|
-
exports.isAntigravityNumberedDialog = isAntigravityNumberedDialog;
|
|
30
|
-
exports.findCommandCodeChromeStart = findCommandCodeChromeStart;
|
|
7
|
+
exports.CLAUDE_SPINNER_CHARS = exports.OPENCODE_V2_SKIP_PATTERNS = exports.isOpencodeV2ComposerVisible = exports.OPENCODE_V2_THINKING_PATTERN = exports.OPENCODE_V2_FOOTER_PATTERN = exports.OPENCODE_V2_IDLE_COMPOSER_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.stripOpenCodeGutter = exports.findOpenCodeComposerRows = exports.findOpenCodeUserEchoEnd = exports.findOpenCodeChromeStart = exports.OPENCODE_FOOTER_STATUS_PATTERN = exports.OPENCODE_USER_ECHO_PATTERN = exports.OPENCODE_GUTTER_ROW_PATTERN = exports.OPENCODE_COMPOSER_BOTTOM_BORDER = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_PERMISSION_PATTERN = exports.OPENCODE_TURN_COMPLETE_PATTERN = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_IDLE_COMPOSER_PATTERN = exports.OPENCODE_PROMPT_PATTERN = exports.findCodexUserEchoIndex = exports.CODEX_USER_ECHO_PATTERN = exports.findCodexChromeStart = exports.CODEX_TRAILED_STATUS_BAR_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_EFFORT_PICKER_FOOTER_PATTERN = exports.CODEX_PICKER_FOOTER_PATTERN = exports.CODEX_FORM_SUBMIT_FOOTER_PATTERN = exports.CODEX_APPROVAL_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.getCodexLifecycleDialog = exports.CODEX_HOOKS_REVIEW_ANCHORS = exports.getCodexActiveDialog = exports.findCodexComposerRow = exports.isCodexPromptReady = exports.CODEX_GENUINE_PROMPT_LINE = exports.CODEX_DIALOG_PATTERN = exports.CODEX_TRUST_QUESTION_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.isCodexTurnActive = exports.CODEX_INTERRUPT_HINT_PATTERN = exports.CODEX_THINKING_PATTERN = void 0;
|
|
8
|
+
exports.ANTIGRAVITY_SURVEY_PATTERN = exports.ANTIGRAVITY_DIALOG_MAX_ROWS = exports.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN = exports.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN = exports.ANTIGRAVITY_NUMBERED_OPTION_PATTERN = exports.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_PATTERN = exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE = exports.COPILOT_SKIP_PATTERNS = exports.isCopilotFolderTrustDialog = exports.COPILOT_FOLDER_TRUST_ANSWER_KEY = exports.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN = exports.COPILOT_FOLDER_TRUST_ANCHORS = exports.isCopilotSelectionFrame = exports.COPILOT_SELECTION_FOOTER_PATTERN = exports.COPILOT_BOOT_BANNER_ANCHORS = exports.COPILOT_TRANSCRIPT_CONTINUATION_PATTERN = exports.COPILOT_TOOL_ROW_PATTERN = exports.COPILOT_TOOL_VERBS = exports.COPILOT_TRANSCRIPT_DIVIDER_PATTERN = exports.COPILOT_USER_ECHO_PATTERN = exports.COPILOT_REASONING_HEADER_PATTERN = exports.COPILOT_BOX_ROW_PATTERN = exports.findCopilotChromeStart = exports.COPILOT_SEPARATOR_PATTERN = exports.readCopilotStatusBar = exports.COPILOT_IDLE_STATUS_PATTERN = exports.COPILOT_WORKING_STATUS_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.isClaudeTrustDialogOpen = exports.resolveClaudeTrustDialogKeys = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.findClaudeChromeStart = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = void 0;
|
|
9
|
+
exports.stripBoxDrawing = exports.extractAnsiSequences = exports.stripAnsi = exports.findOpencodeV2DialogTitle = exports.OPENCODE_V2_DIALOG_TITLE_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.isShellPaneCommand = exports.COMMAND_CODE_SKIP_PATTERNS = exports.findCommandCodeChromeStart = exports.COMMAND_CODE_HOOK_NOTICE_PATTERN = exports.COMMAND_CODE_BANNER_PATTERNS = exports.COMMAND_CODE_MODE_INDICATOR_PATTERN = exports.COMMAND_CODE_COMPLETION_PATTERN = exports.COMMAND_CODE_RESPONSE_MARKER_PATTERN = exports.COMMAND_CODE_THINKING_PATTERN = exports.COMMAND_CODE_INTERRUPT_HINT_PATTERN = exports.COMMAND_CODE_SEPARATOR_PATTERN = exports.COMMAND_CODE_PROMPT_PATTERN = exports.COMMAND_CODE_SPINNER_CHARS = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.isAntigravityNumberedDialog = exports.locateAntigravityDialogRegion = void 0;
|
|
31
10
|
exports.detectThinking = detectThinking;
|
|
32
11
|
exports.getCliToolPatterns = getCliToolPatterns;
|
|
33
|
-
exports.stripBoxDrawing = stripBoxDrawing;
|
|
34
12
|
exports.buildDetectPromptOptions = buildDetectPromptOptions;
|
|
13
|
+
exports.usesFullFramePrompt = usesFullFramePrompt;
|
|
35
14
|
const logger_1 = require("../../lib/logger");
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
15
|
+
const shell_pane_command_1 = require("./shared/shell-pane-command");
|
|
16
|
+
Object.defineProperty(exports, "isShellPaneCommand", { enumerable: true, get: function () { return shell_pane_command_1.isShellPaneCommand; } });
|
|
17
|
+
const pasted_text_1 = require("./shared/pasted-text");
|
|
18
|
+
Object.defineProperty(exports, "PASTED_TEXT_PATTERN", { enumerable: true, get: function () { return pasted_text_1.PASTED_TEXT_PATTERN; } });
|
|
19
|
+
Object.defineProperty(exports, "PASTED_TEXT_DETECT_DELAY", { enumerable: true, get: function () { return pasted_text_1.PASTED_TEXT_DETECT_DELAY; } });
|
|
20
|
+
Object.defineProperty(exports, "MAX_PASTED_TEXT_RETRIES", { enumerable: true, get: function () { return pasted_text_1.MAX_PASTED_TEXT_RETRIES; } });
|
|
21
|
+
const strip_box_drawing_1 = require("./shared/strip-box-drawing");
|
|
22
|
+
Object.defineProperty(exports, "stripBoxDrawing", { enumerable: true, get: function () { return strip_box_drawing_1.stripBoxDrawing; } });
|
|
23
|
+
const patterns_1 = require("./tools/codex/patterns");
|
|
24
|
+
var patterns_2 = require("./tools/codex/patterns");
|
|
25
|
+
Object.defineProperty(exports, "CODEX_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_THINKING_PATTERN; } });
|
|
26
|
+
Object.defineProperty(exports, "CODEX_INTERRUPT_HINT_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_INTERRUPT_HINT_PATTERN; } });
|
|
27
|
+
Object.defineProperty(exports, "isCodexTurnActive", { enumerable: true, get: function () { return patterns_2.isCodexTurnActive; } });
|
|
28
|
+
Object.defineProperty(exports, "CODEX_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_PROMPT_PATTERN; } });
|
|
29
|
+
Object.defineProperty(exports, "CODEX_TRUST_QUESTION_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_TRUST_QUESTION_PATTERN; } });
|
|
30
|
+
Object.defineProperty(exports, "CODEX_DIALOG_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_DIALOG_PATTERN; } });
|
|
31
|
+
Object.defineProperty(exports, "CODEX_GENUINE_PROMPT_LINE", { enumerable: true, get: function () { return patterns_2.CODEX_GENUINE_PROMPT_LINE; } });
|
|
32
|
+
Object.defineProperty(exports, "isCodexPromptReady", { enumerable: true, get: function () { return patterns_2.isCodexPromptReady; } });
|
|
33
|
+
Object.defineProperty(exports, "findCodexComposerRow", { enumerable: true, get: function () { return patterns_2.findCodexComposerRow; } });
|
|
34
|
+
Object.defineProperty(exports, "getCodexActiveDialog", { enumerable: true, get: function () { return patterns_2.getCodexActiveDialog; } });
|
|
35
|
+
Object.defineProperty(exports, "CODEX_HOOKS_REVIEW_ANCHORS", { enumerable: true, get: function () { return patterns_2.CODEX_HOOKS_REVIEW_ANCHORS; } });
|
|
36
|
+
Object.defineProperty(exports, "getCodexLifecycleDialog", { enumerable: true, get: function () { return patterns_2.getCodexLifecycleDialog; } });
|
|
37
|
+
Object.defineProperty(exports, "CODEX_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_SEPARATOR_PATTERN; } });
|
|
38
|
+
Object.defineProperty(exports, "CODEX_SELECTION_LIST_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_SELECTION_LIST_PATTERN; } });
|
|
39
|
+
Object.defineProperty(exports, "CODEX_APPROVAL_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_APPROVAL_FOOTER_PATTERN; } });
|
|
40
|
+
Object.defineProperty(exports, "CODEX_FORM_SUBMIT_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_FORM_SUBMIT_FOOTER_PATTERN; } });
|
|
41
|
+
Object.defineProperty(exports, "CODEX_PICKER_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_PICKER_FOOTER_PATTERN; } });
|
|
42
|
+
Object.defineProperty(exports, "CODEX_EFFORT_PICKER_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_EFFORT_PICKER_FOOTER_PATTERN; } });
|
|
43
|
+
Object.defineProperty(exports, "CODEX_PAGER_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_PAGER_FOOTER_PATTERN; } });
|
|
44
|
+
Object.defineProperty(exports, "CODEX_STATUS_BAR_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_STATUS_BAR_PATTERN; } });
|
|
45
|
+
Object.defineProperty(exports, "CODEX_TRAILED_STATUS_BAR_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_TRAILED_STATUS_BAR_PATTERN; } });
|
|
46
|
+
Object.defineProperty(exports, "findCodexChromeStart", { enumerable: true, get: function () { return patterns_2.findCodexChromeStart; } });
|
|
47
|
+
Object.defineProperty(exports, "CODEX_USER_ECHO_PATTERN", { enumerable: true, get: function () { return patterns_2.CODEX_USER_ECHO_PATTERN; } });
|
|
48
|
+
Object.defineProperty(exports, "findCodexUserEchoIndex", { enumerable: true, get: function () { return patterns_2.findCodexUserEchoIndex; } });
|
|
49
|
+
const patterns_3 = require("./tools/opencode/patterns");
|
|
50
|
+
var patterns_4 = require("./tools/opencode/patterns");
|
|
51
|
+
Object.defineProperty(exports, "OPENCODE_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_PROMPT_PATTERN; } });
|
|
52
|
+
Object.defineProperty(exports, "OPENCODE_IDLE_COMPOSER_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_IDLE_COMPOSER_PATTERN; } });
|
|
53
|
+
Object.defineProperty(exports, "OPENCODE_PROMPT_AFTER_RESPONSE", { enumerable: true, get: function () { return patterns_4.OPENCODE_PROMPT_AFTER_RESPONSE; } });
|
|
54
|
+
Object.defineProperty(exports, "OPENCODE_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_THINKING_PATTERN; } });
|
|
55
|
+
Object.defineProperty(exports, "OPENCODE_LOADING_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_LOADING_PATTERN; } });
|
|
56
|
+
Object.defineProperty(exports, "OPENCODE_RESPONSE_COMPLETE", { enumerable: true, get: function () { return patterns_4.OPENCODE_RESPONSE_COMPLETE; } });
|
|
57
|
+
Object.defineProperty(exports, "OPENCODE_TURN_COMPLETE_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_TURN_COMPLETE_PATTERN; } });
|
|
58
|
+
Object.defineProperty(exports, "OPENCODE_PERMISSION_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_PERMISSION_PATTERN; } });
|
|
59
|
+
Object.defineProperty(exports, "OPENCODE_PROCESSING_INDICATOR", { enumerable: true, get: function () { return patterns_4.OPENCODE_PROCESSING_INDICATOR; } });
|
|
60
|
+
Object.defineProperty(exports, "OPENCODE_COMPOSER_BOTTOM_BORDER", { enumerable: true, get: function () { return patterns_4.OPENCODE_COMPOSER_BOTTOM_BORDER; } });
|
|
61
|
+
Object.defineProperty(exports, "OPENCODE_GUTTER_ROW_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_GUTTER_ROW_PATTERN; } });
|
|
62
|
+
Object.defineProperty(exports, "OPENCODE_USER_ECHO_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_USER_ECHO_PATTERN; } });
|
|
63
|
+
Object.defineProperty(exports, "OPENCODE_FOOTER_STATUS_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_FOOTER_STATUS_PATTERN; } });
|
|
64
|
+
Object.defineProperty(exports, "findOpenCodeChromeStart", { enumerable: true, get: function () { return patterns_4.findOpenCodeChromeStart; } });
|
|
65
|
+
Object.defineProperty(exports, "findOpenCodeUserEchoEnd", { enumerable: true, get: function () { return patterns_4.findOpenCodeUserEchoEnd; } });
|
|
66
|
+
Object.defineProperty(exports, "findOpenCodeComposerRows", { enumerable: true, get: function () { return patterns_4.findOpenCodeComposerRows; } });
|
|
67
|
+
Object.defineProperty(exports, "stripOpenCodeGutter", { enumerable: true, get: function () { return patterns_4.stripOpenCodeGutter; } });
|
|
68
|
+
Object.defineProperty(exports, "OPENCODE_SELECTION_LIST_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_SELECTION_LIST_PATTERN; } });
|
|
69
|
+
Object.defineProperty(exports, "OPENCODE_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_4.OPENCODE_SEPARATOR_PATTERN; } });
|
|
70
|
+
Object.defineProperty(exports, "OPENCODE_SKIP_PATTERNS", { enumerable: true, get: function () { return patterns_4.OPENCODE_SKIP_PATTERNS; } });
|
|
71
|
+
const patterns_5 = require("./tools/opencode-v2/patterns");
|
|
72
|
+
var patterns_6 = require("./tools/opencode-v2/patterns");
|
|
73
|
+
Object.defineProperty(exports, "OPENCODE_V2_IDLE_COMPOSER_PATTERN", { enumerable: true, get: function () { return patterns_6.OPENCODE_V2_IDLE_COMPOSER_PATTERN; } });
|
|
74
|
+
Object.defineProperty(exports, "OPENCODE_V2_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_6.OPENCODE_V2_FOOTER_PATTERN; } });
|
|
75
|
+
Object.defineProperty(exports, "OPENCODE_V2_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_6.OPENCODE_V2_THINKING_PATTERN; } });
|
|
76
|
+
Object.defineProperty(exports, "isOpencodeV2ComposerVisible", { enumerable: true, get: function () { return patterns_6.isOpencodeV2ComposerVisible; } });
|
|
77
|
+
Object.defineProperty(exports, "OPENCODE_V2_SKIP_PATTERNS", { enumerable: true, get: function () { return patterns_6.OPENCODE_V2_SKIP_PATTERNS; } });
|
|
78
|
+
const patterns_7 = require("./tools/claude/patterns");
|
|
79
|
+
var patterns_8 = require("./tools/claude/patterns");
|
|
80
|
+
Object.defineProperty(exports, "CLAUDE_SPINNER_CHARS", { enumerable: true, get: function () { return patterns_8.CLAUDE_SPINNER_CHARS; } });
|
|
81
|
+
Object.defineProperty(exports, "CLAUDE_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_THINKING_PATTERN; } });
|
|
82
|
+
Object.defineProperty(exports, "CLAUDE_INTERRUPT_HINT_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_INTERRUPT_HINT_PATTERN; } });
|
|
83
|
+
Object.defineProperty(exports, "CLAUDE_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_PROMPT_PATTERN; } });
|
|
84
|
+
Object.defineProperty(exports, "CLAUDE_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_SEPARATOR_PATTERN; } });
|
|
85
|
+
Object.defineProperty(exports, "findClaudeChromeStart", { enumerable: true, get: function () { return patterns_8.findClaudeChromeStart; } });
|
|
86
|
+
Object.defineProperty(exports, "CLAUDE_TRUST_DIALOG_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_TRUST_DIALOG_PATTERN; } });
|
|
87
|
+
Object.defineProperty(exports, "resolveClaudeTrustDialogKeys", { enumerable: true, get: function () { return patterns_8.resolveClaudeTrustDialogKeys; } });
|
|
88
|
+
Object.defineProperty(exports, "isClaudeTrustDialogOpen", { enumerable: true, get: function () { return patterns_8.isClaudeTrustDialogOpen; } });
|
|
89
|
+
Object.defineProperty(exports, "CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_8.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN; } });
|
|
90
|
+
Object.defineProperty(exports, "CLAUDE_SELECTION_LIST_FOOTER", { enumerable: true, get: function () { return patterns_8.CLAUDE_SELECTION_LIST_FOOTER; } });
|
|
91
|
+
Object.defineProperty(exports, "CLAUDE_SESSION_ERROR_PATTERNS", { enumerable: true, get: function () { return patterns_8.CLAUDE_SESSION_ERROR_PATTERNS; } });
|
|
92
|
+
Object.defineProperty(exports, "CLAUDE_SESSION_ERROR_REGEX_PATTERNS", { enumerable: true, get: function () { return patterns_8.CLAUDE_SESSION_ERROR_REGEX_PATTERNS; } });
|
|
93
|
+
const patterns_9 = require("./tools/gemini/patterns");
|
|
94
|
+
var patterns_10 = require("./tools/gemini/patterns");
|
|
95
|
+
Object.defineProperty(exports, "GEMINI_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_10.GEMINI_PROMPT_PATTERN; } });
|
|
96
|
+
Object.defineProperty(exports, "GEMINI_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_10.GEMINI_THINKING_PATTERN; } });
|
|
97
|
+
const patterns_11 = require("./tools/vibe-local/patterns");
|
|
98
|
+
var patterns_12 = require("./tools/vibe-local/patterns");
|
|
99
|
+
Object.defineProperty(exports, "VIBE_LOCAL_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_12.VIBE_LOCAL_PROMPT_PATTERN; } });
|
|
100
|
+
Object.defineProperty(exports, "VIBE_LOCAL_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_12.VIBE_LOCAL_THINKING_PATTERN; } });
|
|
101
|
+
const patterns_13 = require("./tools/copilot/patterns");
|
|
102
|
+
var patterns_14 = require("./tools/copilot/patterns");
|
|
103
|
+
Object.defineProperty(exports, "COPILOT_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_PROMPT_PATTERN; } });
|
|
104
|
+
Object.defineProperty(exports, "COPILOT_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_THINKING_PATTERN; } });
|
|
105
|
+
Object.defineProperty(exports, "COPILOT_WORKING_STATUS_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_WORKING_STATUS_PATTERN; } });
|
|
106
|
+
Object.defineProperty(exports, "COPILOT_IDLE_STATUS_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_IDLE_STATUS_PATTERN; } });
|
|
107
|
+
Object.defineProperty(exports, "readCopilotStatusBar", { enumerable: true, get: function () { return patterns_14.readCopilotStatusBar; } });
|
|
108
|
+
Object.defineProperty(exports, "COPILOT_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_SEPARATOR_PATTERN; } });
|
|
109
|
+
Object.defineProperty(exports, "findCopilotChromeStart", { enumerable: true, get: function () { return patterns_14.findCopilotChromeStart; } });
|
|
110
|
+
Object.defineProperty(exports, "COPILOT_BOX_ROW_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_BOX_ROW_PATTERN; } });
|
|
111
|
+
Object.defineProperty(exports, "COPILOT_REASONING_HEADER_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_REASONING_HEADER_PATTERN; } });
|
|
112
|
+
Object.defineProperty(exports, "COPILOT_USER_ECHO_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_USER_ECHO_PATTERN; } });
|
|
113
|
+
Object.defineProperty(exports, "COPILOT_TRANSCRIPT_DIVIDER_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_TRANSCRIPT_DIVIDER_PATTERN; } });
|
|
114
|
+
Object.defineProperty(exports, "COPILOT_TOOL_VERBS", { enumerable: true, get: function () { return patterns_14.COPILOT_TOOL_VERBS; } });
|
|
115
|
+
Object.defineProperty(exports, "COPILOT_TOOL_ROW_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_TOOL_ROW_PATTERN; } });
|
|
116
|
+
Object.defineProperty(exports, "COPILOT_TRANSCRIPT_CONTINUATION_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_TRANSCRIPT_CONTINUATION_PATTERN; } });
|
|
117
|
+
Object.defineProperty(exports, "COPILOT_BOOT_BANNER_ANCHORS", { enumerable: true, get: function () { return patterns_14.COPILOT_BOOT_BANNER_ANCHORS; } });
|
|
118
|
+
Object.defineProperty(exports, "COPILOT_SELECTION_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_SELECTION_FOOTER_PATTERN; } });
|
|
119
|
+
Object.defineProperty(exports, "isCopilotSelectionFrame", { enumerable: true, get: function () { return patterns_14.isCopilotSelectionFrame; } });
|
|
120
|
+
Object.defineProperty(exports, "COPILOT_FOLDER_TRUST_ANCHORS", { enumerable: true, get: function () { return patterns_14.COPILOT_FOLDER_TRUST_ANCHORS; } });
|
|
121
|
+
Object.defineProperty(exports, "COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN", { enumerable: true, get: function () { return patterns_14.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN; } });
|
|
122
|
+
Object.defineProperty(exports, "COPILOT_FOLDER_TRUST_ANSWER_KEY", { enumerable: true, get: function () { return patterns_14.COPILOT_FOLDER_TRUST_ANSWER_KEY; } });
|
|
123
|
+
Object.defineProperty(exports, "isCopilotFolderTrustDialog", { enumerable: true, get: function () { return patterns_14.isCopilotFolderTrustDialog; } });
|
|
124
|
+
Object.defineProperty(exports, "COPILOT_SKIP_PATTERNS", { enumerable: true, get: function () { return patterns_14.COPILOT_SKIP_PATTERNS; } });
|
|
125
|
+
const patterns_15 = require("./tools/antigravity/patterns");
|
|
126
|
+
var patterns_16 = require("./tools/antigravity/patterns");
|
|
127
|
+
Object.defineProperty(exports, "ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE; } });
|
|
128
|
+
Object.defineProperty(exports, "ANTIGRAVITY_COMPOSER_MODE_BANNER_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_COMPOSER_MODE_BANNER_PATTERN; } });
|
|
129
|
+
Object.defineProperty(exports, "ANTIGRAVITY_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_PROMPT_PATTERN; } });
|
|
130
|
+
Object.defineProperty(exports, "ANTIGRAVITY_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_THINKING_PATTERN; } });
|
|
131
|
+
Object.defineProperty(exports, "ANTIGRAVITY_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_SEPARATOR_PATTERN; } });
|
|
132
|
+
Object.defineProperty(exports, "ANTIGRAVITY_SELECTION_LIST_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_SELECTION_LIST_PATTERN; } });
|
|
133
|
+
Object.defineProperty(exports, "ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN; } });
|
|
134
|
+
Object.defineProperty(exports, "ANTIGRAVITY_NUMBERED_OPTION_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_NUMBERED_OPTION_PATTERN; } });
|
|
135
|
+
Object.defineProperty(exports, "ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN; } });
|
|
136
|
+
Object.defineProperty(exports, "ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN; } });
|
|
137
|
+
Object.defineProperty(exports, "ANTIGRAVITY_DIALOG_MAX_ROWS", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_DIALOG_MAX_ROWS; } });
|
|
138
|
+
Object.defineProperty(exports, "ANTIGRAVITY_SURVEY_PATTERN", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_SURVEY_PATTERN; } });
|
|
139
|
+
Object.defineProperty(exports, "locateAntigravityDialogRegion", { enumerable: true, get: function () { return patterns_16.locateAntigravityDialogRegion; } });
|
|
140
|
+
Object.defineProperty(exports, "isAntigravityNumberedDialog", { enumerable: true, get: function () { return patterns_16.isAntigravityNumberedDialog; } });
|
|
141
|
+
Object.defineProperty(exports, "ANTIGRAVITY_SKIP_PATTERNS", { enumerable: true, get: function () { return patterns_16.ANTIGRAVITY_SKIP_PATTERNS; } });
|
|
142
|
+
const patterns_17 = require("./tools/command-code/patterns");
|
|
143
|
+
var patterns_18 = require("./tools/command-code/patterns");
|
|
144
|
+
Object.defineProperty(exports, "COMMAND_CODE_SPINNER_CHARS", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_SPINNER_CHARS; } });
|
|
145
|
+
Object.defineProperty(exports, "COMMAND_CODE_PROMPT_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_PROMPT_PATTERN; } });
|
|
146
|
+
Object.defineProperty(exports, "COMMAND_CODE_SEPARATOR_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_SEPARATOR_PATTERN; } });
|
|
147
|
+
Object.defineProperty(exports, "COMMAND_CODE_INTERRUPT_HINT_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_INTERRUPT_HINT_PATTERN; } });
|
|
148
|
+
Object.defineProperty(exports, "COMMAND_CODE_THINKING_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_THINKING_PATTERN; } });
|
|
149
|
+
Object.defineProperty(exports, "COMMAND_CODE_RESPONSE_MARKER_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_RESPONSE_MARKER_PATTERN; } });
|
|
150
|
+
Object.defineProperty(exports, "COMMAND_CODE_COMPLETION_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_COMPLETION_PATTERN; } });
|
|
151
|
+
Object.defineProperty(exports, "COMMAND_CODE_MODE_INDICATOR_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_MODE_INDICATOR_PATTERN; } });
|
|
152
|
+
Object.defineProperty(exports, "COMMAND_CODE_BANNER_PATTERNS", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_BANNER_PATTERNS; } });
|
|
153
|
+
Object.defineProperty(exports, "COMMAND_CODE_HOOK_NOTICE_PATTERN", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_HOOK_NOTICE_PATTERN; } });
|
|
154
|
+
Object.defineProperty(exports, "findCommandCodeChromeStart", { enumerable: true, get: function () { return patterns_18.findCommandCodeChromeStart; } });
|
|
155
|
+
Object.defineProperty(exports, "COMMAND_CODE_SKIP_PATTERNS", { enumerable: true, get: function () { return patterns_18.COMMAND_CODE_SKIP_PATTERNS; } });
|
|
40
156
|
const logger = (0, logger_1.createLogger)('cli-patterns');
|
|
41
|
-
/**
|
|
42
|
-
* Claude CLI spinner characters (expanded set)
|
|
43
|
-
* These are shown when Claude is thinking/processing
|
|
44
|
-
*/
|
|
45
|
-
exports.CLAUDE_SPINNER_CHARS = [
|
|
46
|
-
'✻', '✽', '⏺', '·', '∴', '✢', '✳', '✶',
|
|
47
|
-
'⦿', '◉', '●', '○', '◌', '◎', '⊙', '⊚',
|
|
48
|
-
'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏', // Braille spinner
|
|
49
|
-
];
|
|
50
|
-
/**
|
|
51
|
-
* Claude thinking pattern
|
|
52
|
-
* Matches spinner character followed by activity text ending with …
|
|
53
|
-
* The text can contain spaces (e.g., "Verifying implementation (dead code detection)…")
|
|
54
|
-
*
|
|
55
|
-
* Alternative 2: "esc to interrupt" status bar text (Issue #188)
|
|
56
|
-
* Claude Code shows "esc to interrupt" in the terminal status bar during active processing.
|
|
57
|
-
* Previous pattern required closing paren `to interrupt\)` matching `(esc to interrupt)`,
|
|
58
|
-
* but Claude Code v2.x status bar format uses `· esc to interrupt ·` without parens.
|
|
59
|
-
* Updated to match `esc to interrupt` which covers both formats.
|
|
60
|
-
*/
|
|
61
|
-
exports.CLAUDE_THINKING_PATTERN = new RegExp(`[${exports.CLAUDE_SPINNER_CHARS.join('')}]\\s+.+…|esc to interrupt`, 'm');
|
|
62
|
-
/**
|
|
63
|
-
* Claude status-bar "esc to interrupt" hint (Issue #805)
|
|
64
|
-
*
|
|
65
|
-
* Claude Code shows "esc to interrupt" in the bottom status bar ONLY while it is
|
|
66
|
-
* actively processing. When idle/ready, the status bar shows shortcut hints
|
|
67
|
-
* (e.g., "? for shortcuts") instead -- so this token is a reliable "running" signal.
|
|
68
|
-
*
|
|
69
|
-
* Why this exists separately from CLAUDE_THINKING_PATTERN's "esc to interrupt"
|
|
70
|
-
* alternative: status detection evaluates the spinner+ellipsis branch of
|
|
71
|
-
* CLAUDE_THINKING_PATTERN within a narrow 5-line window (THINKING_TAIL_LINE_COUNT)
|
|
72
|
-
* to avoid mistaking a completed thinking summary in scrollback for active work
|
|
73
|
-
* (Issue #188). During /pm-auto-dev + subagent runs, the bottom task panel
|
|
74
|
-
* ("⏺ main" / "◯ general-purpose ..." rows) pushes both the "✶ Running…" spinner
|
|
75
|
-
* AND the "esc to interrupt" status bar out of that 5-line window, so the session
|
|
76
|
-
* was misdetected as Ready (Issue #805). Unlike the spinner+ellipsis summary, the
|
|
77
|
-
* status-bar text is repainted live and never lingers in scrollback, so it can be
|
|
78
|
-
* matched in a wider footer window without regressing Issue #188.
|
|
79
|
-
*/
|
|
80
|
-
exports.CLAUDE_INTERRUPT_HINT_PATTERN = /esc to interrupt/;
|
|
81
|
-
/**
|
|
82
|
-
* Codex activity-marker pattern
|
|
83
|
-
* Matches activity indicators like "• Planning", "• Searching", etc.
|
|
84
|
-
* T1.1: Extended to include "Ran" and "Deciding"
|
|
85
|
-
*
|
|
86
|
-
* Issue #1671: these are *transcript records*, not a liveness signal. Codex is
|
|
87
|
-
* inline-rendered (no alternate screen), so every "• Ran <cmd>" / "• Running
|
|
88
|
-
* <cmd>" step it ever printed stays in the pane scrollback forever — measured on
|
|
89
|
-
* a live `mcbd-codex-*` pane: 396 "• Ran" and 11 "• Running" rows, all of them
|
|
90
|
-
* from finished steps. Matching this pattern against a fixed tail window
|
|
91
|
-
* therefore answers "did a step happen recently", not "is Codex working now".
|
|
92
|
-
* Use {@link isCodexTurnActive} for the latter.
|
|
93
|
-
*/
|
|
94
|
-
exports.CODEX_THINKING_PATTERN = /•\s*(Planning|Searching|Exploring|Running|Thinking|Working|Reading|Writing|Analyzing|Ran|Deciding)/m;
|
|
95
|
-
/**
|
|
96
|
-
* Codex live status-line hint (Issue #1671)
|
|
97
|
-
*
|
|
98
|
-
* While a turn is in flight Codex pins a status row directly above the composer:
|
|
99
|
-
*
|
|
100
|
-
* • Working (13s • esc to interrupt) · 1 background terminal running · /ps to view
|
|
101
|
-
*
|
|
102
|
-
* It is repainted in place every tick and erased the moment the turn ends, so —
|
|
103
|
-
* unlike the "• Ran"/"• Running" step records — it never lingers in scrollback.
|
|
104
|
-
* Measured on a 11,000-line capture of an idle Codex pane: zero occurrences of
|
|
105
|
-
* "esc to interrupt", against 396 lingering "• Ran" rows. That makes it the one
|
|
106
|
-
* unambiguous "Codex is still generating" token, mirroring Claude's
|
|
107
|
-
* {@link CLAUDE_INTERRUPT_HINT_PATTERN}.
|
|
108
|
-
*/
|
|
109
|
-
exports.CODEX_INTERRUPT_HINT_PATTERN = /esc to interrupt/;
|
|
110
|
-
/**
|
|
111
|
-
* How far above the last content row Codex's composer ("› …") may sit.
|
|
112
|
-
*
|
|
113
|
-
* Codex pins the composer and the status bar to the bottom of the pane, so the
|
|
114
|
-
* composer lands 2-3 rows above the last non-blank row in every observed frame.
|
|
115
|
-
* The small allowance keeps the search from walking up into the transcript and
|
|
116
|
-
* latching onto the echoed user message, which uses the same "› " marker.
|
|
117
|
-
*/
|
|
118
|
-
const CODEX_COMPOSER_SEARCH_ROWS = 8;
|
|
119
|
-
/**
|
|
120
|
-
* Decide whether a Codex turn is still in flight (Issue #1671).
|
|
121
|
-
*
|
|
122
|
-
* Completion detection used to answer this with {@link CODEX_THINKING_PATTERN}
|
|
123
|
-
* over a fixed 20-row tail. Because "• Ran <cmd>" is a *past-tense record* that
|
|
124
|
-
* never leaves the transcript, a turn that ended with a short final message kept
|
|
125
|
-
* that record inside the tail window and was reported as "still thinking"
|
|
126
|
-
* forever — so its reply was never saved, while a turn whose final message
|
|
127
|
-
* happened to be longer than 20 rows pushed the record out of the window and was
|
|
128
|
-
* saved. Whether a reply reached Message History depended on how long it was.
|
|
129
|
-
*
|
|
130
|
-
* Two signals, both measured against live codex-cli 0.146.0 captures:
|
|
131
|
-
*
|
|
132
|
-
* 1. The live status line ({@link CODEX_INTERRUPT_HINT_PATTERN}) anywhere in the
|
|
133
|
-
* tail window. Present in every generating frame from 1s onwards, absent from
|
|
134
|
-
* every idle frame.
|
|
135
|
-
* 2. An activity marker in the rows immediately above the composer — the band
|
|
136
|
-
* Codex reserves for that status line. Version-agnostic backstop for a Codex
|
|
137
|
-
* build whose status row drops the "esc to interrupt" wording; deliberately
|
|
138
|
-
* narrow (THINKING_TAIL_LINE_COUNT rows, matching the window status-detector
|
|
139
|
-
* already uses) so records further up the transcript cannot reach it.
|
|
140
|
-
*
|
|
141
|
-
* When no composer can be located the frame is not a normal Codex layout (an
|
|
142
|
-
* overlay is up, or the pane is mid-redraw), so signal 2 falls back to the whole
|
|
143
|
-
* tail window — the pre-#1671 behaviour, which errs towards "still active".
|
|
144
|
-
*
|
|
145
|
-
* @param lines - Captured pane lines with trailing blank rows already trimmed
|
|
146
|
-
* @param tailLineCount - Size of the tail window completion detection looks at
|
|
147
|
-
* @returns True while Codex is still generating
|
|
148
|
-
*/
|
|
149
|
-
function isCodexTurnActive(lines, tailLineCount) {
|
|
150
|
-
const tailWindow = (0, ansi_1.stripAnsi)(lines.slice(Math.max(0, lines.length - tailLineCount)).join('\n'));
|
|
151
|
-
// 1. Live status line — unambiguous, never survives the end of a turn.
|
|
152
|
-
if (exports.CODEX_INTERRUPT_HINT_PATTERN.test(tailWindow))
|
|
153
|
-
return true;
|
|
154
|
-
// 2. Activity marker in the status-line band directly above the composer.
|
|
155
|
-
// Searched bottom-up so the composer wins over the echoed user message.
|
|
156
|
-
let composerIndex = -1;
|
|
157
|
-
for (let i = lines.length - 1; i >= Math.max(0, lines.length - CODEX_COMPOSER_SEARCH_ROWS); i--) {
|
|
158
|
-
if (exports.CODEX_PROMPT_PATTERN.test((0, ansi_1.stripAnsi)(lines[i]))) {
|
|
159
|
-
composerIndex = i;
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (composerIndex < 0)
|
|
164
|
-
return exports.CODEX_THINKING_PATTERN.test(tailWindow);
|
|
165
|
-
const bandStart = Math.max(0, composerIndex - thinking_constants_1.THINKING_TAIL_LINE_COUNT + 1);
|
|
166
|
-
const band = (0, ansi_1.stripAnsi)(lines.slice(bandStart, composerIndex + 1).join('\n'));
|
|
167
|
-
return exports.CODEX_THINKING_PATTERN.test(band);
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Claude prompt pattern (waiting for input)
|
|
171
|
-
* Supports both legacy '>' and new '❯' (U+276F) prompt characters
|
|
172
|
-
* Issue #132: Also matches prompts with recommended commands (e.g., "❯ /work-plan")
|
|
173
|
-
*
|
|
174
|
-
* Matches:
|
|
175
|
-
* - Empty prompt: "❯ " or "> "
|
|
176
|
-
* - Prompt with command: "❯ /work-plan" or "> npm install"
|
|
177
|
-
*/
|
|
178
|
-
exports.CLAUDE_PROMPT_PATTERN = /^[>❯](\s*$|\s+\S)/m;
|
|
179
|
-
/**
|
|
180
|
-
* Claude separator pattern
|
|
181
|
-
*/
|
|
182
|
-
exports.CLAUDE_SEPARATOR_PATTERN = /^─{10,}$/m;
|
|
183
|
-
/**
|
|
184
|
-
* Locate the start of Claude Code's bottom-pinned footer within a captured pane.
|
|
185
|
-
*
|
|
186
|
-
* Claude Code v2 draws in the alternate screen and reserves the last rows of the
|
|
187
|
-
* pane for a footer that is never transcript content:
|
|
188
|
-
*
|
|
189
|
-
* <hint row> ← "◉ xhigh · /effort", "tmux detected · …", or blank
|
|
190
|
-
* ──────────────────── ← separator
|
|
191
|
-
* ❯ <input box> ← one or more rows
|
|
192
|
-
* ──────────────────── ← separator
|
|
193
|
-
* ⏸ manual mode on · ? for shortcuts · ← for agents focus
|
|
194
|
-
*
|
|
195
|
-
* The hint row rotates every few seconds while the conversation sits idle, so
|
|
196
|
-
* keeping the footer in an extracted response makes its content hash change on
|
|
197
|
-
* every poll tick. That defeated the content-based dedup added in #1268 and
|
|
198
|
-
* re-saved the same reply once per tick (#1289).
|
|
199
|
-
*
|
|
200
|
-
* The boundary is found structurally rather than by matching hint text: the hint
|
|
201
|
-
* strings are Claude Code's to change, and pattern-matching them is what let this
|
|
202
|
-
* regression through (`? for shortcuts` was already listed as a skip pattern, but
|
|
203
|
-
* the real status bar embeds it mid-line so the anchors never matched). The row
|
|
204
|
-
* above the opening separator is reserved by Claude Code's layout and stays blank
|
|
205
|
-
* even when a reply fills the whole pane, so it is always safe to drop.
|
|
206
|
-
*
|
|
207
|
-
* @param lines - Captured pane lines; trailing blank rows are tolerated
|
|
208
|
-
* @returns Index of the first footer row, or -1 when no footer is present
|
|
209
|
-
*/
|
|
210
|
-
function findClaudeChromeStart(lines) {
|
|
211
|
-
// Issue #1879: the structural search (closing separator → opening separator →
|
|
212
|
-
// prompt glyph, including the "is this really the input box and not a reply
|
|
213
|
-
// fenced by two horizontal rules?" check) moved to `findClaudeInputBox` so the
|
|
214
|
-
// composer reader locates the same box this trimmer does. Behaviour here is
|
|
215
|
-
// unchanged; only the caller of the search moved.
|
|
216
|
-
const box = (0, composer_text_1.findClaudeInputBox)(lines);
|
|
217
|
-
if (box === null)
|
|
218
|
-
return -1;
|
|
219
|
-
// Include the reserved hint row directly above the opening separator.
|
|
220
|
-
return Math.max(0, box.openingSeparator - 1);
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Claude trust dialog pattern (Issue #201)
|
|
224
|
-
*
|
|
225
|
-
* Matches the "Quick safety check" dialog displayed by Claude CLI v2.x
|
|
226
|
-
* when accessing a workspace for the first time.
|
|
227
|
-
*
|
|
228
|
-
* Intentionally uses partial matching (no line-start anchor ^):
|
|
229
|
-
* Other pattern constants (CLAUDE_PROMPT_PATTERN, CLAUDE_SEPARATOR_PATTERN, etc.)
|
|
230
|
-
* use line-start anchors (^), but this pattern needs to match at any position
|
|
231
|
-
* within the tmux output buffer because the dialog text may appear after
|
|
232
|
-
* tmux padding or other output. (SF-001)
|
|
233
|
-
*/
|
|
234
|
-
exports.CLAUDE_TRUST_DIALOG_PATTERN = /Yes, I trust this folder/m;
|
|
235
|
-
/** The option row of the trust dialog's cursor: `❯` (or legacy `>`) after the dialog's left padding. */
|
|
236
|
-
const CLAUDE_TRUST_CURSOR_ROW_PATTERN = /^\s*[>❯]\s+\S/;
|
|
237
|
-
/** How far the cursor row may sit from the `Yes, I trust this folder` row (the dialog has two options). */
|
|
238
|
-
const CLAUDE_TRUST_MAX_CURSOR_DISTANCE = 3;
|
|
239
|
-
/**
|
|
240
|
-
* Keys that answer Claude Code's folder-trust dialog with "Yes", or null when
|
|
241
|
-
* no answerable dialog is on screen (Issue #3078).
|
|
242
|
-
*
|
|
243
|
-
* The dialog comes in two layouts, and Enter alone is only right for one:
|
|
244
|
-
*
|
|
245
|
-
* - default Yes (Issue #201 era, `tests/unit/lib/claude-session.test.ts`):
|
|
246
|
-
* ` ❯ 1. Yes, I trust this folder` / ` 2. No, exit` — Enter confirms Yes.
|
|
247
|
-
* - default No (2.1.259 `tests/fixtures/chat-dialog-card-2254/claude-trust-2-1-259.txt`,
|
|
248
|
-
* 2.1.287 with a permission allow-list
|
|
249
|
-
* `tests/fixtures/claude-trust-dialog-3078/allowlist-default-no-2-1-287.txt`):
|
|
250
|
-
* ` ❯ No, exit` / ` Yes, I trust this folder` — Enter EXITS Claude Code.
|
|
251
|
-
*
|
|
252
|
-
* So the answer is read off the screen: the distance from the cursor row to the
|
|
253
|
-
* Yes row becomes that many `Up`/`Down` presses, then `Enter`. Only the last
|
|
254
|
-
* dialog in the capture counts, and a dialog followed by Claude's input-box
|
|
255
|
-
* separator is history in the scrollback, not an open dialog.
|
|
256
|
-
*
|
|
257
|
-
* @param output - ANSI-stripped pane output
|
|
258
|
-
* @returns tmux key names ending in `Enter` (just `['Enter']` when the cursor is
|
|
259
|
-
* already on Yes), or null when no open dialog / no readable cursor row
|
|
260
|
-
*/
|
|
261
|
-
function resolveClaudeTrustDialogKeys(output) {
|
|
262
|
-
const lines = output.split('\n');
|
|
263
|
-
const yesIndex = findOpenClaudeTrustYesRow(lines);
|
|
264
|
-
if (yesIndex === -1)
|
|
265
|
-
return null;
|
|
266
|
-
for (let distance = 0; distance <= CLAUDE_TRUST_MAX_CURSOR_DISTANCE; distance++) {
|
|
267
|
-
for (const direction of distance === 0 ? [0] : [-1, 1]) {
|
|
268
|
-
const index = yesIndex + direction * distance;
|
|
269
|
-
if (index < 0 || index >= lines.length)
|
|
270
|
-
continue;
|
|
271
|
-
if (!CLAUDE_TRUST_CURSOR_ROW_PATTERN.test(lines[index]))
|
|
272
|
-
continue;
|
|
273
|
-
// Cursor above Yes → move Down; below → move Up.
|
|
274
|
-
const key = direction < 0 ? 'Down' : 'Up';
|
|
275
|
-
return [...Array(distance).fill(key), 'Enter'];
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Whether Claude Code's folder-trust dialog is the open screen (Issue #3078).
|
|
282
|
-
*
|
|
283
|
-
* The dialog's cursor row (` ❯ No, exit`) has the prompt glyph, so the start
|
|
284
|
-
* wait asks this before CLAUDE_PROMPT_PATTERN rather than reading the dialog as
|
|
285
|
-
* a ready prompt.
|
|
286
|
-
*
|
|
287
|
-
* @param output - ANSI-stripped pane output
|
|
288
|
-
*/
|
|
289
|
-
function isClaudeTrustDialogOpen(output) {
|
|
290
|
-
return findOpenClaudeTrustYesRow(output.split('\n')) !== -1;
|
|
291
|
-
}
|
|
292
|
-
/** `#{pane_current_command}` values of an interactive shell, with tmux's `-` login prefix removed. */
|
|
293
|
-
const SHELL_PANE_COMMANDS = new Set(['sh', 'bash', 'zsh', 'fish', 'dash', 'ksh', 'tcsh', 'csh']);
|
|
294
|
-
/**
|
|
295
|
-
* Whether a pane's foreground command is a shell rather than an agent
|
|
296
|
-
* (Issue #3089). Claude Code shows up as `claude`, `node` or its version
|
|
297
|
-
* string (`2.1.287`), never as one of these, so a shell here after the agent
|
|
298
|
-
* was seen running means the agent has exited.
|
|
299
|
-
*
|
|
300
|
-
* @param command - `#{pane_current_command}` of the pane
|
|
301
|
-
*/
|
|
302
|
-
function isShellPaneCommand(command) {
|
|
303
|
-
return SHELL_PANE_COMMANDS.has(command.trim().replace(/^-/, ''));
|
|
304
|
-
}
|
|
305
|
-
/** Row of the last `Yes, I trust this folder`, or -1 when absent or already answered. */
|
|
306
|
-
function findOpenClaudeTrustYesRow(lines) {
|
|
307
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
308
|
-
if (!exports.CLAUDE_TRUST_DIALOG_PATTERN.test(lines[i]))
|
|
309
|
-
continue;
|
|
310
|
-
// Claude's input box (separator rows) drawn below it: the dialog is scrollback.
|
|
311
|
-
return lines.slice(i + 1).some((line) => exports.CLAUDE_SEPARATOR_PATTERN.test(line)) ? -1 : i;
|
|
312
|
-
}
|
|
313
|
-
return -1;
|
|
314
|
-
}
|
|
315
|
-
/**
|
|
316
|
-
* Codex prompt pattern
|
|
317
|
-
* T1.2: Improved to detect empty prompts as well
|
|
318
|
-
*/
|
|
319
|
-
exports.CODEX_PROMPT_PATTERN = /^›\s*/m;
|
|
320
|
-
/**
|
|
321
|
-
* The directory/folder trust dialog's question line, across codex builds
|
|
322
|
-
* (Issue #2884): `Do you trust the contents of this directory?` (<=0.155.1)
|
|
323
|
-
* and `Trust this folder?` (0.157.1, which dropped the "Do you trust" wording
|
|
324
|
-
* entirely -- `tests/fixtures/codex-dialogs-0157/trust.txt`). The two variants
|
|
325
|
-
* are kept as alternatives of one pattern -- rather than duplicated across
|
|
326
|
-
* {@link CODEX_DIALOG_PATTERN}, `getCodexActiveDialog` and
|
|
327
|
-
* `getCodexLifecycleDialog` -- so those three cannot drift into disagreeing
|
|
328
|
-
* about what counts as the trust dialog.
|
|
329
|
-
*/
|
|
330
|
-
exports.CODEX_TRUST_QUESTION_PATTERN = /Do you trust|Trust this folder\?/;
|
|
331
|
-
/**
|
|
332
|
-
* Codex INTERACTIVE startup dialog pattern (Issue #890)
|
|
333
|
-
*
|
|
334
|
-
* Codex shows interactive update-notification and trust dialogs on first launch.
|
|
335
|
-
* Their currently-selected option lines render as "› 1. Update now", which ALSO
|
|
336
|
-
* matches CODEX_PROMPT_PATTERN (the bare "^›" input-prompt pattern). So "is the
|
|
337
|
-
* input prompt ready?" cannot be decided by CODEX_PROMPT_PATTERN alone -- it must
|
|
338
|
-
* also confirm no INTERACTIVE dialog is still active. This pattern matches markers
|
|
339
|
-
* that appear ONLY in interactive dialogs:
|
|
340
|
-
* - Interactive update dialog: "Skip until next version" (the option-3 label)
|
|
341
|
-
* - Trust dialog: {@link CODEX_TRUST_QUESTION_PATTERN}
|
|
342
|
-
* - Dialog confirm footer: "Press enter to continue"
|
|
343
|
-
* - Numbered selection option: "› 1. ..." (leading ›, a digit, a dot)
|
|
344
|
-
*
|
|
345
|
-
* IMPORTANT (Issue #890 regression): the substring "Update available" is
|
|
346
|
-
* deliberately NOT a marker. After the update is skipped, codex keeps a
|
|
347
|
-
* non-interactive banner box ("✨ Update available! ... / Run npm install -g
|
|
348
|
-
* @openai/codex to update.") rendered ABOVE the genuine "› " prompt. Matching
|
|
349
|
-
* "Update available" would make isCodexPromptReady() return false for as long as
|
|
350
|
-
* that banner is visible, hanging waitForReady (~30s) and waitForPrompt (15s) on
|
|
351
|
-
* exactly the first-launch + update-pending case this fix targets. The interactive
|
|
352
|
-
* update dialog is still reliably detected via its other three markers above
|
|
353
|
-
* ("› 1. Update now" + "Skip until next version" + "Press enter to continue").
|
|
354
|
-
*
|
|
355
|
-
* No /g flag (would make .test() stateful); no nested quantifiers (ReDoS-safe).
|
|
356
|
-
*/
|
|
357
|
-
exports.CODEX_DIALOG_PATTERN = new RegExp(`Skip until next version|${exports.CODEX_TRUST_QUESTION_PATTERN.source}|Press enter to continue|^\\s*›\\s*\\d+\\.\\s`, 'm');
|
|
358
|
-
/**
|
|
359
|
-
* Codex genuine input-prompt line (Issue #892).
|
|
360
|
-
*
|
|
361
|
-
* A line whose first non-space glyph is "›" but which is NOT a numbered dialog
|
|
362
|
-
* option ("› 1. ..."). The selected dialog option renders "›" at column 0 too
|
|
363
|
-
* (same column as the live prompt), so the digit-dot negative lookahead is what
|
|
364
|
-
* distinguishes the genuine input line from a dialog option line. Single-line
|
|
365
|
-
* (no /m, no /g) -- callers test it per line to locate the prompt's position.
|
|
366
|
-
*/
|
|
367
|
-
const CODEX_GENUINE_PROMPT_LINE = /^\s*›(?!\s*\d+\.)/;
|
|
368
|
-
/**
|
|
369
|
-
* Decide whether Codex output shows a genuine interactive input prompt rather than
|
|
370
|
-
* a startup dialog (Issue #890, reworked in Issue #892).
|
|
371
|
-
*
|
|
372
|
-
* POSITION-based: capturePane(50) returns scrollback, so a dismissed update/trust
|
|
373
|
-
* dialog lingers ABOVE the live prompt. The original Issue #890 form
|
|
374
|
-
* (`CODEX_PROMPT_PATTERN && !CODEX_DIALOG_PATTERN`) is a whole-window test, so a
|
|
375
|
-
* residual dialog line anywhere in the frame keeps it false forever -- hanging
|
|
376
|
-
* waitForReady/waitForPrompt and (via the re-firing branches) injecting "222...".
|
|
377
|
-
*
|
|
378
|
-
* Instead the frame is ready when a genuine input-prompt line sits BELOW every
|
|
379
|
-
* interactive dialog marker -- i.e. the prompt is the bottom-most active element.
|
|
380
|
-
* CODEX_PROMPT_PATTERN / CODEX_DIALOG_PATTERN are intentionally unchanged here
|
|
381
|
-
* (status-detector.ts / response-checker.ts depend on them).
|
|
382
|
-
*
|
|
383
|
-
* Used by both CodexTool.waitForReady() (startup) and CodexTool.waitForPrompt()
|
|
384
|
-
* (before every send) so a residual dialog is never mistaken for "ready" and, just
|
|
385
|
-
* as importantly, a genuine prompt below stale dialog scrollback IS detected.
|
|
386
|
-
*/
|
|
387
|
-
function isCodexPromptReady(output) {
|
|
388
|
-
const lines = output.split('\n');
|
|
389
|
-
let lastDialogMarkerIdx = -1;
|
|
390
|
-
let lastPromptIdx = -1;
|
|
391
|
-
for (let i = 0; i < lines.length; i++) {
|
|
392
|
-
const line = lines[i];
|
|
393
|
-
if (exports.CODEX_DIALOG_PATTERN.test(line)) {
|
|
394
|
-
// A dialog marker/option line is never itself a genuine prompt.
|
|
395
|
-
lastDialogMarkerIdx = i;
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
if (CODEX_GENUINE_PROMPT_LINE.test(line)) {
|
|
399
|
-
lastPromptIdx = i;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
return lastPromptIdx >= 0 && lastPromptIdx > lastDialogMarkerIdx;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Classify the bottom-most active Codex startup dialog (Issue #892).
|
|
406
|
-
*
|
|
407
|
-
* POSITION-based companion to isCodexPromptReady(): only dialog text appearing
|
|
408
|
-
* BELOW the genuine input-prompt line is considered "active". Dialog lines that
|
|
409
|
-
* remain in scrollback ABOVE a live prompt are ignored, so a dismissed dialog is
|
|
410
|
-
* never re-acted on (this is what stops the update branch from re-sending "2" once
|
|
411
|
-
* the dialog has been skipped -- the root cause of the "222..." prefix).
|
|
412
|
-
*
|
|
413
|
-
* Precedence matches CodexTool.waitForReady()'s historical branch order: the
|
|
414
|
-
* update dialog wins over its own "Press enter to continue" footer, because Enter
|
|
415
|
-
* on the update dialog could confirm the default "1. Update now" (npm install).
|
|
416
|
-
*/
|
|
417
|
-
/**
|
|
418
|
-
* The lines of Codex's ACTIVE region: everything strictly below the bottom-most
|
|
419
|
-
* genuine input-prompt line, or the whole frame when there is no prompt line
|
|
420
|
-
* (Issue #892).
|
|
421
|
-
*
|
|
422
|
-
* This is the one rule that keeps every Codex dialog classifier honest.
|
|
423
|
-
* capturePane returns scrollback, so a dialog that was answered minutes ago is
|
|
424
|
-
* still in the frame; only what sits BELOW the live prompt is still awaiting a
|
|
425
|
-
* key. Shared by getCodexActiveDialog and getCodexLifecycleDialog so the two
|
|
426
|
-
* cannot drift into disagreeing about what "active" means.
|
|
427
|
-
*/
|
|
428
|
-
function codexActiveRegionLines(output) {
|
|
429
|
-
const lines = output.split('\n');
|
|
430
|
-
// Index of the bottom-most genuine input-prompt line (-1 if none).
|
|
431
|
-
let promptIdx = -1;
|
|
432
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
433
|
-
if (CODEX_GENUINE_PROMPT_LINE.test(lines[i])) {
|
|
434
|
-
promptIdx = i;
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return lines.slice(promptIdx + 1);
|
|
439
|
-
}
|
|
440
|
-
function getCodexActiveDialog(output) {
|
|
441
|
-
// Residual dialog text above a live prompt is excluded, so a dialog lingering
|
|
442
|
-
// in scrollback is never treated as active.
|
|
443
|
-
const active = codexActiveRegionLines(output).join('\n');
|
|
444
|
-
if (active === '') {
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
if (active.includes('Skip until next version') ||
|
|
448
|
-
(active.includes('Update') && active.includes('Skip'))) {
|
|
449
|
-
return 'update';
|
|
450
|
-
}
|
|
451
|
-
if (exports.CODEX_TRUST_QUESTION_PATTERN.test(active)) {
|
|
452
|
-
return 'trust';
|
|
453
|
-
}
|
|
454
|
-
if (active.includes('Press enter to continue')) {
|
|
455
|
-
return 'press-enter';
|
|
456
|
-
}
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* Anchors of codex's "Hooks need review" launch dialog — screen 1 of the three
|
|
461
|
-
* it can put in front of a session (Issue #1760, re-measured on codex-cli
|
|
462
|
-
* 0.148.0 for Issue #1829):
|
|
463
|
-
*
|
|
464
|
-
* ```
|
|
465
|
-
* Hooks need review
|
|
466
|
-
* 4 hooks are new or changed.
|
|
467
|
-
* Hooks can run outside the sandbox after you trust them.
|
|
468
|
-
*
|
|
469
|
-
* > 1. Review hooks
|
|
470
|
-
* 2. Trust all and continue
|
|
471
|
-
* 3. Continue without trusting (hooks won't run)
|
|
472
|
-
* Press enter to confirm or esc to go back
|
|
473
|
-
* ```
|
|
474
|
-
*
|
|
475
|
-
* The hook COUNT is data — 0.147.0 said 5, 0.148.0 said 4 — so neither anchor
|
|
476
|
-
* reads it. Both strings are required so a "hooks" mention elsewhere cannot
|
|
477
|
-
* select an option on a live prompt.
|
|
478
|
-
*/
|
|
479
|
-
exports.CODEX_HOOKS_REVIEW_ANCHORS = ['Hooks need review', 'Continue without trusting'];
|
|
480
|
-
/**
|
|
481
|
-
* Footer of screen 2, the hooks LIST, new in codex-cli 0.148.0 (Issue #1829):
|
|
482
|
-
* `Press t to trust all; enter to review hooks; esc to close`.
|
|
483
|
-
*
|
|
484
|
-
* The semicolon is what separates it from screen 3's footer — "trust all;" and
|
|
485
|
-
* "trust;" are disjoint — and matching the footer rather than the table above it
|
|
486
|
-
* keeps the two screens distinguishable by a single line each.
|
|
487
|
-
*/
|
|
488
|
-
const CODEX_HOOKS_LIST_FOOTER_PATTERN = /press\s+t\s+to\s+trust\s+all\s*;/i;
|
|
489
|
-
/**
|
|
490
|
-
* Footer of screen 3, the per-hook review DETAIL (Issue #1829):
|
|
491
|
-
* `Press t to trust; esc to go back`. Where both live sessions in the Issue were
|
|
492
|
-
* found parked.
|
|
493
|
-
*/
|
|
494
|
-
const CODEX_HOOKS_DETAIL_FOOTER_PATTERN = /press\s+t\s+to\s+trust\s*;/i;
|
|
495
|
-
/**
|
|
496
|
-
* How much of the active region {@link getCodexLifecycleDialog} judges, in
|
|
497
|
-
* non-blank lines counted from the bottom (Issue #1829).
|
|
498
|
-
*
|
|
499
|
-
* `getCodexActiveDialog` searches the whole active region, which is right for
|
|
500
|
-
* its caller: `waitForReady` only ever runs during `startSession`, when nothing
|
|
501
|
-
* else can be on screen. This classifier runs on every Auto-Yes poll for the
|
|
502
|
-
* life of the session, where "active region" alone is not enough — a codex
|
|
503
|
-
* approval request renders no `› ` composer line, so an approval that comes up
|
|
504
|
-
* while a dismissed hooks screen is still inside the capture window would have
|
|
505
|
-
* the whole frame as its active region and would be mistaken for the dialog.
|
|
506
|
-
* Requiring the dialog to be in the TAIL is what separates the screen the user
|
|
507
|
-
* is looking at from the one they have already left.
|
|
508
|
-
*
|
|
509
|
-
* 12 lines fits the tallest screen this has to recognise (the review dialog's
|
|
510
|
-
* two anchors sit 6 lines apart) and none of the shorter frames below it.
|
|
511
|
-
*/
|
|
512
|
-
const CODEX_LIFECYCLE_TAIL_LINES = 12;
|
|
513
|
-
/**
|
|
514
|
-
* The interactive update dialog, by its option-3 label or its option-1 line.
|
|
515
|
-
*
|
|
516
|
-
* Deliberately stricter than `getCodexActiveDialog`'s `Update` AND `Skip`
|
|
517
|
-
* fallback: that pair can occur in ordinary agent output, and here a false
|
|
518
|
-
* positive silently stops Auto-Yes answering a real prompt. Both anchors below
|
|
519
|
-
* are dialog chrome that agent output does not produce.
|
|
520
|
-
*/
|
|
521
|
-
const CODEX_UPDATE_DIALOG_ANCHORS = [
|
|
522
|
-
/skip until next version/i,
|
|
523
|
-
/^\s*[›❯]?\s*\d+\.\s*Update now/im,
|
|
524
|
-
];
|
|
525
|
-
/**
|
|
526
|
-
* Classify the bottom-most ACTIVE codex lifecycle screen (Issue #1829).
|
|
527
|
-
*
|
|
528
|
-
* Position-based, via {@link codexActiveRegionLines}: a dialog left in
|
|
529
|
-
* scrollback above a live prompt is not active and returns `null`. That is not
|
|
530
|
-
* a detail — the auto-answer guard in the Auto-Yes poller is built on this
|
|
531
|
-
* function, and a whole-frame version of it would switch Auto-Yes off for the
|
|
532
|
-
* rest of a codex session the moment any launch dialog scrolled past.
|
|
533
|
-
*
|
|
534
|
-
* The two hooks screens are matched FIRST and bottom-up, because a stuck pane
|
|
535
|
-
* holds screen 2 above screen 3 and the way out of each differs. The remaining
|
|
536
|
-
* three are region-level substring tests, which is all their anchors allow:
|
|
537
|
-
* the review dialog's are on two different lines.
|
|
538
|
-
*
|
|
539
|
-
* Deliberately NOT used to decide whether a prompt exists. `detectPrompt` still
|
|
540
|
-
* reports these screens, so a human still sees them; what this function gates is
|
|
541
|
-
* only whether a machine may answer on their behalf.
|
|
542
|
-
*
|
|
543
|
-
* @param output - ANSI-stripped pane capture
|
|
544
|
-
* @returns The active lifecycle screen, or null when none is
|
|
545
|
-
*/
|
|
546
|
-
function getCodexLifecycleDialog(output) {
|
|
547
|
-
const activeLines = codexActiveRegionLines(output);
|
|
548
|
-
const window = [];
|
|
549
|
-
for (let i = activeLines.length - 1; i >= 0 && window.length < CODEX_LIFECYCLE_TAIL_LINES; i--) {
|
|
550
|
-
if (activeLines[i].trim() === '')
|
|
551
|
-
continue;
|
|
552
|
-
window.unshift(activeLines[i]);
|
|
553
|
-
}
|
|
554
|
-
if (window.length === 0)
|
|
555
|
-
return null;
|
|
556
|
-
const text = window.join('\n');
|
|
557
|
-
// One bottom-up pass, returning on the first line that decides the question --
|
|
558
|
-
// including the lines that decide it NEGATIVELY. A stuck pane holds screen 2
|
|
559
|
-
// above screen 3, and an approval request can come up with a hooks screen
|
|
560
|
-
// still inside the capture window; in both cases the screen the user is
|
|
561
|
-
// looking at is the lower one.
|
|
562
|
-
for (let i = window.length - 1; i >= 0; i--) {
|
|
563
|
-
const line = window[i];
|
|
564
|
-
if (CODEX_HOOKS_LIST_FOOTER_PATTERN.test(line))
|
|
565
|
-
return 'hooks-list';
|
|
566
|
-
if (CODEX_HOOKS_DETAIL_FOOTER_PATTERN.test(line))
|
|
567
|
-
return 'hooks-detail';
|
|
568
|
-
// The agent asking the human for permission mid-turn (Issue #1628's
|
|
569
|
-
// "esc to cancel" footer, which no lifecycle screen wears). This is exactly
|
|
570
|
-
// the prompt Auto-Yes exists to answer, so whatever lifecycle text is still
|
|
571
|
-
// above it has been left behind and must not withhold the answer.
|
|
572
|
-
if (exports.CODEX_APPROVAL_FOOTER_PATTERN.test(line))
|
|
573
|
-
return null;
|
|
574
|
-
if (CODEX_UPDATE_DIALOG_ANCHORS.some((pattern) => pattern.test(line)))
|
|
575
|
-
return 'update';
|
|
576
|
-
if (exports.CODEX_TRUST_QUESTION_PATTERN.test(line))
|
|
577
|
-
return 'trust';
|
|
578
|
-
// Both anchors required, so a stray "hooks" mention cannot claim the screen.
|
|
579
|
-
if (line.includes(exports.CODEX_HOOKS_REVIEW_ANCHORS[1]) && text.includes(exports.CODEX_HOOKS_REVIEW_ANCHORS[0])) {
|
|
580
|
-
return 'hooks-review';
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return null;
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Codex separator pattern
|
|
587
|
-
*/
|
|
588
|
-
exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
|
|
589
|
-
/**
|
|
590
|
-
* Codex CLI selection list footer pattern (Issue #619, #622)
|
|
591
|
-
* Detects Codex CLI's interactive selection prompts that use arrow key
|
|
592
|
-
* navigation (e.g., /model command's model and reasoning level selection steps).
|
|
593
|
-
*
|
|
594
|
-
* Matches:
|
|
595
|
-
* - Step 1 (model selection): "Press enter to select reasoning effort, or esc to dismiss."
|
|
596
|
-
* - Step 2 (reasoning level): "Press enter to confirm or esc to go back"
|
|
597
|
-
* - Legacy: "press enter to confirm or esc to cancel"
|
|
598
|
-
* Does NOT match: "press number to confirm" (handled by detectMultipleChoicePrompt)
|
|
599
|
-
*
|
|
600
|
-
* The distinction is important: "press enter to confirm/select" indicates an arrow-key
|
|
601
|
-
* selection list (NavigationButtons), while "press number to confirm" indicates
|
|
602
|
-
* a numbered prompt (PromptPanel with buttons).
|
|
603
|
-
*/
|
|
604
|
-
exports.CODEX_SELECTION_LIST_PATTERN = /press\s+enter\s+to\s+(?:confirm|select)/i;
|
|
605
|
-
/**
|
|
606
|
-
* Codex CLI approval-request footer pattern (Issue #1628).
|
|
607
|
-
*
|
|
608
|
-
* Codex renders an approval request ("Would you like to run the following
|
|
609
|
-
* command?" / "Would you like to make the following edits?") with the SAME
|
|
610
|
-
* "Press enter to confirm" footer as a `/model`-style menu, which is why
|
|
611
|
-
* CODEX_SELECTION_LIST_PATTERN swallows it. The two differ in the escape verb:
|
|
612
|
-
* an approval request can be *cancelled* (it is the agent asking the human for
|
|
613
|
-
* permission), a menu can only be *gone back* from.
|
|
614
|
-
*
|
|
615
|
-
* Measured on codex-cli 0.146.0 (five consecutive live approval frames captured
|
|
616
|
-
* from a real session, plus two live `/model` picker frames):
|
|
617
|
-
* - approval : "Press enter to confirm or esc to cancel"
|
|
618
|
-
* - /model : "Press enter to confirm or esc to go back"
|
|
619
|
-
* - /model : "Press enter to select reasoning effort, or esc to dismiss."
|
|
620
|
-
*
|
|
621
|
-
* Used only as one of two OR'd approval signals (see isCodexApprovalRequest in
|
|
622
|
-
* status-detector.ts); the other is an interrogative question line, so a future
|
|
623
|
-
* rewording of either signal alone does not reopen Issue #1628.
|
|
624
|
-
*
|
|
625
|
-
* No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
|
|
626
|
-
*/
|
|
627
|
-
exports.CODEX_APPROVAL_FOOTER_PATTERN = /esc\s+to\s+cancel/i;
|
|
628
|
-
/**
|
|
629
|
-
* Codex CLI tool-call approval FORM footer (Issue #2609).
|
|
630
|
-
*
|
|
631
|
-
* Codex asks for some tool calls through a form rather than the classic
|
|
632
|
-
* approval list, and closes it with a different sentence. Measured on a live
|
|
633
|
-
* Browser use approval (2026-09-17, twice in one session):
|
|
634
|
-
*
|
|
635
|
-
* ```text
|
|
636
|
-
* • Calling 修正前の専用GUIを開く
|
|
637
|
-
*
|
|
638
|
-
* Field 1/1
|
|
639
|
-
* Allow Browser use to access http://127.0.0.1:60311?
|
|
640
|
-
*
|
|
641
|
-
* origin: http://127.0.0.1:60311
|
|
642
|
-
*
|
|
643
|
-
* › 1. Allow Run the tool and continue.
|
|
644
|
-
* 2. Always allow Run the tool and remember this choice for future tool calls.
|
|
645
|
-
* 3. Cancel Cancel this tool call
|
|
646
|
-
* enter to submit | esc to cancel
|
|
647
|
-
* ```
|
|
648
|
-
*
|
|
649
|
-
* No `press enter to confirm/select`, so {@link CODEX_SELECTION_LIST_PATTERN}
|
|
650
|
-
* misses it and `detectCodexDialog` returned null for a dialog the status
|
|
651
|
-
* detector reported as `waiting` — which `/prompt-response` read as
|
|
652
|
-
* `prompt_no_longer_active` and Auto-Yes as `unclassified-frame`.
|
|
653
|
-
*
|
|
654
|
-
* Deliberately a separate constant rather than another alternative in
|
|
655
|
-
* CODEX_SELECTION_LIST_PATTERN: that one also drives `detect.ts` branch 0.8, and
|
|
656
|
-
* this footer is consumed ONLY by `detectCodexDialog`'s entry gate. It is the
|
|
657
|
-
* whole measured row and nothing looser — `/m` + `^…$` against the trimmed
|
|
658
|
-
* footer rows `findNumberedOptionBlock` returns, so a sentence that merely
|
|
659
|
-
* contains "enter to submit", or "esc to cancel" on its own (the #1928 mutation
|
|
660
|
-
* that rewords the approval footer), still does not vouch for a block.
|
|
661
|
-
*
|
|
662
|
-
* No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
|
|
663
|
-
*/
|
|
664
|
-
exports.CODEX_FORM_SUBMIT_FOOTER_PATTERN = /^enter\s+to\s+submit\s*\|\s*esc\s+to\s+cancel$/im;
|
|
665
|
-
/**
|
|
666
|
-
* Codex CLI 0.157 picker footer pattern (Issue #2868).
|
|
667
|
-
*
|
|
668
|
-
* codex-cli 0.157.1 retitled `/model` ("Select Model and Effort") and replaced
|
|
669
|
-
* its "Press enter to confirm or esc to go back" footer with the terse
|
|
670
|
-
* `enter select · esc back` row (measured: `tests/fixtures/codex-dialogs-0157/`).
|
|
671
|
-
* CODEX_SELECTION_LIST_PATTERN no longer matched, so branch 0.8 missed the
|
|
672
|
-
* picker and the dialog entry gate refused the answer (`prompt_no_longer_active`).
|
|
673
|
-
*
|
|
674
|
-
* Same construction as CODEX_FORM_SUBMIT_FOOTER_PATTERN: the whole measured row
|
|
675
|
-
* (`/m` + `^…$`), tested only against a trimmed single footer row — never a
|
|
676
|
-
* window — so a transcript quoting the words does not vouch for anything.
|
|
677
|
-
* CODEX_SELECTION_LIST_PATTERN is deliberately left as is (#2774 / #2841).
|
|
678
|
-
*
|
|
679
|
-
* No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
|
|
680
|
-
*/
|
|
681
|
-
exports.CODEX_PICKER_FOOTER_PATTERN = /^enter\s+select\s*·\s*esc\s+back$/im;
|
|
682
|
-
/**
|
|
683
|
-
* Codex CLI 0.157 effort-picker footer pattern (Issue #2868).
|
|
684
|
-
*
|
|
685
|
-
* The second `/model` step ("Select Reasoning Level for …") closes with
|
|
686
|
-
* `enter default · s session · esc back` (measured:
|
|
687
|
-
* `tests/fixtures/codex-dialogs-0157/model-picker-effort.txt`). Same
|
|
688
|
-
* construction and rules as CODEX_PICKER_FOOTER_PATTERN: the whole row, tested
|
|
689
|
-
* only against a single trimmed footer row.
|
|
690
|
-
*
|
|
691
|
-
* No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
|
|
692
|
-
*/
|
|
693
|
-
exports.CODEX_EFFORT_PICKER_FOOTER_PATTERN = /^enter\s+default\s*·\s*s\s+session\s*·\s*esc\s+back$/im;
|
|
694
|
-
/**
|
|
695
|
-
* Codex CLI pager / edit-previous (transcript) mode footer pattern (Issue #1017)
|
|
696
|
-
*
|
|
697
|
-
* When Codex enters its transcript pager / "edit previous message" mode, the
|
|
698
|
-
* bottom of the frame shows scroll / edit key hints INSTEAD of the usual
|
|
699
|
-
* "model · N% left · path" status bar, e.g.:
|
|
700
|
-
* "↑/↓ to scroll pgup/pgdn to page home/end to jump"
|
|
701
|
-
* "q to quit esc/← to edit prev → to edit next enter to edit message"
|
|
702
|
-
* together with a scroll-percentage separator ("─ N% ─", NOT "N% left ·").
|
|
703
|
-
*
|
|
704
|
-
* Neither CODEX_SELECTION_LIST_PATTERN (which needs "press enter to
|
|
705
|
-
* confirm/select") nor the "N% left ·" status-bar boundary logic in
|
|
706
|
-
* status-detector.ts fires here, so the read-only TerminalDisplay is left with no
|
|
707
|
-
* way to scroll or escape (the reported bug). This pattern recognizes the pager
|
|
708
|
-
* footer directly — independent of the status bar — so the selection window
|
|
709
|
-
* (NavigationButtons) can be rendered.
|
|
710
|
-
*
|
|
711
|
-
* Matches any of the pager-specific hints (either footer line is sufficient):
|
|
712
|
-
* - scroll/page/jump hints: "↑/↓ to scroll" / "pgup/pgdn to page" / "home/end to jump"
|
|
713
|
-
* - edit-previous hints: "esc/← to edit prev" / "→ to edit next" / "enter to edit message"
|
|
714
|
-
* The two branches are independent so a mangled unicode-arrow footer line is still
|
|
715
|
-
* caught by the ASCII "to edit prev/next/message" and "pgup/pgdn"/"home/end" hints.
|
|
716
|
-
*
|
|
717
|
-
* Does NOT match the genuine "/model" selection list ("press enter to select") —
|
|
718
|
-
* that footer has no scroll/page/jump or edit-prev/next/message hint — so the
|
|
719
|
-
* existing CODEX_SELECTION_LIST_PATTERN path is unaffected (no regression).
|
|
720
|
-
*
|
|
721
|
-
* No /g flag (S4-5: keeps test() stateless). No nested quantifiers (SEC4-001: ReDoS-safe).
|
|
722
|
-
*/
|
|
723
|
-
exports.CODEX_PAGER_FOOTER_PATTERN = /(?:↑\/↓|pgup\/pgdn|home\/end)\s+to\s+(?:scroll|page|jump)|to\s+edit\s+(?:prev|next|message)/i;
|
|
724
|
-
/**
|
|
725
|
-
* Codex CLI status-bar line pattern (Issue #1150)
|
|
726
|
-
*
|
|
727
|
-
* The Codex TUI renders a status bar as the bottom-most content line, just above
|
|
728
|
-
* the input area. status-detector.ts uses it as the footer boundary that separates
|
|
729
|
-
* the conversation content (thinking indicators / idle "›" prompt) from the input
|
|
730
|
-
* area, so both the selection-list check (priority 0.8) and the running/idle check
|
|
731
|
-
* (priority 2.7) depend on locating it.
|
|
732
|
-
*
|
|
733
|
-
* The format drifted across Codex versions — the "N% left ·" token was DROPPED in
|
|
734
|
-
* v0.141 (gpt-5.5), which is exactly what broke Issue #1150:
|
|
735
|
-
* - v0.141 (gpt-5.5): "gpt-5.5 xhigh · ~/share/work/github_kewton/commandmate-issue-947"
|
|
736
|
-
* - legacy (gpt-5.4): "gpt-5.4 high · 21% left · ~/share/work/..."
|
|
737
|
-
* - legacy (o4-mini): " o4-mini 50% left · /path/to/project"
|
|
738
|
-
*
|
|
739
|
-
* The previous pattern required "\d+%\s+left\s+·", so v0.141 bars never matched:
|
|
740
|
-
* the footer boundary stayed -1 and the whole Codex running/idle block was skipped,
|
|
741
|
-
* leaving generating sessions misreported as `ready` (static green dot, no glow).
|
|
742
|
-
*
|
|
743
|
-
* Version-independent anchor: a leading model token, a middle-dot "·" separator,
|
|
744
|
-
* and a filesystem path ("~/…" or "/…") at the END of the line. Any "N% left ·"
|
|
745
|
-
* segment (legacy) is absorbed by ".*·" before the trailing path. Requiring the
|
|
746
|
-
* trailing path keeps this Codex-specific (guarded by cliToolId === 'codex' in
|
|
747
|
-
* status-detector.ts) and stops ordinary conversation lines that merely contain a
|
|
748
|
-
* "·" from being mistaken for the status bar.
|
|
749
|
-
*
|
|
750
|
-
* Single-line by design (no /m, no /g): status-detector.ts tests it per content
|
|
751
|
-
* line. No nested quantifiers (ReDoS-safe; adjacent greedy quantifiers only).
|
|
752
|
-
*/
|
|
753
|
-
exports.CODEX_STATUS_BAR_PATTERN = /^\s*\S.*·\s*~?\/\S*\s*$/;
|
|
754
|
-
/**
|
|
755
|
-
* Codex status bar with something drawn AFTER the path (Issue #2818) — the
|
|
756
|
-
* second shape `findCodexFooterBoundary` in `tools/codex/detect.ts` accepts.
|
|
757
|
-
*
|
|
758
|
-
* From codex 0.154.0 on, the bar stops ending in the path once the first turn
|
|
759
|
-
* has named the thread, and Plan mode adds a right-aligned badge:
|
|
760
|
-
*
|
|
761
|
-
* ```text
|
|
762
|
-
* gpt-5.6-terra low · /private/var/…/repo · Run touch probe.txt
|
|
763
|
-
* gpt-6-astra medium · ~/uat3-…/sandbox-repo Plan mode (shift+tab to cycle)
|
|
764
|
-
* ```
|
|
765
|
-
*
|
|
766
|
-
* {@link CODEX_STATUS_BAR_PATTERN} wants the path last, so every such frame had
|
|
767
|
-
* no boundary and fell to the detector's bar-independent branch D (#1150's
|
|
768
|
-
* safety net). That branch reads the 15-row tail, and on an idle frame the tail
|
|
769
|
-
* still holds the finished turn's `• Ran …` record — which is how an idle
|
|
770
|
-
* session read `running` (#2808's `idle-after-declined-approval.txt`; #2818
|
|
771
|
-
* reproduced it on a turn that simply ran one command and answered).
|
|
772
|
-
*
|
|
773
|
-
* Kept a SEPARATE pattern rather than a widened {@link CODEX_STATUS_BAR_PATTERN}
|
|
774
|
-
* so the change stays on the one reader it was measured for: that pattern is
|
|
775
|
-
* also the stripped-capture landmark of {@link findCodexChromeStart} and the
|
|
776
|
-
* value reader's first test in `model-info-extractor.ts`, neither of which
|
|
777
|
-
* this Issue measured.
|
|
778
|
-
*
|
|
779
|
-
* The shape is the one `CODEX_STATUS_BAR_WITH_TRAILER_PATTERN`
|
|
780
|
-
* (`model-info-extractor.ts`, #2592) reads values off, written out here rather
|
|
781
|
-
* than imported: that module's rule is that it must never be the reason this
|
|
782
|
-
* boundary moves. Head segment, `·`, a path, then EITHER a further `·`
|
|
783
|
-
* segment OR a column gap (two spaces) before right-aligned text. `[^·]*` puts
|
|
784
|
-
* the path right after the FIRST `·`, so codex's in-flight row (`• Working (…)
|
|
785
|
-
* · 1 background terminal running · /ps to view`) is not a bar — its first `·`
|
|
786
|
-
* is followed by a count, not a path.
|
|
787
|
-
*
|
|
788
|
-
* Single-line, no /g, no nested quantifiers (ReDoS-safe), as above.
|
|
789
|
-
*/
|
|
790
|
-
exports.CODEX_TRAILED_STATUS_BAR_PATTERN = /^\s*\S[^·]*·\s*~?\/\S*(?:\s*·[^\n]*|[^\S\n]{2,}\S[^\n]*)$/;
|
|
791
|
-
/**
|
|
792
|
-
* How far above the last non-blank row {@link findCodexChromeStart} looks for the
|
|
793
|
-
* composer.
|
|
794
|
-
*
|
|
795
|
-
* Same allowance and the same reason as {@link CODEX_COMPOSER_SEARCH_ROWS}: codex
|
|
796
|
-
* pins the composer two to three rows above the bottom in every measured frame
|
|
797
|
-
* (`tests/fixtures/codex-live-2310/`), and a wider band would let the search walk
|
|
798
|
-
* into the transcript and mistake the echoed user message — drawn with the same
|
|
799
|
-
* `›` — for the input box.
|
|
800
|
-
*
|
|
801
|
-
* Wider than the 8 rows of the liveness search because 0.15x can draw notices
|
|
802
|
-
* BELOW the composer (`N background terminal running · /ps to view · /stop to
|
|
803
|
-
* close`) that the liveness search never had to step over.
|
|
804
|
-
*/
|
|
805
|
-
const CODEX_CHROME_SEARCH_ROWS = 12;
|
|
806
|
-
/**
|
|
807
|
-
* A row whose first character is codex's `›` glyph, with something after it.
|
|
808
|
-
*
|
|
809
|
-
* Deliberately not {@link CODEX_PROMPT_PATTERN}: that one is multiline and
|
|
810
|
-
* matches a bare `›`, which is right for "is a prompt on screen anywhere?" and
|
|
811
|
-
* wrong for classifying ONE row. Anchored at column 0 because all three of
|
|
812
|
-
* codex's `›` uses are, and an indented `›` in a reply is quoted text.
|
|
813
|
-
*/
|
|
814
|
-
const CODEX_CHROME_GLYPH_ROW_PATTERN = /^›(\s|$)/;
|
|
815
|
-
/**
|
|
816
|
-
* Locate the start of codex's bottom-pinned chrome within a captured pane.
|
|
817
|
-
*
|
|
818
|
-
* The fifth reader of this shape, after {@link findClaudeChromeStart} (#1289),
|
|
819
|
-
* {@link findCopilotChromeStart} (#1897), {@link findOpenCodeChromeStart}
|
|
820
|
-
* (#1911) and {@link findCommandCodeChromeStart} (#2250). codex is the tool that
|
|
821
|
-
* never got one, and Issue #2400 is the bill for that.
|
|
822
|
-
*
|
|
823
|
-
* codex renders inline and pins two rows to the bottom of a settled pane:
|
|
824
|
-
*
|
|
825
|
-
* ```text
|
|
826
|
-
* › Ask Codex to do anything ← composer (placeholder or typed text)
|
|
827
|
-
*
|
|
828
|
-
* gpt-6-astra xhigh · ~/share/work/…/CommandMate ← status bar (model · cwd)
|
|
829
|
-
* ```
|
|
830
|
-
*
|
|
831
|
-
* Below the composer codex may also draw its own notices (`N background terminal
|
|
832
|
-
* running · /ps to view`), so the boundary is "the composer row" rather than a
|
|
833
|
-
* list of footer shapes: everything from the composer down is chrome by
|
|
834
|
-
* construction, whatever codex adds there next.
|
|
835
|
-
*
|
|
836
|
-
* ## What went wrong without it (#2400)
|
|
837
|
-
*
|
|
838
|
-
* While the capture window is NOT saturated codex's extraction starts at
|
|
839
|
-
* `lastCapturedLine`, so the composer only ever mattered as the `endIndex` break
|
|
840
|
-
* — which the extraction loop already had. Once the pane outgrows
|
|
841
|
-
* `CACHE_MAX_CAPTURE_LINES` (#1670) the cursor stops being a position in the
|
|
842
|
-
* capture and `resolveExtractionStartIndex` switches to the newest echoed user
|
|
843
|
-
* prompt. With no `contentEnd`, that backwards search started at the very bottom
|
|
844
|
-
* of the pane and the first `›` it met was the COMPOSER. Extraction then began
|
|
845
|
-
* on the row after it, i.e. on the status bar, and the saved "reply" for every
|
|
846
|
-
* turn on a saturated pane was one row:
|
|
847
|
-
*
|
|
848
|
-
* ```text
|
|
849
|
-
* gpt-6-astra xhigh · ~/share/work/github_kewton/CommandAgent-develop
|
|
850
|
-
* ```
|
|
851
|
-
*
|
|
852
|
-
* Identical on every turn, so `isDuplicateResponse` then locked on it and the
|
|
853
|
-
* pane could not record another reply at all. That is #1289's defect verbatim,
|
|
854
|
-
* one tool later — the same reason `findCommandCodeChromeStart` exists.
|
|
855
|
-
*
|
|
856
|
-
* ## Why the attributes and not the placeholder text
|
|
857
|
-
*
|
|
858
|
-
* The codex branch of `findRecentUserPromptIndex` used to exclude the composer by
|
|
859
|
-
* naming its placeholders (`Implement`, `Find and fix`, `Type`, `Summarize`).
|
|
860
|
-
* Those are codex 0.1x wording, and 0.15x draws `Ask Codex to do anything`, so
|
|
861
|
-
* the list silently stopped matching the thing it was written for. Issue #2310
|
|
862
|
-
* measured what actually separates codex's three uses of `›` (U+203A), and it is
|
|
863
|
-
* the SGR attributes, not the text: the composer glyph is bold (`ESC[1m›`), a
|
|
864
|
-
* transcript echo is dim (`ESC[1;2m›`), a dialog option carries a bold label or
|
|
865
|
-
* a label drawn in the glyph's own colour (one span — since #2798 a coloured
|
|
866
|
-
* glyph alone is not enough, because 0.155.1 colours the composer's glyph too).
|
|
867
|
-
* {@link readCodexGlyphRowKind} is that measurement, and this reader is one of
|
|
868
|
-
* its callers.
|
|
869
|
-
*
|
|
870
|
-
* `-1` is returned for a frame whose bottom-most `›` is an option row: codex
|
|
871
|
-
* replaces the composer with the dialog, so there is no chrome to trim and the
|
|
872
|
-
* caller resolves the frame on the prompt path instead.
|
|
873
|
-
*
|
|
874
|
-
* ## The stripped-capture fallback
|
|
875
|
-
*
|
|
876
|
-
* Auto-Yes hands the detection layer a capture that has already been through
|
|
877
|
-
* `stripAnsi`, and there every `›` is the same byte — {@link
|
|
878
|
-
* readCodexGlyphRowKind} answers `null` on purpose rather than guessing. This
|
|
879
|
-
* reader still has to answer for those frames, so it falls back to the one
|
|
880
|
-
* structural landmark codex pins BELOW the composer and nowhere else: the status
|
|
881
|
-
* bar ({@link CODEX_STATUS_BAR_PATTERN}, `model · /path`). Requiring it means a
|
|
882
|
-
* frame with no bar — a pane mid-redraw, an overlay — yields `-1` and the
|
|
883
|
-
* pre-#2400 reading, which is the direction that costs nothing.
|
|
884
|
-
*
|
|
885
|
-
* @param lines - Captured pane lines, ANSI-bearing or not; trailing blanks tolerated
|
|
886
|
-
* @returns Index of the composer row, or -1 when no composer chrome is present
|
|
887
|
-
*/
|
|
888
|
-
function findCodexChromeStart(lines) {
|
|
889
|
-
let lastRow = lines.length - 1;
|
|
890
|
-
while (lastRow >= 0 && (0, ansi_1.stripAnsi)(lines[lastRow]).trim() === '')
|
|
891
|
-
lastRow--;
|
|
892
|
-
if (lastRow < 0)
|
|
893
|
-
return -1;
|
|
894
|
-
// Bottom-most `›` row within the band codex reserves for its chrome. Anything
|
|
895
|
-
// further up is transcript, and latching onto an echo there would cut the
|
|
896
|
-
// reply this whole reader exists to keep.
|
|
897
|
-
let glyphRow = -1;
|
|
898
|
-
for (let i = lastRow; i >= Math.max(0, lastRow - CODEX_CHROME_SEARCH_ROWS); i--) {
|
|
899
|
-
if (CODEX_CHROME_GLYPH_ROW_PATTERN.test((0, ansi_1.stripAnsi)(lines[i]))) {
|
|
900
|
-
glyphRow = i;
|
|
901
|
-
break;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (glyphRow < 0)
|
|
905
|
-
return -1;
|
|
906
|
-
const kind = (0, cli_patterns_1.readCodexGlyphRowKind)(lines[glyphRow]);
|
|
907
|
-
if (kind === 'composer')
|
|
908
|
-
return glyphRow;
|
|
909
|
-
// A dialog is up (no composer drawn) or the bottom-most `›` is a transcript
|
|
910
|
-
// echo mid-redraw. Neither is chrome to trim.
|
|
911
|
-
if (kind !== null)
|
|
912
|
-
return -1;
|
|
913
|
-
// Stripped capture: no attributes to read. Accept the row as the composer only
|
|
914
|
-
// when codex's status bar is drawn below it, which is where it always sits and
|
|
915
|
-
// where a transcript echo can never be.
|
|
916
|
-
for (let i = glyphRow + 1; i <= lastRow; i++) {
|
|
917
|
-
if (exports.CODEX_STATUS_BAR_PATTERN.test((0, ansi_1.stripAnsi)(lines[i])))
|
|
918
|
-
return glyphRow;
|
|
919
|
-
}
|
|
920
|
-
return -1;
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* The shape of a codex row that could be the echo of a message the user sent.
|
|
924
|
-
*
|
|
925
|
-
* Shape only — `›` at column 0 with text after it — which all three of codex's
|
|
926
|
-
* `›` uses share. {@link findCodexUserEchoIndex} is what tells them apart.
|
|
927
|
-
*/
|
|
928
|
-
exports.CODEX_USER_ECHO_PATTERN = /^›\s+\S/;
|
|
929
|
-
/**
|
|
930
|
-
* Find the newest transcript echo of an operator message in a codex capture.
|
|
931
|
-
*
|
|
932
|
-
* The reader `findRecentUserPromptIndex` anchors codex turns on (Issue #2400).
|
|
933
|
-
* It replaces a negative lookahead over composer placeholder strings
|
|
934
|
-
* (`(?!Implement|Find and fix|Type|Summarize)`) written against codex 0.1x:
|
|
935
|
-
* 0.15x draws `Ask Codex to do anything`, so the guard matched nothing it was
|
|
936
|
-
* written for. The composer became the newest "echo", and on a saturated pane —
|
|
937
|
-
* the one path where this anchor decides where extraction STARTS (#1670) — the
|
|
938
|
-
* reply saved for every turn was the single status-bar row below it.
|
|
939
|
-
*
|
|
940
|
-
* ## What the attributes can and cannot separate
|
|
941
|
-
*
|
|
942
|
-
* #2310 measured the three uses of `›` and this reader adds the fourth reading
|
|
943
|
-
* they left open, captured for #2400 on codex-cli 0.153.4
|
|
944
|
-
* (`tests/fixtures/codex-live-2310/turn-submitted-no-status.txt`): the echo of
|
|
945
|
-
* a message the operator has JUST submitted is drawn `ESC[1m› ESC[0m<text>` —
|
|
946
|
-
* bold glyph, plain label — and only becomes the dim `ESC[1;2m› ` of the
|
|
947
|
-
* measured frames once the turn settles. That shape is indistinguishable from a
|
|
948
|
-
* composer holding typed text, so no per-row attribute rule can separate them.
|
|
949
|
-
*
|
|
950
|
-
* What separates them is position, which codex's layout fixes: the composer is
|
|
951
|
-
* the BOTTOM-MOST `›` row of a frame. So the reader takes the boundary from its
|
|
952
|
-
* caller:
|
|
953
|
-
*
|
|
954
|
-
* - `composerTrimmed` — `findCodexChromeStart` located the chrome and `lines`
|
|
955
|
-
* has already been cut above it, so every `›` row left is transcript and the
|
|
956
|
-
* newest one wins outright.
|
|
957
|
-
* - otherwise — the composer may still be the bottom-most `›` row, so the first
|
|
958
|
-
* candidate is stepped over. This is the structural spelling of the guard the
|
|
959
|
-
* placeholder list used to be, and unlike that list it cannot go stale.
|
|
960
|
-
*
|
|
961
|
-
* Dialog option rows are refused wherever they appear: codex renders inline, so
|
|
962
|
-
* a dialog answered minutes ago is still in the scrollback with its options
|
|
963
|
-
* intact (#1160), and anchoring on one would cut the reply mid-way.
|
|
964
|
-
*
|
|
965
|
-
* @param lines - Captured pane rows, ANSI intact where the caller has it
|
|
966
|
-
* @param contentEnd - Exclusive end of the conversation region
|
|
967
|
-
* @param windowSize - How many rows above `contentEnd` to search
|
|
968
|
-
* @param composerTrimmed - Whether `contentEnd` already excludes the composer
|
|
969
|
-
* @returns Index of the newest echo row, or -1 when none is in the window
|
|
970
|
-
*/
|
|
971
|
-
function findCodexUserEchoIndex(lines, contentEnd, windowSize, composerTrimmed) {
|
|
972
|
-
let composerHandled = composerTrimmed;
|
|
973
|
-
for (let i = Math.min(contentEnd, lines.length) - 1; i >= Math.max(0, contentEnd - windowSize); i--) {
|
|
974
|
-
if (!exports.CODEX_USER_ECHO_PATTERN.test((0, ansi_1.stripAnsi)(lines[i])))
|
|
975
|
-
continue;
|
|
976
|
-
const kind = (0, cli_patterns_1.readCodexGlyphRowKind)(lines[i]);
|
|
977
|
-
// An option row of a dialog still sitting in the scrollback.
|
|
978
|
-
if (kind === 'option')
|
|
979
|
-
continue;
|
|
980
|
-
// Positively an echo: codex has settled the row and drawn its glyph dim.
|
|
981
|
-
if (kind === 'transcript-echo')
|
|
982
|
-
return i;
|
|
983
|
-
// `composer` (bold glyph) or `null` (an ANSI-stripped capture, where all
|
|
984
|
-
// three uses are one byte). Either could be the input box, and the input box
|
|
985
|
-
// is always the bottom-most `›` row.
|
|
986
|
-
if (!composerHandled) {
|
|
987
|
-
composerHandled = true;
|
|
988
|
-
continue;
|
|
989
|
-
}
|
|
990
|
-
return i;
|
|
991
|
-
}
|
|
992
|
-
return -1;
|
|
993
|
-
}
|
|
994
|
-
/**
|
|
995
|
-
* Pasted text pattern
|
|
996
|
-
*
|
|
997
|
-
* Claude CLI displays this when it detects multi-line text paste in the
|
|
998
|
-
* ink-based TextInput. The pattern matches the folded display format.
|
|
999
|
-
*
|
|
1000
|
-
* @example "[Pasted text #1 +46 lines]"
|
|
1001
|
-
* @see Issue #212, #163
|
|
1002
|
-
* @designNote PASTE-001: Pattern matches the start of the indicator only.
|
|
1003
|
-
* The line count (+XX lines) is variable, so we match the fixed prefix
|
|
1004
|
-
* to minimize false negatives. False positive risk is low because
|
|
1005
|
-
* "[Pasted text #" is a unique format generated by Claude CLI's ink renderer.
|
|
1006
|
-
* @designNote PASTE-001-FP (SF-S4-002): When used in skipPatterns,
|
|
1007
|
-
* line-level matching could filter legitimate response lines if Claude's
|
|
1008
|
-
* answer text happens to contain "[Pasted text #". This is unlikely and
|
|
1009
|
-
* acceptable -- only the affected line would be lost.
|
|
1010
|
-
*/
|
|
1011
|
-
exports.PASTED_TEXT_PATTERN = /\[Pasted text #\d+/;
|
|
1012
|
-
/**
|
|
1013
|
-
* Pasted text detection delay (milliseconds)
|
|
1014
|
-
*
|
|
1015
|
-
* Wait time after sendKeys for tmux buffer to reflect [Pasted text] display.
|
|
1016
|
-
*
|
|
1017
|
-
* @see Issue #212
|
|
1018
|
-
* @designNote PASTE-002: 500ms is the empirically measured time for
|
|
1019
|
-
* Claude CLI's ink rendering to complete. capturePane({ startLine: -10 })
|
|
1020
|
-
* reads only the last 10 lines since [Pasted text] appears in the most
|
|
1021
|
-
* recent few lines.
|
|
1022
|
-
*/
|
|
1023
|
-
exports.PASTED_TEXT_DETECT_DELAY = 500;
|
|
1024
|
-
/**
|
|
1025
|
-
* Pasted text detection max retries
|
|
1026
|
-
*
|
|
1027
|
-
* @see Issue #212
|
|
1028
|
-
* @designNote PASTE-003: 3 retries x 500ms = max 1500ms additional delay.
|
|
1029
|
-
* Typically resolves on the first attempt (+500ms).
|
|
1030
|
-
*/
|
|
1031
|
-
exports.MAX_PASTED_TEXT_RETRIES = 3;
|
|
1032
|
-
/**
|
|
1033
|
-
* Gemini interactive REPL prompt pattern
|
|
1034
|
-
* Gemini CLI shows a `>` or `❯` prompt when waiting for user input in interactive mode.
|
|
1035
|
-
*
|
|
1036
|
-
* Two branches (Issue #386):
|
|
1037
|
-
* - Branch 1: `^[>❯]\s*$` -- bare prompt character (empty input line)
|
|
1038
|
-
* - Branch 2: `^\s*[>❯]\s+Type your message.*$` -- new-format prompt with placeholder text
|
|
1039
|
-
* (e.g., " > Type your message or @path/to/file"). Leading whitespace is allowed
|
|
1040
|
-
* because tmux capture-pane output may include padding.
|
|
1041
|
-
*
|
|
1042
|
-
* Branch 2 requires "Type your message" after the indicator to avoid false positives
|
|
1043
|
-
* on quoted response lines (e.g., "> some quoted text").
|
|
1044
|
-
*
|
|
1045
|
-
* @see CLAUDE_PROMPT_PATTERN for similar dual-format matching approach
|
|
1046
|
-
*/
|
|
1047
|
-
// [S4-5] /g flag prohibited: would make test() stateful
|
|
1048
|
-
exports.GEMINI_PROMPT_PATTERN = /^[>❯]\s*$|^\s*[>❯]\s+Type your message.*$/m;
|
|
1049
|
-
/**
|
|
1050
|
-
* Gemini thinking/processing pattern
|
|
1051
|
-
* Gemini CLI shows braille spinner characters and status text while processing.
|
|
1052
|
-
*/
|
|
1053
|
-
exports.GEMINI_THINKING_PATTERN = /[\u2800-\u28FF]|Thinking\.\.\./;
|
|
1054
|
-
/**
|
|
1055
|
-
* OpenCode prompt pattern (Issue #379)
|
|
1056
|
-
* OpenCode TUI shows "Ask anything..." in the input area when waiting for user input.
|
|
1057
|
-
* Unlike Claude/Codex (which use > or ❯), OpenCode uses a text-based prompt indicator.
|
|
1058
|
-
*/
|
|
1059
|
-
exports.OPENCODE_PROMPT_PATTERN = /Ask anything(?:\.\.\.|\u2026)/;
|
|
1060
|
-
/**
|
|
1061
|
-
* OpenCode idle composer pattern (Issue #1883).
|
|
1062
|
-
*
|
|
1063
|
-
* The `Ask anything...` placeholder as opencode actually draws it: **inside the
|
|
1064
|
-
* input box**, behind the box's own gutter (`\u2503`, or `\u2502` on a lighter
|
|
1065
|
-
* border style). Two measured facts make that row positive evidence that the
|
|
1066
|
-
* composer is empty, rather than the mere absence of a busy marker (design
|
|
1067
|
-
* principle D1 in `docs/design/multi-agent-state-architecture.md`):
|
|
1068
|
-
*
|
|
1069
|
-
* - opencode paints the placeholder **only while the input buffer is empty**.
|
|
1070
|
-
* The first typed character replaces the whole row — measured live on
|
|
1071
|
-
* opencode 1.18.20, pane 80x200 (`opencode-live-1883/composer-residual.txt`
|
|
1072
|
-
* holds `\u2503 echo PREFILLED` where the idle frame holds the placeholder).
|
|
1073
|
-
* - the gutter says the row belongs to the input box. `Ask anything...` printed
|
|
1074
|
-
* in a response body has no gutter, and reading that as an idle composer is
|
|
1075
|
-
* the "the phrase is on screen somewhere" inference D1 forbids.
|
|
1076
|
-
*
|
|
1077
|
-
* **Match this against the ANSI-stripped frame BEFORE {@link stripBoxDrawing}**,
|
|
1078
|
-
* which strips the very gutter this pattern anchors on.
|
|
1079
|
-
*
|
|
1080
|
-
* The whitespace runs are `[^\S\n]` (horizontal only) on purpose: plain `\s`
|
|
1081
|
-
* crosses newlines under the `m` flag, which let the gutter of one row pair up
|
|
1082
|
-
* with the phrase several rows below it and matched frames that hold no
|
|
1083
|
-
* composer at all (measured on `phrase-in-response.txt`).
|
|
1084
|
-
*
|
|
1085
|
-
* {@link OPENCODE_PROMPT_PATTERN} stays as it is: `response-checker` and
|
|
1086
|
-
* `OPENCODE_SKIP_PATTERNS` want the bare phrase wherever it lands, because they
|
|
1087
|
-
* are deleting the row from an extracted response, not judging a session.
|
|
1088
|
-
*
|
|
1089
|
-
* opencode 1.18.31 以降は見本文を U+2026(`…`)で描く。ASCII の `...` も実行ファイルに
|
|
1090
|
-
* 残っているので両方を受け付ける(Issue #2915、2026-09-28 に実行ファイルから確認)。
|
|
1091
|
-
*/
|
|
1092
|
-
exports.OPENCODE_IDLE_COMPOSER_PATTERN = /^[^\S\n]*[\u2502\u2503][^\S\n]*Ask anything(?:\.\.\.|\u2026)/m;
|
|
1093
|
-
/**
|
|
1094
|
-
* OpenCode prompt pattern after response completion (Issue #379)
|
|
1095
|
-
* Shows "tab agents ctrl+p commands" in the TUI status bar after a response finishes.
|
|
1096
|
-
* Used as extraction stop condition in response-poller.ts [D2-003].
|
|
1097
|
-
*/
|
|
1098
|
-
exports.OPENCODE_PROMPT_AFTER_RESPONSE = /tab agents\s+ctrl\+p commands/;
|
|
1099
|
-
/**
|
|
1100
|
-
* OpenCode thinking/processing pattern (Issue #379)
|
|
1101
|
-
* OpenCode TUI shows "Thinking:" prefix while the Ollama model is generating a response.
|
|
1102
|
-
* Used by detectThinking() to determine if the tool is actively processing.
|
|
1103
|
-
*/
|
|
1104
|
-
exports.OPENCODE_THINKING_PATTERN = /Thinking:/;
|
|
1105
|
-
/**
|
|
1106
|
-
* OpenCode loading indicator pattern (Issue #379)
|
|
1107
|
-
* Shows a series of 4+ filled square characters (U+2B1D) during initial loading/model warm-up.
|
|
1108
|
-
* Filtered from response extraction via OPENCODE_SKIP_PATTERNS.
|
|
1109
|
-
*/
|
|
1110
|
-
exports.OPENCODE_LOADING_PATTERN = /\u2B1D{4,}/;
|
|
1111
|
-
/**
|
|
1112
|
-
* OpenCode's Build summary LINE, in either of the two forms it is drawn in
|
|
1113
|
-
* (Issue #379, corrected by Issue #1893).
|
|
1114
|
-
*
|
|
1115
|
-
* **This is a line filter, not completion evidence.** It matches
|
|
1116
|
-
* `▣ <Action> · <model>` with the duration OPTIONAL, and opencode 1.18 draws
|
|
1117
|
-
* that duration-less form on a step that is still in flight -- so a frame this
|
|
1118
|
-
* pattern matches may be mid-turn, waiting on a permission dialog, or aborted.
|
|
1119
|
-
* Use {@link OPENCODE_TURN_COMPLETE_PATTERN} to decide that a turn has finished.
|
|
1120
|
-
*
|
|
1121
|
-
* The docstring that stood here until #1893 claimed the opposite ("short
|
|
1122
|
-
* responses may omit the timing portion"). Measured against opencode 1.18.21 at
|
|
1123
|
-
* the production 80x200 geometry, that is wrong in both directions:
|
|
1124
|
-
*
|
|
1125
|
-
* - a 2.3-second answer still carries its duration
|
|
1126
|
-
* (`▣ Build · GPT-5.6 Luna · 2.3s`, `opencode-live-1893/turn-complete-short.txt`),
|
|
1127
|
-
* so no completed turn needs the duration-less branch;
|
|
1128
|
-
* - the duration-less form is what opencode leaves on screen while a tool call
|
|
1129
|
-
* waits for permission and after a rejected one
|
|
1130
|
-
* (`opencode-live-1893/permission-bash.txt`, `…/turn-aborted-no-duration.txt`).
|
|
1131
|
-
*
|
|
1132
|
-
* Kept loose because three callers want the LINE rather than the verdict:
|
|
1133
|
-
* `tui-accumulator.ts`, `response-cleaner.ts` and `polling/response-checker.ts`
|
|
1134
|
-
* all use it to drop the summary row from an extracted response, and the
|
|
1135
|
-
* mid-step row has to be dropped too. #1911 removed the one caller that used it
|
|
1136
|
-
* as a turn BOUNDARY rather than a line filter (the "second-to-last ▣" anchor,
|
|
1137
|
-
* replaced by {@link findOpenCodeUserEchoEnd}); the name is left alone because
|
|
1138
|
-
* the remaining three callers all want the line.
|
|
1139
|
-
*/
|
|
1140
|
-
exports.OPENCODE_RESPONSE_COMPLETE = /\u25A3\s+\w+\s+·\s+\S+(?:\s+·\s+(?:[\d]+h\s*)?(?:[\d]+m\s*)?[\d.]+s)?/;
|
|
1141
|
-
/**
|
|
1142
|
-
* OpenCode's finished-turn marker: the Build summary line WITH its duration
|
|
1143
|
-
* (Issue #1893).
|
|
1144
|
-
*
|
|
1145
|
-
* `▣ Build · GPT-5.6 Luna · 5.2s`. This is the one tool-specific completion
|
|
1146
|
-
* marker design rule D1 recognises today
|
|
1147
|
-
* (`docs/design/multi-agent-state-architecture.md` §4 D1 decision 1, item 1),
|
|
1148
|
-
* and the duration is the whole of what makes it positive evidence: opencode
|
|
1149
|
-
* prints the same row without a duration while a step is still open, which is
|
|
1150
|
-
* how a session parked on a permission dialog was published as
|
|
1151
|
-
* `ready`/`opencode_response_complete` (#1893) and how `isOpenCodeComplete`
|
|
1152
|
-
* saved the dialog body as if it were an answer.
|
|
1153
|
-
*
|
|
1154
|
-
* The model segment is `[^·\n]+` rather than `\S+` because real model names
|
|
1155
|
-
* carry spaces (`GPT-5.6 Luna`): with `\S+` the optional-duration group of
|
|
1156
|
-
* {@link OPENCODE_RESPONSE_COMPLETE} could never reach the duration on a
|
|
1157
|
-
* two-word model, so "with duration" and "without duration" were the same match
|
|
1158
|
-
* there. Excluding the middle dot rather than allowing anything keeps the
|
|
1159
|
-
* quantifier unable to swallow its own delimiter (no nested/ambiguous
|
|
1160
|
-
* quantifier -- ReDoS safe), and `.`/`[^·\n]` never cross a line without the
|
|
1161
|
-
* `m` flag, so the duration has to be on the marker's own row.
|
|
1162
|
-
*
|
|
1163
|
-
* Durations observed: `2.3s`, `5.2s`, `45.2s`; the `Nh`/`Nm` prefixes are
|
|
1164
|
-
* inherited from the #379 pattern and kept for long turns.
|
|
1165
|
-
*/
|
|
1166
|
-
exports.OPENCODE_TURN_COMPLETE_PATTERN = /\u25A3\s+\w+\s+·\s+[^·\n]+·\s+(?:\d+h\s*)?(?:\d+m\s*)?[\d.]+s/;
|
|
1167
|
-
/**
|
|
1168
|
-
* OpenCode's permission dialog, anchored on its button row (Issue #1893).
|
|
1169
|
-
*
|
|
1170
|
-
* opencode 1.18 asks for tool permission with a bottom-anchored box whose last
|
|
1171
|
-
* interactive row is a horizontal button strip:
|
|
1172
|
-
*
|
|
1173
|
-
* ```
|
|
1174
|
-
* ┃ △ Permission required
|
|
1175
|
-
* ┃ # Shell command
|
|
1176
|
-
* ┃ $ ls -la
|
|
1177
|
-
* ┃ Allow once Allow always Reject ctrl+f fullscreen ⇆ select enter con
|
|
1178
|
-
* ```
|
|
1179
|
-
*
|
|
1180
|
-
* Nothing in the detection layer saw it before #1893: it carries no number, no
|
|
1181
|
-
* `(y/n)`, and no "press enter to confirm" footer, so `detectPrompt` answers
|
|
1182
|
-
* `isPrompt: false` and the status detector fell through to the Build marker
|
|
1183
|
-
* above it. The row is matched as POSITIVE evidence that a decision is pending
|
|
1184
|
-
* (design rule D1) -- it is the affordance itself, not the absence of a busy
|
|
1185
|
-
* marker.
|
|
1186
|
-
*
|
|
1187
|
-
* **Match this against the ANSI-stripped frame BEFORE {@link stripBoxDrawing}**,
|
|
1188
|
-
* exactly like {@link OPENCODE_IDLE_COMPOSER_PATTERN}: the leading `┃` (or
|
|
1189
|
-
* `│` on a lighter border style) is what says the row belongs to the dialog box
|
|
1190
|
-
* rather than to a response body that happens to quote the labels -- the
|
|
1191
|
-
* "the phrase is on screen somewhere" inference #1883 had to remove.
|
|
1192
|
-
*
|
|
1193
|
-
* Deliberately NOT anchored on:
|
|
1194
|
-
*
|
|
1195
|
-
* - `enter confirm`, which is truncated to `enter con` at opencode's own 80
|
|
1196
|
-
* column layout (measured);
|
|
1197
|
-
* - `△ Permission required` alone, which is a heading rather than an
|
|
1198
|
-
* affordance and survives in the fullscreen (`ctrl+f`) view whose key handling
|
|
1199
|
-
* was not measured.
|
|
1200
|
-
*
|
|
1201
|
-
* The three labels are the same for the `bash` and the `edit` dialog (measured:
|
|
1202
|
-
* `permission-bash.txt`, `permission-edit.txt`), and the strip is repainted away
|
|
1203
|
-
* the moment the dialog is answered, so a matched row is never scrollback
|
|
1204
|
-
* (`turn-aborted-no-duration.txt` holds no `Allow once`).
|
|
1205
|
-
*
|
|
1206
|
-
* The whitespace runs are `[^\S\n]` (horizontal only) for the reason #1883
|
|
1207
|
-
* documents: plain `\s` crosses newlines under the `m` flag and would pair a
|
|
1208
|
-
* gutter on one row with labels several rows below it.
|
|
1209
|
-
*/
|
|
1210
|
-
exports.OPENCODE_PERMISSION_PATTERN = /^[^\S\n]*[\u2502\u2503][^\S\n]*Allow once[^\S\n]+Allow always[^\S\n]+Reject\b/m;
|
|
1211
|
-
/**
|
|
1212
|
-
* OpenCode's busy footer, in BOTH of the spellings it is drawn in (Issue #379,
|
|
1213
|
-
* widened by Issue #1894).
|
|
1214
|
-
*
|
|
1215
|
-
* opencode 1.18 needs Escape TWICE to abort a turn, and the first press does not
|
|
1216
|
-
* abort anything -- it re-labels the footer:
|
|
1217
|
-
*
|
|
1218
|
-
* ```
|
|
1219
|
-
* ⬝⬝⬝⬝⬝⬝⬝⬝ esc interrupt 6.5K (1%) · $0.00 ctrl+p commands
|
|
1220
|
-
* ⬝■■■■■■⬝ esc again to interrupt 7.2K (1%) · $0.00 ctrl+p commands
|
|
1221
|
-
* ```
|
|
1222
|
-
*
|
|
1223
|
-
* Measured on opencode 1.18.21 at the production 80x200 geometry, sampling the
|
|
1224
|
-
* footer every ~360 ms after a single Escape: the second spelling is up from
|
|
1225
|
-
* 0.31 s to 4.71 s and the row is back to `esc interrupt` at 5.07 s -- a
|
|
1226
|
-
* five-second window, exactly as long as the second-press deadline
|
|
1227
|
-
* ({@link OPENCODE_INTERRUPT_SECOND_ESCAPE_DELAY_MS} is sized against it). The
|
|
1228
|
-
* generation continues throughout; the turn ran to a natural
|
|
1229
|
-
* `▣ Build · GPT-5.6 Luna · 11.3s`, 3 runs out of 3.
|
|
1230
|
-
*
|
|
1231
|
-
* Before #1894 those five seconds matched nothing at all: `detectSessionStatus`
|
|
1232
|
-
* lost branch A and fell through to `running`/`default` while the frame was
|
|
1233
|
-
* fresh, and to `ready`/`no_recent_output` once the poller's
|
|
1234
|
-
* `lastOutputTimestamp` aged past `STALE_OUTPUT_THRESHOLD_MS` -- both of them
|
|
1235
|
-
* `statusEvidence: 'none'`, i.e. a generating session
|
|
1236
|
-
* published with no evidence and, on the second path, as FINISHED. That is the
|
|
1237
|
-
* "vocabulary changed, so `ready` came back" failure design rule D1 names
|
|
1238
|
-
* (`docs/design/multi-agent-state-architecture.md` §4 D1, row #1894), and the
|
|
1239
|
-
* row is the same positive busy evidence in either spelling.
|
|
1240
|
-
*
|
|
1241
|
-
* The optional group is `(?:again to )?` rather than a looser `.*` on purpose:
|
|
1242
|
-
* it matches the two measured strings and nothing between an `esc` and an
|
|
1243
|
-
* `interrupt` several words apart. Linear, no nested quantifiers -- ReDoS safe.
|
|
1244
|
-
*
|
|
1245
|
-
* Filtered from response extraction via OPENCODE_SKIP_PATTERNS, which shares
|
|
1246
|
-
* this constant: the widened row is dropped from a saved answer for the same
|
|
1247
|
-
* reason the narrow one was.
|
|
1248
|
-
*/
|
|
1249
|
-
exports.OPENCODE_PROCESSING_INDICATOR = /esc (?:again to )?interrupt/;
|
|
1250
|
-
/**
|
|
1251
|
-
* OpenCode's composer bottom border: ` ╹▀▀▀▀▀▀…` (Issue #1911).
|
|
1252
|
-
*
|
|
1253
|
-
* `╹` (heavy up) is the corner opencode joins the input box's `┃`
|
|
1254
|
-
* gutter to, and the `▀` run is the box's bottom edge. It is the one row of
|
|
1255
|
-
* the bottom-anchored chrome that can never appear inside a response body, which
|
|
1256
|
-
* makes it the anchor {@link findOpenCodeChromeStart} walks up from.
|
|
1257
|
-
*/
|
|
1258
|
-
exports.OPENCODE_COMPOSER_BOTTOM_BORDER = /^[^\S\n]*╹▀{4,}/;
|
|
1259
|
-
/**
|
|
1260
|
-
* A row that belongs to one of opencode's boxes, matched by its own gutter
|
|
1261
|
-
* (Issue #1911).
|
|
1262
|
-
*
|
|
1263
|
-
* opencode draws three different boxes with the same `┃` gutter (`│`
|
|
1264
|
-
* on a lighter border style): the echoed USER PROMPT in the transcript, the
|
|
1265
|
-
* COMPOSER pinned to the bottom of the pane, and the PERMISSION DIALOG that
|
|
1266
|
-
* replaces the composer. Which one a matched row belongs to is decided by where
|
|
1267
|
-
* it sits, not by what it says — see {@link findOpenCodeChromeStart} and
|
|
1268
|
-
* {@link findOpenCodeUserEchoEnd}.
|
|
1269
|
-
*
|
|
1270
|
-
* **Match against the ANSI-stripped frame BEFORE {@link stripBoxDrawing}**,
|
|
1271
|
-
* which removes the very gutter this anchors on.
|
|
1272
|
-
*/
|
|
1273
|
-
exports.OPENCODE_GUTTER_ROW_PATTERN = /^[^\S\n]*[│┃]/;
|
|
1274
|
-
/**
|
|
1275
|
-
* An echoed user prompt row: a gutter row that carries text (Issue #1911).
|
|
1276
|
-
*
|
|
1277
|
-
* The echo block opencode draws for a submitted message is a blank gutter row,
|
|
1278
|
-
* one or more gutter rows holding the message, and another blank gutter row.
|
|
1279
|
-
* This matches the middle ones.
|
|
1280
|
-
*/
|
|
1281
|
-
exports.OPENCODE_USER_ECHO_PATTERN = /^[^\S\n]*[│┃][^\S\n]*\S/;
|
|
1282
|
-
/**
|
|
1283
|
-
* The status cell of opencode's bottom footer (Issue #1911).
|
|
1284
|
-
*
|
|
1285
|
-
* Measured at the production 80x200 geometry the footer reads
|
|
1286
|
-
* `<cwd> 6.4K (1%) · $ctrl+p` / `commands` while idle and
|
|
1287
|
-
* `⬝⬝⬝⬝⬝⬝⬝⬝ esc interrupt 6.3K (1%) · $0.00 ctrl+p commands` while running:
|
|
1288
|
-
* a context-usage cell followed by the cost sigil. Neither
|
|
1289
|
-
* {@link OPENCODE_PROMPT_AFTER_RESPONSE} (`tab agents ctrl+p commands`, which
|
|
1290
|
-
* opencode only prints on the FIRST idle frame, before any turn has run) nor
|
|
1291
|
-
* {@link OPENCODE_PROCESSING_INDICATOR} covers the idle form, so this row used
|
|
1292
|
-
* to be saved as part of the assistant's reply.
|
|
1293
|
-
*
|
|
1294
|
-
* This is a SECONDARY net. The cwd that shares the row wraps over up to three
|
|
1295
|
-
* further rows that carry no signature at all, so the footer is removed
|
|
1296
|
-
* structurally by {@link findOpenCodeChromeStart}; this pattern only catches the
|
|
1297
|
-
* signed row when that boundary is not available (e.g. a caller holding a
|
|
1298
|
-
* fragment rather than a whole pane).
|
|
1299
|
-
*
|
|
1300
|
-
* Linear, no nested quantifiers — ReDoS safe.
|
|
1301
|
-
*/
|
|
1302
|
-
exports.OPENCODE_FOOTER_STATUS_PATTERN = /\d+(?:\.\d+)?[KMGT]?\s+\(\d+%\)\s+·\s+\$/;
|
|
1303
|
-
/**
|
|
1304
|
-
* Locate the start of opencode's bottom-anchored chrome within a captured pane
|
|
1305
|
-
* (Issue #1911).
|
|
1306
|
-
*
|
|
1307
|
-
* opencode runs in the alternate screen and reserves the last rows of the pane
|
|
1308
|
-
* for chrome that is never transcript content:
|
|
1309
|
-
*
|
|
1310
|
-
* ```
|
|
1311
|
-
* ┃ ← composer box (blank rows + model row),
|
|
1312
|
-
* ┃ Build · GPT-5.6 Luna … or the permission dialog that replaces it
|
|
1313
|
-
* ╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ← composer bottom border
|
|
1314
|
-
* /private/tmp/…-share- 6.4K (1%) · $ctrl+p ← footer, cwd wrapped
|
|
1315
|
-
* work-github-kewton-… commands over up to three rows
|
|
1316
|
-
* ```
|
|
1317
|
-
*
|
|
1318
|
-
* Everything from the returned index down is chrome. This is the opencode twin
|
|
1319
|
-
* of {@link findClaudeChromeStart} and exists for the same reason (#1289): the
|
|
1320
|
-
* footer's cwd rows and the composer's model row were reaching saved responses,
|
|
1321
|
-
* which is defect 1 of #1911.
|
|
1322
|
-
*
|
|
1323
|
-
* Found structurally rather than by matching footer text, because the footer's
|
|
1324
|
-
* text is opencode's to change and two of its four rows (the cwd continuations)
|
|
1325
|
-
* are an arbitrary filesystem path with no signature at all.
|
|
1326
|
-
*
|
|
1327
|
-
* @param lines - Captured pane lines, ANSI-stripped, box drawing intact.
|
|
1328
|
-
* Trailing blank rows are tolerated.
|
|
1329
|
-
* @returns Index of the first chrome row, or -1 when no chrome is recognisable.
|
|
1330
|
-
*/
|
|
1331
|
-
function findOpenCodeChromeStart(lines) {
|
|
1332
|
-
let last = lines.length - 1;
|
|
1333
|
-
while (last >= 0 && lines[last].trim() === '')
|
|
1334
|
-
last--;
|
|
1335
|
-
if (last < 0)
|
|
1336
|
-
return -1;
|
|
1337
|
-
const walkUpGutter = (from) => {
|
|
1338
|
-
let top = from;
|
|
1339
|
-
while (top - 1 >= 0 && exports.OPENCODE_GUTTER_ROW_PATTERN.test(lines[top - 1]))
|
|
1340
|
-
top--;
|
|
1341
|
-
return top;
|
|
1342
|
-
};
|
|
1343
|
-
// The composer's bottom border. Searched from the last row upwards over the
|
|
1344
|
-
// WHOLE pane rather than over the few rows the footer occupies: on the boot
|
|
1345
|
-
// screen opencode centres the composer under its banner (row ~99 of 200) while
|
|
1346
|
-
// the footer stays pinned to the bottom, so a window sized for the footer
|
|
1347
|
-
// misses the border entirely (measured, `opencode-live-1883/boot-idle.txt`).
|
|
1348
|
-
for (let i = last; i >= 0; i--) {
|
|
1349
|
-
if (exports.OPENCODE_COMPOSER_BOTTOM_BORDER.test(lines[i])) {
|
|
1350
|
-
return walkUpGutter(i);
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
// No border: the permission dialog draws over the composer and its own box
|
|
1354
|
-
// runs to the last row of the pane (measured, `opencode-live-1893/permission-*.txt`).
|
|
1355
|
-
if (exports.OPENCODE_GUTTER_ROW_PATTERN.test(lines[last])) {
|
|
1356
|
-
return walkUpGutter(last);
|
|
1357
|
-
}
|
|
1358
|
-
return -1;
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Locate the last row of the NEWEST echoed user prompt in a captured pane
|
|
1362
|
-
* (Issue #1911).
|
|
1363
|
-
*
|
|
1364
|
-
* The turn currently being answered starts on the row after this one, so it is
|
|
1365
|
-
* both the extraction anchor (`resolveExtractionStartIndex`'s opencode branch)
|
|
1366
|
-
* and the floor the finished-turn marker has to sit below before a frame counts
|
|
1367
|
-
* as a completed turn (`isOpenCodeComplete`).
|
|
1368
|
-
*
|
|
1369
|
-
* Before #1911 the anchor was "the second-to-last `▣ Build` row", which has two
|
|
1370
|
-
* measured failure modes: on the first turn of a session there is no second row,
|
|
1371
|
-
* so extraction fell back to line 0 and saved the whole pane; and the row it
|
|
1372
|
-
* anchors on belongs to the PREVIOUS turn, so the echoed prompt of the current
|
|
1373
|
-
* one was always included in the reply.
|
|
1374
|
-
*
|
|
1375
|
-
* @param lines - Captured pane lines, ANSI-stripped, box drawing intact.
|
|
1376
|
-
* @param chromeStart - Result of {@link findOpenCodeChromeStart}; the search
|
|
1377
|
-
* stops above it so the composer's and the permission dialog's own gutter rows
|
|
1378
|
-
* are never read as an echoed prompt. Pass -1 when no chrome was found.
|
|
1379
|
-
* @returns Index of the echo block's last row, or -1 when no echo is on screen
|
|
1380
|
-
* (a turn whose head has scrolled out of the alternate-screen pane).
|
|
1381
|
-
*/
|
|
1382
|
-
function findOpenCodeUserEchoEnd(lines, chromeStart) {
|
|
1383
|
-
const limit = chromeStart >= 0 ? chromeStart : lines.length;
|
|
1384
|
-
for (let i = limit - 1; i >= 0; i--) {
|
|
1385
|
-
if (!exports.OPENCODE_USER_ECHO_PATTERN.test(lines[i]))
|
|
1386
|
-
continue;
|
|
1387
|
-
// The block's trailing blank gutter row(s) belong to the echo, not to the reply.
|
|
1388
|
-
let end = i;
|
|
1389
|
-
while (end + 1 < limit && exports.OPENCODE_GUTTER_ROW_PATTERN.test(lines[end + 1]))
|
|
1390
|
-
end++;
|
|
1391
|
-
return end;
|
|
1392
|
-
}
|
|
1393
|
-
return -1;
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* The rows of opencode's composer that hold the INPUT BUFFER (Issue #1906).
|
|
1397
|
-
*
|
|
1398
|
-
* opencode has no prompt marker — no `>` / `❯` / `›` anywhere near its input —
|
|
1399
|
-
* so "is the message still sitting in the composer?" cannot be asked the way it
|
|
1400
|
-
* is asked of every other TUI. What opencode has instead is a box, and the box
|
|
1401
|
-
* has a fixed shape, measured at the production 80x200 geometry across every
|
|
1402
|
-
* frame in `opencode-live-1883/`, `opencode-live-1893/` and
|
|
1403
|
-
* `opencode-live-1906/`:
|
|
1404
|
-
*
|
|
1405
|
-
* ```
|
|
1406
|
-
* ┃ ← buffer rows: blank when empty,
|
|
1407
|
-
* ┃ Review the send path. one row per (wrapped) line of the
|
|
1408
|
-
* ┃ Check newline handling. typed message, or the
|
|
1409
|
-
* ┃ Report findings. `Ask anything...` placeholder on a
|
|
1410
|
-
* ┃ session that has not answered yet
|
|
1411
|
-
* ┃ Build · GPT-5.6 Luna GitHub Copilot ← the agent/model row, ALWAYS last
|
|
1412
|
-
* ╹▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ← the box's bottom border
|
|
1413
|
-
* ```
|
|
1414
|
-
*
|
|
1415
|
-
* The agent/model row is what makes a naive "any guttered row with text" read
|
|
1416
|
-
* wrong: it always carries text, so the composer would never look empty. It is
|
|
1417
|
-
* identified structurally — the last gutter row before the border — rather than
|
|
1418
|
-
* by matching `Build ·` or a model name, both of which are opencode's to change
|
|
1419
|
-
* and neither of which is stable across agents (`Build`, `Plan`, a custom agent).
|
|
1420
|
-
*
|
|
1421
|
-
* Anchored on the border, not on the bottom of the pane, because opencode
|
|
1422
|
-
* centres the whole box under its banner before the first turn (row ~100 of 200)
|
|
1423
|
-
* and only pins it to the bottom afterwards — see {@link findOpenCodeChromeStart},
|
|
1424
|
-
* whose upward walk this shares.
|
|
1425
|
-
*
|
|
1426
|
-
* Returns `null` when no composer is on screen at all. That is the permission
|
|
1427
|
-
* dialog (it replaces the composer and draws no bottom border — measured,
|
|
1428
|
-
* `opencode-live-1893/permission-*.txt`), a full-screen overlay, or a session
|
|
1429
|
-
* still starting.
|
|
1430
|
-
*
|
|
1431
|
-
* @param lines - Captured pane lines, ANSI-stripped, box drawing intact. Must be
|
|
1432
|
-
* the WHOLE pane: a tail window sized for the other tools does not contain the
|
|
1433
|
-
* box on a pre-first-turn frame.
|
|
1434
|
-
* @returns The buffer rows with their gutters intact, or `null` when the
|
|
1435
|
-
* composer is not on screen. An empty array means a box with no buffer row,
|
|
1436
|
-
* which no measured frame produces.
|
|
1437
|
-
*/
|
|
1438
|
-
function findOpenCodeComposerRows(lines) {
|
|
1439
|
-
let border = -1;
|
|
1440
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
1441
|
-
if (exports.OPENCODE_COMPOSER_BOTTOM_BORDER.test(lines[i])) {
|
|
1442
|
-
border = i;
|
|
1443
|
-
break;
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
if (border < 0)
|
|
1447
|
-
return null;
|
|
1448
|
-
let top = border;
|
|
1449
|
-
while (top - 1 >= 0 && exports.OPENCODE_GUTTER_ROW_PATTERN.test(lines[top - 1]))
|
|
1450
|
-
top--;
|
|
1451
|
-
// `top .. border - 1` are the box's gutter rows; the last of them is the
|
|
1452
|
-
// agent/model row, which belongs to the chrome rather than to the buffer.
|
|
1453
|
-
const modelRow = border - 1;
|
|
1454
|
-
if (modelRow < top)
|
|
1455
|
-
return null;
|
|
1456
|
-
return lines.slice(top, modelRow);
|
|
1457
|
-
}
|
|
1458
|
-
/**
|
|
1459
|
-
* A composer row with its gutter (and the padding either side) removed
|
|
1460
|
-
* (Issue #1906). Blank for an empty buffer row.
|
|
1461
|
-
*/
|
|
1462
|
-
function stripOpenCodeGutter(row) {
|
|
1463
|
-
return row.replace(exports.OPENCODE_GUTTER_ROW_PATTERN, '').trim();
|
|
1464
|
-
}
|
|
1465
|
-
/**
|
|
1466
|
-
* OpenCode TUI selection list pattern (Issue #473, narrowed by Issue #1896).
|
|
1467
|
-
*
|
|
1468
|
-
* Detects the fuzzy-search picker overlay opencode draws for `/models`,
|
|
1469
|
-
* `/providers` and `/connect`, anchored on its HEADER ROW COMPLETE WITH the
|
|
1470
|
-
* right-aligned `esc` hatch:
|
|
1471
|
-
*
|
|
1472
|
-
* ```
|
|
1473
|
-
* Select model esc
|
|
1474
|
-
*
|
|
1475
|
-
* Search
|
|
1476
|
-
*
|
|
1477
|
-
* Recent
|
|
1478
|
-
* ● GPT-5.6 Luna GitHub Copilot
|
|
1479
|
-
* ```
|
|
1480
|
-
*
|
|
1481
|
-
* The `esc` is the picker's own dismiss affordance -- positive evidence that an
|
|
1482
|
-
* overlay is open (design rule D1), rather than the presence of two English
|
|
1483
|
-
* words somewhere on the pane. Until #1896 the pattern was the bare phrase, and
|
|
1484
|
-
* `status-detector.ts` tests it against the WHOLE content area (up to ~200 rows,
|
|
1485
|
-
* because the header can sit far above the last row when the list is long), so
|
|
1486
|
-
* an agent that merely wrote `Select model to continue:` in its answer parked the
|
|
1487
|
-
* session on `waiting` / `opencode_selection_list` for the rest of the session
|
|
1488
|
-
* -- measured live on opencode 1.18.21,
|
|
1489
|
-
* `opencode-live-1896/select-model-in-response.txt`.
|
|
1490
|
-
*
|
|
1491
|
-
* Requiring two or more spaces before `esc` is what separates the header row
|
|
1492
|
-
* from prose: the picker right-aligns the hatch across the overlay's width
|
|
1493
|
-
* (37 spaces in the measured frame), while a sentence that happens to end in
|
|
1494
|
-
* "esc" would not.
|
|
1495
|
-
*
|
|
1496
|
-
* NOT additionally anchored on the `Search` row below the header: its distance
|
|
1497
|
-
* from the header is unmeasured for the `Connect a provider` variant (that
|
|
1498
|
-
* overlay needs an unconfigured provider to open, which the live probe could not
|
|
1499
|
-
* produce without touching the operator's real credentials), and the header's
|
|
1500
|
-
* own hatch is already the affordance.
|
|
1501
|
-
*
|
|
1502
|
-
* The header allowlist is deliberately unchanged, and Issue #2112 kept it that
|
|
1503
|
-
* way rather than adding the five headings it found missing (`Select agent`,
|
|
1504
|
-
* `Sessions`, `Timeline`, `Commands`, and the palette's). #1896 left the
|
|
1505
|
-
* question as "a separate change with its own live frames"; #2046 supplied the
|
|
1506
|
-
* frames and the answer they gave is that a WIDER WORD LIST is the wrong shape.
|
|
1507
|
-
* Three of those dialogs published `ready` / `opencode_response_complete` — the
|
|
1508
|
-
* marker of the previous turn, still on the pane behind the overlay — so the
|
|
1509
|
-
* damage was a false COMPLETION, not a missing NavigationButtons row, and no
|
|
1510
|
-
* heading added here would have changed that ordering. The fix is a gate ahead
|
|
1511
|
-
* of the completion branch that reads the overlay's LAYOUT
|
|
1512
|
-
* (`lib/detection/opencode-modal-overlay.ts`, branch C2 of
|
|
1513
|
-
* `tools/opencode/detect.ts`), which is also what keeps the prose false
|
|
1514
|
-
* positives this pattern was narrowed for from coming back through a longer
|
|
1515
|
-
* list.
|
|
1516
|
-
*
|
|
1517
|
-
* This pattern still runs, and still first: it is the reading that survives ANSI
|
|
1518
|
-
* stripping, where the layout rule cannot see anything at all.
|
|
1519
|
-
*
|
|
1520
|
-
* The whitespace runs are `[^\S\n]` (horizontal only) for the reason Issue #1883
|
|
1521
|
-
* documents: plain `\s` crosses newlines under the `m` flag, which would let a
|
|
1522
|
-
* header on one row pair up with an `esc` several rows below it.
|
|
1523
|
-
*
|
|
1524
|
-
* Linear pattern, no nested quantifiers -- ReDoS safe (S4-001).
|
|
1525
|
-
*/
|
|
1526
|
-
exports.OPENCODE_SELECTION_LIST_PATTERN = /^[^\S\n]*(?:Select[^\S\n]+(?:model|provider)|Connect[^\S\n]+a[^\S\n]+provider)[^\S\n]{2,}esc[^\S\n]*$/m;
|
|
1527
|
-
/**
|
|
1528
|
-
* [Issue #1495] Footer signature of Claude Code's `/model` local-settings overlay.
|
|
1529
|
-
* Verified against a real Claude Code v2.1.218 capture, the footer reads:
|
|
1530
|
-
* "Enter to set as default · s to use this session only · Esc to cancel"
|
|
1531
|
-
*
|
|
1532
|
-
* The overlay renders a ❯-marked numbered model list ("1. Default … 5. Haiku")
|
|
1533
|
-
* under a "Select model" header that detectMultipleChoicePrompt() otherwise
|
|
1534
|
-
* matches as a genuine multiple_choice prompt — which let Auto-Yes Enter-confirm
|
|
1535
|
-
* a selection and silently change the user's default model. This "set as default"
|
|
1536
|
-
* phrasing is unique to the model picker: genuine confirmation prompts never
|
|
1537
|
-
* contain it (the trust dialog uses "Enter to confirm · Esc to cancel", Bash-tool
|
|
1538
|
-
* approvals use "Esc to cancel · Tab to amend", AskUserQuestion uses
|
|
1539
|
-
* "Enter to select · … to navigate"), so it is a safe exclusion signal.
|
|
1540
|
-
*
|
|
1541
|
-
* Linear pattern, no nested quantifiers — ReDoS safe (S4-001).
|
|
1542
|
-
*/
|
|
1543
|
-
exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = /Enter\s+to\s+set\s+as\s+default\b/i;
|
|
1544
|
-
/**
|
|
1545
|
-
* Claude CLI selection list footer pattern
|
|
1546
|
-
* Detects Claude CLI's interactive selection prompts that require
|
|
1547
|
-
* arrow key navigation and Enter to select/toggle.
|
|
1548
|
-
*
|
|
1549
|
-
* Matches footer instruction lines (known variants):
|
|
1550
|
-
* "Enter to select · Tab/Arrow keys to navigate · Esc to cancel"
|
|
1551
|
-
* "Enter to select · ↑/↓ to navigate · n to add notes · Esc to cancel"
|
|
1552
|
-
* "Enter to confirm · Esc to exit" (legacy /model command footer)
|
|
1553
|
-
* "Enter to set as default · s to use this session only · Esc to cancel"
|
|
1554
|
-
* (/model command footer as of Claude Code v2.1.218 — Issue #1495)
|
|
1555
|
-
*
|
|
1556
|
-
* "←/→ to adjust · Enter to confirm · s for this session only · Esc to cancel"
|
|
1557
|
-
* (/effort slider footer as of Claude Code v2.1.257 — Issue #3052; before
|
|
1558
|
-
* 2.1.257 it was "Enter to confirm · Esc to cancel")
|
|
1559
|
-
*
|
|
1560
|
-
* The /effort branch keys on "Enter to confirm · s for this session only" rather
|
|
1561
|
-
* than "←/→ to adjust": the `s` hint is unique to the pickers (the trust dialog's
|
|
1562
|
-
* "Enter to confirm · Esc to cancel" and approval dialogs never carry it), and
|
|
1563
|
-
* keeping the match under the `Enter to` prefix leaves the pattern a single
|
|
1564
|
-
* linear alternation. Linear, no nested quantifiers — ReDoS safe.
|
|
1565
|
-
*
|
|
1566
|
-
* The "set as default" branch lets status-detector classify the `/model` overlay
|
|
1567
|
-
* as a Claude selection list (NavigationButtons + ESC hatch, hasActivePrompt=false)
|
|
1568
|
-
* once detectPrompt() no longer reports it as a prompt (see
|
|
1569
|
-
* CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN).
|
|
1570
|
-
*/
|
|
1571
|
-
exports.CLAUDE_SELECTION_LIST_FOOTER = /Enter\s+to\s+(?:select\s+.*to\s+navigate|confirm\s+·\s+(?:Esc|s\s+for\s+this\s+session\s+only)|set\s+as\s+default)/;
|
|
1572
|
-
/**
|
|
1573
|
-
* OpenCode TUI separator pattern (Issue #379)
|
|
1574
|
-
* Matches lines composed entirely of box-drawing / TUI decoration characters.
|
|
1575
|
-
* Covers: vertical lines (U+2503), box corners, horizontal lines, and other TUI elements.
|
|
1576
|
-
*/
|
|
1577
|
-
exports.OPENCODE_SEPARATOR_PATTERN = /^[\u2503\u2579\u25A3\u2580\u2500\u250C\u2510\u2514\u2518\u251C\u2524\u252C\u2534\u253C]+$/;
|
|
1578
|
-
/**
|
|
1579
|
-
* OpenCode skip patterns for response cleaning (Issue #379)
|
|
1580
|
-
* Lines matching any of these patterns are filtered from extracted responses.
|
|
1581
|
-
* Includes: TUI separators, loading indicators, Build summary prefix,
|
|
1582
|
-
* status bar prompts, processing indicators, input prompt, the footer's
|
|
1583
|
-
* context/cost status cell (Issue #1911), and pasted text markers.
|
|
1584
|
-
*/
|
|
1585
|
-
exports.OPENCODE_SKIP_PATTERNS = [
|
|
1586
|
-
exports.OPENCODE_SEPARATOR_PATTERN,
|
|
1587
|
-
exports.OPENCODE_LOADING_PATTERN,
|
|
1588
|
-
/^Build\s+/,
|
|
1589
|
-
exports.OPENCODE_PROMPT_AFTER_RESPONSE,
|
|
1590
|
-
exports.OPENCODE_PROCESSING_INDICATOR,
|
|
1591
|
-
exports.OPENCODE_PROMPT_PATTERN,
|
|
1592
|
-
exports.OPENCODE_FOOTER_STATUS_PATTERN,
|
|
1593
|
-
exports.PASTED_TEXT_PATTERN,
|
|
1594
|
-
];
|
|
1595
|
-
// =============================================================================
|
|
1596
|
-
// OpenCode V2 (`opencode2`, Issue #2934)
|
|
1597
|
-
// =============================================================================
|
|
1598
|
-
//
|
|
1599
|
-
// A separate tool id from `opencode` (Epic #2370, decision 1). Its TUI is drawn
|
|
1600
|
-
// by the same toolkit and many rows look alike, but v1's constants are v1's:
|
|
1601
|
-
// they are left exactly as they are, and v2 names its own here so a change on
|
|
1602
|
-
// either side cannot move the other. Phase 1 reads only what the send path and
|
|
1603
|
-
// the fallback status reader need; the approval dialog (`Always allow`, the
|
|
1604
|
-
// reverse of v1's `Allow always`), the completion row and the session tabs are
|
|
1605
|
-
// Phase 3's.
|
|
1606
|
-
/**
|
|
1607
|
-
* OpenCode V2's empty composer: the input box's `┃` gutter with the
|
|
1608
|
-
* `Ask anything…` placeholder on the same row (2.0.18 draws U+2026; the ASCII
|
|
1609
|
-
* `...` is accepted too, as for v1 since #2915).
|
|
1610
|
-
*
|
|
1611
|
-
* Measured on 2.0.18 at 80x200 (2026-09-28):
|
|
1612
|
-
* ` ┃ Ask anything… "Fix broken tests"`. Match against the ANSI-stripped
|
|
1613
|
-
* frame BEFORE `stripBoxDrawing`, which removes the gutter this anchors on.
|
|
1614
|
-
*/
|
|
1615
|
-
exports.OPENCODE_V2_IDLE_COMPOSER_PATTERN = /^[^\S\n]*[\u2502\u2503][^\S\n]*Ask anything(?:\.\.\.|\u2026)/m;
|
|
1616
|
-
/**
|
|
1617
|
-
* OpenCode V2's footer, which ends in `ctrl+p commands` on every frame the
|
|
1618
|
-
* TUI draws (before a turn: `<path>:<branch> shift+tab agents ctrl+p commands`;
|
|
1619
|
-
* after one: `<path>:<branch> 8.8K (1%) ctrl+p commands`, 2.0.18).
|
|
1620
|
-
*
|
|
1621
|
-
* Proof that the TUI — and so its composer — is on screen, NOT that it is idle:
|
|
1622
|
-
* a running turn keeps the footer.
|
|
1623
|
-
*/
|
|
1624
|
-
exports.OPENCODE_V2_FOOTER_PATTERN = /ctrl\+p commands/;
|
|
1625
|
-
/**
|
|
1626
|
-
* OpenCode V2 working indicator: the footer's `esc interrupt` hint, drawn only
|
|
1627
|
-
* while a turn is running (2.0.18).
|
|
1628
|
-
*/
|
|
1629
|
-
exports.OPENCODE_V2_THINKING_PATTERN = /esc interrupt/;
|
|
1630
|
-
/**
|
|
1631
|
-
* Whether OpenCode V2's composer is on screen, so a send may type into it
|
|
1632
|
-
* (Issue #2934, D6): the gutter-anchored placeholder, or the footer.
|
|
1633
|
-
*
|
|
1634
|
-
* @param text - ANSI-stripped capture, box drawing intact
|
|
1635
|
-
*/
|
|
1636
|
-
function isOpencodeV2ComposerVisible(text) {
|
|
1637
|
-
return exports.OPENCODE_V2_IDLE_COMPOSER_PATTERN.test(text) || exports.OPENCODE_V2_FOOTER_PATTERN.test(text);
|
|
1638
|
-
}
|
|
1639
157
|
/**
|
|
1640
158
|
* The OpenCode V2 dialog title row and its reader (Issue #2971) live in a
|
|
1641
159
|
* browser-safe leaf since Issue #2983, because the chat surface's dialog card
|
|
@@ -1645,1192 +163,219 @@ function isOpencodeV2ComposerVisible(text) {
|
|
|
1645
163
|
var dialog_title_1 = require("./tools/opencode-v2/dialog-title");
|
|
1646
164
|
Object.defineProperty(exports, "OPENCODE_V2_DIALOG_TITLE_PATTERN", { enumerable: true, get: function () { return dialog_title_1.OPENCODE_V2_DIALOG_TITLE_PATTERN; } });
|
|
1647
165
|
Object.defineProperty(exports, "findOpencodeV2DialogTitle", { enumerable: true, get: function () { return dialog_title_1.findOpencodeV2DialogTitle; } });
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
*
|
|
1847
|
-
* The boundary is found structurally -- closing rule, opening rule, composer
|
|
1848
|
-
* glyph -- and never by matching the status bar's wording. That is the same
|
|
1849
|
-
* reasoning {@link findClaudeChromeStart} records (#1289) plus the measurement
|
|
1850
|
-
* behind {@link COPILOT_WORKING_STATUS_PATTERN}: copilot will print its own
|
|
1851
|
-
* status-bar vocabulary as body text when asked to
|
|
1852
|
-
* (`status-vocabulary-in-response.txt` holds ` ● Working esc interrupt` as a
|
|
1853
|
-
* reply), so a vocabulary rule strong enough to delete the real bar also deletes
|
|
1854
|
-
* that reply. Position is the only thing that separates them.
|
|
1855
|
-
*
|
|
1856
|
-
* Without this trim the bar is transcript as far as every consumer is concerned:
|
|
1857
|
-
* it reached the TUI accumulator on every poll and was saved to History as the
|
|
1858
|
-
* agent's answer (#1897's headline symptom, ` Working esc interrupt GPT-5.6
|
|
1859
|
-
* Terra`), and its spinner glyph and byte counter change on every tick, so it
|
|
1860
|
-
* also defeats the content dedup the alternate-screen tools rely on (#1268).
|
|
1861
|
-
*
|
|
1862
|
-
* @param lines - Captured pane rows, ANSI-bearing or not; trailing blanks tolerated
|
|
1863
|
-
* @returns Index of the first chrome row, or -1 when the pane carries no chrome
|
|
1864
|
-
* (a permission dialog draws its box over the whole bottom of the pane, so
|
|
1865
|
-
* there is no composer and no bar -- those frames return -1 and are left to
|
|
1866
|
-
* `detectPrompt`, exactly as {@link readCopilotStatusBar} leaves them)
|
|
1867
|
-
*/
|
|
1868
|
-
function findCopilotChromeStart(lines) {
|
|
1869
|
-
const isRule = (line) => COPILOT_RULE_ROW.test((0, ansi_1.stripAnsi)(line).trim());
|
|
1870
|
-
let lastRow = lines.length - 1;
|
|
1871
|
-
while (lastRow >= 0 && (0, ansi_1.stripAnsi)(lines[lastRow]).trim() === '')
|
|
1872
|
-
lastRow--;
|
|
1873
|
-
if (lastRow < 0)
|
|
1874
|
-
return -1;
|
|
1875
|
-
let closingRule = -1;
|
|
1876
|
-
for (let i = lastRow; i >= Math.max(0, lastRow - COPILOT_STATUS_BAR_MAX_ROWS); i--) {
|
|
1877
|
-
if (isRule(lines[i])) {
|
|
1878
|
-
closingRule = i;
|
|
1879
|
-
break;
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
if (closingRule < 0)
|
|
1883
|
-
return -1;
|
|
1884
|
-
let openingRule = -1;
|
|
1885
|
-
for (let i = closingRule - 1; i >= Math.max(0, closingRule - COPILOT_COMPOSER_MAX_ROWS); i--) {
|
|
1886
|
-
if (isRule(lines[i])) {
|
|
1887
|
-
openingRule = i;
|
|
1888
|
-
break;
|
|
1889
|
-
}
|
|
1890
|
-
}
|
|
1891
|
-
if (openingRule < 0)
|
|
1892
|
-
return -1;
|
|
1893
|
-
// Confirm the fenced rows are the composer rather than a reply that happens to
|
|
1894
|
-
// sit between two horizontal rules.
|
|
1895
|
-
if (!COPILOT_COMPOSER_GLYPH.test((0, ansi_1.stripAnsi)(lines[openingRule + 1] ?? '')))
|
|
1896
|
-
return -1;
|
|
1897
|
-
// The row above the opening rule is copilot's cwd/branch/session header, which
|
|
1898
|
-
// an overlay (the `/model` picker) replaces with a blank rather than with
|
|
1899
|
-
// transcript -- measured on all five non-dialog fixtures.
|
|
1900
|
-
return Math.max(0, openingRule - 1);
|
|
1901
|
-
}
|
|
1902
|
-
/**
|
|
1903
|
-
* A row copilot draws as part of a box or a collapsed reasoning block
|
|
1904
|
-
* (Issue #1897).
|
|
1905
|
-
*
|
|
1906
|
-
* 1.0.80 renders the model's private reasoning as a `⌄ Thought for 41s` header
|
|
1907
|
-
* followed by rows that each begin `│ `, and draws dialogs inside `╭─╮`/`╰─╯`
|
|
1908
|
-
* frames. {@link COPILOT_SKIP_PATTERNS} already carries a `[╭╮╰╯│]` rule, but
|
|
1909
|
-
* `normalizeCopilotLine` deletes every U+2500..U+257F glyph *before* the skip
|
|
1910
|
-
* patterns are applied, so by the time that rule runs the row it is meant to
|
|
1911
|
-
* catch reads as ordinary prose -- which is how the TUI accumulator came to save
|
|
1912
|
-
* copilot's chain-of-thought as the reply. Matched against the ANSI-stripped row,
|
|
1913
|
-
* before normalisation.
|
|
1914
|
-
*
|
|
1915
|
-
* `─` is deliberately absent: a rule row is {@link COPILOT_SEPARATOR_PATTERN}'s
|
|
1916
|
-
* job, and `── heading ──` is content.
|
|
1917
|
-
*/
|
|
1918
|
-
exports.COPILOT_BOX_ROW_PATTERN = /^\s*[│└╰╭╮╯├┤┬┴┼]/;
|
|
1919
|
-
/**
|
|
1920
|
-
* copilot's collapsed-section header (Issue #1897).
|
|
1921
|
-
*
|
|
1922
|
-
* Measured on 1.0.80: `⌄ Thinking…` while the reasoning block is live, replaced
|
|
1923
|
-
* by `⌄ Thought for 41s` once the turn moves on. Anchored on the `⌄` disclosure
|
|
1924
|
-
* glyph rather than on the words, because "Thinking…" also occurs as body text
|
|
1925
|
-
* -- `status-vocabulary-in-response.txt` contains exactly that row as a reply,
|
|
1926
|
-
* and it must survive cleaning.
|
|
1927
|
-
*/
|
|
1928
|
-
exports.COPILOT_REASONING_HEADER_PATTERN = /^\s*⌄\s/;
|
|
1929
|
-
/**
|
|
1930
|
-
* copilot's echo of the operator's own prompt in the transcript (Issue #1897).
|
|
1931
|
-
*
|
|
1932
|
-
* 1.0.80 draws every transcript row at the pane's one-column indent -- ` ❯ Reply
|
|
1933
|
-
* with exactly the word: pong` -- while the composer at the bottom of the pane is
|
|
1934
|
-
* at column 0. The bare `^[>❯]` form therefore never matched the echo, so
|
|
1935
|
-
* `resolveExtractionStartIndex`'s copilot branch found no anchor, fell back to
|
|
1936
|
-
* line 0, and handed the launch banner to the cleaner as the turn's reply.
|
|
1937
|
-
*
|
|
1938
|
-
* Bounded to two leading spaces rather than `\s*` so a quoted `> …` line inside a
|
|
1939
|
-
* reply cannot be mistaken for a new turn boundary.
|
|
1940
|
-
*/
|
|
1941
|
-
exports.COPILOT_USER_ECHO_PATTERN = /^ {0,2}[>❯]\s+\S/;
|
|
1942
|
-
/**
|
|
1943
|
-
* One of the half-block dividers copilot 1.0.82 boxes a transcript row with
|
|
1944
|
-
* (Issue #2269).
|
|
1945
|
-
*
|
|
1946
|
-
* 1.0.80 drew the echoed prompt as a single bare ` ❯ <text>` row. 1.0.82 draws
|
|
1947
|
-
* a full-width `▄` run above it and a full-width `▀` run below it
|
|
1948
|
-
* (`copilot-live-2269/turn-complete.txt` rows 10 and 12):
|
|
1949
|
-
*
|
|
1950
|
-
* ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
|
1951
|
-
* ❯ Reply with exactly the word: uat-run1 00:27
|
|
1952
|
-
* ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
1953
|
-
* ● uat-run1
|
|
1954
|
-
*
|
|
1955
|
-
* {@link COPILOT_SKIP_PATTERNS}' `/[█▘▝▖▗▔▄▌▐]/` rule already dropped the `▄`
|
|
1956
|
-
* row. `▀` (U+2580) was not in that class, so the row BELOW the echo survived --
|
|
1957
|
-
* and extraction starts one row past the echo, which made a wall of 199 `▀` the
|
|
1958
|
-
* first line of every saved 1.0.82 reply.
|
|
1959
|
-
*
|
|
1960
|
-
* Deliberately not "add `▀` to that character class": this requires a run of ten
|
|
1961
|
-
* or more and nothing else on the row, so a reply that happens to use a single
|
|
1962
|
-
* half block as a glyph is untouched. The optional leading corner accepts the
|
|
1963
|
-
* composer's own fence rows (`╻▄…` / `╹▀…`) as well, which is defence in depth
|
|
1964
|
-
* only -- {@link findCopilotChromeStart} cuts those structurally, and this rule
|
|
1965
|
-
* is what stops them reaching History if a future build moves the fence again.
|
|
1966
|
-
*/
|
|
1967
|
-
exports.COPILOT_TRANSCRIPT_DIVIDER_PATTERN = /^\s*[╻╹]?[▀▄]{10,}\s*$/;
|
|
1968
|
-
/**
|
|
1969
|
-
* The verbs copilot prints on a tool row, as a regex alternation source.
|
|
1970
|
-
*
|
|
1971
|
-
* One list, shared by {@link COPILOT_TOOL_ROW_PATTERN} here and by
|
|
1972
|
-
* `COPILOT_TOOL_ACTION_PATTERN` in `response-cleaner.ts`: the two match the same
|
|
1973
|
-
* vocabulary behind different markers, and a verb added to one and not the other
|
|
1974
|
-
* is a leak nothing fails on.
|
|
1975
|
-
*
|
|
1976
|
-
* `Asked user` / `Asking user` are Issue #2269's addition. copilot answers a
|
|
1977
|
-
* prompt it cannot act on by calling its ask-user tool, and the row it leaves in
|
|
1978
|
-
* the transcript (`● Asked user What would you like me to help with?`, measured
|
|
1979
|
-
* in `copilot-live-2269/turn-oneword-echo-askuser.txt`) was being saved as the
|
|
1980
|
-
* agent's reply -- the "launch screen as the answer" the Issue reports.
|
|
1981
|
-
*/
|
|
1982
|
-
exports.COPILOT_TOOL_VERBS = 'Get|Read|Run|Search|Write|Edit|Delete|List|Create|Fetch|Explore|Execute|Install|Asked user|Asking user|Model changed to:';
|
|
1983
|
-
/**
|
|
1984
|
-
* copilot 1.0.82's tool row, whose marker is a file-type badge (Issue #2269).
|
|
1985
|
-
*
|
|
1986
|
-
* 1.0.80 drew every tool row as `● <Verb> …` and `COPILOT_TOOL_ACTION_PATTERN`
|
|
1987
|
-
* reads exactly that. 1.0.82 puts a short badge for the file the tool touched in
|
|
1988
|
-
* front of the verb instead, and only falls back to `●` for a type it has no
|
|
1989
|
-
* badge for. Measured verbatim over
|
|
1990
|
-
* `copilot-live-2269/turn-tool-badges.txt` and `turn-tool-rows.txt`:
|
|
1991
|
-
*
|
|
1992
|
-
* / Search "a.ts" 1 file found
|
|
1993
|
-
* MD Read note.md L1:1 (1 line read)
|
|
1994
|
-
* TS Read a.ts 1 line read
|
|
1995
|
-
* PY Read c.py 1 line read
|
|
1996
|
-
* {} Read b.json 1 line read
|
|
1997
|
-
* ● Read d.txt 1 line read ← plain text keeps the 1.0.80 marker
|
|
1998
|
-
*
|
|
1999
|
-
* So the badge is `/`, `{}`, or two to four upper-case characters. Two is the
|
|
2000
|
-
* floor on purpose: a one-character class would also match the pronoun in an
|
|
2001
|
-
* English sentence ("I Read the file"), and no measured badge is one character.
|
|
2002
|
-
* `●` is left to `COPILOT_TOOL_ACTION_PATTERN`, which is the rule that already
|
|
2003
|
-
* owns it -- copilot's own prose is `● <text>` too (`● done`, `● 対象ファイルを
|
|
2004
|
-
* 確認して内容を読み込みます。`), and only the verb list separates the two.
|
|
2005
|
-
*
|
|
2006
|
-
* The `$` badge is absent for the same reason: `$ Shell Print requested text 2
|
|
2007
|
-
* lines…` opens a BLOCK whose command rows follow, which is
|
|
2008
|
-
* `COPILOT_TOOL_INVOCATION_PATTERN`'s job, and `Shell` is not in the verb list
|
|
2009
|
-
* anyway.
|
|
2010
|
-
*
|
|
2011
|
-
* No /g flag (S4-5: would make test() stateful). No quantifier nested inside
|
|
2012
|
-
* another (SEC4-001: ReDoS safe).
|
|
2013
|
-
*/
|
|
2014
|
-
exports.COPILOT_TOOL_ROW_PATTERN = new RegExp(`^\\s*(?:\\/|\\{\\}|[A-Z][A-Z0-9+#]{1,3})\\s+(?:${exports.COPILOT_TOOL_VERBS})[\\s:]`);
|
|
2015
|
-
/**
|
|
2016
|
-
* A wrapped continuation of the copilot transcript row above it (Issue #1897).
|
|
2017
|
-
*
|
|
2018
|
-
* Marker rows (` ❯ `, ` ● `, ` $ `, ` ⌄ `) carry one leading space; the rows they
|
|
2019
|
-
* wrap onto are indented further and carry no marker, and a blank row separates
|
|
2020
|
-
* one block from the next (measured on 1.0.80 at 200x1000). Used to walk past the
|
|
2021
|
-
* tail of a long *user* prompt so the extracted reply does not open with the
|
|
2022
|
-
* second half of the operator's own question.
|
|
2023
|
-
*/
|
|
2024
|
-
exports.COPILOT_TRANSCRIPT_CONTINUATION_PATTERN = /^ {2,}\S/;
|
|
2025
|
-
/**
|
|
2026
|
-
* Rows that only copilot's first-launch screen draws (Issue #1897).
|
|
2027
|
-
*
|
|
2028
|
-
* The launch screen is a complete, idle frame: the composer is drawn, the status
|
|
2029
|
-
* bar shows key hints, and nothing about it says "no turn has happened yet". So
|
|
2030
|
-
* `extractResponse` classified it as a finished response and History opened with
|
|
2031
|
-
* the banner as the agent's first message -- before the operator's first prompt.
|
|
2032
|
-
*
|
|
2033
|
-
* These anchors are only ever consulted for a frame that carries no echoed user
|
|
2034
|
-
* prompt at all (see the copilot banner guard in `extractResponse`), which is
|
|
2035
|
-
* what keeps a reply that quotes any of this wording from being suppressed.
|
|
2036
|
-
*/
|
|
2037
|
-
exports.COPILOT_BOOT_BANNER_ANCHORS = [
|
|
2038
|
-
/No copilot-instructions\.md found/,
|
|
2039
|
-
/Copilot uses AI, so always check for mistakes\./,
|
|
2040
|
-
/Copilot v\d[\w.]*\s+uses AI/,
|
|
2041
|
-
/^\s*(?:●\s+)?GitHub Copilot\s+v\d/m,
|
|
2042
|
-
/^\s*(?:●\s+)?Tip:\s*\//m,
|
|
2043
|
-
/Describe a task to get started/,
|
|
2044
|
-
/Prefer a visual workspace\?/,
|
|
2045
|
-
// Issue #2269: copilot's greeting for a prompt it cannot act on. It arrives
|
|
2046
|
-
// through the ask-user tool, so the row that reaches the transcript is
|
|
2047
|
-
// `● Asked user Hi — what would you like to work on?` and the dialog's own
|
|
2048
|
-
// body repeats the question. Both are chrome for a turn that produced no
|
|
2049
|
-
// answer, and the operator saw the greeting saved as the agent's reply.
|
|
2050
|
-
/what would you like to work on\?/i,
|
|
2051
|
-
];
|
|
2052
|
-
/**
|
|
2053
|
-
* The key-hint footer copilot draws under a picker, matched one ROW at a time
|
|
2054
|
-
* (Issue #547; rewritten against live 1.0.80 frames by Issue #1895).
|
|
2055
|
-
*
|
|
2056
|
-
* The pattern this replaced --
|
|
2057
|
-
* `/Search\s+\w+\.\.\.|Select\s+Model|to (?:navigate|select).*Enter to (?:select|confirm)/`
|
|
2058
|
-
* -- matched **none** of the eleven pickers 1.0.80 opens (measured; the frames
|
|
2059
|
-
* are in `tests/unit/lib/detection/fixtures/copilot-picker-1895/`, and #1885 /
|
|
2060
|
-
* #1886 / #1913 reached the same result independently). `/model` renders
|
|
2061
|
-
* `❯ Search models…` with U+2026 rather than three periods, no picker carries
|
|
2062
|
-
* the words `Select Model`, and every footer spells its verbs in lower case.
|
|
2063
|
-
*
|
|
2064
|
-
* What all eleven footers do share is the shape of a key-hint bar: `·`-separated
|
|
2065
|
-
* hints carrying either arrow-key navigation or a lower-case dismiss verb.
|
|
2066
|
-
* Measured verbatim, bottom-most first:
|
|
2067
|
-
*
|
|
2068
|
-
* /model ↑/↓ to navigate · ←/→ reasoning effort · tab context window ·
|
|
2069
|
-
* shift+tab group: recommended · enter to select · esc to cancel
|
|
2070
|
-
* /agent n new agent · ? learn more · esc cancel
|
|
2071
|
-
* /theme ↑/↓ to navigate · enter to select · esc to cancel
|
|
2072
|
-
* /permissions 1-2 to select · ↑/↓ to navigate · enter to confirm · esc to cancel
|
|
2073
|
-
* /skills ↑/↓ to navigate · enter to toggle · esc to close
|
|
2074
|
-
* /mcp ↑/↓ to select · enter to show · a to add · esc to close
|
|
2075
|
-
* /settings / search · ↑/↓ navigate · tab switch scope · enter edit ·
|
|
2076
|
-
* ctrl+r reset · ctrl+e editor · esc close
|
|
2077
|
-
* /statusline ↑/↓ nav · enter toggle · esc close
|
|
2078
|
-
* /subagents ↑/↓ to navigate · space on/off · r reset · enter to select · esc to cancel
|
|
2079
|
-
* /resume / search · ↑/↓ navigate · enter select · ←/→ switch tabs · r refresh ·
|
|
2080
|
-
* x delete · s sort:relevance · esc cancel
|
|
2081
|
-
* /session / search · ↑/↓ navigate · enter open · n new · tab switch tabs · a filter:all
|
|
2082
|
-
*
|
|
2083
|
-
* Neither half of the alternation is universal -- `/agent` has no list to walk
|
|
2084
|
-
* so it prints no `↑/↓`, and `/session` offers no `esc` -- but every footer has
|
|
2085
|
-
* one of them, so the union covers 11/11 while each individual disjunct stays
|
|
2086
|
-
* specific enough to be worth requiring. The slash in `↑/↓` is optional only as
|
|
2087
|
-
* a rewording tolerance: the synthetic frames pinned since Issue #547 spell it
|
|
2088
|
-
* `↑↓`, and whether copilot ever drew it that way is not something this Issue
|
|
2089
|
-
* measured. Every 1.0.80 footer above has the slash.
|
|
2090
|
-
*
|
|
2091
|
-
* Three deliberate narrowings keep this off ordinary text:
|
|
2092
|
-
* - the `·` lookahead, so a sentence such as "press esc to cancel" is not a
|
|
2093
|
-
* footer unless it is also a hint bar;
|
|
2094
|
-
* - lower case, which is what 1.0.80 draws. `(Esc to cancel · 2.3 KiB)` -- the
|
|
2095
|
-
* capitalised spelling in {@link COPILOT_THINKING_PATTERN} -- is a *progress*
|
|
2096
|
-
* row, and it would otherwise satisfy both halves of this pattern;
|
|
2097
|
-
* - `cancel|close` only, so the status bar's `esc interrupt`
|
|
2098
|
-
* ({@link COPILOT_WORKING_STATUS_PATTERN}) is not a footer either.
|
|
2099
|
-
*
|
|
2100
|
-
* The narrowings are defence in depth; the load-bearing guard is positional and
|
|
2101
|
-
* lives in {@link isCopilotSelectionFrame} -- copilot's own answer text can and
|
|
2102
|
-
* does contain these exact rows (`picker-vocabulary-in-response.txt` is a live
|
|
2103
|
-
* frame of it), so no window-scoped match on this vocabulary can be safe.
|
|
2104
|
-
*
|
|
2105
|
-
* No /g flag (S4-5: would make test() stateful). `[^\n]*` cannot cross a row and
|
|
2106
|
-
* no quantifier is nested inside another (SEC4-001: ReDoS safe).
|
|
2107
|
-
*/
|
|
2108
|
-
exports.COPILOT_SELECTION_FOOTER_PATTERN = /^(?=[^\n]*·)[^\n]*(?:↑\/?↓|\besc\s+(?:to\s+)?(?:cancel|close)\b)/m;
|
|
2109
|
-
/**
|
|
2110
|
-
* How many non-blank rows up from the bottom of the pane may carry the footer.
|
|
2111
|
-
*
|
|
2112
|
-
* Nine of the eleven pickers put it on the bottom row itself; `/agent` and
|
|
2113
|
-
* `/subagents` draw their panel with a closing full-width rule underneath it,
|
|
2114
|
-
* which puts the footer two non-blank rows up. Three is that measurement plus
|
|
2115
|
-
* one row of slack -- deliberately far short of a window that could reach the
|
|
2116
|
-
* transcript, which on the production 200x1000 geometry sits ~950 rows above.
|
|
2117
|
-
*/
|
|
2118
|
-
const COPILOT_SELECTION_FOOTER_SCAN_ROWS = 3;
|
|
2119
|
-
/**
|
|
2120
|
-
* Whether the pane is sitting on one of copilot's pickers (Issue #1895).
|
|
2121
|
-
*
|
|
2122
|
-
* Takes the whole frame rather than a window for the same reason
|
|
2123
|
-
* {@link readCopilotStatusBar} does: the evidence is positional, and a call site
|
|
2124
|
-
* cannot be trusted to preserve "near the bottom of the pane" on its own. Two
|
|
2125
|
-
* facts, both measured on 1.0.80, make the position sufficient:
|
|
2126
|
-
*
|
|
2127
|
-
* - **A picker replaces copilot's chrome.** In the idle and generating states
|
|
2128
|
-
* the bottom five rows are cwd / rule / composer / rule / status bar; while a
|
|
2129
|
-
* picker is up, none of them are drawn. So a frame whose bottom row is a
|
|
2130
|
-
* status bar is not a picker, whatever its transcript says -- which is the
|
|
2131
|
-
* whole of the false-positive half of Issue #1895. `detectSessionStatus`
|
|
2132
|
-
* reads the same row for the running verdict at step 0.5, so checking it
|
|
2133
|
-
* first here also fixes the order between the two branches: the status bar
|
|
2134
|
-
* wins, and the picker branch only speaks when copilot has taken it away.
|
|
2135
|
-
* - **An answerable dialog is drawn inside a box; a picker is not.** The
|
|
2136
|
-
* folder-trust and permission dialogs wear the same lower-case
|
|
2137
|
-
* `↑/↓ to navigate · enter to select · esc to cancel` footer, but every one of
|
|
2138
|
-
* their rows reads `│ … │` and the bottom row is `╰─…─╯`. Skipping boxed rows
|
|
2139
|
-
* keeps those on the prompt branch, where they belong: they are the agent
|
|
2140
|
-
* blocked on the human (`hasActivePrompt: true`, exit 10 for `wait`), not a
|
|
2141
|
-
* list the operator opened.
|
|
2142
|
-
*
|
|
2143
|
-
* @param contentLines - Frame rows, ANSI already stripped, in pane order
|
|
2144
|
-
* @returns True when the bottom of the pane is a picker's key-hint footer
|
|
2145
|
-
*/
|
|
2146
|
-
function isCopilotSelectionFrame(contentLines) {
|
|
2147
|
-
if (readCopilotStatusBar(contentLines) !== null)
|
|
2148
|
-
return false;
|
|
2149
|
-
let scanned = 0;
|
|
2150
|
-
for (let i = contentLines.length - 1; i >= 0; i--) {
|
|
2151
|
-
const row = contentLines[i];
|
|
2152
|
-
const trimmed = row.trim();
|
|
2153
|
-
if (trimmed === '')
|
|
2154
|
-
continue;
|
|
2155
|
-
if (++scanned > COPILOT_SELECTION_FOOTER_SCAN_ROWS)
|
|
2156
|
-
break;
|
|
2157
|
-
// A boxed row belongs to a dialog, not a picker (see above).
|
|
2158
|
-
if (trimmed.startsWith('│') || trimmed.endsWith('│'))
|
|
2159
|
-
continue;
|
|
2160
|
-
if (exports.COPILOT_SELECTION_FOOTER_PATTERN.test(row))
|
|
2161
|
-
return true;
|
|
2162
|
-
}
|
|
2163
|
-
return false;
|
|
2164
|
-
}
|
|
2165
|
-
/**
|
|
2166
|
-
* Anchors of Copilot CLI's first-launch "Confirm folder trust" dialog (Issue #1886).
|
|
2167
|
-
*
|
|
2168
|
-
* Recorded from copilot 1.0.80 (`tests/fixtures/copilot-folder-trust-1080.ts`):
|
|
2169
|
-
* copilot asks this once per untrusted git repository, before anything else runs,
|
|
2170
|
-
* and the whole dialog is drawn inside a box — every row reads `│ <content>`.
|
|
2171
|
-
* That is why `COPILOT_PROMPT_PATTERN` (`^[>❯]\s`) does not match the frame at
|
|
2172
|
-
* all and `waitForReady` used to spin its full 30-second window against it.
|
|
2173
|
-
*
|
|
2174
|
-
* Both anchors are required. One of them alone would also match this dialog's
|
|
2175
|
-
* text quoted back inside a model response, and a false positive here does not
|
|
2176
|
-
* merely mis-report a status: it sends a bare `1` into a live composer.
|
|
2177
|
-
*
|
|
2178
|
-
* The anchors live here rather than in `cli-tools/copilot` for the same reason
|
|
2179
|
-
* codex's do (Issue #1829): the Auto-Yes poller judges the same screen through
|
|
2180
|
-
* `detectPrompt`, and two copies of the wording would be two chances to disagree
|
|
2181
|
-
* about what this dialog is.
|
|
2182
|
-
*/
|
|
2183
|
-
exports.COPILOT_FOLDER_TRUST_ANCHORS = [
|
|
2184
|
-
'Confirm folder trust',
|
|
2185
|
-
'Do you trust the files in this folder?',
|
|
2186
|
-
];
|
|
2187
|
-
/**
|
|
2188
|
-
* The one option CommandMate may answer on the operator's behalf: `1. Yes`,
|
|
2189
|
-
* which grants trust for THIS SESSION only.
|
|
2190
|
-
*
|
|
2191
|
-
* Matching the option text — not just the dialog — is the fail-safe. Option 2
|
|
2192
|
-
* ("Yes, and remember this folder for future sessions") writes `trustedFolders`
|
|
2193
|
-
* into `~/.copilot/config.json`, one file shared by every checkout on the
|
|
2194
|
-
* machine (measured: answering `1` leaves that file byte-identical). If copilot
|
|
2195
|
-
* ever reorders the list so that `1` is the remembering variant, this stops
|
|
2196
|
-
* matching, nothing is sent, and the launch degrades to the pre-#1886 stall
|
|
2197
|
-
* instead of silently persisting a trust grant.
|
|
2198
|
-
*
|
|
2199
|
-
* Written against the box-stripped frame, where the row reads `❯ 1. Yes`.
|
|
2200
|
-
* `[ \t]*$` rather than `\s*$` so the trailing anchor cannot roll onto a later
|
|
2201
|
-
* line and accept `1. Yes, and remember ...`.
|
|
2202
|
-
*/
|
|
2203
|
-
exports.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN = /^[ \t]*(?:[>❯][ \t]*)?1\.[ \t]+Yes[ \t]*$/m;
|
|
2204
|
-
/**
|
|
2205
|
-
* Key that selects {@link COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN}.
|
|
2206
|
-
* Measured on 1.0.80: the digit confirms on its own — sending a trailing Enter
|
|
2207
|
-
* would land on the composer that the dialog's dismissal reveals.
|
|
2208
|
-
*/
|
|
2209
|
-
exports.COPILOT_FOLDER_TRUST_ANSWER_KEY = '1';
|
|
2210
|
-
/**
|
|
2211
|
-
* Whether the pane is sitting on the folder-trust dialog with the session-only
|
|
2212
|
-
* option in first position.
|
|
2213
|
-
*
|
|
2214
|
-
* @param output - ANSI-stripped pane capture (box drawing still present)
|
|
2215
|
-
* @returns True when both anchors and the `1. Yes` option row are present
|
|
2216
|
-
*/
|
|
2217
|
-
function isCopilotFolderTrustDialog(output) {
|
|
2218
|
-
if (!exports.COPILOT_FOLDER_TRUST_ANCHORS.every((anchor) => output.includes(anchor))) {
|
|
2219
|
-
return false;
|
|
2220
|
-
}
|
|
2221
|
-
return exports.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN.test(stripBoxDrawing(output));
|
|
2222
|
-
}
|
|
2223
|
-
/**
|
|
2224
|
-
* Copilot skip patterns for response cleaning (Issue #545)
|
|
2225
|
-
* Placeholder patterns - to be refined after Phase 1 TUI investigation.
|
|
2226
|
-
*/
|
|
2227
|
-
exports.COPILOT_SKIP_PATTERNS = [
|
|
2228
|
-
exports.PASTED_TEXT_PATTERN,
|
|
2229
|
-
exports.COPILOT_SEPARATOR_PATTERN,
|
|
2230
|
-
exports.COPILOT_THINKING_PATTERN,
|
|
2231
|
-
// Issue #1895 replaced COPILOT_SELECTION_LIST_PATTERN with the row-scoped
|
|
2232
|
-
// picker footer; the vocabulary it carried (`Search \w+...` / `Select Model`)
|
|
2233
|
-
// is copilot's prose, not its chrome.
|
|
2234
|
-
exports.COPILOT_SELECTION_FOOTER_PATTERN,
|
|
2235
|
-
// Collapsed reasoning header (Issue #1897): "⌄ Thinking…" / "⌄ Thought for 41s"
|
|
2236
|
-
exports.COPILOT_REASONING_HEADER_PATTERN,
|
|
2237
|
-
// 1.0.82's transcript dividers and badge-marked tool rows (Issue #2269)
|
|
2238
|
-
exports.COPILOT_TRANSCRIPT_DIVIDER_PATTERN,
|
|
2239
|
-
exports.COPILOT_TOOL_ROW_PATTERN,
|
|
2240
|
-
// Issue #2269: the anchored box-row rule, so the raw-row consumer agrees with
|
|
2241
|
-
// the accumulator. `[╭╮╰╯│]` below is unanchored and omits `└`, which is the
|
|
2242
|
-
// glyph copilot opens a detail row with (` └ Enable all permissions (tools,
|
|
2243
|
-
// paths, and URLs)` under the launch screen's tip). `normalizeCopilotLine`
|
|
2244
|
-
// deletes every U+2500..U+257F glyph, so by the time the skip patterns run in
|
|
2245
|
-
// `cleanCopilotResponse` that row reads as prose -- which is how the ONE row
|
|
2246
|
-
// of the 1.0.82 launch screen that no other rule caught became the agent's
|
|
2247
|
-
// first reply. `extractCopilotContentLines` already tests this pattern before
|
|
2248
|
-
// normalising (#1897); adding it here is what gives `extractResponse`, which
|
|
2249
|
-
// never normalises, the same answer.
|
|
2250
|
-
exports.COPILOT_BOX_ROW_PATTERN,
|
|
2251
|
-
// Logo/banner lines
|
|
2252
|
-
/^GitHub Copilot\s+v/,
|
|
2253
|
-
/[█▘▝▖▗▔▄▌▐]/,
|
|
2254
|
-
/[╭╮╰╯│]/,
|
|
2255
|
-
// Status bar (branch + model display)
|
|
2256
|
-
/\[⎇\s+\w[^\]]*\]/,
|
|
2257
|
-
// Operation guide lines
|
|
2258
|
-
/^shift\+tab\s/,
|
|
2259
|
-
/^\?\s+for\s+shortcuts/,
|
|
2260
|
-
/^ctrl\+[a-z]\s+\w/,
|
|
2261
|
-
// Prompt lines
|
|
2262
|
-
/^[❯>]\s*(Type\s+@|$)/,
|
|
2263
|
-
// Tip/hint lines. Issue #2269 added the `●` marker: 1.0.82 draws the launch
|
|
2264
|
-
// screen's tip as `● Tip: /allow-all`, and `cleanCopilotResponse` strips the
|
|
2265
|
-
// bullet only AFTER the skip patterns have run, so the bare form never
|
|
2266
|
-
// matched and the tip was the one banner row that reached History.
|
|
2267
|
-
/^\s*(?:●\s+)?Tip:\s*\//,
|
|
2268
|
-
// Issue #2269: copilot's own tab bar, the top row of every 1.0.82 frame. It is
|
|
2269
|
-
// the second banner row the launch screen leaked (the logo, the disclaimer and
|
|
2270
|
-
// the tip's `└ …` detail row are all caught by the glyph rules above), and
|
|
2271
|
-
// between them they are the whole difference between "the launch screen cleans
|
|
2272
|
-
// to nothing and cannot be saved" and "History opens with the banner". Spelled
|
|
2273
|
-
// out in full rather than as a keyword so a reply that mentions one of these
|
|
2274
|
-
// words is untouched.
|
|
2275
|
-
/^\s*Current\s+Sessions\s+Issues\s+Pull requests\s+Gists\s*$/,
|
|
2276
|
-
// Initial display text
|
|
2277
|
-
/^Describe a task to get started/,
|
|
2278
|
-
// Issue #571: Disclaimer, initialization message, environment info
|
|
2279
|
-
/^Copilot uses AI, so always check for mistakes\.$/, // Disclaimer (full-line match to avoid filtering user content mentioning Copilot)
|
|
2280
|
-
/^● 💡/, // Initialization hint message
|
|
2281
|
-
/^● Environment loaded:/, // Environment info
|
|
2282
|
-
];
|
|
2283
|
-
/**
|
|
2284
|
-
* Vibe Local prompt pattern
|
|
2285
|
-
* vibe-local (vibe-coder) shows `ctx:N% ❯` prompt when waiting for user input.
|
|
2286
|
-
* The prompt line includes a context usage percentage prefix.
|
|
2287
|
-
* Examples: "ctx:9% ❯", "ctx:30% ❯", "ctx:9% ❯ /model"
|
|
2288
|
-
*/
|
|
2289
|
-
exports.VIBE_LOCAL_PROMPT_PATTERN = /ctx:\d+%\s*[>❯]/m;
|
|
2290
|
-
/**
|
|
2291
|
-
* Vibe Local thinking/processing pattern
|
|
2292
|
-
* vibe-local shows spinner characters and status text while processing.
|
|
2293
|
-
* Matches braille spinners, "Thinking", and tool execution indicators.
|
|
2294
|
-
*/
|
|
2295
|
-
exports.VIBE_LOCAL_THINKING_PATTERN = /[\u2800-\u28FF]|Thinking|⠋|⠙|⠹|⠸|⠼|⠴|⠦|⠧|⠇|⠏|Running|Executing/;
|
|
2296
|
-
/**
|
|
2297
|
-
* The banner agy paints INTO its empty input box while a non-default permission
|
|
2298
|
-
* mode is on (Issue #2592).
|
|
2299
|
-
*
|
|
2300
|
-
* Measured on agy 1.2.4 in the #2592 UAT
|
|
2301
|
-
* (`tests/fixtures/agent-mode-2592/antigravity-{accept-edits,plan}.txt`, row 18,
|
|
2302
|
-
* between the box's two rules):
|
|
2303
|
-
*
|
|
2304
|
-
* `> Accept-edits mode: file edits auto-approved (shift+tab to cycle)`
|
|
2305
|
-
* `> Plan mode: research & plan only (shift+tab to cycle)`
|
|
2306
|
-
*
|
|
2307
|
-
* It takes the place of the bare `>` an empty box shows in default — it is a
|
|
2308
|
-
* placeholder, not typed text — so a reader that only accepts the bare glyph
|
|
2309
|
-
* reads an idle accept-edits / plan pane as "no input box at all". Before this
|
|
2310
|
-
* pattern existed that is exactly what happened: the status detector fell through
|
|
2311
|
-
* to its `default` floor (`running` + `isUnclassifiedActive`), `commandmate ls`
|
|
2312
|
-
* showed a resting agent as running, `wait` never completed, and
|
|
2313
|
-
* `isAntigravityReady` refused every send. #2592 made those two modes one click
|
|
2314
|
-
* away, so the hole had to close with it.
|
|
2315
|
-
*
|
|
2316
|
-
* The structure is matched rather than the two sentences: `<Name> mode: <text>
|
|
2317
|
-
* (shift+tab to cycle)`, anchored on the key hint agy ends every banner with, so
|
|
2318
|
-
* a third mode needs no edit here. What stays out is anything a user could have
|
|
2319
|
-
* typed into the box — the trailing hint is the part nobody types.
|
|
2320
|
-
*
|
|
2321
|
-
* Whole-row, no `/g`, no nested quantifiers (`[^\n]*` is followed by a literal).
|
|
2322
|
-
*/
|
|
2323
|
-
exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE = '>[^\\S\\n]+[A-Z][A-Za-z-]*[^\\S\\n]mode:[^\\n]*\\(shift\\+tab to cycle\\)[^\\S\\n]*';
|
|
2324
|
-
/** {@link ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE} as a single-row pattern. */
|
|
2325
|
-
exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_PATTERN = new RegExp(`^${exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE}$`, 'm');
|
|
2326
|
-
/**
|
|
2327
|
-
* Antigravity (agy) interactive REPL prompt pattern (Issue #988)
|
|
2328
|
-
* agy shows a bare ">" input box line when waiting for user input. The input box
|
|
2329
|
-
* is always rendered (even while generating), so prompt presence alone does not
|
|
2330
|
-
* mean "ready" — running vs idle is resolved together with the thinking pattern /
|
|
2331
|
-
* footer status bar in status-detector.ts. (Confirmed on machine: line is "> ".)
|
|
2332
|
-
*
|
|
2333
|
-
* Issue #2592: or the mode banner that replaces the bare glyph in an EMPTY box
|
|
2334
|
-
* while accept-edits / plan is on ({@link ANTIGRAVITY_COMPOSER_MODE_BANNER_PATTERN}).
|
|
2335
|
-
* Every consumer of this pattern is asking "is agy's input box drawn and empty?"
|
|
2336
|
-
* — the idle rule in `detection/tools/antigravity/detect.ts`, the survey guard
|
|
2337
|
-
* beside it, `isAntigravityReady` (the send gate) and the liveness probe — and
|
|
2338
|
-
* the answer does not depend on the permission mode, so the banner is folded in
|
|
2339
|
-
* here once rather than at each of them.
|
|
2340
|
-
*/
|
|
2341
|
-
exports.ANTIGRAVITY_PROMPT_PATTERN = new RegExp(`^(?:>\\s*|${exports.ANTIGRAVITY_COMPOSER_MODE_BANNER_SOURCE})$`, 'm');
|
|
2342
|
-
/**
|
|
2343
|
-
* Antigravity (agy) thinking/processing pattern (Issue #988)
|
|
2344
|
-
* While generating, agy shows a braille spinner with "Generating..." in the
|
|
2345
|
-
* conversation area and an "esc to cancel" hint in the footer status bar. When
|
|
2346
|
-
* idle the footer shows "? for shortcuts" instead, so "esc to cancel" is a
|
|
2347
|
-
* reliable running signal. Braille spinner chars (U+2800-U+28FF) also matched.
|
|
2348
|
-
*/
|
|
2349
|
-
exports.ANTIGRAVITY_THINKING_PATTERN = /[\u2800-\u28FF]|Generating|esc to cancel/;
|
|
2350
|
-
/**
|
|
2351
|
-
* Antigravity (agy) separator pattern (Issue #988)
|
|
2352
|
-
* agy draws turn separators and the input-box border with runs of U+2500 (─).
|
|
2353
|
-
*/
|
|
2354
|
-
exports.ANTIGRAVITY_SEPARATOR_PATTERN = /^─{3,}$/m;
|
|
2355
|
-
/**
|
|
2356
|
-
* Antigravity (agy) selection list pattern (Issue #995, broadened in #997)
|
|
2357
|
-
* Detects agy's interactive arrow-key selection TUIs (e.g. the "Switch Model"
|
|
2358
|
-
* model picker, the "Do you want to proceed?" permission-approval menu). Their
|
|
2359
|
-
* footer status bar renders "esc to cancel", which ANTIGRAVITY_THINKING_PATTERN
|
|
2360
|
-
* also matches, so this pattern must be checked BEFORE thinking detection in
|
|
2361
|
-
* status-detector.ts to keep the selection screen from being misreported as
|
|
2362
|
-
* "generating".
|
|
2363
|
-
*
|
|
2364
|
-
* Matches (either is sufficient):
|
|
2365
|
-
* - The "Switch Model" header of the model picker.
|
|
2366
|
-
* - The "↑/↓ Navigate" arrow-key navigation hint, common to every agy
|
|
2367
|
-
* selection TUI footer. Issue #995 originally required an "enter Select"
|
|
2368
|
-
* hint too, but the permission-approval menu footer is
|
|
2369
|
-
* "↑/↓ Navigate · tab Amend · ctrl+g … · ctrl+r Review" (no "enter Select"),
|
|
2370
|
-
* so #997 relaxes this to the "↑/↓ Navigate" footer alone. This covers the
|
|
2371
|
-
* Switch Model picker, permission-approval menus, and future agy selection
|
|
2372
|
-
* TUIs in one shot, while staying agy-specific (the cliToolId === 'antigravity'
|
|
2373
|
-
* guard in status-detector.ts keeps other tools unaffected).
|
|
2374
|
-
*
|
|
2375
|
-
* No /g flag (S4-5: would make test() stateful).
|
|
2376
|
-
* No `.*` at all (SEC4-001: ReDoS safe — strictly safer than the #995 form).
|
|
2377
|
-
*/
|
|
2378
|
-
exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = /Switch Model|↑\/↓\s*Navigate/m;
|
|
2379
|
-
/**
|
|
2380
|
-
* The `↑/↓ Navigate` hint agy draws under every arrow-key screen it owns
|
|
2381
|
-
* (Issue #2364; measured on agy 1.1.27, pane 200x1000, 2026-09-06).
|
|
2382
|
-
*
|
|
2383
|
-
* The same token {@link ANTIGRAVITY_SELECTION_LIST_PATTERN} matches, split out
|
|
2384
|
-
* because the numbered-dialog reader anchors on THIS row and reads upward from
|
|
2385
|
-
* it. It is the one thing all four measured agy dialogs share — the `Do you want
|
|
2386
|
-
* to proceed?` command menu, the `Allow creation of this file?` file menu, the
|
|
2387
|
-
* folder-trust screen and the `/model` picker all end on it — while the words
|
|
2388
|
-
* after it differ on every one (`tab Amend · f full diff`, `tab Amend · ctrl+g
|
|
2389
|
-
* edit/expand command`, `enter Confirm`, `←/→ Effort enter Select`).
|
|
2390
|
-
*/
|
|
2391
|
-
exports.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN = /↑\/↓\s*Navigate/;
|
|
2392
|
-
/**
|
|
2393
|
-
* One numbered option row of agy's dialogs: `> 1. Yes`, ` 4. No`,
|
|
2394
|
-
* ` 2. No, deny creation`.
|
|
2395
|
-
*
|
|
2396
|
-
* The `>` gutter marks the highlighted row and is optional, because only one
|
|
2397
|
-
* of the rows carries it.
|
|
2398
|
-
*/
|
|
2399
|
-
exports.ANTIGRAVITY_NUMBERED_OPTION_PATTERN = /^\s*>?\s*\d+\.\s+\S/m;
|
|
2400
|
-
/**
|
|
2401
|
-
* The header of agy's `/model` picker (Issue #995).
|
|
2402
|
-
*
|
|
2403
|
-
* Kept as its own exclusion even though the picker's rows already fail the
|
|
2404
|
-
* numbered-row test: the Issue #2364 rule is written as "footer + numbered rows
|
|
2405
|
-
* + NOT the Switch Model picker", and the picker is the one agy screen whose
|
|
2406
|
-
* misreading has a measured cost (#995: NavigationButtons vanished).
|
|
2407
|
-
*/
|
|
2408
|
-
exports.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN = /^\s*Switch Model\s*$/;
|
|
2409
|
-
/**
|
|
2410
|
-
* A row that ends the dialog block when reading UPWARD from the footer
|
|
2411
|
-
* (Issue #2364).
|
|
2412
|
-
*
|
|
2413
|
-
* Everything agy draws above its own dialog panel carries one of these:
|
|
2414
|
-
*
|
|
2415
|
-
* - a horizontal rule (the turn separator, the input-box border, or the rule
|
|
2416
|
-
* under the `Command` / `Create file` panel header);
|
|
2417
|
-
* - a `>`-prefixed row that is not a numbered option — the echoed user prompt,
|
|
2418
|
-
* the bare composer, the highlighted row of an UNNUMBERED picker (`> Gemini
|
|
2419
|
-
* 3.8 Flash`, `> Yes, I trust this folder`) or of the slash-command popup
|
|
2420
|
-
* (`> /add-dir Add a directory …`);
|
|
2421
|
-
* - a `●` tool-call row, a `⎿` tool-result row or a `▸ Thought for …` row.
|
|
2422
|
-
*
|
|
2423
|
-
* Bounding the block at the nearest one is what keeps a numbered list in the
|
|
2424
|
-
* MODEL'S PROSE, sitting in the transcript above an open picker or popup, from
|
|
2425
|
-
* being adopted as that screen's options. Wrapped option labels never start
|
|
2426
|
-
* with any of these on the measured frames: agy prints the command text inside
|
|
2427
|
-
* the quotes of `… commands that start with '<cmd>'`, indented under the row
|
|
2428
|
-
* that opened it.
|
|
2429
|
-
*
|
|
2430
|
-
* `stripBoxDrawing` blanks the rule rows before the response poller's copy of
|
|
2431
|
-
* the frame reaches this rule; the `●` / `>` rows survive it, so the block is
|
|
2432
|
-
* bounded the same way on both paths.
|
|
2433
|
-
*/
|
|
2434
|
-
exports.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN = /^\s*(?:─{3,}\s*$|[●⎿▸]|>(?!\s*\d+\.\s+\S))/;
|
|
2435
|
-
/**
|
|
2436
|
-
* How many rows above the `↑/↓ Navigate` footer the dialog block may reach.
|
|
2437
|
-
*
|
|
2438
|
-
* The tallest measured frame (`dialog-bash-wrapped.txt`: header, rule,
|
|
2439
|
-
* `Requesting permission for:`, a four-row command, the question and four
|
|
2440
|
-
* options of which two wrap onto three rows each) spans 18 rows; the cap leaves
|
|
2441
|
-
* room for a longer command without letting a boundary-free frame drag the
|
|
2442
|
-
* whole transcript into the block.
|
|
2443
|
-
*/
|
|
2444
|
-
exports.ANTIGRAVITY_DIALOG_MAX_ROWS = 60;
|
|
2445
|
-
/**
|
|
2446
|
-
* agy's post-answer survey row (Issue #2364).
|
|
2447
|
-
*
|
|
2448
|
-
* Drawn once, in place of the composer, after some tool decisions:
|
|
2449
|
-
*
|
|
2450
|
-
* ```
|
|
2451
|
-
* How's the CLI experience so far? Help us improve:
|
|
2452
|
-
* [1] Good [2] Fine [3] Bad [0] Skip
|
|
2453
|
-
*
|
|
2454
|
-
* ? for shortcuts …
|
|
2455
|
-
* ```
|
|
2456
|
-
*
|
|
2457
|
-
* Every option sits on ONE row in `[N] label` form and the screen takes a
|
|
2458
|
-
* typed digit, not the arrow keys; there is no `↑/↓ Navigate` footer and no bare
|
|
2459
|
-
* `>` composer, so before this pattern existed the frame reached the `default`
|
|
2460
|
-
* floor — `running`, with the chat surface showing "generating" over a screen
|
|
2461
|
-
* that was waiting for a keypress. Anchored to the whole row.
|
|
2462
|
-
*/
|
|
2463
|
-
exports.ANTIGRAVITY_SURVEY_PATTERN = /^\s*\[1\]\s*Good\s+\[2\]\s*Fine\s+\[3\]\s*Bad\s+\[0\]\s*Skip\s*$/m;
|
|
2464
|
-
/**
|
|
2465
|
-
* Find the rows between the last boundary and the last `↑/↓ Navigate` footer
|
|
2466
|
-
* (Issue #2364).
|
|
2467
|
-
*
|
|
2468
|
-
* The footer is searched from the bottom, so an older dialog still in the
|
|
2469
|
-
* scrollback above a newer one is never the one read. Returns null when the
|
|
2470
|
-
* frame has no footer at all.
|
|
2471
|
-
*
|
|
2472
|
-
* Also null when agy's input box is drawn BELOW that footer (Issue #2845): agy
|
|
2473
|
-
* paints no `>` composer while a dialog is open — the dialog takes its place —
|
|
2474
|
-
* so a footer with a composer under it is the model's reply quoting a dialog,
|
|
2475
|
-
* or a dialog left in the scrollback, not a screen waiting for a keypress. It is
|
|
2476
|
-
* the same reading `isAntigravitySurveyOpen` gives the survey row (#2364), and
|
|
2477
|
-
* the composer is recognised by the same {@link ANTIGRAVITY_PROMPT_PATTERN}.
|
|
2478
|
-
*
|
|
2479
|
-
* @param lines - ANSI-stripped rows, box drawing optional
|
|
2480
|
-
*/
|
|
2481
|
-
function locateAntigravityDialogRegion(lines) {
|
|
2482
|
-
let footer = -1;
|
|
2483
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2484
|
-
if (exports.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN.test(lines[i])) {
|
|
2485
|
-
footer = i;
|
|
2486
|
-
break;
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
if (footer < 0)
|
|
2490
|
-
return null;
|
|
2491
|
-
for (let i = footer + 1; i < lines.length; i++) {
|
|
2492
|
-
if (exports.ANTIGRAVITY_PROMPT_PATTERN.test(lines[i]))
|
|
2493
|
-
return null;
|
|
2494
|
-
}
|
|
2495
|
-
const floor = Math.max(0, footer - exports.ANTIGRAVITY_DIALOG_MAX_ROWS);
|
|
2496
|
-
let start = floor;
|
|
2497
|
-
for (let i = footer - 1; i >= floor; i--) {
|
|
2498
|
-
if (exports.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN.test(lines[i])) {
|
|
2499
|
-
start = i + 1;
|
|
2500
|
-
break;
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
return { start, footer };
|
|
2504
|
-
}
|
|
2505
|
-
/**
|
|
2506
|
-
* Is this frame one of agy's NUMBERED dialogs rather than one of its
|
|
2507
|
-
* arrow-key-only pickers? (Issue #2270, re-measured by Issue #2364)
|
|
2508
|
-
*
|
|
2509
|
-
* Both kinds of screen share the `↑/↓ Navigate` footer that
|
|
2510
|
-
* {@link ANTIGRAVITY_SELECTION_LIST_PATTERN} matches, which is why #997 could
|
|
2511
|
-
* widen that pattern to cover the permission menu — and why the menu then
|
|
2512
|
-
* resolved as `antigravity_selection_list`, `hasActivePrompt: false`. On the
|
|
2513
|
-
* chat surface that reads as "a selection list is open, drive it from the
|
|
2514
|
-
* terminal": the arrow buttons can only Enter the highlighted option 1, so
|
|
2515
|
-
* options 2-4 became unreachable, while the poller and the push notification
|
|
2516
|
-
* described the very same frame as a `multiple_choice` prompt.
|
|
2517
|
-
*
|
|
2518
|
-
* #2270 told the two apart by the question line, `Do you want to proceed?`,
|
|
2519
|
-
* because that was the one dialog it had measured. #2364 measured a second one
|
|
2520
|
-
* — agy 1.1.27's file-creation menu asks `Allow creation of this file?` above
|
|
2521
|
-
* `1. Yes, allow creation` / `2. No, deny creation` — and it fell straight back
|
|
2522
|
-
* into the selection-list reading. So the rule is now about the STRUCTURE the
|
|
2523
|
-
* dialogs share and the pickers lack:
|
|
2524
|
-
*
|
|
2525
|
-
* - the `↑/↓ Navigate` footer,
|
|
2526
|
-
* - at least two `N. label` rows between the nearest boundary row and that
|
|
2527
|
-
* footer ({@link ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN}), and
|
|
2528
|
-
* - no `Switch Model` header in that region.
|
|
2529
|
-
*
|
|
2530
|
-
* The Switch Model picker, the folder-trust screen and the slash-command popup
|
|
2531
|
-
* all draw unnumbered rows (`> Gemini 3.8 Flash`, `> Yes, I trust this folder`,
|
|
2532
|
-
* `> /add-dir …`), so they keep the #995 reading — and because a `>` row that
|
|
2533
|
-
* is not numbered is itself a boundary, a numbered list in the transcript above
|
|
2534
|
-
* one of them cannot be counted.
|
|
2535
|
-
*
|
|
2536
|
-
* True here means "hand the frame to the agy dialog reader"
|
|
2537
|
-
* (`tools/antigravity/dialog.ts`), never "this is a prompt": a frame that
|
|
2538
|
-
* passes this test and still fails to read is published as an unclassified
|
|
2539
|
-
* frame, not as a selection list and never as "generating".
|
|
2540
|
-
*
|
|
2541
|
-
* Callers pass the same text they hand {@link ANTIGRAVITY_SELECTION_LIST_PATTERN}.
|
|
2542
|
-
*/
|
|
2543
|
-
function isAntigravityNumberedDialog(text) {
|
|
2544
|
-
const lines = text.split('\n');
|
|
2545
|
-
const region = locateAntigravityDialogRegion(lines);
|
|
2546
|
-
if (region === null)
|
|
2547
|
-
return false;
|
|
2548
|
-
let numberedRows = 0;
|
|
2549
|
-
for (let i = region.start; i < region.footer; i++) {
|
|
2550
|
-
const line = lines[i];
|
|
2551
|
-
if (exports.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN.test(line))
|
|
2552
|
-
return false;
|
|
2553
|
-
if (exports.ANTIGRAVITY_NUMBERED_OPTION_PATTERN.test(line))
|
|
2554
|
-
numberedRows++;
|
|
2555
|
-
}
|
|
2556
|
-
return numberedRows >= 2;
|
|
166
|
+
const CLI_TOOL_PATTERN_TABLE = {
|
|
167
|
+
claude: {
|
|
168
|
+
promptPattern: patterns_7.CLAUDE_PROMPT_PATTERN,
|
|
169
|
+
separatorPattern: patterns_7.CLAUDE_SEPARATOR_PATTERN,
|
|
170
|
+
thinkingPattern: patterns_7.CLAUDE_THINKING_PATTERN,
|
|
171
|
+
skipPatterns: [
|
|
172
|
+
/^─{10,}$/, // Separator lines
|
|
173
|
+
/^[>❯]\s*$/, // Prompt line (legacy '>' and new '❯')
|
|
174
|
+
patterns_7.CLAUDE_THINKING_PATTERN, // Thinking indicators
|
|
175
|
+
/^\s*[⎿⏋]\s+Tip:/, // Tip lines
|
|
176
|
+
/^\s*Tip:/, // Tip lines
|
|
177
|
+
/^\s*\?\s*for shortcuts/, // Shortcuts hint
|
|
178
|
+
/to interrupt\)/, // Part of "esc to interrupt" message
|
|
179
|
+
pasted_text_1.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212)
|
|
180
|
+
],
|
|
181
|
+
// Full frame: their multiple-choice prompts with descriptions can exceed 15 lines: Codex
|
|
182
|
+
// approval prompts with long file lists, Claude "Yes, and don't ask again for:
|
|
183
|
+
// git commit -m …" options that embed full commit messages. `detectPrompt`
|
|
184
|
+
// applies its own 50-line window internally.
|
|
185
|
+
promptOptions: { requireDefaultIndicator: false },
|
|
186
|
+
fullFramePrompt: true,
|
|
187
|
+
},
|
|
188
|
+
codex: {
|
|
189
|
+
promptPattern: patterns_1.CODEX_PROMPT_PATTERN,
|
|
190
|
+
separatorPattern: patterns_1.CODEX_SEPARATOR_PATTERN,
|
|
191
|
+
thinkingPattern: patterns_1.CODEX_THINKING_PATTERN,
|
|
192
|
+
skipPatterns: [
|
|
193
|
+
/^─.*─+$/, // Separator lines
|
|
194
|
+
/^›\s*$/, // Empty prompt line
|
|
195
|
+
/^›\s+(Implement|Find and fix|Type)/, // New prompt suggestions
|
|
196
|
+
patterns_1.CODEX_THINKING_PATTERN, // Activity indicators
|
|
197
|
+
/^\s*\d+%\s+context left/, // Context indicator
|
|
198
|
+
/^\s*for shortcuts$/, // Shortcuts hint
|
|
199
|
+
/╭─+╮/, // Box drawing (top)
|
|
200
|
+
/╰─+╯/, // Box drawing (bottom)
|
|
201
|
+
// T1.3: Additional skip patterns for Codex
|
|
202
|
+
/•\s*Ran\s+/, // Command execution lines
|
|
203
|
+
/^\s*└/, // Tree output (completion indicator)
|
|
204
|
+
/^\s*│/, // Continuation lines
|
|
205
|
+
/\(.*esc to interrupt\)/, // Interrupt hint
|
|
206
|
+
pasted_text_1.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212, defensive)
|
|
207
|
+
],
|
|
208
|
+
// Full frame: their multiple-choice prompts with descriptions can exceed 15 lines: Codex
|
|
209
|
+
// approval prompts with long file lists, Claude "Yes, and don't ask again for:
|
|
210
|
+
// git commit -m …" options that embed full commit messages. `detectPrompt`
|
|
211
|
+
// applies its own 50-line window internally.
|
|
212
|
+
promptOptions: undefined,
|
|
213
|
+
fullFramePrompt: true,
|
|
214
|
+
},
|
|
215
|
+
gemini: {
|
|
216
|
+
promptPattern: patterns_9.GEMINI_PROMPT_PATTERN,
|
|
217
|
+
separatorPattern: /^[─━]{3,}$/m,
|
|
218
|
+
thinkingPattern: patterns_9.GEMINI_THINKING_PATTERN,
|
|
219
|
+
skipPatterns: [
|
|
220
|
+
patterns_9.GEMINI_PROMPT_PATTERN, // Prompt line (DRY: shared with GEMINI_PROMPT_PATTERN)
|
|
221
|
+
patterns_9.GEMINI_THINKING_PATTERN, // Thinking indicators
|
|
222
|
+
/^\s*$/, // Empty lines
|
|
223
|
+
/Gemini\s+\d+\.\d+/, // Version line
|
|
224
|
+
pasted_text_1.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
225
|
+
],
|
|
226
|
+
promptOptions: undefined,
|
|
227
|
+
fullFramePrompt: false,
|
|
228
|
+
},
|
|
229
|
+
'vibe-local': {
|
|
230
|
+
promptPattern: patterns_11.VIBE_LOCAL_PROMPT_PATTERN,
|
|
231
|
+
separatorPattern: /^[·]{10,}$/m, // vibe-local uses middle dot separators
|
|
232
|
+
thinkingPattern: patterns_11.VIBE_LOCAL_THINKING_PATTERN,
|
|
233
|
+
skipPatterns: [
|
|
234
|
+
patterns_11.VIBE_LOCAL_PROMPT_PATTERN, // Prompt line (ctx:N% ❯)
|
|
235
|
+
patterns_11.VIBE_LOCAL_THINKING_PATTERN, // Thinking indicators
|
|
236
|
+
/^\s*$/, // Empty lines
|
|
237
|
+
/vibe-local|vibe-coder/, // Version/banner lines
|
|
238
|
+
/ctx:\s*\d+%/, // Context usage indicator
|
|
239
|
+
/Model\s+\w/, // Model info line
|
|
240
|
+
/Engine\s+\w/, // Engine info line
|
|
241
|
+
/Mode\s+/, // Mode info line
|
|
242
|
+
/RAM\s+/, // RAM info line
|
|
243
|
+
/CWD\s+/, // Working directory line
|
|
244
|
+
/^[·]{10,}$/, // Middle dot separator lines
|
|
245
|
+
/✦\s*Ready/, // Status bar "Ready" indicator
|
|
246
|
+
/ESC:\s*stop/, // Status bar "ESC: stop" hint
|
|
247
|
+
pasted_text_1.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
248
|
+
],
|
|
249
|
+
promptOptions: undefined,
|
|
250
|
+
fullFramePrompt: false,
|
|
251
|
+
},
|
|
252
|
+
opencode: {
|
|
253
|
+
promptPattern: patterns_3.OPENCODE_PROMPT_PATTERN,
|
|
254
|
+
separatorPattern: patterns_3.OPENCODE_SEPARATOR_PATTERN,
|
|
255
|
+
thinkingPattern: patterns_3.OPENCODE_THINKING_PATTERN,
|
|
256
|
+
skipPatterns: patterns_3.OPENCODE_SKIP_PATTERNS,
|
|
257
|
+
// [D2-006] OpenCode prompt "Ask anything..." does not use standard indicators (> / ❯),
|
|
258
|
+
// so requireDefaultIndicator must be false to avoid missing prompt detection.
|
|
259
|
+
//
|
|
260
|
+
// [Issue #1896] `hasNumberedDialogs: false` -- opencode 1.18 renders NO dialog
|
|
261
|
+
// that a typed number drives, so the generic numbered-list inference has
|
|
262
|
+
// nothing to find on its pane and every hit it scored was transcript text.
|
|
263
|
+
// Its two interactive surfaces were both measured at the production 80x200
|
|
264
|
+
// geometry and both are cursor-driven:
|
|
265
|
+
//
|
|
266
|
+
// - the permission dialog is a horizontal button strip
|
|
267
|
+
// ({@link OPENCODE_PERMISSION_PATTERN}, Issue #1893) driven by ←/→ + Enter;
|
|
268
|
+
// typing a number does nothing to it.
|
|
269
|
+
// - the pickers (`/models`, `/providers`, `/connect`, and the ctrl+p command
|
|
270
|
+
// palette) are fuzzy-search lists driven by ↑/↓ + Enter, with no numbers
|
|
271
|
+
// drawn at all. The first three are what
|
|
272
|
+
// {@link OPENCODE_SELECTION_LIST_PATTERN} names; the palette shares the
|
|
273
|
+
// chrome but not the header allowlist, and lands on `running` / `default`.
|
|
274
|
+
//
|
|
275
|
+
// Both keep their own POSITIVE detection in `status-detector.ts`, so `wait`
|
|
276
|
+
// still stops for them (exit 10 via `isSelectionListActive`) and the UI still
|
|
277
|
+
// renders NavigationButtons: nothing that could be answered before stops being
|
|
278
|
+
// answered. What ends is the false positive -- a response whose body ends in
|
|
279
|
+
// `1. / 2. / 3.` + a question was published as
|
|
280
|
+
// `waiting`/`prompt_detected`/`hasActivePrompt: true`, and Auto-Yes typed `1`
|
|
281
|
+
// into the composer and SENT IT as a user utterance (Issue #1896).
|
|
282
|
+
//
|
|
283
|
+
// `requireDefaultIndicator` is kept at its D2-006 value: it is the correct
|
|
284
|
+
// setting for opencode's ❯-less rendering should the numbered path ever be
|
|
285
|
+
// re-enabled, and it still describes the tool.
|
|
286
|
+
// Full frame: their multiple-choice prompts with descriptions can exceed 15 lines: Codex
|
|
287
|
+
// approval prompts with long file lists, Claude "Yes, and don't ask again for:
|
|
288
|
+
// git commit -m …" options that embed full commit messages. `detectPrompt`
|
|
289
|
+
// applies its own 50-line window internally.
|
|
290
|
+
promptOptions: { requireDefaultIndicator: false, hasNumberedDialogs: false },
|
|
291
|
+
fullFramePrompt: true,
|
|
292
|
+
},
|
|
293
|
+
copilot: {
|
|
294
|
+
promptPattern: patterns_13.COPILOT_PROMPT_PATTERN,
|
|
295
|
+
separatorPattern: patterns_13.COPILOT_SEPARATOR_PATTERN,
|
|
296
|
+
thinkingPattern: patterns_13.COPILOT_THINKING_PATTERN,
|
|
297
|
+
skipPatterns: patterns_13.COPILOT_SKIP_PATTERNS,
|
|
298
|
+
// [Issue #545] Copilot prompt pattern may not use standard indicators
|
|
299
|
+
// Full frame: their multiple-choice prompts with descriptions can exceed 15 lines: Codex
|
|
300
|
+
// approval prompts with long file lists, Claude "Yes, and don't ask again for:
|
|
301
|
+
// git commit -m …" options that embed full commit messages. `detectPrompt`
|
|
302
|
+
// applies its own 50-line window internally.
|
|
303
|
+
promptOptions: { requireDefaultIndicator: false },
|
|
304
|
+
fullFramePrompt: true,
|
|
305
|
+
},
|
|
306
|
+
antigravity: {
|
|
307
|
+
promptPattern: patterns_15.ANTIGRAVITY_PROMPT_PATTERN,
|
|
308
|
+
separatorPattern: patterns_15.ANTIGRAVITY_SEPARATOR_PATTERN,
|
|
309
|
+
thinkingPattern: patterns_15.ANTIGRAVITY_THINKING_PATTERN,
|
|
310
|
+
skipPatterns: patterns_15.ANTIGRAVITY_SKIP_PATTERNS,
|
|
311
|
+
// [Issue #999] Antigravity (agy) permission-approval menus highlight the
|
|
312
|
+
// default with an ASCII ">" (0x3E), not the "❯/●/›" indicators that
|
|
313
|
+
// DEFAULT_OPTION_PATTERN recognizes, and their footer is "↑/↓ Navigate"
|
|
314
|
+
// (no "press enter to confirm"). Under the default requireDefaultIndicator=true
|
|
315
|
+
// the Pass 1 gate rejects these menus, so Auto-Yes never responds. Treat agy
|
|
316
|
+
// like claude/opencode/copilot so Pass 2 collects its "1. Yes / … / N. No"
|
|
317
|
+
// options and reports isPrompt=true.
|
|
318
|
+
//
|
|
319
|
+
// [Issue #2364] The `↑/↓ Navigate` dialogs themselves no longer reach
|
|
320
|
+
// `detectPrompt` on either production path: `tools/antigravity/detect.ts`
|
|
321
|
+
// (status) and `detectPromptWithOptions` (response poller) both read them
|
|
322
|
+
// with `detectAntigravityNumberedDialogPrompt` first, because the generic
|
|
323
|
+
// multiple-choice pass reads one row per option and agy wraps a long command
|
|
324
|
+
// across several rows of one label. What this setting still serves is every
|
|
325
|
+
// OTHER numbered agy screen — `/feedback`'s `1-6 Select & Continue` menu is
|
|
326
|
+
// the measured one — which the generic pass reads as before.
|
|
327
|
+
promptOptions: { requireDefaultIndicator: false },
|
|
328
|
+
fullFramePrompt: false,
|
|
329
|
+
},
|
|
330
|
+
// Issue #2250: Command Code's layout is claude-shaped (inline transcript,
|
|
331
|
+
// `❯` composer fenced by two full-width rules) but the constants are its
|
|
332
|
+
// own. Sharing claude's would import the exact defect #2247 had to undo --
|
|
333
|
+
// claude's rules carry a startup-banner reading that keys on `v\d+\.\d+`
|
|
334
|
+
// and `|`, and Command Code prints its version into a `# Command Code
|
|
335
|
+
// v1.40.1` row on every launch.
|
|
336
|
+
'command-code': {
|
|
337
|
+
promptPattern: patterns_17.COMMAND_CODE_PROMPT_PATTERN,
|
|
338
|
+
separatorPattern: patterns_17.COMMAND_CODE_SEPARATOR_PATTERN,
|
|
339
|
+
thinkingPattern: patterns_17.COMMAND_CODE_THINKING_PATTERN,
|
|
340
|
+
skipPatterns: patterns_17.COMMAND_CODE_SKIP_PATTERNS,
|
|
341
|
+
promptOptions: undefined,
|
|
342
|
+
fullFramePrompt: false,
|
|
343
|
+
},
|
|
344
|
+
// Issue #2934: OpenCode V2's own constants (see OPENCODE_V2_* above). The
|
|
345
|
+
// separator row is the same half-block rule v1 draws, so v1's pattern is
|
|
346
|
+
// reused as a value; nothing of v1's is changed.
|
|
347
|
+
'opencode-v2': {
|
|
348
|
+
promptPattern: patterns_5.OPENCODE_V2_IDLE_COMPOSER_PATTERN,
|
|
349
|
+
separatorPattern: patterns_3.OPENCODE_SEPARATOR_PATTERN,
|
|
350
|
+
thinkingPattern: patterns_5.OPENCODE_V2_THINKING_PATTERN,
|
|
351
|
+
skipPatterns: patterns_5.OPENCODE_V2_SKIP_PATTERNS,
|
|
352
|
+
promptOptions: undefined,
|
|
353
|
+
fullFramePrompt: false,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* The table's own row for an id, or undefined. The table is a plain object, so a
|
|
358
|
+
* name such as 'constructor' or '__proto__' must not reach Object.prototype.
|
|
359
|
+
*/
|
|
360
|
+
function ownRowFor(cliToolId) {
|
|
361
|
+
return Object.prototype.hasOwnProperty.call(CLI_TOOL_PATTERN_TABLE, cliToolId)
|
|
362
|
+
? CLI_TOOL_PATTERN_TABLE[cliToolId]
|
|
363
|
+
: undefined;
|
|
2557
364
|
}
|
|
2558
365
|
/**
|
|
2559
|
-
*
|
|
2560
|
-
*
|
|
2561
|
-
* bar ("? for shortcuts ... <model>"), the thinking footer/spinner, banner block
|
|
2562
|
-
* art, and pasted-text markers from extracted responses.
|
|
2563
|
-
*/
|
|
2564
|
-
exports.ANTIGRAVITY_SKIP_PATTERNS = [
|
|
2565
|
-
exports.ANTIGRAVITY_SEPARATOR_PATTERN, // Turn + input-box separators (─ runs)
|
|
2566
|
-
/^>\s*$/, // Bare input prompt line
|
|
2567
|
-
/^\?\s+for\s+shortcuts/, // Idle status bar (model name follows on the same line)
|
|
2568
|
-
exports.ANTIGRAVITY_THINKING_PATTERN, // Spinner / Generating / "esc to cancel" footer
|
|
2569
|
-
/[▄▀█▌▐]/, // Banner block art (defensive; normally above the user-prompt anchor)
|
|
2570
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
2571
|
-
];
|
|
2572
|
-
/**
|
|
2573
|
-
* Command Code status-line spinner glyphs (Issue #2250).
|
|
2574
|
-
*
|
|
2575
|
-
* The complete `getWaveSymbol` frame set, read off the shipped bundle
|
|
2576
|
-
* (`command-code@1.40.1`, `dist/cli.mjs`):
|
|
2577
|
-
* `["·","○","◇","☆","✧","☆","◇","○","⌘"]` — six distinct glyphs, cycled.
|
|
2578
|
-
*
|
|
2579
|
-
* Deliberately NOT folded into {@link CLAUDE_SPINNER_CHARS}. Command Code marks
|
|
2580
|
-
* every assistant message with `⠶` (U+2836, see
|
|
2581
|
-
* {@link COMMAND_CODE_RESPONSE_MARKER_PATTERN}); adding that braille glyph to a
|
|
2582
|
-
* shared spinner class would make each reply's own first row read as "still
|
|
2583
|
-
* generating", and adding these glyphs to claude's class would do the same to
|
|
2584
|
-
* any claude reply that opens with `·`.
|
|
2585
|
-
*/
|
|
2586
|
-
exports.COMMAND_CODE_SPINNER_CHARS = ['·', '○', '◇', '☆', '✧', '⌘'];
|
|
2587
|
-
/**
|
|
2588
|
-
* Command Code composer / dialog-cursor prompt pattern (Issue #2250).
|
|
2589
|
-
*
|
|
2590
|
-
* `❯` (U+276F) only — the ASCII `>` claude also accepts is not a glyph Command
|
|
2591
|
-
* Code 1.40.1 draws, and accepting it here would make every shell prompt and
|
|
2592
|
-
* every quoted diff line in a reply look like a composer.
|
|
2593
|
-
*
|
|
2594
|
-
* The row is drawn in two places, which is why prompt presence alone is not
|
|
2595
|
-
* "ready": the bottom-pinned composer (`❯ Ask your question...` between two
|
|
2596
|
-
* full-width rules) and the highlighted option of a permission dialog
|
|
2597
|
-
* (`❯ 1. Yes`). Completion is resolved structurally instead — see
|
|
2598
|
-
* {@link findCommandCodeChromeStart}.
|
|
2599
|
-
*/
|
|
2600
|
-
exports.COMMAND_CODE_PROMPT_PATTERN = /^❯(\s*$|\s+\S)/m;
|
|
2601
|
-
/**
|
|
2602
|
-
* Command Code separator pattern (Issue #2250).
|
|
2603
|
-
*
|
|
2604
|
-
* Full-pane runs of U+2500 fence the composer above and below, and one more is
|
|
2605
|
-
* drawn above a permission dialog. Measured at 200 columns on every fixture in
|
|
2606
|
-
* `tests/fixtures/command-code-live-2250/`.
|
|
2607
|
-
*/
|
|
2608
|
-
exports.COMMAND_CODE_SEPARATOR_PATTERN = /^─{10,}$/m;
|
|
2609
|
-
/**
|
|
2610
|
-
* Command Code status-line "esc to interrupt" hint (Issue #2250).
|
|
2611
|
-
*
|
|
2612
|
-
* The `Status` component renders `esc to interrupt • <elapsed> • ↓ <tokens>`
|
|
2613
|
-
* only in its `"all"` layout, i.e. at a terminal width of 72 columns or more
|
|
2614
|
-
* (read off the bundle's `layoutMode` ladder; CommandMate panes are 200 wide, so
|
|
2615
|
-
* production always takes that branch). Below 42 columns the whole tail is
|
|
2616
|
-
* dropped and only the spinner + verb remain — which is why
|
|
2617
|
-
* {@link COMMAND_CODE_THINKING_PATTERN} keeps the spinner branch as well.
|
|
2618
|
-
*/
|
|
2619
|
-
exports.COMMAND_CODE_INTERRUPT_HINT_PATTERN = /esc to interrupt/;
|
|
2620
|
-
/**
|
|
2621
|
-
* Command Code thinking/processing pattern (Issue #2250).
|
|
2622
|
-
*
|
|
2623
|
-
* Three measured alternatives:
|
|
2624
|
-
*
|
|
2625
|
-
* 1. the status row — a spinner glyph, then a single word ending in `…`
|
|
2626
|
-
* (`⌘ Planning…`, `· Synthesizing…`; the bundle's verb table is 74
|
|
2627
|
-
* single-word entries, all capitalised, but the class is left case-agnostic
|
|
2628
|
-
* because the `status` prop is not restricted to that table). Anchored at
|
|
2629
|
-
* the start of the line with only leading spaces allowed, so the `·` in the
|
|
2630
|
-
* banner's
|
|
2631
|
-
* `# models: … · taste-1` row and in the idle footer's
|
|
2632
|
-
* `? for shortcuts · taste on` cannot reach it;
|
|
2633
|
-
* 2. `✻ Thinking…` — the reasoning block's header WHILE it streams. It becomes
|
|
2634
|
-
* `✻ Thought for 1 second [ctrl+o to expand]` once the block is closed, and
|
|
2635
|
-
* that past-tense form must NOT match: it sits in the transcript of every
|
|
2636
|
-
* finished turn (`turn-version.txt`);
|
|
2637
|
-
* 3. {@link COMMAND_CODE_INTERRUPT_HINT_PATTERN}, unanchored, for the same
|
|
2638
|
-
* reason claude's pattern carries it.
|
|
2639
|
-
*/
|
|
2640
|
-
exports.COMMAND_CODE_THINKING_PATTERN = new RegExp(`^[^\\S\\n]*[${exports.COMMAND_CODE_SPINNER_CHARS.join('')}][^\\S\\n]+[A-Za-z]+…` +
|
|
2641
|
-
`|^[^\\S\\n]*✻ Thinking…` +
|
|
2642
|
-
`|${exports.COMMAND_CODE_INTERRUPT_HINT_PATTERN.source}`, 'm');
|
|
2643
|
-
/**
|
|
2644
|
-
* Command Code assistant-message marker (Issue #2250).
|
|
2645
|
-
*
|
|
2646
|
-
* `⠶` (U+2836) at column 0, one space, then the reply. Fixed, not a spinner
|
|
2647
|
-
* frame: the bundle declares it as `Ct() ? "⠶" : "#"`, i.e. one constant with an
|
|
2648
|
-
* ASCII fallback for terminals without unicode support. That answers 親 Issue
|
|
2649
|
-
* #2249's 未確定事項 1 — the glyph does not rotate — and it is also why the `#`
|
|
2650
|
-
* fallback is deliberately NOT matched here: on such a terminal it is
|
|
2651
|
-
* indistinguishable from the `# Command Code v1.40.1` banner rows.
|
|
2652
|
-
*
|
|
2653
|
-
* Continuation rows of a multi-line reply are indented by two spaces (the marker
|
|
2654
|
-
* is one column wide and the body box carries `marginLeft: 1`).
|
|
2655
|
-
*/
|
|
2656
|
-
exports.COMMAND_CODE_RESPONSE_MARKER_PATTERN = /^⠶(?:\s|$)/;
|
|
2657
|
-
/**
|
|
2658
|
-
* Command Code turn-completion marker (Issue #2250).
|
|
2659
|
-
*
|
|
2660
|
-
* **Advisory only — never require it to declare a turn finished.** Two measured
|
|
2661
|
-
* reasons: `WorkedDurationNote` renders nothing for a turn under 1000 ms, and
|
|
2662
|
-
* the row belongs to the live turn's UI rather than to the transcript — it is
|
|
2663
|
-
* present in `turn-version.txt` and GONE from `dialog-create-file.txt`, which is
|
|
2664
|
-
* the same pane one prompt later.
|
|
2665
|
-
*/
|
|
2666
|
-
exports.COMMAND_CODE_COMPLETION_PATTERN = /^[^\S\n]*✻ Worked for /m;
|
|
2667
|
-
/**
|
|
2668
|
-
* Command Code footer mode indicator (Issue #2250).
|
|
2669
|
-
*
|
|
2670
|
-
* The row under the composer's closing rule. `? for shortcuts` is only the
|
|
2671
|
-
* DEFAULT-mode spelling: the bundle's `ModeIndicator` swaps it for a mode banner
|
|
2672
|
-
* in the other four permission modes, so a rule that keys on `? for shortcuts`
|
|
2673
|
-
* alone would stop recognising an idle pane the moment the operator pressed
|
|
2674
|
-
* shift+tab. All five spellings are listed.
|
|
2675
|
-
*/
|
|
2676
|
-
exports.COMMAND_CODE_MODE_INDICATOR_PATTERN = /\?\s+for\s+shortcuts|»\s+accept edits on|»\s+permission bypass on|»\s+don't-ask on|^[^\S\n]*plan mode\s/m;
|
|
2677
|
-
/**
|
|
2678
|
-
* Command Code startup banner rows (Issue #2250).
|
|
2679
|
-
*
|
|
2680
|
-
* The three-row header under the block-art logo — `# Command Code v1.40.1`,
|
|
2681
|
-
* `# models: …`, `# <cwd>` — plus the logo itself.
|
|
2682
|
-
*
|
|
2683
|
-
* The version row matches the tool's own NAME and version together, which is the
|
|
2684
|
-
* shape #2247 had to retreat to on claude: a bare `v\d+\.\d+` matched any reply
|
|
2685
|
-
* that mentioned a release, and the turn was silently dropped. Nothing here
|
|
2686
|
-
* matches a bare version string, a `│` table glyph, or a `Tip:` line.
|
|
2687
|
-
*/
|
|
2688
|
-
exports.COMMAND_CODE_BANNER_PATTERNS = [
|
|
2689
|
-
/^#\s+Command Code v\d/, // Name + version, together
|
|
2690
|
-
/^#\s+models:\s/, // Model line
|
|
2691
|
-
/^#\s+[~/]/, // Working-directory line
|
|
2692
|
-
/^[^\S\n]*[█▀▄▌▐]{3,}/, // Block-art logo rows
|
|
2693
|
-
];
|
|
2694
|
-
/**
|
|
2695
|
-
* Command Code hook notice row (Issue #2250).
|
|
2696
|
-
*
|
|
2697
|
-
* `◼ Ran 1 session start hook` — emitted into the transcript when SessionStart
|
|
2698
|
-
* hooks fire, so it lands ABOVE the first user echo and is chrome, not a reply.
|
|
2699
|
-
* Measured on a hooks-enabled 1.40.1 pane while capturing #2249's evidence.
|
|
2700
|
-
* Command Code's hook wiring itself is Phase B (#2251); this row only has to be
|
|
2701
|
-
* kept out of History.
|
|
2702
|
-
*/
|
|
2703
|
-
exports.COMMAND_CODE_HOOK_NOTICE_PATTERN = /^[^\S\n]*◼\s+Ran\s+\d+\s+.*hooks?\b/;
|
|
2704
|
-
/** How far above the last row Command Code's footer row may sit. */
|
|
2705
|
-
const COMMAND_CODE_FOOTER_MAX_ROWS = 4;
|
|
2706
|
-
/** How many rows the composer may span before the block stops looking like chrome. */
|
|
2707
|
-
const COMMAND_CODE_INPUT_BOX_MAX_ROWS = 40;
|
|
2708
|
-
/**
|
|
2709
|
-
* Locate the start of Command Code's bottom-pinned chrome within a captured pane.
|
|
2710
|
-
*
|
|
2711
|
-
* Command Code is inline-rendered, so its transcript grows downwards and the
|
|
2712
|
-
* last four rows of a settled pane are always the same four (measured on
|
|
2713
|
-
* `boot-idle.txt`, `turn-version.txt` and `turn-tool-write.txt`):
|
|
2714
|
-
*
|
|
2715
|
-
* ```text
|
|
2716
|
-
* ──────────────────── ← opening rule
|
|
2717
|
-
* ❯ Ask your question… ← composer (one row per wrapped line)
|
|
2718
|
-
* ──────────────────── ← closing rule
|
|
2719
|
-
* ? for shortcuts · taste on
|
|
2720
|
-
* ```
|
|
2721
|
-
*
|
|
2722
|
-
* Everything from the opening rule down is terminal furniture. Two things go
|
|
2723
|
-
* wrong if it reaches the extractor, and both are regressions this repository
|
|
2724
|
-
* has already paid for once: the composer's placeholder is drawn with the same
|
|
2725
|
-
* `❯ <text>` shape as a transcript echo, so the turn anchor lands on the FOOTER
|
|
2726
|
-
* and the reply extracts as empty (#1289); and the footer row is repainted while
|
|
2727
|
-
* the pane sits idle, so keeping it re-hashes the saved response on every poll
|
|
2728
|
-
* tick (#1268 / #1289).
|
|
2729
|
-
*
|
|
2730
|
-
* Structural, like the three readers next to it, and for the reason spelled out
|
|
2731
|
-
* on `findClaudeChromeStart`: the hint strings belong to Command Code and a rule
|
|
2732
|
-
* that matches them stops working the moment they are reworded. `-1` is the
|
|
2733
|
-
* honest answer for a frame with no composer at all — while a permission dialog
|
|
2734
|
-
* is up the whole block is replaced by the dialog, which the caller resolves on
|
|
2735
|
-
* the prompt path instead.
|
|
2736
|
-
*
|
|
2737
|
-
* @param lines - Captured pane lines, ANSI-bearing or not; trailing blanks tolerated
|
|
2738
|
-
* @returns Index of the opening rule, or -1 when no composer block is present
|
|
366
|
+
* Row for a tool id. An id outside CLI_TOOL_IDS (possible at runtime) falls back
|
|
367
|
+
* to claude's row, as the former `default` branches did.
|
|
2739
368
|
*/
|
|
2740
|
-
function
|
|
2741
|
-
|
|
2742
|
-
let lastRow = lines.length - 1;
|
|
2743
|
-
while (lastRow >= 0 && lines[lastRow].trim() === '')
|
|
2744
|
-
lastRow--;
|
|
2745
|
-
if (lastRow < 0)
|
|
2746
|
-
return -1;
|
|
2747
|
-
// The closing rule sits just above the mode-indicator row.
|
|
2748
|
-
let closingSeparator = -1;
|
|
2749
|
-
for (let i = lastRow; i >= Math.max(0, lastRow - COMMAND_CODE_FOOTER_MAX_ROWS); i--) {
|
|
2750
|
-
if (isSeparator(lines[i])) {
|
|
2751
|
-
closingSeparator = i;
|
|
2752
|
-
break;
|
|
2753
|
-
}
|
|
2754
|
-
}
|
|
2755
|
-
if (closingSeparator < 0)
|
|
2756
|
-
return -1;
|
|
2757
|
-
// Walk up over the composer rows to the opening rule.
|
|
2758
|
-
let openingSeparator = -1;
|
|
2759
|
-
for (let i = closingSeparator - 1; i >= Math.max(0, closingSeparator - COMMAND_CODE_INPUT_BOX_MAX_ROWS); i--) {
|
|
2760
|
-
if (isSeparator(lines[i])) {
|
|
2761
|
-
openingSeparator = i;
|
|
2762
|
-
break;
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
if (openingSeparator < 0)
|
|
2766
|
-
return -1;
|
|
2767
|
-
// Confirm the fenced rows are the composer and not a reply that happens to
|
|
2768
|
-
// contain two horizontal rules.
|
|
2769
|
-
if (!/^❯/.test((0, ansi_1.stripAnsi)(lines[openingSeparator + 1] ?? '')))
|
|
2770
|
-
return -1;
|
|
2771
|
-
return openingSeparator;
|
|
369
|
+
function patternRowFor(cliToolId) {
|
|
370
|
+
return ownRowFor(cliToolId) ?? CLI_TOOL_PATTERN_TABLE.claude;
|
|
2772
371
|
}
|
|
2773
|
-
/**
|
|
2774
|
-
* Command Code skip patterns for response cleaning (Issue #2250).
|
|
2775
|
-
*
|
|
2776
|
-
* The dedicated cleaner Issue #2250 item 8 asks for: the startup banner, the
|
|
2777
|
-
* hook notice, the reasoning and turn summaries (`✻ Thought for` / `✻ Worked
|
|
2778
|
-
* for`), the composer, the rules and the footer.
|
|
2779
|
-
*
|
|
2780
|
-
* Nothing here touches the reply body or a tool block: `⠶ <text>`, ` WRITE
|
|
2781
|
-
* [probe.txt]`, ` └ Created probe.txt (1 line)` and ` 1 │ hello` all
|
|
2782
|
-
* survive (`turn-tool-write.txt`). In particular there is no `^\s*│` rule — the
|
|
2783
|
-
* one codex carries — because Command Code renders file previews with it.
|
|
2784
|
-
*/
|
|
2785
|
-
exports.COMMAND_CODE_SKIP_PATTERNS = [
|
|
2786
|
-
/^─{10,}$/, // Composer rules and the dialog's rule
|
|
2787
|
-
/^❯\s*$/, // Bare composer row
|
|
2788
|
-
/^❯\s+Ask your question\.\.\./, // Composer placeholder
|
|
2789
|
-
exports.COMMAND_CODE_MODE_INDICATOR_PATTERN, // Footer mode indicator
|
|
2790
|
-
exports.COMMAND_CODE_THINKING_PATTERN, // Status row
|
|
2791
|
-
/^[^\S\n]*✻\s+(?:Worked|Thought)\s+for\b/, // Turn / reasoning summaries
|
|
2792
|
-
exports.COMMAND_CODE_HOOK_NOTICE_PATTERN, // "◼ Ran N session start hook"
|
|
2793
|
-
...exports.COMMAND_CODE_BANNER_PATTERNS, // Startup banner
|
|
2794
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
2795
|
-
];
|
|
2796
372
|
/**
|
|
2797
373
|
* Detect if CLI tool is showing "thinking" indicator
|
|
2798
374
|
*/
|
|
2799
375
|
function detectThinking(cliToolId, content) {
|
|
2800
376
|
const log = logger.withContext({ cliToolId });
|
|
2801
377
|
log.debug('detectThinking:check', { contentLength: content.length });
|
|
2802
|
-
|
|
2803
|
-
switch (cliToolId) {
|
|
2804
|
-
case 'claude':
|
|
2805
|
-
result = exports.CLAUDE_THINKING_PATTERN.test(content);
|
|
2806
|
-
break;
|
|
2807
|
-
case 'codex':
|
|
2808
|
-
result = exports.CODEX_THINKING_PATTERN.test(content);
|
|
2809
|
-
break;
|
|
2810
|
-
case 'gemini':
|
|
2811
|
-
result = exports.GEMINI_THINKING_PATTERN.test(content);
|
|
2812
|
-
break;
|
|
2813
|
-
case 'vibe-local':
|
|
2814
|
-
result = exports.VIBE_LOCAL_THINKING_PATTERN.test(content);
|
|
2815
|
-
break;
|
|
2816
|
-
case 'opencode':
|
|
2817
|
-
result = exports.OPENCODE_THINKING_PATTERN.test(content);
|
|
2818
|
-
break;
|
|
2819
|
-
case 'copilot':
|
|
2820
|
-
result = exports.COPILOT_THINKING_PATTERN.test(content);
|
|
2821
|
-
break;
|
|
2822
|
-
case 'antigravity':
|
|
2823
|
-
result = exports.ANTIGRAVITY_THINKING_PATTERN.test(content);
|
|
2824
|
-
break;
|
|
2825
|
-
case 'command-code':
|
|
2826
|
-
result = exports.COMMAND_CODE_THINKING_PATTERN.test(content);
|
|
2827
|
-
break;
|
|
2828
|
-
case 'opencode-v2':
|
|
2829
|
-
result = exports.OPENCODE_V2_THINKING_PATTERN.test(content);
|
|
2830
|
-
break;
|
|
2831
|
-
default:
|
|
2832
|
-
result = exports.CLAUDE_THINKING_PATTERN.test(content);
|
|
2833
|
-
}
|
|
378
|
+
const result = patternRowFor(cliToolId).thinkingPattern.test(content);
|
|
2834
379
|
log.debug('detectThinking:result', { isThinking: result });
|
|
2835
380
|
return result;
|
|
2836
381
|
}
|
|
@@ -2838,190 +383,20 @@ function detectThinking(cliToolId, content) {
|
|
|
2838
383
|
* Get CLI tool patterns for response extraction
|
|
2839
384
|
*/
|
|
2840
385
|
function getCliToolPatterns(cliToolId) {
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
/^─{10,}$/, // Separator lines
|
|
2849
|
-
/^[>❯]\s*$/, // Prompt line (legacy '>' and new '❯')
|
|
2850
|
-
exports.CLAUDE_THINKING_PATTERN, // Thinking indicators
|
|
2851
|
-
/^\s*[⎿⏋]\s+Tip:/, // Tip lines
|
|
2852
|
-
/^\s*Tip:/, // Tip lines
|
|
2853
|
-
/^\s*\?\s*for shortcuts/, // Shortcuts hint
|
|
2854
|
-
/to interrupt\)/, // Part of "esc to interrupt" message
|
|
2855
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212)
|
|
2856
|
-
],
|
|
2857
|
-
};
|
|
2858
|
-
case 'codex':
|
|
2859
|
-
return {
|
|
2860
|
-
promptPattern: exports.CODEX_PROMPT_PATTERN,
|
|
2861
|
-
separatorPattern: exports.CODEX_SEPARATOR_PATTERN,
|
|
2862
|
-
thinkingPattern: exports.CODEX_THINKING_PATTERN,
|
|
2863
|
-
skipPatterns: [
|
|
2864
|
-
/^─.*─+$/, // Separator lines
|
|
2865
|
-
/^›\s*$/, // Empty prompt line
|
|
2866
|
-
/^›\s+(Implement|Find and fix|Type)/, // New prompt suggestions
|
|
2867
|
-
exports.CODEX_THINKING_PATTERN, // Activity indicators
|
|
2868
|
-
/^\s*\d+%\s+context left/, // Context indicator
|
|
2869
|
-
/^\s*for shortcuts$/, // Shortcuts hint
|
|
2870
|
-
/╭─+╮/, // Box drawing (top)
|
|
2871
|
-
/╰─+╯/, // Box drawing (bottom)
|
|
2872
|
-
// T1.3: Additional skip patterns for Codex
|
|
2873
|
-
/•\s*Ran\s+/, // Command execution lines
|
|
2874
|
-
/^\s*└/, // Tree output (completion indicator)
|
|
2875
|
-
/^\s*│/, // Continuation lines
|
|
2876
|
-
/\(.*esc to interrupt\)/, // Interrupt hint
|
|
2877
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212, defensive)
|
|
2878
|
-
],
|
|
2879
|
-
};
|
|
2880
|
-
case 'gemini':
|
|
2881
|
-
return {
|
|
2882
|
-
promptPattern: exports.GEMINI_PROMPT_PATTERN,
|
|
2883
|
-
separatorPattern: /^[─━]{3,}$/m,
|
|
2884
|
-
thinkingPattern: exports.GEMINI_THINKING_PATTERN,
|
|
2885
|
-
skipPatterns: [
|
|
2886
|
-
exports.GEMINI_PROMPT_PATTERN, // Prompt line (DRY: shared with GEMINI_PROMPT_PATTERN)
|
|
2887
|
-
exports.GEMINI_THINKING_PATTERN, // Thinking indicators
|
|
2888
|
-
/^\s*$/, // Empty lines
|
|
2889
|
-
/Gemini\s+\d+\.\d+/, // Version line
|
|
2890
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
2891
|
-
],
|
|
2892
|
-
};
|
|
2893
|
-
case 'vibe-local':
|
|
2894
|
-
return {
|
|
2895
|
-
promptPattern: exports.VIBE_LOCAL_PROMPT_PATTERN,
|
|
2896
|
-
separatorPattern: /^[·]{10,}$/m, // vibe-local uses middle dot separators
|
|
2897
|
-
thinkingPattern: exports.VIBE_LOCAL_THINKING_PATTERN,
|
|
2898
|
-
skipPatterns: [
|
|
2899
|
-
exports.VIBE_LOCAL_PROMPT_PATTERN, // Prompt line (ctx:N% ❯)
|
|
2900
|
-
exports.VIBE_LOCAL_THINKING_PATTERN, // Thinking indicators
|
|
2901
|
-
/^\s*$/, // Empty lines
|
|
2902
|
-
/vibe-local|vibe-coder/, // Version/banner lines
|
|
2903
|
-
/ctx:\s*\d+%/, // Context usage indicator
|
|
2904
|
-
/Model\s+\w/, // Model info line
|
|
2905
|
-
/Engine\s+\w/, // Engine info line
|
|
2906
|
-
/Mode\s+/, // Mode info line
|
|
2907
|
-
/RAM\s+/, // RAM info line
|
|
2908
|
-
/CWD\s+/, // Working directory line
|
|
2909
|
-
/^[·]{10,}$/, // Middle dot separator lines
|
|
2910
|
-
/✦\s*Ready/, // Status bar "Ready" indicator
|
|
2911
|
-
/ESC:\s*stop/, // Status bar "ESC: stop" hint
|
|
2912
|
-
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines]
|
|
2913
|
-
],
|
|
2914
|
-
};
|
|
2915
|
-
case 'opencode':
|
|
2916
|
-
return {
|
|
2917
|
-
promptPattern: exports.OPENCODE_PROMPT_PATTERN,
|
|
2918
|
-
separatorPattern: exports.OPENCODE_SEPARATOR_PATTERN,
|
|
2919
|
-
thinkingPattern: exports.OPENCODE_THINKING_PATTERN,
|
|
2920
|
-
skipPatterns: [...exports.OPENCODE_SKIP_PATTERNS],
|
|
2921
|
-
};
|
|
2922
|
-
case 'copilot':
|
|
2923
|
-
return {
|
|
2924
|
-
promptPattern: exports.COPILOT_PROMPT_PATTERN,
|
|
2925
|
-
separatorPattern: exports.COPILOT_SEPARATOR_PATTERN,
|
|
2926
|
-
thinkingPattern: exports.COPILOT_THINKING_PATTERN,
|
|
2927
|
-
skipPatterns: [...exports.COPILOT_SKIP_PATTERNS],
|
|
2928
|
-
};
|
|
2929
|
-
case 'antigravity':
|
|
2930
|
-
return {
|
|
2931
|
-
promptPattern: exports.ANTIGRAVITY_PROMPT_PATTERN,
|
|
2932
|
-
separatorPattern: exports.ANTIGRAVITY_SEPARATOR_PATTERN,
|
|
2933
|
-
thinkingPattern: exports.ANTIGRAVITY_THINKING_PATTERN,
|
|
2934
|
-
skipPatterns: [...exports.ANTIGRAVITY_SKIP_PATTERNS],
|
|
2935
|
-
};
|
|
2936
|
-
// Issue #2250: Command Code's layout is claude-shaped (inline transcript,
|
|
2937
|
-
// `❯` composer fenced by two full-width rules) but the constants are its
|
|
2938
|
-
// own. Sharing claude's would import the exact defect #2247 had to undo --
|
|
2939
|
-
// claude's rules carry a startup-banner reading that keys on `v\d+\.\d+`
|
|
2940
|
-
// and `|`, and Command Code prints its version into a `# Command Code
|
|
2941
|
-
// v1.40.1` row on every launch.
|
|
2942
|
-
case 'command-code':
|
|
2943
|
-
return {
|
|
2944
|
-
promptPattern: exports.COMMAND_CODE_PROMPT_PATTERN,
|
|
2945
|
-
separatorPattern: exports.COMMAND_CODE_SEPARATOR_PATTERN,
|
|
2946
|
-
thinkingPattern: exports.COMMAND_CODE_THINKING_PATTERN,
|
|
2947
|
-
skipPatterns: [...exports.COMMAND_CODE_SKIP_PATTERNS],
|
|
2948
|
-
};
|
|
2949
|
-
// Issue #2934: OpenCode V2's own constants (see OPENCODE_V2_* above). The
|
|
2950
|
-
// separator row is the same half-block rule v1 draws, so v1's pattern is
|
|
2951
|
-
// reused as a value; nothing of v1's is changed.
|
|
2952
|
-
case 'opencode-v2':
|
|
2953
|
-
return {
|
|
2954
|
-
promptPattern: exports.OPENCODE_V2_IDLE_COMPOSER_PATTERN,
|
|
2955
|
-
separatorPattern: exports.OPENCODE_SEPARATOR_PATTERN,
|
|
2956
|
-
thinkingPattern: exports.OPENCODE_V2_THINKING_PATTERN,
|
|
2957
|
-
skipPatterns: [...exports.OPENCODE_V2_SKIP_PATTERNS],
|
|
2958
|
-
};
|
|
2959
|
-
default:
|
|
2960
|
-
// Default to Claude patterns
|
|
2961
|
-
return getCliToolPatterns('claude');
|
|
2962
|
-
}
|
|
386
|
+
const row = patternRowFor(cliToolId);
|
|
387
|
+
return {
|
|
388
|
+
promptPattern: row.promptPattern,
|
|
389
|
+
separatorPattern: row.separatorPattern,
|
|
390
|
+
thinkingPattern: row.thinkingPattern,
|
|
391
|
+
skipPatterns: [...row.skipPatterns],
|
|
392
|
+
};
|
|
2963
393
|
}
|
|
2964
394
|
// ANSI primitives live in a dependency-free leaf module so client components can
|
|
2965
395
|
// reuse the same tested pattern without pulling this file's server-only imports
|
|
2966
396
|
// (logger/db) into the browser bundle. Re-exported here for existing importers.
|
|
2967
|
-
var
|
|
2968
|
-
Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return
|
|
2969
|
-
Object.defineProperty(exports, "extractAnsiSequences", { enumerable: true, get: function () { return
|
|
2970
|
-
/**
|
|
2971
|
-
* Strip box-drawing border characters from CLI output.
|
|
2972
|
-
* Gemini CLI wraps Action Required prompts in ╭─╮│╰─╯ borders.
|
|
2973
|
-
* Removes │ (U+2502) prefix/suffix and border-only lines (╭╮╰╯─).
|
|
2974
|
-
*
|
|
2975
|
-
* @param str - Input string (typically after stripAnsi())
|
|
2976
|
-
* @returns String with box-drawing borders removed
|
|
2977
|
-
*/
|
|
2978
|
-
function stripBoxDrawing(str) {
|
|
2979
|
-
return str.split('\n').map(line => {
|
|
2980
|
-
// Remove border-only lines (╭──╮, ╰──╯, │ only, ┃ only, ╹▀▀▀, █ scrollbar, etc.)
|
|
2981
|
-
// U+2502 │ (light vertical), U+2503 ┃ (heavy vertical - OpenCode TUI)
|
|
2982
|
-
// U+2579 ╹ (heavy up), U+2580 ▀ (upper half block - OpenCode separator)
|
|
2983
|
-
// U+2588 █ (full block - OpenCode scrollbar)
|
|
2984
|
-
if (/^[\u2502\u2503\u256D\u256E\u256F\u2570\u2500\u2579\u2580\u2588\s]+$/.test(line))
|
|
2985
|
-
return '';
|
|
2986
|
-
// Strip leading whitespace + │/┃ + optional space, trailing space + │/┃/█
|
|
2987
|
-
// OpenCode TUI adds 2-space padding before ┃ borders (e.g., " ┃ content")
|
|
2988
|
-
// OpenCode scrollbar █ appears at end of content lines
|
|
2989
|
-
return line.replace(/^\s*[\u2502\u2503]\s?/, '').replace(/\s*[\u2502\u2503\u2588]$/, '');
|
|
2990
|
-
}).join('\n');
|
|
2991
|
-
}
|
|
2992
|
-
/**
|
|
2993
|
-
* Error patterns that indicate a Claude session failed to start properly
|
|
2994
|
-
* Used by isSessionHealthy() to detect broken sessions (MF-001: SRP)
|
|
2995
|
-
* Style: readonly + as const for type safety (SF-S2-001: follows response-poller.ts precedent)
|
|
2996
|
-
*
|
|
2997
|
-
* SEC-SF-004: Pattern maintenance process:
|
|
2998
|
-
* - When Claude CLI is updated, verify that error messages still match these patterns.
|
|
2999
|
-
* - Test procedure: Intentionally trigger each error condition (e.g., nested session launch)
|
|
3000
|
-
* and confirm the error message is captured by the patterns.
|
|
3001
|
-
* - If Claude CLI introduces localized error messages, add locale-aware patterns or
|
|
3002
|
-
* consider switching to exit code-based detection as a more robust alternative.
|
|
3003
|
-
* - Pattern additions should be accompanied by corresponding test cases in
|
|
3004
|
-
* claude-session.test.ts.
|
|
3005
|
-
*
|
|
3006
|
-
* C-S3-001: Codex/Gemini monitoring note:
|
|
3007
|
-
* These patterns are currently Claude-specific. If Codex or Gemini exhibit similar
|
|
3008
|
-
* "nested session" or startup failure behaviors, analogous error patterns should be
|
|
3009
|
-
* added to their respective tool configurations (codex.ts, gemini.ts) rather than
|
|
3010
|
-
* extending these arrays, to maintain SRP per CLI tool type.
|
|
3011
|
-
*/
|
|
3012
|
-
exports.CLAUDE_SESSION_ERROR_PATTERNS = [
|
|
3013
|
-
'Claude Code cannot be launched inside another Claude Code session',
|
|
3014
|
-
];
|
|
3015
|
-
/**
|
|
3016
|
-
* Regex patterns for Claude session errors requiring context matching
|
|
3017
|
-
* Used by isSessionHealthy() for multi-condition error detection (MF-001: SRP)
|
|
3018
|
-
* Style: readonly + as const for type safety (SF-S2-001: follows response-poller.ts precedent)
|
|
3019
|
-
*
|
|
3020
|
-
* SEC-SF-004: See CLAUDE_SESSION_ERROR_PATTERNS JSDoc for pattern maintenance process.
|
|
3021
|
-
*/
|
|
3022
|
-
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = [
|
|
3023
|
-
/^Error:.*Claude Code/,
|
|
3024
|
-
];
|
|
397
|
+
var ansi_1 = require("./ansi");
|
|
398
|
+
Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return ansi_1.stripAnsi; } });
|
|
399
|
+
Object.defineProperty(exports, "extractAnsiSequences", { enumerable: true, get: function () { return ansi_1.extractAnsiSequences; } });
|
|
3025
400
|
/**
|
|
3026
401
|
* Build DetectPromptOptions for a given CLI tool.
|
|
3027
402
|
* Centralizes cliToolId-to-options mapping logic (DRY - MF-001).
|
|
@@ -3029,73 +404,25 @@ exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = [
|
|
|
3029
404
|
* prompt-detector.ts remains CLI tool independent (Issue #161 principle);
|
|
3030
405
|
* this function lives in cli-patterns.ts which already depends on CLIToolType.
|
|
3031
406
|
*
|
|
3032
|
-
* [
|
|
3033
|
-
*
|
|
3034
|
-
*
|
|
3035
|
-
*
|
|
3036
|
-
*
|
|
407
|
+
* [Extension memo (C-002) — carried out]
|
|
408
|
+
* The memo asked for a registry pattern where tool-specific settings
|
|
409
|
+
* (including promptDetectionOptions) are managed in a Record<CLIToolType, ...>,
|
|
410
|
+
* with the 7th tool as the migration threshold [D1-003]. There are 9 tools
|
|
411
|
+
* today, and Issue #3230 made the move: the settings are the `promptOptions`
|
|
412
|
+
* column of CLI_TOOL_PATTERN_TABLE.
|
|
3037
413
|
*
|
|
3038
414
|
* @param cliToolId - CLI tool identifier
|
|
3039
415
|
* @returns DetectPromptOptions for the tool, or undefined for default behavior
|
|
3040
416
|
*/
|
|
3041
417
|
function buildDetectPromptOptions(cliToolId) {
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
}
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
// geometry and both are cursor-driven:
|
|
3053
|
-
//
|
|
3054
|
-
// - the permission dialog is a horizontal button strip
|
|
3055
|
-
// ({@link OPENCODE_PERMISSION_PATTERN}, Issue #1893) driven by ←/→ + Enter;
|
|
3056
|
-
// typing a number does nothing to it.
|
|
3057
|
-
// - the pickers (`/models`, `/providers`, `/connect`, and the ctrl+p command
|
|
3058
|
-
// palette) are fuzzy-search lists driven by ↑/↓ + Enter, with no numbers
|
|
3059
|
-
// drawn at all. The first three are what
|
|
3060
|
-
// {@link OPENCODE_SELECTION_LIST_PATTERN} names; the palette shares the
|
|
3061
|
-
// chrome but not the header allowlist, and lands on `running` / `default`.
|
|
3062
|
-
//
|
|
3063
|
-
// Both keep their own POSITIVE detection in `status-detector.ts`, so `wait`
|
|
3064
|
-
// still stops for them (exit 10 via `isSelectionListActive`) and the UI still
|
|
3065
|
-
// renders NavigationButtons: nothing that could be answered before stops being
|
|
3066
|
-
// answered. What ends is the false positive -- a response whose body ends in
|
|
3067
|
-
// `1. / 2. / 3.` + a question was published as
|
|
3068
|
-
// `waiting`/`prompt_detected`/`hasActivePrompt: true`, and Auto-Yes typed `1`
|
|
3069
|
-
// into the composer and SENT IT as a user utterance (Issue #1896).
|
|
3070
|
-
//
|
|
3071
|
-
// `requireDefaultIndicator` is kept at its D2-006 value: it is the correct
|
|
3072
|
-
// setting for opencode's ❯-less rendering should the numbered path ever be
|
|
3073
|
-
// re-enabled, and it still describes the tool.
|
|
3074
|
-
if (cliToolId === 'opencode') {
|
|
3075
|
-
return { requireDefaultIndicator: false, hasNumberedDialogs: false };
|
|
3076
|
-
}
|
|
3077
|
-
// [Issue #545] Copilot prompt pattern may not use standard indicators
|
|
3078
|
-
if (cliToolId === 'copilot') {
|
|
3079
|
-
return { requireDefaultIndicator: false };
|
|
3080
|
-
}
|
|
3081
|
-
// [Issue #999] Antigravity (agy) permission-approval menus highlight the
|
|
3082
|
-
// default with an ASCII ">" (0x3E), not the "❯/●/›" indicators that
|
|
3083
|
-
// DEFAULT_OPTION_PATTERN recognizes, and their footer is "↑/↓ Navigate"
|
|
3084
|
-
// (no "press enter to confirm"). Under the default requireDefaultIndicator=true
|
|
3085
|
-
// the Pass 1 gate rejects these menus, so Auto-Yes never responds. Treat agy
|
|
3086
|
-
// like claude/opencode/copilot so Pass 2 collects its "1. Yes / … / N. No"
|
|
3087
|
-
// options and reports isPrompt=true.
|
|
3088
|
-
//
|
|
3089
|
-
// [Issue #2364] The `↑/↓ Navigate` dialogs themselves no longer reach
|
|
3090
|
-
// `detectPrompt` on either production path: `tools/antigravity/detect.ts`
|
|
3091
|
-
// (status) and `detectPromptWithOptions` (response poller) both read them
|
|
3092
|
-
// with `detectAntigravityNumberedDialogPrompt` first, because the generic
|
|
3093
|
-
// multiple-choice pass reads one row per option and agy wraps a long command
|
|
3094
|
-
// across several rows of one label. What this setting still serves is every
|
|
3095
|
-
// OTHER numbered agy screen — `/feedback`'s `1-6 Select & Continue` menu is
|
|
3096
|
-
// the measured one — which the generic pass reads as before.
|
|
3097
|
-
if (cliToolId === 'antigravity') {
|
|
3098
|
-
return { requireDefaultIndicator: false };
|
|
3099
|
-
}
|
|
3100
|
-
return undefined; // Default behavior (requireDefaultIndicator = true)
|
|
418
|
+
// Fresh object per call (callers may mutate); an id outside the table yields undefined.
|
|
419
|
+
const options = ownRowFor(cliToolId)?.promptOptions;
|
|
420
|
+
return options === undefined ? undefined : { ...options };
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Whether a tool hands the FULL frame to `detectPrompt` instead of the 15-line tail.
|
|
424
|
+
* An id outside the table answers false.
|
|
425
|
+
*/
|
|
426
|
+
function usesFullFramePrompt(cliToolId) {
|
|
427
|
+
return ownRowFor(cliToolId)?.fullFramePrompt ?? false;
|
|
3101
428
|
}
|