@rigour-labs/cli 2.21.2 → 3.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/README.md +14 -1
- package/dist/cli.js +36 -5
- package/dist/commands/check.js +68 -11
- package/dist/commands/demo.d.ts +16 -0
- package/dist/commands/demo.js +306 -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/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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* export-audit command
|
|
3
|
+
*
|
|
4
|
+
* Generates a compliance audit package from the last gate check.
|
|
5
|
+
* The artifact compliance officers hand to auditors.
|
|
6
|
+
*
|
|
7
|
+
* Formats: JSON (structured) or Markdown (human-readable)
|
|
8
|
+
*
|
|
9
|
+
* @since v2.17.0
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'fs-extra';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import yaml from 'yaml';
|
|
15
|
+
import { getScoreHistory, getScoreTrend } from '@rigour-labs/core';
|
|
16
|
+
const CLI_VERSION = '2.0.0';
|
|
17
|
+
export async function exportAuditCommand(cwd, options = {}) {
|
|
18
|
+
const format = options.format || 'json';
|
|
19
|
+
const configPath = path.join(cwd, 'rigour.yml');
|
|
20
|
+
let reportPath = path.join(cwd, 'rigour-report.json');
|
|
21
|
+
// If --run, execute a fresh check first
|
|
22
|
+
if (options.run) {
|
|
23
|
+
console.log(chalk.blue('Running fresh rigour check...\n'));
|
|
24
|
+
const { checkCommand } = await import('./check.js');
|
|
25
|
+
try {
|
|
26
|
+
await checkCommand(cwd, [], {});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// checkCommand calls process.exit, so we catch here for the --run flow
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Read config
|
|
33
|
+
let config = {};
|
|
34
|
+
if (await fs.pathExists(configPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
37
|
+
config = yaml.parse(configContent);
|
|
38
|
+
if (config?.output?.report_path) {
|
|
39
|
+
reportPath = path.join(cwd, config.output.report_path);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
}
|
|
44
|
+
// Read report
|
|
45
|
+
if (!(await fs.pathExists(reportPath))) {
|
|
46
|
+
console.error(chalk.red(`Error: No report found at ${reportPath}`));
|
|
47
|
+
console.error(chalk.dim('Run `rigour check` first, or use `rigour export-audit --run`.'));
|
|
48
|
+
process.exit(2);
|
|
49
|
+
}
|
|
50
|
+
let report;
|
|
51
|
+
try {
|
|
52
|
+
const reportContent = await fs.readFile(reportPath, 'utf-8');
|
|
53
|
+
report = JSON.parse(reportContent);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error(chalk.red(`Error reading report: ${error.message}`));
|
|
57
|
+
process.exit(3);
|
|
58
|
+
}
|
|
59
|
+
// Build audit package
|
|
60
|
+
const auditPackage = buildAuditPackage(cwd, report, config);
|
|
61
|
+
// Determine output path
|
|
62
|
+
const outputPath = options.output
|
|
63
|
+
? path.resolve(cwd, options.output)
|
|
64
|
+
: path.join(cwd, `rigour-audit-report.${format}`);
|
|
65
|
+
// Write output
|
|
66
|
+
if (format === 'md') {
|
|
67
|
+
const markdown = renderMarkdown(auditPackage);
|
|
68
|
+
await fs.writeFile(outputPath, markdown, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
await fs.writeJson(outputPath, auditPackage, { spaces: 2 });
|
|
72
|
+
}
|
|
73
|
+
console.log(chalk.green(`\n✔ Audit report exported: ${path.relative(cwd, outputPath)}`));
|
|
74
|
+
console.log(chalk.dim(` Format: ${format.toUpperCase()} | Status: ${auditPackage.summary.status} | Score: ${auditPackage.summary.score}/100`));
|
|
75
|
+
}
|
|
76
|
+
function buildAuditPackage(cwd, report, config) {
|
|
77
|
+
const stats = report.stats || {};
|
|
78
|
+
const failures = report.failures || [];
|
|
79
|
+
// Score trend
|
|
80
|
+
const trend = getScoreTrend(cwd);
|
|
81
|
+
const history = getScoreHistory(cwd, 5);
|
|
82
|
+
// Severity breakdown
|
|
83
|
+
const severityBreakdown = stats.severity_breakdown || {};
|
|
84
|
+
const provenanceBreakdown = stats.provenance_breakdown || {};
|
|
85
|
+
// Gate results from summary
|
|
86
|
+
const gateResults = Object.entries(report.summary || {}).map(([gate, status]) => ({
|
|
87
|
+
gate,
|
|
88
|
+
status: status,
|
|
89
|
+
}));
|
|
90
|
+
// Top violations
|
|
91
|
+
const violations = failures.map((f) => ({
|
|
92
|
+
id: f.id,
|
|
93
|
+
severity: f.severity || 'medium',
|
|
94
|
+
provenance: f.provenance || 'traditional',
|
|
95
|
+
title: f.title,
|
|
96
|
+
details: f.details,
|
|
97
|
+
files: f.files || [],
|
|
98
|
+
line: f.line,
|
|
99
|
+
hint: f.hint,
|
|
100
|
+
}));
|
|
101
|
+
return {
|
|
102
|
+
schema_version: '1.0.0',
|
|
103
|
+
metadata: {
|
|
104
|
+
project: path.basename(cwd),
|
|
105
|
+
rigour_version: CLI_VERSION,
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
preset: config.preset || 'custom',
|
|
108
|
+
config_path: 'rigour.yml',
|
|
109
|
+
generated_by: 'rigour export-audit',
|
|
110
|
+
},
|
|
111
|
+
summary: {
|
|
112
|
+
status: report.status,
|
|
113
|
+
score: stats.score ?? 100,
|
|
114
|
+
ai_health_score: stats.ai_health_score,
|
|
115
|
+
structural_score: stats.structural_score,
|
|
116
|
+
duration_ms: stats.duration_ms,
|
|
117
|
+
total_violations: failures.length,
|
|
118
|
+
},
|
|
119
|
+
severity_breakdown: {
|
|
120
|
+
critical: severityBreakdown.critical || 0,
|
|
121
|
+
high: severityBreakdown.high || 0,
|
|
122
|
+
medium: severityBreakdown.medium || 0,
|
|
123
|
+
low: severityBreakdown.low || 0,
|
|
124
|
+
info: severityBreakdown.info || 0,
|
|
125
|
+
},
|
|
126
|
+
provenance_breakdown: {
|
|
127
|
+
'ai-drift': provenanceBreakdown['ai-drift'] || 0,
|
|
128
|
+
traditional: provenanceBreakdown.traditional || 0,
|
|
129
|
+
security: provenanceBreakdown.security || 0,
|
|
130
|
+
governance: provenanceBreakdown.governance || 0,
|
|
131
|
+
},
|
|
132
|
+
gate_results: gateResults,
|
|
133
|
+
violations,
|
|
134
|
+
score_trend: trend ? {
|
|
135
|
+
direction: trend.direction,
|
|
136
|
+
delta: trend.delta,
|
|
137
|
+
recent_average: trend.recentAvg,
|
|
138
|
+
previous_average: trend.previousAvg,
|
|
139
|
+
last_scores: trend.recentScores,
|
|
140
|
+
} : null,
|
|
141
|
+
recent_history: history.map(h => ({
|
|
142
|
+
timestamp: h.timestamp,
|
|
143
|
+
score: h.score,
|
|
144
|
+
status: h.status,
|
|
145
|
+
})),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function renderMarkdown(audit) {
|
|
149
|
+
const lines = [];
|
|
150
|
+
lines.push(`# Rigour Audit Report`);
|
|
151
|
+
lines.push('');
|
|
152
|
+
lines.push(`**Project:** ${audit.metadata.project}`);
|
|
153
|
+
lines.push(`**Generated:** ${audit.metadata.timestamp}`);
|
|
154
|
+
lines.push(`**Rigour Version:** ${audit.metadata.rigour_version}`);
|
|
155
|
+
lines.push(`**Preset:** ${audit.metadata.preset}`);
|
|
156
|
+
lines.push('');
|
|
157
|
+
// Summary
|
|
158
|
+
lines.push('## Summary');
|
|
159
|
+
lines.push('');
|
|
160
|
+
const statusEmoji = audit.summary.status === 'PASS' ? '✅' : '🛑';
|
|
161
|
+
lines.push(`| Metric | Value |`);
|
|
162
|
+
lines.push(`|:-------|:------|`);
|
|
163
|
+
lines.push(`| **Status** | ${statusEmoji} ${audit.summary.status} |`);
|
|
164
|
+
lines.push(`| **Overall Score** | ${audit.summary.score}/100 |`);
|
|
165
|
+
if (audit.summary.ai_health_score !== undefined) {
|
|
166
|
+
lines.push(`| **AI Health Score** | ${audit.summary.ai_health_score}/100 |`);
|
|
167
|
+
}
|
|
168
|
+
if (audit.summary.structural_score !== undefined) {
|
|
169
|
+
lines.push(`| **Structural Score** | ${audit.summary.structural_score}/100 |`);
|
|
170
|
+
}
|
|
171
|
+
lines.push(`| **Total Violations** | ${audit.summary.total_violations} |`);
|
|
172
|
+
lines.push(`| **Duration** | ${audit.summary.duration_ms}ms |`);
|
|
173
|
+
lines.push('');
|
|
174
|
+
// Severity Breakdown
|
|
175
|
+
lines.push('## Severity Breakdown');
|
|
176
|
+
lines.push('');
|
|
177
|
+
lines.push('| Severity | Count |');
|
|
178
|
+
lines.push('|:---------|:------|');
|
|
179
|
+
for (const [sev, count] of Object.entries(audit.severity_breakdown)) {
|
|
180
|
+
if (count > 0) {
|
|
181
|
+
lines.push(`| ${sev.charAt(0).toUpperCase() + sev.slice(1)} | ${count} |`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
lines.push('');
|
|
185
|
+
// Provenance Breakdown
|
|
186
|
+
lines.push('## Provenance Breakdown');
|
|
187
|
+
lines.push('');
|
|
188
|
+
lines.push('| Category | Count |');
|
|
189
|
+
lines.push('|:---------|:------|');
|
|
190
|
+
for (const [prov, count] of Object.entries(audit.provenance_breakdown)) {
|
|
191
|
+
if (count > 0) {
|
|
192
|
+
lines.push(`| ${prov} | ${count} |`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
lines.push('');
|
|
196
|
+
// Gate Results
|
|
197
|
+
lines.push('## Gate Results');
|
|
198
|
+
lines.push('');
|
|
199
|
+
lines.push('| Gate | Status |');
|
|
200
|
+
lines.push('|:-----|:-------|');
|
|
201
|
+
for (const gate of audit.gate_results) {
|
|
202
|
+
const icon = gate.status === 'PASS' ? '✅' : gate.status === 'FAIL' ? '❌' : '⏭️';
|
|
203
|
+
lines.push(`| ${gate.gate} | ${icon} ${gate.status} |`);
|
|
204
|
+
}
|
|
205
|
+
lines.push('');
|
|
206
|
+
// Violations
|
|
207
|
+
if (audit.violations.length > 0) {
|
|
208
|
+
lines.push('## Violations');
|
|
209
|
+
lines.push('');
|
|
210
|
+
for (let i = 0; i < audit.violations.length; i++) {
|
|
211
|
+
const v = audit.violations[i];
|
|
212
|
+
lines.push(`### ${i + 1}. [${v.severity.toUpperCase()}] ${v.title}`);
|
|
213
|
+
lines.push('');
|
|
214
|
+
lines.push(`- **ID:** \`${v.id}\``);
|
|
215
|
+
lines.push(`- **Severity:** ${v.severity}`);
|
|
216
|
+
lines.push(`- **Provenance:** ${v.provenance}`);
|
|
217
|
+
lines.push(`- **Details:** ${v.details}`);
|
|
218
|
+
if (v.files && v.files.length > 0) {
|
|
219
|
+
lines.push(`- **Files:** ${v.files.join(', ')}`);
|
|
220
|
+
}
|
|
221
|
+
if (v.hint) {
|
|
222
|
+
lines.push(`- **Hint:** ${v.hint}`);
|
|
223
|
+
}
|
|
224
|
+
lines.push('');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Score Trend
|
|
228
|
+
if (audit.score_trend) {
|
|
229
|
+
lines.push('## Score Trend');
|
|
230
|
+
lines.push('');
|
|
231
|
+
const arrow = audit.score_trend.direction === 'improving' ? '↑' :
|
|
232
|
+
audit.score_trend.direction === 'degrading' ? '↓' : '→';
|
|
233
|
+
lines.push(`**Direction:** ${audit.score_trend.direction} ${arrow}`);
|
|
234
|
+
lines.push(`**Recent Average:** ${audit.score_trend.recent_average}/100`);
|
|
235
|
+
lines.push(`**Previous Average:** ${audit.score_trend.previous_average}/100`);
|
|
236
|
+
lines.push(`**Delta:** ${audit.score_trend.delta > 0 ? '+' : ''}${audit.score_trend.delta}`);
|
|
237
|
+
lines.push(`**Recent Scores:** ${audit.score_trend.last_scores.join(' → ')}`);
|
|
238
|
+
lines.push('');
|
|
239
|
+
}
|
|
240
|
+
// Footer
|
|
241
|
+
lines.push('---');
|
|
242
|
+
lines.push(`*Generated by Rigour v${audit.metadata.rigour_version} — ${audit.metadata.timestamp}*`);
|
|
243
|
+
lines.push('');
|
|
244
|
+
return lines.join('\n');
|
|
245
|
+
}
|
package/dist/init-rules.test.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
async function getInitCommand() {
|
|
5
6
|
const { initCommand } = await import('./commands/init.js');
|
|
6
7
|
return initCommand;
|
|
7
8
|
}
|
|
8
9
|
describe('Init Command Rules Verification', () => {
|
|
9
|
-
const testDir = path.join(
|
|
10
|
+
const testDir = path.join(os.tmpdir(), 'rigour-temp-init-rules-test-' + process.pid);
|
|
10
11
|
beforeEach(async () => {
|
|
11
12
|
await fs.ensureDir(testDir);
|
|
12
13
|
});
|
package/dist/smoke.test.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
async function getCheckCommand() {
|
|
5
6
|
const { checkCommand } = await import('./commands/check.js');
|
|
6
7
|
return checkCommand;
|
|
7
8
|
}
|
|
8
9
|
describe('CLI Smoke Test', () => {
|
|
9
|
-
const testDir = path.join(
|
|
10
|
+
const testDir = path.join(os.tmpdir(), 'rigour-temp-smoke-test-' + process.pid);
|
|
10
11
|
beforeEach(async () => {
|
|
11
12
|
await fs.ensureDir(testDir);
|
|
12
13
|
// @ts-ignore
|
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
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
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://rigour.run",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"quality-gates",
|
|
9
|
+
"ai-code-quality",
|
|
10
|
+
"cli",
|
|
11
|
+
"linter",
|
|
12
|
+
"static-analysis",
|
|
13
|
+
"claude",
|
|
14
|
+
"cursor",
|
|
15
|
+
"copilot",
|
|
16
|
+
"mcp",
|
|
17
|
+
"code-review",
|
|
18
|
+
"ci-cd"
|
|
19
|
+
],
|
|
4
20
|
"type": "module",
|
|
5
21
|
"bin": {
|
|
6
22
|
"rigour": "dist/cli.js"
|
|
@@ -28,7 +44,7 @@
|
|
|
28
44
|
"inquirer": "9.2.16",
|
|
29
45
|
"ora": "^8.0.1",
|
|
30
46
|
"yaml": "^2.8.2",
|
|
31
|
-
"@rigour-labs/core": "
|
|
47
|
+
"@rigour-labs/core": "3.0.0"
|
|
32
48
|
},
|
|
33
49
|
"devDependencies": {
|
|
34
50
|
"@types/fs-extra": "^11.0.4",
|