vibe-coding-stats
Version:
TypeScript library for analyzing GitHub repository activity and estimating developer coding metrics. Optimized for vibe coding workflows with session-based commit patterns.
365 lines (286 loc) • 10.7 kB
Markdown
# vibe-coding-stats
[](https://www.npmjs.com/package/vibe-coding-stats)
[](https://opensource.org/licenses/MIT)
A TypeScript library for analyzing GitHub repositories to estimate developer activity in a more "human" way — including hours spent coding, coding sessions, and break patterns.
**Optimized for vibe coding workflows** with session-based commit patterns. The default session timeout (45 minutes) and assumptions work best for repositories with frequent, smaller commits rather than infrequent large commits.
## Features
- 📊 **Coding Metrics**: Estimate total hours, sessions, streaks, and commits
- 📈 **Productivity Insights**: Track longest sessions, average session duration, most productive day of week, minimum break time, and commit frequency patterns
- 🌍 **Timezone Support**: Convert timestamps to any timezone
- 🤖 **Bot Filtering**: Automatically exclude bot commits
- 📅 **Flexible Date Ranges**: Analyze specific time periods
- 🎯 **Author Filtering**: Focus on specific contributors
- 🚀 **TypeScript First**: Full type safety and IntelliSense support
## Installation
```bash
npm install vibe-coding-stats
```
## Quick Start
```typescript
import { getRepoStats } from 'vibe-coding-stats';
// Minimal example - uses default settings
const result = await getRepoStats({ repo: 'coffee-cpu/vibe-coding-stats' });
// All aggregate metrics are in the 'totals' property
console.log(`Total development time: ${result.totals.totalHours} hours`);
console.log(`Across ${result.totals.devDays} days of development`);
console.log(`In ${result.totals.sessionsCount} coding sessions`);
```
## Response Structure
The `getRepoStats()` function returns a structured object:
```typescript
{
repo: string; // Repository identifier (e.g., "owner/repo")
period: {
since?: string;
until?: string;
};
config: {
sessionTimeoutMin: number;
firstCommitBonusMin: number;
timezone: string;
};
totals: { // ← Aggregate statistics across all authors
totalHours: number;
sessionsCount: number;
devDays: number;
totalCommits: number;
avgCommitsPerSession: number;
avgSessionsPerDay: number;
longestSessionHours: number;
avgSessionHours: number;
mostProductiveDayOfWeek?: string;
longestStreakDays: number;
minTimeBetweenSessionsMin?: number;
avgMinutesBetweenCommits?: number;
maxMinutesBetweenCommits?: number;
};
perAuthor: Array<{ // ← Per-author breakdown
author: string;
email: string;
totalHours: number;
sessionsCount: number;
devDays: number;
totalCommits: number;
avgCommitsPerSession: number;
avgSessionsPerDay: number;
longestSessionHours: number;
avgSessionHours: number;
mostProductiveDayOfWeek?: string;
longestStreakDays: number;
minTimeBetweenSessionsMin?: number;
avgMinutesBetweenCommits?: number;
maxMinutesBetweenCommits?: number;
}>;
perDay: Array<{ // ← Daily breakdown
date: string;
totalHours: number;
sessionsCount: number;
totalCommits: number;
authors: string[];
}>;
}
```
## Usage Examples
### Personal Coding Stats for 2024
```typescript
const result = await getRepoStats(
{ repo: 'coffee-cpu/vibe-coding-stats' },
{
since: '2024-01-01',
until: '2024-12-31',
authors: ['coffee-cpu'],
}
);
console.log(`In 2024, you coded for ${result.totals.totalHours} hours`);
console.log(`Longest session: ${result.totals.longestSessionHours} hours`);
console.log(`Most productive day: ${result.totals.mostProductiveDayOfWeek}`);
```
### Last 30 Days with Timezone
```typescript
const result = await getRepoStats(
{ url: 'https://github.com/coffee-cpu/vibe-coding-stats' },
{
since: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
timezone: 'America/New_York',
}
);
// Access totals
console.log(`Total hours (last 30 days): ${result.totals.totalHours}`);
console.log(`Sessions: ${result.totals.sessionsCount}`);
// View per-author breakdown
result.perAuthor.forEach(author => {
console.log(`${author.author}: ${author.totalHours}h, ${author.sessionsCount} sessions`);
if (author.avgMinutesBetweenCommits) {
console.log(` Avg commit gap: ${author.avgMinutesBetweenCommits.toFixed(1)} minutes`);
}
});
```
### With GitHub Token (Higher Rate Limits)
```typescript
const result = await getRepoStats(
{ repo: 'coffee-cpu/vibe-coding-stats' },
{
githubToken: process.env.GITHUB_TOKEN,
}
);
console.log(`Total hours: ${result.totals.totalHours}`);
console.log(`Commits: ${result.totals.totalCommits}`);
```
## API Reference
### `getRepoStats(repoInput, options?)`
Main function to get repository statistics.
**Parameters:**
1. `repoInput` - Repository identifier (required):
```typescript
{ repo: string } // "owner/repo"
// OR
{ url: string } // "https://github.com/owner/repo"
```
2. `options` - Configuration options (optional):
```typescript
{
// Session configuration
sessionTimeoutMin?: number; // default: 45
firstCommitBonusMin?: number; // default: 15
// Filters
since?: string | Date;
until?: string | Date;
authors?: string[];
excludeBots?: boolean; // default: true
excludeMergeCommits?: boolean; // default: false
// Settings
timezone?: string; // default: "UTC"
githubToken?: string; // optional, for higher rate limits
perPage?: number; // default: 100
maxPages?: number;
// Caching
cache?: 'memory' | 'none'; // default: 'memory'
cacheTTLms?: number; // default: 900000 (15 min)
}
```
**Returns:**
```typescript
Promise<{
repo: string;
period: { since?: string; until?: string };
config: {
sessionTimeoutMin: number;
firstCommitBonusMin: number;
timezone: string;
};
totals: {
totalHours: number;
sessionsCount: number;
devDays: number;
totalCommits: number;
avgCommitsPerSession: number;
avgSessionsPerDay: number;
longestSessionHours: number;
avgSessionHours: number;
mostProductiveDayOfWeek?: string;
longestStreakDays: number;
minTimeBetweenSessionsMin?: number;
avgMinutesBetweenCommits?: number;
maxMinutesBetweenCommits?: number;
};
perAuthor: AuthorStats[];
perDay: DayStats[];
}>
```
### Cache Utilities
```typescript
import { clearCache, getCacheSize } from 'vibe-coding-stats';
// Clear the in-memory cache
clearCache();
// Get number of cached entries
const size = getCacheSize();
console.log(`Cache has ${size} entries`);
```
## Common Pitfalls
### GitHub Rate Limiting
Unauthenticated requests are limited to 60 per hour. For production use or analyzing large repositories, provide a GitHub token:
```typescript
const result = await getRepoStats(
{ repo: 'owner/repo' },
{ githubToken: process.env.GITHUB_TOKEN }
);
```
Get a GitHub token from: https://github.com/settings/tokens (requires `public_repo` scope for public repos)
## TypeScript Support
The package includes full TypeScript type definitions:
```typescript
import { getRepoStats, type RepoStats } from 'vibe-coding-stats';
async function fetchStats(): Promise<RepoStats> {
const result = await getRepoStats(
{ repo: 'owner/repo' },
{ sessionTimeoutMin: 45 }
);
// TypeScript knows the structure of result.totals
const hours: number = result.totals.totalHours;
return result;
}
```
You can also import individual types:
```typescript
import type {
RepoStats,
AuthorStats,
DayStats,
StatsOptions
} from 'vibe-coding-stats';
```
## How It Works
### Session Detection
A **coding session** is defined as a series of commits by the same author where consecutive commits are no more than `sessionTimeoutMin` minutes apart (default: 45 minutes).
Session duration is calculated as:
```
duration = (lastCommitTime - firstCommitTime) + firstCommitBonusMin
```
For single-commit sessions:
```
duration = firstCommitBonusMin (default: 15 minutes)
```
### Cross-Midnight Sessions
Sessions can span across midnight and are treated as a single continuous session. The session is assigned to the day it started (based on the first commit's timestamp in the configured timezone).
### Metrics Definitions
- **totalHours**: Sum of all session durations
- **sessionsCount**: Number of distinct coding sessions
- **devDays**: Number of calendar days with at least one commit
- **totalCommits**: Total commit count after filtering
- **avgCommitsPerSession**: totalCommits / sessionsCount
- **avgSessionsPerDay**: sessionsCount / devDays
- **longestSessionHours**: Duration of the longest single coding session
- **avgSessionHours**: Average duration of coding sessions (totalHours / sessionsCount)
- **mostProductiveDayOfWeek**: Day of week with most total coding hours (e.g., "Monday")
- **longestStreakDays**: Longest consecutive days with commits
- **minTimeBetweenSessionsMin**: Minimum time between consecutive sessions by the same author (in minutes)
- **avgMinutesBetweenCommits**: Average time in minutes between consecutive commits within sessions (undefined if all sessions are single-commit)
- **maxMinutesBetweenCommits**: Maximum time in minutes between consecutive commits within sessions (undefined if all sessions are single-commit)
## Limitations
This library provides **approximate** coding activity metrics based on commit history:
- **Only committed work is counted** - Work in progress, uncommitted changes, and experimental branches are not captured
- **Rebases and force-pushes may affect accuracy** - Git history rewriting can alter timestamps and commit counts
- **Doesn't capture non-coding time** - Code review, meetings, debugging, research, and thinking time are not measured
- **Session detection is heuristic** - The timeout-based approach may occasionally split or merge sessions incorrectly
- **Author identification** - Based on Git author info, which can be inconsistent across different machines or configurations
## Demo
Try the live demo at: [https://coffee-cpu.github.io/vibe-coding-stats/](https://coffee-cpu.github.io/vibe-coding-stats/)
## Development
```bash
# Install dependencies
npm install
# Build the library
npm run build -w packages/core
# Run tests
npm test -w packages/core
# Run tests in watch mode
npm run test:watch -w packages/core
# Type check
npm run type-check
# Run demo locally
npm run dev -w demo
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT