├── .github ├── dependabot.yml └── workflows │ ├── master.yml │ └── test-workflow.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── README.md ├── action.yml ├── dist └── index.js ├── package-lock.json ├── package.json ├── src ├── clients │ ├── anthropic.ts │ ├── openai.ts │ └── request.ts ├── index.ts ├── models.ts ├── prompt.ts └── types.ts └── tsconfig.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version-file: '.nvmrc' 22 | cache: 'npm' 23 | 24 | - name: Install dependencies 25 | run: npm ci 26 | 27 | - name: Build 28 | run: npm run build 29 | -------------------------------------------------------------------------------- /.github/workflows/test-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Test action 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | debug: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Configure node 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version-file: '.nvmrc' 18 | cache: 'npm' 19 | 20 | - name: Simulate broken step 21 | continue-on-error: true 22 | run: npm run test 2> gpt_error.log 23 | 24 | - name: Analyze error log with GPT-4 25 | uses: ./ 26 | with: 27 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 28 | 29 | - name: Analyze error log with Claude 30 | uses: ./ 31 | with: 32 | ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .env 4 | .env.local -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 100, 5 | "singleAttributePerLine": false 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CI/CD Debugger 2 | 3 | Debug flaky CI/CD pipelines with models such as GPT-4 or Claude 3. 4 | 5 | ## Inputs 6 | 7 | By providing one of the inputs, the action will use the specified model to analyze the error log: 8 | 9 | - `OPENAI_API_KEY` - OpenAI API key. You can get it from [OpenAI Console](https://platform.openai.com/account/api-keys). When provided, the action will use GPT-4 Turbo model to analyze the error message. 10 | 11 | - `ANTHROPIC_API_KEY` - Anthropic API key. You can get it from [Anthropic console](https://console.anthropic.com/). When provided, the action will use Claude 3 model to analyze the error message. 12 | 13 | ## How to 14 | 15 | 1. Redirect error log of one of your failing steps to `gpt_error.log` file: 16 | 2. Add new step to your workflow and mark it with either `always()` or `failure()` expression: 17 | 18 | ```yaml 19 | jobs: 20 | tests: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Run e2e 24 | id: e2e 25 | run: npm run e2e 2> gpt_error.log 26 | 27 | - uses: przeprogramowani/gpt-debugger@main 28 | if: ${{ failure() }} 29 | with: 30 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 31 | ``` 32 | 33 | ## Output 34 | 35 | Example output from the action: 36 | 37 | ```txt 38 | 🧑‍⚕️ Presenting GPT-4 issue analysis: 39 | 40 | The error indicates that an "e2e" script is expected but not defined in the package.json file. To fix this issue, ensure you have an "e2e" script specified in your package.json file under the "scripts" section. If your intention was to run end-to-end tests, you might need something like: 41 | 42 | "scripts": { 43 | "e2e": "your-e2e-test-command here" 44 | } 45 | 46 | Replace "your-e2e-test-command here" with the actual command you use to run your end-to-end tests. 47 | 48 | Relevant file: 49 | - package.json 50 | ``` 51 | 52 | ## Authors 53 | 54 | - [Przeprogramowani](https://przeprogramowani.pl) 55 | - [Opanuj Frontend](https://opanujfrontend.pl) 56 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD Debugger 2 | description: Debug CI/CD failures with LLMs 3 | 4 | branding: 5 | icon: 'crosshair' 6 | color: 'purple' 7 | 8 | inputs: 9 | OPENAI_API_KEY: 10 | description: 'OpenAI API Key' 11 | ANTHROPIC_API_KEY: 12 | description: 'Anthropic API Key' 13 | 14 | runs: 15 | using: 'composite' 16 | steps: 17 | - name: Run AI Debugging 18 | run: node ${GITHUB_ACTION_PATH}/dist/index.js 19 | shell: bash 20 | env: 21 | OPENAI_API_KEY: ${{ inputs.OPENAI_API_KEY }} 22 | ANTHROPIC_API_KEY: ${{ inputs.ANTHROPIC_API_KEY }} 23 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpt-debugger", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "gpt-debugger", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@anthropic-ai/sdk": "0.39.0", 13 | "openai": "4.91.0" 14 | }, 15 | "devDependencies": { 16 | "@vercel/ncc": "0.38.3", 17 | "typescript": "5.8.2" 18 | } 19 | }, 20 | "node_modules/@anthropic-ai/sdk": { 21 | "version": "0.39.0", 22 | "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.39.0.tgz", 23 | "integrity": "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==", 24 | "license": "MIT", 25 | "dependencies": { 26 | "@types/node": "^18.11.18", 27 | "@types/node-fetch": "^2.6.4", 28 | "abort-controller": "^3.0.0", 29 | "agentkeepalive": "^4.2.1", 30 | "form-data-encoder": "1.7.2", 31 | "formdata-node": "^4.3.2", 32 | "node-fetch": "^2.6.7" 33 | } 34 | }, 35 | "node_modules/@types/node": { 36 | "version": "18.19.26", 37 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", 38 | "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", 39 | "dependencies": { 40 | "undici-types": "~5.26.4" 41 | } 42 | }, 43 | "node_modules/@types/node-fetch": { 44 | "version": "2.6.11", 45 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", 46 | "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", 47 | "dependencies": { 48 | "@types/node": "*", 49 | "form-data": "^4.0.0" 50 | } 51 | }, 52 | "node_modules/@vercel/ncc": { 53 | "version": "0.38.3", 54 | "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.3.tgz", 55 | "integrity": "sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==", 56 | "dev": true, 57 | "license": "MIT", 58 | "bin": { 59 | "ncc": "dist/ncc/cli.js" 60 | } 61 | }, 62 | "node_modules/abort-controller": { 63 | "version": "3.0.0", 64 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 65 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 66 | "dependencies": { 67 | "event-target-shim": "^5.0.0" 68 | }, 69 | "engines": { 70 | "node": ">=6.5" 71 | } 72 | }, 73 | "node_modules/agentkeepalive": { 74 | "version": "4.5.0", 75 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 76 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 77 | "dependencies": { 78 | "humanize-ms": "^1.2.1" 79 | }, 80 | "engines": { 81 | "node": ">= 8.0.0" 82 | } 83 | }, 84 | "node_modules/asynckit": { 85 | "version": "0.4.0", 86 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 87 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 88 | }, 89 | "node_modules/combined-stream": { 90 | "version": "1.0.8", 91 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 92 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 93 | "dependencies": { 94 | "delayed-stream": "~1.0.0" 95 | }, 96 | "engines": { 97 | "node": ">= 0.8" 98 | } 99 | }, 100 | "node_modules/delayed-stream": { 101 | "version": "1.0.0", 102 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 103 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 104 | "engines": { 105 | "node": ">=0.4.0" 106 | } 107 | }, 108 | "node_modules/event-target-shim": { 109 | "version": "5.0.1", 110 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 111 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 112 | "engines": { 113 | "node": ">=6" 114 | } 115 | }, 116 | "node_modules/form-data": { 117 | "version": "4.0.0", 118 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 119 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 120 | "dependencies": { 121 | "asynckit": "^0.4.0", 122 | "combined-stream": "^1.0.8", 123 | "mime-types": "^2.1.12" 124 | }, 125 | "engines": { 126 | "node": ">= 6" 127 | } 128 | }, 129 | "node_modules/form-data-encoder": { 130 | "version": "1.7.2", 131 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 132 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 133 | }, 134 | "node_modules/formdata-node": { 135 | "version": "4.4.1", 136 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 137 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 138 | "dependencies": { 139 | "node-domexception": "1.0.0", 140 | "web-streams-polyfill": "4.0.0-beta.3" 141 | }, 142 | "engines": { 143 | "node": ">= 12.20" 144 | } 145 | }, 146 | "node_modules/formdata-node/node_modules/web-streams-polyfill": { 147 | "version": "4.0.0-beta.3", 148 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 149 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 150 | "engines": { 151 | "node": ">= 14" 152 | } 153 | }, 154 | "node_modules/humanize-ms": { 155 | "version": "1.2.1", 156 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 157 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 158 | "dependencies": { 159 | "ms": "^2.0.0" 160 | } 161 | }, 162 | "node_modules/mime-db": { 163 | "version": "1.52.0", 164 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 165 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 166 | "engines": { 167 | "node": ">= 0.6" 168 | } 169 | }, 170 | "node_modules/mime-types": { 171 | "version": "2.1.35", 172 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 173 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 174 | "dependencies": { 175 | "mime-db": "1.52.0" 176 | }, 177 | "engines": { 178 | "node": ">= 0.6" 179 | } 180 | }, 181 | "node_modules/ms": { 182 | "version": "2.1.3", 183 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 184 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 185 | }, 186 | "node_modules/node-domexception": { 187 | "version": "1.0.0", 188 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 189 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 190 | "funding": [ 191 | { 192 | "type": "github", 193 | "url": "https://github.com/sponsors/jimmywarting" 194 | }, 195 | { 196 | "type": "github", 197 | "url": "https://paypal.me/jimmywarting" 198 | } 199 | ], 200 | "engines": { 201 | "node": ">=10.5.0" 202 | } 203 | }, 204 | "node_modules/node-fetch": { 205 | "version": "2.7.0", 206 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 207 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 208 | "dependencies": { 209 | "whatwg-url": "^5.0.0" 210 | }, 211 | "engines": { 212 | "node": "4.x || >=6.0.0" 213 | }, 214 | "peerDependencies": { 215 | "encoding": "^0.1.0" 216 | }, 217 | "peerDependenciesMeta": { 218 | "encoding": { 219 | "optional": true 220 | } 221 | } 222 | }, 223 | "node_modules/openai": { 224 | "version": "4.91.0", 225 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.91.0.tgz", 226 | "integrity": "sha512-zdDg6eyvUmCP58QAW7/aPb+XdeavJ51pK6AcwZOWG5QNSLIovVz0XonRL9vARGJRmw8iImmvf2A31Q7hoh544w==", 227 | "license": "Apache-2.0", 228 | "dependencies": { 229 | "@types/node": "^18.11.18", 230 | "@types/node-fetch": "^2.6.4", 231 | "abort-controller": "^3.0.0", 232 | "agentkeepalive": "^4.2.1", 233 | "form-data-encoder": "1.7.2", 234 | "formdata-node": "^4.3.2", 235 | "node-fetch": "^2.6.7" 236 | }, 237 | "bin": { 238 | "openai": "bin/cli" 239 | }, 240 | "peerDependencies": { 241 | "ws": "^8.18.0", 242 | "zod": "^3.23.8" 243 | }, 244 | "peerDependenciesMeta": { 245 | "ws": { 246 | "optional": true 247 | }, 248 | "zod": { 249 | "optional": true 250 | } 251 | } 252 | }, 253 | "node_modules/tr46": { 254 | "version": "0.0.3", 255 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 256 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 257 | }, 258 | "node_modules/typescript": { 259 | "version": "5.8.2", 260 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", 261 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", 262 | "dev": true, 263 | "license": "Apache-2.0", 264 | "bin": { 265 | "tsc": "bin/tsc", 266 | "tsserver": "bin/tsserver" 267 | }, 268 | "engines": { 269 | "node": ">=14.17" 270 | } 271 | }, 272 | "node_modules/undici-types": { 273 | "version": "5.26.5", 274 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 275 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 276 | }, 277 | "node_modules/webidl-conversions": { 278 | "version": "3.0.1", 279 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 280 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 281 | }, 282 | "node_modules/whatwg-url": { 283 | "version": "5.0.0", 284 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 285 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 286 | "dependencies": { 287 | "tr46": "~0.0.3", 288 | "webidl-conversions": "^3.0.0" 289 | } 290 | } 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpt-debugger", 3 | "version": "1.0.0", 4 | "description": "Debugging with GPT", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "ncc build src/index.ts -o dist", 8 | "start": "node dist/index.js" 9 | }, 10 | "author": "Przeprogramowani", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@vercel/ncc": "0.38.3", 14 | "typescript": "5.8.2" 15 | }, 16 | "dependencies": { 17 | "@anthropic-ai/sdk": "0.39.0", 18 | "openai": "4.91.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/clients/anthropic.ts: -------------------------------------------------------------------------------- 1 | import Anthropic from '@anthropic-ai/sdk'; 2 | import { AI_MODELS } from '../models'; 3 | 4 | export const anthropicDebugRequest: DebugRequest = async (apiKey: string, prompt: string) => { 5 | const anthropic = new Anthropic({ 6 | apiKey, 7 | }); 8 | 9 | const chatCompletion = await anthropic.messages.create({ 10 | model: AI_MODELS.anthropic.claudeSonnet, 11 | max_tokens: 2048, 12 | messages: [{ role: 'user', content: prompt }], 13 | }); 14 | 15 | if (chatCompletion.content[0].type === 'text') { 16 | return chatCompletion.content[0].text; 17 | } else { 18 | return 'Unsupported content type'; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/clients/openai.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from 'openai'; 2 | import { AI_MODELS } from '../models'; 3 | 4 | export const openAIDebugRequest: DebugRequest = async (apiKey: string, prompt: string) => { 5 | const openai = new OpenAI({ 6 | apiKey, 7 | }); 8 | 9 | const chatCompletion = await openai.chat.completions.create({ 10 | messages: [{ role: 'user', content: prompt }], 11 | model: AI_MODELS.openai.gpt4o, 12 | }); 13 | 14 | return chatCompletion.choices[0].message.content!; 15 | }; 16 | -------------------------------------------------------------------------------- /src/clients/request.ts: -------------------------------------------------------------------------------- 1 | import { openAIDebugRequest } from './openai'; 2 | import { anthropicDebugRequest } from './anthropic'; 3 | 4 | export async function generateDebugResponse(prompt: string) { 5 | if (process.env['OPENAI_API_KEY']) { 6 | console.log('Using OpenAI model to debug the error log.'); 7 | return await openAIDebugRequest(process.env['OPENAI_API_KEY'], prompt); 8 | } 9 | 10 | if (process.env['ANTHROPIC_API_KEY']) { 11 | console.log('Using Anthropic model to debug the error log.'); 12 | return await anthropicDebugRequest(process.env['ANTHROPIC_API_KEY'], prompt); 13 | } 14 | 15 | throw new Error('Provide one of the supported API Keys to get started.'); 16 | } 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, existsSync } from 'fs'; 2 | import { join } from 'path'; 3 | import { buildPrompt } from './prompt'; 4 | import { generateDebugResponse } from './clients/request'; 5 | 6 | const ERROR_LOG_FILE_NAME = 'gpt_error.log'; 7 | 8 | async function main(): Promise { 9 | const logPath = join(process.cwd(), ERROR_LOG_FILE_NAME); 10 | 11 | if (!existsSync(logPath)) { 12 | console.error(`❌ Error log file not found at ${logPath}`); 13 | process.exit(1); 14 | } 15 | 16 | const errorLog = readFileSync(logPath, 'utf8'); 17 | const prompt = buildPrompt(errorLog); 18 | 19 | const debugResponse = await generateDebugResponse(prompt); 20 | 21 | console.log('🧑‍⚕️ Presenting issue analysis:'); 22 | console.log(debugResponse); 23 | } 24 | 25 | main(); 26 | -------------------------------------------------------------------------------- /src/models.ts: -------------------------------------------------------------------------------- 1 | export const AI_MODELS = { 2 | anthropic: { 3 | claudeSonnet: 'claude-3-7-sonnet-20250219', 4 | }, 5 | openai: { 6 | gpt4o: 'gpt-4o', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/prompt.ts: -------------------------------------------------------------------------------- 1 | export const buildPrompt = (errorLog: string): string => ` 2 | You're an expert in debugging issues with CI/CD pipelines of web applications. You've been asked to investigate an error in a pipeline that builds a web application. The error log is wrapped with the tag ERROR_LOG: 3 | 4 | 5 | ${errorLog} 6 | 7 | 8 | If the log is empty, do not provide a solution. Terminate the conversation. 9 | 10 | If it is not empty, solve the problem is a following way: 11 | 1. Identify area of the problem. 12 | 2. List three possible reasons for the error. 13 | 3. Provide a solution for each reason. 14 | 4. If the reason is well-known, you are allowed to go directly to the solution. 15 | 16 | Follow these rules: 17 | - Be concise and to the point. 18 | - Avoid repeating my question in the answer. 19 | - Do not include any sensitive information. 20 | - Do not repeat the same information. 21 | - Do not use Markdown. The response will be logged into the console. 22 | `; 23 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | type DebugRequest = (apiKey: string, prompt: string) => Promise; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "strict": true, 5 | "preserveConstEnums": true, 6 | "noEmit": true, 7 | "sourceMap": false, 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "isolatedModules": true 14 | }, 15 | "exclude": ["node_modules", "**/*.test.ts"] 16 | } 17 | --------------------------------------------------------------------------------