peaks-loop 4.0.15 → 4.0.17

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 (90) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README-en.md +12 -9
  3. package/README.md +15 -4
  4. package/config/eslint/.peaks-rules.cjs +47 -27
  5. package/dist/cli/commands/_register.js +4 -2
  6. package/dist/cli/commands/audit-commands.d.ts +1 -1
  7. package/dist/cli/commands/code-gate-command.d.ts +25 -0
  8. package/dist/cli/commands/code-gate-command.js +86 -0
  9. package/dist/cli/commands/code-mode-gate-commands.js +3 -4
  10. package/dist/cli/commands/code-orchestrator-can-do.js +2 -1
  11. package/dist/cli/commands/code-review-commands.d.ts +2 -30
  12. package/dist/cli/commands/code-review-commands.js +43 -65
  13. package/dist/cli/commands/cron-commands.js +16 -4
  14. package/dist/cli/commands/dashboard-long-run.js +8 -4
  15. package/dist/cli/commands/dispatch-from-dag.d.ts +1 -1
  16. package/dist/cli/commands/hooks-commands.js +56 -3
  17. package/dist/cli/commands/lint-commands.d.ts +21 -0
  18. package/dist/cli/commands/lint-commands.js +146 -0
  19. package/dist/cli/commands/qa-commands.js +1 -2
  20. package/dist/cli/commands/security-audit-commands.d.ts +1 -1
  21. package/dist/cli/commands/security-audit-commands.js +1 -1
  22. package/dist/cli/commands/sediment-commands.d.ts +1 -1
  23. package/dist/cli/commands/slice-commands.js +20 -8
  24. package/dist/cli/commands/statusline-commands.d.ts +1 -0
  25. package/dist/cli/commands/statusline-commands.js +17 -3
  26. package/dist/cli/commands/tech-commands.d.ts +1 -1
  27. package/dist/services/artifacts/request-artifact-service.d.ts +2 -2
  28. package/dist/services/artifacts/request-artifact-service.js +10 -1
  29. package/dist/services/audit/enforcers/lint-reference-shape.js +18 -4
  30. package/dist/services/code/auto-compact-orchestrator.js +9 -1
  31. package/dist/services/code/batch-heartbeat-poller.js +26 -0
  32. package/dist/services/code/orchestrator-can-do.d.ts +24 -0
  33. package/dist/services/code/orchestrator-can-do.js +48 -2
  34. package/dist/services/code/post-compact-detector.d.ts +1 -1
  35. package/dist/services/code-review/ecc-bridge.d.ts +2 -7
  36. package/dist/services/compact-statusline/compact-statusline-service.js +19 -11
  37. package/dist/services/config/config-service.d.ts +2 -19
  38. package/dist/services/config/config-service.js +2 -56
  39. package/dist/services/config/config-types.d.ts +4 -24
  40. package/dist/services/config/config-types.js +1 -10
  41. package/dist/services/container/container-lease.js +21 -8
  42. package/dist/services/context/spillover-store.d.ts +1 -1
  43. package/dist/services/crystallization/crystallization-types.js +20 -8
  44. package/dist/services/crystallization/evidence-brief-builder.js +22 -11
  45. package/dist/services/dispatch/dispatch-record-writer.js +231 -141
  46. package/dist/services/evolution/evolution-types.js +38 -21
  47. package/dist/services/feedback/feedback-promotion-service.js +9 -1
  48. package/dist/services/hooks/pre-tool-code-gate.d.ts +46 -0
  49. package/dist/services/hooks/pre-tool-code-gate.js +91 -0
  50. package/dist/services/ide/current-model-detector.js +1 -2
  51. package/dist/services/lint/detect-eslint.d.ts +10 -0
  52. package/dist/services/lint/detect-eslint.js +62 -0
  53. package/dist/services/lint/detect-ocr-18.d.ts +10 -0
  54. package/dist/services/lint/detect-ocr-18.js +40 -0
  55. package/dist/services/lint/eslint-runner.d.ts +57 -0
  56. package/dist/services/lint/eslint-runner.js +370 -0
  57. package/dist/services/lint/npx-resolver.d.ts +6 -0
  58. package/dist/services/lint/npx-resolver.js +47 -0
  59. package/dist/services/lint/ocr-multilang-adapter.d.ts +34 -0
  60. package/dist/services/lint/ocr-multilang-adapter.js +143 -0
  61. package/dist/services/loop/loop-release-types.js +32 -13
  62. package/dist/services/loop/spec-service.d.ts +7 -1
  63. package/dist/services/loop/spec-service.js +218 -182
  64. package/dist/services/rd/rd-service.js +22 -9
  65. package/dist/services/scan/archetype-service.js +21 -9
  66. package/dist/services/session/binding-store.js +1 -2
  67. package/dist/services/share/bundle-reader.d.ts +6 -9
  68. package/dist/services/share/bundle-reader.js +297 -223
  69. package/dist/services/skills/hooks-settings-service.d.ts +33 -0
  70. package/dist/services/skills/hooks-settings-service.js +27 -1
  71. package/dist/services/slice/calibration-store.js +21 -8
  72. package/dist/services/slice/slice-check-service.js +35 -14
  73. package/dist/services/slice/slice-decompose-service.js +459 -223
  74. package/dist/services/standards/project-context.js +135 -73
  75. package/dist/services/verdict/envelopes.d.ts +1 -2
  76. package/dist/services/verdict/envelopes.js +1 -2
  77. package/dist/services/verdict/verdict-aggregator.d.ts +1 -2
  78. package/dist/services/vm/vm-lease.js +19 -8
  79. package/dist/services/worktree/worktree-lease.d.ts +0 -10
  80. package/dist/services/worktree/worktree-lease.js +19 -8
  81. package/dist/shared/fs-utils.d.ts +1 -1
  82. package/package.json +8 -12
  83. package/skills/bee/peaks-rd/SKILL.md +1 -1
  84. package/skills/bee/peaks-rd/references/jsts-eslint-gate.md +197 -0
  85. package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +132 -0
  86. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +1 -1
  87. package/skills/peaks-code/SKILL.md +47 -1
  88. package/dist/services/code-review/ocr-service.d.ts +0 -129
  89. package/dist/services/code-review/ocr-service.js +0 -372
  90. package/skills/bee/peaks-rd/references/ocr-integration.md +0 -229
