diff --git a/.github/workflows/emergency-controls.yml b/.github/workflows/emergency-controls.yml index d1b65be..2242bb7 100644 --- a/.github/workflows/emergency-controls.yml +++ b/.github/workflows/emergency-controls.yml @@ -1,11 +1,7 @@ --- name: Emergency Controls -# Centralized timeout configuration -env: - EMERGENCY_TIMEOUT_MINUTES: 10 - -"on": +on: workflow_dispatch: inputs: action: @@ -35,6 +31,8 @@ permissions: jobs: verify-authorization: + # Only run on manual dispatch, never on push/PR events + if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest outputs: authorized: ${{ steps.auth-check.outputs.authorized }} @@ -62,7 +60,7 @@ jobs: needs: verify-authorization if: needs.verify-authorization.outputs.authorized == 'true' runs-on: ubuntu-latest - timeout-minutes: ${{ fromJSON(env.EMERGENCY_TIMEOUT_MINUTES) }} + timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v4 diff --git a/package-lock.json b/package-lock.json index 1fa893f..143217b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "chalk": "^4.1.2", "commander": "^11.0.0", "fs-extra": "^11.1.0", - "inquirer": "^8.2.5", + "inquirer": "^10.2.2", "minimatch": "^10.0.3", "validate-npm-package-name": "^5.0.0" }, @@ -744,6 +744,237 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", + "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.5.0.tgz", + "integrity": "sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^2.5.0", + "@inquirer/confirm": "^3.2.0", + "@inquirer/editor": "^2.2.0", + "@inquirer/expand": "^2.3.0", + "@inquirer/input": "^2.3.0", + "@inquirer/number": "^1.1.0", + "@inquirer/password": "^2.2.0", + "@inquirer/rawlist": "^2.3.0", + "@inquirer/search": "^1.1.0", + "@inquirer/select": "^2.5.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -1830,11 +2061,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { - "version": "20.19.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.2.tgz", - "integrity": "sha512-9pLGGwdzOUBDYi0GNjM97FIA+f92fqSke6joWeBjWXllfNxZBs7qeMF7tvtOIsbY45xkWkxrdwUfUf3MnQa9gA==", - "dev": true, + "version": "22.17.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.0.tgz", + "integrity": "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -1847,6 +2086,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "license": "MIT" + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -2432,26 +2677,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -2459,17 +2684,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2550,30 +2764,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2705,37 +2895,13 @@ "dev": true, "license": "MIT" }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "license": "ISC", "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/cliui": { @@ -2787,15 +2953,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2961,18 +3118,6 @@ "node": ">=0.10.0" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3546,30 +3691,6 @@ "bser": "2.1.1" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4016,26 +4137,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4109,6 +4210,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -4122,29 +4224,22 @@ } }, "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-10.2.2.tgz", + "integrity": "sha512-tyao/4Vo36XnUItZ7DnUXX4f1jVao2mSrleV/5IPtW/XAEA26hRVsbc68nuTEKWcr5vMP/1mVoT2O7u8H4v1Vg==", "license": "MIT", "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" + "@inquirer/core": "^9.1.0", + "@inquirer/prompts": "^5.5.0", + "@inquirer/type": "^1.5.3", + "@types/mute-stream": "^0.0.4", + "ansi-escapes": "^4.3.2", + "mute-stream": "^1.0.0", + "run-async": "^3.0.0", + "rxjs": "^7.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=18" } }, "node_modules/is-arrayish": { @@ -4212,15 +4307,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4254,18 +4340,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5142,6 +5216,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { @@ -5158,22 +5233,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5295,6 +5354,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5323,10 +5383,13 @@ "license": "MIT" }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "license": "ISC" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/natural-compare": { "version": "1.4.0", @@ -5477,6 +5540,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -5506,38 +5570,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5887,20 +5919,6 @@ "dev": true, "license": "MIT" }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/regexp-tree": { "version": "0.1.27", "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", @@ -5985,19 +6003,6 @@ "node": ">=10" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -6027,9 +6032,9 @@ } }, "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -6068,26 +6073,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -6148,6 +6133,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC" }, "node_modules/sisteransi": { @@ -6218,15 +6204,6 @@ "node": ">=8" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6376,22 +6353,13 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/tmpl": { @@ -6599,7 +6567,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/universal-user-agent": { @@ -6659,12 +6626,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -6699,15 +6660,6 @@ "makeerror": "1.0.12" } }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6858,6 +6810,18 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 372202e..482632f 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "chalk": "^4.1.2", "commander": "^11.0.0", "fs-extra": "^11.1.0", - "inquirer": "^8.2.5", + "inquirer": "^10.2.2", "minimatch": "^10.0.3", "validate-npm-package-name": "^5.0.0" }, @@ -87,5 +87,8 @@ "ai-review-demo": "./scripts/ai-review-resolvable.sh demo", "ai-review-analyze": "./scripts/ai-review-resolvable.sh analyze" }, - "main": "package.json" + "main": "package.json", + "overrides": { + "tmp": "0.2.4" + } } diff --git a/scripts/ai-review/services/ai-analysis-service.js b/scripts/ai-review/services/ai-analysis-service.js index 6e2371c..edcc800 100755 --- a/scripts/ai-review/services/ai-analysis-service.js +++ b/scripts/ai-review/services/ai-analysis-service.js @@ -238,6 +238,7 @@ For subjective style preferences or speculative optimizations, do not include th return suggestions.map(suggestion => ({ ...suggestion, file_path: file.filename, + line_number: suggestion.line || suggestion.line_number || null, // Standardize line number field commit: file.sha || null, position: this.calculatePosition(file.patch, suggestion.line), timestamp: new Date().toISOString(), diff --git a/scripts/ai-review/services/analysis-orchestrator.js b/scripts/ai-review/services/analysis-orchestrator.js index 0590c77..b8de7d8 100644 --- a/scripts/ai-review/services/analysis-orchestrator.js +++ b/scripts/ai-review/services/analysis-orchestrator.js @@ -420,37 +420,58 @@ The code changes in this pull request meet quality standards and are ready for a const inlineEnabled = process.env.AI_ENABLE_INLINE_COMMENTS !== 'false'; const stats = this.generateStatistics(suggestions); - // Generate summary comment with statistics + // Generate summary comment with statistics only (not detailed suggestions) const summaryComment = this.generateSummaryComment( suggestions, stats, inlineEnabled, - prData + prData, + true // skipDetailedSuggestions = true ); // Always post summary comment first for visibility await this.github.postComment(prNumber, summaryComment); console.log('✅ Posted AI review summary comment'); - // Post inline comments for resolvable suggestions if enabled - if (inlineEnabled && this.hasResolvableSuggestions(suggestions)) { - const inlineComments = this.generateInlineComments(suggestions); + // Post ALL suggestions as inline comments if enabled, otherwise fallback + if (inlineEnabled) { + const inlineComments = this.generateAllInlineComments(suggestions); + let successfulInlineComments = 0; // Post each inline comment individually for (const comment of inlineComments) { try { await this.github.postInlineComment(prNumber, comment); + successfulInlineComments++; } catch (error) { - console.warn(`Failed to post inline comment: ${error.message}`); + console.warn( + `Failed to post inline comment for ${comment.path}:${comment.line}: ${error.message}` + ); + + // Fallback: Add to a list of failed comments that will be posted as regular comments + try { + const fallbackBody = `**File**: \`${comment.path}\` (line ${comment.line})\n\n${comment.body}`; + await this.github.postComment(prNumber, fallbackBody); + } catch (fallbackError) { + console.error( + `Failed fallback comment for ${comment.path}:${comment.line}: ${fallbackError.message}` + ); + } } } console.log( - `✅ Posted ${inlineComments.length} inline resolvable suggestions` + `✅ Posted ${successfulInlineComments} inline comments (${inlineComments.length} total suggestions)` + ); + } else { + // Fallback: Post detailed suggestions as regular comments + const detailedComment = this.formatSuggestionsAsComment(suggestions); + await this.github.postComment(prNumber, detailedComment); + console.log( + '✅ Posted detailed suggestions as regular comments (inline disabled)' ); } - // Note: Detailed review is now included in the summary comment above - console.log('✅ Posted comprehensive review with detailed analysis'); + console.log('✅ Posted comprehensive review with inline analysis'); } catch (error) { console.error('❌ Failed to post suggestions to GitHub:', error.message); // Don't throw - this shouldn't break the workflow @@ -460,17 +481,21 @@ The code changes in this pull request meet quality standards and are ready for a /** * Generate summary comment header */ - generateSummaryComment(suggestions, stats, inlineEnabled, prData) { - const reviewType = inlineEnabled - ? 'Resolvable Comments' - : 'Enhanced Comments'; + generateSummaryComment( + suggestions, + stats, + inlineEnabled, + prData, + skipDetailedSuggestions = false + ) { + const reviewType = inlineEnabled ? 'Inline Comments' : 'Enhanced Comments'; const hasResolvable = stats.by_confidence.very_high > 0; const recommendation = this.generateApprovalRecommendation(stats); let summary = `## 🤖 AI Review by ${reviewType}\n\n`; - if (hasResolvable && inlineEnabled) { - summary += `🔒 **${stats.by_confidence.very_high} critical suggestion${stats.by_confidence.very_high !== 1 ? 's' : ''} require${stats.by_confidence.very_high === 1 ? 's' : ''} immediate attention** (resolvable)\n\n`; + if (suggestions.length > 0 && inlineEnabled) { + summary += `📍 **${suggestions.length} suggestion${suggestions.length !== 1 ? 's' : ''} posted inline** - check the specific files and lines below.\n\n`; } const overallConfidence = this.calculateOverallConfidence(suggestions); @@ -480,10 +505,10 @@ The code changes in this pull request meet quality standards and are ready for a - **Total Suggestions**: ${stats.total} - **Overall Confidence**: ${overallConfidence.label} (${overallConfidence.percentage}%) - **Analysis Quality**: ${analysisQuality} -- **Critical** (≥95%): ${stats.by_confidence.very_high} ${inlineEnabled ? '(resolvable)' : '(high priority)'} -- **High** (80-94%): ${stats.by_confidence.high} (enhanced comments) -- **Medium** (65-79%): ${stats.by_confidence.medium} (informational) -- **Low** (<65%): ${stats.by_confidence.low} (suppressed) +- **Critical** (≥95%): ${stats.by_confidence.very_high} ${inlineEnabled ? '(resolvable inline)' : '(high priority)'} +- **High** (80-94%): ${stats.by_confidence.high} (inline comments) +- **Medium** (65-79%): ${stats.by_confidence.medium} (inline informational) +- **Low** (<65%): ${stats.by_confidence.low} (inline or suppressed) ### Categories ${Object.entries(stats.by_category) @@ -493,15 +518,20 @@ ${Object.entries(stats.by_category) // Add approval recommendation summary += `\n\n${recommendation.icon} **Recommendation: ${recommendation.action}**\n\n${recommendation.reasoning}`; - // Add detailed review section - if (suggestions.length > 0) { + // Only add detailed review section if not skipping (for backward compatibility) + if (!skipDetailedSuggestions && suggestions.length > 0) { summary += `\n\n## 📝 Detailed Review\n\n`; summary += this.formatDetailedSuggestions(suggestions); } if (hasResolvable && inlineEnabled) { summary += `\n\n### 📝 Action Required -Please review and resolve the critical suggestions marked with 🔒 below. These can be applied with one click using GitHub's suggestion feature.`; +Please review and resolve the critical suggestions marked with 🔒 in the inline comments. These can be applied with one click using GitHub's suggestion feature.`; + } + + if (suggestions.length > 0 && inlineEnabled) { + summary += `\n\n### 📂 Review the Files +All suggestions have been posted as inline comments on the specific files and lines. Navigate through the changed files to see detailed feedback.`; } summary += `\n\n--- @@ -726,6 +756,50 @@ Please review and resolve the critical suggestions marked with 🔒 below. These return 'ℹ️'; } + /** + * Get confidence label from score + */ + getConfidenceLabel(confidence) { + if (confidence >= 0.95) { + return 'Critical'; + } + if (confidence >= 0.8) { + return 'High Confidence'; + } + if (confidence >= 0.65) { + return 'Medium Confidence'; + } + if (confidence >= 0.5) { + return 'Low Confidence'; + } + return 'Very Low Confidence'; + } + + /** + * Try to infer line number from suggestion context + */ + inferLineNumber(suggestion) { + // Try to extract line number from various possible fields + if (suggestion.context && typeof suggestion.context === 'object') { + if (suggestion.context.line_number) { + return suggestion.context.line_number; + } + if (suggestion.context.line) { + return suggestion.context.line; + } + } + + // Look for line numbers in the original code context + if (suggestion.originalCode) { + // This is a simple heuristic - in a real implementation you'd want + // to match the code against the actual file diff to find the line + return 1; // Default fallback + } + + // Default to line 1 if we can't infer + return null; + } + /** * Check if there are any resolvable suggestions */ @@ -734,41 +808,91 @@ Please review and resolve the critical suggestions marked with 🔒 below. These } /** - * Generate inline comments for GitHub review + * Generate inline comments for ALL suggestions */ - generateInlineComments(suggestions) { + generateAllInlineComments(suggestions) { const inlineComments = []; - const resolvableLimit = 5; // Limit to prevent spam + const resolvableLimit = 8; // Increased limit for resolvable suggestions let resolvableCount = 0; for (const suggestion of suggestions) { - if (suggestion.confidence >= 0.95 && resolvableCount < resolvableLimit) { - // Generate resolvable suggestion only if we have required fields - if ( - suggestion.line_number && - suggestion.suggestedCode && - suggestion.originalCode && - suggestion.file_path - ) { - inlineComments.push({ - path: suggestion.file_path, - line: suggestion.line_number, - body: `🔒 **Critical**: ${suggestion.description} - -\`\`\`suggestion -${suggestion.suggestedCode} -\`\`\` - -**Confidence**: ${Math.round(suggestion.confidence * 100)}% | **Category**: ${suggestion.category}`, - }); - resolvableCount++; + // Skip very low confidence suggestions to avoid spam + if (suggestion.confidence < 0.5) { + continue; + } + + // Ensure we have the minimum required fields for inline comments + if (!suggestion.file_path) { + console.warn( + `Skipping suggestion without file_path: ${suggestion.description}` + ); + continue; + } + + // Use line_number if available, otherwise try to infer from context or default to 1 + const lineNumber = + suggestion.line_number || + suggestion.line || + this.inferLineNumber(suggestion) || + 1; + + const icon = this.getConfidenceIcon(suggestion.confidence); + const confidencePercent = Math.round(suggestion.confidence * 100); + const confidenceLabel = this.getConfidenceLabel(suggestion.confidence); + + let body = `${icon} **${confidenceLabel}**: ${suggestion.description}`; + + // Add reasoning if available + if (suggestion.reasoning) { + body += `\n\n${suggestion.reasoning}`; + } + + // Handle resolvable suggestions (high confidence with code suggestions) + if ( + suggestion.confidence >= 0.95 && + suggestion.suggestedCode && + suggestion.originalCode && + resolvableCount < resolvableLimit + ) { + body += `\n\n\`\`\`suggestion\n${suggestion.suggestedCode}\n\`\`\``; + resolvableCount++; + } else if (suggestion.suggestedCode) { + // Show suggested code even for lower confidence + body += `\n\n**Suggested Code:**\n\`\`\`\n${suggestion.suggestedCode}\n\`\`\``; + + if (suggestion.originalCode) { + body += `\n\n**Current Code:**\n\`\`\`\n${suggestion.originalCode}\n\`\`\``; } } + + // Add metadata + body += `\n\n**Confidence**: ${confidencePercent}% | **Category**: ${suggestion.category || 'General'}`; + + if (suggestion.severity) { + body += ` | **Severity**: ${suggestion.severity}`; + } + + inlineComments.push({ + path: suggestion.file_path, + line: lineNumber, + body, + }); } + console.log( + `Generated ${inlineComments.length} inline comments (${resolvableCount} resolvable)` + ); return inlineComments; } + /** + * Generate inline comments for GitHub review (legacy method for backward compatibility) + */ + generateInlineComments(suggestions) { + // For backward compatibility, just call the new method + return this.generateAllInlineComments(suggestions); + } + /** * Format all suggestions as a comment (fallback when inline not available) */ diff --git a/tests/ai-review/analysis-orchestrator.test.js b/tests/ai-review/analysis-orchestrator.test.js index dbedff1..92ddab2 100644 --- a/tests/ai-review/analysis-orchestrator.test.js +++ b/tests/ai-review/analysis-orchestrator.test.js @@ -107,10 +107,10 @@ describe('AnalysisOrchestrator', () => { expect.stringContaining('AI Review by') ); - // Should post detailed review in summary + // Should post inline comments instead of detailed review in summary expect(mockGitHub.postComment).toHaveBeenCalledWith( 123, - expect.stringContaining('Detailed Review') + expect.stringContaining('posted inline') ); }); @@ -298,8 +298,8 @@ describe('AnalysisOrchestrator', () => { expect(inlineComments[0].body).toContain('**Confidence**: 96%'); }); - it('should limit resolvable suggestions to 5 per PR', () => { - const suggestions = Array(10) + it('should limit resolvable suggestions to 8 per PR', () => { + const suggestions = Array(12) .fill(null) .map((_, i) => ({ confidence: 0.96, @@ -313,7 +313,14 @@ describe('AnalysisOrchestrator', () => { const inlineComments = orchestrator.generateInlineComments(suggestions); - expect(inlineComments).toHaveLength(5); + // Should create 12 total inline comments, but only 8 should be resolvable + expect(inlineComments).toHaveLength(12); + + // Check that only 8 have suggestion blocks (resolvable) + const resolvableComments = inlineComments.filter(comment => + comment.body.includes('```suggestion') + ); + expect(resolvableComments).toHaveLength(8); }); });