@rigour-labs/cli 3.0.6 → 4.0.1

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.
@@ -0,0 +1,143 @@
1
+ import chalk from 'chalk';
2
+ import { loadSettings, saveSettings, getSettingsPath, updateProviderKey, removeProviderKey } from '@rigour-labs/core';
3
+ /**
4
+ * `rigour settings` — manage ~/.rigour/settings.json
5
+ *
6
+ * Like Claude Code's settings.json or Gemini CLI's config.
7
+ * Stores API keys, default provider, multi-agent config, CLI preferences.
8
+ */
9
+ export async function settingsShowCommand() {
10
+ const settingsPath = getSettingsPath();
11
+ const settings = loadSettings();
12
+ console.log(chalk.bold.cyan('\n Rigour Settings'));
13
+ console.log(chalk.dim(` ${settingsPath}\n`));
14
+ if (Object.keys(settings).length === 0) {
15
+ console.log(chalk.dim(' No settings configured yet.\n'));
16
+ console.log(chalk.dim(' Quick start:'));
17
+ console.log(chalk.dim(' rigour settings set-key anthropic sk-ant-xxx'));
18
+ console.log(chalk.dim(' rigour settings set-key openai sk-xxx'));
19
+ console.log(chalk.dim(' rigour settings set provider anthropic'));
20
+ console.log('');
21
+ return;
22
+ }
23
+ // Show providers
24
+ if (settings.providers && Object.keys(settings.providers).length > 0) {
25
+ console.log(chalk.bold(' Providers:'));
26
+ for (const [name, key] of Object.entries(settings.providers)) {
27
+ if (key) {
28
+ const masked = maskKey(key);
29
+ console.log(` ${chalk.green(name)}: ${chalk.dim(masked)}`);
30
+ }
31
+ }
32
+ console.log('');
33
+ }
34
+ // Show deep defaults
35
+ if (settings.deep) {
36
+ console.log(chalk.bold(' Deep Analysis Defaults:'));
37
+ if (settings.deep.defaultProvider)
38
+ console.log(` Provider: ${chalk.cyan(settings.deep.defaultProvider)}`);
39
+ if (settings.deep.defaultModel)
40
+ console.log(` Model: ${chalk.cyan(settings.deep.defaultModel)}`);
41
+ if (settings.deep.apiBaseUrl)
42
+ console.log(` API Base: ${chalk.cyan(settings.deep.apiBaseUrl)}`);
43
+ if (settings.deep.maxTokens)
44
+ console.log(` Max Tokens: ${settings.deep.maxTokens}`);
45
+ if (settings.deep.temperature !== undefined)
46
+ console.log(` Temperature: ${settings.deep.temperature}`);
47
+ console.log('');
48
+ }
49
+ // Show agent configs
50
+ if (settings.agents && Object.keys(settings.agents).length > 0) {
51
+ console.log(chalk.bold(' Agent Configurations:'));
52
+ for (const [name, config] of Object.entries(settings.agents)) {
53
+ const parts = [];
54
+ if (config.model)
55
+ parts.push(`model: ${config.model}`);
56
+ if (config.provider)
57
+ parts.push(`provider: ${config.provider}`);
58
+ if (config.fallback)
59
+ parts.push(`fallback: ${config.fallback}`);
60
+ console.log(` ${chalk.green(name)}: ${chalk.dim(parts.join(', '))}`);
61
+ }
62
+ console.log('');
63
+ }
64
+ // Show CLI prefs
65
+ if (settings.cli) {
66
+ console.log(chalk.bold(' CLI Preferences:'));
67
+ if (settings.cli.defaultPreset)
68
+ console.log(` Default Preset: ${settings.cli.defaultPreset}`);
69
+ if (settings.cli.colorOutput !== undefined)
70
+ console.log(` Color Output: ${settings.cli.colorOutput}`);
71
+ if (settings.cli.verboseOutput !== undefined)
72
+ console.log(` Verbose: ${settings.cli.verboseOutput}`);
73
+ console.log('');
74
+ }
75
+ }
76
+ export async function settingsSetKeyCommand(provider, apiKey) {
77
+ updateProviderKey(provider, apiKey);
78
+ const masked = maskKey(apiKey);
79
+ console.log(chalk.green(` ✓ ${provider} API key saved: ${masked}`));
80
+ console.log(chalk.dim(` Stored in ${getSettingsPath()}`));
81
+ console.log('');
82
+ console.log(chalk.dim(` Usage: rigour check --deep --provider ${provider}`));
83
+ console.log(chalk.dim(` Or set as default: rigour settings set provider ${provider}`));
84
+ }
85
+ export async function settingsRemoveKeyCommand(provider) {
86
+ removeProviderKey(provider);
87
+ console.log(chalk.green(` ✓ ${provider} API key removed`));
88
+ }
89
+ export async function settingsSetCommand(key, value) {
90
+ const settings = loadSettings();
91
+ // Parse dot-notation keys: "deep.defaultProvider" -> settings.deep.defaultProvider
92
+ const parts = key.split('.');
93
+ let target = settings;
94
+ for (let i = 0; i < parts.length - 1; i++) {
95
+ if (!target[parts[i]])
96
+ target[parts[i]] = {};
97
+ target = target[parts[i]];
98
+ }
99
+ const lastKey = parts[parts.length - 1];
100
+ // Auto-convert booleans and numbers
101
+ if (value === 'true')
102
+ target[lastKey] = true;
103
+ else if (value === 'false')
104
+ target[lastKey] = false;
105
+ else if (!isNaN(Number(value)) && value.trim() !== '')
106
+ target[lastKey] = Number(value);
107
+ else
108
+ target[lastKey] = value;
109
+ saveSettings(settings);
110
+ console.log(chalk.green(` ✓ ${key} = ${value}`));
111
+ }
112
+ export async function settingsGetCommand(key) {
113
+ const settings = loadSettings();
114
+ const parts = key.split('.');
115
+ let value = settings;
116
+ for (const part of parts) {
117
+ if (value === undefined || value === null)
118
+ break;
119
+ value = value[part];
120
+ }
121
+ if (value === undefined) {
122
+ console.log(chalk.dim(` ${key} is not set`));
123
+ }
124
+ else if (typeof value === 'object') {
125
+ console.log(` ${key} = ${JSON.stringify(value, null, 2)}`);
126
+ }
127
+ else {
128
+ console.log(` ${key} = ${value}`);
129
+ }
130
+ }
131
+ export async function settingsResetCommand() {
132
+ saveSettings({});
133
+ console.log(chalk.green(' ✓ Settings reset to defaults'));
134
+ console.log(chalk.dim(` ${getSettingsPath()}`));
135
+ }
136
+ export async function settingsPathCommand() {
137
+ console.log(getSettingsPath());
138
+ }
139
+ function maskKey(key) {
140
+ if (key.length <= 8)
141
+ return '***';
142
+ return key.substring(0, 6) + '...' + key.substring(key.length - 4);
143
+ }
@@ -1,22 +1,113 @@
1
1
  import chalk from 'chalk';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
