@rigour-labs/cli 4.3.6 → 5.0.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/commands/check.js +39 -1
- package/dist/commands/demo-display.d.ts +1 -0
- package/dist/commands/demo-display.js +56 -3
- package/dist/commands/studio.js +13 -0
- package/package.json +2 -2
- package/studio-dist/assets/index-C0n6JWm-.js +301 -0
- package/studio-dist/assets/{index-WNovVfFN.css → index-hcvdpk3Y.css} +1 -1
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-D14slH7l.js +0 -296
package/dist/commands/check.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import yaml from 'yaml';
|
|
5
|
-
import { GateRunner, ConfigSchema, recordScore, getScoreTrend, resolveDeepOptions } from '@rigour-labs/core';
|
|
5
|
+
import { GateRunner, ConfigSchema, recordScore, getScoreTrend, resolveDeepOptions, getProvenanceTrends, getQualityTrend } from '@rigour-labs/core';
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
// Exit codes per spec
|
|
@@ -270,6 +270,34 @@ function renderDeepOutput(report, config, options, resolvedDeepMode) {
|
|
|
270
270
|
console.log(chalk.dim(` Model: ${model} (${tier}) ${inferenceSec}`));
|
|
271
271
|
}
|
|
272
272
|
console.log('');
|
|
273
|
+
// ── Temporal Drift Trends ──
|
|
274
|
+
try {
|
|
275
|
+
const driftCwd = process.cwd();
|
|
276
|
+
const trend = getQualityTrend(driftCwd);
|
|
277
|
+
const provTrends = getProvenanceTrends(driftCwd);
|
|
278
|
+
const hasTrends = trend !== 'stable' || provTrends.aiDrift !== 'stable' || provTrends.structural !== 'stable' || provTrends.security !== 'stable';
|
|
279
|
+
if (hasTrends) {
|
|
280
|
+
const trendIcon = trend === 'improving' ? chalk.green('↑') : trend === 'degrading' ? chalk.red('↓') : chalk.dim('→');
|
|
281
|
+
const trendColor = trend === 'improving' ? chalk.green : trend === 'degrading' ? chalk.red : chalk.dim;
|
|
282
|
+
console.log(` ${chalk.bold('Trend:')} ${trendIcon} ${trendColor(trend.toUpperCase())} (Z-score analysis)`);
|
|
283
|
+
if (provTrends.aiDrift !== 'stable') {
|
|
284
|
+
const c = provTrends.aiDrift === 'degrading' ? chalk.red : chalk.green;
|
|
285
|
+
console.log(` ${chalk.dim(' AI Drift:')} ${c(provTrends.aiDrift)} (Z=${provTrends.aiDriftZScore})`);
|
|
286
|
+
}
|
|
287
|
+
if (provTrends.structural !== 'stable') {
|
|
288
|
+
const c = provTrends.structural === 'degrading' ? chalk.red : chalk.green;
|
|
289
|
+
console.log(` ${chalk.dim(' Structural:')} ${c(provTrends.structural)} (Z=${provTrends.structuralZScore})`);
|
|
290
|
+
}
|
|
291
|
+
if (provTrends.security !== 'stable') {
|
|
292
|
+
const c = provTrends.security === 'degrading' ? chalk.red : chalk.green;
|
|
293
|
+
console.log(` ${chalk.dim(' Security:')} ${c(provTrends.security)} (Z=${provTrends.securityZScore})`);
|
|
294
|
+
}
|
|
295
|
+
console.log('');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Not enough historical data — skip silently
|
|
300
|
+
}
|
|
273
301
|
// Categorize findings by provenance
|
|
274
302
|
const deepFailures = report.failures.filter((f) => f.provenance === 'deep-analysis');
|
|
275
303
|
const aiDriftFailures = report.failures.filter((f) => f.provenance === 'ai-drift');
|
|
@@ -386,6 +414,16 @@ function renderStandardOutput(report, config) {
|
|
|
386
414
|
console.log('Severity: ' + parts.join(', ') + '\n');
|
|
387
415
|
}
|
|
388
416
|
}
|
|
417
|
+
// ── Temporal Drift Trends (standard mode) ──
|
|
418
|
+
try {
|
|
419
|
+
const trend = getQualityTrend(process.cwd());
|
|
420
|
+
if (trend !== 'stable') {
|
|
421
|
+
const trendIcon = trend === 'improving' ? chalk.green('↑') : chalk.red('↓');
|
|
422
|
+
const trendColor = trend === 'improving' ? chalk.green : chalk.red;
|
|
423
|
+
console.log(`Trend: ${trendIcon} ${trendColor(trend)} (Z-score)\n`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
catch { /* ignore */ }
|
|
389
427
|
for (const failure of report.failures) {
|
|
390
428
|
const sev = severityIcon(failure.severity);
|
|
391
429
|
const prov = failure.provenance ? chalk.dim(`[${failure.provenance}]`) : '';
|
|
@@ -8,4 +8,5 @@ export declare function printPlantedIssues(): void;
|
|
|
8
8
|
export declare function displayGateResults(report: any, cinematic: boolean): void;
|
|
9
9
|
export declare function printSeverityBreakdown(stats: any): void;
|
|
10
10
|
export declare function printFailure(failure: any): void;
|
|
11
|
+
export declare function displayDriftSummary(): void;
|
|
11
12
|
export declare function printClosing(cinematic: boolean): void;
|
|
@@ -122,6 +122,16 @@ export function displayGateResults(report, cinematic) {
|
|
|
122
122
|
else {
|
|
123
123
|
console.log(chalk.green.bold('✔ PASS — All quality gates satisfied.\n'));
|
|
124
124
|
}
|
|
125
|
+
// Provenance breakdown
|
|
126
|
+
if (stats.provenance_breakdown) {
|
|
127
|
+
const parts = Object.entries(stats.provenance_breakdown)
|
|
128
|
+
.filter(([, count]) => count > 0)
|
|
129
|
+
.map(([prov, count]) => chalk.dim(`${prov}: ${count}`));
|
|
130
|
+
if (parts.length > 0) {
|
|
131
|
+
console.log(chalk.bold(' Provenance: ') + parts.join(' | '));
|
|
132
|
+
console.log('');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
125
135
|
console.log(chalk.dim(`Finished in ${stats.duration_ms}ms\n`));
|
|
126
136
|
}
|
|
127
137
|
export function printSeverityBreakdown(stats) {
|
|
@@ -152,15 +162,58 @@ export function printFailure(failure) {
|
|
|
152
162
|
console.log(chalk.cyan(` ${failure.hint}`));
|
|
153
163
|
}
|
|
154
164
|
}
|
|
165
|
+
// ── Drift Detection Summary (for demo/Nikhil) ──────────────────────
|
|
166
|
+
export function displayDriftSummary() {
|
|
167
|
+
console.log(chalk.bold.cyan('\n ── v5.1 Drift Detection Engine ──\n'));
|
|
168
|
+
console.log(chalk.white(' Seven production-grade detection systems:\n'));
|
|
169
|
+
console.log(chalk.bold(' 1. EWMA Checkpoint Monitoring'));
|
|
170
|
+
console.log(chalk.dim(' Before: Linear regression on 5 points (one outlier = broken)'));
|
|
171
|
+
console.log(chalk.green(' After: EWMA (α=0.3) — 70% history, 30% new → noise-resistant'));
|
|
172
|
+
console.log(chalk.dim(' Detects: sudden drops AND gradual decline separately\n'));
|
|
173
|
+
console.log(chalk.bold(' 2. Z-Score Adaptive Thresholds'));
|
|
174
|
+
console.log(chalk.dim(' Before: Moving window delta (100→108 = "degrading")'));
|
|
175
|
+
console.log(chalk.green(' After: Z-score normalization — size-independent anomaly detection'));
|
|
176
|
+
console.log(chalk.dim(' Per-provenance: AI drift vs structural vs security tracked independently\n'));
|
|
177
|
+
console.log(chalk.bold(' 3. Three-Pass Duplicate Detection'));
|
|
178
|
+
console.log(chalk.dim(' Pass 1: MD5 hash → exact duplicates (O(n), <10ms)'));
|
|
179
|
+
console.log(chalk.dim(' Pass 2: Jaccard on tree-sitter AST node multisets → structural near-duplicates'));
|
|
180
|
+
console.log(chalk.green(' Pass 3: Semantic embedding (all-MiniLM-L6-v2, 384D) → intent-level duplicates'));
|
|
181
|
+
console.log(chalk.dim(' Catches: .find() vs .filter()[0] — same intent, different AST\n'));
|
|
182
|
+
console.log(chalk.bold(' 4. Temporal Drift Engine'));
|
|
183
|
+
console.log(chalk.dim(' Cross-session trend analysis from SQLite brain'));
|
|
184
|
+
console.log(chalk.green(' Per-provenance EWMA streams + monthly rollups + anomaly detection'));
|
|
185
|
+
console.log(chalk.dim(' Answers: "Is AI getting worse?" independently from "Is code quality dropping?"\n'));
|
|
186
|
+
console.log(chalk.bold(' 5. Dependency Bloat Detection'));
|
|
187
|
+
console.log(chalk.dim(' Before: Only checked forbidden package list'));
|
|
188
|
+
console.log(chalk.green(' After: Unused deps + heavy alternatives + duplicate purpose detection'));
|
|
189
|
+
console.log(chalk.dim(' Catches: axios AND got both installed (AI sessions chose different HTTP clients)\n'));
|
|
190
|
+
console.log(chalk.bold(' 6. Style Drift Detection'));
|
|
191
|
+
console.log(chalk.dim(' Before: No style enforcement beyond linters'));
|
|
192
|
+
console.log(chalk.green(' After: Fingerprint naming, error handling, import style, quotes → compare to baseline'));
|
|
193
|
+
console.log(chalk.dim(' Catches: AI switching from camelCase to snake_case mid-project\n'));
|
|
194
|
+
console.log(chalk.bold(' 7. Logic Drift Foundation'));
|
|
195
|
+
console.log(chalk.dim(' Before: No detection of subtle logic changes'));
|
|
196
|
+
console.log(chalk.green(' After: Tracks comparison operators, branch counts, return counts per function'));
|
|
197
|
+
console.log(chalk.dim(' Catches: >= silently became > (off-by-one), return statements added/removed\n'));
|
|
198
|
+
}
|
|
155
199
|
export function printClosing(cinematic) {
|
|
156
200
|
const divider = chalk.bold.cyan('━'.repeat(50));
|
|
201
|
+
// Show drift detection summary in cinematic mode
|
|
202
|
+
if (cinematic) {
|
|
203
|
+
displayDriftSummary();
|
|
204
|
+
}
|
|
157
205
|
console.log(divider);
|
|
158
|
-
console.log(chalk.bold('What Rigour does:'));
|
|
159
|
-
console.log(chalk.dim(' Catches AI drift (hallucinated imports,
|
|
160
|
-
console.log(chalk.dim(' Blocks security issues (hardcoded keys, injection patterns)'));
|
|
206
|
+
console.log(chalk.bold('What Rigour does (27+ gates):'));
|
|
207
|
+
console.log(chalk.dim(' Catches AI drift (hallucinated imports, duplicates, logic mutations)'));
|
|
208
|
+
console.log(chalk.dim(' Blocks security issues (hardcoded keys, injection patterns, DLP)'));
|
|
161
209
|
console.log(chalk.dim(' Enforces structure (file size, complexity, documentation)'));
|
|
210
|
+
console.log(chalk.dim(' Detects dependency bloat (unused, heavy, duplicate purpose)'));
|
|
211
|
+
console.log(chalk.dim(' Monitors style drift (naming, error handling, imports)'));
|
|
212
|
+
console.log(chalk.dim(' Tracks logic mutations (operator changes, branch count shifts)'));
|
|
162
213
|
console.log(chalk.dim(' Generates audit-ready evidence (scores, trends, reports)'));
|
|
163
214
|
console.log(chalk.dim(' Real-time hooks for Claude, Cursor, Cline, Windsurf'));
|
|
215
|
+
console.log(chalk.dim(' Three-pass duplication: MD5 → AST Jaccard → Semantic Embedding'));
|
|
216
|
+
console.log(chalk.dim(' EWMA + Z-score + temporal drift engine (v5.1)'));
|
|
164
217
|
console.log(divider);
|
|
165
218
|
console.log('');
|
|
166
219
|
if (cinematic) {
|
package/dist/commands/studio.js
CHANGED
|
@@ -344,6 +344,19 @@ async function setupApiAndLaunch(apiPort, studioPort, eventsPath, cwd, studioPro
|
|
|
344
344
|
res.end(e.message);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
|
+
else if (url.pathname === '/api/drift') {
|
|
348
|
+
try {
|
|
349
|
+
const { generateTemporalDriftReport } = await import('@rigour-labs/core');
|
|
350
|
+
const report = generateTemporalDriftReport(cwd);
|
|
351
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
352
|
+
res.end(JSON.stringify(report || { totalScans: 0 }));
|
|
353
|
+
}
|
|
354
|
+
catch (e) {
|
|
355
|
+
// SQLite not available or no data
|
|
356
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
357
|
+
res.end(JSON.stringify({ totalScans: 0 }));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
347
360
|
else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
|
|
348
361
|
let body = '';
|
|
349
362
|
req.on('data', chunk => body += chunk);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "CLI quality gates for AI-generated code. Forces AI agents (Claude, Cursor, Copilot) to meet strict engineering standards with PASS/FAIL enforcement.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://rigour.run",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"inquirer": "9.2.16",
|
|
45
45
|
"ora": "^8.0.1",
|
|
46
46
|
"yaml": "^2.8.2",
|
|
47
|
-
"@rigour-labs/core": "
|
|
47
|
+
"@rigour-labs/core": "5.0.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/fs-extra": "^11.0.4",
|