commandmate 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -0
- package/.next/app-build-manifest.json +72 -0
- package/.next/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +32 -0
- package/.next/cache/.tsbuildinfo +1 -0
- package/.next/cache/config.json +7 -0
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/export-marker.json +1 -0
- package/.next/images-manifest.json +1 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +1 -0
- package/.next/react-loadable-manifest.json +249 -0
- package/.next/required-server-files.json +1 -0
- package/.next/routes-manifest.json +1 -0
- package/.next/server/app/_not-found/page.js +1 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +6 -0
- package/.next/server/app/_not-found.rsc +10 -0
- package/.next/server/app/api/external-apps/[id]/health/route.js +45 -0
- package/.next/server/app/api/external-apps/[id]/health/route.js.nft.json +1 -0
- package/.next/server/app/api/external-apps/[id]/route.js +45 -0
- package/.next/server/app/api/external-apps/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/external-apps/route.js +45 -0
- package/.next/server/app/api/external-apps/route.js.nft.json +1 -0
- package/.next/server/app/api/hooks/claude-done/route.js +19 -0
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -0
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/clone/route.js +1 -0
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/route.js +1 -0
- package/.next/server/app/api/repositories/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/scan/route.js +1 -0
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -0
- package/.next/server/app/api/repositories/sync/route.js +1 -0
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -0
- package/.next/server/app/api/slash-commands/route.js +1 -0
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -0
- package/.next/server/app/api/slash-commands.body +1 -0
- package/.next/server/app/api/slash-commands.meta +1 -0
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/capture/route.js +2 -0
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/logs/route.js +19 -0
- package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/memos/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/messages/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/search/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/search/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/tree/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/viewed/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/route.js +1 -0
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -0
- package/.next/server/app/apple-icon.png/route.js +1 -0
- package/.next/server/app/apple-icon.png/route.js.nft.json +1 -0
- package/.next/server/app/apple-icon.png.body +0 -0
- package/.next/server/app/apple-icon.png.meta +1 -0
- package/.next/server/app/icon.png/route.js +1 -0
- package/.next/server/app/icon.png/route.js.nft.json +1 -0
- package/.next/server/app/icon.png.body +0 -0
- package/.next/server/app/icon.png.meta +1 -0
- package/.next/server/app/index.html +9 -0
- package/.next/server/app/index.meta +5 -0
- package/.next/server/app/index.rsc +8 -0
- package/.next/server/app/page.js +16 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app/proxy/[...path]/route.js +45 -0
- package/.next/server/app/proxy/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/page.js +21 -0
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js +4 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/terminal/page.js +6 -0
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -0
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -0
- package/.next/server/app-paths-manifest.json +46 -0
- package/.next/server/chunks/1318.js +29 -0
- package/.next/server/chunks/1528.js +1 -0
- package/.next/server/chunks/1682.js +6 -0
- package/.next/server/chunks/2518.js +12 -0
- package/.next/server/chunks/3053.js +1 -0
- package/.next/server/chunks/3673.js +1 -0
- package/.next/server/chunks/3853.js +1 -0
- package/.next/server/chunks/434.js +1 -0
- package/.next/server/chunks/4471.js +2 -0
- package/.next/server/chunks/4893.js +2 -0
- package/.next/server/chunks/5972.js +12 -0
- package/.next/server/chunks/6550.js +1 -0
- package/.next/server/chunks/6621.js +1 -0
- package/.next/server/chunks/7213.js +1 -0
- package/.next/server/chunks/7425.js +500 -0
- package/.next/server/chunks/8585.js +1 -0
- package/.next/server/chunks/8887.js +1 -0
- package/.next/server/chunks/8948.js +2 -0
- package/.next/server/chunks/9703.js +31 -0
- package/.next/server/chunks/9723.js +19 -0
- package/.next/server/chunks/font-manifest.json +1 -0
- package/.next/server/edge-runtime-webpack.js +2 -0
- package/.next/server/edge-runtime-webpack.js.map +1 -0
- package/.next/server/font-manifest.json +1 -0
- package/.next/server/functions-config-manifest.json +1 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +32 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages/_app.js +1 -0
- package/.next/server/pages/_app.js.nft.json +1 -0
- package/.next/server/pages/_document.js +1 -0
- package/.next/server/pages/_document.js.nft.json +1 -0
- package/.next/server/pages/_error.js +1 -0
- package/.next/server/pages/_error.js.nft.json +1 -0
- package/.next/server/pages-manifest.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/src/middleware.js +14 -0
- package/.next/server/src/middleware.js.map +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/chunks/0dbeb660.3e800dfbd28be3bd.js +53 -0
- package/.next/static/chunks/1015.0eaa4da7f61149bc.js +59 -0
- package/.next/static/chunks/1098.49268c9fe1b028fa.js +1 -0
- package/.next/static/chunks/13.feeafc7cc620f8c4.js +1 -0
- package/.next/static/chunks/1423.7b1e8bf760d28078.js +1 -0
- package/.next/static/chunks/1582.9f8590f71ff798ca.js +55 -0
- package/.next/static/chunks/1817.a66d96cedb761daa.js +262 -0
- package/.next/static/chunks/2117-d845c2cd62e344a6.js +2 -0
- package/.next/static/chunks/2398.0b21e4eb7006a230.js +93 -0
- package/.next/static/chunks/2526.8ac62b527c9ab703.js +43 -0
- package/.next/static/chunks/2626.2125083a1ff3b80a.js +29 -0
- package/.next/static/chunks/2689.720a4874b02d4211.js +174 -0
- package/.next/static/chunks/2853-d11a80b03c9a1640.js +1 -0
- package/.next/static/chunks/2957-327e43ef4c12808f.js +1 -0
- package/.next/static/chunks/2cdb6380.35626fc6e41bbba4.js +136 -0
- package/.next/static/chunks/30d07d85-393352a92199f695.js +3 -0
- package/.next/static/chunks/3559.f073f72c4466ce0e.js +1 -0
- package/.next/static/chunks/3574.7a94c27e6a496a56.js +63 -0
- package/.next/static/chunks/383.20683891c9a5f2c4.js +4 -0
- package/.next/static/chunks/3843.3fdda732987f7bb8.js +1 -0
- package/.next/static/chunks/3852.822389f445c9b427.js +1 -0
- package/.next/static/chunks/3991.4bc063cb5be3a86c.js +1 -0
- package/.next/static/chunks/4212.52c1bb34fc97d0d0.js +131 -0
- package/.next/static/chunks/4327.3b84aa049900fdeb.js +60 -0
- package/.next/static/chunks/4362.7bd6f0282e49d79b.js +1 -0
- package/.next/static/chunks/4721.40615a5f4f32b5fb.js +1 -0
- package/.next/static/chunks/4851-45df4d388db5623f.js +1 -0
- package/.next/static/chunks/5112.17318d1c6b28044b.js +1 -0
- package/.next/static/chunks/5126.93fa4e797d609286.js +56 -0
- package/.next/static/chunks/5387.47590ac4ef66c864.js +5 -0
- package/.next/static/chunks/5813.4483664ba482beb1.js +1 -0
- package/.next/static/chunks/6143.1450875bd03a2366.js +36 -0
- package/.next/static/chunks/6406.9653f0d41ab85059.js +1 -0
- package/.next/static/chunks/656.d72f25ce819bd77e.js +149 -0
- package/.next/static/chunks/6678.492e73ca42b2a273.js +62 -0
- package/.next/static/chunks/6725-f7607851b7d57eb1.js +1 -0
- package/.next/static/chunks/6792.3c01ac4dda4b5c6d.js +1 -0
- package/.next/static/chunks/7004.808cbf327ef5955e.js +1 -0
- package/.next/static/chunks/7290.09ef84cf94f90c4d.js +1 -0
- package/.next/static/chunks/7415.6b481c2baf363262.js +148 -0
- package/.next/static/chunks/7648-325564a6e12a3257.js +1 -0
- package/.next/static/chunks/7665.47fccad04449a8f9.js +215 -0
- package/.next/static/chunks/7753.6bdce86b7fde3d10.js +166 -0
- package/.next/static/chunks/8125.245a9df052d274fb.js +1 -0
- package/.next/static/chunks/816-7e340dad784be28c.js +1 -0
- package/.next/static/chunks/8288.4883743fa40672e2.js +24 -0
- package/.next/static/chunks/8522.1607e96011c66877.js +1 -0
- package/.next/static/chunks/8772.863c564498d88487.js +1 -0
- package/.next/static/chunks/8841.dadeb1ece8e46004.js +1 -0
- package/.next/static/chunks/8885.f8d9912b40d74811.js +1 -0
- package/.next/static/chunks/90542734.c1553d0fe7fc14fc.js +1 -0
- package/.next/static/chunks/9365-733d8c05712d2888.js +1 -0
- package/.next/static/chunks/9552.b7dfb7903ead934b.js +1 -0
- package/.next/static/chunks/9834.295b45635ce04f5e.js +24 -0
- package/.next/static/chunks/app/_not-found/page-a9d04e58c81115ec.js +1 -0
- package/.next/static/chunks/app/layout-37e55f11dcc8b1bf.js +1 -0
- package/.next/static/chunks/app/page-9cd00de9cc0abc43.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9e5adf57cbbbdf05.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-8c6676303b63fdaf.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/simple-terminal/page-16feb3e86e42f4d1.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-be802baffc84dbd2.js +1 -0
- package/.next/static/chunks/d3ac728e.6c9c508274d4d2d5.js +1 -0
- package/.next/static/chunks/fd9d1056-bbe86e4ae099d5cd.js +1 -0
- package/.next/static/chunks/framework-8e0e0f4a6b83a956.js +1 -0
- package/.next/static/chunks/main-a960f4a5e1a2f598.js +1 -0
- package/.next/static/chunks/main-app-420d93e43682fee5.js +1 -0
- package/.next/static/chunks/pages/_app-3c9ca398d360b709.js +1 -0
- package/.next/static/chunks/pages/_error-cf5ca766ac8f493f.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-3fc79fab9bb738d7.js +1 -0
- package/.next/static/css/5eacd01f773eed7f.css +11 -0
- package/.next/static/css/85fa6dafca566008.css +1 -0
- package/.next/static/css/e174aa24f94ce607.css +3 -0
- package/.next/static/pQTquVjewvoJa7BML07ip/_buildManifest.js +1 -0
- package/.next/static/pQTquVjewvoJa7BML07ip/_ssgManifest.js +1 -0
- package/.next/trace +5 -0
- package/.next/types/app/api/external-apps/[id]/health/route.ts +343 -0
- package/.next/types/app/api/external-apps/[id]/route.ts +343 -0
- package/.next/types/app/api/external-apps/route.ts +343 -0
- package/.next/types/app/api/hooks/claude-done/route.ts +343 -0
- package/.next/types/app/api/repositories/clone/[jobId]/route.ts +343 -0
- package/.next/types/app/api/repositories/clone/route.ts +343 -0
- package/.next/types/app/api/repositories/route.ts +343 -0
- package/.next/types/app/api/repositories/scan/route.ts +343 -0
- package/.next/types/app/api/repositories/sync/route.ts +343 -0
- package/.next/types/app/api/slash-commands/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/auto-yes/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/capture/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/cli-tool/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/current-output/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/files/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/interrupt/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/kill-session/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/logs/[filename]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/logs/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/memos/[memoId]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/memos/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/messages/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/prompt-response/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/respond/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/search/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/send/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/slash-commands/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/start-polling/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/terminal/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/tree/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/tree/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/upload/[...path]/route.ts +343 -0
- package/.next/types/app/api/worktrees/[id]/viewed/route.ts +343 -0
- package/.next/types/app/api/worktrees/route.ts +343 -0
- package/.next/types/app/page.ts +79 -0
- package/.next/types/app/proxy/[...path]/route.ts +343 -0
- package/.next/types/app/worktrees/[id]/files/[...path]/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/simple-terminal/page.ts +79 -0
- package/.next/types/app/worktrees/[id]/terminal/page.ts +79 -0
- package/.next/types/package.json +1 -0
- package/README.md +39 -8
- package/dist/server/server.js +123 -0
- package/dist/server/src/lib/claude-output.js +33 -0
- package/dist/server/src/lib/claude-session.js +312 -0
- package/dist/server/src/lib/cli-patterns.js +137 -0
- package/dist/server/src/lib/cli-session.js +73 -0
- package/dist/server/src/lib/cli-tools/base.js +51 -0
- package/dist/server/src/lib/cli-tools/claude.js +65 -0
- package/dist/server/src/lib/cli-tools/codex.js +132 -0
- package/dist/server/src/lib/cli-tools/gemini.js +122 -0
- package/dist/server/src/lib/cli-tools/index.js +22 -0
- package/dist/server/src/lib/cli-tools/manager.js +143 -0
- package/dist/server/src/lib/cli-tools/types.js +5 -0
- package/dist/server/src/lib/conversation-logger.js +25 -0
- package/dist/server/src/lib/db-instance.js +51 -0
- package/dist/server/src/lib/db-migrations.js +777 -0
- package/dist/server/src/lib/db.js +835 -0
- package/dist/server/src/lib/env.js +179 -0
- package/dist/server/src/lib/log-manager.js +234 -0
- package/dist/server/src/lib/logger.js +232 -0
- package/dist/server/src/lib/prompt-detector.js +285 -0
- package/dist/server/src/lib/response-poller.js +638 -0
- package/dist/server/src/lib/tmux.js +299 -0
- package/dist/server/src/lib/worktrees.js +231 -0
- package/dist/server/src/lib/ws-server.js +323 -0
- package/dist/server/src/types/clone.js +39 -0
- package/dist/server/src/types/conversation.js +9 -0
- package/dist/server/src/types/external-apps.js +6 -0
- package/dist/server/src/types/infinite-messages.js +65 -0
- package/dist/server/src/types/markdown-editor.js +94 -0
- package/dist/server/src/types/models.js +5 -0
- package/dist/server/src/types/sidebar.js +89 -0
- package/dist/server/src/types/slash-commands.js +47 -0
- package/dist/server/src/types/ui-actions.js +8 -0
- package/dist/server/src/types/ui-state.js +62 -0
- package/package.json +8 -4
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI Tool response polling
|
|
4
|
+
* Periodically checks tmux sessions for CLI tool responses (Claude, Codex, Gemini)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.cleanClaudeResponse = cleanClaudeResponse;
|
|
8
|
+
exports.cleanGeminiResponse = cleanGeminiResponse;
|
|
9
|
+
exports.startPolling = startPolling;
|
|
10
|
+
exports.stopPolling = stopPolling;
|
|
11
|
+
exports.stopAllPolling = stopAllPolling;
|
|
12
|
+
exports.getActivePollers = getActivePollers;
|
|
13
|
+
const cli_session_1 = require("./cli-session");
|
|
14
|
+
const db_instance_1 = require("./db-instance");
|
|
15
|
+
const db_1 = require("./db");
|
|
16
|
+
const ws_server_1 = require("./ws-server");
|
|
17
|
+
const prompt_detector_1 = require("./prompt-detector");
|
|
18
|
+
const conversation_logger_1 = require("./conversation-logger");
|
|
19
|
+
const claude_output_1 = require("./claude-output");
|
|
20
|
+
const cli_patterns_1 = require("./cli-patterns");
|
|
21
|
+
/**
|
|
22
|
+
* Polling interval in milliseconds (default: 2 seconds)
|
|
23
|
+
*/
|
|
24
|
+
const POLLING_INTERVAL = 2000;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum polling duration in milliseconds (default: 5 minutes)
|
|
27
|
+
*/
|
|
28
|
+
const MAX_POLLING_DURATION = 5 * 60 * 1000;
|
|
29
|
+
/**
|
|
30
|
+
* Active pollers map: "worktreeId:cliToolId" -> NodeJS.Timeout
|
|
31
|
+
*/
|
|
32
|
+
const activePollers = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Polling start times map: "worktreeId:cliToolId" -> timestamp
|
|
35
|
+
*/
|
|
36
|
+
const pollingStartTimes = new Map();
|
|
37
|
+
/**
|
|
38
|
+
* Generate poller key from worktree ID and CLI tool ID
|
|
39
|
+
*/
|
|
40
|
+
function getPollerKey(worktreeId, cliToolId) {
|
|
41
|
+
return `${worktreeId}:${cliToolId}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Clean up Claude response by removing shell setup commands, environment exports, ANSI codes, and banner
|
|
45
|
+
* Also extracts only the LATEST response to avoid including conversation history
|
|
46
|
+
*
|
|
47
|
+
* @param response - Raw Claude response
|
|
48
|
+
* @returns Cleaned response (only the latest response)
|
|
49
|
+
*/
|
|
50
|
+
function cleanClaudeResponse(response) {
|
|
51
|
+
// First, strip ANSI escape codes
|
|
52
|
+
const cleanedResponse = (0, cli_patterns_1.stripAnsi)(response);
|
|
53
|
+
// Find the LAST user prompt (❯ followed by content) and extract only the response after it
|
|
54
|
+
// This ensures we only get the latest response, not the entire conversation history
|
|
55
|
+
const lines = cleanedResponse.split('\n');
|
|
56
|
+
// Find the last user prompt line index
|
|
57
|
+
let lastUserPromptIndex = -1;
|
|
58
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
59
|
+
// User prompt line: ❯ followed by actual content (not empty ❯)
|
|
60
|
+
if (/^❯\s+\S/.test(lines[i])) {
|
|
61
|
+
lastUserPromptIndex = i;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Extract lines after the last user prompt
|
|
66
|
+
const startIndex = lastUserPromptIndex >= 0 ? lastUserPromptIndex + 1 : 0;
|
|
67
|
+
const responseLines = lines.slice(startIndex);
|
|
68
|
+
// Patterns to remove (Claude-specific setup commands and UI elements)
|
|
69
|
+
// IMPORTANT: These patterns should NOT match legitimate Claude response content
|
|
70
|
+
// Lines starting with ⏺ (Claude output marker) are typically valid content
|
|
71
|
+
const skipPatterns = [
|
|
72
|
+
/CLAUDE_HOOKS_/, // Any CLAUDE_HOOKS reference
|
|
73
|
+
/\/bin\/claude/, // Claude binary path (any variant)
|
|
74
|
+
/^claude\s*$/, // Just "claude" on a line
|
|
75
|
+
/@.*\s+%\s*$/, // Shell prompt (any user@host followed by % at end of line)
|
|
76
|
+
/^[^⏺]*curl.*POST/, // Curl POST commands (not starting with ⏺)
|
|
77
|
+
/^[^⏺]*Content-Type/, // HTTP headers (not in Claude output)
|
|
78
|
+
/^[^⏺]*export\s+CLAUDE_/, // Claude environment exports only
|
|
79
|
+
/^\s*$/, // Empty lines
|
|
80
|
+
// Claude Code banner patterns (only match pure banner elements)
|
|
81
|
+
/^[╭╮╰╯│─\s]+$/, // Box drawing characters only (with spaces)
|
|
82
|
+
/^[│╭╮╰╯].*[│╭╮╰╯]$/, // Lines with box drawing on both sides (banner rows)
|
|
83
|
+
/Claude Code v[\d.]+/, // Version info
|
|
84
|
+
/^Tips for getting started/, // Tips header (at line start)
|
|
85
|
+
/^Welcome back/, // Welcome message (at line start)
|
|
86
|
+
/Run \/init to create/, // Init instruction
|
|
87
|
+
/^Recent activity/, // Activity header (at line start)
|
|
88
|
+
/^No recent activity/, // No activity message (at line start)
|
|
89
|
+
/▐▛███▜▌|▝▜█████▛▘|▘▘ ▝▝/, // ASCII art logo
|
|
90
|
+
/^\s*Opus \d+\.\d+\s*·\s*Claude Max/, // Model info in banner format
|
|
91
|
+
/\.com's Organization/, // Organization info
|
|
92
|
+
/\?\s*for shortcuts\s*$/, // Shortcuts hint at end of line
|
|
93
|
+
/^─{10,}$/, // Separator lines
|
|
94
|
+
/^❯\s*$/, // Empty prompt lines
|
|
95
|
+
];
|
|
96
|
+
// Filter out UI elements and keep only the response content
|
|
97
|
+
const cleanedLines = [];
|
|
98
|
+
for (const line of responseLines) {
|
|
99
|
+
const shouldSkip = skipPatterns.some(pattern => pattern.test(line));
|
|
100
|
+
if (!shouldSkip && line.trim()) {
|
|
101
|
+
cleanedLines.push(line);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Return cleaned content
|
|
105
|
+
return cleanedLines.join('\n').trim();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clean up Gemini response by removing shell prompts and error messages
|
|
109
|
+
*
|
|
110
|
+
* @param response - Raw Gemini response
|
|
111
|
+
* @returns Cleaned response
|
|
112
|
+
*/
|
|
113
|
+
function cleanGeminiResponse(response) {
|
|
114
|
+
// Split response into lines
|
|
115
|
+
const lines = response.split('\n');
|
|
116
|
+
const cleanedLines = [];
|
|
117
|
+
// Patterns to remove
|
|
118
|
+
const skipPatterns = [
|
|
119
|
+
/^maenokota@.*%/, // Shell prompt
|
|
120
|
+
/^zsh:/, // Shell error messages
|
|
121
|
+
/^feature-issue-\d+/, // Worktree indicator
|
|
122
|
+
/^\s*$/, // Empty lines at start
|
|
123
|
+
];
|
|
124
|
+
// Find the ✦ marker (actual Gemini response start)
|
|
125
|
+
let foundMarker = false;
|
|
126
|
+
const afterMarker = [];
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
if (line.includes('✦')) {
|
|
129
|
+
foundMarker = true;
|
|
130
|
+
// Extract content after ✦ marker
|
|
131
|
+
const markerIndex = line.indexOf('✦');
|
|
132
|
+
const afterMarkerContent = line.substring(markerIndex + 1).trim();
|
|
133
|
+
if (afterMarkerContent) {
|
|
134
|
+
afterMarker.push(afterMarkerContent);
|
|
135
|
+
}
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (foundMarker) {
|
|
139
|
+
// Skip shell prompts and errors after ✦ marker
|
|
140
|
+
if (skipPatterns.some(pattern => pattern.test(line))) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
afterMarker.push(line);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// If we found content after ✦, use only that
|
|
147
|
+
if (afterMarker.length > 0) {
|
|
148
|
+
return afterMarker.join('\n').trim();
|
|
149
|
+
}
|
|
150
|
+
// Otherwise, filter the original response
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
if (skipPatterns.some(pattern => pattern.test(line))) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
cleanedLines.push(line);
|
|
156
|
+
}
|
|
157
|
+
return cleanedLines.join('\n').trim();
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Extract CLI tool response from tmux output
|
|
161
|
+
* Detects when a CLI tool has completed a response by looking for tool-specific patterns
|
|
162
|
+
*
|
|
163
|
+
* @param output - Full tmux output
|
|
164
|
+
* @param lastCapturedLine - Number of lines previously captured
|
|
165
|
+
* @param cliToolId - CLI tool ID (claude, codex, gemini)
|
|
166
|
+
* @returns Extracted response or null if incomplete
|
|
167
|
+
*/
|
|
168
|
+
function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
169
|
+
// Trim trailing empty lines from the output before processing
|
|
170
|
+
// This prevents the "last 20 lines" from being all empty due to tmux buffer padding
|
|
171
|
+
const rawLines = output.split('\n');
|
|
172
|
+
let trimmedLength = rawLines.length;
|
|
173
|
+
while (trimmedLength > 0 && rawLines[trimmedLength - 1].trim() === '') {
|
|
174
|
+
trimmedLength--;
|
|
175
|
+
}
|
|
176
|
+
const lines = rawLines.slice(0, trimmedLength);
|
|
177
|
+
const totalLines = lines.length;
|
|
178
|
+
const BUFFER_RESET_TOLERANCE = 25;
|
|
179
|
+
const bufferShrank = totalLines > 0 && lastCapturedLine > BUFFER_RESET_TOLERANCE && (totalLines + BUFFER_RESET_TOLERANCE) < lastCapturedLine;
|
|
180
|
+
const sessionRestarted = totalLines > 0 && lastCapturedLine > 50 && totalLines < 50;
|
|
181
|
+
const bufferReset = bufferShrank || sessionRestarted;
|
|
182
|
+
// No new output (with buffer to handle newline inconsistencies)
|
|
183
|
+
// BUT: if totalLines is much smaller than lastCapturedLine, the buffer was likely reset (session restart)
|
|
184
|
+
// In that case, don't skip - proceed to check for completion
|
|
185
|
+
if (!bufferReset && totalLines < lastCapturedLine - 5) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
// Always check the last 20 lines for completion pattern (more robust than tracking line numbers)
|
|
189
|
+
const checkLineCount = 20;
|
|
190
|
+
const startLine = Math.max(0, totalLines - checkLineCount);
|
|
191
|
+
const linesToCheck = lines.slice(startLine);
|
|
192
|
+
const outputToCheck = linesToCheck.join('\n');
|
|
193
|
+
// Get tool-specific patterns from shared module
|
|
194
|
+
const { promptPattern, separatorPattern, thinkingPattern, skipPatterns } = (0, cli_patterns_1.getCliToolPatterns)(cliToolId);
|
|
195
|
+
const findRecentUserPromptIndex = (windowSize = 60) => {
|
|
196
|
+
// User prompt pattern: supports legacy '>' and new '❯' for Claude
|
|
197
|
+
const userPromptPattern = cliToolId === 'codex'
|
|
198
|
+
? /^›\s+(?!Implement|Find and fix|Type|Summarize)/
|
|
199
|
+
: /^[>❯]\s+\S/;
|
|
200
|
+
for (let i = totalLines - 1; i >= Math.max(0, totalLines - windowSize); i--) {
|
|
201
|
+
const cleanLine = (0, cli_patterns_1.stripAnsi)(lines[i]);
|
|
202
|
+
if (userPromptPattern.test(cleanLine)) {
|
|
203
|
+
return i;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return -1;
|
|
207
|
+
};
|
|
208
|
+
// Early check for Claude permission prompts (before extraction logic)
|
|
209
|
+
// Permission prompts appear after normal responses and need special handling
|
|
210
|
+
if (cliToolId === 'claude') {
|
|
211
|
+
const fullOutput = lines.join('\n');
|
|
212
|
+
// Strip ANSI codes before prompt detection
|
|
213
|
+
const cleanFullOutput = (0, cli_patterns_1.stripAnsi)(fullOutput);
|
|
214
|
+
const promptDetection = (0, prompt_detector_1.detectPrompt)(cleanFullOutput);
|
|
215
|
+
if (promptDetection.isPrompt) {
|
|
216
|
+
// Return the full output as a complete interactive prompt
|
|
217
|
+
// Use the cleaned output without ANSI codes
|
|
218
|
+
return {
|
|
219
|
+
response: cleanFullOutput,
|
|
220
|
+
isComplete: true,
|
|
221
|
+
lineCount: totalLines,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Strip ANSI codes before pattern matching
|
|
226
|
+
const cleanOutputToCheck = (0, cli_patterns_1.stripAnsi)(outputToCheck);
|
|
227
|
+
const hasPrompt = promptPattern.test(cleanOutputToCheck);
|
|
228
|
+
const hasSeparator = separatorPattern.test(cleanOutputToCheck);
|
|
229
|
+
const isThinking = thinkingPattern.test(cleanOutputToCheck);
|
|
230
|
+
// Codex/Gemini completion logic: prompt detected and not thinking (separator optional)
|
|
231
|
+
// - Codex: Interactive TUI, detects › prompt
|
|
232
|
+
// - Gemini: Non-interactive one-shot, detects shell prompt (%, $)
|
|
233
|
+
// Claude: require both prompt and separator
|
|
234
|
+
const isCodexOrGeminiComplete = (cliToolId === 'codex' || cliToolId === 'gemini') && hasPrompt && !isThinking;
|
|
235
|
+
const isClaudeComplete = cliToolId === 'claude' && hasPrompt && hasSeparator && !isThinking;
|
|
236
|
+
if (isCodexOrGeminiComplete || isClaudeComplete) {
|
|
237
|
+
// CLI tool has completed response
|
|
238
|
+
// Extract the response content from lastCapturedLine to the separator (not just last 20 lines)
|
|
239
|
+
const responseLines = [];
|
|
240
|
+
// Handle tmux buffer scrolling: if lastCapturedLine >= totalLines, the buffer has scrolled
|
|
241
|
+
// In this case, we need to find the response in the current visible buffer
|
|
242
|
+
let startIndex;
|
|
243
|
+
// For all tools: check if buffer has been reset/cleared (startIndex would be >= totalLines)
|
|
244
|
+
// This happens when a session is restarted or buffer is cleared
|
|
245
|
+
const bufferWasReset = lastCapturedLine >= totalLines || bufferReset;
|
|
246
|
+
if (bufferWasReset) {
|
|
247
|
+
// Buffer was reset - find the most recent user prompt
|
|
248
|
+
const foundUserPrompt = findRecentUserPromptIndex(40);
|
|
249
|
+
startIndex = foundUserPrompt >= 0 ? foundUserPrompt + 1 : 0;
|
|
250
|
+
}
|
|
251
|
+
else if (cliToolId === 'codex') {
|
|
252
|
+
// Normal case for Codex: use lastCapturedLine
|
|
253
|
+
startIndex = Math.max(0, lastCapturedLine);
|
|
254
|
+
}
|
|
255
|
+
else if (lastCapturedLine >= totalLines - 5) {
|
|
256
|
+
// Buffer may have scrolled - look for the start of the new response
|
|
257
|
+
// Find the last user input prompt to identify where the response starts
|
|
258
|
+
const foundUserPrompt = findRecentUserPromptIndex(50);
|
|
259
|
+
// Start extraction from after the user prompt, or from a safe earlier point
|
|
260
|
+
startIndex = foundUserPrompt >= 0 ? foundUserPrompt + 1 : Math.max(0, totalLines - 40);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
// Normal case: start from lastCapturedLine
|
|
264
|
+
startIndex = Math.max(0, lastCapturedLine);
|
|
265
|
+
}
|
|
266
|
+
let endIndex = totalLines; // Track where extraction actually ended
|
|
267
|
+
for (let i = startIndex; i < totalLines; i++) {
|
|
268
|
+
const line = lines[i];
|
|
269
|
+
const cleanLine = (0, cli_patterns_1.stripAnsi)(line);
|
|
270
|
+
// For Codex: stop at any prompt line (which indicates end of response OR we're already past it)
|
|
271
|
+
if (cliToolId === 'codex' && /^›\s+/.test(cleanLine)) {
|
|
272
|
+
endIndex = i;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
// For Gemini: stop at shell prompt (indicates command completion)
|
|
276
|
+
if (cliToolId === 'gemini' && /^(%|\$|.*@.*[%$#])\s*$/.test(cleanLine)) {
|
|
277
|
+
endIndex = i;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
// Skip lines matching any skip pattern (check against clean line)
|
|
281
|
+
const shouldSkip = skipPatterns.some(pattern => pattern.test(cleanLine));
|
|
282
|
+
if (shouldSkip) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
responseLines.push(line);
|
|
286
|
+
}
|
|
287
|
+
const response = responseLines.join('\n').trim();
|
|
288
|
+
// Additional check: ensure response doesn't contain thinking indicators
|
|
289
|
+
// This prevents saving intermediate states as final responses
|
|
290
|
+
if (thinkingPattern.test(response)) {
|
|
291
|
+
return {
|
|
292
|
+
response: '',
|
|
293
|
+
isComplete: false,
|
|
294
|
+
lineCount: totalLines,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
// CRITICAL FIX: Detect and skip Claude Code startup banner/screen
|
|
298
|
+
// The startup screen contains: ASCII art logo, version info, prompt, separator
|
|
299
|
+
// But no actual response content from the user's query
|
|
300
|
+
if (cliToolId === 'claude') {
|
|
301
|
+
const cleanResponse = (0, cli_patterns_1.stripAnsi)(response);
|
|
302
|
+
// Check for Claude Code banner patterns
|
|
303
|
+
const hasBannerArt = /[╭╮╰╯│]/.test(cleanResponse) || /░{3,}/.test(cleanResponse) || /▓{3,}/.test(cleanResponse);
|
|
304
|
+
const hasVersionInfo = /Claude Code|claude\/|v\d+\.\d+/.test(cleanResponse);
|
|
305
|
+
const hasStartupTips = /Tip:|for shortcuts|\?\s*for help/.test(cleanResponse);
|
|
306
|
+
const hasProjectInit = /^\s*\/Users\/.*$/m.test(cleanResponse) && cleanResponse.split('\n').length < 30;
|
|
307
|
+
// Check if this looks like just a startup screen (no actual response content)
|
|
308
|
+
// A real response would have content AFTER the user's prompt line (> or ❯ message)
|
|
309
|
+
const userPromptMatch = cleanResponse.match(/^[>❯]\s+(\S.*)$/m);
|
|
310
|
+
if (userPromptMatch) {
|
|
311
|
+
// Found user prompt - check if there's actual response content after it
|
|
312
|
+
const userPromptIndex = cleanResponse.indexOf(userPromptMatch[0]);
|
|
313
|
+
const contentAfterPrompt = cleanResponse.substring(userPromptIndex + userPromptMatch[0].length).trim();
|
|
314
|
+
// Filter out just separators, tips, and empty lines
|
|
315
|
+
const contentLines = contentAfterPrompt.split('\n').filter(line => {
|
|
316
|
+
const trimmed = line.trim();
|
|
317
|
+
return trimmed &&
|
|
318
|
+
!skipPatterns.some(p => p.test(trimmed)) &&
|
|
319
|
+
!/^─+$/.test(trimmed);
|
|
320
|
+
});
|
|
321
|
+
if (contentLines.length === 0) {
|
|
322
|
+
return {
|
|
323
|
+
response: '',
|
|
324
|
+
isComplete: false,
|
|
325
|
+
lineCount: totalLines,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else if ((hasBannerArt || hasVersionInfo || hasStartupTips || hasProjectInit) && response.length < 2000) {
|
|
330
|
+
// No user prompt found, but has banner characteristics - likely initial startup
|
|
331
|
+
return {
|
|
332
|
+
response: '',
|
|
333
|
+
isComplete: false,
|
|
334
|
+
lineCount: totalLines,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Gemini-specific check: ensure response contains actual content (✦ marker)
|
|
339
|
+
if (cliToolId === 'gemini') {
|
|
340
|
+
// Check for banner/UI characters (banner should be filtered by skipPatterns, but double-check)
|
|
341
|
+
const bannerCharCount = (response.match(/[░███]/g) || []).length;
|
|
342
|
+
const totalChars = response.length;
|
|
343
|
+
if (bannerCharCount > totalChars * 0.3) {
|
|
344
|
+
return {
|
|
345
|
+
response: '',
|
|
346
|
+
isComplete: false,
|
|
347
|
+
lineCount: totalLines,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
// Check for auth/loading states that should not be treated as complete responses
|
|
351
|
+
if (response.includes('Waiting for auth') ||
|
|
352
|
+
response.includes('⠋') ||
|
|
353
|
+
response.includes('⠙') ||
|
|
354
|
+
response.includes('⠹') ||
|
|
355
|
+
response.includes('⠸') ||
|
|
356
|
+
response.includes('⠼') ||
|
|
357
|
+
response.includes('⠴') ||
|
|
358
|
+
response.includes('⠦') ||
|
|
359
|
+
response.includes('⠧') ||
|
|
360
|
+
response.includes('⠇') ||
|
|
361
|
+
response.includes('⠏')) {
|
|
362
|
+
return {
|
|
363
|
+
response: '',
|
|
364
|
+
isComplete: false,
|
|
365
|
+
lineCount: totalLines,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
if (!response.includes('✦') && response.length < 10) {
|
|
369
|
+
return {
|
|
370
|
+
response: '',
|
|
371
|
+
isComplete: false,
|
|
372
|
+
lineCount: totalLines,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
response,
|
|
378
|
+
isComplete: true,
|
|
379
|
+
lineCount: endIndex, // Use endIndex instead of totalLines to track where we actually stopped
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
// Check if this is an interactive prompt (yes/no or multiple choice)
|
|
383
|
+
// Interactive prompts don't have the ">" prompt and separator, so we need to detect them separately
|
|
384
|
+
const fullOutput = lines.join('\n');
|
|
385
|
+
const promptDetection = (0, prompt_detector_1.detectPrompt)(fullOutput);
|
|
386
|
+
if (promptDetection.isPrompt) {
|
|
387
|
+
// This is an interactive prompt - consider it complete
|
|
388
|
+
return {
|
|
389
|
+
response: fullOutput,
|
|
390
|
+
isComplete: true,
|
|
391
|
+
lineCount: totalLines,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
// Not a prompt, but we may have a partial response in progress (even if Claude shows a spinner)
|
|
395
|
+
const responseLines = [];
|
|
396
|
+
const endIndex = totalLines;
|
|
397
|
+
const partialBufferReset = bufferReset || lastCapturedLine >= endIndex - 5;
|
|
398
|
+
const recentPromptIndex = partialBufferReset ? findRecentUserPromptIndex(80) : -1;
|
|
399
|
+
const startIndex = partialBufferReset
|
|
400
|
+
? (recentPromptIndex >= 0 ? recentPromptIndex + 1 : Math.max(0, endIndex - 80))
|
|
401
|
+
: Math.max(0, lastCapturedLine);
|
|
402
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
403
|
+
const line = lines[i];
|
|
404
|
+
const cleanLine = (0, cli_patterns_1.stripAnsi)(line);
|
|
405
|
+
// Skip lines matching any skip pattern
|
|
406
|
+
const shouldSkip = skipPatterns.some(pattern => pattern.test(cleanLine));
|
|
407
|
+
if (shouldSkip) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
responseLines.push(line);
|
|
411
|
+
}
|
|
412
|
+
const partialResponse = responseLines.join('\n').trim();
|
|
413
|
+
if (partialResponse) {
|
|
414
|
+
return {
|
|
415
|
+
response: partialResponse,
|
|
416
|
+
isComplete: false,
|
|
417
|
+
lineCount: endIndex,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
// Response not yet complete (or is in thinking state)
|
|
421
|
+
return {
|
|
422
|
+
response: '',
|
|
423
|
+
isComplete: false,
|
|
424
|
+
lineCount: totalLines,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Check for CLI tool response once
|
|
429
|
+
*
|
|
430
|
+
* @param worktreeId - Worktree ID
|
|
431
|
+
* @returns True if response was found and processed
|
|
432
|
+
*/
|
|
433
|
+
async function checkForResponse(worktreeId, cliToolId) {
|
|
434
|
+
const db = (0, db_instance_1.getDbInstance)();
|
|
435
|
+
try {
|
|
436
|
+
// Get worktree to verify it exists
|
|
437
|
+
const worktree = (0, db_1.getWorktreeById)(db, worktreeId);
|
|
438
|
+
if (!worktree) {
|
|
439
|
+
console.error(`Worktree ${worktreeId} not found, stopping poller`);
|
|
440
|
+
stopPolling(worktreeId, cliToolId);
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
// Check if CLI tool session is running
|
|
444
|
+
const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId);
|
|
445
|
+
if (!running) {
|
|
446
|
+
stopPolling(worktreeId, cliToolId);
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
// Get session state (last captured line count)
|
|
450
|
+
const sessionState = (0, db_1.getSessionState)(db, worktreeId, cliToolId);
|
|
451
|
+
const lastCapturedLine = sessionState?.lastCapturedLine || 0;
|
|
452
|
+
// Capture current output
|
|
453
|
+
const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, 10000);
|
|
454
|
+
// Extract response
|
|
455
|
+
const result = extractResponse(output, lastCapturedLine, cliToolId);
|
|
456
|
+
if (!result || !result.isComplete) {
|
|
457
|
+
// No new output or response not yet complete
|
|
458
|
+
// But if Claude is processing (thinking), mark any pending prompts as answered
|
|
459
|
+
// This handles cases where user responded to prompts directly via terminal
|
|
460
|
+
const { thinkingPattern } = (0, cli_patterns_1.getCliToolPatterns)(cliToolId);
|
|
461
|
+
const cleanOutput = (0, cli_patterns_1.stripAnsi)(output);
|
|
462
|
+
if (thinkingPattern.test(cleanOutput)) {
|
|
463
|
+
const answeredCount = (0, db_1.markPendingPromptsAsAnswered)(db, worktreeId, cliToolId);
|
|
464
|
+
if (answeredCount > 0) {
|
|
465
|
+
console.log(`Marked ${answeredCount} pending prompt(s) as answered (thinking detected) for ${worktreeId}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
// CRITICAL FIX: If lineCount == lastCapturedLine AND there's no in-progress message,
|
|
471
|
+
// this response has already been saved. Skip to prevent duplicates.
|
|
472
|
+
if (result.lineCount === lastCapturedLine && !sessionState?.inProgressMessageId) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
// Additional duplicate prevention: check if savePendingAssistantResponse
|
|
476
|
+
// already saved this content by comparing line counts
|
|
477
|
+
if (result.lineCount <= lastCapturedLine) {
|
|
478
|
+
console.log(`[checkForResponse] Already saved up to line ${lastCapturedLine}, skipping (result: ${result.lineCount})`);
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
// Response is complete! Check if it's a prompt
|
|
482
|
+
const promptDetection = (0, prompt_detector_1.detectPrompt)(result.response);
|
|
483
|
+
if (promptDetection.isPrompt) {
|
|
484
|
+
// This is a prompt - save as prompt message
|
|
485
|
+
(0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId);
|
|
486
|
+
const message = (0, db_1.createMessage)(db, {
|
|
487
|
+
worktreeId,
|
|
488
|
+
role: 'assistant',
|
|
489
|
+
content: promptDetection.cleanContent,
|
|
490
|
+
messageType: 'prompt',
|
|
491
|
+
promptData: promptDetection.promptData,
|
|
492
|
+
timestamp: new Date(),
|
|
493
|
+
cliToolId,
|
|
494
|
+
});
|
|
495
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount);
|
|
496
|
+
(0, ws_server_1.broadcastMessage)('message', { worktreeId, message });
|
|
497
|
+
stopPolling(worktreeId, cliToolId);
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
// Validate response content is not empty
|
|
501
|
+
if (!result.response || result.response.trim() === '') {
|
|
502
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount);
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
// Parse Claude-specific metadata (summary, log filename, request id)
|
|
506
|
+
const claudeMetadata = cliToolId === 'claude'
|
|
507
|
+
? (0, claude_output_1.parseClaudeOutput)(result.response)
|
|
508
|
+
: undefined;
|
|
509
|
+
// Clean up responses (remove shell prompts, setup commands, and errors)
|
|
510
|
+
let cleanedResponse = result.response;
|
|
511
|
+
if (cliToolId === 'gemini') {
|
|
512
|
+
cleanedResponse = cleanGeminiResponse(result.response);
|
|
513
|
+
}
|
|
514
|
+
else if (cliToolId === 'claude') {
|
|
515
|
+
cleanedResponse = cleanClaudeResponse(result.response);
|
|
516
|
+
}
|
|
517
|
+
// If cleaned response is empty or just "[No content]", skip saving
|
|
518
|
+
// This prevents creating messages for shell setup commands that get filtered out
|
|
519
|
+
if (!cleanedResponse || cleanedResponse.trim() === '' || cleanedResponse === '[No content]') {
|
|
520
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount);
|
|
521
|
+
(0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId);
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
// Create Markdown log file for the conversation pair
|
|
525
|
+
if (cleanedResponse) {
|
|
526
|
+
await (0, conversation_logger_1.recordClaudeConversation)(db, worktreeId, cleanedResponse, cliToolId);
|
|
527
|
+
}
|
|
528
|
+
// Mark any pending prompts as answered since Claude has started processing
|
|
529
|
+
// This handles cases where user responded to prompts directly via terminal
|
|
530
|
+
const answeredCount = (0, db_1.markPendingPromptsAsAnswered)(db, worktreeId, cliToolId);
|
|
531
|
+
if (answeredCount > 0) {
|
|
532
|
+
console.log(`Marked ${answeredCount} pending prompt(s) as answered for ${worktreeId}`);
|
|
533
|
+
}
|
|
534
|
+
// Race condition prevention: re-check session state before saving
|
|
535
|
+
// savePendingAssistantResponse may have already saved this content concurrently
|
|
536
|
+
const currentSessionState = (0, db_1.getSessionState)(db, worktreeId, cliToolId);
|
|
537
|
+
if (currentSessionState && result.lineCount <= currentSessionState.lastCapturedLine) {
|
|
538
|
+
console.log(`[checkForResponse] Race condition detected, skipping save (result: ${result.lineCount}, current: ${currentSessionState.lastCapturedLine})`);
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
// Create new CLI tool message in database
|
|
542
|
+
// (No longer using in-progress messages - frontend shows realtime output instead)
|
|
543
|
+
const message = (0, db_1.createMessage)(db, {
|
|
544
|
+
worktreeId,
|
|
545
|
+
role: 'assistant',
|
|
546
|
+
content: cleanedResponse,
|
|
547
|
+
messageType: 'normal',
|
|
548
|
+
timestamp: new Date(),
|
|
549
|
+
cliToolId,
|
|
550
|
+
summary: claudeMetadata?.summary,
|
|
551
|
+
logFileName: claudeMetadata?.logFileName,
|
|
552
|
+
requestId: claudeMetadata?.requestId,
|
|
553
|
+
});
|
|
554
|
+
// Broadcast message to WebSocket clients
|
|
555
|
+
(0, ws_server_1.broadcastMessage)('message', { worktreeId, message });
|
|
556
|
+
// Update session state
|
|
557
|
+
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
562
|
+
console.error(`Error checking for response (${worktreeId}):`, errorMessage);
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Start polling for CLI tool response
|
|
568
|
+
*
|
|
569
|
+
* @param worktreeId - Worktree ID
|
|
570
|
+
* @param cliToolId - CLI tool ID (claude, codex, gemini)
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* startPolling('feature-foo', 'claude');
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
function startPolling(worktreeId, cliToolId) {
|
|
578
|
+
const pollerKey = getPollerKey(worktreeId, cliToolId);
|
|
579
|
+
// Stop existing poller if any
|
|
580
|
+
stopPolling(worktreeId, cliToolId);
|
|
581
|
+
// Record start time
|
|
582
|
+
pollingStartTimes.set(pollerKey, Date.now());
|
|
583
|
+
// Start polling
|
|
584
|
+
const interval = setInterval(async () => {
|
|
585
|
+
const startTime = pollingStartTimes.get(pollerKey);
|
|
586
|
+
// Check if max duration exceeded
|
|
587
|
+
if (startTime && Date.now() - startTime > MAX_POLLING_DURATION) {
|
|
588
|
+
stopPolling(worktreeId, cliToolId);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// Check for response
|
|
592
|
+
try {
|
|
593
|
+
await checkForResponse(worktreeId, cliToolId);
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
console.error(`[Poller] Error:`, error);
|
|
597
|
+
}
|
|
598
|
+
}, POLLING_INTERVAL);
|
|
599
|
+
activePollers.set(pollerKey, interval);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Stop polling for a worktree and CLI tool combination
|
|
603
|
+
*
|
|
604
|
+
* @param worktreeId - Worktree ID
|
|
605
|
+
* @param cliToolId - CLI tool ID (claude, codex, gemini)
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* ```typescript
|
|
609
|
+
* stopPolling('feature-foo', 'claude');
|
|
610
|
+
* ```
|
|
611
|
+
*/
|
|
612
|
+
function stopPolling(worktreeId, cliToolId) {
|
|
613
|
+
const pollerKey = getPollerKey(worktreeId, cliToolId);
|
|
614
|
+
const interval = activePollers.get(pollerKey);
|
|
615
|
+
if (interval) {
|
|
616
|
+
clearInterval(interval);
|
|
617
|
+
activePollers.delete(pollerKey);
|
|
618
|
+
pollingStartTimes.delete(pollerKey);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Stop all active pollers
|
|
623
|
+
* Used for cleanup on server shutdown
|
|
624
|
+
*/
|
|
625
|
+
function stopAllPolling() {
|
|
626
|
+
for (const pollerKey of activePollers.keys()) {
|
|
627
|
+
const [worktreeId, cliToolId] = pollerKey.split(':');
|
|
628
|
+
stopPolling(worktreeId, cliToolId);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Get list of active pollers
|
|
633
|
+
*
|
|
634
|
+
* @returns Array of worktree IDs currently being polled
|
|
635
|
+
*/
|
|
636
|
+
function getActivePollers() {
|
|
637
|
+
return Array.from(activePollers.keys());
|
|
638
|
+
}
|