@rigour-labs/cli 2.9.4 → 2.11.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/cli.js +20 -21
- package/dist/commands/check.js +77 -50
- package/dist/commands/constants.js +4 -7
- package/dist/commands/explain.js +30 -36
- package/dist/commands/guide.js +15 -21
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +88 -0
- package/dist/commands/init.js +142 -105
- package/dist/commands/run.js +42 -48
- package/dist/commands/setup.js +21 -27
- package/dist/commands/studio.d.ts +2 -0
- package/dist/commands/studio.js +272 -0
- package/dist/init-rules.test.js +34 -34
- package/dist/smoke.test.js +35 -34
- package/package.json +4 -2
- package/src/cli.ts +5 -0
- package/src/commands/check.ts +36 -0
- package/src/commands/index.ts +105 -0
- package/src/commands/init.ts +64 -17
- package/src/commands/studio.ts +273 -0
- package/src/init-rules.test.ts +10 -2
- package/src/smoke.test.ts +10 -2
- package/src/templates/handshake.mdc +23 -26
- package/vitest.config.ts +10 -0
- package/vitest.setup.ts +30 -0
package/dist/cli.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const program = new commander_1.Command();
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { initCommand } from './commands/init.js';
|
|
4
|
+
import { checkCommand } from './commands/check.js';
|
|
5
|
+
import { explainCommand } from './commands/explain.js';
|
|
6
|
+
import { runLoop } from './commands/run.js';
|
|
7
|
+
import { guideCommand } from './commands/guide.js';
|
|
8
|
+
import { setupCommand } from './commands/setup.js';
|
|
9
|
+
import { indexCommand } from './commands/index.js';
|
|
10
|
+
import { studioCommand } from './commands/studio.js';
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program.addCommand(indexCommand);
|
|
14
|
+
program.addCommand(studioCommand);
|
|
16
15
|
program
|
|
17
16
|
.name('rigour')
|
|
18
17
|
.description('🛡️ Rigour: The Quality Gate Loop for AI-Assisted Engineering')
|
|
19
18
|
.version('2.0.0')
|
|
20
|
-
.addHelpText('before',
|
|
19
|
+
.addHelpText('before', chalk.bold.cyan(`
|
|
21
20
|
____ _
|
|
22
21
|
/ __ \\(_)____ ___ __ __ _____
|
|
23
22
|
/ /_/ // // __ \`/ / / / / // ___/
|
|
@@ -41,7 +40,7 @@ Examples:
|
|
|
41
40
|
$ rigour init --ide all # Create files for all IDEs
|
|
42
41
|
`)
|
|
43
42
|
.action(async (options) => {
|
|
44
|
-
await
|
|
43
|
+
await initCommand(process.cwd(), options);
|
|
45
44
|
});
|
|
46
45
|
program
|
|
47
46
|
.command('check')
|
|
@@ -59,7 +58,7 @@ Examples:
|
|
|
59
58
|
$ rigour check --ci # Run in CI environment
|
|
60
59
|
`)
|
|
61
60
|
.action(async (files, options) => {
|
|
62
|
-
await
|
|
61
|
+
await checkCommand(process.cwd(), files, options);
|
|
63
62
|
});
|
|
64
63
|
program
|
|
65
64
|
.command('explain')
|
|
@@ -69,7 +68,7 @@ Examples:
|
|
|
69
68
|
$ rigour explain # Get a human-readable violation summary
|
|
70
69
|
`)
|
|
71
70
|
.action(async () => {
|
|
72
|
-
await
|
|
71
|
+
await explainCommand(process.cwd());
|
|
73
72
|
});
|
|
74
73
|
program
|
|
75
74
|
.command('run')
|
|
@@ -83,7 +82,7 @@ Examples:
|
|
|
83
82
|
$ rigour run -c 5 -- cursor-agent # Run Cursor agent for up to 5 cycles
|
|
84
83
|
`)
|
|
85
84
|
.action(async (args, options) => {
|
|
86
|
-
await
|
|
85
|
+
await runLoop(process.cwd(), args, {
|
|
87
86
|
iterations: parseInt(options.maxCycles),
|
|
88
87
|
failFast: !!options.failFast
|
|
89
88
|
});
|
|
@@ -92,12 +91,12 @@ program
|
|
|
92
91
|
.command('guide')
|
|
93
92
|
.description('Show the interactive engineering guide')
|
|
94
93
|
.action(async () => {
|
|
95
|
-
await
|
|
94
|
+
await guideCommand();
|
|
96
95
|
});
|
|
97
96
|
program
|
|
98
97
|
.command('setup')
|
|
99
98
|
.description('Show installation and global setup guidance')
|
|
100
99
|
.action(async () => {
|
|
101
|
-
await
|
|
100
|
+
await setupCommand();
|
|
102
101
|
});
|
|
103
102
|
program.parse();
|
package/dist/commands/check.js
CHANGED
|
@@ -1,50 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
11
|
-
const core_1 = require("@rigour-labs/core");
|
|
12
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import yaml from 'yaml';
|
|
5
|
+
import { GateRunner, ConfigSchema } from '@rigour-labs/core';
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
import { randomUUID } from 'crypto';
|
|
13
8
|
// Exit codes per spec
|
|
14
9
|
const EXIT_PASS = 0;
|
|
15
10
|
const EXIT_FAIL = 1;
|
|
16
11
|
const EXIT_CONFIG_ERROR = 2;
|
|
17
12
|
const EXIT_INTERNAL_ERROR = 3;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
// Helper to log events for Rigour Studio
|
|
14
|
+
async function logStudioEvent(cwd, event) {
|
|
15
|
+
try {
|
|
16
|
+
const rigourDir = path.join(cwd, ".rigour");
|
|
17
|
+
await fs.ensureDir(rigourDir);
|
|
18
|
+
const eventsPath = path.join(rigourDir, "events.jsonl");
|
|
19
|
+
const logEntry = JSON.stringify({
|
|
20
|
+
id: randomUUID(),
|
|
21
|
+
timestamp: new Date().toISOString(),
|
|
22
|
+
...event
|
|
23
|
+
}) + "\n";
|
|
24
|
+
await fs.appendFile(eventsPath, logEntry);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Silent fail
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function checkCommand(cwd, files = [], options = {}) {
|
|
31
|
+
const configPath = path.join(cwd, 'rigour.yml');
|
|
32
|
+
if (!(await fs.pathExists(configPath))) {
|
|
21
33
|
if (options.json) {
|
|
22
34
|
console.log(JSON.stringify({ error: 'CONFIG_ERROR', message: 'rigour.yml not found' }));
|
|
23
35
|
}
|
|
24
36
|
else if (!options.ci) {
|
|
25
|
-
console.error(
|
|
37
|
+
console.error(chalk.red('Error: rigour.yml not found. Run `rigour init` first.'));
|
|
26
38
|
}
|
|
27
39
|
process.exit(EXIT_CONFIG_ERROR);
|
|
28
40
|
}
|
|
29
41
|
try {
|
|
30
|
-
const configContent = await
|
|
31
|
-
const rawConfig =
|
|
32
|
-
const config =
|
|
42
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
43
|
+
const rawConfig = yaml.parse(configContent);
|
|
44
|
+
const config = ConfigSchema.parse(rawConfig);
|
|
33
45
|
if (!options.ci && !options.json) {
|
|
34
|
-
console.log(
|
|
46
|
+
console.log(chalk.blue('Running Rigour checks...\n'));
|
|
35
47
|
}
|
|
36
|
-
const runner = new
|
|
48
|
+
const runner = new GateRunner(config);
|
|
49
|
+
const requestId = randomUUID();
|
|
50
|
+
await logStudioEvent(cwd, {
|
|
51
|
+
type: "tool_call",
|
|
52
|
+
requestId,
|
|
53
|
+
tool: "rigour_check",
|
|
54
|
+
arguments: { files }
|
|
55
|
+
});
|
|
37
56
|
const report = await runner.run(cwd, files.length > 0 ? files : undefined);
|
|
38
57
|
// Write machine report
|
|
39
|
-
const reportPath =
|
|
40
|
-
await
|
|
58
|
+
const reportPath = path.join(cwd, config.output.report_path);
|
|
59
|
+
await fs.writeJson(reportPath, report, { spaces: 2 });
|
|
60
|
+
await logStudioEvent(cwd, {
|
|
61
|
+
type: "tool_response",
|
|
62
|
+
requestId,
|
|
63
|
+
tool: "rigour_check",
|
|
64
|
+
status: report.status === 'PASS' ? 'success' : 'error',
|
|
65
|
+
content: [{ type: "text", text: `Audit Result: ${report.status}` }],
|
|
66
|
+
_rigour_report: report
|
|
67
|
+
});
|
|
41
68
|
// Generate Fix Packet v2 on failure
|
|
42
69
|
if (report.status === 'FAIL') {
|
|
43
70
|
const { FixPacketService } = await import('@rigour-labs/core');
|
|
44
71
|
const fixPacketService = new FixPacketService();
|
|
45
72
|
const fixPacket = fixPacketService.generate(report, config);
|
|
46
|
-
const fixPacketPath =
|
|
47
|
-
await
|
|
73
|
+
const fixPacketPath = path.join(cwd, 'rigour-fix-packet.json');
|
|
74
|
+
await fs.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
|
|
48
75
|
}
|
|
49
76
|
// JSON output mode
|
|
50
77
|
if (options.json) {
|
|
@@ -70,25 +97,25 @@ async function checkCommand(cwd, files = [], options = {}) {
|
|
|
70
97
|
}
|
|
71
98
|
// Normal human-readable output
|
|
72
99
|
if (report.status === 'PASS') {
|
|
73
|
-
console.log(
|
|
100
|
+
console.log(chalk.green.bold('✔ PASS - All quality gates satisfied.'));
|
|
74
101
|
}
|
|
75
102
|
else {
|
|
76
|
-
console.log(
|
|
103
|
+
console.log(chalk.red.bold('✘ FAIL - Quality gate violations found.\n'));
|
|
77
104
|
for (const failure of report.failures) {
|
|
78
|
-
console.log(
|
|
79
|
-
console.log(
|
|
105
|
+
console.log(chalk.red(`[${failure.id}] ${failure.title}`));
|
|
106
|
+
console.log(chalk.dim(` Details: ${failure.details}`));
|
|
80
107
|
if (failure.files && failure.files.length > 0) {
|
|
81
|
-
console.log(
|
|
82
|
-
failure.files.forEach((f) => console.log(
|
|
108
|
+
console.log(chalk.dim(' Files:'));
|
|
109
|
+
failure.files.forEach((f) => console.log(chalk.dim(` - ${f}`)));
|
|
83
110
|
}
|
|
84
111
|
if (failure.hint) {
|
|
85
|
-
console.log(
|
|
112
|
+
console.log(chalk.cyan(` Hint: ${failure.hint}`));
|
|
86
113
|
}
|
|
87
114
|
console.log('');
|
|
88
115
|
}
|
|
89
|
-
console.log(
|
|
116
|
+
console.log(chalk.yellow(`See ${config.output.report_path} for full details.`));
|
|
90
117
|
}
|
|
91
|
-
console.log(
|
|
118
|
+
console.log(chalk.dim(`\nFinished in ${report.stats.duration_ms}ms`));
|
|
92
119
|
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
93
120
|
}
|
|
94
121
|
catch (error) {
|
|
@@ -97,9 +124,9 @@ async function checkCommand(cwd, files = [], options = {}) {
|
|
|
97
124
|
console.log(JSON.stringify({ error: 'CONFIG_ERROR', details: error.issues }));
|
|
98
125
|
}
|
|
99
126
|
else {
|
|
100
|
-
console.error(
|
|
127
|
+
console.error(chalk.red('\nInvalid rigour.yml configuration:'));
|
|
101
128
|
error.issues.forEach((issue) => {
|
|
102
|
-
console.error(
|
|
129
|
+
console.error(chalk.red(` • ${issue.path.join('.')}: ${issue.message}`));
|
|
103
130
|
});
|
|
104
131
|
}
|
|
105
132
|
process.exit(EXIT_CONFIG_ERROR);
|
|
@@ -108,24 +135,24 @@ async function checkCommand(cwd, files = [], options = {}) {
|
|
|
108
135
|
console.log(JSON.stringify({ error: 'INTERNAL_ERROR', message: error.message }));
|
|
109
136
|
}
|
|
110
137
|
else if (!options.ci) {
|
|
111
|
-
console.error(
|
|
138
|
+
console.error(chalk.red(`Internal error: ${error.message}`));
|
|
112
139
|
}
|
|
113
140
|
process.exit(EXIT_INTERNAL_ERROR);
|
|
114
141
|
}
|
|
115
142
|
}
|
|
116
143
|
async function interactiveMode(report, config) {
|
|
117
144
|
console.clear();
|
|
118
|
-
console.log(
|
|
119
|
-
console.log(
|
|
145
|
+
console.log(chalk.bold.blue('══ Rigour Interactive Review ══\n'));
|
|
146
|
+
console.log(chalk.yellow(`${report.failures.length} violations found.\n`));
|
|
120
147
|
const choices = report.failures.map((f, i) => ({
|
|
121
148
|
name: `[${f.id}] ${f.title}`,
|
|
122
149
|
value: i
|
|
123
150
|
}));
|
|
124
|
-
choices.push(new
|
|
151
|
+
choices.push(new inquirer.Separator());
|
|
125
152
|
choices.push({ name: 'Exit', value: -1 });
|
|
126
153
|
let exit = false;
|
|
127
154
|
while (!exit) {
|
|
128
|
-
const { index } = await
|
|
155
|
+
const { index } = await inquirer.prompt([
|
|
129
156
|
{
|
|
130
157
|
type: 'list',
|
|
131
158
|
name: 'index',
|
|
@@ -140,19 +167,19 @@ async function interactiveMode(report, config) {
|
|
|
140
167
|
}
|
|
141
168
|
const failure = report.failures[index];
|
|
142
169
|
console.clear();
|
|
143
|
-
console.log(
|
|
144
|
-
console.log(
|
|
145
|
-
console.log(`\n${
|
|
170
|
+
console.log(chalk.bold.red(`\nViolation: ${failure.title}`));
|
|
171
|
+
console.log(chalk.dim(`ID: ${failure.id}`));
|
|
172
|
+
console.log(`\n${chalk.bold('Details:')}\n${failure.details}`);
|
|
146
173
|
if (failure.files && failure.files.length > 0) {
|
|
147
|
-
console.log(`\n${
|
|
148
|
-
failure.files.forEach((f) => console.log(
|
|
174
|
+
console.log(`\n${chalk.bold('Impacted Files:')}`);
|
|
175
|
+
failure.files.forEach((f) => console.log(chalk.dim(` - ${f}`)));
|
|
149
176
|
}
|
|
150
177
|
if (failure.hint) {
|
|
151
|
-
console.log(`\n${
|
|
178
|
+
console.log(`\n${chalk.bold.cyan('Hint:')} ${failure.hint}`);
|
|
152
179
|
}
|
|
153
|
-
console.log(
|
|
154
|
-
await
|
|
180
|
+
console.log(chalk.dim('\n' + '─'.repeat(40)));
|
|
181
|
+
await inquirer.prompt([{ type: 'input', name: 'continue', message: 'Press Enter to return to list...' }]);
|
|
155
182
|
console.clear();
|
|
156
|
-
console.log(
|
|
183
|
+
console.log(chalk.bold.blue('══ Rigour Interactive Review ══\n'));
|
|
157
184
|
}
|
|
158
185
|
}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AGNOSTIC_AI_INSTRUCTIONS = exports.COLLABORATION_RULES = exports.DEBUGGING_RULES = exports.CODE_QUALITY_RULES = void 0;
|
|
4
|
-
exports.CODE_QUALITY_RULES = `
|
|
1
|
+
export const CODE_QUALITY_RULES = `
|
|
5
2
|
# Code Quality Standards
|
|
6
3
|
|
|
7
4
|
## PRODUCTION-GRADE CODE ONLY
|
|
@@ -60,7 +57,7 @@ Before asking for "approval," internally verify:
|
|
|
60
57
|
- **Cost Impact**: Does this change increase egress costs (e.g., unnecessary cross-region logging)?
|
|
61
58
|
- **Error Handling**: Does the UI have a graceful fallback if the backend service is slow?
|
|
62
59
|
`;
|
|
63
|
-
|
|
60
|
+
export const DEBUGGING_RULES = `
|
|
64
61
|
# Investigation & Debugging Protocol
|
|
65
62
|
|
|
66
63
|
## INVESTIGATION PROTOCOL
|
|
@@ -109,7 +106,7 @@ Before proposing any fix:
|
|
|
109
106
|
3. Trace the data flow that led to the error
|
|
110
107
|
4. Verify the fix doesn't break other paths
|
|
111
108
|
`;
|
|
112
|
-
|
|
109
|
+
export const COLLABORATION_RULES = `
|
|
113
110
|
# Role & Collaboration
|
|
114
111
|
|
|
115
112
|
You are a Senior Staff Engineer working alongside a Principal Engineer (the user).
|
|
@@ -186,7 +183,7 @@ If thresholds genuinely need adjustment, escalate to the team lead with justific
|
|
|
186
183
|
8. ❌ Losing context between messages
|
|
187
184
|
9. ❌ Modifying rigour.yml to pass quality checks
|
|
188
185
|
`;
|
|
189
|
-
|
|
186
|
+
export const AGNOSTIC_AI_INSTRUCTIONS = `
|
|
190
187
|
# 🤖 CRITICAL INSTRUCTION FOR AI
|
|
191
188
|
|
|
192
189
|
When asked to implement a feature or fix an issue:
|
package/dist/commands/explain.js
CHANGED
|
@@ -1,73 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
async function explainCommand(cwd) {
|
|
11
|
-
const configPath = path_1.default.join(cwd, 'rigour.yml');
|
|
12
|
-
let reportPath = path_1.default.join(cwd, 'rigour-report.json');
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
export async function explainCommand(cwd) {
|
|
5
|
+
const configPath = path.join(cwd, 'rigour.yml');
|
|
6
|
+
let reportPath = path.join(cwd, 'rigour-report.json');
|
|
13
7
|
// Try to read custom path from config
|
|
14
|
-
if (await
|
|
8
|
+
if (await fs.pathExists(configPath)) {
|
|
15
9
|
try {
|
|
16
10
|
const yaml = await import('yaml');
|
|
17
|
-
const configContent = await
|
|
11
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
18
12
|
const config = yaml.parse(configContent);
|
|
19
13
|
if (config?.output?.report_path) {
|
|
20
|
-
reportPath =
|
|
14
|
+
reportPath = path.join(cwd, config.output.report_path);
|
|
21
15
|
}
|
|
22
16
|
}
|
|
23
17
|
catch (e) { }
|
|
24
18
|
}
|
|
25
|
-
if (!(await
|
|
26
|
-
console.error(
|
|
27
|
-
console.error(
|
|
19
|
+
if (!(await fs.pathExists(reportPath))) {
|
|
20
|
+
console.error(chalk.red(`Error: No report found at ${reportPath}`));
|
|
21
|
+
console.error(chalk.dim('Run `rigour check` first to generate a report.'));
|
|
28
22
|
process.exit(2);
|
|
29
23
|
}
|
|
30
24
|
try {
|
|
31
|
-
const reportContent = await
|
|
25
|
+
const reportContent = await fs.readFile(reportPath, 'utf-8');
|
|
32
26
|
const report = JSON.parse(reportContent);
|
|
33
|
-
console.log(
|
|
34
|
-
console.log(
|
|
35
|
-
?
|
|
36
|
-
:
|
|
37
|
-
console.log(
|
|
27
|
+
console.log(chalk.bold('\n📋 Rigour Report Explanation\n'));
|
|
28
|
+
console.log(chalk.bold('Status: ') + (report.status === 'PASS'
|
|
29
|
+
? chalk.green.bold('✅ PASS')
|
|
30
|
+
: chalk.red.bold('🛑 FAIL')));
|
|
31
|
+
console.log(chalk.bold('\nGate Summary:'));
|
|
38
32
|
for (const [gate, status] of Object.entries(report.summary || {})) {
|
|
39
33
|
const icon = status === 'PASS' ? '✅' : status === 'FAIL' ? '❌' : '⏭️';
|
|
40
34
|
console.log(` ${icon} ${gate}: ${status}`);
|
|
41
35
|
}
|
|
42
36
|
if (report.failures && report.failures.length > 0) {
|
|
43
|
-
console.log(
|
|
37
|
+
console.log(chalk.bold.red(`\n🔧 ${report.failures.length} Violation(s) to Fix:\n`));
|
|
44
38
|
report.failures.forEach((failure, index) => {
|
|
45
|
-
console.log(
|
|
46
|
-
console.log(
|
|
39
|
+
console.log(chalk.white(`${index + 1}. `) + chalk.bold.yellow(`[${failure.id.toUpperCase()}]`) + chalk.white(` ${failure.title}`));
|
|
40
|
+
console.log(chalk.dim(` └─ ${failure.details}`));
|
|
47
41
|
if (failure.files && failure.files.length > 0) {
|
|
48
|
-
console.log(
|
|
42
|
+
console.log(chalk.cyan(` 📁 Files: ${failure.files.join(', ')}`));
|
|
49
43
|
}
|
|
50
44
|
if (failure.hint) {
|
|
51
|
-
console.log(
|
|
45
|
+
console.log(chalk.green(` 💡 Hint: ${failure.hint}`));
|
|
52
46
|
}
|
|
53
47
|
console.log('');
|
|
54
48
|
});
|
|
55
49
|
}
|
|
56
50
|
else if (report.status === 'PASS') {
|
|
57
|
-
console.log(
|
|
51
|
+
console.log(chalk.green('\n✨ All quality gates passed! No violations found.\n'));
|
|
58
52
|
}
|
|
59
53
|
if (report.status === 'FAIL') {
|
|
60
|
-
console.log(
|
|
61
|
-
console.log(
|
|
62
|
-
console.log(
|
|
63
|
-
console.log(
|
|
54
|
+
console.log(chalk.bold('\n👉 Next Steps:'));
|
|
55
|
+
console.log(chalk.dim(' 1. Refactor the code to address the violations above.'));
|
|
56
|
+
console.log(chalk.dim(' 2. Run `rigour check` again to verify your fixes.'));
|
|
57
|
+
console.log(chalk.dim(' 3. If using an agent, pass it the violations as constraints.\n'));
|
|
64
58
|
}
|
|
65
59
|
if (report.stats) {
|
|
66
|
-
console.log(
|
|
60
|
+
console.log(chalk.dim(`Duration: ${report.stats.duration_ms}ms`));
|
|
67
61
|
}
|
|
68
62
|
}
|
|
69
63
|
catch (error) {
|
|
70
|
-
console.error(
|
|
64
|
+
console.error(chalk.red(`Error reading report: ${error.message}`));
|
|
71
65
|
process.exit(3);
|
|
72
66
|
}
|
|
73
67
|
}
|
package/dist/commands/guide.js
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
console.log(
|
|
10
|
-
console.log(
|
|
11
|
-
console.log(
|
|
12
|
-
console.log(
|
|
13
|
-
console.log(
|
|
14
|
-
console.log(
|
|
15
|
-
console.log(
|
|
16
|
-
console.log(chalk_1.default.yellow(' • Safety Rails') + chalk_1.default.dim(': Prevents "explosive" refactoring (max files changed).'));
|
|
17
|
-
console.log(chalk_1.default.yellow(' • Strategic Guardians') + chalk_1.default.dim(': Dependency and Architectural boundary enforcement.\n'));
|
|
18
|
-
console.log(chalk_1.default.bold('Workflow Integration:'));
|
|
19
|
-
console.log(chalk_1.default.green(' • Cursor') + chalk_1.default.dim(': Add the MCP server or use the ') + chalk_1.default.cyan('.cursor/rules/rigour.mdc') + chalk_1.default.dim(' handshake.'));
|
|
20
|
-
console.log(chalk_1.default.green(' • CI/CD') + chalk_1.default.dim(': Use ') + chalk_1.default.cyan('rigour check --ci') + chalk_1.default.dim(' to fail PRs that violate quality gates.\n'));
|
|
21
|
-
console.log(chalk_1.default.dim('For more detailed docs, visit: ') + chalk_1.default.underline('https://github.com/erashu212/rigour/docs\n'));
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export async function guideCommand() {
|
|
3
|
+
console.log(chalk.bold.cyan('\n🛡️ Rigour Labs | The Engineering Guide\n'));
|
|
4
|
+
console.log(chalk.bold('Getting Started:'));
|
|
5
|
+
console.log(chalk.dim(' 1. Run ') + chalk.cyan('rigour init') + chalk.dim(' to detect your project role and apply standards.'));
|
|
6
|
+
console.log(chalk.dim(' 2. Run ') + chalk.cyan('rigour check') + chalk.dim(' to see existing violations.'));
|
|
7
|
+
console.log(chalk.dim(' 3. Run ') + chalk.cyan('rigour run -- <your-agent-command>') + chalk.dim(' to automate the fix loop.\n'));
|
|
8
|
+
console.log(chalk.bold('Key Concepts:'));
|
|
9
|
+
console.log(chalk.yellow(' • Fix Packet v2') + chalk.dim(': Structured diagnostics fed directly into AI agents.'));
|
|
10
|
+
console.log(chalk.yellow(' • Safety Rails') + chalk.dim(': Prevents "explosive" refactoring (max files changed).'));
|
|
11
|
+
console.log(chalk.yellow(' • Strategic Guardians') + chalk.dim(': Dependency and Architectural boundary enforcement.\n'));
|
|
12
|
+
console.log(chalk.bold('Workflow Integration:'));
|
|
13
|
+
console.log(chalk.green(' • Cursor') + chalk.dim(': Add the MCP server or use the ') + chalk.cyan('.cursor/rules/rigour.mdc') + chalk.dim(' handshake.'));
|
|
14
|
+
console.log(chalk.green(' • CI/CD') + chalk.dim(': Use ') + chalk.cyan('rigour check --ci') + chalk.dim(' to fail PRs that violate quality gates.\n'));
|
|
15
|
+
console.log(chalk.dim('For more detailed docs, visit: ') + chalk.underline('https://github.com/erashu212/rigour/docs\n'));
|
|
22
16
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Index Command
|
|
3
|
+
*
|
|
4
|
+
* Builds and updates the Rigour Pattern Index to prevent code reinvention.
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import { randomUUID } from 'crypto';
|
|
12
|
+
// Helper to log events for Rigour Studio
|
|
13
|
+
async function logStudioEvent(cwd, event) {
|
|
14
|
+
try {
|
|
15
|
+
const rigourDir = path.join(cwd, ".rigour");
|
|
16
|
+
await fs.ensureDir(rigourDir);
|
|
17
|
+
const eventsPath = path.join(rigourDir, "events.jsonl");
|
|
18
|
+
const logEntry = JSON.stringify({
|
|
19
|
+
id: randomUUID(),
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
...event
|
|
22
|
+
}) + "\n";
|
|
23
|
+
await fs.appendFile(eventsPath, logEntry);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Silent fail
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Dynamic imports are used inside the action handler below to prevent
|
|
30
|
+
// native dependency issues from affecting the rest of the CLI.
|
|
31
|
+
export const indexCommand = new Command('index')
|
|
32
|
+
.description('Build or update the pattern index for the current project')
|
|
33
|
+
.option('-s, --semantic', 'Generate semantic embeddings for better matching (requires Transformers.js)', false)
|
|
34
|
+
.option('-f, --force', 'Force a full rebuild of the index', false)
|
|
35
|
+
.option('-o, --output <path>', 'Custom path for the index file')
|
|
36
|
+
.action(async (options) => {
|
|
37
|
+
const cwd = process.cwd();
|
|
38
|
+
// Dynamic import to isolate native dependencies
|
|
39
|
+
const { PatternIndexer, savePatternIndex, loadPatternIndex, getDefaultIndexPath } = await import('@rigour-labs/core/pattern-index');
|
|
40
|
+
const indexPath = options.output || getDefaultIndexPath(cwd);
|
|
41
|
+
const spinner = ora('Initializing pattern indexer...').start();
|
|
42
|
+
try {
|
|
43
|
+
const requestId = randomUUID();
|
|
44
|
+
await logStudioEvent(cwd, {
|
|
45
|
+
type: "tool_call",
|
|
46
|
+
requestId,
|
|
47
|
+
tool: "rigour_index",
|
|
48
|
+
arguments: options
|
|
49
|
+
});
|
|
50
|
+
const indexer = new PatternIndexer(cwd, {
|
|
51
|
+
useEmbeddings: options.semantic
|
|
52
|
+
});
|
|
53
|
+
let index;
|
|
54
|
+
const existingIndex = await loadPatternIndex(indexPath);
|
|
55
|
+
if (existingIndex && !options.force) {
|
|
56
|
+
spinner.text = 'Updating existing pattern index...';
|
|
57
|
+
index = await indexer.updateIndex(existingIndex);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
spinner.text = 'Building fresh pattern index (this may take a while)...';
|
|
61
|
+
index = await indexer.buildIndex();
|
|
62
|
+
}
|
|
63
|
+
spinner.text = 'Saving index to disk...';
|
|
64
|
+
await savePatternIndex(index, indexPath);
|
|
65
|
+
spinner.succeed(chalk.green(`Pattern index built successfully!`));
|
|
66
|
+
await logStudioEvent(cwd, {
|
|
67
|
+
type: "tool_response",
|
|
68
|
+
requestId,
|
|
69
|
+
tool: "rigour_index",
|
|
70
|
+
status: "success",
|
|
71
|
+
content: [{ type: "text", text: `Index built: ${index.stats.totalPatterns} patterns` }]
|
|
72
|
+
});
|
|
73
|
+
console.log(chalk.blue(`- Total Patterns: ${index.stats.totalPatterns}`));
|
|
74
|
+
console.log(chalk.blue(`- Total Files: ${index.stats.totalFiles}`));
|
|
75
|
+
console.log(chalk.blue(`- Index Path: ${indexPath}`));
|
|
76
|
+
if (options.semantic) {
|
|
77
|
+
console.log(chalk.magenta(`- Semantic Search: Enabled (Local Transformers.js)`));
|
|
78
|
+
}
|
|
79
|
+
const byType = Object.entries(index.stats.byType)
|
|
80
|
+
.map(([type, count]) => `${type}: ${count}`)
|
|
81
|
+
.join(', ');
|
|
82
|
+
console.log(chalk.gray(`Types: ${byType}`));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
spinner.fail(chalk.red(`Failed to build pattern index: ${error.message}`));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|