@boshu2/vibe-check 2.2.1 → 2.3.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 (43) hide show
  1. package/.agents/plans/2025-12-28-ai-safety-integration-plan.md +326 -0
  2. package/.agents/research/2025-12-28-ai-platform-security-integration.md +295 -0
  3. package/.beads/README.md +81 -0
  4. package/.beads/config.yaml +62 -0
  5. package/.beads/interactions.jsonl +0 -0
  6. package/.beads/issues.jsonl +9 -0
  7. package/.beads/metadata.json +4 -0
  8. package/.gitattributes +3 -0
  9. package/AGENTS.md +40 -0
  10. package/CHANGELOG.md +42 -0
  11. package/CLAUDE.md +75 -0
  12. package/dist/ai-safety/contract-drift.d.ts +14 -0
  13. package/dist/ai-safety/contract-drift.d.ts.map +1 -0
  14. package/dist/ai-safety/contract-drift.js +230 -0
  15. package/dist/ai-safety/contract-drift.js.map +1 -0
  16. package/dist/ai-safety/index.d.ts +43 -0
  17. package/dist/ai-safety/index.d.ts.map +1 -0
  18. package/dist/ai-safety/index.js +177 -0
  19. package/dist/ai-safety/index.js.map +1 -0
  20. package/dist/ai-safety/scope-violation.d.ts +18 -0
  21. package/dist/ai-safety/scope-violation.d.ts.map +1 -0
  22. package/dist/ai-safety/scope-violation.js +150 -0
  23. package/dist/ai-safety/scope-violation.js.map +1 -0
  24. package/dist/ai-safety/secret-leakage.d.ts +18 -0
  25. package/dist/ai-safety/secret-leakage.d.ts.map +1 -0
  26. package/dist/ai-safety/secret-leakage.js +188 -0
  27. package/dist/ai-safety/secret-leakage.js.map +1 -0
  28. package/dist/ai-safety/token-spiral.d.ts +17 -0
  29. package/dist/ai-safety/token-spiral.d.ts.map +1 -0
  30. package/dist/ai-safety/token-spiral.js +183 -0
  31. package/dist/ai-safety/token-spiral.js.map +1 -0
  32. package/dist/ai-safety/types.d.ts +122 -0
  33. package/dist/ai-safety/types.d.ts.map +1 -0
  34. package/dist/ai-safety/types.js +32 -0
  35. package/dist/ai-safety/types.js.map +1 -0
  36. package/dist/commands/session.d.ts +9 -0
  37. package/dist/commands/session.d.ts.map +1 -1
  38. package/dist/commands/session.js +42 -0
  39. package/dist/commands/session.js.map +1 -1
  40. package/dist/commands/watch.d.ts.map +1 -1
  41. package/dist/commands/watch.js +59 -0
  42. package/dist/commands/watch.js.map +1 -1
  43. package/package.json +4 -1
