@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.
Files changed (105) hide show
  1. package/README.md +3 -1
  2. package/crosscheck.config.example.yml +20 -4
  3. package/dist/__tests__/detector.test.js +22 -0
  4. package/dist/__tests__/detector.test.js.map +1 -1
  5. package/dist/__tests__/onboard-preservation.test.js +27 -5
  6. package/dist/__tests__/onboard-preservation.test.js.map +1 -1
  7. package/dist/__tests__/onboard-vendors.test.d.ts +2 -0
  8. package/dist/__tests__/onboard-vendors.test.d.ts.map +1 -0
  9. package/dist/__tests__/onboard-vendors.test.js +122 -0
  10. package/dist/__tests__/onboard-vendors.test.js.map +1 -0
  11. package/dist/__tests__/optimize.test.js +18 -2
  12. package/dist/__tests__/optimize.test.js.map +1 -1
  13. package/dist/__tests__/review-models.test.js +5 -5
  14. package/dist/__tests__/review-models.test.js.map +1 -1
  15. package/dist/__tests__/review-strategy.test.js +10 -10
  16. package/dist/__tests__/review-strategy.test.js.map +1 -1
  17. package/dist/__tests__/reviewer-setup.test.d.ts +2 -0
  18. package/dist/__tests__/reviewer-setup.test.d.ts.map +1 -0
  19. package/dist/__tests__/reviewer-setup.test.js +134 -0
  20. package/dist/__tests__/reviewer-setup.test.js.map +1 -0
  21. package/dist/__tests__/runner.test.js +12 -2
  22. package/dist/__tests__/runner.test.js.map +1 -1
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +14 -1
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/commands/issue.d.ts.map +1 -1
  27. package/dist/commands/issue.js +40 -16
  28. package/dist/commands/issue.js.map +1 -1
  29. package/dist/commands/onboard.d.ts +8 -4
  30. package/dist/commands/onboard.d.ts.map +1 -1
  31. package/dist/commands/onboard.js +144 -88
  32. package/dist/commands/onboard.js.map +1 -1
  33. package/dist/commands/optimize.d.ts +1 -1
  34. package/dist/commands/optimize.d.ts.map +1 -1
  35. package/dist/commands/optimize.js +53 -13
  36. package/dist/commands/optimize.js.map +1 -1
  37. package/dist/commands/review.d.ts.map +1 -1
  38. package/dist/commands/review.js +9 -3
  39. package/dist/commands/review.js.map +1 -1
  40. package/dist/commands/run.d.ts +2 -2
  41. package/dist/commands/run.d.ts.map +1 -1
  42. package/dist/commands/run.js +1 -1
  43. package/dist/commands/run.js.map +1 -1
  44. package/dist/commands/status.d.ts.map +1 -1
  45. package/dist/commands/status.js +12 -1
  46. package/dist/commands/status.js.map +1 -1
  47. package/dist/commands/watch.d.ts.map +1 -1
  48. package/dist/commands/watch.js +6 -3
  49. package/dist/commands/watch.js.map +1 -1
  50. package/dist/config/review-model-tiers.json +9 -9
  51. package/dist/config/review-strategy.json +12 -12
  52. package/dist/config/schema.d.ts +102 -16
  53. package/dist/config/schema.d.ts.map +1 -1
  54. package/dist/config/schema.js +28 -3
  55. package/dist/config/schema.js.map +1 -1
  56. package/dist/github/client.js +1 -1
  57. package/dist/github/client.js.map +1 -1
  58. package/dist/github/detector.d.ts +2 -2
  59. package/dist/github/detector.d.ts.map +1 -1
  60. package/dist/github/detector.js +46 -11
  61. package/dist/github/detector.js.map +1 -1
  62. package/dist/lib/comment-bodies.d.ts.map +1 -1
  63. package/dist/lib/comment-bodies.js +4 -2
  64. package/dist/lib/comment-bodies.js.map +1 -1
  65. package/dist/lib/review-models.d.ts +4 -1
  66. package/dist/lib/review-models.d.ts.map +1 -1
  67. package/dist/lib/review-models.js +8 -0
  68. package/dist/lib/review-models.js.map +1 -1
  69. package/dist/lib/reviewer-error.d.ts.map +1 -1
  70. package/dist/lib/reviewer-error.js +12 -8
  71. package/dist/lib/reviewer-error.js.map +1 -1
  72. package/dist/lib/reviewer-setup.d.ts +25 -0
  73. package/dist/lib/reviewer-setup.d.ts.map +1 -0
  74. package/dist/lib/reviewer-setup.js +126 -0
  75. package/dist/lib/reviewer-setup.js.map +1 -0
  76. package/dist/lib/runner.d.ts +8 -7
  77. package/dist/lib/runner.d.ts.map +1 -1
  78. package/dist/lib/runner.js +71 -20
  79. package/dist/lib/runner.js.map +1 -1
  80. package/dist/lib/smart-switch.d.ts +7 -6
  81. package/dist/lib/smart-switch.d.ts.map +1 -1
  82. package/dist/lib/smart-switch.js +9 -2
  83. package/dist/lib/smart-switch.js.map +1 -1
  84. package/dist/lib/vendor.d.ts +2 -2
  85. package/dist/lib/vendor.d.ts.map +1 -1
  86. package/dist/lib/vendor.js +12 -2
  87. package/dist/lib/vendor.js.map +1 -1
  88. package/dist/lib/workflow.d.ts +8 -8
  89. package/dist/lib/workflow.js +1 -1
  90. package/dist/lib/workflow.js.map +1 -1
  91. package/dist/reviewers/fix.d.ts +6 -0
  92. package/dist/reviewers/fix.d.ts.map +1 -1
  93. package/dist/reviewers/fix.js +61 -0
  94. package/dist/reviewers/fix.js.map +1 -1
  95. package/dist/reviewers/opencode-env.d.ts +13 -0
  96. package/dist/reviewers/opencode-env.d.ts.map +1 -0
  97. package/dist/reviewers/opencode-env.js +52 -0
  98. package/dist/reviewers/opencode-env.js.map +1 -0
  99. package/dist/reviewers/opencode.d.ts +17 -0
  100. package/dist/reviewers/opencode.d.ts.map +1 -0
  101. package/dist/reviewers/opencode.js +293 -0
  102. package/dist/reviewers/opencode.js.map +1 -0
  103. package/get-started.md +46 -8
  104. package/get-started.zh.md +4 -0
  105. 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. At least one AI CLI must be authenticated; gh auth is always required. Prints ✓/✗ with fix hints.
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.** If both CLIs are available, choose:
408
- - `cross-vendor` — Claude reviews Codex PRs; Codex reviews Claude PRs (recommended when using both agents)
409
- - `single-vendor` — one AI reviews all PRs (default when only one CLI is installed)
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-5.6-terra # pins the review model; unset = tier model (api-key) / CLI default (subscription)
1196
- effort: medium # low | medium | high | xhigh | max | ultra (ultra: terra/sol only)
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 codex/.
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanbased/crosscheck",
3
- "version": "1.6.1-beta.5",
3
+ "version": "1.7.0-beta.25",
4
4
  "description": "AI code review pipeline that turns agent-written PRs into merge-ready patches",
5
5
  "bin": {
6
6
  "crosscheck": "dist/cli.js",