@rigour-labs/core 4.0.4 → 4.1.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.
Files changed (49) hide show
  1. package/dist/gates/ast-handlers/typescript.js +39 -12
  2. package/dist/gates/ast-handlers/universal.js +9 -3
  3. package/dist/gates/ast.js +15 -1
  4. package/dist/gates/ast.test.d.ts +1 -0
  5. package/dist/gates/ast.test.js +112 -0
  6. package/dist/gates/content.d.ts +5 -0
  7. package/dist/gates/content.js +66 -7
  8. package/dist/gates/content.test.d.ts +1 -0
  9. package/dist/gates/content.test.js +73 -0
  10. package/dist/gates/context-window-artifacts.d.ts +1 -0
  11. package/dist/gates/context-window-artifacts.js +10 -3
  12. package/dist/gates/context.d.ts +1 -0
  13. package/dist/gates/context.js +29 -8
  14. package/dist/gates/deep-analysis.js +2 -2
  15. package/dist/gates/deprecated-apis.d.ts +1 -0
  16. package/dist/gates/deprecated-apis.js +15 -2
  17. package/dist/gates/hallucinated-imports.d.ts +14 -0
  18. package/dist/gates/hallucinated-imports.js +267 -60
  19. package/dist/gates/hallucinated-imports.test.js +164 -1
  20. package/dist/gates/inconsistent-error-handling.d.ts +1 -0
  21. package/dist/gates/inconsistent-error-handling.js +12 -1
  22. package/dist/gates/phantom-apis.d.ts +2 -0
  23. package/dist/gates/phantom-apis.js +28 -3
  24. package/dist/gates/phantom-apis.test.js +14 -0
  25. package/dist/gates/promise-safety.d.ts +2 -0
  26. package/dist/gates/promise-safety.js +31 -9
  27. package/dist/gates/runner.js +8 -2
  28. package/dist/gates/runner.test.d.ts +1 -0
  29. package/dist/gates/runner.test.js +65 -0
  30. package/dist/gates/security-patterns.d.ts +1 -0
  31. package/dist/gates/security-patterns.js +22 -6
  32. package/dist/gates/security-patterns.test.js +18 -0
  33. package/dist/hooks/templates.d.ts +1 -1
  34. package/dist/hooks/templates.js +12 -12
  35. package/dist/inference/executable.d.ts +6 -0
  36. package/dist/inference/executable.js +29 -0
  37. package/dist/inference/executable.test.d.ts +1 -0
  38. package/dist/inference/executable.test.js +41 -0
  39. package/dist/inference/model-manager.d.ts +3 -1
  40. package/dist/inference/model-manager.js +76 -8
  41. package/dist/inference/model-manager.test.d.ts +1 -0
  42. package/dist/inference/model-manager.test.js +24 -0
  43. package/dist/inference/sidecar-provider.d.ts +1 -0
  44. package/dist/inference/sidecar-provider.js +124 -31
  45. package/dist/services/context-engine.js +1 -1
  46. package/dist/templates/universal-config.js +3 -3
  47. package/dist/types/index.js +3 -3
  48. package/dist/utils/scanner.js +6 -0
  49. package/package.json +7 -2
@@ -4,21 +4,74 @@
4
4
  */
5
5
  import path from 'path';
6
6
  import fs from 'fs-extra';
7
+ import { createHash } from 'crypto';
7
8
  import { RIGOUR_DIR } from '../storage/db.js';
8
9
  import { MODELS } from './types.js';
9
10
  const MODELS_DIR = path.join(RIGOUR_DIR, 'models');
