@rigour-labs/core 3.0.5 → 4.0.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.
Files changed (85) hide show
  1. package/dist/deep/fact-extractor.d.ts +80 -0
  2. package/dist/deep/fact-extractor.js +626 -0
  3. package/dist/deep/index.d.ts +14 -0
  4. package/dist/deep/index.js +12 -0
  5. package/dist/deep/prompts.d.ts +22 -0
  6. package/dist/deep/prompts.js +374 -0
  7. package/dist/deep/verifier.d.ts +16 -0
  8. package/dist/deep/verifier.js +388 -0
  9. package/dist/gates/deep-analysis.d.ts +28 -0
  10. package/dist/gates/deep-analysis.js +302 -0
  11. package/dist/gates/deprecated-apis-rules-lang.d.ts +21 -0
  12. package/dist/gates/deprecated-apis-rules-lang.js +311 -0
  13. package/dist/gates/deprecated-apis-rules-node.d.ts +19 -0
  14. package/dist/gates/deprecated-apis-rules-node.js +199 -0
  15. package/dist/gates/deprecated-apis-rules.d.ts +6 -0
  16. package/dist/gates/deprecated-apis-rules.js +6 -0
  17. package/dist/gates/deprecated-apis.js +1 -502
  18. package/dist/gates/hallucinated-imports-lang.d.ts +16 -0
  19. package/dist/gates/hallucinated-imports-lang.js +374 -0
  20. package/dist/gates/hallucinated-imports-stdlib.d.ts +12 -0
  21. package/dist/gates/hallucinated-imports-stdlib.js +228 -0
  22. package/dist/gates/hallucinated-imports.d.ts +0 -98
  23. package/dist/gates/hallucinated-imports.js +10 -678
  24. package/dist/gates/phantom-apis-data.d.ts +33 -0
  25. package/dist/gates/phantom-apis-data.js +398 -0
  26. package/dist/gates/phantom-apis.js +1 -393
  27. package/dist/gates/phantom-apis.test.js +52 -0
  28. package/dist/gates/promise-safety-helpers.d.ts +19 -0
  29. package/dist/gates/promise-safety-helpers.js +101 -0
  30. package/dist/gates/promise-safety-rules.d.ts +7 -0
  31. package/dist/gates/promise-safety-rules.js +19 -0
  32. package/dist/gates/promise-safety.d.ts +1 -21
  33. package/dist/gates/promise-safety.js +51 -257
  34. package/dist/gates/runner.d.ts +4 -2
  35. package/dist/gates/runner.js +46 -1
  36. package/dist/gates/test-quality-lang.d.ts +30 -0
  37. package/dist/gates/test-quality-lang.js +188 -0
  38. package/dist/gates/test-quality.d.ts +0 -14
  39. package/dist/gates/test-quality.js +13 -186
  40. package/dist/index.d.ts +10 -0
  41. package/dist/index.js +12 -2
  42. package/dist/inference/cloud-provider.d.ts +34 -0
  43. package/dist/inference/cloud-provider.js +126 -0
  44. package/dist/inference/index.d.ts +17 -0
  45. package/dist/inference/index.js +23 -0
  46. package/dist/inference/model-manager.d.ts +26 -0
  47. package/dist/inference/model-manager.js +106 -0
  48. package/dist/inference/sidecar-provider.d.ts +15 -0
  49. package/dist/inference/sidecar-provider.js +153 -0
  50. package/dist/inference/types.d.ts +77 -0
  51. package/dist/inference/types.js +19 -0
  52. package/dist/pattern-index/indexer-helpers.d.ts +38 -0
  53. package/dist/pattern-index/indexer-helpers.js +111 -0
  54. package/dist/pattern-index/indexer-lang.d.ts +13 -0
  55. package/dist/pattern-index/indexer-lang.js +244 -0
  56. package/dist/pattern-index/indexer-ts.d.ts +22 -0
  57. package/dist/pattern-index/indexer-ts.js +258 -0
  58. package/dist/pattern-index/indexer.d.ts +4 -106
  59. package/dist/pattern-index/indexer.js +58 -707
  60. package/dist/pattern-index/staleness-data.d.ts +6 -0
  61. package/dist/pattern-index/staleness-data.js +262 -0
  62. package/dist/pattern-index/staleness.js +1 -258
  63. package/dist/settings.d.ts +104 -0
  64. package/dist/settings.js +186 -0
  65. package/dist/storage/db.d.ts +16 -0
  66. package/dist/storage/db.js +132 -0
  67. package/dist/storage/findings.d.ts +14 -0
  68. package/dist/storage/findings.js +38 -0
  69. package/dist/storage/index.d.ts +9 -0
  70. package/dist/storage/index.js +8 -0
  71. package/dist/storage/patterns.d.ts +35 -0
  72. package/dist/storage/patterns.js +62 -0
  73. package/dist/storage/scans.d.ts +42 -0
  74. package/dist/storage/scans.js +55 -0
  75. package/dist/templates/index.d.ts +12 -16
  76. package/dist/templates/index.js +11 -527
  77. package/dist/templates/paradigms.d.ts +2 -0
  78. package/dist/templates/paradigms.js +46 -0
  79. package/dist/templates/presets.d.ts +14 -0
  80. package/dist/templates/presets.js +227 -0
  81. package/dist/templates/universal-config.d.ts +2 -0
  82. package/dist/templates/universal-config.js +190 -0
  83. package/dist/types/index.d.ts +438 -15
  84. package/dist/types/index.js +41 -1
  85. package/package.json +6 -2
