@rigour-labs/cli 5.2.9 → 5.3.1

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/cli.js CHANGED
@@ -225,6 +225,9 @@ Examples:
225
225
  $ git diff main..HEAD | rigour review # Review branch changes
226
226
  $ rigour review --diff changes.patch --deep # Review diff file with deep analysis
227
227
  $ git diff | rigour review --ci # CI-friendly review
228
+ $ git diff main..HEAD | rigour review --files src/a.ts,src/b.ts
229
+
230
+ Tip: Use in CI to gate only lines you changed — faster than full rigour check on large repos.
228
231
  `)
229
232
  .action(async (options) => {
230
233
  await reviewCommand(process.cwd(), options);
@@ -261,7 +264,20 @@ Examples:
261
264
  });
262
265
  const hooksCmd = program
263
266
  .command('hooks')
264
- .description('Manage AI coding tool hook integrations');
267
+ .description('Manage AI coding tool hook integrations (file checks + DLP credential scanning)')
268
+ .addHelpText('after', `
269
+ DLP false-positive learning:
270
+ When a hook blocks your prompt incorrectly, teach Rigour once:
271
+ $ rigour hooks check --dlp-allow-last
272
+
273
+ Learned patterns are stored per-project in .rigour/dlp-feedback.json.
274
+ Real secrets (provider API keys, AWS keys) are never learned away.
275
+
276
+ Examples:
277
+ $ rigour hooks init --tool cursor
278
+ $ rigour hooks check --mode dlp --stdin
279
+ $ rigour hooks check --dlp-allow-last
280
+ `);
265
281
  hooksCmd
266
282
  .command('init')
267
283
  .description('Generate hook configs for AI coding tools (Claude, Cursor, Cline, Windsurf)')
@@ -289,15 +305,25 @@ hooksCmd
289
305
  .option('--timeout <ms>', 'Timeout in milliseconds (default: 5000)')
290
306
  .option('--mode <mode>', 'Check mode: "check" (default) or "dlp" (credential scanning)')
291
307
  .option('--agent <name>', 'Agent name for DLP audit trail (e.g., cursor, claude)')
308
+ .option('--dlp-allow-last', 'Record last DLP block as learned false positives (hook feedback)')
292
309
  .addHelpText('after', `
293
310
  Examples:
294
311
  $ rigour hooks check --files src/app.ts
295
312
  $ rigour hooks check --files src/a.ts,src/b.ts --block
296
313
  $ echo '{"file_path":"src/app.ts"}' | rigour hooks check --stdin
297
314
  $ echo 'AWS_SECRET=AKIA...' | rigour hooks check --mode dlp --stdin
