commandmate 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -0
- package/.next/app-build-manifest.json +72 -0
- package/.next/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +32 -0
- package/.next/cache/.tsbuildinfo +1 -0
- package/.next/cache/config.json +7 -0
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/export-marker.json +1 -0
- package/.next/images-manifest.json +1 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +1 -0
- package/.next/react-loadable-manifest.json +249 -0
- package/.next/required-server-files.json +1 -0
- package/.next/routes-manifest.json +1 -0
- package/.next/server/app/_not-found/page.js +1 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +6 -0
- package/.next/server/app/_not-found.rsc +10 -0
- package/.next/server/app/api/external-apps/[id]/health/route.js +45 -0
- package/.next/server/app/api/external-apps/[id]/health/route.js.nft.json +1 -0
- package/.next/server/app/api/external-apps/[id]/route.js +45 -0
- package/.next/server/app/api/external-apps/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/external-apps/route.js +45 -0
- package/.next/server/app/api/external-apps/route.js.nft.json +1 -0
- package/.next/server/app/api/hooks/claude-done/route.js +19 -0
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -0
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/clone/route.js +1 -0
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/route.js +1 -0
- package/.next/server/app/api/repositories/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/scan/route.js +1 -0
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/sync/route.js +1 -0
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -0
- package/.next/server/app/api/slash-commands/route.js +1 -0
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -0
- package/.next/server/app/api/slash-commands.body +1 -0
- package/.next/server/app/api/slash-commands.meta +1 -0
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/capture/route.js +2 -0
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/route.js +19 -0
- package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/messages/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/search/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/search/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/viewed/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/route.js +1 -0
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -0
- package/.next/server/app/apple-icon.png/route.js +1 -0
- package/.next/server/app/apple-icon.png/route.js.nft.json +1 -0
- package/.next/server/app/apple-icon.png.body +0 -0
- package/.next/server/app/apple-icon.png.meta +1 -0
- package/.next/server/app/icon.png/route.js +1 -0
- package/.next/server/app/icon.png/route.js.nft.json +1 -0
- package/.next/server/app/icon.png.body +0 -0
- package/.next/server/app/icon.png.meta +1 -0
- package/.next/server/app/index.html +9 -0
- package/.next/server/app/index.meta +5 -0
- package/.next/server/app/index.rsc +8 -0
- package/.next/server/app/page.js +16 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app/proxy/[...path]/route.js +45 -0
- package/.next/server/app/proxy/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/page.js +21 -0
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js +4 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/terminal/page.js +6 -0
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -0
- package/.next/server/app-paths-manifest.json +46 -0
- package/.next/server/chunks/1318.js +29 -0
- package/.next/server/chunks/1528.js +1 -0
- package/.next/server/chunks/1682.js +6 -0
- package/.next/server/chunks/2518.js +12 -0
- package/.next/server/chunks/3053.js +1 -0
- package/.next/server/chunks/3673.js +1 -0
- package/.next/server/chunks/3853.js +1 -0
- package/.next/server/chunks/434.js +1 -0
- package/.next/server/chunks/4471.js +2 -0
- package/.next/server/chunks/4893.js +2 -0
- package/.next/server/chunks/5972.js +12 -0
- package/.next/server/chunks/6550.js +1 -0
- package/.next/server/chunks/6621.js +1 -0
- package/.next/server/chunks/7213.js +1 -0
- package/.next/server/chunks/7425.js +500 -0
- package/.next/server/chunks/8585.js +1 -0
- package/.next/server/chunks/8887.js +1 -0
- package/.next/server/chunks/8948.js +2 -0
- package/.next/server/chunks/9703.js +31 -0
- package/.next/server/chunks/9723.js +19 -0
- package/.next/server/chunks/font-manifest.json +1 -0
- package/.next/server/edge-runtime-webpack.js +2 -0
- package/.next/server/edge-runtime-webpack.js.map +1 -0
- package/.next/server/font-manifest.json +1 -0
- package/.next/server/functions-config-manifest.json +1 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +32 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages/_app.js +1 -0
- package/.next/server/pages/_app.js.nft.json +1 -0
- package/.next/server/pages/_document.js +1 -0
- package/.next/server/pages/_document.js.nft.json +1 -0
- package/.next/server/pages/_error.js +1 -0
- package/.next/server/pages/_error.js.nft.json +1 -0
- package/.next/server/pages-manifest.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/src/middleware.js +14 -0
- package/.next/server/src/middleware.js.map +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/chunks/0dbeb660.3e800dfbd28be3bd.js +53 -0
- package/.next/static/chunks/1015.0eaa4da7f61149bc.js +59 -0
- package/.next/static/chunks/1098.49268c9fe1b028fa.js +1 -0
- package/.next/static/chunks/13.feeafc7cc620f8c4.js +1 -0
- package/.next/static/chunks/1423.7b1e8bf760d28078.js +1 -0
- package/.next/static/chunks/1582.9f8590f71ff798ca.js +55 -0
- package/.next/static/chunks/1817.a66d96cedb761daa.js +262 -0
- package/.next/static/chunks/2117-d845c2cd62e344a6.js +2 -0
- package/.next/static/chunks/2398.0b21e4eb7006a230.js +93 -0
- package/.next/static/chunks/2526.8ac62b527c9ab703.js +43 -0
- package/.next/static/chunks/2626.2125083a1ff3b80a.js +29 -0
- package/.next/static/chunks/2689.720a4874b02d4211.js +174 -0
- package/.next/static/chunks/2853-d11a80b03c9a1640.js +1 -0
- package/.next/static/chunks/2957-327e43ef4c12808f.js +1 -0
- package/.next/static/chunks/2cdb6380.35626fc6e41bbba4.js +136 -0
- package/.next/static/chunks/30d07d85-393352a92199f695.js +3 -0
- package/.next/static/chunks/3559.f073f72c4466ce0e.js +1 -0
- package/.next/static/chunks/3574.7a94c27e6a496a56.js +63 -0
- package/.next/static/chunks/383.20683891c9a5f2c4.js +4 -0
- package/.next/static/chunks/3843.3fdda732987f7bb8.js +1 -0
- package/.next/static/chunks/3852.822389f445c9b427.js +1 -0
- package/.next/static/chunks/3991.4bc063cb5be3a86c.js +1 -0
- package/.next/static/chunks/4212.52c1bb34fc97d0d0.js +131 -0
- package/.next/static/chunks/4327.3b84aa049900fdeb.js +60 -0
- package/.next/static/chunks/4362.7bd6f0282e49d79b.js +1 -0
- package/.next/static/chunks/4721.40615a5f4f32b5fb.js +1 -0
- package/.next/static/chunks/4851-45df4d388db5623f.js +1 -0
- package/.next/static/chunks/5112.17318d1c6b28044b.js +1 -0
- package/.next/static/chunks/5126.93fa4e797d609286.js +56 -0
- package/.next/static/chunks/5387.47590ac4ef66c864.js +5 -0
- package/.next/static/chunks/5813.4483664ba482beb1.js +1 -0
- package/.next/static/chunks/6143.1450875bd03a2366.js +36 -0
- package/.next/static/chunks/6406.9653f0d41ab85059.js +1 -0
- package/.next/static/chunks/656.d72f25ce819bd77e.js +149 -0
- package/.next/static/chunks/6678.492e73ca42b2a273.js +62 -0
- package/.next/static/chunks/6725-f7607851b7d57eb1.js +1 -0
- package/.next/static/chunks/6792.3c01ac4dda4b5c6d.js +1 -0
- package/.next/static/chunks/7004.808cbf327ef5955e.js +1 -0
- package/.next/static/chunks/7290.09ef84cf94f90c4d.js +1 -0
- package/.next/static/chunks/7415.6b481c2baf363262.js +148 -0
- package/.next/static/chunks/7648-325564a6e12a3257.js +1 -0
- package/.next/static/chunks/7665.47fccad04449a8f9.js +215 -0
- package/.next/static/chunks/7753.6bdce86b7fde3d10.js +166 -0
- package/.next/static/chunks/8125.245a9df052d274fb.js +1 -0
- package/.next/static/chunks/816-7e340dad784be28c.js +1 -0
- package/.next/static/chunks/8288.4883743fa40672e2.js +24 -0
- package/.next/static/chunks/8522.1607e96011c66877.js +1 -0
- package/.next/static/chunks/8772.863c564498d88487.js +1 -0
- package/.next/static/chunks/8841.dadeb1ece8e46004.js +1 -0
- package/.next/static/chunks/8885.f8d9912b40d74811.js +1 -0
- package/.next/static/chunks/90542734.c1553d0fe7fc14fc.js +1 -0
- package/.next/static/chunks/9365-733d8c05712d2888.js +1 -0
- package/.next/static/chunks/9552.b7dfb7903ead934b.js +1 -0
- package/.next/static/chunks/9834.295b45635ce04f5e.js +24 -0
- package/.next/static/chunks/app/_not-found/page-a9d04e58c81115ec.js +1 -0
- package/.next/static/chunks/app/layout-37e55f11dcc8b1bf.js +1 -0
- package/.next/static/chunks/app/page-9cd00de9cc0abc43.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9e5adf57cbbbdf05.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-8c6676303b63fdaf.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/simple-terminal/page-16feb3e86e42f4d1.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-be802baffc84dbd2.js +1 -0
- package/.next/static/chunks/d3ac728e.6c9c508274d4d2d5.js +1 -0
- package/.next/static/chunks/fd9d1056-bbe86e4ae099d5cd.js +1 -0
- package/.next/static/chunks/framework-8e0e0f4a6b83a956.js +1 -0
- package/.next/static/chunks/main-a960f4a5e1a2f598.js +1 -0
- package/.next/static/chunks/main-app-420d93e43682fee5.js +1 -0
- package/.next/static/chunks/pages/_app-3c9ca398d360b709.js +1 -0
- package/.next/static/chunks/pages/_error-cf5ca766ac8f493f.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-3fc79fab9bb738d7.js +1 -0
- package/.next/static/css/5eacd01f773eed7f.css +11 -0
- package/.next/static/css/85fa6dafca566008.css +1 -0
- package/.next/static/css/e174aa24f94ce607.css +3 -0
- package/.next/static/pQTquVjewvoJa7BML07ip/_buildManifest.js +1 -0
- package/.next/static/pQTquVjewvoJa7BML07ip/_ssgManifest.js +1 -0
- package/.next/trace +5 -0
- package/.next/types/app/api/external-apps/[id]/health/route.ts +343 -0
- package/.next/types/app/api/external-apps/[id]/route.ts +343 -0
- package/.next/types/app/api/external-apps/route.ts +343 -0
- package/.next/types/app/api/hooks/claude-done/route.ts +343 -0
- package/.next/types/app/api/repositories/clone/[jobId]/route.ts +343 -0
- package/.next/types/app/api/repositories/clone/route.ts +343 -0
- package/.next/types/app/api/repositories/route.ts +343 -0
- package/.next/types/app/api/repositories/scan/route.ts +343 -0
- package/.next/types/app/api/repositories/sync/route.ts +343 -0
- package/.next/types/app/api/slash-commands/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/auto-yes/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/capture/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/cli-tool/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/current-output/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/files/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/interrupt/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/kill-session/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/logs/[filename]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/logs/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/memos/[memoId]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/memos/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/messages/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/prompt-response/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/respond/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/search/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/send/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/slash-commands/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/start-polling/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/terminal/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/tree/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/tree/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/upload/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/viewed/route.ts +343 -0
- package/.next/types/app/api/worktrees/route.ts +343 -0
- package/.next/types/app/page.ts +79 -0
- package/.next/types/app/proxy/[...path]/route.ts +343 -0
- package/.next/types/app/worktrees/[id]/files/[...path]/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/simple-terminal/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/terminal/page.ts +79 -0
- package/.next/types/package.json +1 -0
- package/README.md +39 -8
- package/dist/server/server.js +123 -0
- package/dist/server/src/lib/claude-output.js +33 -0
- package/dist/server/src/lib/claude-session.js +312 -0
- package/dist/server/src/lib/cli-patterns.js +137 -0
- package/dist/server/src/lib/cli-session.js +73 -0
- package/dist/server/src/lib/cli-tools/base.js +51 -0
- package/dist/server/src/lib/cli-tools/claude.js +65 -0
- package/dist/server/src/lib/cli-tools/codex.js +132 -0
- package/dist/server/src/lib/cli-tools/gemini.js +122 -0
- package/dist/server/src/lib/cli-tools/index.js +22 -0
- package/dist/server/src/lib/cli-tools/manager.js +143 -0
- package/dist/server/src/lib/cli-tools/types.js +5 -0
- package/dist/server/src/lib/conversation-logger.js +25 -0
- package/dist/server/src/lib/db-instance.js +51 -0
- package/dist/server/src/lib/db-migrations.js +777 -0
- package/dist/server/src/lib/db.js +835 -0
- package/dist/server/src/lib/env.js +179 -0
- package/dist/server/src/lib/log-manager.js +234 -0
- package/dist/server/src/lib/logger.js +232 -0
- package/dist/server/src/lib/prompt-detector.js +285 -0
- package/dist/server/src/lib/response-poller.js +638 -0
- package/dist/server/src/lib/tmux.js +299 -0
- package/dist/server/src/lib/worktrees.js +231 -0
- package/dist/server/src/lib/ws-server.js +323 -0
- package/dist/server/src/types/clone.js +39 -0
- package/dist/server/src/types/conversation.js +9 -0
- package/dist/server/src/types/external-apps.js +6 -0
- package/dist/server/src/types/infinite-messages.js +65 -0
- package/dist/server/src/types/markdown-editor.js +94 -0
- package/dist/server/src/types/models.js +5 -0
- package/dist/server/src/types/sidebar.js +89 -0
- package/dist/server/src/types/slash-commands.js +47 -0
- package/dist/server/src/types/ui-actions.js +8 -0
- package/dist/server/src/types/ui-state.js +62 -0
- package/package.json +8 -4
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* tmux session management
|
|
4
|
+
* Provides functions to manage tmux sessions for Claude CLI integration
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.isTmuxAvailable = isTmuxAvailable;
|
|
8
|
+
exports.hasSession = hasSession;
|
|
9
|
+
exports.listSessions = listSessions;
|
|
10
|
+
exports.createSession = createSession;
|
|
11
|
+
exports.sendKeys = sendKeys;
|
|
12
|
+
exports.capturePane = capturePane;
|
|
13
|
+
exports.killSession = killSession;
|
|
14
|
+
exports.ensureSession = ensureSession;
|
|
15
|
+
exports.sendSpecialKey = sendSpecialKey;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const util_1 = require("util");
|
|
18
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
19
|
+
/**
|
|
20
|
+
* Default timeout for tmux commands (5 seconds)
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_TIMEOUT = 5000;
|
|
23
|
+
/**
|
|
24
|
+
* Check if tmux is installed and available
|
|
25
|
+
*/
|
|
26
|
+
async function isTmuxAvailable() {
|
|
27
|
+
try {
|
|
28
|
+
await execAsync('tmux -V', { timeout: DEFAULT_TIMEOUT });
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if a tmux session exists
|
|
37
|
+
*
|
|
38
|
+
* @param sessionName - Name of the tmux session
|
|
39
|
+
* @returns True if session exists, false otherwise
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const exists = await hasSession('my-session');
|
|
44
|
+
* if (exists) {
|
|
45
|
+
* console.log('Session is running');
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
async function hasSession(sessionName) {
|
|
50
|
+
try {
|
|
51
|
+
await execAsync(`tmux has-session -t "${sessionName}"`, { timeout: DEFAULT_TIMEOUT });
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// tmux has-session returns non-zero exit code if session doesn't exist
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* List all tmux sessions
|
|
61
|
+
*
|
|
62
|
+
* @returns Array of tmux session information
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const sessions = await listSessions();
|
|
67
|
+
* sessions.forEach(s => console.log(`${s.name}: ${s.windows} windows`));
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
async function listSessions() {
|
|
71
|
+
try {
|
|
72
|
+
const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}|#{session_windows}|#{session_attached}"', { timeout: DEFAULT_TIMEOUT });
|
|
73
|
+
if (!stdout || stdout.trim() === '') {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return stdout
|
|
77
|
+
.trim()
|
|
78
|
+
.split('\n')
|
|
79
|
+
.map(line => {
|
|
80
|
+
const [name, windows, attached] = line.split('|');
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
windows: parseInt(windows, 10) || 0,
|
|
84
|
+
attached: attached === '1',
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// No sessions exist or tmux not running
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a new tmux session
|
|
95
|
+
*
|
|
96
|
+
* @param sessionNameOrOptions - Session name or options object
|
|
97
|
+
* @param cwd - Working directory (when using legacy signature)
|
|
98
|
+
*
|
|
99
|
+
* @throws {Error} If session creation fails
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Legacy usage
|
|
104
|
+
* await createSession('my-session', '/path/to/project');
|
|
105
|
+
*
|
|
106
|
+
* // New usage with options
|
|
107
|
+
* await createSession({
|
|
108
|
+
* sessionName: 'my-session',
|
|
109
|
+
* workingDirectory: '/path/to/project',
|
|
110
|
+
* historyLimit: 50000,
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async function createSession(sessionNameOrOptions, cwd) {
|
|
115
|
+
let sessionName;
|
|
116
|
+
let workingDirectory;
|
|
117
|
+
let historyLimit;
|
|
118
|
+
if (typeof sessionNameOrOptions === 'string') {
|
|
119
|
+
// Legacy signature
|
|
120
|
+
sessionName = sessionNameOrOptions;
|
|
121
|
+
workingDirectory = cwd;
|
|
122
|
+
historyLimit = 50000;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// New signature with options
|
|
126
|
+
sessionName = sessionNameOrOptions.sessionName;
|
|
127
|
+
workingDirectory = sessionNameOrOptions.workingDirectory;
|
|
128
|
+
historyLimit = sessionNameOrOptions.historyLimit || 50000;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
// Create session
|
|
132
|
+
await execAsync(`tmux new-session -d -s "${sessionName}" -c "${workingDirectory}"`, { timeout: DEFAULT_TIMEOUT });
|
|
133
|
+
// Set history limit
|
|
134
|
+
await execAsync(`tmux set-option -t "${sessionName}" history-limit ${historyLimit}`, { timeout: DEFAULT_TIMEOUT });
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
138
|
+
throw new Error(`Failed to create tmux session: ${errorMessage}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Send keys to a tmux session
|
|
143
|
+
*
|
|
144
|
+
* @param sessionName - Target session name
|
|
145
|
+
* @param keys - Keys to send (command text)
|
|
146
|
+
* @param sendEnter - Whether to send Enter key after the command (default: true)
|
|
147
|
+
*
|
|
148
|
+
* @throws {Error} If session doesn't exist or command fails
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* await sendKeys('my-session', 'echo hello');
|
|
153
|
+
* await sendKeys('my-session', 'ls -la', true);
|
|
154
|
+
* await sendKeys('my-session', 'incomplete command', false);
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
async function sendKeys(sessionName, keys, sendEnter = true) {
|
|
158
|
+
// Escape single quotes in the keys
|
|
159
|
+
const escapedKeys = keys.replace(/'/g, "'\\''");
|
|
160
|
+
const command = sendEnter
|
|
161
|
+
? `tmux send-keys -t "${sessionName}" '${escapedKeys}' C-m`
|
|
162
|
+
: `tmux send-keys -t "${sessionName}" '${escapedKeys}'`;
|
|
163
|
+
try {
|
|
164
|
+
await execAsync(command, { timeout: DEFAULT_TIMEOUT });
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
168
|
+
throw new Error(`Failed to send keys to tmux session: ${errorMessage}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Capture pane output from a tmux session
|
|
173
|
+
*
|
|
174
|
+
* @param sessionName - Target session name
|
|
175
|
+
* @param linesOrOptions - Number of lines or options object
|
|
176
|
+
* @returns Captured output as string
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* // Legacy usage
|
|
181
|
+
* const output = await capturePane('my-session');
|
|
182
|
+
* const recent = await capturePane('my-session', 100);
|
|
183
|
+
*
|
|
184
|
+
* // New usage with options
|
|
185
|
+
* const full = await capturePane('my-session', {
|
|
186
|
+
* startLine: -10000,
|
|
187
|
+
* endLine: -1,
|
|
188
|
+
* });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
async function capturePane(sessionName, linesOrOptions) {
|
|
192
|
+
let startLine;
|
|
193
|
+
let endLine;
|
|
194
|
+
if (typeof linesOrOptions === 'number') {
|
|
195
|
+
// Legacy signature
|
|
196
|
+
startLine = -linesOrOptions;
|
|
197
|
+
endLine = '-';
|
|
198
|
+
}
|
|
199
|
+
else if (linesOrOptions) {
|
|
200
|
+
// New signature with options
|
|
201
|
+
startLine = linesOrOptions.startLine ?? -10000;
|
|
202
|
+
endLine = linesOrOptions.endLine ?? '-';
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Default
|
|
206
|
+
startLine = -1000;
|
|
207
|
+
endLine = '-';
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const { stdout } = await execAsync(`tmux capture-pane -t "${sessionName}" -p -e -S ${startLine} -E ${endLine}`, {
|
|
211
|
+
timeout: DEFAULT_TIMEOUT,
|
|
212
|
+
maxBuffer: 10 * 1024 * 1024 // 10MB buffer for large Claude outputs
|
|
213
|
+
});
|
|
214
|
+
return stdout;
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
218
|
+
throw new Error(`Failed to capture pane: ${errorMessage}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Kill a tmux session
|
|
223
|
+
*
|
|
224
|
+
* @param sessionName - Session name to kill
|
|
225
|
+
* @returns True if session was killed, false if session didn't exist
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```typescript
|
|
229
|
+
* const killed = await killSession('my-session');
|
|
230
|
+
* if (killed) {
|
|
231
|
+
* console.log('Session terminated');
|
|
232
|
+
* }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
async function killSession(sessionName) {
|
|
236
|
+
try {
|
|
237
|
+
await execAsync(`tmux kill-session -t "${sessionName}"`, {
|
|
238
|
+
timeout: DEFAULT_TIMEOUT,
|
|
239
|
+
});
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
244
|
+
// Session doesn't exist or already killed
|
|
245
|
+
if (errorMessage?.includes('no server running') ||
|
|
246
|
+
errorMessage?.includes("can't find session")) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
// Re-throw unexpected errors
|
|
250
|
+
throw new Error(`Failed to kill tmux session: ${errorMessage}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Ensure a tmux session exists, creating it if necessary
|
|
255
|
+
*
|
|
256
|
+
* @param sessionName - Session name
|
|
257
|
+
* @param cwd - Working directory for the session
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* // Will create session if it doesn't exist
|
|
262
|
+
* await ensureSession('my-session', '/path/to/project');
|
|
263
|
+
*
|
|
264
|
+
* // Safe to call multiple times
|
|
265
|
+
* await ensureSession('my-session', '/path/to/project');
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
async function ensureSession(sessionName, cwd) {
|
|
269
|
+
const exists = await hasSession(sessionName);
|
|
270
|
+
if (!exists) {
|
|
271
|
+
await createSession(sessionName, cwd);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Send a special key to a tmux session
|
|
276
|
+
*
|
|
277
|
+
* @param sessionName - Target session name
|
|
278
|
+
* @param key - Special key to send (Escape, C-c, C-d)
|
|
279
|
+
*
|
|
280
|
+
* @throws {Error} If session doesn't exist or command fails
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* // Send Escape key to interrupt CLI processing
|
|
285
|
+
* await sendSpecialKey('my-session', 'Escape');
|
|
286
|
+
*
|
|
287
|
+
* // Send Ctrl+C for SIGINT
|
|
288
|
+
* await sendSpecialKey('my-session', 'C-c');
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
async function sendSpecialKey(sessionName, key) {
|
|
292
|
+
try {
|
|
293
|
+
await execAsync(`tmux send-keys -t "${sessionName}" ${key}`, { timeout: DEFAULT_TIMEOUT });
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
297
|
+
throw new Error(`Failed to send special key: ${errorMessage}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Git worktree management
|
|
4
|
+
* Scans and manages git worktrees
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.generateWorktreeId = generateWorktreeId;
|
|
11
|
+
exports.parseWorktreeOutput = parseWorktreeOutput;
|
|
12
|
+
exports.getRepositoryPaths = getRepositoryPaths;
|
|
13
|
+
exports.scanWorktrees = scanWorktrees;
|
|
14
|
+
exports.scanMultipleRepositories = scanMultipleRepositories;
|
|
15
|
+
exports.syncWorktreesToDB = syncWorktreesToDB;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const util_1 = require("util");
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const db_1 = require("./db");
|
|
20
|
+
const env_1 = require("./env");
|
|
21
|
+
/**
|
|
22
|
+
* Generate URL-safe ID from repository name and branch name
|
|
23
|
+
*
|
|
24
|
+
* @param branchName - Git branch name (e.g., "feature/foo", "main")
|
|
25
|
+
* @param repositoryName - Repository name (e.g., "MyRepo")
|
|
26
|
+
* @returns URL-safe ID (e.g., "myrepo-feature-foo", "myrepo-main")
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* generateWorktreeId('feature/foo', 'MyRepo') // => 'myrepo-feature-foo'
|
|
31
|
+
* generateWorktreeId('main', 'MyRepo') // => 'myrepo-main'
|
|
32
|
+
* generateWorktreeId('Feature/Foo', 'Repo') // => 'repo-feature-foo'
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function generateWorktreeId(branchName, repositoryName) {
|
|
36
|
+
if (!branchName) {
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
const sanitize = (str) => str
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.replace(/[^a-z0-9-]/g, '-') // Replace non-alphanumeric (except hyphen) with hyphen
|
|
42
|
+
.replace(/-+/g, '-') // Replace consecutive hyphens with single hyphen
|
|
43
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
|
44
|
+
const sanitizedBranch = sanitize(branchName);
|
|
45
|
+
if (repositoryName) {
|
|
46
|
+
const sanitizedRepo = sanitize(repositoryName);
|
|
47
|
+
return `${sanitizedRepo}-${sanitizedBranch}`;
|
|
48
|
+
}
|
|
49
|
+
return sanitizedBranch;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse git worktree list output
|
|
53
|
+
*
|
|
54
|
+
* @param output - Output from `git worktree list`
|
|
55
|
+
* @returns Array of parsed worktree information
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const output = `
|
|
60
|
+
* /path/to/main abc123 [main]
|
|
61
|
+
* /path/to/feature def456 [feature/foo]
|
|
62
|
+
* `;
|
|
63
|
+
* parseWorktreeOutput(output);
|
|
64
|
+
* // => [
|
|
65
|
+
* // { path: '/path/to/main', branch: 'main', commit: 'abc123' },
|
|
66
|
+
* // { path: '/path/to/feature', branch: 'feature/foo', commit: 'def456' }
|
|
67
|
+
* // ]
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function parseWorktreeOutput(output) {
|
|
71
|
+
if (!output || output.trim() === '') {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const lines = output.trim().split('\n');
|
|
75
|
+
const worktrees = [];
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed)
|
|
79
|
+
continue;
|
|
80
|
+
// Format: /path/to/worktree commit [branch]
|
|
81
|
+
// or: /path/to/worktree commit (detached HEAD)
|
|
82
|
+
const match = trimmed.match(/^(.+?)\s+([a-z0-9]+)\s+(?:\[(.+?)\]|\(detached HEAD\))/);
|
|
83
|
+
if (match) {
|
|
84
|
+
const [, pathStr, commit, branch] = match;
|
|
85
|
+
worktrees.push({
|
|
86
|
+
path: pathStr.trim(),
|
|
87
|
+
branch: branch || `detached-${commit}`,
|
|
88
|
+
commit,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return worktrees;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get repository paths from environment variables
|
|
96
|
+
* Supports both WORKTREE_REPOS (comma-separated) and CM_ROOT_DIR (single path)
|
|
97
|
+
*
|
|
98
|
+
* @returns Array of repository root paths
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // WORKTREE_REPOS="/path/to/repo1,/path/to/repo2"
|
|
103
|
+
* getRepositoryPaths(); // => ['/path/to/repo1', '/path/to/repo2']
|
|
104
|
+
*
|
|
105
|
+
* // CM_ROOT_DIR="/path/to/repo"
|
|
106
|
+
* getRepositoryPaths(); // => ['/path/to/repo']
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
function getRepositoryPaths() {
|
|
110
|
+
// Try WORKTREE_REPOS first (supports multiple repos)
|
|
111
|
+
const worktreeRepos = process.env.WORKTREE_REPOS;
|
|
112
|
+
if (worktreeRepos && worktreeRepos.trim()) {
|
|
113
|
+
return worktreeRepos
|
|
114
|
+
.split(',')
|
|
115
|
+
.map(p => p.trim())
|
|
116
|
+
.filter(p => p.length > 0);
|
|
117
|
+
}
|
|
118
|
+
// Fallback to CM_ROOT_DIR / MCBD_ROOT_DIR (Issue #76: env fallback support)
|
|
119
|
+
const rootDir = (0, env_1.getEnvByKey)('CM_ROOT_DIR');
|
|
120
|
+
if (rootDir && rootDir.trim()) {
|
|
121
|
+
return [rootDir.trim()];
|
|
122
|
+
}
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Scan git worktrees in a single repository
|
|
127
|
+
*
|
|
128
|
+
* @param rootDir - Root directory to scan for worktrees
|
|
129
|
+
* @returns Array of Worktree objects
|
|
130
|
+
*
|
|
131
|
+
* @throws {Error} If git command fails (except for "not a git repository")
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const worktrees = await scanWorktrees('/path/to/repo');
|
|
136
|
+
* console.log(worktrees[0].id); // => "main"
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
async function scanWorktrees(rootDir) {
|
|
140
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
141
|
+
try {
|
|
142
|
+
const { stdout } = await execAsync('git worktree list', {
|
|
143
|
+
cwd: rootDir,
|
|
144
|
+
});
|
|
145
|
+
const parsed = parseWorktreeOutput(stdout);
|
|
146
|
+
// Get repository name from path
|
|
147
|
+
const repositoryPath = path_1.default.resolve(rootDir);
|
|
148
|
+
const repositoryName = path_1.default.basename(repositoryPath);
|
|
149
|
+
// Filter and validate worktree paths
|
|
150
|
+
return parsed
|
|
151
|
+
.map((wt) => ({
|
|
152
|
+
id: generateWorktreeId(wt.branch, repositoryName),
|
|
153
|
+
name: wt.branch,
|
|
154
|
+
path: path_1.default.resolve(wt.path),
|
|
155
|
+
repositoryPath,
|
|
156
|
+
repositoryName,
|
|
157
|
+
}))
|
|
158
|
+
.filter((wt) => {
|
|
159
|
+
// Git worktrees can be outside the repo root, so we use a more lenient validation
|
|
160
|
+
// Only filter out obviously dangerous system paths
|
|
161
|
+
const dangerousPaths = ['/etc', '/root', '/sys', '/proc', '/dev', '/boot', '/bin', '/sbin', '/usr/bin', '/usr/sbin'];
|
|
162
|
+
const isDangerous = dangerousPaths.some(danger => wt.path.startsWith(danger));
|
|
163
|
+
if (isDangerous) {
|
|
164
|
+
console.warn(`Skipping potentially unsafe worktree path: ${wt.path}`);
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// Check for path traversal attempts in the path itself
|
|
168
|
+
if (wt.path.includes('\x00') || wt.path.includes('..')) {
|
|
169
|
+
console.warn(`Skipping path with potentially malicious characters: ${wt.path}`);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
// If not a git repository, return empty array
|
|
177
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
178
|
+
const errorCode = error.code;
|
|
179
|
+
if (errorMessage?.includes('not a git repository') ||
|
|
180
|
+
errorCode === 128) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
// Re-throw other errors
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Scan git worktrees in multiple repositories
|
|
189
|
+
*
|
|
190
|
+
* @param repositoryPaths - Array of repository root paths
|
|
191
|
+
* @returns Array of all Worktree objects from all repositories
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* const repos = ['/path/to/repo1', '/path/to/repo2'];
|
|
196
|
+
* const worktrees = await scanMultipleRepositories(repos);
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
async function scanMultipleRepositories(repositoryPaths) {
|
|
200
|
+
const allWorktrees = [];
|
|
201
|
+
for (const repoPath of repositoryPaths) {
|
|
202
|
+
try {
|
|
203
|
+
console.log(`Scanning repository: ${repoPath}`);
|
|
204
|
+
const worktrees = await scanWorktrees(repoPath);
|
|
205
|
+
allWorktrees.push(...worktrees);
|
|
206
|
+
console.log(` Found ${worktrees.length} worktree(s)`);
|
|
207
|
+
}
|
|
208
|
+
catch (_error) {
|
|
209
|
+
console.error(`Error scanning repository ${repoPath}:`, _error);
|
|
210
|
+
// Continue with other repositories even if one fails
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return allWorktrees;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Sync scanned worktrees to database
|
|
217
|
+
*
|
|
218
|
+
* @param db - Database instance
|
|
219
|
+
* @param worktrees - Array of worktrees to sync
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* const worktrees = await scanWorktrees('/path/to/repo');
|
|
224
|
+
* syncWorktreesToDB(db, worktrees);
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
function syncWorktreesToDB(db, worktrees) {
|
|
228
|
+
for (const worktree of worktrees) {
|
|
229
|
+
(0, db_1.upsertWorktree)(db, worktree);
|
|
230
|
+
}
|
|
231
|
+
}
|