repo-wrapped 0.0.6 → 0.0.9
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/.github/agents/complete.agent.md +257 -0
- package/.github/agents/feature-scaffold.agent.md +248 -0
- package/.github/agents/jsdoc.agent.md +243 -0
- package/.github/agents/plan.agent.md +202 -0
- package/.github/agents/spec-writer.agent.md +169 -0
- package/.github/agents/test-writer.agent.md +169 -0
- package/.stylelintrc.json +27 -0
- package/README.md +94 -94
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +446 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +446 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +7039 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/commands/generate.js +262 -5
- package/dist/config/defaults.js +158 -0
- package/dist/config/index.js +10 -0
- package/dist/features/achievements/data/achievements.json +284 -0
- package/dist/features/achievements/engine.js +140 -0
- package/dist/features/achievements/evaluators.js +246 -0
- package/dist/features/achievements/helpers.js +58 -0
- package/dist/features/achievements/index.js +57 -0
- package/dist/features/achievements/loader.js +88 -0
- package/dist/features/achievements/template.js +155 -0
- package/dist/features/achievements/types.js +7 -0
- package/dist/features/commit-quality/analyzer.js +378 -0
- package/dist/features/commit-quality/analyzer.test.js +484 -0
- package/dist/features/commit-quality/index.js +28 -0
- package/dist/features/commit-quality/template.js +114 -0
- package/dist/features/commit-quality/types.js +2 -0
- package/dist/features/comparison/analyzer.js +222 -0
- package/dist/features/comparison/index.js +28 -0
- package/dist/features/comparison/template.js +119 -0
- package/dist/features/comparison/types.js +2 -0
- package/dist/features/contribution-graph/index.js +9 -0
- package/dist/features/contribution-graph/template.js +89 -0
- package/dist/features/events/index.js +31 -0
- package/dist/features/events/parser.js +253 -0
- package/dist/features/events/template.js +113 -0
- package/dist/features/events/types.js +2 -0
- package/dist/features/executive-summary/generator.js +275 -0
- package/dist/features/executive-summary/index.js +27 -0
- package/dist/features/executive-summary/template.js +80 -0
- package/dist/features/executive-summary/types.js +2 -0
- package/dist/features/gaps/analyzer.js +298 -0
- package/dist/features/gaps/analyzer.test.js +517 -0
- package/dist/features/gaps/index.js +27 -0
- package/dist/features/gaps/template.js +190 -0
- package/dist/features/gaps/types.js +2 -0
- package/dist/features/impact/analyzer.js +248 -0
- package/dist/features/impact/index.js +26 -0
- package/dist/features/impact/template.js +118 -0
- package/dist/features/impact/types.js +2 -0
- package/dist/features/index.js +40 -0
- package/dist/features/knowledge/analyzer.js +385 -0
- package/dist/features/knowledge/index.js +26 -0
- package/dist/features/knowledge/template.js +239 -0
- package/dist/features/knowledge/types.js +2 -0
- package/dist/features/streaks/calculator.js +184 -0
- package/dist/features/streaks/calculator.test.js +366 -0
- package/dist/features/streaks/index.js +36 -0
- package/dist/features/streaks/template.js +41 -0
- package/dist/features/streaks/types.js +9 -0
- package/dist/features/team/analyzer.js +316 -0
- package/dist/features/team/index.js +30 -0
- package/dist/features/team/template.js +146 -0
- package/dist/features/team/types.js +2 -0
- package/dist/features/time-patterns/analyzer.js +319 -0
- package/dist/features/time-patterns/analyzer.test.js +278 -0
- package/dist/features/time-patterns/index.js +37 -0
- package/dist/features/time-patterns/template.js +109 -0
- package/dist/features/time-patterns/types.js +9 -0
- package/dist/features/velocity/analyzer.js +257 -0
- package/dist/features/velocity/analyzer.test.js +383 -0
- package/dist/features/velocity/index.js +27 -0
- package/dist/features/velocity/template.js +189 -0
- package/dist/features/velocity/types.js +2 -0
- package/dist/generators/html/scripts/knowledge.js +17 -0
- package/dist/generators/html/styles/base.css +10 -6
- package/dist/generators/html/styles/components.css +121 -1
- package/dist/generators/html/styles/knowledge.css +21 -0
- package/dist/generators/html/styles/leaddev.css +1335 -0
- package/dist/generators/html/styles/strategic-insights.css +1337 -0
- package/dist/generators/html/templates/commitQualitySection.js +28 -2
- package/dist/generators/html/templates/comparisonSection.js +119 -0
- package/dist/generators/html/templates/eventsSection.js +113 -0
- package/dist/generators/html/templates/executiveSummarySection.js +80 -0
- package/dist/generators/html/templates/gapSection.js +190 -0
- package/dist/generators/html/templates/impactSection.js +8 -6
- package/dist/generators/html/templates/knowledgeSection.js +16 -2
- package/dist/generators/html/templates/teamSection.js +146 -0
- package/dist/generators/html/templates/velocitySection.js +189 -0
- package/dist/generators/html/types.js +7 -0
- package/dist/generators/html/utils/analysisRunner.js +93 -0
- package/dist/generators/html/utils/cardBuilder.js +47 -0
- package/dist/generators/html/utils/contextBuilder.js +54 -0
- package/dist/generators/html/utils/htmlDocumentBuilder.js +396 -0
- package/dist/generators/html/utils/kpiBuilder.js +76 -0
- package/dist/generators/html/utils/sectionWrapper.js +71 -0
- package/dist/generators/html/utils/styleLoader.js +2 -1
- package/dist/html/analysisRunner.js +93 -0
- package/dist/html/htmlDocumentBuilder.js +396 -0
- package/dist/html/index.js +29 -0
- package/dist/html/shared/colorUtils.js +61 -0
- package/dist/html/shared/commitMapBuilder.js +23 -0
- package/dist/html/shared/components/cardBuilder.js +47 -0
- package/dist/html/shared/components/index.js +18 -0
- package/dist/html/shared/components/kpiBuilder.js +76 -0
- package/dist/html/shared/components/sectionWrapper.js +71 -0
- package/dist/html/shared/contextBuilder.js +54 -0
- package/dist/html/shared/dateRangeCalculator.js +56 -0
- package/dist/html/shared/developerStatsCalculator.js +28 -0
- package/dist/html/shared/index.js +39 -0
- package/dist/html/shared/scriptLoader.js +15 -0
- package/dist/html/shared/scripts/export.js +125 -0
- package/dist/html/shared/scripts/knowledge.js +137 -0
- package/dist/html/shared/scripts/modal.js +68 -0
- package/dist/html/shared/scripts/navigation.js +156 -0
- package/dist/html/shared/scripts/tabs.js +18 -0
- package/dist/html/shared/scripts/tooltip.js +21 -0
- package/dist/html/shared/styleLoader.js +18 -0
- package/dist/html/shared/styles/achievements.css +387 -0
- package/dist/html/shared/styles/base.css +822 -0
- package/dist/html/shared/styles/components.css +1511 -0
- package/dist/html/shared/styles/knowledge.css +242 -0
- package/dist/html/shared/styles/strategic-insights.css +1337 -0
- package/dist/html/shared/weekGrouper.js +27 -0
- package/dist/html/types.js +7 -0
- package/dist/index.js +54 -21
- package/dist/test/helpers/commitFactory.js +166 -0
- package/dist/test/helpers/dateUtils.js +101 -0
- package/dist/test/helpers/index.js +29 -0
- package/dist/test/setup.js +17 -0
- package/dist/test/smoke.test.js +94 -0
- package/dist/types/achievements.js +7 -0
- package/dist/types/analysis.js +7 -0
- package/dist/types/core.js +7 -0
- package/dist/types/index.js +38 -0
- package/dist/types/options.js +7 -0
- package/dist/types/shared.js +7 -0
- package/dist/types/strategic.js +7 -0
- package/dist/types/summary.js +7 -0
- package/dist/utils/achievementDefinitions.js +22 -22
- package/dist/utils/analyzerContextBuilder.js +124 -0
- package/dist/utils/commitQualityAnalyzer.js +13 -2
- package/dist/utils/emptyResults.js +95 -0
- package/dist/utils/eventAnnotationParser.js +253 -0
- package/dist/utils/executiveSummaryGenerator.js +275 -0
- package/dist/utils/fileHotspotAnalyzer.js +4 -12
- package/dist/utils/gapAnalyzer.js +298 -0
- package/dist/utils/gitParser.test.js +363 -0
- package/dist/utils/htmlGenerator.js +126 -450
- package/dist/utils/impactAnalyzer.js +20 -19
- package/dist/utils/knowledgeDistributionAnalyzer.js +32 -27
- package/dist/utils/matrixGenerator.js +13 -13
- package/dist/utils/rangeComparisonAnalyzer.js +222 -0
- package/dist/utils/streakCalculator.js +77 -27
- package/dist/utils/teamAnalyzer.js +316 -0
- package/dist/utils/timePatternAnalyzer.js +18 -3
- package/dist/utils/velocityAnalyzer.js +257 -0
- package/dist/utils/wrappedGenerator.js +8 -8
- package/package.json +74 -55
- package/vitest.config.ts +46 -0
- package/dist/cli.js +0 -24
- package/dist/commands/index.js +0 -24
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
````chatagent
|
|
2
|
+
---
|
|
3
|
+
description: "Generates unit tests for analyzers, utilities, and HTML templates. Creates Jest/Vitest test files with proper coverage of edge cases and expected outputs."
|
|
4
|
+
tools:
|
|
5
|
+
[
|
|
6
|
+
"vscode",
|
|
7
|
+
"read",
|
|
8
|
+
"edit",
|
|
9
|
+
"search",
|
|
10
|
+
"agent",
|
|
11
|
+
]
|
|
12
|
+
model: Claude Opus 4.5 (copilot)
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
You are a test writer for the git-wrapped project. Your job is to generate comprehensive unit tests for analyzers, utilities, and templates.
|
|
16
|
+
|
|
17
|
+
## Project Context
|
|
18
|
+
|
|
19
|
+
git-wrapped is a CLI tool that analyzes git history and generates reports. It has:
|
|
20
|
+
- **13 feature analyzers** in `src/features/` - pure functions that transform commits into analysis results
|
|
21
|
+
- **HTML templates** in `src/features/*/template.ts` - render analysis data to HTML strings
|
|
22
|
+
- **Utilities** in `src/utils/` - shared helpers like `gitParser.ts`
|
|
23
|
+
- **Empty result factories** in `src/html/shared/emptyResults.ts` - define expected output shapes
|
|
24
|
+
|
|
25
|
+
## Test Framework
|
|
26
|
+
|
|
27
|
+
The project uses **Vitest** (or Jest - check package.json). Tests go in:
|
|
28
|
+
- `src/features/{feature}/__tests__/` for feature tests
|
|
29
|
+
- `src/utils/__tests__/` for utility tests
|
|
30
|
+
- Or colocated as `*.test.ts` next to the source file
|
|
31
|
+
|
|
32
|
+
## Workflow
|
|
33
|
+
|
|
34
|
+
1. **Identify what to test** from the user's request:
|
|
35
|
+
- A specific feature (e.g., "streak calculator")
|
|
36
|
+
- A utility function (e.g., "git parser")
|
|
37
|
+
- A template function (e.g., "achievements section")
|
|
38
|
+
- Or "all" to audit coverage gaps
|
|
39
|
+
|
|
40
|
+
2. **Read the source file** to understand:
|
|
41
|
+
- Function signatures and parameters
|
|
42
|
+
- Return types (check `src/types/index.ts`)
|
|
43
|
+
- Edge cases from conditional logic
|
|
44
|
+
- Empty result shape from `emptyResults.ts`
|
|
45
|
+
|
|
46
|
+
3. **Generate test cases** covering:
|
|
47
|
+
- Happy path with typical input
|
|
48
|
+
- Empty input (empty array, no commits)
|
|
49
|
+
- Single item input
|
|
50
|
+
- Boundary conditions (date ranges, thresholds)
|
|
51
|
+
- Error conditions if applicable
|
|
52
|
+
|
|
53
|
+
4. **Create the test file** with proper structure
|
|
54
|
+
|
|
55
|
+
## Test Template
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
59
|
+
import { analyzeFeature } from '../analyzer';
|
|
60
|
+
import type { Commit } from '../../../types';
|
|
61
|
+
|
|
62
|
+
// Helper to create test commits
|
|
63
|
+
function createCommit(overrides: Partial<Commit> = {}): Commit {
|
|
64
|
+
return {
|
|
65
|
+
hash: 'abc123',
|
|
66
|
+
author: 'Test Author',
|
|
67
|
+
email: 'test@example.com',
|
|
68
|
+
date: new Date('2025-06-15T10:00:00Z'),
|
|
69
|
+
message: 'test commit',
|
|
70
|
+
filesChanged: 1,
|
|
71
|
+
insertions: 10,
|
|
72
|
+
deletions: 5,
|
|
73
|
+
files: [],
|
|
74
|
+
...overrides,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe('analyzeFeature', () => {
|
|
79
|
+
describe('with empty input', () => {
|
|
80
|
+
it('returns empty result structure', () => {
|
|
81
|
+
const result = analyzeFeature([]);
|
|
82
|
+
|
|
83
|
+
expect(result).toEqual({
|
|
84
|
+
// Expected empty shape from emptyResults.ts
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('with single commit', () => {
|
|
90
|
+
it('handles single commit correctly', () => {
|
|
91
|
+
const commits = [createCommit()];
|
|
92
|
+
const result = analyzeFeature(commits);
|
|
93
|
+
|
|
94
|
+
expect(result).toBeDefined();
|
|
95
|
+
// Specific assertions
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('with typical data', () => {
|
|
100
|
+
it('calculates expected metrics', () => {
|
|
101
|
+
const commits = [
|
|
102
|
+
createCommit({ date: new Date('2025-06-01') }),
|
|
103
|
+
createCommit({ date: new Date('2025-06-02') }),
|
|
104
|
+
createCommit({ date: new Date('2025-06-03') }),
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const result = analyzeFeature(commits);
|
|
108
|
+
|
|
109
|
+
// Assert on calculated values
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('edge cases', () => {
|
|
114
|
+
it('handles date boundary conditions', () => {
|
|
115
|
+
// Test start/end of date ranges
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('handles commits on same day', () => {
|
|
119
|
+
// Test deduplication or aggregation
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
````
|
|
124
|
+
|
|
125
|
+
## Feature-Specific Testing Patterns
|
|
126
|
+
|
|
127
|
+
### Analyzers (`src/features/*/analyzer.ts`)
|
|
128
|
+
|
|
129
|
+
- Test the main `analyze*` function
|
|
130
|
+
- Input is typically `Commit[]` or `AnalysisContext`
|
|
131
|
+
- Output shape defined in types and `emptyResults.ts`
|
|
132
|
+
- Test both positional and context-based calling conventions if both exist
|
|
133
|
+
|
|
134
|
+
### Templates (`src/features/*/template.ts`)
|
|
135
|
+
|
|
136
|
+
- Test `build*Section` functions
|
|
137
|
+
- Input is the analysis result type
|
|
138
|
+
- Output is an HTML string
|
|
139
|
+
- Use snapshot testing for complex HTML:
|
|
140
|
+
```typescript
|
|
141
|
+
expect(buildSection(data)).toMatchSnapshot();
|
|
142
|
+
```
|
|
143
|
+
- Test with empty/minimal data to verify graceful handling
|
|
144
|
+
|
|
145
|
+
### Utilities (`src/utils/`)
|
|
146
|
+
|
|
147
|
+
- Test pure functions with various inputs
|
|
148
|
+
- `gitParser.ts` - test parsing of git log output
|
|
149
|
+
- `achievementEngine.ts` - test achievement unlocking logic
|
|
150
|
+
|
|
151
|
+
## Reference Files
|
|
152
|
+
|
|
153
|
+
Always consult:
|
|
154
|
+
|
|
155
|
+
- `src/types/index.ts` - all type definitions
|
|
156
|
+
- `src/html/shared/emptyResults.ts` - empty result shapes
|
|
157
|
+
- `src/features/{feature}/index.ts` - public API
|
|
158
|
+
- Existing tests (if any) for patterns
|
|
159
|
+
|
|
160
|
+
## After Creating Tests
|
|
161
|
+
|
|
162
|
+
1. Suggest running `npm test` or `npx vitest` to verify
|
|
163
|
+
2. Report which test cases were created
|
|
164
|
+
3. Note any edge cases that need manual review
|
|
165
|
+
4. Suggest additional test scenarios if relevant
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "stylelint-config-standard",
|
|
3
|
+
"rules": {
|
|
4
|
+
"color-named": null,
|
|
5
|
+
"custom-property-pattern": null,
|
|
6
|
+
"selector-class-pattern": null,
|
|
7
|
+
"no-descending-specificity": null,
|
|
8
|
+
"no-duplicate-selectors": null,
|
|
9
|
+
"property-no-vendor-prefix": null,
|
|
10
|
+
"value-no-vendor-prefix": null,
|
|
11
|
+
"comment-empty-line-before": null,
|
|
12
|
+
"declaration-empty-line-before": null,
|
|
13
|
+
"rule-empty-line-before": null,
|
|
14
|
+
"alpha-value-notation": null,
|
|
15
|
+
"color-function-notation": null,
|
|
16
|
+
"color-function-alias-notation": null,
|
|
17
|
+
"import-notation": null,
|
|
18
|
+
"media-feature-range-notation": null,
|
|
19
|
+
"declaration-block-single-line-max-declarations": null,
|
|
20
|
+
"value-keyword-case": null,
|
|
21
|
+
"declaration-property-value-no-unknown": null
|
|
22
|
+
},
|
|
23
|
+
"ignoreFiles": [
|
|
24
|
+
"dist/**/*",
|
|
25
|
+
"node_modules/**/*"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/README.md
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
# repo-wrapped
|
|
2
|
-
|
|
3
|
-
A CLI tool for generating Git repository analytics and visualizations.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g repo-wrapped
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
### Generate Visualization
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# Current directory, current year
|
|
17
|
-
repo-wrapped generate
|
|
18
|
-
|
|
19
|
-
# Specific repository with HTML output
|
|
20
|
-
repo-wrapped generate /path/to/repo --html
|
|
21
|
-
|
|
22
|
-
# Analyze entire repository lifetime
|
|
23
|
-
repo-wrapped generate . --all --html
|
|
24
|
-
|
|
25
|
-
# Deep analysis with file-level knowledge distribution
|
|
26
|
-
repo-wrapped generate . --html --deep-analysis
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Options
|
|
30
|
-
|
|
31
|
-
| Flag | Description |
|
|
32
|
-
|------|-------------|
|
|
33
|
-
| `-y, --year <year>` | Year to analyze (default: current year) |
|
|
34
|
-
| `-m, --months <months>` | Number of months to show (1-12, default: 12) |
|
|
35
|
-
| `-a, --all` | Analyze entire repository lifetime |
|
|
36
|
-
| `--html` | Generate HTML report and open in browser |
|
|
37
|
-
| `--body-check` | Include commit body in quality scoring |
|
|
38
|
-
| `--deep-analysis` | Enable file-level analysis for knowledge distribution |
|
|
39
|
-
|
|
40
|
-
### Year in Code Summary
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
repo-wrapped wrapped 2024 . --html
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## HTML Report Sections
|
|
47
|
-
|
|
48
|
-
- **Contribution Graph** - Interactive commit heatmap
|
|
49
|
-
- **Streak Analytics** - Current and longest streaks, active day percentage
|
|
50
|
-
- **Time Patterns** - Hourly/daily distribution, chronotype detection, burnout risk
|
|
51
|
-
- **Commit Quality** - Message quality scoring, conventional commits adherence
|
|
52
|
-
- **File Hotspots** - Most changed files, technical debt indicators
|
|
53
|
-
- **Achievements** - Progress-based badges and milestones
|
|
54
|
-
- **Impact Analysis** - Lines changed, churn metrics
|
|
55
|
-
- **Knowledge Distribution** - Bus factor risk, ownership analysis
|
|
56
|
-
|
|
57
|
-
## Development
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
npm install
|
|
61
|
-
npm run build
|
|
62
|
-
npm link
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Project Structure
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
src/
|
|
69
|
-
├── index.ts # CLI entry point
|
|
70
|
-
├── commands/
|
|
71
|
-
│ └── generate.ts # Generate command
|
|
72
|
-
├── types/
|
|
73
|
-
│ └── index.ts # TypeScript interfaces
|
|
74
|
-
├── generators/
|
|
75
|
-
│ └── html/
|
|
76
|
-
│ ├── scripts/ # Client-side JS
|
|
77
|
-
│ ├── styles/ # CSS files
|
|
78
|
-
│ ├── templates/ # HTML section builders
|
|
79
|
-
│ └── utils/ # Build utilities
|
|
80
|
-
└── utils/
|
|
81
|
-
├── gitParser.ts # Git log parsing
|
|
82
|
-
├── htmlGenerator.ts # HTML report assembly
|
|
83
|
-
├── streakCalculator.ts # Streak analytics
|
|
84
|
-
├── timePatternAnalyzer.ts
|
|
85
|
-
├── commitQualityAnalyzer.ts
|
|
86
|
-
├── fileHotspotAnalyzer.ts
|
|
87
|
-
├── achievementEngine.ts
|
|
88
|
-
├── impactAnalyzer.ts
|
|
89
|
-
└── knowledgeDistributionAnalyzer.ts
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## License
|
|
93
|
-
|
|
94
|
-
MIT
|
|
1
|
+
# repo-wrapped
|
|
2
|
+
|
|
3
|
+
A CLI tool for generating Git repository analytics and visualizations.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g repo-wrapped
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Generate Visualization
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Current directory, current year
|
|
17
|
+
repo-wrapped generate
|
|
18
|
+
|
|
19
|
+
# Specific repository with HTML output
|
|
20
|
+
repo-wrapped generate /path/to/repo --html
|
|
21
|
+
|
|
22
|
+
# Analyze entire repository lifetime
|
|
23
|
+
repo-wrapped generate . --all --html
|
|
24
|
+
|
|
25
|
+
# Deep analysis with file-level knowledge distribution
|
|
26
|
+
repo-wrapped generate . --html --deep-analysis
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
| Flag | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `-y, --year <year>` | Year to analyze (default: current year) |
|
|
34
|
+
| `-m, --months <months>` | Number of months to show (1-12, default: 12) |
|
|
35
|
+
| `-a, --all` | Analyze entire repository lifetime |
|
|
36
|
+
| `--html` | Generate HTML report and open in browser |
|
|
37
|
+
| `--body-check` | Include commit body in quality scoring |
|
|
38
|
+
| `--deep-analysis` | Enable file-level analysis for knowledge distribution |
|
|
39
|
+
|
|
40
|
+
### Year in Code Summary
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
repo-wrapped wrapped 2024 . --html
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## HTML Report Sections
|
|
47
|
+
|
|
48
|
+
- **Contribution Graph** - Interactive commit heatmap
|
|
49
|
+
- **Streak Analytics** - Current and longest streaks, active day percentage
|
|
50
|
+
- **Time Patterns** - Hourly/daily distribution, chronotype detection, burnout risk
|
|
51
|
+
- **Commit Quality** - Message quality scoring, conventional commits adherence
|
|
52
|
+
- **File Hotspots** - Most changed files, technical debt indicators
|
|
53
|
+
- **Achievements** - Progress-based badges and milestones
|
|
54
|
+
- **Impact Analysis** - Lines changed, churn metrics
|
|
55
|
+
- **Knowledge Distribution** - Bus factor risk, ownership analysis
|
|
56
|
+
|
|
57
|
+
## Development
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install
|
|
61
|
+
npm run build
|
|
62
|
+
npm link
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Project Structure
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/
|
|
69
|
+
├── index.ts # CLI entry point
|
|
70
|
+
├── commands/
|
|
71
|
+
│ └── generate.ts # Generate command
|
|
72
|
+
├── types/
|
|
73
|
+
│ └── index.ts # TypeScript interfaces
|
|
74
|
+
├── generators/
|
|
75
|
+
│ └── html/
|
|
76
|
+
│ ├── scripts/ # Client-side JS
|
|
77
|
+
│ ├── styles/ # CSS files
|
|
78
|
+
│ ├── templates/ # HTML section builders
|
|
79
|
+
│ └── utils/ # Build utilities
|
|
80
|
+
└── utils/
|
|
81
|
+
├── gitParser.ts # Git log parsing
|
|
82
|
+
├── htmlGenerator.ts # HTML report assembly
|
|
83
|
+
├── streakCalculator.ts # Streak analytics
|
|
84
|
+
├── timePatternAnalyzer.ts
|
|
85
|
+
├── commitQualityAnalyzer.ts
|
|
86
|
+
├── fileHotspotAnalyzer.ts
|
|
87
|
+
├── achievementEngine.ts
|
|
88
|
+
├── impactAnalyzer.ts
|
|
89
|
+
└── knowledgeDistributionAnalyzer.ts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
body, html {
|
|
2
|
+
margin:0; padding: 0;
|
|
3
|
+
height: 100%;
|
|
4
|
+
}
|
|
5
|
+
body {
|
|
6
|
+
font-family: Helvetica Neue, Helvetica, Arial;
|
|
7
|
+
font-size: 14px;
|
|
8
|
+
color:#333;
|
|
9
|
+
}
|
|
10
|
+
.small { font-size: 12px; }
|
|
11
|
+
*, *:after, *:before {
|
|
12
|
+
-webkit-box-sizing:border-box;
|
|
13
|
+
-moz-box-sizing:border-box;
|
|
14
|
+
box-sizing:border-box;
|
|
15
|
+
}
|
|
16
|
+
h1 { font-size: 20px; margin: 0;}
|
|
17
|
+
h2 { font-size: 14px; }
|
|
18
|
+
pre {
|
|
19
|
+
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
-moz-tab-size: 2;
|
|
23
|
+
-o-tab-size: 2;
|
|
24
|
+
tab-size: 2;
|
|
25
|
+
}
|
|
26
|
+
a { color:#0074D9; text-decoration:none; }
|
|
27
|
+
a:hover { text-decoration:underline; }
|
|
28
|
+
.strong { font-weight: bold; }
|
|
29
|
+
.space-top1 { padding: 10px 0 0 0; }
|
|
30
|
+
.pad2y { padding: 20px 0; }
|
|
31
|
+
.pad1y { padding: 10px 0; }
|
|
32
|
+
.pad2x { padding: 0 20px; }
|
|
33
|
+
.pad2 { padding: 20px; }
|
|
34
|
+
.pad1 { padding: 10px; }
|
|
35
|
+
.space-left2 { padding-left:55px; }
|
|
36
|
+
.space-right2 { padding-right:20px; }
|
|
37
|
+
.center { text-align:center; }
|
|
38
|
+
.clearfix { display:block; }
|
|
39
|
+
.clearfix:after {
|
|
40
|
+
content:'';
|
|
41
|
+
display:block;
|
|
42
|
+
height:0;
|
|
43
|
+
clear:both;
|
|
44
|
+
visibility:hidden;
|
|
45
|
+
}
|
|
46
|
+
.fl { float: left; }
|
|
47
|
+
@media only screen and (max-width:640px) {
|
|
48
|
+
.col3 { width:100%; max-width:100%; }
|
|
49
|
+
.hide-mobile { display:none!important; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.quiet {
|
|
53
|
+
color: #7f7f7f;
|
|
54
|
+
color: rgba(0,0,0,0.5);
|
|
55
|
+
}
|
|
56
|
+
.quiet a { opacity: 0.7; }
|
|
57
|
+
|
|
58
|
+
.fraction {
|
|
59
|
+
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
|
60
|
+
font-size: 10px;
|
|
61
|
+
color: #555;
|
|
62
|
+
background: #E8E8E8;
|
|
63
|
+
padding: 4px 5px;
|
|
64
|
+
border-radius: 3px;
|
|
65
|
+
vertical-align: middle;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
div.path a:link, div.path a:visited { color: #333; }
|
|
69
|
+
table.coverage {
|
|
70
|
+
border-collapse: collapse;
|
|
71
|
+
margin: 10px 0 0 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
table.coverage td {
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
vertical-align: top;
|
|
79
|
+
}
|
|
80
|
+
table.coverage td.line-count {
|
|
81
|
+
text-align: right;
|
|
82
|
+
padding: 0 5px 0 20px;
|
|
83
|
+
}
|
|
84
|
+
table.coverage td.line-coverage {
|
|
85
|
+
text-align: right;
|
|
86
|
+
padding-right: 10px;
|
|
87
|
+
min-width:20px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
table.coverage td span.cline-any {
|
|
91
|
+
display: inline-block;
|
|
92
|
+
padding: 0 5px;
|
|
93
|
+
width: 100%;
|
|
94
|
+
}
|
|
95
|
+
.missing-if-branch {
|
|
96
|
+
display: inline-block;
|
|
97
|
+
margin-right: 5px;
|
|
98
|
+
border-radius: 3px;
|
|
99
|
+
position: relative;
|
|
100
|
+
padding: 0 4px;
|
|
101
|
+
background: #333;
|
|
102
|
+
color: yellow;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.skip-if-branch {
|
|
106
|
+
display: none;
|
|
107
|
+
margin-right: 10px;
|
|
108
|
+
position: relative;
|
|
109
|
+
padding: 0 4px;
|
|
110
|
+
background: #ccc;
|
|
111
|
+
color: white;
|
|
112
|
+
}
|
|
113
|
+
.missing-if-branch .typ, .skip-if-branch .typ {
|
|
114
|
+
color: inherit !important;
|
|
115
|
+
}
|
|
116
|
+
.coverage-summary {
|
|
117
|
+
border-collapse: collapse;
|
|
118
|
+
width: 100%;
|
|
119
|
+
}
|
|
120
|
+
.coverage-summary tr { border-bottom: 1px solid #bbb; }
|
|
121
|
+
.keyline-all { border: 1px solid #ddd; }
|
|
122
|
+
.coverage-summary td, .coverage-summary th { padding: 10px; }
|
|
123
|
+
.coverage-summary tbody { border: 1px solid #bbb; }
|
|
124
|
+
.coverage-summary td { border-right: 1px solid #bbb; }
|
|
125
|
+
.coverage-summary td:last-child { border-right: none; }
|
|
126
|
+
.coverage-summary th {
|
|
127
|
+
text-align: left;
|
|
128
|
+
font-weight: normal;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
.coverage-summary th.file { border-right: none !important; }
|
|
132
|
+
.coverage-summary th.pct { }
|
|
133
|
+
.coverage-summary th.pic,
|
|
134
|
+
.coverage-summary th.abs,
|
|
135
|
+
.coverage-summary td.pct,
|
|
136
|
+
.coverage-summary td.abs { text-align: right; }
|
|
137
|
+
.coverage-summary td.file { white-space: nowrap; }
|
|
138
|
+
.coverage-summary td.pic { min-width: 120px !important; }
|
|
139
|
+
.coverage-summary tfoot td { }
|
|
140
|
+
|
|
141
|
+
.coverage-summary .sorter {
|
|
142
|
+
height: 10px;
|
|
143
|
+
width: 7px;
|
|
144
|
+
display: inline-block;
|
|
145
|
+
margin-left: 0.5em;
|
|
146
|
+
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
|
|
147
|
+
}
|
|
148
|
+
.coverage-summary .sorted .sorter {
|
|
149
|
+
background-position: 0 -20px;
|
|
150
|
+
}
|
|
151
|
+
.coverage-summary .sorted-desc .sorter {
|
|
152
|
+
background-position: 0 -10px;
|
|
153
|
+
}
|
|
154
|
+
.status-line { height: 10px; }
|
|
155
|
+
/* yellow */
|
|
156
|
+
.cbranch-no { background: yellow !important; color: #111; }
|
|
157
|
+
/* dark red */
|
|
158
|
+
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
|
|
159
|
+
.low .chart { border:1px solid #C21F39 }
|
|
160
|
+
.highlighted,
|
|
161
|
+
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
|
|
162
|
+
background: #C21F39 !important;
|
|
163
|
+
}
|
|
164
|
+
/* medium red */
|
|
165
|
+
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
|
|
166
|
+
/* light red */
|
|
167
|
+
.low, .cline-no { background:#FCE1E5 }
|
|
168
|
+
/* light green */
|
|
169
|
+
.high, .cline-yes { background:rgb(230,245,208) }
|
|
170
|
+
/* medium green */
|
|
171
|
+
.cstat-yes { background:rgb(161,215,106) }
|
|
172
|
+
/* dark green */
|
|
173
|
+
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
|
|
174
|
+
.high .chart { border:1px solid rgb(77,146,33) }
|
|
175
|
+
/* dark yellow (gold) */
|
|
176
|
+
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
|
|
177
|
+
.medium .chart { border:1px solid #f9cd0b; }
|
|
178
|
+
/* light yellow */
|
|
179
|
+
.medium { background: #fff4c2; }
|
|
180
|
+
|
|
181
|
+
.cstat-skip { background: #ddd; color: #111; }
|
|
182
|
+
.fstat-skip { background: #ddd; color: #111 !important; }
|
|
183
|
+
.cbranch-skip { background: #ddd !important; color: #111; }
|
|
184
|
+
|
|
185
|
+
span.cline-neutral { background: #eaeaea; }
|
|
186
|
+
|
|
187
|
+
.coverage-summary td.empty {
|
|
188
|
+
opacity: .5;
|
|
189
|
+
padding-top: 4px;
|
|
190
|
+
padding-bottom: 4px;
|
|
191
|
+
line-height: 1;
|
|
192
|
+
color: #888;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.cover-fill, .cover-empty {
|
|
196
|
+
display:inline-block;
|
|
197
|
+
height: 12px;
|
|
198
|
+
}
|
|
199
|
+
.chart {
|
|
200
|
+
line-height: 0;
|
|
201
|
+
}
|
|
202
|
+
.cover-empty {
|
|
203
|
+
background: white;
|
|
204
|
+
}
|
|
205
|
+
.cover-full {
|
|
206
|
+
border-right: none !important;
|
|
207
|
+
}
|
|
208
|
+
pre.prettyprint {
|
|
209
|
+
border: none !important;
|
|
210
|
+
padding: 0 !important;
|
|
211
|
+
margin: 0 !important;
|
|
212
|
+
}
|
|
213
|
+
.com { color: #999 !important; }
|
|
214
|
+
.ignore-none { color: #999; font-weight: normal; }
|
|
215
|
+
|
|
216
|
+
.wrapper {
|
|
217
|
+
min-height: 100%;
|
|
218
|
+
height: auto !important;
|
|
219
|
+
height: 100%;
|
|
220
|
+
margin: 0 auto -48px;
|
|
221
|
+
}
|
|
222
|
+
.footer, .push {
|
|
223
|
+
height: 48px;
|
|
224
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
var jumpToCode = (function init() {
|
|
3
|
+
// Classes of code we would like to highlight in the file view
|
|
4
|
+
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
|
|
5
|
+
|
|
6
|
+
// Elements to highlight in the file listing view
|
|
7
|
+
var fileListingElements = ['td.pct.low'];
|
|
8
|
+
|
|
9
|
+
// We don't want to select elements that are direct descendants of another match
|
|
10
|
+
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
|
|
11
|
+
|
|
12
|
+
// Selector that finds elements on the page to which we can jump
|
|
13
|
+
var selector =
|
|
14
|
+
fileListingElements.join(', ') +
|
|
15
|
+
', ' +
|
|
16
|
+
notSelector +
|
|
17
|
+
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
|
|
18
|
+
|
|
19
|
+
// The NodeList of matching elements
|
|
20
|
+
var missingCoverageElements = document.querySelectorAll(selector);
|
|
21
|
+
|
|
22
|
+
var currentIndex;
|
|
23
|
+
|
|
24
|
+
function toggleClass(index) {
|
|
25
|
+
missingCoverageElements
|
|
26
|
+
.item(currentIndex)
|
|
27
|
+
.classList.remove('highlighted');
|
|
28
|
+
missingCoverageElements.item(index).classList.add('highlighted');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeCurrent(index) {
|
|
32
|
+
toggleClass(index);
|
|
33
|
+
currentIndex = index;
|
|
34
|
+
missingCoverageElements.item(index).scrollIntoView({
|
|
35
|
+
behavior: 'smooth',
|
|
36
|
+
block: 'center',
|
|
37
|
+
inline: 'center'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function goToPrevious() {
|
|
42
|
+
var nextIndex = 0;
|
|
43
|
+
if (typeof currentIndex !== 'number' || currentIndex === 0) {
|
|
44
|
+
nextIndex = missingCoverageElements.length - 1;
|
|
45
|
+
} else if (missingCoverageElements.length > 1) {
|
|
46
|
+
nextIndex = currentIndex - 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
makeCurrent(nextIndex);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function goToNext() {
|
|
53
|
+
var nextIndex = 0;
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
typeof currentIndex === 'number' &&
|
|
57
|
+
currentIndex < missingCoverageElements.length - 1
|
|
58
|
+
) {
|
|
59
|
+
nextIndex = currentIndex + 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
makeCurrent(nextIndex);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return function jump(event) {
|
|
66
|
+
if (
|
|
67
|
+
document.getElementById('fileSearch') === document.activeElement &&
|
|
68
|
+
document.activeElement != null
|
|
69
|
+
) {
|
|
70
|
+
// if we're currently focused on the search input, we don't want to navigate
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
switch (event.which) {
|
|
75
|
+
case 78: // n
|
|
76
|
+
case 74: // j
|
|
77
|
+
goToNext();
|
|
78
|
+
break;
|
|
79
|
+
case 66: // b
|
|
80
|
+
case 75: // k
|
|
81
|
+
case 80: // p
|
|
82
|
+
goToPrevious();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
87
|
+
window.addEventListener('keydown', jumpToCode);
|
|
Binary file
|