From d37daee1670c361a14c5f5a2c7c79db102701a1e Mon Sep 17 00:00:00 2001 From: msukkari Date: Wed, 7 May 2025 16:30:46 -0700 Subject: [PATCH 01/15] push review agent implementation --- packages/agents/reviewAgent/.gitignore | 136 +++ packages/agents/reviewAgent/app.ts | 66 ++ .../reviewAgent/nodes/fetch_file_content.ts | 49 + .../nodes/generate_diff_review_prompt.ts | 44 + .../reviewAgent/nodes/generate_pr_reviews.ts | 46 + .../reviewAgent/nodes/github_pr_parser.ts | 59 ++ .../nodes/github_push_pr_reviews.ts | 32 + .../nodes/invoke_diff_review_llm.ts | 33 + packages/agents/reviewAgent/package.json | 37 + packages/agents/reviewAgent/tsconfig.json | 114 +++ packages/agents/reviewAgent/types.ts | 48 + packages/agents/reviewAgent/yarn.lock | 842 ++++++++++++++++++ staging/fly.toml | 28 - 13 files changed, 1506 insertions(+), 28 deletions(-) create mode 100644 packages/agents/reviewAgent/.gitignore create mode 100644 packages/agents/reviewAgent/app.ts create mode 100644 packages/agents/reviewAgent/nodes/fetch_file_content.ts create mode 100644 packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts create mode 100644 packages/agents/reviewAgent/nodes/generate_pr_reviews.ts create mode 100644 packages/agents/reviewAgent/nodes/github_pr_parser.ts create mode 100644 packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts create mode 100644 packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts create mode 100644 packages/agents/reviewAgent/package.json create mode 100644 packages/agents/reviewAgent/tsconfig.json create mode 100644 packages/agents/reviewAgent/types.ts create mode 100644 packages/agents/reviewAgent/yarn.lock delete mode 100644 staging/fly.toml diff --git a/packages/agents/reviewAgent/.gitignore b/packages/agents/reviewAgent/.gitignore new file mode 100644 index 00000000..1170717c --- /dev/null +++ b/packages/agents/reviewAgent/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/packages/agents/reviewAgent/app.ts b/packages/agents/reviewAgent/app.ts new file mode 100644 index 00000000..ba48f082 --- /dev/null +++ b/packages/agents/reviewAgent/app.ts @@ -0,0 +1,66 @@ +import dotenv from 'dotenv'; +import { App, Octokit } from "octokit"; +import { createNodeMiddleware } from "@octokit/webhooks"; +import fs from "fs"; +import http from "http"; +import { WebhookEventDefinition } from "@octokit/webhooks/types"; +import { generate_pr_reviews } from './nodes/generate_pr_reviews.js'; +import { github_push_pr_reviews } from './nodes/github_push_pr_reviews.js'; +import { github_pr_parser } from './nodes/github_pr_parser.js'; + +dotenv.config(); +const appId = process.env.APP_ID as string; +const webhookSecret = process.env.WEBHOOK_SECRET as string; +const privateKeyPath = process.env.PRIVATE_KEY_PATH as string; + +const privateKey = fs.readFileSync(privateKeyPath, "utf8"); + +const app = new App({ + appId: appId, + privateKey: privateKey, + webhooks: { + secret: webhookSecret + }, +}); + +const rules = [ + "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.", + "Do NOT provide any advice that is not actionable or directly related to the changes.", + "Focus solely on offering specific, objective insights based on the given context and refrain from making broad comments about potential impacts on the system or question intentions behind the changes.", + "Keep comments concise and to the point. Every comment must highlight a specific issue and provide a clear and actionable solution to the developer.", + "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"." +] + +async function handlePullRequestOpened({ + octokit, + payload, +}: { + octokit: Octokit; + payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">; +}) { + console.log(`Received a pull request event for #${payload.pull_request.number}`); + + const prPayload = await github_pr_parser(octokit, payload); + const fileDiffReviews = await generate_pr_reviews(prPayload, rules); + await github_push_pr_reviews(app, prPayload, fileDiffReviews); +} + +app.webhooks.on("pull_request.opened", handlePullRequestOpened); +app.webhooks.on("pull_request.synchronize", handlePullRequestOpened); + +app.webhooks.onError((error) => { + console.error(error); +}); + + +const port = 3050; +const host = 'localhost'; +const path = "/api/webhook"; +const localWebhookUrl = `http://${host}:${port}${path}`; + +const middleware = createNodeMiddleware(app.webhooks, { path }); + +http.createServer(middleware).listen(port, () => { + console.log(`Server is listening for events at: ${localWebhookUrl}`); + console.log('Press Ctrl + C to quit.') +}); diff --git a/packages/agents/reviewAgent/nodes/fetch_file_content.ts b/packages/agents/reviewAgent/nodes/fetch_file_content.ts new file mode 100644 index 00000000..2c90633e --- /dev/null +++ b/packages/agents/reviewAgent/nodes/fetch_file_content.ts @@ -0,0 +1,49 @@ +import { sourcebot_context, sourcebot_pr_payload } from "../types.js"; +import { z } from "zod"; + +// TODO: use original Sourcebot schemas instead of redefining here +const fileSourceResponseSchema = z.object({ + source: z.string(), + language: z.string(), +}); + +const base64Decode = (base64: string): string => { + const binString = atob(base64); + return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString(); +} + +export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { + console.log("Executing fetch_file_content"); + + const fileSourceRequest = { + fileName: filename, + repository: pr_payload.hostDomain + "/" + pr_payload.owner + "/" + pr_payload.repo, + } + console.log(JSON.stringify(fileSourceRequest, null, 2)); + + const response = await fetch('http://localhost:3000/api/source', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Org-Domain': '~' + }, + body: JSON.stringify(fileSourceRequest) + }); + + if (!response.ok) { + throw new Error(`Failed to fetch file content: ${response.statusText}`); + } + + const responseData = await response.json(); + const fileSourceResponse = fileSourceResponseSchema.parse(responseData); + const fileContent = base64Decode(fileSourceResponse.source); + + const fileContentContext: sourcebot_context = { + type: "file_content", + description: `The content of the file ${filename}`, + context: fileContent, + } + + console.log("Completed fetch_file_content"); + return fileContentContext; +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts b/packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts new file mode 100644 index 00000000..16c22706 --- /dev/null +++ b/packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts @@ -0,0 +1,44 @@ +import { sourcebot_diff, sourcebot_context, sourcebot_diff_review_schema } from "../types.js"; +import { zodToJsonSchema } from "zod-to-json-schema"; + +export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => { + console.log("Executing generate_diff_review_prompt"); + + const prompt = ` + You are an expert software engineer that excells at reviewing code changes. Given the input, additional context, and rules defined below, review the code changes and provide a detailed review. The review you provide + must conform to all of the rules defined below. The output format of your review must conform to the output format defined below. + + # Input + + The input is the old and new code snippets, which represent a single hunk from a git diff. The old code snippet is the code before the changes were made, and the new code snippet is the code after the changes were made. Each code snippet + is a sequence of lines each with a line number. + + ## Old Code Snippet + + \`\`\` + ${diff.oldSnippet} + \`\`\` + + ## New Code Snippet + + \`\`\` + ${diff.newSnippet} + \`\`\` + + # Additional Context + + ${context.map(c => `${c.type}: ${c.description}\n\n${c.context}`).join("\n\n----------------------\n\n")} + + # Rules + + - ${rules.join("\n- ")} + + # Output Format (JSON Schema) + The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with + the JSON object in a markdown code block. + ${JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)} + `; + + console.log("Completed generate_diff_review_prompt"); + return prompt; +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts b/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts new file mode 100644 index 00000000..e3666252 --- /dev/null +++ b/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts @@ -0,0 +1,46 @@ +import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "../types.js"; +import { generate_diff_review_prompt } from "./generate_diff_review_prompt.js"; +import { invoke_diff_review_llm } from "./invoke_diff_review_llm.js"; +import { fetch_file_content } from "./fetch_file_content.js"; + +export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { + console.log("Executing generate_pr_reviews"); + + const file_diff_reviews: sourcebot_file_diff_review[] = []; + for (const file_diff of pr_payload.file_diffs) { + const reviews: sourcebot_diff_review[] = []; + + for (const diff of file_diff.diffs) { + const fileContentContext = await fetch_file_content(pr_payload, file_diff.to); + const context: sourcebot_context[] = [ + { + type: "pr_title", + description: "The title of the pull request", + context: pr_payload.title, + }, + { + type: "pr_description", + description: "The description of the pull request", + context: pr_payload.description, + }, + fileContentContext, + ]; + + const prompt = await generate_diff_review_prompt(diff, context, rules); + console.log(prompt); + + const diffReview = await invoke_diff_review_llm(prompt, file_diff.to); + reviews.push(diffReview); + } + + if (reviews.length > 0) { + file_diff_reviews.push({ + filename: file_diff.to, + reviews: reviews, + }); + } + } + + console.log("Completed generate_pr_reviews"); + return file_diff_reviews; +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/github_pr_parser.ts b/packages/agents/reviewAgent/nodes/github_pr_parser.ts new file mode 100644 index 00000000..9cb8e990 --- /dev/null +++ b/packages/agents/reviewAgent/nodes/github_pr_parser.ts @@ -0,0 +1,59 @@ +import { sourcebot_pr_payload, sourcebot_file_diff, sourcebot_diff } from "../types.js" +import { WebhookEventDefinition } from "@octokit/webhooks/types"; +import parse from "parse-diff"; +import { Octokit } from "octokit"; + +export const github_pr_parser = async (octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">): Promise => { + console.log("Executing github_pr_parser"); + + const diff = await octokit.request(payload.pull_request.patch_url); + const parsedDiff: parse.File[] = parse(diff.data); + + const sourcebotFileDiffs: (sourcebot_file_diff | null)[] = parsedDiff.map((file) => { + if (!file.from || !file.to) { + console.log(`Skipping file due to missing from (${file.from}) or to (${file.to})`) + return null; + } + + const diffs: sourcebot_diff[] = file.chunks.map((chunk) => { + let oldSnippet = ""; + let newSnippet = ""; + + for (const change of chunk.changes) { + if (change.type === "normal") { + oldSnippet += change.ln1 + ":" + change.content; + newSnippet += change.ln2 + ":" + change.content; + } else if (change.type === "add") { + newSnippet += change.ln + ":" + change.content; + } else if (change.type === "del") { + oldSnippet += change.ln + ":" + change.content; + } + } + + return { + oldSnippet: oldSnippet, + newSnippet: newSnippet, + } + }); + + return { + from: file.from, + to: file.to, + diffs: diffs, + } + }); + const filteredSourcebotFileDiffs: sourcebot_file_diff[] = sourcebotFileDiffs.filter((file) => file !== null) as sourcebot_file_diff[]; + + console.log("Completed github_pr_parser"); + return { + title: payload.pull_request.title, + description: payload.pull_request.body ?? "", + hostDomain: "github.com", + owner: payload.repository.owner.login, + repo: payload.repository.name, + file_diffs: filteredSourcebotFileDiffs, + number: payload.pull_request.number, + head_sha: payload.pull_request.head.sha, + installation_id: payload.installation!.id, + } +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts b/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts new file mode 100644 index 00000000..38b77144 --- /dev/null +++ b/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts @@ -0,0 +1,32 @@ +import { App } from "octokit"; +import { sourcebot_pr_payload, sourcebot_file_diff_review } from "../types.js"; + +export const github_push_pr_reviews = async (app: App, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { + console.log("Executing github_push_pr_reviews"); + + const installationId = pr_payload.installation_id; + const installation = await app.getInstallationOctokit(installationId); + + for (const file_diff_review of file_diff_reviews) { + for (const review of file_diff_review.reviews) { + await installation.rest.pulls.createReviewComment({ + owner: pr_payload.owner, + repo: pr_payload.repo, + pull_number: pr_payload.number, + body: review.review, + path: file_diff_review.filename, + commit_id: pr_payload.head_sha, + side: "RIGHT", + ...(review.line_start === review.line_end + ? { line: review.line_start } + : { + start_line: review.line_start, + line: review.line_end, + start_side: "RIGHT", + }), + }); + } + } + + console.log("Completed github_push_pr_reviews"); +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts b/packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts new file mode 100644 index 00000000..38e748d3 --- /dev/null +++ b/packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts @@ -0,0 +1,33 @@ +import OpenAI from "openai"; +import dotenv from "dotenv"; +import { sourcebot_diff_review_schema, sourcebot_diff_review } from "../types.js"; + +dotenv.config(); +const openai = new OpenAI({ + apiKey: process.env.OPENAI_API_KEY, +}); + +export const invoke_diff_review_llm = async (prompt: string, filename: string): Promise => { + console.log("Executing invoke_diff_review_llm"); + + try { + const completion = await openai.chat.completions.create({ + model: "gpt-4.1-mini", + messages: [ + { role: "system", content: prompt } + ] + }); + + const openaiResponse = completion.choices[0].message.content; + console.log("OpenAI response: ", openaiResponse); + + const diffReviewJson = JSON.parse(openaiResponse || ''); + const diffReview = sourcebot_diff_review_schema.parse(diffReviewJson); + + console.log("Completed invoke_diff_review_llm"); + return diffReview; + } catch (error) { + console.error('Error calling OpenAI:', error); + throw error; + } +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/package.json b/packages/agents/reviewAgent/package.json new file mode 100644 index 00000000..970d6d33 --- /dev/null +++ b/packages/agents/reviewAgent/package.json @@ -0,0 +1,37 @@ +{ + "name": "review-agent", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "build": "tsc", + "start": "node dist/app.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sourcebot-dev/review-agent.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/sourcebot-dev/review-agent/issues" + }, + "homepage": "https://github.com/sourcebot-dev/review-agent#readme", + "dependencies": { + "@octokit/webhooks": "^13.8.2", + "@octokit/webhooks-definitions": "octokit/webhooks", + "dotenv": "^16.5.0", + "octokit": "^4.1.3", + "openai": "^4.97.0", + "parse-diff": "^0.11.1", + "zod": "^3.24.4", + "zod-to-json-schema": "^3.24.5" + }, + "devDependencies": { + "@types/node": "^22.15.8", + "smee-client": "^3.1.1", + "typescript": "^5.8.3" + } +} diff --git a/packages/agents/reviewAgent/tsconfig.json b/packages/agents/reviewAgent/tsconfig.json new file mode 100644 index 00000000..e2e61a95 --- /dev/null +++ b/packages/agents/reviewAgent/tsconfig.json @@ -0,0 +1,114 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "libReplacement": true, /* Enable lib replacement. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "NodeNext", /* Specify what module code is generated. */ + "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/agents/reviewAgent/types.ts b/packages/agents/reviewAgent/types.ts new file mode 100644 index 00000000..1aed52b5 --- /dev/null +++ b/packages/agents/reviewAgent/types.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; + +export const sourcebot_diff_schema = z.object({ + oldSnippet: z.string(), + newSnippet: z.string(), +}); +export type sourcebot_diff = z.infer; + +export const sourcebot_file_diff_schema = z.object({ + from: z.string(), + to: z.string(), + diffs: z.array(sourcebot_diff_schema), +}); +export type sourcebot_file_diff = z.infer; + +export const sourcebot_pr_payload_schema = z.object({ + title: z.string(), + description: z.string(), + hostDomain: z.string(), + owner: z.string(), + repo: z.string(), + file_diffs: z.array(sourcebot_file_diff_schema), + number: z.number(), + head_sha: z.string(), + installation_id: z.number(), +}); +export type sourcebot_pr_payload = z.infer; + +export const sourcebot_context_schema = z.object({ + type: z.enum(["pr_title", "pr_description", "pr_summary", "comment_chains", "file_content"]), + description: z.string().optional(), + context: z.string(), +}); +export type sourcebot_context = z.infer; + +export const sourcebot_diff_review_schema = z.object({ + line_start: z.number(), + line_end: z.number(), + review: z.string(), +}); +export type sourcebot_diff_review = z.infer; + +export const sourcebot_file_diff_review_schema = z.object({ + filename: z.string(), + reviews: z.array(sourcebot_diff_review_schema), +}); +export type sourcebot_file_diff_review = z.infer; + diff --git a/packages/agents/reviewAgent/yarn.lock b/packages/agents/reviewAgent/yarn.lock new file mode 100644 index 00000000..52f37e65 --- /dev/null +++ b/packages/agents/reviewAgent/yarn.lock @@ -0,0 +1,842 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@octokit/app@npm:^15.1.6": + version: 15.1.6 + resolution: "@octokit/app@npm:15.1.6" + dependencies: + "@octokit/auth-app": "npm:^7.2.1" + "@octokit/auth-unauthenticated": "npm:^6.1.3" + "@octokit/core": "npm:^6.1.5" + "@octokit/oauth-app": "npm:^7.1.6" + "@octokit/plugin-paginate-rest": "npm:^12.0.0" + "@octokit/types": "npm:^14.0.0" + "@octokit/webhooks": "npm:^13.6.1" + checksum: 10c0/865098127d85cc58adda78c9b86cae58c502410a0d9c23d6eeabe633bc6c5e7d5f8901f243c519a0ce72342a8d08d73f120b32a864a8f6e4eeb3ad33326b6825 + languageName: node + linkType: hard + +"@octokit/auth-app@npm:^7.2.1": + version: 7.2.1 + resolution: "@octokit/auth-app@npm:7.2.1" + dependencies: + "@octokit/auth-oauth-app": "npm:^8.1.4" + "@octokit/auth-oauth-user": "npm:^5.1.4" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + toad-cache: "npm:^3.7.0" + universal-github-app-jwt: "npm:^2.2.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/da8890ad8ae554697a4d58427d7633c8d4d6f7acbfc14e98345b85035cca6773c393f5db5767b455dfc8de9bc5bac4da2d24e443cd48e701de11dd52fa128385 + languageName: node + linkType: hard + +"@octokit/auth-oauth-app@npm:^8.1.3, @octokit/auth-oauth-app@npm:^8.1.4": + version: 8.1.4 + resolution: "@octokit/auth-oauth-app@npm:8.1.4" + dependencies: + "@octokit/auth-oauth-device": "npm:^7.1.5" + "@octokit/auth-oauth-user": "npm:^5.1.4" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/2e50711be9ee6c3bf85cf1d58a1d8ae56c36eb894dcfdacabda92b9454b2c49bccc56fb1a989d3e56625885f1ef36278cc01f65c01cde6e3b505b363eac66cc6 + languageName: node + linkType: hard + +"@octokit/auth-oauth-device@npm:^7.1.5": + version: 7.1.5 + resolution: "@octokit/auth-oauth-device@npm:7.1.5" + dependencies: + "@octokit/oauth-methods": "npm:^5.1.5" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/f93a67473c58e8b855af15c7300a0937c9d3c6ea898e5e810f2f9762100c6cee22ae52e18c7f926b0e4c6c52573751d4c4ab6294c44116bed2d9fdde5cbdd35d + languageName: node + linkType: hard + +"@octokit/auth-oauth-user@npm:^5.1.3, @octokit/auth-oauth-user@npm:^5.1.4": + version: 5.1.4 + resolution: "@octokit/auth-oauth-user@npm:5.1.4" + dependencies: + "@octokit/auth-oauth-device": "npm:^7.1.5" + "@octokit/oauth-methods": "npm:^5.1.5" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/2bb597b2e50fbd5c03bee3a276efa72b03105aff1d9e243c18a71b72d9fe4ad4b1cee52c2df3e022c4169c7c1d37ab14d05455d5de982528914af965e14b763a + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^5.0.0": + version: 5.1.2 + resolution: "@octokit/auth-token@npm:5.1.2" + checksum: 10c0/bd4952571d9c559ede1f6ef8f7756900256d19df0180db04da88886a05484c7e6a4397611422e4804465a82addc8c2daa21d0bb4f450403552ee81041a4046d1 + languageName: node + linkType: hard + +"@octokit/auth-unauthenticated@npm:^6.1.2, @octokit/auth-unauthenticated@npm:^6.1.3": + version: 6.1.3 + resolution: "@octokit/auth-unauthenticated@npm:6.1.3" + dependencies: + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/60639987fa30e68c4ecd98dd9f1604b3f4368384979cdfa2ab9ea2d0b04c50b42a990e67167cf2a0098205a994b3a80d76b23bb0de4c8c956a898b977f5ce7d0 + languageName: node + linkType: hard + +"@octokit/core@npm:^6.1.4, @octokit/core@npm:^6.1.5": + version: 6.1.5 + resolution: "@octokit/core@npm:6.1.5" + dependencies: + "@octokit/auth-token": "npm:^5.0.0" + "@octokit/graphql": "npm:^8.2.2" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + before-after-hook: "npm:^3.0.2" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/c89ea754cc33da740fdd69fadb971b4b65c89971bba4e8ad545d3ea7aba79759ee3e195c3b72e7df78f14b8b1d392bddc56e7c385d48b5272319ea6a0246ac7c + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^10.1.4": + version: 10.1.4 + resolution: "@octokit/endpoint@npm:10.1.4" + dependencies: + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/bf7cca71a05dc4751df658588e32642e59c98768e7509521226b997ea4837e2d16efd35c391231c76d888226f4daf80e6a9f347dee01a69f490253654dada581 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^8.2.2": + version: 8.2.2 + resolution: "@octokit/graphql@npm:8.2.2" + dependencies: + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/29cd5af5ed04e416d28798a11907ab861dc73c0af47f8c9c3f4183d81d2e77d88228643825538acc81e7015f78d891f84107929019a673b06a6b38ccea6a24e0 + languageName: node + linkType: hard + +"@octokit/oauth-app@npm:^7.1.6": + version: 7.1.6 + resolution: "@octokit/oauth-app@npm:7.1.6" + dependencies: + "@octokit/auth-oauth-app": "npm:^8.1.3" + "@octokit/auth-oauth-user": "npm:^5.1.3" + "@octokit/auth-unauthenticated": "npm:^6.1.2" + "@octokit/core": "npm:^6.1.4" + "@octokit/oauth-authorization-url": "npm:^7.1.1" + "@octokit/oauth-methods": "npm:^5.1.4" + "@types/aws-lambda": "npm:^8.10.83" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/75264f6cb9336baf815bb85ea450d69c5d33eb70754065c9c5307b10d0efec2d76bb17c7788346dfb8b5e4ad0205e6df841b9c709eaf077390bba793787841f5 + languageName: node + linkType: hard + +"@octokit/oauth-authorization-url@npm:^7.0.0, @octokit/oauth-authorization-url@npm:^7.1.1": + version: 7.1.1 + resolution: "@octokit/oauth-authorization-url@npm:7.1.1" + checksum: 10c0/a2723dde503693d4b0793bc43988d7445bdbd1a4e4994f4e94e635816c87b12a3058609eb5982d91783ddf6cdf663ccdb954b5d05efdc59cb66bc0456d7ba370 + languageName: node + linkType: hard + +"@octokit/oauth-methods@npm:^5.1.4, @octokit/oauth-methods@npm:^5.1.5": + version: 5.1.5 + resolution: "@octokit/oauth-methods@npm:5.1.5" + dependencies: + "@octokit/oauth-authorization-url": "npm:^7.0.0" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/2b1c5f9ef0fcdb2f19fd864303a67bdda296fcb2cc058efc51bf3fbb3cb582475a5532eab05bd7a57277607730fa4a7796431564693fffdb9c4cb2b471484a5c + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^25.0.0": + version: 25.0.0 + resolution: "@octokit/openapi-types@npm:25.0.0" + checksum: 10c0/59c9e5998e08cecec155b776c93d8f6f88ab1a812add61cc65f3de8f3744201565545eac308083d18c9fa330a4381a27bcd771a311ac0348d3590a00f333f233 + languageName: node + linkType: hard + +"@octokit/openapi-webhooks-types@npm:11.0.0": + version: 11.0.0 + resolution: "@octokit/openapi-webhooks-types@npm:11.0.0" + checksum: 10c0/cea59ba6b976a242fa4e6bedfab7e6fc3437381557d2c1876ca3aea5f6d4231559a378f9bc35e09593cb2d466afb4a2415be66b960f3e0a38b65b8b9f22ff90e + languageName: node + linkType: hard + +"@octokit/plugin-paginate-graphql@npm:^5.2.4": + version: 5.2.4 + resolution: "@octokit/plugin-paginate-graphql@npm:5.2.4" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/30601cf11d78e5683098d68fbff071a3d20b1e23758f40b1f43fbc93f69b3d0e07f2c9aaaaee113e586af7f604e809ba93702d932b1a4ea65998c7ab39a40a54 + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^12.0.0": + version: 12.0.0 + resolution: "@octokit/plugin-paginate-rest@npm:12.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/4ed04e8c111ac8cfb0692f917fe09ff6484b7436f0605c661e8051c6fb281c0260c9b067c1422529b948e53b22221b0f7664e2d10a28dcd9db14465c02c7182f + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:14.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/c3f26c5277d4aa0c898d8fdbf84326943ea80496e6f60ae34834415384ab629e1e3702d1ed82d40c31a7370edfcb5fa9fe434b0357b302b3be309879bad1d4e6 + languageName: node + linkType: hard + +"@octokit/plugin-retry@npm:^7.2.1": + version: 7.2.1 + resolution: "@octokit/plugin-retry@npm:7.2.1" + dependencies: + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/0cac8aa32bfe9612cbaba36f51382c69c882ef7927e1deb4b166fe664959887e952205adbf8b9ff461e0128ac3e1bd807ca58e38041a55ddb3214397584f0406 + languageName: node + linkType: hard + +"@octokit/plugin-throttling@npm:^10.0.0": + version: 10.0.0 + resolution: "@octokit/plugin-throttling@npm:10.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ^6.1.3 + checksum: 10c0/60294f89bcc03b034fce557dffcb0dba1b02d1fa1834b3b4f542f8635750df4bd23fc143f7c71f802a22a1aaed089c5da77979e6803abe11908b1c91aa1ba419 + languageName: node + linkType: hard + +"@octokit/request-error@npm:^6.1.7, @octokit/request-error@npm:^6.1.8": + version: 6.1.8 + resolution: "@octokit/request-error@npm:6.1.8" + dependencies: + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/02aa5bfebb5b1b9e152558b4a6f4f7dcb149b41538778ffe0fce3395fd0da5c0862311a78e94723435667581b2a58a7cefa458cf7aa19ae2948ae419276f7ee1 + languageName: node + linkType: hard + +"@octokit/request@npm:^9.2.3": + version: 9.2.3 + resolution: "@octokit/request@npm:9.2.3" + dependencies: + "@octokit/endpoint": "npm:^10.1.4" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + fast-content-type-parse: "npm:^2.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/96067fc9a5f30f2faa00f08015309930561c3ea0536b543e1c91c475f965eabc81c48fc27d401681ebdb3f6c1acc5d46eaa69163ab98b0faa08be1c02ea5b684 + languageName: node + linkType: hard + +"@octokit/types@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/types@npm:14.0.0" + dependencies: + "@octokit/openapi-types": "npm:^25.0.0" + checksum: 10c0/c82da635fe99f265dbef7bf954d45a23ca7ce9c6fc9a8478c247b5435799e5d0eab3ff42f085785ee0882b2de293cab0ab831b379c66f41d00b78412df850ba4 + languageName: node + linkType: hard + +"@octokit/webhooks-definitions@octokit/webhooks": + version: 0.0.0-development + resolution: "@octokit/webhooks-definitions@https://github.com/octokit/webhooks.git#commit=e5304814003f52b2697310a52f9d6bfc07632bd3" + checksum: 10c0/ff90b65b388b3f5ce80a0aa47c6ceb98578f8df6364799db5fc17ffadbbe7e533a7f2c548b279ebd4780e00a2cdcc5f07093b0add975d014af2722496413f75f + languageName: node + linkType: hard + +"@octokit/webhooks-methods@npm:^5.1.1": + version: 5.1.1 + resolution: "@octokit/webhooks-methods@npm:5.1.1" + checksum: 10c0/837aa49dbc7f8bc01448f4eaea32cfb0c1cbfa0b4ac570f7bcda385c71f43e4be05e91a3fb63708448410b27e246570fde42056b6b87d755154d84eff5a6500c + languageName: node + linkType: hard + +"@octokit/webhooks@npm:^13.6.1, @octokit/webhooks@npm:^13.8.2": + version: 13.8.2 + resolution: "@octokit/webhooks@npm:13.8.2" + dependencies: + "@octokit/openapi-webhooks-types": "npm:11.0.0" + "@octokit/request-error": "npm:^6.1.7" + "@octokit/webhooks-methods": "npm:^5.1.1" + checksum: 10c0/5053ab9cff8afe4cd6e7d7a41af3f1b0c0ccc5058482577aa82bb8e5003f3002896c68bd59d7da363204c2d6baa4cca73ed684bb7a5a9286ca4b43172cd1fe9e + languageName: node + linkType: hard + +"@types/aws-lambda@npm:^8.10.83": + version: 8.10.149 + resolution: "@types/aws-lambda@npm:8.10.149" + checksum: 10c0/9ef41214a1b69fa30d89cab575429793f1ccc49cfe3a7aa75228aef31a202bb6d2c306a5b33def61ef29e0d0a1a324412bf6a4fa5a615e61117685584a394047 + languageName: node + linkType: hard + +"@types/node-fetch@npm:^2.6.4": + version: 2.6.12 + resolution: "@types/node-fetch@npm:2.6.12" + dependencies: + "@types/node": "npm:*" + form-data: "npm:^4.0.0" + checksum: 10c0/7693acad5499b7df2d1727d46cff092a63896dc04645f36b973dd6dd754a59a7faba76fcb777bdaa35d80625c6a9dd7257cca9c401a4bab03b04480cda7fd1af + languageName: node + linkType: hard + +"@types/node@npm:*": + version: 22.15.9 + resolution: "@types/node@npm:22.15.9" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/f294ba23e441b4f919394d0e4c80f3f33e858e6f1db0561317148da2db3d3c67809fd1a38de481328933b0d5c6174db40cb6ab65f12b2cf3309df7f07a8e46f6 + languageName: node + linkType: hard + +"@types/node@npm:^18.11.18": + version: 18.19.93 + resolution: "@types/node@npm:18.19.93" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/f4d1ca1a07b88bde6ae46b1f4cf3ace8db98406283f0caa18b22f642a6f4361d801e7efd44ee13d818114bb33b2a56e1a9cd97321b58fd3d8147351fabf4be82 + languageName: node + linkType: hard + +"@types/node@npm:^22.15.8": + version: 22.15.8 + resolution: "@types/node@npm:22.15.8" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/980ddda0c98ce53689d52e8c20fbc7f7a42280780c4617892872c3bc2c342ef7b0bae5ff41eec536981a7d39fd7a9af71f5ce8849b36a2084d3945b7bc438d05 + languageName: node + linkType: hard + +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + +"agentkeepalive@npm:^4.2.1": + version: 4.6.0 + resolution: "agentkeepalive@npm:4.6.0" + dependencies: + humanize-ms: "npm:^1.2.1" + checksum: 10c0/235c182432f75046835b05f239708107138a40103deee23b6a08caee5136873709155753b394ec212e49e60e94a378189562cb01347765515cff61b692c69187 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + +"before-after-hook@npm:^3.0.2": + version: 3.0.2 + resolution: "before-after-hook@npm:3.0.2" + checksum: 10c0/dea640f9e88a1085372c9bcc974b7bf379267490693da92ec102a7d8b515dd1e95f00ef575a146b83ca638104c57406c3427d37bdf082f602dde4b56d05bba14 + languageName: node + linkType: hard + +"bottleneck@npm:^2.15.3": + version: 2.19.5 + resolution: "bottleneck@npm:2.19.5" + checksum: 10c0/b0f72e45b2e0f56a21ba720183f16bef8e693452fb0495d997fa354e42904353a94bd8fd429868e6751bc85e54b6755190519eed5a0ae0a94a5185209ae7c6d0 + languageName: node + linkType: hard + +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + +"commander@npm:^12.0.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + +"dotenv@npm:^16.5.0": + version: 16.5.0 + resolution: "dotenv@npm:16.5.0" + checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9 + languageName: node + linkType: hard + +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.1.0": + version: 2.1.0 + resolution: "es-set-tostringtag@npm:2.1.0" + dependencies: + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.6" + has-tostringtag: "npm:^1.0.2" + hasown: "npm:^2.0.2" + checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af + languageName: node + linkType: hard + +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + +"eventsource-parser@npm:^3.0.1": + version: 3.0.1 + resolution: "eventsource-parser@npm:3.0.1" + checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0 + languageName: node + linkType: hard + +"eventsource@npm:^3.0.5": + version: 3.0.6 + resolution: "eventsource@npm:3.0.6" + dependencies: + eventsource-parser: "npm:^3.0.1" + checksum: 10c0/074d865ea1c7e29e3243f85a13306e89fca2d775b982dca03fa6bfa75c56827fa89cf1ab9e730db24bd6b104cbdcae074f2b37ba498874e9dd9710fbff4979bb + languageName: node + linkType: hard + +"fast-content-type-parse@npm:^2.0.0": + version: 2.0.1 + resolution: "fast-content-type-parse@npm:2.0.1" + checksum: 10c0/e5ff87d75a35ae4cf377df1dca46ec49e7abbdc8513689676ecdef548b94900b50e66e516e64470035d79b9f7010ef15d98c24d8ae803a881363cc59e0715e19 + languageName: node + linkType: hard + +"form-data-encoder@npm:1.7.2": + version: 1.7.2 + resolution: "form-data-encoder@npm:1.7.2" + checksum: 10c0/56553768037b6d55d9de524f97fe70555f0e415e781cb56fc457a68263de3d40fadea2304d4beef2d40b1a851269bd7854e42c362107071892cb5238debe9464 + languageName: node + linkType: hard + +"form-data@npm:^4.0.0": + version: 4.0.2 + resolution: "form-data@npm:4.0.2" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + mime-types: "npm:^2.1.12" + checksum: 10c0/e534b0cf025c831a0929bf4b9bbe1a9a6b03e273a8161f9947286b9b13bf8fb279c6944aae0070c4c311100c6d6dbb815cd955dc217728caf73fad8dc5b8ee9c + languageName: node + linkType: hard + +"formdata-node@npm:^4.3.2": + version: 4.4.1 + resolution: "formdata-node@npm:4.4.1" + dependencies: + node-domexception: "npm:1.0.0" + web-streams-polyfill: "npm:4.0.0-beta.3" + checksum: 10c0/74151e7b228ffb33b565cec69182694ad07cc3fdd9126a8240468bb70a8ba66e97e097072b60bcb08729b24c7ce3fd3e0bd7f1f80df6f9f662b9656786e76f6a + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.2.6": + version: 1.3.0 + resolution: "get-intrinsic@npm:1.3.0" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" + function-bind: "npm:^1.1.2" + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c + languageName: node + linkType: hard + +"gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.2": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" + dependencies: + has-symbols: "npm:^1.0.3" + checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: "npm:^2.0.0" + checksum: 10c0/f34a2c20161d02303c2807badec2f3b49cbfbbb409abd4f95a07377ae01cfe6b59e3d15ac609cffcd8f2521f0eb37b7e1091acf65da99aa2a4f1ad63c21e7e7a + languageName: node + linkType: hard + +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"ms@npm:^2.0.0": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"node-domexception@npm:1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.7": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + +"octokit@npm:^4.1.3": + version: 4.1.3 + resolution: "octokit@npm:4.1.3" + dependencies: + "@octokit/app": "npm:^15.1.6" + "@octokit/core": "npm:^6.1.5" + "@octokit/oauth-app": "npm:^7.1.6" + "@octokit/plugin-paginate-graphql": "npm:^5.2.4" + "@octokit/plugin-paginate-rest": "npm:^12.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^14.0.0" + "@octokit/plugin-retry": "npm:^7.2.1" + "@octokit/plugin-throttling": "npm:^10.0.0" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/a35352dff1d7bacf8123e491489650c29b830a765f78113064edd6bd0aad0e58e8933874394892972fb8b0e43369b6f7a7b354426c61f4d9fc37408891fde582 + languageName: node + linkType: hard + +"openai@npm:^4.97.0": + version: 4.97.0 + resolution: "openai@npm:4.97.0" + dependencies: + "@types/node": "npm:^18.11.18" + "@types/node-fetch": "npm:^2.6.4" + abort-controller: "npm:^3.0.0" + agentkeepalive: "npm:^4.2.1" + form-data-encoder: "npm:1.7.2" + formdata-node: "npm:^4.3.2" + node-fetch: "npm:^2.6.7" + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + bin: + openai: bin/cli + checksum: 10c0/9938eb85102dd0465197e79c0912df9f3afd52be2d9524fd5100f5befbfa70e6bb7803120515ffa0925b7afc14762052b087c7c2f3d0b85852efa22aa1d745d6 + languageName: node + linkType: hard + +"parse-diff@npm:^0.11.1": + version: 0.11.1 + resolution: "parse-diff@npm:0.11.1" + checksum: 10c0/b8a488039f535e0ddaf1cfd4a13c2adc6d142f4e6263de6dc603f9762b59da89703a8f0dc8aacb7c7c66cdbdd2d075a57c63813fb74f66c9b8f5fe8ad2e89689 + languageName: node + linkType: hard + +"review-agent@workspace:.": + version: 0.0.0-use.local + resolution: "review-agent@workspace:." + dependencies: + "@octokit/webhooks": "npm:^13.8.2" + "@octokit/webhooks-definitions": octokit/webhooks + "@types/node": "npm:^22.15.8" + dotenv: "npm:^16.5.0" + octokit: "npm:^4.1.3" + openai: "npm:^4.97.0" + parse-diff: "npm:^0.11.1" + smee-client: "npm:^3.1.1" + typescript: "npm:^5.8.3" + zod: "npm:^3.24.4" + zod-to-json-schema: "npm:^3.24.5" + languageName: unknown + linkType: soft + +"smee-client@npm:^3.1.1": + version: 3.1.1 + resolution: "smee-client@npm:3.1.1" + dependencies: + commander: "npm:^12.0.0" + eventsource: "npm:^3.0.5" + undici: "npm:^6.19.8" + validator: "npm:^13.11.0" + bin: + smee: bin/smee.js + checksum: 10c0/24fd526330d0d0b0763de574c4d6595739eb0413882d617dce07ca35f71fd732c96a9ca723d394e1e9e938b4ef004a9d69797cb700ec72a6fc69396c8128d245 + languageName: node + linkType: hard + +"toad-cache@npm:^3.7.0": + version: 3.7.0 + resolution: "toad-cache@npm:3.7.0" + checksum: 10c0/7dae2782ee20b22c9798bb8b71dec7ec6a0091021d2ea9dd6e8afccab6b65b358fdba49a02209fac574499702e2c000660721516c87c2538d1b2c0ba03e8c0c3 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + +"typescript@npm:^5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici@npm:^6.19.8": + version: 6.21.2 + resolution: "undici@npm:6.21.2" + checksum: 10c0/799bbc02b77dda9b6b12d56d2620a3a4d4cf087908d6a548acc3ce32f21b5c27467f75c2c4b30fab281daf341210be3d685e8fe99854288de541715ae5735027 + languageName: node + linkType: hard + +"universal-github-app-jwt@npm:^2.2.0": + version: 2.2.2 + resolution: "universal-github-app-jwt@npm:2.2.2" + checksum: 10c0/7ae5f031fb89c01a4407459b764c5e6341d725d436e1ceec161f9b754dd4883d9704cc8de53d5b6314b7e1bef8dbc7561799fc23001e706f213d468c17026fb6 + languageName: node + linkType: hard + +"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": + version: 7.0.2 + resolution: "universal-user-agent@npm:7.0.2" + checksum: 10c0/e60517ee929813e6b3ac0ceb3c66deccafadc71341edca160279ff046319c684fd7090a60d63aa61cd34a06c2d2acebeb8c2f8d364244ae7bf8ab788e20cd8c8 + languageName: node + linkType: hard + +"validator@npm:^13.11.0": + version: 13.15.0 + resolution: "validator@npm:13.15.0" + checksum: 10c0/0f13fd7031ac575e8d7828431da8ef5859bac6a38ee65e1d7fdd367dbf1c3d94d95182aecc3183f7fa7a30ff4474bf864d1aff54707620227a2cdbfd36d894c2 + languageName: node + linkType: hard + +"web-streams-polyfill@npm:4.0.0-beta.3": + version: 4.0.0-beta.3 + resolution: "web-streams-polyfill@npm:4.0.0-beta.3" + checksum: 10c0/a9596779db2766990117ed3a158e0b0e9f69b887a6d6ba0779940259e95f99dc3922e534acc3e5a117b5f5905300f527d6fbf8a9f0957faf1d8e585ce3452e8e + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + +"zod-to-json-schema@npm:^3.24.5": + version: 3.24.5 + resolution: "zod-to-json-schema@npm:3.24.5" + peerDependencies: + zod: ^3.24.1 + checksum: 10c0/0745b94ba53e652d39f262641cdeb2f75d24339fb6076a38ce55bcf53d82dfaea63adf524ebc5f658681005401687f8e9551c4feca7c4c882e123e66091dfb90 + languageName: node + linkType: hard + +"zod@npm:^3.24.4": + version: 3.24.4 + resolution: "zod@npm:3.24.4" + checksum: 10c0/ab3112f017562180a41a0f83d870b333677f7d6b77f106696c56894567051b91154714a088149d8387a4f50806a2520efcb666f108cd384a35c236a191186d91 + languageName: node + linkType: hard diff --git a/staging/fly.toml b/staging/fly.toml deleted file mode 100644 index 6d3f2896..00000000 --- a/staging/fly.toml +++ /dev/null @@ -1,28 +0,0 @@ -# fly.toml app configuration file generated for sourcebot-staging on 2025-01-29T15:57:18-08:00 -# -# See https://fly.io/docs/reference/configuration/ for information about how to use this file. -# - -app = 'sourcebot-staging' -primary_region = 'sea' - -[build] - image = 'ghcr.io/sourcebot-dev/sourcebot:staging' - -[[mounts]] - source = 'sourcebot_staging_data' - destination = '/data' - initial_size = "60GB" - -[http_service] - internal_port = 3000 - force_https = true - auto_stop_machines = true - auto_start_machines = true - min_machines_running = 0 - processes = ['app'] - -[[vm]] - memory = '4gb' - cpu_kind = 'performance' - cpus = 2 From 17fce5ca3c66ac0a8b4d4b957704931da435735e Mon Sep 17 00:00:00 2001 From: msukkari Date: Thu, 8 May 2025 15:40:39 -0700 Subject: [PATCH 02/15] feedback --- .../reviewAgent/nodes/fetch_file_content.ts | 2 +- .../reviewAgent/nodes/generate_pr_reviews.ts | 46 ----------------- .../nodes/github_push_pr_reviews.ts | 32 ------------ packages/agents/reviewAgent/package.json | 13 +---- packages/agents/reviewAgent/{ => src}/app.ts | 0 .../src/nodes/fetch_file_content.ts | 40 +++++++++++++++ .../nodes/generate_diff_review_prompt.ts | 0 .../src/nodes/generate_pr_reviews.ts | 49 +++++++++++++++++++ .../{ => src}/nodes/github_pr_parser.ts | 4 ++ .../src/nodes/github_push_pr_reviews.ts | 40 +++++++++++++++ .../{ => src}/nodes/invoke_diff_review_llm.ts | 10 ++-- .../agents/reviewAgent/{ => src}/types.ts | 0 12 files changed, 142 insertions(+), 94 deletions(-) delete mode 100644 packages/agents/reviewAgent/nodes/generate_pr_reviews.ts delete mode 100644 packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts rename packages/agents/reviewAgent/{ => src}/app.ts (100%) create mode 100644 packages/agents/reviewAgent/src/nodes/fetch_file_content.ts rename packages/agents/reviewAgent/{ => src}/nodes/generate_diff_review_prompt.ts (100%) create mode 100644 packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts rename packages/agents/reviewAgent/{ => src}/nodes/github_pr_parser.ts (95%) create mode 100644 packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts rename packages/agents/reviewAgent/{ => src}/nodes/invoke_diff_review_llm.ts (74%) rename packages/agents/reviewAgent/{ => src}/types.ts (100%) diff --git a/packages/agents/reviewAgent/nodes/fetch_file_content.ts b/packages/agents/reviewAgent/nodes/fetch_file_content.ts index 2c90633e..d33c3e50 100644 --- a/packages/agents/reviewAgent/nodes/fetch_file_content.ts +++ b/packages/agents/reviewAgent/nodes/fetch_file_content.ts @@ -1,4 +1,4 @@ -import { sourcebot_context, sourcebot_pr_payload } from "../types.js"; +import { sourcebot_context, sourcebot_pr_payload } from "../src/types.js"; import { z } from "zod"; // TODO: use original Sourcebot schemas instead of redefining here diff --git a/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts b/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts deleted file mode 100644 index e3666252..00000000 --- a/packages/agents/reviewAgent/nodes/generate_pr_reviews.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "../types.js"; -import { generate_diff_review_prompt } from "./generate_diff_review_prompt.js"; -import { invoke_diff_review_llm } from "./invoke_diff_review_llm.js"; -import { fetch_file_content } from "./fetch_file_content.js"; - -export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { - console.log("Executing generate_pr_reviews"); - - const file_diff_reviews: sourcebot_file_diff_review[] = []; - for (const file_diff of pr_payload.file_diffs) { - const reviews: sourcebot_diff_review[] = []; - - for (const diff of file_diff.diffs) { - const fileContentContext = await fetch_file_content(pr_payload, file_diff.to); - const context: sourcebot_context[] = [ - { - type: "pr_title", - description: "The title of the pull request", - context: pr_payload.title, - }, - { - type: "pr_description", - description: "The description of the pull request", - context: pr_payload.description, - }, - fileContentContext, - ]; - - const prompt = await generate_diff_review_prompt(diff, context, rules); - console.log(prompt); - - const diffReview = await invoke_diff_review_llm(prompt, file_diff.to); - reviews.push(diffReview); - } - - if (reviews.length > 0) { - file_diff_reviews.push({ - filename: file_diff.to, - reviews: reviews, - }); - } - } - - console.log("Completed generate_pr_reviews"); - return file_diff_reviews; -} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts b/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts deleted file mode 100644 index 38b77144..00000000 --- a/packages/agents/reviewAgent/nodes/github_push_pr_reviews.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { App } from "octokit"; -import { sourcebot_pr_payload, sourcebot_file_diff_review } from "../types.js"; - -export const github_push_pr_reviews = async (app: App, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { - console.log("Executing github_push_pr_reviews"); - - const installationId = pr_payload.installation_id; - const installation = await app.getInstallationOctokit(installationId); - - for (const file_diff_review of file_diff_reviews) { - for (const review of file_diff_review.reviews) { - await installation.rest.pulls.createReviewComment({ - owner: pr_payload.owner, - repo: pr_payload.repo, - pull_number: pr_payload.number, - body: review.review, - path: file_diff_review.filename, - commit_id: pr_payload.head_sha, - side: "RIGHT", - ...(review.line_start === review.line_end - ? { line: review.line_start } - : { - start_line: review.line_start, - line: review.line_end, - start_side: "RIGHT", - }), - }); - } - } - - console.log("Completed github_push_pr_reviews"); -} \ No newline at end of file diff --git a/packages/agents/reviewAgent/package.json b/packages/agents/reviewAgent/package.json index 970d6d33..9fe1fc20 100644 --- a/packages/agents/reviewAgent/package.json +++ b/packages/agents/reviewAgent/package.json @@ -1,5 +1,5 @@ { - "name": "review-agent", + "name": "@sourcebot/review-agent", "version": "1.0.0", "description": "", "main": "index.js", @@ -8,17 +8,6 @@ "build": "tsc", "start": "node dist/app.js" }, - "repository": { - "type": "git", - "url": "git+https://github.com/sourcebot-dev/review-agent.git" - }, - "keywords": [], - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/sourcebot-dev/review-agent/issues" - }, - "homepage": "https://github.com/sourcebot-dev/review-agent#readme", "dependencies": { "@octokit/webhooks": "^13.8.2", "@octokit/webhooks-definitions": "octokit/webhooks", diff --git a/packages/agents/reviewAgent/app.ts b/packages/agents/reviewAgent/src/app.ts similarity index 100% rename from packages/agents/reviewAgent/app.ts rename to packages/agents/reviewAgent/src/app.ts diff --git a/packages/agents/reviewAgent/src/nodes/fetch_file_content.ts b/packages/agents/reviewAgent/src/nodes/fetch_file_content.ts new file mode 100644 index 00000000..58475849 --- /dev/null +++ b/packages/agents/reviewAgent/src/nodes/fetch_file_content.ts @@ -0,0 +1,40 @@ +import { sourcebot_context, sourcebot_pr_payload } from "../types.js"; +import { fileSourceResponseSchema } from "@sourcebot/web/src/features/search/schemas.js"; +import { base64Decode } from "@sourcebot/web/src/lib/utils.js"; + +export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { + console.log("Executing fetch_file_content"); + + const repoPath = pr_payload.hostDomain + "/" + pr_payload.owner + "/" + pr_payload.repo; + const fileSourceRequest = { + fileName: filename, + repository: repoPath, + } + console.log(JSON.stringify(fileSourceRequest, null, 2)); + + const response = await fetch('http://localhost:3000/api/source', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Org-Domain': '~' + }, + body: JSON.stringify(fileSourceRequest) + }); + + if (!response.ok) { + throw new Error(`Failed to fetch file content for ${filename} from ${repoPath}: ${response.statusText}`); + } + + const responseData = await response.json(); + const fileSourceResponse = fileSourceResponseSchema.parse(responseData); + const fileContent = base64Decode(fileSourceResponse.source); + + const fileContentContext: sourcebot_context = { + type: "file_content", + description: `The content of the file ${filename}`, + context: fileContent, + } + + console.log("Completed fetch_file_content"); + return fileContentContext; +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts b/packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts similarity index 100% rename from packages/agents/reviewAgent/nodes/generate_diff_review_prompt.ts rename to packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts diff --git a/packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts b/packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts new file mode 100644 index 00000000..92754d03 --- /dev/null +++ b/packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts @@ -0,0 +1,49 @@ +import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "../types.js"; +import { generate_diff_review_prompt } from "./generate_diff_review_prompt.js"; +import { invoke_diff_review_llm } from "./invoke_diff_review_llm.js"; +import { fetch_file_content } from "./fetch_file_content.js"; + +export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { + console.log("Executing generate_pr_reviews"); + + const file_diff_reviews: sourcebot_file_diff_review[] = []; + for (const file_diff of pr_payload.file_diffs) { + const reviews: sourcebot_diff_review[] = []; + + for (const diff of file_diff.diffs) { + try { + const fileContentContext = await fetch_file_content(pr_payload, file_diff.to); + const context: sourcebot_context[] = [ + { + type: "pr_title", + description: "The title of the pull request", + context: pr_payload.title, + }, + { + type: "pr_description", + description: "The description of the pull request", + context: pr_payload.description, + }, + fileContentContext, + ]; + + const prompt = await generate_diff_review_prompt(diff, context, rules); + + const diffReview = await invoke_diff_review_llm(prompt, file_diff.to); + reviews.push(diffReview); + } catch (error) { + console.error(`Error fetching file content for ${file_diff.to}: ${error}`); + } + } + + if (reviews.length > 0) { + file_diff_reviews.push({ + filename: file_diff.to, + reviews: reviews, + }); + } + } + + console.log("Completed generate_pr_reviews"); + return file_diff_reviews; +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/github_pr_parser.ts b/packages/agents/reviewAgent/src/nodes/github_pr_parser.ts similarity index 95% rename from packages/agents/reviewAgent/nodes/github_pr_parser.ts rename to packages/agents/reviewAgent/src/nodes/github_pr_parser.ts index 9cb8e990..c248bcbc 100644 --- a/packages/agents/reviewAgent/nodes/github_pr_parser.ts +++ b/packages/agents/reviewAgent/src/nodes/github_pr_parser.ts @@ -6,6 +6,10 @@ import { Octokit } from "octokit"; export const github_pr_parser = async (octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">): Promise => { console.log("Executing github_pr_parser"); + if (!payload.installation) { + throw new Error("Installation not found in github payload"); + } + const diff = await octokit.request(payload.pull_request.patch_url); const parsedDiff: parse.File[] = parse(diff.data); diff --git a/packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts b/packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts new file mode 100644 index 00000000..db2994d9 --- /dev/null +++ b/packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts @@ -0,0 +1,40 @@ +import { App } from "octokit"; +import { sourcebot_pr_payload, sourcebot_file_diff_review } from "../types.js"; + +export const github_push_pr_reviews = async (app: App, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { + console.log("Executing github_push_pr_reviews"); + + try { + const installationId = pr_payload.installation_id; + const installation = await app.getInstallationOctokit(installationId); + + for (const file_diff_review of file_diff_reviews) { + for (const review of file_diff_review.reviews) { + try { + await installation.rest.pulls.createReviewComment({ + owner: pr_payload.owner, + repo: pr_payload.repo, + pull_number: pr_payload.number, + body: review.review, + path: file_diff_review.filename, + commit_id: pr_payload.head_sha, + side: "RIGHT", + ...(review.line_start === review.line_end + ? { line: review.line_start } + : { + start_line: review.line_start, + line: review.line_end, + start_side: "RIGHT", + }), + }); + } catch (error) { + console.error(`Error pushing pr reviews for ${file_diff_review.filename}: ${error}`); + } + } + } + } catch (error) { + console.error(`Error pushing pr reviews: ${error}`); + } + + console.log("Completed github_push_pr_reviews"); +} \ No newline at end of file diff --git a/packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts b/packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts similarity index 74% rename from packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts rename to packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts index 38e748d3..13790013 100644 --- a/packages/agents/reviewAgent/nodes/invoke_diff_review_llm.ts +++ b/packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts @@ -21,11 +21,15 @@ export const invoke_diff_review_llm = async (prompt: string, filename: string): const openaiResponse = completion.choices[0].message.content; console.log("OpenAI response: ", openaiResponse); - const diffReviewJson = JSON.parse(openaiResponse || ''); - const diffReview = sourcebot_diff_review_schema.parse(diffReviewJson); + const diffReviewJson = JSON.parse(openaiResponse || '{}'); + const diffReview = sourcebot_diff_review_schema.safeParse(diffReviewJson); + + if (!diffReview.success) { + throw new Error(`Invalid diff review format: ${diffReview.error}`); + } console.log("Completed invoke_diff_review_llm"); - return diffReview; + return diffReview.data; } catch (error) { console.error('Error calling OpenAI:', error); throw error; diff --git a/packages/agents/reviewAgent/types.ts b/packages/agents/reviewAgent/src/types.ts similarity index 100% rename from packages/agents/reviewAgent/types.ts rename to packages/agents/reviewAgent/src/types.ts From 12002f27d68e81c067a295678dd42fb7222ca8bd Mon Sep 17 00:00:00 2001 From: msukkari Date: Thu, 8 May 2025 17:31:43 -0700 Subject: [PATCH 03/15] wip integrating review agent into monorepo --- .env.development | 8 +- package.json | 3 +- .../{reviewAgent => review-agent}/.gitignore | 0 .../package.json | 0 .../{reviewAgent => review-agent}/src/app.ts | 6 +- packages/agents/review-agent/src/env.ts | 13 + .../src/nodes/fetch_file_content.ts | 0 .../src/nodes/generate_diff_review_prompt.ts | 0 .../src/nodes/generate_pr_reviews.ts | 0 .../src/nodes/github_pr_parser.ts | 0 .../src/nodes/github_push_pr_reviews.ts | 0 .../src/nodes/invoke_diff_review_llm.ts | 0 .../src/types.ts | 0 .../tsconfig.json | 0 .../reviewAgent/nodes/fetch_file_content.ts | 49 - packages/agents/reviewAgent/yarn.lock | 842 ------------------ packages/web/src/app/[domain]/agents/page.tsx | 70 ++ yarn.lock | 575 +++++++++++- 18 files changed, 667 insertions(+), 899 deletions(-) rename packages/agents/{reviewAgent => review-agent}/.gitignore (100%) rename packages/agents/{reviewAgent => review-agent}/package.json (100%) rename packages/agents/{reviewAgent => review-agent}/src/app.ts (92%) create mode 100644 packages/agents/review-agent/src/env.ts rename packages/agents/{reviewAgent => review-agent}/src/nodes/fetch_file_content.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/nodes/generate_diff_review_prompt.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/nodes/generate_pr_reviews.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/nodes/github_pr_parser.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/nodes/github_push_pr_reviews.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/nodes/invoke_diff_review_llm.ts (100%) rename packages/agents/{reviewAgent => review-agent}/src/types.ts (100%) rename packages/agents/{reviewAgent => review-agent}/tsconfig.json (100%) delete mode 100644 packages/agents/reviewAgent/nodes/fetch_file_content.ts delete mode 100644 packages/agents/reviewAgent/yarn.lock create mode 100644 packages/web/src/app/[domain]/agents/page.tsx diff --git a/.env.development b/.env.development index ae6fdbb3..138800a2 100644 --- a/.env.development +++ b/.env.development @@ -78,4 +78,10 @@ SOURCEBOT_TELEMETRY_DISABLED=true # Disables telemetry collection # NODE_ENV= # SOURCEBOT_TENANCY_MODE=single -# NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT= \ No newline at end of file +# NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT= + +# Used for agents +# GITHUB_APP_ID= +# GITHUB_APP_PRIVATE_KEY_PATH= +# GITHUB_APP_WEBHOOK_SECRET= +# OPENAI_API_KEY= \ No newline at end of file diff --git a/package.json b/package.json index f9b8ba68..e98f6628 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "private": true, "workspaces": [ - "packages/*" + "packages/*", + "packages/agents/*" ], "scripts": { "build": "cross-env SKIP_ENV_VALIDATION=1 yarn workspaces foreach -A run build", diff --git a/packages/agents/reviewAgent/.gitignore b/packages/agents/review-agent/.gitignore similarity index 100% rename from packages/agents/reviewAgent/.gitignore rename to packages/agents/review-agent/.gitignore diff --git a/packages/agents/reviewAgent/package.json b/packages/agents/review-agent/package.json similarity index 100% rename from packages/agents/reviewAgent/package.json rename to packages/agents/review-agent/package.json diff --git a/packages/agents/reviewAgent/src/app.ts b/packages/agents/review-agent/src/app.ts similarity index 92% rename from packages/agents/reviewAgent/src/app.ts rename to packages/agents/review-agent/src/app.ts index ba48f082..2765e65b 100644 --- a/packages/agents/reviewAgent/src/app.ts +++ b/packages/agents/review-agent/src/app.ts @@ -52,15 +52,11 @@ app.webhooks.onError((error) => { console.error(error); }); - const port = 3050; -const host = 'localhost'; const path = "/api/webhook"; -const localWebhookUrl = `http://${host}:${port}${path}`; const middleware = createNodeMiddleware(app.webhooks, { path }); http.createServer(middleware).listen(port, () => { - console.log(`Server is listening for events at: ${localWebhookUrl}`); - console.log('Press Ctrl + C to quit.') + console.log(`Http server for review agent running on port ${port} ${path}`); }); diff --git a/packages/agents/review-agent/src/env.ts b/packages/agents/review-agent/src/env.ts new file mode 100644 index 00000000..fd320618 --- /dev/null +++ b/packages/agents/review-agent/src/env.ts @@ -0,0 +1,13 @@ +import { createEnv } from "@t3-oss/env-core"; +import { z } from "zod"; + +export const env = createEnv({ + server: { + GITHUB_APP_ID: z.string(), + GITHUB_APP_WEBHOOK_SECRET: z.string(), + GITHUB_APP_PRIVATE_KEY_PATH: z.string(), + }, + runtimeEnv: process.env, + emptyStringAsUndefined: true, + skipValidation: process.env.SKIP_ENV_VALIDATION === "1", +}) \ No newline at end of file diff --git a/packages/agents/reviewAgent/src/nodes/fetch_file_content.ts b/packages/agents/review-agent/src/nodes/fetch_file_content.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/fetch_file_content.ts rename to packages/agents/review-agent/src/nodes/fetch_file_content.ts diff --git a/packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts b/packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/generate_diff_review_prompt.ts rename to packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts diff --git a/packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts b/packages/agents/review-agent/src/nodes/generate_pr_reviews.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/generate_pr_reviews.ts rename to packages/agents/review-agent/src/nodes/generate_pr_reviews.ts diff --git a/packages/agents/reviewAgent/src/nodes/github_pr_parser.ts b/packages/agents/review-agent/src/nodes/github_pr_parser.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/github_pr_parser.ts rename to packages/agents/review-agent/src/nodes/github_pr_parser.ts diff --git a/packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts b/packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/github_push_pr_reviews.ts rename to packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts diff --git a/packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts b/packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts similarity index 100% rename from packages/agents/reviewAgent/src/nodes/invoke_diff_review_llm.ts rename to packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts diff --git a/packages/agents/reviewAgent/src/types.ts b/packages/agents/review-agent/src/types.ts similarity index 100% rename from packages/agents/reviewAgent/src/types.ts rename to packages/agents/review-agent/src/types.ts diff --git a/packages/agents/reviewAgent/tsconfig.json b/packages/agents/review-agent/tsconfig.json similarity index 100% rename from packages/agents/reviewAgent/tsconfig.json rename to packages/agents/review-agent/tsconfig.json diff --git a/packages/agents/reviewAgent/nodes/fetch_file_content.ts b/packages/agents/reviewAgent/nodes/fetch_file_content.ts deleted file mode 100644 index d33c3e50..00000000 --- a/packages/agents/reviewAgent/nodes/fetch_file_content.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { sourcebot_context, sourcebot_pr_payload } from "../src/types.js"; -import { z } from "zod"; - -// TODO: use original Sourcebot schemas instead of redefining here -const fileSourceResponseSchema = z.object({ - source: z.string(), - language: z.string(), -}); - -const base64Decode = (base64: string): string => { - const binString = atob(base64); - return Buffer.from(Uint8Array.from(binString, (m) => m.codePointAt(0)!).buffer).toString(); -} - -export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { - console.log("Executing fetch_file_content"); - - const fileSourceRequest = { - fileName: filename, - repository: pr_payload.hostDomain + "/" + pr_payload.owner + "/" + pr_payload.repo, - } - console.log(JSON.stringify(fileSourceRequest, null, 2)); - - const response = await fetch('http://localhost:3000/api/source', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Org-Domain': '~' - }, - body: JSON.stringify(fileSourceRequest) - }); - - if (!response.ok) { - throw new Error(`Failed to fetch file content: ${response.statusText}`); - } - - const responseData = await response.json(); - const fileSourceResponse = fileSourceResponseSchema.parse(responseData); - const fileContent = base64Decode(fileSourceResponse.source); - - const fileContentContext: sourcebot_context = { - type: "file_content", - description: `The content of the file ${filename}`, - context: fileContent, - } - - console.log("Completed fetch_file_content"); - return fileContentContext; -} \ No newline at end of file diff --git a/packages/agents/reviewAgent/yarn.lock b/packages/agents/reviewAgent/yarn.lock deleted file mode 100644 index 52f37e65..00000000 --- a/packages/agents/reviewAgent/yarn.lock +++ /dev/null @@ -1,842 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@octokit/app@npm:^15.1.6": - version: 15.1.6 - resolution: "@octokit/app@npm:15.1.6" - dependencies: - "@octokit/auth-app": "npm:^7.2.1" - "@octokit/auth-unauthenticated": "npm:^6.1.3" - "@octokit/core": "npm:^6.1.5" - "@octokit/oauth-app": "npm:^7.1.6" - "@octokit/plugin-paginate-rest": "npm:^12.0.0" - "@octokit/types": "npm:^14.0.0" - "@octokit/webhooks": "npm:^13.6.1" - checksum: 10c0/865098127d85cc58adda78c9b86cae58c502410a0d9c23d6eeabe633bc6c5e7d5f8901f243c519a0ce72342a8d08d73f120b32a864a8f6e4eeb3ad33326b6825 - languageName: node - linkType: hard - -"@octokit/auth-app@npm:^7.2.1": - version: 7.2.1 - resolution: "@octokit/auth-app@npm:7.2.1" - dependencies: - "@octokit/auth-oauth-app": "npm:^8.1.4" - "@octokit/auth-oauth-user": "npm:^5.1.4" - "@octokit/request": "npm:^9.2.3" - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - toad-cache: "npm:^3.7.0" - universal-github-app-jwt: "npm:^2.2.0" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/da8890ad8ae554697a4d58427d7633c8d4d6f7acbfc14e98345b85035cca6773c393f5db5767b455dfc8de9bc5bac4da2d24e443cd48e701de11dd52fa128385 - languageName: node - linkType: hard - -"@octokit/auth-oauth-app@npm:^8.1.3, @octokit/auth-oauth-app@npm:^8.1.4": - version: 8.1.4 - resolution: "@octokit/auth-oauth-app@npm:8.1.4" - dependencies: - "@octokit/auth-oauth-device": "npm:^7.1.5" - "@octokit/auth-oauth-user": "npm:^5.1.4" - "@octokit/request": "npm:^9.2.3" - "@octokit/types": "npm:^14.0.0" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/2e50711be9ee6c3bf85cf1d58a1d8ae56c36eb894dcfdacabda92b9454b2c49bccc56fb1a989d3e56625885f1ef36278cc01f65c01cde6e3b505b363eac66cc6 - languageName: node - linkType: hard - -"@octokit/auth-oauth-device@npm:^7.1.5": - version: 7.1.5 - resolution: "@octokit/auth-oauth-device@npm:7.1.5" - dependencies: - "@octokit/oauth-methods": "npm:^5.1.5" - "@octokit/request": "npm:^9.2.3" - "@octokit/types": "npm:^14.0.0" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/f93a67473c58e8b855af15c7300a0937c9d3c6ea898e5e810f2f9762100c6cee22ae52e18c7f926b0e4c6c52573751d4c4ab6294c44116bed2d9fdde5cbdd35d - languageName: node - linkType: hard - -"@octokit/auth-oauth-user@npm:^5.1.3, @octokit/auth-oauth-user@npm:^5.1.4": - version: 5.1.4 - resolution: "@octokit/auth-oauth-user@npm:5.1.4" - dependencies: - "@octokit/auth-oauth-device": "npm:^7.1.5" - "@octokit/oauth-methods": "npm:^5.1.5" - "@octokit/request": "npm:^9.2.3" - "@octokit/types": "npm:^14.0.0" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/2bb597b2e50fbd5c03bee3a276efa72b03105aff1d9e243c18a71b72d9fe4ad4b1cee52c2df3e022c4169c7c1d37ab14d05455d5de982528914af965e14b763a - languageName: node - linkType: hard - -"@octokit/auth-token@npm:^5.0.0": - version: 5.1.2 - resolution: "@octokit/auth-token@npm:5.1.2" - checksum: 10c0/bd4952571d9c559ede1f6ef8f7756900256d19df0180db04da88886a05484c7e6a4397611422e4804465a82addc8c2daa21d0bb4f450403552ee81041a4046d1 - languageName: node - linkType: hard - -"@octokit/auth-unauthenticated@npm:^6.1.2, @octokit/auth-unauthenticated@npm:^6.1.3": - version: 6.1.3 - resolution: "@octokit/auth-unauthenticated@npm:6.1.3" - dependencies: - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - checksum: 10c0/60639987fa30e68c4ecd98dd9f1604b3f4368384979cdfa2ab9ea2d0b04c50b42a990e67167cf2a0098205a994b3a80d76b23bb0de4c8c956a898b977f5ce7d0 - languageName: node - linkType: hard - -"@octokit/core@npm:^6.1.4, @octokit/core@npm:^6.1.5": - version: 6.1.5 - resolution: "@octokit/core@npm:6.1.5" - dependencies: - "@octokit/auth-token": "npm:^5.0.0" - "@octokit/graphql": "npm:^8.2.2" - "@octokit/request": "npm:^9.2.3" - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - before-after-hook: "npm:^3.0.2" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/c89ea754cc33da740fdd69fadb971b4b65c89971bba4e8ad545d3ea7aba79759ee3e195c3b72e7df78f14b8b1d392bddc56e7c385d48b5272319ea6a0246ac7c - languageName: node - linkType: hard - -"@octokit/endpoint@npm:^10.1.4": - version: 10.1.4 - resolution: "@octokit/endpoint@npm:10.1.4" - dependencies: - "@octokit/types": "npm:^14.0.0" - universal-user-agent: "npm:^7.0.2" - checksum: 10c0/bf7cca71a05dc4751df658588e32642e59c98768e7509521226b997ea4837e2d16efd35c391231c76d888226f4daf80e6a9f347dee01a69f490253654dada581 - languageName: node - linkType: hard - -"@octokit/graphql@npm:^8.2.2": - version: 8.2.2 - resolution: "@octokit/graphql@npm:8.2.2" - dependencies: - "@octokit/request": "npm:^9.2.3" - "@octokit/types": "npm:^14.0.0" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/29cd5af5ed04e416d28798a11907ab861dc73c0af47f8c9c3f4183d81d2e77d88228643825538acc81e7015f78d891f84107929019a673b06a6b38ccea6a24e0 - languageName: node - linkType: hard - -"@octokit/oauth-app@npm:^7.1.6": - version: 7.1.6 - resolution: "@octokit/oauth-app@npm:7.1.6" - dependencies: - "@octokit/auth-oauth-app": "npm:^8.1.3" - "@octokit/auth-oauth-user": "npm:^5.1.3" - "@octokit/auth-unauthenticated": "npm:^6.1.2" - "@octokit/core": "npm:^6.1.4" - "@octokit/oauth-authorization-url": "npm:^7.1.1" - "@octokit/oauth-methods": "npm:^5.1.4" - "@types/aws-lambda": "npm:^8.10.83" - universal-user-agent: "npm:^7.0.0" - checksum: 10c0/75264f6cb9336baf815bb85ea450d69c5d33eb70754065c9c5307b10d0efec2d76bb17c7788346dfb8b5e4ad0205e6df841b9c709eaf077390bba793787841f5 - languageName: node - linkType: hard - -"@octokit/oauth-authorization-url@npm:^7.0.0, @octokit/oauth-authorization-url@npm:^7.1.1": - version: 7.1.1 - resolution: "@octokit/oauth-authorization-url@npm:7.1.1" - checksum: 10c0/a2723dde503693d4b0793bc43988d7445bdbd1a4e4994f4e94e635816c87b12a3058609eb5982d91783ddf6cdf663ccdb954b5d05efdc59cb66bc0456d7ba370 - languageName: node - linkType: hard - -"@octokit/oauth-methods@npm:^5.1.4, @octokit/oauth-methods@npm:^5.1.5": - version: 5.1.5 - resolution: "@octokit/oauth-methods@npm:5.1.5" - dependencies: - "@octokit/oauth-authorization-url": "npm:^7.0.0" - "@octokit/request": "npm:^9.2.3" - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - checksum: 10c0/2b1c5f9ef0fcdb2f19fd864303a67bdda296fcb2cc058efc51bf3fbb3cb582475a5532eab05bd7a57277607730fa4a7796431564693fffdb9c4cb2b471484a5c - languageName: node - linkType: hard - -"@octokit/openapi-types@npm:^25.0.0": - version: 25.0.0 - resolution: "@octokit/openapi-types@npm:25.0.0" - checksum: 10c0/59c9e5998e08cecec155b776c93d8f6f88ab1a812add61cc65f3de8f3744201565545eac308083d18c9fa330a4381a27bcd771a311ac0348d3590a00f333f233 - languageName: node - linkType: hard - -"@octokit/openapi-webhooks-types@npm:11.0.0": - version: 11.0.0 - resolution: "@octokit/openapi-webhooks-types@npm:11.0.0" - checksum: 10c0/cea59ba6b976a242fa4e6bedfab7e6fc3437381557d2c1876ca3aea5f6d4231559a378f9bc35e09593cb2d466afb4a2415be66b960f3e0a38b65b8b9f22ff90e - languageName: node - linkType: hard - -"@octokit/plugin-paginate-graphql@npm:^5.2.4": - version: 5.2.4 - resolution: "@octokit/plugin-paginate-graphql@npm:5.2.4" - peerDependencies: - "@octokit/core": ">=6" - checksum: 10c0/30601cf11d78e5683098d68fbff071a3d20b1e23758f40b1f43fbc93f69b3d0e07f2c9aaaaee113e586af7f604e809ba93702d932b1a4ea65998c7ab39a40a54 - languageName: node - linkType: hard - -"@octokit/plugin-paginate-rest@npm:^12.0.0": - version: 12.0.0 - resolution: "@octokit/plugin-paginate-rest@npm:12.0.0" - dependencies: - "@octokit/types": "npm:^14.0.0" - peerDependencies: - "@octokit/core": ">=6" - checksum: 10c0/4ed04e8c111ac8cfb0692f917fe09ff6484b7436f0605c661e8051c6fb281c0260c9b067c1422529b948e53b22221b0f7664e2d10a28dcd9db14465c02c7182f - languageName: node - linkType: hard - -"@octokit/plugin-rest-endpoint-methods@npm:^14.0.0": - version: 14.0.0 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:14.0.0" - dependencies: - "@octokit/types": "npm:^14.0.0" - peerDependencies: - "@octokit/core": ">=6" - checksum: 10c0/c3f26c5277d4aa0c898d8fdbf84326943ea80496e6f60ae34834415384ab629e1e3702d1ed82d40c31a7370edfcb5fa9fe434b0357b302b3be309879bad1d4e6 - languageName: node - linkType: hard - -"@octokit/plugin-retry@npm:^7.2.1": - version: 7.2.1 - resolution: "@octokit/plugin-retry@npm:7.2.1" - dependencies: - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - bottleneck: "npm:^2.15.3" - peerDependencies: - "@octokit/core": ">=6" - checksum: 10c0/0cac8aa32bfe9612cbaba36f51382c69c882ef7927e1deb4b166fe664959887e952205adbf8b9ff461e0128ac3e1bd807ca58e38041a55ddb3214397584f0406 - languageName: node - linkType: hard - -"@octokit/plugin-throttling@npm:^10.0.0": - version: 10.0.0 - resolution: "@octokit/plugin-throttling@npm:10.0.0" - dependencies: - "@octokit/types": "npm:^14.0.0" - bottleneck: "npm:^2.15.3" - peerDependencies: - "@octokit/core": ^6.1.3 - checksum: 10c0/60294f89bcc03b034fce557dffcb0dba1b02d1fa1834b3b4f542f8635750df4bd23fc143f7c71f802a22a1aaed089c5da77979e6803abe11908b1c91aa1ba419 - languageName: node - linkType: hard - -"@octokit/request-error@npm:^6.1.7, @octokit/request-error@npm:^6.1.8": - version: 6.1.8 - resolution: "@octokit/request-error@npm:6.1.8" - dependencies: - "@octokit/types": "npm:^14.0.0" - checksum: 10c0/02aa5bfebb5b1b9e152558b4a6f4f7dcb149b41538778ffe0fce3395fd0da5c0862311a78e94723435667581b2a58a7cefa458cf7aa19ae2948ae419276f7ee1 - languageName: node - linkType: hard - -"@octokit/request@npm:^9.2.3": - version: 9.2.3 - resolution: "@octokit/request@npm:9.2.3" - dependencies: - "@octokit/endpoint": "npm:^10.1.4" - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - fast-content-type-parse: "npm:^2.0.0" - universal-user-agent: "npm:^7.0.2" - checksum: 10c0/96067fc9a5f30f2faa00f08015309930561c3ea0536b543e1c91c475f965eabc81c48fc27d401681ebdb3f6c1acc5d46eaa69163ab98b0faa08be1c02ea5b684 - languageName: node - linkType: hard - -"@octokit/types@npm:^14.0.0": - version: 14.0.0 - resolution: "@octokit/types@npm:14.0.0" - dependencies: - "@octokit/openapi-types": "npm:^25.0.0" - checksum: 10c0/c82da635fe99f265dbef7bf954d45a23ca7ce9c6fc9a8478c247b5435799e5d0eab3ff42f085785ee0882b2de293cab0ab831b379c66f41d00b78412df850ba4 - languageName: node - linkType: hard - -"@octokit/webhooks-definitions@octokit/webhooks": - version: 0.0.0-development - resolution: "@octokit/webhooks-definitions@https://github.com/octokit/webhooks.git#commit=e5304814003f52b2697310a52f9d6bfc07632bd3" - checksum: 10c0/ff90b65b388b3f5ce80a0aa47c6ceb98578f8df6364799db5fc17ffadbbe7e533a7f2c548b279ebd4780e00a2cdcc5f07093b0add975d014af2722496413f75f - languageName: node - linkType: hard - -"@octokit/webhooks-methods@npm:^5.1.1": - version: 5.1.1 - resolution: "@octokit/webhooks-methods@npm:5.1.1" - checksum: 10c0/837aa49dbc7f8bc01448f4eaea32cfb0c1cbfa0b4ac570f7bcda385c71f43e4be05e91a3fb63708448410b27e246570fde42056b6b87d755154d84eff5a6500c - languageName: node - linkType: hard - -"@octokit/webhooks@npm:^13.6.1, @octokit/webhooks@npm:^13.8.2": - version: 13.8.2 - resolution: "@octokit/webhooks@npm:13.8.2" - dependencies: - "@octokit/openapi-webhooks-types": "npm:11.0.0" - "@octokit/request-error": "npm:^6.1.7" - "@octokit/webhooks-methods": "npm:^5.1.1" - checksum: 10c0/5053ab9cff8afe4cd6e7d7a41af3f1b0c0ccc5058482577aa82bb8e5003f3002896c68bd59d7da363204c2d6baa4cca73ed684bb7a5a9286ca4b43172cd1fe9e - languageName: node - linkType: hard - -"@types/aws-lambda@npm:^8.10.83": - version: 8.10.149 - resolution: "@types/aws-lambda@npm:8.10.149" - checksum: 10c0/9ef41214a1b69fa30d89cab575429793f1ccc49cfe3a7aa75228aef31a202bb6d2c306a5b33def61ef29e0d0a1a324412bf6a4fa5a615e61117685584a394047 - languageName: node - linkType: hard - -"@types/node-fetch@npm:^2.6.4": - version: 2.6.12 - resolution: "@types/node-fetch@npm:2.6.12" - dependencies: - "@types/node": "npm:*" - form-data: "npm:^4.0.0" - checksum: 10c0/7693acad5499b7df2d1727d46cff092a63896dc04645f36b973dd6dd754a59a7faba76fcb777bdaa35d80625c6a9dd7257cca9c401a4bab03b04480cda7fd1af - languageName: node - linkType: hard - -"@types/node@npm:*": - version: 22.15.9 - resolution: "@types/node@npm:22.15.9" - dependencies: - undici-types: "npm:~6.21.0" - checksum: 10c0/f294ba23e441b4f919394d0e4c80f3f33e858e6f1db0561317148da2db3d3c67809fd1a38de481328933b0d5c6174db40cb6ab65f12b2cf3309df7f07a8e46f6 - languageName: node - linkType: hard - -"@types/node@npm:^18.11.18": - version: 18.19.93 - resolution: "@types/node@npm:18.19.93" - dependencies: - undici-types: "npm:~5.26.4" - checksum: 10c0/f4d1ca1a07b88bde6ae46b1f4cf3ace8db98406283f0caa18b22f642a6f4361d801e7efd44ee13d818114bb33b2a56e1a9cd97321b58fd3d8147351fabf4be82 - languageName: node - linkType: hard - -"@types/node@npm:^22.15.8": - version: 22.15.8 - resolution: "@types/node@npm:22.15.8" - dependencies: - undici-types: "npm:~6.21.0" - checksum: 10c0/980ddda0c98ce53689d52e8c20fbc7f7a42280780c4617892872c3bc2c342ef7b0bae5ff41eec536981a7d39fd7a9af71f5ce8849b36a2084d3945b7bc438d05 - languageName: node - linkType: hard - -"abort-controller@npm:^3.0.0": - version: 3.0.0 - resolution: "abort-controller@npm:3.0.0" - dependencies: - event-target-shim: "npm:^5.0.0" - checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": - version: 4.6.0 - resolution: "agentkeepalive@npm:4.6.0" - dependencies: - humanize-ms: "npm:^1.2.1" - checksum: 10c0/235c182432f75046835b05f239708107138a40103deee23b6a08caee5136873709155753b394ec212e49e60e94a378189562cb01347765515cff61b692c69187 - languageName: node - linkType: hard - -"asynckit@npm:^0.4.0": - version: 0.4.0 - resolution: "asynckit@npm:0.4.0" - checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d - languageName: node - linkType: hard - -"before-after-hook@npm:^3.0.2": - version: 3.0.2 - resolution: "before-after-hook@npm:3.0.2" - checksum: 10c0/dea640f9e88a1085372c9bcc974b7bf379267490693da92ec102a7d8b515dd1e95f00ef575a146b83ca638104c57406c3427d37bdf082f602dde4b56d05bba14 - languageName: node - linkType: hard - -"bottleneck@npm:^2.15.3": - version: 2.19.5 - resolution: "bottleneck@npm:2.19.5" - checksum: 10c0/b0f72e45b2e0f56a21ba720183f16bef8e693452fb0495d997fa354e42904353a94bd8fd429868e6751bc85e54b6755190519eed5a0ae0a94a5185209ae7c6d0 - languageName: node - linkType: hard - -"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind-apply-helpers@npm:1.0.2" - dependencies: - es-errors: "npm:^1.3.0" - function-bind: "npm:^1.1.2" - checksum: 10c0/47bd9901d57b857590431243fea704ff18078b16890a6b3e021e12d279bbf211d039155e27d7566b374d49ee1f8189344bac9833dec7a20cdec370506361c938 - languageName: node - linkType: hard - -"combined-stream@npm:^1.0.8": - version: 1.0.8 - resolution: "combined-stream@npm:1.0.8" - dependencies: - delayed-stream: "npm:~1.0.0" - checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 - languageName: node - linkType: hard - -"commander@npm:^12.0.0": - version: 12.1.0 - resolution: "commander@npm:12.1.0" - checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 - languageName: node - linkType: hard - -"delayed-stream@npm:~1.0.0": - version: 1.0.0 - resolution: "delayed-stream@npm:1.0.0" - checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 - languageName: node - linkType: hard - -"dotenv@npm:^16.5.0": - version: 16.5.0 - resolution: "dotenv@npm:16.5.0" - checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9 - languageName: node - linkType: hard - -"dunder-proto@npm:^1.0.1": - version: 1.0.1 - resolution: "dunder-proto@npm:1.0.1" - dependencies: - call-bind-apply-helpers: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - gopd: "npm:^1.2.0" - checksum: 10c0/199f2a0c1c16593ca0a145dbf76a962f8033ce3129f01284d48c45ed4e14fea9bbacd7b3610b6cdc33486cef20385ac054948fefc6272fcce645c09468f93031 - languageName: node - linkType: hard - -"es-define-property@npm:^1.0.1": - version: 1.0.1 - resolution: "es-define-property@npm:1.0.1" - checksum: 10c0/3f54eb49c16c18707949ff25a1456728c883e81259f045003499efba399c08bad00deebf65cccde8c0e07908c1a225c9d472b7107e558f2a48e28d530e34527c - languageName: node - linkType: hard - -"es-errors@npm:^1.3.0": - version: 1.3.0 - resolution: "es-errors@npm:1.3.0" - checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 - languageName: node - linkType: hard - -"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": - version: 1.1.1 - resolution: "es-object-atoms@npm:1.1.1" - dependencies: - es-errors: "npm:^1.3.0" - checksum: 10c0/65364812ca4daf48eb76e2a3b7a89b3f6a2e62a1c420766ce9f692665a29d94fe41fe88b65f24106f449859549711e4b40d9fb8002d862dfd7eb1c512d10be0c - languageName: node - linkType: hard - -"es-set-tostringtag@npm:^2.1.0": - version: 2.1.0 - resolution: "es-set-tostringtag@npm:2.1.0" - dependencies: - es-errors: "npm:^1.3.0" - get-intrinsic: "npm:^1.2.6" - has-tostringtag: "npm:^1.0.2" - hasown: "npm:^2.0.2" - checksum: 10c0/ef2ca9ce49afe3931cb32e35da4dcb6d86ab02592cfc2ce3e49ced199d9d0bb5085fc7e73e06312213765f5efa47cc1df553a6a5154584b21448e9fb8355b1af - languageName: node - linkType: hard - -"event-target-shim@npm:^5.0.0": - version: 5.0.1 - resolution: "event-target-shim@npm:5.0.1" - checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b - languageName: node - linkType: hard - -"eventsource-parser@npm:^3.0.1": - version: 3.0.1 - resolution: "eventsource-parser@npm:3.0.1" - checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0 - languageName: node - linkType: hard - -"eventsource@npm:^3.0.5": - version: 3.0.6 - resolution: "eventsource@npm:3.0.6" - dependencies: - eventsource-parser: "npm:^3.0.1" - checksum: 10c0/074d865ea1c7e29e3243f85a13306e89fca2d775b982dca03fa6bfa75c56827fa89cf1ab9e730db24bd6b104cbdcae074f2b37ba498874e9dd9710fbff4979bb - languageName: node - linkType: hard - -"fast-content-type-parse@npm:^2.0.0": - version: 2.0.1 - resolution: "fast-content-type-parse@npm:2.0.1" - checksum: 10c0/e5ff87d75a35ae4cf377df1dca46ec49e7abbdc8513689676ecdef548b94900b50e66e516e64470035d79b9f7010ef15d98c24d8ae803a881363cc59e0715e19 - languageName: node - linkType: hard - -"form-data-encoder@npm:1.7.2": - version: 1.7.2 - resolution: "form-data-encoder@npm:1.7.2" - checksum: 10c0/56553768037b6d55d9de524f97fe70555f0e415e781cb56fc457a68263de3d40fadea2304d4beef2d40b1a851269bd7854e42c362107071892cb5238debe9464 - languageName: node - linkType: hard - -"form-data@npm:^4.0.0": - version: 4.0.2 - resolution: "form-data@npm:4.0.2" - dependencies: - asynckit: "npm:^0.4.0" - combined-stream: "npm:^1.0.8" - es-set-tostringtag: "npm:^2.1.0" - mime-types: "npm:^2.1.12" - checksum: 10c0/e534b0cf025c831a0929bf4b9bbe1a9a6b03e273a8161f9947286b9b13bf8fb279c6944aae0070c4c311100c6d6dbb815cd955dc217728caf73fad8dc5b8ee9c - languageName: node - linkType: hard - -"formdata-node@npm:^4.3.2": - version: 4.4.1 - resolution: "formdata-node@npm:4.4.1" - dependencies: - node-domexception: "npm:1.0.0" - web-streams-polyfill: "npm:4.0.0-beta.3" - checksum: 10c0/74151e7b228ffb33b565cec69182694ad07cc3fdd9126a8240468bb70a8ba66e97e097072b60bcb08729b24c7ce3fd3e0bd7f1f80df6f9f662b9656786e76f6a - languageName: node - linkType: hard - -"function-bind@npm:^1.1.2": - version: 1.1.2 - resolution: "function-bind@npm:1.1.2" - checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.2.6": - version: 1.3.0 - resolution: "get-intrinsic@npm:1.3.0" - dependencies: - call-bind-apply-helpers: "npm:^1.0.2" - es-define-property: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.1.1" - function-bind: "npm:^1.1.2" - get-proto: "npm:^1.0.1" - gopd: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - hasown: "npm:^2.0.2" - math-intrinsics: "npm:^1.1.0" - checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a - languageName: node - linkType: hard - -"get-proto@npm:^1.0.1": - version: 1.0.1 - resolution: "get-proto@npm:1.0.1" - dependencies: - dunder-proto: "npm:^1.0.1" - es-object-atoms: "npm:^1.0.0" - checksum: 10c0/9224acb44603c5526955e83510b9da41baf6ae73f7398875fba50edc5e944223a89c4a72b070fcd78beb5f7bdda58ecb6294adc28f7acfc0da05f76a2399643c - languageName: node - linkType: hard - -"gopd@npm:^1.2.0": - version: 1.2.0 - resolution: "gopd@npm:1.2.0" - checksum: 10c0/50fff1e04ba2b7737c097358534eacadad1e68d24cccee3272e04e007bed008e68d2614f3987788428fd192a5ae3889d08fb2331417e4fc4a9ab366b2043cead - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": - version: 1.1.0 - resolution: "has-symbols@npm:1.1.0" - checksum: 10c0/dde0a734b17ae51e84b10986e651c664379018d10b91b6b0e9b293eddb32f0f069688c841fb40f19e9611546130153e0a2a48fd7f512891fb000ddfa36f5a20e - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.2": - version: 1.0.2 - resolution: "has-tostringtag@npm:1.0.2" - dependencies: - has-symbols: "npm:^1.0.3" - checksum: 10c0/a8b166462192bafe3d9b6e420a1d581d93dd867adb61be223a17a8d6dad147aa77a8be32c961bb2f27b3ef893cae8d36f564ab651f5e9b7938ae86f74027c48c - languageName: node - linkType: hard - -"hasown@npm:^2.0.2": - version: 2.0.2 - resolution: "hasown@npm:2.0.2" - dependencies: - function-bind: "npm:^1.1.2" - checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 - languageName: node - linkType: hard - -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: "npm:^2.0.0" - checksum: 10c0/f34a2c20161d02303c2807badec2f3b49cbfbbb409abd4f95a07377ae01cfe6b59e3d15ac609cffcd8f2521f0eb37b7e1091acf65da99aa2a4f1ad63c21e7e7a - languageName: node - linkType: hard - -"math-intrinsics@npm:^1.1.0": - version: 1.1.0 - resolution: "math-intrinsics@npm:1.1.0" - checksum: 10c0/7579ff94e899e2f76ab64491d76cf606274c874d8f2af4a442c016bd85688927fcfca157ba6bf74b08e9439dc010b248ce05b96cc7c126a354c3bae7fcb48b7f - languageName: node - linkType: hard - -"mime-db@npm:1.52.0": - version: 1.52.0 - resolution: "mime-db@npm:1.52.0" - checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa - languageName: node - linkType: hard - -"mime-types@npm:^2.1.12": - version: 2.1.35 - resolution: "mime-types@npm:2.1.35" - dependencies: - mime-db: "npm:1.52.0" - checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 - languageName: node - linkType: hard - -"ms@npm:^2.0.0": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 - languageName: node - linkType: hard - -"node-domexception@npm:1.0.0": - version: 1.0.0 - resolution: "node-domexception@npm:1.0.0" - checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b - languageName: node - linkType: hard - -"node-fetch@npm:^2.6.7": - version: 2.7.0 - resolution: "node-fetch@npm:2.7.0" - dependencies: - whatwg-url: "npm:^5.0.0" - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 - languageName: node - linkType: hard - -"octokit@npm:^4.1.3": - version: 4.1.3 - resolution: "octokit@npm:4.1.3" - dependencies: - "@octokit/app": "npm:^15.1.6" - "@octokit/core": "npm:^6.1.5" - "@octokit/oauth-app": "npm:^7.1.6" - "@octokit/plugin-paginate-graphql": "npm:^5.2.4" - "@octokit/plugin-paginate-rest": "npm:^12.0.0" - "@octokit/plugin-rest-endpoint-methods": "npm:^14.0.0" - "@octokit/plugin-retry": "npm:^7.2.1" - "@octokit/plugin-throttling": "npm:^10.0.0" - "@octokit/request-error": "npm:^6.1.8" - "@octokit/types": "npm:^14.0.0" - checksum: 10c0/a35352dff1d7bacf8123e491489650c29b830a765f78113064edd6bd0aad0e58e8933874394892972fb8b0e43369b6f7a7b354426c61f4d9fc37408891fde582 - languageName: node - linkType: hard - -"openai@npm:^4.97.0": - version: 4.97.0 - resolution: "openai@npm:4.97.0" - dependencies: - "@types/node": "npm:^18.11.18" - "@types/node-fetch": "npm:^2.6.4" - abort-controller: "npm:^3.0.0" - agentkeepalive: "npm:^4.2.1" - form-data-encoder: "npm:1.7.2" - formdata-node: "npm:^4.3.2" - node-fetch: "npm:^2.6.7" - peerDependencies: - ws: ^8.18.0 - zod: ^3.23.8 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - bin: - openai: bin/cli - checksum: 10c0/9938eb85102dd0465197e79c0912df9f3afd52be2d9524fd5100f5befbfa70e6bb7803120515ffa0925b7afc14762052b087c7c2f3d0b85852efa22aa1d745d6 - languageName: node - linkType: hard - -"parse-diff@npm:^0.11.1": - version: 0.11.1 - resolution: "parse-diff@npm:0.11.1" - checksum: 10c0/b8a488039f535e0ddaf1cfd4a13c2adc6d142f4e6263de6dc603f9762b59da89703a8f0dc8aacb7c7c66cdbdd2d075a57c63813fb74f66c9b8f5fe8ad2e89689 - languageName: node - linkType: hard - -"review-agent@workspace:.": - version: 0.0.0-use.local - resolution: "review-agent@workspace:." - dependencies: - "@octokit/webhooks": "npm:^13.8.2" - "@octokit/webhooks-definitions": octokit/webhooks - "@types/node": "npm:^22.15.8" - dotenv: "npm:^16.5.0" - octokit: "npm:^4.1.3" - openai: "npm:^4.97.0" - parse-diff: "npm:^0.11.1" - smee-client: "npm:^3.1.1" - typescript: "npm:^5.8.3" - zod: "npm:^3.24.4" - zod-to-json-schema: "npm:^3.24.5" - languageName: unknown - linkType: soft - -"smee-client@npm:^3.1.1": - version: 3.1.1 - resolution: "smee-client@npm:3.1.1" - dependencies: - commander: "npm:^12.0.0" - eventsource: "npm:^3.0.5" - undici: "npm:^6.19.8" - validator: "npm:^13.11.0" - bin: - smee: bin/smee.js - checksum: 10c0/24fd526330d0d0b0763de574c4d6595739eb0413882d617dce07ca35f71fd732c96a9ca723d394e1e9e938b4ef004a9d69797cb700ec72a6fc69396c8128d245 - languageName: node - linkType: hard - -"toad-cache@npm:^3.7.0": - version: 3.7.0 - resolution: "toad-cache@npm:3.7.0" - checksum: 10c0/7dae2782ee20b22c9798bb8b71dec7ec6a0091021d2ea9dd6e8afccab6b65b358fdba49a02209fac574499702e2c000660721516c87c2538d1b2c0ba03e8c0c3 - languageName: node - linkType: hard - -"tr46@npm:~0.0.3": - version: 0.0.3 - resolution: "tr46@npm:0.0.3" - checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 - languageName: node - linkType: hard - -"typescript@npm:^5.8.3": - version: 5.8.3 - resolution: "typescript@npm:5.8.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": - version: 5.8.3 - resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb - languageName: node - linkType: hard - -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 - languageName: node - linkType: hard - -"undici-types@npm:~6.21.0": - version: 6.21.0 - resolution: "undici-types@npm:6.21.0" - checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 - languageName: node - linkType: hard - -"undici@npm:^6.19.8": - version: 6.21.2 - resolution: "undici@npm:6.21.2" - checksum: 10c0/799bbc02b77dda9b6b12d56d2620a3a4d4cf087908d6a548acc3ce32f21b5c27467f75c2c4b30fab281daf341210be3d685e8fe99854288de541715ae5735027 - languageName: node - linkType: hard - -"universal-github-app-jwt@npm:^2.2.0": - version: 2.2.2 - resolution: "universal-github-app-jwt@npm:2.2.2" - checksum: 10c0/7ae5f031fb89c01a4407459b764c5e6341d725d436e1ceec161f9b754dd4883d9704cc8de53d5b6314b7e1bef8dbc7561799fc23001e706f213d468c17026fb6 - languageName: node - linkType: hard - -"universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": - version: 7.0.2 - resolution: "universal-user-agent@npm:7.0.2" - checksum: 10c0/e60517ee929813e6b3ac0ceb3c66deccafadc71341edca160279ff046319c684fd7090a60d63aa61cd34a06c2d2acebeb8c2f8d364244ae7bf8ab788e20cd8c8 - languageName: node - linkType: hard - -"validator@npm:^13.11.0": - version: 13.15.0 - resolution: "validator@npm:13.15.0" - checksum: 10c0/0f13fd7031ac575e8d7828431da8ef5859bac6a38ee65e1d7fdd367dbf1c3d94d95182aecc3183f7fa7a30ff4474bf864d1aff54707620227a2cdbfd36d894c2 - languageName: node - linkType: hard - -"web-streams-polyfill@npm:4.0.0-beta.3": - version: 4.0.0-beta.3 - resolution: "web-streams-polyfill@npm:4.0.0-beta.3" - checksum: 10c0/a9596779db2766990117ed3a158e0b0e9f69b887a6d6ba0779940259e95f99dc3922e534acc3e5a117b5f5905300f527d6fbf8a9f0957faf1d8e585ce3452e8e - languageName: node - linkType: hard - -"webidl-conversions@npm:^3.0.0": - version: 3.0.1 - resolution: "webidl-conversions@npm:3.0.1" - checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db - languageName: node - linkType: hard - -"whatwg-url@npm:^5.0.0": - version: 5.0.0 - resolution: "whatwg-url@npm:5.0.0" - dependencies: - tr46: "npm:~0.0.3" - webidl-conversions: "npm:^3.0.0" - checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 - languageName: node - linkType: hard - -"zod-to-json-schema@npm:^3.24.5": - version: 3.24.5 - resolution: "zod-to-json-schema@npm:3.24.5" - peerDependencies: - zod: ^3.24.1 - checksum: 10c0/0745b94ba53e652d39f262641cdeb2f75d24339fb6076a38ce55bcf53d82dfaea63adf524ebc5f658681005401687f8e9551c4feca7c4c882e123e66091dfb90 - languageName: node - linkType: hard - -"zod@npm:^3.24.4": - version: 3.24.4 - resolution: "zod@npm:3.24.4" - checksum: 10c0/ab3112f017562180a41a0f83d870b333677f7d6b77f106696c56894567051b91154714a088149d8387a4f50806a2520efcb666f108cd384a35c236a191186d91 - languageName: node - linkType: hard diff --git a/packages/web/src/app/[domain]/agents/page.tsx b/packages/web/src/app/[domain]/agents/page.tsx new file mode 100644 index 00000000..1a634a2c --- /dev/null +++ b/packages/web/src/app/[domain]/agents/page.tsx @@ -0,0 +1,70 @@ +import { Header } from "../components/header"; +import Link from "next/link"; +import Image from "next/image"; +import { NavigationMenu } from "../components/navigationMenu"; +import { FaRobot, FaCogs } from "react-icons/fa"; +import { MdRocketLaunch } from "react-icons/md"; + +const agents = [ + { + id: "review-agent", + name: "Review Agent", + description: "An agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase wide context.", + deployUrl: "/agents/review-agent/deploy", + configureUrl: "/agents/review-agent/configure", + }, + // Add more agents here as needed +]; + +export default function AgentsPage({ params: { domain } }: { params: { domain: string } }) { + return ( +
+ +
+
+ {agents.map((agent) => ( +
+ {/* Name and description */} +
+

+ {agent.name} +

+

+ {agent.description} +

+
+ {/* Actions */} +
+ + Deploy + + + Configure + +
+
+ ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index af3c85c0..a2b0e936 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2235,6 +2235,75 @@ __metadata: languageName: node linkType: hard +"@octokit/app@npm:^15.1.6": + version: 15.1.6 + resolution: "@octokit/app@npm:15.1.6" + dependencies: + "@octokit/auth-app": "npm:^7.2.1" + "@octokit/auth-unauthenticated": "npm:^6.1.3" + "@octokit/core": "npm:^6.1.5" + "@octokit/oauth-app": "npm:^7.1.6" + "@octokit/plugin-paginate-rest": "npm:^12.0.0" + "@octokit/types": "npm:^14.0.0" + "@octokit/webhooks": "npm:^13.6.1" + checksum: 10c0/865098127d85cc58adda78c9b86cae58c502410a0d9c23d6eeabe633bc6c5e7d5f8901f243c519a0ce72342a8d08d73f120b32a864a8f6e4eeb3ad33326b6825 + languageName: node + linkType: hard + +"@octokit/auth-app@npm:^7.2.1": + version: 7.2.1 + resolution: "@octokit/auth-app@npm:7.2.1" + dependencies: + "@octokit/auth-oauth-app": "npm:^8.1.4" + "@octokit/auth-oauth-user": "npm:^5.1.4" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + toad-cache: "npm:^3.7.0" + universal-github-app-jwt: "npm:^2.2.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/da8890ad8ae554697a4d58427d7633c8d4d6f7acbfc14e98345b85035cca6773c393f5db5767b455dfc8de9bc5bac4da2d24e443cd48e701de11dd52fa128385 + languageName: node + linkType: hard + +"@octokit/auth-oauth-app@npm:^8.1.3, @octokit/auth-oauth-app@npm:^8.1.4": + version: 8.1.4 + resolution: "@octokit/auth-oauth-app@npm:8.1.4" + dependencies: + "@octokit/auth-oauth-device": "npm:^7.1.5" + "@octokit/auth-oauth-user": "npm:^5.1.4" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/2e50711be9ee6c3bf85cf1d58a1d8ae56c36eb894dcfdacabda92b9454b2c49bccc56fb1a989d3e56625885f1ef36278cc01f65c01cde6e3b505b363eac66cc6 + languageName: node + linkType: hard + +"@octokit/auth-oauth-device@npm:^7.1.5": + version: 7.1.5 + resolution: "@octokit/auth-oauth-device@npm:7.1.5" + dependencies: + "@octokit/oauth-methods": "npm:^5.1.5" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/f93a67473c58e8b855af15c7300a0937c9d3c6ea898e5e810f2f9762100c6cee22ae52e18c7f926b0e4c6c52573751d4c4ab6294c44116bed2d9fdde5cbdd35d + languageName: node + linkType: hard + +"@octokit/auth-oauth-user@npm:^5.1.3, @octokit/auth-oauth-user@npm:^5.1.4": + version: 5.1.4 + resolution: "@octokit/auth-oauth-user@npm:5.1.4" + dependencies: + "@octokit/auth-oauth-device": "npm:^7.1.5" + "@octokit/oauth-methods": "npm:^5.1.5" + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/2bb597b2e50fbd5c03bee3a276efa72b03105aff1d9e243c18a71b72d9fe4ad4b1cee52c2df3e022c4169c7c1d37ab14d05455d5de982528914af965e14b763a + languageName: node + linkType: hard + "@octokit/auth-token@npm:^5.0.0": version: 5.1.2 resolution: "@octokit/auth-token@npm:5.1.2" @@ -2242,6 +2311,16 @@ __metadata: languageName: node linkType: hard +"@octokit/auth-unauthenticated@npm:^6.1.2, @octokit/auth-unauthenticated@npm:^6.1.3": + version: 6.1.3 + resolution: "@octokit/auth-unauthenticated@npm:6.1.3" + dependencies: + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/60639987fa30e68c4ecd98dd9f1604b3f4368384979cdfa2ab9ea2d0b04c50b42a990e67167cf2a0098205a994b3a80d76b23bb0de4c8c956a898b977f5ce7d0 + languageName: node + linkType: hard + "@octokit/core@npm:^6.1.4": version: 6.1.4 resolution: "@octokit/core@npm:6.1.4" @@ -2257,6 +2336,21 @@ __metadata: languageName: node linkType: hard +"@octokit/core@npm:^6.1.5": + version: 6.1.5 + resolution: "@octokit/core@npm:6.1.5" + dependencies: + "@octokit/auth-token": "npm:^5.0.0" + "@octokit/graphql": "npm:^8.2.2" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + before-after-hook: "npm:^3.0.2" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/c89ea754cc33da740fdd69fadb971b4b65c89971bba4e8ad545d3ea7aba79759ee3e195c3b72e7df78f14b8b1d392bddc56e7c385d48b5272319ea6a0246ac7c + languageName: node + linkType: hard + "@octokit/endpoint@npm:^10.1.3": version: 10.1.3 resolution: "@octokit/endpoint@npm:10.1.3" @@ -2267,6 +2361,16 @@ __metadata: languageName: node linkType: hard +"@octokit/endpoint@npm:^10.1.4": + version: 10.1.4 + resolution: "@octokit/endpoint@npm:10.1.4" + dependencies: + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/bf7cca71a05dc4751df658588e32642e59c98768e7509521226b997ea4837e2d16efd35c391231c76d888226f4daf80e6a9f347dee01a69f490253654dada581 + languageName: node + linkType: hard + "@octokit/graphql@npm:^8.1.2": version: 8.2.1 resolution: "@octokit/graphql@npm:8.2.1" @@ -2278,6 +2382,52 @@ __metadata: languageName: node linkType: hard +"@octokit/graphql@npm:^8.2.2": + version: 8.2.2 + resolution: "@octokit/graphql@npm:8.2.2" + dependencies: + "@octokit/request": "npm:^9.2.3" + "@octokit/types": "npm:^14.0.0" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/29cd5af5ed04e416d28798a11907ab861dc73c0af47f8c9c3f4183d81d2e77d88228643825538acc81e7015f78d891f84107929019a673b06a6b38ccea6a24e0 + languageName: node + linkType: hard + +"@octokit/oauth-app@npm:^7.1.6": + version: 7.1.6 + resolution: "@octokit/oauth-app@npm:7.1.6" + dependencies: + "@octokit/auth-oauth-app": "npm:^8.1.3" + "@octokit/auth-oauth-user": "npm:^5.1.3" + "@octokit/auth-unauthenticated": "npm:^6.1.2" + "@octokit/core": "npm:^6.1.4" + "@octokit/oauth-authorization-url": "npm:^7.1.1" + "@octokit/oauth-methods": "npm:^5.1.4" + "@types/aws-lambda": "npm:^8.10.83" + universal-user-agent: "npm:^7.0.0" + checksum: 10c0/75264f6cb9336baf815bb85ea450d69c5d33eb70754065c9c5307b10d0efec2d76bb17c7788346dfb8b5e4ad0205e6df841b9c709eaf077390bba793787841f5 + languageName: node + linkType: hard + +"@octokit/oauth-authorization-url@npm:^7.0.0, @octokit/oauth-authorization-url@npm:^7.1.1": + version: 7.1.1 + resolution: "@octokit/oauth-authorization-url@npm:7.1.1" + checksum: 10c0/a2723dde503693d4b0793bc43988d7445bdbd1a4e4994f4e94e635816c87b12a3058609eb5982d91783ddf6cdf663ccdb954b5d05efdc59cb66bc0456d7ba370 + languageName: node + linkType: hard + +"@octokit/oauth-methods@npm:^5.1.4, @octokit/oauth-methods@npm:^5.1.5": + version: 5.1.5 + resolution: "@octokit/oauth-methods@npm:5.1.5" + dependencies: + "@octokit/oauth-authorization-url": "npm:^7.0.0" + "@octokit/request": "npm:^9.2.3" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/2b1c5f9ef0fcdb2f19fd864303a67bdda296fcb2cc058efc51bf3fbb3cb582475a5532eab05bd7a57277607730fa4a7796431564693fffdb9c4cb2b471484a5c + languageName: node + linkType: hard + "@octokit/openapi-types@npm:^24.2.0": version: 24.2.0 resolution: "@octokit/openapi-types@npm:24.2.0" @@ -2285,6 +2435,29 @@ __metadata: languageName: node linkType: hard +"@octokit/openapi-types@npm:^25.0.0": + version: 25.0.0 + resolution: "@octokit/openapi-types@npm:25.0.0" + checksum: 10c0/59c9e5998e08cecec155b776c93d8f6f88ab1a812add61cc65f3de8f3744201565545eac308083d18c9fa330a4381a27bcd771a311ac0348d3590a00f333f233 + languageName: node + linkType: hard + +"@octokit/openapi-webhooks-types@npm:11.0.0": + version: 11.0.0 + resolution: "@octokit/openapi-webhooks-types@npm:11.0.0" + checksum: 10c0/cea59ba6b976a242fa4e6bedfab7e6fc3437381557d2c1876ca3aea5f6d4231559a378f9bc35e09593cb2d466afb4a2415be66b960f3e0a38b65b8b9f22ff90e + languageName: node + linkType: hard + +"@octokit/plugin-paginate-graphql@npm:^5.2.4": + version: 5.2.4 + resolution: "@octokit/plugin-paginate-graphql@npm:5.2.4" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/30601cf11d78e5683098d68fbff071a3d20b1e23758f40b1f43fbc93f69b3d0e07f2c9aaaaee113e586af7f604e809ba93702d932b1a4ea65998c7ab39a40a54 + languageName: node + linkType: hard + "@octokit/plugin-paginate-rest@npm:^11.4.2": version: 11.6.0 resolution: "@octokit/plugin-paginate-rest@npm:11.6.0" @@ -2296,6 +2469,17 @@ __metadata: languageName: node linkType: hard +"@octokit/plugin-paginate-rest@npm:^12.0.0": + version: 12.0.0 + resolution: "@octokit/plugin-paginate-rest@npm:12.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/4ed04e8c111ac8cfb0692f917fe09ff6484b7436f0605c661e8051c6fb281c0260c9b067c1422529b948e53b22221b0f7664e2d10a28dcd9db14465c02c7182f + languageName: node + linkType: hard + "@octokit/plugin-request-log@npm:^5.3.1": version: 5.3.1 resolution: "@octokit/plugin-request-log@npm:5.3.1" @@ -2316,6 +2500,42 @@ __metadata: languageName: node linkType: hard +"@octokit/plugin-rest-endpoint-methods@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:14.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/c3f26c5277d4aa0c898d8fdbf84326943ea80496e6f60ae34834415384ab629e1e3702d1ed82d40c31a7370edfcb5fa9fe434b0357b302b3be309879bad1d4e6 + languageName: node + linkType: hard + +"@octokit/plugin-retry@npm:^7.2.1": + version: 7.2.1 + resolution: "@octokit/plugin-retry@npm:7.2.1" + dependencies: + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ">=6" + checksum: 10c0/0cac8aa32bfe9612cbaba36f51382c69c882ef7927e1deb4b166fe664959887e952205adbf8b9ff461e0128ac3e1bd807ca58e38041a55ddb3214397584f0406 + languageName: node + linkType: hard + +"@octokit/plugin-throttling@npm:^10.0.0": + version: 10.0.0 + resolution: "@octokit/plugin-throttling@npm:10.0.0" + dependencies: + "@octokit/types": "npm:^14.0.0" + bottleneck: "npm:^2.15.3" + peerDependencies: + "@octokit/core": ^6.1.3 + checksum: 10c0/60294f89bcc03b034fce557dffcb0dba1b02d1fa1834b3b4f542f8635750df4bd23fc143f7c71f802a22a1aaed089c5da77979e6803abe11908b1c91aa1ba419 + languageName: node + linkType: hard + "@octokit/request-error@npm:^6.1.7": version: 6.1.7 resolution: "@octokit/request-error@npm:6.1.7" @@ -2325,6 +2545,15 @@ __metadata: languageName: node linkType: hard +"@octokit/request-error@npm:^6.1.8": + version: 6.1.8 + resolution: "@octokit/request-error@npm:6.1.8" + dependencies: + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/02aa5bfebb5b1b9e152558b4a6f4f7dcb149b41538778ffe0fce3395fd0da5c0862311a78e94723435667581b2a58a7cefa458cf7aa19ae2948ae419276f7ee1 + languageName: node + linkType: hard + "@octokit/request@npm:^9.2.1, @octokit/request@npm:^9.2.2": version: 9.2.2 resolution: "@octokit/request@npm:9.2.2" @@ -2338,6 +2567,19 @@ __metadata: languageName: node linkType: hard +"@octokit/request@npm:^9.2.3": + version: 9.2.3 + resolution: "@octokit/request@npm:9.2.3" + dependencies: + "@octokit/endpoint": "npm:^10.1.4" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + fast-content-type-parse: "npm:^2.0.0" + universal-user-agent: "npm:^7.0.2" + checksum: 10c0/96067fc9a5f30f2faa00f08015309930561c3ea0536b543e1c91c475f965eabc81c48fc27d401681ebdb3f6c1acc5d46eaa69163ab98b0faa08be1c02ea5b684 + languageName: node + linkType: hard + "@octokit/rest@npm:^21.0.2": version: 21.1.1 resolution: "@octokit/rest@npm:21.1.1" @@ -2359,6 +2601,40 @@ __metadata: languageName: node linkType: hard +"@octokit/types@npm:^14.0.0": + version: 14.0.0 + resolution: "@octokit/types@npm:14.0.0" + dependencies: + "@octokit/openapi-types": "npm:^25.0.0" + checksum: 10c0/c82da635fe99f265dbef7bf954d45a23ca7ce9c6fc9a8478c247b5435799e5d0eab3ff42f085785ee0882b2de293cab0ab831b379c66f41d00b78412df850ba4 + languageName: node + linkType: hard + +"@octokit/webhooks-definitions@octokit/webhooks": + version: 0.0.0-development + resolution: "@octokit/webhooks-definitions@https://github.com/octokit/webhooks.git#commit=256fdd82b484f173ac57f14e8c444fecc23c9d80" + checksum: 10c0/ff90b65b388b3f5ce80a0aa47c6ceb98578f8df6364799db5fc17ffadbbe7e533a7f2c548b279ebd4780e00a2cdcc5f07093b0add975d014af2722496413f75f + languageName: node + linkType: hard + +"@octokit/webhooks-methods@npm:^5.1.1": + version: 5.1.1 + resolution: "@octokit/webhooks-methods@npm:5.1.1" + checksum: 10c0/837aa49dbc7f8bc01448f4eaea32cfb0c1cbfa0b4ac570f7bcda385c71f43e4be05e91a3fb63708448410b27e246570fde42056b6b87d755154d84eff5a6500c + languageName: node + linkType: hard + +"@octokit/webhooks@npm:^13.6.1, @octokit/webhooks@npm:^13.8.2": + version: 13.8.2 + resolution: "@octokit/webhooks@npm:13.8.2" + dependencies: + "@octokit/openapi-webhooks-types": "npm:11.0.0" + "@octokit/request-error": "npm:^6.1.7" + "@octokit/webhooks-methods": "npm:^5.1.1" + checksum: 10c0/5053ab9cff8afe4cd6e7d7a41af3f1b0c0ccc5058482577aa82bb8e5003f3002896c68bd59d7da363204c2d6baa4cca73ed684bb7a5a9286ca4b43172cd1fe9e + languageName: node + linkType: hard + "@opentelemetry/api-logs@npm:0.57.2": version: 0.57.2 resolution: "@opentelemetry/api-logs@npm:0.57.2" @@ -5226,6 +5502,24 @@ __metadata: languageName: unknown linkType: soft +"@sourcebot/review-agent@workspace:packages/agents/review-agent": + version: 0.0.0-use.local + resolution: "@sourcebot/review-agent@workspace:packages/agents/review-agent" + dependencies: + "@octokit/webhooks": "npm:^13.8.2" + "@octokit/webhooks-definitions": octokit/webhooks + "@types/node": "npm:^22.15.8" + dotenv: "npm:^16.5.0" + octokit: "npm:^4.1.3" + openai: "npm:^4.97.0" + parse-diff: "npm:^0.11.1" + smee-client: "npm:^3.1.1" + typescript: "npm:^5.8.3" + zod: "npm:^3.24.4" + zod-to-json-schema: "npm:^3.24.5" + languageName: unknown + linkType: soft + "@sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas": version: 0.0.0-use.local resolution: "@sourcebot/schemas@workspace:packages/schemas" @@ -5549,6 +5843,13 @@ __metadata: languageName: node linkType: hard +"@types/aws-lambda@npm:^8.10.83": + version: 8.10.149 + resolution: "@types/aws-lambda@npm:8.10.149" + checksum: 10c0/9ef41214a1b69fa30d89cab575429793f1ccc49cfe3a7aa75228aef31a202bb6d2c306a5b33def61ef29e0d0a1a324412bf6a4fa5a615e61117685584a394047 + languageName: node + linkType: hard + "@types/body-parser@npm:*": version: 1.19.5 resolution: "@types/body-parser@npm:1.19.5" @@ -5692,6 +5993,16 @@ __metadata: languageName: node linkType: hard +"@types/node-fetch@npm:^2.6.4": + version: 2.6.12 + resolution: "@types/node-fetch@npm:2.6.12" + dependencies: + "@types/node": "npm:*" + form-data: "npm:^4.0.0" + checksum: 10c0/7693acad5499b7df2d1727d46cff092a63896dc04645f36b973dd6dd754a59a7faba76fcb777bdaa35d80625c6a9dd7257cca9c401a4bab03b04480cda7fd1af + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=8.1.0, @types/node@npm:^22.7.5": version: 22.13.11 resolution: "@types/node@npm:22.13.11" @@ -5701,6 +6012,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.11.18": + version: 18.19.100 + resolution: "@types/node@npm:18.19.100" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/5524303171eee6788df45d736f5783b5bea27803a596b9cd5669f45487a619e5d8d41d56dd55b8c85c677ffd7c045edd8daea8c4b37e70290bee2a482fc605f6 + languageName: node + linkType: hard + "@types/node@npm:^20": version: 20.17.25 resolution: "@types/node@npm:20.17.25" @@ -5710,6 +6030,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^22.15.8": + version: 22.15.17 + resolution: "@types/node@npm:22.15.17" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/fb92aa10b628683c5b965749f955bc2322485ecb0ea6c2f4cae5f2c7537a16834607e67083a9e9281faaae8d7dee9ada8d6a5c0de9a52c17d82912ef00c0fdd4 + languageName: node + linkType: hard + "@types/nodemailer@npm:^6.4.17": version: 6.4.17 resolution: "@types/nodemailer@npm:6.4.17" @@ -6298,6 +6627,15 @@ __metadata: languageName: node linkType: hard +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: "npm:^5.0.0" + checksum: 10c0/90ccc50f010250152509a344eb2e71977fbf8db0ab8f1061197e3275ddf6c61a41a6edfd7b9409c664513131dd96e962065415325ef23efa5db931b382d24ca5 + languageName: node + linkType: hard + "accepts@npm:~1.3.4, accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -6351,6 +6689,15 @@ __metadata: languageName: node linkType: hard +"agentkeepalive@npm:^4.2.1": + version: 4.6.0 + resolution: "agentkeepalive@npm:4.6.0" + dependencies: + humanize-ms: "npm:^1.2.1" + checksum: 10c0/235c182432f75046835b05f239708107138a40103deee23b6a08caee5136873709155753b394ec212e49e60e94a378189562cb01347765515cff61b692c69187 + languageName: node + linkType: hard + "ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -6738,6 +7085,13 @@ __metadata: languageName: node linkType: hard +"bottleneck@npm:^2.15.3": + version: 2.19.5 + resolution: "bottleneck@npm:2.19.5" + checksum: 10c0/b0f72e45b2e0f56a21ba720183f16bef8e693452fb0495d997fa354e42904353a94bd8fd429868e6751bc85e54b6755190519eed5a0ae0a94a5185209ae7c6d0 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -7410,6 +7764,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.0.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + "commander@npm:^2.19.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -7923,6 +8284,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.5.0": + version: 16.5.0 + resolution: "dotenv@npm:16.5.0" + checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9 + languageName: node + linkType: hard + "dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -8873,6 +9241,29 @@ __metadata: languageName: node linkType: hard +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 10c0/0255d9f936215fd206156fd4caa9e8d35e62075d720dc7d847e89b417e5e62cf1ce6c9b4e0a1633a9256de0efefaf9f8d26924b1f3c8620cffb9db78e7d3076b + languageName: node + linkType: hard + +"eventsource-parser@npm:^3.0.1": + version: 3.0.1 + resolution: "eventsource-parser@npm:3.0.1" + checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0 + languageName: node + linkType: hard + +"eventsource@npm:^3.0.5": + version: 3.0.6 + resolution: "eventsource@npm:3.0.6" + dependencies: + eventsource-parser: "npm:^3.0.1" + checksum: 10c0/074d865ea1c7e29e3243f85a13306e89fca2d775b982dca03fa6bfa75c56827fa89cf1ab9e730db24bd6b104cbdcae074f2b37ba498874e9dd9710fbff4979bb + languageName: node + linkType: hard + "expect-type@npm:^1.1.0": version: 1.2.0 resolution: "expect-type@npm:1.2.0" @@ -9120,6 +9511,13 @@ __metadata: languageName: node linkType: hard +"form-data-encoder@npm:1.7.2": + version: 1.7.2 + resolution: "form-data-encoder@npm:1.7.2" + checksum: 10c0/56553768037b6d55d9de524f97fe70555f0e415e781cb56fc457a68263de3d40fadea2304d4beef2d40b1a851269bd7854e42c362107071892cb5238debe9464 + languageName: node + linkType: hard + "form-data@npm:^4.0.0": version: 4.0.2 resolution: "form-data@npm:4.0.2" @@ -9132,6 +9530,16 @@ __metadata: languageName: node linkType: hard +"formdata-node@npm:^4.3.2": + version: 4.4.1 + resolution: "formdata-node@npm:4.4.1" + dependencies: + node-domexception: "npm:1.0.0" + web-streams-polyfill: "npm:4.0.0-beta.3" + checksum: 10c0/74151e7b228ffb33b565cec69182694ad07cc3fdd9126a8240468bb70a8ba66e97e097072b60bcb08729b24c7ce3fd3e0bd7f1f80df6f9f662b9656786e76f6a + languageName: node + linkType: hard + "forwarded-parse@npm:2.1.2": version: 2.1.2 resolution: "forwarded-parse@npm:2.1.2" @@ -9701,6 +10109,15 @@ __metadata: languageName: node linkType: hard +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: "npm:^2.0.0" + checksum: 10c0/f34a2c20161d02303c2807badec2f3b49cbfbbb409abd4f95a07377ae01cfe6b59e3d15ac609cffcd8f2521f0eb37b7e1091acf65da99aa2a4f1ad63c21e7e7a + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -11061,7 +11478,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.1.1, ms@npm:^2.1.3": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 @@ -11347,6 +11764,13 @@ __metadata: languageName: node linkType: hard +"node-domexception@npm:1.0.0": + version: 1.0.0 + resolution: "node-domexception@npm:1.0.0" + checksum: 10c0/5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b + languageName: node + linkType: hard + "node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -11576,6 +12000,24 @@ __metadata: languageName: node linkType: hard +"octokit@npm:^4.1.3": + version: 4.1.3 + resolution: "octokit@npm:4.1.3" + dependencies: + "@octokit/app": "npm:^15.1.6" + "@octokit/core": "npm:^6.1.5" + "@octokit/oauth-app": "npm:^7.1.6" + "@octokit/plugin-paginate-graphql": "npm:^5.2.4" + "@octokit/plugin-paginate-rest": "npm:^12.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^14.0.0" + "@octokit/plugin-retry": "npm:^7.2.1" + "@octokit/plugin-throttling": "npm:^10.0.0" + "@octokit/request-error": "npm:^6.1.8" + "@octokit/types": "npm:^14.0.0" + checksum: 10c0/a35352dff1d7bacf8123e491489650c29b830a765f78113064edd6bd0aad0e58e8933874394892972fb8b0e43369b6f7a7b354426c61f4d9fc37408891fde582 + languageName: node + linkType: hard + "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -11623,6 +12065,31 @@ __metadata: languageName: node linkType: hard +"openai@npm:^4.97.0": + version: 4.98.0 + resolution: "openai@npm:4.98.0" + dependencies: + "@types/node": "npm:^18.11.18" + "@types/node-fetch": "npm:^2.6.4" + abort-controller: "npm:^3.0.0" + agentkeepalive: "npm:^4.2.1" + form-data-encoder: "npm:1.7.2" + formdata-node: "npm:^4.3.2" + node-fetch: "npm:^2.6.7" + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + bin: + openai: bin/cli + checksum: 10c0/399f07cd04c47d05be89cbcf3edb98650177ca09322ae664ade347bd56830cc0423834b4635341950bd9af59fdf203d3fad1de7927f4d8d2449b08c642c0ee3e + languageName: node + linkType: hard + "openapi-fetch@npm:^0.13.4": version: 0.13.5 resolution: "openapi-fetch@npm:0.13.5" @@ -11722,6 +12189,13 @@ __metadata: languageName: node linkType: hard +"parse-diff@npm:^0.11.1": + version: 0.11.1 + resolution: "parse-diff@npm:0.11.1" + checksum: 10c0/b8a488039f535e0ddaf1cfd4a13c2adc6d142f4e6263de6dc603f9762b59da89703a8f0dc8aacb7c7c66cdbdd2d075a57c63813fb74f66c9b8f5fe8ad2e89689 + languageName: node + linkType: hard + "parse-json@npm:^4.0.0": version: 4.0.0 resolution: "parse-json@npm:4.0.0" @@ -13583,6 +14057,20 @@ __metadata: languageName: node linkType: hard +"smee-client@npm:^3.1.1": + version: 3.1.1 + resolution: "smee-client@npm:3.1.1" + dependencies: + commander: "npm:^12.0.0" + eventsource: "npm:^3.0.5" + undici: "npm:^6.19.8" + validator: "npm:^13.11.0" + bin: + smee: bin/smee.js + checksum: 10c0/24fd526330d0d0b0763de574c4d6595739eb0413882d617dce07ca35f71fd732c96a9ca723d394e1e9e938b4ef004a9d69797cb700ec72a6fc69396c8128d245 + languageName: node + linkType: hard + "smtp-address-parser@npm:1.0.10": version: 1.0.10 resolution: "smtp-address-parser@npm:1.0.10" @@ -14247,6 +14735,13 @@ __metadata: languageName: node linkType: hard +"toad-cache@npm:^3.7.0": + version: 3.7.0 + resolution: "toad-cache@npm:3.7.0" + checksum: 10c0/7dae2782ee20b22c9798bb8b71dec7ec6a0091021d2ea9dd6e8afccab6b65b358fdba49a02209fac574499702e2c000660721516c87c2538d1b2c0ba03e8c0c3 + languageName: node + linkType: hard + "toidentifier@npm:1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" @@ -14479,6 +14974,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A^5#optional!builtin, typescript@patch:typescript@npm%3A^5.6.2#optional!builtin, typescript@patch:typescript@npm%3A^5.7.3#optional!builtin": version: 5.8.2 resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin::version=5.8.2&hash=5786d5" @@ -14489,6 +14994,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb + languageName: node + linkType: hard + "ua-parser-js@npm:^1.0.33": version: 1.0.40 resolution: "ua-parser-js@npm:1.0.40" @@ -14517,6 +15032,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + "undici-types@npm:~6.19.2": version: 6.19.8 resolution: "undici-types@npm:6.19.8" @@ -14531,6 +15053,20 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard + +"undici@npm:^6.19.8": + version: 6.21.2 + resolution: "undici@npm:6.21.2" + checksum: 10c0/799bbc02b77dda9b6b12d56d2620a3a4d4cf087908d6a548acc3ce32f21b5c27467f75c2c4b30fab281daf341210be3d685e8fe99854288de541715ae5735027 + languageName: node + linkType: hard + "unique-filename@npm:^4.0.0": version: 4.0.0 resolution: "unique-filename@npm:4.0.0" @@ -14597,6 +15133,13 @@ __metadata: languageName: node linkType: hard +"universal-github-app-jwt@npm:^2.2.0": + version: 2.2.2 + resolution: "universal-github-app-jwt@npm:2.2.2" + checksum: 10c0/7ae5f031fb89c01a4407459b764c5e6341d725d436e1ceec161f9b754dd4883d9704cc8de53d5b6314b7e1bef8dbc7561799fc23001e706f213d468c17026fb6 + languageName: node + linkType: hard + "universal-user-agent@npm:^7.0.0, universal-user-agent@npm:^7.0.2": version: 7.0.2 resolution: "universal-user-agent@npm:7.0.2" @@ -14728,6 +15271,13 @@ __metadata: languageName: node linkType: hard +"validator@npm:^13.11.0": + version: 13.15.0 + resolution: "validator@npm:13.15.0" + checksum: 10c0/0f13fd7031ac575e8d7828431da8ef5859bac6a38ee65e1d7fdd367dbf1c3d94d95182aecc3183f7fa7a30ff4474bf864d1aff54707620227a2cdbfd36d894c2 + languageName: node + linkType: hard + "vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -14911,6 +15461,13 @@ __metadata: languageName: node linkType: hard +"web-streams-polyfill@npm:4.0.0-beta.3": + version: 4.0.0-beta.3 + resolution: "web-streams-polyfill@npm:4.0.0-beta.3" + checksum: 10c0/a9596779db2766990117ed3a158e0b0e9f69b887a6d6ba0779940259e95f99dc3922e534acc3e5a117b5f5905300f527d6fbf8a9f0957faf1d8e585ce3452e8e + languageName: node + linkType: hard + "web-vitals@npm:^4.2.4": version: 4.2.4 resolution: "web-vitals@npm:4.2.4" @@ -15249,6 +15806,15 @@ __metadata: languageName: node linkType: hard +"zod-to-json-schema@npm:^3.24.5": + version: 3.24.5 + resolution: "zod-to-json-schema@npm:3.24.5" + peerDependencies: + zod: ^3.24.1 + checksum: 10c0/0745b94ba53e652d39f262641cdeb2f75d24339fb6076a38ce55bcf53d82dfaea63adf524ebc5f658681005401687f8e9551c4feca7c4c882e123e66091dfb90 + languageName: node + linkType: hard + "zod@npm:^3.24.2": version: 3.24.2 resolution: "zod@npm:3.24.2" @@ -15256,6 +15822,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.24.4": + version: 3.24.4 + resolution: "zod@npm:3.24.4" + checksum: 10c0/ab3112f017562180a41a0f83d870b333677f7d6b77f106696c56894567051b91154714a088149d8387a4f50806a2520efcb666f108cd384a35c236a191186d91 + languageName: node + linkType: hard + "zwitch@npm:^2.0.4": version: 2.0.4 resolution: "zwitch@npm:2.0.4" From a81cd2494f90d03ead5c9229c18c6de3820bc53a Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 11:24:51 -0700 Subject: [PATCH 04/15] move review agent to web --- package.json | 1 + packages/agents/review-agent/.gitignore | 136 ------------------ packages/agents/review-agent/package.json | 26 ---- packages/agents/review-agent/src/app.ts | 62 -------- packages/agents/review-agent/src/env.ts | 13 -- packages/agents/review-agent/tsconfig.json | 114 --------------- packages/web/package.json | 6 +- .../app/[domain]/browse/[...path]/page.tsx | 3 +- .../web/src/app/api/(server)/webhook/route.ts | 62 ++++++++ packages/web/src/env.mjs | 6 + .../src/features/agents/review-agent/app.ts | 27 ++++ .../review-agent}/nodes/fetch_file_content.ts | 6 +- .../nodes/generate_diff_review_prompt.ts | 2 +- .../nodes/generate_pr_reviews.ts | 10 +- .../review-agent}/nodes/github_pr_parser.ts | 2 +- .../nodes/github_push_pr_reviews.ts | 11 +- .../nodes/invoke_diff_review_llm.ts | 20 +-- .../features/agents/review-agent}/types.ts | 0 yarn.lock | 134 +---------------- 19 files changed, 135 insertions(+), 506 deletions(-) delete mode 100644 packages/agents/review-agent/.gitignore delete mode 100644 packages/agents/review-agent/package.json delete mode 100644 packages/agents/review-agent/src/app.ts delete mode 100644 packages/agents/review-agent/src/env.ts delete mode 100644 packages/agents/review-agent/tsconfig.json create mode 100644 packages/web/src/app/api/(server)/webhook/route.ts create mode 100644 packages/web/src/features/agents/review-agent/app.ts rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/fetch_file_content.ts (85%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/generate_diff_review_prompt.ts (96%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/generate_pr_reviews.ts (81%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/github_pr_parser.ts (97%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/github_push_pr_reviews.ts (76%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/nodes/invoke_diff_review_llm.ts (68%) rename packages/{agents/review-agent/src => web/src/features/agents/review-agent}/types.ts (100%) diff --git a/package.json b/package.json index e98f6628..9b193c70 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev:zoekt": "yarn with-env zoekt-webserver -index .sourcebot/index -rpc", "dev:backend": "yarn with-env yarn workspace @sourcebot/backend dev:watch", "dev:web": "yarn with-env yarn workspace @sourcebot/web dev", + "dev:review-agent": "yarn with-env yarn workspace @sourcebot/review-agent dev", "dev:prisma:migrate:dev": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:dev", "dev:prisma:studio": "yarn with-env yarn workspace @sourcebot/db prisma:studio", "dev:prisma:migrate:reset": "yarn with-env yarn workspace @sourcebot/db prisma:migrate:reset", diff --git a/packages/agents/review-agent/.gitignore b/packages/agents/review-agent/.gitignore deleted file mode 100644 index 1170717c..00000000 --- a/packages/agents/review-agent/.gitignore +++ /dev/null @@ -1,136 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* diff --git a/packages/agents/review-agent/package.json b/packages/agents/review-agent/package.json deleted file mode 100644 index 9fe1fc20..00000000 --- a/packages/agents/review-agent/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@sourcebot/review-agent", - "version": "1.0.0", - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "build": "tsc", - "start": "node dist/app.js" - }, - "dependencies": { - "@octokit/webhooks": "^13.8.2", - "@octokit/webhooks-definitions": "octokit/webhooks", - "dotenv": "^16.5.0", - "octokit": "^4.1.3", - "openai": "^4.97.0", - "parse-diff": "^0.11.1", - "zod": "^3.24.4", - "zod-to-json-schema": "^3.24.5" - }, - "devDependencies": { - "@types/node": "^22.15.8", - "smee-client": "^3.1.1", - "typescript": "^5.8.3" - } -} diff --git a/packages/agents/review-agent/src/app.ts b/packages/agents/review-agent/src/app.ts deleted file mode 100644 index 2765e65b..00000000 --- a/packages/agents/review-agent/src/app.ts +++ /dev/null @@ -1,62 +0,0 @@ -import dotenv from 'dotenv'; -import { App, Octokit } from "octokit"; -import { createNodeMiddleware } from "@octokit/webhooks"; -import fs from "fs"; -import http from "http"; -import { WebhookEventDefinition } from "@octokit/webhooks/types"; -import { generate_pr_reviews } from './nodes/generate_pr_reviews.js'; -import { github_push_pr_reviews } from './nodes/github_push_pr_reviews.js'; -import { github_pr_parser } from './nodes/github_pr_parser.js'; - -dotenv.config(); -const appId = process.env.APP_ID as string; -const webhookSecret = process.env.WEBHOOK_SECRET as string; -const privateKeyPath = process.env.PRIVATE_KEY_PATH as string; - -const privateKey = fs.readFileSync(privateKeyPath, "utf8"); - -const app = new App({ - appId: appId, - privateKey: privateKey, - webhooks: { - secret: webhookSecret - }, -}); - -const rules = [ - "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.", - "Do NOT provide any advice that is not actionable or directly related to the changes.", - "Focus solely on offering specific, objective insights based on the given context and refrain from making broad comments about potential impacts on the system or question intentions behind the changes.", - "Keep comments concise and to the point. Every comment must highlight a specific issue and provide a clear and actionable solution to the developer.", - "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"." -] - -async function handlePullRequestOpened({ - octokit, - payload, -}: { - octokit: Octokit; - payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">; -}) { - console.log(`Received a pull request event for #${payload.pull_request.number}`); - - const prPayload = await github_pr_parser(octokit, payload); - const fileDiffReviews = await generate_pr_reviews(prPayload, rules); - await github_push_pr_reviews(app, prPayload, fileDiffReviews); -} - -app.webhooks.on("pull_request.opened", handlePullRequestOpened); -app.webhooks.on("pull_request.synchronize", handlePullRequestOpened); - -app.webhooks.onError((error) => { - console.error(error); -}); - -const port = 3050; -const path = "/api/webhook"; - -const middleware = createNodeMiddleware(app.webhooks, { path }); - -http.createServer(middleware).listen(port, () => { - console.log(`Http server for review agent running on port ${port} ${path}`); -}); diff --git a/packages/agents/review-agent/src/env.ts b/packages/agents/review-agent/src/env.ts deleted file mode 100644 index fd320618..00000000 --- a/packages/agents/review-agent/src/env.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createEnv } from "@t3-oss/env-core"; -import { z } from "zod"; - -export const env = createEnv({ - server: { - GITHUB_APP_ID: z.string(), - GITHUB_APP_WEBHOOK_SECRET: z.string(), - GITHUB_APP_PRIVATE_KEY_PATH: z.string(), - }, - runtimeEnv: process.env, - emptyStringAsUndefined: true, - skipValidation: process.env.SKIP_ENV_VALIDATION === "1", -}) \ No newline at end of file diff --git a/packages/agents/review-agent/tsconfig.json b/packages/agents/review-agent/tsconfig.json deleted file mode 100644 index e2e61a95..00000000 --- a/packages/agents/review-agent/tsconfig.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "libReplacement": true, /* Enable lib replacement. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "NodeNext", /* Specify what module code is generated. */ - "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ - // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/web/package.json b/packages/web/package.json index 769b0e91..8ce9307c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -118,6 +118,9 @@ "next-auth": "^5.0.0-beta.25", "next-themes": "^0.3.0", "nodemailer": "^6.10.0", + "octokit": "^4.1.3", + "openai": "^4.98.0", + "parse-diff": "^0.11.1", "posthog-js": "^1.161.5", "pretty-bytes": "^6.1.1", "psl": "^1.15.0", @@ -135,7 +138,8 @@ "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^3.1.0", - "zod": "^3.24.2" + "zod": "^3.24.2", + "zod-to-json-schema": "^3.24.5" }, "devDependencies": { "@types/micromatch": "^4.0.9", diff --git a/packages/web/src/app/[domain]/browse/[...path]/page.tsx b/packages/web/src/app/[domain]/browse/[...path]/page.tsx index 89e4e293..afa10999 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/page.tsx +++ b/packages/web/src/app/[domain]/browse/[...path]/page.tsx @@ -3,7 +3,8 @@ import { TopBar } from "@/app/[domain]/components/topBar"; import { Separator } from '@/components/ui/separator'; import { getFileSource } from '@/features/search/fileSourceApi'; import { listRepositories } from '@/features/search/listReposApi'; -import { base64Decode, isServiceError } from "@/lib/utils"; +import { isServiceError } from "@/lib/utils"; +import { base64Decode } from "@/lib/utils"; import { CodePreview } from "./codePreview"; import { ErrorCode } from "@/lib/errorCodes"; import { LuFileX2, LuBookX } from "react-icons/lu"; diff --git a/packages/web/src/app/api/(server)/webhook/route.ts b/packages/web/src/app/api/(server)/webhook/route.ts new file mode 100644 index 00000000..5c24a544 --- /dev/null +++ b/packages/web/src/app/api/(server)/webhook/route.ts @@ -0,0 +1,62 @@ +'use server'; + +import { NextRequest } from "next/server"; +import { App } from "octokit"; +import { WebhookEventDefinition } from "@octokit/webhooks/types"; +import { env } from "@/env.mjs"; +import { processGitHubPullRequest } from "@/features/agents/review-agent/app"; +import fs from "fs"; + +let githubApp: App | undefined; +if (env.GITHUB_APP_ID && env.GITHUB_APP_WEBHOOK_SECRET && env.GITHUB_APP_PRIVATE_KEY_PATH) { + try { + const privateKey = fs.readFileSync(env.GITHUB_APP_PRIVATE_KEY_PATH, "utf8"); + githubApp = new App({ + appId: env.GITHUB_APP_ID, + privateKey: privateKey, + webhooks: { + secret: env.GITHUB_APP_WEBHOOK_SECRET, + }, + }); + } catch (error) { + console.error(`Error initializing GitHub app: ${error}`); + } +} + +function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> { + return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize"); +} + +export const POST = async (request: NextRequest) => { + const body = await request.json(); + const headers = Object.fromEntries(request.headers.entries()); + + console.log('Webhook request headers:', headers); + console.log('Webhook request body:', JSON.stringify(body, null, 2)); + + const githubEvent = headers['x-github-event']; + if (githubEvent) { + console.log('GitHub event received:', githubEvent); + + if (!githubApp) { + console.warn('Received GitHub webhook event but GitHub app env vars are not set'); + return Response.json({ status: 'ok' }); + } + + if (isPullRequestEvent(githubEvent, body)) { + console.log('Received pull request event:', body); + + if (!body.installation) { + console.error('Received github pull request event but installation is not present'); + return Response.json({ status: 'ok' }); + } + + const installationId = body.installation.id; + const octokit = await githubApp.getInstallationOctokit(installationId); + + await processGitHubPullRequest(octokit, body); + } + } + + return Response.json({ status: 'ok' }); +} \ No newline at end of file diff --git a/packages/web/src/env.mjs b/packages/web/src/env.mjs index e75cc5d6..575de62d 100644 --- a/packages/web/src/env.mjs +++ b/packages/web/src/env.mjs @@ -52,6 +52,12 @@ export const env = createEnv({ // EE License SOURCEBOT_EE_LICENSE_KEY: z.string().optional(), + + // GitHub app for review agent + GITHUB_APP_ID: z.string().optional(), + GITHUB_APP_WEBHOOK_SECRET: z.string().optional(), + GITHUB_APP_PRIVATE_KEY_PATH: z.string().optional(), + OPENAI_API_KEY: z.string().optional(), }, // @NOTE: Please make sure of the following: // - Make sure you destructure all client variables in diff --git a/packages/web/src/features/agents/review-agent/app.ts b/packages/web/src/features/agents/review-agent/app.ts new file mode 100644 index 00000000..ae5a60fb --- /dev/null +++ b/packages/web/src/features/agents/review-agent/app.ts @@ -0,0 +1,27 @@ +import { Octokit } from "octokit"; +import { WebhookEventDefinition } from "@octokit/webhooks/types"; +import { generate_pr_reviews } from "@/features/agents/review-agent/nodes/generate_pr_reviews"; +import { github_push_pr_reviews } from "@/features/agents/review-agent/nodes/github_push_pr_reviews"; +import { github_pr_parser } from "@/features/agents/review-agent/nodes/github_pr_parser"; +import { env } from "@/env.mjs"; + +const rules = [ + "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.", + "Do NOT provide any advice that is not actionable or directly related to the changes.", + "Focus solely on offering specific, objective insights based on the given context and refrain from making broad comments about potential impacts on the system or question intentions behind the changes.", + "Keep comments concise and to the point. Every comment must highlight a specific issue and provide a clear and actionable solution to the developer.", + "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"." +] + +export async function processGitHubPullRequest(octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">) { + console.log(`Received a pull request event for #${payload.pull_request.number}`); + + if (!env.OPENAI_API_KEY) { + console.error("OPENAI_API_KEY is not set, skipping review agent"); + return; + } + + const prPayload = await github_pr_parser(octokit, payload); + const fileDiffReviews = await generate_pr_reviews(prPayload, rules); + await github_push_pr_reviews(octokit, prPayload, fileDiffReviews); +} \ No newline at end of file diff --git a/packages/agents/review-agent/src/nodes/fetch_file_content.ts b/packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts similarity index 85% rename from packages/agents/review-agent/src/nodes/fetch_file_content.ts rename to packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts index 58475849..0d0246d7 100644 --- a/packages/agents/review-agent/src/nodes/fetch_file_content.ts +++ b/packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts @@ -1,6 +1,6 @@ -import { sourcebot_context, sourcebot_pr_payload } from "../types.js"; -import { fileSourceResponseSchema } from "@sourcebot/web/src/features/search/schemas.js"; -import { base64Decode } from "@sourcebot/web/src/lib/utils.js"; +import { sourcebot_context, sourcebot_pr_payload } from "@/features/agents/review-agent/types"; +import { fileSourceResponseSchema } from "@/features/search/schemas"; +import { base64Decode } from "@/lib/utils"; export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { console.log("Executing fetch_file_content"); diff --git a/packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts b/packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts similarity index 96% rename from packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts rename to packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts index 16c22706..1955a2eb 100644 --- a/packages/agents/review-agent/src/nodes/generate_diff_review_prompt.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts @@ -1,4 +1,4 @@ -import { sourcebot_diff, sourcebot_context, sourcebot_diff_review_schema } from "../types.js"; +import { sourcebot_diff, sourcebot_context, sourcebot_diff_review_schema } from "@/features/agents/review-agent/types"; import { zodToJsonSchema } from "zod-to-json-schema"; export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => { diff --git a/packages/agents/review-agent/src/nodes/generate_pr_reviews.ts b/packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts similarity index 81% rename from packages/agents/review-agent/src/nodes/generate_pr_reviews.ts rename to packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts index 92754d03..643224b6 100644 --- a/packages/agents/review-agent/src/nodes/generate_pr_reviews.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts @@ -1,7 +1,7 @@ -import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "../types.js"; -import { generate_diff_review_prompt } from "./generate_diff_review_prompt.js"; -import { invoke_diff_review_llm } from "./invoke_diff_review_llm.js"; -import { fetch_file_content } from "./fetch_file_content.js"; +import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "@/features/agents/review-agent/types"; +import { generate_diff_review_prompt } from "@/features/agents/review-agent/nodes/generate_diff_review_prompt"; +import { invoke_diff_review_llm } from "@/features/agents/review-agent/nodes/invoke_diff_review_llm"; +import { fetch_file_content } from "@/features/agents/review-agent/nodes/fetch_file_content"; export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { console.log("Executing generate_pr_reviews"); @@ -29,7 +29,7 @@ export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rule const prompt = await generate_diff_review_prompt(diff, context, rules); - const diffReview = await invoke_diff_review_llm(prompt, file_diff.to); + const diffReview = await invoke_diff_review_llm(prompt); reviews.push(diffReview); } catch (error) { console.error(`Error fetching file content for ${file_diff.to}: ${error}`); diff --git a/packages/agents/review-agent/src/nodes/github_pr_parser.ts b/packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts similarity index 97% rename from packages/agents/review-agent/src/nodes/github_pr_parser.ts rename to packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts index c248bcbc..c441c5fb 100644 --- a/packages/agents/review-agent/src/nodes/github_pr_parser.ts +++ b/packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts @@ -1,4 +1,4 @@ -import { sourcebot_pr_payload, sourcebot_file_diff, sourcebot_diff } from "../types.js" +import { sourcebot_pr_payload, sourcebot_file_diff, sourcebot_diff } from "@/features/agents/review-agent/types"; import { WebhookEventDefinition } from "@octokit/webhooks/types"; import parse from "parse-diff"; import { Octokit } from "octokit"; diff --git a/packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts b/packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts similarity index 76% rename from packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts rename to packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts index db2994d9..33105471 100644 --- a/packages/agents/review-agent/src/nodes/github_push_pr_reviews.ts +++ b/packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts @@ -1,17 +1,14 @@ -import { App } from "octokit"; -import { sourcebot_pr_payload, sourcebot_file_diff_review } from "../types.js"; +import { Octokit } from "octokit"; +import { sourcebot_pr_payload, sourcebot_file_diff_review } from "@/features/agents/review-agent/types"; -export const github_push_pr_reviews = async (app: App, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { +export const github_push_pr_reviews = async (octokit: Octokit, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { console.log("Executing github_push_pr_reviews"); try { - const installationId = pr_payload.installation_id; - const installation = await app.getInstallationOctokit(installationId); - for (const file_diff_review of file_diff_reviews) { for (const review of file_diff_review.reviews) { try { - await installation.rest.pulls.createReviewComment({ + await octokit.rest.pulls.createReviewComment({ owner: pr_payload.owner, repo: pr_payload.repo, pull_number: pr_payload.number, diff --git a/packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts b/packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts similarity index 68% rename from packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts rename to packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts index 13790013..a83ece74 100644 --- a/packages/agents/review-agent/src/nodes/invoke_diff_review_llm.ts +++ b/packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts @@ -1,14 +1,18 @@ import OpenAI from "openai"; -import dotenv from "dotenv"; -import { sourcebot_diff_review_schema, sourcebot_diff_review } from "../types.js"; +import { sourcebot_diff_review_schema, sourcebot_diff_review } from "@/features/agents/review-agent/types"; +import { env } from "@/env.mjs"; -dotenv.config(); -const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, -}); - -export const invoke_diff_review_llm = async (prompt: string, filename: string): Promise => { +export const invoke_diff_review_llm = async (prompt: string): Promise => { console.log("Executing invoke_diff_review_llm"); + + if (!env.OPENAI_API_KEY) { + console.error("OPENAI_API_KEY is not set, skipping review agent"); + throw new Error("OPENAI_API_KEY is not set, skipping review agent"); + } + + const openai = new OpenAI({ + apiKey: env.OPENAI_API_KEY, + }); try { const completion = await openai.chat.completions.create({ diff --git a/packages/agents/review-agent/src/types.ts b/packages/web/src/features/agents/review-agent/types.ts similarity index 100% rename from packages/agents/review-agent/src/types.ts rename to packages/web/src/features/agents/review-agent/types.ts diff --git a/yarn.lock b/yarn.lock index a2b0e936..f1e37a77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2610,13 +2610,6 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks-definitions@octokit/webhooks": - version: 0.0.0-development - resolution: "@octokit/webhooks-definitions@https://github.com/octokit/webhooks.git#commit=256fdd82b484f173ac57f14e8c444fecc23c9d80" - checksum: 10c0/ff90b65b388b3f5ce80a0aa47c6ceb98578f8df6364799db5fc17ffadbbe7e533a7f2c548b279ebd4780e00a2cdcc5f07093b0add975d014af2722496413f75f - languageName: node - linkType: hard - "@octokit/webhooks-methods@npm:^5.1.1": version: 5.1.1 resolution: "@octokit/webhooks-methods@npm:5.1.1" @@ -2624,7 +2617,7 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks@npm:^13.6.1, @octokit/webhooks@npm:^13.8.2": +"@octokit/webhooks@npm:^13.6.1": version: 13.8.2 resolution: "@octokit/webhooks@npm:13.8.2" dependencies: @@ -5502,24 +5495,6 @@ __metadata: languageName: unknown linkType: soft -"@sourcebot/review-agent@workspace:packages/agents/review-agent": - version: 0.0.0-use.local - resolution: "@sourcebot/review-agent@workspace:packages/agents/review-agent" - dependencies: - "@octokit/webhooks": "npm:^13.8.2" - "@octokit/webhooks-definitions": octokit/webhooks - "@types/node": "npm:^22.15.8" - dotenv: "npm:^16.5.0" - octokit: "npm:^4.1.3" - openai: "npm:^4.97.0" - parse-diff: "npm:^0.11.1" - smee-client: "npm:^3.1.1" - typescript: "npm:^5.8.3" - zod: "npm:^3.24.4" - zod-to-json-schema: "npm:^3.24.5" - languageName: unknown - linkType: soft - "@sourcebot/schemas@workspace:*, @sourcebot/schemas@workspace:packages/schemas": version: 0.0.0-use.local resolution: "@sourcebot/schemas@workspace:packages/schemas" @@ -5657,6 +5632,9 @@ __metadata: next-themes: "npm:^0.3.0" nodemailer: "npm:^6.10.0" npm-run-all: "npm:^4.1.5" + octokit: "npm:^4.1.3" + openai: "npm:^4.98.0" + parse-diff: "npm:^0.11.1" postcss: "npm:^8" posthog-js: "npm:^1.161.5" pretty-bytes: "npm:^6.1.1" @@ -5682,6 +5660,7 @@ __metadata: vite-tsconfig-paths: "npm:^5.1.3" vitest: "npm:^2.1.5" zod: "npm:^3.24.2" + zod-to-json-schema: "npm:^3.24.5" languageName: unknown linkType: soft @@ -6030,15 +6009,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^22.15.8": - version: 22.15.17 - resolution: "@types/node@npm:22.15.17" - dependencies: - undici-types: "npm:~6.21.0" - checksum: 10c0/fb92aa10b628683c5b965749f955bc2322485ecb0ea6c2f4cae5f2c7537a16834607e67083a9e9281faaae8d7dee9ada8d6a5c0de9a52c17d82912ef00c0fdd4 - languageName: node - linkType: hard - "@types/nodemailer@npm:^6.4.17": version: 6.4.17 resolution: "@types/nodemailer@npm:6.4.17" @@ -7764,13 +7734,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^12.0.0": - version: 12.1.0 - resolution: "commander@npm:12.1.0" - checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 - languageName: node - linkType: hard - "commander@npm:^2.19.0": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -8284,13 +8247,6 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.5.0": - version: 16.5.0 - resolution: "dotenv@npm:16.5.0" - checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9 - languageName: node - linkType: hard - "dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1": version: 1.0.1 resolution: "dunder-proto@npm:1.0.1" @@ -9248,22 +9204,6 @@ __metadata: languageName: node linkType: hard -"eventsource-parser@npm:^3.0.1": - version: 3.0.1 - resolution: "eventsource-parser@npm:3.0.1" - checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0 - languageName: node - linkType: hard - -"eventsource@npm:^3.0.5": - version: 3.0.6 - resolution: "eventsource@npm:3.0.6" - dependencies: - eventsource-parser: "npm:^3.0.1" - checksum: 10c0/074d865ea1c7e29e3243f85a13306e89fca2d775b982dca03fa6bfa75c56827fa89cf1ab9e730db24bd6b104cbdcae074f2b37ba498874e9dd9710fbff4979bb - languageName: node - linkType: hard - "expect-type@npm:^1.1.0": version: 1.2.0 resolution: "expect-type@npm:1.2.0" @@ -12065,7 +12005,7 @@ __metadata: languageName: node linkType: hard -"openai@npm:^4.97.0": +"openai@npm:^4.98.0": version: 4.98.0 resolution: "openai@npm:4.98.0" dependencies: @@ -14057,20 +13997,6 @@ __metadata: languageName: node linkType: hard -"smee-client@npm:^3.1.1": - version: 3.1.1 - resolution: "smee-client@npm:3.1.1" - dependencies: - commander: "npm:^12.0.0" - eventsource: "npm:^3.0.5" - undici: "npm:^6.19.8" - validator: "npm:^13.11.0" - bin: - smee: bin/smee.js - checksum: 10c0/24fd526330d0d0b0763de574c4d6595739eb0413882d617dce07ca35f71fd732c96a9ca723d394e1e9e938b4ef004a9d69797cb700ec72a6fc69396c8128d245 - languageName: node - linkType: hard - "smtp-address-parser@npm:1.0.10": version: 1.0.10 resolution: "smtp-address-parser@npm:1.0.10" @@ -14974,16 +14900,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.8.3": - version: 5.8.3 - resolution: "typescript@npm:5.8.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48 - languageName: node - linkType: hard - "typescript@patch:typescript@npm%3A^5#optional!builtin, typescript@patch:typescript@npm%3A^5.6.2#optional!builtin, typescript@patch:typescript@npm%3A^5.7.3#optional!builtin": version: 5.8.2 resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin::version=5.8.2&hash=5786d5" @@ -14994,16 +14910,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin": - version: 5.8.3 - resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin::version=5.8.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb - languageName: node - linkType: hard - "ua-parser-js@npm:^1.0.33": version: 1.0.40 resolution: "ua-parser-js@npm:1.0.40" @@ -15053,20 +14959,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.21.0": - version: 6.21.0 - resolution: "undici-types@npm:6.21.0" - checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 - languageName: node - linkType: hard - -"undici@npm:^6.19.8": - version: 6.21.2 - resolution: "undici@npm:6.21.2" - checksum: 10c0/799bbc02b77dda9b6b12d56d2620a3a4d4cf087908d6a548acc3ce32f21b5c27467f75c2c4b30fab281daf341210be3d685e8fe99854288de541715ae5735027 - languageName: node - linkType: hard - "unique-filename@npm:^4.0.0": version: 4.0.0 resolution: "unique-filename@npm:4.0.0" @@ -15271,13 +15163,6 @@ __metadata: languageName: node linkType: hard -"validator@npm:^13.11.0": - version: 13.15.0 - resolution: "validator@npm:13.15.0" - checksum: 10c0/0f13fd7031ac575e8d7828431da8ef5859bac6a38ee65e1d7fdd367dbf1c3d94d95182aecc3183f7fa7a30ff4474bf864d1aff54707620227a2cdbfd36d894c2 - languageName: node - linkType: hard - "vary@npm:^1, vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -15822,13 +15707,6 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.24.4": - version: 3.24.4 - resolution: "zod@npm:3.24.4" - checksum: 10c0/ab3112f017562180a41a0f83d870b333677f7d6b77f106696c56894567051b91154714a088149d8387a4f50806a2520efcb666f108cd384a35c236a191186d91 - languageName: node - linkType: hard - "zwitch@npm:^2.0.4": version: 2.0.4 resolution: "zwitch@npm:2.0.4" From 79dd28837198bf2b85ddf4508ba80e5124734e9c Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 11:40:48 -0700 Subject: [PATCH 05/15] feedback --- packages/web/src/app/api/(server)/webhook/route.ts | 5 ----- .../web/src/features/agents/review-agent/app.ts | 14 ++++++++------ .../{fetch_file_content.ts => fetchFileContent.ts} | 2 +- ...eview_prompt.ts => generateDiffReviewPrompt.ts} | 2 +- ...{generate_pr_reviews.ts => generatePrReview.ts} | 14 +++++++------- .../{github_pr_parser.ts => githubPrParser.ts} | 10 +++++----- ...b_push_pr_reviews.ts => githubPushPrReviews.ts} | 2 +- ...e_diff_review_llm.ts => invokeDiffReviewLlm.ts} | 4 +++- 8 files changed, 26 insertions(+), 27 deletions(-) rename packages/web/src/features/agents/review-agent/nodes/{fetch_file_content.ts => fetchFileContent.ts} (91%) rename packages/web/src/features/agents/review-agent/nodes/{generate_diff_review_prompt.ts => generateDiffReviewPrompt.ts} (93%) rename packages/web/src/features/agents/review-agent/nodes/{generate_pr_reviews.ts => generatePrReview.ts} (67%) rename packages/web/src/features/agents/review-agent/nodes/{github_pr_parser.ts => githubPrParser.ts} (90%) rename packages/web/src/features/agents/review-agent/nodes/{github_push_pr_reviews.ts => githubPushPrReviews.ts} (90%) rename packages/web/src/features/agents/review-agent/nodes/{invoke_diff_review_llm.ts => invokeDiffReviewLlm.ts} (90%) diff --git a/packages/web/src/app/api/(server)/webhook/route.ts b/packages/web/src/app/api/(server)/webhook/route.ts index 5c24a544..6ecc7b60 100644 --- a/packages/web/src/app/api/(server)/webhook/route.ts +++ b/packages/web/src/app/api/(server)/webhook/route.ts @@ -31,9 +31,6 @@ export const POST = async (request: NextRequest) => { const body = await request.json(); const headers = Object.fromEntries(request.headers.entries()); - console.log('Webhook request headers:', headers); - console.log('Webhook request body:', JSON.stringify(body, null, 2)); - const githubEvent = headers['x-github-event']; if (githubEvent) { console.log('GitHub event received:', githubEvent); @@ -44,8 +41,6 @@ export const POST = async (request: NextRequest) => { } if (isPullRequestEvent(githubEvent, body)) { - console.log('Received pull request event:', body); - if (!body.installation) { console.error('Received github pull request event but installation is not present'); return Response.json({ status: 'ok' }); diff --git a/packages/web/src/features/agents/review-agent/app.ts b/packages/web/src/features/agents/review-agent/app.ts index ae5a60fb..bd207352 100644 --- a/packages/web/src/features/agents/review-agent/app.ts +++ b/packages/web/src/features/agents/review-agent/app.ts @@ -1,13 +1,15 @@ import { Octokit } from "octokit"; import { WebhookEventDefinition } from "@octokit/webhooks/types"; -import { generate_pr_reviews } from "@/features/agents/review-agent/nodes/generate_pr_reviews"; -import { github_push_pr_reviews } from "@/features/agents/review-agent/nodes/github_push_pr_reviews"; -import { github_pr_parser } from "@/features/agents/review-agent/nodes/github_pr_parser"; +import { generatePrReviews } from "@/features/agents/review-agent/nodes/generatePrReview"; +import { githubPushPrReviews } from "@/features/agents/review-agent/nodes/githubPushPrReviews"; +import { githubPrParser } from "@/features/agents/review-agent/nodes/githubPrParser"; import { env } from "@/env.mjs"; const rules = [ "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.", "Do NOT provide any advice that is not actionable or directly related to the changes.", + "Do NOT provide any comments or reviews on code that you believe is good, correct, or a good addition. Your job is only to identify issues and provide feedback on how to fix them.", + "If a review for a chunk contains different reviews at different line ranges, return a seperate review object for each line range.", "Focus solely on offering specific, objective insights based on the given context and refrain from making broad comments about potential impacts on the system or question intentions behind the changes.", "Keep comments concise and to the point. Every comment must highlight a specific issue and provide a clear and actionable solution to the developer.", "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"." @@ -21,7 +23,7 @@ export async function processGitHubPullRequest(octokit: Octokit, payload: Webhoo return; } - const prPayload = await github_pr_parser(octokit, payload); - const fileDiffReviews = await generate_pr_reviews(prPayload, rules); - await github_push_pr_reviews(octokit, prPayload, fileDiffReviews); + const prPayload = await githubPrParser(octokit, payload); + const fileDiffReviews = await generatePrReviews(prPayload, rules); + await githubPushPrReviews(octokit, prPayload, fileDiffReviews); } \ No newline at end of file diff --git a/packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts b/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts similarity index 91% rename from packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts rename to packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts index 0d0246d7..4a3c3e92 100644 --- a/packages/web/src/features/agents/review-agent/nodes/fetch_file_content.ts +++ b/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts @@ -2,7 +2,7 @@ import { sourcebot_context, sourcebot_pr_payload } from "@/features/agents/revie import { fileSourceResponseSchema } from "@/features/search/schemas"; import { base64Decode } from "@/lib/utils"; -export const fetch_file_content = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { +export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { console.log("Executing fetch_file_content"); const repoPath = pr_payload.hostDomain + "/" + pr_payload.owner + "/" + pr_payload.repo; diff --git a/packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts b/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts similarity index 93% rename from packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts rename to packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts index 1955a2eb..88ac66d2 100644 --- a/packages/web/src/features/agents/review-agent/nodes/generate_diff_review_prompt.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts @@ -1,7 +1,7 @@ import { sourcebot_diff, sourcebot_context, sourcebot_diff_review_schema } from "@/features/agents/review-agent/types"; import { zodToJsonSchema } from "zod-to-json-schema"; -export const generate_diff_review_prompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => { +export const generateDiffReviewPrompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => { console.log("Executing generate_diff_review_prompt"); const prompt = ` diff --git a/packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts similarity index 67% rename from packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts rename to packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts index 643224b6..ebf940a7 100644 --- a/packages/web/src/features/agents/review-agent/nodes/generate_pr_reviews.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts @@ -1,9 +1,9 @@ import { sourcebot_pr_payload, sourcebot_diff_review, sourcebot_file_diff_review, sourcebot_context } from "@/features/agents/review-agent/types"; -import { generate_diff_review_prompt } from "@/features/agents/review-agent/nodes/generate_diff_review_prompt"; -import { invoke_diff_review_llm } from "@/features/agents/review-agent/nodes/invoke_diff_review_llm"; -import { fetch_file_content } from "@/features/agents/review-agent/nodes/fetch_file_content"; +import { generateDiffReviewPrompt } from "@/features/agents/review-agent/nodes/generateDiffReviewPrompt"; +import { invokeDiffReviewLlm } from "@/features/agents/review-agent/nodes/invokeDiffReviewLlm"; +import { fetchFileContent } from "@/features/agents/review-agent/nodes/fetchFileContent"; -export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { +export const generatePrReviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { console.log("Executing generate_pr_reviews"); const file_diff_reviews: sourcebot_file_diff_review[] = []; @@ -12,7 +12,7 @@ export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rule for (const diff of file_diff.diffs) { try { - const fileContentContext = await fetch_file_content(pr_payload, file_diff.to); + const fileContentContext = await fetchFileContent(pr_payload, file_diff.to); const context: sourcebot_context[] = [ { type: "pr_title", @@ -27,9 +27,9 @@ export const generate_pr_reviews = async (pr_payload: sourcebot_pr_payload, rule fileContentContext, ]; - const prompt = await generate_diff_review_prompt(diff, context, rules); + const prompt = await generateDiffReviewPrompt(diff, context, rules); - const diffReview = await invoke_diff_review_llm(prompt); + const diffReview = await invokeDiffReviewLlm(prompt); reviews.push(diffReview); } catch (error) { console.error(`Error fetching file content for ${file_diff.to}: ${error}`); diff --git a/packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts similarity index 90% rename from packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts rename to packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts index c441c5fb..deb8381a 100644 --- a/packages/web/src/features/agents/review-agent/nodes/github_pr_parser.ts +++ b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts @@ -3,7 +3,7 @@ import { WebhookEventDefinition } from "@octokit/webhooks/types"; import parse from "parse-diff"; import { Octokit } from "octokit"; -export const github_pr_parser = async (octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">): Promise => { +export const githubPrParser = async (octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">): Promise => { console.log("Executing github_pr_parser"); if (!payload.installation) { @@ -25,12 +25,12 @@ export const github_pr_parser = async (octokit: Octokit, payload: WebhookEventDe for (const change of chunk.changes) { if (change.type === "normal") { - oldSnippet += change.ln1 + ":" + change.content; - newSnippet += change.ln2 + ":" + change.content; + oldSnippet += change.ln1 + ":" + change.content + "\n"; + newSnippet += change.ln2 + ":" + change.content + "\n"; } else if (change.type === "add") { - newSnippet += change.ln + ":" + change.content; + newSnippet += change.ln + ":" + change.content + "\n"; } else if (change.type === "del") { - oldSnippet += change.ln + ":" + change.content; + oldSnippet += change.ln + ":" + change.content + "\n"; } } diff --git a/packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts b/packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts similarity index 90% rename from packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts rename to packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts index 33105471..70ecd043 100644 --- a/packages/web/src/features/agents/review-agent/nodes/github_push_pr_reviews.ts +++ b/packages/web/src/features/agents/review-agent/nodes/githubPushPrReviews.ts @@ -1,7 +1,7 @@ import { Octokit } from "octokit"; import { sourcebot_pr_payload, sourcebot_file_diff_review } from "@/features/agents/review-agent/types"; -export const github_push_pr_reviews = async (octokit: Octokit, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { +export const githubPushPrReviews = async (octokit: Octokit, pr_payload: sourcebot_pr_payload, file_diff_reviews: sourcebot_file_diff_review[]) => { console.log("Executing github_push_pr_reviews"); try { diff --git a/packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts similarity index 90% rename from packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts rename to packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts index a83ece74..fe9abdd0 100644 --- a/packages/web/src/features/agents/review-agent/nodes/invoke_diff_review_llm.ts +++ b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts @@ -2,7 +2,7 @@ import OpenAI from "openai"; import { sourcebot_diff_review_schema, sourcebot_diff_review } from "@/features/agents/review-agent/types"; import { env } from "@/env.mjs"; -export const invoke_diff_review_llm = async (prompt: string): Promise => { +export const invokeDiffReviewLlm = async (prompt: string): Promise => { console.log("Executing invoke_diff_review_llm"); if (!env.OPENAI_API_KEY) { @@ -14,6 +14,8 @@ export const invoke_diff_review_llm = async (prompt: string): Promise Date: Fri, 9 May 2025 12:15:24 -0700 Subject: [PATCH 06/15] feedback --- .../web/src/app/api/(server)/source/route.ts | 2 +- .../review-agent/nodes/fetchFileContent.ts | 19 ++++++------------- .../review-agent/nodes/githubPrParser.ts | 14 ++++++++++---- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/web/src/app/api/(server)/source/route.ts b/packages/web/src/app/api/(server)/source/route.ts index 78522f11..dc361a02 100644 --- a/packages/web/src/app/api/(server)/source/route.ts +++ b/packages/web/src/app/api/(server)/source/route.ts @@ -27,7 +27,7 @@ export const POST = async (request: NextRequest) => { } -const postSource = (request: FileSourceRequest, domain: string) => sew(() => +export const postSource = (request: FileSourceRequest, domain: string) => sew(() => withAuth(async (session) => withOrgMembership(session, domain, async ({ orgId }) => { const response = await getFileSource(request, orgId); diff --git a/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts b/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts index 4a3c3e92..edd03274 100644 --- a/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts +++ b/packages/web/src/features/agents/review-agent/nodes/fetchFileContent.ts @@ -1,6 +1,8 @@ import { sourcebot_context, sourcebot_pr_payload } from "@/features/agents/review-agent/types"; import { fileSourceResponseSchema } from "@/features/search/schemas"; import { base64Decode } from "@/lib/utils"; +import { postSource } from "@/app/api/(server)/source/route"; +import { isServiceError } from "@/lib/utils"; export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filename: string): Promise => { console.log("Executing fetch_file_content"); @@ -12,21 +14,12 @@ export const fetchFileContent = async (pr_payload: sourcebot_pr_payload, filenam } console.log(JSON.stringify(fileSourceRequest, null, 2)); - const response = await fetch('http://localhost:3000/api/source', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Org-Domain': '~' - }, - body: JSON.stringify(fileSourceRequest) - }); - - if (!response.ok) { - throw new Error(`Failed to fetch file content for ${filename} from ${repoPath}: ${response.statusText}`); + const response = await postSource(fileSourceRequest, "~"); + if (isServiceError(response)) { + throw new Error(`Failed to fetch file content for ${filename} from ${repoPath}: ${response.message}`); } - const responseData = await response.json(); - const fileSourceResponse = fileSourceResponseSchema.parse(responseData); + const fileSourceResponse = fileSourceResponseSchema.parse(response); const fileContent = base64Decode(fileSourceResponse.source); const fileContentContext: sourcebot_context = { diff --git a/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts index deb8381a..8b2454fb 100644 --- a/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts +++ b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts @@ -10,8 +10,14 @@ export const githubPrParser = async (octokit: Octokit, payload: WebhookEventDefi throw new Error("Installation not found in github payload"); } - const diff = await octokit.request(payload.pull_request.patch_url); - const parsedDiff: parse.File[] = parse(diff.data); + let parsedDiff: parse.File[] = []; + try { + const diff = await octokit.request(payload.pull_request.patch_url); + parsedDiff = parse(diff.data); + } catch (error) { + console.error("Error fetching diff: ", error); + throw error; + } const sourcebotFileDiffs: (sourcebot_file_diff | null)[] = parsedDiff.map((file) => { if (!file.from || !file.to) { @@ -20,8 +26,8 @@ export const githubPrParser = async (octokit: Octokit, payload: WebhookEventDefi } const diffs: sourcebot_diff[] = file.chunks.map((chunk) => { - let oldSnippet = ""; - let newSnippet = ""; + let oldSnippet = `@@ -${chunk.oldStart},${chunk.oldLines} +${chunk.newStart},${chunk.newLines} @@\n`; + let newSnippet = `@@ -${chunk.oldStart},${chunk.oldLines} +${chunk.newStart},${chunk.newLines} @@\n`; for (const change of chunk.changes) { if (change.type === "normal") { From 944aeb58cae79002cba38d7008dd203842881047 Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 14:11:25 -0700 Subject: [PATCH 07/15] add rate limit throttling to octokit --- .../web/src/app/api/(server)/webhook/route.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/web/src/app/api/(server)/webhook/route.ts b/packages/web/src/app/api/(server)/webhook/route.ts index 6ecc7b60..3d0dff51 100644 --- a/packages/web/src/app/api/(server)/webhook/route.ts +++ b/packages/web/src/app/api/(server)/webhook/route.ts @@ -1,22 +1,37 @@ 'use server'; import { NextRequest } from "next/server"; -import { App } from "octokit"; -import { WebhookEventDefinition } from "@octokit/webhooks/types"; +import { App, Octokit } from "octokit"; +import { WebhookEventDefinition} from "@octokit/webhooks/types"; +import { EndpointDefaults } from "@octokit/types"; import { env } from "@/env.mjs"; import { processGitHubPullRequest } from "@/features/agents/review-agent/app"; +import { throttling } from "@octokit/plugin-throttling"; import fs from "fs"; let githubApp: App | undefined; if (env.GITHUB_APP_ID && env.GITHUB_APP_WEBHOOK_SECRET && env.GITHUB_APP_PRIVATE_KEY_PATH) { try { const privateKey = fs.readFileSync(env.GITHUB_APP_PRIVATE_KEY_PATH, "utf8"); + + const throttledOctokit = Octokit.plugin(throttling); githubApp = new App({ appId: env.GITHUB_APP_ID, privateKey: privateKey, webhooks: { secret: env.GITHUB_APP_WEBHOOK_SECRET, }, + Octokit: throttledOctokit, + throttle: { + onRateLimit: (retryAfter: number, options: Required, octokit: Octokit, retryCount: number) => { + if (retryCount > 3) { + console.log(`Rate limit exceeded: ${retryAfter} seconds`); + return false; + } + + return true; + }, + } }); } catch (error) { console.error(`Error initializing GitHub app: ${error}`); From 2d0977e6908383d745300052d9cb6ba97d4cf633 Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 14:21:31 -0700 Subject: [PATCH 08/15] configure agent ui in app --- packages/web/src/app/[domain]/agents/page.tsx | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/web/src/app/[domain]/agents/page.tsx b/packages/web/src/app/[domain]/agents/page.tsx index 1a634a2c..c6969525 100644 --- a/packages/web/src/app/[domain]/agents/page.tsx +++ b/packages/web/src/app/[domain]/agents/page.tsx @@ -1,19 +1,17 @@ -import { Header } from "../components/header"; import Link from "next/link"; -import Image from "next/image"; import { NavigationMenu } from "../components/navigationMenu"; -import { FaRobot, FaCogs } from "react-icons/fa"; +import { FaCogs } from "react-icons/fa"; import { MdRocketLaunch } from "react-icons/md"; +import { env } from "@/env.mjs"; const agents = [ { id: "review-agent", name: "Review Agent", description: "An agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase wide context.", - deployUrl: "/agents/review-agent/deploy", - configureUrl: "/agents/review-agent/configure", + requiredEnvVars: ["GITHUB_APP_ID", "GITHUB_APP_WEBHOOK_SECRET", "GITHUB_APP_PRIVATE_KEY_PATH", "OPENAI_API_KEY"], + configureUrl: "https://docs.sourcebot.dev/docs/agents/review-agent" }, - // Add more agents here as needed ]; export default function AgentsPage({ params: { domain } }: { params: { domain: string } }) { @@ -47,19 +45,21 @@ export default function AgentsPage({ params: { domain } }: { params: { domain: s

{/* Actions */} -
- - Deploy - - - Configure - +
+ {agent.requiredEnvVars.every(envVar => envVar in env && env[envVar as keyof typeof env] !== undefined) ? ( +
+ Agent is configured and accepting requests on /api/webhook +
+ ) : ( + + Configure + + )}
))} From 871d41007bd0116cf1be86de58b60c4b51725326 Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 15:50:13 -0700 Subject: [PATCH 09/15] docs --- docs/docs.json | 7 +++ docs/docs/agents/overview.mdx | 17 ++++++ docs/docs/agents/review-agent.mdx | 57 ++++++++++++++++++ docs/images/github_app_private_key.png | Bin 0 -> 22265 bytes docs/images/review_agent_configured.png | Bin 0 -> 71893 bytes docs/images/review_agent_example.png | Bin 0 -> 216460 bytes packages/web/src/app/[domain]/agents/page.tsx | 1 - .../[domain]/components/navigationMenu.tsx | 7 +++ 8 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs/docs/agents/overview.mdx create mode 100644 docs/docs/agents/review-agent.mdx create mode 100644 docs/images/github_app_private_key.png create mode 100644 docs/images/review_agent_configured.png create mode 100644 docs/images/review_agent_example.png diff --git a/docs/docs.json b/docs/docs.json index add8875e..f5e48551 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -43,6 +43,13 @@ } ] }, + { + "group": "Agents", + "pages": [ + "docs/agents/overview", + "docs/agents/review-agent" + ] + }, { "group": "More", "pages": [ diff --git a/docs/docs/agents/overview.mdx b/docs/docs/agents/overview.mdx new file mode 100644 index 00000000..86fce12d --- /dev/null +++ b/docs/docs/agents/overview.mdx @@ -0,0 +1,17 @@ +--- +title: "Agents Overview" +sidebarTitle: "Overview" +--- + + +Have an idea for an agent that we haven't built? Submit a [feature request](https://github.com/sourcebot-dev/sourcebot/discussions/categories/feature-requests) on our GitHub + + +Agents are automations that leverage the code indexed on Sourcebot to perform a specific task. Once you've setup Sourcebot, check out the +guides below to configure additional agents. + + + + An AI agent that reviews your PRs to identify issues + + \ No newline at end of file diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx new file mode 100644 index 00000000..058bc4ec --- /dev/null +++ b/docs/docs/agents/review-agent.mdx @@ -0,0 +1,57 @@ +--- +title: AI Code Review Agent +sidebarTitle: AI Code Review Agent +--- + + +This agent sends data to OpenAI (through an API key you supply) to perform code reviews. This data includes code from the PR being reviewed, as well as additional relevant context from your +codebase that the agent may fetch to perform the review. + + +This agent provides codebase-aware reviews for your PRs. For each diff, this agent fetches relevant context from Sourcebot and feeds it into an LLM for a detailed review of your changes. + +The AI Code Review Agent is open source and packaged in [Sourcebot](https://github.com/sourcebot-dev/sourcebot). To get started using this agent, [deploy Sourcebot](http://localhost:3001/self-hosting/overview) +and then follow the configuration instructions below. + +![AI Code Review Agent Example](/images/review_agent_example.png) + +# Configure + +This agent currently only supports reviewing GitHub PRs. You configure the agent by creating a GitHub app, installing it into your GitHub organization, and then giving your app info to Sourcebot. + +Before you get started, make sure you have an OpenAPI account that you can create an OpenAPI key with. + + + + Follow the official GitHub guide for [registering a GitHub app](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app) + + - GitHub App name: You can make this whatever you want (ex. Sourcebot Review Agent) + - Homepage URL: You can make this whatever you want (ex. https://www.sourcebot.dev/) + - Webhook URL (**IMPORTANT**): You must set this to point to your Sourcebot deployment at /api/webhook (ex. https://sourcebot.aperture.com/api/webhook). Your Sourcebot deployment must be able to accept requests from GitHub + (either github.com or your self-hosted enterprise server) for this to work. If you're running Sourcebot locally, you can [use smee](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-2-get-a-webhook-proxy-url) to [forward webhooks](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-6-start-your-server) to you local deployment. + - Permissions + - Pull requests: Read + - Contents: Read + - Events: + - Pull request + + + Navigate to your new GitHub app's page and press `Install`. You can find this in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). + + Select the repositories that you want to install the app into. + + + Sourcebot requires the following environment variables to begin reviewing PRs through your new GitHub app: + + - `GITHUB_APP_ID`: The client ID of your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) + - `GITHUB_APP_WEBHOOK_SECRET`: A random webhook secret that you've set in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). This can be anything (ex. `python -c "import secrets; print(secrets.token_hex(10))"` to generate a random secret) + - `GITHUB_APP_PRIVATE_KEY_PATH`: The path to your app's private key. You can generate a private key file for your app in the [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) + ![GitHub App Private Key](/images/github_app_private_key.png) + - `OPENAI_API_KEY`: Your OpenAI API key + + + Navigate to the agents page by pressing `Agents` in the Sourcebot nav menu. If you've configured your environment variables you'll see the following: + + ![Review Agent Configured](/images/review_agent_configured.png) + + \ No newline at end of file diff --git a/docs/images/github_app_private_key.png b/docs/images/github_app_private_key.png new file mode 100644 index 0000000000000000000000000000000000000000..819fae2310d3c718e84ad533f4a5d6a96aee286c GIT binary patch literal 22265 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

}>g+Tap{OyFG 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 literal 0 HcmV?d00001 diff --git a/packages/web/src/app/[domain]/agents/page.tsx b/packages/web/src/app/[domain]/agents/page.tsx index c6969525..f5e4fe2c 100644 --- a/packages/web/src/app/[domain]/agents/page.tsx +++ b/packages/web/src/app/[domain]/agents/page.tsx @@ -1,7 +1,6 @@ import Link from "next/link"; import { NavigationMenu } from "../components/navigationMenu"; import { FaCogs } from "react-icons/fa"; -import { MdRocketLaunch } from "react-icons/md"; import { env } from "@/env.mjs"; const agents = [ diff --git a/packages/web/src/app/[domain]/components/navigationMenu.tsx b/packages/web/src/app/[domain]/components/navigationMenu.tsx index 75ff020c..ba760d6b 100644 --- a/packages/web/src/app/[domain]/components/navigationMenu.tsx +++ b/packages/web/src/app/[domain]/components/navigationMenu.tsx @@ -59,6 +59,13 @@ export const NavigationMenu = async ({ + + + + Agents + + + From a242a690b86f53844fb483fbb4fe068c008061ec Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 17:53:41 -0700 Subject: [PATCH 10/15] add review command logic and add logging for review agent to data cache dir --- .env.development | 18 +++++--- docs/docs/agents/review-agent.mdx | 17 ++++++-- packages/backend/src/env.ts | 2 + packages/backend/src/index.ts | 18 ++------ .../web/src/app/api/(server)/webhook/route.ts | 43 ++++++++++++++++++- packages/web/src/env.mjs | 5 +++ .../src/features/agents/review-agent/app.ts | 24 ++++++++--- .../review-agent/nodes/generatePrReview.ts | 4 +- .../review-agent/nodes/githubPrParser.ts | 23 ++++------ .../review-agent/nodes/invokeDiffReviewLlm.ts | 11 +++-- .../src/features/agents/review-agent/types.ts | 6 ++- supervisord.conf | 2 +- 12 files changed, 121 insertions(+), 52 deletions(-) diff --git a/.env.development b/.env.development index 138800a2..463cc1fa 100644 --- a/.env.development +++ b/.env.development @@ -23,6 +23,8 @@ AUTH_URL="http://localhost:3000" # AUTH_GOOGLE_CLIENT_ID="" # AUTH_GOOGLE_CLIENT_SECRET="" +#DATA_CACHE_DIR="" # Path to the sourcebot cache dir (ex. ~/sourcebot/.sourcebot) + # Email # EMAIL_FROM_ADDRESS="" # The from address for transactional emails. # SMTP_CONNECTION_URL="" # The SMTP connection URL for transactional emails. @@ -51,6 +53,16 @@ REDIS_URL="redis://localhost:6379" # STRIPE_WEBHOOK_SECRET: z.string().optional(), # STRIPE_ENABLE_TEST_CLOCKS=false +# Agents + +# GITHUB_APP_ID= +# GITHUB_APP_PRIVATE_KEY_PATH= +# GITHUB_APP_WEBHOOK_SECRET= +# OPENAI_API_KEY= +REVIEW_AGENT_LOGGING_ENABLED=true +REVIEW_AGENT_AUTO_REVIEW_ENABLED=false +REVIEW_AGENT_REVIEW_COMMAND=review + # Misc # Generated using: @@ -79,9 +91,3 @@ SOURCEBOT_TELEMETRY_DISABLED=true # Disables telemetry collection # SOURCEBOT_TENANCY_MODE=single # NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT= - -# Used for agents -# GITHUB_APP_ID= -# GITHUB_APP_PRIVATE_KEY_PATH= -# GITHUB_APP_WEBHOOK_SECRET= -# OPENAI_API_KEY= \ No newline at end of file diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx index 058bc4ec..fbf52bd9 100644 --- a/docs/docs/agents/review-agent.mdx +++ b/docs/docs/agents/review-agent.mdx @@ -30,10 +30,12 @@ Before you get started, make sure you have an OpenAPI account that you can creat - Webhook URL (**IMPORTANT**): You must set this to point to your Sourcebot deployment at /api/webhook (ex. https://sourcebot.aperture.com/api/webhook). Your Sourcebot deployment must be able to accept requests from GitHub (either github.com or your self-hosted enterprise server) for this to work. If you're running Sourcebot locally, you can [use smee](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-2-get-a-webhook-proxy-url) to [forward webhooks](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-6-start-your-server) to you local deployment. - Permissions - - Pull requests: Read + - Pull requests: Read & Write + - Issues: Read & Write - Contents: Read - Events: - Pull request + - Issue comment Navigate to your new GitHub app's page and press `Install`. You can find this in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). @@ -48,10 +50,19 @@ Before you get started, make sure you have an OpenAPI account that you can creat - `GITHUB_APP_PRIVATE_KEY_PATH`: The path to your app's private key. You can generate a private key file for your app in the [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) ![GitHub App Private Key](/images/github_app_private_key.png) - `OPENAI_API_KEY`: Your OpenAI API key + - `REVIEW_AGENT_AUTO_REVIEW_ENABLED` (default: `false`): If enabled, the review agent will automatically review any new or updated PR. If disabled, you must invoke it using the command defined by `REVIEW_AGENT_REVIEW_COMMAND` + - `REVIEW_AGENT_REVIEW_COMMAND` (default: `review`): The command that invokes the review agent (ex. `/review`) when a user comments on the PR. Don't include the slash character in this value. - Navigate to the agents page by pressing `Agents` in the Sourcebot nav menu. If you've configured your environment variables you'll see the following: + Navigate to the agents page by pressing `Agents` in the Sourcebot nav menu. If you've configured your environment variables correctly you'll see the following: ![Review Agent Configured](/images/review_agent_configured.png) - \ No newline at end of file + + +# Using the agent + +The review agent will not automatically review your PRs by default. To enable this feature, set the `REVIEW_AGENT_AUTO_REVIEW_ENABLED` environment variable to true. + +You can invoke the review agent manually by commenting `/review` on the PR you'd like it to review. You can configure the command that triggers the agent by changing +the `REVIEW_AGENT_REVIEW_COMMAND` environment variable. \ No newline at end of file diff --git a/packages/backend/src/env.ts b/packages/backend/src/env.ts index 472bcec2..512c9dae 100644 --- a/packages/backend/src/env.ts +++ b/packages/backend/src/env.ts @@ -27,6 +27,8 @@ export const env = createEnv({ SOURCEBOT_INSTALL_ID: z.string().default("unknown"), NEXT_PUBLIC_SOURCEBOT_VERSION: z.string().default("unknown"), + DATA_CACHE_DIR: z.string(), + NEXT_PUBLIC_POSTHOG_PAPIK: z.string().optional(), FALLBACK_GITHUB_CLOUD_TOKEN: z.string().optional(), diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 702cb0e5..346f2ff9 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -8,6 +8,7 @@ import path from 'path'; import { AppContext } from "./types.js"; import { main } from "./main.js" import { PrismaClient } from "@sourcebot/db"; +import { env } from "./env.js"; // Register handler for normal exit process.on('exit', (code) => { @@ -36,22 +37,9 @@ process.on('unhandledRejection', (reason, promise) => { process.exit(1); }); +console.log(process.cwd()); -const parser = new ArgumentParser({ - description: "Sourcebot backend tool", -}); - -type Arguments = { - cacheDir: string; -} - -parser.add_argument("--cacheDir", { - help: "Path to .sourcebot cache directory", - required: true, -}); -const args = parser.parse_args() as Arguments; - -const cacheDir = args.cacheDir; +const cacheDir = env.DATA_CACHE_DIR; const reposPath = path.join(cacheDir, 'repos'); const indexPath = path.join(cacheDir, 'index'); diff --git a/packages/web/src/app/api/(server)/webhook/route.ts b/packages/web/src/app/api/(server)/webhook/route.ts index 3d0dff51..4b5f5c2c 100644 --- a/packages/web/src/app/api/(server)/webhook/route.ts +++ b/packages/web/src/app/api/(server)/webhook/route.ts @@ -8,6 +8,7 @@ import { env } from "@/env.mjs"; import { processGitHubPullRequest } from "@/features/agents/review-agent/app"; import { throttling } from "@octokit/plugin-throttling"; import fs from "fs"; +import { GitHubPullRequest } from "@/features/agents/review-agent/types"; let githubApp: App | undefined; if (env.GITHUB_APP_ID && env.GITHUB_APP_WEBHOOK_SECRET && env.GITHUB_APP_PRIVATE_KEY_PATH) { @@ -42,6 +43,10 @@ function isPullRequestEvent(eventHeader: string, payload: unknown): payload is W return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize"); } +function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"issue-comment-created"> { + return eventHeader === "issue_comment" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && payload.action === "created"; +} + export const POST = async (request: NextRequest) => { const body = await request.json(); const headers = Object.fromEntries(request.headers.entries()); @@ -56,6 +61,11 @@ export const POST = async (request: NextRequest) => { } if (isPullRequestEvent(githubEvent, body)) { + if (env.REVIEW_AGENT_AUTO_REVIEW_ENABLED === "false") { + console.log('Review agent auto review (REVIEW_AGENT_AUTO_REVIEW_ENABLED) is disabled, skipping'); + return Response.json({ status: 'ok' }); + } + if (!body.installation) { console.error('Received github pull request event but installation is not present'); return Response.json({ status: 'ok' }); @@ -64,7 +74,38 @@ export const POST = async (request: NextRequest) => { const installationId = body.installation.id; const octokit = await githubApp.getInstallationOctokit(installationId); - await processGitHubPullRequest(octokit, body); + const pullRequest = body.pull_request as GitHubPullRequest; + await processGitHubPullRequest(octokit, pullRequest); + } + + if (isIssueCommentEvent(githubEvent, body)) { + const comment = body.comment.body; + if (!comment) { + console.warn('Received issue comment event but comment body is empty'); + return Response.json({ status: 'ok' }); + } + + if (comment === `/${env.REVIEW_AGENT_REVIEW_COMMAND}`) { + console.log('Review agent review command received, processing'); + + if (!body.installation) { + console.error('Received github issue comment event but installation is not present'); + return Response.json({ status: 'ok' }); + } + + const pullRequestNumber = body.issue.number; + const repositoryName = body.repository.name; + const owner = body.repository.owner.login; + + const octokit = await githubApp.getInstallationOctokit(body.installation.id); + const { data: pullRequest } = await octokit.rest.pulls.get({ + owner, + repo: repositoryName, + pull_number: pullRequestNumber, + }); + + await processGitHubPullRequest(octokit, pullRequest); + } } } diff --git a/packages/web/src/env.mjs b/packages/web/src/env.mjs index 575de62d..9002e4c3 100644 --- a/packages/web/src/env.mjs +++ b/packages/web/src/env.mjs @@ -27,6 +27,8 @@ export const env = createEnv({ AUTH_URL: z.string().url(), AUTH_CREDENTIALS_LOGIN_ENABLED: booleanSchema.default('true'), + DATA_CACHE_DIR: z.string(), + // Email SMTP_CONNECTION_URL: z.string().url().optional(), EMAIL_FROM_ADDRESS: z.string().email().optional(), @@ -58,6 +60,9 @@ export const env = createEnv({ GITHUB_APP_WEBHOOK_SECRET: z.string().optional(), GITHUB_APP_PRIVATE_KEY_PATH: z.string().optional(), OPENAI_API_KEY: z.string().optional(), + REVIEW_AGENT_LOGGING_ENABLED: booleanSchema.default('true'), + REVIEW_AGENT_AUTO_REVIEW_ENABLED: booleanSchema.default('false'), + REVIEW_AGENT_REVIEW_COMMAND: z.string().default('review'), }, // @NOTE: Please make sure of the following: // - Make sure you destructure all client variables in diff --git a/packages/web/src/features/agents/review-agent/app.ts b/packages/web/src/features/agents/review-agent/app.ts index bd207352..09e9a3d7 100644 --- a/packages/web/src/features/agents/review-agent/app.ts +++ b/packages/web/src/features/agents/review-agent/app.ts @@ -1,9 +1,11 @@ import { Octokit } from "octokit"; -import { WebhookEventDefinition } from "@octokit/webhooks/types"; import { generatePrReviews } from "@/features/agents/review-agent/nodes/generatePrReview"; import { githubPushPrReviews } from "@/features/agents/review-agent/nodes/githubPushPrReviews"; import { githubPrParser } from "@/features/agents/review-agent/nodes/githubPrParser"; import { env } from "@/env.mjs"; +import { GitHubPullRequest } from "@/features/agents/review-agent/types"; +import path from "path"; +import fs from "fs"; const rules = [ "Do NOT provide general feedback, summaries, explanations of changes, or praises for making good additions.", @@ -15,15 +17,27 @@ const rules = [ "If there are no issues found on a line range, do NOT respond with any comments. This includes comments such as \"No issues found\" or \"LGTM\"." ] -export async function processGitHubPullRequest(octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">) { - console.log(`Received a pull request event for #${payload.pull_request.number}`); +export async function processGitHubPullRequest(octokit: Octokit, pullRequest: GitHubPullRequest) { + console.log(`Received a pull request event for #${pullRequest.number}`); if (!env.OPENAI_API_KEY) { console.error("OPENAI_API_KEY is not set, skipping review agent"); return; } - const prPayload = await githubPrParser(octokit, payload); - const fileDiffReviews = await generatePrReviews(prPayload, rules); + let reviewAgentLogPath: string | undefined; + if (env.REVIEW_AGENT_LOGGING_ENABLED) { + const reviewAgentLogDir = path.join(env.DATA_CACHE_DIR, "review-agent"); + if (!fs.existsSync(reviewAgentLogDir)) { + fs.mkdirSync(reviewAgentLogDir, { recursive: true }); + } + + const timestamp = new Date().toLocaleString().replace(/[/: ,]/g, ''); + reviewAgentLogPath = path.join(reviewAgentLogDir, `review-agent-${pullRequest.number}-${timestamp}.log`); + console.log(`Review agent logging to ${reviewAgentLogPath}`); + } + + const prPayload = await githubPrParser(octokit, pullRequest); + const fileDiffReviews = await generatePrReviews(reviewAgentLogPath, prPayload, rules); await githubPushPrReviews(octokit, prPayload, fileDiffReviews); } \ No newline at end of file diff --git a/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts index ebf940a7..c202e720 100644 --- a/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts @@ -3,7 +3,7 @@ import { generateDiffReviewPrompt } from "@/features/agents/review-agent/nodes/g import { invokeDiffReviewLlm } from "@/features/agents/review-agent/nodes/invokeDiffReviewLlm"; import { fetchFileContent } from "@/features/agents/review-agent/nodes/fetchFileContent"; -export const generatePrReviews = async (pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { +export const generatePrReviews = async (reviewAgentLogPath: string | undefined, pr_payload: sourcebot_pr_payload, rules: string[]): Promise => { console.log("Executing generate_pr_reviews"); const file_diff_reviews: sourcebot_file_diff_review[] = []; @@ -29,7 +29,7 @@ export const generatePrReviews = async (pr_payload: sourcebot_pr_payload, rules: const prompt = await generateDiffReviewPrompt(diff, context, rules); - const diffReview = await invokeDiffReviewLlm(prompt); + const diffReview = await invokeDiffReviewLlm(reviewAgentLogPath, prompt); reviews.push(diffReview); } catch (error) { console.error(`Error fetching file content for ${file_diff.to}: ${error}`); diff --git a/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts index 8b2454fb..59ea158b 100644 --- a/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts +++ b/packages/web/src/features/agents/review-agent/nodes/githubPrParser.ts @@ -1,18 +1,14 @@ import { sourcebot_pr_payload, sourcebot_file_diff, sourcebot_diff } from "@/features/agents/review-agent/types"; -import { WebhookEventDefinition } from "@octokit/webhooks/types"; import parse from "parse-diff"; import { Octokit } from "octokit"; +import { GitHubPullRequest } from "@/features/agents/review-agent/types"; -export const githubPrParser = async (octokit: Octokit, payload: WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize">): Promise => { +export const githubPrParser = async (octokit: Octokit, pullRequest: GitHubPullRequest): Promise => { console.log("Executing github_pr_parser"); - if (!payload.installation) { - throw new Error("Installation not found in github payload"); - } - let parsedDiff: parse.File[] = []; try { - const diff = await octokit.request(payload.pull_request.patch_url); + const diff = await octokit.request(pullRequest.diff_url); parsedDiff = parse(diff.data); } catch (error) { console.error("Error fetching diff: ", error); @@ -56,14 +52,13 @@ export const githubPrParser = async (octokit: Octokit, payload: WebhookEventDefi console.log("Completed github_pr_parser"); return { - title: payload.pull_request.title, - description: payload.pull_request.body ?? "", + title: pullRequest.title, + description: pullRequest.body ?? "", hostDomain: "github.com", - owner: payload.repository.owner.login, - repo: payload.repository.name, + owner: pullRequest.base.repo.owner.login, + repo: pullRequest.base.repo.name, file_diffs: filteredSourcebotFileDiffs, - number: payload.pull_request.number, - head_sha: payload.pull_request.head.sha, - installation_id: payload.installation!.id, + number: pullRequest.number, + head_sha: pullRequest.head.sha } } \ No newline at end of file diff --git a/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts index fe9abdd0..0807f2e6 100644 --- a/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts +++ b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts @@ -1,8 +1,9 @@ import OpenAI from "openai"; import { sourcebot_diff_review_schema, sourcebot_diff_review } from "@/features/agents/review-agent/types"; import { env } from "@/env.mjs"; +import fs from "fs"; -export const invokeDiffReviewLlm = async (prompt: string): Promise => { +export const invokeDiffReviewLlm = async (reviewAgentLogPath: string | undefined, prompt: string): Promise => { console.log("Executing invoke_diff_review_llm"); if (!env.OPENAI_API_KEY) { @@ -14,7 +15,9 @@ export const invokeDiffReviewLlm = async (prompt: string): Promise; diff --git a/supervisord.conf b/supervisord.conf index fee47fdd..0873b0cf 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -22,7 +22,7 @@ stdout_logfile_maxbytes=0 redirect_stderr=true [program:backend] -command=./prefix-output.sh node packages/backend/dist/index.js --cacheDir %(ENV_DATA_CACHE_DIR)s +command=./prefix-output.sh node packages/backend/dist/index.js autostart=true autorestart=true startretries=3 From 5323cface9f1f257537173012abf90c40f6f0105 Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 18:01:19 -0700 Subject: [PATCH 11/15] fix bug with llm returning multiple reviews in single invocation --- packages/web/src/features/agents/review-agent/app.ts | 10 +++++++++- .../review-agent/nodes/generateDiffReviewPrompt.ts | 4 ++-- .../agents/review-agent/nodes/generatePrReview.ts | 4 ++-- .../agents/review-agent/nodes/invokeDiffReviewLlm.ts | 6 +++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/web/src/features/agents/review-agent/app.ts b/packages/web/src/features/agents/review-agent/app.ts index 09e9a3d7..f62d77a9 100644 --- a/packages/web/src/features/agents/review-agent/app.ts +++ b/packages/web/src/features/agents/review-agent/app.ts @@ -32,7 +32,15 @@ export async function processGitHubPullRequest(octokit: Octokit, pullRequest: Gi fs.mkdirSync(reviewAgentLogDir, { recursive: true }); } - const timestamp = new Date().toLocaleString().replace(/[/: ,]/g, ''); + const timestamp = new Date().toLocaleString('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }).replace(/(\d+)\/(\d+)\/(\d+), (\d+):(\d+):(\d+)/, '$3_$1_$2_$4_$5_$6'); reviewAgentLogPath = path.join(reviewAgentLogDir, `review-agent-${pullRequest.number}-${timestamp}.log`); console.log(`Review agent logging to ${reviewAgentLogPath}`); } diff --git a/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts b/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts index 88ac66d2..13150226 100644 --- a/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generateDiffReviewPrompt.ts @@ -1,4 +1,4 @@ -import { sourcebot_diff, sourcebot_context, sourcebot_diff_review_schema } from "@/features/agents/review-agent/types"; +import { sourcebot_diff, sourcebot_context, sourcebot_file_diff_review_schema } from "@/features/agents/review-agent/types"; import { zodToJsonSchema } from "zod-to-json-schema"; export const generateDiffReviewPrompt = async (diff: sourcebot_diff, context: sourcebot_context[], rules: string[]) => { @@ -36,7 +36,7 @@ export const generateDiffReviewPrompt = async (diff: sourcebot_diff, context: so # Output Format (JSON Schema) The output must be a valid JSON object that conforms to the following JSON schema. Do NOT respond with anything other than the JSON object. Do NOT respond with the JSON object in a markdown code block. - ${JSON.stringify(zodToJsonSchema(sourcebot_diff_review_schema), null, 2)} + ${JSON.stringify(zodToJsonSchema(sourcebot_file_diff_review_schema), null, 2)} `; console.log("Completed generate_diff_review_prompt"); diff --git a/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts index c202e720..c7ad81a3 100644 --- a/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts +++ b/packages/web/src/features/agents/review-agent/nodes/generatePrReview.ts @@ -30,9 +30,9 @@ export const generatePrReviews = async (reviewAgentLogPath: string | undefined, const prompt = await generateDiffReviewPrompt(diff, context, rules); const diffReview = await invokeDiffReviewLlm(reviewAgentLogPath, prompt); - reviews.push(diffReview); + reviews.push(...diffReview.reviews); } catch (error) { - console.error(`Error fetching file content for ${file_diff.to}: ${error}`); + console.error(`Error generating review for ${file_diff.to}: ${error}`); } } diff --git a/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts index 0807f2e6..2a703804 100644 --- a/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts +++ b/packages/web/src/features/agents/review-agent/nodes/invokeDiffReviewLlm.ts @@ -1,9 +1,9 @@ import OpenAI from "openai"; -import { sourcebot_diff_review_schema, sourcebot_diff_review } from "@/features/agents/review-agent/types"; +import { sourcebot_file_diff_review, sourcebot_file_diff_review_schema } from "@/features/agents/review-agent/types"; import { env } from "@/env.mjs"; import fs from "fs"; -export const invokeDiffReviewLlm = async (reviewAgentLogPath: string | undefined, prompt: string): Promise => { +export const invokeDiffReviewLlm = async (reviewAgentLogPath: string | undefined, prompt: string): Promise => { console.log("Executing invoke_diff_review_llm"); if (!env.OPENAI_API_KEY) { @@ -33,7 +33,7 @@ export const invokeDiffReviewLlm = async (reviewAgentLogPath: string | undefined } const diffReviewJson = JSON.parse(openaiResponse || '{}'); - const diffReview = sourcebot_diff_review_schema.safeParse(diffReviewJson); + const diffReview = sourcebot_file_diff_review_schema.safeParse(diffReviewJson); if (!diffReview.success) { throw new Error(`Invalid diff review format: ${diffReview.error}`); From c58655899955c3b2987addb982fd6af5f5dd8e0c Mon Sep 17 00:00:00 2001 From: msukkari Date: Fri, 9 May 2025 18:03:30 -0700 Subject: [PATCH 12/15] fix doc link bug --- docs/docs/agents/review-agent.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx index fbf52bd9..22625368 100644 --- a/docs/docs/agents/review-agent.mdx +++ b/docs/docs/agents/review-agent.mdx @@ -10,7 +10,7 @@ codebase that the agent may fetch to perform the review. This agent provides codebase-aware reviews for your PRs. For each diff, this agent fetches relevant context from Sourcebot and feeds it into an LLM for a detailed review of your changes. -The AI Code Review Agent is open source and packaged in [Sourcebot](https://github.com/sourcebot-dev/sourcebot). To get started using this agent, [deploy Sourcebot](http://localhost:3001/self-hosting/overview) +The AI Code Review Agent is open source and packaged in [Sourcebot](https://github.com/sourcebot-dev/sourcebot). To get started using this agent, [deploy Sourcebot](/self-hosting/overview) and then follow the configuration instructions below. ![AI Code Review Agent Example](/images/review_agent_example.png) From 2307106b83093f9cccc6f651e5ac9446e4c125a0 Mon Sep 17 00:00:00 2001 From: msukkari Date: Sat, 10 May 2025 10:03:41 -0700 Subject: [PATCH 13/15] feedback and improved docs for review agent --- docs/docs/agents/review-agent.mdx | 34 ++++++++++++++++--- packages/web/src/app/[domain]/agents/page.tsx | 2 +- .../web/src/app/api/(server)/webhook/route.ts | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx index 22625368..ec5af8b6 100644 --- a/docs/docs/agents/review-agent.mdx +++ b/docs/docs/agents/review-agent.mdx @@ -28,7 +28,7 @@ Before you get started, make sure you have an OpenAPI account that you can creat - GitHub App name: You can make this whatever you want (ex. Sourcebot Review Agent) - Homepage URL: You can make this whatever you want (ex. https://www.sourcebot.dev/) - Webhook URL (**IMPORTANT**): You must set this to point to your Sourcebot deployment at /api/webhook (ex. https://sourcebot.aperture.com/api/webhook). Your Sourcebot deployment must be able to accept requests from GitHub - (either github.com or your self-hosted enterprise server) for this to work. If you're running Sourcebot locally, you can [use smee](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-2-get-a-webhook-proxy-url) to [forward webhooks](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-6-start-your-server) to you local deployment. + (either github.com or your self-hosted enterprise server) for this to work. If you're running Sourcebot locally, you can [use smee](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-2-get-a-webhook-proxy-url) to [forward webhooks](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#step-6-start-your-server) to your local deployment. - Permissions - Pull requests: Read & Write - Issues: Read & Write @@ -38,20 +38,44 @@ Before you get started, make sure you have an OpenAPI account that you can creat - Issue comment - Navigate to your new GitHub app's page and press `Install`. You can find this in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). - - Select the repositories that you want to install the app into. + Navigate to your new [GitHub app's page](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) and press `Install` Sourcebot requires the following environment variables to begin reviewing PRs through your new GitHub app: - `GITHUB_APP_ID`: The client ID of your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) - `GITHUB_APP_WEBHOOK_SECRET`: A random webhook secret that you've set in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). This can be anything (ex. `python -c "import secrets; print(secrets.token_hex(10))"` to generate a random secret) - - `GITHUB_APP_PRIVATE_KEY_PATH`: The path to your app's private key. You can generate a private key file for your app in the [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings) + - `GITHUB_APP_PRIVATE_KEY_PATH`: The path to your app's private key. If you're running Sourcebot from a container, this is the path to this file from within your container + (ex `/data/review-agent-key.pem`). You must copy the private key file into the directory you mount to Sourcebot (similar to the config file). + + You can generate a private key file for your app in the [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). You must copy this private key file into the + directory that you mount to Sourcebot ![GitHub App Private Key](/images/github_app_private_key.png) - `OPENAI_API_KEY`: Your OpenAI API key - `REVIEW_AGENT_AUTO_REVIEW_ENABLED` (default: `false`): If enabled, the review agent will automatically review any new or updated PR. If disabled, you must invoke it using the command defined by `REVIEW_AGENT_REVIEW_COMMAND` - `REVIEW_AGENT_REVIEW_COMMAND` (default: `review`): The command that invokes the review agent (ex. `/review`) when a user comments on the PR. Don't include the slash character in this value. + + You can find an example docker compose file below. This docker compose file is placed in `~/sourcebot_review_agent_workspace`, and I'm mounting that directory to Sourcebot. The + config file and the app's private key file are also placed in this directory. The paths to these files are given to Sourcebot relative to `/data` since that's the directory + in Sourcebot that I'm mounting to. + + ```yaml + services: + sourcebot: + image: ghcr.io/sourcebot-dev/sourcebot:latest + pull_policy: always + container_name: sourcebot + ports: + - "3000:3000" + volumes: + - "/Users/michael/sourcebot_review_agent_workspace:/data" + environment: + CONFIG_PATH: "/data/config.json" + GITHUB_APP_ID: "my-github-app-id" + GITHUB_APP_WEBHOOK_SECRET: "my-github-app-webhook-secret" + GITHUB_APP_PRIVATE_KEY_PATH: "/data/review-agent-key.pem" + OPENAI_API_KEY: "sk-proj-my-open-api-key" + ``` Navigate to the agents page by pressing `Agents` in the Sourcebot nav menu. If you've configured your environment variables correctly you'll see the following: diff --git a/packages/web/src/app/[domain]/agents/page.tsx b/packages/web/src/app/[domain]/agents/page.tsx index f5e4fe2c..7f283ede 100644 --- a/packages/web/src/app/[domain]/agents/page.tsx +++ b/packages/web/src/app/[domain]/agents/page.tsx @@ -7,7 +7,7 @@ const agents = [ { id: "review-agent", name: "Review Agent", - description: "An agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase wide context.", + description: "An AI code review agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase-wide context.", requiredEnvVars: ["GITHUB_APP_ID", "GITHUB_APP_WEBHOOK_SECRET", "GITHUB_APP_PRIVATE_KEY_PATH", "OPENAI_API_KEY"], configureUrl: "https://docs.sourcebot.dev/docs/agents/review-agent" }, diff --git a/packages/web/src/app/api/(server)/webhook/route.ts b/packages/web/src/app/api/(server)/webhook/route.ts index 4b5f5c2c..4f980d07 100644 --- a/packages/web/src/app/api/(server)/webhook/route.ts +++ b/packages/web/src/app/api/(server)/webhook/route.ts @@ -51,7 +51,7 @@ export const POST = async (request: NextRequest) => { const body = await request.json(); const headers = Object.fromEntries(request.headers.entries()); - const githubEvent = headers['x-github-event']; + const githubEvent = headers['x-github-event'] || headers['X-GitHub-Event']; if (githubEvent) { console.log('GitHub event received:', githubEvent); From 0ccd4a8e0df6998ca384b9ee35064a4484f8a803 Mon Sep 17 00:00:00 2001 From: msukkari Date: Sat, 10 May 2025 10:07:51 -0700 Subject: [PATCH 14/15] review agent doc nits --- docs/docs/agents/review-agent.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/docs/agents/review-agent.mdx b/docs/docs/agents/review-agent.mdx index ec5af8b6..ba1ef642 100644 --- a/docs/docs/agents/review-agent.mdx +++ b/docs/docs/agents/review-agent.mdx @@ -55,9 +55,10 @@ Before you get started, make sure you have an OpenAPI account that you can creat - `REVIEW_AGENT_AUTO_REVIEW_ENABLED` (default: `false`): If enabled, the review agent will automatically review any new or updated PR. If disabled, you must invoke it using the command defined by `REVIEW_AGENT_REVIEW_COMMAND` - `REVIEW_AGENT_REVIEW_COMMAND` (default: `review`): The command that invokes the review agent (ex. `/review`) when a user comments on the PR. Don't include the slash character in this value. - You can find an example docker compose file below. This docker compose file is placed in `~/sourcebot_review_agent_workspace`, and I'm mounting that directory to Sourcebot. The - config file and the app's private key file are also placed in this directory. The paths to these files are given to Sourcebot relative to `/data` since that's the directory - in Sourcebot that I'm mounting to. + You can find an example docker compose file below. + - This docker compose file is placed in `~/sourcebot_review_agent_workspace`, and I'm mounting that directory to Sourcebot + - The config and the app private key files are placed in this directory + - The paths to these files are given to Sourcebot relative to `/data` since that's the directory in Sourcebot that I'm mounting to ```yaml services: From ec9526ffe9b3e8917eb3b703494c840acb73f3cf Mon Sep 17 00:00:00 2001 From: msukkari Date: Sat, 10 May 2025 12:29:26 -0700 Subject: [PATCH 15/15] mcp doc nit --- docs/docs/more/mcp-server.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/more/mcp-server.mdx b/docs/docs/more/mcp-server.mdx index 39968add..d992f7de 100644 --- a/docs/docs/more/mcp-server.mdx +++ b/docs/docs/more/mcp-server.mdx @@ -7,7 +7,7 @@ sidebarTitle: Sourcebot MCP server This feature is only available when [self-hosting](/self-hosting) with [authentication](/self-hosting/more/authentication) disabled. -The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a open standard for providing context to LLMs. The [@sourcebot/mcp](https://www.npmjs.com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interface with your Sourcebot instance, enabling MCP clients like Cursor, Vscode, and others to have context over your entire codebase. +The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is an open standard for providing context to LLMs. The [@sourcebot/mcp](https://www.npmjs.com/package/@sourcebot/mcp) package is a MCP server that enables LLMs to interface with your Sourcebot instance, enabling MCP clients like Cursor, Vscode, and others to have context over your entire codebase. ## Getting Started