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.
- package/CHANGELOG.md +82 -0
- package/README-en.md +12 -9
- package/README.md +15 -4
- package/config/eslint/.peaks-rules.cjs +47 -27
- package/dist/cli/commands/_register.js +4 -2
- package/dist/cli/commands/audit-commands.d.ts +1 -1
- package/dist/cli/commands/code-gate-command.d.ts +25 -0
- package/dist/cli/commands/code-gate-command.js +86 -0
- package/dist/cli/commands/code-mode-gate-commands.js +3 -4
- package/dist/cli/commands/code-orchestrator-can-do.js +2 -1
- package/dist/cli/commands/code-review-commands.d.ts +2 -30
- package/dist/cli/commands/code-review-commands.js +43 -65
- package/dist/cli/commands/cron-commands.js +16 -4
- package/dist/cli/commands/dashboard-long-run.js +8 -4
- package/dist/cli/commands/dispatch-from-dag.d.ts +1 -1
- package/dist/cli/commands/hooks-commands.js +56 -3
- package/dist/cli/commands/lint-commands.d.ts +21 -0
- package/dist/cli/commands/lint-commands.js +146 -0
- package/dist/cli/commands/qa-commands.js +1 -2
- package/dist/cli/commands/security-audit-commands.d.ts +1 -1
- package/dist/cli/commands/security-audit-commands.js +1 -1
- package/dist/cli/commands/sediment-commands.d.ts +1 -1
- package/dist/cli/commands/slice-commands.js +20 -8
- package/dist/cli/commands/statusline-commands.d.ts +1 -0
- package/dist/cli/commands/statusline-commands.js +17 -3
- package/dist/cli/commands/tech-commands.d.ts +1 -1
- package/dist/services/artifacts/request-artifact-service.d.ts +2 -2
- package/dist/services/artifacts/request-artifact-service.js +10 -1
- package/dist/services/audit/enforcers/lint-reference-shape.js +18 -4
- package/dist/services/code/auto-compact-orchestrator.js +9 -1
- package/dist/services/code/batch-heartbeat-poller.js +26 -0
- package/dist/services/code/orchestrator-can-do.d.ts +24 -0
- package/dist/services/code/orchestrator-can-do.js +48 -2
- package/dist/services/code/post-compact-detector.d.ts +1 -1
- package/dist/services/code-review/ecc-bridge.d.ts +2 -7
- package/dist/services/compact-statusline/compact-statusline-service.js +19 -11
- package/dist/services/config/config-service.d.ts +2 -19
- package/dist/services/config/config-service.js +2 -56
- package/dist/services/config/config-types.d.ts +4 -24
- package/dist/services/config/config-types.js +1 -10
- package/dist/services/container/container-lease.js +21 -8
- package/dist/services/context/spillover-store.d.ts +1 -1
- package/dist/services/crystallization/crystallization-types.js +20 -8
- package/dist/services/crystallization/evidence-brief-builder.js +22 -11
- package/dist/services/dispatch/dispatch-record-writer.js +231 -141
- package/dist/services/evolution/evolution-types.js +38 -21
- package/dist/services/feedback/feedback-promotion-service.js +9 -1
- package/dist/services/hooks/pre-tool-code-gate.d.ts +46 -0
- package/dist/services/hooks/pre-tool-code-gate.js +91 -0
- package/dist/services/ide/current-model-detector.js +1 -2
- package/dist/services/lint/detect-eslint.d.ts +10 -0
- package/dist/services/lint/detect-eslint.js +62 -0
- package/dist/services/lint/detect-ocr-18.d.ts +10 -0
- package/dist/services/lint/detect-ocr-18.js +40 -0
- package/dist/services/lint/eslint-runner.d.ts +57 -0
- package/dist/services/lint/eslint-runner.js +370 -0
- package/dist/services/lint/npx-resolver.d.ts +6 -0
- package/dist/services/lint/npx-resolver.js +47 -0
- package/dist/services/lint/ocr-multilang-adapter.d.ts +34 -0
- package/dist/services/lint/ocr-multilang-adapter.js +143 -0
- package/dist/services/loop/loop-release-types.js +32 -13
- package/dist/services/loop/spec-service.d.ts +7 -1
- package/dist/services/loop/spec-service.js +218 -182
- package/dist/services/rd/rd-service.js +22 -9
- package/dist/services/scan/archetype-service.js +21 -9
- package/dist/services/session/binding-store.js +1 -2
- package/dist/services/share/bundle-reader.d.ts +6 -9
- package/dist/services/share/bundle-reader.js +297 -223
- package/dist/services/skills/hooks-settings-service.d.ts +33 -0
- package/dist/services/skills/hooks-settings-service.js +27 -1
- package/dist/services/slice/calibration-store.js +21 -8
- package/dist/services/slice/slice-check-service.js +35 -14
- package/dist/services/slice/slice-decompose-service.js +459 -223
- package/dist/services/standards/project-context.js +135 -73
- package/dist/services/verdict/envelopes.d.ts +1 -2
- package/dist/services/verdict/envelopes.js +1 -2
- package/dist/services/verdict/verdict-aggregator.d.ts +1 -2
- package/dist/services/vm/vm-lease.js +19 -8
- package/dist/services/worktree/worktree-lease.d.ts +0 -10
- package/dist/services/worktree/worktree-lease.js +19 -8
- package/dist/shared/fs-utils.d.ts +1 -1
- package/package.json +8 -12
- package/skills/bee/peaks-rd/SKILL.md +1 -1
- package/skills/bee/peaks-rd/references/jsts-eslint-gate.md +197 -0
- package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +132 -0
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +1 -1
- package/skills/peaks-code/SKILL.md +47 -1
- package/dist/services/code-review/ocr-service.d.ts +0 -129
- package/dist/services/code-review/ocr-service.js +0 -372
- 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`
|