Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ Findings cluster by file, overlapping lines, and description similarity, so five
| `codex` | `codex exec` (read-only sandbox) | `codex login` (ChatGPT sub) or `OPENAI_API_KEY` |
| `claude` | `claude -p` (probes allowed, writes and CI suites denied) | `claude` login or `CLAUDE_CODE_OAUTH_TOKEN` |
| `kimi` | Claude Code against Kimi's Anthropic-compatible endpoint | `KIMI_API_KEY` |
| `glm` | GLM-5.2 via Claude Code against Z.ai's Anthropic-compatible endpoint | `ZAI_API_KEY`, or picked up from `opencode auth login` |
| `grok` | `grok -p` (grok-4.5, high reasoning effort) | `grok login` (SuperGrok / X Premium+) or `GROK_API_KEY` |
| `qwen` | `qwen -p` | `qwen` login (Coding Plan) or API key |
| `qwen` | Qwen (default qwen3.8-max-preview) via Claude Code against the Token Plan Anthropic endpoint | `QWEN_API_KEY` or picked up from `bl config agent` |

Every available engine runs by default; pick explicitly with `--engines codex,kimi`. An engine that's missing, rate-limited, or failing drops out and the review still lands with the rest. If *every* engine fails, the run exits non-zero instead of reporting a clean review.

Expand Down
2 changes: 1 addition & 1 deletion action/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Every engine uses *your* account through *its vendor's own CLI* — that's what
| `post` | `true` | post as PR review (inline comments where the finding anchors to the diff) |
| `fail-on` | `none` | fail the job at/above a severity: `critical`, `major`, `minor`, `nit` |
| `persist-auth` | `true` | cache rotated credentials between runs |
| `version` | `latest` | `@kyora-sh/review` version |
| `version` | `bundled` | run the source shipped with the action (pinned by your `@ref`), or an npm version |

## Security notes

Expand Down
10 changes: 7 additions & 3 deletions action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ inputs:
description: "Cache refreshed engine credentials between runs so seeded tokens keep working after they rotate (see README for the public-repo caveat)"
default: "true"
version:
description: "@kyora-sh/review version to run"
default: "latest"
description: "Which @kyora-sh/review to run: 'bundled' uses the source shipped with the action (pinned by your uses: @ref), or an npm version/tag"
default: "bundled"

runs:
using: "composite"
Expand Down Expand Up @@ -91,7 +91,11 @@ runs:
[ "${{ inputs.post }}" = "true" ] && args+=(--post)
fi
[ "${{ inputs.verify }}" = "true" ] && args+=(--verify)
bunx "@kyora-sh/review@${{ inputs.version }}" "${args[@]}"
if [ "${{ inputs.version }}" = "bundled" ]; then
bun "${GITHUB_ACTION_PATH}/../packages/review/cli/src/index.ts" "${args[@]}"
else
bunx "@kyora-sh/review@${{ inputs.version }}" "${args[@]}"
fi

- name: Persist refreshed credentials
if: always() && inputs.persist-auth == 'true' && steps.setup.outputs.engines != ''
Expand Down
3 changes: 2 additions & 1 deletion packages/review/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ bunx @kyora-sh/review review --pr 123 --post --verify
| `codex` | `codex exec` (read-only sandbox) | `codex login` (ChatGPT sub) or `OPENAI_API_KEY` |
| `claude` | `claude -p` (probes allowed, writes and CI suites denied) | `claude` login or `CLAUDE_CODE_OAUTH_TOKEN` |
| `kimi` | Claude Code against Kimi's Anthropic-compatible endpoint | `KIMI_API_KEY` (+ optional `KIMI_BASE_URL`, `KIMI_MODEL`) |
| `glm` | GLM-5.2 via Claude Code against Z.ai's Anthropic-compatible endpoint | `ZAI_API_KEY` or `opencode auth login` |
| `grok` | `grok -p` | `grok` login or `GROK_API_KEY` / `XAI_API_KEY` |
| `qwen` | `qwen -p` | `qwen` login (Coding Plan) or API key |
| `qwen` | Qwen (default qwen3.8-max-preview) via Claude Code against the Token Plan Anthropic endpoint | `QWEN_API_KEY` or `bl config agent` |

By default every available engine runs; pick explicitly with `--engines codex,kimi`. An engine that's rate-limited or fails just drops out — the review still lands with the rest.

