Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb7de776e4 | |||
| 108b8243a9 | |||
| 59f1e72dec | |||
| 46d95853ae | |||
| e6374a952c |
@@ -1,2 +0,0 @@
|
||||
!examples
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
name: Auto-tag Action Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'package.json'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
auto-tag:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Get package version
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(node -p "require('./package.json').version")
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
# Extract major version (e.g., "0" from "0.0.1")
|
||||
MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
|
||||
echo "major_tag=v$MAJOR_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if tag already exists
|
||||
id: check_tag
|
||||
run: |
|
||||
if git rev-parse "refs/tags/${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
echo "Tag ${{ steps.version.outputs.tag }} already exists"
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "Tag ${{ steps.version.outputs.tag }} does not exist"
|
||||
fi
|
||||
|
||||
- name: Install dependencies and build
|
||||
if: steps.check_tag.outputs.exists == 'false'
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm run build
|
||||
|
||||
- name: Commit built files if changed
|
||||
if: steps.check_tag.outputs.exists == 'false'
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
git add dist/
|
||||
git commit -m "chore: rebuild action for v${{ steps.version.outputs.version }}"
|
||||
git push
|
||||
fi
|
||||
|
||||
- name: Create and push tags
|
||||
if: steps.check_tag.outputs.exists == 'false'
|
||||
run: |
|
||||
# Create specific version tag
|
||||
git tag ${{ steps.version.outputs.tag }}
|
||||
git push origin ${{ steps.version.outputs.tag }}
|
||||
|
||||
# Create/update major version tag (moving tag)
|
||||
git tag -f ${{ steps.version.outputs.major_tag }}
|
||||
git push origin ${{ steps.version.outputs.major_tag }} --force
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: steps.check_tag.outputs.exists == 'false'
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.tag }}
|
||||
release_name: Action Release ${{ steps.version.outputs.tag }}
|
||||
body: |
|
||||
Automated release for action version ${{ steps.version.outputs.version }}
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
- uses: pullfrog/pullfrog@${{ steps.version.outputs.major_tag }}
|
||||
with:
|
||||
message: "Your message here"
|
||||
```
|
||||
|
||||
Or use the specific version:
|
||||
```yaml
|
||||
- uses: pullfrog/pullfrog@${{ steps.version.outputs.tag }}
|
||||
with:
|
||||
message: "Your message here"
|
||||
```
|
||||
draft: false
|
||||
prerelease: false
|
||||
+25
-28
@@ -1,37 +1,34 @@
|
||||
# macOS settings file
|
||||
.DS_Store
|
||||
|
||||
# Contains all your dependencies
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# Replace as required with your build location
|
||||
/build
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# Deal with environment files
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# We'll allow an example .env file which can be copied
|
||||
!.env.example
|
||||
|
||||
# Coverage directory used by testing tools
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# Visual Studio Code configuration
|
||||
.vscode/
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# npm and yarn debug logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# next.js
|
||||
.next
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# sveltekit
|
||||
/.svelte-kit
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
examples
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Pullfrog
|
||||
|
||||
A simple GitHub Action that prints a customizable message to the console.
|
||||
|
||||
+23
-12
@@ -1,17 +1,28 @@
|
||||
name: 'Simple Message Action'
|
||||
description: 'A simple GitHub Action that prints a message to the console'
|
||||
author: 'Pullfrog'
|
||||
name: shockbot
|
||||
description: Ollama-powered code review bot for Gitea
|
||||
author: ShockVPN
|
||||
|
||||
inputs:
|
||||
message:
|
||||
description: 'Message to print to console'
|
||||
required: true
|
||||
default: 'Hello from Pullfrog Action!'
|
||||
prompt:
|
||||
description: Review instruction sent to the model
|
||||
required: false
|
||||
default: "Review this pull request"
|
||||
model:
|
||||
description: Ollama model to use
|
||||
required: false
|
||||
default: "qwen3.6:35b"
|
||||
context_window:
|
||||
description: Max tokens per diff chunk
|
||||
required: false
|
||||
default: "4096"
|
||||
max_tool_calls:
|
||||
description: Max MCP tool calls the model can make per chunk
|
||||
required: false
|
||||
default: "10"
|
||||
|
||||
runs:
|
||||
using: 'node20'
|
||||
main: 'dist/index.js'
|
||||
using: "node24"
|
||||
main: "bootstrap.ts"
|
||||
|
||||
branding:
|
||||
icon: 'message-circle'
|
||||
color: 'blue'
|
||||
# BOT_TOKEN, OLLAMA_HOST, and GITEA_URL must be set as env vars by the consuming workflow.
|
||||
# GITHUB_EVENT_NAME, GITHUB_EVENT_PATH, and GITHUB_REPOSITORY are set by the runner.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { execSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname } from "node:path";
|
||||
|
||||
const dir = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
if (!existsSync(`${dir}/node_modules`)) {
|
||||
console.log("node_modules not found, installing dependencies...");
|
||||
try {
|
||||
execSync("bun install --frozen-lockfile", { stdio: "inherit", cwd: dir, timeout: 120_000 });
|
||||
} catch {
|
||||
console.log("bun unavailable, falling back to npm...");
|
||||
execSync("npm install --no-fund --no-audit", { stdio: "inherit", cwd: dir, timeout: 120_000 });
|
||||
}
|
||||
}
|
||||
|
||||
await import("./entry.ts");
|
||||
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "shockbot",
|
||||
"devDependencies": {
|
||||
"@go-gitea/sdk.js": "^0.2.1",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@types/bun": "latest",
|
||||
"ollama": "^0.6.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@go-gitea/sdk.js": ["@go-gitea/sdk.js@0.2.1", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.0", "@octokit/plugin-retry": "^8.0.1", "@octokit/request-error": "^7.0.0", "@octokit/types": "^15.0.0" } }, "sha512-8H3ci55MHUk8LtS8T4rlkpjNagAWCpBin3J0VhSNobh+XXbLR7kXplwH4ZxXRMqZOi1+gBv3XEk8azicW8zt3g=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="],
|
||||
|
||||
"@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="],
|
||||
|
||||
"@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="],
|
||||
|
||||
"@octokit/endpoint": ["@octokit/endpoint@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag=="],
|
||||
|
||||
"@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="],
|
||||
|
||||
"@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="],
|
||||
|
||||
"@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="],
|
||||
|
||||
"@octokit/plugin-retry": ["@octokit/plugin-retry@8.1.0", "", { "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "peerDependencies": { "@octokit/core": ">=7" } }, "sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw=="],
|
||||
|
||||
"@octokit/request": ["@octokit/request@10.0.10", "", { "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "content-type": "^2.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" } }, "sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w=="],
|
||||
|
||||
"@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="],
|
||||
|
||||
"@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
|
||||
|
||||
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||
|
||||
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
"bottleneck": ["bottleneck@2.19.5", "", {}, "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
||||
|
||||
"content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
|
||||
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
|
||||
"cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||
|
||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
|
||||
|
||||
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
|
||||
|
||||
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
|
||||
|
||||
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
|
||||
|
||||
"eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="],
|
||||
|
||||
"eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="],
|
||||
|
||||
"express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="],
|
||||
|
||||
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||
|
||||
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
|
||||
|
||||
"fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="],
|
||||
|
||||
"hono": ["hono@4.12.23", "", {}, "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="],
|
||||
|
||||
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||
|
||||
"json-with-bigint": ["json-with-bigint@3.5.8", "", {}, "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||
|
||||
"merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||
|
||||
"mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||
|
||||
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
||||
|
||||
"ollama": ["ollama@0.6.3", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-KEWEhIqE5wtfzEIZbDCLH51VFZ6Z3ZSa6sIOg/E/tBV8S51flyqBOXi+bRxlOYKDf8i327zG9eSTb8IJxvm3Zg=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
||||
|
||||
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
|
||||
|
||||
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
|
||||
|
||||
"qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
||||
|
||||
"serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
|
||||
|
||||
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
||||
|
||||
"side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="],
|
||||
|
||||
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
||||
|
||||
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
|
||||
"universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="],
|
||||
|
||||
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||
|
||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||
|
||||
"whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
|
||||
"@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"@octokit/endpoint/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"@octokit/graphql/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"@octokit/plugin-retry/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"@octokit/request/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
|
||||
"@octokit/request-error/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="],
|
||||
|
||||
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||
|
||||
"@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@octokit/plugin-retry/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import type { AggregatedReview, Finding, ReviewSeverity } from "./types.ts";
|
||||
|
||||
const SEVERITY_ORDER: ReviewSeverity[] = ["error", "warning", "nit"];
|
||||
|
||||
const SEVERITY_LABEL: Record<ReviewSeverity, string> = {
|
||||
error: "Errors",
|
||||
warning: "Warnings",
|
||||
nit: "Nits",
|
||||
};
|
||||
|
||||
const SEVERITY_EMOJI: Record<ReviewSeverity, string> = {
|
||||
error: "🔴",
|
||||
warning: "🟡",
|
||||
nit: "🔵",
|
||||
};
|
||||
|
||||
function renderGroup(severity: ReviewSeverity, findings: Finding[]): string {
|
||||
const items = findings.map(
|
||||
(f) => `**\`${f.filename}\`** line ${f.line}\n> ${f.comment}`,
|
||||
);
|
||||
return `### ${SEVERITY_EMOJI[severity]} ${SEVERITY_LABEL[severity]} (${findings.length})\n\n${items.join("\n\n")}`;
|
||||
}
|
||||
|
||||
export function formatReview(
|
||||
review: AggregatedReview,
|
||||
files: string[],
|
||||
model: string,
|
||||
): string {
|
||||
console.log(
|
||||
`Formatting review for ${files.length} file${files.length === 1 ? "" : "s"} with model ${model}...`,
|
||||
);
|
||||
const parts: string[] = [];
|
||||
|
||||
parts.push(
|
||||
`## 🤖 shockbot review\n\n> Reviewed ${files.length} file${files.length === 1 ? "" : "s"} using \`${model}\``,
|
||||
);
|
||||
|
||||
// Summary section — join per-chunk summaries
|
||||
const summaries = review.summaries.filter(Boolean);
|
||||
if (summaries.length > 0) {
|
||||
parts.push(`**Summary**\n\n${summaries.join("\n\n")}`);
|
||||
}
|
||||
|
||||
// Group findings by severity
|
||||
const grouped = new Map<ReviewSeverity, Finding[]>();
|
||||
for (const severity of SEVERITY_ORDER) grouped.set(severity, []);
|
||||
for (const finding of review.findings) {
|
||||
grouped.get(finding.severity)?.push(finding);
|
||||
}
|
||||
|
||||
const hasFindings = review.findings.length > 0;
|
||||
if (hasFindings) {
|
||||
for (const severity of SEVERITY_ORDER) {
|
||||
const group = grouped.get(severity)!;
|
||||
if (group.length > 0) parts.push(renderGroup(severity, group));
|
||||
}
|
||||
} else {
|
||||
parts.push("No issues found. ✅");
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
parts.push(`---\n\n<sub>shockbot · \`${model}\` · ${timestamp}</sub>`);
|
||||
|
||||
return parts.join("\n\n---\n\n");
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
import type { DiffChunk } from "./types.ts";
|
||||
|
||||
// ~4 chars per token is a reasonable rough estimate for code
|
||||
function estimateTokens(text: string): number {
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
|
||||
const SKIP_EXTENSIONS = new Set([
|
||||
"lock",
|
||||
"sum",
|
||||
"map",
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"svg",
|
||||
"ico",
|
||||
"webp",
|
||||
"woff",
|
||||
"woff2",
|
||||
"ttf",
|
||||
"eot",
|
||||
"pdf",
|
||||
"zip",
|
||||
"tar",
|
||||
"gz",
|
||||
"bin",
|
||||
"exe",
|
||||
"dll",
|
||||
"so",
|
||||
"dylib",
|
||||
]);
|
||||
|
||||
const SKIP_PATTERNS = [
|
||||
/^node_modules\//,
|
||||
/^\.git\//,
|
||||
/\/generated\//,
|
||||
/\.pb\.\w+$/,
|
||||
/\.min\.[jt]s$/,
|
||||
];
|
||||
|
||||
function shouldSkip(filename: string): boolean {
|
||||
if (!filename.includes(".")) return true; // no extension = likely binary
|
||||
const ext = filename.split(".").pop()!.toLowerCase();
|
||||
return (
|
||||
SKIP_EXTENSIONS.has(ext) || SKIP_PATTERNS.some((p) => p.test(filename))
|
||||
);
|
||||
}
|
||||
|
||||
const EXT_TO_LANG: Record<string, string> = {
|
||||
ts: "TypeScript",
|
||||
tsx: "TypeScript",
|
||||
js: "JavaScript",
|
||||
jsx: "JavaScript",
|
||||
mjs: "JavaScript",
|
||||
cjs: "JavaScript",
|
||||
py: "Python",
|
||||
go: "Go",
|
||||
rs: "Rust",
|
||||
rb: "Ruby",
|
||||
java: "Java",
|
||||
kt: "Kotlin",
|
||||
kts: "Kotlin",
|
||||
swift: "Swift",
|
||||
cs: "C#",
|
||||
cpp: "C++",
|
||||
cc: "C++",
|
||||
cxx: "C++",
|
||||
hpp: "C++",
|
||||
c: "C",
|
||||
php: "PHP",
|
||||
sh: "Shell",
|
||||
bash: "Shell",
|
||||
zsh: "Shell",
|
||||
yaml: "YAML",
|
||||
yml: "YAML",
|
||||
json: "JSON",
|
||||
md: "Markdown",
|
||||
sql: "SQL",
|
||||
html: "HTML",
|
||||
htm: "HTML",
|
||||
css: "CSS",
|
||||
scss: "CSS",
|
||||
sass: "CSS",
|
||||
less: "CSS",
|
||||
vue: "Vue",
|
||||
svelte: "Svelte",
|
||||
toml: "TOML",
|
||||
xml: "XML",
|
||||
tf: "Terraform",
|
||||
hcl: "HCL",
|
||||
};
|
||||
|
||||
function detectLanguage(filename: string): string {
|
||||
const ext = filename.split(".").pop()?.toLowerCase() ?? "";
|
||||
return EXT_TO_LANG[ext] ?? "plain";
|
||||
}
|
||||
|
||||
export interface DiffFile {
|
||||
filename: string;
|
||||
language: string;
|
||||
hunks: Array<{ header: string; body: string }>;
|
||||
}
|
||||
|
||||
export function parseDiff(rawDiff: string): DiffFile[] {
|
||||
console.log("Parsing diff...");
|
||||
const files: DiffFile[] = [];
|
||||
|
||||
const sections = rawDiff.split(/^diff --git /m).filter((s) => s.trim());
|
||||
|
||||
for (const section of sections) {
|
||||
const lines = section.split("\n");
|
||||
|
||||
// Use +++ / --- lines — more reliable than first line for renames and paths with spaces
|
||||
const plusLine = lines.find((l) => l.startsWith("+++ "));
|
||||
const minusLine = lines.find((l) => l.startsWith("--- "));
|
||||
|
||||
let filename: string;
|
||||
if (plusLine?.startsWith("+++ b/")) {
|
||||
filename = plusLine.slice(6);
|
||||
} else if (
|
||||
plusLine === "+++ /dev/null" &&
|
||||
minusLine?.startsWith("--- a/")
|
||||
) {
|
||||
filename = minusLine.slice(6); // deleted file — use old path
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shouldSkip(filename)) continue;
|
||||
|
||||
const language = detectLanguage(filename);
|
||||
const hunks: DiffFile["hunks"] = [];
|
||||
let header = "";
|
||||
let bodyLines: string[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("@@")) {
|
||||
if (header) hunks.push({ header, body: bodyLines.join("\n") });
|
||||
header = line;
|
||||
bodyLines = [];
|
||||
} else if (header) {
|
||||
bodyLines.push(line);
|
||||
}
|
||||
}
|
||||
if (header) hunks.push({ header, body: bodyLines.join("\n") });
|
||||
|
||||
if (hunks.length > 0) files.push({ filename, language, hunks });
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export function chunkFile(file: DiffFile, maxTokens: number): DiffChunk[] {
|
||||
console.log(
|
||||
`Chunking file ${file.filename} with ${file.hunks.length} hunks...`,
|
||||
);
|
||||
return file.hunks.map(({ header, body }) => {
|
||||
let hunk = body;
|
||||
|
||||
if (estimateTokens(body) > maxTokens) {
|
||||
// Truncate from bottom — top of the hunk has the most useful context
|
||||
const lines = body.split("\n");
|
||||
const charBudget = maxTokens * 4;
|
||||
let chars = 0;
|
||||
let i = 0;
|
||||
while (
|
||||
i < lines.length &&
|
||||
chars + (lines[i]?.length ?? 0) + 1 <= charBudget
|
||||
) {
|
||||
chars += (lines[i]?.length ?? 0) + 1;
|
||||
i++;
|
||||
}
|
||||
hunk = lines.slice(0, i).join("\n");
|
||||
}
|
||||
|
||||
return {
|
||||
filename: file.filename,
|
||||
language: file.language,
|
||||
hunk,
|
||||
context: header,
|
||||
};
|
||||
});
|
||||
}
|
||||
Vendored
-17731
File diff suppressed because one or more lines are too long
@@ -0,0 +1,122 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import type { ActionConfig, PRContext, ChunkReview } from "./types.ts";
|
||||
import { getPR, getPRDiff, addReaction, removeReaction, postReviewComment } from "./gitea.ts";
|
||||
import { parseDiff, chunkFile } from "./diff.ts";
|
||||
import { createMcpServer } from "./mcp.ts";
|
||||
import { reviewChunk, aggregateFindings } from "./review.ts";
|
||||
import { formatReview } from "./comment.ts";
|
||||
|
||||
function readConfig(): ActionConfig {
|
||||
return {
|
||||
prompt: process.env.INPUT_PROMPT ?? "Review this pull request",
|
||||
model: process.env.INPUT_MODEL ?? "qwen3.6:35b",
|
||||
contextWindow: parseInt(process.env.INPUT_CONTEXT_WINDOW ?? "4096", 10),
|
||||
maxToolCalls: parseInt(process.env.INPUT_MAX_TOOL_CALLS ?? "10", 10),
|
||||
};
|
||||
}
|
||||
|
||||
function readEvent(): Record<string, unknown> {
|
||||
const path = process.env.GITHUB_EVENT_PATH;
|
||||
if (!path) return {};
|
||||
try {
|
||||
return JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const config = readConfig();
|
||||
const eventName = process.env.GITHUB_EVENT_NAME ?? "";
|
||||
const event = readEvent();
|
||||
|
||||
const repository = process.env.GITHUB_REPOSITORY ?? "";
|
||||
const slashIdx = repository.indexOf("/");
|
||||
if (slashIdx === -1) throw new Error(`Invalid GITHUB_REPOSITORY: ${repository}`);
|
||||
const owner = repository.slice(0, slashIdx);
|
||||
const repo = repository.slice(slashIdx + 1);
|
||||
|
||||
let prNumber: number;
|
||||
let prompt: string;
|
||||
let triggerCommentId: number | null = null;
|
||||
|
||||
if (eventName === "pull_request") {
|
||||
prNumber = event.number as number;
|
||||
prompt = config.prompt;
|
||||
} else if (eventName === "issue_comment") {
|
||||
const issue = event.issue as Record<string, unknown>;
|
||||
if (!issue?.pull_request) {
|
||||
console.log("issue_comment on non-PR issue, skipping");
|
||||
return;
|
||||
}
|
||||
const comment = event.comment as Record<string, unknown>;
|
||||
const commentBody = String(comment?.body ?? "");
|
||||
if (!commentBody.toLowerCase().includes("@shockbot")) {
|
||||
console.log("comment does not mention @shockbot, skipping");
|
||||
return;
|
||||
}
|
||||
prNumber = issue.number as number;
|
||||
triggerCommentId = comment.id as number;
|
||||
prompt = commentBody.replace(/@shockbot\s*/gi, "").trim() || config.prompt;
|
||||
} else {
|
||||
console.log(`Unsupported event: ${eventName}, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
const prData = await getPR(owner, repo, prNumber);
|
||||
const headSha = prData.head?.sha;
|
||||
if (!headSha) throw new Error(`Could not get head SHA for PR #${prNumber}`);
|
||||
|
||||
const pr: PRContext = { owner, repo, prNumber, headSha };
|
||||
|
||||
if (triggerCommentId !== null) {
|
||||
await addReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
}
|
||||
|
||||
try {
|
||||
const rawDiff = await getPRDiff(pr);
|
||||
if (!rawDiff.trim()) {
|
||||
await postReviewComment(pr, "shockbot: no diff found for this PR.");
|
||||
return;
|
||||
}
|
||||
|
||||
const files = parseDiff(rawDiff);
|
||||
if (files.length === 0) {
|
||||
await postReviewComment(pr, "shockbot: no reviewable files in this PR (all files skipped).");
|
||||
return;
|
||||
}
|
||||
|
||||
const chunks = files.flatMap((f) => chunkFile(f, config.contextWindow));
|
||||
const mcpServer = createMcpServer(pr, config.maxToolCalls);
|
||||
|
||||
const results: ChunkReview[] = [];
|
||||
for (const chunk of chunks) {
|
||||
const result = await reviewChunk(
|
||||
chunk,
|
||||
prompt,
|
||||
config.model,
|
||||
config.contextWindow,
|
||||
mcpServer,
|
||||
);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const aggregated = aggregateFindings(results);
|
||||
const body = formatReview(aggregated, files.map((f) => f.filename), config.model);
|
||||
await postReviewComment(pr, body);
|
||||
|
||||
if (triggerCommentId !== null) {
|
||||
await removeReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
await addReaction(pr, triggerCommentId, "+1").catch(() => {});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Review failed:", err);
|
||||
if (triggerCommentId !== null) {
|
||||
await removeReaction(pr, triggerCommentId, "eyes").catch(() => {});
|
||||
await addReaction(pr, triggerCommentId, "-1").catch(() => {});
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,163 @@
|
||||
import { Gitea } from "@go-gitea/sdk.js";
|
||||
import type {
|
||||
Comment,
|
||||
ChangedFile,
|
||||
PullReview,
|
||||
Reaction,
|
||||
} from "@go-gitea/sdk.js";
|
||||
import type { PRContext } from "./types.ts";
|
||||
|
||||
const client = new Gitea({
|
||||
baseUrl: process.env.GITEA_URL,
|
||||
auth: process.env.BOT_TOKEN,
|
||||
});
|
||||
|
||||
export async function getPRDiff(pr: PRContext): Promise<string> {
|
||||
console.log(`Fetching PR #${pr.prNumber} diff for ${pr.owner}/${pr.repo}`);
|
||||
const res = await client.rest.repository.repoDownloadPullDiffOrPatch({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
index: pr.prNumber,
|
||||
diffType: "diff",
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getPRFiles(pr: PRContext): Promise<ChangedFile[]> {
|
||||
console.log(
|
||||
`Fetching PR #${pr.prNumber} changed files for ${pr.owner}/${pr.repo}`,
|
||||
);
|
||||
const res = await client.rest.repository.repoGetPullRequestFiles({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
index: pr.prNumber,
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function postReviewComment(
|
||||
pr: PRContext,
|
||||
body: string,
|
||||
): Promise<Comment> {
|
||||
console.log(
|
||||
`Posting review comment on PR #${pr.prNumber} for ${pr.owner}/${pr.repo}`,
|
||||
);
|
||||
const res = await client.rest.issue.issueCreateComment({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
index: pr.prNumber,
|
||||
body: { body },
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function postInlineComment(
|
||||
pr: PRContext,
|
||||
file: string,
|
||||
line: number,
|
||||
body: string,
|
||||
): Promise<PullReview> {
|
||||
console.log(
|
||||
`Posting inline review comment on PR #${pr.prNumber} for ${pr.owner}/${pr.repo} at ${file}:${line}`,
|
||||
);
|
||||
const res = await client.rest.repository.repoCreatePullReview({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
index: pr.prNumber,
|
||||
body: {
|
||||
event: "COMMENT",
|
||||
commit_id: pr.headSha,
|
||||
comments: [{ path: file, new_position: line, body }],
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function addReaction(
|
||||
pr: PRContext,
|
||||
commentId: number,
|
||||
reaction: string,
|
||||
): Promise<Reaction> {
|
||||
const res = await client.rest.issue.issuePostCommentReaction({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
id: commentId,
|
||||
content: { content: reaction },
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function removeReaction(
|
||||
pr: PRContext,
|
||||
commentId: number,
|
||||
reaction: string,
|
||||
): Promise<void> {
|
||||
await client.rest.issue.issueDeleteCommentReaction({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
id: commentId,
|
||||
content: { content: reaction },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPR(owner: string, repo: string, prNumber: number) {
|
||||
console.log(`Fetching PR #${prNumber} data for ${owner}/${repo}`);
|
||||
const res = await client.rest.repository.repoGetPullRequest({
|
||||
owner,
|
||||
repo,
|
||||
index: prNumber,
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function getFileContents(
|
||||
pr: PRContext,
|
||||
filePath: string,
|
||||
): Promise<string> {
|
||||
console.log(
|
||||
`Fetching contents of ${filePath} at PR #${pr.prNumber} head for ${pr.owner}/${pr.repo}`,
|
||||
);
|
||||
const res = await client.rest.repository.repoGetContents({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
filepath: filePath,
|
||||
ref: pr.headSha,
|
||||
});
|
||||
const entry = Array.isArray(res.data) ? res.data[0] : res.data;
|
||||
if (!entry?.content || entry.type !== "file") return "";
|
||||
// content is base64-encoded; Buffer handles embedded newlines
|
||||
return Buffer.from(entry.content, "base64").toString("utf8");
|
||||
}
|
||||
|
||||
export async function listDir(
|
||||
pr: PRContext,
|
||||
dirPath: string,
|
||||
): Promise<string[]> {
|
||||
console.log(
|
||||
`Listing directory ${dirPath} at PR #${pr.prNumber} head for ${pr.owner}/${pr.repo}`,
|
||||
);
|
||||
const res = await client.rest.repository.repoGetContents({
|
||||
owner: pr.owner,
|
||||
repo: pr.repo,
|
||||
filepath: dirPath,
|
||||
ref: pr.headSha,
|
||||
});
|
||||
const entries = Array.isArray(res.data) ? res.data : [res.data];
|
||||
return entries.map((e) => e.name ?? "").filter(Boolean);
|
||||
}
|
||||
|
||||
// Gitea REST API v1 has no code search endpoint — returns repo names matching the query.
|
||||
// mcp.ts should strongly prefer disk grep (find_symbol) when workspace is available.
|
||||
export async function searchCode(
|
||||
pr: PRContext,
|
||||
query: string,
|
||||
): Promise<string[]> {
|
||||
console.log(
|
||||
`Searching code for "${query}" at PR #${pr.prNumber} head for ${pr.owner}/${pr.repo}`,
|
||||
);
|
||||
const res = await client.rest.repository.repoSearch({
|
||||
q: query,
|
||||
limit: 20,
|
||||
});
|
||||
return (res.data.data ?? []).map((r) => r.full_name ?? "").filter(Boolean);
|
||||
}
|
||||
@@ -1,18 +1 @@
|
||||
import * as core from '@actions/core';
|
||||
|
||||
try {
|
||||
// Get the message input parameter, with a default fallback
|
||||
const message = core.getInput('message') || 'Hello from Pullfrog Action!';
|
||||
|
||||
// Print the message to console and GitHub Actions logs
|
||||
console.log(`🐸 ${message}`);
|
||||
core.info(`Action executed successfully: ${message}`);
|
||||
|
||||
// Set an output for potential use by other actions
|
||||
core.setOutput('message', message);
|
||||
|
||||
} catch (error) {
|
||||
// Handle any errors and fail the action
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
||||
core.setFailed(`Action failed: ${errorMessage}`);
|
||||
}
|
||||
console.log("Hello via Bun!");
|
||||
@@ -0,0 +1,218 @@
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { join, resolve, relative } from "node:path";
|
||||
import type { Tool } from "ollama";
|
||||
import {
|
||||
getFileContents,
|
||||
listDir as giteaListDir,
|
||||
searchCode,
|
||||
} from "./gitea.ts";
|
||||
import type { PRContext } from "./types.ts";
|
||||
|
||||
const MAX_FILE_LINES = 200;
|
||||
const MAX_GREP_RESULTS = 50;
|
||||
|
||||
export const TOOLS: Tool[] = [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "read_file",
|
||||
description: "Read the contents of a file in the repository.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
required: ["path"],
|
||||
properties: {
|
||||
path: {
|
||||
type: "string",
|
||||
description: "Repo-relative path to the file",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_dir",
|
||||
description: "List files and directories at a path in the repository.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
required: ["path"],
|
||||
properties: {
|
||||
path: { type: "string", description: "Repo-relative directory path" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "find_symbol",
|
||||
description:
|
||||
"Search for a symbol, function, or pattern across the repository.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
required: ["symbol"],
|
||||
properties: {
|
||||
symbol: {
|
||||
type: "string",
|
||||
description: "Symbol name or regex pattern to search for",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export class ToolServer {
|
||||
private _callCount = 0;
|
||||
private readonly pr: PRContext;
|
||||
private readonly workspace: string | null;
|
||||
private readonly maxToolCalls: number;
|
||||
|
||||
constructor(pr: PRContext, workspace: string | null, maxToolCalls: number) {
|
||||
this.pr = pr;
|
||||
this.workspace = workspace;
|
||||
this.maxToolCalls = maxToolCalls;
|
||||
}
|
||||
|
||||
get tools(): Tool[] {
|
||||
return TOOLS;
|
||||
}
|
||||
|
||||
get callCount(): number {
|
||||
return this._callCount;
|
||||
}
|
||||
|
||||
get atLimit(): boolean {
|
||||
return this._callCount >= this.maxToolCalls;
|
||||
}
|
||||
|
||||
resetCallCount(): void {
|
||||
this._callCount = 0;
|
||||
}
|
||||
|
||||
async execute(name: string, args: Record<string, unknown>): Promise<string> {
|
||||
if (this._callCount >= this.maxToolCalls) {
|
||||
return "[tool call limit reached — conclude your review now]";
|
||||
}
|
||||
this._callCount++;
|
||||
|
||||
try {
|
||||
switch (name) {
|
||||
case "read_file":
|
||||
return await this.readFile(String(args["path"] ?? ""));
|
||||
case "list_dir":
|
||||
return await this.listDir(String(args["path"] ?? ""));
|
||||
case "find_symbol":
|
||||
return await this.findSymbol(String(args["symbol"] ?? ""));
|
||||
default:
|
||||
return `[unknown tool: ${name}]`;
|
||||
}
|
||||
} catch (err) {
|
||||
return `[error: ${err instanceof Error ? err.message : String(err)}]`;
|
||||
}
|
||||
}
|
||||
|
||||
// Validates path is repo-relative with no traversal or absolute prefix.
|
||||
private safePath(path: string): string | null {
|
||||
if (!path) return null;
|
||||
if (path.startsWith("/") || path.includes("..")) return null;
|
||||
return path.replace(/\/+/g, "/").replace(/\/$/, "");
|
||||
}
|
||||
|
||||
private async readFile(path: string): Promise<string> {
|
||||
const safe = this.safePath(path);
|
||||
if (!safe) return "[invalid path]";
|
||||
|
||||
if (this.workspace) {
|
||||
const full = join(this.workspace, safe);
|
||||
// Verify resolved path stays inside workspace
|
||||
if (!resolve(full).startsWith(resolve(this.workspace)))
|
||||
return "[invalid path]";
|
||||
if (!existsSync(full)) return "[file not found]";
|
||||
const content = readFileSync(full, "utf8");
|
||||
const lines = content.split("\n");
|
||||
const truncated = lines.slice(0, MAX_FILE_LINES).join("\n");
|
||||
return lines.length > MAX_FILE_LINES
|
||||
? `${truncated}\n... (truncated, ${lines.length - MAX_FILE_LINES} lines omitted)`
|
||||
: truncated;
|
||||
}
|
||||
|
||||
return await getFileContents(this.pr, safe);
|
||||
}
|
||||
|
||||
private async listDir(path: string): Promise<string> {
|
||||
const safe = this.safePath(path || ".");
|
||||
if (!safe) return "[invalid path]";
|
||||
|
||||
if (this.workspace) {
|
||||
const full = join(this.workspace, safe);
|
||||
if (!resolve(full).startsWith(resolve(this.workspace)))
|
||||
return "[invalid path]";
|
||||
if (!existsSync(full)) return "[directory not found]";
|
||||
const entries = readdirSync(full, { withFileTypes: true });
|
||||
return entries
|
||||
.map((e) => `${e.isDirectory() ? "d" : "f"} ${e.name}`)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
const entries = await giteaListDir(this.pr, safe);
|
||||
return entries.join("\n");
|
||||
}
|
||||
|
||||
private async findSymbol(symbol: string): Promise<string> {
|
||||
if (!symbol) return "[no symbol provided]";
|
||||
|
||||
if (this.workspace) {
|
||||
try {
|
||||
// execFileSync avoids shell injection — symbol is passed as an argument, not interpolated
|
||||
const out = execFileSync(
|
||||
"grep",
|
||||
[
|
||||
"-r",
|
||||
"-n",
|
||||
"--include=*.ts",
|
||||
"--include=*.tsx",
|
||||
"--include=*.js",
|
||||
"--include=*.jsx",
|
||||
"--include=*.py",
|
||||
"--include=*.go",
|
||||
"--include=*.rs",
|
||||
"--include=*.rb",
|
||||
"--include=*.java",
|
||||
"--include=*.kt",
|
||||
symbol,
|
||||
this.workspace,
|
||||
],
|
||||
{ encoding: "utf8", timeout: 5000, maxBuffer: 256 * 1024 },
|
||||
);
|
||||
const lines = out
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.slice(0, MAX_GREP_RESULTS);
|
||||
// Strip workspace prefix so paths are repo-relative
|
||||
return (
|
||||
lines.map((l) => l.replace(this.workspace! + "/", "")).join("\n") ||
|
||||
"[not found]"
|
||||
);
|
||||
} catch {
|
||||
return "[not found]";
|
||||
}
|
||||
}
|
||||
|
||||
const results = await searchCode(this.pr, symbol);
|
||||
return results.join("\n") || "[not found]";
|
||||
}
|
||||
}
|
||||
|
||||
export function createMcpServer(
|
||||
pr: PRContext,
|
||||
maxToolCalls: number,
|
||||
): ToolServer {
|
||||
console.log("Initializing tool server...");
|
||||
const ws =
|
||||
process.env.GITEA_WORKSPACE ?? process.env.GITHUB_WORKSPACE ?? null;
|
||||
const workspace = ws && existsSync(ws) ? ws : null;
|
||||
return new ToolServer(pr, workspace, maxToolCalls);
|
||||
}
|
||||
Generated
-391
@@ -1,391 +0,0 @@
|
||||
{
|
||||
"name": "action",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "action",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"rolldown": "^0.12.0",
|
||||
"typescript": "^5.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
|
||||
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/exec": "^1.1.1",
|
||||
"@actions/http-client": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/exec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
|
||||
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/io": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
|
||||
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6",
|
||||
"undici": "^5.25.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/io": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
|
||||
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz",
|
||||
"integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.0.4",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
|
||||
"integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
|
||||
"integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
|
||||
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "0.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
|
||||
"integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.4.3",
|
||||
"@emnapi/runtime": "^1.4.3",
|
||||
"@tybys/wasm-util": "^0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-0.12.2.tgz",
|
||||
"integrity": "sha512-Y3Ajye63Z5KymGUwTLaK7Q6YMvycXqNiXtosecgVzjAwMITCmXdzgnWgzzx5UlWHMrDYL4m5zIeXGB5slLwoMA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-0.12.2.tgz",
|
||||
"integrity": "sha512-ZcVuVFEFBXhp00TUNn+EDYs7SGGLQCznvCeuW1XkM8EC2/LfewU/o4WuJK7CBC4iCSktuFGpw7+zLe8D6iinzg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-0.12.2.tgz",
|
||||
"integrity": "sha512-4fjuQHpm3q/Ly4fcqb8Qn49OQc2EQR2scUbQaOzXr7mIn9Zy8NfdRrsVG4/wpYvihIlTEtVx+ku0IZwcUzzZGg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.12.2.tgz",
|
||||
"integrity": "sha512-MGEDaYLzTQ1kpvt13PzOwnd6O668S1mPM/vgi4O9vCfqJNTXZX8SeAg8Z2dQZbMSUyFDBVKGkz23GRTHqkGIsQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.12.2.tgz",
|
||||
"integrity": "sha512-8uiaMe39twyIAw0do1Gc3O2SpQmyL1A/BucFncEB8eU5jtb3BWIM/X+F+eKDU6c+XZ+S1T025dhR1cGg8y20Xw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.12.2.tgz",
|
||||
"integrity": "sha512-GOFSKaMJueaSSODcqI+0Hu79buHYtGV7h2tydIDkSDD20mQuvwOF7jIVd27yNdlXWS9wLObwEu3BNgHmIj1M6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.12.2.tgz",
|
||||
"integrity": "sha512-+hlRURUSiVP0+PFtjoTxUsiy/2NQpbf3DyUyMyl8Nv5+1BxjB6452VY1iFI+RzG4iLNJslcVcI6d6lJQ5zZYfw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-0.12.2.tgz",
|
||||
"integrity": "sha512-9WKIaQSZZ0i9F5msW4I5kEj6ov+TZLteuTqCzI7nYWDBDm7m/hYkOkdIBf41GC8iKFsgVIQO0kRVAT966U8RxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-0.12.2.tgz",
|
||||
"integrity": "sha512-KY7bNrR3Jk6O0ne8LAaAnq9yI6xuKwhr/L2d1lBwrraCCyLHk0UEv4g6PfJkyUx6GfN0gVYuSxFgo9OggycldA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@napi-rs/wasm-runtime": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.12.2.tgz",
|
||||
"integrity": "sha512-XYBXifPk5iNcPUTyV/yB4tlj5nI+fYoe/8CLHjyUG8GS0l8rCD+jKaAv3Jvz2T2erlkjEPqJXmzhUfBscOUo6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-ia32-msvc": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.12.2.tgz",
|
||||
"integrity": "sha512-SV9LqvEc0d4gCLbkezH+UJ2uj2pTw3mwSKhFJEm6ir1lb05W6y9++m67NRs17fhudkFAz8iKlyPxKtZBKzAKOw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.12.2.tgz",
|
||||
"integrity": "sha512-XnRqHx182tyk1M12OMXqLajIj9DZrOUEKSPYrQUSaMCmHHAhULYP6ki240CV16PBWZW4Q1pwQ7YVKYFevRtvKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
|
||||
"integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz",
|
||||
"integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-0.12.2.tgz",
|
||||
"integrity": "sha512-YJYKiYt2O9XytiQ3Na4Kk29avfIXhvK7udB3wAaVaF4kiSsFKE1167tElO/0eD6tjfJXCvwNxwsyYkBJRtsLmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"bin": {
|
||||
"rolldown": "bin/cli.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-darwin-arm64": "0.12.2",
|
||||
"@rolldown/binding-darwin-x64": "0.12.2",
|
||||
"@rolldown/binding-freebsd-x64": "0.12.2",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "0.12.2",
|
||||
"@rolldown/binding-linux-arm64-gnu": "0.12.2",
|
||||
"@rolldown/binding-linux-arm64-musl": "0.12.2",
|
||||
"@rolldown/binding-linux-x64-gnu": "0.12.2",
|
||||
"@rolldown/binding-linux-x64-musl": "0.12.2",
|
||||
"@rolldown/binding-wasm32-wasi": "0.12.2",
|
||||
"@rolldown/binding-win32-arm64-msvc": "0.12.2",
|
||||
"@rolldown/binding-win32-ia32-msvc": "0.12.2",
|
||||
"@rolldown/binding-win32-x64-msvc": "0.12.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
|
||||
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-29
@@ -1,33 +1,15 @@
|
||||
{
|
||||
"name": "action",
|
||||
"version": "0.0.2",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"example": "examples"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rolldown -c rolldown.config.js",
|
||||
"dev": "rolldown -c --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"typescript": "^5.3.0",
|
||||
"rolldown": "^0.12.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pullfrog/pullfrog.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"name": "shockbot",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pullfrog/pullfrog/issues"
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@go-gitea/sdk.js": "^0.2.1",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@types/bun": "latest",
|
||||
"ollama": "^0.6.3"
|
||||
},
|
||||
"homepage": "https://github.com/pullfrog/pullfrog#readme"
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
-267
@@ -1,267 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@actions/core':
|
||||
specifier: ^1.10.1
|
||||
version: 1.11.1
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^20.10.0
|
||||
version: 20.19.11
|
||||
rolldown:
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.2
|
||||
typescript:
|
||||
specifier: ^5.3.0
|
||||
version: 5.9.2
|
||||
|
||||
packages:
|
||||
|
||||
'@actions/core@1.11.1':
|
||||
resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==}
|
||||
|
||||
'@actions/exec@1.1.1':
|
||||
resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==}
|
||||
|
||||
'@actions/http-client@2.2.3':
|
||||
resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==}
|
||||
|
||||
'@actions/io@1.1.3':
|
||||
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
|
||||
|
||||
'@emnapi/core@1.4.5':
|
||||
resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==}
|
||||
|
||||
'@emnapi/runtime@1.4.5':
|
||||
resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==}
|
||||
|
||||
'@emnapi/wasi-threads@1.0.4':
|
||||
resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==}
|
||||
|
||||
'@fastify/busboy@2.1.1':
|
||||
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||
|
||||
'@rolldown/binding-darwin-arm64@0.12.2':
|
||||
resolution: {integrity: sha512-Y3Ajye63Z5KymGUwTLaK7Q6YMvycXqNiXtosecgVzjAwMITCmXdzgnWgzzx5UlWHMrDYL4m5zIeXGB5slLwoMA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@0.12.2':
|
||||
resolution: {integrity: sha512-ZcVuVFEFBXhp00TUNn+EDYs7SGGLQCznvCeuW1XkM8EC2/LfewU/o4WuJK7CBC4iCSktuFGpw7+zLe8D6iinzg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@0.12.2':
|
||||
resolution: {integrity: sha512-4fjuQHpm3q/Ly4fcqb8Qn49OQc2EQR2scUbQaOzXr7mIn9Zy8NfdRrsVG4/wpYvihIlTEtVx+ku0IZwcUzzZGg==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@0.12.2':
|
||||
resolution: {integrity: sha512-MGEDaYLzTQ1kpvt13PzOwnd6O668S1mPM/vgi4O9vCfqJNTXZX8SeAg8Z2dQZbMSUyFDBVKGkz23GRTHqkGIsQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@0.12.2':
|
||||
resolution: {integrity: sha512-8uiaMe39twyIAw0do1Gc3O2SpQmyL1A/BucFncEB8eU5jtb3BWIM/X+F+eKDU6c+XZ+S1T025dhR1cGg8y20Xw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@0.12.2':
|
||||
resolution: {integrity: sha512-GOFSKaMJueaSSODcqI+0Hu79buHYtGV7h2tydIDkSDD20mQuvwOF7jIVd27yNdlXWS9wLObwEu3BNgHmIj1M6A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@0.12.2':
|
||||
resolution: {integrity: sha512-+hlRURUSiVP0+PFtjoTxUsiy/2NQpbf3DyUyMyl8Nv5+1BxjB6452VY1iFI+RzG4iLNJslcVcI6d6lJQ5zZYfw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@0.12.2':
|
||||
resolution: {integrity: sha512-9WKIaQSZZ0i9F5msW4I5kEj6ov+TZLteuTqCzI7nYWDBDm7m/hYkOkdIBf41GC8iKFsgVIQO0kRVAT966U8RxQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@0.12.2':
|
||||
resolution: {integrity: sha512-KY7bNrR3Jk6O0ne8LAaAnq9yI6xuKwhr/L2d1lBwrraCCyLHk0UEv4g6PfJkyUx6GfN0gVYuSxFgo9OggycldA==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@0.12.2':
|
||||
resolution: {integrity: sha512-XYBXifPk5iNcPUTyV/yB4tlj5nI+fYoe/8CLHjyUG8GS0l8rCD+jKaAv3Jvz2T2erlkjEPqJXmzhUfBscOUo6g==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@0.12.2':
|
||||
resolution: {integrity: sha512-SV9LqvEc0d4gCLbkezH+UJ2uj2pTw3mwSKhFJEm6ir1lb05W6y9++m67NRs17fhudkFAz8iKlyPxKtZBKzAKOw==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@0.12.2':
|
||||
resolution: {integrity: sha512-XnRqHx182tyk1M12OMXqLajIj9DZrOUEKSPYrQUSaMCmHHAhULYP6ki240CV16PBWZW4Q1pwQ7YVKYFevRtvKg==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@tybys/wasm-util@0.10.0':
|
||||
resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==}
|
||||
|
||||
'@types/node@20.19.11':
|
||||
resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==}
|
||||
|
||||
rolldown@0.12.2:
|
||||
resolution: {integrity: sha512-YJYKiYt2O9XytiQ3Na4Kk29avfIXhvK7udB3wAaVaF4kiSsFKE1167tElO/0eD6tjfJXCvwNxwsyYkBJRtsLmQ==}
|
||||
hasBin: true
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tunnel@0.0.6:
|
||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
||||
|
||||
typescript@5.9.2:
|
||||
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
undici@5.29.0:
|
||||
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@actions/core@1.11.1':
|
||||
dependencies:
|
||||
'@actions/exec': 1.1.1
|
||||
'@actions/http-client': 2.2.3
|
||||
|
||||
'@actions/exec@1.1.1':
|
||||
dependencies:
|
||||
'@actions/io': 1.1.3
|
||||
|
||||
'@actions/http-client@2.2.3':
|
||||
dependencies:
|
||||
tunnel: 0.0.6
|
||||
undici: 5.29.0
|
||||
|
||||
'@actions/io@1.1.3': {}
|
||||
|
||||
'@emnapi/core@1.4.5':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.0.4
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/runtime@1.4.5':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/wasi-threads@1.0.4':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@fastify/busboy@2.1.1': {}
|
||||
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.4.5
|
||||
'@emnapi/runtime': 1.4.5
|
||||
'@tybys/wasm-util': 0.10.0
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@0.12.2':
|
||||
dependencies:
|
||||
'@napi-rs/wasm-runtime': 0.2.12
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@0.12.2':
|
||||
optional: true
|
||||
|
||||
'@tybys/wasm-util@0.10.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/node@20.19.11':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
rolldown@0.12.2:
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-darwin-arm64': 0.12.2
|
||||
'@rolldown/binding-darwin-x64': 0.12.2
|
||||
'@rolldown/binding-freebsd-x64': 0.12.2
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 0.12.2
|
||||
'@rolldown/binding-linux-arm64-gnu': 0.12.2
|
||||
'@rolldown/binding-linux-arm64-musl': 0.12.2
|
||||
'@rolldown/binding-linux-x64-gnu': 0.12.2
|
||||
'@rolldown/binding-linux-x64-musl': 0.12.2
|
||||
'@rolldown/binding-wasm32-wasi': 0.12.2
|
||||
'@rolldown/binding-win32-arm64-msvc': 0.12.2
|
||||
'@rolldown/binding-win32-ia32-msvc': 0.12.2
|
||||
'@rolldown/binding-win32-x64-msvc': 0.12.2
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
tunnel@0.0.6: {}
|
||||
|
||||
typescript@5.9.2: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
undici@5.29.0:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.1
|
||||
|
||||
zod@3.25.76: {}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { Ollama } from "ollama";
|
||||
import type { Message } from "ollama";
|
||||
import type {
|
||||
DiffChunk,
|
||||
ChunkReview,
|
||||
ReviewResult,
|
||||
AggregatedReview,
|
||||
Finding,
|
||||
} from "./types.ts";
|
||||
import type { ToolServer } from "./mcp.ts";
|
||||
|
||||
const ollama = new Ollama({ host: process.env.OLLAMA_HOST });
|
||||
|
||||
const SYSTEM_PROMPT = `You are a code reviewer. Your job is to find real bugs, security issues, and significant problems in the changed lines below. Do not comment on style, formatting, or personal preference unless they cause functional problems.
|
||||
|
||||
Be concise. Each comment should say what the problem is and why it matters.
|
||||
|
||||
You have tools available to read files, list directories, and search for symbols in the repository. Use them when you need more context to understand the code being reviewed.
|
||||
|
||||
When you are done gathering context and ready to report your findings, you MUST respond with ONLY a valid JSON object in this exact shape:
|
||||
{
|
||||
"issues": [
|
||||
{ "line": <line number>, "severity": "<error|warning|nit>", "comment": "<your comment>" }
|
||||
],
|
||||
"summary": "<one paragraph overall summary>"
|
||||
}
|
||||
|
||||
Do not include any text outside the JSON object.`;
|
||||
|
||||
export function buildReviewPrompt(
|
||||
chunk: DiffChunk,
|
||||
userPrompt: string,
|
||||
): string {
|
||||
return `${userPrompt}
|
||||
|
||||
File: ${chunk.filename} (${chunk.language})
|
||||
Hunk: ${chunk.context}
|
||||
|
||||
${chunk.hunk}`;
|
||||
}
|
||||
|
||||
function parseReviewResult(content: string): ReviewResult | null {
|
||||
// Strip markdown code fences the model may have added despite instructions
|
||||
const stripped = content
|
||||
.replace(/^```(?:json)?\s*/m, "")
|
||||
.replace(/\s*```\s*$/m, "")
|
||||
.trim();
|
||||
try {
|
||||
const parsed = JSON.parse(stripped);
|
||||
if (!Array.isArray(parsed.issues) || typeof parsed.summary !== "string")
|
||||
return null;
|
||||
return parsed as ReviewResult;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function reviewChunk(
|
||||
chunk: DiffChunk,
|
||||
userPrompt: string,
|
||||
model: string,
|
||||
contextWindow: number,
|
||||
mcpServer: ToolServer,
|
||||
): Promise<ChunkReview> {
|
||||
console.log(`Reviewing chunk from ${chunk.filename} with model ${model}...`);
|
||||
mcpServer.resetCallCount();
|
||||
|
||||
const messages: Message[] = [
|
||||
{ role: "system", content: SYSTEM_PROMPT },
|
||||
{ role: "user", content: buildReviewPrompt(chunk, userPrompt) },
|
||||
];
|
||||
|
||||
let forceConclusion = false;
|
||||
let parseFailures = 0;
|
||||
|
||||
while (true) {
|
||||
const response = await ollama.chat({
|
||||
model,
|
||||
messages,
|
||||
tools: forceConclusion ? undefined : mcpServer.tools,
|
||||
format: "json",
|
||||
stream: false,
|
||||
keep_alive: -1,
|
||||
options: { num_ctx: contextWindow, temperature: 0.2 },
|
||||
});
|
||||
|
||||
const msg = response.message;
|
||||
|
||||
// Model called one or more tools
|
||||
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
||||
messages.push({
|
||||
role: "assistant",
|
||||
content: msg.content ?? "",
|
||||
tool_calls: msg.tool_calls,
|
||||
});
|
||||
|
||||
for (const call of msg.tool_calls) {
|
||||
const result = await mcpServer.execute(
|
||||
call.function.name,
|
||||
call.function.arguments as Record<string, unknown>,
|
||||
);
|
||||
messages.push({
|
||||
role: "tool",
|
||||
content: result,
|
||||
tool_name: call.function.name,
|
||||
});
|
||||
}
|
||||
|
||||
if (mcpServer.atLimit) {
|
||||
forceConclusion = true;
|
||||
messages.push({
|
||||
role: "user",
|
||||
content:
|
||||
"You have reached the tool call limit. Respond now with your final JSON review.",
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Model produced content — try to parse as final JSON review
|
||||
const content = (msg.content ?? "").trim();
|
||||
|
||||
if (content) {
|
||||
const result = parseReviewResult(content);
|
||||
if (result) return { chunk, result };
|
||||
|
||||
// Parse failed — give the model one chance to fix it
|
||||
parseFailures++;
|
||||
if (parseFailures <= 1) {
|
||||
messages.push(
|
||||
{ role: "assistant", content },
|
||||
{
|
||||
role: "user",
|
||||
content:
|
||||
"Your response was not valid JSON. Respond with ONLY the JSON review object, no other text.",
|
||||
},
|
||||
);
|
||||
forceConclusion = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Second failure — return raw content as summary rather than crash
|
||||
return {
|
||||
chunk,
|
||||
result: {
|
||||
issues: [],
|
||||
summary: `[parse error] ${content.slice(0, 500)}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Empty response — shouldn't happen, but bail rather than loop forever
|
||||
return {
|
||||
chunk,
|
||||
result: { issues: [], summary: "[empty response from model]" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function aggregateFindings(results: ChunkReview[]): AggregatedReview {
|
||||
console.log(`Aggregating findings from ${results.length} reviewed chunks...`);
|
||||
const findings: Finding[] = results.flatMap(({ chunk, result }) =>
|
||||
result.issues.map((issue) => ({ ...issue, filename: chunk.filename })),
|
||||
);
|
||||
const summaries = results.map((r) => r.result.summary).filter(Boolean);
|
||||
return { findings, summaries };
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { defineConfig } from 'rolldown';
|
||||
|
||||
export default defineConfig({
|
||||
input: './index.ts',
|
||||
output: {
|
||||
file: './bundle.js',
|
||||
format: 'esm'
|
||||
},
|
||||
platform: 'node',
|
||||
target: 'node20',
|
||||
external: (id) => {
|
||||
// Mark all node modules as external
|
||||
return id.includes('node_modules') || id.startsWith('node:');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"action": "opened",
|
||||
"number": 4,
|
||||
"pull_request": {
|
||||
"number": 4,
|
||||
"title": "Add devices section",
|
||||
"draft": false,
|
||||
"head": {
|
||||
"sha": "94f4a680369ab07b076ba5679f05cc157e8c9a9c",
|
||||
"ref": "AddDevicesSection"
|
||||
},
|
||||
"base": {
|
||||
"ref": "master"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"name": "test-repo",
|
||||
"owner": {
|
||||
"login": "ShockVPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"action": "opened",
|
||||
"number": 4,
|
||||
"pull_request": {
|
||||
"number": 4,
|
||||
"head": {
|
||||
"sha": "94f4a680369ab07b076ba5679f05cc157e8c9a9c"
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"name": "test-repo",
|
||||
"owner": {
|
||||
"login": "ShockVPN"
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
-12
@@ -1,17 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./",
|
||||
"rootDir": "./",
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"declaration": false,
|
||||
"sourceMap": false
|
||||
},
|
||||
"include": ["*.ts"],
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
export interface ActionConfig {
|
||||
prompt: string;
|
||||
model: string;
|
||||
contextWindow: number;
|
||||
maxToolCalls: number;
|
||||
}
|
||||
|
||||
export interface EnvConfig {
|
||||
botToken: string;
|
||||
ollamaHost: string;
|
||||
giteaUrl: string;
|
||||
}
|
||||
|
||||
export interface PRContext {
|
||||
owner: string;
|
||||
repo: string;
|
||||
prNumber: number;
|
||||
headSha: string;
|
||||
}
|
||||
|
||||
export type TriggerType = "pr_open" | "issue_comment";
|
||||
|
||||
export interface DiffChunk {
|
||||
filename: string;
|
||||
language: string;
|
||||
hunk: string;
|
||||
context: string;
|
||||
}
|
||||
|
||||
export type MCPToolName = "read_file" | "list_dir" | "find_symbol";
|
||||
|
||||
export type ReviewSeverity = "error" | "warning" | "nit";
|
||||
|
||||
export interface ReviewIssue {
|
||||
line: number;
|
||||
severity: ReviewSeverity;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
export interface ReviewResult {
|
||||
issues: ReviewIssue[];
|
||||
summary: string;
|
||||
}
|
||||
|
||||
export interface ChunkReview {
|
||||
chunk: DiffChunk;
|
||||
result: ReviewResult;
|
||||
}
|
||||
|
||||
export interface Finding extends ReviewIssue {
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export interface AggregatedReview {
|
||||
findings: Finding[];
|
||||
summaries: string[];
|
||||
}
|
||||
Reference in New Issue
Block a user