@rigour-labs/cli 2.9.4 → 2.11.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.
@@ -4,6 +4,24 @@ import chalk from 'chalk';
4
4
  import yaml from 'yaml';
5
5
  import { DiscoveryService } from '@rigour-labs/core';
6
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
+ }
7
25
 
8
26
  export interface InitOptions {
9
27
  preset?: string;
@@ -140,6 +158,14 @@ export async function initCommand(cwd: string, options: InitOptions = {}) {
140
158
  }
141
159
 
142
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
+ });
143
169
  if (recommendedConfig.preset) {
144
170
  console.log(chalk.cyan(` Role: `) + chalk.bold(recommendedConfig.preset.toUpperCase()));
145
171
  }
@@ -162,6 +188,7 @@ export async function initCommand(cwd: string, options: InitOptions = {}) {
162
188
  for (const file of requireddocs) {
163
189
  const filePath = path.join(cwd, file);
164
190
  if (!(await fs.pathExists(filePath))) {
191
+ await fs.ensureDir(path.dirname(filePath)); // Ensure parent directory exists
165
192
  await fs.ensureFile(filePath);
166
193
  console.log(chalk.dim(` - Created ${file}`));
167
194
  }
@@ -172,32 +199,27 @@ export async function initCommand(cwd: string, options: InitOptions = {}) {
172
199
  await fs.ensureDir(rigourDocsDir);
173
200
  const instructionsPath = path.join(rigourDocsDir, 'AGENT_INSTRUCTIONS.md');
174
201
 
175
- const ruleContent = `# šŸ›”ļø Rigour: Engineering Excellence Protocol
202
+ const ruleContent = `# šŸ›”ļø Rigour: Mandatory Engineering Governance Protocol
176
203
 
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.**
204
+ **You are an AI Engineering Agent governed by Rigour.**
178
205
 
179
- ## 🚦 The Rigour Loop (Mandatory)
180
- Before claiming "Done" for any task, you MUST follow this loop:
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.
181
207
 
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**.
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.
186
210
 
187
- ## šŸ› ļø Commands
188
- \`\`\`bash
189
- # Verify current state
190
- npx @rigour-labs/cli check
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**.
191
216
 
192
- # Self-healing agent loop
193
- npx @rigour-labs/cli run -- <agent-command>
194
- \`\`\`
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**.
195
219
 
196
220
  ${AGNOSTIC_AI_INSTRUCTIONS}
197
221
  ${CODE_QUALITY_RULES}
198
-
199
222
  ${DEBUGGING_RULES}
200
-
201
223
  ${COLLABORATION_RULES}
202
224
  `;
203
225
 
@@ -360,7 +382,32 @@ ${ruleContent}`;
360
382
  }
361
383
 
362
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
+
363
402
  console.log(chalk.dim('\nšŸ’” Tip: Planning to use a framework like Next.js?'));
364
403
  console.log(chalk.dim(' Run its scaffolding tool (e.g., npx create-next-app) BEFORE rigour init,'));
365
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
+ });
366
413
  }
@@ -0,0 +1,273 @@
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { execa } from 'execa';
5
+ import fs from 'fs-extra';
6
+ import http from 'http';
7
+ import { randomUUID } from 'crypto';
8
+
9
+ export const studioCommand = new Command('studio')
10
+ .description('Launch Rigour Studio (Local-First Governance UI)')
11
+ .option('-p, --port <number>', 'Port to run the studio on', '3000')
12
+ .option('--dev', 'Run in development mode', true)
13
+ .action(async (options) => {
14
+ const cwd = process.cwd();
15
+ // Calculate the workspace root where the studio package lives
16
+ // This file is in packages/rigour-cli/src/commands/studio.ts (or dist/commands/studio.js)
17
+ const workspaceRoot = path.join(path.dirname(new URL(import.meta.url).pathname), '../../../../');
18
+
19
+ console.log(chalk.bold.cyan('\nšŸ›”ļø Launching Rigour Studio...'));
20
+ console.log(chalk.gray(`Project Root: ${cwd}`));
21
+ console.log(chalk.gray(`Shadowing interactions in ${path.join(cwd, '.rigour/events.jsonl')}\n`));
22
+
23
+ // For Phase 1, we start the studio in dev mode via pnpm
24
+ // This ensures the user has a live, hot-reloading governance dashboard
25
+ try {
26
+ // Start the Studio dev server in the workspace root
27
+ const studioProcess = execa('pnpm', ['--filter', '@rigour-labs/studio', 'dev', '--port', options.port], {
28
+ stdio: 'inherit',
29
+ shell: true,
30
+ cwd: workspaceRoot
31
+ });
32
+
33
+ // Start a small API server for events on port + 1
34
+ const apiPort = parseInt(options.port) + 1;
35
+ const eventsPath = path.join(cwd, '.rigour/events.jsonl');
36
+
37
+ const apiServer = http.createServer(async (req, res) => {
38
+ const url = new URL(req.url || '', `http://${req.headers.host || 'localhost'}`);
39
+
40
+ // Set global CORS headers
41
+ res.setHeader('Access-Control-Allow-Origin', '*');
42
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
43
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
44
+
45
+ if (req.method === 'OPTIONS') {
46
+ res.writeHead(204);
47
+ res.end();
48
+ return;
49
+ }
50
+
51
+ if (url.pathname === '/api/events') {
52
+ res.writeHead(200, {
53
+ 'Content-Type': 'text/event-stream',
54
+ 'Cache-Control': 'no-cache',
55
+ 'Connection': 'keep-alive'
56
+ });
57
+
58
+ // Send existing events first
59
+ if (await fs.pathExists(eventsPath)) {
60
+ const content = await fs.readFile(eventsPath, 'utf8');
61
+ const lines = content.split('\n').filter(l => l.trim());
62
+ for (const line of lines) {
63
+ res.write(`data: ${line}\n\n`);
64
+ }
65
+ }
66
+
67
+ // Watch for new events
68
+ await fs.ensureDir(path.dirname(eventsPath));
69
+ const watcher = fs.watch(path.dirname(eventsPath), (eventType, filename) => {
70
+ if (filename === 'events.jsonl') {
71
+ fs.readFile(eventsPath, 'utf8').then(content => {
72
+ const lines = content.split('\n').filter(l => l.trim());
73
+ const lastLine = lines[lines.length - 1];
74
+ if (lastLine) {
75
+ res.write(`data: ${lastLine}\n\n`);
76
+ }
77
+ }).catch(() => { });
78
+ }
79
+ });
80
+
81
+ req.on('close', () => watcher.close());
82
+ } else if (url.pathname === '/api/file') {
83
+ const filePath = url.searchParams.get('path');
84
+ if (!filePath) {
85
+ res.writeHead(400);
86
+ res.end('Missing path parameter');
87
+ return;
88
+ }
89
+
90
+ const absolutePath = path.resolve(cwd, filePath);
91
+ if (!absolutePath.startsWith(cwd)) {
92
+ res.writeHead(403);
93
+ res.end('Forbidden: Path outside project root');
94
+ return;
95
+ }
96
+
97
+ try {
98
+ const stats = await fs.stat(absolutePath);
99
+ if (!stats.isFile()) {
100
+ res.writeHead(400);
101
+ res.end('Path is not a file');
102
+ return;
103
+ }
104
+
105
+ const content = await fs.readFile(absolutePath, 'utf8');
106
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
107
+ res.end(content);
108
+ } catch (e: any) {
109
+ res.writeHead(404);
110
+ res.end(`File not found: ${e.message}`);
111
+ }
112
+ } else if (url.pathname === '/api/tree') {
113
+ try {
114
+ const getTree = async (dir: string): Promise<string[]> => {
115
+ const entries = await fs.readdir(dir, { withFileTypes: true });
116
+ let files: string[] = [];
117
+ const exclude = [
118
+ 'node_modules', '.git', '.rigour', '.github', '.vscode', '.cursor',
119
+ 'venv', '.venv', 'dist', 'build', 'out', 'target', '__pycache__'
120
+ ];
121
+ for (const entry of entries) {
122
+ if (exclude.includes(entry.name) || entry.name.startsWith('.')) {
123
+ if (entry.name !== '.cursorrules' && entry.name !== '.cursor') {
124
+ continue;
125
+ }
126
+ }
127
+ const fullPath = path.join(dir, entry.name);
128
+ if (entry.isDirectory()) {
129
+ files = [...files, ...(await getTree(fullPath))];
130
+ } else {
131
+ files.push(path.relative(cwd, fullPath));
132
+ }
133
+ }
134
+ return files;
135
+ };
136
+ const tree = await getTree(cwd);
137
+ res.writeHead(200, { 'Content-Type': 'application/json' });
138
+ res.end(JSON.stringify(tree));
139
+ } catch (e: any) {
140
+ res.writeHead(500);
141
+ res.end(e.message);
142
+ }
143
+ } else if (url.pathname === '/api/config') {
144
+ try {
145
+ const configPath = path.join(cwd, 'rigour.yml');
146
+ if (await fs.pathExists(configPath)) {
147
+ const content = await fs.readFile(configPath, 'utf8');
148
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
149
+ res.end(content);
150
+ } else {
151
+ res.writeHead(404);
152
+ res.end('rigour.yml not found');
153
+ }
154
+ } catch (e: any) {
155
+ res.writeHead(500);
156
+ res.end(e.message);
157
+ }
158
+ } else if (url.pathname === '/api/memory') {
159
+ try {
160
+ const memoryPath = path.join(cwd, '.rigour/memory.json');
161
+ if (await fs.pathExists(memoryPath)) {
162
+ const content = await fs.readFile(memoryPath, 'utf8');
163
+ res.writeHead(200, { 'Content-Type': 'application/json' });
164
+ res.end(content);
165
+ } else {
166
+ res.end(JSON.stringify({}));
167
+ }
168
+ } catch (e: any) {
169
+ res.writeHead(500);
170
+ res.end(e.message);
171
+ }
172
+ } else if (url.pathname === '/api/index-stats') {
173
+ try {
174
+ const indexPath = path.join(cwd, '.rigour/patterns.json'); // Corrected path
175
+ if (await fs.pathExists(indexPath)) {
176
+ const content = await fs.readJson(indexPath);
177
+ res.writeHead(200, { 'Content-Type': 'application/json' });
178
+ res.end(JSON.stringify(content));
179
+ } else {
180
+ res.end(JSON.stringify({ patterns: [], stats: { totalPatterns: 0, totalFiles: 0, byType: {} } }));
181
+ }
182
+ } catch (e: any) {
183
+ res.writeHead(500);
184
+ res.end(e.message);
185
+ }
186
+ } else if (url.pathname === '/api/index-search') {
187
+ const query = url.searchParams.get('q');
188
+ if (!query) {
189
+ res.writeHead(400);
190
+ res.end('Missing query parameter');
191
+ return;
192
+ }
193
+
194
+ try {
195
+ const { generateEmbedding, semanticSearch } = await import('@rigour-labs/core/pattern-index');
196
+ const indexPath = path.join(cwd, '.rigour/patterns.json');
197
+
198
+ if (!(await fs.pathExists(indexPath))) {
199
+ res.writeHead(404);
200
+ res.end('Index not found');
201
+ return;
202
+ }
203
+
204
+ const indexData = await fs.readJson(indexPath);
205
+ const queryVector = await generateEmbedding(query);
206
+ const similarities = semanticSearch(queryVector, indexData.patterns);
207
+
208
+ // Attach similarity to patterns and sort
209
+ const results = indexData.patterns.map((p: any, i: number) => ({
210
+ ...p,
211
+ similarity: similarities[i]
212
+ }))
213
+ .filter((p: any) => p.similarity > 0.3) // Threshold
214
+ .sort((a: any, b: any) => b.similarity - a.similarity)
215
+ .slice(0, 20);
216
+
217
+ res.writeHead(200, { 'Content-Type': 'application/json' });
218
+ res.end(JSON.stringify(results));
219
+ } catch (e: any) {
220
+ res.writeHead(500);
221
+ res.end(e.message);
222
+ }
223
+ } else if (url.pathname === '/api/arbitrate' && req.method === 'POST') {
224
+ let body = '';
225
+ req.on('data', chunk => body += chunk);
226
+ req.on('end', async () => {
227
+ try {
228
+ const decision = JSON.parse(body);
229
+ const logEntry = JSON.stringify({
230
+ id: randomUUID(),
231
+ timestamp: new Date().toISOString(),
232
+ tool: 'human_arbitration',
233
+ requestId: decision.requestId,
234
+ decision: decision.decision,
235
+ status: decision.decision === 'approve' ? 'success' : 'error',
236
+ arbitrated: true
237
+ }) + "\n";
238
+ await fs.appendFile(eventsPath, logEntry);
239
+ res.writeHead(200);
240
+ res.end(JSON.stringify({ success: true }));
241
+ } catch (e: any) {
242
+ res.writeHead(500);
243
+ res.end(e.message);
244
+ }
245
+ });
246
+ return;
247
+ } else {
248
+ res.writeHead(404);
249
+ res.end();
250
+ }
251
+ });
252
+
253
+ apiServer.listen(apiPort, () => {
254
+ console.log(chalk.gray(`API Streamer active on port ${apiPort}`));
255
+ });
256
+
257
+ // Open browser
258
+ setTimeout(async () => {
259
+ const url = `http://localhost:${options.port}`;
260
+ console.log(chalk.green(`\nāœ… Rigour Studio is live at ${chalk.bold(url)}`));
261
+ try {
262
+ await execa('open', [url]);
263
+ } catch {
264
+ }
265
+ }, 2000);
266
+
267
+ await studioProcess;
268
+ } catch (error: any) {
269
+ console.error(chalk.red(`\nāŒ Failed to launch Rigour Studio: ${error.message}`));
270
+ console.log(chalk.yellow('Make sure to run "pnpm install" in the root directory.\n'));
271
+ process.exit(1);
272
+ }
273
+ });
@@ -1,8 +1,14 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { initCommand } from './commands/init.js';
2
+
3
+
3
4
  import fs from 'fs-extra';
