@urmzd/github-insights 2.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 (102) hide show
  1. package/.gitattributes +28 -0
  2. package/.github/dependabot.yml +6 -0
  3. package/.github/pull_request_template.md +14 -0
  4. package/.github/workflows/ci.yml +93 -0
  5. package/.github/workflows/release.yml +59 -0
  6. package/.nvmrc +1 -0
  7. package/.pre-commit-config.yaml +5 -0
  8. package/AGENTS.md +69 -0
  9. package/CHANGELOG.md +260 -0
  10. package/CONTRIBUTING.md +87 -0
  11. package/LICENSE +190 -0
  12. package/README.md +188 -0
  13. package/action.yml +45 -0
  14. package/biome.json +40 -0
  15. package/examples/classic/README.md +9 -0
  16. package/examples/classic/index.svg +14 -0
  17. package/examples/classic/metrics-calendar.svg +14 -0
  18. package/examples/classic/metrics-complexity.svg +14 -0
  19. package/examples/classic/metrics-contributions.svg +14 -0
  20. package/examples/classic/metrics-expertise.svg +14 -0
  21. package/examples/classic/metrics-languages.svg +14 -0
  22. package/examples/classic/metrics-pulse.svg +14 -0
  23. package/examples/ecosystem/README.md +59 -0
  24. package/examples/ecosystem/index.svg +14 -0
  25. package/examples/ecosystem/metrics-calendar.svg +14 -0
  26. package/examples/ecosystem/metrics-complexity.svg +14 -0
  27. package/examples/ecosystem/metrics-contributions.svg +14 -0
  28. package/examples/ecosystem/metrics-expertise.svg +14 -0
  29. package/examples/ecosystem/metrics-languages.svg +14 -0
  30. package/examples/ecosystem/metrics-pulse.svg +14 -0
  31. package/examples/minimal/README.md +9 -0
  32. package/examples/minimal/index.svg +14 -0
  33. package/examples/minimal/metrics-calendar.svg +14 -0
  34. package/examples/minimal/metrics-complexity.svg +14 -0
  35. package/examples/minimal/metrics-contributions.svg +14 -0
  36. package/examples/minimal/metrics-expertise.svg +14 -0
  37. package/examples/minimal/metrics-languages.svg +14 -0
  38. package/examples/minimal/metrics-pulse.svg +14 -0
  39. package/examples/modern/README.md +111 -0
  40. package/examples/modern/index.svg +14 -0
  41. package/examples/modern/metrics-calendar.svg +14 -0
  42. package/examples/modern/metrics-complexity.svg +14 -0
  43. package/examples/modern/metrics-contributions.svg +14 -0
  44. package/examples/modern/metrics-expertise.svg +14 -0
  45. package/examples/modern/metrics-languages.svg +14 -0
  46. package/examples/modern/metrics-pulse.svg +14 -0
  47. package/llms.txt +24 -0
  48. package/metrics/index.svg +14 -0
  49. package/metrics/metrics-calendar.svg +14 -0
  50. package/metrics/metrics-complexity.svg +14 -0
  51. package/metrics/metrics-contributions.svg +14 -0
  52. package/metrics/metrics-domains.svg +14 -0
  53. package/metrics/metrics-expertise.svg +14 -0
  54. package/metrics/metrics-languages.svg +14 -0
  55. package/metrics/metrics-pulse.svg +14 -0
  56. package/metrics/metrics-tech-stack.svg +14 -0
  57. package/package.json +35 -0
  58. package/skills/github-insights/SKILL.md +237 -0
  59. package/sr.yaml +16 -0
  60. package/src/__fixtures__/repos.ts +84 -0
  61. package/src/api.ts +729 -0
  62. package/src/components/bar-chart.test.tsx +38 -0
  63. package/src/components/bar-chart.tsx +54 -0
  64. package/src/components/contribution-calendar.test.tsx +44 -0
  65. package/src/components/contribution-calendar.tsx +94 -0
  66. package/src/components/contribution-cards.test.tsx +36 -0
  67. package/src/components/contribution-cards.tsx +58 -0
  68. package/src/components/donut-chart.test.tsx +36 -0
  69. package/src/components/donut-chart.tsx +102 -0
  70. package/src/components/full-svg.test.tsx +54 -0
  71. package/src/components/full-svg.tsx +59 -0
  72. package/src/components/project-cards.test.tsx +46 -0
  73. package/src/components/project-cards.tsx +66 -0
  74. package/src/components/section.test.tsx +69 -0
  75. package/src/components/section.tsx +79 -0
  76. package/src/components/stat-cards.test.tsx +32 -0
  77. package/src/components/stat-cards.tsx +57 -0
  78. package/src/components/style-defs.test.tsx +26 -0
  79. package/src/components/style-defs.tsx +27 -0
  80. package/src/components/tech-highlights.test.tsx +63 -0
  81. package/src/components/tech-highlights.tsx +109 -0
  82. package/src/config.test.ts +127 -0
  83. package/src/config.ts +103 -0
  84. package/src/index.ts +363 -0
  85. package/src/jsx-factory.test.tsx +86 -0
  86. package/src/jsx-factory.ts +46 -0
  87. package/src/jsx.d.ts +6 -0
  88. package/src/metrics.test.ts +669 -0
  89. package/src/metrics.ts +365 -0
  90. package/src/parsers.test.ts +247 -0
  91. package/src/parsers.ts +146 -0
  92. package/src/readme.test.ts +189 -0
  93. package/src/readme.ts +70 -0
  94. package/src/svg-utils.test.ts +66 -0
  95. package/src/svg-utils.ts +33 -0
  96. package/src/templates.test.ts +412 -0
  97. package/src/templates.ts +296 -0
  98. package/src/theme.ts +33 -0
  99. package/src/types.ts +235 -0
  100. package/teasr.toml +14 -0
  101. package/tsconfig.json +21 -0
  102. package/vitest.config.ts +12 -0
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: github-insights
3
+ description: >-
4
+ Generate and customize SVG metrics visualizations for GitHub profile READMEs.
5
+ Handles setup, configuration, theming, section selection, template choice,
6
+ troubleshooting, and extending with new components or parsers.
7
+ Use when users want to set up, customize, debug, or extend GitHub profile metrics.
8
+ argument-hint: [setup | customize | generate | debug | extend]
9
+ ---
10
+
11
+ # GitHub Metrics — Agent Skill
12
+
13
+ Generate beautiful dark-themed SVG metrics for GitHub profile READMEs. Sections include language breakdowns (donut chart), AI expertise analysis (proficiency bars), contribution pulse (stat cards), contribution calendar (heatmap), signature projects (by stars), and open source contributions.
14
+
15
+ ## Quick Reference
16
+
17
+ | Task | Command |
18
+ |------|---------|
19
+ | Generate locally | `npm run generate` (requires `gh auth login`) |
20
+ | Full CI check | `npm run ci` (fmt + lint + typecheck + test + build) |
21
+ | Build bundle | `npm run build` (ncc → `dist/`) |
22
+ | Run tests | `npm test` (vitest) |
23
+ | Type-check | `npm run typecheck` |
24
+ | Lint | `npm run lint` (biome) |
25
+ | Format fix | `npm run fmt:fix` |
26
+
27
+ ## Setup Guide
28
+
29
+ ### For a user's profile repo (`<username>/<username>`)
30
+
31
+ 1. Create `.github/workflows/metrics.yml`:
32
+
33
+ ```yaml
34
+ name: Metrics
35
+ on:
36
+ schedule:
37
+ - cron: "0 0 * * *"
38
+ workflow_dispatch:
39
+
40
+ permissions:
41
+ contents: write
42
+ models: read
43
+
44
+ jobs:
45
+ generate:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: urmzd/github-insights@main
50
+ with:
51
+ github-token: ${{ secrets.GITHUB_TOKEN }}
52
+ ```
53
+
54
+ 2. Optionally create `github-insights.yml` for customization (see Configuration below).
55
+ 3. Run the workflow — it commits SVGs to `assets/insights/` and generates `README.md`.
56
+
57
+ ### Local development
58
+
59
+ Requires Node.js 22+ and authenticated `gh` CLI.
60
+
61
+ ```sh
62
+ npm install
63
+ npm run generate
64
+ ```
65
+
66
+ Local mode differences:
67
+ - `commit-push` defaults to `false` (no git operations)
68
+ - README writes to `_README.md` (not `README.md`)
69
+ - All three template previews are generated in `examples/{classic,modern,minimal}/`
70
+ - Both preamble variants (full + short) are generated for preview
71
+
72
+ ## Configuration
73
+
74
+ ### `github-insights.yml` (all fields optional)
75
+
76
+ ```yaml
77
+ name: Display Name # overrides GitHub profile name
78
+ pronunciation: pronunciation # shown as subscript in heading
79
+ title: Software Engineer # blockquote under heading; guides AI expertise
80
+ desired_title: Senior SWE # AI context only — biases expertise categories
81
+ bio: Short bio text. # footer text in classic template
82
+ preamble: PREAMBLE.md # path to custom preamble (bypasses AI generation)
83
+ template: classic # "classic" | "modern" | "minimal"
84
+ sections:
85
+ - pulse
86
+ - languages
87
+ - expertise
88
+ - projects
89
+ - contributions
90
+ - calendar
91
+ ```
92
+
93
+ ### Action inputs
94
+
95
+ | Input | Default | Description |
96
+ |-------|---------|-------------|
97
+ | `github-token` | `${{ github.token }}` | Needs `contents: write` + `models: read` |
98
+ | `username` | `${{ github.repository_owner }}` | GitHub login to generate metrics for |
99
+ | `output-dir` | `assets/insights` | Directory for SVG output files |
100
+ | `commit-push` | `true` (CI) / `false` (local) | Whether to commit and push |
101
+ | `commit-message` | `chore: update metrics` | Git commit message |
102
+ | `config-file` | `github-insights.yml` | Path to config file |
103
+ | `readme-path` | `README.md` (CI) / `_README.md` (local) | Set to `none` to skip |
104
+ | `index-only` | `true` | `true` = single combined SVG; `false` = individual section SVGs |
105
+ | `template` | `classic` | README template style |
106
+ | `sections` | (all) | Comma-separated section keys to include |
107
+
108
+ ### Section keys
109
+
110
+ | Key | SVG filename | What it renders |
111
+ |-----|-------------|-----------------|
112
+ | `pulse` | `metrics-pulse.svg` | 4 stat cards: commits, PRs, reviews, active repos |
113
+ | `languages` | `metrics-languages.svg` | Donut chart of top 10 languages by bytes |
114
+ | `expertise` | `metrics-expertise.svg` | AI-generated proficiency bars by category |
115
+ | `projects` | `metrics-complexity.svg` | Top 5 repos by stars with descriptions |
116
+ | `calendar` | `metrics-calendar.svg` | GitHub contribution heatmap (1 year) |
117
+ | `contributions` | `metrics-contributions.svg` | External repos contributed to |
118
+
119
+ ### Templates
120
+
121
+ - **`classic`** (default): Formal layout — `# Name`, blockquote title, full AI preamble (2–4 paragraphs), social badges, SVG metrics, bio footer, attribution.
122
+ - **`modern`**: Friendly — `# Hi, I'm {firstName} 👋`, short preamble, active/maintained/inactive project lists in markdown, selective SVG sections.
123
+ - **`minimal`**: Clean — `# {firstName}`, short preamble, social badges, SVG metrics, attribution.
124
+
125
+ ## Architecture
126
+
127
+ ### Execution flow
128
+
129
+ ```
130
+ Inputs → Fetch (parallel) → AI calls (sequential) → Transform → Render sections → Write SVGs → Generate README → Commit
131
+ ```
132
+
133
+ ### Key source files
134
+
135
+ | File | Role |
136
+ |------|------|
137
+ | `src/index.ts` | Orchestration: fetch → transform → render → write → commit |
138
+ | `src/api.ts` | GitHub GraphQL queries + GitHub Models AI calls |
139
+ | `src/metrics.ts` | Data aggregation, complexity scoring, section building |
140
+ | `src/config.ts` | TOML config loading |
141
+ | `src/types.ts` | All TypeScript interfaces (`UserConfig`, `SectionDef`, `TemplateContext`, etc.) |
142
+ | `src/templates.ts` | Three README template functions + social badge builder |
143
+ | `src/theme.ts` | `THEME` colors, `LAYOUT` dimensions, `BAR_COLORS` palette |
144
+ | `src/parsers.ts` | Dependency manifest parsers (package.json, Cargo.toml, go.mod, etc.) |
145
+ | `src/components/` | SVG rendering components (custom JSX → SVG strings) |
146
+ | `src/jsx-factory.ts` | Custom `h()` / `Fragment()` JSX runtime (no React) |
147
+
148
+ ### Component rendering pattern
149
+
150
+ Every component follows the same signature:
151
+
152
+ ```ts
153
+ function renderXxx(data: ..., y: number): { svg: string; height: number }
154
+ ```
155
+
156
+ Components return SVG string fragments and their rendered height. The `y` parameter is the vertical cursor; heights are accumulated to stack sections vertically.
157
+
158
+ ### Theme constants (hardcoded in `src/theme.ts`)
159
+
160
+ ```
161
+ THEME.bg = "#0d1117" (dark background)
162
+ THEME.cardBg = "#161b22" (card backgrounds)
163
+ THEME.border = "#30363d" (card borders)
164
+ THEME.link = "#58a6ff" (card titles - blue)
165
+ THEME.text = "#c9d1d9" (primary text)
166
+ THEME.secondary= "#8b949e" (labels)
167
+ THEME.muted = "#6e7681" (values)
168
+
169
+ LAYOUT.width = 808 (fixed SVG canvas width)
170
+ LAYOUT.padX = 24
171
+ LAYOUT.padY = 24
172
+ LAYOUT.sectionGap = 30
173
+ ```
174
+
175
+ These are not configurable via inputs or TOML — changing them requires editing source.
176
+
177
+ ## Extending
178
+
179
+ ### Add a new section
180
+
181
+ 1. Create `src/components/<name>.tsx` with a render function: `(data, y) => { svg, height }`
182
+ 2. Import the custom JSX factory: `import { h, Fragment } from "../jsx-factory"`
183
+ 3. Add the section key to `SECTION_KEYS` map in `src/metrics.ts`
184
+ 4. Add a `SectionDef` entry in the `buildSections()` function in `src/metrics.ts`
185
+ 5. Fetch any new data needed in `src/index.ts`
186
+ 6. Add a `*.test.ts` file alongside the component
187
+ 7. Update `action.yml` if new inputs are needed
188
+
189
+ ### Add a new dependency parser
190
+
191
+ 1. Implement `PackageParser` interface in `src/parsers.ts`:
192
+ ```ts
193
+ { filenames: string[]; parseDependencies(text: string): string[] }
194
+ ```
195
+ 2. Add it to the `PARSERS` array — it auto-registers in `PARSER_MAP`
196
+
197
+ ### Add a new README template
198
+
199
+ 1. Add a function to `src/templates.ts` matching `(ctx: TemplateContext) => string`
200
+ 2. Register it in the `TEMPLATES` map
201
+ 3. Add the name to the `TemplateName` union in `src/types.ts`
202
+
203
+ ## Troubleshooting
204
+
205
+ ### "Expertise section missing"
206
+ The expertise section only appears when the AI call succeeds. Check:
207
+ - Token has `models: read` permission
208
+ - The workflow has `permissions: models: read`
209
+ - GitHub Models endpoint is reachable
210
+
211
+ ### "Calendar section missing"
212
+ Only rendered when contribution calendar data exists. The user must have public contributions.
213
+
214
+ ### "Contributions section missing"
215
+ Only rendered when the user has contributed to external (non-owned) repositories in the past year.
216
+
217
+ ### AI preamble is empty or generic
218
+ - The AI call uses `gpt-4.1` via GitHub Models — it needs diverse profile data to generate good output
219
+ - Provide `title` and `desired_title` in TOML config for better results
220
+ - Create a custom `PREAMBLE.md` to bypass AI entirely
221
+
222
+ ### Local generation fails
223
+ - Ensure `gh auth login` is done and `gh auth token` returns a valid token
224
+ - Ensure Node.js 22+ (`node --version`)
225
+ - The `GITHUB_TOKEN` and `GITHUB_REPOSITORY_OWNER` env vars are set automatically by `npm run generate` via `gh`
226
+
227
+ ### SVG looks wrong after changes
228
+ - Run `npm run build` to rebuild the `dist/` bundle — the action runs `dist/index.js`, not source directly
229
+ - SVG width is fixed at 808px; all layout math depends on this
230
+
231
+ ## Code Style Rules
232
+
233
+ - TypeScript strict mode, ES modules
234
+ - Biome for formatting and linting (not ESLint/Prettier)
235
+ - Tests colocated as `*.test.ts` / `*.test.tsx` alongside source
236
+ - Custom JSX factory (`h`, `Fragment`) — NOT React
237
+ - All AI calls and contribution fetches are non-fatal (catch → return empty)
package/sr.yaml ADDED
@@ -0,0 +1,16 @@
1
+ branches:
2
+ - main
3
+
4
+ floating_tags: true
5
+
6
+ version_files:
7
+ - package.json
8
+
9
+ changelog:
10
+ file: CHANGELOG.md
11
+
12
+ commit_types:
13
+ chore: patch
14
+
15
+ artifacts:
16
+ - dist.tar.gz
@@ -0,0 +1,84 @@
1
+ import type {
2
+ ContributionCalendar,
3
+ ContributionData,
4
+ RepoNode,
5
+ UserProfile,
6
+ } from "../types.js";
7
+
8
+ export const makeRepo = (overrides: Partial<RepoNode> = {}): RepoNode => ({
9
+ name: "test-repo",
10
+ description: "A test repository",
11
+ url: "https://github.com/user/test-repo",
12
+ stargazerCount: 10,
13
+ diskUsage: 1024,
14
+ primaryLanguage: { name: "TypeScript", color: "#3178c6" },
15
+ isArchived: false,
16
+ isFork: false,
17
+ createdAt: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString(),
18
+ pushedAt: new Date().toISOString(),
19
+ repositoryTopics: { nodes: [] },
20
+ languages: {
21
+ totalSize: 50000,
22
+ edges: [
23
+ { size: 30000, node: { name: "TypeScript", color: "#3178c6" } },
24
+ { size: 20000, node: { name: "JavaScript", color: "#f1e05a" } },
25
+ ],
26
+ },
27
+ ...overrides,
28
+ });
29
+
30
+ export const makeContributionData = (
31
+ overrides: Partial<ContributionData> = {},
32
+ ): ContributionData => ({
33
+ contributions: {
34
+ totalCommitContributions: 100,
35
+ totalPullRequestContributions: 20,
36
+ totalPullRequestReviewContributions: 10,
37
+ totalRepositoriesWithContributedCommits: 8,
38
+ },
39
+ externalRepos: { totalCount: 0, nodes: [] },
40
+ ...overrides,
41
+ });
42
+
43
+ export const makeContributionCalendar = (): ContributionCalendar => ({
44
+ totalContributions: 365,
45
+ weeks: [
46
+ {
47
+ contributionDays: [
48
+ { contributionCount: 0, date: "2025-01-05", color: "#161b22" },
49
+ { contributionCount: 3, date: "2025-01-06", color: "#0e4429" },
50
+ { contributionCount: 5, date: "2025-01-07", color: "#006d32" },
51
+ { contributionCount: 0, date: "2025-01-08", color: "#161b22" },
52
+ { contributionCount: 1, date: "2025-01-09", color: "#0e4429" },
53
+ { contributionCount: 10, date: "2025-01-10", color: "#39d353" },
54
+ { contributionCount: 0, date: "2025-01-11", color: "#161b22" },
55
+ ],
56
+ },
57
+ {
58
+ contributionDays: [
59
+ { contributionCount: 2, date: "2025-01-12", color: "#0e4429" },
60
+ { contributionCount: 0, date: "2025-01-13", color: "#161b22" },
61
+ { contributionCount: 7, date: "2025-01-14", color: "#006d32" },
62
+ { contributionCount: 0, date: "2025-01-15", color: "#161b22" },
63
+ { contributionCount: 4, date: "2025-01-16", color: "#0e4429" },
64
+ { contributionCount: 0, date: "2025-01-17", color: "#161b22" },
65
+ { contributionCount: 0, date: "2025-01-18", color: "#161b22" },
66
+ ],
67
+ },
68
+ ],
69
+ });
70
+
71
+ export const makeUserProfile = (
72
+ overrides: Partial<UserProfile> = {},
73
+ ): UserProfile => ({
74
+ name: "Urmzd Maharramoff",
75
+ bio: "Building tools for developers",
76
+ company: null,
77
+ location: "Austin, TX",
78
+ websiteUrl: "https://urmzd.dev",
79
+ twitterUsername: "urmzd",
80
+ socialAccounts: [
81
+ { provider: "LINKEDIN", url: "https://linkedin.com/in/urmzd" },
82
+ ],
83
+ ...overrides,
84
+ });