4
+ import { loadSettings, getSettingsPath, isModelCached, getModelsDir } from '@rigour-labs/core';
2
5
  export async function setupCommand() {
3
- console.log(chalk.bold.cyan('\n🛠️ Rigour Labs | Setup & Installation\n'));
4
- console.log(chalk.bold('1. Global Installation (Recommended)'));
5
- console.log(chalk.dim(' To use Rigour anywhere in your terminal:'));
6
- console.log(chalk.green(' $ npm install -g @rigour-labs/cli\n'));
7
- console.log(chalk.bold('2. Project-Local installation'));
8
- console.log(chalk.dim(' To keep Rigour versioned with your project:'));
9
- console.log(chalk.green(' $ npm install --save-dev @rigour-labs/cli\n'));
10
- console.log(chalk.bold('3. Standalone Binaries (Zero-Install)'));
11
- console.log(chalk.dim(' If you do not want to use Node.js:'));
12
- console.log(chalk.dim(' • macOS: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-macos'));
13
- console.log(chalk.dim(' Linux: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-linux'));
14
- console.log(chalk.dim(' • Windows: ') + chalk.cyan('https://github.com/erashu212/rigour/releases/latest/download/rigour-windows.exe\n'));
15
- console.log(chalk.bold('4. MCP Integration (for AI Agents)'));
16
- console.log(chalk.dim(' To let Cursor or Claude use Rigour natively:'));
17
- console.log(chalk.dim(' Path to MCP: ') + chalk.cyan('packages/rigour-mcp/dist/index.js'));
18
- console.log(chalk.dim(' Add this to your Cursor/Claude settings.\n'));
19
- console.log(chalk.bold('Update Guidance:'));
20
- console.log(chalk.dim(' Keep Rigour sharp by updating regularly:'));
21
- console.log(chalk.green(' $ npm install -g @rigour-labs/cli@latest\n'));
6
+ console.log(chalk.bold.cyan('\n🛠️ Rigour Labs | Setup & System Check\n'));
7
+ // ── Section 1: Installation Status ──
8
+ console.log(chalk.bold(' Installation'));
9
+ const cliVersion = getCliVersion();
10
+ if (cliVersion) {
11
+ console.log(chalk.green(` ✔ Rigour CLI ${cliVersion}`));
12
+ }
13
+ // Check if rigour.yml exists in cwd
14
+ const hasConfig = fs.existsSync(path.join(process.cwd(), 'rigour.yml'));
15
+ if (hasConfig) {
16
+ console.log(chalk.green(' rigour.yml found in current directory'));
17
+ }
18
+ else {
19
+ console.log(chalk.yellow(' No rigour.yml run `rigour init` to set up'));
20
+ }
21
+ // ── Section 2: Settings & API Keys ──
22
+ console.log(chalk.bold('\n Settings'));
23
+ const settingsPath = getSettingsPath();
24
+ const settings = loadSettings();
25
+ const providers = settings.providers || {};
26
+ const configuredKeys = Object.entries(providers).filter(([_, key]) => !!key);
27
+ if (configuredKeys.length > 0) {
28
+ for (const [name, key] of configuredKeys) {
29
+ if (key) {
30
+ const masked = key.length > 8 ? key.substring(0, 6) + '...' + key.substring(key.length - 4) : '***';
31
+ console.log(chalk.green(` ✔ ${name}: ${chalk.dim(masked)}`));
32
+ }
33
+ }
34
+ }
35
+ else {
36
+ console.log(chalk.yellow(' ○ No API keys configured'));
37
+ console.log(chalk.dim(` ${settingsPath}`));
38
+ }
39
+ if (settings.deep?.defaultProvider) {
40
+ console.log(chalk.green(` ✔ Default provider: ${settings.deep.defaultProvider}`));
41
+ }
42
+ // ── Section 3: Deep Analysis Readiness ──
43
+ console.log(chalk.bold('\n Deep Analysis'));
44
+ // Check local models
45
+ const hasDeep = isModelCached('deep');
46
+ const hasPro = isModelCached('pro');
47
+ if (hasDeep)
48
+ console.log(chalk.green(' ✔ Local model: deep (Qwen2.5-Coder-0.5B, 350MB)'));
49
+ if (hasPro)
50
+ console.log(chalk.green(' ✔ Local model: pro (Qwen2.5-Coder-1.5B, 900MB)'));
51
+ if (!hasDeep && !hasPro) {
52
+ console.log(chalk.yellow(' ○ No local models cached'));
53
+ console.log(chalk.dim(` Models dir: ${getModelsDir()}`));
54
+ }
55
+ // Check sidecar binary
56
+ let hasSidecar = false;
57
+ try {
58
+ const { execSync } = await import('child_process');
59
+ execSync('which llama-cli 2>/dev/null || which rigour-brain 2>/dev/null', { encoding: 'utf-8', timeout: 3000 });
60
+ hasSidecar = true;
61
+ console.log(chalk.green(' ✔ Inference binary found'));
62
+ }
63
+ catch {
64
+ const binDir = path.join(getModelsDir(), '..', 'bin');
65
+ if (fs.existsSync(path.join(binDir, 'rigour-brain')) || fs.existsSync(path.join(binDir, 'llama-cli'))) {
66
+ hasSidecar = true;
67
+ console.log(chalk.green(' ✔ Inference binary found'));
68
+ }
69
+ else if (configuredKeys.length === 0) {
70
+ console.log(chalk.yellow(' ○ No local inference binary'));
71
+ }
72
+ }
73
+ // Cloud readiness
74
+ const hasCloudKey = configuredKeys.length > 0;
75
+ const hasLocalReady = hasSidecar && (hasDeep || hasPro);
76
+ if (hasCloudKey || hasLocalReady) {
77
+ console.log(chalk.green.bold('\n ✓ Deep analysis is ready'));
78
+ }
79
+ else {
80
+ console.log(chalk.yellow.bold('\n ⚠ Deep analysis not configured'));
81
+ }
82
+ // ── Section 4: Quick Setup Commands ──
83
+ if (!hasCloudKey && !hasLocalReady) {
84
+ console.log(chalk.bold('\n Quick Setup:'));
85
+ console.log(chalk.dim(' # Option A: Cloud (recommended)'));
86
+ console.log(` ${chalk.cyan('rigour settings set-key anthropic')} ${chalk.dim('sk-ant-xxx')}`);
87
+ console.log(` ${chalk.cyan('rigour settings set-key openai')} ${chalk.dim('sk-xxx')}`);
88
+ console.log(` ${chalk.cyan('rigour settings set-key groq')} ${chalk.dim('gsk_xxx')}`);
89
+ console.log('');
90
+ console.log(chalk.dim(' # Option B: 100% Local'));
91
+ console.log(` ${chalk.cyan('rigour check --deep')} ${chalk.dim('# auto-downloads 350MB model')}`);
92
+ }
93
+ // ── Section 5: Installation Methods ──
94
+ console.log(chalk.bold('\n Installation Methods:'));
95
+ console.log(chalk.dim(' Global: ') + chalk.cyan('npm install -g @rigour-labs/cli'));
96
+ console.log(chalk.dim(' Local: ') + chalk.cyan('npm install --save-dev @rigour-labs/cli'));
97
+ console.log(chalk.dim(' No-install: ') + chalk.cyan('npx @rigour-labs/cli check'));
98
+ console.log(chalk.dim(' MCP: ') + chalk.cyan('packages/rigour-mcp/dist/index.js'));
99
+ console.log('');
100
+ }
101
+ function getCliVersion() {
102
+ try {
103
+ const pkgPath = path.resolve(new URL(import.meta.url).pathname, '../../../package.json');
104
+ if (fs.existsSync(pkgPath)) {
105
+ const pkg = fs.readJsonSync(pkgPath);
106
+ return pkg.version || null;
107
+ }
108
+ }
109
+ catch {
110
+ // fallback
111
+ }
112
+ return '2.0.0';
22
113
  }
