github-weekly-reporter 0.1.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 (147) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +163 -0
  3. package/dist/cli/commands/deploy.d.ts +3 -0
  4. package/dist/cli/commands/deploy.d.ts.map +1 -0
  5. package/dist/cli/commands/deploy.js +55 -0
  6. package/dist/cli/commands/deploy.js.map +1 -0
  7. package/dist/cli/commands/generate.d.ts +3 -0
  8. package/dist/cli/commands/generate.d.ts.map +1 -0
  9. package/dist/cli/commands/generate.js +106 -0
  10. package/dist/cli/commands/generate.js.map +1 -0
  11. package/dist/cli/config.d.ts +11 -0
  12. package/dist/cli/config.d.ts.map +1 -0
  13. package/dist/cli/config.js +16 -0
  14. package/dist/cli/config.js.map +1 -0
  15. package/dist/cli/config.test.d.ts +2 -0
  16. package/dist/cli/config.test.d.ts.map +1 -0
  17. package/dist/cli/config.test.js +32 -0
  18. package/dist/cli/config.test.js.map +1 -0
  19. package/dist/cli/index.d.ts +3 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +13 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/collector/aggregate.d.ts +3 -0
  24. package/dist/collector/aggregate.d.ts.map +1 -0
  25. package/dist/collector/aggregate.js +34 -0
  26. package/dist/collector/aggregate.js.map +1 -0
  27. package/dist/collector/aggregate.test.d.ts +2 -0
  28. package/dist/collector/aggregate.test.d.ts.map +1 -0
  29. package/dist/collector/aggregate.test.js +88 -0
  30. package/dist/collector/aggregate.test.js.map +1 -0
  31. package/dist/collector/date-range.d.ts +7 -0
  32. package/dist/collector/date-range.d.ts.map +1 -0
  33. package/dist/collector/date-range.js +8 -0
  34. package/dist/collector/date-range.js.map +1 -0
  35. package/dist/collector/date-range.test.d.ts +2 -0
  36. package/dist/collector/date-range.test.d.ts.map +1 -0
  37. package/dist/collector/date-range.test.js +25 -0
  38. package/dist/collector/date-range.test.js.map +1 -0
  39. package/dist/collector/fetch-contributions.d.ts +12 -0
  40. package/dist/collector/fetch-contributions.d.ts.map +1 -0
  41. package/dist/collector/fetch-contributions.js +24 -0
  42. package/dist/collector/fetch-contributions.js.map +1 -0
  43. package/dist/collector/fetch-issues.d.ts +5 -0
  44. package/dist/collector/fetch-issues.d.ts.map +1 -0
  45. package/dist/collector/fetch-issues.js +31 -0
  46. package/dist/collector/fetch-issues.js.map +1 -0
  47. package/dist/collector/fetch-languages.d.ts +4 -0
  48. package/dist/collector/fetch-languages.d.ts.map +1 -0
  49. package/dist/collector/fetch-languages.js +42 -0
  50. package/dist/collector/fetch-languages.js.map +1 -0
  51. package/dist/collector/fetch-pull-requests.d.ts +5 -0
  52. package/dist/collector/fetch-pull-requests.d.ts.map +1 -0
  53. package/dist/collector/fetch-pull-requests.js +31 -0
  54. package/dist/collector/fetch-pull-requests.js.map +1 -0
  55. package/dist/collector/index.d.ts +3 -0
  56. package/dist/collector/index.d.ts.map +1 -0
  57. package/dist/collector/index.js +50 -0
  58. package/dist/collector/index.js.map +1 -0
  59. package/dist/collector/queries.d.ts +5 -0
  60. package/dist/collector/queries.d.ts.map +1 -0
  61. package/dist/collector/queries.js +81 -0
  62. package/dist/collector/queries.js.map +1 -0
  63. package/dist/deployer/index-page.d.ts +3 -0
  64. package/dist/deployer/index-page.d.ts.map +1 -0
  65. package/dist/deployer/index-page.js +51 -0
  66. package/dist/deployer/index-page.js.map +1 -0
  67. package/dist/deployer/index-page.test.d.ts +2 -0
  68. package/dist/deployer/index-page.test.d.ts.map +1 -0
  69. package/dist/deployer/index-page.test.js +29 -0
  70. package/dist/deployer/index-page.test.js.map +1 -0
  71. package/dist/deployer/index.d.ts +7 -0
  72. package/dist/deployer/index.d.ts.map +1 -0
  73. package/dist/deployer/index.js +16 -0
  74. package/dist/deployer/index.js.map +1 -0
  75. package/dist/deployer/week.d.ts +7 -0
  76. package/dist/deployer/week.d.ts.map +1 -0
  77. package/dist/deployer/week.js +14 -0
  78. package/dist/deployer/week.js.map +1 -0
  79. package/dist/deployer/week.test.d.ts +2 -0
  80. package/dist/deployer/week.test.d.ts.map +1 -0
  81. package/dist/deployer/week.test.js +21 -0
  82. package/dist/deployer/week.test.js.map +1 -0
  83. package/dist/index.d.ts +8 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +5 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/llm/index.d.ts +4 -0
  88. package/dist/llm/index.d.ts.map +1 -0
  89. package/dist/llm/index.js +25 -0
  90. package/dist/llm/index.js.map +1 -0
  91. package/dist/llm/llm.test.d.ts +2 -0
  92. package/dist/llm/llm.test.d.ts.map +1 -0
  93. package/dist/llm/llm.test.js +24 -0
  94. package/dist/llm/llm.test.js.map +1 -0
  95. package/dist/llm/prompt.d.ts +3 -0
  96. package/dist/llm/prompt.d.ts.map +1 -0
  97. package/dist/llm/prompt.js +31 -0
  98. package/dist/llm/prompt.js.map +1 -0
  99. package/dist/llm/prompt.test.d.ts +2 -0
  100. package/dist/llm/prompt.test.d.ts.map +1 -0
  101. package/dist/llm/prompt.test.js +48 -0
  102. package/dist/llm/prompt.test.js.map +1 -0
  103. package/dist/llm/providers/anthropic.d.ts +3 -0
  104. package/dist/llm/providers/anthropic.d.ts.map +1 -0
  105. package/dist/llm/providers/anthropic.js +17 -0
  106. package/dist/llm/providers/anthropic.js.map +1 -0
  107. package/dist/llm/providers/gemini.d.ts +3 -0
  108. package/dist/llm/providers/gemini.d.ts.map +1 -0
  109. package/dist/llm/providers/gemini.js +13 -0
  110. package/dist/llm/providers/gemini.js.map +1 -0
  111. package/dist/llm/providers/openai.d.ts +3 -0
  112. package/dist/llm/providers/openai.d.ts.map +1 -0
  113. package/dist/llm/providers/openai.js +17 -0
  114. package/dist/llm/providers/openai.js.map +1 -0
  115. package/dist/llm/types.d.ts +11 -0
  116. package/dist/llm/types.d.ts.map +1 -0
  117. package/dist/llm/types.js +3 -0
  118. package/dist/llm/types.js.map +1 -0
  119. package/dist/renderer/helpers.d.ts +3 -0
  120. package/dist/renderer/helpers.d.ts.map +1 -0
  121. package/dist/renderer/helpers.js +22 -0
  122. package/dist/renderer/helpers.js.map +1 -0
  123. package/dist/renderer/index.d.ts +3 -0
  124. package/dist/renderer/index.d.ts.map +1 -0
  125. package/dist/renderer/index.js +45 -0
  126. package/dist/renderer/index.js.map +1 -0
  127. package/dist/renderer/renderer.test.d.ts +2 -0
  128. package/dist/renderer/renderer.test.d.ts.map +1 -0
  129. package/dist/renderer/renderer.test.js +111 -0
  130. package/dist/renderer/renderer.test.js.map +1 -0
  131. package/dist/renderer/themes.d.ts +18 -0
  132. package/dist/renderer/themes.d.ts.map +1 -0
  133. package/dist/renderer/themes.js +180 -0
  134. package/dist/renderer/themes.js.map +1 -0
  135. package/dist/types.d.ts +68 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/types.js +3 -0
  138. package/dist/types.js.map +1 -0
  139. package/package.json +56 -0
  140. package/src/renderer/templates/partials/footer.hbs +3 -0
  141. package/src/renderer/templates/partials/header.hbs +7 -0
  142. package/src/renderer/templates/partials/heatmap.hbs +11 -0
  143. package/src/renderer/templates/partials/languages.hbs +19 -0
  144. package/src/renderer/templates/partials/narrative.hbs +10 -0
  145. package/src/renderer/templates/partials/repositories.hbs +25 -0
  146. package/src/renderer/templates/partials/stats.hbs +8 -0
  147. package/src/renderer/templates/report.hbs +27 -0
