@boshu2/vibe-check 1.0.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.
Files changed (69) hide show
  1. package/README.md +115 -0
  2. package/bin/vibe-check.js +2 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +90 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/git.d.ts +4 -0
  8. package/dist/git.d.ts.map +1 -0
  9. package/dist/git.js +64 -0
  10. package/dist/git.js.map +1 -0
  11. package/dist/metrics/flow.d.ts +3 -0
  12. package/dist/metrics/flow.d.ts.map +1 -0
  13. package/dist/metrics/flow.js +48 -0
  14. package/dist/metrics/flow.js.map +1 -0
  15. package/dist/metrics/index.d.ts +4 -0
  16. package/dist/metrics/index.d.ts.map +1 -0
  17. package/dist/metrics/index.js +156 -0
  18. package/dist/metrics/index.js.map +1 -0
  19. package/dist/metrics/rework.d.ts +3 -0
  20. package/dist/metrics/rework.d.ts.map +1 -0
  21. package/dist/metrics/rework.js +45 -0
  22. package/dist/metrics/rework.js.map +1 -0
  23. package/dist/metrics/spirals.d.ts +9 -0
  24. package/dist/metrics/spirals.d.ts.map +1 -0
  25. package/dist/metrics/spirals.js +153 -0
  26. package/dist/metrics/spirals.js.map +1 -0
  27. package/dist/metrics/trust.d.ts +3 -0
  28. package/dist/metrics/trust.d.ts.map +1 -0
  29. package/dist/metrics/trust.js +71 -0
  30. package/dist/metrics/trust.js.map +1 -0
  31. package/dist/metrics/velocity.d.ts +4 -0
  32. package/dist/metrics/velocity.d.ts.map +1 -0
  33. package/dist/metrics/velocity.js +77 -0
  34. package/dist/metrics/velocity.js.map +1 -0
  35. package/dist/output/index.d.ts +6 -0
  36. package/dist/output/index.d.ts.map +1 -0
  37. package/dist/output/index.js +25 -0
  38. package/dist/output/index.js.map +1 -0
  39. package/dist/output/json.d.ts +3 -0
  40. package/dist/output/json.d.ts.map +1 -0
  41. package/dist/output/json.js +52 -0
  42. package/dist/output/json.js.map +1 -0
  43. package/dist/output/markdown.d.ts +3 -0
  44. package/dist/output/markdown.d.ts.map +1 -0
  45. package/dist/output/markdown.js +90 -0
  46. package/dist/output/markdown.js.map +1 -0
  47. package/dist/output/terminal.d.ts +3 -0
  48. package/dist/output/terminal.d.ts.map +1 -0
  49. package/dist/output/terminal.js +90 -0
  50. package/dist/output/terminal.js.map +1 -0
  51. package/dist/types.d.ts +63 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +42 -0
  56. package/src/cli.ts +96 -0
  57. package/src/git.ts +72 -0
  58. package/src/metrics/flow.ts +53 -0
  59. package/src/metrics/index.ts +169 -0
  60. package/src/metrics/rework.ts +45 -0
  61. package/src/metrics/spirals.ts +173 -0
  62. package/src/metrics/trust.ts +80 -0
  63. package/src/metrics/velocity.ts +86 -0
  64. package/src/output/index.ts +20 -0
  65. package/src/output/json.ts +51 -0
  66. package/src/output/markdown.ts +111 -0
  67. package/src/output/terminal.ts +109 -0
  68. package/src/types.ts +68 -0
  69. package/tsconfig.json +19 -0
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.formatTerminal = formatTerminal;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const date_fns_1 = require("date-fns");
9
+ function formatTerminal(result) {
10
+ const lines = [];
11
+ // Header
12
+ lines.push('');
13
+ lines.push(chalk_1.default.bold.cyan('='.repeat(64)));
14
+ lines.push(chalk_1.default.bold.cyan(' VIBE-CHECK RESULTS'));
15
+ lines.push(chalk_1.default.bold.cyan('='.repeat(64)));
16
+ // Period info
17
+ const fromStr = (0, date_fns_1.format)(result.period.from, 'MMM d, yyyy');
18
+ const toStr = (0, date_fns_1.format)(result.period.to, 'MMM d, yyyy');
19
+ lines.push('');
20
+ lines.push(chalk_1.default.gray(` Period: ${fromStr} - ${toStr} (${result.period.activeHours}h active)`));
21
+ lines.push(chalk_1.default.gray(` Commits: ${result.commits.total} total (${result.commits.feat} feat, ${result.commits.fix} fix, ${result.commits.docs} docs, ${result.commits.other} other)`));
22
+ // Metrics table
23
+ lines.push('');
24
+ lines.push(chalk_1.default.bold.white(' METRIC VALUE RATING'));
25
+ lines.push(chalk_1.default.gray(' ' + '-'.repeat(50)));
26
+ const metrics = [
27
+ { name: 'Iteration Velocity', metric: result.metrics.iterationVelocity },
28
+ { name: 'Rework Ratio', metric: result.metrics.reworkRatio },
29
+ { name: 'Trust Pass Rate', metric: result.metrics.trustPassRate },
30
+ { name: 'Debug Spiral Duration', metric: result.metrics.debugSpiralDuration },
31
+ { name: 'Flow Efficiency', metric: result.metrics.flowEfficiency },
32
+ ];
33
+ for (const { name, metric } of metrics) {
34
+ const valueStr = `${metric.value}${metric.unit}`.padEnd(10);
35
+ const ratingStr = formatRating(metric.rating);
36
+ lines.push(` ${name.padEnd(26)} ${valueStr} ${ratingStr}`);
37
+ }
38
+ // Overall rating
39
+ lines.push('');
40
+ lines.push(chalk_1.default.bold.cyan('-'.repeat(64)));
41
+ lines.push(` ${chalk_1.default.bold('OVERALL:')} ${formatOverallRating(result.overall)}`);
42
+ lines.push(chalk_1.default.bold.cyan('-'.repeat(64)));
43
+ // Debug spirals
44
+ if (result.fixChains.length > 0) {
45
+ lines.push('');
46
+ lines.push(chalk_1.default.bold.yellow(` DEBUG SPIRALS (${result.fixChains.length} detected):`));
47
+ for (const chain of result.fixChains) {
48
+ const patternStr = chain.pattern ? ` (${chain.pattern})` : '';
49
+ lines.push(chalk_1.default.yellow(` - ${chain.component}: ${chain.commits} commits, ${chain.duration}m${patternStr}`));
50
+ }
51
+ }
52
+ // Patterns
53
+ if (result.patterns.total > 0) {
54
+ lines.push('');
55
+ lines.push(chalk_1.default.bold.magenta(' PATTERNS:'));
56
+ for (const [pattern, count] of Object.entries(result.patterns.categories)) {
57
+ const tracerNote = pattern !== 'OTHER' ? ' (tracer available)' : '';
58
+ lines.push(chalk_1.default.magenta(` - ${pattern}: ${count} fixes${tracerNote}`));
59
+ }
60
+ }
61
+ lines.push('');
62
+ lines.push(chalk_1.default.bold.cyan('='.repeat(64)));
63
+ lines.push('');
64
+ return lines.join('\n');
65
+ }
66
+ function formatRating(rating) {
67
+ switch (rating) {
68
+ case 'elite':
69
+ return chalk_1.default.green.bold('ELITE');
70
+ case 'high':
71
+ return chalk_1.default.blue.bold('HIGH');
72
+ case 'medium':
73
+ return chalk_1.default.yellow.bold('MEDIUM');
74
+ case 'low':
75
+ return chalk_1.default.red.bold('LOW');
76
+ }
77
+ }
78
+ function formatOverallRating(rating) {
79
+ switch (rating) {
80
+ case 'ELITE':
81
+ return chalk_1.default.green.bold('ELITE');
82
+ case 'HIGH':
83
+ return chalk_1.default.blue.bold('HIGH');
84
+ case 'MEDIUM':
85
+ return chalk_1.default.yellow.bold('MEDIUM');
86
+ case 'LOW':
87
+ return chalk_1.default.red.bold('LOW');
88
+ }
89
+ }
90
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/output/terminal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ export type Rating = 'elite' | 'high' | 'medium' | 'low';
2
+ export type OutputFormat = 'terminal' | 'json' | 'markdown';
3
+ export type OverallRating = 'ELITE' | 'HIGH' | 'MEDIUM' | 'LOW';
4
+ export interface Commit {
5
+ hash: string;
6
+ date: Date;
7
+ message: string;
8
+ type: 'feat' | 'fix' | 'docs' | 'chore' | 'refactor' | 'test' | 'style' | 'other';
9
+ scope: string | null;
10
+ author: string;
11
+ }
12
+ export interface MetricResult {
13
+ value: number;
14
+ unit: string;
15
+ rating: Rating;
16
+ description: string;
17
+ }
18
+ export interface FixChain {
19
+ component: string;
20
+ commits: number;
21
+ duration: number;
22
+ isSpiral: boolean;
23
+ pattern: string | null;
24
+ firstCommit: Date;
25
+ lastCommit: Date;
26
+ }
27
+ export interface PatternSummary {
28
+ categories: Record<string, number>;
29
+ total: number;
30
+ tracerAvailable: number;
31
+ }
32
+ export interface VibeCheckResult {
33
+ period: {
34
+ from: Date;
35
+ to: Date;
36
+ activeHours: number;
37
+ };
38
+ commits: {
39
+ total: number;
40
+ feat: number;
41
+ fix: number;
42
+ docs: number;
43
+ other: number;
44
+ };
45
+ metrics: {
46
+ iterationVelocity: MetricResult;
47
+ reworkRatio: MetricResult;
48
+ trustPassRate: MetricResult;
49
+ debugSpiralDuration: MetricResult;
50
+ flowEfficiency: MetricResult;
51
+ };
52
+ fixChains: FixChain[];
53
+ patterns: PatternSummary;
54
+ overall: OverallRating;
55
+ }
56
+ export interface CliOptions {
57
+ since?: string;
58
+ until?: string;
59
+ format: OutputFormat;
60
+ repo: string;
61
+ verbose: boolean;
62
+ }
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AACzD,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEhE,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,IAAI,CAAC;IAClB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE;QACN,IAAI,EAAE,IAAI,CAAC;QACX,EAAE,EAAE,IAAI,CAAC;QACT,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE;QACP,iBAAiB,EAAE,YAAY,CAAC;QAChC,WAAW,EAAE,YAAY,CAAC;QAC1B,aAAa,EAAE,YAAY,CAAC;QAC5B,mBAAmB,EAAE,YAAY,CAAC;QAClC,cAAc,EAAE,YAAY,CAAC;KAC9B,CAAC;IACF,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@boshu2/vibe-check",
3
+ "version": "1.0.0",
4
+ "description": "Measure vibe coding effectiveness with git commit analysis",
5
+ "main": "dist/cli.js",
6
+ "bin": {
7
+ "vibe-check": "./bin/vibe-check.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "ts-node src/cli.ts",
12
+ "start": "node dist/cli.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "vibe-coding",
17
+ "git",
18
+ "metrics",
19
+ "developer-productivity",
20
+ "cli"
21
+ ],
22
+ "author": "bodefuller",
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/boshu2/vibe-check"
27
+ },
28
+ "dependencies": {
29
+ "chalk": "^4.1.2",
30
+ "commander": "^12.1.0",
31
+ "date-fns": "^3.6.0",
32
+ "simple-git": "^3.27.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.17.6",
36
+ "typescript": "^5.6.3",
37
+ "ts-node": "^10.9.2"
38
+ },
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ }
42
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import { getCommits, isGitRepo } from './git';
6
+ import { analyzeCommits } from './metrics';
7
+ import { formatOutput } from './output';
8
+ import { OutputFormat } from './types';
9
+
10
+ const program = new Command();
11
+
12
+ program
13
+ .name('vibe-check')
14
+ .description('Measure vibe coding effectiveness with git commit analysis')
15
+ .version('1.0.0')
16
+ .option('--since <date>', 'Start date for analysis (e.g., "1 week ago", "2025-11-01")')
17
+ .option('--until <date>', 'End date for analysis (default: now)')
18
+ .option(
19
+ '-f, --format <type>',
20
+ 'Output format: terminal, json, markdown',
21
+ 'terminal'
22
+ )
23
+ .option('-r, --repo <path>', 'Repository path (default: current directory)', process.cwd())
24
+ .option('-v, --verbose', 'Show verbose output', false)
25
+ .action(async (options) => {
26
+ try {
27
+ const { since, until, format, repo, verbose } = options;
28
+
29
+ // Validate format
30
+ const validFormats: OutputFormat[] = ['terminal', 'json', 'markdown'];
31
+ if (!validFormats.includes(format)) {
32
+ console.error(chalk.red(`Invalid format: ${format}`));
33
+ console.error(chalk.gray(`Valid formats: ${validFormats.join(', ')}`));
34
+ process.exit(1);
35
+ }
36
+
37
+ // Check if repo is valid
38
+ if (!(await isGitRepo(repo))) {
39
+ console.error(chalk.red(`Not a git repository: ${repo}`));
40
+ process.exit(1);
41
+ }
42
+
43
+ if (verbose) {
44
+ console.error(chalk.gray(`Analyzing repository: ${repo}`));
45
+ if (since) console.error(chalk.gray(`Since: ${since}`));
46
+ if (until) console.error(chalk.gray(`Until: ${until}`));
47
+ }
48
+
49
+ // Get commits
50
+ const commits = await getCommits(repo, since, until);
51
+
52
+ if (commits.length === 0) {
53
+ if (format === 'terminal') {
54
+ console.log(chalk.yellow('\nNo commits found in the specified range.\n'));
55
+ if (!since) {
56
+ console.log(chalk.gray('Try specifying a date range:'));
57
+ console.log(chalk.gray(' vibe-check --since "1 week ago"'));
58
+ console.log(chalk.gray(' vibe-check --since "2025-11-01"'));
59
+ }
60
+ } else if (format === 'json') {
61
+ console.log(JSON.stringify({ error: 'No commits found', commits: 0 }));
62
+ } else {
63
+ console.log('# Vibe-Check Report\n\nNo commits found in the specified range.');
64
+ }
65
+ process.exit(0);
66
+ }
67
+
68
+ if (verbose) {
69
+ console.error(chalk.gray(`Found ${commits.length} commits`));
70
+ }
71
+
72
+ // Analyze commits
73
+ const result = analyzeCommits(commits);
74
+
75
+ // Output result
76
+ const output = formatOutput(result, format as OutputFormat);
77
+ console.log(output);
78
+
79
+ // Exit with appropriate code based on overall rating
80
+ if (result.overall === 'LOW') {
81
+ process.exit(1);
82
+ }
83
+ } catch (error) {
84
+ if (error instanceof Error) {
85
+ console.error(chalk.red(`Error: ${error.message}`));
86
+ if (options.verbose) {
87
+ console.error(chalk.gray(error.stack));
88
+ }
89
+ } else {
90
+ console.error(chalk.red('An unexpected error occurred'));
91
+ }
92
+ process.exit(1);
93
+ }
94
+ });
95
+
96
+ program.parse();
package/src/git.ts ADDED
@@ -0,0 +1,72 @@
1
+ import simpleGit, { SimpleGit, LogResult, DefaultLogFields } from 'simple-git';
2
+ import { Commit } from './types';
3
+
4
+ const COMMIT_TYPES = ['feat', 'fix', 'docs', 'chore', 'refactor', 'test', 'style'] as const;
5
+
6
+ export async function getCommits(
7
+ repoPath: string,
8
+ since?: string,
9
+ until?: string
10
+ ): Promise<Commit[]> {
11
+ const git: SimpleGit = simpleGit(repoPath);
12
+
13
+ // Build options for git log
14
+ const options: Record<string, string | number | boolean> = {};
15
+
16
+ if (since) {
17
+ options['--since'] = since;
18
+ }
19
+ if (until) {
20
+ options['--until'] = until;
21
+ }
22
+
23
+ try {
24
+ const log: LogResult<DefaultLogFields> = await git.log(options);
25
+
26
+ return log.all.map((entry) => parseCommit(entry));
27
+ } catch (error) {
28
+ if (error instanceof Error) {
29
+ throw new Error(`Failed to read git log: ${error.message}`);
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+
35
+ function parseCommit(entry: DefaultLogFields): Commit {
36
+ const { hash, date, message, author_name } = entry;
37
+
38
+ // Parse conventional commit format: type(scope): description
39
+ const conventionalMatch = message.match(/^(\w+)(?:\(([^)]+)\))?:\s*(.+)/);
40
+
41
+ let type: Commit['type'] = 'other';
42
+ let scope: string | null = null;
43
+
44
+ if (conventionalMatch) {
45
+ const [, rawType, rawScope] = conventionalMatch;
46
+ const normalizedType = rawType.toLowerCase();
47
+
48
+ if (COMMIT_TYPES.includes(normalizedType as typeof COMMIT_TYPES[number])) {
49
+ type = normalizedType as Commit['type'];
50
+ }
51
+ scope = rawScope || null;
52
+ }
53
+
54
+ return {
55
+ hash: hash.substring(0, 7),
56
+ date: new Date(date),
57
+ message: message.split('\n')[0], // First line only
58
+ type,
59
+ scope,
60
+ author: author_name,
61
+ };
62
+ }
63
+
64
+ export async function isGitRepo(repoPath: string): Promise<boolean> {
65
+ const git: SimpleGit = simpleGit(repoPath);
66
+ try {
67
+ await git.status();
68
+ return true;
69
+ } catch {
70
+ return false;
71
+ }
72
+ }
@@ -0,0 +1,53 @@
1
+ import { FixChain, MetricResult, Rating } from '../types';
2
+
3
+ export function calculateFlowEfficiency(
4
+ activeMinutes: number,
5
+ spirals: FixChain[]
6
+ ): MetricResult {
7
+ if (activeMinutes === 0) {
8
+ return {
9
+ value: 100,
10
+ unit: '%',
11
+ rating: 'elite',
12
+ description: 'No active time recorded',
13
+ };
14
+ }
15
+
16
+ const spiralMinutes = spirals
17
+ .filter((s) => s.isSpiral)
18
+ .reduce((sum, s) => sum + s.duration, 0);
19
+
20
+ const efficiency = ((activeMinutes - spiralMinutes) / activeMinutes) * 100;
21
+ const clampedEfficiency = Math.max(0, Math.min(100, efficiency));
22
+ const rating = getRating(clampedEfficiency);
23
+
24
+ return {
25
+ value: Math.round(clampedEfficiency),
26
+ unit: '%',
27
+ rating,
28
+ description: getDescription(rating, spiralMinutes),
29
+ };
30
+ }
31
+
32
+ function getRating(efficiency: number): Rating {
33
+ if (efficiency > 90) return 'elite';
34
+ if (efficiency >= 75) return 'high';
35
+ if (efficiency >= 50) return 'medium';
36
+ return 'low';
37
+ }
38
+
39
+ function getDescription(rating: Rating, spiralMinutes: number): string {
40
+ const spiralText =
41
+ spiralMinutes > 0 ? `${spiralMinutes}m spent in debug spirals` : 'No debug spirals';
42
+
43
+ switch (rating) {
44
+ case 'elite':
45
+ return `${spiralText}. Excellent productive flow`;
46
+ case 'high':
47
+ return `${spiralText}. Good balance`;
48
+ case 'medium':
49
+ return `${spiralText}. Significant debugging overhead`;
50
+ case 'low':
51
+ return `${spiralText}. More debugging than building`;
52
+ }
53
+ }
@@ -0,0 +1,169 @@
1
+ import { Commit, VibeCheckResult, OverallRating, Rating } from '../types';
2
+ import { calculateIterationVelocity, calculateActiveHours } from './velocity';
3
+ import { calculateReworkRatio } from './rework';
4
+ import { calculateTrustPassRate } from './trust';
5
+ import {
6
+ detectFixChains,
7
+ calculateDebugSpiralDuration,
8
+ calculatePatternSummary,
9
+ } from './spirals';
10
+ import { calculateFlowEfficiency } from './flow';
11
+
12
+ export function analyzeCommits(commits: Commit[]): VibeCheckResult {
13
+ if (commits.length === 0) {
14
+ return emptyResult();
15
+ }
16
+
17
+ // Sort commits by date
18
+ const sorted = [...commits].sort((a, b) => a.date.getTime() - b.date.getTime());
19
+ const from = sorted[0].date;
20
+ const to = sorted[sorted.length - 1].date;
21
+ const activeHours = calculateActiveHours(sorted);
22
+
23
+ // Count commit types
24
+ const commitCounts = countCommitTypes(sorted);
25
+
26
+ // Detect fix chains
27
+ const fixChains = detectFixChains(sorted);
28
+
29
+ // Calculate all metrics
30
+ const iterationVelocity = calculateIterationVelocity(sorted);
31
+ const reworkRatio = calculateReworkRatio(sorted);
32
+ const trustPassRate = calculateTrustPassRate(sorted);
33
+ const debugSpiralDuration = calculateDebugSpiralDuration(fixChains);
34
+ const flowEfficiency = calculateFlowEfficiency(activeHours * 60, fixChains);
35
+
36
+ // Calculate pattern summary
37
+ const patterns = calculatePatternSummary(fixChains);
38
+
39
+ // Determine overall rating
40
+ const overall = calculateOverallRating([
41
+ iterationVelocity.rating,
42
+ reworkRatio.rating,
43
+ trustPassRate.rating,
44
+ debugSpiralDuration.rating,
45
+ flowEfficiency.rating,
46
+ ]);
47
+
48
+ return {
49
+ period: {
50
+ from,
51
+ to,
52
+ activeHours: Math.round(activeHours * 10) / 10,
53
+ },
54
+ commits: commitCounts,
55
+ metrics: {
56
+ iterationVelocity,
57
+ reworkRatio,
58
+ trustPassRate,
59
+ debugSpiralDuration,
60
+ flowEfficiency,
61
+ },
62
+ fixChains,
63
+ patterns,
64
+ overall,
65
+ };
66
+ }
67
+
68
+ function countCommitTypes(commits: Commit[]): VibeCheckResult['commits'] {
69
+ const counts = {
70
+ total: commits.length,
71
+ feat: 0,
72
+ fix: 0,
73
+ docs: 0,
74
+ other: 0,
75
+ };
76
+
77
+ for (const commit of commits) {
78
+ switch (commit.type) {
79
+ case 'feat':
80
+ counts.feat++;
81
+ break;
82
+ case 'fix':
83
+ counts.fix++;
84
+ break;
85
+ case 'docs':
86
+ counts.docs++;
87
+ break;
88
+ default:
89
+ counts.other++;
90
+ }
91
+ }
92
+
93
+ return counts;
94
+ }
95
+
96
+ function calculateOverallRating(ratings: Rating[]): OverallRating {
97
+ const scores: Record<Rating, number> = {
98
+ elite: 4,
99
+ high: 3,
100
+ medium: 2,
101
+ low: 1,
102
+ };
103
+
104
+ const avgScore =
105
+ ratings.reduce((sum, r) => sum + scores[r], 0) / ratings.length;
106
+
107
+ if (avgScore >= 3.5) return 'ELITE';
108
+ if (avgScore >= 2.5) return 'HIGH';
109
+ if (avgScore >= 1.5) return 'MEDIUM';
110
+ return 'LOW';
111
+ }
112
+
113
+ function emptyResult(): VibeCheckResult {
114
+ return {
115
+ period: {
116
+ from: new Date(),
117
+ to: new Date(),
118
+ activeHours: 0,
119
+ },
120
+ commits: {
121
+ total: 0,
122
+ feat: 0,
123
+ fix: 0,
124
+ docs: 0,
125
+ other: 0,
126
+ },
127
+ metrics: {
128
+ iterationVelocity: {
129
+ value: 0,
130
+ unit: 'commits/hour',
131
+ rating: 'low',
132
+ description: 'No commits found',
133
+ },
134
+ reworkRatio: {
135
+ value: 0,
136
+ unit: '%',
137
+ rating: 'elite',
138
+ description: 'No commits found',
139
+ },
140
+ trustPassRate: {
141
+ value: 100,
142
+ unit: '%',
143
+ rating: 'elite',
144
+ description: 'No commits found',
145
+ },
146
+ debugSpiralDuration: {
147
+ value: 0,
148
+ unit: 'min',
149
+ rating: 'elite',
150
+ description: 'No debug spirals detected',
151
+ },
152
+ flowEfficiency: {
153
+ value: 100,
154
+ unit: '%',
155
+ rating: 'elite',
156
+ description: 'No active time recorded',
157
+ },
158
+ },
159
+ fixChains: [],
160
+ patterns: {
161
+ categories: {},
162
+ total: 0,
163
+ tracerAvailable: 0,
164
+ },
165
+ overall: 'HIGH',
166
+ };
167
+ }
168
+
169
+ export { calculateActiveHours } from './velocity';
@@ -0,0 +1,45 @@
1
+ import { Commit, MetricResult, Rating } from '../types';
2
+
3
+ export function calculateReworkRatio(commits: Commit[]): MetricResult {
4
+ if (commits.length === 0) {
5
+ return {
6
+ value: 0,
7
+ unit: '%',
8
+ rating: 'elite',
9
+ description: 'No commits found',
10
+ };
11
+ }
12
+
13
+ const fixCommits = commits.filter((c) => c.type === 'fix').length;
14
+ const ratio = (fixCommits / commits.length) * 100;
15
+ const rating = getRating(ratio);
16
+
17
+ return {
18
+ value: Math.round(ratio),
19
+ unit: '%',
20
+ rating,
21
+ description: getDescription(rating, fixCommits, commits.length),
22
+ };
23
+ }
24
+
25
+ function getRating(ratio: number): Rating {
26
+ if (ratio < 30) return 'elite';
27
+ if (ratio < 50) return 'high';
28
+ if (ratio < 70) return 'medium';
29
+ return 'low';
30
+ }
31
+
32
+ function getDescription(rating: Rating, fixes: number, total: number): string {
33
+ const fixText = `${fixes}/${total} commits are fixes`;
34
+
35
+ switch (rating) {
36
+ case 'elite':
37
+ return `${fixText}. Mostly forward progress`;
38
+ case 'high':
39
+ return `${fixText}. Normal for complex work`;
40
+ case 'medium':
41
+ return `${fixText}. Consider validating assumptions before coding`;
42
+ case 'low':
43
+ return `${fixText}. High rework, stop and reassess approach`;
44
+ }
45
+ }