@@ -295,6 +295,43 @@ async function setupApiAndLaunch(apiPort, studioPort, eventsPath, cwd, studioPro
295
295
  res.end(e.message);
296
296
  }
297
297
  }
298
+ else if (url.pathname === '/api/report-stats') {
299
+ try {
300
+ const reportPath = path.join(cwd, 'rigour-report.json');
301
+ if (await fs.pathExists(reportPath)) {
302
+ const report = await fs.readJson(reportPath);
303
+ res.writeHead(200, { 'Content-Type': 'application/json' });
304
+ res.end(JSON.stringify(report.stats || {}));
305
+ }
306
+ else {
307
+ res.writeHead(200, { 'Content-Type': 'application/json' });
308
+ res.end(JSON.stringify({}));
309
+ }
310
+ }
311
+ catch (e) {
312
+ res.writeHead(500);
313
+ res.end(e.message);
314
+ }
315
+ }
316
+ else if (url.pathname === '/api/deep-findings') {
317
+ try {
318
+ const reportPath = path.join(cwd, 'rigour-report.json');
319
+ if (await fs.pathExists(reportPath)) {
320
+ const report = await fs.readJson(reportPath);
321
+ const findings = (report.failures || []).filter((f) => f.provenance === 'deep-analysis' || f.source === 'llm' || f.source === 'hybrid');
322
+ res.writeHead(200, { 'Content-Type': 'application/json' });
323
+ res.end(JSON.stringify(findings));
324
+ }
325
+ else {
326
+ res.writeHead(200, { 'Content-Type': 'application/json' });
327
+ res.end(JSON.stringify([]));
328
+ }
329
+ }
330
+ catch (e) {
331
+ res.writeHead(500);
332
+ res.end(e.message);
333
+ }
334
+ }
298
335
  else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
299
336
  let body = '';
300
337
  req.on('data', chunk => body += chunk);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/cli",
3
- "version": "3.0.6",
3
+ "version": "4.0.1",
4
4
  "description": "CLI quality gates for AI-generated code. Forces AI agents (Claude, Cursor, Copilot) to meet strict engineering standards with PASS/FAIL enforcement.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://rigour.run",
@@ -44,7 +44,7 @@
44
44
  "inquirer": "9.2.16",
45
45
  "ora": "^8.0.1",
46
46
  "yaml": "^2.8.2",
47
- "@rigour-labs/core": "3.0.6"
47
+ "@rigour-labs/core": "4.0.1"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/fs-extra": "^11.0.4",