315
+ $ rigour hooks check --dlp-allow-last
316
+
317
+ DLP learning:
318
+ After a false-positive block, run --dlp-allow-last to store the detection shape
319
+ in .rigour/dlp-feedback.json. Future scans allow matching patterns on the same line.
320
+ Provider keys and high-confidence secrets are never learned away.
298
321
  `)
299
322
  .action(async (options) => {
300
- await hooksCheckCommand(process.cwd(), options);
323
+ await hooksCheckCommand(process.cwd(), {
324
+ ...options,
325
+ dlpAllowLast: options.dlpAllowLast,
326
+ });
301
327
  });
302
328
  // Settings management (like Claude Code's settings.json)
303
329
  const settingsCmd = program
@@ -9,9 +9,16 @@ export function guideCommand() {
9
9
  console.log(chalk.yellow(' • Fix Packet v2') + chalk.dim(': Structured diagnostics fed directly into AI agents.'));
10
10
  console.log(chalk.yellow(' • File Guard') + chalk.dim(': Protects critical paths from agent modification (max files changed).'));
11
11
  console.log(chalk.yellow(' • Security Patterns') + chalk.dim(': Detects XSS, SQL injection, hardcoded secrets, command injection (enabled by default).'));
12
+ console.log(chalk.yellow(' • DLP Hooks') + chalk.dim(': Scans prompts for credentials before they reach the model. Learns from false positives over time.'));
12
13
  console.log(chalk.yellow(' • Strategic Guardians') + chalk.dim(': Dependency and Architectural boundary enforcement.\n'));
14
+ console.log(chalk.bold('Hooks & DLP Learning:'));
15
+ console.log(chalk.dim(' 1. Run ') + chalk.cyan('rigour hooks init') + chalk.dim(' to wire Cursor/Claude/Cline/Windsurf hooks.'));
16
+ console.log(chalk.dim(' 2. When DLP blocks a prompt falsely, run ') + chalk.cyan('rigour hooks check --dlp-allow-last'));
17
+ console.log(chalk.dim(' to teach Rigour that pattern is safe (stored in ') + chalk.cyan('.rigour/dlp-feedback.json') + chalk.dim(').'));
18
+ console.log(chalk.dim(' 3. Real provider keys (OpenAI, AWS, etc.) still block — learning only relaxes generic shapes.\n'));
13
19
  console.log(chalk.bold('Workflow Integration:'));
14
20
  console.log(chalk.green(' • Cursor') + chalk.dim(': Add the MCP server or use the ') + chalk.cyan('.cursor/rules/rigour.mdc') + chalk.dim(' handshake.'));
15
- console.log(chalk.green(' • CI/CD') + chalk.dim(': Use ') + chalk.cyan('rigour check --ci') + chalk.dim(' to fail PRs that violate quality gates.\n'));
21
+ console.log(chalk.green(' • CI/CD') + chalk.dim(': Use ') + chalk.cyan('rigour check --ci') + chalk.dim(' to fail PRs that violate quality gates.'));
22
+ console.log(chalk.green(' • PR Review') + chalk.dim(': Pipe diffs through ') + chalk.cyan('rigour review') + chalk.dim(' to gate only changed lines.\n'));
16
23
  console.log(chalk.dim('For more detailed docs, visit: ') + chalk.underline('https://github.com/erashu212/rigour/docs\n'));
17
24
  }
@@ -30,6 +30,8 @@ export interface HooksCheckOptions {
30
30
  mode?: 'check' | 'dlp';
31
31
  /** Agent name for audit trail (DLP mode) */
32
32
  agent?: string;
33
+ /** Record last DLP block detections as learned false positives (hook feedback) */
34
+ dlpAllowLast?: boolean;
33
35
  }
34
36
  export declare function hooksInitCommand(cwd: string, options?: HooksOptions): Promise<void>;
35
37
  export declare function hooksCheckCommand(cwd: string, options?: HooksCheckOptions): Promise<void>;
@@ -17,7 +17,7 @@ import fs from 'fs-extra';
17
17
  import path from 'path';
18
18
  import chalk from 'chalk';
19
19
  import { randomUUID } from 'crypto';
20
- import { runHookChecker, scanInputForCredentials, formatDLPAlert, createDLPAuditEntry } from '@rigour-labs/core';
20
+ import { runHookChecker, scanInputForCredentials, formatDLPAlert, createDLPAuditEntry, writeDLPBlockManifest, allowLastDLPBlock } from '@rigour-labs/core';
21
21
  // ── Studio event logging ─────────────────────────────────────────────
22
22
  const MAX_EVENT_LOG_LINES = 2000;
23
23
  async function logStudioEvent(cwd, event) {
@@ -499,6 +499,12 @@ function extractCursorPromptText(payload) {
499
499
  return '';
500
500
  }
501
501
  export async function hooksCheckCommand(cwd, options = {}) {
502
+ // ── Learn from last DLP false positive (hook feedback loop) ──
503
+ if (options.dlpAllowLast) {
504
+ const count = await allowLastDLPBlock(cwd, 'hook');
505
+ process.stdout.write(JSON.stringify({ learned: count, message: `Recorded ${count} detection(s) as false positives` }));
506
+ return;
507
+ }
502
508
  // ── DLP Mode: Scan text for credentials ──────────────────
503
509
  if (options.mode === 'dlp') {
504
510
  let rawInput = options.stdin
@@ -528,6 +534,8 @@ export async function hooksCheckCommand(cwd, options = {}) {
528
534
  const result = scanInputForCredentials(textToScan, {
529
535
  enabled: true,
530
536
  block_on_detection: options.block ?? true,
537
+ cwd,
538
+ use_learned_feedback: true,
531
539
  });
532
540
  // Return Cursor-compatible format if detected as Cursor hook
533
541
  if (cursorMode) {
@@ -537,7 +545,7 @@ export async function hooksCheckCommand(cwd, options = {}) {
537
545
  .join('\n');
538
546
  process.stdout.write(JSON.stringify({
539
547
  continue: false,
540
- user_message: `🛑 Rigour DLP: ${result.detections.length} credential(s) detected in your prompt:\n${messages}\n\nReplace with environment variable references before submitting.`,
548
+ user_message: `🛑 Rigour DLP: ${result.detections.length} credential(s) detected in your prompt:\n${messages}\n\nReplace with environment variable references before submitting.\n\nIf this is a false positive, run: rigour hooks check --dlp-allow-last`,
541
549
  }));
542
550
  }
543
551
  else {
@@ -560,6 +568,12 @@ export async function hooksCheckCommand(cwd, options = {}) {
560
568
  // Silent
561
569
  }
562
570
  if (result.status === 'blocked') {
571
+ try {
572
+ await writeDLPBlockManifest(cwd, result.detections, textToScan);
573
+ }
574
+ catch {
575
+ // best-effort
576
+ }
563
577
  process.exitCode = 2;
564
578
  }
565
579
  }
@@ -298,6 +298,19 @@ npx @rigour-labs/cli check # Run quality gates (must PASS before task is done)
298
298
  npx @rigour-labs/cli explain # Explain failures
299
299
  \`\`\`
300
300
 
301
+ ## Context Efficiency Protocol
302
+
303
+ Follow this workflow to minimize token usage without compromising quality:
304
+
305
+ 1. \`rigour_recall\` — load project memory at session start
306
+ 2. \`rigour_index\` — if the pattern index is missing or stale
307
+ 3. \`rigour_agent_register\` — claim a bounded task scope
308
+ 4. \`rigour_context_scope\` — get minimal file list before reading source files
309
+ 5. \`rigour_check_pattern\` — verify no reinvention before writing new code
310
+ 6. Work — only touch files in the scoped edit set
311
+ 7. \`rigour_checkpoint\` — every 30 minutes or before handoff
312
+ 8. \`rigour_check\` — quality gate unchanged (must PASS before done)
313
+
301
314
  ${ruleContent}`;
