commandmate 0.17.0 → 0.19.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 +120 -120
- package/.next/app-path-routes-manifest.json +19 -19
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- 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/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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +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/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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/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/subscriptions/route.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.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.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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +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/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.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.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.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.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.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 +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.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.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.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.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.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.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.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.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.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]/interrupt/route_client-reference-manifest.js +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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.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]/prompt-response/route_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.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.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.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_client-reference-manifest.js +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/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +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.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.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.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/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]/route.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +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 +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +19 -19
- package/.next/server/chunks/1117.js +1 -1
- package/.next/server/chunks/3042.js +1 -1
- package/.next/server/chunks/3753.js +19 -23
- package/.next/server/chunks/5959.js +3 -3
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +10 -9
- package/.next/server/chunks/6283.js +1 -1
- package/.next/server/chunks/8681.js +1 -1
- package/.next/server/chunks/8807.js +492 -485
- package/.next/server/chunks/8856.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/{6488-f4e447464c9e0e22.js → 6488-2c412c29258637b6.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-5ffc84094830babb.js → page-f6e09d25cd3b56f9.js} +1 -1
- package/.next/trace +12 -12
- package/dist/cli/commands/auto-yes.d.ts.map +1 -1
- package/dist/cli/commands/auto-yes.js +9 -2
- package/dist/cli/commands/capture.d.ts +1 -0
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +112 -2
- package/dist/cli/commands/instances.d.ts +24 -0
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +44 -0
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +10 -2
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +47 -19
- package/dist/cli/commands/wait.d.ts +4 -1
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +134 -10
- package/dist/cli/types/api-responses.d.ts +10 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +16 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/pager.d.ts +28 -0
- package/dist/cli/utils/pager.d.ts.map +1 -0
- package/dist/cli/utils/pager.js +57 -0
- package/dist/cli/utils/verify-runner.d.ts +6 -0
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +1 -0
- package/dist/lib/detection/ansi.d.ts +30 -0
- package/dist/lib/detection/ansi.d.ts.map +1 -0
- package/dist/lib/detection/ansi.js +39 -0
- package/dist/lib/tmux/transcript-squeeze.d.ts +76 -0
- package/dist/lib/tmux/transcript-squeeze.d.ts.map +1 -0
- package/dist/lib/tmux/transcript-squeeze.js +96 -0
- package/dist/server/server.js +18 -0
- package/dist/server/src/config/tmux-pane-config.js +33 -1
- package/dist/server/src/lib/cli-tools/antigravity.js +2 -1
- package/dist/server/src/lib/cli-tools/codex.js +3 -2
- package/dist/server/src/lib/cli-tools/copilot.js +2 -2
- package/dist/server/src/lib/cli-tools/gemini.js +2 -2
- package/dist/server/src/lib/cli-tools/opencode.js +2 -2
- package/dist/server/src/lib/cli-tools/vibe-local.js +2 -2
- package/dist/server/src/lib/db/agent-instances-db.js +53 -0
- package/dist/server/src/lib/db/migrations/index.js +2 -0
- package/dist/server/src/lib/db/migrations/runner.js +1 -1
- package/dist/server/src/lib/db/migrations/v52-orphaned-worktree-children.js +76 -0
- package/dist/server/src/lib/db/migrations/worktree-child-tables.js +119 -0
- package/dist/server/src/lib/db/tasks-db.js +46 -1
- package/dist/server/src/lib/db/verification-db.js +21 -3
- package/dist/server/src/lib/db/worktree-db.js +65 -42
- package/dist/server/src/lib/detection/cli-patterns.js +24 -1
- package/dist/server/src/lib/detection/status-detector.js +52 -2
- package/dist/server/src/lib/session/claude-session.js +4 -2
- package/dist/server/src/lib/tmux/read-mode-pager.js +161 -0
- package/dist/server/src/lib/tmux/read-mode.js +327 -0
- package/dist/server/src/lib/tmux/tmux.js +43 -5
- package/package.json +1 -1
- /package/.next/static/{gR8Sh86RrTynJON8SWkWK → -E7I667pJDTBamyG8M_Z-}/_buildManifest.js +0 -0
- /package/.next/static/{gR8Sh86RrTynJON8SWkWK → -E7I667pJDTBamyG8M_Z-}/_ssgManifest.js +0 -0
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* leaves a `running` row behind, which is recoverable evidence, whereas a
|
|
11
11
|
* single write-on-completion would leave nothing at all.
|
|
12
12
|
*
|
|
13
|
+
* The timestamps describe the *execution*, not the writes (Issue #1625): a
|
|
14
|
+
* caller that measured a gate closes the row with the window it measured, so
|
|
15
|
+
* `finished_at - started_at` equals `duration_ms` instead of the gap between
|
|
16
|
+
* two adjacent INSERT/UPDATE statements.
|
|
17
|
+
*
|
|
13
18
|
* The finish* functions throw when the target row does not exist. A silent
|
|
14
19
|
* no-op would let a caller believe it recorded a verdict it never recorded —
|
|
15
20
|
* exactly the failure this table exists to make impossible.
|
|
@@ -64,6 +69,9 @@ function mapGateRow(row) {
|
|
|
64
69
|
logTail: row.log_tail,
|
|
65
70
|
startedAt: new Date(row.started_at),
|
|
66
71
|
finishedAt: row.finished_at === null ? null : new Date(row.finished_at),
|
|
72
|
+
timingsMeasured: row.finished_at !== null &&
|
|
73
|
+
row.duration_ms !== null &&
|
|
74
|
+
row.finished_at - row.started_at === row.duration_ms,
|
|
67
75
|
};
|
|
68
76
|
}
|
|
69
77
|
/** Open a run in `running` state. */
|
|
@@ -113,15 +121,25 @@ function createGateResult(db, runId, input) {
|
|
|
113
121
|
}
|
|
114
122
|
return mapGateRow(row);
|
|
115
123
|
}
|
|
116
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* Close a gate result with its outcome and the interval it occupied.
|
|
126
|
+
*
|
|
127
|
+
* `started_at` is rewritten only when the caller declares an
|
|
128
|
+
* {@link FinishGateResultPatch.executionWindow}: the value written when the row
|
|
129
|
+
* was opened is a placeholder for "this gate was entered", and the measured
|
|
130
|
+
* start is a few milliseconds later — after the insert this call is closing.
|
|
131
|
+
* Leaving the placeholder would put that gap inside the reported window.
|
|
132
|
+
*/
|
|
117
133
|
function finishGateResult(db, gateResultId, patch) {
|
|
118
134
|
const info = db
|
|
119
135
|
.prepare(`
|
|
120
136
|
UPDATE verification_gate_results
|
|
121
|
-
SET status = ?, exit_code = ?, duration_ms = ?, log_tail = ?,
|
|
137
|
+
SET status = ?, exit_code = ?, duration_ms = ?, log_tail = ?,
|
|
138
|
+
started_at = COALESCE(?, started_at),
|
|
139
|
+
finished_at = ?
|
|
122
140
|
WHERE id = ?
|
|
123
141
|
`)
|
|
124
|
-
.run(patch.status, patch.exitCode ?? null, patch.durationMs ?? null, patch.logTail ?? null, Date.now(), gateResultId);
|
|
142
|
+
.run(patch.status, patch.exitCode ?? null, patch.durationMs ?? null, patch.logTail ?? null, patch.executionWindow?.startedAt ?? null, patch.executionWindow?.finishedAt ?? Date.now(), gateResultId);
|
|
125
143
|
if (info.changes === 0) {
|
|
126
144
|
throw new Error(`Verification gate result ${gateResultId} not found`);
|
|
127
145
|
}
|
|
@@ -28,6 +28,7 @@ exports.deleteRepositoryWorktrees = deleteRepositoryWorktrees;
|
|
|
28
28
|
exports.deleteWorktreesByIds = deleteWorktreesByIds;
|
|
29
29
|
const selected_agents_validator_1 = require("../../lib/selected-agents-validator");
|
|
30
30
|
const chat_db_1 = require("./chat-db");
|
|
31
|
+
const worktree_child_tables_1 = require("./migrations/worktree-child-tables");
|
|
31
32
|
/**
|
|
32
33
|
* Get latest user message per CLI tool for multiple worktrees (batch query)
|
|
33
34
|
* Optimized to avoid N+1 query problem
|
|
@@ -269,30 +270,24 @@ function upsertWorktree(db, worktree) {
|
|
|
269
270
|
run();
|
|
270
271
|
}
|
|
271
272
|
/**
|
|
272
|
-
*
|
|
273
|
-
* along with the referencing column. Derived dynamically from the live schema
|
|
274
|
-
* so it never drifts out of sync with future migrations that add child tables.
|
|
273
|
+
* Delete every child row pointing at any of `worktreeIds`.
|
|
275
274
|
*
|
|
276
|
-
*
|
|
275
|
+
* Deliberately covers *all* deletable child tables, not just the FK-less ones:
|
|
276
|
+
* doing so makes deletion correct even when `PRAGMA foreign_keys` is off, and
|
|
277
|
+
* the extra statements against CASCADE tables remove rows that would have been
|
|
278
|
+
* removed anyway. Append-only ledgers are skipped (see getWriteGuardedTables).
|
|
279
|
+
* Must run before the parent row is deleted (Issue #1621).
|
|
277
280
|
*/
|
|
278
|
-
function getWorktreeChildTables(db) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
for (const {
|
|
284
|
-
if (
|
|
281
|
+
function deleteWorktreeChildRows(db, worktreeIds, children = (0, worktree_child_tables_1.getWorktreeChildTables)(db)) {
|
|
282
|
+
if (worktreeIds.length === 0)
|
|
283
|
+
return;
|
|
284
|
+
const { noDelete } = (0, worktree_child_tables_1.getWriteGuardedTables)(db);
|
|
285
|
+
const placeholders = worktreeIds.map(() => '?').join(',');
|
|
286
|
+
for (const { table, column } of children) {
|
|
287
|
+
if (noDelete.has(table))
|
|
285
288
|
continue;
|
|
286
|
-
|
|
287
|
-
// user input, so string interpolation here is safe.
|
|
288
|
-
const fks = db.prepare(`PRAGMA foreign_key_list("${name}")`).all();
|
|
289
|
-
for (const fk of fks) {
|
|
290
|
-
if (fk.table === 'worktrees' && (fk.to === 'id' || fk.to === null)) {
|
|
291
|
-
children.push({ table: name, column: fk.from });
|
|
292
|
-
}
|
|
293
|
-
}
|
|
289
|
+
db.prepare(`DELETE FROM "${table}" WHERE "${column}" IN (${placeholders})`).run(...worktreeIds);
|
|
294
290
|
}
|
|
295
|
-
return children;
|
|
296
291
|
}
|
|
297
292
|
/**
|
|
298
293
|
* Rename a worktree row's primary key from `oldId` to `newId`, re-pointing every
|
|
@@ -300,6 +295,18 @@ function getWorktreeChildTables(db) {
|
|
|
300
295
|
* same-directory branch switch preserve chat history and all related data
|
|
301
296
|
* instead of destroying it (Issue #1151).
|
|
302
297
|
*
|
|
298
|
+
* "Every child table" means every table carrying a worktree reference, not just
|
|
299
|
+
* the ones that declare a foreign key: `tasks`, `verification_runs` and
|
|
300
|
+
* `skill_operations` do not, and used to be left addressing an ID that no
|
|
301
|
+
* longer resolves (Issue #1621).
|
|
302
|
+
*
|
|
303
|
+
* Append-only ledgers are the one exception, and the schema states it itself —
|
|
304
|
+
* `skill_operations` aborts any UPDATE (#1234). An immutable audit row records
|
|
305
|
+
* what happened under the identity in force at the time, and rewriting it is
|
|
306
|
+
* precisely what its trigger exists to prevent; attempting it does not fail
|
|
307
|
+
* quietly either, it aborts the enclosing transaction and takes the rename with
|
|
308
|
+
* it (measured). See getWriteGuardedTables.
|
|
309
|
+
*
|
|
303
310
|
* Must be called inside a transaction. `PRAGMA defer_foreign_keys` defers FK
|
|
304
311
|
* enforcement to COMMIT, letting us repoint the parent PK and its children in
|
|
305
312
|
* any order without needing `ON UPDATE CASCADE` on every constraint.
|
|
@@ -314,15 +321,20 @@ function migrateWorktreeIdPreservingChildren(db, oldId, newId) {
|
|
|
314
321
|
// independently. Resets automatically at the end of the transaction.
|
|
315
322
|
db.pragma('defer_foreign_keys = ON');
|
|
316
323
|
const newExists = db.prepare('SELECT 1 FROM worktrees WHERE id = ?').get(newId);
|
|
317
|
-
const
|
|
324
|
+
const { noUpdate } = (0, worktree_child_tables_1.getWriteGuardedTables)(db);
|
|
325
|
+
const allChildren = (0, worktree_child_tables_1.getWorktreeChildTables)(db);
|
|
326
|
+
const children = allChildren.filter(({ table }) => !noUpdate.has(table));
|
|
318
327
|
if (newExists) {
|
|
319
328
|
// Collision guard. `path` is UNIQUE so two rows for the same directory
|
|
320
329
|
// should never coexist, but if the DB is already inconsistent keep the
|
|
321
|
-
// destination row's data and fold in the source's non-conflicting children
|
|
322
|
-
// conflicting leftovers are removed by CASCADE when the old row is deleted.
|
|
330
|
+
// destination row's data and fold in the source's non-conflicting children.
|
|
323
331
|
for (const { table, column } of children) {
|
|
324
332
|
db.prepare(`UPDATE OR IGNORE "${table}" SET "${column}" = ? WHERE "${column}" = ?`).run(newId, oldId);
|
|
325
333
|
}
|
|
334
|
+
// Conflicting leftovers are dropped explicitly rather than left to CASCADE:
|
|
335
|
+
// tables without a foreign key never cascade, so relying on the parent
|
|
336
|
+
// delete alone would strand their rows on a dead ID (#1621).
|
|
337
|
+
deleteWorktreeChildRows(db, [oldId], allChildren);
|
|
326
338
|
db.prepare('DELETE FROM worktrees WHERE id = ?').run(oldId);
|
|
327
339
|
return;
|
|
328
340
|
}
|
|
@@ -523,29 +535,38 @@ function getWorktreesByRepository(db, repositoryPath) {
|
|
|
523
535
|
}
|
|
524
536
|
/**
|
|
525
537
|
* Delete all worktrees for a given repository path
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
538
|
+
* Child rows are deleted explicitly before the parent, for every table
|
|
539
|
+
* getWorktreeChildTables reports. ON DELETE CASCADE covers only the tables that
|
|
540
|
+
* declare a foreign key; `tasks`, `verification_runs` and `skill_operations`
|
|
541
|
+
* hold a `worktree_id` with no constraint and would otherwise survive their
|
|
542
|
+
* worktree as orphans (#1621). Deleting `verification_runs` still cascades to
|
|
543
|
+
* `verification_gate_results`, which does declare its foreign key.
|
|
531
544
|
*
|
|
532
545
|
* @param db - Database instance
|
|
533
546
|
* @param repositoryPath - Path of the repository to delete
|
|
534
547
|
* @returns Object containing the count of deleted worktrees
|
|
535
548
|
*/
|
|
536
549
|
function deleteRepositoryWorktrees(db, repositoryPath) {
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
550
|
+
const run = db.transaction(() => {
|
|
551
|
+
const ids = db
|
|
552
|
+
.prepare('SELECT id FROM worktrees WHERE repository_path = ?')
|
|
553
|
+
.all(repositoryPath)
|
|
554
|
+
.map((row) => row.id)
|
|
555
|
+
.filter((id) => id !== null);
|
|
556
|
+
deleteWorktreeChildRows(db, ids);
|
|
557
|
+
return db
|
|
558
|
+
.prepare('DELETE FROM worktrees WHERE repository_path = ?')
|
|
559
|
+
.run(repositoryPath).changes;
|
|
560
|
+
});
|
|
561
|
+
return { deletedCount: run() };
|
|
542
562
|
}
|
|
543
563
|
/**
|
|
544
564
|
* Delete worktrees by their IDs
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
565
|
+
* Child rows are deleted explicitly before the parent, for every table
|
|
566
|
+
* getWorktreeChildTables reports — including skill_installations, whose rows
|
|
567
|
+
* used to survive the worktree and make a re-created worktree at the same path
|
|
568
|
+
* un-installable (#1430), and the constraint-less `tasks` / `verification_runs`
|
|
569
|
+
* / `skill_operations`, which no CASCADE can reach (#1621).
|
|
549
570
|
*
|
|
550
571
|
* @param db - Database instance
|
|
551
572
|
* @param worktreeIds - Array of worktree IDs to delete
|
|
@@ -556,9 +577,11 @@ function deleteWorktreesByIds(db, worktreeIds) {
|
|
|
556
577
|
return { deletedCount: 0 };
|
|
557
578
|
}
|
|
558
579
|
const placeholders = worktreeIds.map(() => '?').join(',');
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
580
|
+
const run = db.transaction(() => {
|
|
581
|
+
deleteWorktreeChildRows(db, worktreeIds);
|
|
582
|
+
return db
|
|
583
|
+
.prepare(`DELETE FROM worktrees WHERE id IN (${placeholders})`)
|
|
584
|
+
.run(...worktreeIds).changes;
|
|
585
|
+
});
|
|
586
|
+
return { deletedCount: run() };
|
|
564
587
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shared between response-poller.ts and API routes
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_SELECTION_LIST_PATTERN = exports.COPILOT_SEPARATOR_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_DIALOG_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
7
|
+
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_SELECTION_LIST_PATTERN = exports.COPILOT_SEPARATOR_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_APPROVAL_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_DIALOG_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
8
8
|
exports.findClaudeChromeStart = findClaudeChromeStart;
|
|
9
9
|
exports.isCodexPromptReady = isCodexPromptReady;
|
|
10
10
|
exports.getCodexActiveDialog = getCodexActiveDialog;
|
|
@@ -295,6 +295,29 @@ exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
|
|
|
295
295
|
* a numbered prompt (PromptPanel with buttons).
|
|
296
296
|
*/
|
|
297
297
|
exports.CODEX_SELECTION_LIST_PATTERN = /press\s+enter\s+to\s+(?:confirm|select)/i;
|
|
298
|
+
/**
|
|
299
|
+
* Codex CLI approval-request footer pattern (Issue #1628).
|
|
300
|
+
*
|
|
301
|
+
* Codex renders an approval request ("Would you like to run the following
|
|
302
|
+
* command?" / "Would you like to make the following edits?") with the SAME
|
|
303
|
+
* "Press enter to confirm" footer as a `/model`-style menu, which is why
|
|
304
|
+
* CODEX_SELECTION_LIST_PATTERN swallows it. The two differ in the escape verb:
|
|
305
|
+
* an approval request can be *cancelled* (it is the agent asking the human for
|
|
306
|
+
* permission), a menu can only be *gone back* from.
|
|
307
|
+
*
|
|
308
|
+
* Measured on codex-cli 0.146.0 (five consecutive live approval frames captured
|
|
309
|
+
* from a real session, plus two live `/model` picker frames):
|
|
310
|
+
* - approval : "Press enter to confirm or esc to cancel"
|
|
311
|
+
* - /model : "Press enter to confirm or esc to go back"
|
|
312
|
+
* - /model : "Press enter to select reasoning effort, or esc to dismiss."
|
|
313
|
+
*
|
|
314
|
+
* Used only as one of two OR'd approval signals (see isCodexApprovalRequest in
|
|
315
|
+
* status-detector.ts); the other is an interrogative question line, so a future
|
|
316
|
+
* rewording of either signal alone does not reopen Issue #1628.
|
|
317
|
+
*
|
|
318
|
+
* No /g flag (keeps .test() stateless), no nested quantifiers (ReDoS-safe).
|
|
319
|
+
*/
|
|
320
|
+
exports.CODEX_APPROVAL_FOOTER_PATTERN = /esc\s+to\s+cancel/i;
|
|
298
321
|
/**
|
|
299
322
|
* Codex CLI pager / edit-previous (transcript) mode footer pattern (Issue #1017)
|
|
300
323
|
*
|
|
@@ -100,7 +100,8 @@ const CODEX_NUMBERED_OPTION_PATTERN = /^\s*[❯›●]?\s*\d{1,2}[.)]\s/;
|
|
|
100
100
|
* already-answered prompt left in the 50-line scan window rather than an active one.
|
|
101
101
|
*
|
|
102
102
|
* Codex keeps the answered "1. Yes / 2. No" block + "press number to confirm" footer in
|
|
103
|
-
* its transcript (
|
|
103
|
+
* its transcript (it draws on the normal screen, so tmux retains a scrollback for it —
|
|
104
|
+
* TMUX_HISTORY_LIMIT lines deep) instead of repainting it away like Claude,
|
|
104
105
|
* so detectPrompt() keeps matching it and detectSessionStatus() reports `waiting` even
|
|
105
106
|
* after the user answered and Codex resumed — the sidebar status dot then stays orange
|
|
106
107
|
* forever (the reported bug).
|
|
@@ -147,6 +148,39 @@ function isCodexStalePrompt(contentLines) {
|
|
|
147
148
|
}
|
|
148
149
|
return lastPromptIdx >= 0 && lastThinkingIdx > lastPromptIdx;
|
|
149
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Issue #1628: decide whether a Codex frame that matched CODEX_SELECTION_LIST_PATTERN
|
|
153
|
+
* is the agent ASKING FOR APPROVAL rather than the user browsing a menu.
|
|
154
|
+
*
|
|
155
|
+
* Why this exists: Codex renders both with a "Press enter to confirm" footer, so the
|
|
156
|
+
* priority-0.8 selection-list branch (added for `/model` in Issue #622) also swallowed
|
|
157
|
+
* every approval request and returned `hasActivePrompt: false`. `isPromptWaiting` is the
|
|
158
|
+
* only blocked-on-a-human signal the current-output payload carries, so `commandmate wait
|
|
159
|
+
* --on-prompt agent` could never raise exit 10 for a Codex worker sitting on
|
|
160
|
+
* "Would you like to run the following command?" — it polled until the timeout while the
|
|
161
|
+
* agent was stopped. Auto-Yes was unaffected because it calls detectPrompt() directly,
|
|
162
|
+
* which parses these frames correctly; only the status-detector layer lost them.
|
|
163
|
+
*
|
|
164
|
+
* Two OR'd signals, both measured against live codex-cli 0.146.0 captures
|
|
165
|
+
* (5 approval frames from one real session + 2 `/model` picker frames):
|
|
166
|
+
* 1. an interrogative question line directly above the options
|
|
167
|
+
* ("Would you like to run the following command?" → the extracted question ends
|
|
168
|
+
* with "?"; a picker's ends with its description, e.g. "…browse all models.")
|
|
169
|
+
* 2. the approval escape verb ("esc to cancel" vs a menu's "esc to go back" /
|
|
170
|
+
* "esc to dismiss")
|
|
171
|
+
* Either alone covers every measured approval frame, so a rewording of one does not
|
|
172
|
+
* reopen the bug.
|
|
173
|
+
*
|
|
174
|
+
* Gated on `promptDetection.isPrompt` so a frame detectPrompt() could not parse into
|
|
175
|
+
* options (e.g. the unnumbered "Select a model" list of Issue #619) can never be
|
|
176
|
+
* promoted to an active prompt.
|
|
177
|
+
*/
|
|
178
|
+
function isCodexApprovalRequest(promptDetection, selectionWindow) {
|
|
179
|
+
if (!promptDetection.isPrompt)
|
|
180
|
+
return false;
|
|
181
|
+
const question = promptDetection.promptData?.question?.trim() ?? '';
|
|
182
|
+
return question.endsWith('?') || cli_patterns_1.CODEX_APPROVAL_FOOTER_PATTERN.test(selectionWindow);
|
|
183
|
+
}
|
|
150
184
|
/**
|
|
151
185
|
* Detect session status with confidence level
|
|
152
186
|
*
|
|
@@ -286,6 +320,21 @@ function detectSessionStatus(output, cliToolId, lastOutputTimestamp) {
|
|
|
286
320
|
if (cli_patterns_1.CODEX_SELECTION_LIST_PATTERN.test(codexSelectionWindow)) {
|
|
287
321
|
const codexPromptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
|
|
288
322
|
const codexPromptDetection = (0, prompt_detector_1.detectPrompt)((0, cli_patterns_1.stripBoxDrawing)(cleanOutput), codexPromptOptions);
|
|
323
|
+
// Issue #1628: an approval request wears the same footer as a menu but is the
|
|
324
|
+
// agent blocked on the human, so it must surface as an active prompt (exit 10
|
|
325
|
+
// for `wait`, PromptPanel in the UI) instead of a navigable list. The #1160
|
|
326
|
+
// staleness guard still applies: an ALREADY-ANSWERED approval whose footer is
|
|
327
|
+
// still inside the window, with Codex running below it, is dead scrollback.
|
|
328
|
+
if (isCodexApprovalRequest(codexPromptDetection, codexSelectionWindow) &&
|
|
329
|
+
!isCodexStalePrompt(contentLines)) {
|
|
330
|
+
return {
|
|
331
|
+
status: 'waiting',
|
|
332
|
+
confidence: 'high',
|
|
333
|
+
reason: exports.STATUS_REASON.PROMPT_DETECTED,
|
|
334
|
+
hasActivePrompt: true,
|
|
335
|
+
promptDetection: codexPromptDetection,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
289
338
|
return {
|
|
290
339
|
status: 'waiting',
|
|
291
340
|
confidence: 'high',
|
|
@@ -333,7 +382,8 @@ function detectSessionStatus(output, cliToolId, lastOutputTimestamp) {
|
|
|
333
382
|
if (promptDetection.isPrompt) {
|
|
334
383
|
// Issue #1160: Codex keeps an ANSWERED approval / numbered-choice block
|
|
335
384
|
// ("1. Yes / 2. No" + "press number to confirm" footer) in its transcript
|
|
336
|
-
// (
|
|
385
|
+
// (non-alternate-screen, so tmux keeps TMUX_HISTORY_LIMIT lines of scrollback for
|
|
386
|
+
// it) instead of repainting it away like Claude. detectPrompt's
|
|
337
387
|
// 50-line window then keeps matching that dead prompt, so this priority-1 branch
|
|
338
388
|
// returns `waiting` even after the user answered and Codex resumed — the sidebar
|
|
339
389
|
// status dot stays orange forever (the reported bug). Guard with a position check:
|
|
@@ -502,11 +502,13 @@ async function startClaudeSession(options) {
|
|
|
502
502
|
// (SF-S2-004: Explicit fall-through instead of hidden re-entry)
|
|
503
503
|
}
|
|
504
504
|
try {
|
|
505
|
-
// Create tmux session
|
|
505
|
+
// Create tmux session. Scrollback depth comes from the shared
|
|
506
|
+
// TMUX_HISTORY_LIMIT default (Issue #1624) — do not re-hardcode it here.
|
|
507
|
+
// (Claude itself renders in the alternate screen and keeps history_size at 0,
|
|
508
|
+
// so the limit is inert for this tool; it still applies to the bare shell.)
|
|
506
509
|
await (0, tmux_1.createSession)({
|
|
507
510
|
sessionName,
|
|
508
511
|
workingDirectory: worktreePath,
|
|
509
|
-
historyLimit: 50000,
|
|
510
512
|
});
|
|
511
513
|
// SF-S2-003: Sanitize environment after createSession, before launching Claude CLI
|
|
512
514
|
await (0, session_key_sender_1.sanitizeSessionEnvironment)(sessionName);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The shell pager that `prefix+g` opens inside a tmux popup (Issue #1623, 案A).
|
|
4
|
+
*
|
|
5
|
+
* ## Why the script is embedded here instead of shipped as a file
|
|
6
|
+
*
|
|
7
|
+
* `package.json`'s `files` list publishes `bin/`, `dist/`, `.next/`, `public/`,
|
|
8
|
+
* `scripts/hooks/` and `.env.example` — NOT `scripts/`. A path into the repo
|
|
9
|
+
* therefore does not exist in a global install, and an `npx` install lives in a
|
|
10
|
+
* cache directory npm may garbage-collect out from under a long-lived `bind-key`.
|
|
11
|
+
* Embedding the text and materializing it into `~/.commandmate/bin/` gives the
|
|
12
|
+
* binding one absolute path that is stable across global / local / npx installs.
|
|
13
|
+
*
|
|
14
|
+
* ## Why the squeeze is reimplemented in awk
|
|
15
|
+
*
|
|
16
|
+
* The popup must not depend on the CommandMate server, a node binary, or a built
|
|
17
|
+
* `dist/` — it only has tmux and a shell. awk is present everywhere those are.
|
|
18
|
+
* The duplication is not left to trust: `tests/unit/lib/tmux/transcript-squeeze.test.ts`
|
|
19
|
+
* runs THIS awk program over real captures and asserts byte equality with
|
|
20
|
+
* `squeezeTranscript()`, so the two cannot drift apart silently.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.PAGER_SCRIPT = exports.PAGER_SCRIPT_FILENAME = exports.SQUEEZE_AWK_PROGRAM = exports.SQUEEZE_AWK_LOCALE = void 0;
|
|
24
|
+
/**
|
|
25
|
+
* Locale the squeeze awk program MUST run under.
|
|
26
|
+
*
|
|
27
|
+
* Not cosmetic — the program is byte-oriented by construction, and `%c` is the
|
|
28
|
+
* one place where awk implementations disagree about what a "character" is:
|
|
29
|
+
*
|
|
30
|
+
* | awk | `sprintf("%c", 194)` |
|
|
31
|
+
* |-------------------------|----------------------|
|
|
32
|
+
* | mawk, BWK awk | byte `0xC2` |
|
|
33
|
+
* | gawk, `LC_ALL=C` | byte `0xC2` |
|
|
34
|
+
* | gawk, `*.UTF-8` locale | U+00C2 -> `C3 82` |
|
|
35
|
+
*
|
|
36
|
+
* `NBSP` below is assembled from bytes `C2 A0`, so under gawk in a UTF-8 locale
|
|
37
|
+
* it becomes a four-byte string that can never match a real NBSP — NBSP-only
|
|
38
|
+
* rows stop counting as blank and the frame comes back unsqueezed. That is not
|
|
39
|
+
* hypothetical: it is exactly how this shipped broken, green on macOS (BWK awk)
|
|
40
|
+
* and red on `ubuntu-latest`, whose `/usr/bin/awk` is gawk (Debian ranks the
|
|
41
|
+
* gawk alternative above mawk) under a UTF-8 locale.
|
|
42
|
+
*
|
|
43
|
+
* Pinning the locale — rather than making the regex match both encodings — is
|
|
44
|
+
* deliberate: an alternation covering the character form
|
|
45
|
+
* (`sprintf("%c%c",194,160) "|" sprintf("%c",160)`) fixes gawk but makes BWK awk
|
|
46
|
+
* abort with `multibyte conversion failure` in a UTF-8 locale. Measured on all
|
|
47
|
+
* three implementations, `LC_ALL=C` is the only setting where every one of them
|
|
48
|
+
* agrees, and it is byte-transparent: the same 1000-row fixture comes out with
|
|
49
|
+
* an identical sha256 from BWK awk, mawk and gawk.
|
|
50
|
+
*
|
|
51
|
+
* Scoped to the awk process alone, so `less` and tmux keep the user's locale and
|
|
52
|
+
* still render UTF-8 box drawing.
|
|
53
|
+
*/
|
|
54
|
+
exports.SQUEEZE_AWK_LOCALE = 'C';
|
|
55
|
+
/**
|
|
56
|
+
* awk implementation of {@link import('./transcript-squeeze').squeezeTranscript}.
|
|
57
|
+
*
|
|
58
|
+
* Mirrors the TypeScript rules exactly:
|
|
59
|
+
* - `ANSI` is built from `sprintf("%c")` rather than a regex literal because
|
|
60
|
+
* `\033` / `\a` escapes inside a regex literal are not portable across awk
|
|
61
|
+
* implementations (macOS ships BWK awk, Linux usually gawk/mawk).
|
|
62
|
+
* - NBSP (UTF-8 `C2 A0`) is folded to a space before trimming, because
|
|
63
|
+
* JavaScript's `.trim()` treats U+00A0 as whitespace and awk's `[ \t]` does not.
|
|
64
|
+
* Without this the two implementations disagree on NBSP-only rows. This is the
|
|
65
|
+
* line that requires {@link SQUEEZE_AWK_LOCALE} — read that doc before editing.
|
|
66
|
+
* - Leading/trailing blank runs are dropped, runs of 1-2 kept verbatim, runs of
|
|
67
|
+
* 3+ collapsed to a single row carrying the run's ANSI sequences.
|
|
68
|
+
*/
|
|
69
|
+
exports.SQUEEZE_AWK_PROGRAM = `BEGIN {
|
|
70
|
+
ESC = sprintf("%c", 27); BEL = sprintf("%c", 7); NBSP = sprintf("%c%c", 194, 160)
|
|
71
|
+
ANSI = ESC "\\\\[[0-9;]*[a-zA-Z]|" ESC "\\\\][^" BEL "]*" BEL "|\\\\[[0-9;]*m"
|
|
72
|
+
n = 0
|
|
73
|
+
}
|
|
74
|
+
{ line[++n] = $0 }
|
|
75
|
+
END {
|
|
76
|
+
for (i = 1; i <= n; i++) {
|
|
77
|
+
s = line[i]; gsub(ANSI, "", s); gsub(NBSP, " ", s)
|
|
78
|
+
gsub(/^[ \\t\\r\\f\\v]+|[ \\t\\r\\f\\v]+$/, "", s)
|
|
79
|
+
blank[i] = (s == "")
|
|
80
|
+
}
|
|
81
|
+
seen = 0; i = 1
|
|
82
|
+
while (i <= n) {
|
|
83
|
+
if (!blank[i]) { print line[i]; seen = 1; i++; continue }
|
|
84
|
+
j = i; while (j <= n && blank[j]) j++
|
|
85
|
+
if (seen && j <= n) {
|
|
86
|
+
if (j - i <= 2) { for (k = i; k < j; k++) print line[k] }
|
|
87
|
+
else {
|
|
88
|
+
joined = ""; for (k = i; k < j; k++) joined = joined line[k]
|
|
89
|
+
keep = ""
|
|
90
|
+
while (match(joined, ANSI)) {
|
|
91
|
+
keep = keep substr(joined, RSTART, RLENGTH); joined = substr(joined, RSTART + RLENGTH)
|
|
92
|
+
}
|
|
93
|
+
print keep
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
i = j
|
|
97
|
+
}
|
|
98
|
+
}`;
|
|
99
|
+
/** Basename of the materialized script. */
|
|
100
|
+
exports.PAGER_SCRIPT_FILENAME = 'cm-read-pane.sh';
|
|
101
|
+
/**
|
|
102
|
+
* Full text of the pager script.
|
|
103
|
+
*
|
|
104
|
+
* Session resolution is done INSIDE the script on purpose. `display-popup` does
|
|
105
|
+
* NOT expand tmux formats in its shell-command (measured on 3.5a: a literal
|
|
106
|
+
* `#{session_name}` reaches the shell unexpanded), so the session cannot be baked
|
|
107
|
+
* into the binding. `tmux display-message -p` inside the popup returns the correct
|
|
108
|
+
* session and client, which is what makes one global binding serve every session.
|
|
109
|
+
*
|
|
110
|
+
* `=NAME:` is the exact-match target form required by Issue #1156 — a bare name
|
|
111
|
+
* prefix-matches and would show the `-2` instance's pane while the primary is
|
|
112
|
+
* stopped.
|
|
113
|
+
*/
|
|
114
|
+
exports.PAGER_SCRIPT = `#!/bin/sh
|
|
115
|
+
# CommandMate reading mode (Issue #1623) — generated file, edits are overwritten.
|
|
116
|
+
#
|
|
117
|
+
# Renders the transcript of a CommandMate tmux session with layout-only blank rows
|
|
118
|
+
# squeezed away, so a pager lands on the composer instead of hundreds of rows of
|
|
119
|
+
# padding. Invoked by \`prefix+g\` through \`display-popup -E\`; also runnable by hand:
|
|
120
|
+
#
|
|
121
|
+
# sh ~/.commandmate/bin/${exports.PAGER_SCRIPT_FILENAME} mcbd-claude-my-worktree
|
|
122
|
+
#
|
|
123
|
+
# The popup is a SNAPSHOT. Press the key again to refresh it.
|
|
124
|
+
set -u
|
|
125
|
+
|
|
126
|
+
SESSION="\${1:-}"
|
|
127
|
+
if [ -z "$SESSION" ]; then
|
|
128
|
+
SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null) || SESSION=""
|
|
129
|
+
fi
|
|
130
|
+
if [ -z "$SESSION" ]; then
|
|
131
|
+
echo "cm-read-pane: no tmux session given and none could be resolved" >&2
|
|
132
|
+
exit 1
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
LINES_BACK="\${CM_READ_LINES:-1000}"
|
|
136
|
+
case "$LINES_BACK" in
|
|
137
|
+
''|*[!0-9]*) LINES_BACK=1000 ;;
|
|
138
|
+
esac
|
|
139
|
+
|
|
140
|
+
if [ -n "\${CM_READ_PAGER:-}" ]; then
|
|
141
|
+
PAGER_CMD="$CM_READ_PAGER"
|
|
142
|
+
elif command -v less >/dev/null 2>&1; then
|
|
143
|
+
PAGER_CMD="less -R +G"
|
|
144
|
+
else
|
|
145
|
+
PAGER_CMD="cat"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# LC_ALL is pinned for awk ONLY (not for less, which still renders UTF-8 box
|
|
149
|
+
# drawing in the user's locale). Under gawk in a UTF-8 locale, sprintf("%c",194)
|
|
150
|
+
# yields U+00C2 rather than the byte 0xC2 and the NBSP fold below silently stops
|
|
151
|
+
# matching. See SQUEEZE_AWK_LOCALE in read-mode-pager.ts.
|
|
152
|
+
tmux capture-pane -pe -t "=$SESSION:" -S "-$LINES_BACK" -E - \\
|
|
153
|
+
| LC_ALL=${exports.SQUEEZE_AWK_LOCALE} awk '${exports.SQUEEZE_AWK_PROGRAM}' \\
|
|
154
|
+
| $PAGER_CMD
|
|
155
|
+
|
|
156
|
+
# Without a pager the popup would close before anything could be read.
|
|
157
|
+
if [ "$PAGER_CMD" = "cat" ]; then
|
|
158
|
+
printf '\\n[CommandMate] end of transcript — press Enter to close.' >&2
|
|
159
|
+
read -r _unused || true
|
|
160
|
+
fi
|
|
161
|
+
`;
|