@boshu2/vibe-check 1.7.0 → 1.8.1
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/bundles/actionable-coaching-plan-2025-12-02.md +209 -0
- package/.agents/plans/git-forensics-enhancement-2025-12-05.md +493 -0
- package/.claude/skills/typescript-review.md +152 -0
- package/CHANGELOG.md +41 -5
- package/CLAUDE.md +85 -23
- package/Makefile +43 -19
- package/README.md +178 -172
- package/SECURITY.md +5 -1
- package/assets/logo-dark.svg +47 -0
- package/assets/logo.svg +47 -0
- package/claude-progress.json +28 -7
- package/claude-progress.txt +48 -0
- package/dist/analyzers/patterns.d.ts +62 -0
- package/dist/analyzers/patterns.d.ts.map +1 -0
- package/dist/analyzers/patterns.js +103 -0
- package/dist/analyzers/patterns.js.map +1 -0
- package/dist/analyzers/quality.d.ts +58 -0
- package/dist/analyzers/quality.d.ts.map +1 -0
- package/dist/analyzers/quality.js +114 -0
- package/dist/analyzers/quality.js.map +1 -0
- package/dist/analyzers/sessions.d.ts +45 -0
- package/dist/analyzers/sessions.d.ts.map +1 -0
- package/dist/analyzers/sessions.js +123 -0
- package/dist/analyzers/sessions.js.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +5 -0
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/forensics.d.ts +29 -0
- package/dist/commands/forensics.d.ts.map +1 -0
- package/dist/commands/forensics.js +213 -0
- package/dist/commands/forensics.js.map +1 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +11 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/insights.d.ts +3 -0
- package/dist/commands/insights.d.ts.map +1 -0
- package/dist/commands/insights.js +120 -0
- package/dist/commands/insights.js.map +1 -0
- package/dist/commands/pipeline.d.ts +3 -0
- package/dist/commands/pipeline.d.ts.map +1 -0
- package/dist/commands/pipeline.js +485 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/profile.d.ts +0 -1
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +0 -4
- package/dist/commands/profile.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 +95 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/sessions.d.ts +20 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +201 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +124 -7
- package/dist/commands/watch.js.map +1 -1
- package/dist/inner-loop/context-amnesia.d.ts +20 -0
- package/dist/inner-loop/context-amnesia.d.ts.map +1 -0
- package/dist/inner-loop/context-amnesia.js +249 -0
- package/dist/inner-loop/context-amnesia.js.map +1 -0
- package/dist/inner-loop/index.d.ts +39 -0
- package/dist/inner-loop/index.d.ts.map +1 -0
- package/dist/inner-loop/index.js +208 -0
- package/dist/inner-loop/index.js.map +1 -0
- package/dist/inner-loop/instruction-drift.d.ts +28 -0
- package/dist/inner-loop/instruction-drift.d.ts.map +1 -0
- package/dist/inner-loop/instruction-drift.js +260 -0
- package/dist/inner-loop/instruction-drift.js.map +1 -0
- package/dist/inner-loop/logging-only.d.ts +30 -0
- package/dist/inner-loop/logging-only.d.ts.map +1 -0
- package/dist/inner-loop/logging-only.js +264 -0
- package/dist/inner-loop/logging-only.js.map +1 -0
- package/dist/inner-loop/tests-passing-lie.d.ts +34 -0
- package/dist/inner-loop/tests-passing-lie.d.ts.map +1 -0
- package/dist/inner-loop/tests-passing-lie.js +213 -0
- package/dist/inner-loop/tests-passing-lie.js.map +1 -0
- package/dist/inner-loop/types.d.ts +111 -0
- package/dist/inner-loop/types.d.ts.map +1 -0
- package/dist/inner-loop/types.js +29 -0
- package/dist/inner-loop/types.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +11 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/spiral-history.d.ts +62 -0
- package/dist/storage/spiral-history.d.ts.map +1 -0
- package/dist/storage/spiral-history.js +265 -0
- package/dist/storage/spiral-history.js.map +1 -0
- package/docs/ARCHITECTURE.md +2 -10
- package/docs/FEATURES.md +340 -0
- package/docs/GAMIFICATION.md +19 -266
- package/docs/VIBE-ECOSYSTEM.md +12 -78
- package/feature-list.json +140 -88
- package/package.json +1 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Inner Loop Failure Pattern Detection
|
|
4
|
+
*
|
|
5
|
+
* This module detects the 4 "Inner Loop Disasters" from vibe coding:
|
|
6
|
+
* 1. "Tests Passing" Lie - AI claims success but code doesn't work
|
|
7
|
+
* 2. Context Amnesia - Forgets instructions, re-does work
|
|
8
|
+
* 3. Instruction Drift - "Improves" things not asked for
|
|
9
|
+
* 4. Debug Loop Spiral - Adds logging instead of fixing
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
23
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.analyzeCommitsForLogging = exports.detectLoggingOnlyCommits = exports.inferSessionScope = exports.createSessionScope = exports.detectInstructionDrift = exports.detectContextAmnesia = exports.detectTestsPassingLie = void 0;
|
|
27
|
+
exports.analyzeInnerLoop = analyzeInnerLoop;
|
|
28
|
+
exports.quickInnerLoopCheck = quickInnerLoopCheck;
|
|
29
|
+
exports.formatInnerLoopAnalysis = formatInnerLoopAnalysis;
|
|
30
|
+
const types_1 = require("./types");
|
|
31
|
+
const tests_passing_lie_1 = require("./tests-passing-lie");
|
|
32
|
+
const context_amnesia_1 = require("./context-amnesia");
|
|
33
|
+
const instruction_drift_1 = require("./instruction-drift");
|
|
34
|
+
const logging_only_1 = require("./logging-only");
|
|
35
|
+
// Re-export types
|
|
36
|
+
__exportStar(require("./types"), exports);
|
|
37
|
+
// Re-export individual detectors
|
|
38
|
+
var tests_passing_lie_2 = require("./tests-passing-lie");
|
|
39
|
+
Object.defineProperty(exports, "detectTestsPassingLie", { enumerable: true, get: function () { return tests_passing_lie_2.detectTestsPassingLie; } });
|
|
40
|
+
var context_amnesia_2 = require("./context-amnesia");
|
|
41
|
+
Object.defineProperty(exports, "detectContextAmnesia", { enumerable: true, get: function () { return context_amnesia_2.detectContextAmnesia; } });
|
|
42
|
+
var instruction_drift_2 = require("./instruction-drift");
|
|
43
|
+
Object.defineProperty(exports, "detectInstructionDrift", { enumerable: true, get: function () { return instruction_drift_2.detectInstructionDrift; } });
|
|
44
|
+
Object.defineProperty(exports, "createSessionScope", { enumerable: true, get: function () { return instruction_drift_2.createSessionScope; } });
|
|
45
|
+
Object.defineProperty(exports, "inferSessionScope", { enumerable: true, get: function () { return instruction_drift_2.inferSessionScope; } });
|
|
46
|
+
var logging_only_2 = require("./logging-only");
|
|
47
|
+
Object.defineProperty(exports, "detectLoggingOnlyCommits", { enumerable: true, get: function () { return logging_only_2.detectLoggingOnlyCommits; } });
|
|
48
|
+
Object.defineProperty(exports, "analyzeCommitsForLogging", { enumerable: true, get: function () { return logging_only_2.analyzeCommitsForLogging; } });
|
|
49
|
+
/**
|
|
50
|
+
* Run all inner loop failure pattern detectors.
|
|
51
|
+
*/
|
|
52
|
+
function analyzeInnerLoop(events, filesPerCommit, lineStatsPerCommit, config = {}) {
|
|
53
|
+
const cfg = { ...types_1.DEFAULT_INNER_LOOP_CONFIG, ...config };
|
|
54
|
+
// Run all detectors
|
|
55
|
+
const testsPassingLie = (0, tests_passing_lie_1.detectTestsPassingLie)(events, filesPerCommit, cfg);
|
|
56
|
+
const contextAmnesia = (0, context_amnesia_1.detectContextAmnesia)(events, filesPerCommit, lineStatsPerCommit, cfg);
|
|
57
|
+
const instructionDrift = (0, instruction_drift_1.detectInstructionDrift)(events, filesPerCommit, cfg);
|
|
58
|
+
const loggingOnly = (0, logging_only_1.detectLoggingOnlyCommits)(events, cfg);
|
|
59
|
+
// Calculate summary
|
|
60
|
+
const totalIssues = testsPassingLie.totalLies +
|
|
61
|
+
contextAmnesia.totalIncidents +
|
|
62
|
+
instructionDrift.totalDriftCommits +
|
|
63
|
+
(loggingOnly.detected ? 1 : 0);
|
|
64
|
+
// Critical: Tests Passing Lie, high Context Amnesia
|
|
65
|
+
const criticalIssues = testsPassingLie.totalLies + (contextAmnesia.totalIncidents >= 3 ? 1 : 0);
|
|
66
|
+
// Warning: Instruction Drift, Debug Loop
|
|
67
|
+
const warningIssues = (instructionDrift.detected ? 1 : 0) + (loggingOnly.detected ? 1 : 0);
|
|
68
|
+
// Determine overall health
|
|
69
|
+
let overallHealth = 'healthy';
|
|
70
|
+
if (criticalIssues > 0) {
|
|
71
|
+
overallHealth = 'critical';
|
|
72
|
+
}
|
|
73
|
+
else if (warningIssues > 0 || contextAmnesia.totalIncidents > 0) {
|
|
74
|
+
overallHealth = 'warning';
|
|
75
|
+
}
|
|
76
|
+
// Generate recommendations
|
|
77
|
+
const recommendations = generateRecommendations(testsPassingLie, contextAmnesia, instructionDrift, loggingOnly);
|
|
78
|
+
return {
|
|
79
|
+
testsPassingLie,
|
|
80
|
+
contextAmnesia,
|
|
81
|
+
instructionDrift,
|
|
82
|
+
loggingOnly,
|
|
83
|
+
summary: {
|
|
84
|
+
totalIssuesDetected: totalIssues,
|
|
85
|
+
criticalIssues,
|
|
86
|
+
warningIssues,
|
|
87
|
+
overallHealth,
|
|
88
|
+
},
|
|
89
|
+
recommendations,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generate recommendations based on detected patterns.
|
|
94
|
+
*/
|
|
95
|
+
function generateRecommendations(testsPassingLie, contextAmnesia, instructionDrift, loggingOnly) {
|
|
96
|
+
const recommendations = [];
|
|
97
|
+
// Tests Passing Lie recommendations
|
|
98
|
+
if (testsPassingLie.detected) {
|
|
99
|
+
recommendations.push('🤥 STOP: AI claimed success but code needed fixes. Verify builds/tests actually pass before proceeding.');
|
|
100
|
+
recommendations.push('Run `npm test` or your test command after each "fix" commit to verify.');
|
|
101
|
+
}
|
|
102
|
+
// Context Amnesia recommendations
|
|
103
|
+
if (contextAmnesia.detected) {
|
|
104
|
+
if (contextAmnesia.incidents.some((i) => i.type === 'revert')) {
|
|
105
|
+
recommendations.push('🧠 AI is reverting previous work. Check if changes were intentional before accepting reverts.');
|
|
106
|
+
}
|
|
107
|
+
if (contextAmnesia.incidents.some((i) => i.type === 'reimplementation')) {
|
|
108
|
+
recommendations.push('🧠 AI deleted then re-added code. This suggests context loss - try breaking task into smaller pieces.');
|
|
109
|
+
}
|
|
110
|
+
if (contextAmnesia.incidents.some((i) => i.type === 'forgotten_change')) {
|
|
111
|
+
recommendations.push('🧠 Similar fixes being attempted repeatedly. Consider restarting with clear instructions.');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Instruction Drift recommendations
|
|
115
|
+
if (instructionDrift.detected) {
|
|
116
|
+
if (instructionDrift.drifts.some((d) => d.driftType === 'scope_creep')) {
|
|
117
|
+
recommendations.push('🎯 AI changed files outside your stated scope. Be explicit: "ONLY modify src/feature.ts"');
|
|
118
|
+
}
|
|
119
|
+
if (instructionDrift.drifts.some((d) => d.driftType === 'unrequested_refactor')) {
|
|
120
|
+
recommendations.push('🎯 AI did unrequested refactoring. Add: "Do NOT refactor or clean up other code"');
|
|
121
|
+
}
|
|
122
|
+
if (instructionDrift.drifts.some((d) => d.driftType === 'unrequested_improvement')) {
|
|
123
|
+
recommendations.push('🎯 AI made unrequested "improvements". Be explicit about what NOT to do.');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Debug Loop recommendations
|
|
127
|
+
if (loggingOnly.detected) {
|
|
128
|
+
recommendations.push('🔍 Debug loop detected: Multiple logging commits without fixes. STOP and think about the root cause.');
|
|
129
|
+
recommendations.push('Try: 1) Read error messages carefully, 2) Check the docs, 3) Simplify to minimal reproduction');
|
|
130
|
+
if (loggingOnly.consecutiveLoggingCount >= 5) {
|
|
131
|
+
recommendations.push('⚠️ EMERGENCY: 5+ consecutive debug commits. Consider: git stash, take a break, start fresh.');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// General emergency recommendation
|
|
135
|
+
const totalCritical = testsPassingLie.totalLies +
|
|
136
|
+
contextAmnesia.totalIncidents +
|
|
137
|
+
(loggingOnly.consecutiveLoggingCount >= 3 ? 1 : 0);
|
|
138
|
+
if (totalCritical >= 3) {
|
|
139
|
+
recommendations.unshift('🚨 EMERGENCY PROTOCOL: Multiple inner loop failures detected. STOP → git status → backup → start simple');
|
|
140
|
+
}
|
|
141
|
+
return recommendations;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Quick check for inner loop issues (fast, no diff analysis).
|
|
145
|
+
*/
|
|
146
|
+
function quickInnerLoopCheck(events, filesPerCommit, lineStatsPerCommit) {
|
|
147
|
+
const analysis = analyzeInnerLoop(events, filesPerCommit, lineStatsPerCommit);
|
|
148
|
+
const issues = [];
|
|
149
|
+
if (analysis.testsPassingLie.detected)
|
|
150
|
+
issues.push('tests-passing-lie');
|
|
151
|
+
if (analysis.contextAmnesia.detected)
|
|
152
|
+
issues.push('context-amnesia');
|
|
153
|
+
if (analysis.instructionDrift.detected)
|
|
154
|
+
issues.push('instruction-drift');
|
|
155
|
+
if (analysis.loggingOnly.detected)
|
|
156
|
+
issues.push('debug-loop');
|
|
157
|
+
return {
|
|
158
|
+
hasIssues: issues.length > 0,
|
|
159
|
+
issueCount: issues.length,
|
|
160
|
+
topIssue: issues[0] || null,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Format inner loop analysis for terminal output.
|
|
165
|
+
*/
|
|
166
|
+
function formatInnerLoopAnalysis(analysis) {
|
|
167
|
+
const lines = [];
|
|
168
|
+
// Header
|
|
169
|
+
const healthEmoji = analysis.summary.overallHealth === 'critical'
|
|
170
|
+
? '🚨'
|
|
171
|
+
: analysis.summary.overallHealth === 'warning'
|
|
172
|
+
? '⚠️'
|
|
173
|
+
: '✅';
|
|
174
|
+
lines.push(`\n${healthEmoji} Inner Loop Health: ${analysis.summary.overallHealth.toUpperCase()}`);
|
|
175
|
+
lines.push('─'.repeat(50));
|
|
176
|
+
// Individual pattern results
|
|
177
|
+
if (analysis.testsPassingLie.detected) {
|
|
178
|
+
lines.push(`🤥 Tests Passing Lie: ${analysis.testsPassingLie.message}`);
|
|
179
|
+
}
|
|
180
|
+
if (analysis.contextAmnesia.detected) {
|
|
181
|
+
lines.push(`🧠 Context Amnesia: ${analysis.contextAmnesia.message}`);
|
|
182
|
+
}
|
|
183
|
+
if (analysis.instructionDrift.detected) {
|
|
184
|
+
lines.push(`🎯 Instruction Drift: ${analysis.instructionDrift.message}`);
|
|
185
|
+
}
|
|
186
|
+
if (analysis.loggingOnly.detected) {
|
|
187
|
+
lines.push(`🔍 Debug Loop: ${analysis.loggingOnly.message}`);
|
|
188
|
+
}
|
|
189
|
+
// Summary
|
|
190
|
+
if (analysis.summary.totalIssuesDetected > 0) {
|
|
191
|
+
lines.push('');
|
|
192
|
+
lines.push(`Total: ${analysis.summary.totalIssuesDetected} issue${analysis.summary.totalIssuesDetected > 1 ? 's' : ''} ` +
|
|
193
|
+
`(${analysis.summary.criticalIssues} critical, ${analysis.summary.warningIssues} warning)`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
lines.push('No inner loop issues detected.');
|
|
197
|
+
}
|
|
198
|
+
// Recommendations
|
|
199
|
+
if (analysis.recommendations.length > 0) {
|
|
200
|
+
lines.push('');
|
|
201
|
+
lines.push('Recommendations:');
|
|
202
|
+
for (const rec of analysis.recommendations.slice(0, 5)) {
|
|
203
|
+
lines.push(` ${rec}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return lines.join('\n');
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/inner-loop/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instruction Drift Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects when AI starts "improving" things that weren't asked for:
|
|
5
|
+
* - Changes files outside the stated scope
|
|
6
|
+
* - Unrequested refactors
|
|
7
|
+
* - Style changes to unrelated code
|
|
8
|
+
* - "Cleanup" of code that was working fine
|
|
9
|
+
*
|
|
10
|
+
* This is a key "Inner Loop Disaster" in vibe coding.
|
|
11
|
+
*/
|
|
12
|
+
import { TimelineEvent } from '../types';
|
|
13
|
+
import { InstructionDriftResult, SessionScope, InnerLoopConfig } from './types';
|
|
14
|
+
/**
|
|
15
|
+
* Detect instruction drift in commit history.
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectInstructionDrift(events: TimelineEvent[], filesPerCommit: Map<string, string[]>, config?: Partial<InnerLoopConfig>): InstructionDriftResult;
|
|
18
|
+
/**
|
|
19
|
+
* Set session scope for drift detection.
|
|
20
|
+
* Call this at session start to declare intended working files.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createSessionScope(intendedFiles: string[], intendedDirs: string[], taskDescription?: string): SessionScope;
|
|
23
|
+
/**
|
|
24
|
+
* Infer session scope from first N commits.
|
|
25
|
+
* Use this if no explicit scope was declared.
|
|
26
|
+
*/
|
|
27
|
+
export declare function inferSessionScope(events: TimelineEvent[], filesPerCommit: Map<string, string[]>, commitCount?: number): SessionScope;
|
|
28
|
+
//# sourceMappingURL=instruction-drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruction-drift.d.ts","sourceRoot":"","sources":["../../src/inner-loop/instruction-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,sBAAsB,EAEtB,YAAY,EACZ,eAAe,EAEhB,MAAM,SAAS,CAAC;AA6CjB;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,EAAE,EACvB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM,GACpC,sBAAsB,CA8CxB;AAsKD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EAAE,EACvB,YAAY,EAAE,MAAM,EAAE,EACtB,eAAe,CAAC,EAAE,MAAM,GACvB,YAAY,CAOd;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,EAAE,EACvB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,WAAW,GAAE,MAAU,GACtB,YAAY,CAsBd"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Instruction Drift Detector
|
|
4
|
+
*
|
|
5
|
+
* Detects when AI starts "improving" things that weren't asked for:
|
|
6
|
+
* - Changes files outside the stated scope
|
|
7
|
+
* - Unrequested refactors
|
|
8
|
+
* - Style changes to unrelated code
|
|
9
|
+
* - "Cleanup" of code that was working fine
|
|
10
|
+
*
|
|
11
|
+
* This is a key "Inner Loop Disaster" in vibe coding.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.detectInstructionDrift = detectInstructionDrift;
|
|
15
|
+
exports.createSessionScope = createSessionScope;
|
|
16
|
+
exports.inferSessionScope = inferSessionScope;
|
|
17
|
+
const types_1 = require("./types");
|
|
18
|
+
// Patterns that indicate unrequested changes
|
|
19
|
+
const DRIFT_PATTERNS = {
|
|
20
|
+
unrequested_refactor: [
|
|
21
|
+
/\brefactor/i,
|
|
22
|
+
/\bcleanup\b/i,
|
|
23
|
+
/\bclean\s*up\b/i,
|
|
24
|
+
/\breorganize/i,
|
|
25
|
+
/\brestructure/i,
|
|
26
|
+
/\bsimplify/i,
|
|
27
|
+
/\bimprove\s+code/i,
|
|
28
|
+
],
|
|
29
|
+
unrequested_improvement: [
|
|
30
|
+
/\bimprove/i,
|
|
31
|
+
/\benhance/i,
|
|
32
|
+
/\boptimize/i,
|
|
33
|
+
/\bbetter/i,
|
|
34
|
+
/\bupgrade/i,
|
|
35
|
+
/\bmodernize/i,
|
|
36
|
+
],
|
|
37
|
+
style_change: [
|
|
38
|
+
/\bformat/i,
|
|
39
|
+
/\blint/i,
|
|
40
|
+
/\bstyle\b/i,
|
|
41
|
+
/\bprettier/i,
|
|
42
|
+
/\bindent/i,
|
|
43
|
+
/\bwhitespace/i,
|
|
44
|
+
/\bspacing/i,
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
// Files that are often legitimately touched as side effects
|
|
48
|
+
const ALLOWED_SIDE_EFFECT_FILES = [
|
|
49
|
+
/package\.json$/,
|
|
50
|
+
/package-lock\.json$/,
|
|
51
|
+
/yarn\.lock$/,
|
|
52
|
+
/pnpm-lock\.yaml$/,
|
|
53
|
+
/tsconfig\.json$/,
|
|
54
|
+
/\.gitignore$/,
|
|
55
|
+
/\.env\.example$/,
|
|
56
|
+
/Cargo\.lock$/,
|
|
57
|
+
/go\.sum$/,
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Detect instruction drift in commit history.
|
|
61
|
+
*/
|
|
62
|
+
function detectInstructionDrift(events, filesPerCommit, config = {}) {
|
|
63
|
+
const cfg = { ...types_1.DEFAULT_INNER_LOOP_CONFIG, ...config };
|
|
64
|
+
const drifts = [];
|
|
65
|
+
if (events.length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
detected: false,
|
|
68
|
+
drifts: [],
|
|
69
|
+
totalDriftCommits: 0,
|
|
70
|
+
totalUnauthorizedFiles: 0,
|
|
71
|
+
message: 'No commits to analyze',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// If we have explicit session scope, use it
|
|
75
|
+
if (cfg.sessionScope) {
|
|
76
|
+
const scopeDrifts = detectScopeCreep(events, filesPerCommit, cfg.sessionScope, cfg);
|
|
77
|
+
drifts.push(...scopeDrifts);
|
|
78
|
+
}
|
|
79
|
+
// Always check for unrequested refactors/improvements
|
|
80
|
+
const refactorDrifts = detectUnrequestedRefactors(events, filesPerCommit, cfg);
|
|
81
|
+
drifts.push(...refactorDrifts);
|
|
82
|
+
// Detect sudden file scope explosion
|
|
83
|
+
const explosionDrifts = detectScopeExplosion(events, filesPerCommit, cfg);
|
|
84
|
+
drifts.push(...explosionDrifts);
|
|
85
|
+
// Calculate totals
|
|
86
|
+
const totalDriftCommits = new Set(drifts.map((d) => d.commitHash)).size;
|
|
87
|
+
const totalUnauthorizedFiles = new Set(drifts.flatMap((d) => d.unauthorizedFiles)).size;
|
|
88
|
+
const detected = drifts.length > 0;
|
|
89
|
+
let message = '';
|
|
90
|
+
if (detected) {
|
|
91
|
+
const driftTypes = new Set(drifts.map((d) => d.driftType));
|
|
92
|
+
message = `🎯 Instruction drift detected: ${totalDriftCommits} commit${totalDriftCommits > 1 ? 's' : ''} went outside scope (${Array.from(driftTypes).join(', ')})`;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
detected,
|
|
96
|
+
drifts: drifts.slice(0, 10),
|
|
97
|
+
totalDriftCommits,
|
|
98
|
+
totalUnauthorizedFiles,
|
|
99
|
+
message,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Detect commits that touch files outside the declared session scope.
|
|
104
|
+
*/
|
|
105
|
+
function detectScopeCreep(events, filesPerCommit, scope, config) {
|
|
106
|
+
const drifts = [];
|
|
107
|
+
// Build set of authorized files/patterns
|
|
108
|
+
const authorizedFiles = new Set(scope.intendedFiles);
|
|
109
|
+
const authorizedDirs = scope.intendedDirs;
|
|
110
|
+
for (const event of events) {
|
|
111
|
+
const files = filesPerCommit.get(event.hash) || [];
|
|
112
|
+
const unauthorizedFiles = [];
|
|
113
|
+
for (const file of files) {
|
|
114
|
+
// Skip allowed side-effect files
|
|
115
|
+
if (isAllowedSideEffect(file, config))
|
|
116
|
+
continue;
|
|
117
|
+
// Check if file is authorized
|
|
118
|
+
const isAuthorized = authorizedFiles.has(file) ||
|
|
119
|
+
authorizedDirs.some((dir) => file.startsWith(dir + '/') || file === dir);
|
|
120
|
+
if (!isAuthorized) {
|
|
121
|
+
unauthorizedFiles.push(file);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (unauthorizedFiles.length > 0) {
|
|
125
|
+
drifts.push({
|
|
126
|
+
commitHash: event.hash,
|
|
127
|
+
commitMessage: event.subject,
|
|
128
|
+
timestamp: event.timestamp,
|
|
129
|
+
driftType: 'scope_creep',
|
|
130
|
+
unauthorizedFiles,
|
|
131
|
+
authorizedScope: [...scope.intendedFiles, ...scope.intendedDirs],
|
|
132
|
+
description: `Changed ${unauthorizedFiles.length} file${unauthorizedFiles.length > 1 ? 's' : ''} outside declared scope`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return drifts;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Detect commits that do unrequested refactoring/improvements.
|
|
140
|
+
*/
|
|
141
|
+
function detectUnrequestedRefactors(events, filesPerCommit, config) {
|
|
142
|
+
const drifts = [];
|
|
143
|
+
// Build "normal" working set from first few commits
|
|
144
|
+
const workingSet = new Set();
|
|
145
|
+
const firstCommitCount = Math.min(3, events.length);
|
|
146
|
+
for (let i = 0; i < firstCommitCount; i++) {
|
|
147
|
+
const files = filesPerCommit.get(events[i].hash) || [];
|
|
148
|
+
files.forEach((f) => workingSet.add(f));
|
|
149
|
+
}
|
|
150
|
+
for (const event of events) {
|
|
151
|
+
const message = event.subject;
|
|
152
|
+
// Check for refactor/improvement patterns
|
|
153
|
+
for (const [driftType, patterns] of Object.entries(DRIFT_PATTERNS)) {
|
|
154
|
+
const matchesPattern = patterns.some((p) => p.test(message));
|
|
155
|
+
if (matchesPattern) {
|
|
156
|
+
const files = filesPerCommit.get(event.hash) || [];
|
|
157
|
+
// Check if this touches files outside the working set
|
|
158
|
+
const outsideFiles = files.filter((f) => !workingSet.has(f) && !isAllowedSideEffect(f, config));
|
|
159
|
+
if (outsideFiles.length > 0 || files.length > 5) {
|
|
160
|
+
drifts.push({
|
|
161
|
+
commitHash: event.hash,
|
|
162
|
+
commitMessage: message,
|
|
163
|
+
timestamp: event.timestamp,
|
|
164
|
+
driftType: driftType,
|
|
165
|
+
unauthorizedFiles: outsideFiles,
|
|
166
|
+
authorizedScope: Array.from(workingSet),
|
|
167
|
+
description: `${driftType.replace(/_/g, ' ')}: ${message.substring(0, 50)}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return drifts;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Detect sudden explosion in file scope (touching many more files than normal).
|
|
177
|
+
*/
|
|
178
|
+
function detectScopeExplosion(events, filesPerCommit, config) {
|
|
179
|
+
const drifts = [];
|
|
180
|
+
if (events.length < 3)
|
|
181
|
+
return drifts;
|
|
182
|
+
// Calculate average files per commit
|
|
183
|
+
let totalFiles = 0;
|
|
184
|
+
const fileCounts = [];
|
|
185
|
+
for (const event of events) {
|
|
186
|
+
const files = filesPerCommit.get(event.hash) || [];
|
|
187
|
+
const nonSideEffectFiles = files.filter((f) => !isAllowedSideEffect(f, config));
|
|
188
|
+
fileCounts.push(nonSideEffectFiles.length);
|
|
189
|
+
totalFiles += nonSideEffectFiles.length;
|
|
190
|
+
}
|
|
191
|
+
const avgFiles = totalFiles / events.length;
|
|
192
|
+
const threshold = Math.max(avgFiles * 3, 10); // 3x average or at least 10
|
|
193
|
+
// Find commits that touch way more files than average
|
|
194
|
+
for (let i = 0; i < events.length; i++) {
|
|
195
|
+
const event = events[i];
|
|
196
|
+
const fileCount = fileCounts[i];
|
|
197
|
+
if (fileCount > threshold) {
|
|
198
|
+
const files = filesPerCommit.get(event.hash) || [];
|
|
199
|
+
const nonSideEffectFiles = files.filter((f) => !isAllowedSideEffect(f, config));
|
|
200
|
+
drifts.push({
|
|
201
|
+
commitHash: event.hash,
|
|
202
|
+
commitMessage: event.subject,
|
|
203
|
+
timestamp: event.timestamp,
|
|
204
|
+
driftType: 'scope_creep',
|
|
205
|
+
unauthorizedFiles: nonSideEffectFiles,
|
|
206
|
+
authorizedScope: [],
|
|
207
|
+
description: `Touched ${fileCount} files (avg: ${Math.round(avgFiles)}) - possible scope explosion`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return drifts;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if a file is an allowed side-effect (like package.json).
|
|
215
|
+
*/
|
|
216
|
+
function isAllowedSideEffect(file, config) {
|
|
217
|
+
// Check config allowed files
|
|
218
|
+
if (config.allowedDriftFiles.some((f) => file.endsWith(f))) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
// Check default patterns
|
|
222
|
+
return ALLOWED_SIDE_EFFECT_FILES.some((p) => p.test(file));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Set session scope for drift detection.
|
|
226
|
+
* Call this at session start to declare intended working files.
|
|
227
|
+
*/
|
|
228
|
+
function createSessionScope(intendedFiles, intendedDirs, taskDescription) {
|
|
229
|
+
return {
|
|
230
|
+
intendedFiles,
|
|
231
|
+
intendedDirs,
|
|
232
|
+
taskDescription,
|
|
233
|
+
startTime: new Date(),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Infer session scope from first N commits.
|
|
238
|
+
* Use this if no explicit scope was declared.
|
|
239
|
+
*/
|
|
240
|
+
function inferSessionScope(events, filesPerCommit, commitCount = 3) {
|
|
241
|
+
const files = new Set();
|
|
242
|
+
const dirs = new Set();
|
|
243
|
+
const eventsToUse = events.slice(0, commitCount);
|
|
244
|
+
for (const event of eventsToUse) {
|
|
245
|
+
const commitFiles = filesPerCommit.get(event.hash) || [];
|
|
246
|
+
for (const file of commitFiles) {
|
|
247
|
+
files.add(file);
|
|
248
|
+
// Extract directory
|
|
249
|
+
const dir = file.split('/').slice(0, -1).join('/');
|
|
250
|
+
if (dir)
|
|
251
|
+
dirs.add(dir);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
intendedFiles: Array.from(files),
|
|
256
|
+
intendedDirs: Array.from(dirs),
|
|
257
|
+
startTime: events[0]?.timestamp || new Date(),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=instruction-drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instruction-drift.js","sourceRoot":"","sources":["../../src/inner-loop/instruction-drift.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AAyDH,wDAkDC;AA0KD,gDAWC;AAMD,8CA0BC;AA7TD,mCAMiB;AAEjB,6CAA6C;AAC7C,MAAM,cAAc,GAAG;IACrB,oBAAoB,EAAE;QACpB,aAAa;QACb,cAAc;QACd,iBAAiB;QACjB,eAAe;QACf,gBAAgB;QAChB,aAAa;QACb,mBAAmB;KACpB;IACD,uBAAuB,EAAE;QACvB,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,WAAW;QACX,YAAY;QACZ,cAAc;KACf;IACD,YAAY,EAAE;QACZ,WAAW;QACX,SAAS;QACT,YAAY;QACZ,aAAa;QACb,WAAW;QACX,eAAe;QACf,YAAY;KACb;CACF,CAAC;AAEF,4DAA4D;AAC5D,MAAM,yBAAyB,GAAG;IAChC,gBAAgB;IAChB,qBAAqB;IACrB,aAAa;IACb,kBAAkB;IAClB,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,cAAc;IACd,UAAU;CACX,CAAC;AAEF;;GAEG;AACH,SAAgB,sBAAsB,CACpC,MAAuB,EACvB,cAAqC,EACrC,SAAmC,EAAE;IAErC,MAAM,GAAG,GAAG,EAAE,GAAG,iCAAyB,EAAE,GAAG,MAAM,EAAE,CAAC;IACxD,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,EAAE;YACV,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,CAAC;YACzB,OAAO,EAAE,uBAAuB;SACjC,CAAC;IACJ,CAAC;IAED,4CAA4C;IAC5C,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QACpF,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;IAC9B,CAAC;IAED,sDAAsD;IACtD,MAAM,cAAc,GAAG,0BAA0B,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IAC/E,MAAM,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;IAE/B,qCAAqC;IACrC,MAAM,eAAe,GAAG,oBAAoB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;IAC1E,MAAM,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,CAAC;IAEhC,mBAAmB;IACnB,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;IAExF,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,kCAAkC,iBAAiB,UAAU,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,wBAAwB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACtK,CAAC;IAED,OAAO;QACL,QAAQ;QACR,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC3B,iBAAiB;QACjB,sBAAsB;QACtB,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACvB,MAAuB,EACvB,cAAqC,EACrC,KAAmB,EACnB,MAAuB;IAEvB,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,yCAAyC;IACzC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,KAAK,CAAC,YAAY,CAAC;IAE1C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,iBAAiB,GAAa,EAAE,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,iCAAiC;YACjC,IAAI,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC;gBAAE,SAAS;YAEhD,8BAA8B;YAC9B,MAAM,YAAY,GAChB,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;gBACzB,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC;YAE3E,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC;gBACV,UAAU,EAAE,KAAK,CAAC,IAAI;gBACtB,aAAa,EAAE,KAAK,CAAC,OAAO;gBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,aAAa;gBACxB,iBAAiB;gBACjB,eAAe,EAAE,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,GAAG,KAAK,CAAC,YAAY,CAAC;gBAChE,WAAW,EAAE,WAAW,iBAAiB,CAAC,MAAM,QAAQ,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,yBAAyB;aACzH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,0BAA0B,CACjC,MAAuB,EACvB,cAAqC,EACrC,MAAuB;IAEvB,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,oDAAoD;IACpD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAEpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAE9B,0CAA0C;QAC1C,KAAK,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YACnE,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAE7D,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAEnD,sDAAsD;gBACtD,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,CAC7D,CAAC;gBAEF,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChD,MAAM,CAAC,IAAI,CAAC;wBACV,UAAU,EAAE,KAAK,CAAC,IAAI;wBACtB,aAAa,EAAE,OAAO;wBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;wBAC1B,SAAS,EAAE,SAA0C;wBACrD,iBAAiB,EAAE,YAAY;wBAC/B,eAAe,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;wBACvC,WAAW,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;qBAC5E,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAC3B,MAAuB,EACvB,cAAqC,EACrC,MAAuB;IAEvB,MAAM,MAAM,GAAuB,EAAE,CAAC;IAEtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAErC,qCAAqC;IACrC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAChF,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC3C,UAAU,IAAI,kBAAkB,CAAC,MAAM,CAAC;IAC1C,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,4BAA4B;IAE1E,sDAAsD;IACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAEhC,IAAI,SAAS,GAAG,SAAS,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACnD,MAAM,kBAAkB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAEhF,MAAM,CAAC,IAAI,CAAC;gBACV,UAAU,EAAE,KAAK,CAAC,IAAI;gBACtB,aAAa,EAAE,KAAK,CAAC,OAAO;gBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,SAAS,EAAE,aAAa;gBACxB,iBAAiB,EAAE,kBAAkB;gBACrC,eAAe,EAAE,EAAE;gBACnB,WAAW,EAAE,WAAW,SAAS,gBAAgB,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,8BAA8B;aACpG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,IAAY,EAAE,MAAuB;IAChE,6BAA6B;IAC7B,IAAI,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yBAAyB;IACzB,OAAO,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,SAAgB,kBAAkB,CAChC,aAAuB,EACvB,YAAsB,EACtB,eAAwB;IAExB,OAAO;QACL,aAAa;QACb,YAAY;QACZ,eAAe;QACf,SAAS,EAAE,IAAI,IAAI,EAAE;KACtB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAC/B,MAAuB,EACvB,cAAqC,EACrC,cAAsB,CAAC;IAEvB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAEjD,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACzD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAEhB,oBAAoB;YACpB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,GAAG;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO;QACL,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9B,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging-Only Commit Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects "Debug Loop Spiral" pattern where AI adds logging/print statements
|
|
5
|
+
* instead of actually fixing the root cause. Signs of this:
|
|
6
|
+
*
|
|
7
|
+
* - Commits that only add console.log/print statements
|
|
8
|
+
* - Multiple consecutive debugging commits without substantive changes
|
|
9
|
+
* - Increasing logging density without resolution
|
|
10
|
+
*
|
|
11
|
+
* This is a key "Inner Loop Disaster" in vibe coding.
|
|
12
|
+
*/
|
|
13
|
+
import { TimelineEvent } from '../types';
|
|
14
|
+
import { LoggingOnlyResult, LoggingOnlyCommit, InnerLoopConfig } from './types';
|
|
15
|
+
/**
|
|
16
|
+
* Detect logging-only commits from timeline events.
|
|
17
|
+
* This is a lightweight version that infers from commit messages.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectLoggingOnlyCommits(events: TimelineEvent[], config?: Partial<InnerLoopConfig>): LoggingOnlyResult;
|
|
20
|
+
/**
|
|
21
|
+
* Analyze a commit diff to count logging statements.
|
|
22
|
+
* This is the more accurate version that actually looks at the diff.
|
|
23
|
+
*/
|
|
24
|
+
export declare function analyzeCommitForLogging(repoPath: string, commitHash: string, config?: Partial<InnerLoopConfig>): Promise<LoggingOnlyCommit | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Analyze multiple commits for logging patterns.
|
|
27
|
+
* Returns detailed analysis with diff inspection.
|
|
28
|
+
*/
|
|
29
|
+
export declare function analyzeCommitsForLogging(repoPath: string, events: TimelineEvent[], config?: Partial<InnerLoopConfig>): Promise<LoggingOnlyResult>;
|
|
30
|
+
//# sourceMappingURL=logging-only.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logging-only.d.ts","sourceRoot":"","sources":["../../src/inner-loop/logging-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EAEhB,MAAM,SAAS,CAAC;AAmEjB;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,aAAa,EAAE,EACvB,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM,GACpC,iBAAiB,CAgFnB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM,GACpC,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmDnC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,EAAE,EACvB,MAAM,GAAE,OAAO,CAAC,eAAe,CAAM,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
|