@rigour-labs/cli 1.0.0 ā 1.4.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/LICENSE +1 -1
- package/dist/cli.js +70 -8
- package/dist/commands/check.d.ts +5 -1
- package/dist/commands/check.js +78 -31
- package/dist/commands/explain.d.ts +1 -0
- package/dist/commands/explain.js +67 -0
- package/dist/commands/guide.d.ts +1 -0
- package/dist/commands/guide.js +22 -0
- package/dist/commands/init.d.ts +7 -1
- package/dist/commands/init.js +99 -25
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +63 -19
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +28 -0
- package/package.json +3 -3
- package/src/cli.ts +71 -9
- package/src/commands/check.ts +84 -31
- package/src/commands/explain.ts +68 -0
- package/src/commands/guide.ts +21 -0
- package/src/commands/init.ts +108 -26
- package/src/commands/run.ts +71 -19
- package/src/commands/setup.ts +28 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Rigour
|
|
3
|
+
Copyright (c) 2026 Ashutosh (https://github.com/erashu212), Rigour Labs.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/dist/cli.js
CHANGED
|
@@ -1,33 +1,95 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
7
|
const commander_1 = require("commander");
|
|
5
8
|
const init_js_1 = require("./commands/init.js");
|
|
6
9
|
const check_js_1 = require("./commands/check.js");
|
|
10
|
+
const explain_js_1 = require("./commands/explain.js");
|
|
7
11
|
const run_js_1 = require("./commands/run.js");
|
|
12
|
+
const guide_js_1 = require("./commands/guide.js");
|
|
13
|
+
const setup_js_1 = require("./commands/setup.js");
|
|
14
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
15
|
const program = new commander_1.Command();
|
|
9
16
|
program
|
|
10
17
|
.name('rigour')
|
|
11
|
-
.description('
|
|
12
|
-
.version('1.
|
|
18
|
+
.description('š”ļø Rigour: The Quality Gate Loop for AI-Assisted Engineering')
|
|
19
|
+
.version('1.3.0')
|
|
20
|
+
.addHelpText('before', chalk_1.default.bold.cyan(`
|
|
21
|
+
____ _
|
|
22
|
+
/ __ \\(_)____ ___ __ __ _____
|
|
23
|
+
/ /_/ // // __ \`/ / / / / // ___/
|
|
24
|
+
/ _, _// // /_/ // /_/ / / // /
|
|
25
|
+
/_/ |_|/_/ \\__, / \\__,_/_/ /_/
|
|
26
|
+
/____/
|
|
27
|
+
`));
|
|
13
28
|
program
|
|
14
29
|
.command('init')
|
|
15
|
-
.description('Initialize
|
|
16
|
-
.
|
|
17
|
-
|
|
30
|
+
.description('Initialize Rigour in the current directory')
|
|
31
|
+
.option('-p, --preset <name>', 'Project preset (ui, api, infra, data)')
|
|
32
|
+
.option('--paradigm <name>', 'Coding paradigm (oop, functional, minimal)')
|
|
33
|
+
.option('--dry-run', 'Show detected configuration without writing files')
|
|
34
|
+
.option('--explain', 'Show detection markers for roles and paradigms')
|
|
35
|
+
.addHelpText('after', `
|
|
36
|
+
Examples:
|
|
37
|
+
$ rigour init # Auto-discover role & paradigm
|
|
38
|
+
$ rigour init --preset api --explain # Force API role and show why
|
|
39
|
+
`)
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
await (0, init_js_1.initCommand)(process.cwd(), options);
|
|
18
42
|
});
|
|
19
43
|
program
|
|
20
44
|
.command('check')
|
|
21
45
|
.description('Run quality gate checks')
|
|
46
|
+
.option('--ci', 'CI mode (minimal output, non-zero exit on fail)')
|
|
47
|
+
.option('--json', 'Output report in JSON format')
|
|
48
|
+
.addHelpText('after', `
|
|
49
|
+
Examples:
|
|
50
|
+
$ rigour check # Run interactive check
|
|
51
|
+
$ rigour check --ci # Run in CI environment
|
|
52
|
+
`)
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
await (0, check_js_1.checkCommand)(process.cwd(), options);
|
|
55
|
+
});
|
|
56
|
+
program
|
|
57
|
+
.command('explain')
|
|
58
|
+
.description('Explain the last quality gate report with actionable bullets')
|
|
59
|
+
.addHelpText('after', `
|
|
60
|
+
Examples:
|
|
61
|
+
$ rigour explain # Get a human-readable violation summary
|
|
62
|
+
`)
|
|
22
63
|
.action(async () => {
|
|
23
|
-
await (0,
|
|
64
|
+
await (0, explain_js_1.explainCommand)(process.cwd());
|
|
24
65
|
});
|
|
25
66
|
program
|
|
26
67
|
.command('run')
|
|
27
68
|
.description('Execute an agent command in a loop until quality gates pass')
|
|
28
69
|
.argument('[command...]', 'The agent command to run (e.g., cursor-agent ...)')
|
|
29
|
-
.option('-
|
|
70
|
+
.option('-c, --max-cycles <number>', 'Maximum number of loop iterations', '3')
|
|
71
|
+
.option('--fail-fast', 'Abort loop immediately on first gate failure')
|
|
72
|
+
.addHelpText('after', `
|
|
73
|
+
Examples:
|
|
74
|
+
$ rigour run -- claude "fix issues" # Loop Claude until PASS
|
|
75
|
+
$ rigour run -c 5 -- cursor-agent # Run Cursor agent for up to 5 cycles
|
|
76
|
+
`)
|
|
30
77
|
.action(async (args, options) => {
|
|
31
|
-
await (0, run_js_1.runLoop)(process.cwd(), args, {
|
|
78
|
+
await (0, run_js_1.runLoop)(process.cwd(), args, {
|
|
79
|
+
iterations: parseInt(options.maxCycles),
|
|
80
|
+
failFast: !!options.failFast
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
program
|
|
84
|
+
.command('guide')
|
|
85
|
+
.description('Show the interactive engineering guide')
|
|
86
|
+
.action(async () => {
|
|
87
|
+
await (0, guide_js_1.guideCommand)();
|
|
88
|
+
});
|
|
89
|
+
program
|
|
90
|
+
.command('setup')
|
|
91
|
+
.description('Show installation and global setup guidance')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
await (0, setup_js_1.setupCommand)();
|
|
32
94
|
});
|
|
33
95
|
program.parse();
|
package/dist/commands/check.d.ts
CHANGED
package/dist/commands/check.js
CHANGED
|
@@ -9,43 +9,90 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
11
|
const core_1 = require("@rigour-labs/core");
|
|
12
|
-
|
|
12
|
+
// Exit codes per spec
|
|
13
|
+
const EXIT_PASS = 0;
|
|
14
|
+
const EXIT_FAIL = 1;
|
|
15
|
+
const EXIT_CONFIG_ERROR = 2;
|
|
16
|
+
const EXIT_INTERNAL_ERROR = 3;
|
|
17
|
+
async function checkCommand(cwd, options = {}) {
|
|
13
18
|
const configPath = path_1.default.join(cwd, 'rigour.yml');
|
|
14
19
|
if (!(await fs_extra_1.default.pathExists(configPath))) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const runner = new core_1.GateRunner(config);
|
|
23
|
-
const report = await runner.run(cwd);
|
|
24
|
-
// Write machine report
|
|
25
|
-
const reportPath = path_1.default.join(cwd, config.output.report_path);
|
|
26
|
-
await fs_extra_1.default.writeJson(reportPath, report, { spaces: 2 });
|
|
27
|
-
// Print human summary
|
|
28
|
-
if (report.status === 'PASS') {
|
|
29
|
-
console.log(chalk_1.default.green.bold('ā PASS - All quality gates satisfied.'));
|
|
20
|
+
if (options.json) {
|
|
21
|
+
console.log(JSON.stringify({ error: 'CONFIG_ERROR', message: 'rigour.yml not found' }));
|
|
22
|
+
}
|
|
23
|
+
else if (!options.ci) {
|
|
24
|
+
console.error(chalk_1.default.red('Error: rigour.yml not found. Run `rigour init` first.'));
|
|
25
|
+
}
|
|
26
|
+
process.exit(EXIT_CONFIG_ERROR);
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
try {
|
|
29
|
+
const configContent = await fs_extra_1.default.readFile(configPath, 'utf-8');
|
|
30
|
+
const rawConfig = yaml_1.default.parse(configContent);
|
|
31
|
+
const config = core_1.ConfigSchema.parse(rawConfig);
|
|
32
|
+
if (!options.ci && !options.json) {
|
|
33
|
+
console.log(chalk_1.default.blue('Running Rigour checks...\n'));
|
|
34
|
+
}
|
|
35
|
+
const runner = new core_1.GateRunner(config);
|
|
36
|
+
const report = await runner.run(cwd);
|
|
37
|
+
// Write machine report
|
|
38
|
+
const reportPath = path_1.default.join(cwd, config.output.report_path);
|
|
39
|
+
await fs_extra_1.default.writeJson(reportPath, report, { spaces: 2 });
|
|
40
|
+
// Generate Fix Packet v2 on failure
|
|
41
|
+
if (report.status === 'FAIL') {
|
|
42
|
+
const { FixPacketService } = await import('@rigour-labs/core');
|
|
43
|
+
const fixPacketService = new FixPacketService();
|
|
44
|
+
const fixPacket = fixPacketService.generate(report, config);
|
|
45
|
+
const fixPacketPath = path_1.default.join(cwd, 'rigour-fix-packet.json');
|
|
46
|
+
await fs_extra_1.default.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
|
|
47
|
+
}
|
|
48
|
+
// JSON output mode
|
|
49
|
+
if (options.json) {
|
|
50
|
+
console.log(JSON.stringify(report, null, 2));
|
|
51
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
52
|
+
}
|
|
53
|
+
// CI mode: minimal output
|
|
54
|
+
if (options.ci) {
|
|
55
|
+
if (report.status === 'PASS') {
|
|
56
|
+
console.log('PASS');
|
|
39
57
|
}
|
|
40
|
-
|
|
41
|
-
console.log(
|
|
58
|
+
else {
|
|
59
|
+
console.log(`FAIL: ${report.failures.length} violation(s)`);
|
|
60
|
+
report.failures.forEach((f) => {
|
|
61
|
+
console.log(` - [${f.id}] ${f.title}`);
|
|
62
|
+
});
|
|
42
63
|
}
|
|
43
|
-
|
|
64
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
44
65
|
}
|
|
45
|
-
|
|
66
|
+
// Normal human-readable output
|
|
67
|
+
if (report.status === 'PASS') {
|
|
68
|
+
console.log(chalk_1.default.green.bold('ā PASS - All quality gates satisfied.'));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(chalk_1.default.red.bold('ā FAIL - Quality gate violations found.\n'));
|
|
72
|
+
for (const failure of report.failures) {
|
|
73
|
+
console.log(chalk_1.default.red(`[${failure.id}] ${failure.title}`));
|
|
74
|
+
console.log(chalk_1.default.dim(` Details: ${failure.details}`));
|
|
75
|
+
if (failure.files && failure.files.length > 0) {
|
|
76
|
+
console.log(chalk_1.default.dim(' Files:'));
|
|
77
|
+
failure.files.forEach((f) => console.log(chalk_1.default.dim(` - ${f}`)));
|
|
78
|
+
}
|
|
79
|
+
if (failure.hint) {
|
|
80
|
+
console.log(chalk_1.default.cyan(` Hint: ${failure.hint}`));
|
|
81
|
+
}
|
|
82
|
+
console.log('');
|
|
83
|
+
}
|
|
84
|
+
console.log(chalk_1.default.yellow(`See ${config.output.report_path} for full details.`));
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk_1.default.dim(`\nFinished in ${report.stats.duration_ms}ms`));
|
|
87
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
46
88
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log(JSON.stringify({ error: 'INTERNAL_ERROR', message: error.message }));
|
|
92
|
+
}
|
|
93
|
+
else if (!options.ci) {
|
|
94
|
+
console.error(chalk_1.default.red(`Internal error: ${error.message}`));
|
|
95
|
+
}
|
|
96
|
+
process.exit(EXIT_INTERNAL_ERROR);
|
|
50
97
|
}
|
|
51
98
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function explainCommand(cwd: string): Promise<void>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.explainCommand = explainCommand;
|
|
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');
|
|
13
|
+
// Try to read custom path from config
|
|
14
|
+
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
15
|
+
try {
|
|
16
|
+
const yaml = await import('yaml');
|
|
17
|
+
const configContent = await fs_extra_1.default.readFile(configPath, 'utf-8');
|
|
18
|
+
const config = yaml.parse(configContent);
|
|
19
|
+
if (config?.output?.report_path) {
|
|
20
|
+
reportPath = path_1.default.join(cwd, config.output.report_path);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (e) { }
|
|
24
|
+
}
|
|
25
|
+
if (!(await fs_extra_1.default.pathExists(reportPath))) {
|
|
26
|
+
console.error(chalk_1.default.red(`Error: No report found at ${reportPath}`));
|
|
27
|
+
console.error(chalk_1.default.dim('Run `rigour check` first to generate a report.'));
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const reportContent = await fs_extra_1.default.readFile(reportPath, 'utf-8');
|
|
32
|
+
const report = JSON.parse(reportContent);
|
|
33
|
+
console.log(chalk_1.default.bold('\nš Rigour Report Explanation\n'));
|
|
34
|
+
console.log(chalk_1.default.bold('Status: ') + (report.status === 'PASS'
|
|
35
|
+
? chalk_1.default.green.bold('ā
PASS')
|
|
36
|
+
: chalk_1.default.red.bold('š FAIL')));
|
|
37
|
+
console.log(chalk_1.default.bold('\nGate Summary:'));
|
|
38
|
+
for (const [gate, status] of Object.entries(report.summary || {})) {
|
|
39
|
+
const icon = status === 'PASS' ? 'ā
' : status === 'FAIL' ? 'ā' : 'āļø';
|
|
40
|
+
console.log(` ${icon} ${gate}: ${status}`);
|
|
41
|
+
}
|
|
42
|
+
if (report.failures && report.failures.length > 0) {
|
|
43
|
+
console.log(chalk_1.default.bold.red(`\nš§ ${report.failures.length} Violation(s) to Fix:\n`));
|
|
44
|
+
report.failures.forEach((failure, index) => {
|
|
45
|
+
console.log(chalk_1.default.white(`${index + 1}. `) + chalk_1.default.bold.yellow(`[${failure.id.toUpperCase()}]`) + chalk_1.default.white(` ${failure.title}`));
|
|
46
|
+
console.log(chalk_1.default.dim(` āā ${failure.details}`));
|
|
47
|
+
if (failure.files && failure.files.length > 0) {
|
|
48
|
+
console.log(chalk_1.default.cyan(` š Files: ${failure.files.join(', ')}`));
|
|
49
|
+
}
|
|
50
|
+
if (failure.hint) {
|
|
51
|
+
console.log(chalk_1.default.green(` š” Hint: ${failure.hint}`));
|
|
52
|
+
}
|
|
53
|
+
console.log('');
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else if (report.status === 'PASS') {
|
|
57
|
+
console.log(chalk_1.default.green('\n⨠All quality gates passed! No violations found.\n'));
|
|
58
|
+
}
|
|
59
|
+
if (report.stats) {
|
|
60
|
+
console.log(chalk_1.default.dim(`Duration: ${report.stats.duration_ms}ms`));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error(chalk_1.default.red(`Error reading report: ${error.message}`));
|
|
65
|
+
process.exit(3);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function guideCommand(): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.guideCommand = guideCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
async function guideCommand() {
|
|
9
|
+
console.log(chalk_1.default.bold.cyan('\nš”ļø Rigour Labs | The Engineering Guide\n'));
|
|
10
|
+
console.log(chalk_1.default.bold('Getting Started:'));
|
|
11
|
+
console.log(chalk_1.default.dim(' 1. Run ') + chalk_1.default.cyan('rigour init') + chalk_1.default.dim(' to detect your project role and apply standards.'));
|
|
12
|
+
console.log(chalk_1.default.dim(' 2. Run ') + chalk_1.default.cyan('rigour check') + chalk_1.default.dim(' to see existing violations.'));
|
|
13
|
+
console.log(chalk_1.default.dim(' 3. Run ') + chalk_1.default.cyan('rigour run -- <your-agent-command>') + chalk_1.default.dim(' to automate the fix loop.\n'));
|
|
14
|
+
console.log(chalk_1.default.bold('Key Concepts:'));
|
|
15
|
+
console.log(chalk_1.default.yellow(' ⢠Fix Packet v2') + chalk_1.default.dim(': Structured diagnostics fed directly into AI agents.'));
|
|
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'));
|
|
22
|
+
}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface InitOptions {
|
|
2
|
+
preset?: string;
|
|
3
|
+
paradigm?: string;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
explain?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function initCommand(cwd: string, options?: InitOptions): Promise<void>;
|
package/dist/commands/init.js
CHANGED
|
@@ -9,14 +9,70 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
11
|
const core_1 = require("@rigour-labs/core");
|
|
12
|
-
async function initCommand(cwd) {
|
|
12
|
+
async function initCommand(cwd, options = {}) {
|
|
13
13
|
const discovery = new core_1.DiscoveryService();
|
|
14
|
-
const
|
|
14
|
+
const result = await discovery.discover(cwd);
|
|
15
|
+
let recommendedConfig = result.config;
|
|
16
|
+
// Override with user options if provided and re-apply template logic if necessary
|
|
17
|
+
if (options.preset || options.paradigm) {
|
|
18
|
+
const core = await import('@rigour-labs/core');
|
|
19
|
+
let customBase = { ...core.UNIVERSAL_CONFIG };
|
|
20
|
+
if (options.preset) {
|
|
21
|
+
const t = core.TEMPLATES.find((t) => t.name === options.preset);
|
|
22
|
+
if (t)
|
|
23
|
+
customBase = discovery.mergeConfig(customBase, t.config);
|
|
24
|
+
}
|
|
25
|
+
else if (recommendedConfig.preset) {
|
|
26
|
+
const t = core.TEMPLATES.find((t) => t.name === recommendedConfig.preset);
|
|
27
|
+
if (t)
|
|
28
|
+
customBase = discovery.mergeConfig(customBase, t.config);
|
|
29
|
+
}
|
|
30
|
+
if (options.paradigm) {
|
|
31
|
+
const t = core.PARADIGM_TEMPLATES.find((t) => t.name === options.paradigm);
|
|
32
|
+
if (t)
|
|
33
|
+
customBase = discovery.mergeConfig(customBase, t.config);
|
|
34
|
+
}
|
|
35
|
+
else if (recommendedConfig.paradigm) {
|
|
36
|
+
const t = core.PARADIGM_TEMPLATES.find((t) => t.name === recommendedConfig.paradigm);
|
|
37
|
+
if (t)
|
|
38
|
+
customBase = discovery.mergeConfig(customBase, t.config);
|
|
39
|
+
}
|
|
40
|
+
recommendedConfig = customBase;
|
|
41
|
+
if (options.preset)
|
|
42
|
+
recommendedConfig.preset = options.preset;
|
|
43
|
+
if (options.paradigm)
|
|
44
|
+
recommendedConfig.paradigm = options.paradigm;
|
|
45
|
+
}
|
|
46
|
+
if (options.dryRun || options.explain) {
|
|
47
|
+
console.log(chalk_1.default.bold.blue('\nš Rigour Auto-Discovery (Dry Run):'));
|
|
48
|
+
if (recommendedConfig.preset) {
|
|
49
|
+
console.log(chalk_1.default.cyan(` Role: `) + chalk_1.default.bold(recommendedConfig.preset.toUpperCase()));
|
|
50
|
+
if (options.explain && result.matches.preset) {
|
|
51
|
+
console.log(chalk_1.default.dim(` (Marker found: ${result.matches.preset.marker})`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (recommendedConfig.paradigm) {
|
|
55
|
+
console.log(chalk_1.default.cyan(` Paradigm: `) + chalk_1.default.bold(recommendedConfig.paradigm.toUpperCase()));
|
|
56
|
+
if (options.explain && result.matches.paradigm) {
|
|
57
|
+
console.log(chalk_1.default.dim(` (Marker found: ${result.matches.paradigm.marker})`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log(chalk_1.default.yellow('\n[DRY RUN] No files will be written.'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
15
63
|
const configPath = path_1.default.join(cwd, 'rigour.yml');
|
|
16
64
|
if (await fs_extra_1.default.pathExists(configPath)) {
|
|
17
65
|
console.log(chalk_1.default.yellow('rigour.yml already exists. Skipping initialization.'));
|
|
18
66
|
return;
|
|
19
67
|
}
|
|
68
|
+
console.log(chalk_1.default.bold.blue('\nš Rigour Auto-Discovery:'));
|
|
69
|
+
if (recommendedConfig.preset) {
|
|
70
|
+
console.log(chalk_1.default.cyan(` Role: `) + chalk_1.default.bold(recommendedConfig.preset.toUpperCase()));
|
|
71
|
+
}
|
|
72
|
+
if (recommendedConfig.paradigm) {
|
|
73
|
+
console.log(chalk_1.default.cyan(` Paradigm: `) + chalk_1.default.bold(recommendedConfig.paradigm.toUpperCase()));
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
20
76
|
await fs_extra_1.default.writeFile(configPath, yaml_1.default.stringify(recommendedConfig));
|
|
21
77
|
console.log(chalk_1.default.green('ā Created rigour.yml'));
|
|
22
78
|
// Create required directories and files
|
|
@@ -28,33 +84,51 @@ async function initCommand(cwd) {
|
|
|
28
84
|
console.log(chalk_1.default.dim(` - Created ${file}`));
|
|
29
85
|
}
|
|
30
86
|
}
|
|
31
|
-
// Agent Handshake (
|
|
87
|
+
// Agent Handshake (Universal / AntiGravity / Cursor)
|
|
88
|
+
const rigourDocsDir = path_1.default.join(cwd, 'docs');
|
|
89
|
+
await fs_extra_1.default.ensureDir(rigourDocsDir);
|
|
90
|
+
const instructionsPath = path_1.default.join(rigourDocsDir, 'AGENT_INSTRUCTIONS.md');
|
|
91
|
+
const ruleContent = `# š”ļø Rigour: Engineering Excellence Protocol
|
|
92
|
+
|
|
93
|
+
You are an Elite Software Engineer. You do not just write code that "works"; you write code that is **modular, maintainable, and rigorously verified.**
|
|
94
|
+
|
|
95
|
+
## š¦ The Rigour Loop (Mandatory)
|
|
96
|
+
Before claiming "Done" for any task, you MUST follow this loop:
|
|
97
|
+
|
|
98
|
+
1. **Check**: Run \`npx @rigour-labs/cli check\` to verify compliance.
|
|
99
|
+
2. **Analyze**: If it fails, read \`rigour-fix-packet.json\` (V2 High-Fidelity) for exact failure points and constraints.
|
|
100
|
+
3. **Refactor**: Apply **SOLID** and **DRY** principles to resolve the violations according to constraints.
|
|
101
|
+
4. **Repeat**: Continue until \`npx @rigour-labs/cli check\` returns **PASS**.
|
|
102
|
+
|
|
103
|
+
## š§© Engineering Standards
|
|
104
|
+
- **Single Responsibility**: Keep files small and focused (max 500 lines).
|
|
105
|
+
- **DRY (Don't Repeat Yourself)**: Extract common logic into utilities.
|
|
106
|
+
- **Done is Done**: No \`TODO\` or \`FIXME\` comments allowed in the final state.
|
|
107
|
+
- **Memory Preservation**: Always update docs/SPEC.md, docs/ARCH.md, docs/DECISIONS.md.
|
|
108
|
+
|
|
109
|
+
## š ļø Commands
|
|
110
|
+
\`\`\`bash
|
|
111
|
+
# Verify current state
|
|
112
|
+
npx @rigour-labs/cli check
|
|
113
|
+
|
|
114
|
+
# Self-healing agent loop
|
|
115
|
+
npx @rigour-labs/cli run -- <agent-command>
|
|
116
|
+
\`\`\`
|
|
117
|
+
`;
|
|
118
|
+
// 1. Create Universal Instructions
|
|
119
|
+
await fs_extra_1.default.writeFile(instructionsPath, ruleContent);
|
|
120
|
+
console.log(chalk_1.default.green('ā Initialized Universal Agent Handshake (docs/AGENT_INSTRUCTIONS.md)'));
|
|
121
|
+
// 2. Create Cursor Specific Rules (.mdc)
|
|
32
122
|
const cursorRulesDir = path_1.default.join(cwd, '.cursor', 'rules');
|
|
33
123
|
await fs_extra_1.default.ensureDir(cursorRulesDir);
|
|
34
|
-
const
|
|
35
|
-
const
|
|
124
|
+
const mdcPath = path_1.default.join(cursorRulesDir, 'rigour.mdc');
|
|
125
|
+
const mdcContent = `---
|
|
36
126
|
description: Enforcement of Rigour quality gates and best practices.
|
|
37
127
|
globs: **/*
|
|
38
128
|
---
|
|
39
129
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
## Core Rules
|
|
45
|
-
- **Never claim done** until you run \`rigour check\` and it returns PASS.
|
|
46
|
-
- If checks FAIL, fix **only** the listed failures. Do not add new features or refactor unrelated code.
|
|
47
|
-
- Maintain project memory in \`docs/SPEC.md\`, \`docs/ARCH.md\`, and \`docs/DECISIONS.md\`.
|
|
48
|
-
- Keep files modular. If a file exceeds 500 lines, you MUST break it into smaller components.
|
|
49
|
-
- No \`TODO\` or \`FIXME\` comments allowed in the final submission.
|
|
50
|
-
|
|
51
|
-
## Workflow
|
|
52
|
-
1. Write/Modify code.
|
|
53
|
-
2. Run \`rigour check\`.
|
|
54
|
-
3. If FAIL: Read \`rigour-report.json\` for exact failure points and fix them.
|
|
55
|
-
4. If PASS: You may claim task completion.
|
|
56
|
-
`;
|
|
57
|
-
await fs_extra_1.default.writeFile(rulePath, ruleContent);
|
|
58
|
-
console.log(chalk_1.default.green('ā Initialized Agent Handshake (.cursor/rules/rigour.mdc)'));
|
|
59
|
-
console.log(chalk_1.default.blue('\nRigour is ready. Run `rigour check` to verify your project.'));
|
|
130
|
+
${ruleContent}`;
|
|
131
|
+
await fs_extra_1.default.writeFile(mdcPath, mdcContent);
|
|
132
|
+
console.log(chalk_1.default.green('ā Initialized Cursor Handshake (.cursor/rules/rigour.mdc)'));
|
|
133
|
+
console.log(chalk_1.default.blue('\nRigour is ready. Run `npx @rigour-labs/cli check` to verify your project.'));
|
|
60
134
|
}
|
package/dist/commands/run.d.ts
CHANGED
package/dist/commands/run.js
CHANGED
|
@@ -10,11 +10,16 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
10
10
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
11
|
const execa_1 = require("execa");
|
|
12
12
|
const core_1 = require("@rigour-labs/core");
|
|
13
|
+
// Exit codes per spec
|
|
14
|
+
const EXIT_PASS = 0;
|
|
15
|
+
const EXIT_FAIL = 1;
|
|
16
|
+
const EXIT_CONFIG_ERROR = 2;
|
|
17
|
+
const EXIT_INTERNAL_ERROR = 3;
|
|
13
18
|
async function runLoop(cwd, agentArgs, options) {
|
|
14
19
|
const configPath = path_1.default.join(cwd, 'rigour.yml');
|
|
15
20
|
if (!(await fs_extra_1.default.pathExists(configPath))) {
|
|
16
21
|
console.error(chalk_1.default.red('Error: rigour.yml not found. Run `rigour init` first.'));
|
|
17
|
-
process.exit(
|
|
22
|
+
process.exit(EXIT_CONFIG_ERROR);
|
|
18
23
|
}
|
|
19
24
|
try {
|
|
20
25
|
const configContent = await fs_extra_1.default.readFile(configPath, 'utf-8');
|
|
@@ -28,42 +33,81 @@ async function runLoop(cwd, agentArgs, options) {
|
|
|
28
33
|
console.log(chalk_1.default.bold.blue(`\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`));
|
|
29
34
|
console.log(chalk_1.default.bold.blue(` RIGOUR LOOP: Iteration ${iteration}/${maxIterations}`));
|
|
30
35
|
console.log(chalk_1.default.bold.blue(`āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`));
|
|
31
|
-
// 1.
|
|
32
|
-
|
|
36
|
+
// 1. Prepare Command
|
|
37
|
+
let currentArgs = [...agentArgs];
|
|
38
|
+
if (iteration > 1 && agentArgs.length > 0) {
|
|
39
|
+
// Iteration contract: In later cycles, we focus strictly on the fix packet
|
|
40
|
+
console.log(chalk_1.default.yellow(`\nš REFINEMENT CYCLE - Instructing agent to fix specific violations...`));
|
|
41
|
+
// We keep the first part of the command (the agent) but can append or wrap
|
|
42
|
+
// For simplicity, we assume the agent can read the JSON file we generate
|
|
43
|
+
}
|
|
44
|
+
// Snapshot changed files before agent runs
|
|
45
|
+
let beforeFiles = [];
|
|
46
|
+
try {
|
|
47
|
+
const { stdout } = await (0, execa_1.execa)('git', ['status', '--porcelain'], { cwd });
|
|
48
|
+
beforeFiles = stdout.split('\n').filter(l => l.trim()).map(l => l.slice(3).trim());
|
|
49
|
+
}
|
|
50
|
+
catch (e) { }
|
|
51
|
+
// 2. Run the agent command
|
|
52
|
+
if (currentArgs.length > 0) {
|
|
33
53
|
console.log(chalk_1.default.cyan(`\nš DEPLOYING AGENT:`));
|
|
34
|
-
console.log(chalk_1.default.dim(` Command: ${
|
|
54
|
+
console.log(chalk_1.default.dim(` Command: ${currentArgs.join(' ')}`));
|
|
35
55
|
try {
|
|
36
|
-
await (0, execa_1.execa)(
|
|
56
|
+
await (0, execa_1.execa)(currentArgs[0], currentArgs.slice(1), { shell: true, stdio: 'inherit', cwd });
|
|
37
57
|
}
|
|
38
58
|
catch (error) {
|
|
39
59
|
console.warn(chalk_1.default.yellow(`\nā ļø Agent command finished with non-zero exit code. Rigour will now verify state...`));
|
|
40
60
|
}
|
|
41
61
|
}
|
|
42
|
-
//
|
|
62
|
+
// Snapshot changed files after agent runs
|
|
63
|
+
let afterFiles = [];
|
|
64
|
+
try {
|
|
65
|
+
const { stdout } = await (0, execa_1.execa)('git', ['status', '--porcelain'], { cwd });
|
|
66
|
+
afterFiles = stdout.split('\n').filter(l => l.trim()).map(l => l.slice(3).trim());
|
|
67
|
+
}
|
|
68
|
+
catch (e) { }
|
|
69
|
+
const changedThisCycle = afterFiles.filter(f => !beforeFiles.includes(f));
|
|
70
|
+
const maxFiles = config.gates.safety?.max_files_changed_per_cycle || 10;
|
|
71
|
+
if (changedThisCycle.length > maxFiles) {
|
|
72
|
+
console.log(chalk_1.default.red.bold(`\nš SAFETY RAIL ABORT: Agent changed ${changedThisCycle.length} files (max: ${maxFiles}).`));
|
|
73
|
+
console.log(chalk_1.default.red(` This looks like explosive behavior. Check your agent's instructions.`));
|
|
74
|
+
process.exit(EXIT_FAIL);
|
|
75
|
+
}
|
|
76
|
+
// 3. Run Rigour Check
|
|
43
77
|
console.log(chalk_1.default.magenta('\nš AUDITING QUALITY GATES...'));
|
|
44
78
|
const report = await runner.run(cwd);
|
|
79
|
+
// Write report
|
|
80
|
+
const reportPath = path_1.default.join(cwd, config.output.report_path);
|
|
81
|
+
await fs_extra_1.default.writeJson(reportPath, report, { spaces: 2 });
|
|
45
82
|
if (report.status === 'PASS') {
|
|
46
83
|
console.log(chalk_1.default.green.bold('\n⨠PASS - All quality gates satisfied.'));
|
|
47
84
|
console.log(chalk_1.default.green(` Your solution meets the required Engineering Rigour criteria.\n`));
|
|
48
85
|
return;
|
|
49
86
|
}
|
|
50
|
-
//
|
|
87
|
+
// 4. Generate Fix Packet v2
|
|
88
|
+
const { FixPacketService } = await import('@rigour-labs/core');
|
|
89
|
+
const fixPacketService = new FixPacketService();
|
|
90
|
+
const fixPacket = fixPacketService.generate(report, config);
|
|
91
|
+
const fixPacketPath = path_1.default.join(cwd, 'rigour-fix-packet.json');
|
|
92
|
+
await fs_extra_1.default.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
|
|
51
93
|
console.log(chalk_1.default.red.bold(`\nš FAIL - Found ${report.failures.length} engineering violations.`));
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
console.log(chalk_1.default.
|
|
94
|
+
console.log(chalk_1.default.dim(` Fix Packet generated: rigour-fix-packet.json`));
|
|
95
|
+
if (options.failFast) {
|
|
96
|
+
console.log(chalk_1.default.red.bold(`\nš FAIL-FAST: Aborting loop as requested.`));
|
|
97
|
+
process.exit(EXIT_FAIL);
|
|
98
|
+
}
|
|
99
|
+
// Print summary
|
|
100
|
+
const summary = report.failures.map((f, i) => {
|
|
101
|
+
return chalk_1.default.white(`${i + 1}. `) + chalk_1.default.bold.red(`[${f.id.toUpperCase()}] `) + chalk_1.default.white(f.title);
|
|
102
|
+
}).join('\n');
|
|
103
|
+
console.log(chalk_1.default.bold.white('\nš VIOLATIONS SUMMARY:'));
|
|
104
|
+
console.log(summary);
|
|
62
105
|
if (iteration === maxIterations) {
|
|
63
106
|
console.log(chalk_1.default.red.bold(`\nā CRITICAL: Reached maximum iterations (${maxIterations}).`));
|
|
64
107
|
console.log(chalk_1.default.red(` Quality gates remain unfulfilled. Refactor manually or check agent logs.`));
|
|
65
|
-
process.exit(
|
|
108
|
+
process.exit(EXIT_FAIL);
|
|
66
109
|
}
|
|
110
|
+
console.log(chalk_1.default.dim('\nReturning control to agent for the next refinement cycle...'));
|
|
67
111
|
}
|
|
68
112
|
}
|
|
69
113
|
catch (error) {
|
|
@@ -71,6 +115,6 @@ async function runLoop(cwd, agentArgs, options) {
|
|
|
71
115
|
if (error.issues) {
|
|
72
116
|
console.error(chalk_1.default.dim(JSON.stringify(error.issues, null, 2)));
|
|
73
117
|
}
|
|
74
|
-
process.exit(
|
|
118
|
+
process.exit(EXIT_INTERNAL_ERROR);
|
|
75
119
|
}
|
|
76
120
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupCommand(): Promise<void>;
|