@rigour-labs/core 6.10.0-rc.1 → 6.10.0
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/hooks/checker.js +14 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/review/agent-fixes.d.ts +15 -0
- package/dist/review/agent-fixes.js +75 -13
- package/dist/utils/code-mask.d.ts +4 -2
- package/dist/utils/code-mask.js +6 -2
- package/package.json +6 -6
package/dist/hooks/checker.js
CHANGED
|
@@ -11,6 +11,8 @@ import fs from 'fs-extra';
|
|
|
11
11
|
import path from 'path';
|
|
12
12
|
import yaml from 'yaml';
|
|
13
13
|
import { ConfigSchema } from '../types/index.js';
|
|
14
|
+
import { codeOffsets } from '../utils/code-mask.js';
|
|
15
|
+
import { resolveTsPathTarget } from '../gates/hallucinated-imports/ts-path-target.js';
|
|
14
16
|
import { scanInputForCredentials } from './input-validator.js';
|
|
15
17
|
import { evaluateWriteScope, loadAgentScopesFromDisk } from '../firewall/scope-enforcement.js';
|
|
16
18
|
const JS_TS_PATTERN = /\.(ts|tsx|js|jsx|mts|mjs)$/;
|
|
@@ -57,7 +59,7 @@ async function resolveFile(filePath, cwd) {
|
|
|
57
59
|
/**
|
|
58
60
|
* Run all fast gates on a single file's content.
|
|
59
61
|
*/
|
|
60
|
-
function checkFile(content, relPath, cwd, config) {
|
|
62
|
+
async function checkFile(content, relPath, cwd, config) {
|
|
61
63
|
const failures = [];
|
|
62
64
|
const lines = content.split('\n');
|
|
63
65
|
// Gate 0a: Protected paths — BLOCK writes to .github/, rigour.yml, etc.
|
|
@@ -77,7 +79,7 @@ function checkFile(content, relPath, cwd, config) {
|
|
|
77
79
|
const isJsTs = JS_TS_PATTERN.test(relPath);
|
|
78
80
|
// Gate 2: Hallucinated imports (JS/TS only)
|
|
79
81
|
if (isJsTs) {
|
|
80
|
-
checkHallucinatedImports(content, relPath, cwd, failures);
|
|
82
|
+
await checkHallucinatedImports(content, relPath, cwd, failures);
|
|
81
83
|
}
|
|
82
84
|
// Gate 3: Promise safety (JS/TS only)
|
|
83
85
|
if (isJsTs) {
|
|
@@ -131,7 +133,7 @@ export async function runHookChecker(options) {
|
|
|
131
133
|
continue;
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
|
-
const fileFailures = checkFile(resolved.content, resolved.relPath, cwd, config);
|
|
136
|
+
const fileFailures = await checkFile(resolved.content, resolved.relPath, cwd, config);
|
|
135
137
|
failures.push(...fileFailures);
|
|
136
138
|
}
|
|
137
139
|
if (timedOut) {
|
|
@@ -163,28 +165,24 @@ export async function runHookChecker(options) {
|
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
/**
|
|
166
|
-
* Check for imports of non-existent relative files
|
|
168
|
+
* Check for imports of non-existent relative files, resolved by the same rule as the review's hallucinated-imports gate
|
|
169
|
+
* (ts-path-target.ts): an extensionless path with any source extension or as a folder's index, and a TypeScript ESM
|
|
170
|
+
* specifier naming the emitted file (`./b.js` for `b.ts` or `b.tsx`, `.mjs` for `.mts`, `.cjs` for `.cts`).
|
|
167
171
|
*/
|
|
168
|
-
function checkHallucinatedImports(content, relPath, cwd, failures) {
|
|
172
|
+
async function checkHallucinatedImports(content, relPath, cwd, failures) {
|
|
169
173
|
const importRegex = /(?:import\s+.*\s+from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g;
|
|
174
|
+
// An import written inside a string (a template, a fixture) or a comment is text, not an import.
|
|
175
|
+
const isCode = codeOffsets(content, path.extname(relPath).slice(1));
|
|
170
176
|
let match;
|
|
171
177
|
while ((match = importRegex.exec(content)) !== null) {
|
|
178
|
+
if (!isCode(match.index))
|
|
179
|
+
continue;
|
|
172
180
|
const specifier = match[1] || match[2];
|
|
173
181
|
if (!specifier || !specifier.startsWith('.')) {
|
|
174
182
|
continue;
|
|
175
183
|
}
|
|
176
184
|
const dir = path.dirname(path.join(cwd, relPath));
|
|
177
|
-
|
|
178
|
-
const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs', '/index.ts', '/index.js'];
|
|
179
|
-
const exists = extensions.some(ext => {
|
|
180
|
-
try {
|
|
181
|
-
return fs.existsSync(resolved + ext);
|
|
182
|
-
}
|
|
183
|
-
catch {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
if (!exists) {
|
|
185
|
+
if (!await resolveTsPathTarget(dir, specifier, cwd, new Set())) {
|
|
188
186
|
const lineNum = content.substring(0, match.index).split('\n').length;
|
|
189
187
|
failures.push({
|
|
190
188
|
gate: 'hallucinated-imports',
|
package/dist/index.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export { recordAgentWrites, captureHumanEdits } from './review-learning/human-ed
|
|
|
53
53
|
export { readRepoRules, rulesForDiff, rulesSection, splitRules, type RepoRule } from './review-learning/repo-rules.js';
|
|
54
54
|
export { diffFromGit, GitDiffError, type DiffSource } from './review/git-diff.js';
|
|
55
55
|
export { checkPrecisions, checkId, precisionOf, isMuted, readOutcomes, MUTE_MIN_OUTCOMES, MUTE_BELOW, type CheckPrecision, type CheckOutcome } from './review/check-outcomes.js';
|
|
56
|
-
export { recordReviewOutcome, listResolvedFixes, removeResolvedFix, openFindingCount, listOpenFindings, type ResolvedFix, type FixCapture } from './review/agent-fixes.js';
|
|
56
|
+
export { recordReviewOutcome, recheckOpenFindings, listResolvedFixes, removeResolvedFix, openFindingCount, listOpenFindings, type ResolvedFix, type FixCapture } from './review/agent-fixes.js';
|
|
57
57
|
export { appendStory, readStories, compactDiff, STORIES_FILE, type Story, type CatchStage } from './review/stories.js';
|
|
58
58
|
export { recordLessonsServed, recordPrCatches, recordReuseSuggested, type LessonChannel } from './review/learning-events.js';
|
|
59
59
|
export { computeEffectiveness, readAgentEvents, appendAgentEvent, findingKeys, type AgentEvent, type ReviewEffectiveness } from './review/effectiveness.js';
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ export { recordAgentWrites, captureHumanEdits } from './review-learning/human-ed
|
|
|
51
51
|
export { readRepoRules, rulesForDiff, rulesSection, splitRules } from './review-learning/repo-rules.js';
|
|
52
52
|
export { diffFromGit, GitDiffError } from './review/git-diff.js';
|
|
53
53
|
export { checkPrecisions, checkId, precisionOf, isMuted, readOutcomes, MUTE_MIN_OUTCOMES, MUTE_BELOW } from './review/check-outcomes.js';
|
|
54
|
-
export { recordReviewOutcome, listResolvedFixes, removeResolvedFix, openFindingCount, listOpenFindings } from './review/agent-fixes.js';
|
|
54
|
+
export { recordReviewOutcome, recheckOpenFindings, listResolvedFixes, removeResolvedFix, openFindingCount, listOpenFindings } from './review/agent-fixes.js';
|
|
55
55
|
export { appendStory, readStories, compactDiff, STORIES_FILE } from './review/stories.js';
|
|
56
56
|
export { recordLessonsServed, recordPrCatches, recordReuseSuggested } from './review/learning-events.js';
|
|
57
57
|
export { computeEffectiveness, readAgentEvents, appendAgentEvent, findingKeys } from './review/effectiveness.js';
|
|
@@ -12,6 +12,8 @@ interface OpenFinding {
|
|
|
12
12
|
stage?: CatchStage;
|
|
13
13
|
/** Its dismissal key (quiet.ts): a finding someone dismissed was not fixed. */
|
|
14
14
|
key?: string;
|
|
15
|
+
/** The version of the checks that reported it (CHECKER_VERSION); absent in entries captured before it was kept. */
|
|
16
|
+
checker?: string;
|
|
15
17
|
}
|
|
16
18
|
export interface ResolvedFix {
|
|
17
19
|
id: string;
|
|
@@ -31,12 +33,25 @@ export interface FixCapture {
|
|
|
31
33
|
resolved: number;
|
|
32
34
|
/** The fixes this review resolved, for turning into lessons (storage/fix-lessons.ts). */
|
|
33
35
|
fixes: ResolvedFix[];
|
|
36
|
+
/** Findings another version of the checks opened that this one no longer reports: closed, never credited as fixes. */
|
|
37
|
+
checkerChanged: number;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* Record a review's findings: open new ones, resolve open ones the review no longer reports.
|
|
37
41
|
* Each resolved fix is also kept as a story, credited to the stage that first reported it.
|
|
38
42
|
*/
|
|
39
43
|
export declare function recordReviewOutcome(cwd: string, findings: Failure[], reviewedFiles: string[], stage?: CatchStage): FixCapture;
|
|
44
|
+
/**
|
|
45
|
+
* Re-checks, in place, the findings another version of the checks opened at `stage` (with `all`, every one open at
|
|
46
|
+
* it), with `check` (this version's, for that stage): what it still reports stays open, now as its own. What it no
|
|
47
|
+
* longer reports closes: one another version opened as checker-changed, never a fix; one this version opened the way
|
|
48
|
+
* any review resolves it (a fix when its file changed since, creditFix). Runs nothing when no such finding is open.
|
|
49
|
+
*/
|
|
50
|
+
export declare function recheckOpenFindings(cwd: string, stage: CatchStage, check: (files: string[]) => Promise<Failure[]>, all?: boolean): Promise<{
|
|
51
|
+
closed: number;
|
|
52
|
+
kept: number;
|
|
53
|
+
fixed: number;
|
|
54
|
+
}>;
|
|
40
55
|
export declare function listResolvedFixes(cwd: string): ResolvedFix[];
|
|
41
56
|
export declare function removeResolvedFix(cwd: string, id: string): void;
|
|
42
57
|
export declare function openFindingCount(cwd: string): number;
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
*
|
|
10
10
|
* A finding only counts as resolved when its file was part of the review that
|
|
11
11
|
* no longer reports it: a file committed out of the working tree was not fixed.
|
|
12
|
+
*
|
|
13
|
+
* Nor is a finding an older version of Rigour's checks reported: when the checks change (an upgrade), a finding they
|
|
14
|
+
* no longer report closes as "checker changed", never as a fix, with no story and no outcome. Otherwise a false
|
|
15
|
+
* finding the old checks made would be credited to the agent the first time it edited the file.
|
|
12
16
|
*/
|
|
13
17
|
import crypto from 'crypto';
|
|
14
18
|
import fs from 'fs';
|
|
@@ -19,6 +23,18 @@ import { dismissedKeys, findingKey } from './quiet.js';
|
|
|
19
23
|
const DIR = path.join('.rigour', 'agent-fixes');
|
|
20
24
|
const MAX_FILE_BYTES = 200_000;
|
|
21
25
|
const OPEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
26
|
+
/** The version of Rigour's checks: the core package's own. A finding another version opened is never credited as a fix. */
|
|
27
|
+
const CHECKER_VERSION = coreVersion();
|
|
28
|
+
function coreVersion() {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version ?? 'unknown';
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return 'unknown';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Whether another version of the checks opened it. */
|
|
37
|
+
const stale = (entry) => entry.checker !== CHECKER_VERSION;
|
|
22
38
|
/**
|
|
23
39
|
* Record a review's findings: open new ones, resolve open ones the review no longer reports.
|
|
24
40
|
* Each resolved fix is also kept as a story, credited to the stage that first reported it.
|
|
@@ -26,21 +42,21 @@ const OPEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
26
42
|
export function recordReviewOutcome(cwd, findings, reviewedFiles, stage = 'review') {
|
|
27
43
|
const open = readOpen(cwd);
|
|
28
44
|
const current = new Map(findings.flatMap(f => (f.files?.[0] ? [[`${f.id}:${f.files[0]}`, f]] : [])));
|
|
29
|
-
const fixes = resolveGone(cwd, open, current, new Set(reviewedFiles));
|
|
30
|
-
for (const fix of fixes) {
|
|
31
|
-
appendStory(cwd, { at: fix.resolvedAt, openedAt: fix.openedAt, stage: fix.stage ?? stage, file: fix.file, rule: fix.rule, title: fix.title ?? fix.rule, details: fix.details, diff: compactDiff(fix.before, fix.after) });
|
|
32
|
-
}
|
|
45
|
+
const { resolved: fixes, checkerChanged } = resolveGone(cwd, open, current, new Set(reviewedFiles), stage);
|
|
33
46
|
const opened = openNew(cwd, open, current, stage);
|
|
34
47
|
writeOpen(cwd, open);
|
|
35
|
-
return { opened, resolved: fixes.length, fixes };
|
|
48
|
+
return { opened, resolved: fixes.length, fixes, checkerChanged };
|
|
36
49
|
}
|
|
37
50
|
/** Open findings this review no longer reports: resolved when their file was reviewed and changed. */
|
|
38
|
-
function resolveGone(cwd, open, current, reviewed) {
|
|
51
|
+
function resolveGone(cwd, open, current, reviewed, stage) {
|
|
39
52
|
const resolved = [];
|
|
53
|
+
let checkerChanged = 0;
|
|
40
54
|
const dismissed = dismissedKeys(cwd);
|
|
41
55
|
for (const [key, entry] of Object.entries(open)) {
|
|
42
|
-
if (current.has(key))
|
|
56
|
+
if (current.has(key)) {
|
|
57
|
+
entry.checker = CHECKER_VERSION; // still reported by these checks: theirs now
|
|
43
58
|
continue;
|
|
59
|
+
}
|
|
44
60
|
if (entry.key && dismissed.has(entry.key)) {
|
|
45
61
|
delete open[key]; // gone because a person dismissed it, not because anyone fixed it
|
|
46
62
|
continue;
|
|
@@ -50,14 +66,60 @@ function resolveGone(cwd, open, current, reviewed) {
|
|
|
50
66
|
delete open[key];
|
|
51
67
|
continue;
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
if (stale(entry)) {
|
|
70
|
+
delete open[key]; // the checks changed, not the code: no fix, no story, no outcome
|
|
71
|
+
checkerChanged++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const fix = creditFix(cwd, entry, stage);
|
|
75
|
+
if (fix)
|
|
76
|
+
resolved.push(fix);
|
|
77
|
+
delete open[key];
|
|
78
|
+
}
|
|
79
|
+
return { resolved, checkerChanged };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A finding these checks reported and no longer do, in a file that changed since: the agent's fix, kept with its before
|
|
83
|
+
* and after, a story credited to the stage that first reported it, and a `fixed` outcome. A file unchanged since is no
|
|
84
|
+
* fix (the finding vanished without an edit): undefined.
|
|
85
|
+
*/
|
|
86
|
+
function creditFix(cwd, entry, stage) {
|
|
87
|
+
const after = readSmall(cwd, entry.file);
|
|
88
|
+
if (after === null || after === entry.before)
|
|
89
|
+
return undefined;
|
|
90
|
+
const fix = writeResolved(cwd, entry, after);
|
|
91
|
+
recordOutcome(cwd, checkId({ rule: entry.rule, title: entry.title }), 'fixed');
|
|
92
|
+
appendStory(cwd, { at: fix.resolvedAt, openedAt: fix.openedAt, stage: fix.stage ?? stage, file: fix.file, rule: fix.rule, title: fix.title ?? fix.rule, details: fix.details, diff: compactDiff(fix.before, fix.after) });
|
|
93
|
+
return fix;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Re-checks, in place, the findings another version of the checks opened at `stage` (with `all`, every one open at
|
|
97
|
+
* it), with `check` (this version's, for that stage): what it still reports stays open, now as its own. What it no
|
|
98
|
+
* longer reports closes: one another version opened as checker-changed, never a fix; one this version opened the way
|
|
99
|
+
* any review resolves it (a fix when its file changed since, creditFix). Runs nothing when no such finding is open.
|
|
100
|
+
*/
|
|
101
|
+
export async function recheckOpenFindings(cwd, stage, check, all = false) {
|
|
102
|
+
const open = readOpen(cwd);
|
|
103
|
+
const old = Object.entries(open).filter(([, entry]) => (all || stale(entry)) && entry.stage === stage);
|
|
104
|
+
if (old.length === 0)
|
|
105
|
+
return { closed: 0, kept: 0, fixed: 0 };
|
|
106
|
+
const files = [...new Set(old.map(([, entry]) => entry.file))].filter(file => fs.existsSync(path.join(cwd, file)));
|
|
107
|
+
const reported = new Set((await check(files)).flatMap(f => (f.files?.[0] ? [`${f.id}:${f.files[0]}`] : [])));
|
|
108
|
+
let closed = 0;
|
|
109
|
+
let fixed = 0;
|
|
110
|
+
for (const [key, entry] of old) {
|
|
111
|
+
if (reported.has(key)) {
|
|
112
|
+
entry.checker = CHECKER_VERSION;
|
|
113
|
+
continue;
|
|
57
114
|
}
|
|
115
|
+
if (!stale(entry) && creditFix(cwd, entry, stage))
|
|
116
|
+
fixed++;
|
|
117
|
+
else
|
|
118
|
+
closed++;
|
|
58
119
|
delete open[key];
|
|
59
120
|
}
|
|
60
|
-
|
|
121
|
+
writeOpen(cwd, open);
|
|
122
|
+
return { closed, kept: old.length - closed - fixed, fixed };
|
|
61
123
|
}
|
|
62
124
|
function openNew(cwd, open, current, stage) {
|
|
63
125
|
let opened = 0;
|
|
@@ -68,7 +130,7 @@ function openNew(cwd, open, current, stage) {
|
|
|
68
130
|
const before = readSmall(cwd, file);
|
|
69
131
|
if (before === null)
|
|
70
132
|
continue;
|
|
71
|
-
open[key] = { file, rule: finding.id, title: finding.title, details: finding.details, before, openedAt: new Date().toISOString(), stage, key: findingKey(finding) };
|
|
133
|
+
open[key] = { file, rule: finding.id, title: finding.title, details: finding.details, before, openedAt: new Date().toISOString(), stage, key: findingKey(finding), checker: CHECKER_VERSION };
|
|
72
134
|
opened++;
|
|
73
135
|
}
|
|
74
136
|
return opened;
|
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* a string. Every line starts as code: nothing carries to the next line, so a regex literal holding a quote (`/"/`) or
|
|
8
8
|
* an unclosed quote affects only its own line and can never hide a real call below it.
|
|
9
9
|
*
|
|
10
|
+
* In the C family, a line that starts with `/*` or `*` is a block comment's line (the JSDoc shape) and is all comment.
|
|
11
|
+
*
|
|
10
12
|
* Two limits, both narrowing a false positive and never losing a match the gates made before:
|
|
11
|
-
* - a string or a block comment spanning lines (a Python `"""` docstring, a multi-line template literal,
|
|
12
|
-
* is read as code past its first line, as before;
|
|
13
|
+
* - a string or a block comment spanning lines (a Python `"""` docstring, a multi-line template literal, a block
|
|
14
|
+
* comment whose lines do not start with `*`) is read as code past its first line, as before;
|
|
13
15
|
* - code inside a string interpolation (Python `f"{…}"`, JS `${…}`, Ruby `#{…}`) is string, so a call written inside
|
|
14
16
|
* one does not count as a call.
|
|
15
17
|
*/
|
package/dist/utils/code-mask.js
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* a string. Every line starts as code: nothing carries to the next line, so a regex literal holding a quote (`/"/`) or
|
|
8
8
|
* an unclosed quote affects only its own line and can never hide a real call below it.
|
|
9
9
|
*
|
|
10
|
+
* In the C family, a line that starts with `/*` or `*` is a block comment's line (the JSDoc shape) and is all comment.
|
|
11
|
+
*
|
|
10
12
|
* Two limits, both narrowing a false positive and never losing a match the gates made before:
|
|
11
|
-
* - a string or a block comment spanning lines (a Python `"""` docstring, a multi-line template literal,
|
|
12
|
-
* is read as code past its first line, as before;
|
|
13
|
+
* - a string or a block comment spanning lines (a Python `"""` docstring, a multi-line template literal, a block
|
|
14
|
+
* comment whose lines do not start with `*`) is read as code past its first line, as before;
|
|
13
15
|
* - code inside a string interpolation (Python `f"{…}"`, JS `${…}`, Ruby `#{…}`) is string, so a call written inside
|
|
14
16
|
* one does not count as a call.
|
|
15
17
|
*/
|
|
@@ -22,6 +24,8 @@ const HASH_COMMENTS = new Set(['py', 'rb', 'sh', 'bash', 'zsh', 'yml', 'yaml', '
|
|
|
22
24
|
/** For one line, whether column `col` is code. `ext` is the file's extension, which decides the comment marker. */
|
|
23
25
|
export function codeColumns(line, ext) {
|
|
24
26
|
const hash = HASH_COMMENTS.has(ext.toLowerCase());
|
|
27
|
+
if (!hash && /^\s*(\/\*|\*)/.test(line))
|
|
28
|
+
return () => false;
|
|
25
29
|
const masked = new Uint8Array(line.length);
|
|
26
30
|
for (let i = 0; i < line.length; i++) {
|
|
27
31
|
const c = line[i];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "6.10.0
|
|
3
|
+
"version": "6.10.0",
|
|
4
4
|
"description": "Rigour's review engine: deterministic gates on changed lines, rules and lessons learned from your team's fixes, and per-check precision from what you fix versus dismiss, across TypeScript, JavaScript, Python, Go, Ruby and C#.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.13"
|
|
@@ -75,11 +75,11 @@
|
|
|
75
75
|
"@anthropic-ai/sdk": "^0.132.1",
|
|
76
76
|
"pg": "^8.16.3",
|
|
77
77
|
"openai": "^5.23.2",
|
|
78
|
-
"@rigour-labs/brain-darwin-x64": "6.10.0
|
|
79
|
-
"@rigour-labs/brain-darwin-arm64": "6.10.0
|
|
80
|
-
"@rigour-labs/brain-linux-
|
|
81
|
-
"@rigour-labs/brain-linux-
|
|
82
|
-
"@rigour-labs/brain-win-x64": "6.10.0
|
|
78
|
+
"@rigour-labs/brain-darwin-x64": "6.10.0",
|
|
79
|
+
"@rigour-labs/brain-darwin-arm64": "6.10.0",
|
|
80
|
+
"@rigour-labs/brain-linux-arm64": "6.10.0",
|
|
81
|
+
"@rigour-labs/brain-linux-x64": "6.10.0",
|
|
82
|
+
"@rigour-labs/brain-win-x64": "6.10.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/fs-extra": "^11.0.4",
|