@rigour-labs/cli 2.22.0 → 3.0.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/README.md +14 -1
- package/dist/cli.js +68 -5
- package/dist/commands/check.js +68 -11
- package/dist/commands/demo.d.ts +23 -0
- package/dist/commands/demo.js +618 -0
- package/dist/commands/demo.test.d.ts +1 -0
- package/dist/commands/demo.test.js +59 -0
- package/dist/commands/explain.js +46 -1
- package/dist/commands/export-audit.d.ts +16 -0
- package/dist/commands/export-audit.js +245 -0
- package/dist/commands/hooks.d.ts +22 -0
- package/dist/commands/hooks.js +274 -0
- package/dist/commands/hooks.test.d.ts +1 -0
- package/dist/commands/hooks.test.js +77 -0
- package/dist/commands/init.js +25 -1
- package/dist/commands/init.test.d.ts +1 -0
- package/dist/commands/init.test.js +97 -0
- package/dist/init-rules.test.js +2 -1
- package/dist/smoke.test.js +2 -1
- package/package.json +18 -2
- package/studio-dist/assets/index-C0TtM2OR.js +291 -0
- package/studio-dist/index.html +1 -1
- package/studio-dist/assets/index-Ch-q_mnO.js +0 -291
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ Agent writes code → Rigour checks → FAIL? → Fix Packet → Agent retries
|
|
|
35
35
|
|
|
36
36
|
## ⚙️ Quality Gates
|
|
37
37
|
|
|
38
|
+
### Structural & Security Gates
|
|
38
39
|
| Gate | Description |
|
|
39
40
|
|:---|:---|
|
|
40
41
|
| **File Size** | Max lines per file (default: 300-500) |
|
|
@@ -42,9 +43,21 @@ Agent writes code → Rigour checks → FAIL? → Fix Packet → Agent retries
|
|
|
42
43
|
| **Complexity** | Cyclomatic complexity limits (AST-based) |
|
|
43
44
|
| **Required Docs** | SPEC.md, ARCH.md, README must exist |
|
|
44
45
|
| **File Guard** | Protected paths, max files changed |
|
|
45
|
-
| **Security Patterns** | XSS, SQL injection, hardcoded secrets, command injection
|
|
46
|
+
| **Security Patterns** | XSS, SQL injection, hardcoded secrets, command injection |
|
|
46
47
|
| **Context Alignment** | Prevents drift by anchoring on project patterns |
|
|
47
48
|
|
|
49
|
+
### AI-Native Drift Detection (v2.16+)
|
|
50
|
+
| Gate | Description |
|
|
51
|
+
|:---|:---|
|
|
52
|
+
| **Duplication Drift** | Near-identical functions across files — AI re-invents what it forgot |
|
|
53
|
+
| **Hallucinated Imports** | Imports referencing modules that don't exist (JS/TS, Python, Go, Ruby, C#) |
|
|
54
|
+
| **Inconsistent Error Handling** | Same error type handled differently across agent sessions |
|
|
55
|
+
| **Context Window Artifacts** | Quality degradation within a file — clean top, messy bottom |
|
|
56
|
+
| **Async & Error Safety** | Unsafe async/promise patterns, unhandled errors across 6 languages |
|
|
57
|
+
|
|
58
|
+
### Multi-Language Support
|
|
59
|
+
All gates support **TypeScript, JavaScript, Python, Go, Ruby, and C#/.NET**.
|
|
60
|
+
|
|
48
61
|
## 🛠️ Commands
|
|
49
62
|
|
|
50
63
|
| Command | Purpose |
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,9 @@ import { guideCommand } from './commands/guide.js';
|
|
|
8
8
|
import { setupCommand } from './commands/setup.js';
|
|
9
9
|
import { indexCommand } from './commands/index.js';
|
|
10
10
|
import { studioCommand } from './commands/studio.js';
|
|
11
|
+
import { exportAuditCommand } from './commands/export-audit.js';
|
|
12
|
+
import { demoCommand } from './commands/demo.js';
|
|
13
|
+
import { hooksInitCommand } from './commands/hooks.js';
|
|
11
14
|
import { checkForUpdates } from './utils/version.js';
|
|
12
15
|
import chalk from 'chalk';
|
|
13
16
|
const CLI_VERSION = '2.0.0';
|
|
@@ -29,7 +32,7 @@ program
|
|
|
29
32
|
program
|
|
30
33
|
.command('init')
|
|
31
34
|
.description('Initialize Rigour in the current directory')
|
|
32
|
-
.option('-p, --preset <name>', 'Project preset (ui, api, infra, data)')
|
|
35
|
+
.option('-p, --preset <name>', 'Project preset (ui, api, infra, data, healthcare, fintech, government)')
|
|
33
36
|
.option('--paradigm <name>', 'Coding paradigm (oop, functional, minimal)')
|
|
34
37
|
.option('--ide <name>', 'Target IDE (cursor, vscode, all). Auto-detects if not specified.')
|
|
35
38
|
.option('--dry-run', 'Show detected configuration without writing files')
|
|
@@ -37,10 +40,12 @@ program
|
|
|
37
40
|
.option('-f, --force', 'Force re-initialization, overwriting existing rigour.yml')
|
|
38
41
|
.addHelpText('after', `
|
|
39
42
|
Examples:
|
|
40
|
-
$ rigour init
|
|
41
|
-
$ rigour init --preset api --explain
|
|
42
|
-
$ rigour init --
|
|
43
|
-
$ rigour init --
|
|
43
|
+
$ rigour init # Auto-discover role & paradigm
|
|
44
|
+
$ rigour init --preset api --explain # Force API role and show why
|
|
45
|
+
$ rigour init --preset healthcare # HIPAA-compliant quality gates
|
|
46
|
+
$ rigour init --preset fintech # SOC2/PCI-DSS quality gates
|
|
47
|
+
$ rigour init --preset government # FedRAMP/NIST quality gates
|
|
48
|
+
$ rigour init --ide all # Create files for all IDEs
|
|
44
49
|
`)
|
|
45
50
|
.action(async (options) => {
|
|
46
51
|
await initCommand(process.cwd(), options);
|
|
@@ -91,6 +96,43 @@ Examples:
|
|
|
91
96
|
failFast: !!options.failFast
|
|
92
97
|
});
|
|
93
98
|
});
|
|
99
|
+
program
|
|
100
|
+
.command('export-audit')
|
|
101
|
+
.description('Generate a compliance audit package from the last check')
|
|
102
|
+
.option('-f, --format <type>', 'Output format: json or md', 'json')
|
|
103
|
+
.option('-o, --output <path>', 'Custom output file path')
|
|
104
|
+
.option('--run', 'Run a fresh rigour check before exporting')
|
|
105
|
+
.addHelpText('after', `
|
|
106
|
+
Examples:
|
|
107
|
+
$ rigour export-audit # Export JSON audit package
|
|
108
|
+
$ rigour export-audit --format md # Export Markdown report
|
|
109
|
+
$ rigour export-audit --run # Run check first, then export
|
|
110
|
+
$ rigour export-audit -o audit.json # Custom output path
|
|
111
|
+
`)
|
|
112
|
+
.action(async (options) => {
|
|
113
|
+
await exportAuditCommand(process.cwd(), options);
|
|
114
|
+
});
|
|
115
|
+
program
|
|
116
|
+
.command('demo')
|
|
117
|
+
.description('Run a live demo — see Rigour catch AI drift, security issues, and structural violations')
|
|
118
|
+
.option('--cinematic', 'Screen-recording mode: typewriter effects, simulated AI agent, before/after scores')
|
|
119
|
+
.option('--hooks', 'Focus on real-time hooks catching issues as AI writes code')
|
|
120
|
+
.option('--speed <speed>', 'Pacing: fast, normal, slow (default: normal)', 'normal')
|
|
121
|
+
.addHelpText('after', `
|
|
122
|
+
Examples:
|
|
123
|
+
$ rigour demo # Run the flagship demo
|
|
124
|
+
$ rigour demo --cinematic # Screen-recording optimized (great for GIFs)
|
|
125
|
+
$ rigour demo --cinematic --speed slow # Slower pacing for presentations
|
|
126
|
+
$ rigour demo --hooks # Focus on hooks catching issues
|
|
127
|
+
$ npx @rigour-labs/cli demo # Try without installing
|
|
128
|
+
`)
|
|
129
|
+
.action(async (options) => {
|
|
130
|
+
await demoCommand({
|
|
131
|
+
cinematic: !!options.cinematic,
|
|
132
|
+
hooks: !!options.hooks,
|
|
133
|
+
speed: options.speed || 'normal',
|
|
134
|
+
});
|
|
135
|
+
});
|
|
94
136
|
program
|
|
95
137
|
.command('guide')
|
|
96
138
|
.description('Show the interactive engineering guide')
|
|
@@ -103,6 +145,27 @@ program
|
|
|
103
145
|
.action(async () => {
|
|
104
146
|
await setupCommand();
|
|
105
147
|
});
|
|
148
|
+
const hooksCmd = program
|
|
149
|
+
.command('hooks')
|
|
150
|
+
.description('Manage AI coding tool hook integrations');
|
|
151
|
+
hooksCmd
|
|
152
|
+
.command('init')
|
|
153
|
+
.description('Generate hook configs for AI coding tools (Claude, Cursor, Cline, Windsurf)')
|
|
154
|
+
.option('-t, --tool <name>', 'Target tool(s): claude, cursor, cline, windsurf, all. Auto-detects if not specified.')
|
|
155
|
+
.option('--dry-run', 'Show what files would be created without writing them')
|
|
156
|
+
.option('-f, --force', 'Overwrite existing hook files')
|
|
157
|
+
.option('--block', 'Configure hooks to block on failure (exit code 2)')
|
|
158
|
+
.addHelpText('after', `
|
|
159
|
+
Examples:
|
|
160
|
+
$ rigour hooks init # Auto-detect tools, generate hooks
|
|
161
|
+
$ rigour hooks init --tool claude # Generate Claude Code hooks only
|
|
162
|
+
$ rigour hooks init --tool all # Generate hooks for all tools
|
|
163
|
+
$ rigour hooks init --dry-run # Preview without writing files
|
|
164
|
+
$ rigour hooks init --tool cursor -f # Force overwrite Cursor hooks
|
|
165
|
+
`)
|
|
166
|
+
.action(async (options) => {
|
|
167
|
+
await hooksInitCommand(process.cwd(), options);
|
|
168
|
+
});
|
|
106
169
|
// Check for updates before parsing (non-blocking)
|
|
107
170
|
(async () => {
|
|
108
171
|
try {
|
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 } from '@rigour-labs/core';
|
|
5
|
+
import { GateRunner, ConfigSchema, recordScore, getScoreTrend } from '@rigour-labs/core';
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
7
|
import { randomUUID } from 'crypto';
|
|
8
8
|
// Exit codes per spec
|
|
@@ -57,6 +57,8 @@ export async function checkCommand(cwd, files = [], options = {}) {
|
|
|
57
57
|
// Write machine report
|
|
58
58
|
const reportPath = path.join(cwd, config.output.report_path);
|
|
59
59
|
await fs.writeJson(reportPath, report, { spaces: 2 });
|
|
60
|
+
// Record score for trend tracking
|
|
61
|
+
recordScore(cwd, report);
|
|
60
62
|
await logStudioEvent(cwd, {
|
|
61
63
|
type: "tool_response",
|
|
62
64
|
requestId,
|
|
@@ -81,15 +83,18 @@ export async function checkCommand(cwd, files = [], options = {}) {
|
|
|
81
83
|
});
|
|
82
84
|
return; // Wait for write callback
|
|
83
85
|
}
|
|
84
|
-
// CI mode: minimal output
|
|
86
|
+
// CI mode: minimal output with score
|
|
85
87
|
if (options.ci) {
|
|
86
88
|
if (report.status === 'PASS') {
|
|
87
|
-
|
|
89
|
+
const scoreStr = report.stats.score !== undefined ? ` (${report.stats.score}/100)` : '';
|
|
90
|
+
console.log(`PASS${scoreStr}`);
|
|
88
91
|
}
|
|
89
92
|
else {
|
|
90
|
-
|
|
93
|
+
const scoreStr = report.stats.score !== undefined ? ` Score: ${report.stats.score}/100` : '';
|
|
94
|
+
console.log(`FAIL: ${report.failures.length} violation(s)${scoreStr}`);
|
|
91
95
|
report.failures.forEach((f) => {
|
|
92
|
-
|
|
96
|
+
const sev = (f.severity || 'medium').toUpperCase();
|
|
97
|
+
console.log(` - [${sev}] [${f.id}] ${f.title}`);
|
|
93
98
|
});
|
|
94
99
|
}
|
|
95
100
|
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
@@ -104,21 +109,73 @@ export async function checkCommand(cwd, files = [], options = {}) {
|
|
|
104
109
|
}
|
|
105
110
|
else {
|
|
106
111
|
console.log(chalk.red.bold('✘ FAIL - Quality gate violations found.\n'));
|
|
112
|
+
// Score summary line
|
|
113
|
+
const stats = report.stats;
|
|
114
|
+
const scoreParts = [];
|
|
115
|
+
if (stats.score !== undefined)
|
|
116
|
+
scoreParts.push(`Score: ${stats.score}/100`);
|
|
117
|
+
if (stats.ai_health_score !== undefined)
|
|
118
|
+
scoreParts.push(`AI Health: ${stats.ai_health_score}/100`);
|
|
119
|
+
if (stats.structural_score !== undefined)
|
|
120
|
+
scoreParts.push(`Structural: ${stats.structural_score}/100`);
|
|
121
|
+
if (scoreParts.length > 0) {
|
|
122
|
+
console.log(chalk.bold(scoreParts.join(' | ')) + '\n');
|
|
123
|
+
}
|
|
124
|
+
// Severity breakdown
|
|
125
|
+
if (stats.severity_breakdown) {
|
|
126
|
+
const parts = Object.entries(stats.severity_breakdown)
|
|
127
|
+
.filter(([, count]) => count > 0)
|
|
128
|
+
.map(([sev, count]) => {
|
|
129
|
+
const color = sev === 'critical' ? chalk.red.bold : sev === 'high' ? chalk.red : sev === 'medium' ? chalk.yellow : chalk.dim;
|
|
130
|
+
return color(`${sev}: ${count}`);
|
|
131
|
+
});
|
|
132
|
+
if (parts.length > 0) {
|
|
133
|
+
console.log('Severity: ' + parts.join(', ') + '\n');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Group failures by provenance
|
|
137
|
+
const severityIcon = (s) => {
|
|
138
|
+
switch (s) {
|
|
139
|
+
case 'critical': return chalk.red.bold('CRIT');
|
|
140
|
+
case 'high': return chalk.red('HIGH');
|
|
141
|
+
case 'medium': return chalk.yellow('MED ');
|
|
142
|
+
case 'low': return chalk.dim('LOW ');
|
|
143
|
+
case 'info': return chalk.dim('INFO');
|
|
144
|
+
default: return chalk.yellow('MED ');
|
|
145
|
+
}
|
|
146
|
+
};
|
|
107
147
|
for (const failure of report.failures) {
|
|
108
|
-
|
|
109
|
-
|
|
148
|
+
const sev = severityIcon(failure.severity);
|
|
149
|
+
const prov = failure.provenance ? chalk.dim(`[${failure.provenance}]`) : '';
|
|
150
|
+
console.log(`${sev} ${prov} ${chalk.red(`[${failure.id}]`)} ${failure.title}`);
|
|
151
|
+
console.log(chalk.dim(` Details: ${failure.details}`));
|
|
110
152
|
if (failure.files && failure.files.length > 0) {
|
|
111
|
-
console.log(chalk.dim('
|
|
112
|
-
failure.files.forEach((f) => console.log(chalk.dim(`
|
|
153
|
+
console.log(chalk.dim(' Files:'));
|
|
154
|
+
failure.files.forEach((f) => console.log(chalk.dim(` - ${f}`)));
|
|
113
155
|
}
|
|
114
156
|
if (failure.hint) {
|
|
115
|
-
console.log(chalk.cyan(`
|
|
157
|
+
console.log(chalk.cyan(` Hint: ${failure.hint}`));
|
|
116
158
|
}
|
|
117
159
|
console.log('');
|
|
118
160
|
}
|
|
119
161
|
console.log(chalk.yellow(`See ${config.output.report_path} for full details.`));
|
|
120
162
|
}
|
|
121
|
-
|
|
163
|
+
// Score trend display
|
|
164
|
+
const trend = getScoreTrend(cwd);
|
|
165
|
+
if (trend && trend.recentScores.length >= 3) {
|
|
166
|
+
const arrow = trend.direction === 'improving' ? chalk.green('↑') :
|
|
167
|
+
trend.direction === 'degrading' ? chalk.red('↓') : chalk.dim('→');
|
|
168
|
+
const trendColor = trend.direction === 'improving' ? chalk.green :
|
|
169
|
+
trend.direction === 'degrading' ? chalk.red : chalk.dim;
|
|
170
|
+
const scoresStr = trend.recentScores.map(s => String(s)).join(' → ');
|
|
171
|
+
console.log(trendColor(`\nScore Trend: ${scoresStr} (${trend.direction} ${arrow})`));
|
|
172
|
+
}
|
|
173
|
+
// Stats footer
|
|
174
|
+
const footerParts = [`Finished in ${report.stats.duration_ms}ms`];
|
|
175
|
+
if (report.status === 'PASS' && report.stats.score !== undefined) {
|
|
176
|
+
footerParts.push(`Score: ${report.stats.score}/100`);
|
|
177
|
+
}
|
|
178
|
+
console.log(chalk.dim('\n' + footerParts.join(' | ')));
|
|
122
179
|
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
123
180
|
}
|
|
124
181
|
catch (error) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rigour demo
|
|
3
|
+
*
|
|
4
|
+
* Creates a temp project with intentional AI-generated code issues,
|
|
5
|
+
* runs Rigour against it, and shows the full experience.
|
|
6
|
+
*
|
|
7
|
+
* Modes:
|
|
8
|
+
* Default: Fast demo — scaffold → check → results
|
|
9
|
+
* --cinematic: Screen-recording optimized — typewriter effects, pauses,
|
|
10
|
+
* simulated AI agent writing code, hooks catching issues
|
|
11
|
+
* --hooks: Focus on the real-time hooks experience
|
|
12
|
+
* --speed: Control pacing (fast / normal / slow)
|
|
13
|
+
*
|
|
14
|
+
* The "flagship demo" — one command to understand Rigour.
|
|
15
|
+
*
|
|
16
|
+
* @since v2.17.0 (extended v3.0.0)
|
|
17
|
+
*/
|
|
18
|
+
export interface DemoOptions {
|
|
19
|
+
cinematic?: boolean;
|
|
20
|
+
hooks?: boolean;
|
|
21
|
+
speed?: 'fast' | 'normal' | 'slow';
|
|
22
|
+
}
|
|
23
|
+
export declare function demoCommand(options?: DemoOptions): Promise<void>;
|