11
+ const SHA256_RE = /^[a-f0-9]{64}$/i;
12
+ function getModelMetadataPath(tier) {
13
+ return path.join(MODELS_DIR, MODELS[tier].filename + '.meta.json');
14
+ }
15
+ function isValidMetadata(raw) {
16
+ return !!raw &&
17
+ typeof raw.sha256 === 'string' &&
18
+ SHA256_RE.test(raw.sha256) &&
19
+ typeof raw.sizeBytes === 'number' &&
20
+ typeof raw.verifiedAt === 'string' &&
21
+ typeof raw.sourceUrl === 'string';
22
+ }
23
+ export function extractSha256FromEtag(etag) {
24
+ if (!etag)
25
+ return null;
26
+ const normalized = etag.replace(/^W\//i, '').replace(/^"+|"+$/g, '').trim();
27
+ return SHA256_RE.test(normalized) ? normalized.toLowerCase() : null;
28
+ }
29
+ export async function hashFileSha256(filePath) {
30
+ const hash = createHash('sha256');
31
+ const stream = fs.createReadStream(filePath);
32
+ for await (const chunk of stream) {
33
+ hash.update(chunk);
34
+ }
35
+ return hash.digest('hex');
36
+ }
37
+ async function writeModelMetadata(tier, metadata) {
38
+ const metadataPath = getModelMetadataPath(tier);
39
+ await fs.writeJson(metadataPath, metadata, { spaces: 2 });
40
+ }
41
+ async function readModelMetadata(tier) {
42
+ const metadataPath = getModelMetadataPath(tier);
43
+ if (!(await fs.pathExists(metadataPath))) {
44
+ return null;
45
+ }
46
+ try {
47
+ const raw = await fs.readJson(metadataPath);
48
+ return isValidMetadata(raw) ? raw : null;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
10
54
  /**
11
55
  * Check if a model is already downloaded and valid.
12
56
  */
13
- export function isModelCached(tier) {
57
+ export async function isModelCached(tier) {
14
58
  const model = MODELS[tier];
15
59
  const modelPath = path.join(MODELS_DIR, model.filename);
16
- if (!fs.existsSync(modelPath))
60
+ if (!(await fs.pathExists(modelPath)))
17
61
  return false;
18
- // Basic size check (within 10% tolerance)
19
- const stat = fs.statSync(modelPath);
62
+ const metadata = await readModelMetadata(tier);
63
+ if (!metadata)
64
+ return false;
65
+ // Size check + "changed since verification" check.
66
+ const stat = await fs.stat(modelPath);
20
67
  const tolerance = model.sizeBytes * 0.1;
21
- return stat.size > model.sizeBytes - tolerance;
68
+ if (stat.size <= model.sizeBytes - tolerance)
69
+ return false;
70
+ if (metadata.sizeBytes !== stat.size)
71
+ return false;
72
+ if (new Date(metadata.verifiedAt).getTime() < stat.mtimeMs)
73
+ return false;
74
+ return true;
22
75
  }
23
76
  /**
24
77
  * Get the path to a cached model.
@@ -42,7 +95,7 @@ export async function downloadModel(tier, onProgress) {
42
95
  const tempPath = destPath + '.download';
43
96
  fs.ensureDirSync(MODELS_DIR);
44
97
  // Already cached
45
- if (isModelCached(tier)) {
98
+ if (await isModelCached(tier)) {
46
99
  onProgress?.(`Model ${model.name} already cached`, 100);
47
100
  return destPath;
48
101
  }
@@ -52,18 +105,22 @@ export async function downloadModel(tier, onProgress) {
52
105
  if (!response.ok) {
53
106
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
54
107
  }
108
+ const expectedSha256 = extractSha256FromEtag(response.headers.get('etag'));
55
109
  const contentLength = parseInt(response.headers.get('content-length') || '0', 10);
56
110
  const reader = response.body?.getReader();
57
111
  if (!reader)
58
112
  throw new Error('No response body');
59
113
  const writeStream = fs.createWriteStream(tempPath);
114
+ const hash = createHash('sha256');
60
115
  let downloaded = 0;
61
116
  let lastProgressPercent = 0;
62
117
  while (true) {
63
118
  const { done, value } = await reader.read();
64
119
  if (done)
65
120
  break;
66
- writeStream.write(Buffer.from(value));
121
+ const chunk = Buffer.from(value);
122
+ writeStream.write(chunk);
123
+ hash.update(chunk);
67
124
  downloaded += value.length;
68
125
  if (contentLength > 0) {
69
126
  const percent = Math.round((downloaded / contentLength) * 100);
@@ -78,8 +135,19 @@ export async function downloadModel(tier, onProgress) {
78
135
  writeStream.on('finish', resolve);
79
136
  writeStream.on('error', reject);
80
137
  });
138
+ const actualSha256 = hash.digest('hex');
139
+ if (expectedSha256 && actualSha256 !== expectedSha256) {
140
+ throw new Error(`Model checksum mismatch for ${model.name}: expected ${expectedSha256}, got ${actualSha256}`);
141
+ }
81
142
  // Atomic rename
82
143
  fs.renameSync(tempPath, destPath);
144
+ await writeModelMetadata(tier, {
145
+ sha256: actualSha256,
146
+ sizeBytes: downloaded,
147
+ verifiedAt: new Date().toISOString(),
148
+ sourceUrl: model.url,
149
+ sourceEtag: response.headers.get('etag') || undefined,
150
+ });
83
151
  onProgress?.(`Model ${model.name} ready`, 100);
84
152
  return destPath;
85
153
  }
@@ -93,7 +161,7 @@ export async function downloadModel(tier, onProgress) {
93
161
  * Ensure a model is available, downloading if needed.
94
162
  */
95
163
  export async function ensureModel(tier, onProgress) {
96
- if (isModelCached(tier)) {
164
+ if (await isModelCached(tier)) {
97
165
  return getModelPath(tier);
98
166
  }
99
167
  return downloadModel(tier, onProgress);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'fs-extra';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { extractSha256FromEtag, hashFileSha256 } from './model-manager.js';
6
+ describe('model manager integrity helpers', () => {
7
+ it('extracts sha256 digest from a strong ETag', () => {
8
+ const digest = 'a'.repeat(64);
9
+ expect(extractSha256FromEtag(`"${digest}"`)).toBe(digest);
10
+ expect(extractSha256FromEtag(`W/"${digest}"`)).toBe(digest);
11
+ });
12
+ it('returns null for non-sha ETags', () => {
13
+ expect(extractSha256FromEtag('"not-a-digest"')).toBeNull();
14
+ expect(extractSha256FromEtag(null)).toBeNull();
15
+ });
16
+ it('hashes file contents with sha256', async () => {
17
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'rigour-model-hash-'));
18
+ const filePath = path.join(dir, 'sample.gguf');
19
+ await fs.writeFile(filePath, 'rigour-model-check');
20
+ const digest = await hashFileSha256(filePath);
21
+ expect(digest).toBe('e123266ea4b37a81948a0a844dd58eddfc81737aa6fdf9dafc818fd23bae75f0');
22
+ await fs.remove(dir);
23
+ });
24
+ });
@@ -11,6 +11,7 @@ export declare class SidecarProvider implements InferenceProvider {
11
11
  analyze(prompt: string, options?: InferenceOptions): Promise<string>;
12
12
  dispose(): void;
13
13
  private getPlatformKey;
14
+ private getPlatformPackageName;
14
15
  private resolveBinaryPath;
15
16
  private installSidecarBinary;
16
17
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Sidecar Binary Provider — runs inference via pre-compiled llama.cpp binary.
3
- * Binary ships as @rigour/brain-{platform} optional npm dependency.
3
+ * Binary ships as @rigour-labs/brain-{platform} optional npm dependency.
4
4
  * Falls back to PATH lookup for development/manual installs.
5
5
  */
6
6
  import { execFile } from 'child_process';
@@ -10,14 +10,16 @@ import os from 'os';
10
10
  import fs from 'fs-extra';
11
11
  import { createRequire } from 'module';
12
12
  import { ensureModel, isModelCached, getModelInfo } from './model-manager.js';
13
+ import { ensureExecutableBinary } from './executable.js';
13
14
  const execFileAsync = promisify(execFile);
15
+ const SIDECAR_INSTALL_DIR = path.join(os.homedir(), '.rigour', 'sidecar');
14
16
  /** Platform → npm package mapping */
15
17
  const PLATFORM_PACKAGES = {
16
- 'darwin-arm64': '@rigour/brain-darwin-arm64',
17
- 'darwin-x64': '@rigour/brain-darwin-x64',
18
- 'linux-x64': '@rigour/brain-linux-x64',
19
- 'linux-arm64': '@rigour/brain-linux-arm64',
20
- 'win32-x64': '@rigour/brain-win-x64',
18
+ 'darwin-arm64': '@rigour-labs/brain-darwin-arm64',
19
+ 'darwin-x64': '@rigour-labs/brain-darwin-x64',
20
+ 'linux-x64': '@rigour-labs/brain-linux-x64',
21
+ 'linux-arm64': '@rigour-labs/brain-linux-arm64',
22
+ 'win32-x64': '@rigour-labs/brain-win-x64',
21
23
  };
22
24
  export class SidecarProvider {
23
25
  name = 'sidecar';
@@ -34,11 +36,10 @@ export class SidecarProvider {
34
36
  return binary !== null;
35
37
  }
36
38
  async setup(onProgress) {
37
- const platformKey = this.getPlatformKey();
38
- const packageName = PLATFORM_PACKAGES[platformKey];
39
+ const packageName = this.getPlatformPackageName();
39
40
  // 1. Check/resolve binary
40
41
  this.binaryPath = await this.resolveBinaryPath();
41
- // Auto-bootstrap local sidecar once before failing.
42
+ // Auto-bootstrap local sidecar when missing.
42
43
  if (!this.binaryPath && packageName) {
43
44
  const installed = await this.installSidecarBinary(packageName, onProgress);
44
45
  if (installed) {
@@ -46,12 +47,32 @@ export class SidecarProvider {
46
47
  }
47
48
  }
48
49
  if (!this.binaryPath) {
49
- onProgress?.('⚠ Inference engine not found. Install @rigour/brain-* or add llama-cli to PATH');
50
- throw new Error('Sidecar binary not found. Run: npm install @rigour/brain-' + platformKey);
50
+ onProgress?.('⚠ Inference engine not found. Install @rigour-labs/brain-* or add llama-cli to PATH');
51
+ const installHint = packageName || `@rigour-labs/brain-${this.getPlatformKey()}`;
52
+ throw new Error(`Sidecar binary not found. Run: npm install ${installHint}`);
53
+ }
54
+ let executableCheck = ensureExecutableBinary(this.binaryPath);
55
+ // If the discovered binary is not executable, try a managed reinstall once.
56
+ if (!executableCheck.ok && packageName) {
57
+ onProgress?.('⚠ Inference engine is present but not executable. Reinstalling managed sidecar...');
58
+ const installed = await this.installSidecarBinary(packageName, onProgress);
59
+ if (installed) {
60
+ const refreshedPath = await this.resolveBinaryPath();
61
+ if (refreshedPath) {
62
+ this.binaryPath = refreshedPath;
63
+ executableCheck = ensureExecutableBinary(this.binaryPath);
64
+ }
65
+ }
66
+ }
67
+ if (!executableCheck.ok) {
68
+ throw new Error(`Sidecar binary is not executable: ${this.binaryPath}. Run: chmod +x "${this.binaryPath}"`);
69
+ }
70
+ if (executableCheck.fixed) {
71
+ onProgress?.('✓ Fixed execute permission for inference engine');
51
72
  }
52
73
  onProgress?.('✓ Inference engine ready');
53
74
  // 2. Ensure model is downloaded
54
- if (!isModelCached(this.tier)) {
75
+ if (!(await isModelCached(this.tier))) {
55
76
  const modelInfo = getModelInfo(this.tier);
56
77
  onProgress?.(`⬇ Downloading analysis model (${modelInfo.sizeHuman})...`);
57
78
  }
@@ -80,11 +101,50 @@ export class SidecarProvider {
80
101
  args.push('--json');
81
102
  }
82
103
  try {
83
- const { stdout, stderr } = await execFileAsync(this.binaryPath, args, {
104
+ const execOptions = {
84
105
  timeout: options?.timeout || 60000,
85
106
  maxBuffer: 10 * 1024 * 1024, // 10MB
86
107
  env: { ...process.env, LLAMA_LOG_DISABLE: '1' },
87
- });
108
+ };
109
+ const runInference = async () => {
110
+ return process.platform === 'win32' && this.binaryPath.endsWith('.cmd')
111
+ ? await execFileAsync('cmd.exe', ['/d', '/s', '/c', [this.binaryPath, ...args].map(quoteCmdArg).join(' ')], execOptions)
112
+ : await execFileAsync(this.binaryPath, args, execOptions);
113
+ };
114
+ let stdout;
115
+ try {
116
+ ({ stdout } = await runInference());
117
+ }
118
+ catch (error) {
119
+ // One retry path for stale/bad file mode in packaged installs.
120
+ if (error?.code === 'EACCES') {
121
+ const check = ensureExecutableBinary(this.binaryPath);
122
+ if (check.ok) {
123
+ ({ stdout } = await runInference());
124
+ }
125
+ else {
126
+ const packageName = this.getPlatformPackageName();
127
+ if (packageName) {
128
+ const installed = await this.installSidecarBinary(packageName);
129
+ if (installed) {
130
+ const refreshedPath = await this.resolveBinaryPath();
131
+ if (refreshedPath) {
132
+ this.binaryPath = refreshedPath;
133
+ const refreshedCheck = ensureExecutableBinary(this.binaryPath);
134
+ if (refreshedCheck.ok) {
135
+ ({ stdout } = await runInference());
136
+ return stdout.trim();
137
+ }
138
+ }
139
+ }
140
+ }
141
+ throw error;
142
+ }
143
+ }
144
+ else {
145
+ throw error;
146
+ }
147
+ }
88
148
  // llama.cpp sometimes outputs to stderr for diagnostics — ignore
89
149
  return stdout.trim();
90
150
  }
@@ -92,6 +152,9 @@ export class SidecarProvider {
92
152
  if (error.killed) {
93
153
  throw new Error(`Inference timed out after ${(options?.timeout || 60000) / 1000}s`);
94
154
  }
155
+ if (error?.code === 'EACCES') {
156
+ throw new Error(`Inference binary is not executable: ${this.binaryPath}. Run: chmod +x "${this.binaryPath}"`);
157
+ }
95
158
  throw new Error(`Inference failed: ${error.message}`);
96
159
  }
97
160
  }
@@ -103,19 +166,37 @@ export class SidecarProvider {
103
166
  getPlatformKey() {
104
167
  return `${os.platform()}-${os.arch()}`;
105
168
  }
169
+ getPlatformPackageName() {
170
+ const platformKey = this.getPlatformKey();
171
+ return PLATFORM_PACKAGES[platformKey];
172
+ }
106
173
  async resolveBinaryPath() {
107
174
  const platformKey = this.getPlatformKey();
108
- // Strategy 1: Check @rigour/brain-{platform} optional dependency
175
+ // Strategy 1: Check @rigour-labs/brain-{platform} optional dependency
109
176
  const packageName = PLATFORM_PACKAGES[platformKey];
110
177
  if (packageName) {
178
+ // Prefer Rigour-managed sidecar install root first to avoid brittle global/homebrew layouts.
179
+ const managedPath = path.join(SIDECAR_INSTALL_DIR, 'node_modules', ...packageName.split('/'), 'bin', 'rigour-brain');
180
+ const managedCandidates = os.platform() === 'win32'
181
+ ? [managedPath + '.exe', managedPath + '.cmd', managedPath]
182
+ : [managedPath];
183
+ for (const managedBinPath of managedCandidates) {
184
+ if (await fs.pathExists(managedBinPath)) {
185
+ return managedBinPath;
186
+ }
187
+ }
111
188
  try {
112
189
  const require = createRequire(import.meta.url);
113
190
  const pkgJsonPath = require.resolve(path.posix.join(packageName, 'package.json'));
114
191
  const pkgDir = path.dirname(pkgJsonPath);
115
192
  const resolvedBin = path.join(pkgDir, 'bin', 'rigour-brain');
116
- const resolvedBinPath = os.platform() === 'win32' ? resolvedBin + '.exe' : resolvedBin;
117
- if (await fs.pathExists(resolvedBinPath)) {
118
- return resolvedBinPath;
193
+ const resolvedCandidates = os.platform() === 'win32'
194
+ ? [resolvedBin + '.exe', resolvedBin + '.cmd', resolvedBin]
195
+ : [resolvedBin];
196
+ for (const resolvedBinPath of resolvedCandidates) {
197
+ if (await fs.pathExists(resolvedBinPath)) {
198
+ return resolvedBinPath;
199
+ }
119
200
  }
120
201
  }
121
202
  catch {
@@ -134,9 +215,11 @@ export class SidecarProvider {
134
215
  path.join(os.homedir(), '.npm-global', 'lib', 'node_modules', ...packageName.split('/'), 'bin', 'rigour-brain'),
135
216
  ];
136
217
  for (const p of possiblePaths) {
137
- const binPath = os.platform() === 'win32' ? p + '.exe' : p;
138
- if (await fs.pathExists(binPath)) {
139
- return binPath;
218
+ const candidates = os.platform() === 'win32' ? [p + '.exe', p + '.cmd', p] : [p];
219
+ for (const binPath of candidates) {
220
+ if (await fs.pathExists(binPath)) {
221
+ return binPath;
222
+ }
140
223
  }
141
224
  }
142
225
  }
@@ -146,14 +229,19 @@ export class SidecarProvider {
146
229
  }
147
230
  // Strategy 2: Check ~/.rigour/bin/
148
231
  const localBin = path.join(os.homedir(), '.rigour', 'bin', 'rigour-brain');
149
- const localBinPath = os.platform() === 'win32' ? localBin + '.exe' : localBin;
150
- if (await fs.pathExists(localBinPath)) {
151
- return localBinPath;
232
+ const localCandidates = os.platform() === 'win32'
233
+ ? [localBin + '.exe', localBin + '.cmd', localBin]
234
+ : [localBin];
235
+ for (const localBinPath of localCandidates) {
236
+ if (await fs.pathExists(localBinPath)) {
237
+ return localBinPath;
238
+ }
152
239
  }
153
240
  // Strategy 3: Check PATH for llama-cli (llama.cpp CLI)
241
+ const locator = os.platform() === 'win32' ? 'where' : 'which';
154
242
  try {
155
- const { stdout } = await execFileAsync('which', ['llama-cli']);
156
- const llamaPath = stdout.trim();
243
+ const { stdout } = await execFileAsync(locator, ['llama-cli']);
244
+ const llamaPath = stdout.split(/\r?\n/).map(s => s.trim()).find(Boolean) || '';
157
245
  if (llamaPath && await fs.pathExists(llamaPath)) {
158
246
  return llamaPath;
159
247
  }
@@ -165,9 +253,10 @@ export class SidecarProvider {
165
253
  const altNames = ['llama-cli', 'llama', 'main'];
166
254
  for (const name of altNames) {
167
255
  try {
168
- const { stdout } = await execFileAsync('which', [name]);
169
- if (stdout.trim())
170
- return stdout.trim();
256
+ const { stdout } = await execFileAsync(locator, [name]);
257
+ const resolved = stdout.split(/\r?\n/).map(s => s.trim()).find(Boolean);
258
+ if (resolved && await fs.pathExists(resolved))
259
+ return resolved;
171
260
  }
172
261
  catch {
173
262
  // Continue
@@ -178,8 +267,9 @@ export class SidecarProvider {
178
267
  async installSidecarBinary(packageName, onProgress) {
179
268
  onProgress?.(`⬇ Inference engine missing. Attempting automatic install: ${packageName}`);
180
269
  try {
181
- await execFileAsync('npm', ['install', '--no-save', '--no-package-lock', packageName], {
182
- cwd: process.cwd(),
270
+ await fs.ensureDir(SIDECAR_INSTALL_DIR);
271
+ await execFileAsync(os.platform() === 'win32' ? 'npm.cmd' : 'npm', ['install', '--no-save', '--no-package-lock', '--prefix', SIDECAR_INSTALL_DIR, packageName], {
272
+ cwd: SIDECAR_INSTALL_DIR,
183
273
  timeout: 120000,
184
274
  maxBuffer: 10 * 1024 * 1024,
185
275
  });
@@ -193,3 +283,6 @@ export class SidecarProvider {
193
283
  return true;
194
284
  }
195
285
  }
286
+ function quoteCmdArg(value) {
287
+ return `"${value.replace(/"/g, '\\"')}"`;
288
+ }
@@ -47,7 +47,7 @@ export class ContextEngine {
47
47
  anchors,
48
48
  metadata: {
49
49
  scannedFiles,
50
- detectedCasing: 'unknown', // TODO: Implement casing discovery
50
+ detectedCasing: 'unknown', // Placeholder until casing discovery is implemented
51
51
  }
52
52
  };
53
53
  }
@@ -113,9 +113,9 @@ export const UNIVERSAL_CONFIG = {
113
113
  },
114
114
  context_window_artifacts: {
115
115
  enabled: true,
116
- min_file_lines: 100,
117
- degradation_threshold: 0.4,
118
- signals_required: 2,
116
+ min_file_lines: 180,
117
+ degradation_threshold: 0.55,
118
+ signals_required: 4,
119
119
  },
120
120
  promise_safety: {
121
121
  enabled: true,
@@ -125,9 +125,9 @@ export const GatesSchema = z.object({
125
125
  }).optional().default({}),
126
126
  context_window_artifacts: z.object({
127
127
  enabled: z.boolean().optional().default(true),
128
- min_file_lines: z.number().optional().default(100),
129
- degradation_threshold: z.number().min(0).max(1).optional().default(0.4),
130
- signals_required: z.number().optional().default(2),
128
+ min_file_lines: z.number().optional().default(180),
129
+ degradation_threshold: z.number().min(0).max(1).optional().default(0.55),
130
+ signals_required: z.number().optional().default(4),
131
131
  }).optional().default({}),
132
132
  promise_safety: z.object({
133
133
  enabled: z.boolean().optional().default(true),
@@ -6,6 +6,12 @@ export class FileScanner {
6
6
  static DEFAULT_IGNORE = [
7
7
  '**/node_modules/**',
8
8
  '**/dist/**',
9
+ '**/studio-dist/**',
10
+ '**/.next/**',
11
+ '**/coverage/**',
12
+ '**/out/**',
13
+ '**/target/**',
14
+ '**/examples/**',
9
15
  '**/package-lock.json',
10
16
  '**/pnpm-lock.yaml',
11
17
  '**/.git/**',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "4.0.4",
3
+ "version": "4.1.0",
4
4
  "description": "Deterministic quality gate engine for AI-generated code. AST analysis, drift detection, and Fix Packet generation across TypeScript, JavaScript, Python, Go, Ruby, and C#.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://rigour.run",
@@ -58,7 +58,12 @@
58
58
  "@anthropic-ai/sdk": "^0.30.1",
59
59
  "@xenova/transformers": "^2.17.2",
60
60
  "better-sqlite3": "^11.0.0",
61
- "openai": "^4.104.0"
61
+ "openai": "^4.104.0",
62
+ "@rigour-labs/brain-darwin-arm64": "4.1.0",
63
+ "@rigour-labs/brain-darwin-x64": "4.1.0",
64
+ "@rigour-labs/brain-win-x64": "4.1.0",
65
+ "@rigour-labs/brain-linux-x64": "4.1.0",
66
+ "@rigour-labs/brain-linux-arm64": "4.1.0"
62
67
  },
63
68
  "devDependencies": {
64
69
  "@types/better-sqlite3": "^7.6.12",