commandmate 0.41.1 → 0.42.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 +118 -118
- package/.next/app-path-routes-manifest.json +25 -25
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_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/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +103 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +103 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +1 -103
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.js +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.js +1 -103
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_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 +9 -9
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +25 -25
- package/.next/server/chunks/1068.js +1 -0
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/1501.js +5 -5
- package/.next/server/chunks/1990.js +103 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3707.js +1 -1
- package/.next/server/chunks/3718.js +1 -0
- package/.next/server/chunks/434.js +1 -1
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/5775.js +1 -0
- package/.next/server/chunks/611.js +1 -0
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/650.js +1 -1
- package/.next/server/chunks/6781.js +1 -1
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8581.js +1 -1
- package/.next/server/chunks/8583.js +1 -1
- package/.next/server/chunks/8673.js +2 -2
- package/.next/server/chunks/8728.js +3 -3
- package/.next/server/chunks/8791.js +1 -0
- package/.next/server/chunks/8800.js +1 -1
- package/.next/server/chunks/8918.js +1 -103
- package/.next/server/chunks/9130.js +2 -2
- package/.next/server/chunks/964.js +1 -1
- package/.next/server/chunks/9731.js +3 -3
- package/.next/server/chunks/9882.js +3 -3
- 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/{1642-fb2e176aaadbd137.js → 1642-767aed8e7c61ef7f.js} +1 -1
- package/.next/static/chunks/{2407-7bd75de2f6be3692.js → 2407-faae66d204748f7b.js} +1 -1
- package/.next/static/chunks/{2619-04bc32f026a0d946.js → 2619-a305ad4810f1393a.js} +1 -1
- package/.next/static/chunks/7705-2a2936551b0cf9a3.js +1 -0
- package/.next/static/chunks/8165-235859c6930f01b1.js +1 -0
- package/.next/static/chunks/app/{layout-51fa91ea833c97fc.js → layout-9a715043610e4e27.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-61dd928d635db590.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/{page-373ea37b16d930c7.js → page-c601f3b3ece6557e.js} +1 -1
- package/.next/static/css/{974314e796232f53.css → b3f433d52b8ed5c8.css} +1 -1
- package/.next/trace +13 -13
- package/dist/cli/commands/attach.d.ts +6 -4
- package/dist/cli/commands/attach.d.ts.map +1 -1
- package/dist/cli/commands/attach.js +36 -5
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +5 -0
- package/dist/cli/commands/ls.d.ts.map +1 -1
- package/dist/cli/commands/ls.js +19 -9
- package/dist/cli/commands/whoami.d.ts +9 -25
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +18 -42
- package/dist/cli/types/api-responses.d.ts +37 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/lib/cli-tools/session-name.d.ts +72 -4
- package/dist/lib/cli-tools/session-name.d.ts.map +1 -1
- package/dist/lib/cli-tools/session-name.js +115 -12
- package/dist/lib/detection/tools/verified-against.d.ts +11 -2
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.js +11 -2
- package/dist/lib/session/tmux-session-surface.d.ts +9 -7
- package/dist/lib/session/tmux-session-surface.d.ts.map +1 -1
- package/dist/lib/session/tmux-session-surface.js +15 -8
- package/dist/lib/tmux/legacy-session-alias.d.ts +43 -0
- package/dist/lib/tmux/legacy-session-alias.d.ts.map +1 -0
- package/dist/lib/tmux/legacy-session-alias.js +64 -0
- package/dist/lib/verification/verify-config.d.ts +21 -0
- package/dist/lib/verification/verify-config.d.ts.map +1 -1
- package/dist/lib/verification/verify-config.js +54 -1
- package/dist/server/locales/en/worktree.json +4 -1
- package/dist/server/locales/ja/worktree.json +4 -1
- package/dist/server/server.js +29 -0
- package/dist/server/src/lib/auto-yes-poller.js +15 -0
- package/dist/server/src/lib/cli-tools/antigravity.js +1 -1
- package/dist/server/src/lib/cli-tools/base.js +10 -1
- package/dist/server/src/lib/cli-tools/codex.js +1 -1
- package/dist/server/src/lib/cli-tools/command-code.js +1 -1
- package/dist/server/src/lib/cli-tools/copilot.js +1 -1
- package/dist/server/src/lib/cli-tools/gemini.js +1 -1
- package/dist/server/src/lib/cli-tools/legacy-session-adoption.js +19 -0
- package/dist/server/src/lib/cli-tools/opencode.js +1 -1
- package/dist/server/src/lib/cli-tools/session-name.js +115 -12
- package/dist/server/src/lib/cli-tools/session-namespace.js +55 -0
- package/dist/server/src/lib/cli-tools/session-ownership.js +21 -0
- package/dist/server/src/lib/cli-tools/vibe-local.js +1 -1
- package/dist/server/src/lib/cli-tools/worktree-session-ownership.js +28 -0
- package/dist/server/src/lib/db/app-settings-db.js +29 -0
- package/dist/server/src/lib/detection/cli-patterns.js +46 -8
- package/dist/server/src/lib/detection/tools/codex/cli-patterns.js +8 -2
- package/dist/server/src/lib/detection/tools/codex/detect.js +8 -1
- package/dist/server/src/lib/detection/tools/codex/prompt.js +6 -2
- package/dist/server/src/lib/detection/tools/verified-against.js +11 -2
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +77 -2
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +77 -1
- package/dist/server/src/lib/realtime/terminal-broadcast.js +11 -0
- package/dist/server/src/lib/session/adopt-legacy-sessions.js +89 -0
- package/dist/server/src/lib/session/claude-session.js +13 -10
- package/dist/server/src/lib/session/current-output-builder.js +18 -0
- package/dist/server/src/lib/session/send-user-message.js +26 -0
- package/dist/server/src/lib/session/session-instance-targets.js +53 -0
- package/dist/server/src/lib/session/tmux-session-surface.js +15 -8
- package/dist/server/src/lib/session/worktree-session-reconcile.js +101 -56
- package/dist/server/src/lib/session-cleanup.js +33 -3
- package/dist/server/src/lib/tmux/legacy-session-alias.js +64 -0
- package/dist/server/src/lib/tmux/session-hook-scripts.js +12 -4
- package/dist/server/src/lib/tmux/session-ownership.js +174 -0
- package/dist/server/src/lib/tmux/tmux.js +13 -3
- package/dist/server/src/lib/verification/env-clean-gate.js +46 -14
- package/dist/server/src/lib/verification/gate-runner.js +1 -0
- package/dist/server/src/lib/verification/verify-config.js +54 -1
- package/dist/server/src/lib/ws-server.js +11 -0
- package/package.json +1 -1
- package/release-notes/0.42.0.json +62 -0
- package/.next/server/chunks/3323.js +0 -1
- package/.next/server/chunks/7075.js +0 -1
- package/.next/server/chunks/9080.js +0 -1
- package/.next/static/chunks/7705-25076b57224da370.js +0 -1
- package/.next/static/chunks/8165-d3b7c8a7aa776bc7.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-1e6300dd50cd9a4d.js +0 -1
- /package/.next/static/{_fBjTkjoWfOqdjl2SeMIN → EbTDBXcXRpFNH4eom-HVv}/_buildManifest.js +0 -0
- /package/.next/static/{_fBjTkjoWfOqdjl2SeMIN → EbTDBXcXRpFNH4eom-HVv}/_ssgManifest.js +0 -0
|
@@ -44,6 +44,8 @@ const assistant_response_saver_1 = require("../../lib/assistant-response-saver")
|
|
|
44
44
|
const ws_server_1 = require("../../lib/ws-server");
|
|
45
45
|
const types_2 = require("../../lib/realtime/types");
|
|
46
46
|
const logger_1 = require("../../lib/logger");
|
|
47
|
+
const worktree_session_ownership_1 = require("../../lib/cli-tools/worktree-session-ownership");
|
|
48
|
+
const session_name_1 = require("../../lib/cli-tools/session-name");
|
|
47
49
|
const prompt_waiting_guard_1 = require("../../lib/session/prompt-waiting-guard");
|
|
48
50
|
const opencode_1 = require("../../lib/cli-tools/opencode");
|
|
49
51
|
const logger = (0, logger_1.createLogger)('session/send-user-message');
|
|
@@ -108,6 +110,30 @@ async function sendUserMessage(db, params) {
|
|
|
108
110
|
*/
|
|
109
111
|
const resolvedInstanceId = instanceId ?? cliToolId;
|
|
110
112
|
const cliTool = manager_1.CLIToolManager.getInstance().getTool(cliToolId);
|
|
113
|
+
// Issue #2865: the send route checks ownership itself, but the timer manager
|
|
114
|
+
// and relay delivery reach tmux only through here. A session under this
|
|
115
|
+
// worktree's name that was created in another directory belongs to another
|
|
116
|
+
// CommandMate server; nothing is typed into it and no poller watches it.
|
|
117
|
+
// Checked before the prompt guard, which would otherwise read that pane.
|
|
118
|
+
const sessionName = (0, session_name_1.resolveSessionName)(cliToolId, worktreeId, instanceId);
|
|
119
|
+
const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName, db);
|
|
120
|
+
if (ownership === null || ownership.verdict === 'foreign') {
|
|
121
|
+
logger.warn('send-refused-foreign-session', {
|
|
122
|
+
worktreeId,
|
|
123
|
+
cliToolId,
|
|
124
|
+
instanceId: resolvedInstanceId,
|
|
125
|
+
sessionName,
|
|
126
|
+
sessionPath: ownership?.sessionPath ?? null,
|
|
127
|
+
reason: ownership ? 'foreign' : 'worktree_not_found',
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
stage: 'foreign_session',
|
|
132
|
+
error: ownership
|
|
133
|
+
? `tmux session "${sessionName}" belongs to another CommandMate server`
|
|
134
|
+
: `Worktree '${worktreeId}' not found; cannot confirm tmux session "${sessionName}" is this server's`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
111
137
|
// Issue #1708: refuse before the first side effect. A prompt dialog does not
|
|
112
138
|
// forward keystrokes to the agent — they accumulate in its own input line — so
|
|
113
139
|
// the message is lost AND the next `respond` has to answer a prompt whose
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Every (CLI tool, instance) pair whose tmux session could belong to a worktree
|
|
4
|
+
* (Issue #1621, extracted for #2866).
|
|
5
|
+
*
|
|
6
|
+
* Shared by the startup passes that predict session names from the database —
|
|
7
|
+
* `worktree-session-reconcile.ts` (ID moves) and `adopt-legacy-sessions.ts`
|
|
8
|
+
* (legacy-name adoption) — so both enumerate the same set.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.collectInstanceTargets = collectInstanceTargets;
|
|
12
|
+
const types_1 = require("../../lib/cli-tools/types");
|
|
13
|
+
const agent_instances_db_1 = require("../../lib/db/agent-instances-db");
|
|
14
|
+
/**
|
|
15
|
+
* The primary instance of every CLI tool, plus every roster row under any of
|
|
16
|
+
* `worktreeIds`.
|
|
17
|
+
*
|
|
18
|
+
* The primary instance of every CLI tool is always included — a worktree that
|
|
19
|
+
* predates the roster (#1000) has no `agent_instances` rows at all yet can
|
|
20
|
+
* absolutely have a running `mcbd-claude-<id>` session.
|
|
21
|
+
*
|
|
22
|
+
* @param db - Database instance (read for the agent roster)
|
|
23
|
+
* @param worktreeIds - The worktree IDs whose roster rows to include
|
|
24
|
+
*/
|
|
25
|
+
function collectInstanceTargets(db, worktreeIds) {
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const targets = [];
|
|
28
|
+
const add = (cliToolId, instanceId) => {
|
|
29
|
+
const key = `${cliToolId}|${instanceId}`;
|
|
30
|
+
if (seen.has(key))
|
|
31
|
+
return;
|
|
32
|
+
seen.add(key);
|
|
33
|
+
targets.push({ cliToolId, instanceId });
|
|
34
|
+
};
|
|
35
|
+
for (const cliToolId of types_1.CLI_TOOL_IDS)
|
|
36
|
+
add(cliToolId, cliToolId);
|
|
37
|
+
for (const worktreeId of worktreeIds) {
|
|
38
|
+
let instances;
|
|
39
|
+
try {
|
|
40
|
+
instances = (0, agent_instances_db_1.getAgentInstances)(db, worktreeId);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// No agent_instances table (older database) — the primary instances above
|
|
44
|
+
// already cover the pre-roster shape.
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
for (const instance of instances) {
|
|
48
|
+
if ((0, types_1.isCliToolType)(instance.cliTool))
|
|
49
|
+
add(instance.cliTool, instance.id);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return targets;
|
|
53
|
+
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* The tmux "surface" CommandMate publishes on its own sessions (Issue #2317).
|
|
4
4
|
*
|
|
5
5
|
* Everything here is a pure argument builder or a pure predicate — no
|
|
6
|
-
* `child_process`, no `@/` path alias,
|
|
7
|
-
* constraints put it here rather than under
|
|
8
|
-
* load-bearing:
|
|
6
|
+
* `child_process`, no `@/` path alias, one constants import and the pure
|
|
7
|
+
* session-name parser. Three constraints put it here rather than under
|
|
8
|
+
* `lib/tmux/`, and all three are load-bearing:
|
|
9
9
|
*
|
|
10
10
|
* 1. **`tsconfig.cli.json` sets `"paths": {}`.** The CLI bundle resolves nothing
|
|
11
11
|
* through `@/…`, so a module the `attach` command needs may only reach the
|
|
@@ -67,6 +67,10 @@ exports.buildRemoveAutoPopupHookArgs = buildRemoveAutoPopupHookArgs;
|
|
|
67
67
|
exports.buildDelegateGeometryCommands = buildDelegateGeometryCommands;
|
|
68
68
|
exports.buildRestoreGeometryCommands = buildRestoreGeometryCommands;
|
|
69
69
|
const tmux_pane_config_1 = require("../../config/tmux-pane-config");
|
|
70
|
+
// Relative on purpose (constraint 1 above). `session-name.ts` is already part of
|
|
71
|
+
// the CLI bundle (`commands/attach.ts` imports it) and reaches nothing through
|
|
72
|
+
// `@/…`, so it costs the bundle nothing new (Issue #2866).
|
|
73
|
+
const session_name_1 = require("../cli-tools/session-name");
|
|
70
74
|
/**
|
|
71
75
|
* Session-name prefix every CommandMate tmux session carries.
|
|
72
76
|
*
|
|
@@ -308,8 +312,9 @@ function isLiveAttachSupported(cliToolId) {
|
|
|
308
312
|
/**
|
|
309
313
|
* Whether a tmux session's NAME says it runs a tool `--live` supports.
|
|
310
314
|
*
|
|
311
|
-
* The session name is `mcbd
|
|
312
|
-
*
|
|
315
|
+
* The session name is `mcbd-[<ns>-]<tool>-<worktree>` and `parseSessionName`
|
|
316
|
+
* reads the tool out of either form (Issue #2866), so this is exact rather
|
|
317
|
+
* than a guess. It exists because
|
|
313
318
|
* the two callers that need the answer — the attach hook script's installer and
|
|
314
319
|
* the response poller — hold a session name and nothing else: a hook fires with
|
|
315
320
|
* tmux formats, and the poller must not pay a tmux round-trip to ask about a
|
|
@@ -320,7 +325,8 @@ function isLiveAttachSupported(cliToolId) {
|
|
|
320
325
|
function isLiveAttachEligibleSession(sessionName) {
|
|
321
326
|
if (!isCommandMateSession(sessionName))
|
|
322
327
|
return false;
|
|
323
|
-
|
|
328
|
+
const cliToolId = (0, session_name_1.parseSessionName)(sessionName)?.cliToolId;
|
|
329
|
+
return cliToolId !== undefined && exports.LIVE_ATTACH_TOOLS.includes(cliToolId);
|
|
324
330
|
}
|
|
325
331
|
/**
|
|
326
332
|
* CLI tools that paint their transcript in the terminal's alternate screen.
|
|
@@ -331,8 +337,9 @@ function isLiveAttachEligibleSession(sessionName) {
|
|
|
331
337
|
* else. `attach` prints its hint for exactly these.
|
|
332
338
|
*
|
|
333
339
|
* Deliberately a local list rather than an import of `usesAlternateScreen()`:
|
|
334
|
-
* this module must stay free of `lib/cli-tools
|
|
335
|
-
*
|
|
340
|
+
* this module must stay free of `lib/cli-tools` beyond the pure
|
|
341
|
+
* `session-name.ts`, whose tool graph the CLI bundle cannot afford (see the
|
|
342
|
+
* module docblock). The two are pinned to each other by
|
|
336
343
|
* `tests/unit/tmux/session-surface-2317.test.ts`, so a tool moving in or out of
|
|
337
344
|
* the alternate screen breaks a test rather than silently dropping the hint.
|
|
338
345
|
*/
|
|
@@ -74,11 +74,15 @@ exports.reconcileWorktreeSessions = reconcileWorktreeSessions;
|
|
|
74
74
|
exports.reconcileWorktreeSessionsFromAliases = reconcileWorktreeSessionsFromAliases;
|
|
75
75
|
const manager_1 = require("../../lib/cli-tools/manager");
|
|
76
76
|
const types_1 = require("../../lib/cli-tools/types");
|
|
77
|
-
const validation_1 = require("../../lib/cli-tools/validation");
|
|
78
77
|
const agent_instances_db_1 = require("../../lib/db/agent-instances-db");
|
|
78
|
+
const validation_1 = require("../../lib/cli-tools/validation");
|
|
79
|
+
const session_name_1 = require("../../lib/cli-tools/session-name");
|
|
80
|
+
const session_namespace_1 = require("../../lib/cli-tools/session-namespace");
|
|
81
|
+
const session_instance_targets_1 = require("./session-instance-targets");
|
|
79
82
|
const worktree_alias_db_1 = require("../../lib/db/worktree-alias-db");
|
|
80
83
|
const path_validator_1 = require("../../lib/security/path-validator");
|
|
81
84
|
const tmux_1 = require("../../lib/tmux/tmux");
|
|
85
|
+
const session_ownership_1 = require("../../lib/tmux/session-ownership");
|
|
82
86
|
const control_mode_tmux_transport_1 = require("../../lib/tmux/control-mode-tmux-transport");
|
|
83
87
|
const auto_yes_manager_1 = require("../../lib/polling/auto-yes-manager");
|
|
84
88
|
const response_poller_1 = require("../../lib/polling/response-poller");
|
|
@@ -131,44 +135,16 @@ function normalizeRenames(renames) {
|
|
|
131
135
|
* The roster is read for BOTH IDs because the caller may reconcile either side
|
|
132
136
|
* of the DB move: after `migrateWorktreeIdPreservingChildren` the rows sit under
|
|
133
137
|
* `newId`, but a caller reconciling ahead of the move still finds them under
|
|
134
|
-
* `oldId`.
|
|
135
|
-
* worktree that predates the roster (#1000) has no `agent_instances` rows at all
|
|
136
|
-
* yet can absolutely have a running `mcbd-claude-<id>` session.
|
|
138
|
+
* `oldId`.
|
|
137
139
|
*/
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
const targets = [];
|
|
141
|
-
const add = (cliToolId, instanceId) => {
|
|
142
|
-
const key = `${cliToolId}|${instanceId}`;
|
|
143
|
-
if (seen.has(key))
|
|
144
|
-
return;
|
|
145
|
-
seen.add(key);
|
|
146
|
-
targets.push({ cliToolId, instanceId });
|
|
147
|
-
};
|
|
148
|
-
for (const cliToolId of types_1.CLI_TOOL_IDS)
|
|
149
|
-
add(cliToolId, cliToolId);
|
|
150
|
-
for (const worktreeId of [rename.newId, rename.oldId]) {
|
|
151
|
-
let instances;
|
|
152
|
-
try {
|
|
153
|
-
instances = (0, agent_instances_db_1.getAgentInstances)(db, worktreeId);
|
|
154
|
-
}
|
|
155
|
-
catch {
|
|
156
|
-
// No agent_instances table (older database) — the primary instances above
|
|
157
|
-
// already cover the pre-roster shape.
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
for (const instance of instances) {
|
|
161
|
-
if ((0, types_1.isCliToolType)(instance.cliTool))
|
|
162
|
-
add(instance.cliTool, instance.id);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return targets;
|
|
140
|
+
function collectRenameInstanceTargets(db, rename) {
|
|
141
|
+
return (0, session_instance_targets_1.collectInstanceTargets)(db, [rename.newId, rename.oldId]);
|
|
166
142
|
}
|
|
167
143
|
/** Build the (old session name → new session name) plan for one ID move. */
|
|
168
144
|
function planSessionRenames(db, rename, errors) {
|
|
169
145
|
const manager = manager_1.CLIToolManager.getInstance();
|
|
170
146
|
const plans = [];
|
|
171
|
-
for (const { cliToolId, instanceId } of
|
|
147
|
+
for (const { cliToolId, instanceId } of collectRenameInstanceTargets(db, rename)) {
|
|
172
148
|
try {
|
|
173
149
|
const tool = manager.getTool(cliToolId);
|
|
174
150
|
const oldName = tool.getSessionName(rename.oldId, instanceId);
|
|
@@ -243,27 +219,33 @@ function collectKnownWorktreeIds(db, renames) {
|
|
|
243
219
|
* @param knownIds - Every worktree ID the database can vouch for
|
|
244
220
|
* @returns The attribution, or `null` when nothing in `knownIds` explains the name
|
|
245
221
|
*/
|
|
246
|
-
function attributeSessionName(name, knownIds) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
return null;
|
|
263
|
-
candidate = candidate.slice(0, cut);
|
|
222
|
+
function attributeSessionName(name, knownIds, namespace = (0, session_namespace_1.getSessionNamespace)()) {
|
|
223
|
+
// Issue #2866: legacy names and names in this server's namespace only. A name
|
|
224
|
+
// in another namespace is another server's session, whatever ID it carries.
|
|
225
|
+
const parsed = (0, session_name_1.parseSessionName)(name);
|
|
226
|
+
if (!parsed)
|
|
227
|
+
return null;
|
|
228
|
+
if (parsed.namespace !== null && parsed.namespace !== namespace)
|
|
229
|
+
return null;
|
|
230
|
+
// No CLI tool id is a prefix of another, so the remainder is unambiguously
|
|
231
|
+
// `<id>[-<suffix>]`.
|
|
232
|
+
const { cliToolId, rest } = parsed;
|
|
233
|
+
let candidate = rest;
|
|
234
|
+
for (;;) {
|
|
235
|
+
if (knownIds.has(candidate)) {
|
|
236
|
+
const suffix = candidate.length === rest.length ? undefined : rest.slice(candidate.length + 1);
|
|
237
|
+
return { cliToolId, worktreeId: candidate, suffix: suffix || undefined };
|
|
264
238
|
}
|
|
239
|
+
const cut = candidate.lastIndexOf('-');
|
|
240
|
+
if (cut <= 0)
|
|
241
|
+
return null;
|
|
242
|
+
candidate = candidate.slice(0, cut);
|
|
265
243
|
}
|
|
266
|
-
|
|
244
|
+
}
|
|
245
|
+
/** Whether a live name is another server's namespaced session (Issue #2866). */
|
|
246
|
+
function isOtherNamespaceSession(name, namespace) {
|
|
247
|
+
const parsed = (0, session_name_1.parseSessionName)(name);
|
|
248
|
+
return parsed !== null && parsed.namespace !== null && parsed.namespace !== namespace;
|
|
267
249
|
}
|
|
268
250
|
/**
|
|
269
251
|
* Read the live session list the other way round: attribute each name to a
|
|
@@ -277,10 +259,14 @@ function attributeSessionName(name, knownIds) {
|
|
|
277
259
|
function discoverSessionRenames(renames, liveNames, knownIds, result) {
|
|
278
260
|
const byOldId = new Map(renames.map((rename) => [rename.oldId, rename]));
|
|
279
261
|
const plans = [];
|
|
262
|
+
const namespace = (0, session_namespace_1.getSessionNamespace)();
|
|
280
263
|
for (const name of liveNames) {
|
|
281
264
|
if (!name.startsWith('mcbd-'))
|
|
282
265
|
continue;
|
|
283
|
-
|
|
266
|
+
// Issue #2866: another server's namespace — neither attributed nor reported.
|
|
267
|
+
if (isOtherNamespaceSession(name, namespace))
|
|
268
|
+
continue;
|
|
269
|
+
const attributed = attributeSessionName(name, knownIds, namespace);
|
|
284
270
|
if (!attributed) {
|
|
285
271
|
result.unaccountedSessions.push(name);
|
|
286
272
|
continue;
|
|
@@ -291,7 +277,8 @@ function discoverSessionRenames(renames, liveNames, knownIds, result) {
|
|
|
291
277
|
try {
|
|
292
278
|
const tool = manager_1.CLIToolManager.getInstance().getTool(attributed.cliToolId);
|
|
293
279
|
// Rebuilt through getSessionName rather than by string surgery so the
|
|
294
|
-
// suffix convention stays owned by one place (`cli-tools/
|
|
280
|
+
// suffix convention stays owned by one place (`cli-tools/session-name.ts`).
|
|
281
|
+
// A legacy-form source lands on the current form (Issue #2866).
|
|
295
282
|
const instanceId = attributed.suffix
|
|
296
283
|
? (0, types_1.buildInstanceId)(attributed.cliToolId, attributed.suffix)
|
|
297
284
|
: attributed.cliToolId;
|
|
@@ -322,6 +309,58 @@ function mergeSessionPlans(predicted, discovered) {
|
|
|
322
309
|
}
|
|
323
310
|
return Array.from(byOldName.values());
|
|
324
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* The path of a worktree row, or null. Queried directly for the same reason
|
|
314
|
+
* {@link collectKnownWorktreeIds} is (suites mock `worktree-db`).
|
|
315
|
+
*/
|
|
316
|
+
function readWorktreePath(db, worktreeId) {
|
|
317
|
+
try {
|
|
318
|
+
const row = db.prepare('SELECT path FROM worktrees WHERE id = ?').get(worktreeId);
|
|
319
|
+
return row?.path ?? null;
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Drop the plans whose live source session another CommandMate server created
|
|
327
|
+
* (Issue #2865).
|
|
328
|
+
*
|
|
329
|
+
* Session names carry no server identity, so a live `mcbd-claude-<old>` may be
|
|
330
|
+
* a different server's session that merely shares the worktree ID. Only a
|
|
331
|
+
* session whose `#{session_path}` is the worktree's own directory is renamed;
|
|
332
|
+
* the rest are reported in `skippedSessions`. The directory comes from the
|
|
333
|
+
* rename target's row, falling back to the source's row when the pass runs
|
|
334
|
+
* ahead of the DB move. A plan whose worktree has no row at all cannot be
|
|
335
|
+
* vouched for and is skipped too (fail safe).
|
|
336
|
+
*/
|
|
337
|
+
function excludeForeignSessionPlans(db, plans, live, livePaths, deps, result) {
|
|
338
|
+
const owned = [];
|
|
339
|
+
for (const plan of plans) {
|
|
340
|
+
// Not live: renameSessionsTwoStage drops it without counting it.
|
|
341
|
+
if (!live.has(plan.oldName)) {
|
|
342
|
+
owned.push(plan);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
const worktreePath = readWorktreePath(db, plan.rename.newId) ?? readWorktreePath(db, plan.rename.oldId);
|
|
346
|
+
const sessionPath = livePaths.get(plan.oldName) ?? null;
|
|
347
|
+
if (worktreePath !== null && deps.isSessionOwnedBy(sessionPath, worktreePath)) {
|
|
348
|
+
owned.push(plan);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
result.skippedSessions.push({
|
|
352
|
+
oldName: plan.oldName,
|
|
353
|
+
newName: plan.newName,
|
|
354
|
+
reason: 'session owned by another server',
|
|
355
|
+
});
|
|
356
|
+
logger.warn('reconcile:foreign-session-skipped', {
|
|
357
|
+
sessionName: plan.oldName,
|
|
358
|
+
sessionPath,
|
|
359
|
+
worktreePath,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return owned;
|
|
363
|
+
}
|
|
325
364
|
/** Mint a session name that is free both on the server and within this pass. */
|
|
326
365
|
function allocateTempName(taken, ordinal) {
|
|
327
366
|
let candidate = `${TEMP_SESSION_PREFIX}${ordinal}`;
|
|
@@ -501,13 +540,18 @@ async function reconcileWorktreeSessions(db, renamesOrOldId, newIdOrOptions, may
|
|
|
501
540
|
const deps = {
|
|
502
541
|
listSessions: options?.tmux?.listSessions ?? tmux_1.listSessions,
|
|
503
542
|
renameSession: options?.tmux?.renameSession ?? tmux_1.renameSession,
|
|
543
|
+
isSessionOwnedBy: options?.tmux?.isSessionOwnedBy ?? (0, session_ownership_1.createCachedOwnershipMatcher)(),
|
|
504
544
|
};
|
|
505
545
|
// Listed once, up front: both the prediction pass and the attribution pass
|
|
506
546
|
// need it, and it is what makes a startup with nothing stale cost exactly one
|
|
507
547
|
// `tmux list-sessions`.
|
|
508
548
|
let live = null;
|
|
549
|
+
const livePaths = new Map();
|
|
509
550
|
try {
|
|
510
|
-
|
|
551
|
+
const sessions = await deps.listSessions();
|
|
552
|
+
live = new Set(sessions.map((session) => session.name));
|
|
553
|
+
for (const session of sessions)
|
|
554
|
+
livePaths.set(session.name, session.path);
|
|
511
555
|
}
|
|
512
556
|
catch (error) {
|
|
513
557
|
result.errors.push(`list tmux sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -527,7 +571,8 @@ async function reconcileWorktreeSessions(db, renamesOrOldId, newIdOrOptions, may
|
|
|
527
571
|
else
|
|
528
572
|
result.planSources.discovered++;
|
|
529
573
|
}
|
|
530
|
-
|
|
574
|
+
const ownedPlans = excludeForeignSessionPlans(db, plans, live, livePaths, deps, result);
|
|
575
|
+
await renameSessionsTwoStage(ownedPlans, live, deps, result);
|
|
531
576
|
}
|
|
532
577
|
// Runtime state moves after the sessions: a poller that ticks in between
|
|
533
578
|
// fails one capture against a name that is briefly gone, which is a logged
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.cleanupWorktreeSessions = cleanupWorktreeSessions;
|
|
13
13
|
exports.cleanupMultipleWorktrees = cleanupMultipleWorktrees;
|
|
14
14
|
exports.killWorktreeSession = killWorktreeSession;
|
|
15
|
+
exports.createOwnedSessionKiller = createOwnedSessionKiller;
|
|
15
16
|
exports.cleanupGlobalSessions = cleanupGlobalSessions;
|
|
16
17
|
exports.syncWorktreesAndCleanup = syncWorktreesAndCleanup;
|
|
17
18
|
const response_poller_1 = require("./polling/response-poller");
|
|
@@ -27,6 +28,8 @@ const logger_1 = require("../lib/logger");
|
|
|
27
28
|
const manager_1 = require("./cli-tools/manager");
|
|
28
29
|
const tmux_1 = require("./tmux/tmux");
|
|
29
30
|
const worktrees_1 = require("./git/worktrees");
|
|
31
|
+
const worktree_db_1 = require("./db/worktree-db");
|
|
32
|
+
const session_ownership_1 = require("./cli-tools/session-ownership");
|
|
30
33
|
const logger = (0, logger_1.createLogger)('session-cleanup');
|
|
31
34
|
/**
|
|
32
35
|
* Clean up all CLI tool sessions and pollers for a single worktree
|
|
@@ -164,26 +167,50 @@ async function cleanupMultipleWorktrees(worktreeIds, killSessionFn) {
|
|
|
164
167
|
* MF-C01: getTool() throws Error if tool not found, wrapped in try-catch
|
|
165
168
|
* SF-004: isRunning() correctly awaited
|
|
166
169
|
*
|
|
170
|
+
* Issue #2865: only a session created in `worktreePath` is killed. A
|
|
171
|
+
* same-named session another CommandMate server created (the worktree ID is
|
|
172
|
+
* derived from the directory name, so two servers can share it) is left alone
|
|
173
|
+
* and `false` is returned.
|
|
174
|
+
*
|
|
167
175
|
* @param worktreeId - Worktree ID
|
|
168
176
|
* @param cliToolId - CLI tool type
|
|
177
|
+
* @param worktreePath - The worktree's directory, read from its row BEFORE the row is deleted
|
|
169
178
|
* @returns true if session was killed, false otherwise
|
|
170
179
|
*/
|
|
171
|
-
async function killWorktreeSession(worktreeId, cliToolId) {
|
|
180
|
+
async function killWorktreeSession(worktreeId, cliToolId, worktreePath) {
|
|
172
181
|
try {
|
|
173
182
|
const manager = manager_1.CLIToolManager.getInstance();
|
|
174
183
|
const tool = manager.getTool(cliToolId); // throws Error if not found
|
|
175
184
|
if (!await tool.isRunning(worktreeId))
|
|
176
185
|
return false; // SF-004: await
|
|
186
|
+
const sessionName = tool.getSessionName(worktreeId);
|
|
187
|
+
const ownership = await (0, session_ownership_1.checkSessionOwnership)(sessionName, worktreePath);
|
|
188
|
+
if (ownership.verdict !== 'owned')
|
|
189
|
+
return false;
|
|
177
190
|
// Issue #565: Clear prompt dedup cache on session kill
|
|
178
191
|
const pollerKey = `${worktreeId}:${cliToolId}`;
|
|
179
192
|
(0, response_poller_1.clearPromptHashCache)(pollerKey);
|
|
180
|
-
const sessionName = tool.getSessionName(worktreeId);
|
|
181
193
|
return (0, tmux_1.killSession)(sessionName);
|
|
182
194
|
}
|
|
183
195
|
catch {
|
|
184
196
|
return false;
|
|
185
197
|
}
|
|
186
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* A {@link KillSessionFn} that kills through {@link killWorktreeSession} with
|
|
201
|
+
* each worktree's pre-deletion path (Issue #2865). A worktree with no known
|
|
202
|
+
* path cannot be vouched for, so nothing is killed for it.
|
|
203
|
+
*
|
|
204
|
+
* @param pathById - worktree ID -> path, captured before the rows are deleted
|
|
205
|
+
*/
|
|
206
|
+
function createOwnedSessionKiller(pathById) {
|
|
207
|
+
return async (worktreeId, cliToolId) => {
|
|
208
|
+
const worktreePath = pathById.get(worktreeId);
|
|
209
|
+
if (worktreePath === undefined)
|
|
210
|
+
return false;
|
|
211
|
+
return killWorktreeSession(worktreeId, cliToolId, worktreePath);
|
|
212
|
+
};
|
|
213
|
+
}
|
|
187
214
|
/**
|
|
188
215
|
* Clean up all global assistant sessions.
|
|
189
216
|
* Issue #649: Kill any orphaned mcbd-{cli_tool_id}-__global__ tmux sessions
|
|
@@ -233,6 +260,9 @@ async function cleanupGlobalSessions() {
|
|
|
233
260
|
* @returns SyncResult and sanitized cleanupWarnings
|
|
234
261
|
*/
|
|
235
262
|
async function syncWorktreesAndCleanup(db, worktrees) {
|
|
263
|
+
// Issue #2865: the rows sync deletes are gone afterwards, and their paths are
|
|
264
|
+
// what decides which sessions are this server's to kill.
|
|
265
|
+
const pathById = new Map((0, worktree_db_1.getAllWorktreePathIds)(db).map((row) => [row.id, row.path]));
|
|
236
266
|
const syncResult = (0, worktrees_1.syncWorktreesToDB)(db, worktrees);
|
|
237
267
|
// Issue #649: Clean up orphaned global assistant sessions
|
|
238
268
|
try {
|
|
@@ -244,7 +274,7 @@ async function syncWorktreesAndCleanup(db, worktrees) {
|
|
|
244
274
|
let cleanupWarnings = [];
|
|
245
275
|
if (syncResult.deletedIds.length > 0) {
|
|
246
276
|
try {
|
|
247
|
-
const cleanupResult = await cleanupMultipleWorktrees(syncResult.deletedIds,
|
|
277
|
+
const cleanupResult = await cleanupMultipleWorktrees(syncResult.deletedIds, createOwnedSessionKiller(pathById));
|
|
248
278
|
if (cleanupResult.warnings.length > 0) {
|
|
249
279
|
// SEC-MF-001: Log detailed warnings server-side only
|
|
250
280
|
logger.warn('sync:cleanup-warnings', { warnings: cleanupResult.warnings });
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Legacy tmux session adoption table (Issue #2866).
|
|
4
|
+
*
|
|
5
|
+
* Once the server has a session-name namespace, a session is named
|
|
6
|
+
* `mcbd-{ns}-{cli}-{worktreeId}[-{suffix}]`. Sessions the same server started
|
|
7
|
+
* before that (`mcbd-{cli}-{worktreeId}[-{suffix}]`) are NOT renamed — a rename
|
|
8
|
+
* would lose them for an older CommandMate running beside this one, or after a
|
|
9
|
+
* downgrade. Instead `adoptLegacySessions` records "the new-format name X is
|
|
10
|
+
* served by the legacy session Y", and `resolveSessionName` returns Y for X
|
|
11
|
+
* while the entry lives.
|
|
12
|
+
*
|
|
13
|
+
* An entry is dropped when its legacy session is killed or found gone
|
|
14
|
+
* (`tmux.ts` `killSession` / `hasSession`), so the next start uses the
|
|
15
|
+
* new-format name.
|
|
16
|
+
*
|
|
17
|
+
* Pure (no `@/` alias, no child_process): `cli-tools/session-name.ts` reads it,
|
|
18
|
+
* and that module is part of the CLI bundle (`tsconfig.cli.json` has no paths).
|
|
19
|
+
*
|
|
20
|
+
* Kept on `globalThis` rather than in module scope: under `next start` this
|
|
21
|
+
* module is evaluated once in the custom server's graph and again in each Next
|
|
22
|
+
* route bundle (see `tui-accumulator.ts`, Issue #2223), and the table is filled
|
|
23
|
+
* by the server at startup but read by the routes.
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerLegacyAlias = registerLegacyAlias;
|
|
27
|
+
exports.lookupLegacyAlias = lookupLegacyAlias;
|
|
28
|
+
exports.dropLegacyAliasByLegacyName = dropLegacyAliasByLegacyName;
|
|
29
|
+
exports.clearLegacyAliasesForTests = clearLegacyAliasesForTests;
|
|
30
|
+
function aliases() {
|
|
31
|
+
return (globalThis.__cmLegacySessionAliases ??= new Map());
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Serve the new-format session name `newName` with the existing legacy session
|
|
35
|
+
* `legacyName`.
|
|
36
|
+
*/
|
|
37
|
+
function registerLegacyAlias(newName, legacyName) {
|
|
38
|
+
aliases().set(newName, legacyName);
|
|
39
|
+
}
|
|
40
|
+
/** The legacy session name adopted for `newName`, if any. */
|
|
41
|
+
function lookupLegacyAlias(newName) {
|
|
42
|
+
return aliases().get(newName);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Forget every adoption served by the legacy session `legacyName`. A no-op for
|
|
46
|
+
* a name that is not an adopted legacy session.
|
|
47
|
+
*
|
|
48
|
+
* @returns true when an entry was removed
|
|
49
|
+
*/
|
|
50
|
+
function dropLegacyAliasByLegacyName(legacyName) {
|
|
51
|
+
const table = aliases();
|
|
52
|
+
let dropped = false;
|
|
53
|
+
for (const [newName, legacy] of table) {
|
|
54
|
+
if (legacy === legacyName) {
|
|
55
|
+
table.delete(newName);
|
|
56
|
+
dropped = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return dropped;
|
|
60
|
+
}
|
|
61
|
+
/** Test-only: forget every adoption. */
|
|
62
|
+
function clearLegacyAliasesForTests() {
|
|
63
|
+
aliases().clear();
|
|
64
|
+
}
|
|
@@ -114,12 +114,20 @@ tmux set-option -u -t "=$SESSION:" @cm_delegated 2>/dev/null || true
|
|
|
114
114
|
* `CM_LIVE_ATTACH_HOOK=on`.
|
|
115
115
|
*
|
|
116
116
|
* claude-only by session-name prefix, and that is not a shortcut. The session
|
|
117
|
-
* name is `mcbd-<tool>-<worktree
|
|
118
|
-
*
|
|
119
|
-
*
|
|
117
|
+
* name is `mcbd-<ns>-<tool>-<worktree>` or the legacy `mcbd-<tool>-<worktree>`
|
|
118
|
+
* (Issue #2866), no other CLI tool id begins with `claude`, and the script has
|
|
119
|
+
* nothing but the name to go on — a hook fires with tmux formats, not with
|
|
120
|
+
* CommandMate's roster. The namespace is matched as exactly eight hex digits
|
|
121
|
+
* (Issue #2867), not `*`: `mcbd-*-claude-*` would also match a legacy codex
|
|
122
|
+
* session of a worktree whose id starts with `claude-`. Widening it means widening
|
|
120
123
|
* `LIVE_ATTACH_TOOLS` too, and that needs the per-tool re-measurement
|
|
121
124
|
* Issue #2317 puts out of scope.
|
|
122
125
|
*/
|
|
126
|
+
/**
|
|
127
|
+
* A session-name namespace as a shell `case` glob: eight hex digits, the shape
|
|
128
|
+
* `SESSION_NAMESPACE_PATTERN` accepts (Issue #2866 / #2867).
|
|
129
|
+
*/
|
|
130
|
+
const NAMESPACE_GLOB = '[0-9a-f]'.repeat(8);
|
|
123
131
|
exports.LIVE_DELEGATE_SCRIPT = `#!/bin/sh
|
|
124
132
|
# CommandMate live-attach delegate (Issue #2317) — generated file, edits are overwritten.
|
|
125
133
|
#
|
|
@@ -141,7 +149,7 @@ SESSION="\${1:-}"
|
|
|
141
149
|
CONTROL_MODE="\${2:-0}"
|
|
142
150
|
|
|
143
151
|
case "$SESSION" in
|
|
144
|
-
${tmux_session_surface_1.MCBD_SESSION_PREFIX}claude-*) ;;
|
|
152
|
+
${tmux_session_surface_1.MCBD_SESSION_PREFIX}claude-*|${tmux_session_surface_1.MCBD_SESSION_PREFIX}${NAMESPACE_GLOB}-claude-*) ;;
|
|
145
153
|
*) exit 0 ;;
|
|
146
154
|
esac
|
|
147
155
|
|