package/LICENSE ADDED
@@ -0,0 +1,38 @@
1
+ GitHub Weekly Reporter License
2
+
3
+ Copyright (c) 2026 deariary
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, subject to the following conditions:
9
+
10
+ 1. Attribution Requirement for Commercial Use
11
+
12
+ If the Software is used for commercial purposes (including but not limited
13
+ to use by a for-profit organization, use in a product or service offered
14
+ for sale, or use that generates revenue), all generated output must retain
15
+ the "Powered by deariary" footer link pointing to https://deariary.com
16
+ without modification. The link must remain visible, functional, and not
17
+ marked with rel="nofollow", rel="sponsored", or rel="ugc".
18
+
19
+ 2. Non-Commercial Use
20
+
21
+ Individuals using the Software for personal, non-commercial purposes may
22
+ remove or modify the footer link at their discretion.
23
+
24
+ 3. Derivative Works
25
+
26
+ Derivative works and forks of this Software are subject to the same
27
+ conditions described above. This license must be included in all copies
28
+ or substantial portions of the Software.
29
+
30
+ 4. No Warranty
31
+
32
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
37
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
38
+ DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # GitHub Weekly Reporter
2
+
3
+ Generate beautiful weekly GitHub activity reports with optional AI-powered narratives.
4
+
5
+ Collect your commits, pull requests, issues, and code reviews from the past week, render them as a polished static HTML page, and deploy to GitHub Pages.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npx github-weekly-reporter generate \
11
+ -t $GITHUB_TOKEN \
12
+ -u your-username
13
+
14
+ npx github-weekly-reporter deploy \
15
+ -d ./report \
16
+ -r https://github.com/your-username/weekly-report.git
17
+ ```
18
+
19
+ ## Features
20
+
21
+ - Weekly stats: commits, PRs opened/merged, issues, reviews
22
+ - Top repositories by activity
23
+ - Language breakdown (CSS-only chart)
24
+ - 7-day contribution heatmap
25
+ - AI-generated narrative summary (optional, supports OpenAI / Anthropic / Gemini)
26
+ - Light and dark themes
27
+ - Self-contained HTML (no external requests, no JavaScript required)
28
+ - Deploys to GitHub Pages with weekly archive
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install -g github-weekly-reporter
34
+ ```
35
+
36
+ Or run directly with npx:
37
+
38
+ ```bash
39
+ npx github-weekly-reporter <command>
40
+ ```
41
+
42
+ ## Commands
43
+
44
+ ### `generate`
45
+
46
+ Collect GitHub activity data and generate an HTML report.
47
+
48
+ ```bash
49
+ github-weekly-reporter generate [options]
50
+ ```
51
+
52
+ | Option | Env Variable | Config Key | Description |
53
+ |---|---|---|---|
54
+ | `-t, --token` | `GITHUB_TOKEN` | - | GitHub personal access token (required) |
55
+ | `-u, --username` | `GITHUB_USERNAME` | `username` | GitHub username (required) |
56
+ | `-o, --output` | - | `output` | Output directory (default: `./report`) |
57
+ | `--theme` | - | `theme` | `default` or `dark` |
58
+ | `--llm-provider` | `LLM_PROVIDER` | `llm.provider` | `openai`, `anthropic`, or `gemini` |
59
+ | `--llm-api-key` | `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` | - | LLM API key |
60
+ | `--llm-model` | `LLM_MODEL` | `llm.model` | Model name (e.g. `gpt-4o-mini`) |
61
+
62
+ Priority: CLI flag > environment variable > config file.
63
+
64
+ ### `deploy`
65
+
66
+ Push generated report files to the `gh-pages` branch.
67
+
68
+ ```bash
69
+ github-weekly-reporter deploy [options]
70
+ ```
71
+
72
+ | Option | Description |
73
+ |---|---|
74
+ | `-d, --directory` | Directory containing generated files (default: `./report`) |
75
+ | `-r, --repo` | Repository URL to push to |
76
+
77
+ ## Config File
78
+
79
+ Create `.github-weekly-reporter.toml` in your project root:
80
+
81
+ ```toml
82
+ username = "your-username"
83
+ theme = "dark"
84
+ output = "./report"
85
+
86
+ [llm]
87
+ provider = "openai"
88
+ model = "gpt-4o-mini"
89
+ ```
90
+
91
+ Secrets (tokens, API keys) should be set via environment variables or CLI flags, not in the config file.
92
+
93
+ ## Report Contents
94
+
95
+ | Section | Requires AI | Description |
96
+ |---|---|---|
97
+ | Weekly stats | No | Commits, PRs opened/merged, issues, reviews |
98
+ | Highlight repos | No | Most active repositories with counts |
99
+ | Language breakdown | No | CSS-only bar chart of languages used |
100
+ | Contribution graph | No | 7-day heatmap |
101
+ | AI narrative | Yes | Natural language summary of the week |
102
+ | Footer | - | "Powered by deariary" link |
103
+
104
+ ## Themes
105
+
106
+ Two built-in themes:
107
+
108
+ - `default`: clean light theme
109
+ - `dark`: dark background, GitHub-inspired
110
+
111
+ ## AI Narrative
112
+
113
+ When an LLM provider and API key are configured, the report includes an AI-generated summary. All three major providers are supported:
114
+
115
+ | Provider | Env Variable | Example Model |
116
+ |---|---|---|
117
+ | OpenAI | `OPENAI_API_KEY` | `gpt-4o-mini` |
118
+ | Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` |
119
+ | Google Gemini | `GEMINI_API_KEY` | `gemini-2.0-flash` |
120
+
121
+ If the LLM call fails, the report is generated without the AI section. The action never fails due to LLM errors.
122
+
123
+ ## GitHub Action Usage
124
+
125
+ ```yaml
126
+ name: Weekly Report
127
+ on:
128
+ schedule:
129
+ - cron: '0 9 * * 1' # Every Monday 9:00 UTC
130
+ workflow_dispatch:
131
+
132
+ jobs:
133
+ report:
134
+ runs-on: ubuntu-latest
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+
138
+ - uses: actions/setup-node@v4
139
+ with:
140
+ node-version: '20'
141
+
142
+ - run: npx github-weekly-reporter generate -t ${{ secrets.GITHUB_TOKEN }} -u ${{ github.actor }}
143
+
144
+ - run: npx github-weekly-reporter deploy -d ./report -r https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
145
+ ```
146
+
147
+ ## URL Structure
148
+
149
+ Reports are archived by ISO week:
150
+
151
+ ```
152
+ https://<user>.github.io/<repo>/ # Index page (links to all reports)
153
+ https://<user>.github.io/<repo>/2026/W14/ # Weekly report
154
+ https://<user>.github.io/<repo>/2026/W13/ # Previous week
155
+ ```
156
+
157
+ ## License
158
+
159
+ See [LICENSE](./LICENSE) for details.
160
+
161
+ - Commercial use: "Powered by deariary" footer link must be retained
162
+ - Personal/non-commercial use: footer link may be removed
163
+ - Derivative works: same conditions apply
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const registerDeploy: (program: Command) => void;
3
+ //# sourceMappingURL=deploy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8CpC,eAAO,MAAM,cAAc,GAAI,SAAS,OAAO,KAAG,IAkBjD,CAAC"}
@@ -0,0 +1,55 @@
1
+ // deploy command: push generated report directory to gh-pages branch
2
+ import { deploy } from "../../deployer/index.js";
3
+ import { getWeekId } from "../../deployer/week.js";
4
+ const env = (key) => process.env[key];
5
+ const buildRepoUrl = (repo) => {
6
+ const repoSlug = repo ?? env("GITHUB_REPOSITORY");
7
+ if (!repoSlug) {
8
+ throw new Error("Repository required. Pass --repo or set GITHUB_REPOSITORY.");
9
+ }
10
+ // Already a full URL
11
+ if (repoSlug.startsWith("http") || repoSlug.startsWith("git@")) {
12
+ const token = env("GITHUB_TOKEN");
13
+ if (token && repoSlug.startsWith("https://github.com/")) {
14
+ return repoSlug.replace("https://github.com/", `https://x-access-token:${token}@github.com/`);
15
+ }
16
+ return repoSlug;
17
+ }
18
+ // owner/repo slug
19
+ const token = env("GITHUB_TOKEN");
20
+ if (token) {
21
+ return `https://x-access-token:${token}@github.com/${repoSlug}.git`;
22
+ }
23
+ return `https://github.com/${repoSlug}.git`;
24
+ };
25
+ const run = async (options) => {
26
+ const weekId = getWeekId();
27
+ console.log(`Deploying ${options.directory} to gh-pages...`);
28
+ await deploy({
29
+ repoUrl: options.repoUrl,
30
+ directory: options.directory,
31
+ message: `report: ${weekId.path}`,
32
+ });
33
+ console.log("Deployed successfully!");
34
+ };
35
+ export const registerDeploy = (program) => {
36
+ program
37
+ .command("deploy")
38
+ .description("Deploy generated report to GitHub Pages (gh-pages branch)")
39
+ .option("-d, --directory <dir>", "Directory containing generated report files", "./report")
40
+ .option("-r, --repo <slug>", "Repository (owner/repo or full URL, env: GITHUB_REPOSITORY)")
41
+ .action(async (opts) => {
42
+ try {
43
+ const repoUrl = buildRepoUrl(opts.repo);
44
+ await run({
45
+ directory: opts.directory,
46
+ repoUrl,
47
+ });
48
+ }
49
+ catch (error) {
50
+ console.error("Error:", error instanceof Error ? error.message : error);
51
+ process.exit(1);
52
+ }
53
+ });
54
+ };
55
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAGrE,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAOnD,MAAM,GAAG,GAAG,CAAC,GAAW,EAAsB,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAElE,MAAM,YAAY,GAAG,CAAC,IAAwB,EAAU,EAAE;IACxD,MAAM,QAAQ,GAAG,IAAI,IAAI,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,qBAAqB;IACrB,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/D,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;QAClC,IAAI,KAAK,IAAI,QAAQ,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;YACxD,OAAO,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,0BAA0B,KAAK,cAAc,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kBAAkB;IAClB,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,0BAA0B,KAAK,eAAe,QAAQ,MAAM,CAAC;IACtE,CAAC;IACD,OAAO,sBAAsB,QAAQ,MAAM,CAAC;AAC9C,CAAC,CAAC;AAEF,MAAM,GAAG,GAAG,KAAK,EAAE,OAA6B,EAAiB,EAAE;IACjE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,CAAC,SAAS,iBAAiB,CAAC,CAAC;IAC7D,MAAM,MAAM,CAAC;QACX,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,OAAO,EAAE,WAAW,MAAM,CAAC,IAAI,EAAE;KAClC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAQ,EAAE;IACvD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2DAA2D,CAAC;SACxE,MAAM,CAAC,uBAAuB,EAAE,6CAA6C,EAAE,UAAU,CAAC;SAC1F,MAAM,CAAC,mBAAmB,EAAE,6DAA6D,CAAC;SAC1F,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,GAAG,CAAC;gBACR,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const registerGenerate: (program: Command) => void;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6GpC,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,IAoBnD,CAAC"}
@@ -0,0 +1,106 @@
1
+ // generate command: collect data, render HTML, write files to output directory
2
+ import { writeFile, mkdir, readdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { collectWeeklyData } from "../../collector/index.js";
5
+ import { renderReport } from "../../renderer/index.js";
6
+ import { generateNarrative } from "../../llm/index.js";
7
+ import { renderIndexPage } from "../../deployer/index-page.js";
8
+ import { getWeekId } from "../../deployer/week.js";
9
+ import { loadConfig } from "../config.js";
10
+ const env = (key) => process.env[key];
11
+ // Priority: CLI flag > env var > config file
12
+ const resolveOptions = async (cli) => {
13
+ const config = await loadConfig();
14
+ const token = cli.token ?? env("GITHUB_TOKEN");
15
+ if (!token) {
16
+ throw new Error("GitHub token required. Pass --token or set GITHUB_TOKEN.");
17
+ }
18
+ const username = cli.username ?? env("GITHUB_USERNAME") ?? config.username;
19
+ if (!username) {
20
+ throw new Error("GitHub username required. Pass --username, set GITHUB_USERNAME, or add to config file.");
21
+ }
22
+ const llmProvider = (cli.llmProvider ?? env("LLM_PROVIDER") ?? config.llm?.provider);
23
+ const llmApiKey = cli.llmApiKey
24
+ ?? env("OPENAI_API_KEY")
25
+ ?? env("ANTHROPIC_API_KEY")
26
+ ?? env("GEMINI_API_KEY");
27
+ const llmModel = cli.llmModel ?? env("LLM_MODEL") ?? config.llm?.model;
28
+ return {
29
+ token,
30
+ username,
31
+ output: cli.output ?? config.output ?? "./report",
32
+ theme: (cli.theme ?? config.theme ?? "default"),
33
+ llmProvider,
34
+ llmApiKey,
35
+ llmModel,
36
+ };
37
+ };
38
+ const listReportDirs = async (dir) => {
39
+ const paths = [];
40
+ let entries = [];
41
+ try {
42
+ entries = await readdir(dir);
43
+ }
44
+ catch {
45
+ return paths;
46
+ }
47
+ for (const year of entries.filter((n) => /^\d{4}$/.test(n))) {
48
+ const weeks = await readdir(join(dir, year));
49
+ weeks
50
+ .filter((n) => /^W\d{2}$/.test(n))
51
+ .forEach((w) => paths.push(`${year}/${w}`));
52
+ }
53
+ return paths;
54
+ };
55
+ const run = async (options) => {
56
+ const weekId = getWeekId();
57
+ console.log(`Collecting data for ${options.username}...`);
58
+ const data = await collectWeeklyData(options.token, options.username);
59
+ if (options.llmProvider && options.llmApiKey && options.llmModel) {
60
+ console.log(`Generating AI narrative (${options.llmProvider}/${options.llmModel})...`);
61
+ data.aiNarrative = await generateNarrative(data, {
62
+ provider: options.llmProvider,
63
+ apiKey: options.llmApiKey,
64
+ model: options.llmModel,
65
+ });
66
+ }
67
+ console.log(`Rendering report (theme: ${options.theme})...`);
68
+ const html = renderReport(data, options.theme);
69
+ // Write report to week directory
70
+ const reportDir = join(options.output, weekId.path);
71
+ await mkdir(reportDir, { recursive: true });
72
+ const reportPath = join(reportDir, "index.html");
73
+ await writeFile(reportPath, html, "utf-8");
74
+ console.log(`Report written to ${reportPath}`);
75
+ // Write index page
76
+ const allReports = await listReportDirs(options.output);
77
+ if (!allReports.includes(weekId.path))
78
+ allReports.push(weekId.path);
79
+ const indexHtml = renderIndexPage(allReports, options.theme);
80
+ const indexPath = join(options.output, "index.html");
81
+ await writeFile(indexPath, indexHtml, "utf-8");
82
+ console.log(`Index written to ${indexPath}`);
83
+ };
84
+ export const registerGenerate = (program) => {
85
+ program
86
+ .command("generate")
87
+ .description("Collect GitHub data and generate a weekly report")
88
+ .option("-t, --token <token>", "GitHub token (env: GITHUB_TOKEN)")
89
+ .option("-u, --username <username>", "GitHub username (env: GITHUB_USERNAME, config: username)")
90
+ .option("-o, --output <dir>", "Output directory (config: output)")
91
+ .option("--theme <theme>", "Report theme (config: theme)")
92
+ .option("--llm-provider <provider>", "LLM provider (env: LLM_PROVIDER, config: llm.provider)")
93
+ .option("--llm-api-key <key>", "LLM API key (env: OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY)")
94
+ .option("--llm-model <model>", "LLM model name (env: LLM_MODEL, config: llm.model)")
95
+ .action(async (opts) => {
96
+ try {
97
+ const options = await resolveOptions(opts);
98
+ await run(options);
99
+ }
100
+ catch (error) {
101
+ console.error("Error:", error instanceof Error ? error.message : error);
102
+ process.exit(1);
103
+ }
104
+ });
105
+ };
106
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type FileConfig = {
2
+ username?: string;
3
+ theme?: string;
4
+ output?: string;
5
+ llm?: {
6
+ provider?: string;
7
+ model?: string;
8
+ };
9
+ };
10
+ export declare const loadConfig: (dir?: string) => Promise<FileConfig>;
11
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAIF,eAAO,MAAM,UAAU,GAAU,MAAK,MAAsB,KAAG,OAAO,CAAC,UAAU,CAQhF,CAAC"}
@@ -0,0 +1,16 @@
1
+ // Load config from .github-weekly-reporter.toml
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { parse } from "smol-toml";
5
+ const CONFIG_FILENAME = ".github-weekly-reporter.toml";
6
+ export const loadConfig = async (dir = process.cwd()) => {
7
+ const path = resolve(dir, CONFIG_FILENAME);
8
+ try {
9
+ const content = await readFile(path, "utf-8");
10
+ return parse(content);
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ };
16
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAYlC,MAAM,eAAe,GAAG,8BAA8B,CAAC;AAEvD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE,EAAuB,EAAE;IACnF,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,OAAO,CAAe,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/cli/config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mkdtemp, writeFile, rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { loadConfig } from "./config.js";
6
+ describe("loadConfig", () => {
7
+ it("loads config from .github-weekly-reporter.toml", async () => {
8
+ const dir = await mkdtemp(join(tmpdir(), "gwr-config-"));
9
+ try {
10
+ await writeFile(join(dir, ".github-weekly-reporter.toml"), `username = "testuser"\ntheme = "dark"\n\n[llm]\nprovider = "openai"\nmodel = "gpt-4o-mini"\n`, "utf-8");
11
+ const config = await loadConfig(dir);
12
+ expect(config.username).toBe("testuser");
13
+ expect(config.theme).toBe("dark");
14
+ expect(config.llm?.provider).toBe("openai");
15
+ expect(config.llm?.model).toBe("gpt-4o-mini");
16
+ }
17
+ finally {
18
+ await rm(dir, { recursive: true, force: true });
19
+ }
20
+ });
21
+ it("returns empty object when file does not exist", async () => {
22
+ const dir = await mkdtemp(join(tmpdir(), "gwr-config-"));
23
+ try {
24
+ const config = await loadConfig(dir);
25
+ expect(config).toEqual({});
26
+ }
27
+ finally {
28
+ await rm(dir, { recursive: true, force: true });
29
+ }
30
+ });
31
+ });
32
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/cli/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,8BAA8B,CAAC,EACzC,8FAA8F,EAC9F,OAAO,CACR,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACzC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // CLI entrypoint
3
+ import { Command } from "commander";
4
+ import { registerGenerate } from "./commands/generate.js";
5
+ import { registerDeploy } from "./commands/deploy.js";
6
+ const program = new Command()
7
+ .name("github-weekly-reporter")
8
+ .description("Generate beautiful weekly GitHub activity reports")
9
+ .version("0.1.0");
10
+ registerGenerate(program);
11
+ registerDeploy(program);
12
+ program.parse();
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,iBAAiB;AAEjB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,wBAAwB,CAAC;KAC9B,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAC1B,cAAc,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { PullRequest, Issue, RepositoryActivity } from "../types.js";
2
+ export declare const aggregateRepositories: (pullRequests: PullRequest[], issues: Issue[]) => RepositoryActivity[];
3
+ //# sourceMappingURL=aggregate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate.d.ts","sourceRoot":"","sources":["../../src/collector/aggregate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE1E,eAAO,MAAM,qBAAqB,GAChC,cAAc,WAAW,EAAE,EAC3B,QAAQ,KAAK,EAAE,KACd,kBAAkB,EAmCpB,CAAC"}
@@ -0,0 +1,34 @@
1
+ // Aggregate per-repository activity from PRs and issues
2
+ export const aggregateRepositories = (pullRequests, issues) => {
3
+ const repoMap = new Map();
4
+ const getOrCreate = (name) => {
5
+ const existing = repoMap.get(name);
6
+ if (existing)
7
+ return existing;
8
+ const repo = {
9
+ name,
10
+ commits: 0,
11
+ prsOpened: 0,
12
+ prsMerged: 0,
13
+ issuesOpened: 0,
14
+ issuesClosed: 0,
15
+ url: `https://github.com/${name}`,
16
+ };
17
+ repoMap.set(name, repo);
18
+ return repo;
19
+ };
20
+ pullRequests.forEach((pr) => {
21
+ const repo = getOrCreate(pr.repository);
22
+ repo.prsOpened += 1;
23
+ if (pr.state === "merged")
24
+ repo.prsMerged += 1;
25
+ });
26
+ issues.forEach((issue) => {
27
+ const repo = getOrCreate(issue.repository);
28
+ repo.issuesOpened += 1;
29
+ if (issue.state === "closed")
30
+ repo.issuesClosed += 1;
31
+ });
32
+ return [...repoMap.values()].sort((a, b) => b.prsOpened + b.issuesOpened - (a.prsOpened + a.issuesOpened));
33
+ };
34
+ //# sourceMappingURL=aggregate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/collector/aggregate.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAIxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,YAA2B,EAC3B,MAAe,EACO,EAAE;IACxB,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IAEtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAsB,EAAE;QACvD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,IAAI,GAAuB;YAC/B,IAAI;YACJ,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,CAAC;YACZ,YAAY,EAAE,CAAC;YACf,YAAY,EAAE,CAAC;YACf,GAAG,EAAE,sBAAsB,IAAI,EAAE;SAClC,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACpB,IAAI,EAAE,CAAC,KAAK,KAAK,QAAQ;YAAE,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;QACvB,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ;YAAE,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,YAAY,CAAC,CAChE,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aggregate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate.test.d.ts","sourceRoot":"","sources":["../../src/collector/aggregate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { aggregateRepositories } from "./aggregate.js";
3
+ describe("aggregateRepositories", () => {
4
+ it("aggregates PRs and issues by repository", () => {
5
+ const prs = [
6
+ {
7
+ title: "Fix bug",
8
+ url: "https://github.com/org/repo-a/pull/1",
9
+ repository: "org/repo-a",
10
+ state: "merged",
11
+ createdAt: "2026-04-01T00:00:00Z",
12
+ mergedAt: "2026-04-02T00:00:00Z",
13
+ },
14
+ {
15
+ title: "Add feature",
16
+ url: "https://github.com/org/repo-a/pull/2",
17
+ repository: "org/repo-a",
18
+ state: "open",
19
+ createdAt: "2026-04-02T00:00:00Z",
20
+ mergedAt: null,
21
+ },
22
+ {
23
+ title: "Update docs",
24
+ url: "https://github.com/org/repo-b/pull/1",
25
+ repository: "org/repo-b",
26
+ state: "merged",
27
+ createdAt: "2026-04-01T00:00:00Z",
28
+ mergedAt: "2026-04-01T00:00:00Z",
29
+ },
30
+ ];
31
+ const issues = [
32
+ {
33
+ title: "Bug report",
34
+ url: "https://github.com/org/repo-a/issues/1",
35
+ repository: "org/repo-a",
36
+ state: "closed",
37
+ createdAt: "2026-04-01T00:00:00Z",
38
+ closedAt: "2026-04-02T00:00:00Z",
39
+ },
40
+ ];
41
+ const result = aggregateRepositories(prs, issues);
42
+ expect(result).toHaveLength(2);
43
+ const repoA = result.find((r) => r.name === "org/repo-a");
44
+ expect(repoA).toBeDefined();
45
+ expect(repoA.prsOpened).toBe(2);
46
+ expect(repoA.prsMerged).toBe(1);
47
+ expect(repoA.issuesOpened).toBe(1);
48
+ expect(repoA.issuesClosed).toBe(1);
49
+ const repoB = result.find((r) => r.name === "org/repo-b");
50
+ expect(repoB).toBeDefined();
51
+ expect(repoB.prsOpened).toBe(1);
52
+ expect(repoB.prsMerged).toBe(1);
53
+ });
54
+ it("sorts by total activity (PRs + issues) descending", () => {
55
+ const prs = [
56
+ {
57
+ title: "PR1",
58
+ url: "",
59
+ repository: "org/less-active",
60
+ state: "open",
61
+ createdAt: "",
62
+ mergedAt: null,
63
+ },
64
+ {
65
+ title: "PR2",
66
+ url: "",
67
+ repository: "org/more-active",
68
+ state: "open",
69
+ createdAt: "",
70
+ mergedAt: null,
71
+ },
72
+ {
73
+ title: "PR3",
74
+ url: "",
75
+ repository: "org/more-active",
76
+ state: "merged",
77
+ createdAt: "",
78
+ mergedAt: "",
79
+ },
80
+ ];
81
+ const result = aggregateRepositories(prs, []);
82
+ expect(result[0].name).toBe("org/more-active");
83
+ });
84
+ it("returns empty array when no activity", () => {
85
+ expect(aggregateRepositories([], [])).toEqual([]);
86
+ });
87
+ });
88
+ //# sourceMappingURL=aggregate.test.js.map