@review-agent/core 0.1.1 → 0.1.3
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/dist/index.d.ts +1 -1
- package/dist/index.js +72 -9
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -466,7 +466,7 @@ declare const RECORD_VERSION: 1;
|
|
|
466
466
|
/** The version of this engine, on every record it writes. Kept in step with
|
|
467
467
|
* `packages/core/package.json` by `record.test.ts` — a record whose engine version is a guess is
|
|
468
468
|
* worth less than no record at all (design §4.7). */
|
|
469
|
-
declare const ENGINE_VERSION = "0.1.
|
|
469
|
+
declare const ENGINE_VERSION = "0.1.3";
|
|
470
470
|
type RecordedSkill = {
|
|
471
471
|
id: string;
|
|
472
472
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -16,8 +16,61 @@ import { z as z3 } from "zod";
|
|
|
16
16
|
|
|
17
17
|
// src/cli-process.ts
|
|
18
18
|
import { spawn } from "child_process";
|
|
19
|
+
import { accessSync, constants, realpathSync, statSync } from "fs";
|
|
20
|
+
import { posix, win32 } from "path";
|
|
19
21
|
import { z } from "zod";
|
|
20
|
-
var
|
|
22
|
+
var resolveClaudeBinary = (a) => {
|
|
23
|
+
const platform = a.platform ?? process.platform;
|
|
24
|
+
const path = platform === "win32" ? win32 : posix;
|
|
25
|
+
const isExecutableFile = a.isExecutableFile ?? executableFileTest(platform);
|
|
26
|
+
const real = a.realpath ?? realpathOrSelf;
|
|
27
|
+
const avoid = real(path.resolve(a.avoidDir));
|
|
28
|
+
const inside = (candidate) => {
|
|
29
|
+
const rel = path.relative(avoid, real(candidate));
|
|
30
|
+
return rel === "" || !(rel === ".." || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel));
|
|
31
|
+
};
|
|
32
|
+
const named = a.env.REVIEW_AGENT_CLAUDE_BIN;
|
|
33
|
+
if (named !== void 0 && named !== "") {
|
|
34
|
+
if (!path.isAbsolute(named)) {
|
|
35
|
+
throw new Error("REVIEW_AGENT_CLAUDE_BIN must be an absolute path to the Claude Code binary; a relative one could resolve inside the code under review");
|
|
36
|
+
}
|
|
37
|
+
if (inside(named)) {
|
|
38
|
+
throw new Error("REVIEW_AGENT_CLAUDE_BIN points inside the checkout of the code under review; refusing to run it");
|
|
39
|
+
}
|
|
40
|
+
return named;
|
|
41
|
+
}
|
|
42
|
+
const names = platform === "win32" ? ["claude.exe"] : ["claude"];
|
|
43
|
+
for (const dir of pathEntries(a.env, platform)) {
|
|
44
|
+
if (dir === "" || !path.isAbsolute(dir)) continue;
|
|
45
|
+
for (const name of names) {
|
|
46
|
+
const candidate = path.join(dir, name);
|
|
47
|
+
if (!isExecutableFile(candidate) || inside(candidate)) continue;
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw new Error(platform === "win32" ? "Claude Code was not found: no claude.exe in an absolute PATH directory. Install it with Anthropic's installer (irm https://claude.ai/install.ps1 | iex) or WinGet, or set REVIEW_AGENT_CLAUDE_BIN to the absolute path of claude.exe" : "Claude Code was not found: no executable `claude` in an absolute PATH directory. Install Claude Code, or set REVIEW_AGENT_CLAUDE_BIN to its absolute path");
|
|
52
|
+
};
|
|
53
|
+
var pathEntries = (env, platform) => {
|
|
54
|
+
const key = platform === "win32" ? Object.keys(env).find((k) => k.toUpperCase() === "PATH") : "PATH";
|
|
55
|
+
const value = key === void 0 ? void 0 : env[key];
|
|
56
|
+
return (value ?? "").split(platform === "win32" ? ";" : ":");
|
|
57
|
+
};
|
|
58
|
+
var executableFileTest = (platform) => (p) => {
|
|
59
|
+
try {
|
|
60
|
+
if (!statSync(p).isFile()) return false;
|
|
61
|
+
if (platform !== "win32") accessSync(p, constants.X_OK);
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var realpathOrSelf = (p) => {
|
|
68
|
+
try {
|
|
69
|
+
return realpathSync(p);
|
|
70
|
+
} catch {
|
|
71
|
+
return p;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
21
74
|
var CHILD_ENV_ALLOWLIST = [
|
|
22
75
|
"PATH",
|
|
23
76
|
"HOME",
|
|
@@ -76,11 +129,21 @@ var spawnClaude = (a) => new Promise((resolve3, reject) => {
|
|
|
76
129
|
reject(new Error(refusal));
|
|
77
130
|
return;
|
|
78
131
|
}
|
|
79
|
-
|
|
132
|
+
let bin;
|
|
133
|
+
try {
|
|
134
|
+
bin = resolveClaudeBinary({ env: process.env, avoidDir: a.cwd });
|
|
135
|
+
} catch (e) {
|
|
136
|
+
reject(e instanceof Error ? e : new Error(String(e)));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const viaNode = /\.(c|m)?js$/i.test(bin);
|
|
140
|
+
const child = spawn(viaNode ? process.execPath : bin, viaNode ? [bin, ...a.argv] : a.argv, {
|
|
80
141
|
cwd: a.cwd,
|
|
81
142
|
env: childEnv(a.extraEnv),
|
|
82
143
|
signal: a.signal,
|
|
83
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
144
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
145
|
+
// No console window flashing up when the reviewer runs from a GUI app or a service on Windows.
|
|
146
|
+
windowsHide: true
|
|
84
147
|
});
|
|
85
148
|
let out = "";
|
|
86
149
|
let err = "";
|
|
@@ -311,7 +374,7 @@ var runEngine = async (i) => {
|
|
|
311
374
|
// review in progress, and a login failure reporting it would send someone looking at budgets.
|
|
312
375
|
cutShort: terminalReason !== "completed" && failure?.kind !== "cli-error",
|
|
313
376
|
// Always false, and it is not an oversight: nothing can fire. The spike of 2026-09-13
|
|
314
|
-
// (
|
|
377
|
+
// (a spike during development) proved the delivery half works — a PostToolUse hook passed through
|
|
315
378
|
// --settings survives --restricted and demonstrably puts text in front of the model — and then
|
|
316
379
|
// found there is no spend to trigger it on. The hook event carries no cost, no usage and no
|
|
317
380
|
// token count; --no-session-persistence means the transcript it points at is never written; and
|
|
@@ -857,7 +920,7 @@ var runRecheckPass = async (a) => {
|
|
|
857
920
|
|
|
858
921
|
// src/record.ts
|
|
859
922
|
var RECORD_VERSION = 1;
|
|
860
|
-
var ENGINE_VERSION = "0.1.
|
|
923
|
+
var ENGINE_VERSION = "0.1.3";
|
|
861
924
|
var buildReviewRecord = (a) => {
|
|
862
925
|
const { request, settings, result, startedAt, finishedAt } = a;
|
|
863
926
|
return {
|
|
@@ -912,10 +975,10 @@ import {
|
|
|
912
975
|
existsSync,
|
|
913
976
|
mkdirSync,
|
|
914
977
|
readdirSync,
|
|
915
|
-
realpathSync,
|
|
978
|
+
realpathSync as realpathSync2,
|
|
916
979
|
renameSync,
|
|
917
980
|
rmSync,
|
|
918
|
-
statSync
|
|
981
|
+
statSync as statSync2
|
|
919
982
|
} from "fs";
|
|
920
983
|
import { isAbsolute, join, relative as relative2, resolve as resolve2 } from "path";
|
|
921
984
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
@@ -933,8 +996,8 @@ var SkillError = class extends Error {
|
|
|
933
996
|
var materialiseDir = (source, id) => {
|
|
934
997
|
if (!isAbsolute(source.path)) throw new SkillError(id, "path must be absolute");
|
|
935
998
|
if (!existsSync(source.path)) throw new SkillError(id, "no such directory");
|
|
936
|
-
const real =
|
|
937
|
-
if (!
|
|
999
|
+
const real = realpathSync2(source.path);
|
|
1000
|
+
if (!statSync2(real).isDirectory()) throw new SkillError(id, "not a directory");
|
|
938
1001
|
return real;
|
|
939
1002
|
};
|
|
940
1003
|
var GITHUB_REPO = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|