Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb7de776e4 | |||
| 108b8243a9 | |||
| 59f1e72dec | |||
| 46d95853ae | |||
| e6374a952c |
@@ -1,2 +0,0 @@
|
||||
!examples
|
||||
|
||||
+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.1",
|
||||
"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