@rigour-labs/core 5.2.5 → 5.2.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/hooks/checker.js
CHANGED
|
@@ -13,6 +13,23 @@ import yaml from 'yaml';
|
|
|
13
13
|
import { ConfigSchema } from '../types/index.js';
|
|
14
14
|
import { scanInputForCredentials } from './input-validator.js';
|
|
15
15
|
const JS_TS_PATTERN = /\.(ts|tsx|js|jsx|mts|mjs)$/;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a file matches any ignore pattern from rigour.yml.
|
|
18
|
+
*/
|
|
19
|
+
function isIgnored(relPath, patterns) {
|
|
20
|
+
const normalized = relPath.replace(/\\/g, '/');
|
|
21
|
+
return patterns.some(pattern => {
|
|
22
|
+
const clean = pattern.replace(/\\/g, '/');
|
|
23
|
+
if (normalized === clean)
|
|
24
|
+
return true;
|
|
25
|
+
// Glob: foo/** matches foo/bar/baz
|
|
26
|
+
const prefix = clean.replace('/**', '').replace('/*', '');
|
|
27
|
+
if (prefix !== clean) {
|
|
28
|
+
return normalized.startsWith(prefix + '/') || normalized === prefix;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
16
33
|
/**
|
|
17
34
|
* Load rigour config from cwd, falling back to defaults.
|
|
18
35
|
*/
|
|
@@ -42,7 +59,9 @@ async function resolveFile(filePath, cwd) {
|
|
|
42
59
|
function checkFile(content, relPath, cwd, config) {
|
|
43
60
|
const failures = [];
|
|
44
61
|
const lines = content.split('\n');
|
|
45
|
-
// Gate
|
|
62
|
+
// Gate 0a: Protected paths — BLOCK writes to .github/, rigour.yml, etc.
|
|
63
|
+
checkProtectedPaths(relPath, config, failures);
|
|
64
|
+
// Gate 0b: Memory & Skills Governance — block writes to agent-native memory paths
|
|
46
65
|
checkGovernance(content, relPath, config, failures);
|
|
47
66
|
// Gate 1: File size
|
|
48
67
|
const maxLines = config.gates.max_file_lines ?? 500;
|
|
@@ -78,6 +97,7 @@ export async function runHookChecker(options) {
|
|
|
78
97
|
try {
|
|
79
98
|
const config = await loadConfig(cwd);
|
|
80
99
|
const deadline = start + timeout_ms;
|
|
100
|
+
const ignorePatterns = config.ignore ?? [];
|
|
81
101
|
for (const filePath of files) {
|
|
82
102
|
if (Date.now() > deadline) {
|
|
83
103
|
break;
|
|
@@ -86,6 +106,10 @@ export async function runHookChecker(options) {
|
|
|
86
106
|
if (!resolved) {
|
|
87
107
|
continue;
|
|
88
108
|
}
|
|
109
|
+
// Respect rigour.yml ignore patterns
|
|
110
|
+
if (isIgnored(resolved.relPath, ignorePatterns)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
89
113
|
const fileFailures = checkFile(resolved.content, resolved.relPath, cwd, config);
|
|
90
114
|
failures.push(...fileFailures);
|
|
91
115
|
}
|
|
@@ -222,6 +246,33 @@ function checkCommandInjection(line, i, relPath, failures) {
|
|
|
222
246
|
});
|
|
223
247
|
}
|
|
224
248
|
}
|
|
249
|
+
// ── Protected Paths Enforcement (real-time) ───────────────────────
|
|
250
|
+
/**
|
|
251
|
+
* Block writes to protected paths defined in rigour.yml safety.protected_paths.
|
|
252
|
+
* This runs in real-time via hooks — BEFORE the agent commits the write.
|
|
253
|
+
*/
|
|
254
|
+
function checkProtectedPaths(relPath, config, failures) {
|
|
255
|
+
const protectedPaths = config.gates.safety?.protected_paths ?? [];
|
|
256
|
+
if (protectedPaths.length === 0)
|
|
257
|
+
return;
|
|
258
|
+
const normalizedPath = relPath.replace(/\\/g, '/');
|
|
259
|
+
const matched = protectedPaths.find(pattern => {
|
|
260
|
+
const clean = pattern.replace('/**', '').replace('/*', '');
|
|
261
|
+
if (normalizedPath === clean)
|
|
262
|
+
return true;
|
|
263
|
+
if (clean.endsWith('/'))
|
|
264
|
+
return normalizedPath.startsWith(clean);
|
|
265
|
+
return normalizedPath.startsWith(clean + '/');
|
|
266
|
+
});
|
|
267
|
+
if (matched) {
|
|
268
|
+
failures.push({
|
|
269
|
+
gate: 'file-guard',
|
|
270
|
+
file: relPath,
|
|
271
|
+
message: `BLOCKED: Agent cannot write to protected path "${relPath}" (matches ${matched}). CI/CD, docs, and config files require human review.`,
|
|
272
|
+
severity: 'critical',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
225
276
|
// ── Memory & Skills Governance (v4.2+) ────────────────────────────
|
|
226
277
|
/**
|
|
227
278
|
* Simple glob matcher — handles exact paths, `*` (single segment),
|
package/dist/hooks/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface HookConfig {
|
|
|
17
17
|
block_on_failure: boolean;
|
|
18
18
|
}
|
|
19
19
|
/** The fast gates that can run per-file in <200ms */
|
|
20
|
-
export declare const FAST_GATE_IDS: readonly ["governance", "hallucinated-imports", "promise-safety", "security-patterns", "file-size"];
|
|
20
|
+
export declare const FAST_GATE_IDS: readonly ["file-guard", "governance", "hallucinated-imports", "promise-safety", "security-patterns", "file-size"];
|
|
21
21
|
export declare const DEFAULT_HOOK_CONFIG: HookConfig;
|
|
22
22
|
export type FastGateId = typeof FAST_GATE_IDS[number];
|
|
23
23
|
export interface HookCheckerResult {
|
package/dist/hooks/types.js
CHANGED
|
@@ -13,5 +13,11 @@ export declare class SidecarProvider implements InferenceProvider {
|
|
|
13
13
|
private getPlatformKey;
|
|
14
14
|
private getPlatformPackageName;
|
|
15
15
|
private resolveBinaryPath;
|
|
16
|
+
/**
|
|
17
|
+
* Download llama-cli directly from llama.cpp GitHub releases.
|
|
18
|
+
* Extracts the binary to ~/.rigour/bin/llama-cli
|
|
19
|
+
*/
|
|
20
|
+
private downloadLlamaCli;
|
|
21
|
+
/** Legacy: install via npm brain package */
|
|
16
22
|
private installSidecarBinary;
|
|
17
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sidecar Binary Provider — runs inference via pre-compiled llama.cpp binary.
|
|
3
|
-
*
|
|
4
|
-
* Falls back to PATH lookup for development/manual installs.
|
|
3
|
+
* Auto-downloads llama-cli from GitHub releases on first use.
|
|
4
|
+
* Falls back to npm packages or PATH lookup for development/manual installs.
|
|
5
5
|
*/
|
|
6
6
|
import { execFile } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
@@ -13,7 +13,8 @@ import { ensureModel, isModelCached, getModelInfo } from './model-manager.js';
|
|
|
13
13
|
import { ensureExecutableBinary } from './executable.js';
|
|
14
14
|
const execFileAsync = promisify(execFile);
|
|
15
15
|
const SIDECAR_INSTALL_DIR = path.join(os.homedir(), '.rigour', 'sidecar');
|
|
16
|
-
|
|
16
|
+
const BINARY_DIR = path.join(os.homedir(), '.rigour', 'bin');
|
|
17
|
+
/** Platform → npm package mapping (legacy, still checked) */
|
|
17
18
|
const PLATFORM_PACKAGES = {
|
|
18
19
|
'darwin-arm64': '@rigour-labs/brain-darwin-arm64',
|
|
19
20
|
'darwin-x64': '@rigour-labs/brain-darwin-x64',
|
|
@@ -21,6 +22,16 @@ const PLATFORM_PACKAGES = {
|
|
|
21
22
|
'linux-arm64': '@rigour-labs/brain-linux-arm64',
|
|
22
23
|
'win32-x64': '@rigour-labs/brain-win-x64',
|
|
23
24
|
};
|
|
25
|
+
const LLAMA_RELEASE_TAG = 'b5604';
|
|
26
|
+
const LLAMA_RELEASE_BASE = `https://github.com/ggml-org/llama.cpp/releases/download/${LLAMA_RELEASE_TAG}`;
|
|
27
|
+
/** Platform → llama.cpp release asset name */
|
|
28
|
+
const LLAMA_RELEASE_ASSETS = {
|
|
29
|
+
'darwin-arm64': `llama-${LLAMA_RELEASE_TAG}-bin-macos-arm64.zip`,
|
|
30
|
+
'darwin-x64': `llama-${LLAMA_RELEASE_TAG}-bin-macos-x64.zip`,
|
|
31
|
+
'linux-x64': `llama-${LLAMA_RELEASE_TAG}-bin-ubuntu-x64.zip`,
|
|
32
|
+
'win32-x64': `llama-${LLAMA_RELEASE_TAG}-bin-win-cpu-x64.zip`,
|
|
33
|
+
// linux-arm64 not published by llama.cpp — users must build from source or use cloud provider
|
|
34
|
+
};
|
|
24
35
|
export class SidecarProvider {
|
|
25
36
|
name = 'sidecar';
|
|
26
37
|
binaryPath = null;
|
|
@@ -36,26 +47,35 @@ export class SidecarProvider {
|
|
|
36
47
|
return binary !== null;
|
|
37
48
|
}
|
|
38
49
|
async setup(onProgress) {
|
|
39
|
-
const packageName = this.getPlatformPackageName();
|
|
40
50
|
// 1. Check/resolve binary
|
|
41
51
|
this.binaryPath = await this.resolveBinaryPath();
|
|
42
|
-
// Auto-bootstrap
|
|
43
|
-
if (!this.binaryPath
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
52
|
+
// Auto-bootstrap: download llama-cli directly from GitHub releases
|
|
53
|
+
if (!this.binaryPath) {
|
|
54
|
+
const downloaded = await this.downloadLlamaCli(onProgress);
|
|
55
|
+
if (downloaded) {
|
|
46
56
|
this.binaryPath = await this.resolveBinaryPath();
|
|
47
57
|
}
|
|
48
58
|
}
|
|
59
|
+
// Legacy fallback: try npm package install
|
|
60
|
+
if (!this.binaryPath) {
|
|
61
|
+
const packageName = this.getPlatformPackageName();
|
|
62
|
+
if (packageName) {
|
|
63
|
+
const installed = await this.installSidecarBinary(packageName, onProgress);
|
|
64
|
+
if (installed) {
|
|
65
|
+
this.binaryPath = await this.resolveBinaryPath();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
49
69
|
if (!this.binaryPath) {
|
|
50
|
-
onProgress?.('⚠ Inference engine not found.
|
|
51
|
-
|
|
52
|
-
throw new Error(`Sidecar binary not found. Run: npm install ${installHint}`);
|
|
70
|
+
onProgress?.('⚠ Inference engine not found. Rigour will auto-download on next attempt.');
|
|
71
|
+
throw new Error(`Sidecar binary not found. Check network connection and retry.`);
|
|
53
72
|
}
|
|
54
73
|
let executableCheck = ensureExecutableBinary(this.binaryPath);
|
|
55
74
|
// If the discovered binary is not executable, try a managed reinstall once.
|
|
56
|
-
|
|
75
|
+
const retryPackage = this.getPlatformPackageName();
|
|
76
|
+
if (!executableCheck.ok && retryPackage) {
|
|
57
77
|
onProgress?.('⚠ Inference engine is present but not executable. Reinstalling managed sidecar...');
|
|
58
|
-
const installed = await this.installSidecarBinary(
|
|
78
|
+
const installed = await this.installSidecarBinary(retryPackage, onProgress);
|
|
59
79
|
if (installed) {
|
|
60
80
|
const refreshedPath = await this.resolveBinaryPath();
|
|
61
81
|
if (refreshedPath) {
|
|
@@ -172,6 +192,12 @@ export class SidecarProvider {
|
|
|
172
192
|
}
|
|
173
193
|
async resolveBinaryPath() {
|
|
174
194
|
const platformKey = this.getPlatformKey();
|
|
195
|
+
// Strategy 0: Check ~/.rigour/bin/llama-cli (auto-downloaded from GitHub releases)
|
|
196
|
+
const binaryName = os.platform() === 'win32' ? 'llama-cli.exe' : 'llama-cli';
|
|
197
|
+
const autoDownloadedPath = path.join(BINARY_DIR, binaryName);
|
|
198
|
+
if (await fs.pathExists(autoDownloadedPath)) {
|
|
199
|
+
return autoDownloadedPath;
|
|
200
|
+
}
|
|
175
201
|
// Strategy 1: Check @rigour-labs/brain-{platform} optional dependency
|
|
176
202
|
const packageName = PLATFORM_PACKAGES[platformKey];
|
|
177
203
|
if (packageName) {
|
|
@@ -264,8 +290,73 @@ export class SidecarProvider {
|
|
|
264
290
|
}
|
|
265
291
|
return null;
|
|
266
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Download llama-cli directly from llama.cpp GitHub releases.
|
|
295
|
+
* Extracts the binary to ~/.rigour/bin/llama-cli
|
|
296
|
+
*/
|
|
297
|
+
async downloadLlamaCli(onProgress) {
|
|
298
|
+
const platformKey = this.getPlatformKey();
|
|
299
|
+
const assetName = LLAMA_RELEASE_ASSETS[platformKey];
|
|
300
|
+
if (!assetName)
|
|
301
|
+
return false;
|
|
302
|
+
const url = `${LLAMA_RELEASE_BASE}/${assetName}`;
|
|
303
|
+
const zipPath = path.join(BINARY_DIR, assetName);
|
|
304
|
+
const binaryName = os.platform() === 'win32' ? 'llama-cli.exe' : 'llama-cli';
|
|
305
|
+
const destPath = path.join(BINARY_DIR, binaryName);
|
|
306
|
+
// Already downloaded
|
|
307
|
+
if (await fs.pathExists(destPath))
|
|
308
|
+
return true;
|
|
309
|
+
onProgress?.(`⬇ Downloading inference engine (llama.cpp ${LLAMA_RELEASE_TAG})...`);
|
|
310
|
+
try {
|
|
311
|
+
await fs.ensureDir(BINARY_DIR);
|
|
312
|
+
// Download zip
|
|
313
|
+
const response = await fetch(url, { redirect: 'follow' });
|
|
314
|
+
if (!response.ok) {
|
|
315
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
316
|
+
}
|
|
317
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
318
|
+
await fs.writeFile(zipPath, buffer);
|
|
319
|
+
onProgress?.(' Extracting...');
|
|
320
|
+
// Extract llama-cli from zip
|
|
321
|
+
if (os.platform() === 'win32') {
|
|
322
|
+
await execFileAsync('powershell', [
|
|
323
|
+
'-Command',
|
|
324
|
+
`Expand-Archive -Path "${zipPath}" -DestinationPath "${BINARY_DIR}/llama-extract" -Force`,
|
|
325
|
+
], { timeout: 60000 });
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
await execFileAsync('unzip', ['-o', zipPath, '-d', path.join(BINARY_DIR, 'llama-extract')], {
|
|
329
|
+
timeout: 60000,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
// Find llama-cli in extracted files
|
|
333
|
+
const extractDir = path.join(BINARY_DIR, 'llama-extract');
|
|
334
|
+
const llamaBin = await findFileRecursive(extractDir, binaryName);
|
|
335
|
+
if (!llamaBin) {
|
|
336
|
+
throw new Error(`${binaryName} not found in release archive`);
|
|
337
|
+
}
|
|
338
|
+
await fs.copy(llamaBin, destPath);
|
|
339
|
+
if (os.platform() !== 'win32') {
|
|
340
|
+
await fs.chmod(destPath, 0o755);
|
|
341
|
+
}
|
|
342
|
+
// Cleanup
|
|
343
|
+
await fs.remove(zipPath);
|
|
344
|
+
await fs.remove(extractDir);
|
|
345
|
+
onProgress?.(`✓ Inference engine ready (${LLAMA_RELEASE_TAG})`);
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
const reason = typeof error?.message === 'string' ? error.message : 'unknown error';
|
|
350
|
+
onProgress?.(`⚠ Download failed: ${reason}`);
|
|
351
|
+
// Cleanup partial downloads
|
|
352
|
+
await fs.remove(zipPath).catch(() => { });
|
|
353
|
+
await fs.remove(path.join(BINARY_DIR, 'llama-extract')).catch(() => { });
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** Legacy: install via npm brain package */
|
|
267
358
|
async installSidecarBinary(packageName, onProgress) {
|
|
268
|
-
onProgress?.(`⬇
|
|
359
|
+
onProgress?.(`⬇ Trying npm fallback: ${packageName}`);
|
|
269
360
|
try {
|
|
270
361
|
await fs.ensureDir(SIDECAR_INSTALL_DIR);
|
|
271
362
|
await execFileAsync(os.platform() === 'win32' ? 'npm.cmd' : 'npm', ['install', '--no-save', '--no-package-lock', '--prefix', SIDECAR_INSTALL_DIR, packageName], {
|
|
@@ -276,7 +367,7 @@ export class SidecarProvider {
|
|
|
276
367
|
}
|
|
277
368
|
catch (error) {
|
|
278
369
|
const reason = typeof error?.message === 'string' ? error.message : 'unknown install error';
|
|
279
|
-
onProgress?.(`⚠
|
|
370
|
+
onProgress?.(`⚠ npm install failed: ${reason}`);
|
|
280
371
|
return false;
|
|
281
372
|
}
|
|
282
373
|
onProgress?.(`✓ Installed ${packageName}`);
|
|
@@ -286,3 +377,19 @@ export class SidecarProvider {
|
|
|
286
377
|
function quoteCmdArg(value) {
|
|
287
378
|
return `"${value.replace(/"/g, '\\"')}"`;
|
|
288
379
|
}
|
|
380
|
+
/** Recursively find a file by name in a directory */
|
|
381
|
+
async function findFileRecursive(dir, filename) {
|
|
382
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
383
|
+
for (const entry of entries) {
|
|
384
|
+
const fullPath = path.join(dir, entry.name);
|
|
385
|
+
if (entry.isDirectory()) {
|
|
386
|
+
const found = await findFileRecursive(fullPath, filename);
|
|
387
|
+
if (found)
|
|
388
|
+
return found;
|
|
389
|
+
}
|
|
390
|
+
else if (entry.name === filename) {
|
|
391
|
+
return fullPath;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/core",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.7",
|
|
4
4
|
"description": "AI-native quality gate engine with local Bayesian learning. AST analysis, drift detection, Fix Packet generation, and agent self-healing across TypeScript, JavaScript, Python, Go, Ruby, and C#.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://rigour.run",
|
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
"@xenova/transformers": "^2.17.2",
|
|
67
67
|
"sqlite3": "^5.1.7",
|
|
68
68
|
"openai": "^4.104.0",
|
|
69
|
-
"@rigour-labs/brain-darwin-arm64": "5.2.
|
|
70
|
-
"@rigour-labs/brain-
|
|
71
|
-
"@rigour-labs/brain-linux-
|
|
72
|
-
"@rigour-labs/brain-
|
|
73
|
-
"@rigour-labs/brain-
|
|
69
|
+
"@rigour-labs/brain-darwin-arm64": "5.2.7",
|
|
70
|
+
"@rigour-labs/brain-linux-arm64": "5.2.7",
|
|
71
|
+
"@rigour-labs/brain-linux-x64": "5.2.7",
|
|
72
|
+
"@rigour-labs/brain-win-x64": "5.2.7",
|
|
73
|
+
"@rigour-labs/brain-darwin-x64": "5.2.7"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@types/fs-extra": "^11.0.4",
|