@rigour-labs/cli 2.10.0 → 2.12.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,366 +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
-
8
- export interface InitOptions {
9
- preset?: string;
10
- paradigm?: string;
11
- ide?: 'cursor' | 'vscode' | 'cline' | 'claude' | 'gemini' | 'codex' | 'windsurf' | 'all';
12
- dryRun?: boolean;
13
- explain?: boolean;
14
- }
15
-
16
- type DetectedIDE = 'cursor' | 'vscode' | 'cline' | 'claude' | 'gemini' | 'codex' | 'windsurf' | 'unknown';
17
-
18
- function detectIDE(cwd: string): DetectedIDE {
19
- // Check for Claude Code markers
20
- if (fs.existsSync(path.join(cwd, 'CLAUDE.md')) || fs.existsSync(path.join(cwd, '.claude'))) {
21
- return 'claude';
22
- }
23
-
24
- // Check for Gemini Code Assist markers
25
- if (fs.existsSync(path.join(cwd, '.gemini'))) {
26
- return 'gemini';
27
- }
28
-
29
- // Check for Codex/Aider AGENTS.md (universal standard)
30
- if (fs.existsSync(path.join(cwd, 'AGENTS.md'))) {
31
- return 'codex';
32
- }
33
-
34
- // Check for Windsurf markers
35
- if (fs.existsSync(path.join(cwd, '.windsurfrules')) || fs.existsSync(path.join(cwd, '.windsurf'))) {
36
- return 'windsurf';
37
- }
38
-
39
- // Check for Cline-specific markers
40
- if (fs.existsSync(path.join(cwd, '.clinerules'))) {
41
- return 'cline';
42
- }
43
-
44
- // Check for Cursor-specific markers
45
- if (fs.existsSync(path.join(cwd, '.cursor'))) {
46
- return 'cursor';
47
- }
48
-
49
- // Check for VS Code markers
50
- if (fs.existsSync(path.join(cwd, '.vscode'))) {
51
- return 'vscode';
52
- }
53
-
54
- // Check environment variables that IDEs/Agents set
55
- const termProgram = process.env.TERM_PROGRAM || '';
56
- const terminal = process.env.TERMINAL_EMULATOR || '';
57
- const appName = process.env.APP_NAME || '';
58
-
59
- if (termProgram.toLowerCase().includes('cursor') || terminal.toLowerCase().includes('cursor')) {
60
- return 'cursor';
61
- }
62
-
63
- if (termProgram.toLowerCase().includes('cline') || appName.toLowerCase().includes('cline')) {
64
- return 'cline';
65
- }
66
-
67
- if (termProgram.toLowerCase().includes('vscode') || process.env.VSCODE_INJECTION) {
68
- return 'vscode';
69
- }
70
-
71
- // Check for Claude Code environment
72
- if (process.env.CLAUDE_CODE || process.env.ANTHROPIC_API_KEY) {
73
- return 'claude';
74
- }
75
-
76
- // Check for Gemini environment
77
- if (process.env.GEMINI_API_KEY || process.env.GOOGLE_CLOUD_PROJECT) {
78
- return 'gemini';
79
- }
80
-
81
- return 'unknown';
82
- }
83
-
84
-
85
- export async function initCommand(cwd: string, options: InitOptions = {}) {
86
- const discovery = new DiscoveryService();
87
- const result = await discovery.discover(cwd);
88
- let recommendedConfig = result.config;
89
-
90
- // Override with user options if provided and re-apply template logic if necessary
91
- if (options.preset || options.paradigm) {
92
- const core = await import('@rigour-labs/core');
93
-
94
- let customBase = { ...core.UNIVERSAL_CONFIG };
95
-
96
- if (options.preset) {
97
- const t = core.TEMPLATES.find((t: any) => t.name === options.preset);
98
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
99
- } else if (recommendedConfig.preset) {
100
- const t = core.TEMPLATES.find((t: any) => t.name === recommendedConfig.preset);
101
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
102
- }
103
-
104
- if (options.paradigm) {
105
- const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === options.paradigm);
106
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
107
- } else if (recommendedConfig.paradigm) {
108
- const t = core.PARADIGM_TEMPLATES.find((t: any) => t.name === recommendedConfig.paradigm);
109
- if (t) customBase = (discovery as any).mergeConfig(customBase, t.config);
110
- }
111
-
112
- recommendedConfig = customBase;
113
- if (options.preset) recommendedConfig.preset = options.preset;
114
- if (options.paradigm) recommendedConfig.paradigm = options.paradigm;
115
- }
116
-
117
- if (options.dryRun || options.explain) {
118
- console.log(chalk.bold.blue('\n🔍 Rigour Auto-Discovery (Dry Run):'));
119
- if (recommendedConfig.preset) {
120
- console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
121
- if (options.explain && result.matches.preset) {
122
- console.log(chalk.dim(` (Marker found: ${result.matches.preset.marker})`));
123
- }
124
- }
125
- if (recommendedConfig.paradigm) {
126
- console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
127
- if (options.explain && result.matches.paradigm) {
128
- console.log(chalk.dim(` (Marker found: ${result.matches.paradigm.marker})`));
129
- }
130
- }
131
- console.log(chalk.yellow('\n[DRY RUN] No files will be written.'));
132
- return;
133
- }
134
-
135
- const configPath = path.join(cwd, 'rigour.yml');
136
-
137
- if (await fs.pathExists(configPath)) {
138
- console.log(chalk.yellow('rigour.yml already exists. Skipping initialization.'));
139
- return;
140
- }
141
-
142
- console.log(chalk.bold.blue('\n🔍 Rigour Auto-Discovery:'));
143
- if (recommendedConfig.preset) {
144
- console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
145
- }
146
- if (recommendedConfig.paradigm) {
147
- console.log(chalk.cyan(` Paradigm: `) + chalk.bold(recommendedConfig.paradigm.toUpperCase()));
148
- }
149
- console.log('');
150
-
151
- const yamlHeader = `# ⚠️ TEAM STANDARD - DO NOT MODIFY WITHOUT TEAM APPROVAL
152
- # AI Assistants: Adjust YOUR code to meet these standards, not the other way around.
153
- # Modifying thresholds or adding ignores to pass checks defeats the purpose of Rigour.
154
- # See: docs/AGENT_INSTRUCTIONS.md for the correct workflow.
155
-
156
- `;
157
- await fs.writeFile(configPath, yamlHeader + yaml.stringify(recommendedConfig));
158
- console.log(chalk.green('✔ Created rigour.yml'));
159
-
160
- // Create required directories and files
161
- const requireddocs = recommendedConfig.gates.required_files || [];
162
- for (const file of requireddocs) {
163
- const filePath = path.join(cwd, file);
164
- if (!(await fs.pathExists(filePath))) {
165
- await fs.ensureFile(filePath);
166
- console.log(chalk.dim(` - Created ${file}`));
167
- }
168
- }
169
-
170
- // Agent Handshake (Universal / AntiGravity / Cursor)
171
- const rigourDocsDir = path.join(cwd, 'docs');
172
- await fs.ensureDir(rigourDocsDir);
173
- const instructionsPath = path.join(rigourDocsDir, 'AGENT_INSTRUCTIONS.md');
174
-
175
- const ruleContent = `# 🛡️ Rigour: Engineering Excellence Protocol
176
-
177
- You are an Elite Software Engineer. You do not just write code that "works"; you write code that is **modular, maintainable, and rigorously verified.**
178
-
179
- ## 🚦 The Rigour Loop (Mandatory)
180
- Before claiming "Done" for any task, you MUST follow this loop:
181
-
182
- 1. **Check**: Run \`npx @rigour-labs/cli check\` to verify compliance.
183
- 2. **Analyze**: If it fails, read \`rigour-fix-packet.json\` (V2 High-Fidelity) for exact failure points and constraints.
184
- 3. **Refactor**: Apply **SOLID** and **DRY** principles to resolve the violations according to constraints.
185
- 4. **Repeat**: Continue until \`npx @rigour-labs/cli check\` returns **PASS**.
186
-
187
- ## 🛠️ Commands
188
- \`\`\`bash
189
- # Verify current state
190
- npx @rigour-labs/cli check
191
-
192
- # Self-healing agent loop
193
- npx @rigour-labs/cli run -- <agent-command>
194
- \`\`\`
195
-
196
- ${AGNOSTIC_AI_INSTRUCTIONS}
197
- ${CODE_QUALITY_RULES}
198
-
199
- ${DEBUGGING_RULES}
200
-
201
- ${COLLABORATION_RULES}
202
- `;
203
-
204
- // 1. Create Universal Instructions
205
- if (!(await fs.pathExists(instructionsPath))) {
206
- await fs.writeFile(instructionsPath, ruleContent);
207
- console.log(chalk.green('✔ Initialized Universal Agent Handshake (docs/AGENT_INSTRUCTIONS.md)'));
208
- }
209
-
210
- // 2. Create IDE-Specific Rules based on detection or user preference
211
- const detectedIDE = detectIDE(cwd);
212
- const targetIDE = options.ide || (detectedIDE !== 'unknown' ? detectedIDE : 'all');
213
-
214
- if (detectedIDE !== 'unknown' && !options.ide) {
215
- console.log(chalk.dim(` (Auto-detected IDE: ${detectedIDE})`));
216
- }
217
-
218
- if (targetIDE === 'cursor' || targetIDE === 'all') {
219
- const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
220
- await fs.ensureDir(cursorRulesDir);
221
- const mdcPath = path.join(cursorRulesDir, 'rigour.mdc');
222
- const mdcContent = `---
223
- description: Enforcement of Rigour quality gates and best practices.
224
- globs: **/*
225
- ---
226
-
227
- ${ruleContent}`;
228
-
229
- if (!(await fs.pathExists(mdcPath))) {
230
- await fs.writeFile(mdcPath, mdcContent);
231
- console.log(chalk.green('✔ Initialized Cursor Handshake (.cursor/rules/rigour.mdc)'));
232
- }
233
- }
234
-
235
- if (targetIDE === 'vscode' || targetIDE === 'all') {
236
- // VS Code users use the universal AGENT_INSTRUCTIONS.md (already created above)
237
- // We could also add .vscode/settings.json or snippets here if needed
238
- console.log(chalk.green('✔ VS Code mode - using Universal Handshake (docs/AGENT_INSTRUCTIONS.md)'));
239
- }
240
-
241
- if (targetIDE === 'cline' || targetIDE === 'all') {
242
- const clineRulesPath = path.join(cwd, '.clinerules');
243
- if (!(await fs.pathExists(clineRulesPath))) {
244
- await fs.writeFile(clineRulesPath, ruleContent);
245
- console.log(chalk.green('✔ Initialized Cline Handshake (.clinerules)'));
246
- }
247
- }
248
-
249
- // Claude Code (CLAUDE.md)
250
- if (targetIDE === 'claude' || targetIDE === 'all') {
251
- const claudePath = path.join(cwd, 'CLAUDE.md');
252
- const claudeContent = `# CLAUDE.md - Project Instructions for Claude Code
253
-
254
- This file provides Claude Code with context about this project.
255
-
256
- ## Project Overview
257
-
258
- This project uses Rigour for quality gates. Always run \`npx @rigour-labs/cli check\` before marking tasks complete.
259
-
260
- ## Commands
261
-
262
- \`\`\`bash
263
- # Verify quality gates
264
- npx @rigour-labs/cli check
265
-
266
- # Get fix packet for failures
267
- npx @rigour-labs/cli explain
268
-
269
- # Self-healing agent loop
270
- npx @rigour-labs/cli run -- claude "<task>"
271
- \`\`\`
272
-
273
- ${ruleContent}`;
274
-
275
- if (!(await fs.pathExists(claudePath))) {
276
- await fs.writeFile(claudePath, claudeContent);
277
- console.log(chalk.green('✔ Initialized Claude Code Handshake (CLAUDE.md)'));
278
- }
279
- }
280
-
281
- // Gemini Code Assist (.gemini/styleguide.md)
282
- if (targetIDE === 'gemini' || targetIDE === 'all') {
283
- const geminiDir = path.join(cwd, '.gemini');
284
- await fs.ensureDir(geminiDir);
285
- const geminiStylePath = path.join(geminiDir, 'styleguide.md');
286
- const geminiContent = `# Gemini Code Assist Style Guide
287
-
288
- This project uses Rigour for quality gates.
289
-
290
- ## Required Before Completion
291
-
292
- Always run \`npx @rigour-labs/cli check\` before marking any task complete.
293
-
294
- ${ruleContent}`;
295
-
296
- if (!(await fs.pathExists(geminiStylePath))) {
297
- await fs.writeFile(geminiStylePath, geminiContent);
298
- console.log(chalk.green('✔ Initialized Gemini Handshake (.gemini/styleguide.md)'));
299
- }
300
- }
301
-
302
- // OpenAI Codex / Aider (AGENTS.md - Universal Standard)
303
- if (targetIDE === 'codex' || targetIDE === 'all') {
304
- const agentsPath = path.join(cwd, 'AGENTS.md');
305
- const agentsContent = `# AGENTS.md - Universal AI Agent Instructions
306
-
307
- This file provides instructions for AI coding agents (Codex, Aider, and others).
308
-
309
- ## Setup
310
-
311
- \`\`\`bash
312
- npm install
313
- npm run dev
314
- npm test
315
- \`\`\`
316
-
317
- ## Quality Gates
318
-
319
- This project uses Rigour. Before completing any task:
320
-
321
- \`\`\`bash
322
- npx @rigour-labs/cli check
323
- \`\`\`
324
-
325
- ${ruleContent}`;
326
-
327
- if (!(await fs.pathExists(agentsPath))) {
328
- await fs.writeFile(agentsPath, agentsContent);
329
- console.log(chalk.green('✔ Initialized Universal Agent Handshake (AGENTS.md)'));
330
- }
331
- }
332
-
333
- // Windsurf (.windsurfrules)
334
- if (targetIDE === 'windsurf' || targetIDE === 'all') {
335
- const windsurfPath = path.join(cwd, '.windsurfrules');
336
- if (!(await fs.pathExists(windsurfPath))) {
337
- await fs.writeFile(windsurfPath, ruleContent);
338
- console.log(chalk.green('✔ Initialized Windsurf Handshake (.windsurfrules)'));
339
- }
340
- }
341
-
342
- // 3. Update .gitignore
343
- const gitignorePath = path.join(cwd, '.gitignore');
344
- const ignorePatterns = ['rigour-report.json', 'rigour-fix-packet.json', '.rigour/'];
345
- try {
346
- let content = '';
347
- if (await fs.pathExists(gitignorePath)) {
348
- content = await fs.readFile(gitignorePath, 'utf-8');
349
- }
350
-
351
- const toAdd = ignorePatterns.filter(p => !content.includes(p));
352
- if (toAdd.length > 0) {
353
- const separator = content.endsWith('\n') ? '' : '\n';
354
- const newContent = `${content}${separator}\n# Rigour Artifacts\n${toAdd.join('\n')}\n`;
355
- await fs.writeFile(gitignorePath, newContent);
356
- console.log(chalk.green('✔ Updated .gitignore'));
357
- }
358
- } catch (e) {
359
- // Failing to update .gitignore isn't fatal
360
- }
361
-
362
- console.log(chalk.blue('\nRigour is ready. Run `npx @rigour-labs/cli check` to verify your project.'));
363
- console.log(chalk.dim('\n💡 Tip: Planning to use a framework like Next.js?'));
364
- console.log(chalk.dim(' Run its scaffolding tool (e.g., npx create-next-app) BEFORE rigour init,'));
365
- console.log(chalk.dim(' or move rigour.yml and docs/ aside temporarily to satisfy empty-directory checks.'));
366
- }
@@ -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
- }
@@ -1,59 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
-
3
-
4
- import fs from 'fs-extra';
5
- import path from 'path';
6
-
7
- async function getInitCommand() {
8
- const { initCommand } = await import('./commands/init.js');
9
- return initCommand;
10
- }
11
-
12
- describe('Init Command Rules Verification', () => {
13
- const testDir = path.join(process.cwd(), 'temp-init-rules-test');
14
-
15
- beforeEach(async () => {
16
- await fs.ensureDir(testDir);
17
- });
18
-
19
- afterEach(async () => {
20
- await fs.remove(testDir);
21
- });
22
-
23
- it('should create instructions with agnostic rules and cursor rules on init', async () => {
24
- const initCommand = await getInitCommand();
25
- // Run init in test directory with all IDEs to verify rules in both locations
26
- await initCommand(testDir, { ide: 'all' });
27
-
28
- const instructionsPath = path.join(testDir, 'docs', 'AGENT_INSTRUCTIONS.md');
29
- const mdcPath = path.join(testDir, '.cursor', 'rules', 'rigour.mdc');
30
-
31
- expect(await fs.pathExists(instructionsPath)).toBe(true);
32
- expect(await fs.pathExists(mdcPath)).toBe(true);
33
-
34
- const instructionsContent = await fs.readFile(instructionsPath, 'utf-8');
35
- const mdcContent = await fs.readFile(mdcPath, 'utf-8');
36
-
37
- // Check for agnostic instructions
38
- expect(instructionsContent).toContain('# 🤖 CRITICAL INSTRUCTION FOR AI');
39
- expect(instructionsContent).toContain('VERIFICATION PROOF REQUIRED');
40
-
41
- // Check for key sections in universal instructions
42
- expect(instructionsContent).toContain('# 🛡️ Rigour: Engineering Excellence Protocol');
43
- expect(instructionsContent).toContain('# Code Quality Standards');
44
-
45
- // Check that MDC includes agnostic rules
46
- expect(mdcContent).toContain('# 🤖 CRITICAL INSTRUCTION FOR AI');
47
- });
48
-
49
- it('should create .clinerules when ide is cline or all', async () => {
50
- const initCommand = await getInitCommand();
51
- await initCommand(testDir, { ide: 'cline' });
52
- const clineRulesPath = path.join(testDir, '.clinerules');
53
- expect(await fs.pathExists(clineRulesPath)).toBe(true);
54
-
55
- const content = await fs.readFile(clineRulesPath, 'utf-8');
56
- expect(content).toContain('# 🤖 CRITICAL INSTRUCTION FOR AI');
57
- });
58
-
59
- });
package/src/smoke.test.ts DELETED
@@ -1,76 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
-
3
- import fs from 'fs-extra';
4
- import path from 'path';
5
-
6
- async function getCheckCommand() {
7
- const { checkCommand } = await import('./commands/check.js');
8
- return checkCommand;
9
- }
10
-
11
- describe('CLI Smoke Test', () => {
12
- const testDir = path.join(process.cwd(), 'temp-smoke-test');
13
-
14
- beforeEach(async () => {
15
- await fs.ensureDir(testDir);
16
- // @ts-ignore
17
- vi.spyOn(process, 'exit').mockImplementation(() => { });
18
- });
19
-
20
- afterEach(async () => {
21
- await fs.remove(testDir);
22
- vi.restoreAllMocks();
23
- });
24
-
25
- it('should respect ignore patterns and avoid EPERM', async () => {
26
- const restrictedDir = path.join(testDir, '.restricted');
27
- await fs.ensureDir(restrictedDir);
28
- await fs.writeFile(path.join(restrictedDir, 'secret.js'), 'TODO: leak');
29
-
30
- await fs.writeFile(path.join(testDir, 'rigour.yml'), `
31
- version: 1
32
- ignore:
33
- - ".restricted/**"
34
- gates:
35
- forbid_todos: true
36
- required_files: []
37
- `);
38
-
39
- // Simulate EPERM by changing permissions
40
- await fs.chmod(restrictedDir, 0o000);
41
-
42
- try {
43
- // We need to mock process.exit or checkCommand should not exit if we want to test it easily
44
- // For now, we'll just verify it doesn't throw before it would exit (internal logic)
45
- // But checkCommand calls process.exit(1) on failure.
46
-
47
- // Re-importing checkCommand to ensure it uses the latest core
48
- const checkCommand = await getCheckCommand();
49
- await expect(checkCommand(testDir, [], { ci: true })).resolves.not.toThrow();
50
- } finally {
51
- await fs.chmod(restrictedDir, 0o777);
52
- }
53
- });
54
-
55
- it('should check specific files when provided', async () => {
56
- await fs.writeFile(path.join(testDir, 'bad.js'), 'TODO: fixme');
57
- await fs.writeFile(path.join(testDir, 'good.js'), 'console.log("hello")');
58
- await fs.writeFile(path.join(testDir, 'rigour.yml'), `
59
- version: 1
60
- gates:
61
- forbid_todos: true
62
- required_files: []
63
- `);
64
-
65
- // If we check ONLY good.js, it should PASS (exit PASS)
66
- const checkCommand = await getCheckCommand();
67
- await checkCommand(testDir, [path.join(testDir, 'good.js')], { ci: true });
68
- expect(process.exit).toHaveBeenCalledWith(0);
69
-
70
- // If we check bad.js, it should FAIL (exit FAIL)
71
- vi.clearAllMocks();
72
- const checkCommandFail = await getCheckCommand();
73
- await checkCommandFail(testDir, [path.join(testDir, 'bad.js')], { ci: true });
74
- expect(process.exit).toHaveBeenCalledWith(1);
75
- });
76
- });