@@ -1,372 +0,0 @@
1
- /**
2
- * Open Code Review (ocr) integration — soft-optional augmentation
3
- * for peaks-rd's Gate B3 (code review evidence).
4
- *
5
- * Per the "skill-first / CLI-auxiliary" tenet, peaks-rd SKILL.md
6
- * is the primary surface; this CLI primitive returns a structured
7
- * JSON envelope the skill consumes to produce a second-opinion
8
- * code review alongside its own LLM review.
9
- *
10
- * Mirrors the ECC 64-agents soft-optional pattern
11
- * (`src/services/agent/ecc-agent-service.ts`):
12
- * - same subprocessRunner injection seam (testability),
13
- * - same multi-state reason enum (clear failure-mode reporting),
14
- * - same soft-fail policy (never blocks the umbrella).
15
- *
16
- * === Source of truth: peaks-loop's own config.json ===
17
- *
18
- * peaks-loop does NOT auto-configure the LLM endpoint, and it does
19
- * NOT write `~/.opencodereview/config.json`. The user is the only
20
- * party that touches their LLM token / URL / model. The single
21
- * discoverable place they declare those values is
22
- * `peaksConfig.ocr.llm` under their `~/.peaks/config.json` (or
23
- * `.peaks/config.json` in a project root).
24
- *
25
- * peaksConfig.ocr.llm.url → OCR_LLM_URL
26
- * peaksConfig.ocr.llm.authToken → OCR_LLM_TOKEN
27
- * peaksConfig.ocr.llm.model → OCR_LLM_MODEL
28
- * peaksConfig.ocr.llm.useAnthropic → OCR_USE_ANTHROPIC
29
- * peaksConfig.ocr.llm.authHeader → OCR_LLM_AUTH_HEADER
30
- *
31
- * When `runOcrReview` spawns the ocr subprocess it injects those
32
- * values as env vars. The ocr package treats env vars as the
33
- * highest-priority config source, so peaks-loop never has to
34
- * materialise `~/.opencodereview/config.json` itself.
35
- *
36
- * To see the JSON template to paste, run:
37
- * `peaks code-review config-template`
38
- *
39
- * The ocr package is declared in package.json:peerDependencies
40
- * (was promoted to `dependencies` in 2.0.1, demoted to
41
- * `optionalDependencies` in 2.0.3, then to `peerDependencies` in
42
- * 2.8.2 — the ocr postinstall downloads a Go binary via HTTPS,
43
- * which fails in restricted/proxied environments and would
44
- * otherwise slow or abort the whole `npm i -g peaks-loop` flow).
45
- * Peaks-cli ships with ocr *not* installed; if the user wants it,
46
- * they run
47
- * `npm i -g @alibaba-group/open-code-review`
48
- * and peaks-loop's 5-state detector (below) reports whether the
49
- * binary is actually usable. pnpm-based installs additionally need
50
- * `pnpm approve-builds @alibaba-group/open-code-review` for the
51
- * binary download to run. Either way, peaks-loop never blocks on it.
52
- */
53
- import { spawnSync } from 'node:child_process';
54
- import { existsSync } from 'node:fs';
55
- import { join } from 'node:path';
56
- import { fileURLToPath } from 'node:url';
57
- import { dirname } from 'node:path';
58
- const OCR_DETECT_TIMEOUT_MS = 5000;
59
- const OCR_REVIEW_TIMEOUT_MS = 180000;
60
- const OCR_INSTALL_HINT = 'Install: `npm i -g @alibaba-group/open-code-review` (peaks-loop 2.8.2 ships with ocr as a peer dependency — its postinstall downloads a Go binary via HTTPS, which fails in some restricted/proxied environments; that\'s why peaks-loop does not auto-install it). Then add your LLM endpoint to ~/.peaks/config.json — run `peaks code-review config-template` for the JSON snippet to paste. Under pnpm you also need `pnpm approve-builds @alibaba-group/open-code-review` to allow the binary download.';
61
- const OCR_CONFIG_TEMPLATE = JSON.stringify({
62
- ocr: {
63
- llm: {
64
- url: 'https://api.example.com/v1/messages',
65
- authToken: '<your-api-key>',
66
- model: 'claude-3-5-sonnet-latest',
67
- useAnthropic: true,
68
- authHeader: 'x-api-key'
69
- }
70
- }
71
- }, null, 2);
72
- const DEFAULT_RUNNER = {
73
- run(command, args, options) {
74
- try {
75
- const r = spawnSync(command, args, {
76
- encoding: 'utf8',
77
- stdio: ['ignore', 'pipe', 'pipe'],
78
- timeout: options.timeoutMs,
79
- cwd: options.cwd,
80
- env: options.env,
81
- });
82
- return {
83
- status: r.status,
84
- stdout: r.stdout ?? '',
85
- stderr: r.stderr ?? '',
86
- };
87
- }
88
- catch (err) {
89
- return {
90
- status: null,
91
- stdout: '',
92
- stderr: '',
93
- error: err instanceof Error ? err.message : String(err),
94
- };
95
- }
96
- },
97
- };
98
- /**
99
- * Locate the ocr launcher script (`bin/ocr.js`) inside our own
100
- * node_modules tree. Returns null when the npm package is not
101
- * present (peaks-loop was installed but its dependency tree is
102
- * corrupt, or the user removed it).
103
- *
104
- * Walks up from this file (dist/services/code-review/) to
105
- * find the project root, then checks node_modules.
106
- */
107
- export function resolveOcrLauncher(searchRoots) {
108
- const candidates = [];
109
- for (const root of searchRoots) {
110
- candidates.push(join(root, 'node_modules', '@alibaba-group', 'open-code-review', 'bin', 'ocr.js'));
111
- }
112
- for (const candidate of candidates) {
113
- if (existsSync(candidate)) {
114
- return candidate;
115
- }
116
- }
117
- return null;
118
- }
119
- /**
120
- * Resolve search roots for the ocr launcher. We look in two
121
- * places: (1) the peaks-loop install root (next to our own dist/),
122
- * (2) the user's cwd.
123
- */
124
- export function defaultOcrSearchRoots(currentDirPath, cwd) {
125
- // currentDirPath is dist/services/code-review/ under tsc rootDir=src;
126
- // walk up 3 to repo root. If tsconfig.build.json#rootDir ever reverts
127
- // to "." (emits dist/src/services/code-review/), this must become
128
- // walk-4 — keep these two in lockstep.
129
- const peaksRoot = join(currentDirPath, '..', '..', '..');
130
- return [peaksRoot, cwd];
131
- }
132
- /**
133
- * Validate the `peaksConfig.ocr.llm` block the caller (CLI / test)
134
- * read out of `~/.peaks/config.json`. Returns the list of missing
135
- * required keys (`url`, `authToken`, `model`); empty array means
136
- * the block is ready to drive the ocr subprocess.
137
- *
138
- * The block is independent of the OCR package's own
139
- * `~/.opencodereview/config.json` file. peaks-loop only ever reads
140
- * from its own config; the env-var injection in `runOcrReview`
141
- * makes the legacy file irrelevant.
142
- */
143
- export function getOcrLlmMissingFields(llm) {
144
- if (llm === null) {
145
- return ['ocr.llm.url', 'ocr.llm.authToken', 'ocr.llm.model'];
146
- }
147
- const missing = [];
148
- if (typeof llm.url !== 'string' || llm.url.length === 0)
149
- missing.push('ocr.llm.url');
150
- if (typeof llm.authToken !== 'string' || llm.authToken.length === 0)
151
- missing.push('ocr.llm.authToken');
152
- if (typeof llm.model !== 'string' || llm.model.length === 0)
153
- missing.push('ocr.llm.model');
154
- return missing;
155
- }
156
- /**
157
- * Build the env-var overlay peaks-loop injects into the ocr
158
- * subprocess. Maps `peaksConfig.ocr.llm` onto the OCR package's
159
- * env-var surface (its highest-priority config source).
160
- */
161
- export function buildOcrEnv(llm) {
162
- const env = {};
163
- if (typeof llm.url === 'string' && llm.url.length > 0)
164
- env.OCR_LLM_URL = llm.url;
165
- if (typeof llm.authToken === 'string' && llm.authToken.length > 0)
166
- env.OCR_LLM_TOKEN = llm.authToken;
167
- if (typeof llm.model === 'string' && llm.model.length > 0)
168
- env.OCR_LLM_MODEL = llm.model;
169
- if (typeof llm.useAnthropic === 'boolean')
170
- env.OCR_USE_ANTHROPIC = String(llm.useAnthropic);
171
- if (typeof llm.authHeader === 'string' && llm.authHeader.length > 0)
172
- env.OCR_LLM_AUTH_HEADER = llm.authHeader;
173
- return env;
174
- }
175
- /**
176
- * The JSON template the user pastes into their peaks-loop config
177
- * (`peaksConfig.ocr.llm`). Returned as a stable string so the
178
- * `peaks code-review config-template` CLI command can print it
179
- * verbatim, and so the detector's `nextActions` payload embeds
180
- * the same shape the user is told to add.
181
- */
182
- export function getOcrConfigTemplate() {
183
- return OCR_CONFIG_TEMPLATE;
184
- }
185
- /**
186
- * Detect the full ocr install + config state. The 5 reason states
187
- * are unchanged from the soft-optional 2.0.0 contract; only the
188
- * source of `config-missing` moved from `~/.opencodereview/config.json`
189
- * to `peaksConfig.ocr.llm`.
190
- */
191
- export function detectOcr(options) {
192
- const currentDirPath = dirname(fileURLToPath(import.meta.url));
193
- const roots = options.searchRoots ?? defaultOcrSearchRoots(currentDirPath, options.cwd);
194
- const launcher = resolveOcrLauncher(roots);
195
- const warnings = [];
196
- const nextActions = [];
197
- if (launcher === null) {
198
- const missing = getOcrLlmMissingFields(options.peaksOcrConfig);
199
- return {
200
- state: 'package-missing',
201
- packageInstalled: false,
202
- binaryPath: null,
203
- version: null,
204
- configPath: options.peaksConfigPath,
205
- configValid: missing.length === 0,
206
- missingKeys: missing,
207
- warnings,
208
- nextActions: [
209
- '@alibaba-group/open-code-review is not installed in this project or peaks-loop root.',
210
- OCR_INSTALL_HINT,
211
- ],
212
- };
213
- }
214
- // Check whether the platform-specific binary downloaded
215
- // successfully. The launcher's own check is identical:
216
- // bin/opencodereview(.exe) next to bin/ocr.js.
217
- const isWindows = process.platform === 'win32';
218
- const binaryName = isWindows ? 'opencodereview.exe' : 'opencodereview';
219
- const binaryPath = join(dirname(launcher), binaryName);
220
- if (!existsSync(binaryPath)) {
221
- const missing = getOcrLlmMissingFields(options.peaksOcrConfig);
222
- return {
223
- state: 'binary-missing',
224
- packageInstalled: true,
225
- binaryPath: null,
226
- version: null,
227
- configPath: options.peaksConfigPath,
228
- configValid: missing.length === 0,
229
- missingKeys: missing,
230
- warnings: [
231
- 'ocr npm package is installed but the platform binary failed to download (likely network or postinstall blocked).',
232
- ],
233
- nextActions: [
234
- 'For npm installs the binary downloads automatically; for pnpm run: `pnpm approve-builds @alibaba-group/open-code-review`.',
235
- 'Or run the installer directly: `node node_modules/@alibaba-group/open-code-review/scripts/install.js`.',
236
- 'Network-blocked installs can pre-download from https://github.com/alibaba/open-code-review/releases and place the binary at: ' + binaryPath,
237
- ],
238
- };
239
- }
240
- // Probe the binary for its version. We invoke through the
241
- // node launcher so the upstream's update-check + arg-parse
242
- // logic stays canonical.
243
- const runner = options.runner ?? DEFAULT_RUNNER;
244
- const probe = runner.run('node', [launcher, 'version'], { timeoutMs: OCR_DETECT_TIMEOUT_MS });
245
- let version = null;
246
- if (probe.status === 0 && probe.stdout.length > 0) {
247
- const match = /(\d+\.\d+\.\d+)/.exec(probe.stdout);
248
- version = match !== null ? match[1] ?? null : probe.stdout.trim().slice(0, 32);
249
- }
250
- const missing = getOcrLlmMissingFields(options.peaksOcrConfig);
251
- if (missing.length > 0) {
252
- return {
253
- state: 'config-missing',
254
- packageInstalled: true,
255
- binaryPath,
256
- version,
257
- configPath: options.peaksConfigPath,
258
- configValid: false,
259
- missingKeys: missing,
260
- warnings: [
261
- `ocr is installed but peaks-loop's ocr.llm config is incomplete: missing ${missing.join(', ')}.`,
262
- ],
263
- nextActions: [
264
- `Paste the following into ${options.peaksConfigPath} under "ocr.llm":`,
265
- OCR_CONFIG_TEMPLATE,
266
- 'Or run `peaks code-review config-template` to print the snippet again.',
267
- 'Until configured, peaks-rd skips the ocr second-opinion step and proceeds with its own LLM review only.',
268
- ],
269
- };
270
- }
271
- return {
272
- state: 'ready',
273
- packageInstalled: true,
274
- binaryPath,
275
- version,
276
- configPath: options.peaksConfigPath,
277
- configValid: true,
278
- missingKeys: [],
279
- warnings,
280
- nextActions,
281
- };
282
- }
283
- /**
284
- * Run ocr review and return the structured result. Detects state
285
- * first; soft-fails when ocr isn't ready (the caller — typically
286
- * peaks-rd — proceeds without the second-opinion review).
287
- *
288
- * The LLM endpoint config from `peaksConfig.ocr.llm` is injected
289
- * as env vars (`OCR_LLM_URL` / `OCR_LLM_TOKEN` / ...), which the
290
- * ocr package treats as the highest-priority config source. This
291
- * is how peaks-loop wires the user-managed config into the ocr
292
- * subprocess without ever writing `~/.opencodereview/config.json`.
293
- */
294
- export function runOcrReview(options) {
295
- const detect = detectOcr(options);
296
- if (detect.state !== 'ready') {
297
- return {
298
- spawned: false,
299
- state: detect.state,
300
- exitCode: null,
301
- stdout: '',
302
- stderr: '',
303
- durationMs: 0,
304
- parsed: null,
305
- warnings: detect.warnings,
306
- nextActions: detect.nextActions,
307
- };
308
- }
309
- // Resolve the launcher path again (we know it exists because detect.state === 'ready')
310
- const currentDirPath = dirname(fileURLToPath(import.meta.url));
311
- const roots = options.searchRoots ?? defaultOcrSearchRoots(currentDirPath, options.cwd);
312
- const launcher = resolveOcrLauncher(roots);
313
- if (launcher === null) {
314
- // Should never happen given state === 'ready', but stay safe.
315
- return {
316
- spawned: false,
317
- state: 'detection-failed',
318
- exitCode: null,
319
- stdout: '',
320
- stderr: 'ocr launcher disappeared between detect and run',
321
- durationMs: 0,
322
- parsed: null,
323
- warnings: ['ocr was detected as ready but the launcher path is no longer resolvable.'],
324
- nextActions: ['Re-run `peaks code-review detect-ocr --json` to refresh.'],
325
- };
326
- }
327
- // Inject the LLM endpoint config from peaks-loop's config.json
328
- // as env vars — the ocr package's highest-priority config path.
329
- const env = options.peaksOcrConfig === null
330
- ? process.env
331
- : { ...process.env, ...buildOcrEnv(options.peaksOcrConfig) };
332
- const args = ['review', '--format', 'json'];
333
- if (typeof options.input.from === 'string' && options.input.from.length > 0) {
334
- args.push('--from', options.input.from);
335
- }
336
- if (typeof options.input.to === 'string' && options.input.to.length > 0) {
337
- args.push('--to', options.input.to);
338
- }
339
- if (typeof options.input.commit === 'string' && options.input.commit.length > 0) {
340
- args.push('--commit', options.input.commit);
341
- }
342
- const runner = options.runner ?? DEFAULT_RUNNER;
343
- const start = Date.now();
344
- const r = runner.run('node', [launcher, ...args], {
345
- cwd: options.input.projectRoot,
346
- timeoutMs: OCR_REVIEW_TIMEOUT_MS,
347
- env,
348
- });
349
- const durationMs = Date.now() - start;
350
- let parsed = null;
351
- if (r.status === 0 && r.stdout.length > 0) {
352
- try {
353
- parsed = JSON.parse(r.stdout);
354
- }
355
- catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
356
- // Leave parsed=null; the caller can read raw stdout.
357
- }
358
- }
359
- return {
360
- spawned: true,
361
- state: 'ready',
362
- exitCode: r.status,
363
- stdout: r.stdout,
364
- stderr: r.stderr,
365
- durationMs,
366
- parsed,
367
- warnings: r.status === 0 ? [] : [`ocr review exited with status ${r.status}`],
368
- nextActions: r.status === 0
369
- ? []
370
- : ['Inspect stderr for the failure. Re-run `peaks code-review detect-ocr --json` to verify config still valid.'],
371
- };
372
- }
@@ -1,229 +0,0 @@
1
- # OCR (Open Code Review) integration
2
-
3
- > Soft-optional second-opinion code review for peaks-rd Gate B3.
4
- > Mirrors the ECC 64-agents pattern (spec §7.2): peaks-loop ships
5
- > `@alibaba-group/open-code-review` as an **`optionalDependency`**
6
- > (was promoted to `dependencies` in 2.0.1 and reverted in 2.0.3
7
- > because its postinstall downloads a Go binary via HTTPS and would
8
- > otherwise abort `npm i -g peaks-loop` in restricted/proxied
9
- > environments). The LLM endpoint config still lives under
10
- > `peaksConfig.ocr.llm` in the user's `~/.peaks/config.json` (single
11
- > source of truth, user-managed). When the user installs + configures
12
- > ocr, the wrapper turns its output into structured `code-review.md`
13
- > evidence; when missing, peaks-rd proceeds LLM-only and the slice
14
- > ships without the second opinion.
15
-
16
- ## What ocr is
17
-
18
- [Open Code Review](https://github.com/alibaba/open-code-review) is
19
- an AI-powered code review CLI from Alibaba. It reads git diffs,
20
- sends the changed files to a **user-configured LLM endpoint**
21
- (OpenAI- or Anthropic-compatible), and emits structured
22
- line-precise review comments. It is NOT a hosted service —
23
- all LLM traffic goes to the user's own configured endpoint.
24
-
25
- Distribution: npm `@alibaba-group/open-code-review` (Go binary
26
- inside; the npm postinstall downloads the platform-specific
27
- binary from GitHub Releases).
28
-
29
- ## Why peaks-rd uses it (soft-optional)
30
-
31
- The default peaks-rd code-review evidence is produced by the
32
- main RD LLM (or the `code-reviewer` sub-agent in the parallel
33
- fan-out). That's one pair of eyes. When ocr is available, the
34
- wrapper adds a **second pair** — an independent LLM tuned for
35
- code review — and the two reviews are merged into the same
36
- `code-review.md` file. Soft-optional: if ocr isn't installed or
37
- configured, RD proceeds with the LLM-only review and the slice
38
- ships without the second opinion.
39
-
40
- ## Install
41
-
42
- `@alibaba-group/open-code-review` is an **`optionalDependency`** of
43
- peaks-loop 2.0.3+ (was a required `dependency` in 2.0.1/2.0.2; reverted
44
- because the postinstall downloads a Go binary via HTTPS, which fails in
45
- restricted/proxied environments and would otherwise abort
46
- `npm i -g peaks-loop`). peaks-loop does NOT auto-install it. To enable
47
- the second-opinion review:
48
-
49
- ```bash
50
- npm i -g @alibaba-group/open-code-review
51
- ```
52
-
53
- (Under pnpm you also need `pnpm approve-builds @alibaba-group/open-code-review`
54
- so the binary download script can run.) Verify with:
55
-
56
- ```bash
57
- peaks code-review detect-ocr --json
58
- ```
59
-
60
- Five possible states:
61
-
62
- | state | Meaning | Recovery |
63
- |---|---|---|
64
- | `ready` | Installed + binary downloaded + peaks-loop's `peaksConfig.ocr.llm` valid | Nothing — `run-ocr` will work. |
65
- | `package-missing` | npm dep not installed (peaks-loop 2.0.3+ ships with ocr as an `optionalDependency`, so the common cause is the user has not installed it yet, or it was removed from node_modules) | `npm i -g @alibaba-group/open-code-review` (peaks-loop no longer auto-installs it; under pnpm also run `pnpm approve-builds @alibaba-group/open-code-review`) |
66
- | `binary-missing` | npm dep present but Go binary did not download | `pnpm approve-builds @alibaba-group/open-code-review`, OR run `node node_modules/@alibaba-group/open-code-review/scripts/install.js`, OR manually fetch from https://github.com/alibaba/open-code-review/releases and place the binary at the path shown in `nextActions[2]`. |
67
- | `config-missing` | binary present but `peaksConfig.ocr.llm` is empty or partial | See "Configure" below. |
68
- | `detection-failed` | Unexpected error during detection | Inspect stderr; re-run probe. |
69
-
70
- ## Configure (one-time, per user) — peaks-loop does NOT auto-configure
71
-
72
- The LLM endpoint config is **user-maintained inside peaks-loop's own
73
- config** at `~/.peaks/config.json` under the `ocr.llm` key. The user
74
- is the only party that touches their LLM token / URL / model. peaks-loop
75
- never auto-writes the config and never writes `~/.opencodereview/config.json`.
76
-
77
- ```bash
78
- # 1) Print the JSON snippet to paste (read-only, no side effects):
79
- peaks code-review config-template --json
80
-
81
- # 2) Paste the snippet into ~/.peaks/config.json under "ocr.llm",
82
- # replace <your-api-key> with your real key. Alternatively,
83
- # set keys one at a time:
84
- peaks config set --key ocr.llm.url --value 'https://api.example.com/v1/messages'
85
- peaks config set --key ocr.llm.authToken --value '<your-key>'
86
- peaks config set --key ocr.llm.model --value 'claude-3-5-sonnet-latest'
87
- peaks config set --key ocr.llm.useAnthropic --value 'true'
88
- peaks config set --key ocr.llm.authHeader --value 'x-api-key'
89
-
90
- # 3) Verify readiness (peaks-rd also runs this automatically):
91
- peaks code-review detect-ocr --json
92
- ```
93
-
94
- ### Field map: `peaksConfig.ocr.llm` ↔ ocr subprocess env vars
95
-
96
- peaks-rd calls ocr with the `peaksConfig.ocr.llm` values **injected as
97
- env vars** (ocr's highest-priority config path). The mapping is:
98
-
99
- | `peaksConfig.ocr.llm.*` | Spawn env var | Notes |
100
- |---|---|---|
101
- | `url` | `OCR_LLM_URL` | HTTPS endpoint, no embedded credentials |
102
- | `authToken` | `OCR_LLM_TOKEN` | Sensitive — stored only in the user-layer `~/.peaks/config.json`; `peaks config get` redacts this field |
103
- | `model` | `OCR_LLM_MODEL` | e.g. `claude-3-5-sonnet-latest` |
104
- | `useAnthropic` | `OCR_USE_ANTHROPIC` | Boolean; serialised as `"true"` / `"false"` |
105
- | `authHeader` | `OCR_LLM_AUTH_HEADER` | One of `authorization` (default Bearer), `x-api-key` (for `sk-ant-*` keys), or `bearer` |
106
-
107
- The `~/.opencodereview/config.json` file the user might have set up
108
- for 2.0.0 is no longer consulted by peaks-loop. The user may delete it
109
- at their discretion — the ocr subprocess ignores the file when peaks-
110
- cli's env vars are present (and the env-var surface is highest priority).
111
-
112
- ### Required vs optional fields
113
-
114
- The minimum for `state == "ready"` is the **url + authToken + model**
115
- triple. `useAnthropic` and `authHeader` are optional; `authHeader`
116
- defaults to `authorization` (Bearer) inside the ocr subprocess, but
117
- `sk-ant-*` keys require `authHeader: "x-api-key"`.
118
-
119
- When the user has not yet populated the config, `detect-ocr` returns
120
- `state: "config-missing"` with `missingKeys: ["ocr.llm.url",
121
- "ocr.llm.authToken", "ocr.llm.model"]` and a templated
122
- `nextActions[1]` payload that includes the JSON snippet to paste.
123
-
124
- ## Use from peaks-rd (LLM workflow)
125
-
126
- In Gate B3 (code review evidence), before writing
127
- `.peaks/_runtime/<sid>/rd/code-review.md`, the code-reviewer
128
- sub-agent runs:
129
-
130
- ```bash
131
- # 1. Detect
132
- peaks code-review detect-ocr --json
133
- # 2. If state == "ready", run the review
134
- peaks code-review run-ocr --json --project . --from origin/main --to HEAD
135
- ```
136
-
137
- The `run-ocr` envelope is:
138
-
139
- ```jsonc
140
- {
141
- "ok": true,
142
- "command": "code-review.run-ocr",
143
- "data": {
144
- "spawned": true,
145
- "state": "ready",
146
- "exitCode": 0,
147
- "stdout": "...",
148
- "stderr": "",
149
- "durationMs": 12345,
150
- "parsed": {
151
- "findings": [
152
- { "file": "src/foo.ts", "line": 42, "severity": "minor", "message": "..." }
153
- ]
154
- },
155
- "warnings": [],
156
- "nextActions": []
157
- },
158
- ...
159
- }
160
- ```
161
-
162
- Merge `data.parsed.findings` into `code-review.md` under
163
- `## Second opinion (ocr)`. Cite each finding by file + line.
164
- Reconcile disagreements with the LLM-only review explicitly
165
- (don't silently drop one source).
166
-
167
- ## Soft-fail policy
168
-
169
- `peaks code-review run-ocr` **never** sets a non-zero exit code,
170
- even when ocr is not ready or the subprocess fails. The envelope
171
- `ok` field carries the success signal; the caller (peaks-rd) is
172
- expected to pattern-match on `data.state` and proceed without the
173
- second opinion if needed. This matches the ECC 64-agents
174
- soft-fail policy and the peaks-loop "minimal user operation"
175
- tenet — missing ocr should never block a slice.
176
-
177
- ## Security
178
-
179
- - ocr sends your changed files to whatever LLM endpoint you
180
- configure. Treat this the same as any external code-review
181
- tool you opt into: don't point it at a free public endpoint
182
- for proprietary code; use a vendor / self-hosted endpoint with
183
- appropriate data controls.
184
- - peaks-loop does NOT auto-configure ocr. Your `ocr.llm.authToken`
185
- is yours. Rotate as needed. The token is stored only in the
186
- user-layer `~/.peaks/config.json` (project layer rejects writes
187
- to any key matching `isSensitiveConfigPath`), and
188
- `peaks config get` redacts it as `***`.
189
- - peaks-loop's wrapper records ocr's `stdout` verbatim in the
190
- envelope (and in `code-review.md` when peaks-rd merges
191
- findings). Don't put secrets in your code being reviewed.
192
- - The `peaks code-review config-template` snippet embeds the
193
- placeholder string `<your-api-key>`; the user is expected to
194
- replace it before pasting.
195
-
196
- ## Failure modes (real)
197
-
198
- These are the actual failure modes the wrapper has been
199
- dogfooded against:
200
-
201
- 1. **Network blocked from GitHub Releases** during postinstall →
202
- `binary-missing`. peaks-loop still runs cleanly because ocr
203
- is detected as not-ready; user manually fetches the binary
204
- and places it at `nextActions[2]`'s path.
205
- 2. **pnpm-installed peaks-loop** → ocr postinstall blocked by
206
- pnpm's safe-by-default policy → `binary-missing`. Recover
207
- with `pnpm approve-builds @alibaba-group/open-code-review`.
208
- 3. **No / partial LLM config** → `config-missing` with
209
- `missingKeys` listing the unpopulated fields. Recover by
210
- pasting the `peaks code-review config-template` output into
211
- `~/.peaks/config.json` (or by `peaks config set` per-key).
212
- 4. **Wrong key / wrong endpoint** → ocr subprocess exits non-zero;
213
- wrapper soft-fails (`ok: false`, `warnings[0]` includes the
214
- exit code, `stderr` carries ocr's own error message).
215
- 5. **User 2.0.0 → 2.0.1 migration** — they configured
216
- `~/.opencodereview/config.json` for 2.0.0; peaks-loop 2.0.1
217
- no longer reads that file. They paste the same values into
218
- `~/.peaks/config.json` under `ocr.llm` (peaks-loop handles the
219
- camelCase conversion in the template).
220
-
221
- ## See also
222
-
223
- - ocr upstream: https://github.com/alibaba/open-code-review
224
- - peaks-loop source: `src/services/code-review/ocr-service.ts`,
225
- `src/cli/commands/code-review-commands.ts`
226
- - peaks-loop config schema: `src/services/config/config-types.ts`
227
- (`OcrLlmConfig`, `OcrConfig`, `PeaksConfig.ocr?`)
228
- - ECC 64-agents soft-optional pattern (mirrored):
229
- `src/services/agent/ecc-agent-service.ts`