diff --git a/modules/opencode/default.nix b/modules/opencode/default.nix index 551ea78..3606e38 100644 --- a/modules/opencode/default.nix +++ b/modules/opencode/default.nix @@ -79,13 +79,41 @@ in format = "dotenv"; }; - home.packages = [ (opencodePkg' inputs'.llm-agents.packages.opencode2 "opencode2") ]; + home.packages = [ + (opencodePkg' inputs'.llm-agents.packages.opencode2 "opencode2") + self'.packages.open-code-review + ]; + + # OCR config — single source of truth, reuses the opencode-go auth + # already present in ~/.local/share/opencode/auth.json. The api_key is + # resolved via api_key_cmd so no secret lands in the nix store. + # Endpoint + model verified against https://opencode.ai/docs/go/#endpoints + home.file.".opencodereview/config.json".text = builtins.toJSON { + provider = "opencode-go"; + custom_providers = { + "opencode-go" = { + url = "https://opencode.ai/zen/go/v1"; + protocol = "openai"; + model = "deepseek-v4-pro"; + api_key_cmd = "${lib.getExe pkgs.jq} -r '.[\"opencode-go\"].key' ${config.home.homeDirectory}/.local/share/opencode/auth.json"; + }; + }; + }; + + xdg.configFile."opencode/tools" = { + source = "${self'.packages.open-code-review}/share/opencode/tools"; + recursive = true; + }; programs.opencode = { enable = true; package = opencodePkg' inputs'.llm-agents.packages.opencode "opencode"; enableMcpIntegration = cfg.enableMcp; - commands = ./commands; + commands = + (lib.mapAttrs' (name: _: lib.nameValuePair (lib.removeSuffix ".md" name) (./commands + "/${name}")) ( + lib.filterAttrs (n: v: v == "regular" && lib.hasSuffix ".md" n) (builtins.readDir ./commands) + )) + // (self'.packages.open-code-review.passthru.commands or { }); agents = ./agents; skills = toString ( pkgs.symlinkJoin { @@ -97,7 +125,6 @@ in } ); settings = { - plugin = [ "@mohak34/opencode-notifier@0.2.8" ]; experimental = { batch_tool = true; }; diff --git a/pkgs/open-code-review/commands/delegate-review.md b/pkgs/open-code-review/commands/delegate-review.md new file mode 100644 index 0000000..78705e3 --- /dev/null +++ b/pkgs/open-code-review/commands/delegate-review.md @@ -0,0 +1,48 @@ +--- +description: Run OCR in delegation mode — OCR handles file selection and rules, the host agent performs the actual review. +--- + +Invoke OpenCodeReview (OCR) in delegation mode. OCR determines which files to review and provides review rules; you perform the actual code review using your own capabilities. + +## Workflow + +### Step 1: Preview + +```bash +ocr delegate preview [user-args] +``` + +- Default (no user arguments): workspace mode (staged + unstaged + untracked). +- If the user provides `--commit` or `-c`: pass through as-is. +- If the user provides `--from` and `--to`: pass through as-is. +- (Optional) Provide `--background "context"` or `-b "context"` for business context. +- If `ocr` is not found, it is already on PATH via nix (`open-code-review` package). + +This outputs mode/ref metadata and the reviewable file list. + +### Step 2: Get Rules + +Pass all reviewable file paths to get their review checklists: + +```bash +ocr delegate rule ... +``` + +### Step 3: Get Diffs and Review + +For each reviewable file, get its diff using git (based on mode/ref from Step 1): +- Range: `git diff .. -- ` +- Commit: `git show -- ` +- Workspace: `git diff HEAD -- ` (or read directly for untracked files) + +Then review focusing on: correctness, security, performance, error handling, concurrency, maintainability. Only comment on changed code (+ lines). + +### Step 4: Report and Fix + +Classify each issue by severity: + +- **High**: Obvious bugs, security issues, data loss, or clear mistakes with precise fix proposals +- **Medium**: Reasonable concerns, performance suggestions, or fixes requiring manual work +- **Low**: Discard silently (likely false positives, nitpicks, or insufficient context) + +Automatically fix High and Medium issues that are safe and well-defined when the user asks for fixes; otherwise report them. diff --git a/pkgs/open-code-review/commands/ocr-health.md b/pkgs/open-code-review/commands/ocr-health.md new file mode 100644 index 0000000..adaa42b --- /dev/null +++ b/pkgs/open-code-review/commands/ocr-health.md @@ -0,0 +1,5 @@ +--- +description: Check OpenCodeReview and its LLM connection +--- + +Use the ocr_health tool and explain any configuration problem concisely. diff --git a/pkgs/open-code-review/commands/ocr-review.md b/pkgs/open-code-review/commands/ocr-review.md new file mode 100644 index 0000000..e36cb72 --- /dev/null +++ b/pkgs/open-code-review/commands/ocr-review.md @@ -0,0 +1,5 @@ +--- +description: Review code changes with OpenCodeReview +--- + +Use the ocr_review tool to review the requested target. Treat the following text as review intent, target details, and business context: $ARGUMENTS. If no target is specified, review the current workspace changes. Report findings by severity with exact file and line references. diff --git a/pkgs/open-code-review/package.nix b/pkgs/open-code-review/package.nix new file mode 100644 index 0000000..2deb6ea --- /dev/null +++ b/pkgs/open-code-review/package.nix @@ -0,0 +1,106 @@ +{ + lib, + stdenv, + buildGoModule, + fetchFromGitHub, + fetchNpmDeps, + npmHooks, + nodejs, + esbuild, +}: +let + version = "1.9.10"; + + src = fetchFromGitHub { + owner = "alibaba"; + repo = "open-code-review"; + tag = "v${version}"; + hash = "sha256-/HwWnPtn1CApQEVM6CXheR2LPjU0CCy2X2tciMu+gkM="; + }; + + # OpenCode custom tools use the Zod schema definitions from @opencode-ai/plugin. + # Bundled into a standalone ESM file so no runtime node_modules resolution is needed. + npmDeps = fetchNpmDeps { + name = "open-code-review-tools-npm-deps"; + inherit version; + src = ./plugin; + hash = "sha256-gbGsgoJbjqeFeGJJbYjmrede7z8/3dhT0Sy42/dF5kM="; + }; + + toolsBundle = stdenv.mkDerivation { + pname = "open-code-review-tools"; + inherit version src; + + nativeBuildInputs = [ + npmHooks.npmConfigHook + nodejs + esbuild + ]; + inherit npmDeps; + + prePatch = '' + cp ${./plugin/package.json} package.json + cp ${./plugin/package-lock.json} package-lock.json + cp ${./plugin/ocr-tools.ts} ocr-tools.ts + ''; + + buildPhase = '' + esbuild ocr-tools.ts \ + --bundle --format=esm --platform=node \ + --outfile=ocr.js + ''; + + installPhase = '' + mkdir -p $out/share/opencode/tools + install -m644 ocr.js $out/share/opencode/tools/ocr.js + ''; + }; +in +buildGoModule { + pname = "open-code-review"; + inherit version src; + + vendorHash = "sha256-RdIDGoDx/aIqm7gD2acHi9THVjZg5DkNU0y2S/cHm28="; + + # Mirror upstream release builds: static, stripped, version-stamped. + env.CGO_ENABLED = "0"; + ldflags = [ + "-s" + "-w" + "-X main.Version=v${version}" + ]; + + subPackages = [ "cmd/opencodereview" ]; + + # The unit tests shell out to `git init`; not worth wiring a check + # environment for a locally-packaged tool. + doCheck = false; + + postInstall = '' + mv $out/bin/opencodereview $out/bin/ocr + + mkdir -p $out/share/opencode/tools $out/share/opencode/commands + cp ${toolsBundle}/share/opencode/tools/* $out/share/opencode/tools/ + install -m644 ${./commands/ocr-review.md} $out/share/opencode/commands/ocr-review.md + install -m644 ${./commands/ocr-health.md} $out/share/opencode/commands/ocr-health.md + install -m644 ${./commands/delegate-review.md} $out/share/opencode/commands/delegate-review.md + ''; + + passthru = { + commands = { + ocr-review = ./commands/ocr-review.md; + ocr-health = ./commands/ocr-health.md; + delegate-review = ./commands/delegate-review.md; + }; + }; + + meta = with lib; { + description = "AI-powered code review CLI"; + homepage = "https://github.com/alibaba/open-code-review"; + changelog = "https://github.com/alibaba/open-code-review/releases/tag/v${version}"; + license = licenses.asl20; + mainProgram = "ocr"; + platforms = platforms.linux; + maintainers = with maintainers; [ karitham ]; + }; +} diff --git a/pkgs/open-code-review/plugin/ocr-tools.ts b/pkgs/open-code-review/plugin/ocr-tools.ts new file mode 100644 index 0000000..39c640a --- /dev/null +++ b/pkgs/open-code-review/plugin/ocr-tools.ts @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2026 alibaba/open-code-review Contributors + +import { spawn } from "node:child_process" +import { tool } from "@opencode-ai/plugin" + +interface ReviewInput { + commit?: string + from?: string + to?: string + resume?: string + background?: string + exclude?: string + model?: string + concurrency?: number + timeoutMinutes?: number + overallTimeoutMinutes?: number + maxTools?: number + maxGitProcesses?: number + preview?: boolean +} + +interface OcrInvocation { + command: string + prefixArgs: string[] +} + +interface RunOptions { + cwd: string + timeoutMs?: number | null + maxOutputBytes?: number + invocation?: OcrInvocation + signal?: AbortSignal +} + +interface RunResult { + stdout: string + stderr: string + exitCode: number +} + +class OcrExecutionError extends Error { + readonly exitCode: number | null + readonly stderr: string + readonly stdout: string + + constructor(message: string, result: { + exitCode: number | null + stderr?: string + stdout?: string + }) { + super(message) + this.name = "OcrExecutionError" + this.exitCode = result.exitCode ?? null + this.stderr = result.stderr ?? "" + this.stdout = result.stdout ?? "" + } +} + +function pushValue(args: string[], flag: string, value: string | number | undefined): void { + if (value !== undefined && value !== "") { + args.push(flag, String(value)) + } +} + +function buildReviewArgs(input: ReviewInput, repo: string): string[] { + const hasRange = input.from !== undefined || input.to !== undefined + if (hasRange && (!input.from || !input.to)) { + throw new Error("Both 'from' and 'to' are required for a branch comparison.") + } + if (input.commit && hasRange) { + throw new Error("Use either 'commit' or a 'from'/'to' range, not both.") + } + if (input.resume && (input.commit || hasRange)) { + throw new Error("'resume' cannot be combined with 'commit' or a 'from'/'to' range.") + } + if (input.preview && input.resume) { + throw new Error("'preview' and 'resume' cannot be used together.") + } + + const args = ["review", "--audience", "agent"] + if (!input.preview) { + args.push("--format", "json") + } + args.push("--repo", repo) + + pushValue(args, "--commit", input.commit) + pushValue(args, "--from", input.from) + pushValue(args, "--to", input.to) + pushValue(args, "--resume", input.resume) + pushValue(args, "--background", input.background) + pushValue(args, "--exclude", input.exclude) + pushValue(args, "--model", input.model) + pushValue(args, "--concurrency", input.concurrency) + pushValue(args, "--timeout", input.timeoutMinutes) + pushValue(args, "--max-tools", input.maxTools) + pushValue(args, "--max-git-procs", input.maxGitProcesses) + + if (input.preview) { + args.push("--preview") + } + return args +} + +function appendChunk( + chunks: Uint8Array[], + currentBytes: number, + chunk: Uint8Array, + maxBytes: number, +): number { + const nextBytes = currentBytes + chunk.byteLength + if (nextBytes > maxBytes) { + throw new Error(`OCR output exceeded the ${maxBytes}-byte safety limit.`) + } + chunks.push(chunk) + return nextBytes +} + +async function runOcr(args: string[], options: RunOptions): Promise { + const invocation = options.invocation ?? { command: "ocr", prefixArgs: [] } + const timeoutMs = options.timeoutMs === undefined ? 15 * 60 * 1000 : options.timeoutMs + const maxOutputBytes = options.maxOutputBytes ?? 10 * 1024 * 1024 + + return await new Promise((resolve, reject) => { + const child = spawn( + invocation.command, + [...invocation.prefixArgs, ...args], + { + cwd: options.cwd, + env: process.env, + shell: false, + detached: true, + }, + ) + child.stdin.end() + + const stdoutChunks: Uint8Array[] = [] + const stderrChunks: Uint8Array[] = [] + let outputBytes = 0 + let settled = false + let closed = false + let timer: ReturnType | undefined + let forceKillTimer: ReturnType | undefined + let abort: (() => void) | undefined + + const finish = (callback: () => void): void => { + if (settled) return + settled = true + clearTimeout(timer) + if (abort) { + options.signal?.removeEventListener("abort", abort) + } + callback() + } + + const killProcessGroup = (signal: NodeJS.Signals): void => { + if (closed || child.pid === undefined) return + if (process.platform === "win32") { + spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" }) + return + } + try { + process.kill(-child.pid, signal) + } catch { + child.kill(signal) + } + } + + const terminateChild = (): void => { + if (closed) return + killProcessGroup("SIGTERM") + forceKillTimer ??= setTimeout(() => { + if (!closed) { + killProcessGroup("SIGKILL") + } + }, 3_000) + } + + const failForOutputLimit = (error: Error): void => { + terminateChild() + finish(() => reject(error)) + } + + child.stdout.on("data", (chunk: Buffer) => { + try { + outputBytes = appendChunk(stdoutChunks, outputBytes, chunk, maxOutputBytes) + } catch (error) { + failForOutputLimit(error as Error) + } + }) + child.stderr.on("data", (chunk: Buffer) => { + try { + outputBytes = appendChunk(stderrChunks, outputBytes, chunk, maxOutputBytes) + } catch (error) { + failForOutputLimit(error as Error) + } + }) + + child.on("error", (error) => { + const message = error.message.includes("ENOENT") + ? "OpenCodeReview is not installed or 'ocr' is not on PATH." + : `Failed to start OpenCodeReview: ${error.message}` + finish(() => reject(new OcrExecutionError(message, { exitCode: null }))) + }) + + child.on("close", (exitCode) => { + closed = true + clearTimeout(forceKillTimer) + finish(() => { + const result = { + stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(), + stderr: Buffer.concat(stderrChunks).toString("utf8").trim(), + exitCode: exitCode ?? 1, + } + if (exitCode !== 0) { + reject(new OcrExecutionError( + result.stderr || result.stdout || `OpenCodeReview exited with code ${result.exitCode}.`, + result, + )) + return + } + resolve(result) + }) + }) + + abort = (): void => { + terminateChild() + finish(() => reject(new OcrExecutionError( + "OpenCodeReview was cancelled by OpenCode.", + { + exitCode: null, + stdout: Buffer.concat(stdoutChunks).toString("utf8"), + stderr: Buffer.concat(stderrChunks).toString("utf8"), + }, + ))) + } + options.signal?.addEventListener("abort", abort, { once: true }) + + if (timeoutMs !== null) { + timer = setTimeout(() => { + terminateChild() + finish(() => reject(new OcrExecutionError( + `OpenCodeReview timed out after ${Math.round(timeoutMs / 1000)} seconds.`, + { + exitCode: null, + stdout: Buffer.concat(stdoutChunks).toString("utf8"), + stderr: Buffer.concat(stderrChunks).toString("utf8"), + }, + ))) + }, timeoutMs) + } + + if (options.signal?.aborted) { + abort() + } + }) +} + +function formatReviewResult(result: RunResult, preview: boolean): string { + if (preview) { + return result.stdout || "No files changed." + } + if (result.stdout === "") { + return "No changes detected; OCR produced no output." + } + try { + JSON.parse(result.stdout) + return result.stdout + } catch { + throw new OcrExecutionError("OpenCodeReview returned invalid JSON.", result) + } +} + +const optionalString = (description: string) => + tool.schema.string().optional().describe(description) + +const optionalPositiveInt = (description: string) => + tool.schema.number().int().positive().optional().describe(description) + +const reviewArgs = { + commit: optionalString("Review one commit against its parent."), + from: optionalString("Base ref for a branch/range comparison. Must be paired with 'to'."), + to: optionalString("Target ref for a branch/range comparison. Must be paired with 'from'."), + resume: optionalString("Resume a previous OCR review session by ID."), + background: optionalString("Business or requirement context that the implementation should satisfy."), + exclude: optionalString("Comma-separated gitignore-style exclusion patterns."), + model: optionalString("Override the model configured in OpenCodeReview."), + concurrency: optionalPositiveInt("Maximum concurrent file reviews."), + timeoutMinutes: optionalPositiveInt("Per-file OCR timeout in minutes."), + overallTimeoutMinutes: optionalPositiveInt( + "Optional wall-clock timeout for the complete OCR process in minutes.", + ), + maxTools: optionalPositiveInt("Maximum tool-call rounds per file; OCR enforces a minimum of 10."), + maxGitProcesses: optionalPositiveInt("Maximum concurrent Git subprocesses."), + preview: tool.schema.boolean().optional().describe( + "List the files that would be reviewed without calling an LLM.", + ), +} + +export const review = tool({ + description: + "Run OpenCodeReview on workspace changes, one commit, or a ref range. " + + "Returns structured line-level findings as JSON. Use preview=true to inspect scope without LLM usage.", + args: reviewArgs, + async execute(args, context) { + const input = args as ReviewInput + const cwd = context.worktree || context.directory || process.cwd() + const defaultOverallMs = 30 * 60 * 1000 + const options: RunOptions = { + cwd, + signal: context.abort, + timeoutMs: input.overallTimeoutMinutes !== undefined + ? input.overallTimeoutMinutes * 60 * 1000 + : defaultOverallMs, + } + const result = await runOcr(buildReviewArgs(input, cwd), options) + return formatReviewResult(result, input.preview === true) + }, +}) + +export const health = tool({ + description: + "Check the installed OpenCodeReview version and verify its configured LLM connection.", + args: {}, + async execute(_args, context) { + const cwd = context.worktree || context.directory || process.cwd() + const [version, llm] = await Promise.allSettled([ + runOcr(["version"], { + cwd, + timeoutMs: 30_000, + signal: context.abort, + }), + runOcr(["llm", "test"], { + cwd, + timeoutMs: 60_000, + signal: context.abort, + }), + ]) + const rejected = [version, llm].find( + (result): result is PromiseRejectedResult => result.status === "rejected", + ) + if (context.abort?.aborted && rejected) { + throw rejected.reason + } + + const parts: string[] = [] + if (version.status === "fulfilled") { + parts.push(version.value.stdout) + } else { + parts.push(`Version check failed: ${version.reason?.message ?? "unknown error"}`) + } + if (llm.status === "fulfilled") { + parts.push(llm.value.stdout, llm.value.stderr) + } else { + parts.push(`LLM connection check failed: ${llm.reason?.message ?? "unknown error"}`) + } + return parts.filter(Boolean).join("\n") + }, +}) diff --git a/pkgs/open-code-review/plugin/package-lock.json b/pkgs/open-code-review/plugin/package-lock.json new file mode 100644 index 0000000..fbb6491 --- /dev/null +++ b/pkgs/open-code-review/plugin/package-lock.json @@ -0,0 +1,404 @@ +{ + "name": "open-code-review-tools", + "version": "1.9.10", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "open-code-review-tools", + "version": "1.9.10", + "dependencies": { + "@opencode-ai/plugin": "1.18.22" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz", + "integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@opencode-ai/plugin": { + "version": "1.18.22", + "resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.18.22.tgz", + "integrity": "sha512-ExKQu8EkJRZgPzKIEiYvLuTs/wYsOxDFerjwHG7pJJhOeXqNOuPYLzuU4w3cpzMZp5vu7q2larJ/+K6M4ax2Xw==", + "license": "MIT", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@opencode-ai/sdk": "1.18.22", + "effect": "4.0.0-beta.83", + "zod": "4.1.8" + }, + "peerDependencies": { + "@opentui/core": ">=0.4.5", + "@opentui/keymap": ">=0.4.5", + "@opentui/solid": ">=0.4.5" + }, + "peerDependenciesMeta": { + "@opentui/core": { + "optional": true + }, + "@opentui/keymap": { + "optional": true + }, + "@opentui/solid": { + "optional": true + } + } + }, + "node_modules/@opencode-ai/sdk": { + "version": "1.18.22", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.18.22.tgz", + "integrity": "sha512-fRZ2r6nqdLBXfs7j531aibrqCC9tpbL2tmz3ThnwtLa3bMNUozEVAOPnZjh86JcTBDhNtACt5CNgKDZ1Pve1NA==", + "license": "MIT", + "dependencies": { + "cross-spawn": "7.0.6" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/effect": { + "version": "4.0.0-beta.83", + "resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.83.tgz", + "integrity": "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "fast-check": "^4.8.0", + "find-my-way-ts": "^0.1.6", + "ini": "^7.0.0", + "kubernetes-types": "^1.30.0", + "msgpackr": "^2.0.1", + "multipasta": "^0.2.7", + "toml": "^4.1.1", + "uuid": "^14.0.0", + "yaml": "^2.9.0" + } + }, + "node_modules/fast-check": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz", + "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^8.0.0" + }, + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/find-my-way-ts": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz", + "integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==", + "license": "MIT" + }, + "node_modules/ini": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz", + "integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==", + "license": "ISC", + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/kubernetes-types": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz", + "integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==", + "license": "Apache-2.0" + }, + "node_modules/msgpackr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.5.tgz", + "integrity": "sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.4" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/multipasta": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.8.tgz", + "integrity": "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==", + "license": "MIT" + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pure-rand": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", + "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/toml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz", + "integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/uuid": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz", + "integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", + "integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/pkgs/open-code-review/plugin/package.json b/pkgs/open-code-review/plugin/package.json new file mode 100644 index 0000000..31abd4b --- /dev/null +++ b/pkgs/open-code-review/plugin/package.json @@ -0,0 +1,9 @@ +{ + "name": "open-code-review-tools", + "version": "1.9.10", + "private": true, + "type": "module", + "dependencies": { + "@opencode-ai/plugin": "1.18.22" + } +}