@rigour-labs/cli 2.19.0 β 2.19.2
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/README.md +85 -0
- package/dist/cli.js +18 -2
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +10 -2
- package/dist/utils/version.d.ts +7 -0
- package/dist/utils/version.js +81 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @rigour-labs/cli
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@rigour-labs/cli)
|
|
4
|
+
[](https://www.npmjs.com/package/@rigour-labs/cli)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**Local-first quality gates for AI-generated code.**
|
|
8
|
+
Rigour forces AI agents to meet strict engineering standards before marking tasks "Done".
|
|
9
|
+
|
|
10
|
+
> **Zero cloud. Zero telemetry. PASS/FAIL is always free.**
|
|
11
|
+
|
|
12
|
+
## π Quick Start
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx rigour init # Initialize quality gates
|
|
16
|
+
npx rigour check # Verify code quality
|
|
17
|
+
npx rigour run -- claude "Build feature X" # Agent loop
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## π The Problem
|
|
21
|
+
|
|
22
|
+
AI agents often fall into **"Vibe Coding"**βclaiming success based on narrative, not execution:
|
|
23
|
+
|
|
24
|
+
1. Agent makes a change
|
|
25
|
+
2. Agent **claims** "Task 100% complete"
|
|
26
|
+
3. **CI Fails** with type errors, lint failures, or broken tests
|
|
27
|
+
|
|
28
|
+
**Rigour breaks this cycle** by forcing agents to face the same verification tools (ruff, mypy, vitest) that CI runsβlocally and immediately.
|
|
29
|
+
|
|
30
|
+
## π How It Works
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Agent writes code β Rigour checks β FAIL? β Fix Packet β Agent retries β PASS β
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## βοΈ Quality Gates
|
|
37
|
+
|
|
38
|
+
| Gate | Description |
|
|
39
|
+
|:---|:---|
|
|
40
|
+
| **File Size** | Max lines per file (default: 300-500) |
|
|
41
|
+
| **Hygiene** | No TODO/FIXME comments allowed |
|
|
42
|
+
| **Complexity** | Cyclomatic complexity limits (AST-based) |
|
|
43
|
+
| **Required Docs** | SPEC.md, ARCH.md, README must exist |
|
|
44
|
+
| **Safety Rails** | Protected paths, max files changed |
|
|
45
|
+
| **Context Alignment** | Prevents drift by anchoring on project patterns |
|
|
46
|
+
|
|
47
|
+
## π οΈ Commands
|
|
48
|
+
|
|
49
|
+
| Command | Purpose |
|
|
50
|
+
|:---|:---|
|
|
51
|
+
| `rigour init` | Setup Rigour in your project |
|
|
52
|
+
| `rigour check` | Validate code against quality gates |
|
|
53
|
+
| `rigour check --ci` | CI mode with appropriate output |
|
|
54
|
+
| `rigour explain` | Detailed explanation of validation results |
|
|
55
|
+
| `rigour run` | Supervisor loop for iterative refinement |
|
|
56
|
+
| `rigour studio` | Dashboard for monitoring |
|
|
57
|
+
| `rigour index` | Build semantic index of codebase patterns |
|
|
58
|
+
|
|
59
|
+
## π€ Works With
|
|
60
|
+
|
|
61
|
+
- **Claude Code**: `rigour run -- claude "..."`
|
|
62
|
+
- **Cursor / Cline / Gemini**: Via MCP server (`rigour_check`, `rigour_explain`)
|
|
63
|
+
|
|
64
|
+
## π Documentation
|
|
65
|
+
|
|
66
|
+
**[π Full Documentation β](https://docs.rigour.run/)**
|
|
67
|
+
|
|
68
|
+
| Quick Links | |
|
|
69
|
+
|:---|:---|
|
|
70
|
+
| [Getting Started](https://docs.rigour.run/getting-started) | Install and run in 60 seconds |
|
|
71
|
+
| [CLI Reference](https://docs.rigour.run/cli/commands) | All commands and options |
|
|
72
|
+
| [Configuration](https://docs.rigour.run/reference/configuration) | Customize quality gates |
|
|
73
|
+
| [MCP Integration](https://docs.rigour.run/mcp/mcp-server) | AI agent setup |
|
|
74
|
+
|
|
75
|
+
## π§ͺ CI Integration
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
- run: npx rigour check --ci
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## π License
|
|
82
|
+
|
|
83
|
+
MIT Β© [Rigour Labs](https://github.com/rigour-labs)
|
|
84
|
+
|
|
85
|
+
> *"Rigour adds the engineering."*
|
package/dist/cli.js
CHANGED
|
@@ -8,14 +8,16 @@ import { guideCommand } from './commands/guide.js';
|
|
|
8
8
|
import { setupCommand } from './commands/setup.js';
|
|
9
9
|
import { indexCommand } from './commands/index.js';
|
|
10
10
|
import { studioCommand } from './commands/studio.js';
|
|
11
|
+
import { checkForUpdates } from './utils/version.js';
|
|
11
12
|
import chalk from 'chalk';
|
|
13
|
+
const CLI_VERSION = '2.0.0';
|
|
12
14
|
const program = new Command();
|
|
13
15
|
program.addCommand(indexCommand);
|
|
14
16
|
program.addCommand(studioCommand);
|
|
15
17
|
program
|
|
16
18
|
.name('rigour')
|
|
17
19
|
.description('π‘οΈ Rigour: The Quality Gate Loop for AI-Assisted Engineering')
|
|
18
|
-
.version(
|
|
20
|
+
.version(CLI_VERSION)
|
|
19
21
|
.addHelpText('before', chalk.bold.cyan(`
|
|
20
22
|
____ _
|
|
21
23
|
/ __ \\(_)____ ___ __ __ _____
|
|
@@ -32,6 +34,7 @@ program
|
|
|
32
34
|
.option('--ide <name>', 'Target IDE (cursor, vscode, all). Auto-detects if not specified.')
|
|
33
35
|
.option('--dry-run', 'Show detected configuration without writing files')
|
|
34
36
|
.option('--explain', 'Show detection markers for roles and paradigms')
|
|
37
|
+
.option('-f, --force', 'Force re-initialization, overwriting existing rigour.yml')
|
|
35
38
|
.addHelpText('after', `
|
|
36
39
|
Examples:
|
|
37
40
|
$ rigour init # Auto-discover role & paradigm
|
|
@@ -100,4 +103,17 @@ program
|
|
|
100
103
|
.action(async () => {
|
|
101
104
|
await setupCommand();
|
|
102
105
|
});
|
|
103
|
-
|
|
106
|
+
// Check for updates before parsing (non-blocking)
|
|
107
|
+
(async () => {
|
|
108
|
+
try {
|
|
109
|
+
const updateInfo = await checkForUpdates(CLI_VERSION);
|
|
110
|
+
if (updateInfo?.hasUpdate) {
|
|
111
|
+
console.log(chalk.yellow(`\nβ‘ Update available: ${updateInfo.currentVersion} β ${updateInfo.latestVersion}`));
|
|
112
|
+
console.log(chalk.dim(` Run: npx @rigour-labs/cli@latest init --force\n`));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Ignore version check errors
|
|
117
|
+
}
|
|
118
|
+
program.parse();
|
|
119
|
+
})();
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export interface InitOptions {
|
|
|
4
4
|
ide?: 'cursor' | 'vscode' | 'cline' | 'claude' | 'gemini' | 'codex' | 'windsurf' | 'all';
|
|
5
5
|
dryRun?: boolean;
|
|
6
6
|
explain?: boolean;
|
|
7
|
+
force?: boolean;
|
|
7
8
|
}
|
|
8
9
|
export declare function initCommand(cwd: string, options?: InitOptions): Promise<void>;
|
package/dist/commands/init.js
CHANGED
|
@@ -127,8 +127,16 @@ export async function initCommand(cwd, options = {}) {
|
|
|
127
127
|
}
|
|
128
128
|
const configPath = path.join(cwd, 'rigour.yml');
|
|
129
129
|
if (await fs.pathExists(configPath)) {
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (!options.force) {
|
|
131
|
+
console.log(chalk.yellow('rigour.yml already exists.'));
|
|
132
|
+
console.log(chalk.dim(' β Run with --force to regenerate with latest templates'));
|
|
133
|
+
console.log(chalk.dim(' β Your current config will be backed up to rigour.yml.bak'));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Backup existing config
|
|
137
|
+
const backupPath = path.join(cwd, 'rigour.yml.bak');
|
|
138
|
+
await fs.copy(configPath, backupPath);
|
|
139
|
+
console.log(chalk.dim(` Backed up existing config to rigour.yml.bak`));
|
|
132
140
|
}
|
|
133
141
|
console.log(chalk.bold.blue('\nπ Rigour Auto-Discovery:'));
|
|
134
142
|
const requestId = randomUUID();
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const CACHE_FILE = path.join(os.homedir(), '.rigour-version-cache.json');
|
|
5
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
6
|
+
const NPM_REGISTRY_URL = 'https://registry.npmjs.org/@rigour-labs/cli/latest';
|
|
7
|
+
async function getCachedVersion() {
|
|
8
|
+
try {
|
|
9
|
+
if (await fs.pathExists(CACHE_FILE)) {
|
|
10
|
+
const cache = await fs.readJson(CACHE_FILE);
|
|
11
|
+
if (Date.now() - cache.timestamp < CACHE_TTL_MS) {
|
|
12
|
+
return cache.latestVersion;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Ignore cache read errors
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
async function cacheVersion(version) {
|
|
22
|
+
try {
|
|
23
|
+
await fs.writeJson(CACHE_FILE, {
|
|
24
|
+
latestVersion: version,
|
|
25
|
+
timestamp: Date.now()
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Ignore cache write errors
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function fetchLatestVersion() {
|
|
33
|
+
try {
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
36
|
+
const response = await fetch(NPM_REGISTRY_URL, {
|
|
37
|
+
signal: controller.signal,
|
|
38
|
+
headers: { 'Accept': 'application/json' }
|
|
39
|
+
});
|
|
40
|
+
clearTimeout(timeout);
|
|
41
|
+
if (!response.ok)
|
|
42
|
+
return null;
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return data.version || null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Network errors are non-fatal
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function compareVersions(current, latest) {
|
|
52
|
+
const currentParts = current.replace(/^v/, '').split('.').map(Number);
|
|
53
|
+
const latestParts = latest.replace(/^v/, '').split('.').map(Number);
|
|
54
|
+
for (let i = 0; i < 3; i++) {
|
|
55
|
+
const c = currentParts[i] || 0;
|
|
56
|
+
const l = latestParts[i] || 0;
|
|
57
|
+
if (l > c)
|
|
58
|
+
return true;
|
|
59
|
+
if (l < c)
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
export async function checkForUpdates(currentVersion) {
|
|
65
|
+
// Try cache first
|
|
66
|
+
let latestVersion = await getCachedVersion();
|
|
67
|
+
// Fetch from npm if no cache
|
|
68
|
+
if (!latestVersion) {
|
|
69
|
+
latestVersion = await fetchLatestVersion();
|
|
70
|
+
if (latestVersion) {
|
|
71
|
+
await cacheVersion(latestVersion);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!latestVersion)
|
|
75
|
+
return null;
|
|
76
|
+
return {
|
|
77
|
+
hasUpdate: compareVersions(currentVersion, latestVersion),
|
|
78
|
+
currentVersion,
|
|
79
|
+
latestVersion
|
|
80
|
+
};
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"rigour": "dist/cli.js"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"inquirer": "9.2.16",
|
|
29
29
|
"ora": "^8.0.1",
|
|
30
30
|
"yaml": "^2.8.2",
|
|
31
|
-
"@rigour-labs/core": "2.19.
|
|
31
|
+
"@rigour-labs/core": "2.19.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/fs-extra": "^11.0.4",
|