Compare commits

..

2 Commits

Author SHA1 Message Date
5b780addba
ci: test ci with the workflow file 2026-08-05 20:44:21 +08:00
fc9f41839c
ci: test ci with workflow file 2026-08-05 19:44:08 +08:00
6 changed files with 414 additions and 175 deletions

View File

@ -6,8 +6,6 @@ permissions:
on:
issues:
types: [opened, labeled]
issue_comment:
types: [created]
jobs:
welcome-new-issues:
@ -28,30 +26,6 @@ jobs:
- 💬 加入[QQ交流群](https://qm.qq.com/q/TpeP9Uv2yk)
- 🔍 搜索[现有issues](https://github.com/neteasecloudmusicapienhanced/api-enhanced/issues) 看是否有类似问题
handle-help-wanted:
if: contains(github.event.issue.labels.*.name, 'question')
runs-on: ubuntu-latest
steps:
- name: Add help resources
uses: peter-evans/create-or-update-comment@v5
with:
issue-number: ${{ github.event.issue.number }}
body: |
🆘 需要帮助?
这里有一些可能对您有用的资源:
📚 **文档资源**
- [项目文档](https://neteasecloudmusicapienhanced.js.org)
🔍 **常见问题**
- 搜索[已关闭的issues](https://github.com/neteasecloudmusicapienhanced/api-enhanced/issues?q=is%3Aissue+is%3Aclosed) 看看是否有类似的问题已经被解答。
💬 **即时帮助**
- 加入QQ群https://qm.qq.com/q/TpeP9Uv2yk
如果以上资源无法解决您的问题,请提供更多详细信息,我们会尽快为您解答!
stale-issues:
if: github.event.action == 'opened'
runs-on: ubuntu-latest

59
.github/workflows/opencode-auto.yml vendored Normal file
View File

@ -0,0 +1,59 @@
name: OpenCode Automation
on:
issues:
types: [opened]
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: write
issues: write
pull-requests: write
concurrency:
group: auto-${{ github.event.issue.number || github.event.pull_request.number }}
cancel-in-progress: false
jobs:
automate:
# 排除机器人自己的触发,防止自我循环
if: |
github.actor != 'takanashi-hoshino-agent[bot]' &&
github.actor != 'github-actions[bot]' &&
github.actor != 'opencode-agent[bot]'
runs-on: ubuntu-latest
steps:
- name: Generate bot token
id: token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
- name: Checkout
uses: actions/checkout@v6
with:
token: ${{ steps.token.outputs.token }}
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install opencode
run: npm i -g opencode-ai@latest
- name: Run automation
env:
GITHUB_TOKEN: ${{ steps.token.outputs.token }}
MODEL: opencode/deepseek-v4-flash-free
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
ISSUE_PROMPT: ${{ secrets.OPENCODE_PROMPT }}
PR_PROMPT: |
${{ secrets.OPENCODE_PROMPT }}
${{ secrets.PR_REVIEW_PROMPT }}
run: node scripts/opencode-automation.mjs

View File

@ -5,8 +5,6 @@ on:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened]
permissions:
contents: write
@ -16,10 +14,13 @@ permissions:
jobs:
bot:
if: |
contains(github.event.comment.body, ' /oc') ||
(contains(github.event.comment.body, ' /oc') ||
startsWith(github.event.comment.body, '/oc') ||
contains(github.event.comment.body, ' /opencode') ||
startsWith(github.event.comment.body, '/opencode')
startsWith(github.event.comment.body, '/opencode')) &&
github.actor != 'opencode-agent[bot]' &&
github.actor != 'takanashi-hoshino-agent[bot]' &&
github.actor != 'github-actions[bot]'
runs-on: ubuntu-latest
permissions:
contents: read
@ -51,4 +52,5 @@ jobs:
with:
model: opencode/deepseek-v4-flash-free
use_github_token: true
prompt: ${{ secrets.OPENCODE_PROMPT }}
prompt: |
${{ secrets.OPENCODE_PROMPT }}

View File

@ -75,8 +75,8 @@
"data"
],
"dependencies": {
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.3.4",
"axios": "^1.18.1",
"@neteasecloudmusicapienhanced/unblockmusic-utils": "^0.4.0",
"axios": "^1.19.0",
"crypto-js": "^4.2.0",
"dotenv": "^17.4.2",
"express": "^5.2.1",
@ -89,7 +89,7 @@
"safe-decode-uri-component": "^1.2.1",
"tunnel": "^0.0.6",
"xml2js": "^0.6.2",
"yargs": "^18.0.0"
"yargs": "^18.1.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.6",
@ -98,17 +98,17 @@
"@types/express-fileupload": "^1.5.1",
"@types/mocha": "^10.0.10",
"@types/node": "25.9.2",
"@typescript-eslint/eslint-plugin": "^8.65.0",
"@typescript-eslint/parser": "^8.65.0",
"@typescript-eslint/eslint-plugin": "^8.66.0",
"@typescript-eslint/parser": "^8.66.0",
"eslint": "^9.39.5",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-html": "^8.1.4",
"eslint-plugin-prettier": "^5.5.6",
"globals": "^17.7.0",
"globals": "^17.9.0",
"husky": "^9.1.7",
"intelli-espower-loader": "^1.1.0",
"lint-staged": "^16.4.0",
"mocha": "^11.7.6",
"mocha": "^11.8.0",
"nodemon": "^3.1.14",
"pkg": "^5.8.1",
"power-assert": "^1.6.1",

270
pnpm-lock.yaml generated
View File

@ -9,11 +9,11 @@ importers:
.:
dependencies:
'@neteasecloudmusicapienhanced/unblockmusic-utils':
specifier: ^0.3.4
version: 0.3.4
specifier: ^0.4.0
version: 0.4.0
axios:
specifier: ^1.18.1
version: 1.18.1
specifier: ^1.19.0
version: 1.19.0
crypto-js:
specifier: ^4.2.0
version: 4.2.0
@ -51,8 +51,8 @@ importers:
specifier: ^0.6.2
version: 0.6.2
yargs:
specifier: ^18.0.0
version: 18.0.0
specifier: ^18.1.0
version: 18.1.0
devDependencies:
'@eslint/eslintrc':
specifier: ^3.3.6
@ -73,11 +73,11 @@ importers:
specifier: 25.9.2
version: 25.9.2
'@typescript-eslint/eslint-plugin':
specifier: ^8.65.0
version: 8.65.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)
specifier: ^8.66.0
version: 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/parser':
specifier: ^8.65.0
version: 8.65.0(eslint@9.39.5)(typescript@5.9.3)
specifier: ^8.66.0
version: 8.66.0(eslint@9.39.5)(typescript@5.9.3)
eslint:
specifier: ^9.39.5
version: 9.39.5
@ -91,8 +91,8 @@ importers:
specifier: ^5.5.6
version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6)
globals:
specifier: ^17.7.0
version: 17.7.0
specifier: ^17.9.0
version: 17.9.0
husky:
specifier: ^9.1.7
version: 9.1.7
@ -103,8 +103,8 @@ importers:
specifier: ^16.4.0
version: 16.4.0
mocha:
specifier: ^11.7.6
version: 11.7.6
specifier: ^11.8.0
version: 11.8.0
nodemon:
specifier: ^3.1.14
version: 3.1.14
@ -222,8 +222,8 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.4':
resolution: {integrity: sha512-3Lu/eYnDqf9/rHxa5G38TZxM7QVUBZpzKMWBmT6STHaMb6ztFRfrVrejIC1r7IUvtCWkoRulD+8UlllNTEsd6g==}
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.0':
resolution: {integrity: sha512-KUDdBEe3IK4tPSt2UHdFweYbhoZUYGBxbt8FuqQu5bZdpymfpecZxLf+a8G/JvIYHph4H+KxYlq6+MD9oXCfjg==}
hasBin: true
'@nodelib/fs.scandir@2.1.5':
@ -271,8 +271,8 @@ packages:
'@types/express-fileupload@1.5.1':
resolution: {integrity: sha512-DllImBVI1lCyjl2klky/TEwk60mbNebgXv1669h66g9TfptWSrEFq5a/raHSutaFzjSm1tmn9ypdNfu4jPSixQ==}
'@types/express-serve-static-core@5.1.2':
resolution: {integrity: sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==}
'@types/express-serve-static-core@5.1.3':
resolution: {integrity: sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==}
'@types/express@5.0.6':
resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==}
@ -301,63 +301,63 @@ packages:
'@types/serve-static@2.2.0':
resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==}
'@typescript-eslint/eslint-plugin@8.65.0':
resolution: {integrity: sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==}
'@typescript-eslint/eslint-plugin@8.66.0':
resolution: {integrity: sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.65.0
'@typescript-eslint/parser': ^8.66.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/parser@8.65.0':
resolution: {integrity: sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==}
'@typescript-eslint/parser@8.66.0':
resolution: {integrity: sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/project-service@8.65.0':
resolution: {integrity: sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==}
'@typescript-eslint/project-service@8.66.0':
resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/scope-manager@8.65.0':
resolution: {integrity: sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==}
'@typescript-eslint/scope-manager@8.66.0':
resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.65.0':
resolution: {integrity: sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==}
'@typescript-eslint/tsconfig-utils@8.66.0':
resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/type-utils@8.65.0':
resolution: {integrity: sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==}
'@typescript-eslint/type-utils@8.66.0':
resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/types@8.65.0':
resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==}
'@typescript-eslint/types@8.66.0':
resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.65.0':
resolution: {integrity: sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==}
'@typescript-eslint/typescript-estree@8.66.0':
resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/utils@8.65.0':
resolution: {integrity: sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==}
'@typescript-eslint/utils@8.66.0':
resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/visitor-keys@8.65.0':
resolution: {integrity: sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==}
'@typescript-eslint/visitor-keys@8.66.0':
resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@unblockneteasemusic/server@0.28.0':
@ -386,8 +386,8 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
acorn@8.17.0:
resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
acorn@8.18.0:
resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==}
engines: {node: '>=0.4.0'}
hasBin: true
@ -485,8 +485,8 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
axios@1.18.1:
resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==}
axios@1.19.0:
resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@ -517,14 +517,14 @@ packages:
resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
engines: {node: '>=18'}
brace-expansion@1.1.16:
resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==}
brace-expansion@1.1.18:
resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==}
brace-expansion@2.1.2:
resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==}
brace-expansion@2.1.4:
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
brace-expansion@5.0.8:
resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==}
brace-expansion@5.0.9:
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
engines: {node: 20 || >=22}
braces@3.0.3:
@ -1153,8 +1153,8 @@ packages:
flatstr@1.0.12:
resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==}
flatted@3.4.3:
resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==}
flatted@3.4.4:
resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
follow-redirects@1.16.0:
resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==}
@ -1262,8 +1262,8 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
globals@17.7.0:
resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==}
globals@17.9.0:
resolution: {integrity: sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==}
engines: {node: '>=18'}
globalthis@1.0.4:
@ -1400,8 +1400,8 @@ packages:
resolution: {integrity: sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==}
engines: {node: '>=10'}
ip-address@10.2.0:
resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
ip-address@10.4.0:
resolution: {integrity: sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==}
engines: {node: '>= 12'}
ipaddr.js@1.9.1:
@ -1565,8 +1565,8 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
js-yaml@4.3.0:
resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==}
js-yaml@4.3.1:
resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
hasBin: true
jsesc@2.5.2:
@ -1699,8 +1699,8 @@ packages:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
minimatch@10.2.5:
resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
minimatch@10.2.6:
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
engines: {node: 18 || 20 || >=22}
minimatch@3.1.5:
@ -1720,8 +1720,8 @@ packages:
mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
mocha@11.7.6:
resolution: {integrity: sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==}
mocha@11.8.0:
resolution: {integrity: sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
@ -2158,8 +2158,8 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sax@1.6.0:
resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
sax@1.6.1:
resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==}
engines: {node: '>=11.0.0'}
secure-json-parse@2.7.0:
@ -2400,8 +2400,8 @@ packages:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
tinyexec@1.2.4:
resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
tinyexec@1.3.0:
resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==}
engines: {node: '>=18'}
tinyglobby@0.2.17:
@ -2655,8 +2655,8 @@ packages:
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
engines: {node: '>=12'}
yargs@18.0.0:
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
yargs@18.1.0:
resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
yocto-queue@0.1.0:
@ -2718,7 +2718,7 @@ snapshots:
globals: 14.0.0
ignore: 5.3.2
import-fresh: 3.3.1
js-yaml: 4.3.0
js-yaml: 4.3.1
minimatch: 3.1.5
strip-json-comments: 3.1.1
transitivePeerDependencies:
@ -2772,10 +2772,10 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.3.4':
'@neteasecloudmusicapienhanced/unblockmusic-utils@0.4.0':
dependencies:
'@unblockneteasemusic/server': 0.28.0
axios: 1.18.1
axios: 1.19.0
dotenv: 17.4.2
express: 4.22.2
https: 1.0.0
@ -2831,7 +2831,7 @@ snapshots:
'@types/busboy': 1.5.4
'@types/express': 5.0.6
'@types/express-serve-static-core@5.1.2':
'@types/express-serve-static-core@5.1.3':
dependencies:
'@types/node': 25.9.2
'@types/qs': 6.15.1
@ -2841,7 +2841,7 @@ snapshots:
'@types/express@5.0.6':
dependencies:
'@types/body-parser': 1.19.6
'@types/express-serve-static-core': 5.1.2
'@types/express-serve-static-core': 5.1.3
'@types/serve-static': 2.2.0
'@types/http-errors@2.0.5': {}
@ -2867,14 +2867,14 @@ snapshots:
'@types/http-errors': 2.0.5
'@types/node': 25.9.2
'@typescript-eslint/eslint-plugin@8.65.0(@typescript-eslint/parser@8.65.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.9.3))(eslint@9.39.5)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.65.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.65.0
'@typescript-eslint/type-utils': 8.65.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/utils': 8.65.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.65.0
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.66.0
eslint: 9.39.5
ignore: 7.0.6
natural-compare: 1.4.0
@ -2883,41 +2883,41 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.65.0(eslint@9.39.5)(typescript@5.9.3)':
'@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.65.0
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.65.0
'@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.66.0
debug: 4.4.3
eslint: 9.39.5
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.65.0(typescript@5.9.3)':
'@typescript-eslint/project-service@8.66.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3)
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3)
'@typescript-eslint/types': 8.66.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.65.0':
'@typescript-eslint/scope-manager@8.66.0':
dependencies:
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/visitor-keys': 8.65.0
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/visitor-keys': 8.66.0
'@typescript-eslint/tsconfig-utils@8.65.0(typescript@5.9.3)':
'@typescript-eslint/tsconfig-utils@8.66.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
'@typescript-eslint/type-utils@8.65.0(eslint@9.39.5)(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.66.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.65.0(eslint@9.39.5)(typescript@5.9.3)
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.5
ts-api-utils: 2.5.0(typescript@5.9.3)
@ -2925,16 +2925,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.65.0': {}
'@typescript-eslint/types@8.66.0': {}
'@typescript-eslint/typescript-estree@8.65.0(typescript@5.9.3)':
'@typescript-eslint/typescript-estree@8.66.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.65.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.65.0(typescript@5.9.3)
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/visitor-keys': 8.65.0
'@typescript-eslint/project-service': 8.66.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3)
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/visitor-keys': 8.66.0
debug: 4.4.3
minimatch: 10.2.5
minimatch: 10.2.6
semver: 7.8.5
tinyglobby: 0.2.17
ts-api-utils: 2.5.0(typescript@5.9.3)
@ -2942,20 +2942,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.65.0(eslint@9.39.5)(typescript@5.9.3)':
'@typescript-eslint/utils@8.66.0(eslint@9.39.5)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5)
'@typescript-eslint/scope-manager': 8.65.0
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/typescript-estree': 8.65.0(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/types': 8.66.0
'@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3)
eslint: 9.39.5
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/visitor-keys@8.65.0':
'@typescript-eslint/visitor-keys@8.66.0':
dependencies:
'@typescript-eslint/types': 8.65.0
'@typescript-eslint/types': 8.66.0
eslint-visitor-keys: 5.0.1
'@unblockneteasemusic/server@0.28.0':
@ -2976,13 +2976,13 @@ snapshots:
acorn-es7-plugin@1.1.7: {}
acorn-jsx@5.3.2(acorn@8.17.0):
acorn-jsx@5.3.2(acorn@8.18.0):
dependencies:
acorn: 8.17.0
acorn: 8.18.0
acorn@5.7.4: {}
acorn@8.17.0: {}
acorn@8.18.0: {}
agent-base@6.0.2:
dependencies:
@ -3072,7 +3072,7 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
axios@1.18.1:
axios@1.19.0:
dependencies:
follow-redirects: 1.16.0
form-data: 4.0.6
@ -3129,16 +3129,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
brace-expansion@1.1.16:
brace-expansion@1.1.18:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@2.1.2:
brace-expansion@2.1.4:
dependencies:
balanced-match: 1.0.2
brace-expansion@5.0.8:
brace-expansion@5.0.9:
dependencies:
balanced-match: 4.0.4
@ -3765,8 +3765,8 @@ snapshots:
espree@10.4.0:
dependencies:
acorn: 8.17.0
acorn-jsx: 5.3.2(acorn@8.17.0)
acorn: 8.18.0
acorn-jsx: 5.3.2(acorn@8.18.0)
eslint-visitor-keys: 4.2.1
esprima@2.7.3: {}
@ -3959,14 +3959,14 @@ snapshots:
flat-cache@4.0.1:
dependencies:
flatted: 3.4.3
flatted: 3.4.4
keyv: 4.5.4
flat@5.0.2: {}
flatstr@1.0.12: {}
flatted@3.4.3: {}
flatted@3.4.4: {}
follow-redirects@1.16.0: {}
@ -4085,7 +4085,7 @@ snapshots:
globals@14.0.0: {}
globals@17.7.0: {}
globals@17.9.0: {}
globalthis@1.0.4:
dependencies:
@ -4219,7 +4219,7 @@ snapshots:
from2: 2.3.0
p-is-promise: 3.0.0
ip-address@10.2.0: {}
ip-address@10.4.0: {}
ipaddr.js@1.9.1: {}
@ -4378,7 +4378,7 @@ snapshots:
joycon@3.1.1: {}
js-yaml@4.3.0:
js-yaml@4.3.1:
dependencies:
argparse: 2.0.1
@ -4418,7 +4418,7 @@ snapshots:
listr2: 9.0.5
picomatch: 4.0.5
string-argv: 0.3.2
tinyexec: 1.2.4
tinyexec: 1.3.0
yaml: 2.9.0
listr2@9.0.5:
@ -4498,17 +4498,17 @@ snapshots:
mimic-response@3.1.0: {}
minimatch@10.2.5:
minimatch@10.2.6:
dependencies:
brace-expansion: 5.0.8
brace-expansion: 5.0.9
minimatch@3.1.5:
dependencies:
brace-expansion: 1.1.16
brace-expansion: 1.1.18
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.2
brace-expansion: 2.1.4
minimist@1.2.8: {}
@ -4516,7 +4516,7 @@ snapshots:
mkdirp-classic@0.5.3: {}
mocha@11.7.6:
mocha@11.8.0:
dependencies:
browser-stdout: 1.3.1
chokidar: 4.0.3
@ -4527,7 +4527,7 @@ snapshots:
glob: 10.5.0
he: 1.2.0
is-path-inside: 3.0.3
js-yaml: 4.3.0
js-yaml: 4.3.1
log-symbols: 4.1.0
minimatch: 9.0.9
ms: 2.1.3
@ -4602,7 +4602,7 @@ snapshots:
chokidar: 3.6.0
debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
minimatch: 10.2.5
minimatch: 10.2.6
pstree.remy: 1.1.8
semver: 7.8.5
simple-update-notifier: 2.0.0
@ -5073,7 +5073,7 @@ snapshots:
safer-buffer@2.1.2: {}
sax@1.6.0: {}
sax@1.6.1: {}
secure-json-parse@2.7.0: {}
@ -5233,7 +5233,7 @@ snapshots:
socks@2.8.9:
dependencies:
ip-address: 10.2.0
ip-address: 10.4.0
smart-buffer: 4.2.0
sonic-boom@1.4.1:
@ -5387,7 +5387,7 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
tinyexec@1.2.4: {}
tinyexec@1.3.0: {}
tinyglobby@0.2.17:
dependencies:
@ -5620,7 +5620,7 @@ snapshots:
xml2js@0.6.2:
dependencies:
sax: 1.6.0
sax: 1.6.1
xmlbuilder: 11.0.1
xml@1.0.1: {}
@ -5687,12 +5687,12 @@ snapshots:
y18n: 5.0.8
yargs-parser: 21.1.1
yargs@18.0.0:
yargs@18.1.0:
dependencies:
cliui: 9.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
string-width: 7.2.0
string-width: 8.2.2
y18n: 5.0.8
yargs-parser: 22.0.0

