@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,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intervention Effectiveness Scoring
|
|
3
|
+
*
|
|
4
|
+
* Analyzes how well different interventions break spirals
|
|
5
|
+
* and provides data-driven recommendations.
|
|
6
|
+
*/
|
|
7
|
+
import { InterventionMemory, InterventionType } from '../gamification/types';
|
|
8
|
+
export interface InterventionScore {
|
|
9
|
+
type: InterventionType;
|
|
10
|
+
name: string;
|
|
11
|
+
icon: string;
|
|
12
|
+
effectiveness: number;
|
|
13
|
+
avgResolutionTime: number;
|
|
14
|
+
totalUses: number;
|
|
15
|
+
successRate: number;
|
|
16
|
+
bestForPatterns: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface EffectivenessAnalysis {
|
|
19
|
+
scores: InterventionScore[];
|
|
20
|
+
topRecommendation: InterventionType | null;
|
|
21
|
+
insights: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Calculate effectiveness scores for all interventions
|
|
25
|
+
*/
|
|
26
|
+
export declare function calculateEffectiveness(memory: InterventionMemory | undefined): EffectivenessAnalysis;
|
|
27
|
+
/**
|
|
28
|
+
* Get recommended intervention for a specific situation
|
|
29
|
+
*/
|
|
30
|
+
export declare function getRecommendation(memory: InterventionMemory | undefined, pattern?: string, currentDuration?: number): {
|
|
31
|
+
intervention: InterventionType;
|
|
32
|
+
reason: string;
|
|
33
|
+
urgency: 'low' | 'medium' | 'high';
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Compare two interventions
|
|
37
|
+
*/
|
|
38
|
+
export declare function compareInterventions(memory: InterventionMemory | undefined, type1: InterventionType, type2: InterventionType): {
|
|
39
|
+
winner: InterventionType | null;
|
|
40
|
+
comparison: string;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=intervention-effectiveness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intervention-effectiveness.d.ts","sourceRoot":"","sources":["../../src/patterns/intervention-effectiveness.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,kBAAkB,EAAsB,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEjG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,iBAAiB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAaD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,qBAAqB,CAyEpG;AAmDD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,MAAM,GACvB;IACD,YAAY,EAAE,gBAAgB,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACpC,CAkCA;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,gBAAgB,GACtB;IACD,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB,CAyCA"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Intervention Effectiveness Scoring
|
|
4
|
+
*
|
|
5
|
+
* Analyzes how well different interventions break spirals
|
|
6
|
+
* and provides data-driven recommendations.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.calculateEffectiveness = calculateEffectiveness;
|
|
10
|
+
exports.getRecommendation = getRecommendation;
|
|
11
|
+
exports.compareInterventions = compareInterventions;
|
|
12
|
+
// Intervention metadata
|
|
13
|
+
const INTERVENTION_META = {
|
|
14
|
+
TRACER_TEST: { name: 'Tracer Test', icon: '🧪' },
|
|
15
|
+
BREAK: { name: 'Take a Break', icon: '☕' },
|
|
16
|
+
DOCS: { name: 'Read Docs', icon: '📚' },
|
|
17
|
+
REFACTOR: { name: 'Refactor', icon: '🔄' },
|
|
18
|
+
HELP: { name: 'Ask for Help', icon: '🤝' },
|
|
19
|
+
ROLLBACK: { name: 'Rollback', icon: '⏪' },
|
|
20
|
+
OTHER: { name: 'Other', icon: '💡' },
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Calculate effectiveness scores for all interventions
|
|
24
|
+
*/
|
|
25
|
+
function calculateEffectiveness(memory) {
|
|
26
|
+
if (!memory || memory.records.length === 0) {
|
|
27
|
+
return {
|
|
28
|
+
scores: [],
|
|
29
|
+
topRecommendation: null,
|
|
30
|
+
insights: ['No intervention data yet. Use `vibe-check intervene` to record your techniques.'],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Group records by intervention type
|
|
34
|
+
const byType = new Map();
|
|
35
|
+
for (const record of memory.records) {
|
|
36
|
+
if (!byType.has(record.type)) {
|
|
37
|
+
byType.set(record.type, []);
|
|
38
|
+
}
|
|
39
|
+
byType.get(record.type).push(record);
|
|
40
|
+
}
|
|
41
|
+
// Calculate scores for each type
|
|
42
|
+
const scores = [];
|
|
43
|
+
for (const [type, records] of byType.entries()) {
|
|
44
|
+
const meta = INTERVENTION_META[type] || { name: type, icon: '💡' };
|
|
45
|
+
// Average resolution time
|
|
46
|
+
const totalDuration = records.reduce((sum, r) => sum + r.spiralDuration, 0);
|
|
47
|
+
const avgResolutionTime = Math.round(totalDuration / records.length);
|
|
48
|
+
// Find patterns this intervention is used for
|
|
49
|
+
const patternCounts = new Map();
|
|
50
|
+
for (const record of records) {
|
|
51
|
+
if (record.spiralPattern) {
|
|
52
|
+
patternCounts.set(record.spiralPattern, (patternCounts.get(record.spiralPattern) || 0) + 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const bestForPatterns = Array.from(patternCounts.entries())
|
|
56
|
+
.sort((a, b) => b[1] - a[1])
|
|
57
|
+
.slice(0, 2)
|
|
58
|
+
.map(([pattern]) => pattern);
|
|
59
|
+
// Calculate effectiveness score
|
|
60
|
+
// Lower resolution time = higher effectiveness
|
|
61
|
+
// More uses = more reliable data
|
|
62
|
+
const baseScore = Math.max(0, 100 - avgResolutionTime); // Penalize long resolutions
|
|
63
|
+
const usageBonus = Math.min(20, records.length * 2); // Up to 20 bonus for usage
|
|
64
|
+
const effectiveness = Math.min(100, Math.round(baseScore + usageBonus));
|
|
65
|
+
scores.push({
|
|
66
|
+
type,
|
|
67
|
+
name: meta.name,
|
|
68
|
+
icon: meta.icon,
|
|
69
|
+
effectiveness,
|
|
70
|
+
avgResolutionTime,
|
|
71
|
+
totalUses: records.length,
|
|
72
|
+
successRate: 100, // All recorded interventions are "successful" by definition
|
|
73
|
+
bestForPatterns,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
// Sort by effectiveness
|
|
77
|
+
scores.sort((a, b) => b.effectiveness - a.effectiveness);
|
|
78
|
+
// Generate insights
|
|
79
|
+
const insights = generateInsights(scores, memory);
|
|
80
|
+
return {
|
|
81
|
+
scores,
|
|
82
|
+
topRecommendation: scores.length > 0 ? scores[0].type : null,
|
|
83
|
+
insights,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generate actionable insights from effectiveness data
|
|
88
|
+
*/
|
|
89
|
+
function generateInsights(scores, memory) {
|
|
90
|
+
const insights = [];
|
|
91
|
+
if (scores.length === 0)
|
|
92
|
+
return insights;
|
|
93
|
+
// Top performer insight
|
|
94
|
+
const top = scores[0];
|
|
95
|
+
if (top.totalUses >= 3) {
|
|
96
|
+
insights.push(`${top.icon} ${top.name} is your most effective technique (${top.avgResolutionTime}m avg resolution)`);
|
|
97
|
+
}
|
|
98
|
+
// Underused effective techniques
|
|
99
|
+
const underused = scores.filter(s => s.effectiveness > 70 && s.totalUses < 3);
|
|
100
|
+
if (underused.length > 0) {
|
|
101
|
+
const technique = underused[0];
|
|
102
|
+
insights.push(`Try ${technique.icon} ${technique.name} more often - it resolves issues in ${technique.avgResolutionTime}m`);
|
|
103
|
+
}
|
|
104
|
+
// Pattern-specific recommendations
|
|
105
|
+
const patternInterventions = memory.effectiveByPattern;
|
|
106
|
+
for (const [pattern, types] of Object.entries(patternInterventions)) {
|
|
107
|
+
if (types.length > 0) {
|
|
108
|
+
const bestType = types[0];
|
|
109
|
+
const meta = INTERVENTION_META[bestType];
|
|
110
|
+
if (meta) {
|
|
111
|
+
insights.push(`For ${pattern} issues, ${meta.icon} ${meta.name} works best for you`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Time-based insight
|
|
116
|
+
if (memory.avgTimeToIntervene > 30) {
|
|
117
|
+
insights.push(`You wait ${memory.avgTimeToIntervene}m on average before intervening. Try the 15-minute rule.`);
|
|
118
|
+
}
|
|
119
|
+
return insights.slice(0, 4); // Max 4 insights
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get recommended intervention for a specific situation
|
|
123
|
+
*/
|
|
124
|
+
function getRecommendation(memory, pattern, currentDuration) {
|
|
125
|
+
const defaultRec = {
|
|
126
|
+
intervention: 'TRACER_TEST',
|
|
127
|
+
reason: 'Write a test to validate your assumptions',
|
|
128
|
+
urgency: 'medium',
|
|
129
|
+
};
|
|
130
|
+
if (!memory || memory.records.length === 0) {
|
|
131
|
+
return defaultRec;
|
|
132
|
+
}
|
|
133
|
+
// Check for pattern-specific recommendation
|
|
134
|
+
if (pattern && memory.effectiveByPattern[pattern]?.length > 0) {
|
|
135
|
+
const type = memory.effectiveByPattern[pattern][0];
|
|
136
|
+
const meta = INTERVENTION_META[type];
|
|
137
|
+
return {
|
|
138
|
+
intervention: type,
|
|
139
|
+
reason: `${meta.icon} ${meta.name} has worked for ${pattern} before`,
|
|
140
|
+
urgency: currentDuration && currentDuration > 30 ? 'high' : 'medium',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Fall back to most used effective intervention
|
|
144
|
+
if (memory.topInterventions.length > 0) {
|
|
145
|
+
const type = memory.topInterventions[0];
|
|
146
|
+
const meta = INTERVENTION_META[type];
|
|
147
|
+
return {
|
|
148
|
+
intervention: type,
|
|
149
|
+
reason: `${meta.icon} ${meta.name} is your most reliable technique`,
|
|
150
|
+
urgency: currentDuration && currentDuration > 45 ? 'high' : 'medium',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return defaultRec;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Compare two interventions
|
|
157
|
+
*/
|
|
158
|
+
function compareInterventions(memory, type1, type2) {
|
|
159
|
+
if (!memory || memory.records.length === 0) {
|
|
160
|
+
return { winner: null, comparison: 'Not enough data to compare' };
|
|
161
|
+
}
|
|
162
|
+
const records1 = memory.records.filter(r => r.type === type1);
|
|
163
|
+
const records2 = memory.records.filter(r => r.type === type2);
|
|
164
|
+
if (records1.length === 0 && records2.length === 0) {
|
|
165
|
+
return { winner: null, comparison: 'Neither intervention has been used' };
|
|
166
|
+
}
|
|
167
|
+
const avg1 = records1.length > 0
|
|
168
|
+
? records1.reduce((sum, r) => sum + r.spiralDuration, 0) / records1.length
|
|
169
|
+
: Infinity;
|
|
170
|
+
const avg2 = records2.length > 0
|
|
171
|
+
? records2.reduce((sum, r) => sum + r.spiralDuration, 0) / records2.length
|
|
172
|
+
: Infinity;
|
|
173
|
+
const meta1 = INTERVENTION_META[type1];
|
|
174
|
+
const meta2 = INTERVENTION_META[type2];
|
|
175
|
+
if (avg1 < avg2) {
|
|
176
|
+
const diff = Math.round(avg2 - avg1);
|
|
177
|
+
return {
|
|
178
|
+
winner: type1,
|
|
179
|
+
comparison: `${meta1.icon} ${meta1.name} resolves ${diff}m faster than ${meta2.icon} ${meta2.name}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
else if (avg2 < avg1) {
|
|
183
|
+
const diff = Math.round(avg1 - avg2);
|
|
184
|
+
return {
|
|
185
|
+
winner: type2,
|
|
186
|
+
comparison: `${meta2.icon} ${meta2.name} resolves ${diff}m faster than ${meta1.icon} ${meta1.name}`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
return {
|
|
191
|
+
winner: null,
|
|
192
|
+
comparison: `${meta1.icon} ${meta1.name} and ${meta2.icon} ${meta2.name} are equally effective`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=intervention-effectiveness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intervention-effectiveness.js","sourceRoot":"","sources":["../../src/patterns/intervention-effectiveness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TimelineSession } from '../types';
|
|
2
|
+
export interface LateNightSpiralResult {
|
|
3
|
+
detected: boolean;
|
|
4
|
+
spirals: LateNightSpiral[];
|
|
5
|
+
totalDuration: number;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LateNightSpiral {
|
|
9
|
+
sessionId: string;
|
|
10
|
+
startTime: Date;
|
|
11
|
+
endTime: Date;
|
|
12
|
+
duration: number;
|
|
13
|
+
fixCount: number;
|
|
14
|
+
component: string;
|
|
15
|
+
resolved: boolean;
|
|
16
|
+
nextDayResolution?: {
|
|
17
|
+
duration: number;
|
|
18
|
+
improvement: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detect late-night spirals: debug sessions after 22:00 with 3+ fix commits.
|
|
23
|
+
*
|
|
24
|
+
* Definition: Debug session after 22:00 with 3+ fix commits
|
|
25
|
+
* Value: Show that tired debugging is inefficient
|
|
26
|
+
*
|
|
27
|
+
* Enhanced: Also compare with "fresh eyes" resolution time if available
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectLateNightSpiral(sessions: TimelineSession[]): LateNightSpiralResult;
|
|
30
|
+
//# sourceMappingURL=late-night.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"late-night.d.ts","sourceRoot":"","sources":["../../src/patterns/late-night.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,eAAe,EAAY,MAAM,UAAU,CAAC;AAEpE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAOD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,eAAe,EAAE,GAC1B,qBAAqB,CAwFvB"}
|
|
@@ -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"}
|