commandmate 0.47.2 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +215 -215
- package/.next/app-path-routes-manifest.json +41 -41
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route.js +3 -3
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +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_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_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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route.js +1 -1
- 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 -1
- 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]/prompt-response/strict/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/strict/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -103
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.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_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 +12 -12
- 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 +41 -41
- package/.next/server/chunks/1191.js +11 -4
- package/.next/server/chunks/1501.js +1 -1
- package/.next/server/chunks/1601.js +2 -2
- package/.next/server/chunks/1656.js +2 -2
- package/.next/server/chunks/1945.js +3 -3
- package/.next/server/chunks/1990.js +103 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2422.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2534.js +1 -1
- package/.next/server/chunks/255.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3505.js +2 -2
- package/.next/server/chunks/4248.js +1 -1
- package/.next/server/chunks/4280.js +1 -0
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/6119.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6486.js +9 -8
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/720.js +103 -0
- package/.next/server/chunks/7604.js +2 -0
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +7 -7
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -1
- package/.next/server/chunks/8807.js +19 -18
- package/.next/server/chunks/8918.js +2 -104
- package/.next/server/chunks/936.js +1 -1
- package/.next/server/chunks/9390.js +1 -1
- package/.next/server/chunks/9540.js +1 -1
- package/.next/server/chunks/9627.js +3 -3
- package/.next/server/chunks/9731.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/2099-c3e0ab70a1b95dd3.js +1 -0
- package/.next/static/chunks/{2407-a6533805a86d05bd.js → 2407-26139a8a2f1adbe8.js} +1 -1
- package/.next/static/chunks/{2619-04bc32f026a0d946.js → 2619-a305ad4810f1393a.js} +1 -1
- package/.next/static/chunks/{2863-6a07ec82fb52f4dd.js → 2863-f0121af1cbbedb3b.js} +1 -1
- package/.next/static/chunks/{8165-263172c17ae8e8c3.js → 8165-c68b3dc844210e6a.js} +1 -1
- package/.next/static/chunks/8407-35bbfc84793d7004.js +1 -0
- package/.next/static/chunks/9079-406b84d3cc891a87.js +1 -0
- package/.next/static/chunks/app/{layout-538d8d2746ad1588.js → layout-46b4cbf07520d9b6.js} +1 -1
- package/.next/static/chunks/app/sessions/page-fb241bed579c67aa.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-cbc7749a33450cc5.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-373ea37b16d930c7.js → page-c601f3b3ece6557e.js} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/auto-yes.d.ts +24 -0
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +73 -3
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +10 -1
- package/dist/cli/commands/prompt.d.ts.map +1 -1
- package/dist/cli/commands/prompt.js +5 -3
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +15 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +101 -13
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +27 -0
- package/dist/cli/types/api-responses.d.ts +116 -2
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +4 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.d.ts +0 -18
- package/dist/cli/utils/resource-resolvers.d.ts.map +1 -1
- package/dist/cli/utils/resource-resolvers.js +1 -51
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts +54 -2
- package/dist/lib/polling/auto-yes-suppression-reason.d.ts.map +1 -1
- package/dist/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/lib/prompt-observation/types.d.ts +13 -0
- package/dist/lib/prompt-observation/types.d.ts.map +1 -1
- package/dist/server/locales/en/worktree.json +2 -1
- package/dist/server/locales/ja/worktree.json +2 -1
- package/dist/server/src/lib/auto-yes-poller-answer-target.js +38 -0
- package/dist/server/src/lib/auto-yes-poller-keys.js +35 -0
- package/dist/server/src/lib/auto-yes-poller-session-presence.js +26 -0
- package/dist/server/src/lib/auto-yes-poller-tick.js +233 -0
- package/dist/server/src/lib/auto-yes-poller.js +337 -376
- package/dist/server/src/lib/auto-yes-state.js +50 -2
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +107 -14
- package/dist/server/src/lib/db/chat-db.js +5 -1
- package/dist/server/src/lib/db/verification-db.js +23 -2
- package/dist/server/src/lib/detection/option-recommendation.js +120 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice-approval-context.js +290 -0
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +396 -350
- package/dist/server/src/lib/detection/prompt-detector.js +92 -21
- package/dist/server/src/lib/detection/prompt-screen-key.js +60 -0
- package/dist/server/src/lib/detection/tools/antigravity/dialog.js +158 -35
- package/dist/server/src/lib/detection/tools/claude/detect.js +111 -10
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +67 -0
- package/dist/server/src/lib/detection/tools/dialog-block.js +16 -0
- package/dist/server/src/lib/detection/waiting-screen-reading.js +98 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +7 -0
- package/dist/server/src/lib/polling/auto-yes-confirm-page.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger-types.js +3 -0
- package/dist/server/src/lib/polling/auto-yes-confirmation-ledger.js +34 -3
- package/dist/server/src/lib/polling/auto-yes-decision-state.js +195 -0
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +139 -10
- package/dist/server/src/lib/polling/auto-yes-enter-fallback.js +14 -0
- package/dist/server/src/lib/polling/auto-yes-manual-answer.js +122 -0
- package/dist/server/src/lib/polling/auto-yes-option-effect.js +157 -0
- package/dist/server/src/lib/polling/auto-yes-policy.js +11 -5
- package/dist/server/src/lib/polling/auto-yes-poller-capture.js +15 -2
- package/dist/server/src/lib/polling/auto-yes-poller-generation.js +42 -1
- package/dist/server/src/lib/polling/auto-yes-poller-receipt-log.js +42 -5
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +147 -49
- package/dist/server/src/lib/polling/auto-yes-poller-steps.js +172 -2
- package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +2 -2
- package/dist/server/src/lib/polling/auto-yes-question-choice.js +57 -0
- package/dist/server/src/lib/polling/auto-yes-resolver.js +96 -21
- package/dist/server/src/lib/polling/auto-yes-suppression-reason.js +33 -2
- package/dist/server/src/lib/polling/auto-yes-suppression-state.js +25 -1
- package/dist/server/src/lib/polling/prompt-screen-reading.js +232 -0
- package/dist/server/src/lib/polling/response-checker-dialog-gate.js +39 -0
- package/dist/server/src/lib/polling/response-checker.js +133 -43
- package/dist/server/src/lib/prompt-answer-sender.js +104 -10
- package/dist/server/src/lib/push/notification-dedup.js +15 -2
- package/dist/server/src/lib/push/prompt-card-state.js +140 -19
- package/dist/server/src/lib/push/prompt-push-gate.js +158 -12
- package/dist/server/src/lib/push/push-sender.js +23 -3
- package/dist/server/src/lib/push/resolution-push-notifier.js +14 -0
- package/dist/server/src/lib/push/waiting-push-notifier.js +139 -17
- package/dist/server/src/lib/realtime/terminal-broadcast.js +16 -0
- package/dist/server/src/lib/session/agent-event-state.js +25 -5
- package/dist/server/src/lib/session/agent-event-turn.js +140 -46
- package/dist/server/src/lib/session/cli-session.js +21 -2
- package/dist/server/src/lib/session/current-output-builder.js +64 -11
- package/dist/server/src/lib/session/send-attempt-state.js +23 -7
- package/dist/server/src/lib/session/waiting-episode-binding-observation.js +289 -0
- package/dist/server/src/lib/session/waiting-episode-binding-resolve.js +38 -0
- package/dist/server/src/lib/session/waiting-episode-binding.js +276 -0
- package/dist/server/src/lib/session/waiting-episode-state.js +307 -17
- package/dist/server/src/lib/session/waiting-episode-verdict.js +174 -0
- package/dist/server/src/lib/tmux/tmux-capture-cache.js +76 -11
- package/dist/server/src/lib/tmux/tmux.js +54 -9
- package/dist/server/src/lib/verification/gate-runner.js +162 -13
- package/dist/server/src/lib/verification/machine-lock.js +5 -0
- package/dist/server/src/lib/ws-server.js +18 -3
- package/dist/server/src/types/auto-yes-answer-mode.js +38 -0
- package/dist/server/src/types/auto-yes-decision.js +61 -0
- package/dist/server/src/types/auto-yes.js +6 -0
- package/dist/server/src/types/prompt-reading.js +32 -0
- package/dist/types/auto-yes-answer-mode.d.ts +35 -0
- package/dist/types/auto-yes-answer-mode.d.ts.map +1 -0
- package/dist/types/auto-yes-answer-mode.js +38 -0
- package/docs/en/user-guide/webapp-guide.md +33 -4
- package/package.json +1 -1
- package/public/sw.js +76 -4
- package/release-notes/0.48.0.json +57 -0
- package/.next/server/chunks/1559.js +0 -2
- package/.next/server/chunks/2460.js +0 -1
- package/.next/server/chunks/5595.js +0 -1
- package/.next/server/chunks/610.js +0 -1
- package/.next/static/chunks/602-1c1488959215e140.js +0 -1
- package/.next/static/chunks/8407-155f84e5ba05f1d5.js +0 -1
- package/.next/static/chunks/9079-24f9fa549ed298f1.js +0 -1
- package/.next/static/chunks/app/sessions/page-e02f1adb1d1c1ccc.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-46aced20b2b1c010.js +0 -1
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → PTlAswXLq8PAe2vUJ8dVP}/_buildManifest.js +0 -0
- /package/.next/static/{etorRtpxbQqqOzdUeXdw2 → PTlAswXLq8PAe2vUJ8dVP}/_ssgManifest.js +0 -0
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/** Multiple choice prompt detection logic extracted from prompt-detector.ts (Issue #575). */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.APPROVAL_TARGET_MAX_LOOKBACK = void 0;
|
|
4
|
+
exports.joinApprovalTarget = exports.findApprovalContextStart = exports.findApprovalContextRange = exports.APPROVAL_TARGET_MAX_LOOKBACK = void 0;
|
|
5
5
|
exports.findClaudeTaskPanelLines = findClaudeTaskPanelLines;
|
|
6
6
|
exports.findClaudeChrome = findClaudeChrome;
|
|
7
7
|
exports.maskClaudeChrome = maskClaudeChrome;
|
|
8
8
|
exports.isTypedTextFieldOption = isTypedTextFieldOption;
|
|
9
|
-
exports.findApprovalContextStart = findApprovalContextStart;
|
|
10
|
-
exports.joinApprovalTarget = joinApprovalTarget;
|
|
11
9
|
exports.buildMultipleChoiceResult = buildMultipleChoiceResult;
|
|
12
10
|
exports.detectMultipleChoicePrompt = detectMultipleChoicePrompt;
|
|
13
11
|
const tui_detection_frame_1 = require("./tui-detection-frame");
|
|
14
12
|
const cli_patterns_1 = require("./cli-patterns");
|
|
13
|
+
const patterns_1 = require("./tools/claude/patterns");
|
|
15
14
|
const prompt_view_1 = require("../../lib/session/prompt-view");
|
|
15
|
+
const option_recommendation_1 = require("./option-recommendation");
|
|
16
16
|
const picker_chrome_1 = require("./tools/claude/picker-chrome");
|
|
17
|
+
const prompt_detect_multiple_choice_approval_context_1 = require("./prompt-detect-multiple-choice-approval-context");
|
|
18
|
+
var prompt_detect_multiple_choice_approval_context_2 = require("./prompt-detect-multiple-choice-approval-context");
|
|
19
|
+
Object.defineProperty(exports, "APPROVAL_TARGET_MAX_LOOKBACK", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.APPROVAL_TARGET_MAX_LOOKBACK; } });
|
|
20
|
+
Object.defineProperty(exports, "findApprovalContextRange", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.findApprovalContextRange; } });
|
|
21
|
+
Object.defineProperty(exports, "findApprovalContextStart", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.findApprovalContextStart; } });
|
|
22
|
+
Object.defineProperty(exports, "joinApprovalTarget", { enumerable: true, get: function () { return prompt_detect_multiple_choice_approval_context_2.joinApprovalTarget; } });
|
|
17
23
|
// ============================================================================
|
|
18
24
|
// Constants
|
|
19
25
|
// ============================================================================
|
|
@@ -220,13 +226,6 @@ function maskClaudeChrome(lines, chrome) {
|
|
|
220
226
|
* Anchored, no nested quantifiers — ReDoS safe (S4-001).
|
|
221
227
|
*/
|
|
222
228
|
const PANE_WRAPPED_LABEL_PATTERN = /^\s{3,}\S/;
|
|
223
|
-
/**
|
|
224
|
-
* Pattern for separator lines (horizontal rules).
|
|
225
|
-
* Matches lines consisting only of dash (-) or em-dash (─) characters.
|
|
226
|
-
* Used to skip separator lines in question extraction and non-option line handling.
|
|
227
|
-
* Anchored at both ends -- ReDoS safe (S4-001).
|
|
228
|
-
*/
|
|
229
|
-
const SEPARATOR_LINE_PATTERN = /^[-─]+$/;
|
|
230
229
|
/**
|
|
231
230
|
* How many lines above `questionEndIndex` the question text is collected from.
|
|
232
231
|
* Also the point the Issue #1699 approval-context scan starts walking up from,
|
|
@@ -552,7 +551,7 @@ function findQuestionLineInRange(lines, startIndex, scanRange, lowerBound) {
|
|
|
552
551
|
for (let i = startIndex - 1; i >= scanLimit; i--) {
|
|
553
552
|
const candidateLine = lines[i]?.trim() ?? '';
|
|
554
553
|
// Skip empty lines and separator lines (horizontal rules)
|
|
555
|
-
if (!candidateLine || SEPARATOR_LINE_PATTERN.test(candidateLine))
|
|
554
|
+
if (!candidateLine || prompt_detect_multiple_choice_approval_context_1.SEPARATOR_LINE_PATTERN.test(candidateLine))
|
|
556
555
|
continue;
|
|
557
556
|
if (isQuestionLikeLine(candidateLine)) {
|
|
558
557
|
return true;
|
|
@@ -649,26 +648,86 @@ function isContinuationLine(rawLine, line) {
|
|
|
649
648
|
}
|
|
650
649
|
return false;
|
|
651
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* The user's own message echo (Issue #3832): claude's `❯ msg`, codex's `› msg`.
|
|
653
|
+
* The cursor of an option row (`❯ 1.`) is not one. `>` is left out on purpose:
|
|
654
|
+
* codex's trust dialog opens with `> You are in <dir>` and a here-document's
|
|
655
|
+
* body carries `> ` rows, so a `>` row may be the prompt's own.
|
|
656
|
+
*/
|
|
657
|
+
const USER_ECHO_PATTERN = /^[❯›]\s(?!\s*\d+[.)]\s)/;
|
|
658
|
+
/**
|
|
659
|
+
* codex's end-of-turn row (Issue #3832): `Worked for 17s • 19:30`, or the rule
|
|
660
|
+
* form `─ Worked for 1m 2s ─`. claude's is {@link CLAUDE_TURN_COMPLETE_PATTERN}.
|
|
661
|
+
*/
|
|
662
|
+
const CODEX_TURN_COMPLETE_PATTERN = /^─*\s*Worked for\s+(?:\d+h\s*)?(?:\d+m\s*)?\d+s\b/;
|
|
663
|
+
/** True when the nearest non-blank row above `row` (down to `floor`) ends a finished turn. */
|
|
664
|
+
function followsFinishedTurn(lines, row, floor) {
|
|
665
|
+
for (let i = row - 1; i >= floor; i--) {
|
|
666
|
+
const line = lines[i];
|
|
667
|
+
if (line === undefined || line.trim() === '')
|
|
668
|
+
continue;
|
|
669
|
+
return patterns_1.CLAUDE_TURN_COMPLETE_PATTERN.test(line) || CODEX_TURN_COMPLETE_PATTERN.test(line.trim());
|
|
670
|
+
}
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* The lowest row in `[from, questionEndIndex)` that is the user's message echo
|
|
675
|
+
* (Issue #3832), or -1.
|
|
676
|
+
*
|
|
677
|
+
* A `❯ ` / `› ` row is taken for the echo only when the screen leaves no doubt:
|
|
678
|
+
* drawn left of the dialog's frame (claude's echo at column 0 against its frame
|
|
679
|
+
* at 1, codex's at 0 against 2), and right under the previous turn's end-of-turn
|
|
680
|
+
* row (`✻ Baked for 3s`, `Worked for 17s`). A body row that happens to start
|
|
681
|
+
* with the glyph — at the frame's column, or under the command it belongs to —
|
|
682
|
+
* is not one, so the rows above it stay in `question` and the deny surface is
|
|
683
|
+
* taken wide.
|
|
684
|
+
*/
|
|
685
|
+
function findUserEchoRow(lines, questionEndIndex, from, panelEnd, floor) {
|
|
686
|
+
const frameIndent = (0, prompt_detect_multiple_choice_approval_context_1.panelFrameIndent)(lines, questionEndIndex, panelEnd);
|
|
687
|
+
for (let i = questionEndIndex - 1; i >= from; i--) {
|
|
688
|
+
const line = lines[i];
|
|
689
|
+
if (line === undefined || line.trim() === '')
|
|
690
|
+
continue;
|
|
691
|
+
if ((0, prompt_detect_multiple_choice_approval_context_1.indentOf)(line) < frameIndent && USER_ECHO_PATTERN.test(line.trim())
|
|
692
|
+
&& followsFinishedTurn(lines, i, floor)) {
|
|
693
|
+
return i;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return -1;
|
|
697
|
+
}
|
|
652
698
|
/**
|
|
653
699
|
* Extract question text from the lines around questionEndIndex.
|
|
654
700
|
* Collects non-empty, non-separator lines from up to 5 lines before questionEndIndex
|
|
655
701
|
* through questionEndIndex itself, joining them with spaces.
|
|
656
702
|
*
|
|
703
|
+
* [Issue #3832] Not above the user's message echo ({@link findUserEchoRow}):
|
|
704
|
+
* that message opened the current turn, so the rows above it — the previous
|
|
705
|
+
* turn's `⏺ IMPL_COMPLETED` / `✻ Cogitated …`, codex's `• HTTP/2 200` /
|
|
706
|
+
* `Worked for 17s` on the measured screens — are not the prompt's and used to
|
|
707
|
+
* reach the deny surface through `question`. The echo row itself stays. No
|
|
708
|
+
* other row bounds the window: a `⏺` / `●` / `•` row may be the call the prompt
|
|
709
|
+
* asks about (codex's `• Calling …`, agy's `● Bash(…)`), and `question` is what
|
|
710
|
+
* keeps such a row in the deny surface when `approvalTarget` cuts at it.
|
|
711
|
+
*
|
|
657
712
|
* @param lines - Array of output lines
|
|
658
713
|
* @param questionEndIndex - Index of the last line before options, or -1 if not found
|
|
659
714
|
* @param floor - First row the question may start at. Issue #2486: one below an
|
|
660
715
|
* AskUserQuestion picker's tab row, so neither that row nor the transcript
|
|
661
716
|
* above it becomes part of the question; 0 when there is none.
|
|
717
|
+
* @param panelEnd - Exclusive end of the dialog's own rows, which set the frame
|
|
718
|
+
* column the echo is drawn at or left of (Issue #3832)
|
|
662
719
|
* @returns Extracted question text, or generic fallback if questionEndIndex is -1
|
|
663
720
|
*/
|
|
664
|
-
function extractQuestionText(lines, questionEndIndex, floor = 0) {
|
|
721
|
+
function extractQuestionText(lines, questionEndIndex, floor = 0, panelEnd = questionEndIndex + 1) {
|
|
665
722
|
if (questionEndIndex < 0) {
|
|
666
723
|
return 'Please select an option:';
|
|
667
724
|
}
|
|
725
|
+
const windowStart = Math.max(floor, questionEndIndex - QUESTION_CONTEXT_LINES);
|
|
726
|
+
const start = Math.max(windowStart, findUserEchoRow(lines, questionEndIndex, windowStart, panelEnd, floor));
|
|
668
727
|
const questionLines = [];
|
|
669
|
-
for (let i =
|
|
728
|
+
for (let i = start; i <= questionEndIndex; i++) {
|
|
670
729
|
const line = lines[i].trim();
|
|
671
|
-
if (line && !SEPARATOR_LINE_PATTERN.test(line)) {
|
|
730
|
+
if (line && !prompt_detect_multiple_choice_approval_context_1.SEPARATOR_LINE_PATTERN.test(line)) {
|
|
672
731
|
questionLines.push(line);
|
|
673
732
|
}
|
|
674
733
|
}
|
|
@@ -694,161 +753,6 @@ function extractInstructionText(lines, questionEndIndex, effectiveEnd) {
|
|
|
694
753
|
const joined = blockLines.join('\n').trim();
|
|
695
754
|
return joined.length > 0 ? joined : undefined;
|
|
696
755
|
}
|
|
697
|
-
/**
|
|
698
|
-
* Lines that mean "the previous turn ended here" when scanning upward.
|
|
699
|
-
*
|
|
700
|
-
* Every supported CLI renders its transcript as marker-prefixed rows — Claude's
|
|
701
|
-
* tool-use `⏺` and result `⎿`, Codex's activity `•`, its approval receipts `✔`
|
|
702
|
-
* / `✗`, the user's own `›` / `❯` / `>` echo, and its output continuation `└`.
|
|
703
|
-
* The lines a prompt panel puts above its question (a header such as "Bash
|
|
704
|
-
* command", the command itself, a one-line description) never carry one, which
|
|
705
|
-
* is what makes this usable as the panel's upper edge after `stripBoxDrawing`
|
|
706
|
-
* has erased the real box border.
|
|
707
|
-
*
|
|
708
|
-
* Horizontal rules count too: Codex draws one between turns.
|
|
709
|
-
*
|
|
710
|
-
* [Issue #3817] Not inside the panel's own frame: the command a panel shows can
|
|
711
|
-
* carry the same glyphs (a here-document's `> quoted` rows), drawn deeper than
|
|
712
|
-
* the panel's header, question and option rows. See
|
|
713
|
-
* {@link findApprovalContextStart}.
|
|
714
|
-
*
|
|
715
|
-
* Anchored at the start of the trimmed line — ReDoS safe (no repetition).
|
|
716
|
-
*/
|
|
717
|
-
const TURN_BOUNDARY_PATTERN = /^[⏺⎿●○•✔✓✖✗✘›»❯└├>]/;
|
|
718
|
-
/**
|
|
719
|
-
* How far above the question the approval context may reach (Issue #1699).
|
|
720
|
-
*
|
|
721
|
-
* The boundary marker above is the actual fix; this cap only exists so a frame
|
|
722
|
-
* that happens to contain no marker at all (a bare shell `(y/n)` prompt under
|
|
723
|
-
* plain command output) cannot drag an unbounded scrollback window into the
|
|
724
|
-
* deny surface the way the old fixed 19-line lookback did. Sized for the
|
|
725
|
-
* tallest real permission panel measured: header + blank + a wrapped multi-line
|
|
726
|
-
* command + description + blank.
|
|
727
|
-
*/
|
|
728
|
-
exports.APPROVAL_TARGET_MAX_LOOKBACK = 12;
|
|
729
|
-
/**
|
|
730
|
-
* A previous tool call's result row (Issue #3817): claude's `⎿`, codex's `└` /
|
|
731
|
-
* `├` output continuation. Never the user's echo or a quote (`>`).
|
|
732
|
-
*/
|
|
733
|
-
const RESULT_MARKER_PATTERN = /^[⎿└├]/;
|
|
734
|
-
/** Width of a row's leading whitespace. */
|
|
735
|
-
function indentOf(line) {
|
|
736
|
-
return line.length - line.trimStart().length;
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* The column the dialog's own frame is drawn at (Issue #3817): the least
|
|
740
|
-
* indented non-blank row from the question down to `panelEnd`. Those rows are
|
|
741
|
-
* the question, the option block and the footer — claude puts them at column 1
|
|
742
|
-
* and the command at column 3, codex its `› 1.` cursor at column 0.
|
|
743
|
-
*/
|
|
744
|
-
function panelFrameIndent(lines, questionLineIndex, panelEnd) {
|
|
745
|
-
let indent = Infinity;
|
|
746
|
-
for (let i = questionLineIndex; i < Math.min(panelEnd, lines.length); i++) {
|
|
747
|
-
const line = lines[i];
|
|
748
|
-
if (line === undefined || line.trim() === '')
|
|
749
|
-
continue;
|
|
750
|
-
indent = Math.min(indent, indentOf(line));
|
|
751
|
-
}
|
|
752
|
-
return indent === Infinity ? 0 : indent;
|
|
753
|
-
}
|
|
754
|
-
/** True when this line carries a transcript marker or is a rule. */
|
|
755
|
-
function isTurnBoundaryLine(rawLine) {
|
|
756
|
-
if (rawLine === undefined)
|
|
757
|
-
return false;
|
|
758
|
-
const line = rawLine.trim();
|
|
759
|
-
if (line === '')
|
|
760
|
-
return false;
|
|
761
|
-
return TURN_BOUNDARY_PATTERN.test(line) || SEPARATOR_LINE_PATTERN.test(line);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* First line index that still belongs to *this* prompt, scanning upward.
|
|
765
|
-
*
|
|
766
|
-
* Walks up from `questionLineIndex - 1` and stops just below the nearest turn
|
|
767
|
-
* boundary; with no boundary in reach it falls back to the lookback cap.
|
|
768
|
-
* Everything above the returned index is a previous turn and must never reach a
|
|
769
|
-
* deny pattern — that poisoning is Issue #1699.
|
|
770
|
-
*
|
|
771
|
-
* [Issue #3817] A marker row indented deeper than the dialog's frame
|
|
772
|
-
* ({@link panelFrameIndent}) is the body of the command the panel asks about
|
|
773
|
-
* when a frame row sits above it — the `> quoted` rows of a here-document
|
|
774
|
-
* under a claude panel's ` Bash command` header. Cutting there took the command
|
|
775
|
-
* rows above it out of the deny surface. The previous turn's markers are drawn
|
|
776
|
-
* at or left of the frame (`⏺`, the user's `> msg` / `❯ msg` echo, a rule), or
|
|
777
|
-
* deeper with no frame row above them (claude's ` ⎿` result right under its
|
|
778
|
-
* `⏺`), and both still end the scan.
|
|
779
|
-
*
|
|
780
|
-
* A frame row cannot be told from a body row drawn at the frame's column (a
|
|
781
|
-
* shell here-document's `EOF`), so a frame row is taken as an older panel's
|
|
782
|
-
* header only in one shape: a tool-use block — a marker row at the frame's
|
|
783
|
-
* column, then that row, then deeper rows that are all result markers
|
|
784
|
-
* ({@link RESULT_MARKER_PATTERN}). Anything else between two frame rows is read
|
|
785
|
-
* as body: when the screen cannot tell, the deny surface is taken wide.
|
|
786
|
-
*
|
|
787
|
-
* Shared by both detector paths: multiple_choice passes its `questionEndIndex`,
|
|
788
|
-
* yes_no the line its pattern matched on.
|
|
789
|
-
*
|
|
790
|
-
* @param lines - Full frame, already ANSI/box stripped
|
|
791
|
-
* @param questionLineIndex - Line the prompt's own question sits on
|
|
792
|
-
* @param panelEnd - Exclusive end of the dialog's own rows (Issue #3817); their
|
|
793
|
-
* least indented row is the frame. Defaults to the question row alone.
|
|
794
|
-
* @returns Index of the first line of the current prompt's own block
|
|
795
|
-
*/
|
|
796
|
-
function findApprovalContextStart(lines, questionLineIndex, panelEnd = questionLineIndex + 1) {
|
|
797
|
-
const floor = Math.max(0, questionLineIndex - exports.APPROVAL_TARGET_MAX_LOOKBACK);
|
|
798
|
-
const frameIndent = panelFrameIndent(lines, questionLineIndex, panelEnd);
|
|
799
|
-
// The lowest deeper marker row with no frame row found above it yet.
|
|
800
|
-
let unenclosed = -1;
|
|
801
|
-
// The deeper rows since the last frame row: are they all result markers?
|
|
802
|
-
let segmentRows = 0;
|
|
803
|
-
let segmentAllResults = true;
|
|
804
|
-
for (let i = questionLineIndex - 1; i >= floor; i--) {
|
|
805
|
-
const line = lines[i];
|
|
806
|
-
if (line === undefined || line.trim() === '')
|
|
807
|
-
continue;
|
|
808
|
-
const atFrame = indentOf(line) <= frameIndent;
|
|
809
|
-
const marker = isTurnBoundaryLine(line);
|
|
810
|
-
if (marker && atFrame)
|
|
811
|
-
return (unenclosed >= 0 ? unenclosed : i) + 1;
|
|
812
|
-
if (!atFrame) {
|
|
813
|
-
if (marker && unenclosed < 0)
|
|
814
|
-
unenclosed = i;
|
|
815
|
-
segmentRows++;
|
|
816
|
-
segmentAllResults &&= RESULT_MARKER_PATTERN.test(line.trim());
|
|
817
|
-
continue;
|
|
818
|
-
}
|
|
819
|
-
// A frame row: an older tool-use block's header only in that one shape.
|
|
820
|
-
if (unenclosed >= 0 && segmentRows > 0 && segmentAllResults && isMarkerRowAtFrameAbove(lines, i, floor, frameIndent)) {
|
|
821
|
-
return unenclosed + 1;
|
|
822
|
-
}
|
|
823
|
-
unenclosed = -1;
|
|
824
|
-
segmentRows = 0;
|
|
825
|
-
segmentAllResults = true;
|
|
826
|
-
}
|
|
827
|
-
return unenclosed >= 0 ? unenclosed + 1 : floor;
|
|
828
|
-
}
|
|
829
|
-
/** True when the nearest non-blank row above `row` is a marker row at the frame's column. */
|
|
830
|
-
function isMarkerRowAtFrameAbove(lines, row, floor, frameIndent) {
|
|
831
|
-
for (let i = row - 1; i >= floor; i--) {
|
|
832
|
-
const line = lines[i];
|
|
833
|
-
if (line === undefined || line.trim() === '')
|
|
834
|
-
continue;
|
|
835
|
-
return indentOf(line) <= frameIndent && isTurnBoundaryLine(line);
|
|
836
|
-
}
|
|
837
|
-
return false;
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Join a slice of the frame into an approval-target string.
|
|
841
|
-
*
|
|
842
|
-
* @returns The trimmed block, or undefined when it is empty
|
|
843
|
-
*/
|
|
844
|
-
function joinApprovalTarget(lines, start, end) {
|
|
845
|
-
const joined = lines
|
|
846
|
-
.slice(start, end)
|
|
847
|
-
.map(l => l.trimEnd())
|
|
848
|
-
.join('\n')
|
|
849
|
-
.trim();
|
|
850
|
-
return joined.length > 0 ? joined : undefined;
|
|
851
|
-
}
|
|
852
756
|
/**
|
|
853
757
|
* Extract what this multiple-choice prompt is asking approval for (Issue #1699).
|
|
854
758
|
*
|
|
@@ -864,8 +768,9 @@ function joinApprovalTarget(lines, start, end) {
|
|
|
864
768
|
*
|
|
865
769
|
* Residual, deliberately left alone: `question` itself is built from up to
|
|
866
770
|
* {@link QUESTION_CONTEXT_LINES} lines above `questionEndIndex` and is also part
|
|
867
|
-
* of the deny surface.
|
|
868
|
-
*
|
|
771
|
+
* of the deny surface. Issue #3832 only stops it at the user's message echo
|
|
772
|
+
* (see {@link extractQuestionText}); a previous turn below no echo still
|
|
773
|
+
* reaches it, which over-suppresses rather than under-suppresses.
|
|
869
774
|
*
|
|
870
775
|
* [Issue #2486] One exception to both: neither reaches above an AskUserQuestion
|
|
871
776
|
* picker's tab row. Everything up there is the transcript — the Bash call Claude
|
|
@@ -882,7 +787,19 @@ function extractApprovalTarget(lines, questionEndIndex, effectiveEnd, floor = 0)
|
|
|
882
787
|
if (questionEndIndex < 0) {
|
|
883
788
|
return undefined;
|
|
884
789
|
}
|
|
885
|
-
|
|
790
|
+
const range = (0, prompt_detect_multiple_choice_approval_context_1.findApprovalContextRange)(lines, questionEndIndex, effectiveEnd);
|
|
791
|
+
// Issue #3881: the picker's tab row is a boundary of its own, so a scan it
|
|
792
|
+
// cuts short is complete whatever the scan said about the rows above.
|
|
793
|
+
const bounded = floor > 0 && floor >= range.start;
|
|
794
|
+
const start = Math.max(floor, range.start);
|
|
795
|
+
return {
|
|
796
|
+
approvalTarget: (0, prompt_detect_multiple_choice_approval_context_1.joinApprovalTarget)(lines, start, effectiveEnd),
|
|
797
|
+
approvalContext: {
|
|
798
|
+
startRow: start,
|
|
799
|
+
endRow: effectiveEnd,
|
|
800
|
+
completeness: bounded ? 'complete' : range.completeness,
|
|
801
|
+
},
|
|
802
|
+
};
|
|
886
803
|
}
|
|
887
804
|
/**
|
|
888
805
|
* Build the final PromptDetectionResult for a multiple choice prompt.
|
|
@@ -901,7 +818,7 @@ function extractApprovalTarget(lines, questionEndIndex, effectiveEnd, floor = 0)
|
|
|
901
818
|
* @returns PromptDetectionResult with isPrompt: true and multiple_choice data
|
|
902
819
|
*/
|
|
903
820
|
function buildMultipleChoiceResult(question, collectedOptions, context, output, truncateRawContentFn, submitMode, isAskUserQuestion) {
|
|
904
|
-
const { instructionText, approvalTarget } = context ?? {};
|
|
821
|
+
const { instructionText, approvalTarget, approvalContext } = context ?? {};
|
|
905
822
|
return {
|
|
906
823
|
isPrompt: true,
|
|
907
824
|
promptData: {
|
|
@@ -919,11 +836,16 @@ function buildMultipleChoiceResult(question, collectedOptions, context, output,
|
|
|
919
836
|
status: 'pending',
|
|
920
837
|
instructionText,
|
|
921
838
|
...(approvalTarget !== undefined ? { approvalTarget } : {}),
|
|
839
|
+
// Issue #3881 (H-3): only the case the resolver must act on.
|
|
840
|
+
...(approvalTarget !== undefined && approvalContext?.completeness === 'truncated'
|
|
841
|
+
? { approvalTargetCompleteness: 'truncated' }
|
|
842
|
+
: {}),
|
|
922
843
|
...(submitMode ? { submitMode } : {}),
|
|
923
844
|
...(isAskUserQuestion ? { isAskUserQuestion: true } : {}),
|
|
924
845
|
},
|
|
925
846
|
cleanContent: question.trim(),
|
|
926
847
|
rawContent: truncateRawContentFn(output.trim()), // Issue #235: complete prompt output (truncated) [MF-001]
|
|
848
|
+
...(approvalContext !== undefined ? { approvalContext } : {}),
|
|
927
849
|
};
|
|
928
850
|
}
|
|
929
851
|
// ============================================================================
|
|
@@ -975,6 +897,61 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
975
897
|
// C-003: Use ?? true for readability instead of !== false double negation
|
|
976
898
|
const requireDefault = options?.requireDefaultIndicator ?? true;
|
|
977
899
|
const lines = output.split('\n');
|
|
900
|
+
const { scanStart, effectiveEnd, hasAskUserQuestionFooter } = findScanWindow(lines);
|
|
901
|
+
const scanWindow = lines.slice(scanStart, effectiveEnd);
|
|
902
|
+
if (hasModelOverlayFooter(scanWindow)) {
|
|
903
|
+
return noPromptResult(output);
|
|
904
|
+
}
|
|
905
|
+
const { hasConfirmationFooter, hasNumberFooter } = findConfirmationFooter(scanWindow);
|
|
906
|
+
// ==========================================================================
|
|
907
|
+
// Pass 1: Check for ❯ indicator existence in scan window
|
|
908
|
+
// If no ❯ lines found and requireDefault is true, there is no multiple_choice prompt.
|
|
909
|
+
// When requireDefault is false, skip this gate entirely to allow ❯-less detection.
|
|
910
|
+
// ==========================================================================
|
|
911
|
+
if (requireDefault) {
|
|
912
|
+
const hasDefaultLine = hasDefaultOptionLine(scanWindow);
|
|
913
|
+
if (!hasDefaultLine && !hasConfirmationFooter) {
|
|
914
|
+
return noPromptResult(output);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
const scan = collectOptions(lines, scanStart, effectiveEnd);
|
|
918
|
+
if (scan === null) {
|
|
919
|
+
return noPromptResult(output);
|
|
920
|
+
}
|
|
921
|
+
const { collectedOptions, optionRows, questionEndIndex } = scan;
|
|
922
|
+
if (!passesOptionValidation(collectedOptions, requireDefault, hasConfirmationFooter, questionEndIndex, lines, scanStart)) {
|
|
923
|
+
return noPromptResult(output);
|
|
924
|
+
}
|
|
925
|
+
// [Issue #2486] An AskUserQuestion picker's tab row is the dialog's top edge:
|
|
926
|
+
// the row is chrome and everything above it is transcript. Found once so
|
|
927
|
+
// `question` and `approvalTarget` start at the same row; 0 on a frame with no
|
|
928
|
+
// tab row, which leaves every other prompt exactly as it was. `instructionText`
|
|
929
|
+
// keeps its wide window — it is the human's view, and on a preview picker the
|
|
930
|
+
// pane it carries is what the question asks the human to look at.
|
|
931
|
+
const pickerTop = questionEndIndex < 0
|
|
932
|
+
? 0
|
|
933
|
+
: (0, picker_chrome_1.findAskUserQuestionTabRow)(lines, questionEndIndex, prompt_detect_multiple_choice_approval_context_1.APPROVAL_TARGET_MAX_LOOKBACK) + 1;
|
|
934
|
+
const question = extractQuestionText(lines, questionEndIndex, pickerTop, effectiveEnd);
|
|
935
|
+
const instructionText = extractInstructionText(lines, questionEndIndex, effectiveEnd);
|
|
936
|
+
// Issue #1699: the same block, cut at the previous turn's boundary. Display
|
|
937
|
+
// keeps the wide window; deny patterns are judged against this one.
|
|
938
|
+
const approvalBlock = extractApprovalTarget(lines, questionEndIndex, effectiveEnd, pickerTop);
|
|
939
|
+
// Issue #616: Determine submitMode from confirmation footer (detected in single-pass above)
|
|
940
|
+
const submitMode = hasConfirmationFooter
|
|
941
|
+
? (hasNumberFooter ? 'answer_only' : 'answer_then_enter')
|
|
942
|
+
: undefined;
|
|
943
|
+
const result = buildMultipleChoiceResult(question, collectedOptions, { instructionText, ...approvalBlock }, output, truncateRawContentFn, submitMode, hasAskUserQuestionFooter);
|
|
944
|
+
// [Issue #3863] Only the AskUserQuestion picker: the one this parser reads
|
|
945
|
+
// as a question, whose rows are a title and an indented description.
|
|
946
|
+
if (!hasAskUserQuestionFooter)
|
|
947
|
+
return result;
|
|
948
|
+
const paneColumns = (0, option_recommendation_1.lowerBoundPaneColumns)(lines);
|
|
949
|
+
return {
|
|
950
|
+
...result,
|
|
951
|
+
optionRecommendations: collectedOptions.map((option, index) => readPickerOptionRecommendation(option.label, optionRows[index], paneColumns)),
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function findScanWindow(lines) {
|
|
978
955
|
// Strip trailing empty lines (tmux terminal padding) before computing scan window.
|
|
979
956
|
// tmux buffers often end with many empty padding lines that would shift the
|
|
980
957
|
// scan window away from the actual prompt content.
|
|
@@ -1008,7 +985,9 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1008
985
|
break;
|
|
1009
986
|
}
|
|
1010
987
|
}
|
|
1011
|
-
|
|
988
|
+
return { scanStart, effectiveEnd, hasAskUserQuestionFooter };
|
|
989
|
+
}
|
|
990
|
+
function hasModelOverlayFooter(scanWindow) {
|
|
1012
991
|
// [Issue #1495] Claude `/model` local-settings overlay guard.
|
|
1013
992
|
// The `/model` overlay renders a ❯-marked numbered model list ("1. Default …
|
|
1014
993
|
// 5. Haiku") under a "Select model" header, which the option-collection logic
|
|
@@ -1024,9 +1003,12 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1024
1003
|
// selection list via CLAUDE_SELECTION_LIST_FOOTER (NavigationButtons + ESC hatch).
|
|
1025
1004
|
for (const rawLine of scanWindow) {
|
|
1026
1005
|
if (cli_patterns_1.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN.test(rawLine)) {
|
|
1027
|
-
return
|
|
1006
|
+
return true;
|
|
1028
1007
|
}
|
|
1029
1008
|
}
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
function findConfirmationFooter(scanWindow) {
|
|
1030
1012
|
// Single-pass footer detection: identify both confirmation footer presence and
|
|
1031
1013
|
// specific "press number to confirm" variant in one iteration (Issue #616).
|
|
1032
1014
|
let hasConfirmationFooter = false;
|
|
@@ -1041,31 +1023,36 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1041
1023
|
break; // Both patterns are subsets of CONFIRMATION_FOOTER; one match suffices
|
|
1042
1024
|
}
|
|
1043
1025
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
for (const rawLine of scanWindow) {
|
|
1052
|
-
const line = rawLine.trim();
|
|
1053
|
-
if (DEFAULT_OPTION_PATTERN.test(line)) {
|
|
1054
|
-
hasDefaultLine = true;
|
|
1055
|
-
break;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
if (!hasDefaultLine && !hasConfirmationFooter) {
|
|
1059
|
-
return noPromptResult(output);
|
|
1026
|
+
return { hasConfirmationFooter, hasNumberFooter };
|
|
1027
|
+
}
|
|
1028
|
+
function hasDefaultOptionLine(scanWindow) {
|
|
1029
|
+
for (const rawLine of scanWindow) {
|
|
1030
|
+
const line = rawLine.trim();
|
|
1031
|
+
if (DEFAULT_OPTION_PATTERN.test(line)) {
|
|
1032
|
+
return true;
|
|
1060
1033
|
}
|
|
1061
1034
|
}
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
function collectOptions(lines, scanStart, effectiveEnd) {
|
|
1062
1038
|
// ==========================================================================
|
|
1063
1039
|
// Pass 2: Collect options (executed when Pass 1 passes or is skipped)
|
|
1064
1040
|
// Scan from end to find options, using both patterns.
|
|
1065
1041
|
// ==========================================================================
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1042
|
+
const state = {
|
|
1043
|
+
collectedOptions: [],
|
|
1044
|
+
// [Issue #3863] Per collected option (same order): the option row as drawn
|
|
1045
|
+
// and the rows under it, for telling its title from its description.
|
|
1046
|
+
optionRows: [],
|
|
1047
|
+
// Continuation rows met since the option below was collected — the rows
|
|
1048
|
+
// UNDER the option the reverse scan reaches next.
|
|
1049
|
+
rowsBelow: [],
|
|
1050
|
+
questionEndIndex: -1,
|
|
1051
|
+
continuationLineCount: 0,
|
|
1052
|
+
// [Issue #2486] Wrapped label rows beside a preview pane, held for the option
|
|
1053
|
+
// ABOVE them, which the reverse scan has not reached yet.
|
|
1054
|
+
paneWrap: [],
|
|
1055
|
+
};
|
|
1069
1056
|
// [Issue #1708] Claude's bottom-anchored task panel is an overlay, not part of
|
|
1070
1057
|
// the prompt frame. Resolved as a block up front because the reverse scan meets
|
|
1071
1058
|
// its rows before its header and so cannot recognise them one at a time.
|
|
@@ -1074,16 +1061,13 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1074
1061
|
// rows the scan likewise meets before the top border that identifies it — and
|
|
1075
1062
|
// some of which it shares with the options column.
|
|
1076
1063
|
const chrome = findClaudeChrome(lines, scanStart, effectiveEnd);
|
|
1077
|
-
// [Issue #2486] Wrapped label rows beside a preview pane, held for the option
|
|
1078
|
-
// ABOVE them, which the reverse scan has not reached yet.
|
|
1079
|
-
let paneWrap = [];
|
|
1080
1064
|
for (let i = effectiveEnd - 1; i >= scanStart; i--) {
|
|
1081
1065
|
// [Issue #1708] Chrome rows are metadata. Same handling as the
|
|
1082
1066
|
// COLLAPSED_OUTPUT / SUMMARY guards below: skip without ending the scan, so
|
|
1083
1067
|
// the options rendered ABOVE the panel (or beside the pane) are still
|
|
1084
1068
|
// reachable.
|
|
1085
1069
|
if (chrome.rows.has(i)) {
|
|
1086
|
-
continuationLineCount++;
|
|
1070
|
+
state.continuationLineCount++;
|
|
1087
1071
|
continue;
|
|
1088
1072
|
}
|
|
1089
1073
|
// [Issue #2486] A row the preview pane shares with the options column is
|
|
@@ -1093,143 +1077,180 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1093
1077
|
// Original indentation preserved; only the pane is cut off.
|
|
1094
1078
|
const rawLine = paneCut === undefined ? lines[i] : lines[i].slice(0, paneCut).trimEnd();
|
|
1095
1079
|
const line = rawLine.trim();
|
|
1096
|
-
|
|
1097
|
-
if (paneCut !== undefined && PANE_WRAPPED_LABEL_PATTERN.test(rawLine)) {
|
|
1098
|
-
paneWrap.unshift(line);
|
|
1099
|
-
continuationLineCount++;
|
|
1080
|
+
if (isMetadataRow(state, rawLine, line, paneCut)) {
|
|
1100
1081
|
continue;
|
|
1101
1082
|
}
|
|
1102
|
-
|
|
1103
|
-
// metadata, not selectable options. Skip them before option parsing to
|
|
1104
|
-
// avoid misclassifying "12" as an option number.
|
|
1105
|
-
if (COLLAPSED_OUTPUT_PATTERN.test(line)) {
|
|
1106
|
-
continuationLineCount++;
|
|
1083
|
+
if (tryCollectOption(state, rawLine, line, paneCut)) {
|
|
1107
1084
|
continue;
|
|
1108
1085
|
}
|
|
1109
|
-
// [Issue #704] Claude v2.1.142 renders summary lines like "… +1 pending"
|
|
1110
|
-
// BELOW the real options. Without this guard, NORMAL_OPTION_PATTERN below
|
|
1111
|
-
// would match the summary line as `option N / label='pending'`, which
|
|
1112
|
-
// would then poison isValidPrecedingOption() and cause every real option
|
|
1113
|
-
// above (1./2./3.) to be rejected — resulting in collectedOptions.length<2
|
|
1114
|
-
// and a spurious no_prompt verdict. Treat the summary line as metadata
|
|
1115
|
-
// (same continuation handling as COLLAPSED_OUTPUT_PATTERN above) so the
|
|
1116
|
-
// reverse scan continues into the actual option block.
|
|
1117
|
-
//
|
|
1118
|
-
// Defense-in-depth: works alongside the S2 effectiveEnd trim above. Either
|
|
1119
|
-
// S1 or S2 alone is sufficient to fix Issue #704; keeping both protects
|
|
1120
|
-
// against future Claude renderings where the footer line text changes
|
|
1121
|
-
// (S1 still catches) or the summary line wording changes (S2 still
|
|
1122
|
-
// catches via footer trim).
|
|
1123
|
-
if (SUMMARY_LINE_PATTERN.test(line)) {
|
|
1124
|
-
continuationLineCount++;
|
|
1125
|
-
continue;
|
|
1126
|
-
}
|
|
1127
|
-
// Try DEFAULT_OPTION_PATTERN first (❯ indicator)
|
|
1128
|
-
const defaultMatch = line.match(DEFAULT_OPTION_PATTERN);
|
|
1129
|
-
if (defaultMatch) {
|
|
1130
|
-
const number = parseInt(defaultMatch[1], 10);
|
|
1131
|
-
if (number <= 20 && isValidPrecedingOption(number, collectedOptions)) {
|
|
1132
|
-
// [Issue #2486] A label that wrapped beside a preview pane is whole again.
|
|
1133
|
-
const label = [defaultMatch[2].trim(), ...paneWrap].join(' ');
|
|
1134
|
-
paneWrap = [];
|
|
1135
|
-
collectedOptions.unshift({ number, label, isDefault: true });
|
|
1136
|
-
continuationLineCount = 0;
|
|
1137
|
-
continue;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
// Try NORMAL_OPTION_PATTERN (no ❯ indicator)
|
|
1141
|
-
const normalMatch = line.match(NORMAL_OPTION_PATTERN);
|
|
1142
|
-
if (normalMatch) {
|
|
1143
|
-
const number = parseInt(normalMatch[1], 10);
|
|
1144
|
-
// Skip unreasonably large option numbers (e.g., "[… 373 lines]" from Codex
|
|
1145
|
-
// collapsed output matches as option 373). CLI prompts never exceed 20 options.
|
|
1146
|
-
if (number > 20) {
|
|
1147
|
-
// Treat as non-option line
|
|
1148
|
-
}
|
|
1149
|
-
else if (!isValidPrecedingOption(number, collectedOptions)) {
|
|
1150
|
-
// Treat as non-option line (e.g., diff line numbers from Codex approval prompts)
|
|
1151
|
-
}
|
|
1152
|
-
else {
|
|
1153
|
-
const label = [normalMatch[2].trim(), ...paneWrap].join(' ');
|
|
1154
|
-
paneWrap = [];
|
|
1155
|
-
collectedOptions.unshift({ number, label, isDefault: false });
|
|
1156
|
-
continuationLineCount = 0;
|
|
1157
|
-
continue;
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
1086
|
// [Issue #287 Bug3] User input prompt barrier:
|
|
1161
1087
|
// When no options have been collected yet and the line starts with ❯ (U+276F)
|
|
1162
1088
|
// but did NOT match DEFAULT_OPTION_PATTERN above, this line is a Claude Code
|
|
1163
1089
|
// user input prompt (e.g., "❯ 1", "❯ /command") or idle prompt ("❯").
|
|
1164
1090
|
// Anything above this line in the scrollback is historical conversation text,
|
|
1165
1091
|
// not an active prompt. Stop scanning to prevent false positives.
|
|
1166
|
-
if (collectedOptions.length === 0 && (line.startsWith('
|
|
1167
|
-
return
|
|
1092
|
+
if (state.collectedOptions.length === 0 && (line.startsWith('❯') || line.startsWith('●') || line.startsWith('›'))) {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
// [Issue #3863] An indented row under the bottom option: its wrapped title
|
|
1096
|
+
// or its description. Only remembered; the scan goes on as before.
|
|
1097
|
+
if (state.collectedOptions.length === 0 && line && /^\s{3,}/.test(rawLine)) {
|
|
1098
|
+
state.rowsBelow.unshift(rawLine);
|
|
1168
1099
|
}
|
|
1169
1100
|
// Non-option line handling
|
|
1170
|
-
if (collectedOptions.length > 0 && line && !SEPARATOR_LINE_PATTERN.test(line)) {
|
|
1171
|
-
|
|
1172
|
-
// continuation BEFORE question keywords. These are option description
|
|
1173
|
-
// lines (e.g., " Let Gemini CLI decide the best model...") that may
|
|
1174
|
-
// contain keywords like "decide" or "select" but are NOT question lines.
|
|
1175
|
-
// The 2-space " Select model" case (Issue #256) is excluded by the 4+ threshold.
|
|
1176
|
-
const isDeepIndent = /^\s{4,}/.test(rawLine);
|
|
1177
|
-
if (isDeepIndent && isContinuationLine(rawLine, line)) {
|
|
1178
|
-
continuationLineCount++;
|
|
1179
|
-
if (continuationLineCount > MAX_DEEP_INDENT_CONTINUATION_LINES) {
|
|
1180
|
-
questionEndIndex = i;
|
|
1181
|
-
break;
|
|
1182
|
-
}
|
|
1183
|
-
continue;
|
|
1184
|
-
}
|
|
1185
|
-
// [MF-001 / Issue #256] Check if line is a question-like line BEFORE
|
|
1186
|
-
// continuation check. This preserves isContinuationLine()'s SRP by not
|
|
1187
|
-
// mixing question detection into it. Without this pre-check, indented
|
|
1188
|
-
// question lines (e.g., " Select model") could be misclassified as
|
|
1189
|
-
// continuation lines by isContinuationLine()'s hasLeadingSpaces check.
|
|
1190
|
-
//
|
|
1191
|
-
// [SF-S4-003] Both this pre-check and SEC-001b upward scan use the same
|
|
1192
|
-
// isQuestionLikeLine() function intentionally (DRY). If a question line is
|
|
1193
|
-
// caught here, SEC-001b upward scan is not needed (questionEndIndex line
|
|
1194
|
-
// itself passes isQuestionLikeLine()).
|
|
1195
|
-
if (isQuestionLikeLine(line)) {
|
|
1196
|
-
questionEndIndex = i;
|
|
1101
|
+
if (state.collectedOptions.length > 0 && line && !prompt_detect_multiple_choice_approval_context_1.SEPARATOR_LINE_PATTERN.test(line)) {
|
|
1102
|
+
if (scanNonOptionLine(state, rawLine, line, i) === 'break') {
|
|
1197
1103
|
break;
|
|
1198
1104
|
}
|
|
1199
|
-
// Check if this is a continuation line (indented line between options,
|
|
1200
|
-
// or path/filename fragments from terminal width wrapping - Issue #181)
|
|
1201
|
-
if (isContinuationLine(rawLine, line)) {
|
|
1202
|
-
continuationLineCount++;
|
|
1203
|
-
const maxContinuationLines = /^\s{4,}/.test(rawLine)
|
|
1204
|
-
? MAX_DEEP_INDENT_CONTINUATION_LINES
|
|
1205
|
-
: MAX_CONTINUATION_LINES;
|
|
1206
|
-
// Issue #372: Codex TUI indents all output with 2 spaces, causing
|
|
1207
|
-
// every line to match isContinuationLine(). Limit the scan distance
|
|
1208
|
-
// to prevent traversing into body text where numbered lists would be
|
|
1209
|
-
// collected as false options.
|
|
1210
|
-
if (continuationLineCount > maxContinuationLines) {
|
|
1211
|
-
questionEndIndex = i;
|
|
1212
|
-
break;
|
|
1213
|
-
}
|
|
1214
|
-
// Skip continuation lines and continue scanning for more options
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
// Found a non-empty, non-separator line before options - likely the question
|
|
1218
|
-
questionEndIndex = i;
|
|
1219
|
-
break;
|
|
1220
1105
|
}
|
|
1221
1106
|
}
|
|
1107
|
+
return {
|
|
1108
|
+
collectedOptions: state.collectedOptions,
|
|
1109
|
+
optionRows: state.optionRows,
|
|
1110
|
+
questionEndIndex: state.questionEndIndex,
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
/** Rows the reverse scan skips as metadata without ending the scan. */
|
|
1114
|
+
function isMetadataRow(state, rawLine, line, paneCut) {
|
|
1115
|
+
// [Issue #2486] See PANE_WRAPPED_LABEL_PATTERN: the tail of the label above.
|
|
1116
|
+
if (paneCut !== undefined && PANE_WRAPPED_LABEL_PATTERN.test(rawLine)) {
|
|
1117
|
+
state.paneWrap.unshift(line);
|
|
1118
|
+
state.continuationLineCount++;
|
|
1119
|
+
return true;
|
|
1120
|
+
}
|
|
1121
|
+
// Collapsed preview markers like "[… 12 lines] ctrl + a view all" are
|
|
1122
|
+
// metadata, not selectable options. Skip them before option parsing to
|
|
1123
|
+
// avoid misclassifying "12" as an option number.
|
|
1124
|
+
if (COLLAPSED_OUTPUT_PATTERN.test(line)) {
|
|
1125
|
+
state.continuationLineCount++;
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
// [Issue #704] Claude v2.1.142 renders summary lines like "… +1 pending"
|
|
1129
|
+
// BELOW the real options. Without this guard, NORMAL_OPTION_PATTERN below
|
|
1130
|
+
// would match the summary line as `option N / label='pending'`, which
|
|
1131
|
+
// would then poison isValidPrecedingOption() and cause every real option
|
|
1132
|
+
// above (1./2./3.) to be rejected — resulting in collectedOptions.length<2
|
|
1133
|
+
// and a spurious no_prompt verdict. Treat the summary line as metadata
|
|
1134
|
+
// (same continuation handling as COLLAPSED_OUTPUT_PATTERN above) so the
|
|
1135
|
+
// reverse scan continues into the actual option block.
|
|
1136
|
+
//
|
|
1137
|
+
// Defense-in-depth: works alongside the S2 effectiveEnd trim above. Either
|
|
1138
|
+
// S1 or S2 alone is sufficient to fix Issue #704; keeping both protects
|
|
1139
|
+
// against future Claude renderings where the footer line text changes
|
|
1140
|
+
// (S1 still catches) or the summary line wording changes (S2 still
|
|
1141
|
+
// catches via footer trim).
|
|
1142
|
+
if (SUMMARY_LINE_PATTERN.test(line)) {
|
|
1143
|
+
state.continuationLineCount++;
|
|
1144
|
+
return true;
|
|
1145
|
+
}
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
/** Collects the row as an option when either option pattern accepts it. */
|
|
1149
|
+
function tryCollectOption(state, rawLine, line, paneCut) {
|
|
1150
|
+
// Try DEFAULT_OPTION_PATTERN first (❯ indicator)
|
|
1151
|
+
const defaultMatch = line.match(DEFAULT_OPTION_PATTERN);
|
|
1152
|
+
if (defaultMatch) {
|
|
1153
|
+
const number = parseInt(defaultMatch[1], 10);
|
|
1154
|
+
if (number <= 20 && isValidPrecedingOption(number, state.collectedOptions)) {
|
|
1155
|
+
// [Issue #2486] A label that wrapped beside a preview pane is whole again.
|
|
1156
|
+
collectOption(state, rawLine, number, defaultMatch[2], true, paneCut);
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
// Try NORMAL_OPTION_PATTERN (no ❯ indicator)
|
|
1161
|
+
const normalMatch = line.match(NORMAL_OPTION_PATTERN);
|
|
1162
|
+
if (normalMatch) {
|
|
1163
|
+
const number = parseInt(normalMatch[1], 10);
|
|
1164
|
+
// Skip unreasonably large option numbers (e.g., "[… 373 lines]" from Codex
|
|
1165
|
+
// collapsed output matches as option 373). CLI prompts never exceed 20 options.
|
|
1166
|
+
if (number > 20) {
|
|
1167
|
+
// Treat as non-option line
|
|
1168
|
+
}
|
|
1169
|
+
else if (!isValidPrecedingOption(number, state.collectedOptions)) {
|
|
1170
|
+
// Treat as non-option line (e.g., diff line numbers from Codex approval prompts)
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
collectOption(state, rawLine, number, normalMatch[2], false, paneCut);
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
function collectOption(state, rawLine, number, matchedLabel, isDefault, paneCut) {
|
|
1180
|
+
const label = [matchedLabel.trim(), ...state.paneWrap].join(' ');
|
|
1181
|
+
state.optionRows.unshift({ row: rawLine, besidePane: paneCut !== undefined || state.paneWrap.length > 0, rowsBelow: state.rowsBelow });
|
|
1182
|
+
state.rowsBelow = [];
|
|
1183
|
+
state.paneWrap = [];
|
|
1184
|
+
state.collectedOptions.unshift({ number, label, isDefault });
|
|
1185
|
+
state.continuationLineCount = 0;
|
|
1186
|
+
}
|
|
1187
|
+
/** A non-empty, non-separator, non-option row above collected options. */
|
|
1188
|
+
function scanNonOptionLine(state, rawLine, line, i) {
|
|
1189
|
+
// [Issue #460] For deeply indented lines (4+ leading spaces), check
|
|
1190
|
+
// continuation BEFORE question keywords. These are option description
|
|
1191
|
+
// lines (e.g., " Let Gemini CLI decide the best model...") that may
|
|
1192
|
+
// contain keywords like "decide" or "select" but are NOT question lines.
|
|
1193
|
+
// The 2-space " Select model" case (Issue #256) is excluded by the 4+ threshold.
|
|
1194
|
+
const isDeepIndent = /^\s{4,}/.test(rawLine);
|
|
1195
|
+
if (isDeepIndent && isContinuationLine(rawLine, line)) {
|
|
1196
|
+
state.rowsBelow.unshift(rawLine);
|
|
1197
|
+
state.continuationLineCount++;
|
|
1198
|
+
if (state.continuationLineCount > MAX_DEEP_INDENT_CONTINUATION_LINES) {
|
|
1199
|
+
state.questionEndIndex = i;
|
|
1200
|
+
return 'break';
|
|
1201
|
+
}
|
|
1202
|
+
return 'continue';
|
|
1203
|
+
}
|
|
1204
|
+
// [MF-001 / Issue #256] Check if line is a question-like line BEFORE
|
|
1205
|
+
// continuation check. This preserves isContinuationLine()'s SRP by not
|
|
1206
|
+
// mixing question detection into it. Without this pre-check, indented
|
|
1207
|
+
// question lines (e.g., " Select model") could be misclassified as
|
|
1208
|
+
// continuation lines by isContinuationLine()'s hasLeadingSpaces check.
|
|
1209
|
+
//
|
|
1210
|
+
// [SF-S4-003] Both this pre-check and SEC-001b upward scan use the same
|
|
1211
|
+
// isQuestionLikeLine() function intentionally (DRY). If a question line is
|
|
1212
|
+
// caught here, SEC-001b upward scan is not needed (questionEndIndex line
|
|
1213
|
+
// itself passes isQuestionLikeLine()).
|
|
1214
|
+
if (isQuestionLikeLine(line)) {
|
|
1215
|
+
state.questionEndIndex = i;
|
|
1216
|
+
return 'break';
|
|
1217
|
+
}
|
|
1218
|
+
// Check if this is a continuation line (indented line between options,
|
|
1219
|
+
// or path/filename fragments from terminal width wrapping - Issue #181)
|
|
1220
|
+
if (isContinuationLine(rawLine, line)) {
|
|
1221
|
+
state.rowsBelow.unshift(rawLine);
|
|
1222
|
+
state.continuationLineCount++;
|
|
1223
|
+
const maxContinuationLines = /^\s{4,}/.test(rawLine)
|
|
1224
|
+
? MAX_DEEP_INDENT_CONTINUATION_LINES
|
|
1225
|
+
: MAX_CONTINUATION_LINES;
|
|
1226
|
+
// Issue #372: Codex TUI indents all output with 2 spaces, causing
|
|
1227
|
+
// every line to match isContinuationLine(). Limit the scan distance
|
|
1228
|
+
// to prevent traversing into body text where numbered lists would be
|
|
1229
|
+
// collected as false options.
|
|
1230
|
+
if (state.continuationLineCount > maxContinuationLines) {
|
|
1231
|
+
state.questionEndIndex = i;
|
|
1232
|
+
return 'break';
|
|
1233
|
+
}
|
|
1234
|
+
// Skip continuation lines and continue scanning for more options
|
|
1235
|
+
return 'continue';
|
|
1236
|
+
}
|
|
1237
|
+
// Found a non-empty, non-separator line before options - likely the question
|
|
1238
|
+
state.questionEndIndex = i;
|
|
1239
|
+
return 'break';
|
|
1240
|
+
}
|
|
1241
|
+
/** Layers 3–5: whether the collected options make a prompt. */
|
|
1242
|
+
function passesOptionValidation(collectedOptions, requireDefault, hasConfirmationFooter, questionEndIndex, lines, scanStart) {
|
|
1222
1243
|
// Layer 3: Consecutive number validation (defensive measure)
|
|
1223
1244
|
const optionNumbers = collectedOptions.map(opt => opt.number);
|
|
1224
1245
|
if (!isConsecutiveFromOne(optionNumbers)) {
|
|
1225
|
-
return
|
|
1246
|
+
return false;
|
|
1226
1247
|
}
|
|
1227
1248
|
// Layer 4: Must have at least 2 options. When requireDefault is true,
|
|
1228
1249
|
// also require at least one option with ❯ indicator.
|
|
1229
1250
|
const hasDefaultIndicator = collectedOptions.some(opt => opt.isDefault);
|
|
1230
1251
|
const allowMissingDefaultIndicator = requireDefault && hasConfirmationFooter;
|
|
1231
1252
|
if (collectedOptions.length < 2 || (requireDefault && !hasDefaultIndicator && !allowMissingDefaultIndicator)) {
|
|
1232
|
-
return
|
|
1253
|
+
return false;
|
|
1233
1254
|
}
|
|
1234
1255
|
// Layer 5 [SEC-001]: Enhanced question line validation for requireDefaultIndicator=false.
|
|
1235
1256
|
// When requireDefault is false, apply stricter validation to prevent false positives
|
|
@@ -1244,46 +1265,71 @@ function detectMultipleChoicePrompt(output, options, truncateRawContentFn) {
|
|
|
1244
1265
|
// (「実装方針を選んでください」) be detected. Indicator-less frames (#193 capture
|
|
1245
1266
|
// artifacts) still go through SEC-001a/b unchanged.
|
|
1246
1267
|
if (!requireDefault && !hasDefaultIndicator) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
+
return passesQuestionLineValidation(questionEndIndex, lines, scanStart);
|
|
1269
|
+
}
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
function passesQuestionLineValidation(questionEndIndex, lines, scanStart) {
|
|
1273
|
+
// SEC-001a: No question line found (questionEndIndex === -1) - reject.
|
|
1274
|
+
// Prevents generic question fallback from triggering Auto-Yes
|
|
1275
|
+
// on plain numbered lists that happen to be consecutive from 1.
|
|
1276
|
+
if (questionEndIndex === -1) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
// SEC-001b: Question line exists but is not actually a question/selection request.
|
|
1280
|
+
// Validates that the question line contains a question mark or a selection keyword
|
|
1281
|
+
// with colon, distinguishing "Select an option:" from "Recommendations:".
|
|
1282
|
+
//
|
|
1283
|
+
// [Issue #256] Enhanced with upward scan via findQuestionLineInRange() (SF-003).
|
|
1284
|
+
// When questionEndIndex line itself is not a question-like line, scan upward
|
|
1285
|
+
// within QUESTION_SCAN_RANGE to find a question line above it. This handles:
|
|
1286
|
+
// - Multi-line question wrapping where ? is on a line above questionEndIndex
|
|
1287
|
+
// - Model selection prompts where "Select model" is above description lines
|
|
1288
|
+
const questionLine = lines[questionEndIndex]?.trim() ?? '';
|
|
1289
|
+
if (!isQuestionLikeLine(questionLine)) {
|
|
1290
|
+
// Upward scan: look for a question-like line above questionEndIndex
|
|
1291
|
+
if (!findQuestionLineInRange(lines, questionEndIndex, QUESTION_SCAN_RANGE, scanStart)) {
|
|
1292
|
+
return false;
|
|
1268
1293
|
}
|
|
1269
1294
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1295
|
+
return true;
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* [Issue #3863] What an AskUserQuestion option's title says about being
|
|
1299
|
+
* recommended (`option-recommendation`).
|
|
1300
|
+
*
|
|
1301
|
+
* The picker draws a title row and its description indented under it; a title
|
|
1302
|
+
* too long for the pane wraps to the same indent, so the rows alone do not
|
|
1303
|
+
* say which they are. Read:
|
|
1304
|
+
*
|
|
1305
|
+
* - no row under it: the label is the title;
|
|
1306
|
+
* - beside a preview pane (#2486), where the column draws no descriptions:
|
|
1307
|
+
* the label — its wrapped rows already joined — is the title, as long as no
|
|
1308
|
+
* other row is under it;
|
|
1309
|
+
* - the row under it could not be the title's wrap (its first word would have
|
|
1310
|
+
* fit on the option row, `cannotBeWrapOf`): the label is the title;
|
|
1311
|
+
* - the row under it may be the wrap and holds nothing but a marker: the title
|
|
1312
|
+
* wrapped right before its marker — and ends there only if nothing after
|
|
1313
|
+
* the marker continues it: no further row, or a row that could not be the
|
|
1314
|
+
* marker row's wrap (its first word would have fit after the marker). A row
|
|
1315
|
+
* that may still be the title wrapping on (a word too long to fit after the
|
|
1316
|
+
* marker) leaves the title's end, and so the marker's place, unknown;
|
|
1317
|
+
* - otherwise `unknown`.
|
|
1318
|
+
*/
|
|
1319
|
+
function readPickerOptionRecommendation(label, reading, paneColumns) {
|
|
1320
|
+
if (reading === undefined)
|
|
1321
|
+
return 'unknown';
|
|
1322
|
+
const [next] = reading.rowsBelow;
|
|
1323
|
+
if (next === undefined)
|
|
1324
|
+
return (0, option_recommendation_1.isRecommendedTitle)(label);
|
|
1325
|
+
if (reading.besidePane)
|
|
1326
|
+
return 'unknown';
|
|
1327
|
+
if ((0, option_recommendation_1.cannotBeWrapOf)(reading.row, next, paneColumns))
|
|
1328
|
+
return (0, option_recommendation_1.isRecommendedTitle)(label);
|
|
1329
|
+
if (!(0, option_recommendation_1.isRecommendationMarker)(next))
|
|
1330
|
+
return 'unknown';
|
|
1331
|
+
const after = reading.rowsBelow[1];
|
|
1332
|
+
if (after === undefined || (0, option_recommendation_1.cannotBeWrapOf)(next, after, paneColumns))
|
|
1333
|
+
return true;
|
|
1334
|
+
return 'unknown';
|
|
1289
1335
|
}
|