@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.
@@ -5,12 +5,18 @@ import yaml from 'yaml';
5
5
  import { execa } from 'execa';
6
6
  import { GateRunner, ConfigSchema } from '@rigour-labs/core';
7
7
 
8
- export async function runLoop(cwd: string, agentArgs: string[], options: { iterations: number }) {
8
+ // Exit codes per spec
9
+ const EXIT_PASS = 0;
10
+ const EXIT_FAIL = 1;
11
+ const EXIT_CONFIG_ERROR = 2;
12
+ const EXIT_INTERNAL_ERROR = 3;
13
+
14
+ export async function runLoop(cwd: string, agentArgs: string[], options: { iterations: number, failFast?: boolean }) {
9
15
  const configPath = path.join(cwd, 'rigour.yml');
10
16
 
11
17
  if (!(await fs.pathExists(configPath))) {
12
18
  console.error(chalk.red('Error: rigour.yml not found. Run `rigour init` first.'));
13
- process.exit(1);
19
+ process.exit(EXIT_CONFIG_ERROR);
14
20
  }
15
21
 
16
22
  try {
@@ -28,52 +34,98 @@ export async function runLoop(cwd: string, agentArgs: string[], options: { itera
28
34
  console.log(chalk.bold.blue(` RIGOUR LOOP: Iteration ${iteration}/${maxIterations}`));
29
35
  console.log(chalk.bold.blue(`══════════════════════════════════════════════════════════════════`));
30
36
 
31
- // 1. Run the agent command
32
- if (agentArgs.length > 0) {
37
+ // 1. Prepare Command
38
+ let currentArgs = [...agentArgs];
39
+ if (iteration > 1 && agentArgs.length > 0) {
40
+ // Iteration contract: In later cycles, we focus strictly on the fix packet
41
+ console.log(chalk.yellow(`\n🔄 REFINEMENT CYCLE - Instructing agent to fix specific violations...`));
42
+ // We keep the first part of the command (the agent) but can append or wrap
43
+ // For simplicity, we assume the agent can read the JSON file we generate
44
+ }
45
+
46
+ // Snapshot changed files before agent runs
47
+ let beforeFiles: string[] = [];
48
+ try {
49
+ const { stdout } = await execa('git', ['status', '--porcelain'], { cwd });
50
+ beforeFiles = stdout.split('\n').filter(l => l.trim()).map(l => l.slice(3).trim());
51
+ } catch (e) { }
52
+
53
+ // 2. Run the agent command
54
+ if (currentArgs.length > 0) {
33
55
  console.log(chalk.cyan(`\n🚀 DEPLOYING AGENT:`));
34
- console.log(chalk.dim(` Command: ${agentArgs.join(' ')}`));
56
+ console.log(chalk.dim(` Command: ${currentArgs.join(' ')}`));
35
57
  try {
36
- await execa(agentArgs[0], agentArgs.slice(1), { shell: true, stdio: 'inherit', cwd });
58
+ await execa(currentArgs[0], currentArgs.slice(1), { shell: true, stdio: 'inherit', cwd });
37
59
  } catch (error: any) {
38
60
  console.warn(chalk.yellow(`\n⚠️ Agent command finished with non-zero exit code. Rigour will now verify state...`));
39
61
  }
40
62
  }
41
63
 
42
- // 2. Run Rigour Check
64
+ // Snapshot changed files after agent runs
65
+ let afterFiles: string[] = [];
66
+ try {
67
+ const { stdout } = await execa('git', ['status', '--porcelain'], { cwd });
68
+ afterFiles = stdout.split('\n').filter(l => l.trim()).map(l => l.slice(3).trim());
69
+ } catch (e) { }
70
+
71
+ const changedThisCycle = afterFiles.filter(f => !beforeFiles.includes(f));
72
+ const maxFiles = config.gates.safety?.max_files_changed_per_cycle || 10;
73
+
74
+ if (changedThisCycle.length > maxFiles) {
75
+ console.log(chalk.red.bold(`\n🛑 SAFETY RAIL ABORT: Agent changed ${changedThisCycle.length} files (max: ${maxFiles}).`));
76
+ console.log(chalk.red(` This looks like explosive behavior. Check your agent's instructions.`));
77
+ process.exit(EXIT_FAIL);
78
+ }
79
+
80
+ // 3. Run Rigour Check
43
81
  console.log(chalk.magenta('\n🔍 AUDITING QUALITY GATES...'));
44
82
  const report = await runner.run(cwd);
45
83
 
84
+ // Write report
85
+ const reportPath = path.join(cwd, config.output.report_path);
86
+ await fs.writeJson(reportPath, report, { spaces: 2 });
87
+
46
88
  if (report.status === 'PASS') {
47
89
  console.log(chalk.green.bold('\n✨ PASS - All quality gates satisfied.'));
48
90
  console.log(chalk.green(` Your solution meets the required Engineering Rigour criteria.\n`));
49
91
  return;
50
92
  }
51
93
 
52
- // 3. Generate and print Fix Packet for next iteration
94
+ // 4. Generate Fix Packet v2
95
+ const { FixPacketService } = await import('@rigour-labs/core');
96
+ const fixPacketService = new FixPacketService();
97
+ const fixPacket = fixPacketService.generate(report, config);
98
+ const fixPacketPath = path.join(cwd, 'rigour-fix-packet.json');
99
+ await fs.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
100
+
53
101
  console.log(chalk.red.bold(`\n🛑 FAIL - Found ${report.failures.length} engineering violations.`));
102
+ console.log(chalk.dim(` Fix Packet generated: rigour-fix-packet.json`));
54
103
 
55
- const fixPacket = report.failures.map((f, i) => {
56
- let msg = chalk.white(`${i + 1}. `) + chalk.bold.red(`[${f.id.toUpperCase()}] `) + chalk.white(f.title);
57
- msg += `\n ├─ ` + chalk.dim(`Details: ${f.details}`);
58
- if (f.hint) msg += `\n └─ ` + chalk.yellow(`FIX: ${f.hint}`);
59
- return msg;
60
- }).join('\n\n');
104
+ if (options.failFast) {
105
+ console.log(chalk.red.bold(`\n🛑 FAIL-FAST: Aborting loop as requested.`));
106
+ process.exit(EXIT_FAIL);
107
+ }
61
108
 
62
- console.log(chalk.bold.white('\n📋 ACTIONABLE FIX PACKET:'));
63
- console.log(fixPacket);
64
- console.log(chalk.dim('\nReturning control to agent for the next refinement cycle...'));
109
+ // Print summary
110
+ const summary = report.failures.map((f, i) => {
111
+ return chalk.white(`${i + 1}. `) + chalk.bold.red(`[${f.id.toUpperCase()}] `) + chalk.white(f.title);
112
+ }).join('\n');
113
+ console.log(chalk.bold.white('\n📋 VIOLATIONS SUMMARY:'));
114
+ console.log(summary);
65
115
 
66
116
  if (iteration === maxIterations) {
67
117
  console.log(chalk.red.bold(`\n❌ CRITICAL: Reached maximum iterations (${maxIterations}).`));
68
118
  console.log(chalk.red(` Quality gates remain unfulfilled. Refactor manually or check agent logs.`));
69
- process.exit(1);
119
+ process.exit(EXIT_FAIL);
70
120
  }
121
+
122
+ console.log(chalk.dim('\nReturning control to agent for the next refinement cycle...'));
71
123
  }
72
124
  } catch (error: any) {
73
125
  console.error(chalk.red(`\n❌ FATAL ERROR: ${error.message}`));
74
126
  if (error.issues) {
75
127
  console.error(chalk.dim(JSON.stringify(error.issues, null, 2)));
76
128
  }
77
- process.exit(1);
129
+ process.exit(EXIT_INTERNAL_ERROR);
78
130
  }
79
131
  }
@@ -0,0 +1,28 @@
1
+ import chalk from 'chalk';
2
+
3
+ export async function setupCommand() {
4
+ console.log(chalk.bold.cyan('\n🛠️ Rigour Labs | Setup & Installation\n'));
5
+
6
+ console.log(chalk.bold('1. Global Installation (Recommended)'));
7
+ console.log(chalk.dim(' To use Rigour anywhere in your terminal:'));
8
+ console.log(chalk.green(' $ npm install -g @rigour-labs/cli\n'));
9
+
10
+ console.log(chalk.bold('2. Project-Local installation'));
11
+ console.log(chalk.dim(' To keep Rigour versioned with your project:'));
12
+ console.log(chalk.green(' $ npm install --save-dev @rigour-labs/cli\n'));
13
+
14
+ console.log(chalk.bold('3. Standalone Binaries (Zero-Install)'));
15
+ console.log(chalk.dim(' If you do not want to use Node.js:'));
16
+ console.log(chalk.dim(' • macOS: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-macos'));
17
+ console.log(chalk.dim(' • Linux: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-linux'));
18
+ console.log(chalk.dim(' • Windows: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-windows.exe\n'));
19
+
20
+ console.log(chalk.bold('4. MCP Integration (for AI Agents)'));
21
+ console.log(chalk.dim(' To let Cursor or Claude use Rigour natively:'));
22
+ console.log(chalk.dim(' Path to MCP: ') + chalk.cyan('packages/rigour-mcp/dist/index.js'));
23
+ console.log(chalk.dim(' Add this to your Cursor/Claude settings.\n'));
24
+
25
+ console.log(chalk.bold('Update Guidance:'));
26
+ console.log(chalk.dim(' Keep Rigour sharp by updating regularly:'));
27
+ console.log(chalk.green(' $ npm install -g @rigour-labs/cli@latest\n'));
28
+ }