@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
|
@@ -0,0 +1,28 @@
|
|
|
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.setupCommand = setupCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
async function setupCommand() {
|
|
9
|
+
console.log(chalk_1.default.bold.cyan('\nš ļø Rigour Labs | Setup & Installation\n'));
|
|
10
|
+
console.log(chalk_1.default.bold('1. Global Installation (Recommended)'));
|
|
11
|
+
console.log(chalk_1.default.dim(' To use Rigour anywhere in your terminal:'));
|
|
12
|
+
console.log(chalk_1.default.green(' $ npm install -g @rigour-labs/cli\n'));
|
|
13
|
+
console.log(chalk_1.default.bold('2. Project-Local installation'));
|
|
14
|
+
console.log(chalk_1.default.dim(' To keep Rigour versioned with your project:'));
|
|
15
|
+
console.log(chalk_1.default.green(' $ npm install --save-dev @rigour-labs/cli\n'));
|
|
16
|
+
console.log(chalk_1.default.bold('3. Standalone Binaries (Zero-Install)'));
|
|
17
|
+
console.log(chalk_1.default.dim(' If you do not want to use Node.js:'));
|
|
18
|
+
console.log(chalk_1.default.dim(' ⢠macOS: ') + chalk_1.default.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-macos'));
|
|
19
|
+
console.log(chalk_1.default.dim(' ⢠Linux: ') + chalk_1.default.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-linux'));
|
|
20
|
+
console.log(chalk_1.default.dim(' ⢠Windows: ') + chalk_1.default.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-windows.exe\n'));
|
|
21
|
+
console.log(chalk_1.default.bold('4. MCP Integration (for AI Agents)'));
|
|
22
|
+
console.log(chalk_1.default.dim(' To let Cursor or Claude use Rigour natively:'));
|
|
23
|
+
console.log(chalk_1.default.dim(' Path to MCP: ') + chalk_1.default.cyan('packages/rigour-mcp/dist/index.js'));
|
|
24
|
+
console.log(chalk_1.default.dim(' Add this to your Cursor/Claude settings.\n'));
|
|
25
|
+
console.log(chalk_1.default.bold('Update Guidance:'));
|
|
26
|
+
console.log(chalk_1.default.dim(' Keep Rigour sharp by updating regularly:'));
|
|
27
|
+
console.log(chalk_1.default.green(' $ npm install -g @rigour-labs/cli@latest\n'));
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"bin": {
|
|
5
5
|
"rigour": "dist/cli.js"
|
|
6
6
|
},
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "https://github.com/rigour-labs/rigour"
|
|
10
10
|
},
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"fs-extra": "^11.2.0",
|
|
21
21
|
"globby": "^14.0.1",
|
|
22
22
|
"yaml": "^2.8.2",
|
|
23
|
-
"@rigour-labs/core": "1.
|
|
23
|
+
"@rigour-labs/core": "1.4.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/fs-extra": "^11.0.4",
|
package/src/cli.ts
CHANGED
|
@@ -2,36 +2,98 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { initCommand } from './commands/init.js';
|
|
4
4
|
import { checkCommand } from './commands/check.js';
|
|
5
|
+
import { explainCommand } from './commands/explain.js';
|
|
5
6
|
import { runLoop } from './commands/run.js';
|
|
7
|
+
import { guideCommand } from './commands/guide.js';
|
|
8
|
+
import { setupCommand } from './commands/setup.js';
|
|
9
|
+
import chalk from 'chalk';
|
|
6
10
|
|
|
7
11
|
const program = new Command();
|
|
8
12
|
|
|
9
13
|
program
|
|
10
14
|
.name('rigour')
|
|
11
|
-
.description('
|
|
12
|
-
.version('1.
|
|
15
|
+
.description('š”ļø Rigour: The Quality Gate Loop for AI-Assisted Engineering')
|
|
16
|
+
.version('1.3.0')
|
|
17
|
+
.addHelpText('before', chalk.bold.cyan(`
|
|
18
|
+
____ _
|
|
19
|
+
/ __ \\(_)____ ___ __ __ _____
|
|
20
|
+
/ /_/ // // __ \`/ / / / / // ___/
|
|
21
|
+
/ _, _// // /_/ // /_/ / / // /
|
|
22
|
+
/_/ |_|/_/ \\__, / \\__,_/_/ /_/
|
|
23
|
+
/____/
|
|
24
|
+
`));
|
|
13
25
|
|
|
14
26
|
program
|
|
15
27
|
.command('init')
|
|
16
|
-
.description('Initialize
|
|
17
|
-
.
|
|
18
|
-
|
|
28
|
+
.description('Initialize Rigour in the current directory')
|
|
29
|
+
.option('-p, --preset <name>', 'Project preset (ui, api, infra, data)')
|
|
30
|
+
.option('--paradigm <name>', 'Coding paradigm (oop, functional, minimal)')
|
|
31
|
+
.option('--dry-run', 'Show detected configuration without writing files')
|
|
32
|
+
.option('--explain', 'Show detection markers for roles and paradigms')
|
|
33
|
+
.addHelpText('after', `
|
|
34
|
+
Examples:
|
|
35
|
+
$ rigour init # Auto-discover role & paradigm
|
|
36
|
+
$ rigour init --preset api --explain # Force API role and show why
|
|
37
|
+
`)
|
|
38
|
+
.action(async (options: any) => {
|
|
39
|
+
await initCommand(process.cwd(), options);
|
|
19
40
|
});
|
|
20
41
|
|
|
21
42
|
program
|
|
22
43
|
.command('check')
|
|
23
44
|
.description('Run quality gate checks')
|
|
45
|
+
.option('--ci', 'CI mode (minimal output, non-zero exit on fail)')
|
|
46
|
+
.option('--json', 'Output report in JSON format')
|
|
47
|
+
.addHelpText('after', `
|
|
48
|
+
Examples:
|
|
49
|
+
$ rigour check # Run interactive check
|
|
50
|
+
$ rigour check --ci # Run in CI environment
|
|
51
|
+
`)
|
|
52
|
+
.action(async (options: any) => {
|
|
53
|
+
await checkCommand(process.cwd(), options);
|
|
54
|
+
});
|
|
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
|
+
`)
|
|
24
63
|
.action(async () => {
|
|
25
|
-
await
|
|
64
|
+
await explainCommand(process.cwd());
|
|
26
65
|
});
|
|
27
66
|
|
|
28
67
|
program
|
|
29
68
|
.command('run')
|
|
30
69
|
.description('Execute an agent command in a loop until quality gates pass')
|
|
31
70
|
.argument('[command...]', 'The agent command to run (e.g., cursor-agent ...)')
|
|
32
|
-
.option('-
|
|
33
|
-
.
|
|
34
|
-
|
|
71
|
+
.option('-c, --max-cycles <number>', 'Maximum number of loop iterations', '3')
|
|
72
|
+
.option('--fail-fast', 'Abort loop immediately on first gate failure')
|
|
73
|
+
.addHelpText('after', `
|
|
74
|
+
Examples:
|
|
75
|
+
$ rigour run -- claude "fix issues" # Loop Claude until PASS
|
|
76
|
+
$ rigour run -c 5 -- cursor-agent # Run Cursor agent for up to 5 cycles
|
|
77
|
+
`)
|
|
78
|
+
.action(async (args: string[], options: any) => {
|
|
79
|
+
await runLoop(process.cwd(), args, {
|
|
80
|
+
iterations: parseInt(options.maxCycles),
|
|
81
|
+
failFast: !!options.failFast
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
program
|
|
86
|
+
.command('guide')
|
|
87
|
+
.description('Show the interactive engineering guide')
|
|
88
|
+
.action(async () => {
|
|
89
|
+
await guideCommand();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
program
|
|
93
|
+
.command('setup')
|
|
94
|
+
.description('Show installation and global setup guidance')
|
|
95
|
+
.action(async () => {
|
|
96
|
+
await setupCommand();
|
|
35
97
|
});
|
|
36
98
|
|
|
37
99
|
program.parse();
|
package/src/commands/check.ts
CHANGED
|
@@ -4,52 +4,105 @@ import chalk from 'chalk';
|
|
|
4
4
|
import yaml from 'yaml';
|
|
5
5
|
import { GateRunner, ConfigSchema, Failure } from '@rigour-labs/core';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Exit codes per spec
|
|
8
|
+
const EXIT_PASS = 0;
|
|
9
|
+
const EXIT_FAIL = 1;
|
|
10
|
+
const EXIT_CONFIG_ERROR = 2;
|
|
11
|
+
const EXIT_INTERNAL_ERROR = 3;
|
|
12
|
+
|
|
13
|
+
export interface CheckOptions {
|
|
14
|
+
ci?: boolean;
|
|
15
|
+
json?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function checkCommand(cwd: string, options: CheckOptions = {}) {
|
|
8
19
|
const configPath = path.join(cwd, 'rigour.yml');
|
|
9
20
|
|
|
10
21
|
if (!(await fs.pathExists(configPath))) {
|
|
11
|
-
|
|
12
|
-
|
|
22
|
+
if (options.json) {
|
|
23
|
+
console.log(JSON.stringify({ error: 'CONFIG_ERROR', message: 'rigour.yml not found' }));
|
|
24
|
+
} else if (!options.ci) {
|
|
25
|
+
console.error(chalk.red('Error: rigour.yml not found. Run `rigour init` first.'));
|
|
26
|
+
}
|
|
27
|
+
process.exit(EXIT_CONFIG_ERROR);
|
|
13
28
|
}
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
try {
|
|
31
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
32
|
+
const rawConfig = yaml.parse(configContent);
|
|
33
|
+
const config = ConfigSchema.parse(rawConfig);
|
|
18
34
|
|
|
19
|
-
|
|
35
|
+
if (!options.ci && !options.json) {
|
|
36
|
+
console.log(chalk.blue('Running Rigour checks...\n'));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const runner = new GateRunner(config);
|
|
40
|
+
const report = await runner.run(cwd);
|
|
20
41
|
|
|
21
|
-
|
|
22
|
-
|
|
42
|
+
// Write machine report
|
|
43
|
+
const reportPath = path.join(cwd, config.output.report_path);
|
|
44
|
+
await fs.writeJson(reportPath, report, { spaces: 2 });
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
// Generate Fix Packet v2 on failure
|
|
47
|
+
if (report.status === 'FAIL') {
|
|
48
|
+
const { FixPacketService } = await import('@rigour-labs/core');
|
|
49
|
+
const fixPacketService = new FixPacketService();
|
|
50
|
+
const fixPacket = fixPacketService.generate(report, config);
|
|
51
|
+
const fixPacketPath = path.join(cwd, 'rigour-fix-packet.json');
|
|
52
|
+
await fs.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
|
|
53
|
+
}
|
|
27
54
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
55
|
+
// JSON output mode
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify(report, null, 2));
|
|
58
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
59
|
+
}
|
|
33
60
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
// CI mode: minimal output
|
|
62
|
+
if (options.ci) {
|
|
63
|
+
if (report.status === 'PASS') {
|
|
64
|
+
console.log('PASS');
|
|
65
|
+
} else {
|
|
66
|
+
console.log(`FAIL: ${report.failures.length} violation(s)`);
|
|
67
|
+
report.failures.forEach((f: Failure) => {
|
|
68
|
+
console.log(` - [${f.id}] ${f.title}`);
|
|
69
|
+
});
|
|
40
70
|
}
|
|
41
|
-
|
|
42
|
-
|
|
71
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Normal human-readable output
|
|
75
|
+
if (report.status === 'PASS') {
|
|
76
|
+
console.log(chalk.green.bold('ā PASS - All quality gates satisfied.'));
|
|
77
|
+
} else {
|
|
78
|
+
console.log(chalk.red.bold('ā FAIL - Quality gate violations found.\n'));
|
|
79
|
+
|
|
80
|
+
for (const failure of report.failures as Failure[]) {
|
|
81
|
+
console.log(chalk.red(`[${failure.id}] ${failure.title}`));
|
|
82
|
+
console.log(chalk.dim(` Details: ${failure.details}`));
|
|
83
|
+
if (failure.files && failure.files.length > 0) {
|
|
84
|
+
console.log(chalk.dim(' Files:'));
|
|
85
|
+
failure.files.forEach((f: string) => console.log(chalk.dim(` - ${f}`)));
|
|
86
|
+
}
|
|
87
|
+
if (failure.hint) {
|
|
88
|
+
console.log(chalk.cyan(` Hint: ${failure.hint}`));
|
|
89
|
+
}
|
|
90
|
+
console.log('');
|
|
43
91
|
}
|
|
44
|
-
|
|
92
|
+
|
|
93
|
+
console.log(chalk.yellow(`See ${config.output.report_path} for full details.`));
|
|
45
94
|
}
|
|
46
95
|
|
|
47
|
-
console.log(chalk.
|
|
48
|
-
}
|
|
96
|
+
console.log(chalk.dim(`\nFinished in ${report.stats.duration_ms}ms`));
|
|
49
97
|
|
|
50
|
-
|
|
98
|
+
process.exit(report.status === 'PASS' ? EXIT_PASS : EXIT_FAIL);
|
|
51
99
|
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
} catch (error: any) {
|
|
101
|
+
if (options.json) {
|
|
102
|
+
console.log(JSON.stringify({ error: 'INTERNAL_ERROR', message: error.message }));
|
|
103
|
+
} else if (!options.ci) {
|
|
104
|
+
console.error(chalk.red(`Internal error: ${error.message}`));
|
|
105
|
+
}
|
|
106
|
+
process.exit(EXIT_INTERNAL_ERROR);
|
|
54
107
|
}
|
|
55
108
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export async function explainCommand(cwd: string) {
|
|
6
|
+
const configPath = path.join(cwd, 'rigour.yml');
|
|
7
|
+
let reportPath = path.join(cwd, 'rigour-report.json');
|
|
8
|
+
|
|
9
|
+
// Try to read custom path from config
|
|
10
|
+
if (await fs.pathExists(configPath)) {
|
|
11
|
+
try {
|
|
12
|
+
const yaml = await import('yaml');
|
|
13
|
+
const configContent = await fs.readFile(configPath, 'utf-8');
|
|
14
|
+
const config = yaml.parse(configContent);
|
|
15
|
+
if (config?.output?.report_path) {
|
|
16
|
+
reportPath = path.join(cwd, config.output.report_path);
|
|
17
|
+
}
|
|
18
|
+
} catch (e) { }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!(await fs.pathExists(reportPath))) {
|
|
22
|
+
console.error(chalk.red(`Error: No report found at ${reportPath}`));
|
|
23
|
+
console.error(chalk.dim('Run `rigour check` first to generate a report.'));
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const reportContent = await fs.readFile(reportPath, 'utf-8');
|
|
29
|
+
const report = JSON.parse(reportContent);
|
|
30
|
+
|
|
31
|
+
console.log(chalk.bold('\nš Rigour Report Explanation\n'));
|
|
32
|
+
console.log(chalk.bold('Status: ') + (report.status === 'PASS'
|
|
33
|
+
? chalk.green.bold('ā
PASS')
|
|
34
|
+
: chalk.red.bold('š FAIL')));
|
|
35
|
+
|
|
36
|
+
console.log(chalk.bold('\nGate Summary:'));
|
|
37
|
+
for (const [gate, status] of Object.entries(report.summary || {})) {
|
|
38
|
+
const icon = status === 'PASS' ? 'ā
' : status === 'FAIL' ? 'ā' : 'āļø';
|
|
39
|
+
console.log(` ${icon} ${gate}: ${status}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (report.failures && report.failures.length > 0) {
|
|
43
|
+
console.log(chalk.bold.red(`\nš§ ${report.failures.length} Violation(s) to Fix:\n`));
|
|
44
|
+
|
|
45
|
+
report.failures.forEach((failure: any, index: number) => {
|
|
46
|
+
console.log(chalk.white(`${index + 1}. `) + chalk.bold.yellow(`[${failure.id.toUpperCase()}]`) + chalk.white(` ${failure.title}`));
|
|
47
|
+
console.log(chalk.dim(` āā ${failure.details}`));
|
|
48
|
+
if (failure.files && failure.files.length > 0) {
|
|
49
|
+
console.log(chalk.cyan(` š Files: ${failure.files.join(', ')}`));
|
|
50
|
+
}
|
|
51
|
+
if (failure.hint) {
|
|
52
|
+
console.log(chalk.green(` š” Hint: ${failure.hint}`));
|
|
53
|
+
}
|
|
54
|
+
console.log('');
|
|
55
|
+
});
|
|
56
|
+
} else if (report.status === 'PASS') {
|
|
57
|
+
console.log(chalk.green('\n⨠All quality gates passed! No violations found.\n'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (report.stats) {
|
|
61
|
+
console.log(chalk.dim(`Duration: ${report.stats.duration_ms}ms`));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
} catch (error: any) {
|
|
65
|
+
console.error(chalk.red(`Error reading report: ${error.message}`));
|
|
66
|
+
process.exit(3);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export async function guideCommand() {
|
|
4
|
+
console.log(chalk.bold.cyan('\nš”ļø Rigour Labs | The Engineering Guide\n'));
|
|
5
|
+
|
|
6
|
+
console.log(chalk.bold('Getting Started:'));
|
|
7
|
+
console.log(chalk.dim(' 1. Run ') + chalk.cyan('rigour init') + chalk.dim(' to detect your project role and apply standards.'));
|
|
8
|
+
console.log(chalk.dim(' 2. Run ') + chalk.cyan('rigour check') + chalk.dim(' to see existing violations.'));
|
|
9
|
+
console.log(chalk.dim(' 3. Run ') + chalk.cyan('rigour run -- <your-agent-command>') + chalk.dim(' to automate the fix loop.\n'));
|
|
10
|
+
|
|
11
|
+
console.log(chalk.bold('Key Concepts:'));
|
|
12
|
+
console.log(chalk.yellow(' ⢠Fix Packet v2') + chalk.dim(': Structured diagnostics fed directly into AI agents.'));
|
|
13
|
+
console.log(chalk.yellow(' ⢠Safety Rails') + chalk.dim(': Prevents "explosive" refactoring (max files changed).'));
|
|
14
|
+
console.log(chalk.yellow(' ⢠Strategic Guardians') + chalk.dim(': Dependency and Architectural boundary enforcement.\n'));
|
|
15
|
+
|
|
16
|
+
console.log(chalk.bold('Workflow Integration:'));
|
|
17
|
+
console.log(chalk.green(' ⢠Cursor') + chalk.dim(': Add the MCP server or use the ') + chalk.cyan('.cursor/rules/rigour.mdc') + chalk.dim(' handshake.'));
|
|
18
|
+
console.log(chalk.green(' ⢠CI/CD') + chalk.dim(': Use ') + chalk.cyan('rigour check --ci') + chalk.dim(' to fail PRs that violate quality gates.\n'));
|
|
19
|
+
|
|
20
|
+
console.log(chalk.dim('For more detailed docs, visit: ') + chalk.underline('https://github.com/erashu212/rigour/docs\n'));
|
|
21
|
+
}
|
package/src/commands/init.ts
CHANGED
|
@@ -4,9 +4,62 @@ import chalk from 'chalk';
|
|
|
4
4
|
import yaml from 'yaml';
|
|
5
5
|
import { DiscoveryService } from '@rigour-labs/core';
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export interface InitOptions {
|
|
8
|
+
preset?: string;
|
|
9
|
+
paradigm?: string;
|
|
10
|
+
dryRun?: boolean;
|
|
11
|
+
explain?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function initCommand(cwd: string, options: InitOptions = {}) {
|
|
8
15
|
const discovery = new DiscoveryService();
|
|
9
|
-
const
|
|
16
|
+
const result = await discovery.discover(cwd);
|
|
17
|
+
let recommendedConfig = result.config;
|
|
18
|
+
|
|
19
|
+
// Override with user options if provided and re-apply template logic if necessary
|
|
20
|
+
if (options.preset || options.paradigm) {
|
|
21
|
+
const core = await import('@rigour-labs/core');
|
|
22
|
+
|
|
23
|
+
let customBase = { ...core.UNIVERSAL_CONFIG };
|
|
24
|
+
|
|
25
|
+
if (options.preset) {
|
|
26
|
+
const t = core.TEMPLATES.find((t: any) => t.name === options.preset);
|
|
27
|
+
if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
|
|
28
|
+
} else if (recommendedConfig.preset) {
|
|
29
|
+
const t = core.TEMPLATES.find((t: any) => t.name === recommendedConfig.preset);
|
|
30
|
+
if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (options.paradigm) {
|
|
34
|
+
const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === options.paradigm);
|
|
35
|
+
if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
|
|
36
|
+
} else if (recommendedConfig.paradigm) {
|
|
37
|
+
const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === recommendedConfig.paradigm);
|
|
38
|
+
if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
recommendedConfig = customBase;
|
|
42
|
+
if (options.preset) recommendedConfig.preset = options.preset;
|
|
43
|
+
if (options.paradigm) recommendedConfig.paradigm = options.paradigm;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (options.dryRun || options.explain) {
|
|
47
|
+
console.log(chalk.bold.blue('\nš Rigour Auto-Discovery (Dry Run):'));
|
|
48
|
+
if (recommendedConfig.preset) {
|
|
49
|
+
console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
|
|
50
|
+
if (options.explain && result.matches.preset) {
|
|
51
|
+
console.log(chalk.dim(` (Marker found: ${result.matches.preset.marker})`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (recommendedConfig.paradigm) {
|
|
55
|
+
console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
|
|
56
|
+
if (options.explain && result.matches.paradigm) {
|
|
57
|
+
console.log(chalk.dim(` (Marker found: ${result.matches.paradigm.marker})`));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log(chalk.yellow('\n[DRY RUN] No files will be written.'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
10
63
|
|
|
11
64
|
const configPath = path.join(cwd, 'rigour.yml');
|
|
12
65
|
|
|
@@ -15,6 +68,15 @@ export async function initCommand(cwd: string) {
|
|
|
15
68
|
return;
|
|
16
69
|
}
|
|
17
70
|
|
|
71
|
+
console.log(chalk.bold.blue('\nš Rigour Auto-Discovery:'));
|
|
72
|
+
if (recommendedConfig.preset) {
|
|
73
|
+
console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
|
|
74
|
+
}
|
|
75
|
+
if (recommendedConfig.paradigm) {
|
|
76
|
+
console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
|
|
77
|
+
}
|
|
78
|
+
console.log('');
|
|
79
|
+
|
|
18
80
|
await fs.writeFile(configPath, yaml.stringify(recommendedConfig));
|
|
19
81
|
console.log(chalk.green('ā Created rigour.yml'));
|
|
20
82
|
|
|
@@ -28,36 +90,56 @@ export async function initCommand(cwd: string) {
|
|
|
28
90
|
}
|
|
29
91
|
}
|
|
30
92
|
|
|
31
|
-
// Agent Handshake (
|
|
32
|
-
const
|
|
33
|
-
await fs.ensureDir(
|
|
34
|
-
const
|
|
93
|
+
// Agent Handshake (Universal / AntiGravity / Cursor)
|
|
94
|
+
const rigourDocsDir = path.join(cwd, 'docs');
|
|
95
|
+
await fs.ensureDir(rigourDocsDir);
|
|
96
|
+
const instructionsPath = path.join(rigourDocsDir, 'AGENT_INSTRUCTIONS.md');
|
|
35
97
|
|
|
36
|
-
const ruleContent =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
98
|
+
const ruleContent = `# š”ļø Rigour: Engineering Excellence Protocol
|
|
99
|
+
|
|
100
|
+
You are an Elite Software Engineer. You do not just write code that "works"; you write code that is **modular, maintainable, and rigorously verified.**
|
|
101
|
+
|
|
102
|
+
## š¦ The Rigour Loop (Mandatory)
|
|
103
|
+
Before claiming "Done" for any task, you MUST follow this loop:
|
|
40
104
|
|
|
41
|
-
|
|
105
|
+
1. **Check**: Run \`npx @rigour-labs/cli check\` to verify compliance.
|
|
106
|
+
2. **Analyze**: If it fails, read \`rigour-fix-packet.json\` (V2 High-Fidelity) for exact failure points and constraints.
|
|
107
|
+
3. **Refactor**: Apply **SOLID** and **DRY** principles to resolve the violations according to constraints.
|
|
108
|
+
4. **Repeat**: Continue until \`npx @rigour-labs/cli check\` returns **PASS**.
|
|
42
109
|
|
|
43
|
-
|
|
110
|
+
## š§© Engineering Standards
|
|
111
|
+
- **Single Responsibility**: Keep files small and focused (max 500 lines).
|
|
112
|
+
- **DRY (Don't Repeat Yourself)**: Extract common logic into utilities.
|
|
113
|
+
- **Done is Done**: No \`TODO\` or \`FIXME\` comments allowed in the final state.
|
|
114
|
+
- **Memory Preservation**: Always update docs/SPEC.md, docs/ARCH.md, docs/DECISIONS.md.
|
|
44
115
|
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
- Keep files modular. If a file exceeds 500 lines, you MUST break it into smaller components.
|
|
50
|
-
- No \`TODO\` or \`FIXME\` comments allowed in the final submission.
|
|
116
|
+
## š ļø Commands
|
|
117
|
+
\`\`\`bash
|
|
118
|
+
# Verify current state
|
|
119
|
+
npx @rigour-labs/cli check
|
|
51
120
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
3. If FAIL: Read \`rigour-report.json\` for exact failure points and fix them.
|
|
56
|
-
4. If PASS: You may claim task completion.
|
|
121
|
+
# Self-healing agent loop
|
|
122
|
+
npx @rigour-labs/cli run -- <agent-command>
|
|
123
|
+
\`\`\`
|
|
57
124
|
`;
|
|
58
125
|
|
|
59
|
-
|
|
60
|
-
|
|
126
|
+
// 1. Create Universal Instructions
|
|
127
|
+
await fs.writeFile(instructionsPath, ruleContent);
|
|
128
|
+
console.log(chalk.green('ā Initialized Universal Agent Handshake (docs/AGENT_INSTRUCTIONS.md)'));
|
|
129
|
+
|
|
130
|
+
// 2. Create Cursor Specific Rules (.mdc)
|
|
131
|
+
const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
|
|
132
|
+
await fs.ensureDir(cursorRulesDir);
|
|
133
|
+
const mdcPath = path.join(cursorRulesDir, 'rigour.mdc');
|
|
134
|
+
const mdcContent = `---
|
|
135
|
+
description: Enforcement of Rigour quality gates and best practices.
|
|
136
|
+
globs: **/*
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
${ruleContent}`;
|
|
140
|
+
|
|
141
|
+
await fs.writeFile(mdcPath, mdcContent);
|
|
142
|
+
console.log(chalk.green('ā Initialized Cursor Handshake (.cursor/rules/rigour.mdc)'));
|
|
61
143
|
|
|
62
|
-
console.log(chalk.blue('\nRigour is ready. Run `rigour check` to verify your project.'));
|
|
144
|
+
console.log(chalk.blue('\nRigour is ready. Run `npx @rigour-labs/cli check` to verify your project.'));
|
|
63
145
|
}
|