View File

@ -0,0 +1,204 @@
#!/usr/bin/env node
import { spawnSync } from 'node:child_process'
import { readFileSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
const eventName = process.env.GITHUB_EVENT_NAME
const eventPath = process.env.GITHUB_EVENT_PATH
const token = process.env.GITHUB_TOKEN
const repo = process.env.GITHUB_REPOSITORY
const model = process.env.MODEL || 'opencode/deepseek-v4-flash-free'
const botLogin = process.env.BOT_LOGIN || 'takanashi-hoshino-agent[bot]'
if (!eventPath || !token || !repo) {
console.error(
'Missing required env: GITHUB_EVENT_PATH / GITHUB_TOKEN / GITHUB_REPOSITORY',
)
process.exit(1)
}
if (!process.env.OPENCODE_API_KEY) {
console.error('Missing OPENCODE_API_KEY')
process.exit(1)
}
const event = JSON.parse(readFileSync(eventPath, 'utf8'))
const [owner, repoName] = repo.split('/')
const api = `https://api.github.com/repos/${owner}/${repoName}`
const headers = {
Authorization: `Bearer ${token}`,
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
'User-Agent': 'opencode-automation',
}
async function get(path) {
const res = await fetch(`${api}${path}`, { headers })
if (!res.ok) throw new Error(`GET ${path}: HTTP ${res.status}`)
return res.json()
}
async function post(path, body) {
const res = await fetch(`${api}${path}`, {
method: 'POST',
headers,
body: JSON.stringify(body),
})
const text = await res.text()
if (!res.ok) throw new Error(`POST ${path}: HTTP ${res.status} ${text}`)
return text ? JSON.parse(text) : null
}
/** 无头模式跑 opencode返回模型输出的文本 */
function runOpencode(prompt, contextFile) {
const args = ['run', '--auto', '-m', model, '--format', 'default', prompt]
if (contextFile) args.push('-f', contextFile)
console.log(
`Running: opencode run --auto -m ${model} (prompt ${prompt.length} chars${contextFile ? ', context attached' : ''})`,
)
const res = spawnSync('opencode', args, {
encoding: 'utf8',
maxBuffer: 128 * 1024 * 1024,
})
if (res.error) throw res.error
if (res.status !== 0) {
if (res.stderr) console.error('opencode stderr:\n' + res.stderr)
throw new Error(`opencode exited with code ${res.status}`)
}
return (res.stdout || '').trim()
}
/** 该 issue/PR 是否已有 bot 评论(用于去重,避免 synchronize 重复评论) */
async function hasBotComment(target) {
const comments = await get(`/issues/${target}/comments?per_page=100`)
return comments.some((c) => c.user?.login === botLogin)
}
async function comment(target, body) {
if (!body) {
console.log('No output from agent, skip comment')
return
}
if (body.length > 60000) body = body.slice(0, 60000) + '\n\n_...(truncated)_'
await post(`/issues/${target}/comments`, { body })
console.log(`Commented on #${target}`)
}
async function handleIssue(issue) {
const n = issue.number
console.log(`Handling issue #${n}`)
if (await hasBotComment(n)) {
console.log(`Already handled by bot, skip`)
return
}
const comments = await get(`/issues/${n}/comments?per_page=100`)
const thread = comments
.filter((c) => c.user?.login !== botLogin)
.map(
(c) =>
`- **${c.user?.login}** (${c.created_at}):\n ${(c.body || '').replace(/\n/g, '\n ')}`,
)
.join('\n')
const ctx = [
`# Issue #${n}: ${issue.title}`,
``,
`- State: ${issue.state}`,
`- Author: ${issue.user?.login}`,
`- Created: ${issue.created_at}`,
``,
`## Body`,
``,
issue.body || '(empty)',
...(thread ? [`\n## Comments\n\n${thread}`] : []),
].join('\n')
const ctxFile = join(tmpdir(), 'opencode-issue-context.md')
writeFileSync(ctxFile, ctx)
const prompt =
process.env.ISSUE_PROMPT ||
[
`You are a maintainer bot for the ${repo} repository.`,
`Read the attached file for the issue context.`,
`If the issue has a clear fix or points to relevant docs, reply with documentation links and/or`,
`error-handling guidance for code examples. If nothing actionable, reply with exactly "NO_ACTION".`,
`Do NOT modify any files, do NOT commit, do NOT push. Output only the comment text.`,
].join('\n')
const output = runOpencode(prompt, ctxFile)
if (output === 'NO_ACTION' || output.startsWith('NO_ACTION')) {
console.log('Agent decided no action needed')
return
}
await comment(n, output)
}
async function handlePullRequest(pr) {
const n = pr.number
console.log(`Handling PR #${n}`)
if (await hasBotComment(n)) {
console.log(`Already handled by bot, skip`)
return
}
let files = []
try {
files = await get(`/pulls/${n}/files?per_page=100`)
} catch (e) {
console.warn(`Failed to fetch changed files: ${e.message}`)
}
const fileList = files
.map((f) => `- ${f.status} ${f.filename} (+${f.additions}/-${f.deletions})`)
.join('\n')
const ctx = [
`# PR #${n}: ${pr.title}`,
``,
`- State: ${pr.state}`,
`- Author: ${pr.user?.login}`,
`- Base: ${pr.base?.ref} <- Head: ${pr.head?.ref}`,
`- Created: ${pr.created_at}`,
`- Stats: +${pr.additions}/-${pr.deletions}, ${pr.changed_files} files`,
``,
`## Body`,
``,
pr.body || '(empty)',
...(fileList ? [`\n## Changed files\n\n${fileList}`] : []),
].join('\n')
const ctxFile = join(tmpdir(), 'opencode-pr-context.md')
writeFileSync(ctxFile, ctx)
const prompt =
process.env.PR_PROMPT ||
[
`You are a maintainer bot for the ${repo} repository.`,
`Read the attached file for the PR context. The PR branch is already checked out.`,
`Run \`git diff HEAD^1 HEAD\` (or \`git diff origin/${pr.base?.ref}...HEAD\`) to see the changes.`,
`Review the code changes: point out bugs, risks and improvements, and suggest fixes for obvious issues.`,
`If the PR looks good, say so concisely.`,
`Do NOT modify any files, do NOT commit, do NOT push. Output only the review comment text.`,
].join('\n')
const output = runOpencode(prompt, ctxFile)
await comment(n, output)
}
async function main() {
console.log(`Event: ${eventName} on ${repo}`)
if (eventName === 'issues') {
await handleIssue(event.issue)
} else if (eventName === 'pull_request') {
await handlePullRequest(event.pull_request)
} else {
console.log(`Unhandled event type: ${eventName}`)
}
}
main().catch((e) => {
console.error(`Automation failed: ${e.message}`)
process.exit(1)
})