@boshu2/vibe-check 1.5.0 → 1.6.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/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 +165 -144
- package/claude-progress.json +191 -9
- package/claude-progress.txt +257 -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/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/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/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/docs/images/dashboard.png +0 -0
- package/feature-list.json +48 -0
- 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 -44
- package/PLAN-ultimate-game.md +0 -1362
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectLateNightSpiral = detectLateNightSpiral;
|
|
4
|
+
// Late night threshold: 22:00 (10 PM)
|
|
5
|
+
const LATE_NIGHT_HOUR = 22;
|
|
6
|
+
// Early morning threshold: 06:00 (6 AM)
|
|
7
|
+
const EARLY_MORNING_HOUR = 6;
|
|
8
|
+
/**
|
|
9
|
+
* Detect late-night spirals: debug sessions after 22:00 with 3+ fix commits.
|
|
10
|
+
*
|
|
11
|
+
* Definition: Debug session after 22:00 with 3+ fix commits
|
|
12
|
+
* Value: Show that tired debugging is inefficient
|
|
13
|
+
*
|
|
14
|
+
* Enhanced: Also compare with "fresh eyes" resolution time if available
|
|
15
|
+
*/
|
|
16
|
+
function detectLateNightSpiral(sessions) {
|
|
17
|
+
const lateNightSpirals = [];
|
|
18
|
+
for (const session of sessions) {
|
|
19
|
+
// Check if session is during late night hours
|
|
20
|
+
const sessionHour = session.start.getHours();
|
|
21
|
+
const isLateNight = sessionHour >= LATE_NIGHT_HOUR || sessionHour < EARLY_MORNING_HOUR;
|
|
22
|
+
if (!isLateNight)
|
|
23
|
+
continue;
|
|
24
|
+
// Check for spirals in this session
|
|
25
|
+
for (const spiral of session.spirals) {
|
|
26
|
+
// A spiral is 3+ fix commits for same component
|
|
27
|
+
if (spiral.commits < 3)
|
|
28
|
+
continue;
|
|
29
|
+
// Find if this component was fixed the next day
|
|
30
|
+
const nextDayFix = findNextDayResolution(spiral.component, session, sessions);
|
|
31
|
+
lateNightSpirals.push({
|
|
32
|
+
sessionId: session.id,
|
|
33
|
+
startTime: spiral.firstCommit,
|
|
34
|
+
endTime: spiral.lastCommit,
|
|
35
|
+
duration: spiral.duration,
|
|
36
|
+
fixCount: spiral.commits,
|
|
37
|
+
component: spiral.component,
|
|
38
|
+
resolved: !nextDayFix, // If no next-day fix, they solved it at night
|
|
39
|
+
nextDayResolution: nextDayFix,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Also check for high fix density even without formal spirals
|
|
43
|
+
const fixCommits = session.commits.filter(c => c.type === 'fix');
|
|
44
|
+
if (fixCommits.length >= 3 && session.spirals.length === 0) {
|
|
45
|
+
// Late night session with lots of fixes but no detected spiral
|
|
46
|
+
// Could be a diffuse debugging session
|
|
47
|
+
const components = new Set(fixCommits.map(c => c.scope || 'unknown'));
|
|
48
|
+
for (const component of components) {
|
|
49
|
+
const componentFixes = fixCommits.filter(c => (c.scope || 'unknown') === component);
|
|
50
|
+
if (componentFixes.length >= 3) {
|
|
51
|
+
const duration = componentFixes.length > 1
|
|
52
|
+
? Math.round((componentFixes[componentFixes.length - 1].timestamp.getTime() -
|
|
53
|
+
componentFixes[0].timestamp.getTime()) / (1000 * 60))
|
|
54
|
+
: session.duration;
|
|
55
|
+
lateNightSpirals.push({
|
|
56
|
+
sessionId: session.id,
|
|
57
|
+
startTime: componentFixes[0].timestamp,
|
|
58
|
+
endTime: componentFixes[componentFixes.length - 1].timestamp,
|
|
59
|
+
duration,
|
|
60
|
+
fixCount: componentFixes.length,
|
|
61
|
+
component,
|
|
62
|
+
resolved: true, // Assume resolved if no next-day data
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Calculate total duration
|
|
69
|
+
const totalDuration = lateNightSpirals.reduce((sum, s) => sum + s.duration, 0);
|
|
70
|
+
// Generate message
|
|
71
|
+
let message = '';
|
|
72
|
+
if (lateNightSpirals.length > 0) {
|
|
73
|
+
const withFreshEyes = lateNightSpirals.filter(s => s.nextDayResolution);
|
|
74
|
+
if (withFreshEyes.length > 0) {
|
|
75
|
+
const avgImprovement = withFreshEyes.reduce((sum, s) => sum + (s.nextDayResolution?.improvement || 0), 0) / withFreshEyes.length;
|
|
76
|
+
message = `🌙 ${lateNightSpirals.length} late-night spiral${lateNightSpirals.length > 1 ? 's' : ''}: ` +
|
|
77
|
+
`${formatDuration(totalDuration)} debugging after 10pm. ` +
|
|
78
|
+
`Fresh eyes solve ${Math.round(avgImprovement * 100)}% faster.`;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
message = `🌙 ${lateNightSpirals.length} late-night spiral${lateNightSpirals.length > 1 ? 's' : ''}: ` +
|
|
82
|
+
`${formatDuration(totalDuration)} debugging after 10pm. Consider sleeping on it!`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
detected: lateNightSpirals.length > 0,
|
|
87
|
+
spirals: lateNightSpirals,
|
|
88
|
+
totalDuration,
|
|
89
|
+
message,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if the same component was fixed more quickly the next day
|
|
94
|
+
*/
|
|
95
|
+
function findNextDayResolution(component, lateNightSession, allSessions) {
|
|
96
|
+
// Look for sessions the next day
|
|
97
|
+
const lateNightDate = lateNightSession.start.toDateString();
|
|
98
|
+
for (const session of allSessions) {
|
|
99
|
+
// Skip the late night session itself
|
|
100
|
+
if (session.id === lateNightSession.id)
|
|
101
|
+
continue;
|
|
102
|
+
// Check if this session is the next day (or later morning)
|
|
103
|
+
const sessionDate = session.start.toDateString();
|
|
104
|
+
const sessionHour = session.start.getHours();
|
|
105
|
+
// Must be after the late night session
|
|
106
|
+
if (session.start <= lateNightSession.end)
|
|
107
|
+
continue;
|
|
108
|
+
// Look for fixes to the same component
|
|
109
|
+
const componentFixes = session.commits.filter(c => c.type === 'fix' && (c.scope || 'unknown') === component);
|
|
110
|
+
if (componentFixes.length > 0) {
|
|
111
|
+
// Found a next-day resolution
|
|
112
|
+
// Calculate how long it took with fresh eyes
|
|
113
|
+
const freshDuration = componentFixes.length > 1
|
|
114
|
+
? Math.round((componentFixes[componentFixes.length - 1].timestamp.getTime() -
|
|
115
|
+
componentFixes[0].timestamp.getTime()) / (1000 * 60))
|
|
116
|
+
: 15; // Assume 15 minutes for single-commit fixes
|
|
117
|
+
// Compare with late night duration
|
|
118
|
+
const lateNightDuration = lateNightSession.spirals.find(s => s.component === component)?.duration || 30;
|
|
119
|
+
const improvement = lateNightDuration > 0
|
|
120
|
+
? (lateNightDuration - freshDuration) / lateNightDuration
|
|
121
|
+
: 0;
|
|
122
|
+
return {
|
|
123
|
+
duration: freshDuration,
|
|
124
|
+
improvement: Math.max(0, improvement), // Only positive improvements
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Format duration for display
|
|
132
|
+
*/
|
|
133
|
+
function formatDuration(minutes) {
|
|
134
|
+
if (minutes < 60) {
|
|
135
|
+
return `${minutes}m`;
|
|
136
|
+
}
|
|
137
|
+
const hours = Math.floor(minutes / 60);
|
|
138
|
+
const mins = minutes % 60;
|
|
139
|
+
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=late-night.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"late-night.js","sourceRoot":"","sources":["../../src/patterns/late-night.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TimelineSession } from '../types';
|
|
2
|
+
export interface PostDeleteSprintResult {
|
|
3
|
+
detected: boolean;
|
|
4
|
+
deleteSession: {
|
|
5
|
+
id: string;
|
|
6
|
+
linesDeleted: number;
|
|
7
|
+
timestamp: Date;
|
|
8
|
+
} | null;
|
|
9
|
+
sprintSession: {
|
|
10
|
+
id: string;
|
|
11
|
+
features: number;
|
|
12
|
+
duration: number;
|
|
13
|
+
velocity: number;
|
|
14
|
+
} | null;
|
|
15
|
+
velocityIncrease: number;
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Detect post-delete sprints across sessions.
|
|
20
|
+
*
|
|
21
|
+
* Definition: Deletions > 2× additions in one session, then velocity >50% above baseline
|
|
22
|
+
* Value: Shows ROI of simplification
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectPostDeleteSprint(sessions: TimelineSession[], commitStats: Map<string, {
|
|
25
|
+
additions: number;
|
|
26
|
+
deletions: number;
|
|
27
|
+
}>): PostDeleteSprintResult;
|
|
28
|
+
//# sourceMappingURL=post-delete-sprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-delete-sprint.d.ts","sourceRoot":"","sources":["../../src/patterns/post-delete-sprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE;QACb,EAAE,EAAE,MAAM,CAAC;QACX,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,IAAI,CAAC;KACjB,GAAG,IAAI,CAAC;IACT,aAAa,EAAE;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EAAE,EAC3B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,GACjE,sBAAsB,CAmFxB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectPostDeleteSprint = detectPostDeleteSprint;
|
|
4
|
+
/**
|
|
5
|
+
* Detect post-delete sprints across sessions.
|
|
6
|
+
*
|
|
7
|
+
* Definition: Deletions > 2× additions in one session, then velocity >50% above baseline
|
|
8
|
+
* Value: Shows ROI of simplification
|
|
9
|
+
*/
|
|
10
|
+
function detectPostDeleteSprint(sessions, commitStats) {
|
|
11
|
+
if (sessions.length < 2) {
|
|
12
|
+
return {
|
|
13
|
+
detected: false,
|
|
14
|
+
deleteSession: null,
|
|
15
|
+
sprintSession: null,
|
|
16
|
+
velocityIncrease: 0,
|
|
17
|
+
message: 'Not enough sessions to detect pattern',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Calculate baseline velocity (features per hour across all sessions)
|
|
21
|
+
let totalFeatures = 0;
|
|
22
|
+
let totalMinutes = 0;
|
|
23
|
+
for (const session of sessions) {
|
|
24
|
+
const featCount = session.commits.filter(c => c.type === 'feat').length;
|
|
25
|
+
totalFeatures += featCount;
|
|
26
|
+
totalMinutes += session.duration;
|
|
27
|
+
}
|
|
28
|
+
const baselineVelocity = totalMinutes > 0 ? (totalFeatures / totalMinutes) * 60 : 0;
|
|
29
|
+
// Find sessions with heavy deletions (deletions > 2× additions)
|
|
30
|
+
for (let i = 0; i < sessions.length - 1; i++) {
|
|
31
|
+
const session = sessions[i];
|
|
32
|
+
// Calculate total additions/deletions for this session
|
|
33
|
+
let sessionAdditions = 0;
|
|
34
|
+
let sessionDeletions = 0;
|
|
35
|
+
for (const commit of session.commits) {
|
|
36
|
+
const stats = commitStats.get(commit.hash);
|
|
37
|
+
if (stats) {
|
|
38
|
+
sessionAdditions += stats.additions;
|
|
39
|
+
sessionDeletions += stats.deletions;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Check if this is a "delete session" (deletions > 2× additions)
|
|
43
|
+
const isDeleteSession = sessionDeletions > sessionAdditions * 2 && sessionDeletions > 100;
|
|
44
|
+
if (isDeleteSession) {
|
|
45
|
+
// Check subsequent sessions for sprint behavior
|
|
46
|
+
for (let j = i + 1; j < sessions.length; j++) {
|
|
47
|
+
const sprintCandidate = sessions[j];
|
|
48
|
+
const sprintFeatures = sprintCandidate.commits.filter(c => c.type === 'feat').length;
|
|
49
|
+
const sprintDuration = sprintCandidate.duration;
|
|
50
|
+
// Calculate sprint velocity
|
|
51
|
+
const sprintVelocity = sprintDuration > 0 ? (sprintFeatures / sprintDuration) * 60 : 0;
|
|
52
|
+
// Check if sprint velocity is >50% above baseline
|
|
53
|
+
const velocityIncrease = baselineVelocity > 0
|
|
54
|
+
? ((sprintVelocity - baselineVelocity) / baselineVelocity) * 100
|
|
55
|
+
: 0;
|
|
56
|
+
if (velocityIncrease > 50 && sprintFeatures >= 3) {
|
|
57
|
+
return {
|
|
58
|
+
detected: true,
|
|
59
|
+
deleteSession: {
|
|
60
|
+
id: session.id,
|
|
61
|
+
linesDeleted: sessionDeletions,
|
|
62
|
+
timestamp: session.start,
|
|
63
|
+
},
|
|
64
|
+
sprintSession: {
|
|
65
|
+
id: sprintCandidate.id,
|
|
66
|
+
features: sprintFeatures,
|
|
67
|
+
duration: sprintDuration,
|
|
68
|
+
velocity: Math.round(sprintVelocity * 10) / 10,
|
|
69
|
+
},
|
|
70
|
+
velocityIncrease: Math.round(velocityIncrease),
|
|
71
|
+
message: `${Math.round(velocityIncrease)}% faster after removing ${sessionDeletions} lines`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
detected: false,
|
|
79
|
+
deleteSession: null,
|
|
80
|
+
sprintSession: null,
|
|
81
|
+
velocityIncrease: 0,
|
|
82
|
+
message: 'No post-delete sprint detected',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=post-delete-sprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-delete-sprint.js","sourceRoot":"","sources":["../../src/patterns/post-delete-sprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spiral Regression Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects when spiral patterns re-emerge after a period of improvement.
|
|
5
|
+
* Uses weekly trend analysis to identify regressions.
|
|
6
|
+
*/
|
|
7
|
+
import { StoredSession, TrendData } from '../storage/timeline-store';
|
|
8
|
+
export interface RegressionAlert {
|
|
9
|
+
type: 'spiral_regression' | 'recovery_time_regression';
|
|
10
|
+
severity: 'warning' | 'critical';
|
|
11
|
+
message: string;
|
|
12
|
+
metric: string;
|
|
13
|
+
currentValue: number;
|
|
14
|
+
baselineValue: number;
|
|
15
|
+
changePercent: number;
|
|
16
|
+
period: string;
|
|
17
|
+
recommendation: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RegressionAnalysis {
|
|
20
|
+
hasRegression: boolean;
|
|
21
|
+
alerts: RegressionAlert[];
|
|
22
|
+
summary: string;
|
|
23
|
+
improvementStreak: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Detect spiral regressions from trend data
|
|
27
|
+
*/
|
|
28
|
+
export declare function detectRegressions(trends: TrendData): RegressionAnalysis;
|
|
29
|
+
/**
|
|
30
|
+
* Analyze recovery time trends for a specific pattern
|
|
31
|
+
*/
|
|
32
|
+
export declare function analyzeRecoveryTimeTrend(sessions: StoredSession[], component: string): {
|
|
33
|
+
improving: boolean;
|
|
34
|
+
trend: 'improving' | 'stable' | 'worsening';
|
|
35
|
+
avgRecoveryTime: number;
|
|
36
|
+
recentRecoveryTime: number;
|
|
37
|
+
samples: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Get all component recovery trends
|
|
41
|
+
*/
|
|
42
|
+
export declare function getAllRecoveryTrends(sessions: StoredSession[]): Array<{
|
|
43
|
+
component: string;
|
|
44
|
+
trend: 'improving' | 'stable' | 'worsening';
|
|
45
|
+
avgTime: number;
|
|
46
|
+
recentTime: number;
|
|
47
|
+
samples: number;
|
|
48
|
+
}>;
|
|
49
|
+
//# sourceMappingURL=spiral-regression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spiral-regression.d.ts","sourceRoot":"","sources":["../../src/patterns/spiral-regression.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,SAAS,EAAa,MAAM,2BAA2B,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,GAAG,0BAA0B,CAAC;IACvD,QAAQ,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAkDvE;AAwGD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,MAAM,GAChB;IACD,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAgDA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAyBD"}
|
|
@@ -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"}
|