@@ -0,0 +1,186 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
4
+ import { Logger } from './utils/logger.js';
5
+ /**
6
+ * Get the settings file path: ~/.rigour/settings.json
7
+ */
8
+ export function getSettingsPath() {
9
+ const homeDir = os.homedir();
10
+ return path.join(homeDir, '.rigour', 'settings.json');
11
+ }
12
+ /**
13
+ * Load settings from ~/.rigour/settings.json
14
+ * Returns empty object if file not found or is malformed
15
+ */
16
+ export function loadSettings() {
17
+ const settingsPath = getSettingsPath();
18
+ try {
19
+ if (!fs.existsSync(settingsPath)) {
20
+ Logger.debug(`Settings file not found at ${settingsPath}`);
21
+ return {};
22
+ }
23
+ const content = fs.readFileSync(settingsPath, 'utf-8');
24
+ const settings = JSON.parse(content);
25
+ Logger.debug(`Settings loaded from ${settingsPath}`);
26
+ return settings;
27
+ }
28
+ catch (error) {
29
+ if (error instanceof SyntaxError) {
30
+ Logger.warn(`Malformed JSON in ${settingsPath}: ${error.message}`);
31
+ }
32
+ else if (error instanceof Error) {
33
+ Logger.warn(`Failed to read settings from ${settingsPath}: ${error.message}`);
34
+ }
35
+ else {
36
+ Logger.warn(`Failed to read settings from ${settingsPath}`);
37
+ }
38
+ return {};
39
+ }
40
+ }
41
+ /**
42
+ * Save settings to ~/.rigour/settings.json
43
+ */
44
+ export function saveSettings(settings) {
45
+ const settingsPath = getSettingsPath();
46
+ const settingsDir = path.dirname(settingsPath);
47
+ try {
48
+ // Ensure directory exists
49
+ fs.ensureDirSync(settingsDir);
50
+ // Write with pretty formatting (2-space indent)
51
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
52
+ Logger.debug(`Settings saved to ${settingsPath}`);
53
+ }
54
+ catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ Logger.error(`Failed to save settings to ${settingsPath}: ${message}`);
57
+ throw error;
58
+ }
59
+ }
60
+ /**
61
+ * Map common provider names to their settings.json key
62
+ * This allows --provider claude to resolve to anthropic API key
63
+ */
64
+ function normalizeProviderName(provider) {
65
+ const mapping = {
66
+ 'claude': 'anthropic',
67
+ 'anthropic': 'anthropic',
68
+ 'gpt': 'openai',
69
+ 'openai': 'openai',
70
+ 'groq': 'groq',
71
+ 'deepseek': 'deepseek',
72
+ 'mistral': 'mistral',
73
+ 'together': 'together',
74
+ 'gemini': 'gemini',
75
+ 'google': 'gemini',
76
+ 'ollama': 'ollama',
77
+ };
78
+ return mapping[provider.toLowerCase()] || provider;
79
+ }
80
+ /**
81
+ * Resolve deep analysis options by merging CLI flags with settings.json
82
+ * CLI flags always take precedence over settings.json values
83
+ *
84
+ * @param cliOptions CLI flags provided by user
85
+ * @returns Merged options with CLI taking precedence
86
+ */
87
+ export function resolveDeepOptions(cliOptions) {
88
+ const settings = loadSettings();
89
+ const result = {};
90
+ // 1. Start with settings.json defaults
91
+ if (settings.deep?.apiBaseUrl) {
92
+ result.apiBaseUrl = settings.deep.apiBaseUrl;
93
+ }
94
+ if (settings.deep?.maxTokens) {
95
+ result.maxTokens = settings.deep.maxTokens;
96
+ }
97
+ if (settings.deep?.temperature) {
98
+ result.temperature = settings.deep.temperature;
99
+ }
100
+ // 2. Apply provider selection (settings or default)
101
+ let selectedProvider = settings.deep?.defaultProvider || 'anthropic';
102
+ if (cliOptions.provider) {
103
+ selectedProvider = cliOptions.provider;
104
+ }
105
+ result.provider = selectedProvider;
106
+ // 3. Apply model selection (settings or default)
107
+ if (settings.deep?.defaultModel) {
108
+ result.modelName = settings.deep.defaultModel;
109
+ }
110
+ if (cliOptions.modelName) {
111
+ result.modelName = cliOptions.modelName;
112
+ }
113
+ // 4. Resolve API key
114
+ // CLI flag takes highest precedence
115
+ if (cliOptions.apiKey) {
116
+ result.apiKey = cliOptions.apiKey;
117
+ }
118
+ else if (settings.providers) {
119
+ // Otherwise look up provider key in settings.providers
120
+ const normalizedProvider = normalizeProviderName(selectedProvider);
121
+ const apiKey = settings.providers[normalizedProvider];
122
+ if (apiKey) {
123
+ result.apiKey = apiKey;
124
+ }
125
+ }
126
+ // 5. Override with CLI flags (highest priority)
127
+ if (cliOptions.apiBaseUrl) {
128
+ result.apiBaseUrl = cliOptions.apiBaseUrl;
129
+ }
130
+ if (cliOptions.maxTokens) {
131
+ result.maxTokens = cliOptions.maxTokens;
132
+ }
133
+ if (cliOptions.temperature) {
134
+ result.temperature = cliOptions.temperature;
135
+ }
136
+ return result;
137
+ }
138
+ /**
139
+ * Get a specific provider's API key from settings
140
+ * Supports both normalized names (claude -> anthropic) and exact keys
141
+ */
142
+ export function getProviderKey(providerName) {
143
+ const settings = loadSettings();
144
+ if (!settings.providers) {
145
+ return undefined;
146
+ }
147
+ const normalized = normalizeProviderName(providerName);
148
+ return settings.providers[normalized];
149
+ }
150
+ /**
151
+ * Get agent configuration from settings
152
+ */
153
+ export function getAgentConfig(agentName) {
154
+ const settings = loadSettings();
155
+ return settings.agents?.[agentName];
156
+ }
157
+ /**
158
+ * Get CLI preferences from settings
159
+ */
160
+ export function getCliPreferences() {
161
+ const settings = loadSettings();
162
+ return settings.cli || {};
163
+ }
164
+ /**
165
+ * Update a specific provider key in settings
166
+ */
167
+ export function updateProviderKey(provider, apiKey) {
168
+ const settings = loadSettings();
169
+ const normalized = normalizeProviderName(provider);
170
+ if (!settings.providers) {
171
+ settings.providers = {};
172
+ }
173
+ settings.providers[normalized] = apiKey;
174
+ saveSettings(settings);
175
+ }
176
+ /**
177
+ * Remove a provider key from settings
178
+ */
179
+ export function removeProviderKey(provider) {
180
+ const settings = loadSettings();
181
+ const normalized = normalizeProviderName(provider);
182
+ if (settings.providers && settings.providers[normalized]) {
183
+ delete settings.providers[normalized];
184
+ saveSettings(settings);
185
+ }
186
+ }
@@ -0,0 +1,16 @@
1
+ declare const RIGOUR_DIR: string;
2
+ declare const DB_PATH: string;
3
+ export interface RigourDB {
4
+ db: any;
5
+ close(): void;
6
+ }
7
+ /**
8
+ * Open (or create) the Rigour SQLite database.
9
+ * Returns null if better-sqlite3 is not available.
10
+ */
11
+ export declare function openDatabase(dbPath?: string): RigourDB | null;
12
+ /**
13
+ * Check if SQLite is available (better-sqlite3 installed)
14
+ */
15
+ export declare function isSQLiteAvailable(): boolean;
16
+ export { RIGOUR_DIR, DB_PATH };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * SQLite storage layer for Rigour Brain.
3
+ * Single file at ~/.rigour/rigour.db stores all scan history, findings,
4
+ * learned patterns, and feedback. ACID-safe, portable, queryable.
5
+ */
6
+ import path from 'path';
7
+ import os from 'os';
8
+ import fs from 'fs-extra';
9
+ import { createRequire } from 'module';
10
+ // better-sqlite3 is optional — graceful degradation if not installed.
11
+ // It's a native C++ addon that uses require() semantics, so we use createRequire.
12
+ let Database = null;
13
+ let _dbResolved = false;
14
+ function loadDatabase() {
15
+ if (_dbResolved)
16
+ return Database;
17
+ _dbResolved = true;
18
+ try {
19
+ const require = createRequire(import.meta.url);
20
+ Database = require('better-sqlite3');
21
+ }
22
+ catch {
23
+ Database = null;
24
+ }
25
+ return Database;
26
+ }
27
+ const RIGOUR_DIR = path.join(os.homedir(), '.rigour');
28
+ const DB_PATH = path.join(RIGOUR_DIR, 'rigour.db');
29
+ const SCHEMA_SQL = `
30
+ -- Every scan result, forever
31
+ CREATE TABLE IF NOT EXISTS scans (
32
+ id TEXT PRIMARY KEY,
33
+ repo TEXT NOT NULL,
34
+ commit_hash TEXT,
35
+ timestamp INTEGER NOT NULL,
36
+ ai_health_score INTEGER,
37
+ code_quality_score INTEGER,
38
+ overall_score INTEGER,
39
+ files_scanned INTEGER,
40
+ duration_ms INTEGER,
41
+ deep_tier TEXT,
42
+ deep_model TEXT
43
+ );
44
+
45
+ -- Every finding from every scan
46
+ CREATE TABLE IF NOT EXISTS findings (
47
+ id TEXT PRIMARY KEY,
48
+ scan_id TEXT REFERENCES scans(id),
49
+ file TEXT NOT NULL,
50
+ line INTEGER,
51
+ category TEXT NOT NULL,
52
+ severity TEXT NOT NULL,
53
+ source TEXT NOT NULL,
54
+ provenance TEXT,
55
+ description TEXT,
56
+ suggestion TEXT,
57
+ confidence REAL,
58
+ verified INTEGER DEFAULT 0
59
+ );
60
+
61
+ -- Learned patterns (the Brain's memory)
62
+ CREATE TABLE IF NOT EXISTS patterns (
63
+ id TEXT PRIMARY KEY,
64
+ repo TEXT,
65
+ pattern TEXT NOT NULL,
66
+ description TEXT,
67
+ strength REAL DEFAULT 0.3,
68
+ times_seen INTEGER DEFAULT 1,
69
+ first_seen INTEGER NOT NULL,
70
+ last_seen INTEGER NOT NULL,
71
+ source TEXT NOT NULL
72
+ );
73
+
74
+ -- Human feedback on findings
75
+ CREATE TABLE IF NOT EXISTS feedback (
76
+ id TEXT PRIMARY KEY,
77
+ finding_id TEXT REFERENCES findings(id),
78
+ rating TEXT NOT NULL,
79
+ comment TEXT,
80
+ timestamp INTEGER NOT NULL
81
+ );
82
+
83
+ -- Codebase index (AST graph)
84
+ CREATE TABLE IF NOT EXISTS codebase (
85
+ id TEXT PRIMARY KEY,
86
+ repo TEXT NOT NULL,
87
+ file TEXT NOT NULL,
88
+ functions TEXT,
89
+ imports TEXT,
90
+ exports TEXT,
91
+ complexity_metrics TEXT,
92
+ last_indexed INTEGER NOT NULL
93
+ );
94
+
95
+ -- Indexes for performance
96
+ CREATE INDEX IF NOT EXISTS idx_scans_repo ON scans(repo);
97
+ CREATE INDEX IF NOT EXISTS idx_scans_timestamp ON scans(timestamp);
98
+ CREATE INDEX IF NOT EXISTS idx_findings_scan ON findings(scan_id);
99
+ CREATE INDEX IF NOT EXISTS idx_findings_category ON findings(category);
100
+ CREATE INDEX IF NOT EXISTS idx_patterns_repo ON patterns(repo);
101
+ CREATE INDEX IF NOT EXISTS idx_patterns_strength ON patterns(strength);
102
+ `;
103
+ /**
104
+ * Open (or create) the Rigour SQLite database.
105
+ * Returns null if better-sqlite3 is not available.
106
+ */
107
+ export function openDatabase(dbPath) {
108
+ const Db = loadDatabase();
109
+ if (!Db)
110
+ return null;
111
+ const resolvedPath = dbPath || DB_PATH;
112
+ fs.ensureDirSync(path.dirname(resolvedPath));
113
+ const db = new Db(resolvedPath);
114
+ // WAL mode for better concurrent read performance
115
+ db.pragma('journal_mode = WAL');
116
+ db.pragma('foreign_keys = ON');
117
+ // Run schema migration
118
+ db.exec(SCHEMA_SQL);
119
+ return {
120
+ db,
121
+ close() {
122
+ db.close();
123
+ },
124
+ };
125
+ }
126
+ /**
127
+ * Check if SQLite is available (better-sqlite3 installed)
128
+ */
129
+ export function isSQLiteAvailable() {
130
+ return loadDatabase() !== null;
131
+ }
132
+ export { RIGOUR_DIR, DB_PATH };
@@ -0,0 +1,14 @@
1
+ import type { RigourDB } from './db.js';
2
+ import type { Failure } from '../types/index.js';
3
+ /**
4
+ * Insert findings from a scan report into SQLite.
5
+ */
6
+ export declare function insertFindings(store: RigourDB, scanId: string, failures: Failure[]): void;
7
+ /**
8
+ * Get findings for a specific scan.
9
+ */
10
+ export declare function getFindingsForScan(store: RigourDB, scanId: string): any[];
11
+ /**
12
+ * Get all deep analysis findings for a repo.
13
+ */
14
+ export declare function getDeepFindings(store: RigourDB, repo: string, limit?: number): any[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Findings CRUD operations for Rigour Brain SQLite storage.
3
+ */
4
+ import { randomUUID } from 'crypto';
5
+ /**
6
+ * Insert findings from a scan report into SQLite.
7
+ */
8
+ export function insertFindings(store, scanId, failures) {
9
+ const stmt = store.db.prepare(`
10
+ INSERT INTO findings (id, scan_id, file, line, category, severity, source, provenance, description, suggestion, confidence, verified)
11
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
12
+ `);
13
+ const insertMany = store.db.transaction((items) => {
14
+ for (const f of items) {
15
+ stmt.run(randomUUID(), scanId, f.files?.[0] || 'unknown', f.line ?? null, f.category || f.id, f.severity || 'medium', f.source || 'ast', f.provenance || 'traditional', f.details, f.hint ?? null, f.confidence ?? null, f.verified ? 1 : 0);
16
+ }
17
+ });
18
+ insertMany(failures);
19
+ }
20
+ /**
21
+ * Get findings for a specific scan.
22
+ */
23
+ export function getFindingsForScan(store, scanId) {
24
+ const stmt = store.db.prepare('SELECT * FROM findings WHERE scan_id = ? ORDER BY severity ASC');
25
+ return stmt.all(scanId);
26
+ }
27
+ /**
28
+ * Get all deep analysis findings for a repo.
29
+ */
30
+ export function getDeepFindings(store, repo, limit = 50) {
31
+ const stmt = store.db.prepare(`
32
+ SELECT f.* FROM findings f
33
+ JOIN scans s ON f.scan_id = s.id
34
+ WHERE s.repo = ? AND f.source = 'llm'
35
+ ORDER BY f.confidence DESC LIMIT ?
36
+ `);
37
+ return stmt.all(repo, limit);
38
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Rigour Brain — SQLite storage layer.
3
+ * Everything in one file: ~/.rigour/rigour.db
4
+ */
5
+ export { openDatabase, isSQLiteAvailable, RIGOUR_DIR, DB_PATH } from './db.js';
6
+ export type { RigourDB } from './db.js';
7
+ export { insertScan, getRecentScans, getScoreTrendFromDB, getTopIssues } from './scans.js';
8
+ export { insertFindings, getFindingsForScan, getDeepFindings } from './findings.js';
9
+ export { reinforcePattern, decayPatterns, getStrongPatterns, getPatterns, getHardRules } from './patterns.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Rigour Brain — SQLite storage layer.
3
+ * Everything in one file: ~/.rigour/rigour.db
4
+ */
5
+ export { openDatabase, isSQLiteAvailable, RIGOUR_DIR, DB_PATH } from './db.js';
6
+ export { insertScan, getRecentScans, getScoreTrendFromDB, getTopIssues } from './scans.js';
7
+ export { insertFindings, getFindingsForScan, getDeepFindings } from './findings.js';
8
+ export { reinforcePattern, decayPatterns, getStrongPatterns, getPatterns, getHardRules } from './patterns.js';
@@ -0,0 +1,35 @@
1
+ import type { RigourDB } from './db.js';
2
+ export interface PatternRecord {
3
+ id: string;
4
+ repo: string | null;
5
+ pattern: string;
6
+ description: string | null;
7
+ strength: number;
8
+ times_seen: number;
9
+ first_seen: number;
10
+ last_seen: number;
11
+ source: string;
12
+ }
13
+ /**
14
+ * Record or reinforce a pattern.
15
+ * If the pattern already exists for this repo, increase strength.
16
+ * Otherwise, create a new pattern.
17
+ */
18
+ export declare function reinforcePattern(store: RigourDB, repo: string, pattern: string, description: string, source: 'ast' | 'llm' | 'human_feedback'): void;
19
+ /**
20
+ * Decay patterns not seen in the last N days.
21
+ */
22
+ export declare function decayPatterns(store: RigourDB, daysThreshold?: number): number;
23
+ /**
24
+ * Get strong patterns for a repo (strength > threshold).
25
+ */
26
+ export declare function getStrongPatterns(store: RigourDB, repo: string, threshold?: number): PatternRecord[];
27
+ /**
28
+ * Get all patterns for a repo.
29
+ */
30
+ export declare function getPatterns(store: RigourDB, repo: string): PatternRecord[];
31
+ /**
32
+ * Get patterns promoted to hard rules (strength > 0.9).
33
+ * These can be used as AST-level checks without LLM inference.
34
+ */
35
+ export declare function getHardRules(store: RigourDB, repo: string): PatternRecord[];
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pattern learning and reinforcement for Rigour Brain.
3
+ * Patterns grow in strength when seen repeatedly, decay when absent.
4
+ */
5
+ import { randomUUID } from 'crypto';
6
+ /**
7
+ * Record or reinforce a pattern.
8
+ * If the pattern already exists for this repo, increase strength.
9
+ * Otherwise, create a new pattern.
10
+ */
11
+ export function reinforcePattern(store, repo, pattern, description, source) {
12
+ const now = Date.now();
13
+ const existing = store.db.prepare('SELECT * FROM patterns WHERE repo = ? AND pattern = ?').get(repo, pattern);
14
+ if (existing) {
15
+ store.db.prepare(`
16
+ UPDATE patterns
17
+ SET strength = MIN(strength + 0.15, 1.0),
18
+ times_seen = times_seen + 1,
19
+ last_seen = ?,
20
+ description = COALESCE(?, description)
21
+ WHERE id = ?
22
+ `).run(now, description, existing.id);
23
+ }
24
+ else {
25
+ store.db.prepare(`
26
+ INSERT INTO patterns (id, repo, pattern, description, strength, times_seen, first_seen, last_seen, source)
27
+ VALUES (?, ?, ?, ?, 0.3, 1, ?, ?, ?)
28
+ `).run(randomUUID(), repo, pattern, description, now, now, source);
29
+ }
30
+ }
31
+ /**
32
+ * Decay patterns not seen in the last N days.
33
+ */
34
+ export function decayPatterns(store, daysThreshold = 30) {
35
+ const cutoff = Date.now() - (daysThreshold * 24 * 60 * 60 * 1000);
36
+ const result = store.db.prepare(`
37
+ UPDATE patterns SET strength = MAX(strength - 0.05, 0.0)
38
+ WHERE last_seen < ?
39
+ `).run(cutoff);
40
+ // Prune dead patterns
41
+ store.db.prepare('DELETE FROM patterns WHERE strength < 0.1').run();
42
+ return result.changes;
43
+ }
44
+ /**
45
+ * Get strong patterns for a repo (strength > threshold).
46
+ */
47
+ export function getStrongPatterns(store, repo, threshold = 0.7) {
48
+ return store.db.prepare('SELECT * FROM patterns WHERE repo = ? AND strength >= ? ORDER BY strength DESC').all(repo, threshold);
49
+ }
50
+ /**
51
+ * Get all patterns for a repo.
52
+ */
53
+ export function getPatterns(store, repo) {
54
+ return store.db.prepare('SELECT * FROM patterns WHERE repo = ? ORDER BY strength DESC').all(repo);
55
+ }
56
+ /**
57
+ * Get patterns promoted to hard rules (strength > 0.9).
58
+ * These can be used as AST-level checks without LLM inference.
59
+ */
60
+ export function getHardRules(store, repo) {
61
+ return store.db.prepare('SELECT * FROM patterns WHERE repo = ? AND strength >= 0.9 ORDER BY times_seen DESC').all(repo);
62
+ }
@@ -0,0 +1,42 @@
1
+ import type { RigourDB } from './db.js';
2
+ import type { Report } from '../types/index.js';
3
+ export interface ScanRecord {
4
+ id: string;
5
+ repo: string;
6
+ commit_hash?: string;
7
+ timestamp: number;
8
+ ai_health_score?: number;
9
+ code_quality_score?: number;
10
+ overall_score?: number;
11
+ files_scanned?: number;
12
+ duration_ms?: number;
13
+ deep_tier?: string;
14
+ deep_model?: string;
15
+ }
16
+ /**
17
+ * Insert a scan record from a Rigour report.
18
+ */
19
+ export declare function insertScan(store: RigourDB, repo: string, report: Report, meta?: {
20
+ commitHash?: string;
21
+ filesScanned?: number;
22
+ deepTier?: string;
23
+ deepModel?: string;
24
+ }): string;
25
+ /**
26
+ * Get recent scans for a repo (newest first).
27
+ */
28
+ export declare function getRecentScans(store: RigourDB, repo: string, limit?: number): ScanRecord[];
29
+ /**
30
+ * Get score trend for a repo.
31
+ */
32
+ export declare function getScoreTrendFromDB(store: RigourDB, repo: string, limit?: number): {
33
+ scores: number[];
34
+ direction: 'improving' | 'degrading' | 'stable';
35
+ };
36
+ /**
37
+ * Get most common issue categories for a repo.
38
+ */
39
+ export declare function getTopIssues(store: RigourDB, repo: string, limit?: number): {
40
+ category: string;
41
+ count: number;
42
+ }[];
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Scan CRUD operations for Rigour Brain SQLite storage.
3
+ */
4
+ import { randomUUID } from 'crypto';
5
+ /**
6
+ * Insert a scan record from a Rigour report.
7
+ */
8
+ export function insertScan(store, repo, report, meta) {
9
+ const id = randomUUID();
10
+ const stmt = store.db.prepare(`
11
+ INSERT INTO scans (id, repo, commit_hash, timestamp, ai_health_score, code_quality_score, overall_score, files_scanned, duration_ms, deep_tier, deep_model)
12
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
13
+ `);
14
+ stmt.run(id, repo, meta?.commitHash || null, Date.now(), report.stats.ai_health_score ?? null, report.stats.code_quality_score ?? null, report.stats.score ?? null, meta?.filesScanned ?? null, report.stats.duration_ms, meta?.deepTier ?? null, meta?.deepModel ?? null);
15
+ return id;
16
+ }
17
+ /**
18
+ * Get recent scans for a repo (newest first).
19
+ */
20
+ export function getRecentScans(store, repo, limit = 10) {
21
+ const stmt = store.db.prepare(`
22
+ SELECT * FROM scans WHERE repo = ? ORDER BY timestamp DESC LIMIT ?
23
+ `);
24
+ return stmt.all(repo, limit);
25
+ }
26
+ /**
27
+ * Get score trend for a repo.
28
+ */
29
+ export function getScoreTrendFromDB(store, repo, limit = 10) {
30
+ const scans = getRecentScans(store, repo, limit);
31
+ const scores = scans
32
+ .filter(s => s.overall_score != null)
33
+ .map(s => s.overall_score)
34
+ .reverse(); // oldest first
35
+ if (scores.length < 2)
36
+ return { scores, direction: 'stable' };
37
+ const recent = scores.slice(-3);
38
+ const avg = recent.reduce((a, b) => a + b, 0) / recent.length;
39
+ const older = scores.slice(0, -3);
40
+ const olderAvg = older.length > 0 ? older.reduce((a, b) => a + b, 0) / older.length : avg;
41
+ const direction = avg > olderAvg + 2 ? 'improving' : avg < olderAvg - 2 ? 'degrading' : 'stable';
42
+ return { scores, direction };
43
+ }
44
+ /**
45
+ * Get most common issue categories for a repo.
46
+ */
47
+ export function getTopIssues(store, repo, limit = 10) {
48
+ const stmt = store.db.prepare(`
49
+ SELECT f.category, COUNT(*) as count FROM findings f
50
+ JOIN scans s ON f.scan_id = s.id
51
+ WHERE s.repo = ?
52
+ GROUP BY f.category ORDER BY count DESC LIMIT ?
53
+ `);
54
+ return stmt.all(repo, limit);
55
+ }
@@ -1,16 +1,12 @@
1
- import { Config, Commands, Gates } from '../types/index.js';
2
- export interface Template {
3
- name: string;
4
- markers: string[];
5
- config: {
6
- preset?: string;
7
- paradigm?: string;
8
- commands?: Partial<Commands>;
9
- gates?: Partial<Gates>;
10
- planned?: string[];
11
- ignore?: string[];
12
- };
13
- }
14
- export declare const TEMPLATES: Template[];
15
- export declare const PARADIGM_TEMPLATES: Template[];
16
- export declare const UNIVERSAL_CONFIG: Config;
1
+ /**
2
+ * Templates — Public Barrel
3
+ *
4
+ * Re-exports from sub-modules so that existing importers keep working.
5
+ * - Preset definitions → ./presets.ts
6
+ * - Paradigm templates → ./paradigms.ts
7
+ * - Universal config → ./universal-config.ts
8
+ */
9
+ export type { Template } from './presets.js';
10
+ export { TEMPLATES } from './presets.js';
11
+ export { PARADIGM_TEMPLATES } from './paradigms.js';
12
+ export { UNIVERSAL_CONFIG } from './universal-config.js';