@humanbased/crosscheck 1.6.1-beta.5 → 1.7.0-beta.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/crosscheck.config.example.yml +20 -4
- package/dist/__tests__/detector.test.js +22 -0
- package/dist/__tests__/detector.test.js.map +1 -1
- package/dist/__tests__/onboard-preservation.test.js +27 -5
- package/dist/__tests__/onboard-preservation.test.js.map +1 -1
- package/dist/__tests__/onboard-vendors.test.d.ts +2 -0
- package/dist/__tests__/onboard-vendors.test.d.ts.map +1 -0
- package/dist/__tests__/onboard-vendors.test.js +122 -0
- package/dist/__tests__/onboard-vendors.test.js.map +1 -0
- package/dist/__tests__/optimize.test.js +18 -2
- package/dist/__tests__/optimize.test.js.map +1 -1
- package/dist/__tests__/review-models.test.js +5 -5
- package/dist/__tests__/review-models.test.js.map +1 -1
- package/dist/__tests__/review-strategy.test.js +10 -10
- package/dist/__tests__/review-strategy.test.js.map +1 -1
- package/dist/__tests__/reviewer-setup.test.d.ts +2 -0
- package/dist/__tests__/reviewer-setup.test.d.ts.map +1 -0
- package/dist/__tests__/reviewer-setup.test.js +134 -0
- package/dist/__tests__/reviewer-setup.test.js.map +1 -0
- package/dist/__tests__/runner.test.js +12 -2
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/issue.d.ts.map +1 -1
- package/dist/commands/issue.js +40 -16
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/onboard.d.ts +8 -4
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +144 -88
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/optimize.d.ts +1 -1
- package/dist/commands/optimize.d.ts.map +1 -1
- package/dist/commands/optimize.js +53 -13
- package/dist/commands/optimize.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +9 -3
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +2 -2
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +12 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +6 -3
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/review-model-tiers.json +9 -9
- package/dist/config/review-strategy.json +12 -12
- package/dist/config/schema.d.ts +102 -16
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +28 -3
- package/dist/config/schema.js.map +1 -1
- package/dist/github/client.js +1 -1
- package/dist/github/client.js.map +1 -1
- package/dist/github/detector.d.ts +2 -2
- package/dist/github/detector.d.ts.map +1 -1
- package/dist/github/detector.js +46 -11
- package/dist/github/detector.js.map +1 -1
- package/dist/lib/comment-bodies.d.ts.map +1 -1
- package/dist/lib/comment-bodies.js +4 -2
- package/dist/lib/comment-bodies.js.map +1 -1
- package/dist/lib/review-models.d.ts +4 -1
- package/dist/lib/review-models.d.ts.map +1 -1
- package/dist/lib/review-models.js +8 -0
- package/dist/lib/review-models.js.map +1 -1
- package/dist/lib/reviewer-error.d.ts.map +1 -1
- package/dist/lib/reviewer-error.js +12 -8
- package/dist/lib/reviewer-error.js.map +1 -1
- package/dist/lib/reviewer-setup.d.ts +25 -0
- package/dist/lib/reviewer-setup.d.ts.map +1 -0
- package/dist/lib/reviewer-setup.js +126 -0
- package/dist/lib/reviewer-setup.js.map +1 -0
- package/dist/lib/runner.d.ts +8 -7
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +71 -20
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/smart-switch.d.ts +7 -6
- package/dist/lib/smart-switch.d.ts.map +1 -1
- package/dist/lib/smart-switch.js +9 -2
- package/dist/lib/smart-switch.js.map +1 -1
- package/dist/lib/vendor.d.ts +2 -2
- package/dist/lib/vendor.d.ts.map +1 -1
- package/dist/lib/vendor.js +12 -2
- package/dist/lib/vendor.js.map +1 -1
- package/dist/lib/workflow.d.ts +8 -8
- package/dist/lib/workflow.js +1 -1
- package/dist/lib/workflow.js.map +1 -1
- package/dist/reviewers/fix.d.ts +6 -0
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +61 -0
- package/dist/reviewers/fix.js.map +1 -1
- package/dist/reviewers/opencode-env.d.ts +13 -0
- package/dist/reviewers/opencode-env.d.ts.map +1 -0
- package/dist/reviewers/opencode-env.js +52 -0
- package/dist/reviewers/opencode-env.js.map +1 -0
- package/dist/reviewers/opencode.d.ts +17 -0
- package/dist/reviewers/opencode.d.ts.map +1 -0
- package/dist/reviewers/opencode.js +293 -0
- package/dist/reviewers/opencode.js.map +1 -0
- package/get-started.md +46 -8
- package/get-started.zh.md +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import { rmSync, mkdtempSync, renameSync, existsSync, writeFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import { DEFAULT_REVIEW_INSTRUCTIONS } from '../lib/workflow.js';
|
|
6
|
+
import { resolveOpenCodeModel } from '../lib/review-models.js';
|
|
7
|
+
import { withTimeoutRetry } from '../lib/with-timeout-retry.js';
|
|
8
|
+
import { vendorFailureSummary } from '../lib/vendor-error-summary.js';
|
|
9
|
+
import { tierTimeoutMs } from './tier-timeouts.js';
|
|
10
|
+
import { buildOpenCodeEnv } from './opencode-env.js';
|
|
11
|
+
import { loadRepositoryReviewGuidance } from '../lib/repository-guidance.js';
|
|
12
|
+
import { withCredentialFreeOrigin } from '../lib/clone.js';
|
|
13
|
+
// OpenCode's reasoning-effort ladder is the `#variant` suffix on
|
|
14
|
+
// `--model provider/model#variant`. Variants are provider-defined, not one
|
|
15
|
+
// global ladder like claude/codex: deepseek-v4-* (the opencode models
|
|
16
|
+
// crosscheck ships policy for) expose none / high / max, with no low/medium.
|
|
17
|
+
// Whitelist rather than translation — anything outside this vocabulary falls
|
|
18
|
+
// back to `high` instead of reaching the CLI as an unsupported variant.
|
|
19
|
+
const OPENCODE_EFFORT_MAP = {
|
|
20
|
+
none: 'none',
|
|
21
|
+
high: 'high',
|
|
22
|
+
max: 'max',
|
|
23
|
+
};
|
|
24
|
+
export function opencodeEffort(effort) {
|
|
25
|
+
return (effort && OPENCODE_EFFORT_MAP[effort]) ?? 'high';
|
|
26
|
+
}
|
|
27
|
+
export function parseOpenCodeOutput(raw) {
|
|
28
|
+
let review = '';
|
|
29
|
+
let inputTokens = 0;
|
|
30
|
+
let outputTokens = 0;
|
|
31
|
+
let reasoningTokens = 0;
|
|
32
|
+
let sawTokens = false;
|
|
33
|
+
for (const line of raw.split('\n')) {
|
|
34
|
+
if (!line.trim())
|
|
35
|
+
continue;
|
|
36
|
+
let event;
|
|
37
|
+
try {
|
|
38
|
+
event = JSON.parse(line);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Non-JSON line (progress/log noise) — skip, never fail the whole parse.
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (event.type === 'text' && event.part?.text)
|
|
45
|
+
review += event.part.text;
|
|
46
|
+
if (event.type === 'step_finish' && event.part?.tokens) {
|
|
47
|
+
// Each model step emits its own payload; accumulate rather than overwrite,
|
|
48
|
+
// and include reasoning so a multi-step run is not undercounted.
|
|
49
|
+
const t = event.part.tokens;
|
|
50
|
+
if (typeof t.input === 'number')
|
|
51
|
+
inputTokens += t.input;
|
|
52
|
+
if (typeof t.output === 'number')
|
|
53
|
+
outputTokens += t.output;
|
|
54
|
+
if (typeof t.reasoning === 'number')
|
|
55
|
+
reasoningTokens += t.reasoning;
|
|
56
|
+
sawTokens = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const tokensUsed = sawTokens ? inputTokens + outputTokens + reasoningTokens : undefined;
|
|
60
|
+
return { review, tokensUsed };
|
|
61
|
+
}
|
|
62
|
+
// OpenCode follows the same VERDICT rule as claude/codex, so a well-behaved run
|
|
63
|
+
// ends with the line. Match it case-insensitively: a `Verdict: BLOCK` that the
|
|
64
|
+
// old exact-match check missed then received an appended APPROVE, which the
|
|
65
|
+
// parser read as the final verdict. When no verdict was stated at all, default
|
|
66
|
+
// to NEEDS WORK — never APPROVE, which would clear a PR on a verdict nobody
|
|
67
|
+
// gave (an empty or refusing body is caught earlier by detectInconclusiveReview).
|
|
68
|
+
export function inferVerdictFromOpenCodeOutput(text) {
|
|
69
|
+
return /\bVERDICT\s*:/i.test(text) ? text : `${text}\n\nVERDICT: NEEDS WORK`;
|
|
70
|
+
}
|
|
71
|
+
// Detect transient OpenCode errors that should be retried (rate limits, socket
|
|
72
|
+
// disconnects). OpenCode reports these through the same shapes as codex.
|
|
73
|
+
function isRetryableOpenCodeError(message) {
|
|
74
|
+
return /socket.*closed|429|rate limit|connection.*reset|econnreset/i.test(message);
|
|
75
|
+
}
|
|
76
|
+
const MAX_OPENCODE_RETRIES = 2;
|
|
77
|
+
const OPENCODE_RETRY_DELAY_MS = 5000;
|
|
78
|
+
// Scans stderr bottom-up for the first fatal/error line, skipping OpenCode
|
|
79
|
+
// header/log boilerplate.
|
|
80
|
+
function extractOpenCodeErrorSummary(stderr) {
|
|
81
|
+
const lines = stderr.split('\n').map(l => l.trim()).filter(Boolean);
|
|
82
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
83
|
+
const l = lines[i];
|
|
84
|
+
if (/^(fatal|error):/i.test(l))
|
|
85
|
+
return l;
|
|
86
|
+
}
|
|
87
|
+
return lines.filter(l => !l.startsWith('---') &&
|
|
88
|
+
!/^\d{4}-\d{2}-\d{2}T\d/.test(l) &&
|
|
89
|
+
!/ WARN /i.test(l)).at(-1);
|
|
90
|
+
}
|
|
91
|
+
// OpenCode auto-discovers a project's own opencode.json / .opencode directory
|
|
92
|
+
// from the working directory upward, and loads any plugin they register. In an
|
|
93
|
+
// untrusted checkout that is an arbitrary-code channel independent of the
|
|
94
|
+
// prompt, and OpenCode v2 has no `--pure` / config override flag to suppress it.
|
|
95
|
+
// Remove them before the run so only the operator's global config applies. The
|
|
96
|
+
// clone is a throwaway and a review reads the diff, not these files.
|
|
97
|
+
// A restrictive permission policy for the untrusted run. OpenCode v2 has no
|
|
98
|
+
// sandbox flag, so this is the tool-denial layer: a review inspects the diff by
|
|
99
|
+
// running read-only git, so allow exactly those shell commands and deny every
|
|
100
|
+
// other one. `--auto` auto-approves `ask` but cannot widen a `deny`.
|
|
101
|
+
// `allowEdit` is left for the fix step, which must write files.
|
|
102
|
+
export function opencodePolicy(allowEdit) {
|
|
103
|
+
return {
|
|
104
|
+
permissions: [
|
|
105
|
+
{ action: 'shell', resource: '*', effect: 'deny' },
|
|
106
|
+
{ action: 'shell', resource: 'git status *', effect: 'allow' },
|
|
107
|
+
{ action: 'shell', resource: 'git diff *', effect: 'allow' },
|
|
108
|
+
{ action: 'shell', resource: 'git log *', effect: 'allow' },
|
|
109
|
+
{ action: 'shell', resource: 'git show *', effect: 'allow' },
|
|
110
|
+
...(allowEdit ? [] : [{ action: 'edit', resource: '*', effect: 'deny' }]),
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function isolateCheckoutOpenCodeConfig(repoDir, allowEdit = false) {
|
|
115
|
+
const backupDir = mkdtempSync(join(tmpdir(), 'crosscheck-oc-config-'));
|
|
116
|
+
const moved = [];
|
|
117
|
+
for (const rel of ['opencode.json', 'opencode.jsonc', '.opencode']) {
|
|
118
|
+
const from = join(repoDir, rel);
|
|
119
|
+
if (!existsSync(from))
|
|
120
|
+
continue;
|
|
121
|
+
const to = join(backupDir, rel.replace(/[/\\]/g, '_'));
|
|
122
|
+
try {
|
|
123
|
+
renameSync(from, to);
|
|
124
|
+
moved.push({ from, to });
|
|
125
|
+
}
|
|
126
|
+
catch { /* best effort */ }
|
|
127
|
+
}
|
|
128
|
+
// Install the policy as the project config this run will load; remove it on
|
|
129
|
+
// restore so a later fix's `git add -A` cannot stage it.
|
|
130
|
+
let policyWritten = false;
|
|
131
|
+
try {
|
|
132
|
+
writeFileSync(join(repoDir, 'opencode.json'), JSON.stringify(opencodePolicy(allowEdit), null, 2));
|
|
133
|
+
policyWritten = true;
|
|
134
|
+
}
|
|
135
|
+
catch { /* best effort */ }
|
|
136
|
+
// Restore the checkout's own config so a later fix step does not stage these
|
|
137
|
+
// tracked deletions as changed files (git add -A would then commit and push).
|
|
138
|
+
return () => {
|
|
139
|
+
if (policyWritten) {
|
|
140
|
+
try {
|
|
141
|
+
rmSync(join(repoDir, 'opencode.json'), { force: true });
|
|
142
|
+
}
|
|
143
|
+
catch { /* best effort */ }
|
|
144
|
+
}
|
|
145
|
+
const failed = [];
|
|
146
|
+
for (const { from, to } of moved.reverse()) {
|
|
147
|
+
try {
|
|
148
|
+
renameSync(to, from);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
failed.push(from);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Delete the backup only when every path was restored. If the agent recreated
|
|
155
|
+
// a conflicting path, keep the backup and fail loudly so the original tracked
|
|
156
|
+
// configuration is never silently lost.
|
|
157
|
+
if (failed.length > 0) {
|
|
158
|
+
throw new Error(`could not restore OpenCode config: ${failed.join(', ')} (backup kept at ${backupDir})`);
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
rmSync(backupDir, { recursive: true, force: true });
|
|
162
|
+
}
|
|
163
|
+
catch { /* best effort */ }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export async function runOpenCodeReview(repoDir, baseBranch, prTitle, quality, vendor, stepInstructions, onLog, timeoutMs, onRetry, issueContext, _skillSession) {
|
|
167
|
+
const model = resolveOpenCodeModel(quality, vendor);
|
|
168
|
+
const effort = opencodeEffort(vendor.effort);
|
|
169
|
+
const tierTimeout = tierTimeoutMs(quality.tier);
|
|
170
|
+
// timeoutMs: 0 → no cap (crazy/halfcrazy); undefined → tier-based default; positive → user-specified
|
|
171
|
+
const resolvedTimeout = timeoutMs === undefined ? tierTimeout : timeoutMs === 0 ? undefined : timeoutMs;
|
|
172
|
+
const focusLine = quality.focus.length > 0
|
|
173
|
+
? `Focus areas: ${quality.focus.join(', ')}.`
|
|
174
|
+
: '';
|
|
175
|
+
const customLine = quality.custom_prompt ?? '';
|
|
176
|
+
const behaviorInstructions = stepInstructions ?? DEFAULT_REVIEW_INSTRUCTIONS;
|
|
177
|
+
const repositoryGuidance = loadRepositoryReviewGuidance(repoDir, baseBranch);
|
|
178
|
+
// Same block order as runClaudeReview/runCodexReview, so the same PR gets the
|
|
179
|
+
// same brief whichever vendor draws it: behaviour block ends on the verdict
|
|
180
|
+
// rule, repository guidance after it (reference material, not a first step).
|
|
181
|
+
const prompt = [
|
|
182
|
+
`You are reviewing a pull request titled: "${prTitle}".`,
|
|
183
|
+
`The branch \`${baseBranch}\` is the base. Review only the changes introduced in this PR.`,
|
|
184
|
+
issueContext ?? '',
|
|
185
|
+
focusLine,
|
|
186
|
+
customLine,
|
|
187
|
+
behaviorInstructions,
|
|
188
|
+
repositoryGuidance,
|
|
189
|
+
].filter(Boolean).join('\n\n');
|
|
190
|
+
// OpenCode takes the prompt on stdin (like claude/codex): `opencode run` with
|
|
191
|
+
// no message argument reads stdin, and keeping the prompt out of argv stops
|
|
192
|
+
// repository guidance from reaching the process list. `--standalone` runs a
|
|
193
|
+
// private server, so a review of untrusted code never shares a session with
|
|
194
|
+
// the operator's interactive work. `--auto` is required even for a read-only
|
|
195
|
+
// review: a headless run auto-rejects permission requests otherwise, and
|
|
196
|
+
// reviewing a diff can trip `external_directory` (the clone lives outside the
|
|
197
|
+
// agent's workspace root) which is fatal without it. The env allowlist and
|
|
198
|
+
// the throwaway clone bound the blast radius of the widened permissions.
|
|
199
|
+
//
|
|
200
|
+
// OpenCode has no separate effort flag — reasoning effort is the `#variant`
|
|
201
|
+
// suffix on `--model provider/model#variant`. Without a pinned model there is
|
|
202
|
+
// nowhere to hang the suffix, so `vendors.opencode.effort` is honoured only
|
|
203
|
+
// when `vendors.opencode.model` is set; otherwise OpenCode's configured
|
|
204
|
+
// default model AND its default variant run, and `effort` is reported but
|
|
205
|
+
// not applied (it is not a claim the CLI was given).
|
|
206
|
+
const modelArgs = model ? ['--model', `${model}#${effort}`] : [];
|
|
207
|
+
const args = ['run', '--format', 'json', '--auto', '--standalone', ...modelArgs];
|
|
208
|
+
onLog?.(` running: opencode run --format json --auto --standalone${model ? ` --model ${model}#${effort}` : ''}`);
|
|
209
|
+
let lastErr = undefined;
|
|
210
|
+
for (let attempt = 1; attempt <= MAX_OPENCODE_RETRIES; attempt++) {
|
|
211
|
+
try {
|
|
212
|
+
const { result: { stdout }, retried } = await withCredentialFreeOrigin(repoDir, () => {
|
|
213
|
+
// Hide any OpenCode config the untrusted checkout carries while the agent
|
|
214
|
+
// runs; restore it after so a later fix step cannot stage the deletion.
|
|
215
|
+
const restoreConfig = isolateCheckoutOpenCodeConfig(repoDir);
|
|
216
|
+
return withTimeoutRetry(resolvedTimeout, (t) => execa('opencode', args, {
|
|
217
|
+
cwd: repoDir,
|
|
218
|
+
timeout: t,
|
|
219
|
+
input: prompt,
|
|
220
|
+
// extendEnv: false or execa merges process.env back in and the
|
|
221
|
+
// allowlist means nothing.
|
|
222
|
+
extendEnv: false,
|
|
223
|
+
// No `${repoDir}/node_modules/.bin` on PATH: the checkout is untrusted
|
|
224
|
+
// and a committed `node_modules/.bin/opencode` would shadow the real
|
|
225
|
+
// executable. A review only reads the diff, so repo-local tools are
|
|
226
|
+
// not needed the way codex.ts assumed they were.
|
|
227
|
+
env: buildOpenCodeEnv(),
|
|
228
|
+
}), {
|
|
229
|
+
onRetry: (effectiveMs, delayMs) => (onRetry ?? onLog)?.(` ⏱ opencode timed out at ${effectiveMs / 1000}s — waiting ${delayMs / 1000}s and retrying once`),
|
|
230
|
+
}).finally(restoreConfig);
|
|
231
|
+
});
|
|
232
|
+
const { review: parsedReview, tokensUsed } = parseOpenCodeOutput(stdout ?? '');
|
|
233
|
+
const rawReview = parsedReview.trim();
|
|
234
|
+
const review = inferVerdictFromOpenCodeOutput(rawReview);
|
|
235
|
+
// Report effort only when a pinned model actually carried the #variant;
|
|
236
|
+
// with no model there is no suffix, so claiming a level would be false.
|
|
237
|
+
return { review, tokensUsed, model: model ?? 'default', effort: model ? effort : undefined, retried };
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
const execa = err;
|
|
241
|
+
const rawStderr = execa.stderr ?? '';
|
|
242
|
+
const fullMessage = rawStderr || execa.message || '';
|
|
243
|
+
if (isRetryableOpenCodeError(fullMessage) && attempt < MAX_OPENCODE_RETRIES) {
|
|
244
|
+
const delay = OPENCODE_RETRY_DELAY_MS * attempt; // 5s, 10s
|
|
245
|
+
onLog?.(` opencode: transient error (${fullMessage.slice(0, 80)}), retrying in ${delay / 1000}s (attempt ${attempt}/${MAX_OPENCODE_RETRIES})...`);
|
|
246
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
247
|
+
lastErr = err;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const effectiveMs = execa.effectiveTimeoutMs ?? resolvedTimeout;
|
|
251
|
+
const retryNote = execa.retryDelayMs !== undefined ? ' (retried once)' : '';
|
|
252
|
+
const summary = execa.timedOut
|
|
253
|
+
? `timed out after ${effectiveMs !== undefined ? effectiveMs / 1000 : '?'}s${retryNote} — PR diff may be too large (tier: ${quality.tier})`
|
|
254
|
+
: vendorFailureSummary(execa, extractOpenCodeErrorSummary);
|
|
255
|
+
const thrown = Object.assign(new Error(`opencode: ${summary}`), {
|
|
256
|
+
exitCode: execa.exitCode,
|
|
257
|
+
timedOut: execa.timedOut,
|
|
258
|
+
stderr: rawStderr,
|
|
259
|
+
effectiveTimeoutMs: effectiveMs,
|
|
260
|
+
retryDelayMs: execa.retryDelayMs,
|
|
261
|
+
});
|
|
262
|
+
throw thrown;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (lastErr)
|
|
266
|
+
throw lastErr;
|
|
267
|
+
throw new Error('opencode: unexpected retry loop exit');
|
|
268
|
+
}
|
|
269
|
+
export async function checkOpenCodeAuth() {
|
|
270
|
+
try {
|
|
271
|
+
// `--format json` returns one record per provider, so human-readable headers
|
|
272
|
+
// and summaries can never be miscounted as authenticated providers the way
|
|
273
|
+
// the text form's line count could.
|
|
274
|
+
const { stdout } = await execa('opencode', ['auth', 'list', '--format', 'json'], { timeout: 10_000 });
|
|
275
|
+
let parsed;
|
|
276
|
+
try {
|
|
277
|
+
parsed = JSON.parse(stdout);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
parsed = null;
|
|
281
|
+
}
|
|
282
|
+
const providers = Array.isArray(parsed) ? parsed : [];
|
|
283
|
+
if (providers.length === 0) {
|
|
284
|
+
return { ok: false, detail: 'no providers configured — run: opencode auth login' };
|
|
285
|
+
}
|
|
286
|
+
return { ok: true, detail: `${providers.length} provider(s) configured` };
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
const error = err;
|
|
290
|
+
return { ok: false, detail: error.stderr ?? error.message ?? 'not found' };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/reviewers/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAC/E,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAE3B,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAE9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAA;AAE1D,iEAAiE;AACjE,2EAA2E;AAC3E,sEAAsE;AACtE,6EAA6E;AAC7E,6EAA6E;AAC7E,wEAAwE;AACxE,MAAM,mBAAmB,GAA2B;IAClD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;CACX,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,OAAO,CAAC,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC,IAAI,MAAM,CAAA;AAC1D,CAAC;AAiBD,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,eAAe,GAAG,CAAC,CAAA;IACvB,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC1B,IAAI,KAAoB,CAAA;QACxB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAA;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,SAAQ;QACV,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAA;QACxE,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;YACvD,2EAA2E;YAC3E,iEAAiE;YACjE,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAA;YAC3B,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;gBAAE,WAAW,IAAI,CAAC,CAAC,KAAK,CAAA;YACvD,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;gBAAE,YAAY,IAAI,CAAC,CAAC,MAAM,CAAA;YAC1D,IAAI,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;gBAAE,eAAe,IAAI,CAAC,CAAC,SAAS,CAAA;YACnE,SAAS,GAAG,IAAI,CAAA;QAClB,CAAC;IACH,CAAC;IACD,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,SAAS,CAAA;IACvF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAA;AAC/B,CAAC;AAED,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,+EAA+E;AAC/E,4EAA4E;AAC5E,kFAAkF;AAClF,MAAM,UAAU,8BAA8B,CAAC,IAAY;IACzD,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,yBAAyB,CAAA;AAC9E,CAAC;AAED,+EAA+E;AAC/E,yEAAyE;AACzE,SAAS,wBAAwB,CAAC,OAAe;IAC/C,OAAO,6DAA6D,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AACpF,CAAC;AAED,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAC9B,MAAM,uBAAuB,GAAG,IAAI,CAAA;AAEpC,2EAA2E;AAC3E,0BAA0B;AAC1B,SAAS,2BAA2B,CAAC,MAAc;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACnE,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAClB,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACtB,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;QACpB,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CACnB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;AACV,CAAC;AAED,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,iFAAiF;AACjF,+EAA+E;AAC/E,qEAAqE;AACrE,4EAA4E;AAC5E,gFAAgF;AAChF,8EAA8E;AAC9E,qEAAqE;AACrE,gEAAgE;AAChE,MAAM,UAAU,cAAc,CAAC,SAAkB;IAC/C,OAAO;QACL,WAAW,EAAE;YACX,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE;YAClD,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE;YAC9D,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE;YAC5D,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE;YAC3D,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE;YAC5D,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;SAC1E;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,OAAe,EAAE,SAAS,GAAG,KAAK;IAC9E,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAA;IACtE,MAAM,KAAK,GAAwC,EAAE,CAAA;IACrD,KAAK,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,gBAAgB,EAAE,WAAW,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC/B,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;QACtD,IAAI,CAAC;YAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACpF,CAAC;IACD,4EAA4E;IAC5E,yDAAyD;IACzD,IAAI,aAAa,GAAG,KAAK,CAAA;IACzB,IAAI,CAAC;QACH,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;QACjG,aAAa,GAAG,IAAI,CAAA;IACtB,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,8EAA8E;IAC9E,OAAO,GAAG,EAAE;QACV,IAAI,aAAa,EAAE,CAAC;YAAC,IAAI,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAAC,CAAC;QAClH,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC3C,IAAI,CAAC;gBAAC,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAAC,CAAC;QAC1D,CAAC;QACD,8EAA8E;QAC9E,8EAA8E;QAC9E,wCAAwC;QACxC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,SAAS,GAAG,CAAC,CAAA;QAC1G,CAAC;QACD,IAAI,CAAC;YAAC,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACzF,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,UAAkB,EAClB,OAAe,EACf,OAAsB,EACtB,MAA4B,EAC5B,gBAAyB,EACzB,KAA6B,EAC7B,SAAkB,EAClB,OAA+B,EAC/B,YAAqB,EACrB,aAAsC;IAEtC,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5C,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,qGAAqG;IACrG,MAAM,eAAe,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvG,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACxC,CAAC,CAAC,gBAAgB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QAC7C,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAA;IAE9C,MAAM,oBAAoB,GAAG,gBAAgB,IAAI,2BAA2B,CAAA;IAC5E,MAAM,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAE5E,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,MAAM,MAAM,GAAG;QACb,6CAA6C,OAAO,IAAI;QACxD,gBAAgB,UAAU,gEAAgE;QAC1F,YAAY,IAAI,EAAE;QAClB,SAAS;QACT,UAAU;QACV,oBAAoB;QACpB,kBAAkB;KACnB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAE9B,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,4EAA4E;IAC5E,6EAA6E;IAC7E,yEAAyE;IACzE,8EAA8E;IAC9E,2EAA2E;IAC3E,yEAAyE;IACzE,EAAE;IACF,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,wEAAwE;IACxE,0EAA0E;IAC1E,qDAAqD;IACrD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAChE,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,SAAS,CAAC,CAAA;IAEhF,KAAK,EAAE,CAAC,4DAA4D,KAAK,CAAC,CAAC,CAAC,YAAY,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEjH,IAAI,OAAO,GAAY,SAAS,CAAA;IAChC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,oBAAoB,EAAE,OAAO,EAAE,EAAE,CAAC;QACjE,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,wBAAwB,CAAC,OAAO,EAAE,GAAG,EAAE;gBACnF,0EAA0E;gBAC1E,wEAAwE;gBACxE,MAAM,aAAa,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAA;gBAC5D,OAAO,gBAAgB,CACrB,eAAe,EACf,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE;oBAC7B,GAAG,EAAE,OAAO;oBACZ,OAAO,EAAE,CAAC;oBACV,KAAK,EAAE,MAAM;oBACb,+DAA+D;oBAC/D,2BAA2B;oBAC3B,SAAS,EAAE,KAAK;oBAChB,uEAAuE;oBACvE,qEAAqE;oBACrE,oEAAoE;oBACpE,iDAAiD;oBACjD,GAAG,EAAE,gBAAgB,EAAE;iBACxB,CAAC,EACF;oBACE,OAAO,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAChC,CAAC,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC,6BAA6B,WAAW,GAAG,IAAI,eAAe,OAAO,GAAG,IAAI,qBAAqB,CAAC;iBAC1H,CACF,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;YAC1B,CAAC,CAAC,CAAA;YAEF,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;YAC9E,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,EAAE,CAAA;YACrC,MAAM,MAAM,GAAG,8BAA8B,CAAC,SAAS,CAAC,CAAA;YACxD,wEAAwE;YACxE,wEAAwE;YACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,CAAA;QACvG,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,GAAwJ,CAAA;YACtK,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAA;YACpC,MAAM,WAAW,GAAG,SAAS,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,CAAA;YAEpD,IAAI,wBAAwB,CAAC,WAAW,CAAC,IAAI,OAAO,GAAG,oBAAoB,EAAE,CAAC;gBAC5E,MAAM,KAAK,GAAG,uBAAuB,GAAG,OAAO,CAAA,CAAC,UAAU;gBAC1D,KAAK,EAAE,CAAC,gCAAgC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,KAAK,GAAG,IAAI,cAAc,OAAO,IAAI,oBAAoB,MAAM,CAAC,CAAA;gBAClJ,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;gBAC9D,OAAO,GAAG,GAAG,CAAA;gBACb,SAAQ;YACV,CAAC;YAED,MAAM,WAAW,GAAG,KAAK,CAAC,kBAAkB,IAAI,eAAe,CAAA;YAC/D,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAA;YAC3E,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ;gBAC5B,CAAC,CAAC,mBAAmB,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,SAAS,sCAAsC,OAAO,CAAC,IAAI,GAAG;gBAC3I,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,2BAA2B,CAAC,CAAA;YAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,OAAO,EAAE,CAAC,EAAE;gBAC9D,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,MAAM,EAAE,SAAS;gBACjB,kBAAkB,EAAE,WAAW;gBAC/B,YAAY,EAAE,KAAK,CAAC,YAAY;aACjC,CAAC,CAAA;YACF,MAAM,MAAM,CAAA;QACd,CAAC;IACH,CAAC;IAED,IAAI,OAAO;QAAE,MAAM,OAAO,CAAA;IAC1B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;AACzD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,6EAA6E;QAC7E,2EAA2E;QAC3E,oCAAoC;QACpC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;QACrG,IAAI,MAAe,CAAA;QACnB,IAAI,CAAC;YAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,MAAM,GAAG,IAAI,CAAA;QAAC,CAAC;QAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QACrD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oDAAoD,EAAE,CAAA;QACpF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,yBAAyB,EAAE,CAAA;IAC3E,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,GAA4C,CAAA;QAC1D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,WAAW,EAAE,CAAA;IAC5E,CAAC;AACH,CAAC"}
|
package/get-started.md
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
|
|
38
38
|
## Prerequisites
|
|
39
39
|
|
|
40
|
-
You need GitHub CLI and at least one authenticated AI reviewer CLI before crosscheck can run a one-shot review. Install both Claude Code and Codex only if you want cross-vendor review routing.
|
|
40
|
+
You need GitHub CLI and at least one authenticated AI reviewer CLI before crosscheck can run a one-shot review. Install both Claude Code and Codex only if you want cross-vendor review routing. OpenCode is an optional third harness, disabled by default.
|
|
41
41
|
|
|
42
42
|
### Claude Code
|
|
43
43
|
|
|
@@ -65,6 +65,15 @@ printenv OPENAI_API_KEY | codex login --with-api-key
|
|
|
65
65
|
|
|
66
66
|
Then set `auth: api-key` in your config to enable model selection.
|
|
67
67
|
|
|
68
|
+
### OpenCode
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm install -g @opencode/cli
|
|
72
|
+
opencode auth login # sign in to a provider (e.g. DeepSeek, Anthropic, OpenAI)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
OpenCode is **opt-in**: enable it with `vendors.opencode.enabled: true` (or `crosscheck onboard`). Its model and provider come from your OpenCode config (`opencode auth login` / `opencode.json`) — crosscheck pins no model by default, so reviews run against whichever model OpenCode is configured to use. Reasoning effort is carried as the `#variant` suffix on `--model provider/model#variant`, so `vendors.opencode.effort` (none | high | max) applies only when `vendors.opencode.model` is also set.
|
|
76
|
+
|
|
68
77
|
### GitHub CLI
|
|
69
78
|
|
|
70
79
|
```bash
|
|
@@ -384,6 +393,10 @@ What it checks: `codex` CLI, `claude` CLI, `gh` CLI, GitHub auth, and webhook-se
|
|
|
384
393
|
|
|
385
394
|
### `crosscheck onboard`
|
|
386
395
|
|
|
396
|
+
Interactive onboarding starts with a checklist of Claude, Codex, and OpenCode, including tools that are not installed or need login. Select one or more tools first, then choose to run the displayed installation/login command, handle setup in another terminal and check again, skip that tool, or cancel. Commands run only when explicitly chosen. Setup rechecks each tool after installation/login and again before saving; skipped or unready tools are never enabled. One ready selection uses single-vendor mode; multiple ready selections use cross-vendor mode. Existing choices are preselected, and OpenCode is unchecked by default on fresh mixed installs.
|
|
397
|
+
|
|
398
|
+
With `--yes`, no installation/login commands run and no tools are selected interactively: existing opt-outs are preserved and unavailable tools are disabled. If no enabled reviewer is available, setup stops without writing config. A fresh OpenCode-only installation enables OpenCode. These checks verify installation and saved login/provider configuration, not a live request to each model or provider account permissions.
|
|
399
|
+
|
|
387
400
|
The recommended first-time setup command. Walks through ten steps interactively and writes a ready-to-use config.
|
|
388
401
|
|
|
389
402
|
```bash
|
|
@@ -396,7 +409,7 @@ crosscheck onboard --reconfigure # re-run setup even if config already exists
|
|
|
396
409
|
|
|
397
410
|
**The ten steps:**
|
|
398
411
|
|
|
399
|
-
**Step 1 — Environment check.** Verifies codex CLI, claude CLI, gh CLI, and GitHub token.
|
|
412
|
+
**Step 1 — Environment check.** Verifies codex CLI, claude CLI, gh CLI, and GitHub token. GitHub auth is required. Interactive setup can start without an installed AI tool and then guides tool selection, installation, and login; `--yes` requires an enabled ready tool.
|
|
400
413
|
|
|
401
414
|
**Step 2 — Deployment mode.** Choose how crosscheck scopes itself:
|
|
402
415
|
- `personal` — monitors your personal repos + all orgs you belong to; reviews only PRs you author
|
|
@@ -404,9 +417,9 @@ crosscheck onboard --reconfigure # re-run setup even if config already exists
|
|
|
404
417
|
|
|
405
418
|
**Step 3 — Repo selection.** Lists accessible repos and orgs; you pick which ones to watch. Org-level selection covers all repos in the org with one webhook.
|
|
406
419
|
|
|
407
|
-
**Step 4 — Review mode.**
|
|
408
|
-
- `cross-vendor` —
|
|
409
|
-
- `single-vendor` — one AI reviews all PRs (
|
|
420
|
+
**Step 4 — Review mode.** Uses the tools retained after guided setup:
|
|
421
|
+
- `cross-vendor` — enabled tools review PRs written by another tool (used for multiple ready selections)
|
|
422
|
+
- `single-vendor` — one AI reviews all PRs (used for one ready selection)
|
|
410
423
|
|
|
411
424
|
**Step 5 — Primary author.** In personal cross-vendor mode, choose which agent usually authors your PRs so Crosscheck can route reviews to the other vendor.
|
|
412
425
|
|
|
@@ -794,6 +807,7 @@ crosscheck status
|
|
|
794
807
|
Auth
|
|
795
808
|
✓ codex authenticated
|
|
796
809
|
✓ claude 2.1.x (Claude Code)
|
|
810
|
+
✓ opencode v2.x
|
|
797
811
|
✓ GITHUB_TOKEN via gh auth login
|
|
798
812
|
✓ WEBHOOK_SECRET auto-managed at ~/.crosscheck/webhook-secret
|
|
799
813
|
|
|
@@ -804,6 +818,8 @@ crosscheck status
|
|
|
804
818
|
enabled skills code-review-skill (by @awesome-skills, MIT), diagnosing-bugs (by @mattpocock, MIT)
|
|
805
819
|
codex auth subscription
|
|
806
820
|
claude model sonnet
|
|
821
|
+
opencode enabled false
|
|
822
|
+
opencode model configured in opencode.json
|
|
807
823
|
per-review budget $2.00/review
|
|
808
824
|
|
|
809
825
|
Impact
|
|
@@ -817,6 +833,7 @@ crosscheck status
|
|
|
817
833
|
CLIs
|
|
818
834
|
codex codex-cli 0.128.0
|
|
819
835
|
claude 2.1.x (Claude Code)
|
|
836
|
+
opencode v2.x
|
|
820
837
|
```
|
|
821
838
|
|
|
822
839
|
| Flag | Description |
|
|
@@ -1192,8 +1209,8 @@ vendors:
|
|
|
1192
1209
|
codex:
|
|
1193
1210
|
enabled: true
|
|
1194
1211
|
auth: subscription # subscription | api-key
|
|
1195
|
-
model: gpt-
|
|
1196
|
-
effort: medium # low | medium | high | xhigh | max | ultra
|
|
1212
|
+
model: gpt-6.1-sol # pins the review model; unset = tier model (api-key) / CLI default (subscription)
|
|
1213
|
+
effort: medium # low | medium | high | xhigh | max | ultra
|
|
1197
1214
|
# timeout_sec: 1200 # max seconds per CLI call; unset = tier-based (300/600/1200)
|
|
1198
1215
|
|
|
1199
1216
|
claude:
|
|
@@ -1202,6 +1219,16 @@ vendors:
|
|
|
1202
1219
|
effort: medium # low | medium | high | max
|
|
1203
1220
|
# timeout_sec: 1200 # max seconds per CLI call; unset = tier-based (300/600/1200)
|
|
1204
1221
|
|
|
1222
|
+
# Opt-in third harness; disabled by default. Its model/provider come from
|
|
1223
|
+
# `opencode auth login` / opencode.json, not from a crosscheck catalog.
|
|
1224
|
+
# Reasoning effort rides the `--model provider/model#variant` suffix, so
|
|
1225
|
+
# `effort` applies only when `model` is also set.
|
|
1226
|
+
opencode:
|
|
1227
|
+
enabled: false
|
|
1228
|
+
# model: provider/model # e.g. alibaba-cn/deepseek-v4-pro; unset = opencode.json default
|
|
1229
|
+
effort: high # none | high | max
|
|
1230
|
+
# timeout_sec: 1200 # max seconds per CLI call; unset = tier-based (300/600/1200)
|
|
1231
|
+
|
|
1205
1232
|
# ── Quality ───────────────────────────────────────────────────────────────────
|
|
1206
1233
|
quality:
|
|
1207
1234
|
# Structured findings and incremental follow-ups; set false for legacy prose reviews.
|
|
@@ -1285,12 +1312,18 @@ routing:
|
|
|
1285
1312
|
claude_reviews_patterns:
|
|
1286
1313
|
- "Generated with \\[OpenAI Codex\\]" # Codex attribution footer
|
|
1287
1314
|
- "Co-Authored-By: codex" # commit trailer
|
|
1315
|
+
opencode_reviews_patterns:
|
|
1316
|
+
- "Generated with \\[OpenCode\\]" # OpenCode attribution footer
|
|
1317
|
+
- "Co-Authored-By: opencode" # commit trailer
|
|
1288
1318
|
|
|
1289
|
-
# Branch prefix detection (signal 3). Claude Code uses claude/, Codex uses
|
|
1319
|
+
# Branch prefix detection (signal 3). Claude Code uses claude/, Codex uses
|
|
1320
|
+
# codex/, OpenCode uses opencode/.
|
|
1290
1321
|
claude_branch_prefixes:
|
|
1291
1322
|
- "claude/"
|
|
1292
1323
|
codex_branch_prefixes:
|
|
1293
1324
|
- "codex/"
|
|
1325
|
+
opencode_branch_prefixes:
|
|
1326
|
+
- "opencode/"
|
|
1294
1327
|
|
|
1295
1328
|
# Restrict reviews to PRs opened by these GitHub logins.
|
|
1296
1329
|
# Auto-filled with your GitHub login by `crosscheck init` or first `crosscheck watch`.
|
|
@@ -1304,6 +1337,11 @@ routing:
|
|
|
1304
1337
|
author_routes:
|
|
1305
1338
|
your-github-login: claude # your PRs → treated as Claude-authored → Codex reviews
|
|
1306
1339
|
|
|
1340
|
+
# Reviewer for PRs crosscheck cannot attribute (origin: human). 'auto' = the
|
|
1341
|
+
# first authenticated vendor (codex, then claude, then opencode); an explicit
|
|
1342
|
+
# 'codex' | 'claude' | 'opencode' always uses that vendor; null = skip the PR.
|
|
1343
|
+
fallback_reviewer: auto
|
|
1344
|
+
|
|
1307
1345
|
# ── Tunnel (watch mode only) ──────────────────────────────────────────────────
|
|
1308
1346
|
# localhost.run (default) — SSH tunnel, zero install, URL changes on reconnect.
|
|
1309
1347
|
# smee — stable relay via smee.io; events queued while offline.
|
package/get-started.zh.md
CHANGED
|
@@ -354,6 +354,10 @@ crosscheck init --config /path/to/crosscheck.config.yml
|
|
|
354
354
|
|
|
355
355
|
### `crosscheck onboard`
|
|
356
356
|
|
|
357
|
+
交互式初始化先列出 Claude、Codex、OpenCode,标明可用、待登录或未安装;可以先勾选一个或多个想用的工具,再逐个安装、登录。向导显示将运行的命令,只有明确选择执行时才运行;也可以在另一终端处理后重新检测、跳过该工具或取消。安装和登录完成后会重新检测,保存前再次校验;跳过或尚未准备好的工具不会启用。最终保留一个工具时使用单工具模式,保留多个时使用多工具模式。已有选择会预先勾选;首次混合安装默认不勾选 OpenCode。
|
|
358
|
+
|
|
359
|
+
使用 `--yes` 时不安装、不登录、不弹出工具选择,保留已有禁用设置并过滤不可用工具;没有已启用且可用的工具就停止保存。首次配置且只有 OpenCode 可用时会启用它。检测覆盖安装和已有登录/供应商配置,不代表已实际调用每个模型或验证供应商账户权限。
|
|
360
|
+
|
|
357
361
|
推荐的首次配置命令,交互式引导完成七个步骤并写入可用配置。
|
|
358
362
|
|
|
359
363
|
```bash
|