@jlongo78/agent-spaces 0.9.6 → 0.9.8
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/standalone/.claude/settings.local.json +68 -0
- package/.next/standalone/.claude/spaces-env.json +1 -0
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +19 -19
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.html +1 -1
- package/.next/standalone/.next/server/app/admin/users.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.html +1 -1
- package/.next/standalone/.next/server/app/analytics.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js +1 -1
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/export/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/settings/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/timeline/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/usage/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/handshake/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/search/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/search/route.js +2 -2
- package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js +2 -2
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tags/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/whisper/config/route.js +1 -1
- package/.next/standalone/.next/server/app/api/whisper/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/whisper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wizard/chart/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js +7 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/wizard/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/wizard/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/route.js +2 -2
- package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/cortex.html +1 -1
- package/.next/standalone/.next/server/app/cortex.rsc +3 -3
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login.html +1 -1
- package/.next/standalone/.next/server/app/login.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects.html +1 -1
- package/.next/standalone/.next/server/app/m/projects.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions.html +1 -1
- package/.next/standalone/.next/server/app/m/sessions.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/settings.html +1 -1
- package/.next/standalone/.next/server/app/m/settings.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/terminal.html +1 -1
- package/.next/standalone/.next/server/app/m/terminal.rsc +3 -3
- package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m.html +1 -1
- package/.next/standalone/.next/server/app/m.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.html +1 -1
- package/.next/standalone/.next/server/app/network.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.html +1 -1
- package/.next/standalone/.next/server/app/projects.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.html +1 -1
- package/.next/standalone/.next/server/app/sessions.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.html +1 -1
- package/.next/standalone/.next/server/app/terminal.rsc +3 -3
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr/page/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/app/vr/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/vr.html +1 -1
- package/.next/standalone/.next/server/app/vr.rsc +3 -3
- package/.next/standalone/.next/server/app/vr.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/vr.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/vr.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/vr/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/vr.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.html +1 -1
- package/.next/standalone/.next/server/app/workspaces.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00e90fc6._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01ab8675._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03974f05._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bb331da9._.js → [root-of-the-server]__046c9b91._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04ae6bf0._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__056fa416._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ac4ea3f._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8e64cb._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d95165f0._.js → [root-of-the-server]__0facd39e._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10bc76a3._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__9d68157b._.js → [root-of-the-server]__115f3934._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__11f155f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__160e7c73._.js +22 -33
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91e23c96._.js → [root-of-the-server]__17a3b966._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__277d9445._.js → [root-of-the-server]__17d3a2b2._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a86c055._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20b5e9c4._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__28d6fbd8._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04f04898._.js → [root-of-the-server]__2a3f866b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a95bb38b._.js → [root-of-the-server]__316617e7._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__32ad8f71._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6fe5e6c8._.js → [root-of-the-server]__35457394._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1f054c65._.js → [root-of-the-server]__35de78e6._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3685ffcb._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__614458b7._.js → [root-of-the-server]__38954988._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__426ad936._.js +106 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4985c034._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c6ce9ed._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5cebe58a._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5d5e4789._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__65676930._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__67cab326._.js +58 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__698c6f01._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c64af29._.js +131 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__73aed9f5._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac84b704._.js → [root-of-the-server]__79b6a9bb._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__e56abacf._.js → [root-of-the-server]__7db704c6._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__812ca02b._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__821f50fa._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8716b86e._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__884ef754._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__88cdbd68._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89d9aba9._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d536cb5._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8df8c5d1._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2d7a454e._.js → [root-of-the-server]__8f2ccc41._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a2c5fc5._.js → [root-of-the-server]__95c9d682._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e5d7774._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cc6e2885._.js → [root-of-the-server]__9edcff87._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a049dfc2._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a5b4bb9a._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__929ea03a._.js → [root-of-the-server]__a83262a1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9cd1240._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d7f822._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad08c221._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad585f2f._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__afcb8f7d._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__bc250d43._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__bce2a6e7._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c011bf91._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0ac2895._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c130a00c._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c37d6380._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cae392eb._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cc2616bb._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d501fa9b._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d59c6c15._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5c1db32._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5d92527._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__dba60c86._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2384f98e._.js → [root-of-the-server]__de14b9ae._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e10643d1._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__00fdfbda._.js → [root-of-the-server]__e2a996e5._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d903941._.js → [root-of-the-server]__e3477417._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__32dc5513._.js → [root-of-the-server]__e4db362e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__49e42a3a._.js → [root-of-the-server]__e4e70b86._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac39ecc7._.js → [root-of-the-server]__e54925af._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e8edc5b0._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eafd040b._.js → [root-of-the-server]__eab4d83b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ead29015._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__194955d4._.js → [root-of-the-server]__f056fd83._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f0e99572._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fe1e16d0._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff9cd277._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ffaea2ce._.js +98 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wizard_chart_route_actions_888e2ec1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2cffc362._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__f1050870._.js → [root-of-the-server]__47c97637._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_17b946fd._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_2a1d79e7._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_5c3c4cfa._.js +7 -5
- package/.next/standalone/.next/server/chunks/ssr/_ba432382._.js +7 -5
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_cortex_page_tsx_0f33d8b3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +4 -4
- package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__90eeddae._.js +1 -1
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0852575eb90c1e8d.js +85 -0
- package/.next/standalone/.next/static/chunks/{74d0ac0b1d0a1b79.js → 10b00b4f66102dcf.js} +1 -1
- package/.next/standalone/.next/static/chunks/{16eb77953dee9ea3.js → 19c71a8376c23c58.js} +1 -1
- package/.next/standalone/.next/static/chunks/2b769d1597e4fc1c.css +3 -0
- package/.next/standalone/.next/static/chunks/{3e91fc608659c524.js → 350271fe79509caf.js} +1 -1
- package/.next/standalone/.next/static/chunks/{70423c7afd8abf5f.js → 597847c22200c212.js} +1 -1
- package/.next/standalone/.next/static/chunks/{fb0abd1933b2b2e1.js → 7f6a14f1849fa94d.js} +1 -1
- package/.next/standalone/.next/static/chunks/{7ecd9bbb0ce4d68a.js → b7c8fe9b7275a84f.js} +1 -1
- package/.next/standalone/.next/static/chunks/{6245135a7afb8c7b.js → c9b10fc55516d142.js} +8 -6
- package/.next/standalone/.next/static/chunks/d0065f48eab94944.js +1 -0
- package/.next/standalone/.next/static/chunks/{180c1b9ff31b979f.js → f7b34c807badf95d.js} +8 -6
- package/.next/standalone/.spaces/cortex-context.md +50 -144
- package/.next/standalone/LICENSE +661 -661
- package/.next/standalone/NOTICE +5 -5
- package/.next/standalone/README.md +131 -131
- package/.next/standalone/bin/cortex-hook.js +79 -79
- package/.next/standalone/bin/cortex-hook.sh +62 -62
- package/.next/standalone/bin/cortex-learn-hook.js +138 -138
- package/.next/standalone/bin/cortex-mcp.js +60 -60
- package/.next/standalone/bin/cortex-pi-extension.ts +170 -170
- package/.next/standalone/bin/fix-standalone-externals.js +79 -79
- package/.next/standalone/bin/lib/auto-setup.js +110 -110
- package/.next/standalone/bin/mdns-service.js +171 -171
- package/.next/standalone/bin/postinstall.js +35 -35
- package/.next/standalone/bin/setup-admin.js +195 -195
- package/.next/standalone/bin/spaces-dev.js +247 -247
- package/.next/standalone/bin/spaces-install.js +660 -660
- package/.next/standalone/bin/spaces-postinstall.js +50 -50
- package/.next/standalone/bin/spaces-reset-totp.js +50 -50
- package/.next/standalone/bin/spaces-service.js +1046 -1046
- package/.next/standalone/bin/spaces-setup.js +253 -253
- package/.next/standalone/bin/spaces.js +808 -805
- package/.next/standalone/bin/ssh-auth-keys.sh +68 -68
- package/.next/standalone/bin/terminal-server.js +2819 -2781
- package/.next/standalone/cortex-hook-debug.log +57 -23
- package/.next/standalone/docker-compose.yml +28 -28
- package/.next/standalone/docs/architecture.md +387 -387
- package/.next/standalone/docs/cortex-integration-reference.md +268 -268
- package/.next/standalone/docs/cortex.md +293 -293
- package/.next/standalone/docs/getting-started.md +96 -96
- package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-design.md +133 -133
- package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-plan.md +959 -959
- package/.next/standalone/docs/plans/2026-03-02-security-audit.md +229 -229
- package/.next/standalone/docs/plans/2026-03-07-service-command-design.md +146 -146
- package/.next/standalone/docs/plans/2026-03-07-service-command-plan.md +254 -254
- package/.next/standalone/docs/server-install.md +564 -564
- package/.next/standalone/docs/social-card.html +150 -150
- package/.next/standalone/docs/superpowers/plans/2026-03-12-spaces-cortex.md +5270 -5270
- package/.next/standalone/docs/superpowers/plans/2026-03-13-cortex-wiring.md +1387 -1387
- package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-entity-graph.md +1923 -1923
- package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-knowledge-evolution.md +1113 -1113
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-boundary-engine.md +853 -853
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-context-engine.md +1274 -1274
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-signal-ingestion.md +933 -933
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-lobes.md +1080 -1080
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-gravity-system.md +768 -768
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-ui.md +1108 -1108
- package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -1846
- package/.next/standalone/docs/superpowers/plans/2026-03-19-vr-phase1-shell.md +1639 -1639
- package/.next/standalone/docs/superpowers/plans/2026-03-27-dockview-pane-layout.md +98 -98
- package/.next/standalone/docs/superpowers/specs/2026-03-11-universe-view-design.md +320 -320
- package/.next/standalone/docs/superpowers/specs/2026-03-12-spaces-brain-design.md +720 -720
- package/.next/standalone/docs/superpowers/specs/2026-03-13-cortex-wiring-design.md +268 -268
- package/.next/standalone/docs/superpowers/specs/2026-03-14-cortex-v2-design.md +623 -623
- package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-lobes-design.md +263 -263
- package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-v2-ui-design.md +240 -240
- package/.next/standalone/docs/superpowers/specs/2026-03-16-pane-ux-design.md +77 -77
- package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -341
- package/.next/standalone/docs/superpowers/specs/2026-03-19-vr-phase1-shell-design.md +288 -288
- package/.next/standalone/docs/superpowers/specs/2026-03-27-pane-diff-review-and-project-wizard-design.md +322 -322
- package/.next/standalone/docs/tiers.md +104 -104
- package/.next/standalone/eslint.config.mjs +18 -18
- package/.next/standalone/next.config.ts +20 -20
- package/.next/standalone/nginx.conf +53 -53
- package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
- package/.next/standalone/node_modules/@img/{sharp-linux-x64 → sharp-win32-x64}/package.json +39 -46
- package/.next/standalone/package-lock.json +14985 -14986
- package/.next/standalone/package.json +111 -111
- package/.next/standalone/postcss.config.mjs +7 -7
- package/.next/standalone/scripts/rebuild.cmd +65 -65
- package/.next/standalone/scripts/rebuild.sh +59 -59
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/app/(desktop)/admin/analytics/page.tsx +266 -266
- package/.next/standalone/src/app/(desktop)/admin/users/page.tsx +399 -399
- package/.next/standalone/src/app/(desktop)/analytics/page.tsx +166 -166
- package/.next/standalone/src/app/(desktop)/cortex/page.tsx +81 -81
- package/.next/standalone/src/app/(desktop)/dashboard-client.tsx +56 -56
- package/.next/standalone/src/app/(desktop)/layout.tsx +18 -18
- package/.next/standalone/src/app/(desktop)/network/page.tsx +137 -137
- package/.next/standalone/src/app/(desktop)/page.tsx +17 -17
- package/.next/standalone/src/app/(desktop)/projects/page.tsx +68 -68
- package/.next/standalone/src/app/(desktop)/sessions/[id]/page.tsx +519 -519
- package/.next/standalone/src/app/(desktop)/sessions/page.tsx +145 -145
- package/.next/standalone/src/app/(desktop)/settings/page.tsx +446 -446
- package/.next/standalone/src/app/(desktop)/terminal/layout.tsx +7 -7
- package/.next/standalone/src/app/(desktop)/terminal/page.tsx +1330 -1291
- package/.next/standalone/src/app/(desktop)/terminal/pane/[id]/page.tsx +211 -211
- package/.next/standalone/src/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.tsx +252 -252
- package/.next/standalone/src/app/(desktop)/workspaces/page.tsx +12 -12
- package/.next/standalone/src/app/api/admin/analytics/route.ts +10 -10
- package/.next/standalone/src/app/api/admin/users/[id]/route.ts +20 -20
- package/.next/standalone/src/app/api/admin/users/route.ts +15 -15
- package/.next/standalone/src/app/api/analytics/overview/route.ts +80 -80
- package/.next/standalone/src/app/api/auth/login/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/logout/route.ts +9 -9
- package/.next/standalone/src/app/api/auth/me/route.ts +22 -22
- package/.next/standalone/src/app/api/auth/totp/setup/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/totp/status/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/totp/verify/route.ts +10 -10
- package/.next/standalone/src/app/api/benchmark/lobes/route.ts +16 -16
- package/.next/standalone/src/app/api/benchmark/run/route.ts +113 -92
- package/.next/standalone/src/app/api/benchmark/runs/[id]/route.ts +26 -26
- package/.next/standalone/src/app/api/benchmark/runs/route.ts +16 -16
- package/.next/standalone/src/app/api/benchmark/status/route.ts +35 -35
- package/.next/standalone/src/app/api/bulk/route.ts +34 -34
- package/.next/standalone/src/app/api/chat/route.ts +85 -85
- package/.next/standalone/src/app/api/config/route.ts +30 -30
- package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
- package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -27
- package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -23
- package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -23
- package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -29
- package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -23
- package/.next/standalone/src/app/api/cortex/export/route.ts +40 -40
- package/.next/standalone/src/app/api/cortex/federation/pending/route.ts +20 -20
- package/.next/standalone/src/app/api/cortex/federation/resolve/route.ts +43 -43
- package/.next/standalone/src/app/api/cortex/federation/search/route.ts +35 -35
- package/.next/standalone/src/app/api/cortex/federation/teach/route.ts +76 -76
- package/.next/standalone/src/app/api/cortex/graph/edges/route.ts +112 -112
- package/.next/standalone/src/app/api/cortex/graph/entities/[id]/route.ts +73 -73
- package/.next/standalone/src/app/api/cortex/graph/entities/route.ts +75 -75
- package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +203 -203
- package/.next/standalone/src/app/api/cortex/import/route.ts +75 -75
- package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
- package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +39 -39
- package/.next/standalone/src/app/api/cortex/ingest/status/route.ts +15 -15
- package/.next/standalone/src/app/api/cortex/knowledge/[id]/route.ts +91 -91
- package/.next/standalone/src/app/api/cortex/knowledge/route.ts +97 -97
- package/.next/standalone/src/app/api/cortex/lobes/[id]/route.ts +67 -67
- package/.next/standalone/src/app/api/cortex/lobes/route.ts +22 -22
- package/.next/standalone/src/app/api/cortex/lobes/share/route.ts +80 -80
- package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -43
- package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -46
- package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
- package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +8 -8
- package/.next/standalone/src/app/api/cortex/search/route.ts +57 -45
- package/.next/standalone/src/app/api/cortex/settings/route.ts +35 -35
- package/.next/standalone/src/app/api/cortex/status/route.ts +169 -169
- package/.next/standalone/src/app/api/cortex/timeline/route.ts +42 -42
- package/.next/standalone/src/app/api/cortex/usage/route.ts +31 -31
- package/.next/standalone/src/app/api/cortex/workspace/[id]/context/route.ts +41 -41
- package/.next/standalone/src/app/api/events/route.ts +40 -40
- package/.next/standalone/src/app/api/files/route.ts +187 -187
- package/.next/standalone/src/app/api/folders/route.ts +107 -97
- package/.next/standalone/src/app/api/network/connect-callback/route.ts +11 -11
- package/.next/standalone/src/app/api/network/connect-request/[id]/route.ts +11 -11
- package/.next/standalone/src/app/api/network/connect-request/route.ts +17 -17
- package/.next/standalone/src/app/api/network/discovered/route.ts +9 -9
- package/.next/standalone/src/app/api/network/handshake/route.ts +25 -25
- package/.next/standalone/src/app/api/network/health/route.ts +10 -10
- package/.next/standalone/src/app/api/network/identity/route.ts +15 -15
- package/.next/standalone/src/app/api/network/keys/[id]/route.ts +10 -10
- package/.next/standalone/src/app/api/network/keys/route.ts +15 -15
- package/.next/standalone/src/app/api/network/nodes/[id]/route.ts +15 -15
- package/.next/standalone/src/app/api/network/nodes/check/route.ts +9 -9
- package/.next/standalone/src/app/api/network/nodes/route.ts +15 -15
- package/.next/standalone/src/app/api/network/panes/[id]/route.ts +78 -62
- package/.next/standalone/src/app/api/network/panes/route.ts +61 -50
- package/.next/standalone/src/app/api/network/projects/route.ts +32 -25
- package/.next/standalone/src/app/api/network/proxy/[nodeId]/[...path]/route.ts +25 -25
- package/.next/standalone/src/app/api/network/search/route.ts +45 -38
- package/.next/standalone/src/app/api/network/sessions/[id]/messages/route.ts +43 -36
- package/.next/standalone/src/app/api/network/sessions/[id]/route.ts +41 -34
- package/.next/standalone/src/app/api/network/sessions/route.ts +50 -43
- package/.next/standalone/src/app/api/network/terminal/token/route.ts +10 -10
- package/.next/standalone/src/app/api/network/workspaces/[id]/route.ts +80 -71
- package/.next/standalone/src/app/api/network/workspaces/route.ts +87 -85
- package/.next/standalone/src/app/api/panes/[id]/diff/route.ts +121 -121
- package/.next/standalone/src/app/api/panes/[id]/route.ts +60 -60
- package/.next/standalone/src/app/api/panes/route.ts +39 -39
- package/.next/standalone/src/app/api/projects/route.ts +13 -13
- package/.next/standalone/src/app/api/proxy/models/[modelId]/[...path]/route.ts +80 -80
- package/.next/standalone/src/app/api/proxy/models/[modelId]/status/route.ts +33 -33
- package/.next/standalone/src/app/api/search/route.ts +47 -47
- package/.next/standalone/src/app/api/sessions/[id]/chat/route.ts +120 -120
- package/.next/standalone/src/app/api/sessions/[id]/messages/route.ts +34 -34
- package/.next/standalone/src/app/api/sessions/[id]/route.ts +73 -73
- package/.next/standalone/src/app/api/sessions/route.ts +64 -64
- package/.next/standalone/src/app/api/sync/route.ts +24 -24
- package/.next/standalone/src/app/api/tags/route.ts +35 -35
- package/.next/standalone/src/app/api/tier/route.ts +16 -16
- package/.next/standalone/src/app/api/updates/route.ts +65 -65
- package/.next/standalone/src/app/api/whisper/config/route.ts +50 -42
- package/.next/standalone/src/app/api/whisper/route.ts +91 -91
- package/.next/standalone/src/app/api/wizard/chart/route.ts +129 -0
- package/.next/standalone/src/app/api/wizard/chat/route.ts +113 -113
- package/.next/standalone/src/app/api/workspaces/[id]/context/[key]/route.ts +39 -39
- package/.next/standalone/src/app/api/workspaces/[id]/context/route.ts +28 -28
- package/.next/standalone/src/app/api/workspaces/[id]/messages/[msgId]/route.ts +17 -17
- package/.next/standalone/src/app/api/workspaces/[id]/messages/route.ts +39 -39
- package/.next/standalone/src/app/api/workspaces/[id]/route.ts +47 -47
- package/.next/standalone/src/app/api/workspaces/[id]/sessions/route.ts +62 -62
- package/.next/standalone/src/app/api/workspaces/route.ts +79 -79
- package/.next/standalone/src/app/globals.css +88 -88
- package/.next/standalone/src/app/layout.tsx +33 -33
- package/.next/standalone/src/app/login/layout.tsx +7 -7
- package/.next/standalone/src/app/login/page.tsx +315 -315
- package/.next/standalone/src/app/m/layout.tsx +16 -16
- package/.next/standalone/src/app/m/page.tsx +118 -118
- package/.next/standalone/src/app/m/projects/page.tsx +64 -64
- package/.next/standalone/src/app/m/sessions/[id]/page.tsx +168 -168
- package/.next/standalone/src/app/m/sessions/page.tsx +177 -177
- package/.next/standalone/src/app/m/settings/page.tsx +230 -230
- package/.next/standalone/src/app/m/terminal/page.tsx +413 -413
- package/.next/standalone/src/app/vr/page.tsx +21 -21
- package/.next/standalone/src/app/vr/vr-app.tsx +163 -163
- package/.next/standalone/src/app/vr/vr-controls.tsx +139 -139
- package/.next/standalone/src/app/vr/vr-door.tsx +82 -82
- package/.next/standalone/src/app/vr/vr-environment.tsx +71 -71
- package/.next/standalone/src/app/vr/vr-gaze.tsx +89 -89
- package/.next/standalone/src/app/vr/vr-layout.ts +49 -49
- package/.next/standalone/src/app/vr/vr-lobby.tsx +97 -97
- package/.next/standalone/src/app/vr/vr-pane.tsx +195 -195
- package/.next/standalone/src/app/vr/vr-room.tsx +79 -79
- package/.next/standalone/src/app/vr/vr-terminal.tsx +303 -303
- package/.next/standalone/src/components/auth/totp-gate.tsx +183 -183
- package/.next/standalone/src/components/bus/activity-panel.tsx +261 -261
- package/.next/standalone/src/components/common/color-picker.tsx +35 -35
- package/.next/standalone/src/components/common/dev-directory-picker.tsx +339 -339
- package/.next/standalone/src/components/common/folder-picker.tsx +200 -200
- package/.next/standalone/src/components/common/tag-picker.tsx +190 -190
- package/.next/standalone/src/components/common/workspace-picker.tsx +113 -113
- package/.next/standalone/src/components/cortex/benchmark-tab.tsx +894 -880
- package/.next/standalone/src/components/cortex/constants.ts +29 -29
- package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -304
- package/.next/standalone/src/components/cortex/cortex-indicator.tsx +44 -44
- package/.next/standalone/src/components/cortex/cortex-panel.tsx +140 -140
- package/.next/standalone/src/components/cortex/cortex-settings.tsx +280 -280
- package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -810
- package/.next/standalone/src/components/cortex/entity-detail.tsx +101 -101
- package/.next/standalone/src/components/cortex/entity-graph.tsx +382 -382
- package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -212
- package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
- package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -109
- package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -158
- package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -215
- package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -126
- package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -113
- package/.next/standalone/src/components/dashboard/activity-chart.tsx +41 -41
- package/.next/standalone/src/components/dashboard/model-usage-chart.tsx +61 -61
- package/.next/standalone/src/components/dashboard/recent-sessions.tsx +68 -68
- package/.next/standalone/src/components/dashboard/stats-cards.tsx +36 -36
- package/.next/standalone/src/components/files/file-explorer.tsx +703 -703
- package/.next/standalone/src/components/layout/providers.tsx +38 -38
- package/.next/standalone/src/components/layout/sidebar.tsx +170 -170
- package/.next/standalone/src/components/layout/tier-provider.tsx +53 -53
- package/.next/standalone/src/components/layout/update-banner.tsx +92 -92
- package/.next/standalone/src/components/mobile/bottom-nav.tsx +46 -46
- package/.next/standalone/src/components/mobile/immersive-voice-button.tsx +123 -123
- package/.next/standalone/src/components/mobile/mobile-chat-input.tsx +244 -244
- package/.next/standalone/src/components/mobile/mobile-header.tsx +44 -44
- package/.next/standalone/src/components/mobile/mobile-session-card.tsx +56 -56
- package/.next/standalone/src/components/mobile/mobile-terminal-input.tsx +74 -74
- package/.next/standalone/src/components/mobile/mobile-terminal-pane.tsx +302 -302
- package/.next/standalone/src/components/mobile/mobile-terminal-toolbar.tsx +76 -76
- package/.next/standalone/src/components/mobile/pull-to-refresh.tsx +82 -82
- package/.next/standalone/src/components/mobile/voice-input.tsx +53 -53
- package/.next/standalone/src/components/network/api-key-list.tsx +190 -190
- package/.next/standalone/src/components/network/connection-requests.tsx +94 -94
- package/.next/standalone/src/components/network/node-add-dialog.tsx +131 -131
- package/.next/standalone/src/components/network/node-badge.tsx +26 -26
- package/.next/standalone/src/components/network/node-list.tsx +207 -207
- package/.next/standalone/src/components/network/node-selector.tsx +49 -49
- package/.next/standalone/src/components/sessions/session-filters.tsx +116 -116
- package/.next/standalone/src/components/sessions/session-list.tsx +485 -485
- package/.next/standalone/src/components/terminal/pane-diff-panel.tsx +179 -179
- package/.next/standalone/src/components/terminal/terminal-pane.tsx +1530 -1464
- package/.next/standalone/src/components/viewer/chat-input.tsx +275 -275
- package/.next/standalone/src/components/viewer/message-renderer.tsx +551 -551
- package/.next/standalone/src/components/wizard/chart-wizard.tsx +405 -0
- package/.next/standalone/src/components/wizard/project-wizard.tsx +153 -153
- package/.next/standalone/src/components/wizard/wizard-chat.tsx +99 -99
- package/.next/standalone/src/components/wizard/wizard-plan-summary.tsx +103 -103
- package/.next/standalone/src/components/wizard/wizard-review.tsx +225 -225
- package/.next/standalone/src/components/workspace/universe-cluster.tsx +131 -131
- package/.next/standalone/src/components/workspace/universe-orb.tsx +128 -128
- package/.next/standalone/src/components/workspace/universe-types.ts +22 -22
- package/.next/standalone/src/components/workspace/universe-utils.ts +11 -11
- package/.next/standalone/src/components/workspace/universe-view.tsx +397 -397
- package/.next/standalone/src/components/workspace/workspace-chooser.tsx +634 -634
- package/.next/standalone/src/hooks/use-benchmark.ts +72 -71
- package/.next/standalone/src/hooks/use-bus.ts +147 -147
- package/.next/standalone/src/hooks/use-idle-detection.ts +79 -79
- package/.next/standalone/src/hooks/use-network.ts +229 -229
- package/.next/standalone/src/hooks/use-sessions.ts +437 -437
- package/.next/standalone/src/hooks/use-speech-recognition.ts +114 -113
- package/.next/standalone/src/hooks/use-sse.ts +35 -35
- package/.next/standalone/src/hooks/use-tier.ts +39 -39
- package/.next/standalone/src/lib/agents.ts +97 -97
- package/.next/standalone/src/lib/aider/parser.ts +111 -111
- package/.next/standalone/src/lib/api.ts +19 -19
- package/.next/standalone/src/lib/auth.ts +47 -47
- package/.next/standalone/src/lib/claude/parser.ts +212 -212
- package/.next/standalone/src/lib/claude/stats.ts +204 -204
- package/.next/standalone/src/lib/codex/parser.test.ts +111 -111
- package/.next/standalone/src/lib/codex/parser.ts +287 -287
- package/.next/standalone/src/lib/config.ts +132 -132
- package/.next/standalone/src/lib/cortex/benchmark.ts +83 -67
- package/.next/standalone/src/lib/cortex/config.ts +42 -42
- package/.next/standalone/src/lib/cortex/debug.ts +10 -10
- package/.next/standalone/src/lib/cortex/distillation/usage-store.ts +18 -18
- package/.next/standalone/src/lib/cortex/graph/resolver.ts +10 -10
- package/.next/standalone/src/lib/cortex/graph/types.ts +22 -22
- package/.next/standalone/src/lib/cortex/index.ts +109 -56
- package/.next/standalone/src/lib/cortex/ingestion/bootstrap.ts +14 -14
- package/.next/standalone/src/lib/cortex/knowledge/compat.ts +14 -14
- package/.next/standalone/src/lib/cortex/knowledge/contradiction.ts +10 -10
- package/.next/standalone/src/lib/cortex/knowledge/types.ts +67 -67
- package/.next/standalone/src/lib/cortex/lobes/config.ts +16 -16
- package/.next/standalone/src/lib/cortex/lobes/resolver.ts +8 -8
- package/.next/standalone/src/lib/cortex/lobes/shares.ts +14 -14
- package/.next/standalone/src/lib/cortex/mcp/server.ts +12 -12
- package/.next/standalone/src/lib/cortex/portability/exporter.ts +6 -6
- package/.next/standalone/src/lib/cortex/portability/importer.ts +10 -10
- package/.next/standalone/src/lib/cortex/retrieval/context-engine.ts +10 -10
- package/.next/standalone/src/lib/cortex/types.ts +39 -39
- package/.next/standalone/src/lib/cost-calculator.ts +48 -48
- package/.next/standalone/src/lib/db/init.ts +71 -71
- package/.next/standalone/src/lib/db/queries.ts +740 -827
- package/.next/standalone/src/lib/db/schema.ts +206 -206
- package/.next/standalone/src/lib/events/sse.ts +36 -36
- package/.next/standalone/src/lib/forge/parser.ts +52 -52
- package/.next/standalone/src/lib/gemini/parser.ts +258 -258
- package/.next/standalone/src/lib/license.ts +56 -56
- package/.next/standalone/src/lib/pro.ts +31 -31
- package/.next/standalone/src/lib/shell-user.ts +101 -0
- package/.next/standalone/src/lib/sync/indexer.ts +504 -504
- package/.next/standalone/src/lib/sync/watcher.ts +64 -64
- package/.next/standalone/src/lib/teams.ts +31 -31
- package/.next/standalone/src/lib/telemetry.ts +75 -75
- package/.next/standalone/src/lib/terminal/server.ts +188 -188
- package/.next/standalone/src/lib/tier.ts +38 -38
- package/.next/standalone/src/lib/utils.ts +72 -72
- package/.next/standalone/src/lib/vms/manager.ts +121 -121
- package/.next/standalone/src/middleware.ts +133 -133
- package/.next/standalone/src/types/claude.ts +208 -208
- package/.next/standalone/src/types/network.ts +61 -61
- package/.next/standalone/tests/setup.ts +8 -8
- package/.next/standalone/tsconfig.json +34 -34
- package/.next/standalone/vitest.config.ts +24 -24
- package/LICENSE +661 -661
- package/README.md +131 -131
- package/bin/cortex-hook.js +79 -79
- package/bin/cortex-hook.sh +62 -62
- package/bin/cortex-learn-hook.js +138 -138
- package/bin/cortex-mcp.js +60 -60
- package/bin/cortex-pi-extension.ts +170 -170
- package/bin/fix-standalone-externals.js +79 -79
- package/bin/lib/auto-setup.js +110 -110
- package/bin/mdns-service.js +171 -171
- package/bin/postinstall.js +35 -35
- package/bin/setup-admin.js +195 -195
- package/bin/spaces-dev.js +247 -247
- package/bin/spaces-install.js +660 -660
- package/bin/spaces-postinstall.js +50 -50
- package/bin/spaces-reset-totp.js +50 -50
- package/bin/spaces-service.js +1046 -1046
- package/bin/spaces-setup.js +253 -253
- package/bin/spaces.js +808 -805
- package/bin/ssh-auth-keys.sh +68 -68
- package/bin/terminal-server.js +2819 -2781
- package/package.json +111 -111
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0903a426._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09e8ccc9._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__11c684b1._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1572d4ef._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__186cd0bb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__212760e6._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__228595ec._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__283c890f._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f300a68._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f452778._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__35f8e77e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__379fc2e9._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3b40d79f._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3d3dca2b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4d5b78d2._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__54163e52._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__563c0817._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5812f90a._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c5e87f5._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__60d15b16._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__69d315e5._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__71f29038._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__74084e07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7921aa80._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e077dd8._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7ebc4280._.js +0 -131
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__857c60bb._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__874fe565._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8e2171f7._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__95659b2d._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9679b91e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a90729a1._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad4346fa._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b0862d69._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b43306ee._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b689ff5e._.js +0 -106
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ba87daaa._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0461005._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c1deb5f3._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c8a62f42._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cabaac2b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cb027619._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf608218._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cfc1290d._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0109b9b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0125483._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d048ee6b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d12644e7._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e3cc946c._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e6fd27f8._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__efb8251e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f44c6882._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f85283de._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__feceb3e4._.js +0 -98
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__843070a6._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_e84a0c06._.js +0 -3
- package/.next/standalone/.next/static/chunks/470cade58d4eceeb.css +0 -3
- package/.next/standalone/.next/static/chunks/9d4164833c2c1fd6.js +0 -85
- package/.next/standalone/.next/static/chunks/f091f4bf8d80fd07.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
- package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_ssgManifest.js +0 -0
- /package/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-win32-x64}/versions.json +0 -0
|
@@ -1,959 +1,959 @@
|
|
|
1
|
-
# Multi-Agent Session & Project Support — Implementation Plan
|
|
2
|
-
|
|
3
|
-
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
-
|
|
5
|
-
**Goal:** Let users browse, search, tag, and resume sessions for Codex, Gemini, and Aider alongside Claude Code.
|
|
6
|
-
|
|
7
|
-
**Architecture:** Add `agent_type` column to existing sessions/projects tables. Write one parser per agent that reads its native disk format and maps into the shared `upsertProject`/`upsertSession` shape. Update agents.ts resume flags, terminal server spawn logic, watcher, and session UI with agent-type filtering.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** Next.js (App Router), SQLite (better-sqlite3), Node.js fs, TypeScript
|
|
10
|
-
|
|
11
|
-
**Design doc:** `docs/plans/2026-02-24-multi-agent-sessions-design.md`
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
### Task 1: DB Schema — add `agent_type` columns
|
|
16
|
-
|
|
17
|
-
**Files:**
|
|
18
|
-
- Modify: `src/lib/db/schema.ts:119-136` (addCol migration section)
|
|
19
|
-
|
|
20
|
-
**Step 1: Add migration columns**
|
|
21
|
-
|
|
22
|
-
In `schema.ts`, after the existing `addCol` calls (around line 136), add:
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
// Multi-agent support: add agent_type to sessions and projects
|
|
26
|
-
addCol('sessions', 'agent_type', "TEXT DEFAULT 'claude'");
|
|
27
|
-
addCol('projects', 'agent_type', "TEXT DEFAULT 'claude'");
|
|
28
|
-
addCol('projects', 'agent_path', 'TEXT');
|
|
29
|
-
|
|
30
|
-
// Copy claude_path -> agent_path for existing rows that don't have it yet
|
|
31
|
-
try { db.exec("UPDATE projects SET agent_path = claude_path WHERE agent_path IS NULL AND claude_path IS NOT NULL"); } catch { /* */ }
|
|
32
|
-
|
|
33
|
-
// Index for filtering by agent type
|
|
34
|
-
try { db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_agent_type ON sessions(agent_type)'); } catch { /* */ }
|
|
35
|
-
try { db.exec('CREATE INDEX IF NOT EXISTS idx_projects_agent_type ON projects(agent_type)'); } catch { /* */ }
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Step 2: Verify by running dev server**
|
|
39
|
-
|
|
40
|
-
Run: `npm run dev`
|
|
41
|
-
Expected: Server starts without errors. Existing sessions still appear (defaulted to `agent_type='claude'`).
|
|
42
|
-
|
|
43
|
-
**Step 3: Commit**
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
feat: add agent_type columns to sessions and projects tables
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
### Task 2: Update queries and types for agent_type
|
|
52
|
-
|
|
53
|
-
**Files:**
|
|
54
|
-
- Modify: `src/lib/db/queries.ts:7-14` (upsertProject), `src/lib/db/queries.ts:31-61` (upsertSession), `src/lib/db/queries.ts:63-73` (SessionQueryParams), `src/lib/db/queries.ts:75-161` (getSessions)
|
|
55
|
-
- Modify: `src/types/claude.ts:135-142` (Project), `src/types/claude.ts:144-164` (SessionWithMeta)
|
|
56
|
-
|
|
57
|
-
**Step 1: Update types**
|
|
58
|
-
|
|
59
|
-
In `src/types/claude.ts`, add `agentType` to `Project` (after line 139) and `SessionWithMeta` (after line 163):
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
// Project — add agentType field
|
|
63
|
-
export interface Project {
|
|
64
|
-
id: string;
|
|
65
|
-
name: string;
|
|
66
|
-
path: string;
|
|
67
|
-
claudePath: string;
|
|
68
|
-
agentType: string;
|
|
69
|
-
sessionCount: number;
|
|
70
|
-
lastActivity: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// SessionWithMeta — add agentType field
|
|
74
|
-
export interface SessionWithMeta {
|
|
75
|
-
// ... existing fields ...
|
|
76
|
-
nodeId?: string;
|
|
77
|
-
nodeName?: string;
|
|
78
|
-
agentType: string;
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Step 2: Update upsertProject**
|
|
83
|
-
|
|
84
|
-
In `src/lib/db/queries.ts`, modify `upsertProject` (line 7) to accept and store `agentType`:
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
export function upsertProject(project: { id: string; name: string; path: string; claudePath: string; agentType?: string }) {
|
|
88
|
-
const db = getDb();
|
|
89
|
-
db.prepare(`
|
|
90
|
-
INSERT INTO projects (id, name, path, claude_path, agent_path, agent_type)
|
|
91
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
92
|
-
ON CONFLICT(id) DO UPDATE SET name=excluded.name, path=excluded.path, claude_path=excluded.claude_path, agent_path=excluded.agent_path, agent_type=excluded.agent_type
|
|
93
|
-
`).run(project.id, project.name, project.path, project.claudePath, project.claudePath, project.agentType || 'claude');
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Step 3: Update upsertSession**
|
|
98
|
-
|
|
99
|
-
Modify `upsertSession` (line 31) to accept and store `agentType`:
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
export function upsertSession(session: {
|
|
103
|
-
id: string;
|
|
104
|
-
sessionId: string;
|
|
105
|
-
projectId: string;
|
|
106
|
-
firstPrompt: string;
|
|
107
|
-
summary: string;
|
|
108
|
-
messageCount: number;
|
|
109
|
-
created: string;
|
|
110
|
-
modified: string;
|
|
111
|
-
gitBranch: string;
|
|
112
|
-
projectPath: string;
|
|
113
|
-
fullPath: string;
|
|
114
|
-
agentType?: string;
|
|
115
|
-
}) {
|
|
116
|
-
const db = getDb();
|
|
117
|
-
db.prepare(`
|
|
118
|
-
INSERT INTO sessions (id, session_id, project_id, first_prompt, summary, message_count, created, modified, git_branch, project_path, full_path, agent_type)
|
|
119
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
120
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
121
|
-
first_prompt=excluded.first_prompt,
|
|
122
|
-
summary=excluded.summary,
|
|
123
|
-
message_count=excluded.message_count,
|
|
124
|
-
modified=excluded.modified,
|
|
125
|
-
git_branch=excluded.git_branch,
|
|
126
|
-
full_path=excluded.full_path,
|
|
127
|
-
agent_type=excluded.agent_type
|
|
128
|
-
`).run(
|
|
129
|
-
session.id, session.sessionId, session.projectId,
|
|
130
|
-
session.firstPrompt, session.summary, session.messageCount,
|
|
131
|
-
session.created, session.modified, session.gitBranch,
|
|
132
|
-
session.projectPath, session.fullPath, session.agentType || 'claude'
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Step 4: Add agentType to SessionQueryParams and getSessions**
|
|
138
|
-
|
|
139
|
-
In `SessionQueryParams` (line 63), add:
|
|
140
|
-
```typescript
|
|
141
|
-
agentType?: string;
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
In `getSessions` (around line 112), add the filter clause after the `search` block:
|
|
145
|
-
```typescript
|
|
146
|
-
if (params.agentType) {
|
|
147
|
-
where.push('s.agent_type = ?');
|
|
148
|
-
queryParams.push(params.agentType);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
In the SELECT statement (line 128), add `s.agent_type as agentType` to the column list.
|
|
153
|
-
|
|
154
|
-
Also add `s.agent_type as agentType` to the SELECT in `getSessionById` (line 166), `getWorkspaceSessions` (line 332), and `getAllProjects` (line 18 — add `p.agent_type as agentType`).
|
|
155
|
-
|
|
156
|
-
**Step 5: Commit**
|
|
157
|
-
|
|
158
|
-
```
|
|
159
|
-
feat: add agentType to queries, types, and session filtering
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
### Task 3: Expand getUserPaths with agent directories
|
|
165
|
-
|
|
166
|
-
**Files:**
|
|
167
|
-
- Modify: `src/lib/config.ts:42-49` (getUserPaths return)
|
|
168
|
-
|
|
169
|
-
**Step 1: Add agent paths to return object**
|
|
170
|
-
|
|
171
|
-
In `getUserPaths()`, expand the return object (line 42):
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
return {
|
|
175
|
-
claudeDir: path.join(homeDir, '.claude'),
|
|
176
|
-
claudeProjectsDir: path.join(homeDir, '.claude', 'projects'),
|
|
177
|
-
statsPath: path.join(homeDir, '.claude', 'stats-cache.json'),
|
|
178
|
-
codexDir: path.join(homeDir, '.codex'),
|
|
179
|
-
codexSessionsDir: path.join(homeDir, '.codex', 'sessions'),
|
|
180
|
-
geminiDir: path.join(homeDir, '.gemini'),
|
|
181
|
-
geminiChatsBaseDir: path.join(homeDir, '.gemini', 'tmp'),
|
|
182
|
-
geminiProjectsRegistry: path.join(homeDir, '.gemini', 'projects.json'),
|
|
183
|
-
spacesDir,
|
|
184
|
-
dbPath: path.join(spacesDir, 'spaces.db'),
|
|
185
|
-
configPath: path.join(spacesDir, 'config.json'),
|
|
186
|
-
};
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
**Step 2: Commit**
|
|
190
|
-
|
|
191
|
-
```
|
|
192
|
-
feat: add Codex and Gemini paths to getUserPaths
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
### Task 4: Codex parser
|
|
198
|
-
|
|
199
|
-
**Files:**
|
|
200
|
-
- Create: `src/lib/codex/parser.ts`
|
|
201
|
-
|
|
202
|
-
**Step 1: Write the parser**
|
|
203
|
-
|
|
204
|
-
The Codex parser scans `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. The first JSONL line is a `SessionMeta` item containing `id` (UUID), `cwd`, `timestamp`. Subsequent lines contain conversation events.
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
import fs from 'fs';
|
|
208
|
-
import path from 'path';
|
|
209
|
-
|
|
210
|
-
interface CodexSessionMeta {
|
|
211
|
-
id: string;
|
|
212
|
-
cwd: string;
|
|
213
|
-
timestamp: string;
|
|
214
|
-
firstPrompt: string;
|
|
215
|
-
messageCount: number;
|
|
216
|
-
modified: string;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Scan ~/.codex/sessions/ for rollout files and extract metadata.
|
|
221
|
-
*/
|
|
222
|
-
export function scanCodexSessions(sessionsDir: string): CodexSessionMeta[] {
|
|
223
|
-
const results: CodexSessionMeta[] = [];
|
|
224
|
-
|
|
225
|
-
if (!fs.existsSync(sessionsDir)) return results;
|
|
226
|
-
|
|
227
|
-
// Walk YYYY/MM/DD structure
|
|
228
|
-
for (const year of safeReaddir(sessionsDir)) {
|
|
229
|
-
const yearPath = path.join(sessionsDir, year);
|
|
230
|
-
if (!isDir(yearPath)) continue;
|
|
231
|
-
for (const month of safeReaddir(yearPath)) {
|
|
232
|
-
const monthPath = path.join(yearPath, month);
|
|
233
|
-
if (!isDir(monthPath)) continue;
|
|
234
|
-
for (const day of safeReaddir(monthPath)) {
|
|
235
|
-
const dayPath = path.join(monthPath, day);
|
|
236
|
-
if (!isDir(dayPath)) continue;
|
|
237
|
-
for (const file of safeReaddir(dayPath)) {
|
|
238
|
-
if (!file.startsWith('rollout-') || !file.endsWith('.jsonl')) continue;
|
|
239
|
-
const filePath = path.join(dayPath, file);
|
|
240
|
-
const meta = parseRolloutMeta(filePath);
|
|
241
|
-
if (meta) results.push(meta);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return results;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function parseRolloutMeta(filePath: string): CodexSessionMeta | null {
|
|
251
|
-
try {
|
|
252
|
-
// Read first ~8KB to get SessionMeta and first user message
|
|
253
|
-
const fd = fs.openSync(filePath, 'r');
|
|
254
|
-
const buf = Buffer.alloc(8192);
|
|
255
|
-
const bytesRead = fs.readSync(fd, buf, 0, 8192, 0);
|
|
256
|
-
fs.closeSync(fd);
|
|
257
|
-
|
|
258
|
-
const chunk = buf.toString('utf-8', 0, bytesRead);
|
|
259
|
-
const lines = chunk.split('\n').filter(l => l.trim());
|
|
260
|
-
|
|
261
|
-
let id = '';
|
|
262
|
-
let cwd = '';
|
|
263
|
-
let timestamp = '';
|
|
264
|
-
let firstPrompt = '';
|
|
265
|
-
let messageCount = 0;
|
|
266
|
-
|
|
267
|
-
for (const line of lines) {
|
|
268
|
-
try {
|
|
269
|
-
const entry = JSON.parse(line);
|
|
270
|
-
const item = entry.item || entry;
|
|
271
|
-
|
|
272
|
-
// SessionMeta item
|
|
273
|
-
if (item.type === 'session_meta' || item.id) {
|
|
274
|
-
if (!id && item.id) {
|
|
275
|
-
id = item.id;
|
|
276
|
-
cwd = item.cwd || '';
|
|
277
|
-
timestamp = entry.timestamp || item.timestamp || '';
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Count events that look like messages
|
|
282
|
-
if (entry.item?.type === 'event' || entry.type === 'event') {
|
|
283
|
-
messageCount++;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Extract first user message
|
|
287
|
-
if (!firstPrompt) {
|
|
288
|
-
const payload = entry.item?.payload || entry.payload;
|
|
289
|
-
if (payload?.type === 'user_message' || payload?.type === 'UserMessage') {
|
|
290
|
-
firstPrompt = (typeof payload.content === 'string'
|
|
291
|
-
? payload.content
|
|
292
|
-
: JSON.stringify(payload.content)
|
|
293
|
-
).slice(0, 500);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
} catch { /* skip unparseable lines */ }
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (!id) {
|
|
300
|
-
// Fallback: extract session ID from filename (rollout-<timestamp>-<uuid>.jsonl)
|
|
301
|
-
const match = filePath.match(/rollout-[^-]+-(.+)\.jsonl$/);
|
|
302
|
-
if (match) id = match[1];
|
|
303
|
-
else return null;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const stat = fs.statSync(filePath);
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
id,
|
|
310
|
-
cwd,
|
|
311
|
-
timestamp: timestamp || stat.birthtime.toISOString(),
|
|
312
|
-
firstPrompt,
|
|
313
|
-
messageCount,
|
|
314
|
-
modified: stat.mtime.toISOString(),
|
|
315
|
-
};
|
|
316
|
-
} catch {
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function safeReaddir(dir: string): string[] {
|
|
322
|
-
try { return fs.readdirSync(dir); } catch { return []; }
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function isDir(p: string): boolean {
|
|
326
|
-
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
**Step 2: Commit**
|
|
331
|
-
|
|
332
|
-
```
|
|
333
|
-
feat: add Codex session parser
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
### Task 5: Gemini parser
|
|
339
|
-
|
|
340
|
-
**Files:**
|
|
341
|
-
- Create: `src/lib/gemini/parser.ts`
|
|
342
|
-
|
|
343
|
-
**Step 1: Write the parser**
|
|
344
|
-
|
|
345
|
-
Gemini stores sessions at `~/.gemini/tmp/<project-slug>/chats/session-<timestamp>-<id>.json`. Each is a JSON file with `sessionId`, `startTime`, `lastUpdated`, `messages[]`, and optional `summary`.
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
import fs from 'fs';
|
|
349
|
-
import path from 'path';
|
|
350
|
-
|
|
351
|
-
interface GeminiSessionMeta {
|
|
352
|
-
sessionId: string;
|
|
353
|
-
projectSlug: string;
|
|
354
|
-
projectPath: string;
|
|
355
|
-
startTime: string;
|
|
356
|
-
lastUpdated: string;
|
|
357
|
-
firstPrompt: string;
|
|
358
|
-
summary: string;
|
|
359
|
-
messageCount: number;
|
|
360
|
-
fullPath: string;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Scan ~/.gemini/tmp/*/chats/ for session files.
|
|
365
|
-
* Optionally reads projects.json to map slugs to real project paths.
|
|
366
|
-
*/
|
|
367
|
-
export function scanGeminiSessions(geminiChatsBaseDir: string, projectsRegistryPath: string): GeminiSessionMeta[] {
|
|
368
|
-
const results: GeminiSessionMeta[] = [];
|
|
369
|
-
|
|
370
|
-
if (!fs.existsSync(geminiChatsBaseDir)) return results;
|
|
371
|
-
|
|
372
|
-
// Load project registry (slug -> path mapping)
|
|
373
|
-
const projectMap = loadProjectRegistry(projectsRegistryPath);
|
|
374
|
-
|
|
375
|
-
for (const slug of safeReaddir(geminiChatsBaseDir)) {
|
|
376
|
-
const slugDir = path.join(geminiChatsBaseDir, slug);
|
|
377
|
-
if (!isDir(slugDir)) continue;
|
|
378
|
-
|
|
379
|
-
const chatsDir = path.join(slugDir, 'chats');
|
|
380
|
-
if (!fs.existsSync(chatsDir)) continue;
|
|
381
|
-
|
|
382
|
-
for (const file of safeReaddir(chatsDir)) {
|
|
383
|
-
if (!file.startsWith('session-') || !file.endsWith('.json')) continue;
|
|
384
|
-
const filePath = path.join(chatsDir, file);
|
|
385
|
-
const meta = parseGeminiSession(filePath, slug, projectMap);
|
|
386
|
-
if (meta) results.push(meta);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return results;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function parseGeminiSession(
|
|
394
|
-
filePath: string,
|
|
395
|
-
projectSlug: string,
|
|
396
|
-
projectMap: Map<string, string>
|
|
397
|
-
): GeminiSessionMeta | null {
|
|
398
|
-
try {
|
|
399
|
-
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
400
|
-
const data = JSON.parse(raw);
|
|
401
|
-
|
|
402
|
-
const sessionId = data.sessionId || '';
|
|
403
|
-
if (!sessionId) return null;
|
|
404
|
-
|
|
405
|
-
// Extract first user message
|
|
406
|
-
let firstPrompt = '';
|
|
407
|
-
const messages = data.messages || [];
|
|
408
|
-
for (const msg of messages) {
|
|
409
|
-
if (msg.type === 'user' && !firstPrompt) {
|
|
410
|
-
if (typeof msg.content === 'string') {
|
|
411
|
-
firstPrompt = msg.content.slice(0, 500);
|
|
412
|
-
} else if (msg.content?.parts) {
|
|
413
|
-
// Gemini uses parts array with text fields
|
|
414
|
-
const textParts = msg.content.parts
|
|
415
|
-
.filter((p: any) => typeof p === 'string' || p.text)
|
|
416
|
-
.map((p: any) => typeof p === 'string' ? p : p.text);
|
|
417
|
-
firstPrompt = textParts.join(' ').slice(0, 500);
|
|
418
|
-
}
|
|
419
|
-
break;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
return {
|
|
424
|
-
sessionId,
|
|
425
|
-
projectSlug,
|
|
426
|
-
projectPath: projectMap.get(projectSlug) || '',
|
|
427
|
-
startTime: data.startTime || '',
|
|
428
|
-
lastUpdated: data.lastUpdated || '',
|
|
429
|
-
firstPrompt,
|
|
430
|
-
summary: data.summary || '',
|
|
431
|
-
messageCount: messages.length,
|
|
432
|
-
fullPath: filePath,
|
|
433
|
-
};
|
|
434
|
-
} catch {
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function loadProjectRegistry(registryPath: string): Map<string, string> {
|
|
440
|
-
const map = new Map<string, string>();
|
|
441
|
-
if (!registryPath || !fs.existsSync(registryPath)) return map;
|
|
442
|
-
try {
|
|
443
|
-
const data = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
444
|
-
// projects.json maps {projectPath: slugId} or an array of entries
|
|
445
|
-
if (Array.isArray(data)) {
|
|
446
|
-
for (const entry of data) {
|
|
447
|
-
if (entry.slug && entry.path) map.set(entry.slug, entry.path);
|
|
448
|
-
if (entry.id && entry.path) map.set(entry.id, entry.path);
|
|
449
|
-
}
|
|
450
|
-
} else if (typeof data === 'object') {
|
|
451
|
-
for (const [projectPath, slug] of Object.entries(data)) {
|
|
452
|
-
if (typeof slug === 'string') map.set(slug, projectPath);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
} catch { /* ignore */ }
|
|
456
|
-
return map;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function safeReaddir(dir: string): string[] {
|
|
460
|
-
try { return fs.readdirSync(dir); } catch { return []; }
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function isDir(p: string): boolean {
|
|
464
|
-
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
465
|
-
}
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
**Step 2: Commit**
|
|
469
|
-
|
|
470
|
-
```
|
|
471
|
-
feat: add Gemini session parser
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
### Task 6: Aider parser
|
|
477
|
-
|
|
478
|
-
**Files:**
|
|
479
|
-
- Create: `src/lib/aider/parser.ts`
|
|
480
|
-
|
|
481
|
-
**Step 1: Write the parser**
|
|
482
|
-
|
|
483
|
-
Aider stores a single `.aider.chat.history.md` per project directory. One "session" entry per project. Count `#### ` markers for message count, extract first one as firstPrompt.
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
import fs from 'fs';
|
|
487
|
-
import path from 'path';
|
|
488
|
-
import crypto from 'crypto';
|
|
489
|
-
|
|
490
|
-
interface AiderSessionMeta {
|
|
491
|
-
projectPath: string;
|
|
492
|
-
firstPrompt: string;
|
|
493
|
-
messageCount: number;
|
|
494
|
-
modified: string;
|
|
495
|
-
created: string;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Scan a list of project directories for .aider.chat.history.md files.
|
|
500
|
-
*/
|
|
501
|
-
export function scanAiderSessions(projectDirs: string[]): AiderSessionMeta[] {
|
|
502
|
-
const results: AiderSessionMeta[] = [];
|
|
503
|
-
const seen = new Set<string>();
|
|
504
|
-
|
|
505
|
-
for (const dir of projectDirs) {
|
|
506
|
-
if (seen.has(dir)) continue;
|
|
507
|
-
seen.add(dir);
|
|
508
|
-
|
|
509
|
-
const historyFile = path.join(dir, '.aider.chat.history.md');
|
|
510
|
-
if (!fs.existsSync(historyFile)) continue;
|
|
511
|
-
|
|
512
|
-
const meta = parseAiderHistory(historyFile, dir);
|
|
513
|
-
if (meta) results.push(meta);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
return results;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function parseAiderHistory(filePath: string, projectDir: string): AiderSessionMeta | null {
|
|
520
|
-
try {
|
|
521
|
-
const stat = fs.statSync(filePath);
|
|
522
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
523
|
-
const lines = content.split('\n');
|
|
524
|
-
|
|
525
|
-
let firstPrompt = '';
|
|
526
|
-
let messageCount = 0;
|
|
527
|
-
|
|
528
|
-
for (const line of lines) {
|
|
529
|
-
if (line.startsWith('#### ')) {
|
|
530
|
-
messageCount++;
|
|
531
|
-
if (!firstPrompt) {
|
|
532
|
-
firstPrompt = line.slice(5).trim().slice(0, 500);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
if (messageCount === 0) return null;
|
|
538
|
-
|
|
539
|
-
return {
|
|
540
|
-
projectPath: projectDir,
|
|
541
|
-
firstPrompt,
|
|
542
|
-
messageCount,
|
|
543
|
-
modified: stat.mtime.toISOString(),
|
|
544
|
-
created: stat.birthtime.toISOString(),
|
|
545
|
-
};
|
|
546
|
-
} catch {
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* Generate a deterministic session ID from a project path.
|
|
553
|
-
* Aider has no session IDs, so we hash the path.
|
|
554
|
-
*/
|
|
555
|
-
export function aiderSessionId(projectPath: string): string {
|
|
556
|
-
return 'aider-' + crypto.createHash('sha256').update(projectPath).digest('hex').slice(0, 16);
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Generate a deterministic project ID from a project path for Aider.
|
|
561
|
-
*/
|
|
562
|
-
export function aiderProjectId(projectPath: string): string {
|
|
563
|
-
return 'aider-proj-' + crypto.createHash('sha256').update(projectPath).digest('hex').slice(0, 12);
|
|
564
|
-
}
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
**Step 2: Commit**
|
|
568
|
-
|
|
569
|
-
```
|
|
570
|
-
feat: add Aider session parser
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
---
|
|
574
|
-
|
|
575
|
-
### Task 7: Update indexer to scan all agents
|
|
576
|
-
|
|
577
|
-
**Files:**
|
|
578
|
-
- Modify: `src/lib/sync/indexer.ts`
|
|
579
|
-
|
|
580
|
-
**Step 1: Refactor fullSync into per-agent scanners**
|
|
581
|
-
|
|
582
|
-
Replace the existing `fullSync()` with a version that calls four sub-scanners. Keep the existing Claude logic as `syncClaude()`, then add `syncCodex()`, `syncGemini()`, `syncAider()`.
|
|
583
|
-
|
|
584
|
-
Add these imports at top:
|
|
585
|
-
```typescript
|
|
586
|
-
import { scanCodexSessions } from '../codex/parser';
|
|
587
|
-
import { scanGeminiSessions } from '../gemini/parser';
|
|
588
|
-
import { scanAiderSessions, aiderSessionId, aiderProjectId } from '../aider/parser';
|
|
589
|
-
import { readConfig } from '../config';
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
Rewrite `fullSync()` to:
|
|
593
|
-
```typescript
|
|
594
|
-
export async function fullSync(): Promise<{ projects: number; sessions: number }> {
|
|
595
|
-
const username = getCurrentUser();
|
|
596
|
-
const paths = getUserPaths(username);
|
|
597
|
-
|
|
598
|
-
const db = getDb();
|
|
599
|
-
let projectCount = 0;
|
|
600
|
-
let sessionCount = 0;
|
|
601
|
-
|
|
602
|
-
const insertMany = db.transaction(() => {
|
|
603
|
-
const claude = syncClaude(paths.claudeProjectsDir);
|
|
604
|
-
projectCount += claude.projects;
|
|
605
|
-
sessionCount += claude.sessions;
|
|
606
|
-
|
|
607
|
-
const codex = syncCodex(paths.codexSessionsDir);
|
|
608
|
-
projectCount += codex.projects;
|
|
609
|
-
sessionCount += codex.sessions;
|
|
610
|
-
|
|
611
|
-
const gemini = syncGemini(paths.geminiChatsBaseDir, paths.geminiProjectsRegistry);
|
|
612
|
-
projectCount += gemini.projects;
|
|
613
|
-
sessionCount += gemini.sessions;
|
|
614
|
-
|
|
615
|
-
// Aider: scan devDirectories + known project paths
|
|
616
|
-
const config = readConfig(username);
|
|
617
|
-
const knownProjectPaths = db.prepare(
|
|
618
|
-
"SELECT DISTINCT project_path FROM sessions WHERE project_path != '' AND project_path IS NOT NULL"
|
|
619
|
-
).all().map((r: any) => r.project_path);
|
|
620
|
-
const aiderDirs = [...new Set([...(config.devDirectories || []), ...knownProjectPaths])];
|
|
621
|
-
const aider = syncAider(aiderDirs);
|
|
622
|
-
projectCount += aider.projects;
|
|
623
|
-
sessionCount += aider.sessions;
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
insertMany();
|
|
627
|
-
return { projects: projectCount, sessions: sessionCount };
|
|
628
|
-
}
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
Extract existing Claude logic into `syncClaude(projectsDir)` that returns `{ projects, sessions }`. Same signature for the other three:
|
|
632
|
-
|
|
633
|
-
- `syncClaude(projectsDir)` — existing logic, passes `agentType: 'claude'` to upserts
|
|
634
|
-
- `syncCodex(sessionsDir)` — calls `scanCodexSessions`, derives projects from `cwd`, upserts with `agentType: 'codex'`
|
|
635
|
-
- `syncGemini(chatsBaseDir, registryPath)` — calls `scanGeminiSessions`, upserts with `agentType: 'gemini'`
|
|
636
|
-
- `syncAider(projectDirs)` — calls `scanAiderSessions`, uses `aiderProjectId`/`aiderSessionId` for IDs, upserts with `agentType: 'aider'`
|
|
637
|
-
|
|
638
|
-
Keep `enrichMissingSessions`, `buildFtsIndex`, `isSyncNeeded`, and `decodeProjectName` unchanged.
|
|
639
|
-
|
|
640
|
-
**Step 2: Verify sync runs**
|
|
641
|
-
|
|
642
|
-
Run: `npm run dev` and visit the sessions page.
|
|
643
|
-
Expected: Existing Claude sessions still appear. If Codex/Gemini/Aider directories exist, their sessions also appear.
|
|
644
|
-
|
|
645
|
-
**Step 3: Commit**
|
|
646
|
-
|
|
647
|
-
```
|
|
648
|
-
feat: update indexer to scan Codex, Gemini, and Aider sessions
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
### Task 8: Update watcher to watch all agent directories
|
|
654
|
-
|
|
655
|
-
**Files:**
|
|
656
|
-
- Modify: `src/lib/sync/watcher.ts`
|
|
657
|
-
|
|
658
|
-
**Step 1: Watch multiple directories**
|
|
659
|
-
|
|
660
|
-
Update `initWatcher` to watch Codex and Gemini directories too. Aider is project-local so we skip it — it syncs on fullSync.
|
|
661
|
-
|
|
662
|
-
Replace the existing single-directory watcher with a loop over agent directories:
|
|
663
|
-
|
|
664
|
-
```typescript
|
|
665
|
-
import os from 'os';
|
|
666
|
-
import fs from 'fs';
|
|
667
|
-
import { getUserPaths } from '../config';
|
|
668
|
-
import { fullSync } from './indexer';
|
|
669
|
-
import { sseManager } from '../events/sse';
|
|
670
|
-
|
|
671
|
-
let watcherInitialized = false;
|
|
672
|
-
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
673
|
-
|
|
674
|
-
export async function initWatcher() {
|
|
675
|
-
if (watcherInitialized) return;
|
|
676
|
-
watcherInitialized = true;
|
|
677
|
-
|
|
678
|
-
const processUser = os.userInfo().username;
|
|
679
|
-
const paths = getUserPaths(processUser);
|
|
680
|
-
|
|
681
|
-
const watchDirs: { dir: string; filter: (f: string) => boolean }[] = [
|
|
682
|
-
{
|
|
683
|
-
dir: paths.claudeProjectsDir,
|
|
684
|
-
filter: (f) => f.endsWith('.jsonl') || f.endsWith('sessions-index.json'),
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
dir: paths.codexSessionsDir,
|
|
688
|
-
filter: (f) => f.endsWith('.jsonl'),
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
dir: paths.geminiChatsBaseDir,
|
|
692
|
-
filter: (f) => f.endsWith('.json') && f.includes('session-'),
|
|
693
|
-
},
|
|
694
|
-
];
|
|
695
|
-
|
|
696
|
-
try {
|
|
697
|
-
const chokidar = await import('chokidar');
|
|
698
|
-
|
|
699
|
-
for (const { dir, filter } of watchDirs) {
|
|
700
|
-
if (!fs.existsSync(dir)) continue;
|
|
701
|
-
|
|
702
|
-
const watcher = chokidar.watch(dir, {
|
|
703
|
-
ignoreInitial: true,
|
|
704
|
-
depth: 5,
|
|
705
|
-
persistent: true,
|
|
706
|
-
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
watcher.on('all', (event: string, filePath: string) => {
|
|
710
|
-
if (!filter(filePath)) return;
|
|
711
|
-
|
|
712
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
713
|
-
debounceTimer = setTimeout(async () => {
|
|
714
|
-
try {
|
|
715
|
-
await fullSync();
|
|
716
|
-
sseManager.broadcast('sync', { type: event, file: filePath, timestamp: Date.now() });
|
|
717
|
-
} catch (err) {
|
|
718
|
-
console.error('[spaces] Watcher sync error:', err);
|
|
719
|
-
}
|
|
720
|
-
}, 1000);
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
console.log('[spaces] File watcher started on', dir);
|
|
724
|
-
}
|
|
725
|
-
} catch (err) {
|
|
726
|
-
console.error('[spaces] Failed to start file watcher:', err);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
**Step 2: Commit**
|
|
732
|
-
|
|
733
|
-
```
|
|
734
|
-
feat: watch Codex and Gemini directories for live session updates
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
---
|
|
738
|
-
|
|
739
|
-
### Task 9: Update agents.ts with resume flags
|
|
740
|
-
|
|
741
|
-
**Files:**
|
|
742
|
-
- Modify: `src/lib/agents.ts`
|
|
743
|
-
|
|
744
|
-
**Step 1: Update Codex and Gemini entries**
|
|
745
|
-
|
|
746
|
-
```typescript
|
|
747
|
-
codex: {
|
|
748
|
-
id: 'codex',
|
|
749
|
-
name: 'Codex CLI',
|
|
750
|
-
command: 'codex',
|
|
751
|
-
resumeFlag: 'resume', // subcommand: codex resume <id>
|
|
752
|
-
supportsResume: true,
|
|
753
|
-
color: '#10b981',
|
|
754
|
-
description: 'OpenAI Codex CLI',
|
|
755
|
-
},
|
|
756
|
-
gemini: {
|
|
757
|
-
id: 'gemini',
|
|
758
|
-
name: 'Gemini CLI',
|
|
759
|
-
command: 'gemini',
|
|
760
|
-
resumeFlag: '--resume', // flag: gemini --resume <id>
|
|
761
|
-
supportsResume: true,
|
|
762
|
-
color: '#3b82f6',
|
|
763
|
-
description: 'Google Gemini CLI',
|
|
764
|
-
},
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
**Step 2: Commit**
|
|
768
|
-
|
|
769
|
-
```
|
|
770
|
-
feat: enable resume support for Codex and Gemini agents
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
---
|
|
774
|
-
|
|
775
|
-
### Task 10: Update terminal server spawn logic
|
|
776
|
-
|
|
777
|
-
**Files:**
|
|
778
|
-
- Modify: `bin/terminal-server.js:309-316` (AGENTS object)
|
|
779
|
-
- Modify: `bin/terminal-server.js:537-583` (resume spawn logic)
|
|
780
|
-
|
|
781
|
-
**Step 1: Update AGENTS object**
|
|
782
|
-
|
|
783
|
-
```javascript
|
|
784
|
-
const AGENTS = {
|
|
785
|
-
shell: { command: '', resumeFlag: '', resumeStyle: '' },
|
|
786
|
-
claude: { command: 'claude', resumeFlag: '--resume', resumeStyle: 'flag' },
|
|
787
|
-
codex: { command: 'codex', resumeFlag: 'resume', resumeStyle: 'subcommand' },
|
|
788
|
-
gemini: { command: 'gemini', resumeFlag: '--resume', resumeStyle: 'flag' },
|
|
789
|
-
aider: { command: 'aider', resumeFlag: '', resumeStyle: '' },
|
|
790
|
-
custom: { command: '', resumeFlag: '', resumeStyle: '' },
|
|
791
|
-
};
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
**Step 2: Update resume spawn logic**
|
|
795
|
-
|
|
796
|
-
Replace the Claude-specific resume block (lines 537-583) with agent-generic logic. Keep the Claude `findSessionCwd` special case. For other agents, use the same delay pattern:
|
|
797
|
-
|
|
798
|
-
- Claude: `claude --resume <sessionId>` (with CWD lookup)
|
|
799
|
-
- Codex: `codex resume <sessionId>` (subcommand)
|
|
800
|
-
- Gemini: `gemini --resume <sessionId>` (flag)
|
|
801
|
-
|
|
802
|
-
See design doc for full spawn logic.
|
|
803
|
-
|
|
804
|
-
**Step 3: Commit**
|
|
805
|
-
|
|
806
|
-
```
|
|
807
|
-
feat: terminal server supports Codex and Gemini session resume
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
---
|
|
811
|
-
|
|
812
|
-
### Task 11: Update session API to accept agentType filter
|
|
813
|
-
|
|
814
|
-
**Files:**
|
|
815
|
-
- Modify: `src/app/api/sessions/route.ts`
|
|
816
|
-
|
|
817
|
-
**Step 1: Add agentType param**
|
|
818
|
-
|
|
819
|
-
In the GET handler (line 13), add `agentType` to params:
|
|
820
|
-
|
|
821
|
-
```typescript
|
|
822
|
-
const params = {
|
|
823
|
-
// ... existing params ...
|
|
824
|
-
agentType: searchParams.get('agentType') || undefined,
|
|
825
|
-
};
|
|
826
|
-
```
|
|
827
|
-
|
|
828
|
-
**Step 2: Commit**
|
|
829
|
-
|
|
830
|
-
```
|
|
831
|
-
feat: session API accepts agentType filter parameter
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
---
|
|
835
|
-
|
|
836
|
-
### Task 12: Add agent-type filter to session filters UI
|
|
837
|
-
|
|
838
|
-
**Files:**
|
|
839
|
-
- Modify: `src/components/sessions/session-filters.tsx`
|
|
840
|
-
|
|
841
|
-
**Step 1: Add agentType prop and filter chips**
|
|
842
|
-
|
|
843
|
-
Add `agentType` / `onAgentTypeChange` props to `SessionFiltersProps`. Import `AGENT_TYPES` from `@/lib/agents`.
|
|
844
|
-
|
|
845
|
-
Add a row of colored filter chips (All, Claude, Codex, Gemini, Aider) above the existing search/sort row. Each chip shows a colored dot matching the agent's color and highlights when active.
|
|
846
|
-
|
|
847
|
-
**Step 2: Wire up in parent component**
|
|
848
|
-
|
|
849
|
-
Find the parent page that renders `<SessionFilters>` and add `agentType` state. Pass it to `<SessionFilters>` and include `agentType` in the session fetch params.
|
|
850
|
-
|
|
851
|
-
**Step 3: Commit**
|
|
852
|
-
|
|
853
|
-
```
|
|
854
|
-
feat: add agent-type filter chips to session list
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
---
|
|
858
|
-
|
|
859
|
-
### Task 13: Add agent badge to session list rows
|
|
860
|
-
|
|
861
|
-
**Files:**
|
|
862
|
-
- Modify: `src/components/sessions/session-list.tsx:147-148`
|
|
863
|
-
|
|
864
|
-
**Step 1: Add agent badge**
|
|
865
|
-
|
|
866
|
-
Import `AGENT_TYPES` from `@/lib/agents`. In the `SessionRow` component, after the project name span (line 148), add a small colored badge for non-Claude sessions:
|
|
867
|
-
|
|
868
|
-
```typescript
|
|
869
|
-
{session.agentType && session.agentType !== 'claude' && (
|
|
870
|
-
<span
|
|
871
|
-
className="inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded font-medium"
|
|
872
|
-
style={{
|
|
873
|
-
backgroundColor: `${AGENT_TYPES[session.agentType]?.color || '#71717a'}15`,
|
|
874
|
-
color: AGENT_TYPES[session.agentType]?.color || '#71717a',
|
|
875
|
-
}}
|
|
876
|
-
>
|
|
877
|
-
{AGENT_TYPES[session.agentType]?.name || session.agentType}
|
|
878
|
-
</span>
|
|
879
|
-
)}
|
|
880
|
-
```
|
|
881
|
-
|
|
882
|
-
**Step 2: Commit**
|
|
883
|
-
|
|
884
|
-
```
|
|
885
|
-
feat: show agent badge on non-Claude session rows
|
|
886
|
-
```
|
|
887
|
-
|
|
888
|
-
---
|
|
889
|
-
|
|
890
|
-
### Task 14: Update resume picker for Codex and Gemini
|
|
891
|
-
|
|
892
|
-
**Files:**
|
|
893
|
-
- Modify: `src/app/(desktop)/terminal/page.tsx`
|
|
894
|
-
|
|
895
|
-
**Step 1: Filter sessions by agent type in resume picker**
|
|
896
|
-
|
|
897
|
-
Find the `useEffect` that loads sessions for the resume picker (around line 161-176). Add `agentType` to the query params:
|
|
898
|
-
|
|
899
|
-
```typescript
|
|
900
|
-
sp.set('agentType', newAgentType);
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
This ensures the resume picker for Codex shows only Codex sessions, Gemini shows only Gemini sessions, etc.
|
|
904
|
-
|
|
905
|
-
**Step 2: Commit**
|
|
906
|
-
|
|
907
|
-
```
|
|
908
|
-
feat: resume picker filters sessions by selected agent type
|
|
909
|
-
```
|
|
910
|
-
|
|
911
|
-
---
|
|
912
|
-
|
|
913
|
-
### Task 15: Update hooks to pass agentType
|
|
914
|
-
|
|
915
|
-
**Files:**
|
|
916
|
-
- Modify: `src/hooks/use-sessions.ts`
|
|
917
|
-
|
|
918
|
-
**Step 1: Add agentType to SessionsParams and query key**
|
|
919
|
-
|
|
920
|
-
In the `SessionsParams` interface, add:
|
|
921
|
-
```typescript
|
|
922
|
-
agentType?: string;
|
|
923
|
-
```
|
|
924
|
-
|
|
925
|
-
In `useSessions`, add it to the URL params:
|
|
926
|
-
```typescript
|
|
927
|
-
if (params.agentType) sp.set('agentType', params.agentType);
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
And include it in the React Query key so changing agent type triggers a refetch.
|
|
931
|
-
|
|
932
|
-
**Step 2: Commit**
|
|
933
|
-
|
|
934
|
-
```
|
|
935
|
-
feat: session hooks support agentType filtering
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
---
|
|
939
|
-
|
|
940
|
-
### Task 16: Integration verification
|
|
941
|
-
|
|
942
|
-
**Step 1: Run dev server and verify**
|
|
943
|
-
|
|
944
|
-
Run: `npm run dev`
|
|
945
|
-
|
|
946
|
-
Check:
|
|
947
|
-
- Sessions page loads without errors
|
|
948
|
-
- Agent filter chips appear (All, Claude, Codex, Gemini, Aider)
|
|
949
|
-
- Clicking a filter shows only that agent's sessions
|
|
950
|
-
- Claude sessions still have all existing functionality (star, tag, search, resume)
|
|
951
|
-
- If Codex/Gemini directories exist on the machine, their sessions appear
|
|
952
|
-
- Resume picker for Codex/Gemini shows only that agent's sessions
|
|
953
|
-
- Terminal pane creation with Codex/Gemini resume mode works
|
|
954
|
-
|
|
955
|
-
**Step 2: Final commit**
|
|
956
|
-
|
|
957
|
-
```
|
|
958
|
-
feat: multi-agent session support for Codex, Gemini, and Aider
|
|
959
|
-
```
|
|
1
|
+
# Multi-Agent Session & Project Support — Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Let users browse, search, tag, and resume sessions for Codex, Gemini, and Aider alongside Claude Code.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Add `agent_type` column to existing sessions/projects tables. Write one parser per agent that reads its native disk format and maps into the shared `upsertProject`/`upsertSession` shape. Update agents.ts resume flags, terminal server spawn logic, watcher, and session UI with agent-type filtering.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Next.js (App Router), SQLite (better-sqlite3), Node.js fs, TypeScript
|
|
10
|
+
|
|
11
|
+
**Design doc:** `docs/plans/2026-02-24-multi-agent-sessions-design.md`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### Task 1: DB Schema — add `agent_type` columns
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Modify: `src/lib/db/schema.ts:119-136` (addCol migration section)
|
|
19
|
+
|
|
20
|
+
**Step 1: Add migration columns**
|
|
21
|
+
|
|
22
|
+
In `schema.ts`, after the existing `addCol` calls (around line 136), add:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// Multi-agent support: add agent_type to sessions and projects
|
|
26
|
+
addCol('sessions', 'agent_type', "TEXT DEFAULT 'claude'");
|
|
27
|
+
addCol('projects', 'agent_type', "TEXT DEFAULT 'claude'");
|
|
28
|
+
addCol('projects', 'agent_path', 'TEXT');
|
|
29
|
+
|
|
30
|
+
// Copy claude_path -> agent_path for existing rows that don't have it yet
|
|
31
|
+
try { db.exec("UPDATE projects SET agent_path = claude_path WHERE agent_path IS NULL AND claude_path IS NOT NULL"); } catch { /* */ }
|
|
32
|
+
|
|
33
|
+
// Index for filtering by agent type
|
|
34
|
+
try { db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_agent_type ON sessions(agent_type)'); } catch { /* */ }
|
|
35
|
+
try { db.exec('CREATE INDEX IF NOT EXISTS idx_projects_agent_type ON projects(agent_type)'); } catch { /* */ }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Step 2: Verify by running dev server**
|
|
39
|
+
|
|
40
|
+
Run: `npm run dev`
|
|
41
|
+
Expected: Server starts without errors. Existing sessions still appear (defaulted to `agent_type='claude'`).
|
|
42
|
+
|
|
43
|
+
**Step 3: Commit**
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
feat: add agent_type columns to sessions and projects tables
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### Task 2: Update queries and types for agent_type
|
|
52
|
+
|
|
53
|
+
**Files:**
|
|
54
|
+
- Modify: `src/lib/db/queries.ts:7-14` (upsertProject), `src/lib/db/queries.ts:31-61` (upsertSession), `src/lib/db/queries.ts:63-73` (SessionQueryParams), `src/lib/db/queries.ts:75-161` (getSessions)
|
|
55
|
+
- Modify: `src/types/claude.ts:135-142` (Project), `src/types/claude.ts:144-164` (SessionWithMeta)
|
|
56
|
+
|
|
57
|
+
**Step 1: Update types**
|
|
58
|
+
|
|
59
|
+
In `src/types/claude.ts`, add `agentType` to `Project` (after line 139) and `SessionWithMeta` (after line 163):
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Project — add agentType field
|
|
63
|
+
export interface Project {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
path: string;
|
|
67
|
+
claudePath: string;
|
|
68
|
+
agentType: string;
|
|
69
|
+
sessionCount: number;
|
|
70
|
+
lastActivity: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// SessionWithMeta — add agentType field
|
|
74
|
+
export interface SessionWithMeta {
|
|
75
|
+
// ... existing fields ...
|
|
76
|
+
nodeId?: string;
|
|
77
|
+
nodeName?: string;
|
|
78
|
+
agentType: string;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Step 2: Update upsertProject**
|
|
83
|
+
|
|
84
|
+
In `src/lib/db/queries.ts`, modify `upsertProject` (line 7) to accept and store `agentType`:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
export function upsertProject(project: { id: string; name: string; path: string; claudePath: string; agentType?: string }) {
|
|
88
|
+
const db = getDb();
|
|
89
|
+
db.prepare(`
|
|
90
|
+
INSERT INTO projects (id, name, path, claude_path, agent_path, agent_type)
|
|
91
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
92
|
+
ON CONFLICT(id) DO UPDATE SET name=excluded.name, path=excluded.path, claude_path=excluded.claude_path, agent_path=excluded.agent_path, agent_type=excluded.agent_type
|
|
93
|
+
`).run(project.id, project.name, project.path, project.claudePath, project.claudePath, project.agentType || 'claude');
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Step 3: Update upsertSession**
|
|
98
|
+
|
|
99
|
+
Modify `upsertSession` (line 31) to accept and store `agentType`:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
export function upsertSession(session: {
|
|
103
|
+
id: string;
|
|
104
|
+
sessionId: string;
|
|
105
|
+
projectId: string;
|
|
106
|
+
firstPrompt: string;
|
|
107
|
+
summary: string;
|
|
108
|
+
messageCount: number;
|
|
109
|
+
created: string;
|
|
110
|
+
modified: string;
|
|
111
|
+
gitBranch: string;
|
|
112
|
+
projectPath: string;
|
|
113
|
+
fullPath: string;
|
|
114
|
+
agentType?: string;
|
|
115
|
+
}) {
|
|
116
|
+
const db = getDb();
|
|
117
|
+
db.prepare(`
|
|
118
|
+
INSERT INTO sessions (id, session_id, project_id, first_prompt, summary, message_count, created, modified, git_branch, project_path, full_path, agent_type)
|
|
119
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
120
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
121
|
+
first_prompt=excluded.first_prompt,
|
|
122
|
+
summary=excluded.summary,
|
|
123
|
+
message_count=excluded.message_count,
|
|
124
|
+
modified=excluded.modified,
|
|
125
|
+
git_branch=excluded.git_branch,
|
|
126
|
+
full_path=excluded.full_path,
|
|
127
|
+
agent_type=excluded.agent_type
|
|
128
|
+
`).run(
|
|
129
|
+
session.id, session.sessionId, session.projectId,
|
|
130
|
+
session.firstPrompt, session.summary, session.messageCount,
|
|
131
|
+
session.created, session.modified, session.gitBranch,
|
|
132
|
+
session.projectPath, session.fullPath, session.agentType || 'claude'
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Step 4: Add agentType to SessionQueryParams and getSessions**
|
|
138
|
+
|
|
139
|
+
In `SessionQueryParams` (line 63), add:
|
|
140
|
+
```typescript
|
|
141
|
+
agentType?: string;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
In `getSessions` (around line 112), add the filter clause after the `search` block:
|
|
145
|
+
```typescript
|
|
146
|
+
if (params.agentType) {
|
|
147
|
+
where.push('s.agent_type = ?');
|
|
148
|
+
queryParams.push(params.agentType);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
In the SELECT statement (line 128), add `s.agent_type as agentType` to the column list.
|
|
153
|
+
|
|
154
|
+
Also add `s.agent_type as agentType` to the SELECT in `getSessionById` (line 166), `getWorkspaceSessions` (line 332), and `getAllProjects` (line 18 — add `p.agent_type as agentType`).
|
|
155
|
+
|
|
156
|
+
**Step 5: Commit**
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
feat: add agentType to queries, types, and session filtering
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### Task 3: Expand getUserPaths with agent directories
|
|
165
|
+
|
|
166
|
+
**Files:**
|
|
167
|
+
- Modify: `src/lib/config.ts:42-49` (getUserPaths return)
|
|
168
|
+
|
|
169
|
+
**Step 1: Add agent paths to return object**
|
|
170
|
+
|
|
171
|
+
In `getUserPaths()`, expand the return object (line 42):
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
return {
|
|
175
|
+
claudeDir: path.join(homeDir, '.claude'),
|
|
176
|
+
claudeProjectsDir: path.join(homeDir, '.claude', 'projects'),
|
|
177
|
+
statsPath: path.join(homeDir, '.claude', 'stats-cache.json'),
|
|
178
|
+
codexDir: path.join(homeDir, '.codex'),
|
|
179
|
+
codexSessionsDir: path.join(homeDir, '.codex', 'sessions'),
|
|
180
|
+
geminiDir: path.join(homeDir, '.gemini'),
|
|
181
|
+
geminiChatsBaseDir: path.join(homeDir, '.gemini', 'tmp'),
|
|
182
|
+
geminiProjectsRegistry: path.join(homeDir, '.gemini', 'projects.json'),
|
|
183
|
+
spacesDir,
|
|
184
|
+
dbPath: path.join(spacesDir, 'spaces.db'),
|
|
185
|
+
configPath: path.join(spacesDir, 'config.json'),
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Step 2: Commit**
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
feat: add Codex and Gemini paths to getUserPaths
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Task 4: Codex parser
|
|
198
|
+
|
|
199
|
+
**Files:**
|
|
200
|
+
- Create: `src/lib/codex/parser.ts`
|
|
201
|
+
|
|
202
|
+
**Step 1: Write the parser**
|
|
203
|
+
|
|
204
|
+
The Codex parser scans `~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl`. The first JSONL line is a `SessionMeta` item containing `id` (UUID), `cwd`, `timestamp`. Subsequent lines contain conversation events.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import fs from 'fs';
|
|
208
|
+
import path from 'path';
|
|
209
|
+
|
|
210
|
+
interface CodexSessionMeta {
|
|
211
|
+
id: string;
|
|
212
|
+
cwd: string;
|
|
213
|
+
timestamp: string;
|
|
214
|
+
firstPrompt: string;
|
|
215
|
+
messageCount: number;
|
|
216
|
+
modified: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Scan ~/.codex/sessions/ for rollout files and extract metadata.
|
|
221
|
+
*/
|
|
222
|
+
export function scanCodexSessions(sessionsDir: string): CodexSessionMeta[] {
|
|
223
|
+
const results: CodexSessionMeta[] = [];
|
|
224
|
+
|
|
225
|
+
if (!fs.existsSync(sessionsDir)) return results;
|
|
226
|
+
|
|
227
|
+
// Walk YYYY/MM/DD structure
|
|
228
|
+
for (const year of safeReaddir(sessionsDir)) {
|
|
229
|
+
const yearPath = path.join(sessionsDir, year);
|
|
230
|
+
if (!isDir(yearPath)) continue;
|
|
231
|
+
for (const month of safeReaddir(yearPath)) {
|
|
232
|
+
const monthPath = path.join(yearPath, month);
|
|
233
|
+
if (!isDir(monthPath)) continue;
|
|
234
|
+
for (const day of safeReaddir(monthPath)) {
|
|
235
|
+
const dayPath = path.join(monthPath, day);
|
|
236
|
+
if (!isDir(dayPath)) continue;
|
|
237
|
+
for (const file of safeReaddir(dayPath)) {
|
|
238
|
+
if (!file.startsWith('rollout-') || !file.endsWith('.jsonl')) continue;
|
|
239
|
+
const filePath = path.join(dayPath, file);
|
|
240
|
+
const meta = parseRolloutMeta(filePath);
|
|
241
|
+
if (meta) results.push(meta);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function parseRolloutMeta(filePath: string): CodexSessionMeta | null {
|
|
251
|
+
try {
|
|
252
|
+
// Read first ~8KB to get SessionMeta and first user message
|
|
253
|
+
const fd = fs.openSync(filePath, 'r');
|
|
254
|
+
const buf = Buffer.alloc(8192);
|
|
255
|
+
const bytesRead = fs.readSync(fd, buf, 0, 8192, 0);
|
|
256
|
+
fs.closeSync(fd);
|
|
257
|
+
|
|
258
|
+
const chunk = buf.toString('utf-8', 0, bytesRead);
|
|
259
|
+
const lines = chunk.split('\n').filter(l => l.trim());
|
|
260
|
+
|
|
261
|
+
let id = '';
|
|
262
|
+
let cwd = '';
|
|
263
|
+
let timestamp = '';
|
|
264
|
+
let firstPrompt = '';
|
|
265
|
+
let messageCount = 0;
|
|
266
|
+
|
|
267
|
+
for (const line of lines) {
|
|
268
|
+
try {
|
|
269
|
+
const entry = JSON.parse(line);
|
|
270
|
+
const item = entry.item || entry;
|
|
271
|
+
|
|
272
|
+
// SessionMeta item
|
|
273
|
+
if (item.type === 'session_meta' || item.id) {
|
|
274
|
+
if (!id && item.id) {
|
|
275
|
+
id = item.id;
|
|
276
|
+
cwd = item.cwd || '';
|
|
277
|
+
timestamp = entry.timestamp || item.timestamp || '';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Count events that look like messages
|
|
282
|
+
if (entry.item?.type === 'event' || entry.type === 'event') {
|
|
283
|
+
messageCount++;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Extract first user message
|
|
287
|
+
if (!firstPrompt) {
|
|
288
|
+
const payload = entry.item?.payload || entry.payload;
|
|
289
|
+
if (payload?.type === 'user_message' || payload?.type === 'UserMessage') {
|
|
290
|
+
firstPrompt = (typeof payload.content === 'string'
|
|
291
|
+
? payload.content
|
|
292
|
+
: JSON.stringify(payload.content)
|
|
293
|
+
).slice(0, 500);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} catch { /* skip unparseable lines */ }
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!id) {
|
|
300
|
+
// Fallback: extract session ID from filename (rollout-<timestamp>-<uuid>.jsonl)
|
|
301
|
+
const match = filePath.match(/rollout-[^-]+-(.+)\.jsonl$/);
|
|
302
|
+
if (match) id = match[1];
|
|
303
|
+
else return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const stat = fs.statSync(filePath);
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
id,
|
|
310
|
+
cwd,
|
|
311
|
+
timestamp: timestamp || stat.birthtime.toISOString(),
|
|
312
|
+
firstPrompt,
|
|
313
|
+
messageCount,
|
|
314
|
+
modified: stat.mtime.toISOString(),
|
|
315
|
+
};
|
|
316
|
+
} catch {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function safeReaddir(dir: string): string[] {
|
|
322
|
+
try { return fs.readdirSync(dir); } catch { return []; }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function isDir(p: string): boolean {
|
|
326
|
+
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Step 2: Commit**
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
feat: add Codex session parser
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### Task 5: Gemini parser
|
|
339
|
+
|
|
340
|
+
**Files:**
|
|
341
|
+
- Create: `src/lib/gemini/parser.ts`
|
|
342
|
+
|
|
343
|
+
**Step 1: Write the parser**
|
|
344
|
+
|
|
345
|
+
Gemini stores sessions at `~/.gemini/tmp/<project-slug>/chats/session-<timestamp>-<id>.json`. Each is a JSON file with `sessionId`, `startTime`, `lastUpdated`, `messages[]`, and optional `summary`.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import fs from 'fs';
|
|
349
|
+
import path from 'path';
|
|
350
|
+
|
|
351
|
+
interface GeminiSessionMeta {
|
|
352
|
+
sessionId: string;
|
|
353
|
+
projectSlug: string;
|
|
354
|
+
projectPath: string;
|
|
355
|
+
startTime: string;
|
|
356
|
+
lastUpdated: string;
|
|
357
|
+
firstPrompt: string;
|
|
358
|
+
summary: string;
|
|
359
|
+
messageCount: number;
|
|
360
|
+
fullPath: string;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Scan ~/.gemini/tmp/*/chats/ for session files.
|
|
365
|
+
* Optionally reads projects.json to map slugs to real project paths.
|
|
366
|
+
*/
|
|
367
|
+
export function scanGeminiSessions(geminiChatsBaseDir: string, projectsRegistryPath: string): GeminiSessionMeta[] {
|
|
368
|
+
const results: GeminiSessionMeta[] = [];
|
|
369
|
+
|
|
370
|
+
if (!fs.existsSync(geminiChatsBaseDir)) return results;
|
|
371
|
+
|
|
372
|
+
// Load project registry (slug -> path mapping)
|
|
373
|
+
const projectMap = loadProjectRegistry(projectsRegistryPath);
|
|
374
|
+
|
|
375
|
+
for (const slug of safeReaddir(geminiChatsBaseDir)) {
|
|
376
|
+
const slugDir = path.join(geminiChatsBaseDir, slug);
|
|
377
|
+
if (!isDir(slugDir)) continue;
|
|
378
|
+
|
|
379
|
+
const chatsDir = path.join(slugDir, 'chats');
|
|
380
|
+
if (!fs.existsSync(chatsDir)) continue;
|
|
381
|
+
|
|
382
|
+
for (const file of safeReaddir(chatsDir)) {
|
|
383
|
+
if (!file.startsWith('session-') || !file.endsWith('.json')) continue;
|
|
384
|
+
const filePath = path.join(chatsDir, file);
|
|
385
|
+
const meta = parseGeminiSession(filePath, slug, projectMap);
|
|
386
|
+
if (meta) results.push(meta);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return results;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function parseGeminiSession(
|
|
394
|
+
filePath: string,
|
|
395
|
+
projectSlug: string,
|
|
396
|
+
projectMap: Map<string, string>
|
|
397
|
+
): GeminiSessionMeta | null {
|
|
398
|
+
try {
|
|
399
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
400
|
+
const data = JSON.parse(raw);
|
|
401
|
+
|
|
402
|
+
const sessionId = data.sessionId || '';
|
|
403
|
+
if (!sessionId) return null;
|
|
404
|
+
|
|
405
|
+
// Extract first user message
|
|
406
|
+
let firstPrompt = '';
|
|
407
|
+
const messages = data.messages || [];
|
|
408
|
+
for (const msg of messages) {
|
|
409
|
+
if (msg.type === 'user' && !firstPrompt) {
|
|
410
|
+
if (typeof msg.content === 'string') {
|
|
411
|
+
firstPrompt = msg.content.slice(0, 500);
|
|
412
|
+
} else if (msg.content?.parts) {
|
|
413
|
+
// Gemini uses parts array with text fields
|
|
414
|
+
const textParts = msg.content.parts
|
|
415
|
+
.filter((p: any) => typeof p === 'string' || p.text)
|
|
416
|
+
.map((p: any) => typeof p === 'string' ? p : p.text);
|
|
417
|
+
firstPrompt = textParts.join(' ').slice(0, 500);
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
sessionId,
|
|
425
|
+
projectSlug,
|
|
426
|
+
projectPath: projectMap.get(projectSlug) || '',
|
|
427
|
+
startTime: data.startTime || '',
|
|
428
|
+
lastUpdated: data.lastUpdated || '',
|
|
429
|
+
firstPrompt,
|
|
430
|
+
summary: data.summary || '',
|
|
431
|
+
messageCount: messages.length,
|
|
432
|
+
fullPath: filePath,
|
|
433
|
+
};
|
|
434
|
+
} catch {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function loadProjectRegistry(registryPath: string): Map<string, string> {
|
|
440
|
+
const map = new Map<string, string>();
|
|
441
|
+
if (!registryPath || !fs.existsSync(registryPath)) return map;
|
|
442
|
+
try {
|
|
443
|
+
const data = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
444
|
+
// projects.json maps {projectPath: slugId} or an array of entries
|
|
445
|
+
if (Array.isArray(data)) {
|
|
446
|
+
for (const entry of data) {
|
|
447
|
+
if (entry.slug && entry.path) map.set(entry.slug, entry.path);
|
|
448
|
+
if (entry.id && entry.path) map.set(entry.id, entry.path);
|
|
449
|
+
}
|
|
450
|
+
} else if (typeof data === 'object') {
|
|
451
|
+
for (const [projectPath, slug] of Object.entries(data)) {
|
|
452
|
+
if (typeof slug === 'string') map.set(slug, projectPath);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} catch { /* ignore */ }
|
|
456
|
+
return map;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function safeReaddir(dir: string): string[] {
|
|
460
|
+
try { return fs.readdirSync(dir); } catch { return []; }
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isDir(p: string): boolean {
|
|
464
|
+
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
**Step 2: Commit**
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
feat: add Gemini session parser
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
### Task 6: Aider parser
|
|
477
|
+
|
|
478
|
+
**Files:**
|
|
479
|
+
- Create: `src/lib/aider/parser.ts`
|
|
480
|
+
|
|
481
|
+
**Step 1: Write the parser**
|
|
482
|
+
|
|
483
|
+
Aider stores a single `.aider.chat.history.md` per project directory. One "session" entry per project. Count `#### ` markers for message count, extract first one as firstPrompt.
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
import fs from 'fs';
|
|
487
|
+
import path from 'path';
|
|
488
|
+
import crypto from 'crypto';
|
|
489
|
+
|
|
490
|
+
interface AiderSessionMeta {
|
|
491
|
+
projectPath: string;
|
|
492
|
+
firstPrompt: string;
|
|
493
|
+
messageCount: number;
|
|
494
|
+
modified: string;
|
|
495
|
+
created: string;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Scan a list of project directories for .aider.chat.history.md files.
|
|
500
|
+
*/
|
|
501
|
+
export function scanAiderSessions(projectDirs: string[]): AiderSessionMeta[] {
|
|
502
|
+
const results: AiderSessionMeta[] = [];
|
|
503
|
+
const seen = new Set<string>();
|
|
504
|
+
|
|
505
|
+
for (const dir of projectDirs) {
|
|
506
|
+
if (seen.has(dir)) continue;
|
|
507
|
+
seen.add(dir);
|
|
508
|
+
|
|
509
|
+
const historyFile = path.join(dir, '.aider.chat.history.md');
|
|
510
|
+
if (!fs.existsSync(historyFile)) continue;
|
|
511
|
+
|
|
512
|
+
const meta = parseAiderHistory(historyFile, dir);
|
|
513
|
+
if (meta) results.push(meta);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return results;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function parseAiderHistory(filePath: string, projectDir: string): AiderSessionMeta | null {
|
|
520
|
+
try {
|
|
521
|
+
const stat = fs.statSync(filePath);
|
|
522
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
523
|
+
const lines = content.split('\n');
|
|
524
|
+
|
|
525
|
+
let firstPrompt = '';
|
|
526
|
+
let messageCount = 0;
|
|
527
|
+
|
|
528
|
+
for (const line of lines) {
|
|
529
|
+
if (line.startsWith('#### ')) {
|
|
530
|
+
messageCount++;
|
|
531
|
+
if (!firstPrompt) {
|
|
532
|
+
firstPrompt = line.slice(5).trim().slice(0, 500);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (messageCount === 0) return null;
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
projectPath: projectDir,
|
|
541
|
+
firstPrompt,
|
|
542
|
+
messageCount,
|
|
543
|
+
modified: stat.mtime.toISOString(),
|
|
544
|
+
created: stat.birthtime.toISOString(),
|
|
545
|
+
};
|
|
546
|
+
} catch {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Generate a deterministic session ID from a project path.
|
|
553
|
+
* Aider has no session IDs, so we hash the path.
|
|
554
|
+
*/
|
|
555
|
+
export function aiderSessionId(projectPath: string): string {
|
|
556
|
+
return 'aider-' + crypto.createHash('sha256').update(projectPath).digest('hex').slice(0, 16);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Generate a deterministic project ID from a project path for Aider.
|
|
561
|
+
*/
|
|
562
|
+
export function aiderProjectId(projectPath: string): string {
|
|
563
|
+
return 'aider-proj-' + crypto.createHash('sha256').update(projectPath).digest('hex').slice(0, 12);
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Step 2: Commit**
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
feat: add Aider session parser
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
### Task 7: Update indexer to scan all agents
|
|
576
|
+
|
|
577
|
+
**Files:**
|
|
578
|
+
- Modify: `src/lib/sync/indexer.ts`
|
|
579
|
+
|
|
580
|
+
**Step 1: Refactor fullSync into per-agent scanners**
|
|
581
|
+
|
|
582
|
+
Replace the existing `fullSync()` with a version that calls four sub-scanners. Keep the existing Claude logic as `syncClaude()`, then add `syncCodex()`, `syncGemini()`, `syncAider()`.
|
|
583
|
+
|
|
584
|
+
Add these imports at top:
|
|
585
|
+
```typescript
|
|
586
|
+
import { scanCodexSessions } from '../codex/parser';
|
|
587
|
+
import { scanGeminiSessions } from '../gemini/parser';
|
|
588
|
+
import { scanAiderSessions, aiderSessionId, aiderProjectId } from '../aider/parser';
|
|
589
|
+
import { readConfig } from '../config';
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Rewrite `fullSync()` to:
|
|
593
|
+
```typescript
|
|
594
|
+
export async function fullSync(): Promise<{ projects: number; sessions: number }> {
|
|
595
|
+
const username = getCurrentUser();
|
|
596
|
+
const paths = getUserPaths(username);
|
|
597
|
+
|
|
598
|
+
const db = getDb();
|
|
599
|
+
let projectCount = 0;
|
|
600
|
+
let sessionCount = 0;
|
|
601
|
+
|
|
602
|
+
const insertMany = db.transaction(() => {
|
|
603
|
+
const claude = syncClaude(paths.claudeProjectsDir);
|
|
604
|
+
projectCount += claude.projects;
|
|
605
|
+
sessionCount += claude.sessions;
|
|
606
|
+
|
|
607
|
+
const codex = syncCodex(paths.codexSessionsDir);
|
|
608
|
+
projectCount += codex.projects;
|
|
609
|
+
sessionCount += codex.sessions;
|
|
610
|
+
|
|
611
|
+
const gemini = syncGemini(paths.geminiChatsBaseDir, paths.geminiProjectsRegistry);
|
|
612
|
+
projectCount += gemini.projects;
|
|
613
|
+
sessionCount += gemini.sessions;
|
|
614
|
+
|
|
615
|
+
// Aider: scan devDirectories + known project paths
|
|
616
|
+
const config = readConfig(username);
|
|
617
|
+
const knownProjectPaths = db.prepare(
|
|
618
|
+
"SELECT DISTINCT project_path FROM sessions WHERE project_path != '' AND project_path IS NOT NULL"
|
|
619
|
+
).all().map((r: any) => r.project_path);
|
|
620
|
+
const aiderDirs = [...new Set([...(config.devDirectories || []), ...knownProjectPaths])];
|
|
621
|
+
const aider = syncAider(aiderDirs);
|
|
622
|
+
projectCount += aider.projects;
|
|
623
|
+
sessionCount += aider.sessions;
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
insertMany();
|
|
627
|
+
return { projects: projectCount, sessions: sessionCount };
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Extract existing Claude logic into `syncClaude(projectsDir)` that returns `{ projects, sessions }`. Same signature for the other three:
|
|
632
|
+
|
|
633
|
+
- `syncClaude(projectsDir)` — existing logic, passes `agentType: 'claude'` to upserts
|
|
634
|
+
- `syncCodex(sessionsDir)` — calls `scanCodexSessions`, derives projects from `cwd`, upserts with `agentType: 'codex'`
|
|
635
|
+
- `syncGemini(chatsBaseDir, registryPath)` — calls `scanGeminiSessions`, upserts with `agentType: 'gemini'`
|
|
636
|
+
- `syncAider(projectDirs)` — calls `scanAiderSessions`, uses `aiderProjectId`/`aiderSessionId` for IDs, upserts with `agentType: 'aider'`
|
|
637
|
+
|
|
638
|
+
Keep `enrichMissingSessions`, `buildFtsIndex`, `isSyncNeeded`, and `decodeProjectName` unchanged.
|
|
639
|
+
|
|
640
|
+
**Step 2: Verify sync runs**
|
|
641
|
+
|
|
642
|
+
Run: `npm run dev` and visit the sessions page.
|
|
643
|
+
Expected: Existing Claude sessions still appear. If Codex/Gemini/Aider directories exist, their sessions also appear.
|
|
644
|
+
|
|
645
|
+
**Step 3: Commit**
|
|
646
|
+
|
|
647
|
+
```
|
|
648
|
+
feat: update indexer to scan Codex, Gemini, and Aider sessions
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### Task 8: Update watcher to watch all agent directories
|
|
654
|
+
|
|
655
|
+
**Files:**
|
|
656
|
+
- Modify: `src/lib/sync/watcher.ts`
|
|
657
|
+
|
|
658
|
+
**Step 1: Watch multiple directories**
|
|
659
|
+
|
|
660
|
+
Update `initWatcher` to watch Codex and Gemini directories too. Aider is project-local so we skip it — it syncs on fullSync.
|
|
661
|
+
|
|
662
|
+
Replace the existing single-directory watcher with a loop over agent directories:
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
import os from 'os';
|
|
666
|
+
import fs from 'fs';
|
|
667
|
+
import { getUserPaths } from '../config';
|
|
668
|
+
import { fullSync } from './indexer';
|
|
669
|
+
import { sseManager } from '../events/sse';
|
|
670
|
+
|
|
671
|
+
let watcherInitialized = false;
|
|
672
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
673
|
+
|
|
674
|
+
export async function initWatcher() {
|
|
675
|
+
if (watcherInitialized) return;
|
|
676
|
+
watcherInitialized = true;
|
|
677
|
+
|
|
678
|
+
const processUser = os.userInfo().username;
|
|
679
|
+
const paths = getUserPaths(processUser);
|
|
680
|
+
|
|
681
|
+
const watchDirs: { dir: string; filter: (f: string) => boolean }[] = [
|
|
682
|
+
{
|
|
683
|
+
dir: paths.claudeProjectsDir,
|
|
684
|
+
filter: (f) => f.endsWith('.jsonl') || f.endsWith('sessions-index.json'),
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
dir: paths.codexSessionsDir,
|
|
688
|
+
filter: (f) => f.endsWith('.jsonl'),
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
dir: paths.geminiChatsBaseDir,
|
|
692
|
+
filter: (f) => f.endsWith('.json') && f.includes('session-'),
|
|
693
|
+
},
|
|
694
|
+
];
|
|
695
|
+
|
|
696
|
+
try {
|
|
697
|
+
const chokidar = await import('chokidar');
|
|
698
|
+
|
|
699
|
+
for (const { dir, filter } of watchDirs) {
|
|
700
|
+
if (!fs.existsSync(dir)) continue;
|
|
701
|
+
|
|
702
|
+
const watcher = chokidar.watch(dir, {
|
|
703
|
+
ignoreInitial: true,
|
|
704
|
+
depth: 5,
|
|
705
|
+
persistent: true,
|
|
706
|
+
awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100 },
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
watcher.on('all', (event: string, filePath: string) => {
|
|
710
|
+
if (!filter(filePath)) return;
|
|
711
|
+
|
|
712
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
713
|
+
debounceTimer = setTimeout(async () => {
|
|
714
|
+
try {
|
|
715
|
+
await fullSync();
|
|
716
|
+
sseManager.broadcast('sync', { type: event, file: filePath, timestamp: Date.now() });
|
|
717
|
+
} catch (err) {
|
|
718
|
+
console.error('[spaces] Watcher sync error:', err);
|
|
719
|
+
}
|
|
720
|
+
}, 1000);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
console.log('[spaces] File watcher started on', dir);
|
|
724
|
+
}
|
|
725
|
+
} catch (err) {
|
|
726
|
+
console.error('[spaces] Failed to start file watcher:', err);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Step 2: Commit**
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
feat: watch Codex and Gemini directories for live session updates
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
### Task 9: Update agents.ts with resume flags
|
|
740
|
+
|
|
741
|
+
**Files:**
|
|
742
|
+
- Modify: `src/lib/agents.ts`
|
|
743
|
+
|
|
744
|
+
**Step 1: Update Codex and Gemini entries**
|
|
745
|
+
|
|
746
|
+
```typescript
|
|
747
|
+
codex: {
|
|
748
|
+
id: 'codex',
|
|
749
|
+
name: 'Codex CLI',
|
|
750
|
+
command: 'codex',
|
|
751
|
+
resumeFlag: 'resume', // subcommand: codex resume <id>
|
|
752
|
+
supportsResume: true,
|
|
753
|
+
color: '#10b981',
|
|
754
|
+
description: 'OpenAI Codex CLI',
|
|
755
|
+
},
|
|
756
|
+
gemini: {
|
|
757
|
+
id: 'gemini',
|
|
758
|
+
name: 'Gemini CLI',
|
|
759
|
+
command: 'gemini',
|
|
760
|
+
resumeFlag: '--resume', // flag: gemini --resume <id>
|
|
761
|
+
supportsResume: true,
|
|
762
|
+
color: '#3b82f6',
|
|
763
|
+
description: 'Google Gemini CLI',
|
|
764
|
+
},
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
**Step 2: Commit**
|
|
768
|
+
|
|
769
|
+
```
|
|
770
|
+
feat: enable resume support for Codex and Gemini agents
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
### Task 10: Update terminal server spawn logic
|
|
776
|
+
|
|
777
|
+
**Files:**
|
|
778
|
+
- Modify: `bin/terminal-server.js:309-316` (AGENTS object)
|
|
779
|
+
- Modify: `bin/terminal-server.js:537-583` (resume spawn logic)
|
|
780
|
+
|
|
781
|
+
**Step 1: Update AGENTS object**
|
|
782
|
+
|
|
783
|
+
```javascript
|
|
784
|
+
const AGENTS = {
|
|
785
|
+
shell: { command: '', resumeFlag: '', resumeStyle: '' },
|
|
786
|
+
claude: { command: 'claude', resumeFlag: '--resume', resumeStyle: 'flag' },
|
|
787
|
+
codex: { command: 'codex', resumeFlag: 'resume', resumeStyle: 'subcommand' },
|
|
788
|
+
gemini: { command: 'gemini', resumeFlag: '--resume', resumeStyle: 'flag' },
|
|
789
|
+
aider: { command: 'aider', resumeFlag: '', resumeStyle: '' },
|
|
790
|
+
custom: { command: '', resumeFlag: '', resumeStyle: '' },
|
|
791
|
+
};
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
**Step 2: Update resume spawn logic**
|
|
795
|
+
|
|
796
|
+
Replace the Claude-specific resume block (lines 537-583) with agent-generic logic. Keep the Claude `findSessionCwd` special case. For other agents, use the same delay pattern:
|
|
797
|
+
|
|
798
|
+
- Claude: `claude --resume <sessionId>` (with CWD lookup)
|
|
799
|
+
- Codex: `codex resume <sessionId>` (subcommand)
|
|
800
|
+
- Gemini: `gemini --resume <sessionId>` (flag)
|
|
801
|
+
|
|
802
|
+
See design doc for full spawn logic.
|
|
803
|
+
|
|
804
|
+
**Step 3: Commit**
|
|
805
|
+
|
|
806
|
+
```
|
|
807
|
+
feat: terminal server supports Codex and Gemini session resume
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
### Task 11: Update session API to accept agentType filter
|
|
813
|
+
|
|
814
|
+
**Files:**
|
|
815
|
+
- Modify: `src/app/api/sessions/route.ts`
|
|
816
|
+
|
|
817
|
+
**Step 1: Add agentType param**
|
|
818
|
+
|
|
819
|
+
In the GET handler (line 13), add `agentType` to params:
|
|
820
|
+
|
|
821
|
+
```typescript
|
|
822
|
+
const params = {
|
|
823
|
+
// ... existing params ...
|
|
824
|
+
agentType: searchParams.get('agentType') || undefined,
|
|
825
|
+
};
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Step 2: Commit**
|
|
829
|
+
|
|
830
|
+
```
|
|
831
|
+
feat: session API accepts agentType filter parameter
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
### Task 12: Add agent-type filter to session filters UI
|
|
837
|
+
|
|
838
|
+
**Files:**
|
|
839
|
+
- Modify: `src/components/sessions/session-filters.tsx`
|
|
840
|
+
|
|
841
|
+
**Step 1: Add agentType prop and filter chips**
|
|
842
|
+
|
|
843
|
+
Add `agentType` / `onAgentTypeChange` props to `SessionFiltersProps`. Import `AGENT_TYPES` from `@/lib/agents`.
|
|
844
|
+
|
|
845
|
+
Add a row of colored filter chips (All, Claude, Codex, Gemini, Aider) above the existing search/sort row. Each chip shows a colored dot matching the agent's color and highlights when active.
|
|
846
|
+
|
|
847
|
+
**Step 2: Wire up in parent component**
|
|
848
|
+
|
|
849
|
+
Find the parent page that renders `<SessionFilters>` and add `agentType` state. Pass it to `<SessionFilters>` and include `agentType` in the session fetch params.
|
|
850
|
+
|
|
851
|
+
**Step 3: Commit**
|
|
852
|
+
|
|
853
|
+
```
|
|
854
|
+
feat: add agent-type filter chips to session list
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
859
|
+
### Task 13: Add agent badge to session list rows
|
|
860
|
+
|
|
861
|
+
**Files:**
|
|
862
|
+
- Modify: `src/components/sessions/session-list.tsx:147-148`
|
|
863
|
+
|
|
864
|
+
**Step 1: Add agent badge**
|
|
865
|
+
|
|
866
|
+
Import `AGENT_TYPES` from `@/lib/agents`. In the `SessionRow` component, after the project name span (line 148), add a small colored badge for non-Claude sessions:
|
|
867
|
+
|
|
868
|
+
```typescript
|
|
869
|
+
{session.agentType && session.agentType !== 'claude' && (
|
|
870
|
+
<span
|
|
871
|
+
className="inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded font-medium"
|
|
872
|
+
style={{
|
|
873
|
+
backgroundColor: `${AGENT_TYPES[session.agentType]?.color || '#71717a'}15`,
|
|
874
|
+
color: AGENT_TYPES[session.agentType]?.color || '#71717a',
|
|
875
|
+
}}
|
|
876
|
+
>
|
|
877
|
+
{AGENT_TYPES[session.agentType]?.name || session.agentType}
|
|
878
|
+
</span>
|
|
879
|
+
)}
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
**Step 2: Commit**
|
|
883
|
+
|
|
884
|
+
```
|
|
885
|
+
feat: show agent badge on non-Claude session rows
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
### Task 14: Update resume picker for Codex and Gemini
|
|
891
|
+
|
|
892
|
+
**Files:**
|
|
893
|
+
- Modify: `src/app/(desktop)/terminal/page.tsx`
|
|
894
|
+
|
|
895
|
+
**Step 1: Filter sessions by agent type in resume picker**
|
|
896
|
+
|
|
897
|
+
Find the `useEffect` that loads sessions for the resume picker (around line 161-176). Add `agentType` to the query params:
|
|
898
|
+
|
|
899
|
+
```typescript
|
|
900
|
+
sp.set('agentType', newAgentType);
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
This ensures the resume picker for Codex shows only Codex sessions, Gemini shows only Gemini sessions, etc.
|
|
904
|
+
|
|
905
|
+
**Step 2: Commit**
|
|
906
|
+
|
|
907
|
+
```
|
|
908
|
+
feat: resume picker filters sessions by selected agent type
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
### Task 15: Update hooks to pass agentType
|
|
914
|
+
|
|
915
|
+
**Files:**
|
|
916
|
+
- Modify: `src/hooks/use-sessions.ts`
|
|
917
|
+
|
|
918
|
+
**Step 1: Add agentType to SessionsParams and query key**
|
|
919
|
+
|
|
920
|
+
In the `SessionsParams` interface, add:
|
|
921
|
+
```typescript
|
|
922
|
+
agentType?: string;
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
In `useSessions`, add it to the URL params:
|
|
926
|
+
```typescript
|
|
927
|
+
if (params.agentType) sp.set('agentType', params.agentType);
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
And include it in the React Query key so changing agent type triggers a refetch.
|
|
931
|
+
|
|
932
|
+
**Step 2: Commit**
|
|
933
|
+
|
|
934
|
+
```
|
|
935
|
+
feat: session hooks support agentType filtering
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
---
|
|
939
|
+
|
|
940
|
+
### Task 16: Integration verification
|
|
941
|
+
|
|
942
|
+
**Step 1: Run dev server and verify**
|
|
943
|
+
|
|
944
|
+
Run: `npm run dev`
|
|
945
|
+
|
|
946
|
+
Check:
|
|
947
|
+
- Sessions page loads without errors
|
|
948
|
+
- Agent filter chips appear (All, Claude, Codex, Gemini, Aider)
|
|
949
|
+
- Clicking a filter shows only that agent's sessions
|
|
950
|
+
- Claude sessions still have all existing functionality (star, tag, search, resume)
|
|
951
|
+
- If Codex/Gemini directories exist on the machine, their sessions appear
|
|
952
|
+
- Resume picker for Codex/Gemini shows only that agent's sessions
|
|
953
|
+
- Terminal pane creation with Codex/Gemini resume mode works
|
|
954
|
+
|
|
955
|
+
**Step 2: Final commit**
|
|
956
|
+
|
|
957
|
+
```
|
|
958
|
+
feat: multi-agent session support for Codex, Gemini, and Aider
|
|
959
|
+
```
|