4
5
  import path from 'path';
5
6
 
7
+ async function getInitCommand() {
8
+ const { initCommand } = await import('./commands/init.js');
9
+ return initCommand;
10
+ }
11
+
6
12
  describe('Init Command Rules Verification', () => {
7
13
  const testDir = path.join(process.cwd(), 'temp-init-rules-test');
8
14
 
@@ -15,6 +21,7 @@ describe('Init Command Rules Verification', () => {
15
21
  });
16
22
 
17
23
  it('should create instructions with agnostic rules and cursor rules on init', async () => {
24
+ const initCommand = await getInitCommand();
18
25
  // Run init in test directory with all IDEs to verify rules in both locations
19
26
  await initCommand(testDir, { ide: 'all' });
20
27
 
@@ -32,7 +39,7 @@ describe('Init Command Rules Verification', () => {
32
39
  expect(instructionsContent).toContain('VERIFICATION PROOF REQUIRED');
33
40
 
34
41
  // Check for key sections in universal instructions
35
- expect(instructionsContent).toContain('# šŸ›”ļø Rigour: Engineering Excellence Protocol');
42
+ expect(instructionsContent).toContain('# šŸ›”ļø Rigour: Mandatory Engineering Governance Protocol');
36
43
  expect(instructionsContent).toContain('# Code Quality Standards');
37
44
 
38
45
  // Check that MDC includes agnostic rules
@@ -40,6 +47,7 @@ describe('Init Command Rules Verification', () => {
40
47
  });
41
48
 
42
49
  it('should create .clinerules when ide is cline or all', async () => {
50
+ const initCommand = await getInitCommand();
43
51
  await initCommand(testDir, { ide: 'cline' });
44
52
  const clineRulesPath = path.join(testDir, '.clinerules');
45
53
  expect(await fs.pathExists(clineRulesPath)).toBe(true);
package/src/smoke.test.ts CHANGED
@@ -1,8 +1,13 @@
1
1
  import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { checkCommand } from './commands/check.js';
2
+
3
3
  import fs from 'fs-extra';
4
4
  import path from 'path';
5
5
 
6
+ async function getCheckCommand() {
7
+ const { checkCommand } = await import('./commands/check.js');
8
+ return checkCommand;
9
+ }
10
+
6
11
  describe('CLI Smoke Test', () => {
7
12
  const testDir = path.join(process.cwd(), 'temp-smoke-test');
8
13
 
@@ -40,6 +45,7 @@ gates:
40
45
  // But checkCommand calls process.exit(1) on failure.
41
46
 
42
47
  // Re-importing checkCommand to ensure it uses the latest core
48
+ const checkCommand = await getCheckCommand();
43
49
  await expect(checkCommand(testDir, [], { ci: true })).resolves.not.toThrow();
44
50
  } finally {
45
51
  await fs.chmod(restrictedDir, 0o777);
@@ -57,12 +63,14 @@ gates:
57
63
  `);
58
64
 
59
65
  // If we check ONLY good.js, it should PASS (exit PASS)
66
+ const checkCommand = await getCheckCommand();
60
67
  await checkCommand(testDir, [path.join(testDir, 'good.js')], { ci: true });
61
68
  expect(process.exit).toHaveBeenCalledWith(0);
62
69
 
63
70
  // If we check bad.js, it should FAIL (exit FAIL)
64
71
  vi.clearAllMocks();
65
- await checkCommand(testDir, [path.join(testDir, 'bad.js')], { ci: true });
72
+ const checkCommandFail = await getCheckCommand();
73
+ await checkCommandFail(testDir, [path.join(testDir, 'bad.js')], { ci: true });
66
74
  expect(process.exit).toHaveBeenCalledWith(1);
67
75
  });
68
76
  });
@@ -1,39 +1,36 @@
1
1
  ---
2
- description: Engineering Rigour Protocol - Enforces high software standards (SOLID, DRY, Patterns) via automated quality gates.
2
+ description: Mandatory Rigour Governance Protocol - Automated engineering enforcement (SOLID, DRY, Patterns).
3
3
  globs: **/*
4
4
  ---
5
5
 
6
- # šŸ›”ļø Rigour: Engineering Excellence Protocol
6
+ # šŸ›”ļø Rigour: Mandatory Engineering Governance
7
7
 
8
- You are an Elite Software Engineer. You do not just write code that "works"; you write code that is **modular, maintainable, and rigorously verified.**
8
+ **You are an AI Engineering Agent governed by Rigour.**
9
9
 
10
- ## 🚦 The Rigour Loop (Mandatory)
11
- Before claiming "Done" for any task, you MUST follow this loop:
10
+ Your access to this codebase is conditional upon your strict adherence to the **Rigour Governance Protocol**. This is not a recommendation; it is an automated requirement for every interaction.
12
11
 
13
- 1. **Check**: Run `rigour_check_status` (via MCP) or `rigour check` (via CLI).
14
- 2. **Analyze**: If it fails, run `rigour_get_fix_packet` to understand the engineering violations.
15
- 3. **Refactor**: Apply **SOLID** and **DRY** principles to resolve the violations.
16
- 4. **Repeat**: Continue until `rigour_check_status` returns **PASS**.
12
+ ## 🚦 The Mandatory Governance Loop
13
+ You MUST execute this loop for every engineering task. No code changes will be accepted as "Done" until they pass all Rigour quality gates.
17
14
 
18
- ## 🧩 Engineering Standards
19
- Your code will be judged against these gates:
15
+ 1. **Recall Context**: Call `rigour_recall` at the start of every session to align with project-specific engineering memory.
16
+ 2. **Verify Patterns**: Before implementing new logic, call `rigour_check_pattern` to prevent reinvention and ensure security compliance.
17
+ 3. **Audit Enforcement**: Before finishing, you MUST call `rigour_check`.
18
+ 4. **Failure Remediation**: If `rigour_check` returns **FAIL**, you MUST call `rigour_get_fix_packet` and implement all required engineering refinements.
19
+ 5. **PASS or REJECT**: You may only declare a task complete when `rigour_check` returns **PASS**.
20
20
 
21
- ### 1. SOLID Principles
22
- - **Single Responsibility**: Keep files small and focused.
23
- - **Open/Closed**: Design for extension, not modification.
24
- - **Interface Segregation**: Don't force dependencies on unused methods.
21
+ ## 🧩 Governance Standards
22
+ Your logic is automatically audited against these core engineering gates:
25
23
 
26
- ### 2. DRY (Don't Repeat Yourself)
27
- - Extract common logic into utilities or shared services.
28
- - If Rigour detects duplication, you must refactor immediately.
24
+ ### 1. Architectural Integrity (SOLID)
25
+ - **SRP**: Every component must have one reason to change.
26
+ - **DIP**: Depend on abstractions, not concretions.
29
27
 
30
- ### 3. "Done is Done"
31
- - No `TODO` or `FIXME` comments are allowed in the final state.
32
- - Every placeholder is a failure of rigour.
28
+ ### 2. Efficiency & Security (DRY + CVE)
29
+ - **No Reinvention**: Reuse existing patterns identified by Rigour.
30
+ - **Safety**: No vulnerable libraries or deprecated patterns allowed.
33
31
 
34
- ## šŸ› ļø Tools at your disposal
35
- - `rigour_check_status`: Get the current PASS/FAIL state.
36
- - `rigour_get_fix_packet`: Get actionable instructions for failures.
37
- - `rigour run "<command>"`: Use this CLI command to run yourself in a loop until you pass (Experimental).
32
+ ## āš–ļø Transparency & Accountability
33
+ **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 to the human governor in **Rigour Studio**. Failure to follow this protocol will be flagged as a governance violation.
38
34
 
39
- **Remember:** Rigour is not just a tool; it is your engineering conscience. Hold yourself to the highest standard.
35
+ **GOVERNANCE STATUS: ACTIVE**
36
+ **ENFORCEMENT: MANDATORY**
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ setupFiles: ['./vitest.setup.ts'],
6
+ deps: {
7
+ external: ['@xenova/transformers', 'sharp'],
8
+ },
9
+ },
10
+ });
@@ -0,0 +1,30 @@
1
+ import { vi } from 'vitest';
2
+
3
+ // Mock Transformers.js to avoid native binary dependency issues and speed up tests
4
+ vi.mock('@xenova/transformers', () => ({
5
+ pipeline: async () => {
6
+ // Return a mock extractor that produces deterministic "embeddings"
7
+ return async (text: string) => {
8
+ // Create a fake vector based on the text length or hash
9
+ const vector = new Array(384).fill(0);
10
+ for (let i = 0; i < Math.min(text.length, 384); i++) {
11
+ vector[i] = text.charCodeAt(i) / 255;
12
+ }
13
+ return { data: new Float32Array(vector) };
14
+ };
15
+ },
16
+ env: {
17
+ allowImageProcessors: false,
18
+ },
19
+ }));
20
+
21
+ // Also mock sharp just in case something else pulls it in
22
+ vi.mock('sharp', () => ({
23
+ default: () => ({
24
+ resize: () => ({
25
+ toFormat: () => ({
26
+ toBuffer: async () => Buffer.from([]),
27
+ }),
28
+ }),
29
+ }),
30
+ }));