@@ -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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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Secret Leakage Detector
3
+ *
4
+ * Scans commit diffs for exposed secrets: API keys, tokens, credentials.
5
+ * Ported from ai-platform/tests/agents/test_agent_security.py
6
+ */
7
+ import { Commit } from '../types.js';
8
+ import { SecretLeakageResult, AISafetyConfig } from './types.js';
9
+ /**
10
+ * Detect secrets leaked in commit diffs.
11
+ */
12
+ export declare function detectSecretLeakage(repoPath: string, commits: Commit[], config?: Partial<AISafetyConfig>): Promise<SecretLeakageResult>;
13
+ /**
14
+ * Synchronous version that works with already-fetched commit data.
15
+ * For use when diffs aren't available (e.g., from JSONL timeline).
16
+ */
17
+ export declare function detectSecretLeakageFromMessages(commits: Commit[], config?: Partial<AISafetyConfig>): SecretLeakageResult;
18
+ //# sourceMappingURL=secret-leakage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-leakage.d.ts","sourceRoot":"","sources":["../../src/ai-safety/secret-leakage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,mBAAmB,EAGnB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,OAAO,CAAC,mBAAmB,CAAC,CAwF9B;AAoDD;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,mBAAmB,CAyDrB"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Secret Leakage Detector
3
+ *
4
+ * Scans commit diffs for exposed secrets: API keys, tokens, credentials.
5
+ * Ported from ai-platform/tests/agents/test_agent_security.py
6
+ */
7
+ import { simpleGit } from 'simple-git';
8
+ import { SECRET_PATTERNS, } from './types.js';
9
+ /**
10
+ * Detect secrets leaked in commit diffs.
11
+ */
12
+ export async function detectSecretLeakage(repoPath, commits, config = {}) {
13
+ if (!config.secretDetectionEnabled) {
14
+ return {
15
+ detected: false,
16
+ findings: [],
17
+ totalFindings: 0,
18
+ criticalFindings: 0,
19
+ message: 'Secret detection disabled',
20
+ };
21
+ }
22
+ const findings = [];
23
+ const git = simpleGit(repoPath);
24
+ // Merge custom patterns if provided
25
+ const patterns = {
26
+ ...SECRET_PATTERNS,
27
+ ...(config.customSecretPatterns || {}),
28
+ };
29
+ for (const commit of commits) {
30
+ try {
31
+ let contentToScan;
32
+ if (config.secretScanDepth === 'shallow') {
33
+ // Only scan commit message
34
+ contentToScan = commit.message;
35
+ }
36
+ else {
37
+ // Full scan: get diff content using simple-git
38
+ const diff = await git.show([commit.hash, '--format=', '--patch']);
39
+ contentToScan = diff;
40
+ }
41
+ // Scan for each secret pattern
42
+ for (const [patternName, regex] of Object.entries(patterns)) {
43
+ // Reset regex state
44
+ regex.lastIndex = 0;
45
+ let match;
46
+ while ((match = regex.exec(contentToScan)) !== null) {
47
+ const matchedText = match[0];
48
+ const startIndex = match.index;
49
+ // Get line context (20 chars before and after)
50
+ const contextStart = Math.max(0, startIndex - 20);
51
+ const contextEnd = Math.min(contentToScan.length, startIndex + matchedText.length + 20);
52
+ const rawContext = contentToScan.substring(contextStart, contextEnd);
53
+ // Mask the secret in context (show first 8 chars only)
54
+ const maskedSecret = maskSecret(matchedText);
55
+ const lineContext = rawContext.replace(matchedText, maskedSecret);
56
+ // Determine file from diff context
57
+ const file = extractFileFromDiff(contentToScan, startIndex);
58
+ findings.push({
59
+ commitHash: commit.hash,
60
+ commitMessage: commit.message,
61
+ timestamp: commit.date,
62
+ file: file || 'unknown',
63
+ pattern: patternName,
64
+ matchedText: maskedSecret,
65
+ lineContext: lineContext.replace(/\n/g, ' ').trim(),
66
+ severity: determineSeverity(patternName),
67
+ });
68
+ }
69
+ }
70
+ }
71
+ catch (error) {
72
+ // Skip commits we can't analyze
73
+ continue;
74
+ }
75
+ }
76
+ const criticalFindings = findings.filter((f) => f.severity === 'critical').length;
77
+ const detected = findings.length > 0;
78
+ let message = 'No secrets detected';
79
+ if (detected) {
80
+ message = `${findings.length} secret(s) found (${criticalFindings} critical)`;
81
+ }
82
+ return {
83
+ detected,
84
+ findings,
85
+ totalFindings: findings.length,
86
+ criticalFindings,
87
+ message,
88
+ };
89
+ }
90
+ /**
91
+ * Mask a secret, showing only first 8 characters.
92
+ */
93
+ function maskSecret(secret) {
94
+ if (secret.length <= 8) {
95
+ return '***';
96
+ }
97
+ return secret.substring(0, 8) + '***';
98
+ }
99
+ /**
100
+ * Extract filename from diff context.
101
+ * Looks for lines like "+++ b/path/to/file.ts"
102
+ */
103
+ function extractFileFromDiff(diff, position) {
104
+ // Find the nearest "+++ b/" before the match position
105
+ const beforeMatch = diff.substring(0, position);
106
+ const lines = beforeMatch.split('\n');
107
+ // Search backwards for file header
108
+ for (let i = lines.length - 1; i >= 0; i--) {
109
+ const line = lines[i];
110
+ if (line.startsWith('+++ b/')) {
111
+ return line.substring(6); // Remove "+++ b/"
112
+ }
113
+ if (line.startsWith('diff --git')) {
114
+ // Found diff header without +++ b/, extract from diff line
115
+ const match = line.match(/diff --git a\/.+ b\/(.+)/);
116
+ return match ? match[1] : null;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ /**
122
+ * Determine severity based on pattern type.
123
+ */
124
+ function determineSeverity(patternName) {
125
+ // High-value secrets are critical
126
+ const criticalPatterns = [
127
+ 'OpenAI API Key',
128
+ 'GitHub Personal Access Token',
129
+ 'GitLab Personal Access Token',
130
+ 'AWS Access Key',
131
+ ];
132
+ return criticalPatterns.includes(patternName) ? 'critical' : 'warning';
133
+ }
134
+ /**
135
+ * Synchronous version that works with already-fetched commit data.
136
+ * For use when diffs aren't available (e.g., from JSONL timeline).
137
+ */
138
+ export function detectSecretLeakageFromMessages(commits, config = {}) {
139
+ if (!config.secretDetectionEnabled) {
140
+ return {
141
+ detected: false,
142
+ findings: [],
143
+ totalFindings: 0,
144
+ criticalFindings: 0,
145
+ message: 'Secret detection disabled',
146
+ };
147
+ }
148
+ const findings = [];
149
+ const patterns = {
150
+ ...SECRET_PATTERNS,
151
+ ...(config.customSecretPatterns || {}),
152
+ };
153
+ for (const commit of commits) {
154
+ const contentToScan = commit.message;
155
+ for (const [patternName, regex] of Object.entries(patterns)) {
156
+ regex.lastIndex = 0;
157
+ let match;
158
+ while ((match = regex.exec(contentToScan)) !== null) {
159
+ const matchedText = match[0];
160
+ const maskedSecret = maskSecret(matchedText);
161
+ findings.push({
162
+ commitHash: commit.hash,
163
+ commitMessage: commit.message,
164
+ timestamp: commit.date,
165
+ file: 'commit-message',
166
+ pattern: patternName,
167
+ matchedText: maskedSecret,
168
+ lineContext: commit.message.substring(0, 80),
169
+ severity: determineSeverity(patternName),
170
+ });
171
+ }
172
+ }
173
+ }
174
+ const criticalFindings = findings.filter((f) => f.severity === 'critical').length;
175
+ const detected = findings.length > 0;
176
+ let message = 'No secrets in commit messages';
177
+ if (detected) {
178
+ message = `${findings.length} secret(s) in messages (${criticalFindings} critical)`;
179
+ }
180
+ return {
181
+ detected,
182
+ findings,
183
+ totalFindings: findings.length,
184
+ criticalFindings,
185
+ message,
186
+ };
187
+ }
188
+ //# sourceMappingURL=secret-leakage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-leakage.js","sourceRoot":"","sources":["../../src/ai-safety/secret-leakage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Token Spiral Detector
3
+ *
4
+ * Estimates token usage from commit size and detects explosion patterns.
5
+ * Uses heuristic: ~4 characters per token (GPT-style tokenization).
6
+ * Detects when commits exceed baseline by configurable threshold.
7
+ */
8
+ import { Commit } from '../types.js';
9
+ import { TokenSpiralResult, AISafetyConfig } from './types.js';
10
+ /**
11
+ * Detect token spirals in commit history.
12
+ */
13
+ export declare function detectTokenSpiral(commits: Commit[], lineStatsPerCommit: Map<string, {
14
+ additions: number;
15
+ deletions: number;
16
+ }>, filesPerCommit: Map<string, string[]>, config?: Partial<AISafetyConfig>): TokenSpiralResult;
17
+ //# sourceMappingURL=token-spiral.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-spiral.d.ts","sourceRoot":"","sources":["../../src/ai-safety/token-spiral.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EACL,iBAAiB,EAGjB,cAAc,EACf,MAAM,YAAY,CAAC;AA+EpB;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EAAE,EACjB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,EACzE,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAM,GACnC,iBAAiB,CAwJnB"}