commandmate 0.1.5 → 0.1.7
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/3jNZMmFnQhc5G7met-OU4/_buildManifest.js +1 -0
- package/.next/static/3jNZMmFnQhc5G7met-OU4/_ssgManifest.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/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/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +143 -27
- package/dist/cli/types/index.d.ts +18 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.d.ts +25 -0
- package/dist/cli/utils/env-setup.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.js +56 -1
- package/dist/cli/utils/prompt.d.ts +68 -0
- package/dist/cli/utils/prompt.d.ts.map +1 -0
- package/dist/cli/utils/prompt.js +208 -0
- 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,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive Prompt Utilities
|
|
4
|
+
* Issue #119: Interactive init support
|
|
5
|
+
*
|
|
6
|
+
* Provides readline-based prompts for CLI commands.
|
|
7
|
+
* Reference implementation: scripts/setup-env.sh
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.closeReadline = closeReadline;
|
|
44
|
+
exports.expandTilde = expandTilde;
|
|
45
|
+
exports.resolvePath = resolvePath;
|
|
46
|
+
exports.validatePort = validatePort;
|
|
47
|
+
exports.prompt = prompt;
|
|
48
|
+
exports.confirm = confirm;
|
|
49
|
+
exports.isInteractive = isInteractive;
|
|
50
|
+
const readline = __importStar(require("readline"));
|
|
51
|
+
const os_1 = require("os");
|
|
52
|
+
const path_1 = require("path");
|
|
53
|
+
/**
|
|
54
|
+
* Readline interface singleton
|
|
55
|
+
* Reused across prompts to avoid creating multiple interfaces
|
|
56
|
+
*/
|
|
57
|
+
let rlInstance = null;
|
|
58
|
+
/**
|
|
59
|
+
* Get or create readline interface
|
|
60
|
+
*/
|
|
61
|
+
function getReadlineInterface() {
|
|
62
|
+
if (!rlInstance) {
|
|
63
|
+
rlInstance = readline.createInterface({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return rlInstance;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Close readline interface
|
|
72
|
+
* Should be called when all prompts are done
|
|
73
|
+
*/
|
|
74
|
+
function closeReadline() {
|
|
75
|
+
if (rlInstance) {
|
|
76
|
+
rlInstance.close();
|
|
77
|
+
rlInstance = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Expand tilde (~) to home directory
|
|
82
|
+
*
|
|
83
|
+
* @param path - Path that may contain tilde
|
|
84
|
+
* @returns Path with tilde expanded to home directory
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* expandTilde('~/repos') // '/Users/xxx/repos'
|
|
88
|
+
* expandTilde('/absolute/path') // '/absolute/path'
|
|
89
|
+
*/
|
|
90
|
+
function expandTilde(path) {
|
|
91
|
+
if (path.startsWith('~/')) {
|
|
92
|
+
return path.replace(/^~/, (0, os_1.homedir)());
|
|
93
|
+
}
|
|
94
|
+
if (path === '~') {
|
|
95
|
+
return (0, os_1.homedir)();
|
|
96
|
+
}
|
|
97
|
+
return path;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolve path to absolute path
|
|
101
|
+
* Handles tilde expansion and relative paths
|
|
102
|
+
*
|
|
103
|
+
* @param path - Path to resolve
|
|
104
|
+
* @returns Absolute path
|
|
105
|
+
*/
|
|
106
|
+
function resolvePath(path) {
|
|
107
|
+
const expanded = expandTilde(path);
|
|
108
|
+
return (0, path_1.resolve)(expanded);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Validate port number
|
|
112
|
+
*
|
|
113
|
+
* @param input - Port number as string
|
|
114
|
+
* @returns Error message or true if valid
|
|
115
|
+
*/
|
|
116
|
+
function validatePort(input) {
|
|
117
|
+
const port = parseInt(input, 10);
|
|
118
|
+
if (isNaN(port)) {
|
|
119
|
+
return 'Port must be a number';
|
|
120
|
+
}
|
|
121
|
+
if (port < 1 || port > 65535) {
|
|
122
|
+
return 'Port must be between 1 and 65535';
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Interactive prompt with default value and validation
|
|
128
|
+
*
|
|
129
|
+
* @param question - Question to display
|
|
130
|
+
* @param options - Prompt options (default, validate)
|
|
131
|
+
* @returns User input or default value
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* const port = await prompt('Server port', { default: '3000', validate: validatePort });
|
|
135
|
+
*/
|
|
136
|
+
async function prompt(question, options = {}) {
|
|
137
|
+
const rl = getReadlineInterface();
|
|
138
|
+
const { default: defaultValue, validate } = options;
|
|
139
|
+
const displayQuestion = defaultValue
|
|
140
|
+
? `? ${question} [${defaultValue}]: `
|
|
141
|
+
: `? ${question}: `;
|
|
142
|
+
return new Promise((resolvePromise) => {
|
|
143
|
+
const askQuestion = () => {
|
|
144
|
+
rl.question(displayQuestion, (answer) => {
|
|
145
|
+
const trimmedAnswer = answer.trim();
|
|
146
|
+
const result = trimmedAnswer || defaultValue || '';
|
|
147
|
+
// Validate if validator provided
|
|
148
|
+
if (validate && result) {
|
|
149
|
+
const validationResult = validate(result);
|
|
150
|
+
if (validationResult !== true) {
|
|
151
|
+
console.log(` Error: ${validationResult}`);
|
|
152
|
+
askQuestion(); // Re-ask
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
resolvePromise(result);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
askQuestion();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Interactive Yes/No confirmation
|
|
164
|
+
*
|
|
165
|
+
* @param question - Question to display
|
|
166
|
+
* @param options - Confirm options (default)
|
|
167
|
+
* @returns true for Yes, false for No
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* const enableExternal = await confirm('Enable external access?', { default: false });
|
|
171
|
+
*/
|
|
172
|
+
async function confirm(question, options = {}) {
|
|
173
|
+
const rl = getReadlineInterface();
|
|
174
|
+
const defaultValue = options.default ?? false;
|
|
175
|
+
const hint = defaultValue ? '(Y/n)' : '(y/N)';
|
|
176
|
+
const displayQuestion = `? ${question} ${hint}: `;
|
|
177
|
+
return new Promise((resolvePromise) => {
|
|
178
|
+
const askQuestion = () => {
|
|
179
|
+
rl.question(displayQuestion, (answer) => {
|
|
180
|
+
const trimmedAnswer = answer.trim().toLowerCase();
|
|
181
|
+
if (trimmedAnswer === '') {
|
|
182
|
+
resolvePromise(defaultValue);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (trimmedAnswer === 'y' || trimmedAnswer === 'yes') {
|
|
186
|
+
resolvePromise(true);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (trimmedAnswer === 'n' || trimmedAnswer === 'no') {
|
|
190
|
+
resolvePromise(false);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
// Invalid input - re-ask
|
|
194
|
+
console.log(' Please answer with y/n or yes/no');
|
|
195
|
+
askQuestion();
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
askQuestion();
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Check if running in interactive mode (TTY)
|
|
203
|
+
*
|
|
204
|
+
* @returns true if stdin is a TTY (interactive terminal)
|
|
205
|
+
*/
|
|
206
|
+
function isInteractive() {
|
|
207
|
+
return process.stdin.isTTY === true;
|
|
208
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Custom Next.js Server with WebSocket Support
|
|
4
|
+
* Integrates WebSocket server for real-time communication
|
|
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
|
+
// IMPORTANT: Register uncaught exception handler FIRST, before any imports
|
|
11
|
+
// This ensures we catch WebSocket frame errors before other handlers
|
|
12
|
+
process.on('uncaughtException', (error) => {
|
|
13
|
+
// Check for WebSocket-related errors that are non-fatal
|
|
14
|
+
const isWebSocketError = error.code === 'WS_ERR_INVALID_UTF8' ||
|
|
15
|
+
error.code === 'WS_ERR_INVALID_CLOSE_CODE' ||
|
|
16
|
+
error.code === 'WS_ERR_UNEXPECTED_RSV_1' ||
|
|
17
|
+
error.code === 'ECONNRESET' ||
|
|
18
|
+
error.code === 'EPIPE' ||
|
|
19
|
+
(error instanceof RangeError && error.message?.includes('Invalid WebSocket frame')) ||
|
|
20
|
+
error.message?.includes('write after end');
|
|
21
|
+
if (isWebSocketError) {
|
|
22
|
+
// Silently ignore these non-fatal WebSocket frame errors
|
|
23
|
+
// They commonly occur when mobile browsers send malformed close frames
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// For other uncaught exceptions, log and exit
|
|
27
|
+
console.error('Uncaught exception:', error);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
const http_1 = require("http");
|
|
31
|
+
const url_1 = require("url");
|
|
32
|
+
const next_1 = __importDefault(require("next"));
|
|
33
|
+
const ws_server_1 = require("./src/lib/ws-server");
|
|
34
|
+
const worktrees_1 = require("./src/lib/worktrees");
|
|
35
|
+
const db_instance_1 = require("./src/lib/db-instance");
|
|
36
|
+
const response_poller_1 = require("./src/lib/response-poller");
|
|
37
|
+
const db_migrations_1 = require("./src/lib/db-migrations");
|
|
38
|
+
const env_1 = require("./src/lib/env");
|
|
39
|
+
const dev = process.env.NODE_ENV !== 'production';
|
|
40
|
+
const hostname = (0, env_1.getEnvByKey)('CM_BIND') || '127.0.0.1';
|
|
41
|
+
const port = parseInt((0, env_1.getEnvByKey)('CM_PORT') || '3000', 10);
|
|
42
|
+
// Create Next.js app
|
|
43
|
+
const app = (0, next_1.default)({ dev, hostname, port });
|
|
44
|
+
const handle = app.getRequestHandler();
|
|
45
|
+
app.prepare().then(() => {
|
|
46
|
+
// Create HTTP server
|
|
47
|
+
const server = (0, http_1.createServer)(async (req, res) => {
|
|
48
|
+
try {
|
|
49
|
+
const parsedUrl = (0, url_1.parse)(req.url, true);
|
|
50
|
+
await handle(req, res, parsedUrl);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.error('Error handling request', err);
|
|
54
|
+
res.statusCode = 500;
|
|
55
|
+
res.end('Internal Server Error');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// Setup WebSocket server
|
|
59
|
+
(0, ws_server_1.setupWebSocket)(server);
|
|
60
|
+
// Scan and sync worktrees on startup
|
|
61
|
+
async function initializeWorktrees() {
|
|
62
|
+
try {
|
|
63
|
+
// Run database migrations first
|
|
64
|
+
console.log('Running database migrations...');
|
|
65
|
+
const db = (0, db_instance_1.getDbInstance)();
|
|
66
|
+
(0, db_migrations_1.runMigrations)(db);
|
|
67
|
+
// Get repository paths from environment variables
|
|
68
|
+
const repositoryPaths = (0, worktrees_1.getRepositoryPaths)();
|
|
69
|
+
if (repositoryPaths.length === 0) {
|
|
70
|
+
console.warn('Warning: No repository paths configured');
|
|
71
|
+
console.warn('Set WORKTREE_REPOS (comma-separated) or MCBD_ROOT_DIR');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
console.log(`Configured repositories: ${repositoryPaths.length}`);
|
|
75
|
+
repositoryPaths.forEach((path, i) => {
|
|
76
|
+
console.log(` ${i + 1}. ${path}`);
|
|
77
|
+
});
|
|
78
|
+
// Scan all repositories
|
|
79
|
+
const worktrees = await (0, worktrees_1.scanMultipleRepositories)(repositoryPaths);
|
|
80
|
+
// Sync to database
|
|
81
|
+
(0, worktrees_1.syncWorktreesToDB)(db, worktrees);
|
|
82
|
+
console.log(`✓ Total: ${worktrees.length} worktree(s) synced to database`);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error('Error initializing worktrees:', error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
server.listen(port, async (err) => {
|
|
89
|
+
if (err)
|
|
90
|
+
throw err;
|
|
91
|
+
console.log(`> Ready on http://${hostname}:${port}`);
|
|
92
|
+
console.log(`> WebSocket server ready`);
|
|
93
|
+
// Initialize worktrees after server starts
|
|
94
|
+
await initializeWorktrees();
|
|
95
|
+
});
|
|
96
|
+
// Graceful shutdown with timeout
|
|
97
|
+
let isShuttingDown = false;
|
|
98
|
+
function gracefulShutdown(signal) {
|
|
99
|
+
if (isShuttingDown) {
|
|
100
|
+
console.log('Shutdown already in progress, forcing exit...');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
isShuttingDown = true;
|
|
104
|
+
console.log(`${signal} received: shutting down...`);
|
|
105
|
+
// Stop polling first
|
|
106
|
+
(0, response_poller_1.stopAllPolling)();
|
|
107
|
+
// Close WebSocket connections immediately (don't wait)
|
|
108
|
+
(0, ws_server_1.closeWebSocket)();
|
|
109
|
+
// Force exit after 3 seconds if graceful shutdown fails
|
|
110
|
+
const forceExitTimeout = setTimeout(() => {
|
|
111
|
+
console.log('Graceful shutdown timeout, forcing exit...');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}, 3000);
|
|
114
|
+
// Try graceful HTTP server close
|
|
115
|
+
server.close(() => {
|
|
116
|
+
clearTimeout(forceExitTimeout);
|
|
117
|
+
console.log('Server closed gracefully');
|
|
118
|
+
process.exit(0);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
122
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
123
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude output parsing utilities
|
|
4
|
+
* Shared between webhook handlers and polling logic
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.parseClaudeOutput = parseClaudeOutput;
|
|
8
|
+
const LOG_FILE_REGEX = /📄 Session log: (.+?\/([^\/\s]+\.jsonl))/;
|
|
9
|
+
const REQUEST_ID_REGEX = /Request ID: ([^\s\n]+)/;
|
|
10
|
+
const SUMMARY_REGEX = /Summary: (.+?)(?:\n─|$)/s;
|
|
11
|
+
/**
|
|
12
|
+
* Parse Claude CLI output and extract metadata that the UI relies on.
|
|
13
|
+
*
|
|
14
|
+
* @param output Raw tmux capture string
|
|
15
|
+
*/
|
|
16
|
+
function parseClaudeOutput(output) {
|
|
17
|
+
const result = {
|
|
18
|
+
content: output,
|
|
19
|
+
};
|
|
20
|
+
const logFileMatch = LOG_FILE_REGEX.exec(output);
|
|
21
|
+
if (logFileMatch) {
|
|
22
|
+
result.logFileName = logFileMatch[2];
|
|
23
|
+
}
|
|
24
|
+
const requestIdMatch = REQUEST_ID_REGEX.exec(output);
|
|
25
|
+
if (requestIdMatch) {
|
|
26
|
+
result.requestId = requestIdMatch[1];
|
|
27
|
+
}
|
|
28
|
+
const summaryMatch = SUMMARY_REGEX.exec(output);
|
|
29
|
+
if (summaryMatch) {
|
|
30
|
+
result.summary = summaryMatch[1].trim();
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Claude CLI session management
|
|
4
|
+
* Manages Claude CLI sessions within tmux for each worktree
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getSessionName = getSessionName;
|
|
8
|
+
exports.isClaudeInstalled = isClaudeInstalled;
|
|
9
|
+
exports.isClaudeRunning = isClaudeRunning;
|
|
10
|
+
exports.getClaudeSessionState = getClaudeSessionState;
|
|
11
|
+
exports.startClaudeSession = startClaudeSession;
|
|
12
|
+
exports.sendMessageToClaude = sendMessageToClaude;
|
|
13
|
+
exports.captureClaudeOutput = captureClaudeOutput;
|
|
14
|
+
exports.stopClaudeSession = stopClaudeSession;
|
|
15
|
+
exports.restartClaudeSession = restartClaudeSession;
|
|
16
|
+
const tmux_1 = require("./tmux");
|
|
17
|
+
const child_process_1 = require("child_process");
|
|
18
|
+
const util_1 = require("util");
|
|
19
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
20
|
+
/**
|
|
21
|
+
* Cached Claude CLI path
|
|
22
|
+
*/
|
|
23
|
+
let cachedClaudePath = null;
|
|
24
|
+
/**
|
|
25
|
+
* Get Claude CLI path dynamically
|
|
26
|
+
* Uses CLAUDE_PATH environment variable if set, otherwise finds via 'which'
|
|
27
|
+
*/
|
|
28
|
+
async function getClaudePath() {
|
|
29
|
+
// Return cached path if available
|
|
30
|
+
if (cachedClaudePath) {
|
|
31
|
+
return cachedClaudePath;
|
|
32
|
+
}
|
|
33
|
+
// Check environment variable first
|
|
34
|
+
if (process.env.CLAUDE_PATH) {
|
|
35
|
+
cachedClaudePath = process.env.CLAUDE_PATH;
|
|
36
|
+
return cachedClaudePath;
|
|
37
|
+
}
|
|
38
|
+
// Find claude via 'which' command
|
|
39
|
+
try {
|
|
40
|
+
const { stdout } = await execAsync('which claude', { timeout: 5000 });
|
|
41
|
+
cachedClaudePath = stdout.trim();
|
|
42
|
+
return cachedClaudePath;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Fallback to common paths
|
|
46
|
+
const fallbackPaths = [
|
|
47
|
+
'/opt/homebrew/bin/claude', // macOS Homebrew (Apple Silicon)
|
|
48
|
+
'/usr/local/bin/claude', // macOS Homebrew (Intel) / Linux
|
|
49
|
+
'/usr/bin/claude', // Linux system install
|
|
50
|
+
];
|
|
51
|
+
for (const path of fallbackPaths) {
|
|
52
|
+
try {
|
|
53
|
+
await execAsync(`test -x "${path}"`, { timeout: 1000 });
|
|
54
|
+
cachedClaudePath = path;
|
|
55
|
+
return cachedClaudePath;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Path not found, try next
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
throw new Error('Claude CLI not found. Set CLAUDE_PATH environment variable or install Claude CLI.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get tmux session name for a worktree
|
|
66
|
+
*
|
|
67
|
+
* @param worktreeId - Worktree ID
|
|
68
|
+
* @returns tmux session name
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* getSessionName('feature-foo') // => 'mcbd-claude-feature-foo'
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
function getSessionName(worktreeId) {
|
|
76
|
+
return `mcbd-claude-${worktreeId}`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if Claude is installed and available
|
|
80
|
+
*
|
|
81
|
+
* @returns True if Claude CLI is available
|
|
82
|
+
*/
|
|
83
|
+
async function isClaudeInstalled() {
|
|
84
|
+
try {
|
|
85
|
+
await execAsync('which claude', { timeout: 5000 });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if Claude session is running
|
|
94
|
+
*
|
|
95
|
+
* @param worktreeId - Worktree ID
|
|
96
|
+
* @returns True if Claude session exists and is running
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const running = await isClaudeRunning('feature-foo');
|
|
101
|
+
* if (running) {
|
|
102
|
+
* console.log('Claude is ready');
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
async function isClaudeRunning(worktreeId) {
|
|
107
|
+
const sessionName = getSessionName(worktreeId);
|
|
108
|
+
return await (0, tmux_1.hasSession)(sessionName);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get Claude session state
|
|
112
|
+
*
|
|
113
|
+
* @param worktreeId - Worktree ID
|
|
114
|
+
* @returns Session state information
|
|
115
|
+
*/
|
|
116
|
+
async function getClaudeSessionState(worktreeId) {
|
|
117
|
+
const sessionName = getSessionName(worktreeId);
|
|
118
|
+
const isRunning = await (0, tmux_1.hasSession)(sessionName);
|
|
119
|
+
return {
|
|
120
|
+
sessionName,
|
|
121
|
+
isRunning,
|
|
122
|
+
lastActivity: new Date(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Start a Claude CLI session in tmux
|
|
127
|
+
*
|
|
128
|
+
* @param options - Session options
|
|
129
|
+
* @throws {Error} If Claude CLI is not installed or session creation fails
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* await startClaudeSession({
|
|
134
|
+
* worktreeId: 'feature-foo',
|
|
135
|
+
* worktreePath: '/path/to/worktree',
|
|
136
|
+
* baseUrl: 'http://localhost:3000',
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
async function startClaudeSession(options) {
|
|
141
|
+
const { worktreeId, worktreePath } = options;
|
|
142
|
+
// Check if Claude is installed
|
|
143
|
+
const claudeAvailable = await isClaudeInstalled();
|
|
144
|
+
if (!claudeAvailable) {
|
|
145
|
+
throw new Error('Claude CLI is not installed or not in PATH');
|
|
146
|
+
}
|
|
147
|
+
const sessionName = getSessionName(worktreeId);
|
|
148
|
+
// Check if session already exists
|
|
149
|
+
const exists = await (0, tmux_1.hasSession)(sessionName);
|
|
150
|
+
if (exists) {
|
|
151
|
+
console.log(`Claude session ${sessionName} already exists`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
// Create tmux session with large history buffer for Claude output
|
|
156
|
+
await (0, tmux_1.createSession)({
|
|
157
|
+
sessionName,
|
|
158
|
+
workingDirectory: worktreePath,
|
|
159
|
+
historyLimit: 50000,
|
|
160
|
+
});
|
|
161
|
+
// Get Claude CLI path dynamically
|
|
162
|
+
const claudePath = await getClaudePath();
|
|
163
|
+
// Start Claude CLI in interactive mode using dynamically resolved path
|
|
164
|
+
await (0, tmux_1.sendKeys)(sessionName, claudePath, true);
|
|
165
|
+
// Wait for Claude to initialize with dynamic detection
|
|
166
|
+
// Check for Claude prompt instead of fixed delay
|
|
167
|
+
const maxWaitTime = 10000; // 10 seconds max
|
|
168
|
+
const pollInterval = 500; // Check every 500ms
|
|
169
|
+
const startTime = Date.now();
|
|
170
|
+
while (Date.now() - startTime < maxWaitTime) {
|
|
171
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
172
|
+
try {
|
|
173
|
+
const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -50 });
|
|
174
|
+
// Claude is ready when we see the prompt (> ) or separator line
|
|
175
|
+
if (/^>\s*$/m.test(output) || /^─{10,}$/m.test(output)) {
|
|
176
|
+
console.log(`✓ Claude initialized in ${Date.now() - startTime}ms`);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Ignore capture errors during initialization
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
console.log(`✓ Started Claude session: ${sessionName}`);
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
188
|
+
throw new Error(`Failed to start Claude session: ${errorMessage}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Send a message to Claude CLI
|
|
193
|
+
*
|
|
194
|
+
* @param worktreeId - Worktree ID
|
|
195
|
+
* @param message - Message content to send
|
|
196
|
+
* @throws {Error} If session doesn't exist
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* await sendMessageToClaude('feature-foo', 'Explain this code');
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
async function sendMessageToClaude(worktreeId, message) {
|
|
204
|
+
const sessionName = getSessionName(worktreeId);
|
|
205
|
+
// Check if session exists
|
|
206
|
+
const exists = await (0, tmux_1.hasSession)(sessionName);
|
|
207
|
+
if (!exists) {
|
|
208
|
+
throw new Error(`Claude session ${sessionName} does not exist. Start the session first.`);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
// Send message to Claude (without Enter)
|
|
212
|
+
await (0, tmux_1.sendKeys)(sessionName, message, false);
|
|
213
|
+
// Wait a moment for the text to be typed
|
|
214
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
215
|
+
// Send Enter key to submit (single Enter submits in Claude Code CLI)
|
|
216
|
+
await execAsync(`tmux send-keys -t "${sessionName}" C-m`);
|
|
217
|
+
// Wait a moment for the message to be processed
|
|
218
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
219
|
+
console.log(`✓ Sent message to Claude session: ${sessionName}`);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
223
|
+
throw new Error(`Failed to send message to Claude: ${errorMessage}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Capture Claude session output
|
|
228
|
+
*
|
|
229
|
+
* @param worktreeId - Worktree ID
|
|
230
|
+
* @param lines - Number of lines to capture (default: 1000)
|
|
231
|
+
* @returns Captured output
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```typescript
|
|
235
|
+
* const output = await captureClaudeOutput('feature-foo');
|
|
236
|
+
* console.log(output);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
async function captureClaudeOutput(worktreeId, lines = 1000) {
|
|
240
|
+
const sessionName = getSessionName(worktreeId);
|
|
241
|
+
// Check if session exists
|
|
242
|
+
const exists = await (0, tmux_1.hasSession)(sessionName);
|
|
243
|
+
if (!exists) {
|
|
244
|
+
throw new Error(`Claude session ${sessionName} does not exist`);
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
return await (0, tmux_1.capturePane)(sessionName, { startLine: -lines });
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
251
|
+
throw new Error(`Failed to capture Claude output: ${errorMessage}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Stop a Claude session
|
|
256
|
+
*
|
|
257
|
+
* @param worktreeId - Worktree ID
|
|
258
|
+
* @returns True if session was stopped, false if it didn't exist
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* await stopClaudeSession('feature-foo');
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
async function stopClaudeSession(worktreeId) {
|
|
266
|
+
const sessionName = getSessionName(worktreeId);
|
|
267
|
+
try {
|
|
268
|
+
// Send Ctrl+D to exit Claude gracefully
|
|
269
|
+
const exists = await (0, tmux_1.hasSession)(sessionName);
|
|
270
|
+
if (exists) {
|
|
271
|
+
await (0, tmux_1.sendKeys)(sessionName, '', false);
|
|
272
|
+
// Send Ctrl+D (ASCII 4)
|
|
273
|
+
await execAsync(`tmux send-keys -t "${sessionName}" C-d`);
|
|
274
|
+
// Wait a moment for Claude to exit
|
|
275
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
276
|
+
}
|
|
277
|
+
// Kill the tmux session
|
|
278
|
+
const killed = await (0, tmux_1.killSession)(sessionName);
|
|
279
|
+
if (killed) {
|
|
280
|
+
console.log(`✓ Stopped Claude session: ${sessionName}`);
|
|
281
|
+
}
|
|
282
|
+
return killed;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
286
|
+
console.error(`Error stopping Claude session: ${errorMessage}`);
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Restart a Claude session
|
|
292
|
+
*
|
|
293
|
+
* @param options - Session options
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* await restartClaudeSession({
|
|
298
|
+
* worktreeId: 'feature-foo',
|
|
299
|
+
* worktreePath: '/path/to/worktree',
|
|
300
|
+
* baseUrl: 'http://localhost:3000',
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
async function restartClaudeSession(options) {
|
|
305
|
+
const { worktreeId } = options;
|
|
306
|
+
// Stop existing session
|
|
307
|
+
await stopClaudeSession(worktreeId);
|
|
308
|
+
// Wait a moment before restarting
|
|
309
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
310
|
+
// Start new session
|
|
311
|
+
await startClaudeSession(options);
|
|
312
|
+
}
|