diff --git a/bun.lock b/bun.lock index efce7cb72..48d536a16 100644 --- a/bun.lock +++ b/bun.lock @@ -77,6 +77,8 @@ "minimatch": "^10.1.1", "node-html-parser": "^6.1.13", "strip-ansi": "^7.1.2", + "winston": "^3.17.0", + "winston-daily-rotate-file": "^5.0.0", }, "devDependencies": { "@types/diff": "^7.0.2", @@ -191,6 +193,10 @@ "@borewit/text-codec": ["@borewit/text-codec@0.2.1", "", {}, "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw=="], + "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], + + "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], @@ -397,6 +403,8 @@ "@slack/web-api": ["@slack/web-api@7.13.0", "", { "dependencies": { "@slack/logger": "^4.0.0", "@slack/types": "^2.18.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", "axios": "^1.11.0", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" } }, "sha512-ERcExbWrnkDN8ovoWWe6Wgt/usanj1dWUd18dJLpctUI4mlPS0nKt81Joh8VI+OPbNnY1lIilVt9gdMBD9U2ig=="], + "@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="], + "@tailwindcss/cli": ["@tailwindcss/cli@4.1.18", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "enhanced-resolve": "^5.18.3", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.18" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-sMZ+lZbDyxwjD2E0L7oRUjJ01Ffjtme5OtjvvnC+cV4CEDcbqzbp25TCpxHj6kWLU9+DlqJOiNgSOgctC2aZmg=="], "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], @@ -453,6 +461,8 @@ "@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="], + "@types/triple-beam": ["@types/triple-beam@1.3.5", "", {}, "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="], + "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], @@ -511,6 +521,8 @@ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="], @@ -561,10 +573,14 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "color": ["color@5.0.3", "", { "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" } }, "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="], + "colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="], "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], @@ -617,6 +633,8 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "enabled": ["enabled@2.0.0", "", {}, "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="], + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], @@ -655,12 +673,18 @@ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "fecha": ["fecha@4.2.3", "", {}, "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + "file-stream-rotator": ["file-stream-rotator@0.6.1", "", { "dependencies": { "moment": "^2.29.1" } }, "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ=="], + "file-type": ["file-type@21.2.0", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-vCYBgFOrJQLoTzDyAXAL/RFfKnXXpUYt4+tipVy26nJJhT7ftgGETf2tAQF59EEL61i3MrorV/PG6tf7LJK7eg=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="], + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], @@ -765,6 +789,8 @@ "katex": ["katex@0.16.27", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw=="], + "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], + "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], @@ -805,6 +831,8 @@ "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], + "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], @@ -835,6 +863,8 @@ "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -861,10 +891,14 @@ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + "ollama": ["ollama@0.6.3", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "one-time": ["one-time@1.0.0", "", { "dependencies": { "fn.name": "1.x.x" } }, "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g=="], + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "openai": ["openai@6.10.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A=="], @@ -917,7 +951,7 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], @@ -937,6 +971,8 @@ "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], @@ -959,6 +995,8 @@ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], @@ -995,6 +1033,8 @@ "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -1017,6 +1057,8 @@ "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + "triple-beam": ["triple-beam@1.4.1", "", {}, "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg=="], + "ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -1045,6 +1087,12 @@ "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "winston": ["winston@3.19.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA=="], + + "winston-daily-rotate-file": ["winston-daily-rotate-file@5.0.0", "", { "dependencies": { "file-stream-rotator": "^0.6.1", "object-hash": "^3.0.0", "triple-beam": "^1.4.1", "winston-transport": "^4.7.0" }, "peerDependencies": { "winston": "^3" } }, "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw=="], + + "winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="], + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -1107,8 +1155,6 @@ "@types/ws/@types/node": ["@types/node@24.10.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg=="], - "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "bun-types/@types/node": ["@types/node@24.10.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg=="], "cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -1123,10 +1169,16 @@ "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "color/color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="], + + "color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], + "concurrently/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -1147,8 +1199,6 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -1187,6 +1237,8 @@ "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], + "concurrently/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], diff --git a/packages/ai/src/cli.ts b/packages/ai/src/cli.ts index ef230a74f..012711f8a 100755 --- a/packages/ai/src/cli.ts +++ b/packages/ai/src/cli.ts @@ -1,5 +1,6 @@ #!/usr/bin/env bun +import { logger } from "@oh-my-pi/pi-coding-agent"; import { loginAnthropic } from "./utils/oauth/anthropic"; import { loginGitHubCopilot } from "./utils/oauth/github-copilot"; import { loginAntigravity } from "./utils/oauth/google-antigravity"; @@ -26,7 +27,8 @@ async function loadAuth(): Promise; + /** File logger for error/warning/debug messages */ + logger: Logger; /** Injected @sinclair/typebox module */ typebox: typeof import("@sinclair/typebox"); /** Injected pi-coding-agent exports */ diff --git a/packages/coding-agent/src/core/logger.ts b/packages/coding-agent/src/core/logger.ts new file mode 100644 index 000000000..98c8f79eb --- /dev/null +++ b/packages/coding-agent/src/core/logger.ts @@ -0,0 +1,112 @@ +/** + * Centralized file logger for pi. + * + * Logs to ~/.pi/logs/ with size-based rotation, supporting concurrent pi instances. + * Each log entry includes process.pid for traceability. + */ + +import { existsSync, mkdirSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import winston from "winston"; +import DailyRotateFile from "winston-daily-rotate-file"; +import { CONFIG_DIR_NAME } from "../config"; + +/** Get the logs directory (~/.pi/logs/) */ +function getLogsDir(): string { + return join(homedir(), CONFIG_DIR_NAME, "logs"); +} + +/** Ensure logs directory exists */ +function ensureLogsDir(): string { + const logsDir = getLogsDir(); + if (!existsSync(logsDir)) { + mkdirSync(logsDir, { recursive: true }); + } + return logsDir; +} + +/** Custom format that includes pid and flattens metadata */ +const logFormat = winston.format.combine( + winston.format.timestamp({ format: "YYYY-MM-DDTHH:mm:ss.SSSZ" }), + winston.format.printf(({ timestamp, level, message, ...meta }) => { + const entry: Record = { + timestamp, + level, + pid: process.pid, + message, + }; + // Flatten metadata into entry + for (const [key, value] of Object.entries(meta)) { + if (key !== "level" && key !== "timestamp" && key !== "message") { + entry[key] = value; + } + } + return JSON.stringify(entry); + }), +); + +/** Size-based rotating file transport */ +const fileTransport = new DailyRotateFile({ + dirname: ensureLogsDir(), + filename: "pi.%DATE%.log", + datePattern: "YYYY-MM-DD", + maxSize: "10m", + maxFiles: 5, + zippedArchive: true, +}); + +/** The winston logger instance */ +const winstonLogger = winston.createLogger({ + level: "debug", + format: logFormat, + transports: [fileTransport], + // Don't exit on error - logging failures shouldn't crash the app + exitOnError: false, +}); + +/** Logger type exposed to plugins and internal code */ +export interface Logger { + error(message: string, context?: Record): void; + warn(message: string, context?: Record): void; + debug(message: string, context?: Record): void; +} + +/** + * Centralized logger for pi. + * + * Logs to ~/.pi/logs/pi.YYYY-MM-DD.log with size-based rotation. + * Safe for concurrent access from multiple pi instances. + * + * @example + * ```typescript + * import { logger } from "../core/logger"; + * + * logger.error("MCP request failed", { url, method }); + * logger.warn("Theme file invalid, using fallback", { path }); + * logger.debug("LSP fallback triggered", { reason }); + * ``` + */ +export const logger: Logger = { + error(message: string, context?: Record): void { + try { + winstonLogger.error(message, context); + } catch { + // Silently ignore logging failures + } + }, + warn(message: string, context?: Record): void { + try { + winstonLogger.warn(message, context); + } catch { + // Silently ignore logging failures + } + }, + debug(message: string, context?: Record): void { + try { + winstonLogger.debug(message, context); + } catch { + // Silently ignore logging failures + } + }, +}; diff --git a/packages/coding-agent/src/core/slash-commands.ts b/packages/coding-agent/src/core/slash-commands.ts index eab83a15c..943c5d3eb 100644 --- a/packages/coding-agent/src/core/slash-commands.ts +++ b/packages/coding-agent/src/core/slash-commands.ts @@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs"; import { homedir } from "node:os"; import { join, resolve } from "node:path"; import { CONFIG_DIR_NAME, getCommandsDir } from "../config"; +import { logger } from "./logger"; /** * Represents a custom slash command loaded from a file @@ -161,13 +162,13 @@ function loadCommandsFromDir(dir: string, source: CommandSource, subdir: string content, source: sourceStr, }); - } catch (_error) { - // Silently skip files that can't be read + } catch (err) { + logger.debug("Failed to read slash command file", { error: String(err) }); } } } - } catch (_error) { - // Silently skip directories that can't be read + } catch (err) { + logger.debug("Failed to read slash command directory", { error: String(err) }); } return commands; diff --git a/packages/coding-agent/src/core/tools/exa/index.ts b/packages/coding-agent/src/core/tools/exa/index.ts index 24ca1ecca..8c8b3c11c 100644 --- a/packages/coding-agent/src/core/tools/exa/index.ts +++ b/packages/coding-agent/src/core/tools/exa/index.ts @@ -44,7 +44,6 @@ export function getExaTools(settings: Required): CustomTool): string { - const timestamp = new Date().toISOString(); - const contextStr = context ? ` ${JSON.stringify(context)}` : ""; - return `[${timestamp}] ${message}${contextStr}\n`; -} - -/** Append to log file (creates directory if needed) */ -export function logError(type: LogType, message: string, context?: Record): void { - try { - const configDir = getConfigDir(); - if (!existsSync(configDir)) { - mkdirSync(configDir, { recursive: true }); - } - - const logPath = join(configDir, LOG_FILES[type]); - const entry = formatEntry(message, context); - appendFileSync(logPath, entry); - } catch { - // Silently ignore logging failures - we don't want to break tool execution - } -} - -/** Log MCP fetch/call errors */ -export function logExaError(message: string, context?: Record): void { - logError("exa", message, context); -} - -/** Log render/view errors */ -export function logViewError(message: string, context?: Record): void { - logError("view", message, context); -} diff --git a/packages/coding-agent/src/core/tools/exa/mcp-client.ts b/packages/coding-agent/src/core/tools/exa/mcp-client.ts index 953947c29..aaf39689f 100644 --- a/packages/coding-agent/src/core/tools/exa/mcp-client.ts +++ b/packages/coding-agent/src/core/tools/exa/mcp-client.ts @@ -6,7 +6,7 @@ import type { TSchema } from "@sinclair/typebox"; import type { CustomTool } from "../../custom-tools/types"; -import { logExaError } from "./logger"; +import { logger } from "../../logger"; import type { ExaRenderDetails, ExaSearchResponse, @@ -89,7 +89,7 @@ export async function callMCP(url: string, method: string, params?: Record { const response = (await callMCP(url, "tools/list")) as MCPToolsResponse; if (response.error) { - logExaError("Websets MCP tools/list error", { error: response.error }); + logger.error("Websets MCP tools/list error", { error: response.error }); throw new Error(`MCP error: ${response.error.message}`); } @@ -139,7 +139,7 @@ export async function callExaTool(toolName: string, args: Record formatDiagnostic(d.diagnostic, d.filePath)); const more = collected.length > 50 ? `\n ... and ${collected.length - 50} more` : ""; return { output: `${summary}:\n${formatted.map((f) => ` ${f}`).join("\n")}${more}`, projectType }; - } catch (_e) { + } catch (err) { + logger.debug("LSP diagnostics failed, falling back to shell", { error: String(err) }); // Fall through to shell command } } diff --git a/packages/coding-agent/src/core/tools/task/render.ts b/packages/coding-agent/src/core/tools/task/render.ts index c0a793151..7c2664467 100644 --- a/packages/coding-agent/src/core/tools/task/render.ts +++ b/packages/coding-agent/src/core/tools/task/render.ts @@ -207,36 +207,51 @@ function renderReviewResult( // Findings in tree structure if (findings.length > 0) { lines.push(`${continuePrefix}`); // Spacing - const displayCount = expanded ? findings.length : Math.min(3, findings.length); + lines.push(...renderFindings(findings, continuePrefix, expanded, theme)); + } - for (let i = 0; i < displayCount; i++) { - const finding = findings[i]; - const isLastFinding = i === displayCount - 1 && (expanded || findings.length <= 3); - const findingPrefix = isLastFinding ? "└─" : "├─"; - const findingContinue = isLastFinding ? " " : "│ "; + return lines; +} - const priority = PRIORITY_LABELS[finding.priority] ?? "P?"; - const color = finding.priority === 0 ? "error" : finding.priority === 1 ? "warning" : "muted"; - const titleText = finding.title.replace(/^\[P\d\]\s*/, ""); - const loc = `${path.basename(finding.file_path)}:${finding.line_start}`; +/** + * Render review findings list (used with and without submit_review). + */ +function renderFindings( + findings: ReportFindingDetails[], + continuePrefix: string, + expanded: boolean, + theme: Theme, +): string[] { + const lines: string[] = []; + const displayCount = expanded ? findings.length : Math.min(3, findings.length); - lines.push( - `${continuePrefix}${findingPrefix} ${theme.fg(color, `[${priority}]`)} ${titleText} ${theme.fg("dim", loc)}`, - ); + for (let i = 0; i < displayCount; i++) { + const finding = findings[i]; + const isLastFinding = i === displayCount - 1 && (expanded || findings.length <= 3); + const findingPrefix = isLastFinding ? "└─" : "├─"; + const findingContinue = isLastFinding ? " " : "│ "; - // Show body when expanded - if (expanded && finding.body) { - // Wrap body text - const bodyLines = finding.body.split("\n"); - for (const bodyLine of bodyLines) { - lines.push(`${continuePrefix}${findingContinue}${theme.fg("dim", bodyLine)}`); - } + const priority = PRIORITY_LABELS[finding.priority] ?? "P?"; + const color = finding.priority === 0 ? "error" : finding.priority === 1 ? "warning" : "muted"; + const titleText = finding.title.replace(/^\[P\d\]\s*/, ""); + const loc = `${path.basename(finding.file_path)}:${finding.line_start}`; + + lines.push( + `${continuePrefix}${findingPrefix} ${theme.fg(color, `[${priority}]`)} ${titleText} ${theme.fg("dim", loc)}`, + ); + + // Show body when expanded + if (expanded && finding.body) { + // Wrap body text + const bodyLines = finding.body.split("\n"); + for (const bodyLine of bodyLines) { + lines.push(`${continuePrefix}${findingContinue}${theme.fg("dim", bodyLine)}`); } } + } - if (!expanded && findings.length > 3) { - lines.push(`${continuePrefix}${theme.fg("dim", `... ${findings.length - 3} more findings`)}`); - } + if (!expanded && findings.length > 3) { + lines.push(`${continuePrefix}${theme.fg("dim", `... ${findings.length - 3} more findings`)}`); } return lines; @@ -282,6 +297,14 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool lines.push(...renderReviewResult(summary, findings, continuePrefix, expanded, theme)); return lines; } + if (reportFindingData && reportFindingData.length > 0) { + lines.push( + `${continuePrefix}${theme.fg("warning", "!")} ${theme.fg("dim", "Review summary missing (submit_review not called)")}`, + ); + lines.push(`${continuePrefix}`); // Spacing + lines.push(...renderFindings(reportFindingData, continuePrefix, expanded, theme)); + return lines; + } // Check for extracted tool data with custom renderers (skip review tools) let hasCustomRendering = false; diff --git a/packages/coding-agent/src/core/tools/web-fetch.ts b/packages/coding-agent/src/core/tools/web-fetch.ts index c8b7b0295..00870f615 100644 --- a/packages/coding-agent/src/core/tools/web-fetch.ts +++ b/packages/coding-agent/src/core/tools/web-fetch.ts @@ -5,6 +5,7 @@ import * as path from "node:path"; import type { AgentTool } from "@oh-my-pi/pi-agent-core"; import { Type } from "@sinclair/typebox"; import { parse as parseHtml } from "node-html-parser"; +import { logger } from "../logger"; // ============================================================================= // Types and Constants @@ -174,9 +175,10 @@ async function loadPage(url: string, options: LoadPageOptions = {}): Promise { }, }, ); - } catch (_err) { - // Silently ignore tool errors + } catch (err) { + logger.warn("Tool onSession error", { error: String(err) }); } } } diff --git a/packages/mom/src/agent.ts b/packages/mom/src/agent.ts index d140481d4..a230f46c7 100644 --- a/packages/mom/src/agent.ts +++ b/packages/mom/src/agent.ts @@ -10,6 +10,7 @@ import { convertToLlm, formatSkillsForPrompt, loadSkillsFromDir, + logger, ModelRegistry, type Skill, } from "@oh-my-pi/pi-coding-agent"; @@ -686,8 +687,8 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi log.logWarning(`Slack API error (${errorContext})`, errMsg); try { await ctx.respondInThread(`_Error: ${errMsg}_`); - } catch { - // Ignore + } catch (err) { + logger.warn("Failed to respond in thread", { error: String(err) }); } } }); @@ -734,7 +735,8 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi mimeType, data: readFileSync(fullPath).toString("base64"), }); - } catch { + } catch (err) { + logger.debug("Failed to read file as image", { path: fullPath, error: String(err) }); nonImagePaths.push(fullPath); } } else { diff --git a/packages/mom/src/context.ts b/packages/mom/src/context.ts index 5225489cd..5765fb554 100644 --- a/packages/mom/src/context.ts +++ b/packages/mom/src/context.ts @@ -17,6 +17,7 @@ import { buildSessionContext, type CompactionEntry, type FileEntry, + logger, type ModelChangeEntry, type SessionContext, type SessionEntry, @@ -221,8 +222,8 @@ export class MomSessionManager { }; newMessages.push({ timestamp: date, slackTs, message: userMessage }); - } catch { - // Skip malformed lines + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); } } @@ -268,8 +269,8 @@ export class MomSessionManager { try { const entry = JSON.parse(line) as FileEntry; entries.push(entry); - } catch { - // Skip malformed lines + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); } } @@ -425,7 +426,8 @@ export class MomSettingsManager { try { const content = readFileSync(this.settingsPath, "utf-8"); return JSON.parse(content); - } catch { + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); return {}; } } @@ -557,7 +559,9 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n if (excludeAfterTs && entry.ts >= excludeAfterTs) continue; logMessages.push(entry); } - } catch {} + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); + } } if (logMessages.length === 0) return 0; @@ -574,7 +578,9 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n // We store the original slack ts in a way we can recover // Actually, let's just check by content match since ts formats differ } - } catch {} + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); + } } } @@ -602,7 +608,9 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n existingMessages.add(content); } } - } catch {} + } catch (err) { + logger.debug("Context parsing error", { error: String(err) }); + } } } diff --git a/packages/mom/src/events.ts b/packages/mom/src/events.ts index 7a017e83f..79780273c 100644 --- a/packages/mom/src/events.ts +++ b/packages/mom/src/events.ts @@ -1,6 +1,7 @@ import { existsSync, type FSWatcher, mkdirSync, readdirSync, statSync, unlinkSync, watch } from "node:fs"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; +import { logger } from "@oh-my-pi/pi-coding-agent"; import { Cron } from "croner"; import * as log from "./log"; import type { SlackBot, SlackEvent } from "./slack"; @@ -266,8 +267,8 @@ export class EventsWatcher { this.deleteFile(filename); return; } - } catch { - // File may have been deleted + } catch (err) { + logger.debug("File stat failed", { filename, error: String(err) }); return; } diff --git a/packages/mom/src/main.ts b/packages/mom/src/main.ts index 2569a2a6a..820a79539 100755 --- a/packages/mom/src/main.ts +++ b/packages/mom/src/main.ts @@ -1,6 +1,7 @@ #!/usr/bin/env bun import { join, resolve } from "node:path"; +import { logger } from "@oh-my-pi/pi-coding-agent"; import { type AgentRunner, getOrCreateRunner } from "./agent"; import { syncLogToContext } from "./context"; import { downloadChannel } from "./download"; @@ -214,8 +215,8 @@ function createSlackContext(event: SlackEvent, slack: SlackBot, state: ChannelSt for (let i = threadMessageTs.length - 1; i >= 0; i--) { try { await slack.deleteMessage(event.channel, threadMessageTs[i]); - } catch { - // Ignore errors deleting thread messages + } catch (err) { + logger.debug("Failed to delete thread message", { error: String(err) }); } } threadMessageTs.length = 0; diff --git a/packages/mom/src/sandbox.ts b/packages/mom/src/sandbox.ts index 944291ca9..6e701215c 100644 --- a/packages/mom/src/sandbox.ts +++ b/packages/mom/src/sandbox.ts @@ -1,3 +1,5 @@ +import { logger } from "@oh-my-pi/pi-coding-agent"; + export type SandboxConfig = { type: "host" } | { type: "docker"; container: string }; export function parseSandboxArg(value: string): SandboxConfig { @@ -193,17 +195,18 @@ function killProcessTree(pid: number): void { if (process.platform === "win32") { try { Bun.spawn(["taskkill", "/F", "/T", "/PID", String(pid)], { stdout: "ignore", stderr: "ignore" }); - } catch { - // Ignore errors + } catch (err) { + logger.debug("Process kill failed", { pid, error: String(err) }); } } else { try { process.kill(-pid, "SIGKILL"); - } catch { + } catch (err) { + logger.debug("Process kill failed", { pid, error: String(err) }); try { process.kill(pid, "SIGKILL"); - } catch { - // Process already dead + } catch (err) { + logger.debug("Process kill failed", { pid, error: String(err) }); } } } diff --git a/packages/mom/src/slack.ts b/packages/mom/src/slack.ts index a1ae913b0..fa11195d7 100644 --- a/packages/mom/src/slack.ts +++ b/packages/mom/src/slack.ts @@ -1,5 +1,6 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs"; import { basename, join } from "node:path"; +import { logger } from "@oh-my-pi/pi-coding-agent"; import { SocketModeClient } from "@slack/socket-mode"; import { WebClient } from "@slack/web-api"; import * as log from "./log"; @@ -445,7 +446,9 @@ export class SlackBot { try { const entry = JSON.parse(line); if (entry.ts) timestamps.add(entry.ts); - } catch {} + } catch (err) { + logger.debug("Failed to parse log entry JSON", { error: String(err) }); + } } return timestamps; } diff --git a/packages/mom/src/store.ts b/packages/mom/src/store.ts index 02594273b..92100a232 100644 --- a/packages/mom/src/store.ts +++ b/packages/mom/src/store.ts @@ -1,6 +1,7 @@ import { existsSync, mkdirSync, readFileSync } from "node:fs"; import { appendFile } from "node:fs/promises"; import { join } from "node:path"; +import { logger } from "@oh-my-pi/pi-coding-agent"; import * as log from "./log"; export interface Attachment { @@ -177,7 +178,8 @@ export class ChannelStore { const lastLine = lines[lines.length - 1]; const message = JSON.parse(lastLine) as LoggedMessage; return message.ts; - } catch { + } catch (err) { + logger.debug("Failed to parse last message timestamp", { error: String(err) }); return null; } } diff --git a/packages/tui/src/autocomplete.ts b/packages/tui/src/autocomplete.ts index a08d84362..df9c87455 100644 --- a/packages/tui/src/autocomplete.ts +++ b/packages/tui/src/autocomplete.ts @@ -387,7 +387,8 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { const fullPath = join(searchDir, entry.name); isDirectory = statSync(fullPath).isDirectory(); } catch { - // Broken symlink or permission error - treat as file + // Broken symlink, file deleted between readdir and stat, or permission error + continue; } } @@ -459,7 +460,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { }); return suggestions; - } catch (_e) { + } catch { // Directory doesn't exist or not accessible return []; } @@ -527,6 +528,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { return suggestions; } catch { + // Directory doesn't exist or not accessible return []; } } diff --git a/packages/tui/src/terminal-image.ts b/packages/tui/src/terminal-image.ts index a0aa569c9..cd9707f38 100644 --- a/packages/tui/src/terminal-image.ts +++ b/packages/tui/src/terminal-image.ts @@ -1,3 +1,4 @@ + export type ImageProtocol = "kitty" | "iterm2" | null; export interface TerminalCapabilities { diff --git a/packages/tui/src/tui.ts b/packages/tui/src/tui.ts index 515340c26..480de938f 100644 --- a/packages/tui/src/tui.ts +++ b/packages/tui/src/tui.ts @@ -86,6 +86,7 @@ export class TUI extends Container { private cursorRow = 0; // Track where cursor is (0-indexed, relative to our first line) private inputBuffer = ""; // Buffer for parsing terminal responses private cellSizeQueryPending = false; + private inputQueue: string[] = []; // Queue input during cell size query to avoid interleaving constructor(terminal: Terminal) { super(); @@ -142,9 +143,24 @@ export class TUI extends Container { this.inputBuffer += data; const filtered = this.parseCellSizeResponse(); if (filtered.length === 0) return; - data = filtered; + if (filtered.length > 0) { + this.inputQueue.push(filtered); + } + // Process queued input after cell size response completes + if (!this.cellSizeQueryPending && this.inputQueue.length > 0) { + const queued = this.inputQueue; + this.inputQueue = []; + for (const item of queued) { + this.processInput(item); + } + } + return; } + this.processInput(data); + } + + private processInput(data: string): void { // Global debug key handler (Shift+Ctrl+D) if (isShiftCtrlD(data) && this.onDebug) { this.onDebug(); @@ -169,16 +185,17 @@ export class TUI extends Container { const heightPx = parseInt(match[1], 10); const widthPx = parseInt(match[2], 10); + // Remove the response from buffer first + this.inputBuffer = this.inputBuffer.replace(responsePattern, ""); + this.cellSizeQueryPending = false; + if (heightPx > 0 && widthPx > 0) { setCellDimensions({ widthPx, heightPx }); // Invalidate all components so images re-render with correct dimensions + // This is safe now because cellSizeQueryPending=false prevents race with render this.invalidate(); this.requestRender(); } - - // Remove the response from buffer - this.inputBuffer = this.inputBuffer.replace(responsePattern, ""); - this.cellSizeQueryPending = false; } // Check if we have a partial cell size response starting (wait for more data) @@ -206,8 +223,11 @@ export class TUI extends Container { } private doRender(): void { + // Capture terminal dimensions at start to ensure consistency throughout render const width = this.terminal.columns; const height = this.terminal.rows; + // Snapshot cursor position at start of render for consistent viewport calculations + const currentCursorRow = this.cursorRow; // Render all components to get new lines const newLines = this.render(width); @@ -267,10 +287,10 @@ export class TUI extends Container { } // Check if firstChanged is outside the viewport - // cursorRow is the line where cursor is (0-indexed) - // Viewport shows lines from (cursorRow - height + 1) to cursorRow + // Use snapshotted cursor position for consistent viewport calculation + // Viewport shows lines from (currentCursorRow - height + 1) to currentCursorRow // If firstChanged < viewportTop, we need full re-render - const viewportTop = this.cursorRow - height + 1; + const viewportTop = currentCursorRow - height + 1; if (firstChanged < viewportTop) { // First change is above viewport - need full re-render let buffer = "\x1b[?2026h"; // Begin synchronized output @@ -291,8 +311,8 @@ export class TUI extends Container { // Build buffer with all updates wrapped in synchronized output let buffer = "\x1b[?2026h"; // Begin synchronized output - // Move cursor to first changed line - const lineDiff = firstChanged - this.cursorRow; + // Move cursor to first changed line using snapshotted position + const lineDiff = firstChanged - currentCursorRow; if (lineDiff > 0) { buffer += `\x1b[${lineDiff}B`; // Move down } else if (lineDiff < 0) { @@ -323,7 +343,9 @@ export class TUI extends Container { try { fs.mkdirSync(path.dirname(crashLogPath), { recursive: true }); fs.writeFileSync(crashLogPath, crashData); - } catch {} + } catch { + // Ignore - crash log is best-effort + } throw new Error(`Rendered line ${i} exceeds terminal width. Debug log written to ${crashLogPath}`); } buffer += line;