Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1abbd7ff41 | |||
| 6a0d9cc244 | |||
| ab468aa32f | |||
| ba61fc6679 | |||
| a226797098 | |||
| c05a47d5d8 | |||
| ec9f69c670 | |||
| 9239b40372 | |||
| f6efe56478 | |||
| 3d47375b24 | |||
| 609b022547 | |||
| ad2680524d | |||
| 85d8885a8b | |||
| 4beee54a60 | |||
| f42dc28da8 | |||
| c5b1e63811 | |||
| 2cacf2ada3 | |||
| bcc312a448 | |||
| 8345d54aa9 | |||
| b9bbc2f671 | |||
| dd84fe4871 |
@@ -0,0 +1,2 @@
|
|||||||
|
!examples
|
||||||
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
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: Verify built files are up to date
|
||||||
|
if: steps.check_tag.outputs.exists == 'false'
|
||||||
|
run: |
|
||||||
|
# Check if there are any uncommitted changes (built files should already be committed via pre-commit hook)
|
||||||
|
if [[ -n $(git status --porcelain) ]]; then
|
||||||
|
echo "❌ Error: There are uncommitted changes. Built files should be committed via pre-commit hook."
|
||||||
|
git status
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ All built files are up to date"
|
||||||
|
|
||||||
|
- 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: ${{ 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
|
||||||
+30
-27
@@ -1,34 +1,37 @@
|
|||||||
# dependencies (bun install)
|
# macOS settings file
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Contains all your dependencies
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# output
|
# Replace as required with your build location
|
||||||
out
|
/build
|
||||||
dist
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# code coverage
|
# Deal with environment files
|
||||||
coverage
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# logs
|
|
||||||
logs
|
|
||||||
_.log
|
|
||||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
|
||||||
|
|
||||||
# dotenv environment variable files
|
|
||||||
.env
|
.env
|
||||||
.env.development.local
|
.env.*
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
.env.local
|
|
||||||
|
|
||||||
# caches
|
# We'll allow an example .env file which can be copied
|
||||||
.eslintcache
|
!.env.example
|
||||||
.cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# IntelliJ based IDEs
|
# Coverage directory used by testing tools
|
||||||
.idea
|
coverage
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
# Visual Studio Code configuration
|
||||||
.DS_Store
|
.vscode/
|
||||||
|
|
||||||
|
|
||||||
|
# npm and yarn debug logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
.next
|
||||||
|
|
||||||
|
# sveltekit
|
||||||
|
/.svelte-kit
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
|
examples
|
||||||
Executable
+6
@@ -0,0 +1,6 @@
|
|||||||
|
# Build the action before committing
|
||||||
|
echo "🔨 Building action..."
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Add the built files to the commit
|
||||||
|
git add index.cjs dist/
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Pullfrog
|
||||||
|
|
||||||
|
A simple GitHub Action that prints a customizable message to the console.
|
||||||
|
|
||||||
+13
-21
@@ -1,28 +1,20 @@
|
|||||||
name: shockbot
|
name: 'Pullfrog Claude Code Action'
|
||||||
description: Ollama-powered code review bot for Gitea
|
description: 'Execute Claude Code with a prompt using Anthropic API'
|
||||||
author: ShockVPN
|
author: 'Pullfrog'
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
prompt:
|
prompt:
|
||||||
description: Review instruction sent to the model
|
description: 'Prompt to send to Claude Code'
|
||||||
|
required: true
|
||||||
|
default: 'Hello from Claude Code!'
|
||||||
|
anthropic_api_key:
|
||||||
|
description: 'Anthropic API key for Claude Code authentication'
|
||||||
required: false
|
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:
|
runs:
|
||||||
using: "node24"
|
using: 'node20'
|
||||||
main: "bootstrap.ts"
|
main: 'index.cjs'
|
||||||
|
|
||||||
# BOT_TOKEN, OLLAMA_HOST, and GITEA_URL must be set as env vars by the consuming workflow.
|
branding:
|
||||||
# GITHUB_EVENT_NAME, GITHUB_EVENT_PATH, and GITHUB_REPOSITORY are set by the runner.
|
icon: 'code'
|
||||||
|
color: 'orange'
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import * as core from "@actions/core";
|
||||||
|
import { executeCommand } from "../utils/exec";
|
||||||
|
import { createTempFile } from "../utils/files";
|
||||||
|
import type { Agent, AgentConfig, AgentResult } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claude Code agent implementation
|
||||||
|
*/
|
||||||
|
export class ClaudeAgent implements Agent {
|
||||||
|
private apiKey: string;
|
||||||
|
|
||||||
|
constructor(config: AgentConfig) {
|
||||||
|
if (!config.apiKey) {
|
||||||
|
throw new Error("Claude agent requires an API key");
|
||||||
|
}
|
||||||
|
this.apiKey = config.apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install Claude Code CLI
|
||||||
|
*/
|
||||||
|
async install(): Promise<void> {
|
||||||
|
core.info("Installing Claude Code...");
|
||||||
|
try {
|
||||||
|
await executeCommand("curl -fsSL https://claude.ai/install.sh | bash -s 1.0.93");
|
||||||
|
core.info("Claude Code installed successfully");
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to install Claude Code: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute Claude Code with the given prompt
|
||||||
|
*/
|
||||||
|
async execute(prompt: string): Promise<AgentResult> {
|
||||||
|
core.info("Executing Claude Code...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a temporary file for the prompt
|
||||||
|
const promptFile = createTempFile(prompt, "prompt.txt");
|
||||||
|
|
||||||
|
// Execute Claude Code with the prompt
|
||||||
|
const command = `$HOME/.local/bin/claude --dangerously-skip-permissions "${promptFile}"`;
|
||||||
|
core.info(`Executing: ${command}`);
|
||||||
|
|
||||||
|
const { stdout, stderr } = await executeCommand(command, {
|
||||||
|
ANTHROPIC_API_KEY: this.apiKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
core.warning(`Claude Code stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
core.info("Claude Code output:");
|
||||||
|
console.log(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info("Claude Code executed successfully");
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
output: stdout,
|
||||||
|
error: stderr || undefined,
|
||||||
|
metadata: {
|
||||||
|
promptFile,
|
||||||
|
command,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: `Failed to execute Claude Code: ${errorMessage}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { ClaudeAgent } from "./claude";
|
||||||
|
import type { Agent, AgentConfig } from "./types";
|
||||||
|
|
||||||
|
export type AgentType = "claude";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating agent instances
|
||||||
|
*/
|
||||||
|
export function createAgent(type: AgentType, config: AgentConfig): Agent {
|
||||||
|
switch (type) {
|
||||||
|
case "claude":
|
||||||
|
return new ClaudeAgent(config);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported agent type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./claude";
|
||||||
|
export * from "./factory";
|
||||||
|
export * from "./types";
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Standard interface for all Pullfrog agents
|
||||||
|
*/
|
||||||
|
export interface Agent {
|
||||||
|
/**
|
||||||
|
* Install the agent and any required dependencies
|
||||||
|
*/
|
||||||
|
install(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the agent with the given prompt
|
||||||
|
* @param prompt The prompt to send to the agent
|
||||||
|
* @param options Additional options specific to the agent
|
||||||
|
*/
|
||||||
|
execute(prompt: string, options?: Record<string, any>): Promise<AgentResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result returned by agent execution
|
||||||
|
*/
|
||||||
|
export interface AgentResult {
|
||||||
|
success: boolean;
|
||||||
|
output?: string;
|
||||||
|
error?: string;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for agent creation
|
||||||
|
*/
|
||||||
|
export interface AgentConfig {
|
||||||
|
apiKey?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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");
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
{
|
|
||||||
"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
@@ -1,65 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
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
@@ -1,122 +0,0 @@
|
|||||||
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,14 @@
|
|||||||
|
import { build } from 'esbuild';
|
||||||
|
|
||||||
|
await build({
|
||||||
|
entryPoints: ['./index.ts'],
|
||||||
|
bundle: true,
|
||||||
|
outfile: './index.cjs',
|
||||||
|
format: 'cjs',
|
||||||
|
platform: 'node',
|
||||||
|
target: 'node20',
|
||||||
|
minify: false,
|
||||||
|
sourcemap: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ Build completed successfully!');
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
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 +1,39 @@
|
|||||||
console.log("Hello via Bun!");
|
import * as core from "@actions/core";
|
||||||
|
import { createAgent } from "./agents/factory";
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Get inputs
|
||||||
|
const prompt = core.getInput("prompt", { required: true });
|
||||||
|
const anthropicApiKey = core.getInput("anthropic_api_key", { required: true });
|
||||||
|
|
||||||
|
if (!anthropicApiKey) {
|
||||||
|
throw new Error("anthropic_api_key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`🐸 Pullfrog Claude Code Action starting...`);
|
||||||
|
core.info(`Prompt: ${prompt}`);
|
||||||
|
|
||||||
|
// Create and install the Claude agent
|
||||||
|
const agent = createAgent("claude", { apiKey: anthropicApiKey });
|
||||||
|
await agent.install();
|
||||||
|
|
||||||
|
// Execute the agent with the prompt
|
||||||
|
const result = await agent.execute(prompt);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error || "Agent execution failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set outputs
|
||||||
|
core.setOutput("status", "success");
|
||||||
|
core.setOutput("prompt", prompt);
|
||||||
|
core.setOutput("output", result.output || "");
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
||||||
|
core.setFailed(`Action failed: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute main function
|
||||||
|
main();
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
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
+601
@@ -0,0 +1,601 @@
|
|||||||
|
{
|
||||||
|
"name": "action",
|
||||||
|
"version": "0.0.2",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "action",
|
||||||
|
"version": "0.0.2",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.10.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"esbuild": "^0.25.9",
|
||||||
|
"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/@esbuild/aix-ppc64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"aix"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/android-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
|
||||||
|
"cpu": [
|
||||||
|
"loong64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
|
||||||
|
"cpu": [
|
||||||
|
"mips64el"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/linux-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"sunos"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/win32-x64": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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/@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/esbuild": {
|
||||||
|
"version": "0.25.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
|
||||||
|
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.25.9",
|
||||||
|
"@esbuild/android-arm": "0.25.9",
|
||||||
|
"@esbuild/android-arm64": "0.25.9",
|
||||||
|
"@esbuild/android-x64": "0.25.9",
|
||||||
|
"@esbuild/darwin-arm64": "0.25.9",
|
||||||
|
"@esbuild/darwin-x64": "0.25.9",
|
||||||
|
"@esbuild/freebsd-arm64": "0.25.9",
|
||||||
|
"@esbuild/freebsd-x64": "0.25.9",
|
||||||
|
"@esbuild/linux-arm": "0.25.9",
|
||||||
|
"@esbuild/linux-arm64": "0.25.9",
|
||||||
|
"@esbuild/linux-ia32": "0.25.9",
|
||||||
|
"@esbuild/linux-loong64": "0.25.9",
|
||||||
|
"@esbuild/linux-mips64el": "0.25.9",
|
||||||
|
"@esbuild/linux-ppc64": "0.25.9",
|
||||||
|
"@esbuild/linux-riscv64": "0.25.9",
|
||||||
|
"@esbuild/linux-s390x": "0.25.9",
|
||||||
|
"@esbuild/linux-x64": "0.25.9",
|
||||||
|
"@esbuild/netbsd-arm64": "0.25.9",
|
||||||
|
"@esbuild/netbsd-x64": "0.25.9",
|
||||||
|
"@esbuild/openbsd-arm64": "0.25.9",
|
||||||
|
"@esbuild/openbsd-x64": "0.25.9",
|
||||||
|
"@esbuild/openharmony-arm64": "0.25.9",
|
||||||
|
"@esbuild/sunos-x64": "0.25.9",
|
||||||
|
"@esbuild/win32-arm64": "0.25.9",
|
||||||
|
"@esbuild/win32-ia32": "0.25.9",
|
||||||
|
"@esbuild/win32-x64": "0.25.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
-12
@@ -1,15 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "shockbot",
|
"name": "action",
|
||||||
"module": "index.ts",
|
"version": "0.0.5",
|
||||||
"type": "module",
|
"main": "index.js",
|
||||||
"private": true,
|
"directories": {
|
||||||
"devDependencies": {
|
"example": "examples"
|
||||||
"@go-gitea/sdk.js": "^0.2.1",
|
|
||||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
||||||
"@types/bun": "latest",
|
|
||||||
"ollama": "^0.6.3"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"scripts": {
|
||||||
"typescript": "^5"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
}
|
|
||||||
|
"build": "node esbuild.config.js",
|
||||||
|
"dev": "node esbuild.config.js --watch",
|
||||||
|
"prepare": "husky"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.10.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"esbuild": "^0.25.9",
|
||||||
|
"husky": "^9.0.0",
|
||||||
|
"typescript": "^5.3.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/pullfrog/pullfrog.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "module",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/pullfrog/pullfrog/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/pullfrog/pullfrog#readme"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+372
@@ -0,0 +1,372 @@
|
|||||||
|
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
|
||||||
|
esbuild:
|
||||||
|
specifier: ^0.25.9
|
||||||
|
version: 0.25.9
|
||||||
|
husky:
|
||||||
|
specifier: ^9.0.0
|
||||||
|
version: 9.1.7
|
||||||
|
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==}
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.9':
|
||||||
|
resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.9':
|
||||||
|
resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.9':
|
||||||
|
resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.9':
|
||||||
|
resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.9':
|
||||||
|
resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openharmony]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.9':
|
||||||
|
resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.9':
|
||||||
|
resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@fastify/busboy@2.1.1':
|
||||||
|
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@types/node@20.19.11':
|
||||||
|
resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==}
|
||||||
|
|
||||||
|
esbuild@0.25.9:
|
||||||
|
resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
husky@9.1.7:
|
||||||
|
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
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'}
|
||||||
|
|
||||||
|
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': {}
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openharmony-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.9':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@fastify/busboy@2.1.1': {}
|
||||||
|
|
||||||
|
'@types/node@20.19.11':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
|
esbuild@0.25.9:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.25.9
|
||||||
|
'@esbuild/android-arm': 0.25.9
|
||||||
|
'@esbuild/android-arm64': 0.25.9
|
||||||
|
'@esbuild/android-x64': 0.25.9
|
||||||
|
'@esbuild/darwin-arm64': 0.25.9
|
||||||
|
'@esbuild/darwin-x64': 0.25.9
|
||||||
|
'@esbuild/freebsd-arm64': 0.25.9
|
||||||
|
'@esbuild/freebsd-x64': 0.25.9
|
||||||
|
'@esbuild/linux-arm': 0.25.9
|
||||||
|
'@esbuild/linux-arm64': 0.25.9
|
||||||
|
'@esbuild/linux-ia32': 0.25.9
|
||||||
|
'@esbuild/linux-loong64': 0.25.9
|
||||||
|
'@esbuild/linux-mips64el': 0.25.9
|
||||||
|
'@esbuild/linux-ppc64': 0.25.9
|
||||||
|
'@esbuild/linux-riscv64': 0.25.9
|
||||||
|
'@esbuild/linux-s390x': 0.25.9
|
||||||
|
'@esbuild/linux-x64': 0.25.9
|
||||||
|
'@esbuild/netbsd-arm64': 0.25.9
|
||||||
|
'@esbuild/netbsd-x64': 0.25.9
|
||||||
|
'@esbuild/openbsd-arm64': 0.25.9
|
||||||
|
'@esbuild/openbsd-x64': 0.25.9
|
||||||
|
'@esbuild/openharmony-arm64': 0.25.9
|
||||||
|
'@esbuild/sunos-x64': 0.25.9
|
||||||
|
'@esbuild/win32-arm64': 0.25.9
|
||||||
|
'@esbuild/win32-ia32': 0.25.9
|
||||||
|
'@esbuild/win32-x64': 0.25.9
|
||||||
|
|
||||||
|
husky@9.1.7: {}
|
||||||
|
|
||||||
|
tunnel@0.0.6: {}
|
||||||
|
|
||||||
|
typescript@5.9.2: {}
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
|
undici@5.29.0:
|
||||||
|
dependencies:
|
||||||
|
'@fastify/busboy': 2.1.1
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
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,22 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"action": "opened",
|
|
||||||
"number": 4,
|
|
||||||
"pull_request": {
|
|
||||||
"number": 4,
|
|
||||||
"head": {
|
|
||||||
"sha": "94f4a680369ab07b076ba5679f05cc157e8c9a9c"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"name": "test-repo",
|
|
||||||
"owner": {
|
|
||||||
"login": "ShockVPN"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+12
-24
@@ -1,29 +1,17 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// Environment setup & latest features
|
"target": "ES2020",
|
||||||
"lib": ["ESNext"],
|
"module": "commonjs",
|
||||||
"target": "ESNext",
|
"lib": ["ES2020"],
|
||||||
"module": "Preserve",
|
"outDir": "./",
|
||||||
"moduleDetection": "force",
|
"rootDir": "./",
|
||||||
"jsx": "react-jsx",
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
// Bundler mode
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"verbatimModuleSyntax": true,
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
// Best practices
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"declaration": false,
|
||||||
"noImplicitOverride": true,
|
"sourceMap": false
|
||||||
|
},
|
||||||
// Some stricter flags (disabled by default)
|
"include": ["*.ts"],
|
||||||
"noUnusedLocals": false,
|
"exclude": ["node_modules", "**/*.test.ts"]
|
||||||
"noUnusedParameters": false,
|
|
||||||
"noPropertyAccessFromIndexSignature": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
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[];
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { exec } from "node:child_process";
|
||||||
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
|
export const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a shell command with optional environment variables
|
||||||
|
*/
|
||||||
|
export async function executeCommand(
|
||||||
|
command: string,
|
||||||
|
env?: Record<string, string>
|
||||||
|
): Promise<{ stdout: string; stderr: string }> {
|
||||||
|
const execEnv = env ? { ...process.env, ...env } : process.env;
|
||||||
|
return execAsync(command, { env: execEnv });
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { mkdtempSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a temporary file with the given content
|
||||||
|
*/
|
||||||
|
export function createTempFile(content: string, filename = "temp.txt"): string {
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), "pullfrog-"));
|
||||||
|
const filePath = join(tempDir, filename);
|
||||||
|
writeFileSync(filePath, content);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user