@boshu2/vibe-check 1.7.0 → 1.8.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/actionable-coaching-plan-2025-12-02.md +209 -0
- package/.agents/plans/git-forensics-enhancement-2025-12-05.md +493 -0
- package/.claude/skills/typescript-review.md +152 -0
- package/CHANGELOG.md +41 -5
- package/CLAUDE.md +27 -23
- package/Makefile +0 -13
- package/README.md +126 -173
- package/SECURITY.md +5 -1
- package/assets/logo-dark.svg +47 -0
- package/assets/logo.svg +47 -0
- package/claude-progress.json +28 -7
- package/claude-progress.txt +48 -0
- package/dist/analyzers/patterns.d.ts +62 -0
- package/dist/analyzers/patterns.d.ts.map +1 -0
- package/dist/analyzers/patterns.js +103 -0
- package/dist/analyzers/patterns.js.map +1 -0
- package/dist/analyzers/quality.d.ts +58 -0
- package/dist/analyzers/quality.d.ts.map +1 -0
- package/dist/analyzers/quality.js +114 -0
- package/dist/analyzers/quality.js.map +1 -0
- package/dist/analyzers/sessions.d.ts +45 -0
- package/dist/analyzers/sessions.d.ts.map +1 -0
- package/dist/analyzers/sessions.js +123 -0
- package/dist/analyzers/sessions.js.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +5 -0
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/forensics.d.ts +29 -0
- package/dist/commands/forensics.d.ts.map +1 -0
- package/dist/commands/forensics.js +213 -0
- package/dist/commands/forensics.js.map +1 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +11 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/insights.d.ts +3 -0
- package/dist/commands/insights.d.ts.map +1 -0
- package/dist/commands/insights.js +120 -0
- package/dist/commands/insights.js.map +1 -0
- package/dist/commands/pipeline.d.ts +3 -0
- package/dist/commands/pipeline.d.ts.map +1 -0
- package/dist/commands/pipeline.js +485 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/profile.d.ts +0 -1
- package/dist/commands/profile.d.ts.map +1 -1
- package/dist/commands/profile.js +0 -4
- package/dist/commands/profile.js.map +1 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +38 -0
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/sessions.d.ts +20 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +201 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +48 -7
- package/dist/commands/watch.js.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +11 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/spiral-history.d.ts +62 -0
- package/dist/storage/spiral-history.d.ts.map +1 -0
- package/dist/storage/spiral-history.js +265 -0
- package/dist/storage/spiral-history.js.map +1 -0
- package/docs/ARCHITECTURE.md +2 -10
- package/docs/GAMIFICATION.md +19 -266
- package/docs/VIBE-ECOSYSTEM.md +12 -78
- package/feature-list.json +140 -88
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Detection Analyzer (VIBE-045)
|
|
3
|
+
*
|
|
4
|
+
* Analyzes git history to detect failure patterns:
|
|
5
|
+
* - Debug spirals ("take N" pattern)
|
|
6
|
+
* - Vague commits (<20 chars)
|
|
7
|
+
* - Context amnesia (repeated scope visits)
|
|
8
|
+
*
|
|
9
|
+
* Proven algorithm from release-engineering retrospective (475 commits analyzed).
|
|
10
|
+
*/
|
|
11
|
+
import { Commit } from '../types';
|
|
12
|
+
export interface DebugSpiral {
|
|
13
|
+
count: number;
|
|
14
|
+
durationMinutes: number;
|
|
15
|
+
commits: string[];
|
|
16
|
+
dates: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface VagueCommitInfo {
|
|
19
|
+
count: number;
|
|
20
|
+
percentage: number;
|
|
21
|
+
threshold: number;
|
|
22
|
+
examples: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface ContextAmnesiaInfo {
|
|
25
|
+
scopes: {
|
|
26
|
+
name: string;
|
|
27
|
+
visits: number;
|
|
28
|
+
}[];
|
|
29
|
+
}
|
|
30
|
+
export interface PatternDetectionResult {
|
|
31
|
+
debugSpirals: DebugSpiral | null;
|
|
32
|
+
vagueCommits: VagueCommitInfo;
|
|
33
|
+
contextAmnesia: ContextAmnesiaInfo;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect "take N" pattern indicating debug spirals.
|
|
37
|
+
*
|
|
38
|
+
* A debug spiral is detected when 3+ commits match the pattern "take N" or similar
|
|
39
|
+
* retry patterns, indicating the developer is stuck in a fix-retry loop.
|
|
40
|
+
*/
|
|
41
|
+
export declare function detectDebugSpirals(commits: Commit[]): DebugSpiral | null;
|
|
42
|
+
/**
|
|
43
|
+
* Detect vague commits - messages that are too short to be meaningful.
|
|
44
|
+
*
|
|
45
|
+
* Threshold: < 20 characters is considered vague.
|
|
46
|
+
* Examples: "ci", "v3", "blah", "take 2", "fix"
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectVagueCommits(commits: Commit[], threshold?: number): VagueCommitInfo;
|
|
49
|
+
/**
|
|
50
|
+
* Detect context amnesia - repeatedly returning to the same scope/component.
|
|
51
|
+
*
|
|
52
|
+
* This indicates the developer keeps revisiting the same area, possibly
|
|
53
|
+
* due to incomplete understanding or forgotten context.
|
|
54
|
+
*
|
|
55
|
+
* Reports scopes that appear in 5+ commits.
|
|
56
|
+
*/
|
|
57
|
+
export declare function detectContextAmnesia(commits: Commit[], minVisits?: number): ContextAmnesiaInfo;
|
|
58
|
+
/**
|
|
59
|
+
* Run all pattern detection on a set of commits.
|
|
60
|
+
*/
|
|
61
|
+
export declare function detectPatterns(commits: Commit[]): PatternDetectionResult;
|
|
62
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/analyzers/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,eAAe,CAAC;IAC9B,cAAc,EAAE,kBAAkB,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,GAAG,IAAI,CAoCxE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,GAAE,MAAW,GACrB,eAAe,CAmBjB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EAAE,EACjB,SAAS,GAAE,MAAU,GACpB,kBAAkB,CAiBpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAMxE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern Detection Analyzer (VIBE-045)
|
|
4
|
+
*
|
|
5
|
+
* Analyzes git history to detect failure patterns:
|
|
6
|
+
* - Debug spirals ("take N" pattern)
|
|
7
|
+
* - Vague commits (<20 chars)
|
|
8
|
+
* - Context amnesia (repeated scope visits)
|
|
9
|
+
*
|
|
10
|
+
* Proven algorithm from release-engineering retrospective (475 commits analyzed).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.detectDebugSpirals = detectDebugSpirals;
|
|
14
|
+
exports.detectVagueCommits = detectVagueCommits;
|
|
15
|
+
exports.detectContextAmnesia = detectContextAmnesia;
|
|
16
|
+
exports.detectPatterns = detectPatterns;
|
|
17
|
+
/**
|
|
18
|
+
* Detect "take N" pattern indicating debug spirals.
|
|
19
|
+
*
|
|
20
|
+
* A debug spiral is detected when 3+ commits match the pattern "take N" or similar
|
|
21
|
+
* retry patterns, indicating the developer is stuck in a fix-retry loop.
|
|
22
|
+
*/
|
|
23
|
+
function detectDebugSpirals(commits) {
|
|
24
|
+
const takePattern = /^take\s*[0-9]+$/i;
|
|
25
|
+
const retryPatterns = [
|
|
26
|
+
/^(fix|try|attempt)\s*#?\d+$/i, // "fix 2", "try #3"
|
|
27
|
+
/^(wip|temp|test)\s*\d*$/i, // "wip", "wip2"
|
|
28
|
+
/^v\d+$/i, // "v2", "v3"
|
|
29
|
+
takePattern,
|
|
30
|
+
];
|
|
31
|
+
const spiralCommits = commits.filter((c) => retryPatterns.some((pattern) => pattern.test(c.message.trim())));
|
|
32
|
+
if (spiralCommits.length < 3) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Sort by date to calculate duration
|
|
36
|
+
const sorted = [...spiralCommits].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
37
|
+
const first = sorted[0];
|
|
38
|
+
const last = sorted[sorted.length - 1];
|
|
39
|
+
const durationMinutes = (last.date.getTime() - first.date.getTime()) / (60 * 1000);
|
|
40
|
+
// Get unique dates
|
|
41
|
+
const dates = [...new Set(sorted.map((c) => c.date.toISOString().split('T')[0]))];
|
|
42
|
+
return {
|
|
43
|
+
count: spiralCommits.length,
|
|
44
|
+
durationMinutes: Math.round(durationMinutes * 10) / 10,
|
|
45
|
+
commits: spiralCommits.map((c) => c.hash),
|
|
46
|
+
dates,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Detect vague commits - messages that are too short to be meaningful.
|
|
51
|
+
*
|
|
52
|
+
* Threshold: < 20 characters is considered vague.
|
|
53
|
+
* Examples: "ci", "v3", "blah", "take 2", "fix"
|
|
54
|
+
*/
|
|
55
|
+
function detectVagueCommits(commits, threshold = 20) {
|
|
56
|
+
const vagueCommits = commits.filter((c) => c.message.trim().length < threshold);
|
|
57
|
+
// Get example vague messages (deduplicated, max 10)
|
|
58
|
+
const examples = [
|
|
59
|
+
...new Set(vagueCommits.map((c) => c.message.trim())),
|
|
60
|
+
].slice(0, 10);
|
|
61
|
+
return {
|
|
62
|
+
count: vagueCommits.length,
|
|
63
|
+
percentage: commits.length > 0
|
|
64
|
+
? Math.round((vagueCommits.length / commits.length) * 1000) / 10
|
|
65
|
+
: 0,
|
|
66
|
+
threshold,
|
|
67
|
+
examples,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Detect context amnesia - repeatedly returning to the same scope/component.
|
|
72
|
+
*
|
|
73
|
+
* This indicates the developer keeps revisiting the same area, possibly
|
|
74
|
+
* due to incomplete understanding or forgotten context.
|
|
75
|
+
*
|
|
76
|
+
* Reports scopes that appear in 5+ commits.
|
|
77
|
+
*/
|
|
78
|
+
function detectContextAmnesia(commits, minVisits = 5) {
|
|
79
|
+
const scopeCounts = new Map();
|
|
80
|
+
for (const commit of commits) {
|
|
81
|
+
if (commit.scope) {
|
|
82
|
+
const current = scopeCounts.get(commit.scope) || 0;
|
|
83
|
+
scopeCounts.set(commit.scope, current + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Filter to scopes with high visit counts, sorted descending
|
|
87
|
+
const scopes = Array.from(scopeCounts.entries())
|
|
88
|
+
.filter(([, count]) => count >= minVisits)
|
|
89
|
+
.sort((a, b) => b[1] - a[1])
|
|
90
|
+
.map(([name, visits]) => ({ name, visits }));
|
|
91
|
+
return { scopes };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Run all pattern detection on a set of commits.
|
|
95
|
+
*/
|
|
96
|
+
function detectPatterns(commits) {
|
|
97
|
+
return {
|
|
98
|
+
debugSpirals: detectDebugSpirals(commits),
|
|
99
|
+
vagueCommits: detectVagueCommits(commits),
|
|
100
|
+
contextAmnesia: detectContextAmnesia(commits),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/analyzers/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quality Metrics Analyzer (VIBE-045)
|
|
3
|
+
*
|
|
4
|
+
* Calculates commit message quality metrics:
|
|
5
|
+
* - Conventional commits percentage
|
|
6
|
+
* - Descriptive commits percentage
|
|
7
|
+
* - Vague commits percentage
|
|
8
|
+
*
|
|
9
|
+
* Proven algorithm from release-engineering retrospective.
|
|
10
|
+
*/
|
|
11
|
+
import { Commit } from '../types';
|
|
12
|
+
export interface QualityMetrics {
|
|
13
|
+
totalCommits: number;
|
|
14
|
+
conventionalCommits: {
|
|
15
|
+
count: number;
|
|
16
|
+
percentage: number;
|
|
17
|
+
};
|
|
18
|
+
descriptiveCommits: {
|
|
19
|
+
count: number;
|
|
20
|
+
percentage: number;
|
|
21
|
+
minLength: number;
|
|
22
|
+
};
|
|
23
|
+
vagueCommits: {
|
|
24
|
+
count: number;
|
|
25
|
+
percentage: number;
|
|
26
|
+
threshold: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if a commit message follows conventional commit format.
|
|
31
|
+
*
|
|
32
|
+
* Format: type(scope?): description
|
|
33
|
+
* Examples: "feat: add login", "fix(auth): handle token expiry"
|
|
34
|
+
*/
|
|
35
|
+
export declare function isConventionalCommit(message: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a commit message is descriptive (meaningful length).
|
|
38
|
+
*
|
|
39
|
+
* A descriptive commit has at least 20 characters in the first line.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isDescriptiveCommit(message: string, minLength?: number): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Check if a commit message is vague (too short to be meaningful).
|
|
44
|
+
*
|
|
45
|
+
* Threshold: < 20 characters
|
|
46
|
+
*/
|
|
47
|
+
export declare function isVagueCommit(message: string, threshold?: number): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Calculate quality metrics for a set of commits.
|
|
50
|
+
*/
|
|
51
|
+
export declare function calculateQualityMetrics(commits: Commit[], vagueThreshold?: number, descriptiveMinLength?: number): QualityMetrics;
|
|
52
|
+
/**
|
|
53
|
+
* Generate a recommendation based on quality metrics.
|
|
54
|
+
*
|
|
55
|
+
* Returns 'sweep' if cleanup is recommended, 'maintain' if quality is acceptable.
|
|
56
|
+
*/
|
|
57
|
+
export declare function getRecommendation(metrics: QualityMetrics, hasSpirals: boolean): 'sweep' | 'maintain' | 'celebrate';
|
|
58
|
+
//# sourceMappingURL=quality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/analyzers/quality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,kBAAkB,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAmBD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAM7D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAW,GACrB,OAAO,CAGT;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAW,GACrB,OAAO,CAGT;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EAAE,EACjB,cAAc,GAAE,MAAW,EAC3B,oBAAoB,GAAE,MAAW,GAChC,cAAc,CAyChB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,OAAO,GAClB,OAAO,GAAG,UAAU,GAAG,WAAW,CAgBpC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Quality Metrics Analyzer (VIBE-045)
|
|
4
|
+
*
|
|
5
|
+
* Calculates commit message quality metrics:
|
|
6
|
+
* - Conventional commits percentage
|
|
7
|
+
* - Descriptive commits percentage
|
|
8
|
+
* - Vague commits percentage
|
|
9
|
+
*
|
|
10
|
+
* Proven algorithm from release-engineering retrospective.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isConventionalCommit = isConventionalCommit;
|
|
14
|
+
exports.isDescriptiveCommit = isDescriptiveCommit;
|
|
15
|
+
exports.isVagueCommit = isVagueCommit;
|
|
16
|
+
exports.calculateQualityMetrics = calculateQualityMetrics;
|
|
17
|
+
exports.getRecommendation = getRecommendation;
|
|
18
|
+
/**
|
|
19
|
+
* Conventional commit types as per spec.
|
|
20
|
+
*/
|
|
21
|
+
const CONVENTIONAL_TYPES = [
|
|
22
|
+
'feat',
|
|
23
|
+
'fix',
|
|
24
|
+
'docs',
|
|
25
|
+
'style',
|
|
26
|
+
'refactor',
|
|
27
|
+
'perf',
|
|
28
|
+
'test',
|
|
29
|
+
'build',
|
|
30
|
+
'ci',
|
|
31
|
+
'chore',
|
|
32
|
+
'revert',
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Check if a commit message follows conventional commit format.
|
|
36
|
+
*
|
|
37
|
+
* Format: type(scope?): description
|
|
38
|
+
* Examples: "feat: add login", "fix(auth): handle token expiry"
|
|
39
|
+
*/
|
|
40
|
+
function isConventionalCommit(message) {
|
|
41
|
+
const conventionalPattern = new RegExp(`^(${CONVENTIONAL_TYPES.join('|')})(\\([^)]+\\))?:\\s*.+`, 'i');
|
|
42
|
+
return conventionalPattern.test(message.trim());
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a commit message is descriptive (meaningful length).
|
|
46
|
+
*
|
|
47
|
+
* A descriptive commit has at least 20 characters in the first line.
|
|
48
|
+
*/
|
|
49
|
+
function isDescriptiveCommit(message, minLength = 20) {
|
|
50
|
+
const firstLine = message.trim().split('\n')[0];
|
|
51
|
+
return firstLine.length >= minLength;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if a commit message is vague (too short to be meaningful).
|
|
55
|
+
*
|
|
56
|
+
* Threshold: < 20 characters
|
|
57
|
+
*/
|
|
58
|
+
function isVagueCommit(message, threshold = 20) {
|
|
59
|
+
const firstLine = message.trim().split('\n')[0];
|
|
60
|
+
return firstLine.length < threshold;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Calculate quality metrics for a set of commits.
|
|
64
|
+
*/
|
|
65
|
+
function calculateQualityMetrics(commits, vagueThreshold = 20, descriptiveMinLength = 20) {
|
|
66
|
+
const total = commits.length;
|
|
67
|
+
if (total === 0) {
|
|
68
|
+
return {
|
|
69
|
+
totalCommits: 0,
|
|
70
|
+
conventionalCommits: { count: 0, percentage: 0 },
|
|
71
|
+
descriptiveCommits: { count: 0, percentage: 0, minLength: descriptiveMinLength },
|
|
72
|
+
vagueCommits: { count: 0, percentage: 0, threshold: vagueThreshold },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const conventionalCount = commits.filter((c) => isConventionalCommit(c.message)).length;
|
|
76
|
+
const descriptiveCount = commits.filter((c) => isDescriptiveCommit(c.message, descriptiveMinLength)).length;
|
|
77
|
+
const vagueCount = commits.filter((c) => isVagueCommit(c.message, vagueThreshold)).length;
|
|
78
|
+
return {
|
|
79
|
+
totalCommits: total,
|
|
80
|
+
conventionalCommits: {
|
|
81
|
+
count: conventionalCount,
|
|
82
|
+
percentage: Math.round((conventionalCount / total) * 1000) / 10,
|
|
83
|
+
},
|
|
84
|
+
descriptiveCommits: {
|
|
85
|
+
count: descriptiveCount,
|
|
86
|
+
percentage: Math.round((descriptiveCount / total) * 1000) / 10,
|
|
87
|
+
minLength: descriptiveMinLength,
|
|
88
|
+
},
|
|
89
|
+
vagueCommits: {
|
|
90
|
+
count: vagueCount,
|
|
91
|
+
percentage: Math.round((vagueCount / total) * 1000) / 10,
|
|
92
|
+
threshold: vagueThreshold,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Generate a recommendation based on quality metrics.
|
|
98
|
+
*
|
|
99
|
+
* Returns 'sweep' if cleanup is recommended, 'maintain' if quality is acceptable.
|
|
100
|
+
*/
|
|
101
|
+
function getRecommendation(metrics, hasSpirals) {
|
|
102
|
+
// Sweep if: >50% vague commits OR any debug spirals
|
|
103
|
+
if (metrics.vagueCommits.percentage > 50 || hasSpirals) {
|
|
104
|
+
return 'sweep';
|
|
105
|
+
}
|
|
106
|
+
// Celebrate if: >80% conventional AND <10% vague
|
|
107
|
+
if (metrics.conventionalCommits.percentage > 80 &&
|
|
108
|
+
metrics.vagueCommits.percentage < 10) {
|
|
109
|
+
return 'celebrate';
|
|
110
|
+
}
|
|
111
|
+
// Otherwise maintain current practices
|
|
112
|
+
return 'maintain';
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.js","sourceRoot":"","sources":["../../src/analyzers/quality.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Detection Algorithm (VIBE-046)
|
|
3
|
+
*
|
|
4
|
+
* Identifies work sessions from git history using configurable time gap threshold.
|
|
5
|
+
* Proven algorithm from release-engineering retrospective (46 sessions detected from 475 commits).
|
|
6
|
+
*
|
|
7
|
+
* Default threshold: 90 minutes between commits starts a new session.
|
|
8
|
+
*/
|
|
9
|
+
import { Commit } from '../types';
|
|
10
|
+
export interface DetectedSession {
|
|
11
|
+
sessionId: number;
|
|
12
|
+
startDate: Date;
|
|
13
|
+
endDate: Date;
|
|
14
|
+
durationMinutes: number;
|
|
15
|
+
commits: Commit[];
|
|
16
|
+
commitCount: number;
|
|
17
|
+
}
|
|
18
|
+
export interface SessionStats {
|
|
19
|
+
totalSessions: number;
|
|
20
|
+
totalCommits: number;
|
|
21
|
+
avgCommitsPerSession: number;
|
|
22
|
+
avgDurationMinutes: number;
|
|
23
|
+
medianDurationMinutes: number;
|
|
24
|
+
longestSessionMinutes: number;
|
|
25
|
+
shortestSessionMinutes: number;
|
|
26
|
+
}
|
|
27
|
+
export interface SessionDetectionResult {
|
|
28
|
+
sessions: DetectedSession[];
|
|
29
|
+
stats: SessionStats;
|
|
30
|
+
analysisRange: {
|
|
31
|
+
from: Date;
|
|
32
|
+
to: Date;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect work sessions from a list of commits.
|
|
37
|
+
*
|
|
38
|
+
* A new session starts when the gap between consecutive commits exceeds the threshold.
|
|
39
|
+
*
|
|
40
|
+
* @param commits List of commits (will be sorted by date)
|
|
41
|
+
* @param gapThresholdMinutes Minutes between commits to start new session (default: 90)
|
|
42
|
+
* @returns Session detection result with sessions and statistics
|
|
43
|
+
*/
|
|
44
|
+
export declare function detectSessions(commits: Commit[], gapThresholdMinutes?: number): SessionDetectionResult;
|
|
45
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/analyzers/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE;QACb,IAAI,EAAE,IAAI,CAAC;QACX,EAAE,EAAE,IAAI,CAAC;KACV,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EAAE,EACjB,mBAAmB,GAAE,MAAW,GAC/B,sBAAsB,CAkExB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Detection Algorithm (VIBE-046)
|
|
4
|
+
*
|
|
5
|
+
* Identifies work sessions from git history using configurable time gap threshold.
|
|
6
|
+
* Proven algorithm from release-engineering retrospective (46 sessions detected from 475 commits).
|
|
7
|
+
*
|
|
8
|
+
* Default threshold: 90 minutes between commits starts a new session.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.detectSessions = detectSessions;
|
|
12
|
+
/**
|
|
13
|
+
* Detect work sessions from a list of commits.
|
|
14
|
+
*
|
|
15
|
+
* A new session starts when the gap between consecutive commits exceeds the threshold.
|
|
16
|
+
*
|
|
17
|
+
* @param commits List of commits (will be sorted by date)
|
|
18
|
+
* @param gapThresholdMinutes Minutes between commits to start new session (default: 90)
|
|
19
|
+
* @returns Session detection result with sessions and statistics
|
|
20
|
+
*/
|
|
21
|
+
function detectSessions(commits, gapThresholdMinutes = 90) {
|
|
22
|
+
if (commits.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
sessions: [],
|
|
25
|
+
stats: {
|
|
26
|
+
totalSessions: 0,
|
|
27
|
+
totalCommits: 0,
|
|
28
|
+
avgCommitsPerSession: 0,
|
|
29
|
+
avgDurationMinutes: 0,
|
|
30
|
+
medianDurationMinutes: 0,
|
|
31
|
+
longestSessionMinutes: 0,
|
|
32
|
+
shortestSessionMinutes: 0,
|
|
33
|
+
},
|
|
34
|
+
analysisRange: {
|
|
35
|
+
from: new Date(),
|
|
36
|
+
to: new Date(),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Sort commits by date (oldest first)
|
|
41
|
+
const sortedCommits = [...commits].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
42
|
+
const gapThresholdMs = gapThresholdMinutes * 60 * 1000;
|
|
43
|
+
const sessions = [];
|
|
44
|
+
let currentSessionCommits = [];
|
|
45
|
+
let sessionId = 1;
|
|
46
|
+
for (const commit of sortedCommits) {
|
|
47
|
+
if (currentSessionCommits.length === 0) {
|
|
48
|
+
// Start first session
|
|
49
|
+
currentSessionCommits = [commit];
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const lastCommit = currentSessionCommits[currentSessionCommits.length - 1];
|
|
53
|
+
const gap = commit.date.getTime() - lastCommit.date.getTime();
|
|
54
|
+
if (gap > gapThresholdMs) {
|
|
55
|
+
// Gap exceeded - save current session and start new one
|
|
56
|
+
sessions.push(createSession(sessionId, currentSessionCommits));
|
|
57
|
+
sessionId++;
|
|
58
|
+
currentSessionCommits = [commit];
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Continue current session
|
|
62
|
+
currentSessionCommits.push(commit);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Don't forget the last session
|
|
67
|
+
if (currentSessionCommits.length > 0) {
|
|
68
|
+
sessions.push(createSession(sessionId, currentSessionCommits));
|
|
69
|
+
}
|
|
70
|
+
// Calculate statistics
|
|
71
|
+
const stats = calculateStats(sessions);
|
|
72
|
+
return {
|
|
73
|
+
sessions,
|
|
74
|
+
stats,
|
|
75
|
+
analysisRange: {
|
|
76
|
+
from: sortedCommits[0].date,
|
|
77
|
+
to: sortedCommits[sortedCommits.length - 1].date,
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createSession(sessionId, commits) {
|
|
82
|
+
const startDate = commits[0].date;
|
|
83
|
+
const endDate = commits[commits.length - 1].date;
|
|
84
|
+
const durationMinutes = (endDate.getTime() - startDate.getTime()) / (60 * 1000);
|
|
85
|
+
return {
|
|
86
|
+
sessionId,
|
|
87
|
+
startDate,
|
|
88
|
+
endDate,
|
|
89
|
+
durationMinutes: Math.round(durationMinutes * 10) / 10, // Round to 1 decimal
|
|
90
|
+
commits,
|
|
91
|
+
commitCount: commits.length,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function calculateStats(sessions) {
|
|
95
|
+
if (sessions.length === 0) {
|
|
96
|
+
return {
|
|
97
|
+
totalSessions: 0,
|
|
98
|
+
totalCommits: 0,
|
|
99
|
+
avgCommitsPerSession: 0,
|
|
100
|
+
avgDurationMinutes: 0,
|
|
101
|
+
medianDurationMinutes: 0,
|
|
102
|
+
longestSessionMinutes: 0,
|
|
103
|
+
shortestSessionMinutes: 0,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const totalCommits = sessions.reduce((sum, s) => sum + s.commitCount, 0);
|
|
107
|
+
const durations = sessions.map((s) => s.durationMinutes).sort((a, b) => a - b);
|
|
108
|
+
// Calculate median
|
|
109
|
+
const mid = Math.floor(durations.length / 2);
|
|
110
|
+
const medianDuration = durations.length % 2 === 0
|
|
111
|
+
? (durations[mid - 1] + durations[mid]) / 2
|
|
112
|
+
: durations[mid];
|
|
113
|
+
return {
|
|
114
|
+
totalSessions: sessions.length,
|
|
115
|
+
totalCommits,
|
|
116
|
+
avgCommitsPerSession: Math.round((totalCommits / sessions.length) * 10) / 10,
|
|
117
|
+
avgDurationMinutes: Math.round((durations.reduce((sum, d) => sum + d, 0) / durations.length) * 10) / 10,
|
|
118
|
+
medianDurationMinutes: Math.round(medianDuration * 10) / 10,
|
|
119
|
+
longestSessionMinutes: durations[durations.length - 1],
|
|
120
|
+
shortestSessionMinutes: durations[0],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/analyzers/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,10 @@ program.addCommand((0, commands_1.createTimelineCommand)());
|
|
|
22
22
|
program.addCommand((0, commands_1.createCacheCommand)());
|
|
23
23
|
program.addCommand((0, commands_1.createDashboardCommand)());
|
|
24
24
|
program.addCommand((0, commands_1.createSessionCommand)());
|
|
25
|
+
program.addCommand((0, commands_1.createInsightsCommand)());
|
|
26
|
+
program.addCommand((0, commands_1.createPipelineCommand)());
|
|
27
|
+
program.addCommand((0, commands_1.createSessionsCommand)());
|
|
28
|
+
program.addCommand((0, commands_1.createForensicsCommand)());
|
|
25
29
|
// Default behavior: if no subcommand, run analyze with passed options
|
|
26
30
|
// This maintains backwards compatibility with v1.x usage
|
|
27
31
|
program
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AACpC,yCAAyU;AAEzU,8DAA8D;AAC9D,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAE/C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,OAAO,CAAC,OAAO,CAAC;KAChB,uBAAuB,EAAE;KACzB,kBAAkB,EAAE,CAAC;AAExB,kBAAkB;AAClB,OAAO,CAAC,UAAU,CAAC,IAAA,+BAAoB,GAAE,CAAC,CAAC;AAC3C,OAAO,CAAC,UAAU,CAAC,IAAA,6BAAkB,GAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,IAAA,+BAAoB,GAAE,CAAC,CAAC;AAC3C,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAqB,GAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,IAAA,6BAAkB,GAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAqB,GAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,IAAA,6BAAkB,GAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,IAAA,iCAAsB,GAAE,CAAC,CAAC;AAC7C,OAAO,CAAC,UAAU,CAAC,IAAA,+BAAoB,GAAE,CAAC,CAAC;AAC3C,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAqB,GAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAqB,GAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAqB,GAAE,CAAC,CAAC;AAC5C,OAAO,CAAC,UAAU,CAAC,IAAA,iCAAsB,GAAE,CAAC,CAAC;AAE7C,sEAAsE;AACtE,yDAAyD;AACzD,OAAO;KACJ,MAAM,CAAC,gBAAgB,EAAE,8CAA8C,CAAC;KACxE,MAAM,CAAC,gBAAgB,EAAE,sCAAsC,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,yCAAyC,EAAE,UAAU,CAAC;KACpF,MAAM,CAAC,mBAAmB,EAAE,iBAAiB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KAC7D,MAAM,CAAC,eAAe,EAAE,qBAAqB,EAAE,KAAK,CAAC;KACrD,MAAM,CAAC,SAAS,EAAE,4CAA4C,EAAE,KAAK,CAAC;KACtE,MAAM,CAAC,qBAAqB,EAAE,4BAA4B,CAAC;KAC3D,MAAM,CAAC,cAAc,EAAE,mCAAmC,EAAE,KAAK,CAAC;KAClE,MAAM,CAAC,YAAY,EAAE,0DAA0D,CAAC;KAChF,MAAM,CAAC,iBAAiB,EAAE,yDAAyD,CAAC;KACpF,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,wDAAwD;IACxD,MAAM,IAAA,qBAAU,EAAC;QACf,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,oBAAoB,IAAI,OAAO,CAkB9C;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgYvE"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -22,6 +22,7 @@ const types_1 = require("../gamification/types");
|
|
|
22
22
|
const start_1 = require("./start");
|
|
23
23
|
const sessions_1 = require("../sessions");
|
|
24
24
|
const analysis_1 = require("../analysis");
|
|
25
|
+
const spiral_history_1 = require("../storage/spiral-history");
|
|
25
26
|
function createAnalyzeCommand() {
|
|
26
27
|
const cmd = new commander_1.Command('analyze')
|
|
27
28
|
.description('Analyze git history for vibe coding metrics')
|
|
@@ -150,6 +151,10 @@ async function runAnalyze(options) {
|
|
|
150
151
|
const trustPassRate = result.metrics.trustPassRate.value;
|
|
151
152
|
const reworkRatio = result.metrics.reworkRatio.value;
|
|
152
153
|
const spiralCount = result.fixChains.filter(fc => fc.isSpiral).length;
|
|
154
|
+
// Record detected spirals to history for personalized coaching
|
|
155
|
+
for (const chain of result.fixChains.filter(fc => fc.isSpiral)) {
|
|
156
|
+
(0, spiral_history_1.appendSpiral)(chain.pattern || 'OTHER', chain.component, chain.duration, chain.commits);
|
|
157
|
+
}
|
|
153
158
|
// Session comparison (if manual session was active via `start`)
|
|
154
159
|
if (session && format === 'terminal') {
|
|
155
160
|
const levelInfo = start_1.LEVEL_INFO[session.level];
|