302
315
  if (!(await fs.pathExists(agentsPath)) || options.force) {
303
316
  await fs.writeFile(agentsPath, agentsContent);
@@ -363,6 +363,139 @@ async function setupApiAndLaunch(apiPort, studioPort, eventsPath, cwd, studioPro
363
363
  res.end(JSON.stringify({ totalScans: 0 }));
364
364
  }
365
365
  }
366
+ else if (url.pathname === '/api/context-stats') {
367
+ try {
368
+ const { getTaskContextStats } = await import('@rigour-labs/core');
369
+ const taskId = url.searchParams.get('taskId') || undefined;
370
+ const stats = await getTaskContextStats(taskId, cwd);
371
+ res.writeHead(200, { 'Content-Type': 'application/json' });
372
+ res.end(JSON.stringify(stats));
373
+ }
374
+ catch (e) {
375
+ res.writeHead(500);
376
+ res.end(JSON.stringify({ error: e.message }));
377
+ }
378
+ }
379
+ else if (url.pathname === '/api/task-cost') {
380
+ try {
381
+ const { getTaskCostStats } = await import('@rigour-labs/core');
382
+ const taskId = url.searchParams.get('taskId') || undefined;
383
+ const costStats = await getTaskCostStats(taskId, cwd);
384
+ res.writeHead(200, { 'Content-Type': 'application/json' });
385
+ res.end(JSON.stringify(costStats));
386
+ }
387
+ catch (e) {
388
+ res.writeHead(500);
389
+ res.end(JSON.stringify({ error: e.message }));
390
+ }
391
+ }
392
+ else if (url.pathname === '/api/cache-stats') {
393
+ try {
394
+ const { getCacheStats } = await import('@rigour-labs/core');
395
+ const stats = await getCacheStats(cwd);
396
+ res.writeHead(200, { 'Content-Type': 'application/json' });
397
+ res.end(JSON.stringify(stats));
398
+ }
399
+ catch (e) {
400
+ res.writeHead(500);
401
+ res.end(JSON.stringify({ error: e.message }));
402
+ }
403
+ }
404
+ else if (url.pathname === '/api/context-explain') {
405
+ try {
406
+ const { explainContext } = await import('@rigour-labs/core');
407
+ const target = url.searchParams.get('target') || 'all';
408
+ const taskId = url.searchParams.get('taskId') || undefined;
409
+ const explanation = await explainContext(target, taskId, cwd);
410
+ res.writeHead(200, { 'Content-Type': 'application/json' });
411
+ res.end(JSON.stringify(explanation));
412
+ }
413
+ catch (e) {
414
+ res.writeHead(500);
415
+ res.end(JSON.stringify({ error: e.message }));
416
+ }
417
+ }
418
+ else if (url.pathname === '/api/context-scope') {
419
+ try {
420
+ const { getContextScopeSummary } = await import('@rigour-labs/core');
421
+ const summary = await getContextScopeSummary(cwd);
422
+ res.writeHead(200, { 'Content-Type': 'application/json' });
423
+ res.end(JSON.stringify(summary));
424
+ }
425
+ catch (e) {
426
+ res.writeHead(500);
427
+ res.end(JSON.stringify({ error: e.message }));
428
+ }
429
+ }
430
+ else if (url.pathname === '/api/checkpoint-metrics') {
431
+ try {
432
+ const { getCheckpointSummary } = await import('@rigour-labs/core');
433
+ const taskId = url.searchParams.get('taskId') || undefined;
434
+ const summary = await getCheckpointSummary(taskId, cwd);
435
+ res.writeHead(200, { 'Content-Type': 'application/json' });
436
+ res.end(JSON.stringify(summary));
437
+ }
438
+ catch (e) {
439
+ res.writeHead(500);
440
+ res.end(JSON.stringify({ error: e.message }));
441
+ }
442
+ }
443
+ else if (url.pathname === '/api/cursor-api-key/status') {
444
+ try {
445
+ const { getCursorApiKey } = await import('@rigour-labs/core');
446
+ const configured = Boolean(getCursorApiKey());
447
+ res.writeHead(200, { 'Content-Type': 'application/json' });
448
+ res.end(JSON.stringify({ configured }));
449
+ }
450
+ catch (e) {
451
+ res.writeHead(500);
452
+ res.end(JSON.stringify({ error: e.message }));
453
+ }
454
+ }
455
+ else if (url.pathname === '/api/cursor-api-key' && req.method === 'POST') {
456
+ let body = '';
457
+ req.on('data', chunk => body += chunk);
458
+ req.on('end', async () => {
459
+ try {
460
+ const { apiKey } = JSON.parse(body || '{}');
461
+ if (!apiKey || typeof apiKey !== 'string' || !apiKey.trim()) {
462
+ res.writeHead(400, { 'Content-Type': 'application/json' });
463
+ res.end(JSON.stringify({ error: 'Missing apiKey' }));
464
+ return;
465
+ }
466
+ const { updateCursorApiKey } = await import('@rigour-labs/core');
467
+ updateCursorApiKey(apiKey.trim());
468
+ res.writeHead(200, { 'Content-Type': 'application/json' });
469
+ res.end(JSON.stringify({ success: true, configured: true }));
470
+ }
471
+ catch (e) {
472
+ res.writeHead(500);
473
+ res.end(JSON.stringify({ error: e.message }));
474
+ }
475
+ });
476
+ }
477
+ else if (url.pathname === '/api/import-cursor-usage' && req.method === 'POST') {
478
+ let body = '';
479
+ req.on('data', chunk => body += chunk);
480
+ req.on('end', async () => {
481
+ try {
482
+ const { importCursorUsageCsv, importCursorUsageJson } = await import('@rigour-labs/core');
483
+ let importedCount = 0;
484
+ if (body.trim().startsWith('{') || body.trim().startsWith('[')) {
485
+ importedCount = await importCursorUsageJson(JSON.parse(body), cwd);
486
+ }
487
+ else {
488
+ importedCount = await importCursorUsageCsv(body, cwd);
489
+ }
490
+ res.writeHead(200, { 'Content-Type': 'application/json' });
491
+ res.end(JSON.stringify({ success: true, importedCount }));
492
+ }
493
+ catch (e) {
494
+ res.writeHead(500);
495
+ res.end(JSON.stringify({ error: e.message }));
496
+ }
497
+ });
498
+ }
366
499
  else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
