commandmate 0.36.1 → 0.38.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 +172 -172
- package/.next/app-path-routes-manifest.json +30 -30
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +1 -1
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route.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_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 +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +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.js.nft.json +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.js.nft.json +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.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.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +1 -1
- 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]/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 +4 -3
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -6
- 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 +1 -1
- 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 +2 -7
- 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.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js.nft.json +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.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_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_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 -9
- 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 +30 -30
- package/.next/server/chunks/1467.js +1 -1
- package/.next/server/chunks/1707.js +1 -1
- package/.next/server/chunks/1886.js +2 -2
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3480.js +1 -18
- package/.next/server/chunks/3730.js +1 -1
- package/.next/server/chunks/4207.js +5 -0
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/650.js +1 -1
- package/.next/server/chunks/6661.js +1 -1
- package/.next/server/chunks/6677.js +2 -2
- package/.next/server/chunks/6793.js +13 -0
- package/.next/server/chunks/7075.js +1 -1
- package/.next/server/chunks/8052.js +74 -0
- package/.next/server/chunks/8728.js +2 -2
- package/.next/server/chunks/9080.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{2407-e738634dab84534c.js → 2407-6fd60b674e20ec06.js} +1 -1
- package/.next/static/chunks/4388-a778968d9c4e9327.js +1 -0
- package/.next/static/chunks/4949-7dbce1029c6bdbb9.js +1 -0
- package/.next/static/chunks/998-292ebcd30f329eb1.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-86ef4510c70adefc.js +1 -0
- package/.next/trace +14 -14
- package/README.md +1 -1
- package/dist/cli/commands/ls.d.ts +64 -0
- package/dist/cli/commands/ls.d.ts.map +1 -1
- package/dist/cli/commands/ls.js +238 -30
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +14 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +154 -15
- package/dist/cli/docs/agent-operations.d.ts +1 -1
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +37 -1
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/utils/server-url.d.ts +27 -0
- package/dist/cli/utils/server-url.d.ts.map +1 -1
- package/dist/cli/utils/server-url.js +48 -6
- package/dist/lib/cli-tools/types.d.ts +22 -1
- package/dist/lib/cli-tools/types.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.d.ts +8 -0
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.js +8 -0
- package/dist/server/locales/en/worktree.json +21 -0
- package/dist/server/locales/ja/worktree.json +21 -0
- package/dist/server/src/lib/cli-tools/agent-mode-spec.js +327 -0
- package/dist/server/src/lib/cli-tools/base.js +22 -0
- package/dist/server/src/lib/cmate-parser.js +26 -6
- package/dist/server/src/lib/cmate-validator.js +354 -0
- package/dist/server/src/lib/detection/cli-patterns.js +40 -2
- package/dist/server/src/lib/detection/model-info-extractor.js +40 -3
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +44 -0
- package/dist/server/src/lib/detection/tools/antigravity/detect.js +9 -0
- package/dist/server/src/lib/detection/tools/command-code/detect.js +14 -3
- package/dist/server/src/lib/detection/tools/command-code/permission.js +104 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +8 -0
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +20 -2
- package/dist/server/src/lib/prompt-answer-sender.js +339 -16
- package/dist/server/src/lib/session/claude-executor.js +190 -17
- package/dist/server/src/types/cli-tool-contracts.js +75 -1
- package/dist/types/cli-tool-contracts.d.ts +162 -0
- package/dist/types/cli-tool-contracts.d.ts.map +1 -1
- package/dist/types/cli-tool-contracts.js +75 -1
- package/package.json +1 -1
- package/.next/server/chunks/6337.js +0 -1
- package/.next/server/chunks/663.js +0 -79
- package/.next/static/chunks/4388-410b1d8ed0d8d98b.js +0 -1
- package/.next/static/chunks/4949-f547605bd6078157.js +0 -1
- package/.next/static/chunks/998-ac0ded6d5eeb4dc0.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3d147d6dd2fdc101.js +0 -1
- /package/.next/static/{OjmwYkJmQbLPFMjXdrB1R → rM1BO5QIz3tynGdyJy0T-}/_buildManifest.js +0 -0
- /package/.next/static/{OjmwYkJmQbLPFMjXdrB1R → rM1BO5QIz3tynGdyJy0T-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Command Code's permission dialog, read for the `detectDialog` seam
|
|
4
|
+
* (Issue #2574).
|
|
5
|
+
*
|
|
6
|
+
* ## What this is for, and what it is not
|
|
7
|
+
*
|
|
8
|
+
* Epic #2249 決定 3 left Command Code without a `detectDialog`, and its reason
|
|
9
|
+
* still holds: `PreToolUse` fires AFTER the dialog is answered, so a hook-driven
|
|
10
|
+
* permission decision cannot dismiss it, and Auto-Yes stays on the numbered
|
|
11
|
+
* response path. That decision is about who ANSWERS the dialog. It says nothing
|
|
12
|
+
* about how an answer has to be TYPED, which is the other thing `detectDialog`
|
|
13
|
+
* declares — `answerMode` for #2033's key guard, and `submitMode` for whether
|
|
14
|
+
* the sender follows the digit with an Enter. This module fills only that half;
|
|
15
|
+
* the Auto-Yes rollout for this tool stays `legacy` (see
|
|
16
|
+
* `AUTO_YES_DIALOG_GATE_DEFAULT_MODE`).
|
|
17
|
+
*
|
|
18
|
+
* ## Why the Enter is the defect
|
|
19
|
+
*
|
|
20
|
+
* Measured live on 1.53.1 at 200x1000: a bare `1` (or `2`) closes the dialog and
|
|
21
|
+
* runs the command — the digit is a hotkey. With no `submitMode` on the prompt,
|
|
22
|
+
* `sendPromptAnswer` resolved `answer_then_enter` and sent `C-m` a moment later,
|
|
23
|
+
* by which time the composer was back. A draft typed while the agent was
|
|
24
|
+
* thinking sat in that composer, and the Enter submitted it as a new user
|
|
25
|
+
* message. Declaring `answer_only` here moves that decision onto the frame the
|
|
26
|
+
* answer is actually sent at, so the Auto-Yes poller, `respond` and the chat
|
|
27
|
+
* surface all get it without each consumer having to re-read the screen.
|
|
28
|
+
*
|
|
29
|
+
* ## The shape, on every build captured
|
|
30
|
+
*
|
|
31
|
+
* The same rows on 1.40.1 (`dialog-create-file.txt`, `dialog-shell-command.txt`),
|
|
32
|
+
* 1.49.0 (`dialog-shell-1490.txt`, `dialog-kill-task-1490.txt`) and a live
|
|
33
|
+
* 1.53.1 pane:
|
|
34
|
+
*
|
|
35
|
+
* ──────────────────────────… (200 columns)
|
|
36
|
+
* Execute Shell Command
|
|
37
|
+
* Command Code needs to execute sleep 25 && echo done.
|
|
38
|
+
* Press [ctrl+e] to explain this command
|
|
39
|
+
* ❯ 1. Yes
|
|
40
|
+
* 2. Yes, don't ask again for `sleep` commands in this project
|
|
41
|
+
* 3. No, tell Command Code what to do differently
|
|
42
|
+
* ↑/↓ navigate · enter select · ctrl+e explain · Run cmd --yolo to bypass all permissions (Docs ↗)
|
|
43
|
+
*
|
|
44
|
+
* The rule is positional as well as textual: the hint bar must be the LAST
|
|
45
|
+
* content row and the numbered run must sit directly on top of it. Command Code
|
|
46
|
+
* draws its composer at the bottom of the pane for the whole of a turn and after
|
|
47
|
+
* it, so the only frames whose last row is not the composer are the ones where
|
|
48
|
+
* an overlay took its place. An agent reply that quotes the hint bar still has
|
|
49
|
+
* the composer under it, and an answered dialog is repainted as the tool call
|
|
50
|
+
* with the composer back below.
|
|
51
|
+
*
|
|
52
|
+
* ## Input contract
|
|
53
|
+
*
|
|
54
|
+
* Either spelling of the capture: as captured, or through
|
|
55
|
+
* `stripBoxDrawing(stripAnsi(...))` as Auto-Yes holds it. Nothing below reads
|
|
56
|
+
* the rule row `stripBoxDrawing` blanks, so both reach the same verdict.
|
|
57
|
+
*
|
|
58
|
+
* ## Deliberately not recognised
|
|
59
|
+
*
|
|
60
|
+
* `AskUserQuestion` (`./dialog.ts`, no footer), the `/model` picker (`enter to
|
|
61
|
+
* select · esc to cancel`) and the `/usage` panel (`Press Esc to close`). Their
|
|
62
|
+
* prompts already carry what they need, or have no prompt to answer, and while
|
|
63
|
+
* the rollout is `legacy` a `null` here gates nothing.
|
|
64
|
+
*/
|
|
65
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
+
exports.COMMAND_CODE_PERMISSION_FOOTER_PATTERN = void 0;
|
|
67
|
+
exports.detectCommandCodePermissionDialog = detectCommandCodePermissionDialog;
|
|
68
|
+
const dialog_block_1 = require("../dialog-block");
|
|
69
|
+
/**
|
|
70
|
+
* The hint bar under a permission dialog.
|
|
71
|
+
*
|
|
72
|
+
* `enter select` — not `enter to select`, which is the `/model` picker's — and
|
|
73
|
+
* the `to bypass all permissions` tail, which is what makes it a PERMISSION
|
|
74
|
+
* dialog rather than any other numbered overlay. `ctrl+e explain ·` sits
|
|
75
|
+
* between the two on shell commands and is absent on file edits, so the middle
|
|
76
|
+
* is not spelled out.
|
|
77
|
+
*/
|
|
78
|
+
exports.COMMAND_CODE_PERMISSION_FOOTER_PATTERN = /^\s*↑\/↓\s+navigate\s+·\s+enter\s+select\s+·.*\bto\s+bypass\s+all\s+permissions\b/;
|
|
79
|
+
/** The cursor glyph Command Code draws on the highlighted option. */
|
|
80
|
+
const COMMAND_CODE_CURSOR_GLYPH = '❯';
|
|
81
|
+
/**
|
|
82
|
+
* Does this frame carry Command Code's permission dialog?
|
|
83
|
+
*
|
|
84
|
+
* @param frame - The normalised capture, either spelling (see the module docblock)
|
|
85
|
+
* @returns `permission` / `numbered` / `answer_only` with the drawn options, or null
|
|
86
|
+
*/
|
|
87
|
+
function detectCommandCodePermissionDialog(frame) {
|
|
88
|
+
const rows = frame.contentLines;
|
|
89
|
+
const footer = rows[rows.length - 1];
|
|
90
|
+
if (footer === undefined || !exports.COMMAND_CODE_PERMISSION_FOOTER_PATTERN.test(footer))
|
|
91
|
+
return null;
|
|
92
|
+
// One footer row and nothing else between it and the bottom option.
|
|
93
|
+
const block = (0, dialog_block_1.findNumberedOptionBlock)(rows, rows.length, 1);
|
|
94
|
+
if (block === null || block.footer !== footer.trim())
|
|
95
|
+
return null;
|
|
96
|
+
if (block.selectedGlyph !== COMMAND_CODE_CURSOR_GLYPH)
|
|
97
|
+
return null;
|
|
98
|
+
return {
|
|
99
|
+
kind: 'permission',
|
|
100
|
+
options: block.options,
|
|
101
|
+
answerMode: 'numbered',
|
|
102
|
+
submitMode: 'answer_only',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -132,6 +132,14 @@ exports.ANTIGRAVITY_VERIFIED_AGAINST = {
|
|
|
132
132
|
* nobody took. When a live 1.53.x session is probed — the question screen, the
|
|
133
133
|
* keys it accepts and what one of them advances — bump this to that exact
|
|
134
134
|
* version with its own `capturedAt`.
|
|
135
|
+
*
|
|
136
|
+
* ## Not advanced by Issue #2574 either
|
|
137
|
+
*
|
|
138
|
+
* #2574 did send keys at a live 1.53.1 pane, but at the PERMISSION dialog, not
|
|
139
|
+
* the question screen: a bare digit commits it, which is the `submitMode` its
|
|
140
|
+
* `detectDialog` declares. The rule itself was read off the 1.40.1 and 1.49.0
|
|
141
|
+
* dialog frames above, and no 1.53.1 frame was added to the fixture directory,
|
|
142
|
+
* so the condition in the previous paragraph is still unmet.
|
|
135
143
|
*/
|
|
136
144
|
exports.COMMAND_CODE_VERIFIED_AGAINST = {
|
|
137
145
|
version: '1.40.1',
|
|
@@ -87,6 +87,8 @@ exports.AUTO_YES_DIALOG_GATE_MODES = ['enforce', 'legacy'];
|
|
|
87
87
|
* to write a dialog rule against. Gating it on a rule inferred from another
|
|
88
88
|
* tool's frames is the mistake #1979 had to correct; leaving it `legacy` keeps
|
|
89
89
|
* agy's Auto-Yes working exactly as it does today.
|
|
90
|
+
* - `command-code` is the one `legacy` tool that DOES declare rules (Issue
|
|
91
|
+
* #2574), and they are not enough to gate on. See its row.
|
|
90
92
|
*/
|
|
91
93
|
exports.AUTO_YES_DIALOG_GATE_DEFAULT_MODE = {
|
|
92
94
|
claude: 'enforce',
|
|
@@ -99,8 +101,24 @@ exports.AUTO_YES_DIALOG_GATE_DEFAULT_MODE = {
|
|
|
99
101
|
// Issue #2250 / Epic #2249 決定 3: Command Code's Auto-Yes stays on the legacy
|
|
100
102
|
// numbered-response path. Its `PreToolUse` hook fires AFTER the permission
|
|
101
103
|
// dialog is answered (measured: dialog 00:11:37, answer 00:11:46, hook
|
|
102
|
-
// 00:11:46), so a `permissionDecision` cannot dismiss the dialog
|
|
103
|
-
//
|
|
104
|
+
// 00:11:46), so a `permissionDecision` cannot dismiss the dialog.
|
|
105
|
+
//
|
|
106
|
+
// Issue #2574 gave the tool a `detectDialog` (`hasDialogRules: true`) and kept
|
|
107
|
+
// this row `legacy` on purpose. The rule recognises the PERMISSION dialog only;
|
|
108
|
+
// it exists to tell `sendPromptAnswer` that a digit commits without an Enter.
|
|
109
|
+
// Enforcing on it would break the other dialog this tool draws:
|
|
110
|
+
//
|
|
111
|
+
// - `AskUserQuestion` (#2521 / #2522) is not a permission dialog, so
|
|
112
|
+
// `judgeDialogPresence` would answer `present: false` for it and
|
|
113
|
+
// `/prompt-response` would refuse a readable question with
|
|
114
|
+
// `unsupported_dialog_layout`;
|
|
115
|
+
// - teaching the rule that screen would not fix Auto-Yes either: the question
|
|
116
|
+
// reader is anchored on the rule row `stripBoxDrawing` blanks
|
|
117
|
+
// (`tools/command-code/dialog.ts`, 確定仕様 C), and this gate is handed the
|
|
118
|
+
// box-stripped `cleanOutput`, so every question would be suppressed.
|
|
119
|
+
//
|
|
120
|
+
// Promote only once both dialogs are recognised on both spellings, with a
|
|
121
|
+
// `/prompt-response` test pinning that the question is still answered.
|
|
104
122
|
'command-code': 'legacy',
|
|
105
123
|
};
|
|
106
124
|
/**
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* the promptType/defaultOptionNumber fallback introduced in Bug1).
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.PromptAnswerRejectedError = exports.ANSWER_MODE_KEYS_REASON = void 0;
|
|
10
|
+
exports.FreeTextAtChoiceOnlyPromptError = exports.FreeTextAnswerRejectedError = exports.FREE_TEXT_AT_MENU_ROW_REASON = exports.PromptAnswerRejectedError = exports.ANSWER_MODE_KEYS_REASON = void 0;
|
|
11
11
|
exports.sendPromptAnswer = sendPromptAnswer;
|
|
12
12
|
const tmux_1 = require("./tmux/tmux");
|
|
13
13
|
const frame_1 = require("../lib/detection/tools/frame");
|
|
14
14
|
const registry_1 = require("../lib/detection/tools/registry");
|
|
15
|
+
const prompt_detect_multiple_choice_1 = require("../lib/detection/prompt-detect-multiple-choice");
|
|
15
16
|
const models_1 = require("../types/models");
|
|
16
17
|
const tmux_capture_cache_1 = require("./tmux/tmux-capture-cache");
|
|
17
18
|
const cli_tool_timing_config_1 = require("../config/cli-tool-timing-config");
|
|
@@ -67,6 +68,130 @@ class PromptAnswerRejectedError extends Error {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
exports.PromptAnswerRejectedError = PromptAnswerRejectedError;
|
|
71
|
+
/**
|
|
72
|
+
* The reason code a caller gets back when free text meets a prompt whose
|
|
73
|
+
* text-bearing options are all menu rows (Issue #2573).
|
|
74
|
+
*
|
|
75
|
+
* The code `/prompt-response` already answers "this answer cannot be mapped onto
|
|
76
|
+
* a choice, and nothing was sent" with (#1681, #2522), so `respond` reports it
|
|
77
|
+
* without a new branch.
|
|
78
|
+
*/
|
|
79
|
+
exports.FREE_TEXT_AT_MENU_ROW_REASON = 'unresolvable_answer';
|
|
80
|
+
/**
|
|
81
|
+
* The sentence for each refusal, built from a tool id and option numbers only.
|
|
82
|
+
*
|
|
83
|
+
* Fixed text, a tool id and option numbers, never the answer (SEC-003, as in
|
|
84
|
+
* `prompt-answer-semantic`): the answer is whatever the operator typed, and
|
|
85
|
+
* `/prompt-response` returns this message to the client verbatim.
|
|
86
|
+
*
|
|
87
|
+
* The two sentences sit side by side on purpose. They differ in the one thing
|
|
88
|
+
* the operator has to act on — what to do next — and that is only comparable
|
|
89
|
+
* when both are readable at once.
|
|
90
|
+
*/
|
|
91
|
+
function freeTextRefusalMessage(cliToolId, optionNumbers, detail) {
|
|
92
|
+
const numbers = optionNumbers.join(', ');
|
|
93
|
+
if (detail === undefined) {
|
|
94
|
+
return `Refused to type free text at ${cliToolId}'s prompt: option ${numbers} `
|
|
95
|
+
+ 'reads as taking your own words, but on this screen it is a menu row, not a text field. '
|
|
96
|
+
+ 'The text would be ignored and the Enter after it would confirm whatever is highlighted. '
|
|
97
|
+
+ 'Nothing was sent. Answer with the option number, then send the instructions as a message.';
|
|
98
|
+
}
|
|
99
|
+
return `Refused to type free text at ${cliToolId}'s prompt: option ${numbers} is the text field `
|
|
100
|
+
+ `on this screen, but the cursor is on option ${detail.cursorOptionNumber}. The text would be `
|
|
101
|
+
+ `ignored and the Enter after it would confirm option ${detail.cursorOptionNumber}. Nothing `
|
|
102
|
+
+ 'was sent. Move the cursor onto the text field in the terminal and send the text again — '
|
|
103
|
+
+ 'sending its option number does not move the cursor, it selects nothing.';
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Free text refused BEFORE any key reached the pane (Issue #2573, #2584).
|
|
107
|
+
*
|
|
108
|
+
* A sibling of {@link PromptAnswerRejectedError}, not a use of it, because the
|
|
109
|
+
* two cite different evidence. That one cites the dialog the tool's own rules
|
|
110
|
+
* vouched for (`dialogKind` / `answerMode`), and no such verdict can decide this
|
|
111
|
+
* case: Command Code has had rules since Issue #2574, and the permission dialog
|
|
112
|
+
* this guard is first about is vouched `numbered` — the verdict that lets a
|
|
113
|
+
* DIGIT through — while the `AskUserQuestion` screen whose free text must keep
|
|
114
|
+
* working is deliberately not recognised by those rules at all
|
|
115
|
+
* (`tools/command-code/permission.ts`). `answerMode` says how a CHOICE is
|
|
116
|
+
* pressed; it says nothing about which row is a text field, nor about which row
|
|
117
|
+
* the cursor is on. So what this one cites is the rows.
|
|
118
|
+
*
|
|
119
|
+
* One class rather than two because `/prompt-response` is out of reach of both
|
|
120
|
+
* Issues' scope and matches this type by `instanceof` to report the refusal at
|
|
121
|
+
* all; {@link FreeTextRefusalKind} is what tells the two cases apart.
|
|
122
|
+
*/
|
|
123
|
+
class FreeTextAnswerRejectedError extends Error {
|
|
124
|
+
/** Machine-readable code, always {@link FREE_TEXT_AT_MENU_ROW_REASON}. */
|
|
125
|
+
reason;
|
|
126
|
+
/**
|
|
127
|
+
* The text-bearing options this refusal is about: the menu rows for
|
|
128
|
+
* `menu_row`, the measured text field(s) for `cursor_elsewhere`.
|
|
129
|
+
*/
|
|
130
|
+
optionNumbers;
|
|
131
|
+
/** Which measurement refused (Issue #2584). */
|
|
132
|
+
kind;
|
|
133
|
+
/** The option the Enter would have confirmed, or null for `menu_row`. */
|
|
134
|
+
cursorOptionNumber;
|
|
135
|
+
constructor(cliToolId, optionNumbers, detail) {
|
|
136
|
+
super(freeTextRefusalMessage(cliToolId, optionNumbers, detail));
|
|
137
|
+
this.name = 'FreeTextAnswerRejectedError';
|
|
138
|
+
this.reason = exports.FREE_TEXT_AT_MENU_ROW_REASON;
|
|
139
|
+
this.optionNumbers = optionNumbers;
|
|
140
|
+
this.kind = detail?.kind ?? 'menu_row';
|
|
141
|
+
this.cursorOptionNumber = detail?.cursorOptionNumber ?? null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.FreeTextAnswerRejectedError = FreeTextAnswerRejectedError;
|
|
145
|
+
/**
|
|
146
|
+
* Free text refused because NO row on the screen takes typed text (Issue #2583).
|
|
147
|
+
*
|
|
148
|
+
* ## The hole #2573 left
|
|
149
|
+
*
|
|
150
|
+
* {@link FreeTextAnswerRejectedError} looks for rows that READ as taking the
|
|
151
|
+
* operator's own words (`requiresTextInput`, i.e. `TEXT_INPUT_PATTERNS`) and
|
|
152
|
+
* only judges the answer when it finds at least one. A permission dialog with
|
|
153
|
+
* no such row was therefore never judged at all, and the text arm typed the
|
|
154
|
+
* answer and pressed Enter. Measured on 2026-09-16 against develop `9f4b4e29`:
|
|
155
|
+
*
|
|
156
|
+
* | tool | dialog | rows | result |
|
|
157
|
+
* |------|--------|------|--------|
|
|
158
|
+
* | Claude Code 2.1.273 | Bash command | `1. Yes` / `2. Yes, and always allow access to …` / `3. Yes, and switch to auto mode` / `4. No` | `success: true`, the `mkdir` RAN |
|
|
159
|
+
* | Antigravity 1.2.3 | Run this command? | `1. Yes, run command` / `2. Yes, and always allow …` / `3. Yes, … (Persist to settings.json)` / `4. No, cancel` | `success: true`, the `mkdir` RAN |
|
|
160
|
+
*
|
|
161
|
+
* Same screens, but with a path containing the word `custom`, `/custom/i`
|
|
162
|
+
* flagged the quoting row and #2573's guard refused. So what was left decided
|
|
163
|
+
* whether a refusal meaning "do not do this" approved instead: the SPELLING of
|
|
164
|
+
* the command being approved.
|
|
165
|
+
*
|
|
166
|
+
* ## What the caller is told, and why the count rather than the rows
|
|
167
|
+
*
|
|
168
|
+
* There is no row to name here — that is the whole condition — so the message
|
|
169
|
+
* cites how many options the screen has and points at the option number. It
|
|
170
|
+
* never quotes the answer (SEC-003, as in `prompt-answer-semantic`): the answer
|
|
171
|
+
* is whatever the operator typed and `/prompt-response` hands this message back
|
|
172
|
+
* to the client verbatim.
|
|
173
|
+
*
|
|
174
|
+
* Shares {@link FREE_TEXT_AT_MENU_ROW_REASON} with its sibling deliberately:
|
|
175
|
+
* the reason code is what `respond` and `/prompt-response` branch on, and both
|
|
176
|
+
* cases are the same promise to the caller — the answer could not be mapped onto
|
|
177
|
+
* a choice and NOTHING was sent.
|
|
178
|
+
*/
|
|
179
|
+
class FreeTextAtChoiceOnlyPromptError extends Error {
|
|
180
|
+
/** Machine-readable code, always {@link FREE_TEXT_AT_MENU_ROW_REASON}. */
|
|
181
|
+
reason;
|
|
182
|
+
/** How many options the screen offers, none of which is a text field. */
|
|
183
|
+
optionCount;
|
|
184
|
+
constructor(cliToolId, optionCount) {
|
|
185
|
+
super(`Refused to type free text at ${cliToolId}'s prompt: none of its ${optionCount} options is a `
|
|
186
|
+
+ 'text field on this screen, so the text would be ignored and the Enter after it would '
|
|
187
|
+
+ 'confirm whatever is highlighted. Nothing was sent. Answer with the option number instead, '
|
|
188
|
+
+ 'then send your instructions as a separate message.');
|
|
189
|
+
this.name = 'FreeTextAtChoiceOnlyPromptError';
|
|
190
|
+
this.reason = exports.FREE_TEXT_AT_MENU_ROW_REASON;
|
|
191
|
+
this.optionCount = optionCount;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
exports.FreeTextAtChoiceOnlyPromptError = FreeTextAtChoiceOnlyPromptError;
|
|
70
195
|
/**
|
|
71
196
|
* The pane to judge the tool's own dialog rules against.
|
|
72
197
|
*
|
|
@@ -127,52 +252,68 @@ async function readGuardFrame(params) {
|
|
|
127
252
|
* - It never fires for a tool with no measured dialog rules
|
|
128
253
|
* (`hasDialogRules === false`: gemini, antigravity, vibe-local). Gating on
|
|
129
254
|
* rules that do not exist would silence those tools, which is the rollout
|
|
130
|
-
* mistake `auto-yes-dialog-gate` documents.
|
|
255
|
+
* mistake `auto-yes-dialog-gate` documents. command-code has had rules since
|
|
256
|
+
* Issue #2574 (before it, it belonged in this list and was missing from it);
|
|
257
|
+
* they only ever answer `numbered`, so on that tool this guard never refuses
|
|
258
|
+
* and its reading is used for the Enter instead (see {@link resolveSubmitMode}).
|
|
131
259
|
* - It does not MAP the answer onto the dialog's buttons. Turning "3" into the
|
|
132
260
|
* two ←/→ presses that reach `Reject` is Issue P1-7's structured-decision
|
|
133
261
|
* work; this Issue only stops the wrong keystroke.
|
|
134
262
|
*
|
|
263
|
+
* @returns The `numbered` dialog the tool vouched for on this frame, or null when
|
|
264
|
+
* the guard did not read one (non-numeric answer, no rules, unreadable pane,
|
|
265
|
+
* or no dialog on screen). Returned so the Enter decision is made off the
|
|
266
|
+
* same reading rather than a second capture (Issue #2574).
|
|
135
267
|
* @throws {PromptAnswerRejectedError} when the tool vouched for a dialog on this
|
|
136
268
|
* frame whose `answerMode` is not {@link TEXT_ANSWERABLE_ANSWER_MODE}.
|
|
137
269
|
*/
|
|
138
270
|
async function assertAnswerModeAcceptsNumber(params) {
|
|
139
271
|
if (!/^\d+$/.test(params.answer))
|
|
140
|
-
return;
|
|
272
|
+
return null;
|
|
141
273
|
const detector = (0, registry_1.getToolStatusDetector)(params.cliToolId);
|
|
142
274
|
if (!detector.hasDialogRules)
|
|
143
|
-
return;
|
|
275
|
+
return null;
|
|
144
276
|
const frame = await readGuardFrame(params);
|
|
145
277
|
if (frame === null)
|
|
146
|
-
return;
|
|
278
|
+
return null;
|
|
147
279
|
const dialog = detector.detectDialog((0, frame_1.normalizeFrame)(frame));
|
|
148
280
|
if (dialog === null)
|
|
149
|
-
return;
|
|
281
|
+
return null;
|
|
150
282
|
if (dialog.answerMode === TEXT_ANSWERABLE_ANSWER_MODE)
|
|
151
|
-
return;
|
|
283
|
+
return dialog;
|
|
152
284
|
throw new PromptAnswerRejectedError(params.cliToolId, params.answer, dialog);
|
|
153
285
|
}
|
|
154
286
|
/**
|
|
155
|
-
* Resolve the effective SubmitMode from promptData, fallback, and default.
|
|
156
|
-
* Resolution order: promptData.submitMode -> fallbackSubmitMode -> 'answer_then_enter'.
|
|
287
|
+
* Resolve the effective SubmitMode from the dialog on screen, promptData, fallback, and default.
|
|
288
|
+
* Resolution order: dialog.submitMode -> promptData.submitMode -> fallbackSubmitMode -> 'answer_then_enter'.
|
|
157
289
|
* Invalid values are normalized to 'answer_then_enter' via allowlist validation.
|
|
158
290
|
*
|
|
291
|
+
* Issue #2574: the dialog comes first because it is the tool's own measurement,
|
|
292
|
+
* read off the frame the answer is about to land on. `promptData` may have been
|
|
293
|
+
* built by the generic parser, which cannot see that a digit is a hotkey —
|
|
294
|
+
* Command Code's permission dialog was published with no `submitMode`, and the
|
|
295
|
+
* Enter sent after its `1` submitted whatever draft was waiting in the composer.
|
|
296
|
+
*
|
|
159
297
|
* @returns The resolved SubmitMode, guaranteed to be a valid value.
|
|
160
298
|
*/
|
|
161
|
-
function resolveSubmitMode(params) {
|
|
299
|
+
function resolveSubmitMode(params, dialog) {
|
|
162
300
|
const fromPromptData = params.promptData?.type === 'multiple_choice'
|
|
163
301
|
? params.promptData.submitMode
|
|
164
302
|
: undefined;
|
|
165
|
-
const raw = fromPromptData ?? params.fallbackSubmitMode ?? 'answer_then_enter';
|
|
303
|
+
const raw = dialog?.submitMode ?? fromPromptData ?? params.fallbackSubmitMode ?? 'answer_then_enter';
|
|
166
304
|
return (0, models_1.isValidSubmitMode)(raw) ? raw : 'answer_then_enter';
|
|
167
305
|
}
|
|
168
306
|
/**
|
|
169
307
|
* Determine whether the Enter key should be suppressed after sending the answer text.
|
|
170
308
|
* answer_only mode applies only when the prompt is multiple_choice and the answer is numeric.
|
|
309
|
+
* A numbered dialog the tool vouched for is a multiple-choice prompt in its own right,
|
|
310
|
+
* which covers a caller that could not supply promptData (Issue #2574).
|
|
171
311
|
*/
|
|
172
|
-
function shouldSuppressEnter(params, submitMode) {
|
|
312
|
+
function shouldSuppressEnter(params, submitMode, dialog) {
|
|
173
313
|
if (submitMode !== 'answer_only')
|
|
174
314
|
return false;
|
|
175
|
-
const isMultipleChoice =
|
|
315
|
+
const isMultipleChoice = dialog !== null
|
|
316
|
+
|| params.promptData?.type === 'multiple_choice'
|
|
176
317
|
|| params.fallbackPromptType === 'multiple_choice';
|
|
177
318
|
return isMultipleChoice && /^\d+$/.test(params.answer);
|
|
178
319
|
}
|
|
@@ -186,6 +327,177 @@ function buildNavigationKeys(offset) {
|
|
|
186
327
|
const direction = offset > 0 ? 'Down' : 'Up';
|
|
187
328
|
return Array.from({ length: Math.abs(offset) }, () => direction);
|
|
188
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Refuse free text aimed at a menu row (Issue #2573).
|
|
332
|
+
*
|
|
333
|
+
* ## The hole
|
|
334
|
+
*
|
|
335
|
+
* `PromptPanel` / `MobilePromptSheet` sent the operator's TEXT instead of the
|
|
336
|
+
* option number whenever the selected option had `requiresTextInput`, and
|
|
337
|
+
* `respond <id> "<text>"` sends text by construction. Nothing below maps text
|
|
338
|
+
* onto a choice — the cursor arm takes digits only — so the text arm typed it at
|
|
339
|
+
* the dialog and pressed Enter. On Command Code 1.53.1's permission dialog
|
|
340
|
+
* (`3. No, tell Command Code what to do differently`) the characters are ignored
|
|
341
|
+
* and the Enter confirms the highlighted `1. Yes`: measured, a reason meaning
|
|
342
|
+
* "do not do this" ran the `mkdir` it was sent to stop, and `/prompt-response`
|
|
343
|
+
* answered `success: true`.
|
|
344
|
+
*
|
|
345
|
+
* ## Why it is keyed on the rows and not on "is the answer a number"
|
|
346
|
+
*
|
|
347
|
+
* Free text is the RIGHT answer on the screen next door. Command Code's
|
|
348
|
+
* `AskUserQuestion` ends in `Type something...`, a real `TextInput` (#2522), and
|
|
349
|
+
* both rows carry `requiresTextInput`. So the question is whether the prompt's
|
|
350
|
+
* text-bearing options include one measured to be a field
|
|
351
|
+
* ({@link isTypedTextFieldOption}); only when every one of them is a menu row is
|
|
352
|
+
* the text refused.
|
|
353
|
+
*
|
|
354
|
+
* ## Issue #2583: a screen with no text-bearing row is judged too
|
|
355
|
+
*
|
|
356
|
+
* The bullet that used to sit here said this guard does not judge a prompt with
|
|
357
|
+
* NO `requiresTextInput` option, citing #1726's pin — "sessions the rows cannot
|
|
358
|
+
* speak for keep behaving as they did". It named the hole it was leaving open in
|
|
359
|
+
* as many words (`the same Enter still sits under free text sent at, e.g.,
|
|
360
|
+
* claude's 1. Yes / 2. … / 3. No`), and #2583 then measured it: on claude 2.1.273
|
|
361
|
+
* and agy 1.2.3 a refusal typed at a Bash-approval dialog answered
|
|
362
|
+
* `success: true` and RAN the command. Whether it was refused came down to
|
|
363
|
+
* whether the quoted path happened to contain the word `custom`, which is
|
|
364
|
+
* `/custom/i` in `TEXT_INPUT_PATTERNS` — the spelling of someone else's argument.
|
|
365
|
+
*
|
|
366
|
+
* So the rule is now the one the guard's name always claimed: free text needs a
|
|
367
|
+
* text field, and a `multiple_choice` prompt whose rows are all choices has none.
|
|
368
|
+
*
|
|
369
|
+
* **What this costs, and why it is still the right trade.** #1726's pin is about
|
|
370
|
+
* AUTHORITY: that Issue reads the agent's own `AskUserQuestion` payload and must
|
|
371
|
+
* not let its absence narrow what a hook-less session accepts, because there the
|
|
372
|
+
* screen is the only authority. The rows judged here ARE the screen — the fresh
|
|
373
|
+
* capture `/prompt-response` re-verified, parsed by the same reader that draws
|
|
374
|
+
* the answer panels — so nothing is being second-guessed from a payload that is
|
|
375
|
+
* missing. What #1726 keeps is its actual subject: an out-of-RANGE number is
|
|
376
|
+
* still sent when no payload vouches for the list (this guard reads only
|
|
377
|
+
* non-numeric answers), and a call with no `promptData` at all still goes
|
|
378
|
+
* through untouched. The one case that regresses is an operator who has walked
|
|
379
|
+
* the cursor onto a row that opens a field the rows do not advertise — claude's
|
|
380
|
+
* `4. Type something.`, which no pattern flags — and sends text for it; they now
|
|
381
|
+
* get a refusal with the dialog still up instead of a silent approval. That
|
|
382
|
+
* direction is the safe one, and cursor position is #2584's subject, not this
|
|
383
|
+
* one's.
|
|
384
|
+
*
|
|
385
|
+
* ## What it deliberately does NOT do
|
|
386
|
+
*
|
|
387
|
+
* - It does not judge a call with no `promptData`, nor a `multiple_choice` with
|
|
388
|
+
* an empty option list. Both mean the rows are unknown, which is exactly where
|
|
389
|
+
* #1726's pin still holds — and where `/prompt-response` has already refused
|
|
390
|
+
* for its own reasons if the pane could be read at all.
|
|
391
|
+
* - It does not deliver the reason. Sending the number and then the text as a
|
|
392
|
+
* message is the follow-up (#2573 対応内容 2). Issue #2574 has since removed the
|
|
393
|
+
* Enter that would have landed the reason in the wrong place on command-code,
|
|
394
|
+
* so what is still missing is a measurement, per tool, of where the input goes
|
|
395
|
+
* once the digit has confirmed.
|
|
396
|
+
*
|
|
397
|
+
* @throws {FreeTextAtChoiceOnlyPromptError} when the answer is not a number and
|
|
398
|
+
* no option of the multiple-choice prompt even reads as taking text (#2583).
|
|
399
|
+
* @throws {FreeTextAnswerRejectedError} when the answer is not a number and every
|
|
400
|
+
* text-bearing option of the multiple-choice prompt is a menu row (#2573).
|
|
401
|
+
*/
|
|
402
|
+
function assertFreeTextHasATextField(params) {
|
|
403
|
+
if (/^\d+$/.test(params.answer))
|
|
404
|
+
return;
|
|
405
|
+
const { promptData } = params;
|
|
406
|
+
if (promptData?.type !== 'multiple_choice')
|
|
407
|
+
return;
|
|
408
|
+
// No rows to read is not the same as rows that take no text: a prompt this
|
|
409
|
+
// degraded says nothing about where characters land, so it keeps the
|
|
410
|
+
// pre-#2583 path (see the pin discussion above).
|
|
411
|
+
if (promptData.options.length === 0)
|
|
412
|
+
return;
|
|
413
|
+
const textBearing = promptData.options.filter((option) => option.requiresTextInput === true);
|
|
414
|
+
// Issue #2583. Deliberately BEFORE the `isTypedTextFieldOption` reading below
|
|
415
|
+
// and never inside it: that reading decides between a field and a menu row on
|
|
416
|
+
// a screen that has at least one candidate, and this branch is the screen that
|
|
417
|
+
// has none.
|
|
418
|
+
if (textBearing.length === 0) {
|
|
419
|
+
throw new FreeTextAtChoiceOnlyPromptError(params.cliToolId, promptData.options.length);
|
|
420
|
+
}
|
|
421
|
+
if (textBearing.some(prompt_detect_multiple_choice_1.isTypedTextFieldOption))
|
|
422
|
+
return;
|
|
423
|
+
throw new FreeTextAnswerRejectedError(params.cliToolId, textBearing.map((option) => option.number));
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Refuse free text while the cursor is on another row (Issue #2584).
|
|
427
|
+
*
|
|
428
|
+
* ## The hole #2573 left open
|
|
429
|
+
*
|
|
430
|
+
* {@link assertFreeTextHasATextField} asks what KIND the text-bearing rows are,
|
|
431
|
+
* and lets the text through the moment one of them is a measured field. On
|
|
432
|
+
* Command Code's `AskUserQuestion` that is one question short, because the field
|
|
433
|
+
* is a row of a list and typed characters only reach it while the cursor is
|
|
434
|
+
* resting there. Measured on 1.53.1 (#2584), with `Type something...` on row 3
|
|
435
|
+
* and the `❯` on row 1:
|
|
436
|
+
*
|
|
437
|
+
* ```
|
|
438
|
+
* ❯ 1. apple
|
|
439
|
+
* 2. banana
|
|
440
|
+
* 3. Type something...
|
|
441
|
+
* ```
|
|
442
|
+
*
|
|
443
|
+
* `answer: "UAT-FREETEXT-KIWI"` was ignored, the Enter after it confirmed
|
|
444
|
+
* `apple`, and `/prompt-response` answered `success: true` — the same failure
|
|
445
|
+
* shape as #2573, reached through the screen #2573 deliberately exempts. The
|
|
446
|
+
* SAME request on the SAME screen with the `❯` on row 3 records the text, so
|
|
447
|
+
* nothing but the cursor decides which of the two it means.
|
|
448
|
+
*
|
|
449
|
+
* ## Why it refuses instead of moving the cursor there first
|
|
450
|
+
*
|
|
451
|
+
* The obvious alternative is to reuse the cursor arm: compute `field - cursor`
|
|
452
|
+
* and send that many Down presses before typing. The arithmetic is available —
|
|
453
|
+
* both numbers are read off the frame — but the KEYS are not measured for this
|
|
454
|
+
* screen. The cursor arm is claude's and agy's by construction
|
|
455
|
+
* (`isCursorNavMultiChoice`, and `tools/command-code/dialog.ts` says so where it
|
|
456
|
+
* publishes this prompt), and what IS measured for Command Code is the opposite
|
|
457
|
+
* input method: the digit is a hotkey that selects on its own, which is why the
|
|
458
|
+
* screen is published `answer_only` (#2574). #2522 measured the remaining half —
|
|
459
|
+
* the digit aimed at the field row selects nothing — so this repository has no
|
|
460
|
+
* measured keystroke that moves this cursor at all.
|
|
461
|
+
*
|
|
462
|
+
* Sending Down at it anyway would, if the list does not take arrow keys,
|
|
463
|
+
* reproduce exactly today's bug with extra keystrokes in front of it and still
|
|
464
|
+
* answer `success: true`. That is the mistake `assertAnswerModeAcceptsNumber`
|
|
465
|
+
* was written against: gate on the measurement, and stop the wrong keystroke
|
|
466
|
+
* rather than invent a mapping. Mapping the answer onto the field — teaching the
|
|
467
|
+
* sender to arm this cursor once the movement key has been measured live — is
|
|
468
|
+
* the follow-up, and it can be added under this guard without changing what any
|
|
469
|
+
* caller sees today.
|
|
470
|
+
*
|
|
471
|
+
* ## What it deliberately does NOT do
|
|
472
|
+
*
|
|
473
|
+
* - It does not fire on a prompt with no measured field: that is #2573's case
|
|
474
|
+
* above (every text-bearing row is a menu row) or the pre-#2573 pass-through
|
|
475
|
+
* (#1726), and neither changes here.
|
|
476
|
+
* - It does not fire when NO option carries the cursor. agy's picker publishes
|
|
477
|
+
* its highlight without `isDefault` at all (`#999`), and a screen whose cursor
|
|
478
|
+
* cannot be read is one this guard has nothing to say about — the same
|
|
479
|
+
* fail-open {@link readGuardFrame} takes for an unreadable pane.
|
|
480
|
+
*
|
|
481
|
+
* @throws {FreeTextAnswerRejectedError} with `kind: 'cursor_elsewhere'` when the
|
|
482
|
+
* answer is not a number, the prompt has a measured text field, and the cursor
|
|
483
|
+
* is resting on some other option.
|
|
484
|
+
*/
|
|
485
|
+
function assertFreeTextCursorIsOnTheField(params) {
|
|
486
|
+
if (/^\d+$/.test(params.answer))
|
|
487
|
+
return;
|
|
488
|
+
const { promptData } = params;
|
|
489
|
+
if (promptData?.type !== 'multiple_choice')
|
|
490
|
+
return;
|
|
491
|
+
const fields = promptData.options.filter(prompt_detect_multiple_choice_1.isTypedTextFieldOption);
|
|
492
|
+
if (fields.length === 0)
|
|
493
|
+
return;
|
|
494
|
+
const cursor = promptData.options.find((option) => option.isDefault === true);
|
|
495
|
+
if (cursor === undefined)
|
|
496
|
+
return;
|
|
497
|
+
if ((0, prompt_detect_multiple_choice_1.isTypedTextFieldOption)(cursor))
|
|
498
|
+
return;
|
|
499
|
+
throw new FreeTextAnswerRejectedError(params.cliToolId, fields.map((option) => option.number), { kind: 'cursor_elsewhere', cursorOptionNumber: cursor.number });
|
|
500
|
+
}
|
|
189
501
|
/**
|
|
190
502
|
* Send an answer to a tmux session, using cursor-key navigation for
|
|
191
503
|
* Claude Code / Antigravity multiple-choice prompts and text input for
|
|
@@ -202,7 +514,18 @@ async function sendPromptAnswer(params) {
|
|
|
202
514
|
// inside the text arm because the number->offset arithmetic the cursor arm
|
|
203
515
|
// does is just as meaningless on an unnumbered button strip as typing the
|
|
204
516
|
// digit is.
|
|
205
|
-
await assertAnswerModeAcceptsNumber(params);
|
|
517
|
+
const dialog = await assertAnswerModeAcceptsNumber(params);
|
|
518
|
+
// Issue #2573 / #2583: the same promise for text. The text arm below types
|
|
519
|
+
// whatever it is given and presses Enter, so text aimed at a screen that has
|
|
520
|
+
// no text field on it — a menu row that reads as one, or a permission dialog
|
|
521
|
+
// whose rows are all choices — has to stop here, before the branch, or the
|
|
522
|
+
// Enter confirms the highlighted default.
|
|
523
|
+
assertFreeTextHasATextField(params);
|
|
524
|
+
// Issue #2584: and the half #2573 left open. A prompt that HAS a real text
|
|
525
|
+
// field still swallows the characters while the cursor is on another row, and
|
|
526
|
+
// the Enter after them confirms that row — so the cursor has to be checked
|
|
527
|
+
// here too, before the branch.
|
|
528
|
+
assertFreeTextCursorIsOnTheField(params);
|
|
206
529
|
// Determine if this is an arrow-key-navigated multiple-choice prompt.
|
|
207
530
|
// Claude Code and Antigravity (agy) both render selection menus that only
|
|
208
531
|
// respond to cursor navigation + Enter, not to typed option numbers
|
|
@@ -276,8 +599,8 @@ async function sendPromptAnswer(params) {
|
|
|
276
599
|
// Standard CLI prompt: send text + Enter (y/n, Approve?, etc.)
|
|
277
600
|
await (0, tmux_1.sendKeys)(sessionName, answer, false);
|
|
278
601
|
// Issue #616: Resolve submitMode and determine whether to suppress Enter
|
|
279
|
-
const resolvedSubmitMode = resolveSubmitMode(params);
|
|
280
|
-
if (!shouldSuppressEnter(params, resolvedSubmitMode)) {
|
|
602
|
+
const resolvedSubmitMode = resolveSubmitMode(params, dialog);
|
|
603
|
+
if (!shouldSuppressEnter(params, resolvedSubmitMode, dialog)) {
|
|
281
604
|
// Wait a moment for the input to be processed
|
|
282
605
|
await new Promise(resolve => setTimeout(resolve, cli_tool_timing_config_1.TUI_TEXT_INPUT_WAIT_MS));
|
|
283
606
|
// Send Enter
|