@boshu2/vibe-check 1.4.0 → 1.6.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/bundles/insight-mining-dashboard-research-2025-11-30.md +400 -0
- package/.agents/bundles/storage-enhancement-research-2025-11-30.md +292 -0
- package/.agents/bundles/timeline-feature-research-complete-2025-11-30.md +301 -0
- package/.agents/plans/insight-dashboard-plan-2025-11-30.md +1130 -0
- package/.agents/plans/json-storage-enhancement-plan.md +717 -0
- package/.agents/plans/storage-hardening-and-cache-plan.md +592 -0
- package/.agents/plans/test-coverage-gaps-plan.md +1117 -0
- package/.agents/plans/timeline-feature-plan.md +193 -0
- package/.agents/plans/vibe_timeline_research_findings.md +553 -0
- package/.claude/settings.local.json +1 -0
- package/.vibe-check/.gitignore +6 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +24 -0
- package/CONTRIBUTING.md +227 -0
- package/README.md +200 -143
- package/claude-progress.json +200 -12
- package/claude-progress.txt +310 -0
- package/dashboard/app.js +75 -2
- package/dashboard/dashboard-data.json +653 -0
- package/dashboard/index.html +13 -0
- package/dashboard/styles.css +61 -0
- package/dist/analysis/cross-session-analysis.d.ts +68 -0
- package/dist/analysis/cross-session-analysis.d.ts.map +1 -0
- package/dist/analysis/cross-session-analysis.js +174 -0
- package/dist/analysis/cross-session-analysis.js.map +1 -0
- package/dist/analysis/index.d.ts +2 -0
- package/dist/analysis/index.d.ts.map +1 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/cli.js +10 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +2 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +105 -2
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +168 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/dashboard.d.ts +8 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +109 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +8 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +140 -31
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/timeline.d.ts +14 -0
- package/dist/commands/timeline.d.ts.map +1 -0
- package/dist/commands/timeline.js +462 -0
- package/dist/commands/timeline.js.map +1 -0
- package/dist/gamification/badges.d.ts +29 -0
- package/dist/gamification/badges.d.ts.map +1 -0
- package/dist/gamification/badges.js +114 -0
- package/dist/gamification/badges.js.map +1 -0
- package/dist/gamification/challenges.d.ts +42 -0
- package/dist/gamification/challenges.d.ts.map +1 -0
- package/dist/gamification/challenges.js +184 -0
- package/dist/gamification/challenges.js.map +1 -0
- package/dist/gamification/hall-of-fame.d.ts +17 -0
- package/dist/gamification/hall-of-fame.d.ts.map +1 -0
- package/dist/gamification/hall-of-fame.js +64 -0
- package/dist/gamification/hall-of-fame.js.map +1 -0
- package/dist/gamification/leaderboards.d.ts +49 -0
- package/dist/gamification/leaderboards.d.ts.map +1 -0
- package/dist/gamification/leaderboards.js +179 -0
- package/dist/gamification/leaderboards.js.map +1 -0
- package/dist/gamification/share.d.ts +29 -0
- package/dist/gamification/share.d.ts.map +1 -0
- package/dist/gamification/share.js +57 -0
- package/dist/gamification/share.js.map +1 -0
- package/dist/gamification/stats.d.ts +34 -0
- package/dist/gamification/stats.d.ts.map +1 -0
- package/dist/gamification/stats.js +91 -0
- package/dist/gamification/stats.js.map +1 -0
- package/dist/gamification/streaks.d.ts +9 -1
- package/dist/gamification/streaks.d.ts.map +1 -1
- package/dist/gamification/streaks.js +37 -4
- package/dist/gamification/streaks.js.map +1 -1
- package/dist/gamification/types.d.ts +35 -0
- package/dist/gamification/types.d.ts.map +1 -1
- package/dist/gamification/types.js +11 -3
- package/dist/gamification/types.js.map +1 -1
- package/dist/gamification/xp.d.ts +6 -2
- package/dist/gamification/xp.d.ts.map +1 -1
- package/dist/gamification/xp.js +27 -5
- package/dist/gamification/xp.js.map +1 -1
- package/dist/git.d.ts +24 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +94 -0
- package/dist/git.js.map +1 -1
- package/dist/insights/generators.d.ts +44 -0
- package/dist/insights/generators.d.ts.map +1 -0
- package/dist/insights/generators.js +289 -0
- package/dist/insights/generators.js.map +1 -0
- package/dist/insights/index.d.ts +16 -0
- package/dist/insights/index.d.ts.map +1 -0
- package/dist/insights/index.js +171 -0
- package/dist/insights/index.js.map +1 -0
- package/dist/insights/types.d.ts +93 -0
- package/dist/insights/types.d.ts.map +1 -0
- package/dist/insights/types.js +6 -0
- package/dist/insights/types.js.map +1 -0
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/output/terminal.js +39 -0
- package/dist/output/terminal.js.map +1 -1
- package/dist/output/timeline-html.d.ts +6 -0
- package/dist/output/timeline-html.d.ts.map +1 -0
- package/dist/output/timeline-html.js +389 -0
- package/dist/output/timeline-html.js.map +1 -0
- package/dist/output/timeline-markdown.d.ts +6 -0
- package/dist/output/timeline-markdown.d.ts.map +1 -0
- package/dist/output/timeline-markdown.js +167 -0
- package/dist/output/timeline-markdown.js.map +1 -0
- package/dist/output/timeline.d.ts +9 -0
- package/dist/output/timeline.d.ts.map +1 -0
- package/dist/output/timeline.js +318 -0
- package/dist/output/timeline.js.map +1 -0
- package/dist/patterns/detour.d.ts +32 -0
- package/dist/patterns/detour.d.ts.map +1 -0
- package/dist/patterns/detour.js +137 -0
- package/dist/patterns/detour.js.map +1 -0
- package/dist/patterns/flow-state.d.ts +16 -0
- package/dist/patterns/flow-state.d.ts.map +1 -0
- package/dist/patterns/flow-state.js +40 -0
- package/dist/patterns/flow-state.js.map +1 -0
- package/dist/patterns/index.d.ts +8 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +22 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/intervention-effectiveness.d.ts +42 -0
- package/dist/patterns/intervention-effectiveness.d.ts.map +1 -0
- package/dist/patterns/intervention-effectiveness.js +196 -0
- package/dist/patterns/intervention-effectiveness.js.map +1 -0
- package/dist/patterns/late-night.d.ts +30 -0
- package/dist/patterns/late-night.d.ts.map +1 -0
- package/dist/patterns/late-night.js +141 -0
- package/dist/patterns/late-night.js.map +1 -0
- package/dist/patterns/post-delete-sprint.d.ts +28 -0
- package/dist/patterns/post-delete-sprint.d.ts.map +1 -0
- package/dist/patterns/post-delete-sprint.js +85 -0
- package/dist/patterns/post-delete-sprint.js.map +1 -0
- package/dist/patterns/spiral-regression.d.ts +49 -0
- package/dist/patterns/spiral-regression.d.ts.map +1 -0
- package/dist/patterns/spiral-regression.js +219 -0
- package/dist/patterns/spiral-regression.js.map +1 -0
- package/dist/patterns/thrashing.d.ts +25 -0
- package/dist/patterns/thrashing.d.ts.map +1 -0
- package/dist/patterns/thrashing.js +111 -0
- package/dist/patterns/thrashing.js.map +1 -0
- package/dist/storage/atomic.d.ts +40 -0
- package/dist/storage/atomic.d.ts.map +1 -0
- package/dist/storage/atomic.js +155 -0
- package/dist/storage/atomic.js.map +1 -0
- package/dist/storage/commit-log.d.ts +35 -0
- package/dist/storage/commit-log.d.ts.map +1 -0
- package/dist/storage/commit-log.js +128 -0
- package/dist/storage/commit-log.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +33 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/schema.d.ts +32 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +37 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/timeline-store.d.ts +117 -0
- package/dist/storage/timeline-store.d.ts.map +1 -0
- package/dist/storage/timeline-store.js +438 -0
- package/dist/storage/timeline-store.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +458 -0
- package/docs/DATA-ARCHITECTURE.md +565 -0
- package/docs/GAMIFICATION.md +564 -0
- package/docs/JSON-STORAGE-PATTERNS.md +512 -0
- package/docs/METRICS-EXPLAINED.md +394 -0
- package/docs/UNIFIED-ECOSYSTEM.md +560 -0
- package/docs/VIBE-ECOSYSTEM.md +406 -0
- package/feature-list.json +103 -1
- package/package.json +2 -1
- package/vitest.config.ts +1 -5
- package/.vibe-check/calibration.json +0 -38
- package/.vibe-check/latest.json +0 -114
- package/.vibe-check/sessions.json +0 -34
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Spiral Regression Detection
|
|
4
|
+
*
|
|
5
|
+
* Detects when spiral patterns re-emerge after a period of improvement.
|
|
6
|
+
* Uses weekly trend analysis to identify regressions.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.detectRegressions = detectRegressions;
|
|
10
|
+
exports.analyzeRecoveryTimeTrend = analyzeRecoveryTimeTrend;
|
|
11
|
+
exports.getAllRecoveryTrends = getAllRecoveryTrends;
|
|
12
|
+
/**
|
|
13
|
+
* Detect spiral regressions from trend data
|
|
14
|
+
*/
|
|
15
|
+
function detectRegressions(trends) {
|
|
16
|
+
const alerts = [];
|
|
17
|
+
if (trends.weekly.length < 3) {
|
|
18
|
+
return {
|
|
19
|
+
hasRegression: false,
|
|
20
|
+
alerts: [],
|
|
21
|
+
summary: 'Not enough data for regression detection (need 3+ weeks)',
|
|
22
|
+
improvementStreak: 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Check spiral rate regression
|
|
26
|
+
const spiralRegression = detectSpiralRateRegression(trends.weekly);
|
|
27
|
+
if (spiralRegression) {
|
|
28
|
+
alerts.push(spiralRegression);
|
|
29
|
+
}
|
|
30
|
+
// Check flow state regression
|
|
31
|
+
const flowRegression = detectFlowStateRegression(trends.weekly);
|
|
32
|
+
if (flowRegression) {
|
|
33
|
+
alerts.push(flowRegression);
|
|
34
|
+
}
|
|
35
|
+
// Calculate improvement streak (weeks of improvement before current)
|
|
36
|
+
const improvementStreak = calculateImprovementStreak(trends.weekly);
|
|
37
|
+
// Generate summary
|
|
38
|
+
let summary;
|
|
39
|
+
if (alerts.length === 0) {
|
|
40
|
+
if (improvementStreak > 0) {
|
|
41
|
+
summary = `${improvementStreak}-week improvement streak. Keep it up!`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
summary = 'No regressions detected';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const critical = alerts.filter(a => a.severity === 'critical').length;
|
|
49
|
+
if (critical > 0) {
|
|
50
|
+
summary = `${critical} critical regression(s) detected - action needed`;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
summary = `${alerts.length} warning(s) - monitor closely`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
hasRegression: alerts.length > 0,
|
|
58
|
+
alerts,
|
|
59
|
+
summary,
|
|
60
|
+
improvementStreak,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Detect if spiral rate has regressed after improvement
|
|
65
|
+
*/
|
|
66
|
+
function detectSpiralRateRegression(weeks) {
|
|
67
|
+
if (weeks.length < 3)
|
|
68
|
+
return null;
|
|
69
|
+
// Get recent weeks (newest first in our data)
|
|
70
|
+
const recent = weeks.slice(-3);
|
|
71
|
+
const [oldest, middle, newest] = recent;
|
|
72
|
+
// Calculate spiral rates (spirals per session)
|
|
73
|
+
const oldestRate = oldest.sessions > 0 ? oldest.spirals / oldest.sessions : 0;
|
|
74
|
+
const middleRate = middle.sessions > 0 ? middle.spirals / middle.sessions : 0;
|
|
75
|
+
const newestRate = newest.sessions > 0 ? newest.spirals / newest.sessions : 0;
|
|
76
|
+
// Pattern: was improving (rate went down), then got worse
|
|
77
|
+
const wasImproving = middleRate < oldestRate;
|
|
78
|
+
const hasRegressed = newestRate > middleRate * 1.5; // 50% increase threshold
|
|
79
|
+
if (wasImproving && hasRegressed && newestRate > 0.1) {
|
|
80
|
+
const changePercent = Math.round((newestRate - middleRate) / Math.max(middleRate, 0.01) * 100);
|
|
81
|
+
return {
|
|
82
|
+
type: 'spiral_regression',
|
|
83
|
+
severity: changePercent > 100 ? 'critical' : 'warning',
|
|
84
|
+
message: `Spiral rate increased ${changePercent}% after previous improvement`,
|
|
85
|
+
metric: 'spiral_rate',
|
|
86
|
+
currentValue: Math.round(newestRate * 100) / 100,
|
|
87
|
+
baselineValue: Math.round(middleRate * 100) / 100,
|
|
88
|
+
changePercent,
|
|
89
|
+
period: `${newest.weekStart}`,
|
|
90
|
+
recommendation: 'Review recent commits for unfamiliar patterns. Consider adding tracer tests.',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Detect if flow state frequency has regressed
|
|
97
|
+
*/
|
|
98
|
+
function detectFlowStateRegression(weeks) {
|
|
99
|
+
if (weeks.length < 3)
|
|
100
|
+
return null;
|
|
101
|
+
const recent = weeks.slice(-3);
|
|
102
|
+
const [oldest, middle, newest] = recent;
|
|
103
|
+
// Calculate flow state rates
|
|
104
|
+
const oldestRate = oldest.sessions > 0 ? oldest.flowStates / oldest.sessions : 0;
|
|
105
|
+
const middleRate = middle.sessions > 0 ? middle.flowStates / middle.sessions : 0;
|
|
106
|
+
const newestRate = newest.sessions > 0 ? newest.flowStates / newest.sessions : 0;
|
|
107
|
+
// Pattern: had good flow (high rate), then dropped significantly
|
|
108
|
+
const hadGoodFlow = middleRate >= 0.3; // At least 30% flow sessions
|
|
109
|
+
const hasRegressed = newestRate < middleRate * 0.5 && middleRate > 0; // 50% drop
|
|
110
|
+
if (hadGoodFlow && hasRegressed) {
|
|
111
|
+
const changePercent = Math.round((middleRate - newestRate) / middleRate * 100);
|
|
112
|
+
return {
|
|
113
|
+
type: 'spiral_regression',
|
|
114
|
+
severity: 'warning',
|
|
115
|
+
message: `Flow state frequency dropped ${changePercent}% this week`,
|
|
116
|
+
metric: 'flow_rate',
|
|
117
|
+
currentValue: Math.round(newestRate * 100),
|
|
118
|
+
baselineValue: Math.round(middleRate * 100),
|
|
119
|
+
changePercent: -changePercent,
|
|
120
|
+
period: `${newest.weekStart}`,
|
|
121
|
+
recommendation: 'Protect deep work time. Check for interruption patterns.',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Calculate consecutive weeks of improvement
|
|
128
|
+
*/
|
|
129
|
+
function calculateImprovementStreak(weeks) {
|
|
130
|
+
if (weeks.length < 2)
|
|
131
|
+
return 0;
|
|
132
|
+
let streak = 0;
|
|
133
|
+
// Work backwards from second-to-last week
|
|
134
|
+
for (let i = weeks.length - 2; i >= 0; i--) {
|
|
135
|
+
const current = weeks[i];
|
|
136
|
+
const next = weeks[i + 1];
|
|
137
|
+
const currentRate = current.sessions > 0 ? current.spirals / current.sessions : 0;
|
|
138
|
+
const nextRate = next.sessions > 0 ? next.spirals / next.sessions : 0;
|
|
139
|
+
// Improvement = lower spiral rate
|
|
140
|
+
if (nextRate <= currentRate) {
|
|
141
|
+
streak++;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return streak;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Analyze recovery time trends for a specific pattern
|
|
151
|
+
*/
|
|
152
|
+
function analyzeRecoveryTimeTrend(sessions, component) {
|
|
153
|
+
// Filter sessions with spirals in this component
|
|
154
|
+
const relevantSessions = sessions.filter(s => s.spiralComponents.includes(component));
|
|
155
|
+
if (relevantSessions.length < 2) {
|
|
156
|
+
return {
|
|
157
|
+
improving: false,
|
|
158
|
+
trend: 'stable',
|
|
159
|
+
avgRecoveryTime: 0,
|
|
160
|
+
recentRecoveryTime: 0,
|
|
161
|
+
samples: relevantSessions.length,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Sort by date
|
|
165
|
+
const sorted = [...relevantSessions].sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
|
166
|
+
// Calculate average duration (proxy for recovery time)
|
|
167
|
+
const allDurations = sorted.map(s => s.duration);
|
|
168
|
+
const avgRecoveryTime = allDurations.reduce((a, b) => a + b, 0) / allDurations.length;
|
|
169
|
+
// Recent average (last 3)
|
|
170
|
+
const recentDurations = allDurations.slice(-3);
|
|
171
|
+
const recentRecoveryTime = recentDurations.reduce((a, b) => a + b, 0) / recentDurations.length;
|
|
172
|
+
// Determine trend
|
|
173
|
+
const changePercent = (recentRecoveryTime - avgRecoveryTime) / avgRecoveryTime * 100;
|
|
174
|
+
let trend;
|
|
175
|
+
if (changePercent < -15) {
|
|
176
|
+
trend = 'improving';
|
|
177
|
+
}
|
|
178
|
+
else if (changePercent > 15) {
|
|
179
|
+
trend = 'worsening';
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
trend = 'stable';
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
improving: trend === 'improving',
|
|
186
|
+
trend,
|
|
187
|
+
avgRecoveryTime: Math.round(avgRecoveryTime),
|
|
188
|
+
recentRecoveryTime: Math.round(recentRecoveryTime),
|
|
189
|
+
samples: relevantSessions.length,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get all component recovery trends
|
|
194
|
+
*/
|
|
195
|
+
function getAllRecoveryTrends(sessions) {
|
|
196
|
+
// Get unique components with spirals
|
|
197
|
+
const components = new Set();
|
|
198
|
+
for (const session of sessions) {
|
|
199
|
+
for (const comp of session.spiralComponents) {
|
|
200
|
+
components.add(comp);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const trends = [];
|
|
204
|
+
for (const component of components) {
|
|
205
|
+
const analysis = analyzeRecoveryTimeTrend(sessions, component);
|
|
206
|
+
if (analysis.samples >= 2) {
|
|
207
|
+
trends.push({
|
|
208
|
+
component,
|
|
209
|
+
trend: analysis.trend,
|
|
210
|
+
avgTime: analysis.avgRecoveryTime,
|
|
211
|
+
recentTime: analysis.recentRecoveryTime,
|
|
212
|
+
samples: analysis.samples,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Sort by sample count (most data first)
|
|
217
|
+
return trends.sort((a, b) => b.samples - a.samples);
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=spiral-regression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spiral-regression.js","sourceRoot":"","sources":["../../src/patterns/spiral-regression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { TimelineEvent } from '../types';
|
|
2
|
+
export interface ThrashingFile {
|
|
3
|
+
path: string;
|
|
4
|
+
touchCount: number;
|
|
5
|
+
commits: string[];
|
|
6
|
+
netChange: number;
|
|
7
|
+
efficiency: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ThrashingResult {
|
|
10
|
+
detected: boolean;
|
|
11
|
+
files: ThrashingFile[];
|
|
12
|
+
totalThrashingMinutes: number;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Detect thrashing patterns in commits.
|
|
17
|
+
*
|
|
18
|
+
* Definition: Same file touched 5+ times in 2 hours, low net change
|
|
19
|
+
* Value: Identify incomplete understanding
|
|
20
|
+
*/
|
|
21
|
+
export declare function detectThrashing(events: TimelineEvent[], filesPerCommit: Map<string, string[]>, lineStatsPerCommit: Map<string, {
|
|
22
|
+
additions: number;
|
|
23
|
+
deletions: number;
|
|
24
|
+
}>): ThrashingResult;
|
|
25
|
+
//# sourceMappingURL=thrashing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thrashing.d.ts","sourceRoot":"","sources":["../../src/patterns/thrashing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EAAE,EACvB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EACrC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACxE,eAAe,CAoHjB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectThrashing = detectThrashing;
|
|
4
|
+
const date_fns_1 = require("date-fns");
|
|
5
|
+
/**
|
|
6
|
+
* Detect thrashing patterns in commits.
|
|
7
|
+
*
|
|
8
|
+
* Definition: Same file touched 5+ times in 2 hours, low net change
|
|
9
|
+
* Value: Identify incomplete understanding
|
|
10
|
+
*/
|
|
11
|
+
function detectThrashing(events, filesPerCommit, lineStatsPerCommit) {
|
|
12
|
+
if (events.length < 5) {
|
|
13
|
+
return {
|
|
14
|
+
detected: false,
|
|
15
|
+
files: [],
|
|
16
|
+
totalThrashingMinutes: 0,
|
|
17
|
+
message: 'Not enough commits to detect thrashing',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Track file touches within 2-hour windows
|
|
21
|
+
const fileTouches = new Map();
|
|
22
|
+
for (const event of events) {
|
|
23
|
+
const files = filesPerCommit.get(event.hash) || [];
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
if (!fileTouches.has(file)) {
|
|
26
|
+
fileTouches.set(file, { commits: [], timestamps: [] });
|
|
27
|
+
}
|
|
28
|
+
const touches = fileTouches.get(file);
|
|
29
|
+
touches.commits.push(event.hash);
|
|
30
|
+
touches.timestamps.push(event.timestamp);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Find files with 5+ touches in any 2-hour window
|
|
34
|
+
const thrashingFiles = [];
|
|
35
|
+
for (const [file, touches] of fileTouches) {
|
|
36
|
+
// Skip config files and lock files
|
|
37
|
+
if (file.includes('package-lock') || file.includes('yarn.lock') ||
|
|
38
|
+
file.endsWith('.json') && !file.includes('src/')) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Check for 5+ touches within 2 hours
|
|
42
|
+
if (touches.commits.length >= 5) {
|
|
43
|
+
// Find the densest 2-hour window
|
|
44
|
+
let maxTouchesInWindow = 0;
|
|
45
|
+
let windowCommits = [];
|
|
46
|
+
for (let i = 0; i < touches.timestamps.length; i++) {
|
|
47
|
+
const windowStart = touches.timestamps[i];
|
|
48
|
+
const commitsInWindow = [];
|
|
49
|
+
for (let j = i; j < touches.timestamps.length; j++) {
|
|
50
|
+
const gap = (0, date_fns_1.differenceInMinutes)(touches.timestamps[j], windowStart);
|
|
51
|
+
if (gap <= 120) {
|
|
52
|
+
commitsInWindow.push(touches.commits[j]);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (commitsInWindow.length > maxTouchesInWindow) {
|
|
56
|
+
maxTouchesInWindow = commitsInWindow.length;
|
|
57
|
+
windowCommits = commitsInWindow;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (maxTouchesInWindow >= 5) {
|
|
61
|
+
// Calculate net change for this file
|
|
62
|
+
let totalAdditions = 0;
|
|
63
|
+
let totalDeletions = 0;
|
|
64
|
+
for (const commitHash of windowCommits) {
|
|
65
|
+
const stats = lineStatsPerCommit.get(commitHash);
|
|
66
|
+
if (stats) {
|
|
67
|
+
// This is total stats for commit, not per-file
|
|
68
|
+
// For simplicity, we'll estimate based on commit count
|
|
69
|
+
totalAdditions += stats.additions;
|
|
70
|
+
totalDeletions += stats.deletions;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const netChange = totalAdditions - totalDeletions;
|
|
74
|
+
const totalChurn = totalAdditions + totalDeletions;
|
|
75
|
+
// Efficiency: how much of the work was "kept"
|
|
76
|
+
// Low efficiency (high churn, low net) = thrashing
|
|
77
|
+
const efficiency = totalChurn > 0
|
|
78
|
+
? Math.round((Math.abs(netChange) / totalChurn) * 100)
|
|
79
|
+
: 0;
|
|
80
|
+
// Only flag if efficiency is low (< 50%)
|
|
81
|
+
if (efficiency < 50) {
|
|
82
|
+
thrashingFiles.push({
|
|
83
|
+
path: file,
|
|
84
|
+
touchCount: maxTouchesInWindow,
|
|
85
|
+
commits: windowCommits,
|
|
86
|
+
netChange,
|
|
87
|
+
efficiency,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Sort by touch count (worst thrashing first)
|
|
94
|
+
thrashingFiles.sort((a, b) => b.touchCount - a.touchCount);
|
|
95
|
+
// Calculate total thrashing time (rough estimate)
|
|
96
|
+
const totalThrashingMinutes = thrashingFiles.reduce((sum, f) => {
|
|
97
|
+
// Estimate 5 minutes per touch for thrashing files
|
|
98
|
+
return sum + (f.touchCount * 5);
|
|
99
|
+
}, 0);
|
|
100
|
+
const detected = thrashingFiles.length > 0;
|
|
101
|
+
const message = detected
|
|
102
|
+
? `${thrashingFiles.length} file${thrashingFiles.length > 1 ? 's' : ''} with repeated edits (potential confusion)`
|
|
103
|
+
: 'No thrashing detected';
|
|
104
|
+
return {
|
|
105
|
+
detected,
|
|
106
|
+
files: thrashingFiles.slice(0, 5), // Top 5 worst
|
|
107
|
+
totalThrashingMinutes,
|
|
108
|
+
message,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=thrashing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thrashing.js","sourceRoot":"","sources":["../../src/patterns/thrashing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure .gitignore exists in a directory.
|
|
3
|
+
* Creates one if missing to prevent accidental commits.
|
|
4
|
+
*/
|
|
5
|
+
export declare function ensureGitignore(dir: string): void;
|
|
6
|
+
/**
|
|
7
|
+
* Write data atomically using temp file + rename pattern.
|
|
8
|
+
* This prevents corruption if the process is killed mid-write.
|
|
9
|
+
*/
|
|
10
|
+
export declare function atomicWriteSync(filePath: string, data: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Append a line to a file (for NDJSON).
|
|
13
|
+
* Creates file if it doesn't exist.
|
|
14
|
+
*/
|
|
15
|
+
export declare function appendLineSync(filePath: string, line: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Result of reading NDJSON with error info
|
|
18
|
+
*/
|
|
19
|
+
export interface NdjsonReadResult<T> {
|
|
20
|
+
items: T[];
|
|
21
|
+
errors: number;
|
|
22
|
+
total: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read NDJSON file and parse each line.
|
|
26
|
+
* Returns empty array if file doesn't exist.
|
|
27
|
+
* Skips malformed lines gracefully instead of failing.
|
|
28
|
+
*/
|
|
29
|
+
export declare function readNdjsonSync<T>(filePath: string): T[];
|
|
30
|
+
/**
|
|
31
|
+
* Read NDJSON file with detailed error reporting.
|
|
32
|
+
* Use this when you need to know about parse failures.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readNdjsonWithErrors<T>(filePath: string): NdjsonReadResult<T>;
|
|
35
|
+
/**
|
|
36
|
+
* Safely read and parse JSON with fallback.
|
|
37
|
+
* Returns fallback if file doesn't exist or is corrupted.
|
|
38
|
+
*/
|
|
39
|
+
export declare function safeReadJsonSync<T>(filePath: string, fallback: T): T;
|
|
40
|
+
//# sourceMappingURL=atomic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic.d.ts","sourceRoot":"","sources":["../../src/storage/atomic.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAUjD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAYpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQnE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,EAAE,CAGvD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAyB7E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAuBpE"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ensureGitignore = ensureGitignore;
|
|
37
|
+
exports.atomicWriteSync = atomicWriteSync;
|
|
38
|
+
exports.appendLineSync = appendLineSync;
|
|
39
|
+
exports.readNdjsonSync = readNdjsonSync;
|
|
40
|
+
exports.readNdjsonWithErrors = readNdjsonWithErrors;
|
|
41
|
+
exports.safeReadJsonSync = safeReadJsonSync;
|
|
42
|
+
/**
|
|
43
|
+
* Atomic file operations for safe JSON storage
|
|
44
|
+
*/
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const crypto_1 = require("crypto");
|
|
48
|
+
const GITIGNORE_CONTENT = `# vibe-check local data
|
|
49
|
+
# This directory contains personal productivity data
|
|
50
|
+
# Do NOT commit to version control
|
|
51
|
+
|
|
52
|
+
*
|
|
53
|
+
!.gitignore
|
|
54
|
+
`;
|
|
55
|
+
/**
|
|
56
|
+
* Ensure .gitignore exists in a directory.
|
|
57
|
+
* Creates one if missing to prevent accidental commits.
|
|
58
|
+
*/
|
|
59
|
+
function ensureGitignore(dir) {
|
|
60
|
+
const gitignorePath = path.join(dir, '.gitignore');
|
|
61
|
+
if (!fs.existsSync(dir)) {
|
|
62
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
65
|
+
fs.writeFileSync(gitignorePath, GITIGNORE_CONTENT, 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Write data atomically using temp file + rename pattern.
|
|
70
|
+
* This prevents corruption if the process is killed mid-write.
|
|
71
|
+
*/
|
|
72
|
+
function atomicWriteSync(filePath, data) {
|
|
73
|
+
const dir = path.dirname(filePath);
|
|
74
|
+
const tempPath = path.join(dir, `.${path.basename(filePath)}.${(0, crypto_1.randomBytes)(6).toString('hex')}.tmp`);
|
|
75
|
+
// Ensure directory exists with .gitignore
|
|
76
|
+
ensureGitignore(dir);
|
|
77
|
+
// Write to temp file
|
|
78
|
+
fs.writeFileSync(tempPath, data, 'utf-8');
|
|
79
|
+
// Atomic rename (POSIX guarantees atomicity)
|
|
80
|
+
fs.renameSync(tempPath, filePath);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Append a line to a file (for NDJSON).
|
|
84
|
+
* Creates file if it doesn't exist.
|
|
85
|
+
*/
|
|
86
|
+
function appendLineSync(filePath, line) {
|
|
87
|
+
const dir = path.dirname(filePath);
|
|
88
|
+
// Ensure directory exists with .gitignore
|
|
89
|
+
ensureGitignore(dir);
|
|
90
|
+
// Append with newline
|
|
91
|
+
fs.appendFileSync(filePath, line + '\n', 'utf-8');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Read NDJSON file and parse each line.
|
|
95
|
+
* Returns empty array if file doesn't exist.
|
|
96
|
+
* Skips malformed lines gracefully instead of failing.
|
|
97
|
+
*/
|
|
98
|
+
function readNdjsonSync(filePath) {
|
|
99
|
+
const result = readNdjsonWithErrors(filePath);
|
|
100
|
+
return result.items;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Read NDJSON file with detailed error reporting.
|
|
104
|
+
* Use this when you need to know about parse failures.
|
|
105
|
+
*/
|
|
106
|
+
function readNdjsonWithErrors(filePath) {
|
|
107
|
+
if (!fs.existsSync(filePath)) {
|
|
108
|
+
return { items: [], errors: 0, total: 0 };
|
|
109
|
+
}
|
|
110
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
111
|
+
const lines = content.split('\n').filter(line => line.trim().length > 0);
|
|
112
|
+
const items = [];
|
|
113
|
+
let errors = 0;
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
try {
|
|
116
|
+
items.push(JSON.parse(line));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Skip malformed lines - they may be from interrupted writes
|
|
120
|
+
errors++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (errors > 0) {
|
|
124
|
+
console.warn(`Warning: Skipped ${errors}/${lines.length} malformed lines in ${filePath}`);
|
|
125
|
+
}
|
|
126
|
+
return { items, errors, total: lines.length };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Safely read and parse JSON with fallback.
|
|
130
|
+
* Returns fallback if file doesn't exist or is corrupted.
|
|
131
|
+
*/
|
|
132
|
+
function safeReadJsonSync(filePath, fallback) {
|
|
133
|
+
if (!fs.existsSync(filePath)) {
|
|
134
|
+
return fallback;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
138
|
+
return JSON.parse(content);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
// Log corruption but don't crash
|
|
142
|
+
console.warn(`Warning: Could not parse ${filePath}, using fallback`);
|
|
143
|
+
// Backup corrupted file for debugging
|
|
144
|
+
const backupPath = `${filePath}.corrupted.${Date.now()}`;
|
|
145
|
+
try {
|
|
146
|
+
fs.renameSync(filePath, backupPath);
|
|
147
|
+
console.warn(`Corrupted file backed up to: ${backupPath}`);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Ignore backup failures
|
|
151
|
+
}
|
|
152
|
+
return fallback;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=atomic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/storage/atomic.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,0CAUC;AAMD,0CAYC;AAMD,wCAQC;AAgBD,wCAGC;AAMD,oDAyBC;AAMD,4CAuBC;AA5ID;;GAEG;AACH,uCAAyB;AACzB,2CAA6B;AAC7B,mCAAqC;AAErC,MAAM,iBAAiB,GAAG;;;;;;CAMzB,CAAC;AAEF;;;GAGG;AACH,SAAgB,eAAe,CAAC,GAAW;IACzC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEnD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAClC,EAAE,CAAC,aAAa,CAAC,aAAa,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,IAAY;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAA,oBAAW,EAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAErG,0CAA0C;IAC1C,eAAe,CAAC,GAAG,CAAC,CAAC;IAErB,qBAAqB;IACrB,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1C,6CAA6C;IAC7C,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,QAAgB,EAAE,IAAY;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEnC,0CAA0C;IAC1C,eAAe,CAAC,GAAG,CAAC,CAAC;IAErB,sBAAsB;IACtB,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAWD;;;;GAIG;AACH,SAAgB,cAAc,CAAI,QAAgB;IAChD,MAAM,MAAM,GAAG,oBAAoB,CAAI,QAAQ,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAI,QAAgB;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEzE,MAAM,KAAK,GAAQ,EAAE,CAAC;IACtB,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;YAC7D,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC;IAED,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,oBAAoB,MAAM,IAAI,KAAK,CAAC,MAAM,uBAAuB,QAAQ,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,SAAgB,gBAAgB,CAAI,QAAgB,EAAE,QAAW;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iCAAiC;QACjC,OAAO,CAAC,IAAI,CAAC,4BAA4B,QAAQ,kBAAkB,CAAC,CAAC;QAErE,sCAAsC;QACtC,MAAM,UAAU,GAAG,GAAG,QAAQ,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACzD,IAAI,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,yBAAyB;QAC3B,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Commit } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Stored commit format (minimal for storage efficiency)
|
|
4
|
+
*/
|
|
5
|
+
export interface StoredCommit {
|
|
6
|
+
h: string;
|
|
7
|
+
d: string;
|
|
8
|
+
m: string;
|
|
9
|
+
t: string;
|
|
10
|
+
s: string | null;
|
|
11
|
+
a: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get commit log file path
|
|
15
|
+
*/
|
|
16
|
+
export declare function getCommitLogPath(repoPath?: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Append new commits to the log.
|
|
19
|
+
* Skips commits that already exist (by hash).
|
|
20
|
+
*/
|
|
21
|
+
export declare function appendCommits(commits: Commit[], repoPath?: string): number;
|
|
22
|
+
/**
|
|
23
|
+
* Read all commits from the log.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readCommitLog(repoPath?: string): Commit[];
|
|
26
|
+
/**
|
|
27
|
+
* Get the most recent commit hash from the log.
|
|
28
|
+
* Returns empty string if log is empty.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getLastLoggedCommitHash(repoPath?: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Get commit count in the log.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCommitLogCount(repoPath?: string): number;
|
|
35
|
+
//# sourceMappingURL=commit-log.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-log.d.ts","sourceRoot":"","sources":["../../src/storage/commit-log.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAMlC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEzE;AA8BD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAoBzF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,EAAE,CAIxE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAOhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAI1E"}
|