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
package/README.md
CHANGED
|
@@ -49,11 +49,43 @@ Claude Code での開発経験があり、本業の傍らで個人開発を続
|
|
|
49
49
|
|
|
50
50
|
- macOS / Linux(tmux 依存のため Windows は非対応)
|
|
51
51
|
- Node.js v20+、npm、git、tmux、openssl
|
|
52
|
-
- Claude CLI
|
|
52
|
+
- Claude CLI(オプション)
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### インストール
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
```bash
|
|
57
|
+
npm install -g commandmate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### セットアップと起動
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
commandmate init # 依存チェック、環境設定、DB初期化
|
|
64
|
+
commandmate start --daemon # バックグラウンドで起動
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
ブラウザで http://localhost:3000 にアクセスしてください。
|
|
68
|
+
|
|
69
|
+
### CLI コマンド
|
|
70
|
+
|
|
71
|
+
| コマンド | 説明 |
|
|
72
|
+
|---------|------|
|
|
73
|
+
| `commandmate init` | 初期設定(対話形式) |
|
|
74
|
+
| `commandmate init --defaults` | 初期設定(デフォルト値) |
|
|
75
|
+
| `commandmate start --daemon` | バックグラウンド起動 |
|
|
76
|
+
| `commandmate start -p 3001` | ポート指定で起動 |
|
|
77
|
+
| `commandmate stop` | サーバー停止 |
|
|
78
|
+
| `commandmate status` | 状態確認 |
|
|
79
|
+
|
|
80
|
+
詳しくは [CLI セットアップガイド](./docs/user-guide/cli-setup-guide.md) を参照してください。
|
|
81
|
+
|
|
82
|
+
### モバイルからのアクセス
|
|
83
|
+
|
|
84
|
+
`commandmate init` で外部アクセスを有効にすると、`CM_BIND=0.0.0.0` と `CM_AUTH_TOKEN` が自動設定されます。同一LAN内から `http://<PCのIP>:3000` にアクセスします。
|
|
85
|
+
|
|
86
|
+
## 開発者向けセットアップ
|
|
87
|
+
|
|
88
|
+
コントリビューターや開発環境を構築する場合は、git clone を使用してください。
|
|
57
89
|
|
|
58
90
|
```bash
|
|
59
91
|
git clone https://github.com/Kewton/CommandMate.git
|
|
@@ -61,8 +93,6 @@ cd CommandMate
|
|
|
61
93
|
./scripts/setup.sh # 依存チェック、環境設定、ビルド、起動まで自動実行
|
|
62
94
|
```
|
|
63
95
|
|
|
64
|
-
ブラウザで http://localhost:3000 にアクセスしてください。
|
|
65
|
-
|
|
66
96
|
### 手動セットアップ(カスタマイズしたい場合)
|
|
67
97
|
|
|
68
98
|
```bash
|
|
@@ -76,7 +106,7 @@ npm run build
|
|
|
76
106
|
npm start
|
|
77
107
|
```
|
|
78
108
|
|
|
79
|
-
|
|
109
|
+
> **Note**: `./scripts/*` スクリプトは開発環境でのみ使用可能です。グローバルインストール(`npm install -g`)では `commandmate` CLI を使用してください。
|
|
80
110
|
|
|
81
111
|
> **Note**: 旧名称の環境変数(`MCBD_*`)も後方互換性のためサポートされていますが、新名称(`CM_*`)の使用を推奨します。
|
|
82
112
|
|
|
@@ -104,13 +134,14 @@ A: 現時点では個人利用を想定しています。複数人での同時
|
|
|
104
134
|
|
|
105
135
|
| ドキュメント | 説明 |
|
|
106
136
|
|-------------|------|
|
|
137
|
+
| [CLI セットアップガイド](./docs/user-guide/cli-setup-guide.md) | インストールと初期設定 |
|
|
138
|
+
| [Webアプリ操作ガイド](./docs/user-guide/webapp-guide.md) | Webアプリの基本操作 |
|
|
139
|
+
| [クイックスタート](./docs/user-guide/quick-start.md) | Claude Codeコマンドの使い方 |
|
|
107
140
|
| [コンセプト](./docs/concept.md) | ビジョンと解決する課題 |
|
|
108
141
|
| [アーキテクチャ](./docs/architecture.md) | システム設計 |
|
|
109
142
|
| [デプロイガイド](./docs/DEPLOYMENT.md) | 本番環境構築手順 |
|
|
110
143
|
| [移行ガイド](./docs/migration-to-commandmate.md) | MyCodeBranchDesk からの移行手順 |
|
|
111
144
|
| [UI/UXガイド](./docs/UI_UX_GUIDE.md) | UI実装の詳細 |
|
|
112
|
-
| [Webアプリ操作ガイド](./docs/user-guide/webapp-guide.md) | Webアプリの基本操作 |
|
|
113
|
-
| [クイックスタート](./docs/user-guide/quick-start.md) | Claude Codeコマンドの使い方 |
|
|
114
145
|
| [Trust & Safety](./docs/TRUST_AND_SAFETY.md) | セキュリティと権限の考え方 |
|
|
115
146
|
|
|
116
147
|
## Contributing
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Common CLI tool patterns for response detection
|
|
4
|
+
* Shared between response-poller.ts and API routes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.GEMINI_PROMPT_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
8
|
+
exports.detectThinking = detectThinking;
|
|
9
|
+
exports.getCliToolPatterns = getCliToolPatterns;
|
|
10
|
+
exports.stripAnsi = stripAnsi;
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const logger = (0, logger_1.createLogger)('cli-patterns');
|
|
13
|
+
/**
|
|
14
|
+
* Claude CLI spinner characters (expanded set)
|
|
15
|
+
* These are shown when Claude is thinking/processing
|
|
16
|
+
*/
|
|
17
|
+
exports.CLAUDE_SPINNER_CHARS = [
|
|
18
|
+
'✻', '✽', '⏺', '·', '∴', '✢', '✳', '✶',
|
|
19
|
+
'⦿', '◉', '●', '○', '◌', '◎', '⊙', '⊚',
|
|
20
|
+
'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏', // Braille spinner
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Claude thinking pattern
|
|
24
|
+
* Matches spinner character followed by activity text ending with …
|
|
25
|
+
* The text can contain spaces (e.g., "Verifying implementation (dead code detection)…")
|
|
26
|
+
*/
|
|
27
|
+
exports.CLAUDE_THINKING_PATTERN = new RegExp(`[${exports.CLAUDE_SPINNER_CHARS.join('')}]\\s+.+…|to interrupt\\)`, 'm');
|
|
28
|
+
/**
|
|
29
|
+
* Codex thinking pattern
|
|
30
|
+
* Matches activity indicators like "• Planning", "• Searching", etc.
|
|
31
|
+
*/
|
|
32
|
+
exports.CODEX_THINKING_PATTERN = /•\s*(Planning|Searching|Exploring|Running|Thinking|Working|Reading|Writing|Analyzing)/m;
|
|
33
|
+
/**
|
|
34
|
+
* Claude prompt pattern (waiting for input)
|
|
35
|
+
* Supports both legacy '>' and new '❯' (U+276F) prompt characters
|
|
36
|
+
*/
|
|
37
|
+
exports.CLAUDE_PROMPT_PATTERN = /^[>❯]\s*$/m;
|
|
38
|
+
/**
|
|
39
|
+
* Claude separator pattern
|
|
40
|
+
*/
|
|
41
|
+
exports.CLAUDE_SEPARATOR_PATTERN = /^─{10,}$/m;
|
|
42
|
+
/**
|
|
43
|
+
* Codex prompt pattern
|
|
44
|
+
*/
|
|
45
|
+
exports.CODEX_PROMPT_PATTERN = /^›\s+.+/m;
|
|
46
|
+
/**
|
|
47
|
+
* Codex separator pattern
|
|
48
|
+
*/
|
|
49
|
+
exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
|
|
50
|
+
/**
|
|
51
|
+
* Gemini shell prompt pattern
|
|
52
|
+
*/
|
|
53
|
+
exports.GEMINI_PROMPT_PATTERN = /^(%|\$|.*@.*[%$#])\s*$/m;
|
|
54
|
+
/**
|
|
55
|
+
* Detect if CLI tool is showing "thinking" indicator
|
|
56
|
+
*/
|
|
57
|
+
function detectThinking(cliToolId, content) {
|
|
58
|
+
const log = logger.withContext({ cliToolId });
|
|
59
|
+
log.debug('detectThinking:check', { contentLength: content.length });
|
|
60
|
+
let result;
|
|
61
|
+
switch (cliToolId) {
|
|
62
|
+
case 'claude':
|
|
63
|
+
result = exports.CLAUDE_THINKING_PATTERN.test(content);
|
|
64
|
+
break;
|
|
65
|
+
case 'codex':
|
|
66
|
+
result = exports.CODEX_THINKING_PATTERN.test(content);
|
|
67
|
+
break;
|
|
68
|
+
case 'gemini':
|
|
69
|
+
// Gemini doesn't have a thinking indicator in one-shot mode
|
|
70
|
+
result = false;
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
result = exports.CLAUDE_THINKING_PATTERN.test(content);
|
|
74
|
+
}
|
|
75
|
+
log.debug('detectThinking:result', { isThinking: result });
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get CLI tool patterns for response extraction
|
|
80
|
+
*/
|
|
81
|
+
function getCliToolPatterns(cliToolId) {
|
|
82
|
+
switch (cliToolId) {
|
|
83
|
+
case 'claude':
|
|
84
|
+
return {
|
|
85
|
+
promptPattern: exports.CLAUDE_PROMPT_PATTERN,
|
|
86
|
+
separatorPattern: exports.CLAUDE_SEPARATOR_PATTERN,
|
|
87
|
+
thinkingPattern: exports.CLAUDE_THINKING_PATTERN,
|
|
88
|
+
skipPatterns: [
|
|
89
|
+
/^─{10,}$/, // Separator lines
|
|
90
|
+
/^[>❯]\s*$/, // Prompt line (legacy '>' and new '❯')
|
|
91
|
+
exports.CLAUDE_THINKING_PATTERN, // Thinking indicators
|
|
92
|
+
/^\s*[⎿⏋]\s+Tip:/, // Tip lines
|
|
93
|
+
/^\s*Tip:/, // Tip lines
|
|
94
|
+
/^\s*\?\s*for shortcuts/, // Shortcuts hint
|
|
95
|
+
/to interrupt\)/, // Part of "esc to interrupt" message
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
case 'codex':
|
|
99
|
+
return {
|
|
100
|
+
promptPattern: exports.CODEX_PROMPT_PATTERN,
|
|
101
|
+
separatorPattern: exports.CODEX_SEPARATOR_PATTERN,
|
|
102
|
+
thinkingPattern: exports.CODEX_THINKING_PATTERN,
|
|
103
|
+
skipPatterns: [
|
|
104
|
+
/^─.*─+$/, // Separator lines
|
|
105
|
+
/^›\s*$/, // Empty prompt line
|
|
106
|
+
/^›\s+(Implement|Find and fix|Type)/, // New prompt suggestions
|
|
107
|
+
exports.CODEX_THINKING_PATTERN, // Activity indicators
|
|
108
|
+
/^\s*\d+%\s+context left/, // Context indicator
|
|
109
|
+
/^\s*for shortcuts$/, // Shortcuts hint
|
|
110
|
+
/╭─+╮/, // Box drawing (top)
|
|
111
|
+
/╰─+╯/, // Box drawing (bottom)
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
case 'gemini':
|
|
115
|
+
return {
|
|
116
|
+
promptPattern: exports.GEMINI_PROMPT_PATTERN,
|
|
117
|
+
separatorPattern: /^gemini\s+--\s+/m,
|
|
118
|
+
thinkingPattern: /(?!)/m, // Never matches - one-shot execution
|
|
119
|
+
skipPatterns: [
|
|
120
|
+
/^gemini\s+--\s+/, // Command line itself
|
|
121
|
+
exports.GEMINI_PROMPT_PATTERN, // Shell prompt lines
|
|
122
|
+
/^\s*$/, // Empty lines
|
|
123
|
+
],
|
|
124
|
+
};
|
|
125
|
+
default:
|
|
126
|
+
// Default to Claude patterns
|
|
127
|
+
return getCliToolPatterns('claude');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Strip ANSI escape codes from a string
|
|
132
|
+
* Optimized version at module level for performance
|
|
133
|
+
*/
|
|
134
|
+
const ANSI_PATTERN = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\[[0-9;]*m/g;
|
|
135
|
+
function stripAnsi(str) {
|
|
136
|
+
return str.replace(ANSI_PATTERN, '');
|
|
137
|
+
}
|