@rigour-labs/cli 2.11.0 → 2.13.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.
@@ -1,105 +0,0 @@
1
- /**
2
- * Index Command
3
- *
4
- * Builds and updates the Rigour Pattern Index to prevent code reinvention.
5
- */
6
-
7
- import { Command } from 'commander';
8
- import path from 'path';
9
- import chalk from 'chalk';
10
- import ora from 'ora';
11
- import fs from 'fs-extra';
12
- import { randomUUID } from 'crypto';
13
-
14
- // Helper to log events for Rigour Studio
15
- async function logStudioEvent(cwd: string, event: any) {
16
- try {
17
- const rigourDir = path.join(cwd, ".rigour");
18
- await fs.ensureDir(rigourDir);
19
- const eventsPath = path.join(rigourDir, "events.jsonl");
20
- const logEntry = JSON.stringify({
21
- id: randomUUID(),
22
- timestamp: new Date().toISOString(),
23
- ...event
24
- }) + "\n";
25
- await fs.appendFile(eventsPath, logEntry);
26
- } catch {
27
- // Silent fail
28
- }
29
- }
30
- // Dynamic imports are used inside the action handler below to prevent
31
- // native dependency issues from affecting the rest of the CLI.
32
-
33
- export const indexCommand = new Command('index')
34
- .description('Build or update the pattern index for the current project')
35
- .option('-s, --semantic', 'Generate semantic embeddings for better matching (requires Transformers.js)', false)
36
- .option('-f, --force', 'Force a full rebuild of the index', false)
37
- .option('-o, --output <path>', 'Custom path for the index file')
38
- .action(async (options) => {
39
- const cwd = process.cwd();
40
-
41
- // Dynamic import to isolate native dependencies
42
- const {
43
- PatternIndexer,
44
- savePatternIndex,
45
- loadPatternIndex,
46
- getDefaultIndexPath
47
- } = await import('@rigour-labs/core/pattern-index');
48
-
49
- const indexPath = options.output || getDefaultIndexPath(cwd);
50
- const spinner = ora('Initializing pattern indexer...').start();
51
-
52
- try {
53
- const requestId = randomUUID();
54
- await logStudioEvent(cwd, {
55
- type: "tool_call",
56
- requestId,
57
- tool: "rigour_index",
58
- arguments: options
59
- });
60
-
61
- const indexer = new PatternIndexer(cwd, {
62
- useEmbeddings: options.semantic
63
- });
64
-
65
- let index;
66
- const existingIndex = await loadPatternIndex(indexPath);
67
-
68
- if (existingIndex && !options.force) {
69
- spinner.text = 'Updating existing pattern index...';
70
- index = await indexer.updateIndex(existingIndex);
71
- } else {
72
- spinner.text = 'Building fresh pattern index (this may take a while)...';
73
- index = await indexer.buildIndex();
74
- }
75
-
76
- spinner.text = 'Saving index to disk...';
77
- await savePatternIndex(index, indexPath);
78
-
79
- spinner.succeed(chalk.green(`Pattern index built successfully!`));
80
-
81
- await logStudioEvent(cwd, {
82
- type: "tool_response",
83
- requestId,
84
- tool: "rigour_index",
85
- status: "success",
86
- content: [{ type: "text", text: `Index built: ${index.stats.totalPatterns} patterns` }]
87
- });
88
- console.log(chalk.blue(`- Total Patterns: ${index.stats.totalPatterns}`));
89
- console.log(chalk.blue(`- Total Files: ${index.stats.totalFiles}`));
90
- console.log(chalk.blue(`- Index Path: ${indexPath}`));
91
-
92
- if (options.semantic) {
93
- console.log(chalk.magenta(`- Semantic Search: Enabled (Local Transformers.js)`));
94
- }
95
-
96
- const byType = Object.entries(index.stats.byType)
97
- .map(([type, count]) => `${type}: ${count}`)
98
- .join(', ');
99
- console.log(chalk.gray(`Types: ${byType}`));
100
-
101
- } catch (error: any) {
102
- spinner.fail(chalk.red(`Failed to build pattern index: ${error.message}`));
103
- process.exit(1);
104
- }
105
- });
@@ -1,413 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import chalk from 'chalk';
4
- import yaml from 'yaml';
5
- import { DiscoveryService } from '@rigour-labs/core';
6
- import { CODE_QUALITY_RULES, DEBUGGING_RULES, COLLABORATION_RULES, AGNOSTIC_AI_INSTRUCTIONS } from './constants.js';
7
- import { randomUUID } from 'crypto';
8
-
9
- // Helper to log events for Rigour Studio
10
- async function logStudioEvent(cwd: string, event: any) {
11
- try {
12
- const rigourDir = path.join(cwd, ".rigour");
13
- await fs.ensureDir(rigourDir);
14
- const eventsPath = path.join(rigourDir, "events.jsonl");
15
- const logEntry = JSON.stringify({
16
- id: randomUUID(),
17
- timestamp: new Date().toISOString(),
18
- ...event
19
- }) + "\n";
20
- await fs.appendFile(eventsPath, logEntry);
21
- } catch {
22
- // Silent fail
23
- }
24
- }
25
-
26
- export interface InitOptions {
27
- preset?: string;
28
- paradigm?: string;
29
- ide?: 'cursor' | 'vscode' | 'cline' | 'claude' | 'gemini' | 'codex' | 'windsurf' | 'all';
30
- dryRun?: boolean;
31
- explain?: boolean;
32
- }
33
-
34
- type DetectedIDE = 'cursor' | 'vscode' | 'cline' | 'claude' | 'gemini' | 'codex' | 'windsurf' | 'unknown';
35
-
36
- function detectIDE(cwd: string): DetectedIDE {
37
- // Check for Claude Code markers
38
- if (fs.existsSync(path.join(cwd, 'CLAUDE.md')) || fs.existsSync(path.join(cwd, '.claude'))) {
39
- return 'claude';
40
- }
41
-
42
- // Check for Gemini Code Assist markers
43
- if (fs.existsSync(path.join(cwd, '.gemini'))) {
44
- return 'gemini';
45
- }
46
-
47
- // Check for Codex/Aider AGENTS.md (universal standard)
48
- if (fs.existsSync(path.join(cwd, 'AGENTS.md'))) {
49
- return 'codex';
50
- }
51
-
52
- // Check for Windsurf markers
53
- if (fs.existsSync(path.join(cwd, '.windsurfrules')) || fs.existsSync(path.join(cwd, '.windsurf'))) {
54
- return 'windsurf';
55
- }
56
-
57
- // Check for Cline-specific markers
58
- if (fs.existsSync(path.join(cwd, '.clinerules'))) {
59
- return 'cline';
60
- }
61
-
62
- // Check for Cursor-specific markers
63
- if (fs.existsSync(path.join(cwd, '.cursor'))) {
64
- return 'cursor';
65
- }
66
-
67
- // Check for VS Code markers
68
- if (fs.existsSync(path.join(cwd, '.vscode'))) {
69
- return 'vscode';
70
- }
71
-
72
- // Check environment variables that IDEs/Agents set
73
- const termProgram = process.env.TERM_PROGRAM || '';
74
- const terminal = process.env.TERMINAL_EMULATOR || '';
75
- const appName = process.env.APP_NAME || '';
76
-
77
- if (termProgram.toLowerCase().includes('cursor') || terminal.toLowerCase().includes('cursor')) {
78
- return 'cursor';
79
- }
80
-
81
- if (termProgram.toLowerCase().includes('cline') || appName.toLowerCase().includes('cline')) {
82
- return 'cline';
83
- }
84
-
85
- if (termProgram.toLowerCase().includes('vscode') || process.env.VSCODE_INJECTION) {
86
- return 'vscode';
87
- }
88
-
89
- // Check for Claude Code environment
90
- if (process.env.CLAUDE_CODE || process.env.ANTHROPIC_API_KEY) {
91
- return 'claude';
92
- }
93
-
94
- // Check for Gemini environment
95
- if (process.env.GEMINI_API_KEY || process.env.GOOGLE_CLOUD_PROJECT) {
96
- return 'gemini';
97
- }
98
-
99
- return 'unknown';
100
- }
101
-
102
-
103
- export async function initCommand(cwd: string, options: InitOptions = {}) {
104
- const discovery = new DiscoveryService();
105
- const result = await discovery.discover(cwd);
106
- let recommendedConfig = result.config;
107
-
108
- // Override with user options if provided and re-apply template logic if necessary
109
- if (options.preset || options.paradigm) {
110
- const core = await import('@rigour-labs/core');
111
-
112
- let customBase = { ...core.UNIVERSAL_CONFIG };
113
-
114
- if (options.preset) {
115
- const t = core.TEMPLATES.find((t: any) => t.name === options.preset);
116
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
117
- } else if (recommendedConfig.preset) {
118
- const t = core.TEMPLATES.find((t: any) => t.name === recommendedConfig.preset);
119
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
120
- }
121
-
122
- if (options.paradigm) {
123
- const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === options.paradigm);
124
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
125
- } else if (recommendedConfig.paradigm) {
126
- const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === recommendedConfig.paradigm);
127
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
128
- }
129
-
130
- recommendedConfig = customBase;
131
- if (options.preset) recommendedConfig.preset = options.preset;
132
- if (options.paradigm) recommendedConfig.paradigm = options.paradigm;
133
- }
134
-
135
- if (options.dryRun || options.explain) {
136
- console.log(chalk.bold.blue('\n🔍 Rigour Auto-Discovery (Dry Run):'));
137
- if (recommendedConfig.preset) {
138
- console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
139
- if (options.explain && result.matches.preset) {
140
- console.log(chalk.dim(` (Marker found: ${result.matches.preset.marker})`));
141
- }
142
- }
143
- if (recommendedConfig.paradigm) {
144
- console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
145
- if (options.explain && result.matches.paradigm) {
146
- console.log(chalk.dim(` (Marker found: ${result.matches.paradigm.marker})`));
147
- }
148
- }
149
- console.log(chalk.yellow('\n[DRY RUN] No files will be written.'));
150
- return;
151
- }
152
-
153
- const configPath = path.join(cwd, 'rigour.yml');
154
-
155
- if (await fs.pathExists(configPath)) {
156
- console.log(chalk.yellow('rigour.yml already exists. Skipping initialization.'));
157
- return;
158
- }
159
-
160
- console.log(chalk.bold.blue('\n🔍 Rigour Auto-Discovery:'));
161
-
162
- const requestId = randomUUID();
163
- await logStudioEvent(cwd, {
164
- type: "tool_call",
165
- requestId,
166
- tool: "rigour_init",
167
- arguments: options
168
- });
169
- if (recommendedConfig.preset) {
170
- console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
171
- }
172
- if (recommendedConfig.paradigm) {
173
- console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
174
- }
175
- console.log('');
176
-
177
- const yamlHeader = `# ⚠️ TEAM STANDARD - DO NOT MODIFY WITHOUT TEAM APPROVAL
178
- # AI Assistants: Adjust YOUR code to meet these standards, not the other way around.
179
- # Modifying thresholds or adding ignores to pass checks defeats the purpose of Rigour.
180
- # See: docs/AGENT_INSTRUCTIONS.md for the correct workflow.
181
-
182
- `;
183
- await fs.writeFile(configPath, yamlHeader + yaml.stringify(recommendedConfig));
184
- console.log(chalk.green('✔ Created rigour.yml'));
185
-
186
- // Create required directories and files
187
- const requireddocs = recommendedConfig.gates.required_files || [];
188
- for (const file of requireddocs) {
189
- const filePath = path.join(cwd, file);
190
- if (!(await fs.pathExists(filePath))) {
191
- await fs.ensureDir(path.dirname(filePath)); // Ensure parent directory exists
192
- await fs.ensureFile(filePath);
193
- console.log(chalk.dim(` - Created ${file}`));
194
- }
195
- }
196
-
197
- // Agent Handshake (Universal / AntiGravity / Cursor)
198
- const rigourDocsDir = path.join(cwd, 'docs');
199
- await fs.ensureDir(rigourDocsDir);
200
- const instructionsPath = path.join(rigourDocsDir, 'AGENT_INSTRUCTIONS.md');
201
-
202
- const ruleContent = `# 🛡️ Rigour: Mandatory Engineering Governance Protocol
203
-
204
- **You are an AI Engineering Agent governed by Rigour.**
205
-
206
- Your access to this codebase is conditional upon your strict adherence to the **Rigour Governance Protocol**. This is an automated requirement for every engineering task.
207
-
208
- ## 🚦 The Mandatory Governance Loop
209
- You MUST execute this loop for every task. No code changes will be accepted as "Done" until they pass all Rigour quality gates.
210
-
211
- 1. **Context Loading**: Call \`rigour_recall\` (MCP) to align with project-specific engineering memory.
212
- 2. **Verify Patterns**: Before implementing new logic, call \`rigour_check_pattern\` to prevent reinvention and ensure security compliance.
213
- 3. **Audit Enforcement**: Before finishing, you MUST call \`npx @rigour-labs/cli check\`.
214
- 4. **Failure Remediation**: If the check returns **FAIL**, you MUST implement all required engineering refinements.
215
- 5. **PASS or REJECT**: You may only declare a task complete when the check returns **PASS**.
216
-
217
- ## ⚖️ Transparency & Accountability
218
- **Every action, tool call, and logic shift you perform is automatically logged to the project's local audit trail.** This data is visible in real-time in the human-monitored **Rigour Studio**.
219
-
220
- ${AGNOSTIC_AI_INSTRUCTIONS}
221
- ${CODE_QUALITY_RULES}
222
- ${DEBUGGING_RULES}
223
- ${COLLABORATION_RULES}
224
- `;
225
-
226
- // 1. Create Universal Instructions
227
- if (!(await fs.pathExists(instructionsPath))) {
228
- await fs.writeFile(instructionsPath, ruleContent);
229
- console.log(chalk.green('✔ Initialized Universal Agent Handshake (docs/AGENT_INSTRUCTIONS.md)'));
230
- }
231
-
232
- // 2. Create IDE-Specific Rules based on detection or user preference
233
- const detectedIDE = detectIDE(cwd);
234
- const targetIDE = options.ide || (detectedIDE !== 'unknown' ? detectedIDE : 'all');
235
-
236
- if (detectedIDE !== 'unknown' && !options.ide) {
237
- console.log(chalk.dim(` (Auto-detected IDE: ${detectedIDE})`));
238
- }
239
-
240
- if (targetIDE === 'cursor' || targetIDE === 'all') {
241
- const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
242
- await fs.ensureDir(cursorRulesDir);
243
- const mdcPath = path.join(cursorRulesDir, 'rigour.mdc');
244
- const mdcContent = `---
245
- description: Enforcement of Rigour quality gates and best practices.
246
- globs: **/*
247
- ---
248
-
249
- ${ruleContent}`;
250
-
251
- if (!(await fs.pathExists(mdcPath))) {
252
- await fs.writeFile(mdcPath, mdcContent);
253
- console.log(chalk.green('✔ Initialized Cursor Handshake (.cursor/rules/rigour.mdc)'));
254
- }
255
- }
256
-
257
- if (targetIDE === 'vscode' || targetIDE === 'all') {
258
- // VS Code users use the universal AGENT_INSTRUCTIONS.md (already created above)
259
- // We could also add .vscode/settings.json or snippets here if needed
260
- console.log(chalk.green('✔ VS Code mode - using Universal Handshake (docs/AGENT_INSTRUCTIONS.md)'));
261
- }
262
-
263
- if (targetIDE === 'cline' || targetIDE === 'all') {
264
- const clineRulesPath = path.join(cwd, '.clinerules');
265
- if (!(await fs.pathExists(clineRulesPath))) {
266
- await fs.writeFile(clineRulesPath, ruleContent);
267
- console.log(chalk.green('✔ Initialized Cline Handshake (.clinerules)'));
268
- }
269
- }
270
-
271
- // Claude Code (CLAUDE.md)
272
- if (targetIDE === 'claude' || targetIDE === 'all') {
273
- const claudePath = path.join(cwd, 'CLAUDE.md');
274
- const claudeContent = `# CLAUDE.md - Project Instructions for Claude Code
275
-
276
- This file provides Claude Code with context about this project.
277
-
278
- ## Project Overview
279
-
280
- This project uses Rigour for quality gates. Always run \`npx @rigour-labs/cli check\` before marking tasks complete.
281
-
282
- ## Commands
283
-
284
- \`\`\`bash
285
- # Verify quality gates
286
- npx @rigour-labs/cli check
287
-
288
- # Get fix packet for failures
289
- npx @rigour-labs/cli explain
290
-
291
- # Self-healing agent loop
292
- npx @rigour-labs/cli run -- claude "<task>"
293
- \`\`\`
294
-
295
- ${ruleContent}`;
296
-
297
- if (!(await fs.pathExists(claudePath))) {
298
- await fs.writeFile(claudePath, claudeContent);
299
- console.log(chalk.green('✔ Initialized Claude Code Handshake (CLAUDE.md)'));
300
- }
301
- }
302
-
303
- // Gemini Code Assist (.gemini/styleguide.md)
304
- if (targetIDE === 'gemini' || targetIDE === 'all') {
305
- const geminiDir = path.join(cwd, '.gemini');
306
- await fs.ensureDir(geminiDir);
307
- const geminiStylePath = path.join(geminiDir, 'styleguide.md');
308
- const geminiContent = `# Gemini Code Assist Style Guide
309
-
310
- This project uses Rigour for quality gates.
311
-
312
- ## Required Before Completion
313
-
314
- Always run \`npx @rigour-labs/cli check\` before marking any task complete.
315
-
316
- ${ruleContent}`;
317
-
318
- if (!(await fs.pathExists(geminiStylePath))) {
319
- await fs.writeFile(geminiStylePath, geminiContent);
320
- console.log(chalk.green('✔ Initialized Gemini Handshake (.gemini/styleguide.md)'));
321
- }
322
- }
323
-
324
- // OpenAI Codex / Aider (AGENTS.md - Universal Standard)
325
- if (targetIDE === 'codex' || targetIDE === 'all') {
326
- const agentsPath = path.join(cwd, 'AGENTS.md');
327
- const agentsContent = `# AGENTS.md - Universal AI Agent Instructions
328
-
329
- This file provides instructions for AI coding agents (Codex, Aider, and others).
330
-
331
- ## Setup
332
-
333
- \`\`\`bash
334
- npm install
335
- npm run dev
336
- npm test
337
- \`\`\`
338
-
339
- ## Quality Gates
340
-
341
- This project uses Rigour. Before completing any task:
342
-
343
- \`\`\`bash
344
- npx @rigour-labs/cli check
345
- \`\`\`
346
-
347
- ${ruleContent}`;
348
-
349
- if (!(await fs.pathExists(agentsPath))) {
350
- await fs.writeFile(agentsPath, agentsContent);
351
- console.log(chalk.green('✔ Initialized Universal Agent Handshake (AGENTS.md)'));
352
- }
353
- }
354
-
355
- // Windsurf (.windsurfrules)
356
- if (targetIDE === 'windsurf' || targetIDE === 'all') {
357
- const windsurfPath = path.join(cwd, '.windsurfrules');
358
- if (!(await fs.pathExists(windsurfPath))) {
359
- await fs.writeFile(windsurfPath, ruleContent);
360
- console.log(chalk.green('✔ Initialized Windsurf Handshake (.windsurfrules)'));
361
- }
362
- }
363
-
364
- // 3. Update .gitignore
365
- const gitignorePath = path.join(cwd, '.gitignore');
366
- const ignorePatterns = ['rigour-report.json', 'rigour-fix-packet.json', '.rigour/'];
367
- try {
368
- let content = '';
369
- if (await fs.pathExists(gitignorePath)) {
370
- content = await fs.readFile(gitignorePath, 'utf-8');
371
- }
372
-
373
- const toAdd = ignorePatterns.filter(p => !content.includes(p));
374
- if (toAdd.length > 0) {
375
- const separator = content.endsWith('\n') ? '' : '\n';
376
- const newContent = `${content}${separator}\n# Rigour Artifacts\n${toAdd.join('\n')}\n`;
377
- await fs.writeFile(gitignorePath, newContent);
378
- console.log(chalk.green('✔ Updated .gitignore'));
379
- }
380
- } catch (e) {
381
- // Failing to update .gitignore isn't fatal
382
- }
383
-
384
- console.log(chalk.blue('\nRigour is ready. Run `npx @rigour-labs/cli check` to verify your project.'));
385
- console.log(chalk.cyan('Next Step: ') + chalk.bold('rigour index') + chalk.dim(' (Populate the Pattern Index)'));
386
-
387
- // Bootstrap initial memory for the Studio
388
- const rigourDir = path.join(cwd, ".rigour");
389
- await fs.ensureDir(rigourDir);
390
- const memPath = path.join(rigourDir, "memory.json");
391
- if (!(await fs.pathExists(memPath))) {
392
- await fs.writeJson(memPath, {
393
- memories: {
394
- "project_boot": {
395
- value: `Governance initiated via '${options.preset || 'api'}' preset. This project is now monitored by Rigour Studio.`,
396
- timestamp: new Date().toISOString()
397
- }
398
- }
399
- }, { spaces: 2 });
400
- }
401
-
402
- console.log(chalk.dim('\n💡 Tip: Planning to use a framework like Next.js?'));
403
- console.log(chalk.dim(' Run its scaffolding tool (e.g., npx create-next-app) BEFORE rigour init,'));
404
- console.log(chalk.dim(' or move rigour.yml and docs/ aside temporarily to satisfy empty-directory checks.'));
405
-
406
- await logStudioEvent(cwd, {
407
- type: "tool_response",
408
- requestId,
409
- tool: "rigour_init",
410
- status: "success",
411
- content: [{ type: "text", text: `Rigour Governance Initialized` }]
412
- });
413
- }
@@ -1,135 +0,0 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import chalk from 'chalk';
4
- import yaml from 'yaml';
5
- import { execa } from 'execa';
6
- import { GateRunner, ConfigSchema } from '@rigour-labs/core';
7
-
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 }) {
15
- const configPath = path.join(cwd, 'rigour.yml');
16
-
17
- if (!(await fs.pathExists(configPath))) {
18
- console.error(chalk.red('Error: rigour.yml not found. Run `rigour init` first.'));
19
- process.exit(EXIT_CONFIG_ERROR);
20
- }
21
-
22
- try {
23
- const configContent = await fs.readFile(configPath, 'utf-8');
24
- const rawConfig = yaml.parse(configContent);
25
- const config = ConfigSchema.parse(rawConfig);
26
- const runner = new GateRunner(config);
27
-
28
- let iteration = 0;
29
- const maxIterations = options.iterations;
30
-
31
- while (iteration < maxIterations) {
32
- iteration++;
33
- console.log(chalk.bold.blue(`\n══════════════════════════════════════════════════════════════════`));
34
- console.log(chalk.bold.blue(` RIGOUR LOOP: Iteration ${iteration}/${maxIterations}`));
35
- console.log(chalk.bold.blue(`══════════════════════════════════════════════════════════════════`));
36
-
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
- const getTrackedChanges = async () => {
47
- try {
48
- const { stdout } = await execa('git', ['status', '--porcelain'], { cwd });
49
- return stdout.split('\n')
50
- .filter(l => l.trim())
51
- .filter(line => /M|A|D|R/.test(line.slice(0, 2)))
52
- .map(l => l.slice(3).trim());
53
- } catch (e) {
54
- return [];
55
- }
56
- };
57
-
58
- // Snapshot changed files before agent runs
59
- const beforeFiles = await getTrackedChanges();
60
-
61
- // 2. Run the agent command
62
- if (currentArgs.length > 0) {
63
- console.log(chalk.cyan(`\n🚀 DEPLOYING AGENT:`));
64
- console.log(chalk.dim(` Command: ${currentArgs.join(' ')}`));
65
- try {
66
- await execa(currentArgs[0], currentArgs.slice(1), { shell: true, stdio: 'inherit', cwd });
67
- } catch (error: any) {
68
- console.warn(chalk.yellow(`\n⚠️ Agent command finished with non-zero exit code. Rigour will now verify state...`));
69
- }
70
- }
71
-
72
- // Snapshot changed files after agent runs
73
- const afterFiles = await getTrackedChanges();
74
-
75
- const changedThisCycle = afterFiles.filter(f => !beforeFiles.includes(f));
76
- const maxFiles = config.gates.safety?.max_files_changed_per_cycle || 10;
77
-
78
- if (changedThisCycle.length > maxFiles) {
79
- console.log(chalk.red.bold(`\n🛑 SAFETY RAIL ABORT: Agent changed ${changedThisCycle.length} files (max: ${maxFiles}).`));
80
- console.log(chalk.red(` This looks like explosive behavior. Check your agent's instructions.`));
81
- process.exit(EXIT_FAIL);
82
- }
83
-
84
- // 3. Run Rigour Check
85
- console.log(chalk.magenta('\n🔍 AUDITING QUALITY GATES...'));
86
- const report = await runner.run(cwd);
87
-
88
- // Write report
89
- const reportPath = path.join(cwd, config.output.report_path);
90
- await fs.writeJson(reportPath, report, { spaces: 2 });
91
-
92
- if (report.status === 'PASS') {
93
- console.log(chalk.green.bold('\n✨ PASS - All quality gates satisfied.'));
94
- console.log(chalk.green(` Your solution meets the required Engineering Rigour criteria.\n`));
95
- return;
96
- }
97
-
98
- // 4. Generate Fix Packet v2
99
- const { FixPacketService } = await import('@rigour-labs/core');
100
- const fixPacketService = new FixPacketService();
101
- const fixPacket = fixPacketService.generate(report, config);
102
- const fixPacketPath = path.join(cwd, 'rigour-fix-packet.json');
103
- await fs.writeJson(fixPacketPath, fixPacket, { spaces: 2 });
104
-
105
- console.log(chalk.red.bold(`\n🛑 FAIL - Found ${report.failures.length} engineering violations.`));
106
- console.log(chalk.dim(` Fix Packet generated: rigour-fix-packet.json`));
107
-
108
- if (options.failFast) {
109
- console.log(chalk.red.bold(`\n🛑 FAIL-FAST: Aborting loop as requested.`));
110
- process.exit(EXIT_FAIL);
111
- }
112
-
113
- // Print summary
114
- const summary = report.failures.map((f, i) => {
115
- return chalk.white(`${i + 1}. `) + chalk.bold.red(`[${f.id.toUpperCase()}] `) + chalk.white(f.title);
116
- }).join('\n');
117
- console.log(chalk.bold.white('\n📋 VIOLATIONS SUMMARY:'));
118
- console.log(summary);
119
-
120
- if (iteration === maxIterations) {
121
- console.log(chalk.red.bold(`\n❌ CRITICAL: Reached maximum iterations (${maxIterations}).`));
122
- console.log(chalk.red(` Quality gates remain unfulfilled. Refactor manually or check agent logs.`));
123
- process.exit(EXIT_FAIL);
124
- }
125
-
126
- console.log(chalk.dim('\nReturning control to agent for the next refinement cycle...'));
127
- }
128
- } catch (error: any) {
129
- console.error(chalk.red(`\n❌ FATAL ERROR: ${error.message}`));
130
- if (error.issues) {
131
- console.error(chalk.dim(JSON.stringify(error.issues, null, 2)));
132
- }
133
- process.exit(EXIT_INTERNAL_ERROR);
134
- }
135
- }
@@ -1,28 +0,0 @@
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
- }