@rigour-labs/core 3.0.6 → 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.
- package/dist/deep/fact-extractor.d.ts +80 -0
- package/dist/deep/fact-extractor.js +626 -0
- package/dist/deep/index.d.ts +14 -0
- package/dist/deep/index.js +12 -0
- package/dist/deep/prompts.d.ts +22 -0
- package/dist/deep/prompts.js +374 -0
- package/dist/deep/verifier.d.ts +16 -0
- package/dist/deep/verifier.js +388 -0
- package/dist/gates/deep-analysis.d.ts +28 -0
- package/dist/gates/deep-analysis.js +302 -0
- package/dist/gates/runner.d.ts +4 -2
- package/dist/gates/runner.js +46 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -2
- package/dist/inference/cloud-provider.d.ts +34 -0
- package/dist/inference/cloud-provider.js +126 -0
- package/dist/inference/index.d.ts +17 -0
- package/dist/inference/index.js +23 -0
- package/dist/inference/model-manager.d.ts +26 -0
- package/dist/inference/model-manager.js +106 -0
- package/dist/inference/sidecar-provider.d.ts +15 -0
- package/dist/inference/sidecar-provider.js +153 -0
- package/dist/inference/types.d.ts +77 -0
- package/dist/inference/types.js +19 -0
- package/dist/settings.d.ts +104 -0
- package/dist/settings.js +186 -0
- package/dist/storage/db.d.ts +16 -0
- package/dist/storage/db.js +132 -0
- package/dist/storage/findings.d.ts +14 -0
- package/dist/storage/findings.js +38 -0
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.js +8 -0
- package/dist/storage/patterns.d.ts +35 -0
- package/dist/storage/patterns.js +62 -0
- package/dist/storage/scans.d.ts +42 -0
- package/dist/storage/scans.js +55 -0
- package/dist/templates/universal-config.js +19 -0
- package/dist/types/index.d.ts +438 -15
- package/dist/types/index.js +41 -1
- package/package.json +6 -2
|
@@ -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
|
+
}
|
|
@@ -155,6 +155,25 @@ export const UNIVERSAL_CONFIG = {
|
|
|
155
155
|
max_mocks_per_test: 5,
|
|
156
156
|
ignore_patterns: [],
|
|
157
157
|
},
|
|
158
|
+
deep: {
|
|
159
|
+
enabled: false,
|
|
160
|
+
pro: false,
|
|
161
|
+
provider: 'local',
|
|
162
|
+
threads: 4,
|
|
163
|
+
max_tokens: 512,
|
|
164
|
+
temperature: 0.1,
|
|
165
|
+
timeout_ms: 60000,
|
|
166
|
+
checks: {
|
|
167
|
+
solid: true,
|
|
168
|
+
dry: true,
|
|
169
|
+
design_patterns: true,
|
|
170
|
+
language_idioms: true,
|
|
171
|
+
error_handling: true,
|
|
172
|
+
test_quality: true,
|
|
173
|
+
architecture: true,
|
|
174
|
+
code_smells: true,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
158
177
|
},
|
|
159
178
|
hooks: {
|
|
160
179
|
enabled: false,
|