commandmate 0.18.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 +131 -131
- package/.next/app-path-routes-manifest.json +23 -23
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/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 +23 -23
- 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 +5 -4
- package/.next/server/chunks/6283.js +1 -1
- package/.next/server/chunks/8681.js +1 -1
- package/.next/server/chunks/8807.js +492 -487
- 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-109e3825c7249e47.js → 6488-2c412c29258637b6.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-12b3c5f32fc6094e.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 +20 -11
- 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 +80 -6
- 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/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/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/{-BSmW0Ae2YsxyCl_ue7_d → -E7I667pJDTBamyG8M_Z-}/_buildManifest.js +0 -0
- /package/.next/static/{-BSmW0Ae2YsxyCl_ue7_d → -E7I667pJDTBamyG8M_Z-}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Migration v52: delete child rows pointing at a worktree that no longer exists
|
|
4
|
+
* (Issue #1621, Phase 0).
|
|
5
|
+
*
|
|
6
|
+
* `getWorktreeChildTables` used to enumerate child tables from foreign-key
|
|
7
|
+
* metadata alone, so `tasks` (#1548), `verification_runs` (#1544) and
|
|
8
|
+
* `skill_operations` (#1428) — each holding a `worktree_id` with no constraint
|
|
9
|
+
* declared — were invisible to both the rename path and the delete path. No
|
|
10
|
+
* CASCADE fires without a foreign key, so every worktree deleted since those
|
|
11
|
+
* tables landed left their rows behind, and every worktree ID rename left them
|
|
12
|
+
* addressing an ID nothing resolves. The code fix stops new orphans; this
|
|
13
|
+
* migration removes the ones already written.
|
|
14
|
+
*
|
|
15
|
+
* The sweep reuses the shared enumeration rather than naming tables, so the
|
|
16
|
+
* FK-less tables above and the FK-declared ones are treated identically — an
|
|
17
|
+
* orphan in a CASCADE table is equally unreachable, it just needed a
|
|
18
|
+
* `foreign_keys = OFF` window to be created.
|
|
19
|
+
*
|
|
20
|
+
* Three deliberate narrowings:
|
|
21
|
+
*
|
|
22
|
+
* - Append-only ledgers are excluded (`getDeletableWorktreeChildTables`).
|
|
23
|
+
* `skill_operations` holds a `worktree_id` and does have rows referencing
|
|
24
|
+
* worktrees that are gone, but it declares `BEFORE DELETE ... RAISE(ABORT)`
|
|
25
|
+
* (#1234) and is read across every worktree by the audit feed. Deleting from
|
|
26
|
+
* it aborts the statement, which here would roll back the migration and take
|
|
27
|
+
* startup with it (measured). Its rows are history, not orphans.
|
|
28
|
+
* - `worktree_id IS NULL` rows are left alone. Every current child table
|
|
29
|
+
* declares the column NOT NULL, so this cannot fire today; if a future table
|
|
30
|
+
* makes it nullable, a NULL means "not attached", not "attached to something
|
|
31
|
+
* gone", and deleting it would be data loss rather than cleanup.
|
|
32
|
+
* - `verification_gate_results` has no `worktree_id` and is not swept directly.
|
|
33
|
+
* It reaches its worktree through `verification_runs(id) ON DELETE CASCADE`,
|
|
34
|
+
* so deleting the orphaned runs takes its rows with them (better-sqlite3
|
|
35
|
+
* enables `foreign_keys` by default, and db-instance.ts sets it explicitly).
|
|
36
|
+
* `task_events` is *not* in the same position: v51 declares no foreign key to
|
|
37
|
+
* `tasks` on purpose, because history that vanishes with its subject cannot
|
|
38
|
+
* be audited afterwards. That is a design choice, not an orphan, so it stays.
|
|
39
|
+
*/
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.v52_migrations = void 0;
|
|
42
|
+
const worktree_child_tables_1 = require("./worktree-child-tables");
|
|
43
|
+
exports.v52_migrations = [
|
|
44
|
+
{
|
|
45
|
+
version: 52,
|
|
46
|
+
name: 'delete-orphaned-worktree-children',
|
|
47
|
+
up: (db) => {
|
|
48
|
+
let deleted = 0;
|
|
49
|
+
for (const { table, column } of (0, worktree_child_tables_1.getDeletableWorktreeChildTables)(db)) {
|
|
50
|
+
// Identifiers come from sqlite_master / PRAGMA, never from user input.
|
|
51
|
+
// The alias is `cm_parent` rather than something short like `w` so it
|
|
52
|
+
// cannot collide with a child table that happens to be named the same.
|
|
53
|
+
const result = db
|
|
54
|
+
.prepare(`DELETE FROM "${table}"
|
|
55
|
+
WHERE "${column}" IS NOT NULL
|
|
56
|
+
AND NOT EXISTS (
|
|
57
|
+
SELECT 1 FROM worktrees AS cm_parent
|
|
58
|
+
WHERE cm_parent.id = "${table}"."${column}"
|
|
59
|
+
)`)
|
|
60
|
+
.run();
|
|
61
|
+
if (result.changes > 0) {
|
|
62
|
+
console.log(`Deleted ${result.changes} orphaned row(s) from ${table}.${column}`);
|
|
63
|
+
deleted += result.changes;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (deleted === 0) {
|
|
67
|
+
console.log('No orphaned worktree child rows found');
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
down: () => {
|
|
71
|
+
// Deleted rows cannot be resurrected. Defined anyway so rollback tooling
|
|
72
|
+
// (and the tests that rebuild a v51 database) keep working.
|
|
73
|
+
console.log('No rollback for orphaned child row cleanup (rows are gone)');
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
];
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Live-schema introspection for "which tables reference a worktree".
|
|
4
|
+
*
|
|
5
|
+
* Lives under `migrations/` rather than next to `worktree-db.ts` because the
|
|
6
|
+
* dependency has to point this way: migration v52 sweeps orphans with the same
|
|
7
|
+
* enumeration the runtime uses, and a migration that imports `worktree-db.ts`
|
|
8
|
+
* inherits every mock of it — `vi.mock('@/lib/db/worktree-db')` in an unrelated
|
|
9
|
+
* suite made `runMigrations` throw `No "getDeletableWorktreeChildTables" export
|
|
10
|
+
* is defined` (measured across 5 tests). Schema introspection is the more
|
|
11
|
+
* foundational of the two, so it is what the other side imports.
|
|
12
|
+
*
|
|
13
|
+
* Issue #1621.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.getWorktreeChildTables = getWorktreeChildTables;
|
|
17
|
+
exports.getWriteGuardedTables = getWriteGuardedTables;
|
|
18
|
+
exports.getDeletableWorktreeChildTables = getDeletableWorktreeChildTables;
|
|
19
|
+
/** Conventional name of the column holding a worktree reference. */
|
|
20
|
+
const WORKTREE_REFERENCE_COLUMN = 'worktree_id';
|
|
21
|
+
/**
|
|
22
|
+
* Discover every table that references a worktree, along with the referencing
|
|
23
|
+
* column. Derived dynamically from the live schema so it never drifts out of
|
|
24
|
+
* sync with future migrations that add child tables.
|
|
25
|
+
*
|
|
26
|
+
* Two sources are unioned:
|
|
27
|
+
*
|
|
28
|
+
* 1. a `worktree_id` column, **whether or not** the table declares a foreign key
|
|
29
|
+
* 2. a declared foreign key to `worktrees(id)`, whatever the column is named
|
|
30
|
+
*
|
|
31
|
+
* Source 1 is what Issue #1621 adds. FK metadata alone (all this did before)
|
|
32
|
+
* silently skipped `tasks` (#1548), `verification_runs` (#1544) and
|
|
33
|
+
* `skill_operations` (#1428): all three carry a `worktree_id` but declare no
|
|
34
|
+
* constraint, so a rename left them pointing at an ID that no longer exists and
|
|
35
|
+
* a delete left them behind as orphans — no CASCADE fires without a FK.
|
|
36
|
+
*
|
|
37
|
+
* Source 2 is kept so a future table whose FK column is *not* named
|
|
38
|
+
* `worktree_id` keeps being followed; dropping it would be a regression for
|
|
39
|
+
* every table #1151 already handled.
|
|
40
|
+
*
|
|
41
|
+
* Issue #1151, #1621.
|
|
42
|
+
*/
|
|
43
|
+
function getWorktreeChildTables(db) {
|
|
44
|
+
const tables = db
|
|
45
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
46
|
+
.all();
|
|
47
|
+
const children = [];
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
const add = (table, column) => {
|
|
50
|
+
// JSON encoding keeps the pair unambiguous whatever the identifiers contain.
|
|
51
|
+
const key = JSON.stringify([table, column]);
|
|
52
|
+
if (seen.has(key))
|
|
53
|
+
return;
|
|
54
|
+
seen.add(key);
|
|
55
|
+
children.push({ table, column });
|
|
56
|
+
};
|
|
57
|
+
for (const { name: tableName } of tables) {
|
|
58
|
+
if (tableName === 'worktrees')
|
|
59
|
+
continue;
|
|
60
|
+
// PRAGMA identifiers cannot be bound; `tableName` comes from sqlite_master,
|
|
61
|
+
// not user input, so string interpolation here is safe.
|
|
62
|
+
const columns = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
63
|
+
if (columns.some((column) => column.name === WORKTREE_REFERENCE_COLUMN)) {
|
|
64
|
+
add(tableName, WORKTREE_REFERENCE_COLUMN);
|
|
65
|
+
}
|
|
66
|
+
const fks = db.prepare(`PRAGMA foreign_key_list("${tableName}")`).all();
|
|
67
|
+
for (const fk of fks) {
|
|
68
|
+
if (fk.table === 'worktrees' && (fk.to === 'id' || fk.to === null)) {
|
|
69
|
+
add(tableName, fk.from);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return children;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Tables the schema itself refuses to let us rewrite, per statement kind.
|
|
77
|
+
*
|
|
78
|
+
* An append-only ledger enforces itself with a `BEFORE UPDATE` / `BEFORE DELETE`
|
|
79
|
+
* trigger that calls `RAISE(ABORT, ...)`; `skill_operations` declares both
|
|
80
|
+
* (#1234, migrations v44/v48). Such a table carries a `worktree_id` yet is
|
|
81
|
+
* deliberately *not* owned by its worktree's lifetime: `querySkillOperationAudit`
|
|
82
|
+
* reads it across every worktree for the dashboard feed, exactly like v51 keeps
|
|
83
|
+
* `task_events` alive after its task is gone. Sweeping it is not cleanup, it is
|
|
84
|
+
* destroying audit history — and it does not even fail safely: the trigger
|
|
85
|
+
* aborts the statement, which rolls back the whole enclosing transaction, so a
|
|
86
|
+
* blind sweep breaks worktree deletion outright (measured: `deleteWorktreesByIds`
|
|
87
|
+
* threw `skill_operations is append-only` and the worktree row survived; the
|
|
88
|
+
* same sweep inside migration v52 fails startup).
|
|
89
|
+
*
|
|
90
|
+
* Detected from trigger SQL rather than by table name so a future ledger is
|
|
91
|
+
* protected the moment it declares its guard (Issue #1621).
|
|
92
|
+
*/
|
|
93
|
+
function getWriteGuardedTables(db) {
|
|
94
|
+
const triggers = db
|
|
95
|
+
.prepare("SELECT tbl_name, sql FROM sqlite_master WHERE type = 'trigger'")
|
|
96
|
+
.all();
|
|
97
|
+
const noUpdate = new Set();
|
|
98
|
+
const noDelete = new Set();
|
|
99
|
+
for (const trigger of triggers) {
|
|
100
|
+
if (!trigger.sql)
|
|
101
|
+
continue;
|
|
102
|
+
if (!/\bRAISE\s*\(\s*ABORT/i.test(trigger.sql))
|
|
103
|
+
continue;
|
|
104
|
+
if (/\bBEFORE\s+UPDATE\b/i.test(trigger.sql))
|
|
105
|
+
noUpdate.add(trigger.tbl_name);
|
|
106
|
+
if (/\bBEFORE\s+DELETE\b/i.test(trigger.sql))
|
|
107
|
+
noDelete.add(trigger.tbl_name);
|
|
108
|
+
}
|
|
109
|
+
return { noUpdate, noDelete };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Child tables whose rows a worktree actually owns — everything
|
|
113
|
+
* getWorktreeChildTables reports, minus the append-only ledgers that outlive it.
|
|
114
|
+
* This is the set a delete or an orphan sweep may touch (Issue #1621).
|
|
115
|
+
*/
|
|
116
|
+
function getDeletableWorktreeChildTables(db) {
|
|
117
|
+
const { noDelete } = getWriteGuardedTables(db);
|
|
118
|
+
return getWorktreeChildTables(db).filter(({ table }) => !noDelete.has(table));
|
|
119
|
+
}
|
|
@@ -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);
|