367
500
  let body = '';
368
501
  req.on('data', chunk => body += chunk);
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
1
  {
2
- "name": "@rigour-labs/cli",
3
- "version": "5.2.9",
4
- "description": "AI-native quality gates with local LLM analysis. Forces AI agents (Claude, Cursor, Copilot, Cline, Windsurf) to meet engineering standards. Bayesian Brain learns your codebase. Zero config: npx rigour-scan.",
5
- "license": "MIT",
6
- "homepage": "https://rigour.run",
7
- "keywords": [
8
- "ai",
9
- "llm",
10
- "ai-code-quality",
11
- "ai-agent",
12
- "quality-gates",
13
- "cli",
14
- "linter",
15
- "static-analysis",
16
- "security",
17
- "vibe-coding",
18
- "claude",
19
- "cursor",
20
- "copilot",
21
- "cline",
22
- "windsurf",
23
- "mcp",
24
- "code-review",
25
- "ci-cd",
26
- "fix-packets",
27
- "agent-governance"
28
- ],
29
- "type": "module",
30
- "bin": {
31
- "rigour": "dist/cli.js"
32
- },
33
- "files": [
34
- "dist",
35
- "studio-dist",
36
- "templates"
37
- ],
38
- "repository": {
39
- "type": "git",
40
- "url": "https://github.com/rigour-labs/rigour"
41
- },
42
- "publishConfig": {
43
- "access": "public",
44
- "provenance": true
45
- },
46
- "dependencies": {
47
- "chalk": "^5.3.0",
48
- "commander": "^12.0.0",
49
- "cosmiconfig": "^9.0.0",
50
- "execa": "^8.0.1",
51
- "fs-extra": "^11.2.0",
52
- "globby": "^14.0.1",
53
- "inquirer": "9.2.16",
54
- "ora": "^8.0.1",
55
- "yaml": "^2.8.2",
56
- "@rigour-labs/core": "5.2.9"
57
- },
58
- "devDependencies": {
59
- "@types/fs-extra": "^11.0.4",
60
- "@types/inquirer": "9.0.7",
61
- "@types/node": "^25.0.3"
62
- },
63
- "scripts": {
64
- "build": "tsc && pnpm bundle-studio",
65
- "bundle-studio": "node scripts/bundle-studio.js",
66
- "test": "vitest run"
67
- }
68
- }
2
+ "name": "@rigour-labs/cli",
3
+ "version": "5.3.1",
4
+ "description": "AI-native quality gates with local LLM analysis. Forces AI agents (Claude, Cursor, Copilot, Cline, Windsurf) to meet engineering standards. Bayesian Brain learns your codebase. Zero config: npx rigour-scan.",
5
+ "license": "MIT",
6
+ "homepage": "https://rigour.run",
7
+ "keywords": [
8
+ "ai",
9
+ "llm",
10
+ "ai-code-quality",
11
+ "ai-agent",
12
+ "quality-gates",
13
+ "cli",
14
+ "linter",
15
+ "static-analysis",
16
+ "security",
17
+ "vibe-coding",
18
+ "claude",
19
+ "cursor",
20
+ "copilot",
21
+ "cline",
22
+ "windsurf",
23
+ "mcp",
24
+ "code-review",
25
+ "ci-cd",
26
+ "fix-packets",
27
+ "agent-governance"
28
+ ],
29
+ "type": "module",
30
+ "bin": {
31
+ "rigour": "dist/cli.js"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "studio-dist",
36
+ "templates"
37
+ ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/rigour-labs/rigour"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "provenance": true
45
+ },
46
+ "scripts": {
47
+ "build": "tsc && pnpm bundle-studio",
48
+ "bundle-studio": "node scripts/bundle-studio.js",
49
+ "test": "vitest run"
50
+ },
51
+ "dependencies": {
52
+ "@rigour-labs/core": "workspace:*",
53
+ "chalk": "^5.3.0",
54
+ "commander": "^12.0.0",
55
+ "cosmiconfig": "^9.0.0",
56
+ "execa": "^8.0.1",
57
+ "fs-extra": "^11.2.0",
58
+ "globby": "^14.0.1",
59
+ "inquirer": "9.2.16",
60
+ "ora": "^8.0.1",
61
+ "yaml": "^2.8.2"
62
+ },
63
+ "devDependencies": {
64
+ "@types/fs-extra": "^11.0.4",
65
+ "@types/inquirer": "9.0.7",
66
+ "@types/node": "^25.0.3"
67
+ }
68
+ }