Expand Down
58 changes: 53 additions & 5 deletions packages/review/cli/src/engines.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readFileSync } from "node:fs"
import { mkdtemp, rm } from "node:fs/promises"
import { tmpdir } from "node:os"
import { homedir, tmpdir } from "node:os"
import { join } from "node:path"
import type { EngineOverride, ReviewConfig } from "./types"

Expand All @@ -9,6 +10,8 @@ export interface EngineDef {
bin: string
/** env vars that must all be present for this engine to be selectable */
requiresEnv?: string[]
/** extra readiness check; returns a reason when unavailable, null when ready */
ready?: () => string | null
/** tokens {prompt} {schema} {out} are substituted; first element is replaced by the resolved bin */
args: string[]
env?: () => Record<string, string | undefined>
Expand All @@ -17,6 +20,27 @@ export interface EngineDef {
authHint: string
}

function bailianKey(): string | undefined {
if (process.env.QWEN_API_KEY) return process.env.QWEN_API_KEY
try {
const config = JSON.parse(readFileSync(join(homedir(), ".config/opencode/opencode.json"), "utf8"))
return config.provider?.["bailian-cli"]?.options?.apiKey ?? undefined
} catch {
return undefined
}
}

function zaiKey(): string | undefined {
if (process.env.ZAI_API_KEY) return process.env.ZAI_API_KEY
try {
const auth = JSON.parse(readFileSync(join(homedir(), ".local/share/opencode/auth.json"), "utf8"))
const entry = auth["zai-coding-plan"]
return entry?.key ?? entry?.apiKey ?? undefined
} catch {
return undefined
}
}

const CLAUDE_DENIED = [
"Write", "Edit", "MultiEdit", "NotebookEdit", "WebFetch", "WebSearch",
"Bash(bun test:*)", "Bash(bun run:*)", "Bash(bun install:*)", "Bash(bunx turbo:*)",
Expand Down Expand Up @@ -71,6 +95,20 @@ export const ENGINES: EngineDef[] = [
}),
authHint: "set KIMI_API_KEY (Kimi membership / platform.kimi.ai); optional KIMI_BASE_URL, KIMI_MODEL",
},
{
id: "glm",
label: "GLM 5.2 (Z.ai, via Claude Code)",
bin: "claude",
ready: () => (zaiKey() ? null : "no Z.ai key (set ZAI_API_KEY or run `opencode auth login` → Z.AI Coding Plan)"),
args: CLAUDE_ARGS,
env: () => ({
ANTHROPIC_BASE_URL: process.env.ZAI_BASE_URL ?? "https://api.z.ai/api/anthropic",
ANTHROPIC_AUTH_TOKEN: zaiKey(),
ANTHROPIC_MODEL: process.env.ZAI_MODEL ?? "glm-5.2",
ANTHROPIC_API_KEY: undefined,
}),
authHint: "set ZAI_API_KEY (GLM Coding Plan), or log in once via `opencode auth login` — the key is picked up from there",
},
{
id: "grok",
label: "Grok Build (xAI)",
Expand All @@ -80,10 +118,18 @@ export const ENGINES: EngineDef[] = [
},
{
id: "qwen",
label: "Qwen Code (Alibaba)",
bin: "qwen",
args: ["-p", "{prompt}"],
authHint: "run `qwen` once to log in (Coding Plan), or configure an API key per qwen-code docs",
label: "Qwen (Alibaba Token Plan, via Claude Code)",
bin: "claude",
ready: () => (bailianKey() ? null : "no Token Plan key (set QWEN_API_KEY or run `bl config agent --agent opencode ...`)"),
args: CLAUDE_ARGS,
env: () => ({
ANTHROPIC_BASE_URL:
process.env.QWEN_BASE_URL ?? "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic",
ANTHROPIC_AUTH_TOKEN: bailianKey(),
ANTHROPIC_MODEL: process.env.QWEN_MODEL ?? "qwen3.8-max-preview",
ANTHROPIC_API_KEY: undefined,
}),
authHint: "set QWEN_API_KEY (Token Plan key), or run `bl config agent` once — the key is picked up from there",
},
]

Expand All @@ -107,6 +153,8 @@ export function engineStatus(engine: EngineDef, override: EngineOverride | undef
return { engine, available: false, reason: `${name} not set` }
}
}
const notReady = engine.ready?.()
if (notReady) return { engine, available: false, reason: notReady }
return { engine, available: true, reason: "ready" }
}

Expand Down
Loading