@rigour-labs/core 6.7.5 → 6.7.7
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 +2 -0
- package/dist/index.js +2 -0
- package/dist/review/backtest-init.d.ts +15 -0
- package/dist/review/backtest-init.js +30 -16
- package/dist/review/backtest-judges.test.js +1 -1
- package/dist/review/backtest-last.d.ts +40 -0
- package/dist/review/backtest-last.js +116 -0
- package/dist/review/backtest-last.test.d.ts +1 -0
- package/dist/review/backtest-last.test.js +109 -0
- package/dist/review/backtest.d.ts +26 -0
- package/dist/review/backtest.js +5 -5
- package/dist/review/reviewer/adapters.d.ts +11 -4
- package/dist/review/reviewer/adapters.js +44 -5
- package/dist/review/reviewer/adapters.test.js +16 -1
- package/dist/review/reviewer/api-judge.d.ts +28 -0
- package/dist/review/reviewer/api-judge.js +161 -0
- package/dist/review/reviewer/api-judge.test.d.ts +1 -0
- package/dist/review/reviewer/api-judge.test.js +87 -0
- package/dist/review/reviewer/background.js +1 -1
- package/dist/review/reviewer/context.d.ts +3 -1
- package/dist/review/reviewer/context.js +8 -3
- package/dist/review/reviewer/context.test.js +2 -0
- package/dist/review/reviewer/prompt.js +11 -4
- package/dist/review/reviewer/record.d.ts +71 -0
- package/dist/review/reviewer/record.js +69 -0
- package/dist/review/reviewer/record.test.d.ts +1 -0
- package/dist/review/reviewer/record.test.js +32 -0
- package/dist/review/reviewer/settings.d.ts +10 -0
- package/dist/review/reviewer/settings.js +3 -1
- package/dist/review/reviewer/store.d.ts +2 -0
- package/dist/review/reviewer/store.js +4 -0
- package/dist/review/reviewer/usage.test.js +1 -1
- package/dist/review/reviewer/verdict.d.ts +34 -1
- package/dist/review/reviewer/verdict.js +58 -6
- package/dist/review/reviewer.d.ts +15 -0
- package/dist/review/reviewer.js +43 -12
- package/dist/review/reviewer.test.js +89 -3
- package/dist/review-learning/lessons.d.ts +2 -0
- package/dist/review-learning/lessons.js +3 -3
- package/dist/review-learning/repo-rules.d.ts +6 -4
- package/dist/review-learning/repo-rules.js +32 -9
- package/dist/review-learning/repo-rules.test.js +13 -0
- package/dist/templates/universal-config.js +1 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/index.js +15 -1
- package/package.json +6 -6
|
@@ -10,11 +10,16 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
|
-
import
|
|
13
|
+
import crypto from 'crypto';
|
|
14
|
+
import { isSpecific, meaningfulWords } from './lessons.js';
|
|
14
15
|
const RULE_FILES = ['AGENTS.md', 'CLAUDE.md', '.github/copilot-instructions.md'];
|
|
15
16
|
const RULE_DIRS = ['.cursor/rules'];
|
|
16
17
|
const MAX_RULE_CHARS = 600;
|
|
17
18
|
const MAX_RULES = 5;
|
|
19
|
+
/** A paragraph that continues the rule before it (its reason, how to apply it, an example) rather than a rule of its own. */
|
|
20
|
+
const CONTINUES = /^\**\s*(why|how to apply|example|examples|evidence|exception|exceptions|fix|note)\b\s*:?\**\s*:?/i;
|
|
21
|
+
/** Worded as a requirement: the team said must, never, always, only, every, do not. A rule without these is guidance. */
|
|
22
|
+
const REQUIREMENT = /\b(must|never|always|only|every|do not|don't|forbidden|required|non-negotiable)\b/i;
|
|
18
23
|
export function readRepoRules(cwd) {
|
|
19
24
|
const files = [
|
|
20
25
|
...RULE_FILES.filter(f => fs.existsSync(path.join(cwd, f))),
|
|
@@ -45,21 +50,39 @@ export function splitRules(source, text) {
|
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
flush();
|
|
48
|
-
|
|
53
|
+
// A "Why:" or "How to apply:" paragraph belongs to the rule above it: alone it is not checkable.
|
|
54
|
+
const merged = [];
|
|
55
|
+
for (const block of blocks) {
|
|
56
|
+
if (CONTINUES.test(block) && merged.length)
|
|
57
|
+
merged[merged.length - 1] = `${merged[merged.length - 1]} ${block}`;
|
|
58
|
+
else
|
|
59
|
+
merged.push(block);
|
|
60
|
+
}
|
|
61
|
+
return merged.map(block => ({
|
|
62
|
+
id: crypto.createHash('sha256').update(`${source}\u0000${block.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim()}`).digest('hex').slice(0, 10),
|
|
49
63
|
source,
|
|
50
64
|
text: block.length > MAX_RULE_CHARS ? `${block.slice(0, MAX_RULE_CHARS)}…` : block,
|
|
65
|
+
requirement: REQUIREMENT.test(block),
|
|
51
66
|
paths: [...new Set([...block.matchAll(/`([\w@.~-]+\/[\w./@*-]*)`/g)].map(m => m[1].replace(/\*.*$/, '').replace(/^\.\//, '')))].filter(Boolean),
|
|
52
67
|
symbols: [...new Set([...block.matchAll(/`([A-Za-z_$][\w$]*)(?:\(\))?`/g)].map(m => m[1]))].filter(isSpecific),
|
|
53
68
|
}));
|
|
54
69
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
70
|
+
/**
|
|
71
|
+
* The rules most likely to apply to a change, most specific first: a rule naming a path the change
|
|
72
|
+
* touches or an identifier in it ranks above one that only shares words with it (the change's paths
|
|
73
|
+
* and added names, split into words), and a rule sharing fewer than two words is left out. Ranking,
|
|
74
|
+
* not filtering: on a large change most rules share some words, so the judge decides applicability
|
|
75
|
+
* rule by rule from the top `limit`.
|
|
76
|
+
*/
|
|
77
|
+
function rulesForChange(rules, files, symbols, limit = MAX_RULES) {
|
|
78
|
+
const changeWords = new Set([...files.flatMap(f => f.split(/[/._-]+/)), ...symbols].flatMap(meaningfulWords));
|
|
57
79
|
const scored = rules.map(rule => {
|
|
58
80
|
const pathHits = rule.paths.filter(p => files.some(f => f === p || f.startsWith(p.endsWith('/') ? p : `${p}/`) || f.endsWith(`/${p}`))).length;
|
|
59
81
|
const symbolHits = rule.symbols.filter(s => symbols.has(s)).length;
|
|
60
|
-
|
|
82
|
+
const shared = new Set(meaningfulWords(rule.text).filter(w => changeWords.has(w))).size;
|
|
83
|
+
return { rule, named: 3 * pathHits + 2 * symbolHits, shared };
|
|
61
84
|
});
|
|
62
|
-
return scored.filter(s => s.
|
|
85
|
+
return scored.filter(s => s.named > 0 || s.shared >= 2).sort((a, b) => b.named - a.named || b.shared - a.shared).slice(0, limit).map(s => s.rule);
|
|
63
86
|
}
|
|
64
87
|
export function rulesSection(rules) {
|
|
65
88
|
if (rules.length === 0)
|
|
@@ -74,11 +97,11 @@ function listRuleFiles(cwd, dir) {
|
|
|
74
97
|
return [];
|
|
75
98
|
}
|
|
76
99
|
}
|
|
77
|
-
/** The rules that apply to a diff's changed files and added identifiers
|
|
78
|
-
export function rulesForDiff(cwd, diff, enabled = false) {
|
|
100
|
+
/** The rules that apply to a diff's changed files and added identifiers, the top `limit`. */
|
|
101
|
+
export function rulesForDiff(cwd, diff, enabled = false, limit = MAX_RULES) {
|
|
79
102
|
if (!enabled)
|
|
80
103
|
return [];
|
|
81
104
|
const files = [...diff.matchAll(/^\+\+\+ b\/(.+)$/gm)].map(m => m[1].trim());
|
|
82
105
|
const added = diff.split('\n').filter(line => line.startsWith('+') && !line.startsWith('+++')).join('\n');
|
|
83
|
-
return rulesForChange(readRepoRules(cwd), files, new Set(added.match(/[A-Za-z_$][\w$]*/g) ?? []));
|
|
106
|
+
return rulesForChange(readRepoRules(cwd), files, new Set(added.match(/[A-Za-z_$][\w$]*/g) ?? []), limit);
|
|
84
107
|
}
|
|
@@ -28,6 +28,19 @@ describe('repository rules', () => {
|
|
|
28
28
|
expect(rules.map(r => r.text.slice(0, 30))).toEqual(['**Migrations are append-only.*', 'Every outbound send goes throu', 'Prefer `fetchWithTimeout` over']);
|
|
29
29
|
expect(rules[1]).toMatchObject({ paths: ['src/lib/delivery.ts'], symbols: ['deliverOrder'] });
|
|
30
30
|
expect(rules[0].paths).toEqual(['migrations/']);
|
|
31
|
+
expect(rules.map(r => r.requirement)).toEqual([true, true, false]); // "never", "every"; "prefer" is guidance
|
|
32
|
+
expect(rules[0].id).toMatch(/^[0-9a-f]{10}$/);
|
|
33
|
+
expect(splitRules('AGENTS.md', AGENTS)[0].id).toBe(rules[0].id); // stable across runs
|
|
34
|
+
});
|
|
35
|
+
it('keeps a rule\'s "Why" and "How to apply" paragraphs with it, and ranks a rule naming the change above one that only shares its words', () => {
|
|
36
|
+
const text = '- **Use the design system.** Every control comes from `src/lib/ui`.\n\n**Why:** one source of styling.\n\n**How to apply:** import from `$lib/ui`, never a raw `<button>`.\n\n- Bound both ends of every time window a scheduled job reads.\n\n- Name the index a new query relies on in the migrations.\n';
|
|
37
|
+
const rules = splitRules('AGENTS.md', text);
|
|
38
|
+
expect(rules.map(r => r.text.slice(0, 24))).toEqual(['**Use the design system.', 'Bound both ends of every', 'Name the index a new que']);
|
|
39
|
+
expect(rules[0].text).toContain('**How to apply:**');
|
|
40
|
+
fs.writeFileSync(path.join(repo, 'AGENTS.md'), text);
|
|
41
|
+
const change = diff('src/lib/ui/Button.svelte', 'const timeWindow = scheduledJob.readsRows();');
|
|
42
|
+
expect(rulesForDiff(repo, change, true, 10).map(r => r.text.slice(0, 12))).toEqual(['**Use the de', 'Bound both e']); // the path hit first, then shared words; the index rule shares nothing
|
|
43
|
+
expect(rulesForDiff(repo, change, true, 1)).toHaveLength(1);
|
|
31
44
|
});
|
|
32
45
|
it('shows only the rules that name what the change touches, and nothing when disabled', () => {
|
|
33
46
|
expect(rulesForDiff(repo, diff('migrations/2026_add.sql', 'alter table x;'), true).map(r => r.text.slice(0, 20))).toEqual(['**Migrations are app']);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2457,6 +2457,32 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2457
2457
|
escalate: z.ZodDefault<z.ZodOptional<z.ZodEnum<["always", "risk"]>>>;
|
|
2458
2458
|
/** A model per reviewer name for cross-examination (a narrow verification task), e.g. { claude: "haiku" }. */
|
|
2459
2459
|
cross_models: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
2460
|
+
/** Reasoning effort per reviewer name where the CLI or API takes one (codex, api): low, medium or high. */
|
|
2461
|
+
reasoning: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["low", "medium", "high"]>>>>;
|
|
2462
|
+
/**
|
|
2463
|
+
* A judge reached through a model API (OpenAI-compatible chat completions with tools), named `api` in
|
|
2464
|
+
* `reviewers`: any model the team can call. The key is read from the environment variable `key_env`, never
|
|
2465
|
+
* from this file. `vendor` is the model's maker, for cross and full modes (inferred from the model name when unset).
|
|
2466
|
+
*/
|
|
2467
|
+
api: z.ZodOptional<z.ZodObject<{
|
|
2468
|
+
url: z.ZodString;
|
|
2469
|
+
model: z.ZodString;
|
|
2470
|
+
key_env: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
2471
|
+
vendor: z.ZodOptional<z.ZodEnum<["anthropic", "openai", "google", "other"]>>;
|
|
2472
|
+
max_turns: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
2473
|
+
}, "strip", z.ZodTypeAny, {
|
|
2474
|
+
model: string;
|
|
2475
|
+
url: string;
|
|
2476
|
+
key_env: string;
|
|
2477
|
+
max_turns: number;
|
|
2478
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2479
|
+
}, {
|
|
2480
|
+
model: string;
|
|
2481
|
+
url: string;
|
|
2482
|
+
key_env?: string | undefined;
|
|
2483
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2484
|
+
max_turns?: number | undefined;
|
|
2485
|
+
}>>;
|
|
2460
2486
|
/**
|
|
2461
2487
|
* Environment variables a judge's CLI must not see, per reviewer name, e.g. { codex: { unset: [OPENAI_API_KEY] } }
|
|
2462
2488
|
* when that variable holds a key meant for another service. RIGOUR_API_KEY is never passed to a judge.
|
|
@@ -2482,12 +2508,20 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2482
2508
|
judges: 2 | 3;
|
|
2483
2509
|
escalate: "always" | "risk";
|
|
2484
2510
|
cross_models: Record<string, string>;
|
|
2511
|
+
reasoning: Record<string, "high" | "medium" | "low">;
|
|
2485
2512
|
judge_env: Record<string, {
|
|
2486
2513
|
unset: string[];
|
|
2487
2514
|
}>;
|
|
2488
2515
|
model?: string | undefined;
|
|
2489
2516
|
max_runs_per_day?: number | undefined;
|
|
2490
2517
|
max_usd_per_day?: number | undefined;
|
|
2518
|
+
api?: {
|
|
2519
|
+
model: string;
|
|
2520
|
+
url: string;
|
|
2521
|
+
key_env: string;
|
|
2522
|
+
max_turns: number;
|
|
2523
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2524
|
+
} | undefined;
|
|
2491
2525
|
}, {
|
|
2492
2526
|
enabled?: boolean | undefined;
|
|
2493
2527
|
mode?: "single" | "cross" | "full" | undefined;
|
|
@@ -2505,6 +2539,14 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2505
2539
|
judges?: 2 | 3 | undefined;
|
|
2506
2540
|
escalate?: "always" | "risk" | undefined;
|
|
2507
2541
|
cross_models?: Record<string, string> | undefined;
|
|
2542
|
+
reasoning?: Record<string, "high" | "medium" | "low"> | undefined;
|
|
2543
|
+
api?: {
|
|
2544
|
+
model: string;
|
|
2545
|
+
url: string;
|
|
2546
|
+
key_env?: string | undefined;
|
|
2547
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2548
|
+
max_turns?: number | undefined;
|
|
2549
|
+
} | undefined;
|
|
2508
2550
|
judge_env?: Record<string, {
|
|
2509
2551
|
unset?: string[] | undefined;
|
|
2510
2552
|
}> | undefined;
|
|
@@ -2526,12 +2568,20 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2526
2568
|
judges: 2 | 3;
|
|
2527
2569
|
escalate: "always" | "risk";
|
|
2528
2570
|
cross_models: Record<string, string>;
|
|
2571
|
+
reasoning: Record<string, "high" | "medium" | "low">;
|
|
2529
2572
|
judge_env: Record<string, {
|
|
2530
2573
|
unset: string[];
|
|
2531
2574
|
}>;
|
|
2532
2575
|
model?: string | undefined;
|
|
2533
2576
|
max_runs_per_day?: number | undefined;
|
|
2534
2577
|
max_usd_per_day?: number | undefined;
|
|
2578
|
+
api?: {
|
|
2579
|
+
model: string;
|
|
2580
|
+
url: string;
|
|
2581
|
+
key_env: string;
|
|
2582
|
+
max_turns: number;
|
|
2583
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2584
|
+
} | undefined;
|
|
2535
2585
|
};
|
|
2536
2586
|
github_account?: string | undefined;
|
|
2537
2587
|
}, {
|
|
@@ -2555,6 +2605,14 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2555
2605
|
judges?: 2 | 3 | undefined;
|
|
2556
2606
|
escalate?: "always" | "risk" | undefined;
|
|
2557
2607
|
cross_models?: Record<string, string> | undefined;
|
|
2608
|
+
reasoning?: Record<string, "high" | "medium" | "low"> | undefined;
|
|
2609
|
+
api?: {
|
|
2610
|
+
model: string;
|
|
2611
|
+
url: string;
|
|
2612
|
+
key_env?: string | undefined;
|
|
2613
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2614
|
+
max_turns?: number | undefined;
|
|
2615
|
+
} | undefined;
|
|
2558
2616
|
judge_env?: Record<string, {
|
|
2559
2617
|
unset?: string[] | undefined;
|
|
2560
2618
|
}> | undefined;
|
|
@@ -2832,12 +2890,20 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
2832
2890
|
judges: 2 | 3;
|
|
2833
2891
|
escalate: "always" | "risk";
|
|
2834
2892
|
cross_models: Record<string, string>;
|
|
2893
|
+
reasoning: Record<string, "high" | "medium" | "low">;
|
|
2835
2894
|
judge_env: Record<string, {
|
|
2836
2895
|
unset: string[];
|
|
2837
2896
|
}>;
|
|
2838
2897
|
model?: string | undefined;
|
|
2839
2898
|
max_runs_per_day?: number | undefined;
|
|
2840
2899
|
max_usd_per_day?: number | undefined;
|
|
2900
|
+
api?: {
|
|
2901
|
+
model: string;
|
|
2902
|
+
url: string;
|
|
2903
|
+
key_env: string;
|
|
2904
|
+
max_turns: number;
|
|
2905
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
2906
|
+
} | undefined;
|
|
2841
2907
|
};
|
|
2842
2908
|
github_account?: string | undefined;
|
|
2843
2909
|
};
|
|
@@ -3120,6 +3186,14 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
3120
3186
|
judges?: 2 | 3 | undefined;
|
|
3121
3187
|
escalate?: "always" | "risk" | undefined;
|
|
3122
3188
|
cross_models?: Record<string, string> | undefined;
|
|
3189
|
+
reasoning?: Record<string, "high" | "medium" | "low"> | undefined;
|
|
3190
|
+
api?: {
|
|
3191
|
+
model: string;
|
|
3192
|
+
url: string;
|
|
3193
|
+
key_env?: string | undefined;
|
|
3194
|
+
vendor?: "anthropic" | "openai" | "google" | "other" | undefined;
|
|
3195
|
+
max_turns?: number | undefined;
|
|
3196
|
+
} | undefined;
|
|
3123
3197
|
judge_env?: Record<string, {
|
|
3124
3198
|
unset?: string[] | undefined;
|
|
3125
3199
|
}> | undefined;
|
package/dist/types/index.js
CHANGED
|
@@ -324,7 +324,7 @@ export const GatesSchema = z.object({
|
|
|
324
324
|
timeout_ms: z.number().optional(), // per model call; default: cloud 120s, local 60s, local --max 240s
|
|
325
325
|
budget_ms: z.number().optional(), // whole deep run; files not started in time are reported as skipped
|
|
326
326
|
agentic: z.boolean().optional(), // cloud tier: the model may read the repository while it reviews (default true)
|
|
327
|
-
repo_rules: z.boolean().optional(), // show the
|
|
327
|
+
repo_rules: z.boolean().optional(), // show the model review, the agent review list and the stop hook the rules in AGENTS.md / CLAUDE.md / Cursor rules that name what the change touches (default false); the reviewer always checks them
|
|
328
328
|
review_lessons: z.enum(['verified', 'all', 'off']).optional(), // the team's past review lessons: they raise a function's risk and are shown to the reviewer. verified (default), all, or off
|
|
329
329
|
router: z.object({
|
|
330
330
|
enabled: z.boolean().optional(), // default true
|
|
@@ -423,6 +423,20 @@ export const ConfigSchema = z.object({
|
|
|
423
423
|
escalate: z.enum(['always', 'risk']).optional().default('always'),
|
|
424
424
|
/** A model per reviewer name for cross-examination (a narrow verification task), e.g. { claude: "haiku" }. */
|
|
425
425
|
cross_models: z.record(ModelName).optional().default({}),
|
|
426
|
+
/** Reasoning effort per reviewer name where the CLI or API takes one (codex, api): low, medium or high. */
|
|
427
|
+
reasoning: z.record(z.enum(['low', 'medium', 'high'])).optional().default({}),
|
|
428
|
+
/**
|
|
429
|
+
* A judge reached through a model API (OpenAI-compatible chat completions with tools), named `api` in
|
|
430
|
+
* `reviewers`: any model the team can call. The key is read from the environment variable `key_env`, never
|
|
431
|
+
* from this file. `vendor` is the model's maker, for cross and full modes (inferred from the model name when unset).
|
|
432
|
+
*/
|
|
433
|
+
api: z.object({
|
|
434
|
+
url: z.string().url(),
|
|
435
|
+
model: z.string().min(1),
|
|
436
|
+
key_env: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, 'an environment variable name').optional().default('RIGOUR_JUDGE_API_KEY'),
|
|
437
|
+
vendor: z.enum(['anthropic', 'openai', 'google', 'other']).optional(),
|
|
438
|
+
max_turns: z.number().int().positive().optional().default(60),
|
|
439
|
+
}).optional(),
|
|
426
440
|
/**
|
|
427
441
|
* Environment variables a judge's CLI must not see, per reviewer name, e.g. { codex: { unset: [OPENAI_API_KEY] } }
|
|
428
442
|
* when that variable holds a key meant for another service. RIGOUR_API_KEY is never passed to a judge.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "6.7.
|
|
3
|
+
"version": "6.7.7",
|
|
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"
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"@anthropic-ai/sdk": "^0.30.1",
|
|
73
73
|
"pg": "^8.16.3",
|
|
74
74
|
"openai": "^5.23.2",
|
|
75
|
-
"@rigour-labs/brain-darwin-
|
|
76
|
-
"@rigour-labs/brain-
|
|
77
|
-
"@rigour-labs/brain-
|
|
78
|
-
"@rigour-labs/brain-
|
|
79
|
-
"@rigour-labs/brain-linux-x64": "6.7.
|
|
75
|
+
"@rigour-labs/brain-darwin-arm64": "6.7.7",
|
|
76
|
+
"@rigour-labs/brain-darwin-x64": "6.7.7",
|
|
77
|
+
"@rigour-labs/brain-linux-arm64": "6.7.7",
|
|
78
|
+
"@rigour-labs/brain-win-x64": "6.7.7",
|
|
79
|
+
"@rigour-labs/brain-linux-x64": "6.7.7"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@types/fs-extra": "^11.0.4",
|