├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── api │ ├── get-ai-models.ts │ └── send-prompt.ts ├── cmd │ ├── create-pr.ts │ ├── existing-model.ts │ ├── install-model.ts │ ├── integrate-external-api.ts │ └── setup.ts ├── db │ ├── add-ai-model.ts │ ├── add-gpt-api-key.ts │ ├── get-default-ai.ts │ ├── get-default-api-key.ts │ └── init-db.ts ├── helpers │ ├── constants.ts │ ├── generate-gemeni-pr.ts │ ├── generate-pr-summary.ts │ ├── read-latest-commits.ts │ └── setup-directory.ts ├── index.ts └── types │ └── external-apis.d.ts ├── template.md ├── tsconfig.json └── tsup.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 2 | 3 | # Logs 4 | 5 | logs 6 | _.log 7 | npm-debug.log_ 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | .pnpm-debug.log* 12 | 13 | # Caches 14 | 15 | .cache 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 20 | 21 | # Runtime data 22 | 23 | pids 24 | _.pid 25 | _.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | 34 | coverage 35 | *.lcov 36 | 37 | # nyc test coverage 38 | 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | 43 | .grunt 44 | 45 | # Bower dependency directory (https://bower.io/) 46 | 47 | bower_components 48 | 49 | # node-waf configuration 50 | 51 | .lock-wscript 52 | 53 | # Compiled binary addons (https://nodejs.org/api/addons.html) 54 | 55 | build/Release 56 | 57 | # Dependency directories 58 | 59 | node_modules/ 60 | jspm_packages/ 61 | 62 | # Snowpack dependency directory (https://snowpack.dev/) 63 | 64 | web_modules/ 65 | 66 | # TypeScript cache 67 | 68 | *.tsbuildinfo 69 | 70 | # Optional npm cache directory 71 | 72 | .npm 73 | 74 | # Optional eslint cache 75 | 76 | .eslintcache 77 | 78 | # Optional stylelint cache 79 | 80 | .stylelintcache 81 | 82 | # Microbundle cache 83 | 84 | .rpt2_cache/ 85 | .rts2_cache_cjs/ 86 | .rts2_cache_es/ 87 | .rts2_cache_umd/ 88 | 89 | # Optional REPL history 90 | 91 | .node_repl_history 92 | 93 | # Output of 'npm pack' 94 | 95 | *.tgz 96 | 97 | # Yarn Integrity file 98 | 99 | .yarn-integrity 100 | 101 | # dotenv environment variable files 102 | 103 | .env 104 | .env.development.local 105 | .env.test.local 106 | .env.production.local 107 | .env.local 108 | 109 | # parcel-bundler cache (https://parceljs.org/) 110 | 111 | .parcel-cache 112 | 113 | # Next.js build output 114 | 115 | .next 116 | out 117 | 118 | # Nuxt.js build / generate output 119 | 120 | .nuxt 121 | dist 122 | 123 | # Gatsby files 124 | 125 | # Comment in the public line in if your project uses Gatsby and not Next.js 126 | 127 | # https://nextjs.org/blog/next-9-1#public-directory-support 128 | 129 | # public 130 | 131 | # vuepress build output 132 | 133 | .vuepress/dist 134 | 135 | # vuepress v2.x temp and cache directory 136 | 137 | .temp 138 | 139 | # Docusaurus cache and generated files 140 | 141 | .docusaurus 142 | 143 | # Serverless directories 144 | 145 | .serverless/ 146 | 147 | # FuseBox cache 148 | 149 | .fusebox/ 150 | 151 | # DynamoDB Local files 152 | 153 | .dynamodb/ 154 | 155 | # TernJS port file 156 | 157 | .tern-port 158 | 159 | # Stores VSCode versions used for testing VSCode extensions 160 | 161 | .vscode-test 162 | 163 | # yarn v2 164 | 165 | .yarn/cache 166 | .yarn/unplugged 167 | .yarn/build-state.yml 168 | .yarn/install-state.gz 169 | .pnp.* 170 | 171 | # IntelliJ based IDEs 172 | .idea 173 | 174 | # Finder (MacOS) folder config 175 | .DS_Store 176 | 177 | commits-log.txt 178 | pr-buddy.sqlite 179 | pr-summary.md 180 | 181 | bun.lockb 182 | 183 | dist/ 184 | node_modules/ 185 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Build files 3 | /src 4 | 5 | # Node Modules 6 | /node_modules 7 | 8 | # TS stuff 9 | tsconfig.json 10 | tsup.config.ts 11 | 12 | commits-log.txt 13 | pr-buddy.sqlite 14 | pr-summary.md 15 | 16 | bun.lockb -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pr Buddy 2 | 3 | `pr-buddy` is a command-line tool designed to help automate the creation of Pull Request (PR) documentation. It reads your latest Git commit and generates a PR description in markdown format based on a customizable template. 4 | 5 | ## Features 6 | 7 | - Generate a PR description based on your latest Git commit. 8 | - Use a markdown template to customize your PR documentation. 9 | - Easily install and set up AI assistance if needed. 10 | 11 | ## Installation 12 | 13 | You can install `pr-buddy` globally using either Bun or npm. 14 | 15 | You need to have bun installed since core modules depend on bun 16 | 17 | To install bun via npm 18 | 19 | ```bash 20 | npm install -g bun 21 | ``` 22 | 23 | ### Using Bun 24 | 25 | ```bash 26 | bun add -g pr-buddy-ai 27 | ``` 28 | 29 | ### Using npm 30 | 31 | ```bash 32 | npm i -g pr-buddy-ai 33 | ``` 34 | 35 | ## Usage 36 | 37 | Once installed, you can initialize the tool using the following command: 38 | 39 | ```bash 40 | pr-buddy init 41 | ``` 42 | 43 | This will start the interactive CLI. 44 | 45 | ### Create a Pull Request 46 | 47 | To create a PR document based on your latest Git commit, run: 48 | 49 | ```bash 50 | pr-buddy init 51 | ``` 52 | 53 | Then choose the `Create PR` option. `pr-buddy` will read the latest commit and generate a Pull Request description in markdown format, which you can use as your PR documentation. 54 | 55 | You have the ability to use either gemini or a locally installed ollama AI. 56 | 57 | ### Using a Custom Template 58 | 59 | `pr-buddy` allows you to customize the generated PR document using a markdown template. Simply provide a `template.md` file in your root directory of your project that follows the structure you need for your PRs. An example of the template format can be found in the [GitHub repository](#). 60 | 61 | ## Example 62 | 63 | ```bash 64 | pr-buddy init 65 | # Choose 'Create PR' 66 | # A PR markdown file will be generated based on the latest Git commit. 67 | ``` 68 | 69 | ## License 70 | 71 | This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for details. -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pr-buddy-ai", 3 | "version": "0.0.3", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "pr-buddy-ai", 9 | "version": "0.0.4", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@google/generative-ai": "^0.20.0", 13 | "@types/cli-spinner": "^0.2.3", 14 | "@types/figlet": "^1.5.8", 15 | "chalk": "^5.3.0", 16 | "cli-spinner": "^0.2.10", 17 | "commander": "^12.1.0", 18 | "execa": "^9.4.0", 19 | "figlet": "^1.7.0", 20 | "inquirer": "^10.2.2", 21 | "ollama": "^0.5.9", 22 | "openai": "^4.65.0", 23 | "pr-buddy-ai": "file:", 24 | "tsup": "^8.3.0" 25 | }, 26 | "bin": { 27 | "pr-buddy": "dist/index.js" 28 | }, 29 | "devDependencies": { 30 | "@types/bun": "latest" 31 | }, 32 | "peerDependencies": { 33 | "typescript": "^5.6.2" 34 | } 35 | }, 36 | "node_modules/@esbuild/linux-x64": { 37 | "version": "0.23.1", 38 | "cpu": [ 39 | "x64" 40 | ], 41 | "license": "MIT", 42 | "optional": true, 43 | "os": [ 44 | "linux" 45 | ], 46 | "engines": { 47 | "node": ">=18" 48 | } 49 | }, 50 | "node_modules/@esbuild/win32-x64": { 51 | "version": "0.23.1", 52 | "cpu": [ 53 | "x64" 54 | ], 55 | "license": "MIT", 56 | "optional": true, 57 | "os": [ 58 | "win32" 59 | ], 60 | "engines": { 61 | "node": ">=18" 62 | } 63 | }, 64 | "node_modules/@google/generative-ai": { 65 | "version": "0.20.0", 66 | "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.20.0.tgz", 67 | "integrity": "sha512-uJQNDr1sihvBJ9w8B0ESpNdX9aEueAMXgwnTuhTo+LnI7DD0M1KHnOWzxb2l6cM1rRHzvkdgJNNfeybcqg7uVg==", 68 | "engines": { 69 | "node": ">=18.0.0" 70 | } 71 | }, 72 | "node_modules/@inquirer/checkbox": { 73 | "version": "2.5.0", 74 | "license": "MIT", 75 | "dependencies": { 76 | "@inquirer/core": "^9.1.0", 77 | "@inquirer/figures": "^1.0.5", 78 | "@inquirer/type": "^1.5.3", 79 | "ansi-escapes": "^4.3.2", 80 | "yoctocolors-cjs": "^2.1.2" 81 | }, 82 | "engines": { 83 | "node": ">=18" 84 | } 85 | }, 86 | "node_modules/@inquirer/confirm": { 87 | "version": "3.2.0", 88 | "license": "MIT", 89 | "dependencies": { 90 | "@inquirer/core": "^9.1.0", 91 | "@inquirer/type": "^1.5.3" 92 | }, 93 | "engines": { 94 | "node": ">=18" 95 | } 96 | }, 97 | "node_modules/@inquirer/core": { 98 | "version": "9.2.1", 99 | "license": "MIT", 100 | "dependencies": { 101 | "@inquirer/figures": "^1.0.6", 102 | "@inquirer/type": "^2.0.0", 103 | "@types/mute-stream": "^0.0.4", 104 | "@types/node": "^22.5.5", 105 | "@types/wrap-ansi": "^3.0.0", 106 | "ansi-escapes": "^4.3.2", 107 | "cli-width": "^4.1.0", 108 | "mute-stream": "^1.0.0", 109 | "signal-exit": "^4.1.0", 110 | "strip-ansi": "^6.0.1", 111 | "wrap-ansi": "^6.2.0", 112 | "yoctocolors-cjs": "^2.1.2" 113 | }, 114 | "engines": { 115 | "node": ">=18" 116 | } 117 | }, 118 | "node_modules/@inquirer/core/node_modules/@inquirer/type": { 119 | "version": "2.0.0", 120 | "license": "MIT", 121 | "dependencies": { 122 | "mute-stream": "^1.0.0" 123 | }, 124 | "engines": { 125 | "node": ">=18" 126 | } 127 | }, 128 | "node_modules/@inquirer/editor": { 129 | "version": "2.2.0", 130 | "license": "MIT", 131 | "dependencies": { 132 | "@inquirer/core": "^9.1.0", 133 | "@inquirer/type": "^1.5.3", 134 | "external-editor": "^3.1.0" 135 | }, 136 | "engines": { 137 | "node": ">=18" 138 | } 139 | }, 140 | "node_modules/@inquirer/expand": { 141 | "version": "2.3.0", 142 | "license": "MIT", 143 | "dependencies": { 144 | "@inquirer/core": "^9.1.0", 145 | "@inquirer/type": "^1.5.3", 146 | "yoctocolors-cjs": "^2.1.2" 147 | }, 148 | "engines": { 149 | "node": ">=18" 150 | } 151 | }, 152 | "node_modules/@inquirer/figures": { 153 | "version": "1.0.6", 154 | "license": "MIT", 155 | "engines": { 156 | "node": ">=18" 157 | } 158 | }, 159 | "node_modules/@inquirer/input": { 160 | "version": "2.3.0", 161 | "license": "MIT", 162 | "dependencies": { 163 | "@inquirer/core": "^9.1.0", 164 | "@inquirer/type": "^1.5.3" 165 | }, 166 | "engines": { 167 | "node": ">=18" 168 | } 169 | }, 170 | "node_modules/@inquirer/number": { 171 | "version": "1.1.0", 172 | "license": "MIT", 173 | "dependencies": { 174 | "@inquirer/core": "^9.1.0", 175 | "@inquirer/type": "^1.5.3" 176 | }, 177 | "engines": { 178 | "node": ">=18" 179 | } 180 | }, 181 | "node_modules/@inquirer/password": { 182 | "version": "2.2.0", 183 | "license": "MIT", 184 | "dependencies": { 185 | "@inquirer/core": "^9.1.0", 186 | "@inquirer/type": "^1.5.3", 187 | "ansi-escapes": "^4.3.2" 188 | }, 189 | "engines": { 190 | "node": ">=18" 191 | } 192 | }, 193 | "node_modules/@inquirer/prompts": { 194 | "version": "5.5.0", 195 | "license": "MIT", 196 | "dependencies": { 197 | "@inquirer/checkbox": "^2.5.0", 198 | "@inquirer/confirm": "^3.2.0", 199 | "@inquirer/editor": "^2.2.0", 200 | "@inquirer/expand": "^2.3.0", 201 | "@inquirer/input": "^2.3.0", 202 | "@inquirer/number": "^1.1.0", 203 | "@inquirer/password": "^2.2.0", 204 | "@inquirer/rawlist": "^2.3.0", 205 | "@inquirer/search": "^1.1.0", 206 | "@inquirer/select": "^2.5.0" 207 | }, 208 | "engines": { 209 | "node": ">=18" 210 | } 211 | }, 212 | "node_modules/@inquirer/rawlist": { 213 | "version": "2.3.0", 214 | "license": "MIT", 215 | "dependencies": { 216 | "@inquirer/core": "^9.1.0", 217 | "@inquirer/type": "^1.5.3", 218 | "yoctocolors-cjs": "^2.1.2" 219 | }, 220 | "engines": { 221 | "node": ">=18" 222 | } 223 | }, 224 | "node_modules/@inquirer/search": { 225 | "version": "1.1.0", 226 | "license": "MIT", 227 | "dependencies": { 228 | "@inquirer/core": "^9.1.0", 229 | "@inquirer/figures": "^1.0.5", 230 | "@inquirer/type": "^1.5.3", 231 | "yoctocolors-cjs": "^2.1.2" 232 | }, 233 | "engines": { 234 | "node": ">=18" 235 | } 236 | }, 237 | "node_modules/@inquirer/select": { 238 | "version": "2.5.0", 239 | "license": "MIT", 240 | "dependencies": { 241 | "@inquirer/core": "^9.1.0", 242 | "@inquirer/figures": "^1.0.5", 243 | "@inquirer/type": "^1.5.3", 244 | "ansi-escapes": "^4.3.2", 245 | "yoctocolors-cjs": "^2.1.2" 246 | }, 247 | "engines": { 248 | "node": ">=18" 249 | } 250 | }, 251 | "node_modules/@inquirer/type": { 252 | "version": "1.5.5", 253 | "license": "MIT", 254 | "dependencies": { 255 | "mute-stream": "^1.0.0" 256 | }, 257 | "engines": { 258 | "node": ">=18" 259 | } 260 | }, 261 | "node_modules/@isaacs/cliui": { 262 | "version": "8.0.2", 263 | "license": "ISC", 264 | "dependencies": { 265 | "string-width": "^5.1.2", 266 | "string-width-cjs": "npm:string-width@^4.2.0", 267 | "strip-ansi": "^7.0.1", 268 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", 269 | "wrap-ansi": "^8.1.0", 270 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" 271 | }, 272 | "engines": { 273 | "node": ">=12" 274 | } 275 | }, 276 | "node_modules/@isaacs/cliui/node_modules/string-width": { 277 | "version": "5.1.2", 278 | "license": "MIT", 279 | "dependencies": { 280 | "eastasianwidth": "^0.2.0", 281 | "emoji-regex": "^9.2.2", 282 | "strip-ansi": "^7.0.1" 283 | }, 284 | "engines": { 285 | "node": ">=12" 286 | }, 287 | "funding": { 288 | "url": "https://github.com/sponsors/sindresorhus" 289 | } 290 | }, 291 | "node_modules/@isaacs/cliui/node_modules/string-width-cjs": { 292 | "name": "string-width", 293 | "version": "4.2.3", 294 | "license": "MIT", 295 | "dependencies": { 296 | "emoji-regex": "^8.0.0", 297 | "is-fullwidth-code-point": "^3.0.0", 298 | "strip-ansi": "^6.0.1" 299 | }, 300 | "engines": { 301 | "node": ">=8" 302 | } 303 | }, 304 | "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/ansi-regex": { 305 | "version": "5.0.1", 306 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 307 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 308 | "engines": { 309 | "node": ">=8" 310 | } 311 | }, 312 | "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/emoji-regex": { 313 | "version": "8.0.0", 314 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 315 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 316 | }, 317 | "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/strip-ansi": { 318 | "version": "6.0.1", 319 | "license": "MIT", 320 | "dependencies": { 321 | "ansi-regex": "^5.0.1" 322 | }, 323 | "engines": { 324 | "node": ">=8" 325 | } 326 | }, 327 | "node_modules/@isaacs/cliui/node_modules/string-width/node_modules/emoji-regex": { 328 | "version": "9.2.2", 329 | "license": "MIT" 330 | }, 331 | "node_modules/@isaacs/cliui/node_modules/strip-ansi": { 332 | "version": "7.1.0", 333 | "license": "MIT", 334 | "dependencies": { 335 | "ansi-regex": "^6.0.1" 336 | }, 337 | "engines": { 338 | "node": ">=12" 339 | }, 340 | "funding": { 341 | "url": "https://github.com/chalk/strip-ansi?sponsor=1" 342 | } 343 | }, 344 | "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs": { 345 | "name": "strip-ansi", 346 | "version": "6.0.1", 347 | "license": "MIT", 348 | "dependencies": { 349 | "ansi-regex": "^5.0.1" 350 | }, 351 | "engines": { 352 | "node": ">=8" 353 | } 354 | }, 355 | "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { 356 | "version": "5.0.1", 357 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 358 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 359 | "engines": { 360 | "node": ">=8" 361 | } 362 | }, 363 | "node_modules/@isaacs/cliui/node_modules/strip-ansi/node_modules/ansi-regex": { 364 | "version": "6.1.0", 365 | "license": "MIT", 366 | "engines": { 367 | "node": ">=12" 368 | }, 369 | "funding": { 370 | "url": "https://github.com/chalk/ansi-regex?sponsor=1" 371 | } 372 | }, 373 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { 374 | "version": "8.1.0", 375 | "license": "MIT", 376 | "dependencies": { 377 | "ansi-styles": "^6.1.0", 378 | "string-width": "^5.0.1", 379 | "strip-ansi": "^7.0.1" 380 | }, 381 | "engines": { 382 | "node": ">=12" 383 | }, 384 | "funding": { 385 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 386 | } 387 | }, 388 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs": { 389 | "name": "wrap-ansi", 390 | "version": "7.0.0", 391 | "license": "MIT", 392 | "dependencies": { 393 | "ansi-styles": "^4.0.0", 394 | "string-width": "^4.1.0", 395 | "strip-ansi": "^6.0.0" 396 | }, 397 | "engines": { 398 | "node": ">=10" 399 | }, 400 | "funding": { 401 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 402 | } 403 | }, 404 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { 405 | "version": "5.0.1", 406 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 407 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 408 | "engines": { 409 | "node": ">=8" 410 | } 411 | }, 412 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { 413 | "version": "4.3.0", 414 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 415 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 416 | "dependencies": { 417 | "color-convert": "^2.0.1" 418 | }, 419 | "engines": { 420 | "node": ">=8" 421 | }, 422 | "funding": { 423 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 424 | } 425 | }, 426 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { 427 | "version": "8.0.0", 428 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 429 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 430 | }, 431 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/string-width": { 432 | "version": "4.2.3", 433 | "license": "MIT", 434 | "dependencies": { 435 | "emoji-regex": "^8.0.0", 436 | "is-fullwidth-code-point": "^3.0.0", 437 | "strip-ansi": "^6.0.1" 438 | }, 439 | "engines": { 440 | "node": ">=8" 441 | } 442 | }, 443 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { 444 | "version": "6.0.1", 445 | "license": "MIT", 446 | "dependencies": { 447 | "ansi-regex": "^5.0.1" 448 | }, 449 | "engines": { 450 | "node": ">=8" 451 | } 452 | }, 453 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi/node_modules/ansi-styles": { 454 | "version": "6.2.1", 455 | "license": "MIT", 456 | "engines": { 457 | "node": ">=12" 458 | }, 459 | "funding": { 460 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 461 | } 462 | }, 463 | "node_modules/@jridgewell/gen-mapping": { 464 | "version": "0.3.5", 465 | "license": "MIT", 466 | "dependencies": { 467 | "@jridgewell/set-array": "^1.2.1", 468 | "@jridgewell/sourcemap-codec": "^1.4.10", 469 | "@jridgewell/trace-mapping": "^0.3.24" 470 | }, 471 | "engines": { 472 | "node": ">=6.0.0" 473 | } 474 | }, 475 | "node_modules/@jridgewell/resolve-uri": { 476 | "version": "3.1.2", 477 | "license": "MIT", 478 | "engines": { 479 | "node": ">=6.0.0" 480 | } 481 | }, 482 | "node_modules/@jridgewell/set-array": { 483 | "version": "1.2.1", 484 | "license": "MIT", 485 | "engines": { 486 | "node": ">=6.0.0" 487 | } 488 | }, 489 | "node_modules/@jridgewell/sourcemap-codec": { 490 | "version": "1.5.0", 491 | "license": "MIT" 492 | }, 493 | "node_modules/@jridgewell/trace-mapping": { 494 | "version": "0.3.25", 495 | "license": "MIT", 496 | "dependencies": { 497 | "@jridgewell/resolve-uri": "^3.1.0", 498 | "@jridgewell/sourcemap-codec": "^1.4.14" 499 | } 500 | }, 501 | "node_modules/@pkgjs/parseargs": { 502 | "version": "0.11.0", 503 | "license": "MIT", 504 | "optional": true, 505 | "engines": { 506 | "node": ">=14" 507 | } 508 | }, 509 | "node_modules/@rollup/rollup-linux-x64-gnu": { 510 | "version": "4.22.4", 511 | "cpu": [ 512 | "x64" 513 | ], 514 | "license": "MIT", 515 | "optional": true, 516 | "os": [ 517 | "linux" 518 | ] 519 | }, 520 | "node_modules/@rollup/rollup-linux-x64-musl": { 521 | "version": "4.22.4", 522 | "cpu": [ 523 | "x64" 524 | ], 525 | "license": "MIT", 526 | "optional": true, 527 | "os": [ 528 | "linux" 529 | ] 530 | }, 531 | "node_modules/@rollup/rollup-win32-x64-msvc": { 532 | "version": "4.22.4", 533 | "cpu": [ 534 | "x64" 535 | ], 536 | "license": "MIT", 537 | "optional": true, 538 | "os": [ 539 | "win32" 540 | ] 541 | }, 542 | "node_modules/@sec-ant/readable-stream": { 543 | "version": "0.4.1", 544 | "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", 545 | "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==" 546 | }, 547 | "node_modules/@sindresorhus/merge-streams": { 548 | "version": "4.0.0", 549 | "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", 550 | "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", 551 | "engines": { 552 | "node": ">=18" 553 | }, 554 | "funding": { 555 | "url": "https://github.com/sponsors/sindresorhus" 556 | } 557 | }, 558 | "node_modules/@types/bun": { 559 | "version": "1.1.10", 560 | "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.10.tgz", 561 | "integrity": "sha512-76KYVSwrHwr9zsnk6oLXOGs9KvyBg3U066GLO4rk6JZk1ypEPGCUDZ5yOiESyIHWs9cx9iC8r01utYN32XdmgA==", 562 | "dev": true, 563 | "dependencies": { 564 | "bun-types": "1.1.29" 565 | } 566 | }, 567 | "node_modules/@types/cli-spinner": { 568 | "version": "0.2.3", 569 | "license": "MIT", 570 | "dependencies": { 571 | "@types/node": "*" 572 | } 573 | }, 574 | "node_modules/@types/estree": { 575 | "version": "1.0.5", 576 | "license": "MIT" 577 | }, 578 | "node_modules/@types/figlet": { 579 | "version": "1.5.8", 580 | "license": "MIT" 581 | }, 582 | "node_modules/@types/mute-stream": { 583 | "version": "0.0.4", 584 | "license": "MIT", 585 | "dependencies": { 586 | "@types/node": "*" 587 | } 588 | }, 589 | "node_modules/@types/mute-stream/node_modules/@types/node": { 590 | "version": "20.12.14", 591 | "license": "MIT", 592 | "dependencies": { 593 | "undici-types": "~5.26.4" 594 | } 595 | }, 596 | "node_modules/@types/mute-stream/node_modules/@types/node/node_modules/undici-types": { 597 | "version": "5.26.5", 598 | "license": "MIT" 599 | }, 600 | "node_modules/@types/node": { 601 | "version": "22.6.1", 602 | "license": "MIT", 603 | "dependencies": { 604 | "undici-types": "~6.19.2" 605 | } 606 | }, 607 | "node_modules/@types/node-fetch": { 608 | "version": "2.6.11", 609 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", 610 | "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", 611 | "dependencies": { 612 | "@types/node": "*", 613 | "form-data": "^4.0.0" 614 | } 615 | }, 616 | "node_modules/@types/wrap-ansi": { 617 | "version": "3.0.0", 618 | "license": "MIT" 619 | }, 620 | "node_modules/@types/ws": { 621 | "version": "8.5.12", 622 | "dev": true, 623 | "license": "MIT", 624 | "dependencies": { 625 | "@types/node": "*" 626 | } 627 | }, 628 | "node_modules/@types/ws/node_modules/@types/node": { 629 | "version": "20.12.14", 630 | "dev": true, 631 | "license": "MIT", 632 | "dependencies": { 633 | "undici-types": "~5.26.4" 634 | } 635 | }, 636 | "node_modules/@types/ws/node_modules/@types/node/node_modules/undici-types": { 637 | "version": "5.26.5", 638 | "dev": true, 639 | "license": "MIT" 640 | }, 641 | "node_modules/abort-controller": { 642 | "version": "3.0.0", 643 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 644 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 645 | "dependencies": { 646 | "event-target-shim": "^5.0.0" 647 | }, 648 | "engines": { 649 | "node": ">=6.5" 650 | } 651 | }, 652 | "node_modules/agentkeepalive": { 653 | "version": "4.5.0", 654 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 655 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 656 | "dependencies": { 657 | "humanize-ms": "^1.2.1" 658 | }, 659 | "engines": { 660 | "node": ">= 8.0.0" 661 | } 662 | }, 663 | "node_modules/ansi-escapes": { 664 | "version": "4.3.2", 665 | "license": "MIT", 666 | "dependencies": { 667 | "type-fest": "^0.21.3" 668 | }, 669 | "engines": { 670 | "node": ">=8" 671 | }, 672 | "funding": { 673 | "url": "https://github.com/sponsors/sindresorhus" 674 | } 675 | }, 676 | "node_modules/ansi-regex": { 677 | "version": "5.0.1", 678 | "license": "MIT", 679 | "engines": { 680 | "node": ">=8" 681 | } 682 | }, 683 | "node_modules/ansi-styles": { 684 | "version": "4.3.0", 685 | "license": "MIT", 686 | "dependencies": { 687 | "color-convert": "^2.0.1" 688 | }, 689 | "engines": { 690 | "node": ">=8" 691 | }, 692 | "funding": { 693 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 694 | } 695 | }, 696 | "node_modules/any-promise": { 697 | "version": "1.3.0", 698 | "license": "MIT" 699 | }, 700 | "node_modules/anymatch": { 701 | "version": "3.1.3", 702 | "license": "ISC", 703 | "dependencies": { 704 | "normalize-path": "^3.0.0", 705 | "picomatch": "^2.0.4" 706 | }, 707 | "engines": { 708 | "node": ">= 8" 709 | } 710 | }, 711 | "node_modules/anymatch/node_modules/picomatch": { 712 | "version": "2.3.1", 713 | "license": "MIT", 714 | "engines": { 715 | "node": ">=8.6" 716 | }, 717 | "funding": { 718 | "url": "https://github.com/sponsors/jonschlinkert" 719 | } 720 | }, 721 | "node_modules/asynckit": { 722 | "version": "0.4.0", 723 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 724 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 725 | }, 726 | "node_modules/balanced-match": { 727 | "version": "1.0.2", 728 | "license": "MIT" 729 | }, 730 | "node_modules/binary-extensions": { 731 | "version": "2.3.0", 732 | "license": "MIT", 733 | "engines": { 734 | "node": ">=8" 735 | }, 736 | "funding": { 737 | "url": "https://github.com/sponsors/sindresorhus" 738 | } 739 | }, 740 | "node_modules/brace-expansion": { 741 | "version": "2.0.1", 742 | "license": "MIT", 743 | "dependencies": { 744 | "balanced-match": "^1.0.0" 745 | } 746 | }, 747 | "node_modules/braces": { 748 | "version": "3.0.3", 749 | "license": "MIT", 750 | "dependencies": { 751 | "fill-range": "^7.1.1" 752 | }, 753 | "engines": { 754 | "node": ">=8" 755 | } 756 | }, 757 | "node_modules/bun-types": { 758 | "version": "1.1.29", 759 | "dev": true, 760 | "license": "MIT", 761 | "dependencies": { 762 | "@types/node": "~20.12.8", 763 | "@types/ws": "~8.5.10" 764 | } 765 | }, 766 | "node_modules/bun-types/node_modules/@types/node": { 767 | "version": "20.12.14", 768 | "dev": true, 769 | "license": "MIT", 770 | "dependencies": { 771 | "undici-types": "~5.26.4" 772 | } 773 | }, 774 | "node_modules/bun-types/node_modules/@types/node/node_modules/undici-types": { 775 | "version": "5.26.5", 776 | "dev": true, 777 | "license": "MIT" 778 | }, 779 | "node_modules/bundle-require": { 780 | "version": "5.0.0", 781 | "license": "MIT", 782 | "dependencies": { 783 | "load-tsconfig": "^0.2.3" 784 | }, 785 | "engines": { 786 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 787 | }, 788 | "peerDependencies": { 789 | "esbuild": ">=0.18" 790 | } 791 | }, 792 | "node_modules/cac": { 793 | "version": "6.7.14", 794 | "license": "MIT", 795 | "engines": { 796 | "node": ">=8" 797 | } 798 | }, 799 | "node_modules/chalk": { 800 | "version": "5.3.0", 801 | "license": "MIT", 802 | "engines": { 803 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 804 | }, 805 | "funding": { 806 | "url": "https://github.com/chalk/chalk?sponsor=1" 807 | } 808 | }, 809 | "node_modules/chardet": { 810 | "version": "0.7.0", 811 | "license": "MIT" 812 | }, 813 | "node_modules/chokidar": { 814 | "version": "3.6.0", 815 | "license": "MIT", 816 | "dependencies": { 817 | "anymatch": "~3.1.2", 818 | "braces": "~3.0.2", 819 | "glob-parent": "~5.1.2", 820 | "is-binary-path": "~2.1.0", 821 | "is-glob": "~4.0.1", 822 | "normalize-path": "~3.0.0", 823 | "readdirp": "~3.6.0" 824 | }, 825 | "engines": { 826 | "node": ">= 8.10.0" 827 | }, 828 | "funding": { 829 | "url": "https://paulmillr.com/funding/" 830 | }, 831 | "optionalDependencies": { 832 | "fsevents": "~2.3.2" 833 | } 834 | }, 835 | "node_modules/cli-spinner": { 836 | "version": "0.2.10", 837 | "license": "MIT", 838 | "engines": { 839 | "node": ">=0.10" 840 | } 841 | }, 842 | "node_modules/cli-width": { 843 | "version": "4.1.0", 844 | "license": "ISC", 845 | "engines": { 846 | "node": ">= 12" 847 | } 848 | }, 849 | "node_modules/color-convert": { 850 | "version": "2.0.1", 851 | "license": "MIT", 852 | "dependencies": { 853 | "color-name": "~1.1.4" 854 | }, 855 | "engines": { 856 | "node": ">=7.0.0" 857 | } 858 | }, 859 | "node_modules/color-name": { 860 | "version": "1.1.4", 861 | "license": "MIT" 862 | }, 863 | "node_modules/combined-stream": { 864 | "version": "1.0.8", 865 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 866 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 867 | "dependencies": { 868 | "delayed-stream": "~1.0.0" 869 | }, 870 | "engines": { 871 | "node": ">= 0.8" 872 | } 873 | }, 874 | "node_modules/commander": { 875 | "version": "12.1.0", 876 | "license": "MIT", 877 | "engines": { 878 | "node": ">=18" 879 | } 880 | }, 881 | "node_modules/consola": { 882 | "version": "3.2.3", 883 | "license": "MIT", 884 | "engines": { 885 | "node": "^14.18.0 || >=16.10.0" 886 | } 887 | }, 888 | "node_modules/cross-spawn": { 889 | "version": "7.0.3", 890 | "license": "MIT", 891 | "dependencies": { 892 | "path-key": "^3.1.0", 893 | "shebang-command": "^2.0.0", 894 | "which": "^2.0.1" 895 | }, 896 | "engines": { 897 | "node": ">= 8" 898 | } 899 | }, 900 | "node_modules/debug": { 901 | "version": "4.3.7", 902 | "license": "MIT", 903 | "dependencies": { 904 | "ms": "^2.1.3" 905 | }, 906 | "engines": { 907 | "node": ">=6.0" 908 | }, 909 | "peerDependenciesMeta": { 910 | "supports-color": { 911 | "optional": true 912 | } 913 | } 914 | }, 915 | "node_modules/delayed-stream": { 916 | "version": "1.0.0", 917 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 918 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 919 | "engines": { 920 | "node": ">=0.4.0" 921 | } 922 | }, 923 | "node_modules/eastasianwidth": { 924 | "version": "0.2.0", 925 | "license": "MIT" 926 | }, 927 | "node_modules/emoji-regex": { 928 | "version": "8.0.0", 929 | "license": "MIT" 930 | }, 931 | "node_modules/esbuild": { 932 | "version": "0.23.1", 933 | "hasInstallScript": true, 934 | "license": "MIT", 935 | "bin": { 936 | "esbuild": "bin/esbuild" 937 | }, 938 | "engines": { 939 | "node": ">=18" 940 | }, 941 | "optionalDependencies": { 942 | "@esbuild/aix-ppc64": "0.23.1", 943 | "@esbuild/android-arm": "0.23.1", 944 | "@esbuild/android-arm64": "0.23.1", 945 | "@esbuild/android-x64": "0.23.1", 946 | "@esbuild/darwin-arm64": "0.23.1", 947 | "@esbuild/darwin-x64": "0.23.1", 948 | "@esbuild/freebsd-arm64": "0.23.1", 949 | "@esbuild/freebsd-x64": "0.23.1", 950 | "@esbuild/linux-arm": "0.23.1", 951 | "@esbuild/linux-arm64": "0.23.1", 952 | "@esbuild/linux-ia32": "0.23.1", 953 | "@esbuild/linux-loong64": "0.23.1", 954 | "@esbuild/linux-mips64el": "0.23.1", 955 | "@esbuild/linux-ppc64": "0.23.1", 956 | "@esbuild/linux-riscv64": "0.23.1", 957 | "@esbuild/linux-s390x": "0.23.1", 958 | "@esbuild/linux-x64": "0.23.1", 959 | "@esbuild/netbsd-x64": "0.23.1", 960 | "@esbuild/openbsd-arm64": "0.23.1", 961 | "@esbuild/openbsd-x64": "0.23.1", 962 | "@esbuild/sunos-x64": "0.23.1", 963 | "@esbuild/win32-arm64": "0.23.1", 964 | "@esbuild/win32-ia32": "0.23.1", 965 | "@esbuild/win32-x64": "0.23.1" 966 | } 967 | }, 968 | "node_modules/event-target-shim": { 969 | "version": "5.0.1", 970 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 971 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 972 | "engines": { 973 | "node": ">=6" 974 | } 975 | }, 976 | "node_modules/execa": { 977 | "version": "9.4.0", 978 | "resolved": "https://registry.npmjs.org/execa/-/execa-9.4.0.tgz", 979 | "integrity": "sha512-yKHlle2YGxZE842MERVIplWwNH5VYmqqcPFgtnlU//K8gxuFFXu0pwd/CrfXTumFpeEiufsP7+opT/bPJa1yVw==", 980 | "dependencies": { 981 | "@sindresorhus/merge-streams": "^4.0.0", 982 | "cross-spawn": "^7.0.3", 983 | "figures": "^6.1.0", 984 | "get-stream": "^9.0.0", 985 | "human-signals": "^8.0.0", 986 | "is-plain-obj": "^4.1.0", 987 | "is-stream": "^4.0.1", 988 | "npm-run-path": "^6.0.0", 989 | "pretty-ms": "^9.0.0", 990 | "signal-exit": "^4.1.0", 991 | "strip-final-newline": "^4.0.0", 992 | "yoctocolors": "^2.0.0" 993 | }, 994 | "engines": { 995 | "node": "^18.19.0 || >=20.5.0" 996 | }, 997 | "funding": { 998 | "url": "https://github.com/sindresorhus/execa?sponsor=1" 999 | } 1000 | }, 1001 | "node_modules/external-editor": { 1002 | "version": "3.1.0", 1003 | "license": "MIT", 1004 | "dependencies": { 1005 | "chardet": "^0.7.0", 1006 | "iconv-lite": "^0.4.24", 1007 | "tmp": "^0.0.33" 1008 | }, 1009 | "engines": { 1010 | "node": ">=4" 1011 | } 1012 | }, 1013 | "node_modules/figlet": { 1014 | "version": "1.7.0", 1015 | "license": "MIT", 1016 | "bin": { 1017 | "figlet": "bin/index.js" 1018 | }, 1019 | "engines": { 1020 | "node": ">= 0.4.0" 1021 | } 1022 | }, 1023 | "node_modules/figures": { 1024 | "version": "6.1.0", 1025 | "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", 1026 | "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", 1027 | "dependencies": { 1028 | "is-unicode-supported": "^2.0.0" 1029 | }, 1030 | "engines": { 1031 | "node": ">=18" 1032 | }, 1033 | "funding": { 1034 | "url": "https://github.com/sponsors/sindresorhus" 1035 | } 1036 | }, 1037 | "node_modules/fill-range": { 1038 | "version": "7.1.1", 1039 | "license": "MIT", 1040 | "dependencies": { 1041 | "to-regex-range": "^5.0.1" 1042 | }, 1043 | "engines": { 1044 | "node": ">=8" 1045 | } 1046 | }, 1047 | "node_modules/foreground-child": { 1048 | "version": "3.3.0", 1049 | "license": "ISC", 1050 | "dependencies": { 1051 | "cross-spawn": "^7.0.0", 1052 | "signal-exit": "^4.0.1" 1053 | }, 1054 | "engines": { 1055 | "node": ">=14" 1056 | }, 1057 | "funding": { 1058 | "url": "https://github.com/sponsors/isaacs" 1059 | } 1060 | }, 1061 | "node_modules/form-data": { 1062 | "version": "4.0.0", 1063 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 1064 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 1065 | "dependencies": { 1066 | "asynckit": "^0.4.0", 1067 | "combined-stream": "^1.0.8", 1068 | "mime-types": "^2.1.12" 1069 | }, 1070 | "engines": { 1071 | "node": ">= 6" 1072 | } 1073 | }, 1074 | "node_modules/form-data-encoder": { 1075 | "version": "1.7.2", 1076 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 1077 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 1078 | }, 1079 | "node_modules/formdata-node": { 1080 | "version": "4.4.1", 1081 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 1082 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 1083 | "dependencies": { 1084 | "node-domexception": "1.0.0", 1085 | "web-streams-polyfill": "4.0.0-beta.3" 1086 | }, 1087 | "engines": { 1088 | "node": ">= 12.20" 1089 | } 1090 | }, 1091 | "node_modules/get-stream": { 1092 | "version": "9.0.1", 1093 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", 1094 | "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", 1095 | "dependencies": { 1096 | "@sec-ant/readable-stream": "^0.4.1", 1097 | "is-stream": "^4.0.1" 1098 | }, 1099 | "engines": { 1100 | "node": ">=18" 1101 | }, 1102 | "funding": { 1103 | "url": "https://github.com/sponsors/sindresorhus" 1104 | } 1105 | }, 1106 | "node_modules/glob": { 1107 | "version": "10.4.5", 1108 | "license": "ISC", 1109 | "dependencies": { 1110 | "foreground-child": "^3.1.0", 1111 | "jackspeak": "^3.1.2", 1112 | "minimatch": "^9.0.4", 1113 | "minipass": "^7.1.2", 1114 | "package-json-from-dist": "^1.0.0", 1115 | "path-scurry": "^1.11.1" 1116 | }, 1117 | "bin": { 1118 | "glob": "dist/esm/bin.mjs" 1119 | }, 1120 | "funding": { 1121 | "url": "https://github.com/sponsors/isaacs" 1122 | } 1123 | }, 1124 | "node_modules/glob-parent": { 1125 | "version": "5.1.2", 1126 | "license": "ISC", 1127 | "dependencies": { 1128 | "is-glob": "^4.0.1" 1129 | }, 1130 | "engines": { 1131 | "node": ">= 6" 1132 | } 1133 | }, 1134 | "node_modules/human-signals": { 1135 | "version": "8.0.0", 1136 | "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", 1137 | "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", 1138 | "engines": { 1139 | "node": ">=18.18.0" 1140 | } 1141 | }, 1142 | "node_modules/humanize-ms": { 1143 | "version": "1.2.1", 1144 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 1145 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 1146 | "dependencies": { 1147 | "ms": "^2.0.0" 1148 | } 1149 | }, 1150 | "node_modules/iconv-lite": { 1151 | "version": "0.4.24", 1152 | "license": "MIT", 1153 | "dependencies": { 1154 | "safer-buffer": ">= 2.1.2 < 3" 1155 | }, 1156 | "engines": { 1157 | "node": ">=0.10.0" 1158 | } 1159 | }, 1160 | "node_modules/inquirer": { 1161 | "version": "10.2.2", 1162 | "license": "MIT", 1163 | "dependencies": { 1164 | "@inquirer/core": "^9.1.0", 1165 | "@inquirer/prompts": "^5.5.0", 1166 | "@inquirer/type": "^1.5.3", 1167 | "@types/mute-stream": "^0.0.4", 1168 | "ansi-escapes": "^4.3.2", 1169 | "mute-stream": "^1.0.0", 1170 | "run-async": "^3.0.0", 1171 | "rxjs": "^7.8.1" 1172 | }, 1173 | "engines": { 1174 | "node": ">=18" 1175 | } 1176 | }, 1177 | "node_modules/is-binary-path": { 1178 | "version": "2.1.0", 1179 | "license": "MIT", 1180 | "dependencies": { 1181 | "binary-extensions": "^2.0.0" 1182 | }, 1183 | "engines": { 1184 | "node": ">=8" 1185 | } 1186 | }, 1187 | "node_modules/is-extglob": { 1188 | "version": "2.1.1", 1189 | "license": "MIT", 1190 | "engines": { 1191 | "node": ">=0.10.0" 1192 | } 1193 | }, 1194 | "node_modules/is-fullwidth-code-point": { 1195 | "version": "3.0.0", 1196 | "license": "MIT", 1197 | "engines": { 1198 | "node": ">=8" 1199 | } 1200 | }, 1201 | "node_modules/is-glob": { 1202 | "version": "4.0.3", 1203 | "license": "MIT", 1204 | "dependencies": { 1205 | "is-extglob": "^2.1.1" 1206 | }, 1207 | "engines": { 1208 | "node": ">=0.10.0" 1209 | } 1210 | }, 1211 | "node_modules/is-number": { 1212 | "version": "7.0.0", 1213 | "license": "MIT", 1214 | "engines": { 1215 | "node": ">=0.12.0" 1216 | } 1217 | }, 1218 | "node_modules/is-plain-obj": { 1219 | "version": "4.1.0", 1220 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", 1221 | "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", 1222 | "engines": { 1223 | "node": ">=12" 1224 | }, 1225 | "funding": { 1226 | "url": "https://github.com/sponsors/sindresorhus" 1227 | } 1228 | }, 1229 | "node_modules/is-stream": { 1230 | "version": "4.0.1", 1231 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", 1232 | "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", 1233 | "engines": { 1234 | "node": ">=18" 1235 | }, 1236 | "funding": { 1237 | "url": "https://github.com/sponsors/sindresorhus" 1238 | } 1239 | }, 1240 | "node_modules/is-unicode-supported": { 1241 | "version": "2.1.0", 1242 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", 1243 | "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", 1244 | "engines": { 1245 | "node": ">=18" 1246 | }, 1247 | "funding": { 1248 | "url": "https://github.com/sponsors/sindresorhus" 1249 | } 1250 | }, 1251 | "node_modules/isexe": { 1252 | "version": "2.0.0", 1253 | "license": "ISC" 1254 | }, 1255 | "node_modules/jackspeak": { 1256 | "version": "3.4.3", 1257 | "license": "BlueOak-1.0.0", 1258 | "dependencies": { 1259 | "@isaacs/cliui": "^8.0.2" 1260 | }, 1261 | "funding": { 1262 | "url": "https://github.com/sponsors/isaacs" 1263 | }, 1264 | "optionalDependencies": { 1265 | "@pkgjs/parseargs": "^0.11.0" 1266 | } 1267 | }, 1268 | "node_modules/joycon": { 1269 | "version": "3.1.1", 1270 | "license": "MIT", 1271 | "engines": { 1272 | "node": ">=10" 1273 | } 1274 | }, 1275 | "node_modules/lilconfig": { 1276 | "version": "3.1.2", 1277 | "license": "MIT", 1278 | "engines": { 1279 | "node": ">=14" 1280 | }, 1281 | "funding": { 1282 | "url": "https://github.com/sponsors/antonk52" 1283 | } 1284 | }, 1285 | "node_modules/lines-and-columns": { 1286 | "version": "1.2.4", 1287 | "license": "MIT" 1288 | }, 1289 | "node_modules/load-tsconfig": { 1290 | "version": "0.2.5", 1291 | "license": "MIT", 1292 | "engines": { 1293 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 1294 | } 1295 | }, 1296 | "node_modules/lodash.sortby": { 1297 | "version": "4.7.0", 1298 | "license": "MIT" 1299 | }, 1300 | "node_modules/lru-cache": { 1301 | "version": "10.4.3", 1302 | "license": "ISC" 1303 | }, 1304 | "node_modules/merge-stream": { 1305 | "version": "2.0.0", 1306 | "license": "MIT" 1307 | }, 1308 | "node_modules/mime-db": { 1309 | "version": "1.52.0", 1310 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1311 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1312 | "engines": { 1313 | "node": ">= 0.6" 1314 | } 1315 | }, 1316 | "node_modules/mime-types": { 1317 | "version": "2.1.35", 1318 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1319 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1320 | "dependencies": { 1321 | "mime-db": "1.52.0" 1322 | }, 1323 | "engines": { 1324 | "node": ">= 0.6" 1325 | } 1326 | }, 1327 | "node_modules/mimic-fn": { 1328 | "version": "2.1.0", 1329 | "license": "MIT", 1330 | "engines": { 1331 | "node": ">=6" 1332 | } 1333 | }, 1334 | "node_modules/minimatch": { 1335 | "version": "9.0.5", 1336 | "license": "ISC", 1337 | "dependencies": { 1338 | "brace-expansion": "^2.0.1" 1339 | }, 1340 | "engines": { 1341 | "node": ">=16 || 14 >=14.17" 1342 | }, 1343 | "funding": { 1344 | "url": "https://github.com/sponsors/isaacs" 1345 | } 1346 | }, 1347 | "node_modules/minipass": { 1348 | "version": "7.1.2", 1349 | "license": "ISC", 1350 | "engines": { 1351 | "node": ">=16 || 14 >=14.17" 1352 | } 1353 | }, 1354 | "node_modules/ms": { 1355 | "version": "2.1.3", 1356 | "license": "MIT" 1357 | }, 1358 | "node_modules/mute-stream": { 1359 | "version": "1.0.0", 1360 | "license": "ISC", 1361 | "engines": { 1362 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0" 1363 | } 1364 | }, 1365 | "node_modules/mz": { 1366 | "version": "2.7.0", 1367 | "license": "MIT", 1368 | "dependencies": { 1369 | "any-promise": "^1.0.0", 1370 | "object-assign": "^4.0.1", 1371 | "thenify-all": "^1.0.0" 1372 | } 1373 | }, 1374 | "node_modules/node-domexception": { 1375 | "version": "1.0.0", 1376 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 1377 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 1378 | "funding": [ 1379 | { 1380 | "type": "github", 1381 | "url": "https://github.com/sponsors/jimmywarting" 1382 | }, 1383 | { 1384 | "type": "github", 1385 | "url": "https://paypal.me/jimmywarting" 1386 | } 1387 | ], 1388 | "engines": { 1389 | "node": ">=10.5.0" 1390 | } 1391 | }, 1392 | "node_modules/node-fetch": { 1393 | "version": "2.7.0", 1394 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1395 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1396 | "dependencies": { 1397 | "whatwg-url": "^5.0.0" 1398 | }, 1399 | "engines": { 1400 | "node": "4.x || >=6.0.0" 1401 | }, 1402 | "peerDependencies": { 1403 | "encoding": "^0.1.0" 1404 | }, 1405 | "peerDependenciesMeta": { 1406 | "encoding": { 1407 | "optional": true 1408 | } 1409 | } 1410 | }, 1411 | "node_modules/node-fetch/node_modules/tr46": { 1412 | "version": "0.0.3", 1413 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1414 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1415 | }, 1416 | "node_modules/node-fetch/node_modules/webidl-conversions": { 1417 | "version": "3.0.1", 1418 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1419 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1420 | }, 1421 | "node_modules/node-fetch/node_modules/whatwg-url": { 1422 | "version": "5.0.0", 1423 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1424 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1425 | "dependencies": { 1426 | "tr46": "~0.0.3", 1427 | "webidl-conversions": "^3.0.0" 1428 | } 1429 | }, 1430 | "node_modules/normalize-path": { 1431 | "version": "3.0.0", 1432 | "license": "MIT", 1433 | "engines": { 1434 | "node": ">=0.10.0" 1435 | } 1436 | }, 1437 | "node_modules/npm-run-path": { 1438 | "version": "6.0.0", 1439 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", 1440 | "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", 1441 | "dependencies": { 1442 | "path-key": "^4.0.0", 1443 | "unicorn-magic": "^0.3.0" 1444 | }, 1445 | "engines": { 1446 | "node": ">=18" 1447 | }, 1448 | "funding": { 1449 | "url": "https://github.com/sponsors/sindresorhus" 1450 | } 1451 | }, 1452 | "node_modules/npm-run-path/node_modules/path-key": { 1453 | "version": "4.0.0", 1454 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 1455 | "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 1456 | "engines": { 1457 | "node": ">=12" 1458 | }, 1459 | "funding": { 1460 | "url": "https://github.com/sponsors/sindresorhus" 1461 | } 1462 | }, 1463 | "node_modules/object-assign": { 1464 | "version": "4.1.1", 1465 | "license": "MIT", 1466 | "engines": { 1467 | "node": ">=0.10.0" 1468 | } 1469 | }, 1470 | "node_modules/ollama": { 1471 | "version": "0.5.9", 1472 | "license": "MIT", 1473 | "dependencies": { 1474 | "whatwg-fetch": "^3.6.20" 1475 | } 1476 | }, 1477 | "node_modules/onetime": { 1478 | "version": "5.1.2", 1479 | "license": "MIT", 1480 | "dependencies": { 1481 | "mimic-fn": "^2.1.0" 1482 | }, 1483 | "engines": { 1484 | "node": ">=6" 1485 | }, 1486 | "funding": { 1487 | "url": "https://github.com/sponsors/sindresorhus" 1488 | } 1489 | }, 1490 | "node_modules/openai": { 1491 | "version": "4.65.0", 1492 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.65.0.tgz", 1493 | "integrity": "sha512-LfA4KUBpH/8rA3vjCQ74LZtdK/8wx9W6Qxq8MHqEdImPsN1XPQ2ompIuJWkKS6kXt5Cs5i8Eb65IIo4M7U+yeQ==", 1494 | "dependencies": { 1495 | "@types/node": "^18.11.18", 1496 | "@types/node-fetch": "^2.6.4", 1497 | "abort-controller": "^3.0.0", 1498 | "agentkeepalive": "^4.2.1", 1499 | "form-data-encoder": "1.7.2", 1500 | "formdata-node": "^4.3.2", 1501 | "node-fetch": "^2.6.7" 1502 | }, 1503 | "bin": { 1504 | "openai": "bin/cli" 1505 | }, 1506 | "peerDependencies": { 1507 | "zod": "^3.23.8" 1508 | }, 1509 | "peerDependenciesMeta": { 1510 | "zod": { 1511 | "optional": true 1512 | } 1513 | } 1514 | }, 1515 | "node_modules/openai/node_modules/@types/node": { 1516 | "version": "18.19.54", 1517 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz", 1518 | "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==", 1519 | "dependencies": { 1520 | "undici-types": "~5.26.4" 1521 | } 1522 | }, 1523 | "node_modules/openai/node_modules/undici-types": { 1524 | "version": "5.26.5", 1525 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1526 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1527 | }, 1528 | "node_modules/os-tmpdir": { 1529 | "version": "1.0.2", 1530 | "license": "MIT", 1531 | "engines": { 1532 | "node": ">=0.10.0" 1533 | } 1534 | }, 1535 | "node_modules/package-json-from-dist": { 1536 | "version": "1.0.0", 1537 | "license": "BlueOak-1.0.0" 1538 | }, 1539 | "node_modules/parse-ms": { 1540 | "version": "4.0.0", 1541 | "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", 1542 | "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", 1543 | "engines": { 1544 | "node": ">=18" 1545 | }, 1546 | "funding": { 1547 | "url": "https://github.com/sponsors/sindresorhus" 1548 | } 1549 | }, 1550 | "node_modules/path-key": { 1551 | "version": "3.1.1", 1552 | "license": "MIT", 1553 | "engines": { 1554 | "node": ">=8" 1555 | } 1556 | }, 1557 | "node_modules/path-scurry": { 1558 | "version": "1.11.1", 1559 | "license": "BlueOak-1.0.0", 1560 | "dependencies": { 1561 | "lru-cache": "^10.2.0", 1562 | "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" 1563 | }, 1564 | "engines": { 1565 | "node": ">=16 || 14 >=14.18" 1566 | }, 1567 | "funding": { 1568 | "url": "https://github.com/sponsors/isaacs" 1569 | } 1570 | }, 1571 | "node_modules/picocolors": { 1572 | "version": "1.1.0", 1573 | "license": "ISC" 1574 | }, 1575 | "node_modules/pirates": { 1576 | "version": "4.0.6", 1577 | "license": "MIT", 1578 | "engines": { 1579 | "node": ">= 6" 1580 | } 1581 | }, 1582 | "node_modules/postcss-load-config": { 1583 | "version": "6.0.1", 1584 | "funding": [ 1585 | { 1586 | "type": "opencollective", 1587 | "url": "https://opencollective.com/postcss/" 1588 | }, 1589 | { 1590 | "type": "github", 1591 | "url": "https://github.com/sponsors/ai" 1592 | } 1593 | ], 1594 | "license": "MIT", 1595 | "dependencies": { 1596 | "lilconfig": "^3.1.1" 1597 | }, 1598 | "engines": { 1599 | "node": ">= 18" 1600 | }, 1601 | "peerDependencies": { 1602 | "jiti": ">=1.21.0", 1603 | "postcss": ">=8.0.9", 1604 | "tsx": "^4.8.1", 1605 | "yaml": "^2.4.2" 1606 | }, 1607 | "peerDependenciesMeta": { 1608 | "jiti": { 1609 | "optional": true 1610 | }, 1611 | "postcss": { 1612 | "optional": true 1613 | }, 1614 | "tsx": { 1615 | "optional": true 1616 | }, 1617 | "yaml": { 1618 | "optional": true 1619 | } 1620 | } 1621 | }, 1622 | "node_modules/pr-buddy-ai": { 1623 | "resolved": "", 1624 | "link": true 1625 | }, 1626 | "node_modules/pretty-ms": { 1627 | "version": "9.1.0", 1628 | "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.1.0.tgz", 1629 | "integrity": "sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==", 1630 | "dependencies": { 1631 | "parse-ms": "^4.0.0" 1632 | }, 1633 | "engines": { 1634 | "node": ">=18" 1635 | }, 1636 | "funding": { 1637 | "url": "https://github.com/sponsors/sindresorhus" 1638 | } 1639 | }, 1640 | "node_modules/punycode": { 1641 | "version": "2.3.1", 1642 | "license": "MIT", 1643 | "engines": { 1644 | "node": ">=6" 1645 | } 1646 | }, 1647 | "node_modules/readdirp": { 1648 | "version": "3.6.0", 1649 | "license": "MIT", 1650 | "dependencies": { 1651 | "picomatch": "^2.2.1" 1652 | }, 1653 | "engines": { 1654 | "node": ">=8.10.0" 1655 | } 1656 | }, 1657 | "node_modules/readdirp/node_modules/picomatch": { 1658 | "version": "2.3.1", 1659 | "license": "MIT", 1660 | "engines": { 1661 | "node": ">=8.6" 1662 | }, 1663 | "funding": { 1664 | "url": "https://github.com/sponsors/jonschlinkert" 1665 | } 1666 | }, 1667 | "node_modules/resolve-from": { 1668 | "version": "5.0.0", 1669 | "license": "MIT", 1670 | "engines": { 1671 | "node": ">=8" 1672 | } 1673 | }, 1674 | "node_modules/rollup": { 1675 | "version": "4.22.4", 1676 | "license": "MIT", 1677 | "dependencies": { 1678 | "@types/estree": "1.0.5" 1679 | }, 1680 | "bin": { 1681 | "rollup": "dist/bin/rollup" 1682 | }, 1683 | "engines": { 1684 | "node": ">=18.0.0", 1685 | "npm": ">=8.0.0" 1686 | }, 1687 | "optionalDependencies": { 1688 | "@rollup/rollup-android-arm-eabi": "4.22.4", 1689 | "@rollup/rollup-android-arm64": "4.22.4", 1690 | "@rollup/rollup-darwin-arm64": "4.22.4", 1691 | "@rollup/rollup-darwin-x64": "4.22.4", 1692 | "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", 1693 | "@rollup/rollup-linux-arm-musleabihf": "4.22.4", 1694 | "@rollup/rollup-linux-arm64-gnu": "4.22.4", 1695 | "@rollup/rollup-linux-arm64-musl": "4.22.4", 1696 | "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", 1697 | "@rollup/rollup-linux-riscv64-gnu": "4.22.4", 1698 | "@rollup/rollup-linux-s390x-gnu": "4.22.4", 1699 | "@rollup/rollup-linux-x64-gnu": "4.22.4", 1700 | "@rollup/rollup-linux-x64-musl": "4.22.4", 1701 | "@rollup/rollup-win32-arm64-msvc": "4.22.4", 1702 | "@rollup/rollup-win32-ia32-msvc": "4.22.4", 1703 | "@rollup/rollup-win32-x64-msvc": "4.22.4", 1704 | "fsevents": "~2.3.2" 1705 | } 1706 | }, 1707 | "node_modules/run-async": { 1708 | "version": "3.0.0", 1709 | "license": "MIT", 1710 | "engines": { 1711 | "node": ">=0.12.0" 1712 | } 1713 | }, 1714 | "node_modules/rxjs": { 1715 | "version": "7.8.1", 1716 | "license": "Apache-2.0", 1717 | "dependencies": { 1718 | "tslib": "^2.1.0" 1719 | } 1720 | }, 1721 | "node_modules/safer-buffer": { 1722 | "version": "2.1.2", 1723 | "license": "MIT" 1724 | }, 1725 | "node_modules/shebang-command": { 1726 | "version": "2.0.0", 1727 | "license": "MIT", 1728 | "dependencies": { 1729 | "shebang-regex": "^3.0.0" 1730 | }, 1731 | "engines": { 1732 | "node": ">=8" 1733 | } 1734 | }, 1735 | "node_modules/shebang-regex": { 1736 | "version": "3.0.0", 1737 | "license": "MIT", 1738 | "engines": { 1739 | "node": ">=8" 1740 | } 1741 | }, 1742 | "node_modules/signal-exit": { 1743 | "version": "4.1.0", 1744 | "license": "ISC", 1745 | "engines": { 1746 | "node": ">=14" 1747 | }, 1748 | "funding": { 1749 | "url": "https://github.com/sponsors/isaacs" 1750 | } 1751 | }, 1752 | "node_modules/source-map": { 1753 | "version": "0.8.0-beta.0", 1754 | "license": "BSD-3-Clause", 1755 | "dependencies": { 1756 | "whatwg-url": "^7.0.0" 1757 | }, 1758 | "engines": { 1759 | "node": ">= 8" 1760 | } 1761 | }, 1762 | "node_modules/string-width": { 1763 | "version": "4.2.3", 1764 | "license": "MIT", 1765 | "dependencies": { 1766 | "emoji-regex": "^8.0.0", 1767 | "is-fullwidth-code-point": "^3.0.0", 1768 | "strip-ansi": "^6.0.1" 1769 | }, 1770 | "engines": { 1771 | "node": ">=8" 1772 | } 1773 | }, 1774 | "node_modules/strip-ansi": { 1775 | "version": "6.0.1", 1776 | "license": "MIT", 1777 | "dependencies": { 1778 | "ansi-regex": "^5.0.1" 1779 | }, 1780 | "engines": { 1781 | "node": ">=8" 1782 | } 1783 | }, 1784 | "node_modules/strip-final-newline": { 1785 | "version": "4.0.0", 1786 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", 1787 | "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", 1788 | "engines": { 1789 | "node": ">=18" 1790 | }, 1791 | "funding": { 1792 | "url": "https://github.com/sponsors/sindresorhus" 1793 | } 1794 | }, 1795 | "node_modules/sucrase": { 1796 | "version": "3.35.0", 1797 | "license": "MIT", 1798 | "dependencies": { 1799 | "@jridgewell/gen-mapping": "^0.3.2", 1800 | "commander": "^4.0.0", 1801 | "glob": "^10.3.10", 1802 | "lines-and-columns": "^1.1.6", 1803 | "mz": "^2.7.0", 1804 | "pirates": "^4.0.1", 1805 | "ts-interface-checker": "^0.1.9" 1806 | }, 1807 | "bin": { 1808 | "sucrase": "bin/sucrase", 1809 | "sucrase-node": "bin/sucrase-node" 1810 | }, 1811 | "engines": { 1812 | "node": ">=16 || 14 >=14.17" 1813 | } 1814 | }, 1815 | "node_modules/sucrase/node_modules/commander": { 1816 | "version": "4.1.1", 1817 | "license": "MIT", 1818 | "engines": { 1819 | "node": ">= 6" 1820 | } 1821 | }, 1822 | "node_modules/thenify": { 1823 | "version": "3.3.1", 1824 | "license": "MIT", 1825 | "dependencies": { 1826 | "any-promise": "^1.0.0" 1827 | } 1828 | }, 1829 | "node_modules/thenify-all": { 1830 | "version": "1.6.0", 1831 | "license": "MIT", 1832 | "dependencies": { 1833 | "thenify": ">= 3.1.0 < 4" 1834 | }, 1835 | "engines": { 1836 | "node": ">=0.8" 1837 | } 1838 | }, 1839 | "node_modules/tinyglobby": { 1840 | "version": "0.2.6", 1841 | "license": "ISC", 1842 | "dependencies": { 1843 | "fdir": "^6.3.0", 1844 | "picomatch": "^4.0.2" 1845 | }, 1846 | "engines": { 1847 | "node": ">=12.0.0" 1848 | } 1849 | }, 1850 | "node_modules/tinyglobby/node_modules/fdir": { 1851 | "version": "6.3.0", 1852 | "license": "MIT", 1853 | "peerDependencies": { 1854 | "picomatch": "^3 || ^4" 1855 | }, 1856 | "peerDependenciesMeta": { 1857 | "picomatch": { 1858 | "optional": true 1859 | } 1860 | } 1861 | }, 1862 | "node_modules/tinyglobby/node_modules/picomatch": { 1863 | "version": "4.0.2", 1864 | "license": "MIT", 1865 | "engines": { 1866 | "node": ">=12" 1867 | }, 1868 | "funding": { 1869 | "url": "https://github.com/sponsors/jonschlinkert" 1870 | } 1871 | }, 1872 | "node_modules/tmp": { 1873 | "version": "0.0.33", 1874 | "license": "MIT", 1875 | "dependencies": { 1876 | "os-tmpdir": "~1.0.2" 1877 | }, 1878 | "engines": { 1879 | "node": ">=0.6.0" 1880 | } 1881 | }, 1882 | "node_modules/to-regex-range": { 1883 | "version": "5.0.1", 1884 | "license": "MIT", 1885 | "dependencies": { 1886 | "is-number": "^7.0.0" 1887 | }, 1888 | "engines": { 1889 | "node": ">=8.0" 1890 | } 1891 | }, 1892 | "node_modules/tr46": { 1893 | "version": "1.0.1", 1894 | "license": "MIT", 1895 | "dependencies": { 1896 | "punycode": "^2.1.0" 1897 | } 1898 | }, 1899 | "node_modules/tree-kill": { 1900 | "version": "1.2.2", 1901 | "license": "MIT", 1902 | "bin": { 1903 | "tree-kill": "cli.js" 1904 | } 1905 | }, 1906 | "node_modules/ts-interface-checker": { 1907 | "version": "0.1.13", 1908 | "license": "Apache-2.0" 1909 | }, 1910 | "node_modules/tslib": { 1911 | "version": "2.7.0", 1912 | "license": "0BSD" 1913 | }, 1914 | "node_modules/tsup": { 1915 | "version": "8.3.0", 1916 | "license": "MIT", 1917 | "dependencies": { 1918 | "bundle-require": "^5.0.0", 1919 | "cac": "^6.7.14", 1920 | "chokidar": "^3.6.0", 1921 | "consola": "^3.2.3", 1922 | "debug": "^4.3.5", 1923 | "esbuild": "^0.23.0", 1924 | "execa": "^5.1.1", 1925 | "joycon": "^3.1.1", 1926 | "picocolors": "^1.0.1", 1927 | "postcss-load-config": "^6.0.1", 1928 | "resolve-from": "^5.0.0", 1929 | "rollup": "^4.19.0", 1930 | "source-map": "0.8.0-beta.0", 1931 | "sucrase": "^3.35.0", 1932 | "tinyglobby": "^0.2.1", 1933 | "tree-kill": "^1.2.2" 1934 | }, 1935 | "bin": { 1936 | "tsup": "dist/cli-default.js", 1937 | "tsup-node": "dist/cli-node.js" 1938 | }, 1939 | "engines": { 1940 | "node": ">=18" 1941 | }, 1942 | "peerDependencies": { 1943 | "@microsoft/api-extractor": "^7.36.0", 1944 | "@swc/core": "^1", 1945 | "postcss": "^8.4.12", 1946 | "typescript": ">=4.5.0" 1947 | }, 1948 | "peerDependenciesMeta": { 1949 | "@microsoft/api-extractor": { 1950 | "optional": true 1951 | }, 1952 | "@swc/core": { 1953 | "optional": true 1954 | }, 1955 | "postcss": { 1956 | "optional": true 1957 | }, 1958 | "typescript": { 1959 | "optional": true 1960 | } 1961 | } 1962 | }, 1963 | "node_modules/tsup/node_modules/execa": { 1964 | "version": "5.1.1", 1965 | "license": "MIT", 1966 | "dependencies": { 1967 | "cross-spawn": "^7.0.3", 1968 | "get-stream": "^6.0.0", 1969 | "human-signals": "^2.1.0", 1970 | "is-stream": "^2.0.0", 1971 | "merge-stream": "^2.0.0", 1972 | "npm-run-path": "^4.0.1", 1973 | "onetime": "^5.1.2", 1974 | "signal-exit": "^3.0.3", 1975 | "strip-final-newline": "^2.0.0" 1976 | }, 1977 | "engines": { 1978 | "node": ">=10" 1979 | }, 1980 | "funding": { 1981 | "url": "https://github.com/sindresorhus/execa?sponsor=1" 1982 | } 1983 | }, 1984 | "node_modules/tsup/node_modules/execa/node_modules/get-stream": { 1985 | "version": "6.0.1", 1986 | "license": "MIT", 1987 | "engines": { 1988 | "node": ">=10" 1989 | }, 1990 | "funding": { 1991 | "url": "https://github.com/sponsors/sindresorhus" 1992 | } 1993 | }, 1994 | "node_modules/tsup/node_modules/execa/node_modules/human-signals": { 1995 | "version": "2.1.0", 1996 | "license": "Apache-2.0", 1997 | "engines": { 1998 | "node": ">=10.17.0" 1999 | } 2000 | }, 2001 | "node_modules/tsup/node_modules/execa/node_modules/is-stream": { 2002 | "version": "2.0.1", 2003 | "license": "MIT", 2004 | "engines": { 2005 | "node": ">=8" 2006 | }, 2007 | "funding": { 2008 | "url": "https://github.com/sponsors/sindresorhus" 2009 | } 2010 | }, 2011 | "node_modules/tsup/node_modules/execa/node_modules/npm-run-path": { 2012 | "version": "4.0.1", 2013 | "license": "MIT", 2014 | "dependencies": { 2015 | "path-key": "^3.0.0" 2016 | }, 2017 | "engines": { 2018 | "node": ">=8" 2019 | } 2020 | }, 2021 | "node_modules/tsup/node_modules/execa/node_modules/signal-exit": { 2022 | "version": "3.0.7", 2023 | "license": "ISC" 2024 | }, 2025 | "node_modules/tsup/node_modules/execa/node_modules/strip-final-newline": { 2026 | "version": "2.0.0", 2027 | "license": "MIT", 2028 | "engines": { 2029 | "node": ">=6" 2030 | } 2031 | }, 2032 | "node_modules/type-fest": { 2033 | "version": "0.21.3", 2034 | "license": "(MIT OR CC0-1.0)", 2035 | "engines": { 2036 | "node": ">=10" 2037 | }, 2038 | "funding": { 2039 | "url": "https://github.com/sponsors/sindresorhus" 2040 | } 2041 | }, 2042 | "node_modules/typescript": { 2043 | "version": "5.6.2", 2044 | "license": "Apache-2.0", 2045 | "peer": true, 2046 | "bin": { 2047 | "tsc": "bin/tsc", 2048 | "tsserver": "bin/tsserver" 2049 | }, 2050 | "engines": { 2051 | "node": ">=14.17" 2052 | } 2053 | }, 2054 | "node_modules/undici-types": { 2055 | "version": "6.19.8", 2056 | "license": "MIT" 2057 | }, 2058 | "node_modules/unicorn-magic": { 2059 | "version": "0.3.0", 2060 | "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", 2061 | "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", 2062 | "engines": { 2063 | "node": ">=18" 2064 | }, 2065 | "funding": { 2066 | "url": "https://github.com/sponsors/sindresorhus" 2067 | } 2068 | }, 2069 | "node_modules/web-streams-polyfill": { 2070 | "version": "4.0.0-beta.3", 2071 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 2072 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 2073 | "engines": { 2074 | "node": ">= 14" 2075 | } 2076 | }, 2077 | "node_modules/webidl-conversions": { 2078 | "version": "4.0.2", 2079 | "license": "BSD-2-Clause" 2080 | }, 2081 | "node_modules/whatwg-fetch": { 2082 | "version": "3.6.20", 2083 | "license": "MIT" 2084 | }, 2085 | "node_modules/whatwg-url": { 2086 | "version": "7.1.0", 2087 | "license": "MIT", 2088 | "dependencies": { 2089 | "lodash.sortby": "^4.7.0", 2090 | "tr46": "^1.0.1", 2091 | "webidl-conversions": "^4.0.2" 2092 | } 2093 | }, 2094 | "node_modules/which": { 2095 | "version": "2.0.2", 2096 | "license": "ISC", 2097 | "dependencies": { 2098 | "isexe": "^2.0.0" 2099 | }, 2100 | "bin": { 2101 | "node-which": "bin/node-which" 2102 | }, 2103 | "engines": { 2104 | "node": ">= 8" 2105 | } 2106 | }, 2107 | "node_modules/wrap-ansi": { 2108 | "version": "6.2.0", 2109 | "license": "MIT", 2110 | "dependencies": { 2111 | "ansi-styles": "^4.0.0", 2112 | "string-width": "^4.1.0", 2113 | "strip-ansi": "^6.0.0" 2114 | }, 2115 | "engines": { 2116 | "node": ">=8" 2117 | } 2118 | }, 2119 | "node_modules/yoctocolors": { 2120 | "version": "2.1.1", 2121 | "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", 2122 | "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", 2123 | "engines": { 2124 | "node": ">=18" 2125 | }, 2126 | "funding": { 2127 | "url": "https://github.com/sponsors/sindresorhus" 2128 | } 2129 | }, 2130 | "node_modules/yoctocolors-cjs": { 2131 | "version": "2.1.2", 2132 | "license": "MIT", 2133 | "engines": { 2134 | "node": ">=18" 2135 | }, 2136 | "funding": { 2137 | "url": "https://github.com/sponsors/sindresorhus" 2138 | } 2139 | } 2140 | } 2141 | } 2142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pr-buddy-ai", 3 | "version": "0.0.5", 4 | "description": "An AI tool to help you write better pull requests", 5 | "author": "Mohammed Ibrahim (Github: MohammedIbrahim8887)", 6 | "license": "MIT", 7 | "module": "index.ts", 8 | "type": "module", 9 | "devDependencies": { 10 | "@types/bun": "latest" 11 | }, 12 | "bin": { 13 | "pr-buddy": "./dist/index.js" 14 | }, 15 | "scripts": { 16 | "start": "bun run ./dist/index.js init", 17 | "dev": "bun run ./src/index.ts init", 18 | "build": "tsup" 19 | }, 20 | "peerDependencies": { 21 | "typescript": "^5.6.2" 22 | }, 23 | "dependencies": { 24 | "@google/generative-ai": "^0.20.0", 25 | "@types/cli-spinner": "^0.2.3", 26 | "@types/figlet": "^1.5.8", 27 | "chalk": "^5.3.0", 28 | "cli-spinner": "^0.2.10", 29 | "commander": "^12.1.0", 30 | "figlet": "^1.7.0", 31 | "inquirer": "^10.2.2", 32 | "ollama": "^0.5.9", 33 | "openai": "^4.65.0", 34 | "pr-buddy-ai": "file:", 35 | "tsup": "^8.3.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/api/get-ai-models.ts: -------------------------------------------------------------------------------- 1 | import ollama from "ollama"; 2 | 3 | const getAIModels = async () => { 4 | const resp = await ollama.list(); 5 | return resp.models; 6 | }; 7 | 8 | export default getAIModels; 9 | -------------------------------------------------------------------------------- /src/api/send-prompt.ts: -------------------------------------------------------------------------------- 1 | import { file } from "bun"; 2 | import ollama from "ollama"; 3 | import getDefaultAI from "../db/get-default-ai"; 4 | import { prSummaryFilePath } from "../helpers/constants"; 5 | const sendPropmt = async (prompt: string) => { 6 | const message = { role: 'user', content: prompt}; 7 | const model = await getDefaultAI() 8 | 9 | if(!model) { 10 | throw new Error('No default AI model found'); 11 | } 12 | 13 | const writer = file(prSummaryFilePath).writer() 14 | try { 15 | const resp = await ollama.chat({ 16 | model: model, 17 | messages: [message], 18 | stream: true, 19 | }); 20 | 21 | for await (const part of resp) { 22 | writer.write(part.message.content) 23 | } 24 | writer.flush() 25 | 26 | } catch (error:any) { 27 | throw new Error('Error sending prompt: ' + error.message); 28 | } 29 | } 30 | 31 | export default sendPropmt; -------------------------------------------------------------------------------- /src/cmd/create-pr.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { Spinner } from "cli-spinner"; 3 | import generatePrSummary from "../helpers/generate-pr-summary"; 4 | import { readLatestCommits } from "../helpers/read-latest-commits"; 5 | import { setupDirectory } from "../helpers/setup-directory"; 6 | 7 | const createPr = async () => { 8 | console.log(chalk.cyan("Waking up AI...")); 9 | const spinner = new Spinner(); 10 | try { 11 | setupDirectory().then(async () => { 12 | console.log(chalk.cyan("Waking up ollama...")); 13 | spinner.start(); 14 | spinner.stop(); 15 | console.log(chalk.cyan("Reading Commits...")); 16 | spinner.start(); 17 | await readLatestCommits(); 18 | spinner.stop(); 19 | console.log(chalk("Parsing Commits...")); 20 | generatePrSummary(); 21 | }); 22 | } catch (err: any) { 23 | console.error(chalk.red("Error creating PR: " + err.message)); 24 | spinner.stop(); 25 | return new Error(err); 26 | } 27 | }; 28 | 29 | export default createPr; 30 | -------------------------------------------------------------------------------- /src/cmd/existing-model.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import getAIModels from "../api/get-ai-models"; 3 | import inquirer from "inquirer"; 4 | import { Spinner } from "cli-spinner"; 5 | import addAIModel from "../db/add-ai-model"; 6 | 7 | const existingModel = async () => { 8 | const models = await getAIModels(); 9 | if(!models) { 10 | console.log(chalk.red("No models found. Please install a model first.")) 11 | } 12 | const modelChoices: string[] = models?.map((model)=> ( 13 | `Model Name :${model.name}, Model Size: ${model.size}` 14 | )) 15 | 16 | const spinnner = new Spinner() 17 | 18 | inquirer.prompt([ 19 | { 20 | type: "list", 21 | name: "model", 22 | message: "Choose a model to use as a default AI model", 23 | choices: modelChoices 24 | } 25 | ]).then(async (answers) => { 26 | for (const model of models) { 27 | if (answers.model === `Model Name :${model.name}, Model Size: ${model.size}`) { 28 | console.log(chalk.green(`You have selected ${model.name}`)) 29 | console.log(chalk.cyan(`Setting ${model.name}`)) 30 | spinnner.start() 31 | await addAIModel(model.name) 32 | spinnner.stop() 33 | console.log(chalk.cyan(`${model.name} has been set as the default AI model. Let's start creating PRs`)) 34 | } 35 | } 36 | }) 37 | } 38 | 39 | export default existingModel -------------------------------------------------------------------------------- /src/cmd/install-model.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "bun"; 2 | import chalk from "chalk"; 3 | import { Spinner } from "cli-spinner"; 4 | import addAIModel from "../db/add-ai-model"; 5 | 6 | const installModel = async () => { 7 | const spinner = new Spinner(); 8 | try { 9 | await $`ollama -v`; 10 | await $`ollama run phi:latest`; 11 | console.log( 12 | chalk.cyan("AI installation complete please type /bye to exit") 13 | ); 14 | spinner.stop(); 15 | addAIModel("phi3:latest", true); 16 | } catch { 17 | console.log(chalk.red("Ollama is not installed. Installing Ollama")); 18 | console.log(chalk.red("Installing")); 19 | const { text } = 20 | await $`sudo curl -fsSL https://ollama.com/install.sh | sh`; 21 | console.log(chalk.cyan.bold("Please enter your password")); 22 | console.log(chalk.green(text)); 23 | 24 | await $`ollama run phi:latest`; 25 | console.log( 26 | chalk.cyan("AI installation complete please type /bye to exit") 27 | ); 28 | spinner.stop(); 29 | addAIModel("phi3:latest", true); 30 | console.log(chalk.green("Model installed successfully")); 31 | } 32 | }; 33 | 34 | export default installModel; 35 | -------------------------------------------------------------------------------- /src/cmd/integrate-external-api.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { Spinner } from "cli-spinner"; 3 | import inquirer from "inquirer"; 4 | import addAIModel from "../db/add-ai-model"; 5 | import { addGptApiKey } from "../db/add-gpt-api-key"; 6 | 7 | export const integrateExternalApi = async (apiName: string) => { 8 | const spinner = new Spinner(); 9 | console.log(chalk.cyan("Enter your open ai key")); 10 | try { 11 | inquirer 12 | .prompt([ 13 | { 14 | type: "input", 15 | name: apiName, 16 | message: "Enter your open ai key", 17 | }, 18 | ]) 19 | .then((answer) => { 20 | spinner.start() 21 | console.log(chalk.cyan("Adding Your API Key...")) 22 | addGptApiKey(apiName, answer.geminiAI); 23 | spinner.stop() 24 | console.log(chalk.cyan(`setting ${apiName} as default AI model`)); 25 | spinner.start() 26 | addAIModel(apiName, true); 27 | spinner.stop() 28 | }); 29 | } catch (err: any) { 30 | console.log(chalk.red(err.message)); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/cmd/setup.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import inquirer from "inquirer"; 3 | import existingModel from "./existing-model"; 4 | import installModel from "./install-model"; 5 | import { integrateExternalApi } from "./integrate-external-api"; 6 | 7 | const setup = async () => { 8 | console.log(chalk.cyan("Getting ready to setup your pr buddy...")); 9 | 10 | inquirer 11 | .prompt([ 12 | { 13 | type: "list", 14 | name: "action", 15 | message: 16 | "Choose which method you would like to use to setup your pr buddy", 17 | choices: [ 18 | "Install a model", 19 | "Use an existing model", 20 | // "Integrate ChatGPT", 21 | // "Integrate Claude-AI", 22 | "Integrate Gemini-AI", 23 | ], 24 | }, 25 | ]) 26 | .then((answers) => { 27 | switch (answers.action) { 28 | case "Install a model": 29 | installModel(); 30 | break; 31 | case "Use an existing model": 32 | existingModel(); 33 | break; 34 | /** 35 | * ? Test out with when getting openAI and claudeAI api key 36 | */ 37 | // case "Integrate ChatGPT": 38 | // integrateExternalApi("openAI"); 39 | // break; 40 | // case "Integrate Claude-AI": 41 | // integrateExternalApi("claudeAI"); 42 | // break; 43 | case "Integrate Gemini-AI": 44 | console.log( 45 | chalk.cyan( 46 | "Get your API key for free from https://aistudio.google.com/app/apikey", 47 | ), 48 | ); 49 | integrateExternalApi("geminiAI"); 50 | break; 51 | default: 52 | console.log(chalk.red("Invalid option")); 53 | } 54 | }); 55 | }; 56 | 57 | export default setup; 58 | -------------------------------------------------------------------------------- /src/db/add-ai-model.ts: -------------------------------------------------------------------------------- 1 | import db from "./init-db"; 2 | 3 | const addAIModel = async (model: string, isDefault?: boolean) => { 4 | db().query("BEGIN"); 5 | try { 6 | const defaultModelQuery = `SELECT id FROM models WHERE isDefault = TRUE LIMIT 1`; 7 | const defaultModelResult = db().query(defaultModelQuery); 8 | const id = defaultModelResult.get("id") 9 | if(isDefault) { 10 | if (!defaultModelResult.values("not-found")) { 11 | const updateDefaultQuery = db().query("UPDATE models SET isDefault = FALSE WHERE id = $id;") 12 | updateDefaultQuery.run({$id: id as any}); 13 | return model 14 | } else { 15 | const insertQuery = db().query("INSERT INTO models (name, isDefault) VALUES($name, TRUE) ON CONFLICT(name) DO UPDATE SET isDefault = TRUE RETURNING name;"); 16 | const result = insertQuery.run({$name: model}); 17 | const newDefaultModel = result; 18 | return newDefaultModel; 19 | } 20 | } 21 | 22 | const insertQuery = db().query("INSERT INTO models (name, isDefault) VALUES($name, FALSE) ON CONFLICT(name) DO UPDATE SET isDefault = TRUE RETURNING name;"); 23 | 24 | const result = insertQuery.run({$name: model}); 25 | const newDefaultModel = result; 26 | 27 | db().query("COMMIT"); 28 | 29 | return newDefaultModel; 30 | } catch (err: any) { 31 | db().query("ROLLBACK"); 32 | throw new Error('Error setting default AI: ' + err.message); 33 | } 34 | } 35 | 36 | export default addAIModel; 37 | -------------------------------------------------------------------------------- /src/db/add-gpt-api-key.ts: -------------------------------------------------------------------------------- 1 | import addAIModel from "./add-ai-model"; 2 | import db from "./init-db"; 3 | 4 | export const addGptApiKey = async (modelName: string, apiKey: string) => { 5 | db().query("BEGIN"); 6 | 7 | try { 8 | const modelQuery = db().query("SELECT id FROM models WHERE name = $name"); 9 | modelQuery.run({ $name: modelName }); 10 | const modelResult = (await modelQuery.get()) as { id: number }; 11 | 12 | let modelId: number; 13 | 14 | if (modelResult) { 15 | console.log("JUST FOINDD ITTTT", modelResult.id); 16 | modelId = modelResult.id; 17 | } else { 18 | await addAIModel(modelName); 19 | const modelQuery = db().query("SELECT id FROM models WHERE name = $name"); 20 | modelQuery.run({ $name: modelName }); 21 | const modelResult = (await modelQuery.get()) as { id: number }; 22 | modelId = modelResult.id; 23 | } 24 | 25 | const insertQuery = db().query( 26 | "INSERT INTO api_keys (modelId, key) VALUES ($modelId, $key);" 27 | ); 28 | console.log(apiKey) 29 | insertQuery.run({ $modelId: modelId, $key: apiKey }); 30 | 31 | db().query("COMMIT"); 32 | 33 | return apiKey; 34 | } catch (err: any) { 35 | db().query("ROLLBACK"); 36 | console.error(err); 37 | throw new Error("Error adding GPT API key: " + err.message); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/db/get-default-ai.ts: -------------------------------------------------------------------------------- 1 | import db from "./init-db" 2 | 3 | const getDefaultAI = async ():Promise => { 4 | try { 5 | const query = db().query("SELECT * FROM models WHERE isDefault = TRUE"); 6 | const result = await query.get("name") as {name:string, id: number, isDefault: boolean}; 7 | return result?.name; 8 | } catch(err:any){ 9 | throw new Error('Error getting default AI: ' + err.message); 10 | } 11 | } 12 | 13 | export default getDefaultAI; -------------------------------------------------------------------------------- /src/db/get-default-api-key.ts: -------------------------------------------------------------------------------- 1 | import db from "./init-db"; 2 | 3 | const getApiKey = async (modelName: string): Promise => { 4 | try { 5 | const modelQuery = db().query("SELECT id FROM models WHERE name = $name"); 6 | modelQuery.run({ $name: modelName }); 7 | const modelResult = (await modelQuery.get()) as { id: number }; 8 | 9 | if (!modelResult) { 10 | throw new Error(`Model with name "${modelName}" not found.`); 11 | } 12 | 13 | const modelId = modelResult.id; 14 | 15 | const apiKeyQuery = db().query( 16 | "SELECT key FROM api_keys WHERE modelId = $modelId" 17 | ); 18 | apiKeyQuery.run({ $modelId: modelId }); 19 | const apiKeyResult = (await apiKeyQuery.get()) as { key: string }; 20 | 21 | return apiKeyResult?.key; 22 | } catch (err: any) { 23 | throw new Error("Error getting API key: " + err.message); 24 | } 25 | }; 26 | 27 | export default getApiKey; 28 | -------------------------------------------------------------------------------- /src/db/init-db.ts: -------------------------------------------------------------------------------- 1 | import { localDB } from "../helpers/constants"; 2 | import { Database } from "bun:sqlite"; 3 | 4 | export const initDb = () => { 5 | const db = new Database(localDB, { create: true }); 6 | 7 | const modelQuery = db.query(`CREATE TABLE IF NOT EXISTS models ( 8 | id INTEGER PRIMARY KEY, 9 | name TEXT NOT NULL UNIQUE, 10 | isDefault BOOLEAN DEFAULT FALSE 11 | );`); 12 | 13 | modelQuery.run(); 14 | 15 | const apiKeyQuery = db.query(`CREATE TABLE IF NOT EXISTS api_keys ( 16 | id INTEGER PRIMARY KEY, 17 | modelId INTEGER NOT NULL, 18 | key TEXT NOT NULL UNIQUE, 19 | FOREIGN KEY (modelId) REFERENCES models(id) ON DELETE CASCADE 20 | );`); 21 | 22 | apiKeyQuery.run(); 23 | 24 | return db; 25 | }; 26 | 27 | export default initDb; 28 | -------------------------------------------------------------------------------- /src/helpers/constants.ts: -------------------------------------------------------------------------------- 1 | export const systemDBPath = `${process.env.HOME}/.local/share/pr-buddy/`; 2 | export const localDB = `pr-buddy.sqlite`; 3 | export const aiUrl = "http://localhost:11434/api/" 4 | export const prSummaryFilePath = "pr-summary.md"; 5 | export const commitFilePath = `commits-log.txt`; 6 | export const prTemplate = "template.md" -------------------------------------------------------------------------------- /src/helpers/generate-gemeni-pr.ts: -------------------------------------------------------------------------------- 1 | import { GoogleGenerativeAI } from "@google/generative-ai"; 2 | import getApiKey from "../db/get-default-api-key"; 3 | import { file } from "bun"; 4 | import { prSummaryFilePath } from "./constants"; 5 | 6 | export const generateGeminiPr = async (prompt: string) => { 7 | const apiKey = await getApiKey("geminiAI"); 8 | const genAI = new GoogleGenerativeAI(apiKey); 9 | const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" }); 10 | 11 | try { 12 | const result = await model.generateContent(prompt); 13 | const writer = file(prSummaryFilePath).writer(); 14 | writer.write(result.response.text()); 15 | writer.flush(); 16 | } catch (error: any) { 17 | throw new Error("Error sending prompt: " + error.message); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/helpers/generate-pr-summary.ts: -------------------------------------------------------------------------------- 1 | import { file } from "bun"; 2 | import chalk from "chalk"; 3 | import { Spinner } from "cli-spinner"; 4 | import sendPropmt from "../api/send-prompt"; 5 | import getDefaultAI from "../db/get-default-ai"; 6 | import { commitFilePath, prTemplate } from "./constants"; 7 | import { generateGeminiPr } from "./generate-gemeni-pr"; 8 | 9 | const generatePrSummary = async () => { 10 | try { 11 | const spinner = new Spinner(); 12 | spinner.setSpinnerTitle(chalk.cyan("Generatig PR summary...")); 13 | spinner.start(); 14 | const commitFile = await file(commitFilePath).text(); 15 | const template = await file(prTemplate).text(); 16 | const propmt = `Please create a pull request summary in markdown format based on the provided git diff. Ensure that the summary adheres strictly to the template given and focuses solely on the content without including any code examples. Here is the git diff: ${commitFile}. Additionally, please follow this template for your summary: ${template}`; 17 | 18 | switch (await getDefaultAI()) { 19 | case "geminiAI": 20 | await generateGeminiPr(propmt); 21 | break; 22 | case "openAI": 23 | await sendPropmt(propmt); 24 | break; 25 | case "claudeAI": 26 | await sendPropmt(propmt); 27 | break; 28 | default: 29 | await sendPropmt(propmt); 30 | break; 31 | } 32 | spinner.stop(); 33 | console.log(chalk("Your PR is ready. checkout pr-summary.md")); 34 | } catch (error: any) { 35 | throw new Error("Error generating PR summary: " + error.message); 36 | } 37 | }; 38 | 39 | export default generatePrSummary; 40 | getDefaultAI(); 41 | -------------------------------------------------------------------------------- /src/helpers/read-latest-commits.ts: -------------------------------------------------------------------------------- 1 | import { $, file, write } from "bun"; 2 | import { join } from "path"; 3 | import { commitFilePath } from "./constants"; 4 | 5 | export const readLatestCommits = async () => { 6 | try { 7 | const branchResult = await $`git rev-parse --abbrev-ref HEAD`.quiet(); 8 | const currentBranch = branchResult.stdout 9 | ? branchResult.stdout.toString().trim() 10 | : null; 11 | 12 | if (!currentBranch) { 13 | throw new Error("Failed to get the current branch."); 14 | } 15 | 16 | const trackingResult = 17 | await $`git rev-parse --abbrev-ref --symbolic-full-name @{u}` 18 | .quiet() 19 | .catch(() => null); 20 | 21 | let result; 22 | 23 | if (trackingResult && trackingResult.stdout) { 24 | const remoteBranch = trackingResult.stdout.toString().trim(); 25 | console.log(`Comparing with remote branch: ${remoteBranch}`); 26 | result = 27 | await $`git log ${remoteBranch}..HEAD -p --pretty=format:"%h - %an, %ar : %s"`.quiet(); 28 | } else { 29 | console.log(`No remote tracking branch. Comparing with local HEAD.`); 30 | result = 31 | await $`git log HEAD -p --pretty=format:"%h - %an, %ar : %s"`.quiet(); 32 | } 33 | 34 | const commitsWithDiffs = result.stdout 35 | ? result.stdout.toString().trim() 36 | : null; 37 | 38 | if (!commitsWithDiffs) { 39 | throw new Error("No commits found or git command failed."); 40 | } 41 | 42 | const filePath = join(process.cwd(), commitFilePath); 43 | 44 | let lastSavedCommit = ""; 45 | const fileExists = await file(filePath).exists(); 46 | 47 | if (fileExists) { 48 | const fileContent = await file(filePath).text(); 49 | if (fileContent) { 50 | lastSavedCommit = fileContent.trim(); 51 | } 52 | } 53 | 54 | if (commitsWithDiffs !== lastSavedCommit) { 55 | await write(filePath, commitsWithDiffs + "\n"); 56 | console.log(`Commits with diffs written to: ${filePath}`); 57 | } else { 58 | console.log("The commits with diffs are already saved."); 59 | } 60 | 61 | return commitsWithDiffs; 62 | } catch (error: any) { 63 | console.log(error); 64 | throw new Error("Error reading commits with diffs: " + error.message); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /src/helpers/setup-directory.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "bun"; 2 | import chalk from "chalk"; 3 | import { Spinner } from "cli-spinner"; 4 | 5 | const downloadTemplate = async () => { 6 | const spinner = new Spinner(); 7 | const url = "https://raw.githubusercontent.com/MohammedIbrahim8887/pr-buddy/main/template.md"; 8 | spinner.start(); 9 | console.log(chalk.cyan("Downloading template.md from GitHub...")); 10 | 11 | try { 12 | const res = await fetch(url); 13 | if (!res.ok) { 14 | throw new Error("Failed to download template.md"); 15 | } 16 | 17 | const data = await res.text(); 18 | await Bun.write("template.md", data); 19 | console.log("template.md downloaded and saved in the root directory."); 20 | } catch (error) { 21 | console.error(chalk.red(`Error during template download: ${error}`)); 22 | } finally { 23 | spinner.stop(); 24 | } 25 | }; 26 | 27 | export const setupDirectory = async () => { 28 | try { 29 | const { stdout } = await $`git rev-parse --is-inside-work-tree`; 30 | 31 | if (!stdout.toString().trim()) { 32 | console.error(chalk.red("Git is not initialized in this folder.")); 33 | throw new Error("Git is not initialized in this folder."); 34 | } 35 | 36 | console.log(chalk.green("Git is initialized.")); 37 | const gitignore = Bun.file(".gitignore"); 38 | 39 | if (!await gitignore.exists()) { 40 | console.log(chalk.yellow(".gitignore not found in the root directory.")); 41 | await Bun.write(".gitignore", "commits-log.txt\npr-buddy.sqlite\npr-summary.md\n"); 42 | console.log(chalk.green(".gitignore created with default values.")); 43 | } else { 44 | const gitignoreContent = await Bun.file(".gitignore").text(); 45 | const requiredFiles = ["commits-log.txt", "pr-buddy.sqlite", "pr-summary.md"]; 46 | const missingFiles = requiredFiles.filter(file => !gitignoreContent.includes(file)); 47 | 48 | if (missingFiles.length > 0) { 49 | console.log(chalk.yellow(`Warning: The following files are missing from .gitignore: ${missingFiles.join(", ")}`)); 50 | } else { 51 | console.log(chalk.green("All required files are present in .gitignore.")); 52 | } 53 | } 54 | 55 | const template = Bun.file("template.md"); 56 | if (!await template.exists()) { 57 | console.log(chalk.yellow("template.md not found in the root directory.")); 58 | await downloadTemplate(); 59 | } else { 60 | console.log(chalk.green("template.md already exists.")); 61 | } 62 | 63 | } catch (error) { 64 | console.error(chalk.red(`Error: ${error}`)); 65 | process.exit(1); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bun 2 | 3 | import chalk from "chalk"; 4 | import { Command } from "commander"; 5 | import figlet from "figlet"; 6 | import inquirer from "inquirer"; 7 | import createPr from "./cmd/create-pr"; 8 | import setup from "./cmd/setup"; 9 | import { platform } from "os"; 10 | import { $ } from "bun"; 11 | 12 | const program = new Command(); 13 | 14 | program 15 | .name("Pr-Buddy") 16 | .description("An AI tool to help you write better pull requests messages") 17 | .version("0.0.6"); 18 | 19 | program 20 | .command("init") 21 | .description("Initializing the project") 22 | .action(() => { 23 | figlet("PR - BUDDY", { font: "3-D" }, (err, data) => { 24 | console.log(chalk.green(data)); 25 | const currentPlatform = platform(); 26 | if (err) { 27 | console.log("Sorry, something went wrong"); 28 | console.dir(err); 29 | return; 30 | } 31 | 32 | try { 33 | if(currentPlatform !== "win32") { 34 | $`ollama --version`; 35 | console.log(chalk.green("Ollama is installed. Starting the server...")); 36 | $`ollama serve`.quiet().catch((err) => { 37 | console.log(chalk.red("Failed to start Ollama server:"), err); 38 | }); 39 | } 40 | } catch (err) { 41 | console.error( 42 | chalk.red( 43 | "Ollama is not installed. You can install it or either use gemini-ai" 44 | , err) 45 | ); 46 | } finally { 47 | console.log( 48 | chalk.green( 49 | "Welcome to PR-Buddy, An AI tool to help you write better pull request messages" 50 | ) 51 | ); 52 | 53 | inquirer 54 | .prompt([ 55 | { 56 | type: "list", 57 | name: "action", 58 | message: "What would you like to do?", 59 | choices: ["Setup", "Create a PR"], 60 | }, 61 | ]) 62 | .then((answers) => { 63 | if (answers.action === "Setup") { 64 | setup(); 65 | } else if (answers.action === "Create a PR") { 66 | createPr(); 67 | } 68 | }); 69 | } 70 | }); 71 | }); 72 | 73 | program.parse(); 74 | -------------------------------------------------------------------------------- /src/types/external-apis.d.ts: -------------------------------------------------------------------------------- 1 | type ChatCompletion = { 2 | id: string; 3 | object: string; 4 | created: number; 5 | model: string; 6 | system_fingerprint: string; 7 | choices: { 8 | index: number; 9 | message: { 10 | role: string; 11 | content: string; 12 | }; 13 | logprobs: null | any; 14 | finish_reason: string; 15 | }[]; 16 | usage: { 17 | prompt_tokens: number; 18 | completion_tokens: number; 19 | total_tokens: number; 20 | completion_tokens_details: { 21 | reasoning_tokens: number; 22 | }; 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /template.md: -------------------------------------------------------------------------------- 1 | # Pull Request Description 2 | 3 | ## 📋 Overview 4 | [Provide a clear, concise summary of the changes introduced by this PR] 5 | 6 | ## 🎯 Purpose 7 | - **Issue Reference**: [Link to issue tracker/ticket if applicable] 8 | - **Problem Solved**: 9 | - [Describe the specific problem or pain point this PR addresses] 10 | - [Provide context and background] 11 | 12 | ## 🚀 Key Changes 13 | - [List major changes and improvements] 14 | - [Highlight significant refactoring or architectural changes] 15 | 16 | ## 🛠 Implementation Details 17 | ### What Changed 18 | - [Detailed breakdown of specific code modifications] 19 | - [Explain the approach taken to solve the problem] 20 | 21 | ### Technical Considerations 22 | - [Discuss any complex logic or algorithms implemented] 23 | - [Note any performance optimizations] 24 | - [Explain trade-offs made during implementation] 25 | 26 | ## 🧪 Testing 27 | ### Test Coverage 28 | - [ ] Unit tests added/updated 29 | - [ ] Integration tests added/updated 30 | - [ ] Manual testing completed 31 | 32 | ### Test Scenarios 33 | - [List specific test cases covered] 34 | - [Describe edge cases tested] 35 | - [Provide reproduction steps for any scenarios] 36 | 37 | ## 🔍 Potential Impact 38 | ### Risks 39 | - [Identify potential risks or side effects of the changes] 40 | - [Discuss any backward compatibility concerns] 41 | 42 | ### Performance 43 | - [Note any performance implications] 44 | - [Include benchmark results if applicable] 45 | 46 | ## 📝 Additional Notes 47 | - [Any additional context, rationale, or explanations] 48 | - [Future improvements or follow-up tasks] 49 | 50 | ## 🖼 Visual Changes 51 | [If applicable, include screenshots or GIFs demonstrating UI/UX changes] 52 | 53 | ## ✅ Checklist 54 | ### Code Quality 55 | - [ ] Code follows project coding standards 56 | - [ ] Sufficient code comments added 57 | - [ ] Performed self-code review 58 | - [ ] No new linting warnings introduced 59 | 60 | ### Testing 61 | - [ ] Added/updated unit tests 62 | - [ ] Verified all tests pass 63 | - [ ] Tested edge cases and potential failure modes 64 | 65 | ### Documentation 66 | - [ ] Updated relevant documentation 67 | - [ ] Added/updated inline code documentation 68 | - [ ] Updated README or project docs if necessary 69 | 70 | ## 🔗 Related Work 71 | - Closes #[Issue Number] 72 | - Related to #[Other Related Issues] 73 | 74 | ## 📦 Changes Type 75 | - [ ] Bug Fix 76 | - [ ] Feature Addition 77 | - [ ] Performance Improvement 78 | - [ ] Refactoring 79 | - [ ] Documentation Update 80 | - [ ] Breaking Change 81 | - [ ] Other: [Specify] 82 | 83 | --- 84 | 85 | **Additional Context** 86 | [Any other relevant information that doesn't fit above] 87 | 88 | PR Generated with [PR-Buddy](https://github.com/MohammedIbrahim8887/pr-buddy) 🔥🔥 -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext", "DOM"], 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "moduleDetection": "force", 7 | "jsx": "react-jsx", 8 | "allowJs": true, 9 | "outDir": "./dist", 10 | "rootDir": "./src", 11 | "moduleResolution": "bundler", 12 | "verbatimModuleSyntax": true, 13 | "noEmit": false, 14 | "strict": true, 15 | "skipLibCheck": true, 16 | "esModuleInterop": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noEmitOnError": false, // Allow emit even if there are errors 19 | "noUnusedLocals": false, 20 | "noUnusedParameters": false, 21 | "noPropertyAccessFromIndexSignature": false 22 | }, 23 | "include": ["src/**/*"], 24 | "exclude": ["node_modules", "dist"] 25 | } 26 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | format: ["cjs", "esm"], 5 | entry: ["./src/index.ts"], 6 | dts: false, // Disable declaration generation 7 | shims: true, 8 | skipNodeModulesBundle: true, 9 | clean: true, 10 | }); 11 | --------------------------------------------------------------------------------