@boshu2/vibe-check 2.2.1 → 2.4.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/.agents/plans/2025-12-28-ai-safety-integration-plan.md +326 -0
- package/.agents/plans/2025-12-29-complexity-driver-plan.md +225 -0
- package/.agents/plans/2025-12-29-complexity-drivers-plan.md +253 -0
- package/.agents/research/2025-12-28-ai-platform-security-integration.md +295 -0
- package/.agents/research/2025-12-29-complexity-driver-architecture.md +392 -0
- package/.agents/research/2025-12-29-complexity-drivers.md +227 -0
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +62 -0
- package/.beads/interactions.jsonl +0 -0
- package/.beads/issues.jsonl +21 -0
- package/.beads/metadata.json +4 -0
- package/.gitattributes +3 -0
- package/AGENTS.md +40 -0
- package/CHANGELOG.md +69 -0
- package/CLAUDE.md +75 -0
- package/README.md +71 -0
- package/dist/ai-safety/contract-drift.d.ts +14 -0
- package/dist/ai-safety/contract-drift.d.ts.map +1 -0
- package/dist/ai-safety/contract-drift.js +230 -0
- package/dist/ai-safety/contract-drift.js.map +1 -0
- package/dist/ai-safety/index.d.ts +43 -0
- package/dist/ai-safety/index.d.ts.map +1 -0
- package/dist/ai-safety/index.js +177 -0
- package/dist/ai-safety/index.js.map +1 -0
- package/dist/ai-safety/scope-violation.d.ts +18 -0
- package/dist/ai-safety/scope-violation.d.ts.map +1 -0
- package/dist/ai-safety/scope-violation.js +150 -0
- package/dist/ai-safety/scope-violation.js.map +1 -0
- package/dist/ai-safety/secret-leakage.d.ts +18 -0
- package/dist/ai-safety/secret-leakage.d.ts.map +1 -0
- package/dist/ai-safety/secret-leakage.js +188 -0
- package/dist/ai-safety/secret-leakage.js.map +1 -0
- package/dist/ai-safety/token-spiral.d.ts +17 -0
- package/dist/ai-safety/token-spiral.d.ts.map +1 -0
- package/dist/ai-safety/token-spiral.js +183 -0
- package/dist/ai-safety/token-spiral.js.map +1 -0
- package/dist/ai-safety/types.d.ts +122 -0
- package/dist/ai-safety/types.d.ts.map +1 -0
- package/dist/ai-safety/types.js +32 -0
- package/dist/ai-safety/types.js.map +1 -0
- package/dist/analyzers/complexity.d.ts +92 -0
- package/dist/analyzers/complexity.d.ts.map +1 -0
- package/dist/analyzers/complexity.js +79 -0
- package/dist/analyzers/complexity.js.map +1 -0
- package/dist/analyzers/modularity.d.ts +3 -1
- package/dist/analyzers/modularity.d.ts.map +1 -1
- package/dist/analyzers/modularity.js +32 -6
- package/dist/analyzers/modularity.js.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/driver.d.ts +18 -0
- package/dist/commands/driver.d.ts.map +1 -0
- package/dist/commands/driver.js +58 -0
- package/dist/commands/driver.js.map +1 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/modularity.d.ts +2 -0
- package/dist/commands/modularity.d.ts.map +1 -1
- package/dist/commands/modularity.js +86 -7
- package/dist/commands/modularity.js.map +1 -1
- package/dist/commands/session.d.ts +9 -0
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +42 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +59 -0
- package/dist/commands/watch.js.map +1 -1
- package/drivers/README.md +327 -0
- package/drivers/go.sh +131 -0
- package/drivers/java.sh +137 -0
- package/drivers/javascript.sh +134 -0
- package/drivers/php.sh +132 -0
- package/drivers/python.sh +90 -0
- package/drivers/rust.sh +132 -0
- package/package.json +4 -1
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract Drift Detector
|
|
3
|
+
*
|
|
4
|
+
* Tracks commit message format compliance over time.
|
|
5
|
+
* Detects degradation from conventional commits format.
|
|
6
|
+
* Reports drift percentage and trend.
|
|
7
|
+
*/
|
|
8
|
+
// Valid conventional commit types
|
|
9
|
+
const VALID_TYPES = new Set([
|
|
10
|
+
'feat', 'fix', 'docs', 'style', 'refactor', 'perf',
|
|
11
|
+
'test', 'build', 'ci', 'chore', 'revert', 'tracer', 'tb',
|
|
12
|
+
]);
|
|
13
|
+
// Vague words that indicate low-quality commit messages
|
|
14
|
+
const VAGUE_WORDS = new Set([
|
|
15
|
+
'fix', 'update', 'change', 'modify', 'stuff', 'things',
|
|
16
|
+
'misc', 'wip', 'temp', 'test', 'debug', 'asdf', 'foo',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Calculate compliance score for a single commit message.
|
|
20
|
+
* Returns 0-100 where 100 is perfect conventional commit format.
|
|
21
|
+
*/
|
|
22
|
+
function scoreCommitCompliance(message) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
let score = 100;
|
|
25
|
+
// Check conventional commit format: type(scope): description
|
|
26
|
+
const conventionalMatch = message.match(/^(\w+)(?:\(([^)]+)\))?:\s*(.+)/);
|
|
27
|
+
if (!conventionalMatch) {
|
|
28
|
+
// No conventional format at all
|
|
29
|
+
if (!message.includes(':')) {
|
|
30
|
+
issues.push('missing_colon');
|
|
31
|
+
score -= 30;
|
|
32
|
+
}
|
|
33
|
+
// Try to detect if there's a type-like prefix
|
|
34
|
+
const words = message.split(/\s+/);
|
|
35
|
+
if (words.length > 0 && !VALID_TYPES.has(words[0].toLowerCase())) {
|
|
36
|
+
issues.push('missing_type');
|
|
37
|
+
score -= 25;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const [, type, , description] = conventionalMatch;
|
|
42
|
+
// Check if type is valid
|
|
43
|
+
if (!VALID_TYPES.has(type.toLowerCase())) {
|
|
44
|
+
issues.push('invalid_type');
|
|
45
|
+
score -= 20;
|
|
46
|
+
}
|
|
47
|
+
// Check description quality
|
|
48
|
+
if (!description || description.trim().length === 0) {
|
|
49
|
+
issues.push('no_description');
|
|
50
|
+
score -= 30;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// Check for vague descriptions
|
|
54
|
+
const descWords = description.toLowerCase().split(/\s+/);
|
|
55
|
+
const vagueCount = descWords.filter(w => VAGUE_WORDS.has(w)).length;
|
|
56
|
+
if (vagueCount > 0 && descWords.length <= 3) {
|
|
57
|
+
issues.push('vague_message');
|
|
58
|
+
score -= 15;
|
|
59
|
+
}
|
|
60
|
+
// Check for too short description
|
|
61
|
+
if (description.length < 10) {
|
|
62
|
+
issues.push('too_short');
|
|
63
|
+
score -= 10;
|
|
64
|
+
}
|
|
65
|
+
// Check for all caps (shouting)
|
|
66
|
+
if (description === description.toUpperCase() && description.length > 5) {
|
|
67
|
+
issues.push('all_caps');
|
|
68
|
+
score -= 10;
|
|
69
|
+
}
|
|
70
|
+
// Check for lowercase start (should be lowercase for conventional commits)
|
|
71
|
+
if (description[0] && description[0] === description[0].toUpperCase() &&
|
|
72
|
+
description[0] !== description[0].toLowerCase()) {
|
|
73
|
+
// Starts with uppercase - minor issue
|
|
74
|
+
issues.push('starts_with_lowercase');
|
|
75
|
+
score -= 5;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Ensure score stays in bounds
|
|
80
|
+
return {
|
|
81
|
+
score: Math.max(0, Math.min(100, score)),
|
|
82
|
+
issues,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Calculate entropy score for commit messages.
|
|
87
|
+
* Higher entropy (0-1) means more random/inconsistent messages.
|
|
88
|
+
*/
|
|
89
|
+
function calculateEntropy(commits) {
|
|
90
|
+
if (commits.length === 0)
|
|
91
|
+
return 0;
|
|
92
|
+
// Count type distribution
|
|
93
|
+
const typeCounts = new Map();
|
|
94
|
+
for (const commit of commits) {
|
|
95
|
+
const count = typeCounts.get(commit.type) || 0;
|
|
96
|
+
typeCounts.set(commit.type, count + 1);
|
|
97
|
+
}
|
|
98
|
+
// Calculate Shannon entropy
|
|
99
|
+
let entropy = 0;
|
|
100
|
+
const total = commits.length;
|
|
101
|
+
for (const count of typeCounts.values()) {
|
|
102
|
+
const p = count / total;
|
|
103
|
+
if (p > 0) {
|
|
104
|
+
entropy -= p * Math.log2(p);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Normalize to 0-1 range (max entropy = log2(n) for n categories)
|
|
108
|
+
const maxEntropy = Math.log2(Math.max(typeCounts.size, 1));
|
|
109
|
+
const normalizedEntropy = maxEntropy > 0 ? entropy / maxEntropy : 0;
|
|
110
|
+
// Invert: we want high entropy for BAD (inconsistent) commits
|
|
111
|
+
// Low variety in types is actually good for contract compliance
|
|
112
|
+
// So we measure "type consistency" inversely
|
|
113
|
+
// Actually, for contract drift, we want to measure message QUALITY entropy
|
|
114
|
+
// A mix of good and bad messages = medium entropy
|
|
115
|
+
// All bad messages = low entropy (consistently bad)
|
|
116
|
+
// All good messages = low entropy (consistently good)
|
|
117
|
+
// For our purposes, we'll measure the "other" type ratio as entropy proxy
|
|
118
|
+
const otherCount = typeCounts.get('other') || 0;
|
|
119
|
+
const otherRatio = otherCount / total;
|
|
120
|
+
return otherRatio; // 0 = all conventional, 1 = all non-conventional
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Detect contract drift in commit messages.
|
|
124
|
+
*/
|
|
125
|
+
export function detectContractDrift(commits, config = {}) {
|
|
126
|
+
// Check if contract checking is enabled
|
|
127
|
+
if (config.contractCheckEnabled === false) {
|
|
128
|
+
return {
|
|
129
|
+
detected: false,
|
|
130
|
+
driftMetrics: {
|
|
131
|
+
baselineCompliance: 0,
|
|
132
|
+
currentCompliance: 0,
|
|
133
|
+
driftPercentage: 0,
|
|
134
|
+
trend: 'stable',
|
|
135
|
+
entropyScore: 0,
|
|
136
|
+
},
|
|
137
|
+
degradingCommits: [],
|
|
138
|
+
totalDegradingCommits: 0,
|
|
139
|
+
message: 'Contract checking disabled',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (commits.length === 0) {
|
|
143
|
+
return {
|
|
144
|
+
detected: false,
|
|
145
|
+
driftMetrics: {
|
|
146
|
+
baselineCompliance: 100,
|
|
147
|
+
currentCompliance: 100,
|
|
148
|
+
driftPercentage: 0,
|
|
149
|
+
trend: 'stable',
|
|
150
|
+
entropyScore: 0,
|
|
151
|
+
},
|
|
152
|
+
degradingCommits: [],
|
|
153
|
+
totalDegradingCommits: 0,
|
|
154
|
+
message: 'No commits to analyze',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const driftThreshold = config.driftThreshold ?? 30;
|
|
158
|
+
// Sort commits by date (oldest first)
|
|
159
|
+
const sortedCommits = [...commits].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
160
|
+
// Calculate compliance for all commits
|
|
161
|
+
const complianceData = sortedCommits.map(commit => ({
|
|
162
|
+
commit,
|
|
163
|
+
...scoreCommitCompliance(commit.message),
|
|
164
|
+
}));
|
|
165
|
+
// Determine baseline window (first 20% or at least 3 commits)
|
|
166
|
+
const baselineSize = Math.max(3, Math.floor(commits.length * 0.2));
|
|
167
|
+
const currentSize = Math.max(3, Math.floor(commits.length * 0.2));
|
|
168
|
+
const baselineCommits = complianceData.slice(0, baselineSize);
|
|
169
|
+
const currentCommits = complianceData.slice(-currentSize);
|
|
170
|
+
// Calculate average compliance
|
|
171
|
+
const baselineCompliance = baselineCommits.length > 0
|
|
172
|
+
? baselineCommits.reduce((sum, c) => sum + c.score, 0) / baselineCommits.length
|
|
173
|
+
: 100;
|
|
174
|
+
const currentCompliance = currentCommits.length > 0
|
|
175
|
+
? currentCommits.reduce((sum, c) => sum + c.score, 0) / currentCommits.length
|
|
176
|
+
: 100;
|
|
177
|
+
// Calculate drift percentage (negative = improvement, positive = degradation)
|
|
178
|
+
const driftPercentage = baselineCompliance > 0
|
|
179
|
+
? ((baselineCompliance - currentCompliance) / baselineCompliance) * 100
|
|
180
|
+
: 0;
|
|
181
|
+
// Determine trend
|
|
182
|
+
let trend = 'stable';
|
|
183
|
+
if (driftPercentage > 10) {
|
|
184
|
+
trend = 'degrading';
|
|
185
|
+
}
|
|
186
|
+
else if (driftPercentage < -10) {
|
|
187
|
+
trend = 'improving';
|
|
188
|
+
}
|
|
189
|
+
// Calculate entropy score
|
|
190
|
+
const entropyScore = calculateEntropy(sortedCommits);
|
|
191
|
+
// Find degrading commits (score below 70)
|
|
192
|
+
const degradingCommits = complianceData
|
|
193
|
+
.filter(c => c.score < 70)
|
|
194
|
+
.map(c => ({
|
|
195
|
+
hash: c.commit.hash,
|
|
196
|
+
message: c.commit.message,
|
|
197
|
+
timestamp: c.commit.date,
|
|
198
|
+
issues: c.issues,
|
|
199
|
+
complianceScore: c.score,
|
|
200
|
+
}));
|
|
201
|
+
// Detect if contract drift is significant
|
|
202
|
+
const detected = driftPercentage > driftThreshold || degradingCommits.length > commits.length * 0.3;
|
|
203
|
+
// Generate message
|
|
204
|
+
let message = 'Commit format compliance stable';
|
|
205
|
+
if (detected) {
|
|
206
|
+
if (trend === 'degrading') {
|
|
207
|
+
message = `Commit format degrading: ${driftPercentage.toFixed(0)}% drift from baseline`;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
message = `${degradingCommits.length} commits below quality threshold`;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (trend === 'improving') {
|
|
214
|
+
message = 'Commit format improving over time';
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
detected,
|
|
218
|
+
driftMetrics: {
|
|
219
|
+
baselineCompliance: Math.round(baselineCompliance),
|
|
220
|
+
currentCompliance: Math.round(currentCompliance),
|
|
221
|
+
driftPercentage: Math.round(driftPercentage),
|
|
222
|
+
trend,
|
|
223
|
+
entropyScore: Math.round(entropyScore * 100) / 100,
|
|
224
|
+
},
|
|
225
|
+
degradingCommits,
|
|
226
|
+
totalDegradingCommits: degradingCommits.length,
|
|
227
|
+
message,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=contract-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-drift.js","sourceRoot":"","sources":["../../src/ai-safety/contract-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Safety Detection Module
|
|
3
|
+
*
|
|
4
|
+
* This module detects LLM-specific antipatterns in commit history:
|
|
5
|
+
* 1. Secret Leakage - Exposed API keys, tokens, credentials
|
|
6
|
+
* 2. Scope Violation - Commits touching files outside declared domain
|
|
7
|
+
* 3. Contract Drift - Commit message format degrading over time
|
|
8
|
+
* 4. Token Spiral - Runaway token consumption (context explosion)
|
|
9
|
+
*/
|
|
10
|
+
import { Commit } from '../types.js';
|
|
11
|
+
import { AISafetyAnalysis, AISafetyConfig } from './types.js';
|
|
12
|
+
export * from './types.js';
|
|
13
|
+
export { detectSecretLeakage, detectSecretLeakageFromMessages } from './secret-leakage.js';
|
|
14
|
+
export { detectScopeViolations, loadScopeConfig } from './scope-violation.js';
|
|
15
|
+
export { detectContractDrift } from './contract-drift.js';
|
|
16
|
+
export { detectTokenSpiral } from './token-spiral.js';
|
|
17
|
+
/**
|
|
18
|
+
* Run all AI safety detectors.
|
|
19
|
+
*
|
|
20
|
+
* @param commits - List of commits to analyze
|
|
21
|
+
* @param filesPerCommit - Map of commit hash to changed files
|
|
22
|
+
* @param config - Detection configuration
|
|
23
|
+
* @param repoPath - Optional repository path for scope config loading
|
|
24
|
+
* @param lineStatsPerCommit - Optional line stats for token estimation
|
|
25
|
+
* @returns AI safety analysis results
|
|
26
|
+
*/
|
|
27
|
+
export declare function analyzeAISafety(commits: Commit[], filesPerCommit: Map<string, string[]>, config?: Partial<AISafetyConfig>, repoPath?: string, lineStatsPerCommit?: Map<string, {
|
|
28
|
+
additions: number;
|
|
29
|
+
deletions: number;
|
|
30
|
+
}>): AISafetyAnalysis;
|
|
31
|
+
/**
|
|
32
|
+
* Quick check for AI safety issues (fast, minimal analysis).
|
|
33
|
+
*/
|
|
34
|
+
export declare function quickAISafetyCheck(commits: Commit[], filesPerCommit: Map<string, string[]>): {
|
|
35
|
+
hasIssues: boolean;
|
|
36
|
+
issueCount: number;
|
|
37
|
+
topIssue: string | null;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Format AI safety analysis for terminal output.
|
|
41
|
+
*/
|
|
42
|
+
export declare function formatAISafetyAnalysis(analysis: AISafetyAnalysis): string;
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai-safety/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,gBAAgB,EAChB,cAAc,EAMf,MAAM,YAAY,CAAC;AAOpB,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,EACpC,QAAQ,CAAC,EAAE,MAAM,EACjB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACzE,gBAAgB,CA4DlB;AAyED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACpC;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAcA;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAgDzE"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Safety Detection Module
|
|
3
|
+
*
|
|
4
|
+
* This module detects LLM-specific antipatterns in commit history:
|
|
5
|
+
* 1. Secret Leakage - Exposed API keys, tokens, credentials
|
|
6
|
+
* 2. Scope Violation - Commits touching files outside declared domain
|
|
7
|
+
* 3. Contract Drift - Commit message format degrading over time
|
|
8
|
+
* 4. Token Spiral - Runaway token consumption (context explosion)
|
|
9
|
+
*/
|
|
10
|
+
import { DEFAULT_AI_SAFETY_CONFIG, } from './types.js';
|
|
11
|
+
import { detectSecretLeakageFromMessages } from './secret-leakage.js';
|
|
12
|
+
import { detectScopeViolations } from './scope-violation.js';
|
|
13
|
+
import { detectContractDrift } from './contract-drift.js';
|
|
14
|
+
import { detectTokenSpiral } from './token-spiral.js';
|
|
15
|
+
// Re-export types
|
|
16
|
+
export * from './types.js';
|
|
17
|
+
// Re-export detector functions
|
|
18
|
+
export { detectSecretLeakage, detectSecretLeakageFromMessages } from './secret-leakage.js';
|
|
19
|
+
export { detectScopeViolations, loadScopeConfig } from './scope-violation.js';
|
|
20
|
+
export { detectContractDrift } from './contract-drift.js';
|
|
21
|
+
export { detectTokenSpiral } from './token-spiral.js';
|
|
22
|
+
/**
|
|
23
|
+
* Run all AI safety detectors.
|
|
24
|
+
*
|
|
25
|
+
* @param commits - List of commits to analyze
|
|
26
|
+
* @param filesPerCommit - Map of commit hash to changed files
|
|
27
|
+
* @param config - Detection configuration
|
|
28
|
+
* @param repoPath - Optional repository path for scope config loading
|
|
29
|
+
* @param lineStatsPerCommit - Optional line stats for token estimation
|
|
30
|
+
* @returns AI safety analysis results
|
|
31
|
+
*/
|
|
32
|
+
export function analyzeAISafety(commits, filesPerCommit, config = {}, repoPath, lineStatsPerCommit) {
|
|
33
|
+
const cfg = { ...DEFAULT_AI_SAFETY_CONFIG, ...config };
|
|
34
|
+
// Run secret leakage detector (synchronous version for commit messages)
|
|
35
|
+
const secretLeakage = detectSecretLeakageFromMessages(commits, cfg);
|
|
36
|
+
// Run scope violation detector
|
|
37
|
+
const scopeViolations = detectScopeViolations(commits, filesPerCommit, cfg, repoPath);
|
|
38
|
+
// Run contract drift detector
|
|
39
|
+
const contractDrift = detectContractDrift(commits, cfg);
|
|
40
|
+
// Run token spiral detector
|
|
41
|
+
const tokenSpiral = detectTokenSpiral(commits, lineStatsPerCommit || new Map(), filesPerCommit, cfg);
|
|
42
|
+
// Calculate summary
|
|
43
|
+
const totalIssues = secretLeakage.totalFindings +
|
|
44
|
+
scopeViolations.totalViolations +
|
|
45
|
+
contractDrift.totalDegradingCommits +
|
|
46
|
+
tokenSpiral.spirals.length;
|
|
47
|
+
const criticalIssues = secretLeakage.criticalFindings + scopeViolations.totalViolations;
|
|
48
|
+
const warningIssues = contractDrift.totalDegradingCommits + (tokenSpiral.detected ? 1 : 0);
|
|
49
|
+
// Determine overall health
|
|
50
|
+
let overallHealth = 'healthy';
|
|
51
|
+
if (criticalIssues > 0) {
|
|
52
|
+
overallHealth = 'critical';
|
|
53
|
+
}
|
|
54
|
+
else if (warningIssues > 0) {
|
|
55
|
+
overallHealth = 'warning';
|
|
56
|
+
}
|
|
57
|
+
// Generate recommendations
|
|
58
|
+
const recommendations = generateRecommendations(secretLeakage, scopeViolations, contractDrift, tokenSpiral);
|
|
59
|
+
return {
|
|
60
|
+
secretLeakage,
|
|
61
|
+
scopeViolations,
|
|
62
|
+
contractDrift,
|
|
63
|
+
tokenSpiral,
|
|
64
|
+
summary: {
|
|
65
|
+
totalIssues,
|
|
66
|
+
criticalIssues,
|
|
67
|
+
warningIssues,
|
|
68
|
+
overallHealth,
|
|
69
|
+
},
|
|
70
|
+
recommendations,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate recommendations based on detected AI safety issues.
|
|
75
|
+
*/
|
|
76
|
+
function generateRecommendations(secretLeakage, scopeViolations, contractDrift, tokenSpiral) {
|
|
77
|
+
const recommendations = [];
|
|
78
|
+
// Secret Leakage recommendations
|
|
79
|
+
if (secretLeakage.detected) {
|
|
80
|
+
recommendations.push('🔐 CRITICAL: Secrets detected in commits. Rotate exposed credentials immediately.');
|
|
81
|
+
recommendations.push('Run `git filter-repo` or BFG Repo-Cleaner to remove secrets from git history.');
|
|
82
|
+
if (secretLeakage.criticalFindings > 0) {
|
|
83
|
+
recommendations.push('⚠️ High-value secrets found (API keys, tokens). This is a SECURITY INCIDENT.');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Scope Violation recommendations
|
|
87
|
+
if (scopeViolations.detected) {
|
|
88
|
+
recommendations.push('🎯 AI modified files outside declared scope. Be more explicit about allowed files.');
|
|
89
|
+
recommendations.push('Example: "ONLY modify src/feature.ts and src/feature.test.ts"');
|
|
90
|
+
}
|
|
91
|
+
// Contract Drift recommendations
|
|
92
|
+
if (contractDrift.detected) {
|
|
93
|
+
const drift = contractDrift.driftMetrics;
|
|
94
|
+
if (drift.trend === 'degrading') {
|
|
95
|
+
recommendations.push(`📉 Commit message quality degrading (${drift.driftPercentage.toFixed(0)}% worse). Reinforce commit format expectations.`);
|
|
96
|
+
recommendations.push('Remind AI to use conventional commits: "feat:", "fix:", "docs:", etc.');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Token Spiral recommendations
|
|
100
|
+
if (tokenSpiral.detected) {
|
|
101
|
+
if (tokenSpiral.estimatedTokens.explosionDetected) {
|
|
102
|
+
recommendations.push('💥 Token usage exploded. Context window may be filling up with repeated content.');
|
|
103
|
+
recommendations.push('Consider: 1) Break task into smaller pieces, 2) Start fresh session, 3) Use more focused prompts');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Emergency recommendation for multiple critical issues
|
|
107
|
+
if (secretLeakage.criticalFindings > 0 && scopeViolations.totalViolations > 2) {
|
|
108
|
+
recommendations.unshift('🚨 SECURITY ALERT: Multiple critical AI safety issues. STOP and review all changes before proceeding.');
|
|
109
|
+
}
|
|
110
|
+
return recommendations;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Quick check for AI safety issues (fast, minimal analysis).
|
|
114
|
+
*/
|
|
115
|
+
export function quickAISafetyCheck(commits, filesPerCommit) {
|
|
116
|
+
const analysis = analyzeAISafety(commits, filesPerCommit);
|
|
117
|
+
const issues = [];
|
|
118
|
+
if (analysis.secretLeakage.detected)
|
|
119
|
+
issues.push('secret-leakage');
|
|
120
|
+
if (analysis.scopeViolations.detected)
|
|
121
|
+
issues.push('scope-violation');
|
|
122
|
+
if (analysis.contractDrift.detected)
|
|
123
|
+
issues.push('contract-drift');
|
|
124
|
+
if (analysis.tokenSpiral.detected)
|
|
125
|
+
issues.push('token-spiral');
|
|
126
|
+
return {
|
|
127
|
+
hasIssues: issues.length > 0,
|
|
128
|
+
issueCount: issues.length,
|
|
129
|
+
topIssue: issues[0] || null,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Format AI safety analysis for terminal output.
|
|
134
|
+
*/
|
|
135
|
+
export function formatAISafetyAnalysis(analysis) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
// Header
|
|
138
|
+
const healthEmoji = analysis.summary.overallHealth === 'critical'
|
|
139
|
+
? '🚨'
|
|
140
|
+
: analysis.summary.overallHealth === 'warning'
|
|
141
|
+
? '⚠️'
|
|
142
|
+
: '✅';
|
|
143
|
+
lines.push(`\n${healthEmoji} AI Safety: ${analysis.summary.overallHealth.toUpperCase()}`);
|
|
144
|
+
lines.push('─'.repeat(50));
|
|
145
|
+
// Individual pattern results
|
|
146
|
+
if (analysis.secretLeakage.detected) {
|
|
147
|
+
lines.push(`🔐 Secret Leakage: ${analysis.secretLeakage.message}`);
|
|
148
|
+
}
|
|
149
|
+
if (analysis.scopeViolations.detected) {
|
|
150
|
+
lines.push(`🎯 Scope Violations: ${analysis.scopeViolations.message}`);
|
|
151
|
+
}
|
|
152
|
+
if (analysis.contractDrift.detected) {
|
|
153
|
+
lines.push(`📉 Contract Drift: ${analysis.contractDrift.message}`);
|
|
154
|
+
}
|
|
155
|
+
if (analysis.tokenSpiral.detected) {
|
|
156
|
+
lines.push(`💥 Token Spiral: ${analysis.tokenSpiral.message}`);
|
|
157
|
+
}
|
|
158
|
+
// Summary
|
|
159
|
+
if (analysis.summary.totalIssues > 0) {
|
|
160
|
+
lines.push('');
|
|
161
|
+
lines.push(`Total: ${analysis.summary.totalIssues} issue${analysis.summary.totalIssues > 1 ? 's' : ''} ` +
|
|
162
|
+
`(${analysis.summary.criticalIssues} critical, ${analysis.summary.warningIssues} warning)`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
lines.push('No AI safety issues detected.');
|
|
166
|
+
}
|
|
167
|
+
// Recommendations
|
|
168
|
+
if (analysis.recommendations.length > 0) {
|
|
169
|
+
lines.push('');
|
|
170
|
+
lines.push('Recommendations:');
|
|
171
|
+
for (const rec of analysis.recommendations.slice(0, 5)) {
|
|
172
|
+
lines.push(` ${rec}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai-safety/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Violation Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects commits touching files outside declared scope.
|
|
5
|
+
* Scope can be defined via .vibe-check/scope.yaml or passed in config.
|
|
6
|
+
* Uses glob patterns (minimatch) for flexible matching.
|
|
7
|
+
*/
|
|
8
|
+
import { Commit } from '../types.js';
|
|
9
|
+
import { ScopeViolationResult, ScopeDefinition, AISafetyConfig } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Load scope configuration from .vibe-check/scope.yaml
|
|
12
|
+
*/
|
|
13
|
+
export declare function loadScopeConfig(repoPath: string): ScopeDefinition | null;
|
|
14
|
+
/**
|
|
15
|
+
* Detect scope violations in commits.
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectScopeViolations(commits: Commit[], filesPerCommit: Map<string, string[]>, config?: Partial<AISafetyConfig>, repoPath?: string): ScopeViolationResult;
|
|
18
|
+
//# sourceMappingURL=scope-violation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-violation.d.ts","sourceRoot":"","sources":["../../src/ai-safety/scope-violation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,oBAAoB,EAEpB,eAAe,EACf,cAAc,EACf,MAAM,YAAY,CAAC;AAcpB;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAwBxE;AA2CD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,EACpC,QAAQ,CAAC,EAAE,MAAM,GAChB,oBAAoB,CAwFtB"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope Violation Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects commits touching files outside declared scope.
|
|
5
|
+
* Scope can be defined via .vibe-check/scope.yaml or passed in config.
|
|
6
|
+
* Uses glob patterns (minimatch) for flexible matching.
|
|
7
|
+
*/
|
|
8
|
+
import { minimatch } from 'minimatch';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import * as yaml from 'js-yaml';
|
|
12
|
+
/**
|
|
13
|
+
* Load scope configuration from .vibe-check/scope.yaml
|
|
14
|
+
*/
|
|
15
|
+
export function loadScopeConfig(repoPath) {
|
|
16
|
+
const configPath = path.join(repoPath, '.vibe-check', 'scope.yaml');
|
|
17
|
+
try {
|
|
18
|
+
if (!fs.existsSync(configPath)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
22
|
+
const config = yaml.load(content);
|
|
23
|
+
if (!config?.scope) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
allowedPatterns: config.scope.allowed_patterns || [],
|
|
28
|
+
allowedDirs: config.scope.allowed_dirs || [],
|
|
29
|
+
description: config.scope.description,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Config file doesn't exist or is invalid
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a file path matches any of the allowed patterns.
|
|
39
|
+
*/
|
|
40
|
+
function isFileAllowed(filePath, scope, exceptions) {
|
|
41
|
+
// Check exceptions first (always allowed)
|
|
42
|
+
for (const exception of exceptions) {
|
|
43
|
+
if (minimatch(filePath, exception) || filePath === exception) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
// Also check just the filename for common exceptions
|
|
47
|
+
const fileName = path.basename(filePath);
|
|
48
|
+
if (fileName === exception) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Check allowed patterns (glob)
|
|
53
|
+
for (const pattern of scope.allowedPatterns) {
|
|
54
|
+
if (minimatch(filePath, pattern, { matchBase: true })) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Check allowed directories
|
|
59
|
+
for (const dir of scope.allowedDirs) {
|
|
60
|
+
// Normalize paths for comparison
|
|
61
|
+
const normalizedDir = dir.endsWith('/') ? dir : dir + '/';
|
|
62
|
+
const normalizedFile = filePath.startsWith('/') ? filePath.slice(1) : filePath;
|
|
63
|
+
if (normalizedFile.startsWith(normalizedDir) || normalizedFile.startsWith(dir)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Detect scope violations in commits.
|
|
71
|
+
*/
|
|
72
|
+
export function detectScopeViolations(commits, filesPerCommit, config = {}, repoPath) {
|
|
73
|
+
// Check if scope checking is enabled
|
|
74
|
+
if (config.scopeCheckEnabled === false) {
|
|
75
|
+
return {
|
|
76
|
+
detected: false,
|
|
77
|
+
violations: [],
|
|
78
|
+
totalViolations: 0,
|
|
79
|
+
totalUnauthorizedFiles: 0,
|
|
80
|
+
message: 'Scope checking disabled',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Try to load scope from config or file
|
|
84
|
+
let scope = config.scope;
|
|
85
|
+
if (!scope && repoPath) {
|
|
86
|
+
scope = loadScopeConfig(repoPath) || undefined;
|
|
87
|
+
}
|
|
88
|
+
// No scope defined = no violations possible
|
|
89
|
+
if (!scope) {
|
|
90
|
+
return {
|
|
91
|
+
detected: false,
|
|
92
|
+
violations: [],
|
|
93
|
+
totalViolations: 0,
|
|
94
|
+
totalUnauthorizedFiles: 0,
|
|
95
|
+
message: 'No scope defined (use .vibe-check/scope.yaml or config)',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
// Check for empty scope (nothing allowed = everything violates)
|
|
99
|
+
if (scope.allowedPatterns.length === 0 && scope.allowedDirs.length === 0) {
|
|
100
|
+
return {
|
|
101
|
+
detected: false,
|
|
102
|
+
violations: [],
|
|
103
|
+
totalViolations: 0,
|
|
104
|
+
totalUnauthorizedFiles: 0,
|
|
105
|
+
message: 'Scope defined but empty (no patterns or dirs)',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const exceptions = config.allowedExceptions || [
|
|
109
|
+
'package.json',
|
|
110
|
+
'package-lock.json',
|
|
111
|
+
'tsconfig.json',
|
|
112
|
+
'.gitignore',
|
|
113
|
+
'README.md',
|
|
114
|
+
];
|
|
115
|
+
const violations = [];
|
|
116
|
+
let totalUnauthorizedFiles = 0;
|
|
117
|
+
for (const commit of commits) {
|
|
118
|
+
const files = filesPerCommit.get(commit.hash) || [];
|
|
119
|
+
const unauthorizedFiles = [];
|
|
120
|
+
for (const file of files) {
|
|
121
|
+
if (!isFileAllowed(file, scope, exceptions)) {
|
|
122
|
+
unauthorizedFiles.push(file);
|
|
123
|
+
totalUnauthorizedFiles++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (unauthorizedFiles.length > 0) {
|
|
127
|
+
violations.push({
|
|
128
|
+
commitHash: commit.hash,
|
|
129
|
+
commitMessage: commit.message,
|
|
130
|
+
timestamp: commit.date,
|
|
131
|
+
unauthorizedFiles,
|
|
132
|
+
declaredScope: [...scope.allowedPatterns, ...scope.allowedDirs],
|
|
133
|
+
description: `Commit touched ${unauthorizedFiles.length} file(s) outside declared scope`,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const detected = violations.length > 0;
|
|
138
|
+
let message = 'No scope violations detected';
|
|
139
|
+
if (detected) {
|
|
140
|
+
message = `${violations.length} commit(s) violated scope (${totalUnauthorizedFiles} unauthorized files)`;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
detected,
|
|
144
|
+
violations,
|
|
145
|
+
totalViolations: violations.length,
|
|
146
|
+
totalUnauthorizedFiles,
|
|
147
|
+
message,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=scope-violation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-violation.js","sourceRoot":"","sources":["../../src/ai-safety/scope-violation.ts"],"names":[],"mappings":"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"}
|