github-weekly-reporter 0.1.0 → 0.2.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 (176) hide show
  1. package/README.md +86 -115
  2. package/dist/cli/commands/deploy.d.ts.map +1 -1
  3. package/dist/cli/commands/deploy.js +9 -3
  4. package/dist/cli/commands/deploy.js.map +1 -1
  5. package/dist/cli/commands/fetch.d.ts +3 -0
  6. package/dist/cli/commands/fetch.d.ts.map +1 -0
  7. package/dist/cli/commands/fetch.js +148 -0
  8. package/dist/cli/commands/fetch.js.map +1 -0
  9. package/dist/cli/commands/generate.d.ts.map +1 -1
  10. package/dist/cli/commands/generate.js +59 -80
  11. package/dist/cli/commands/generate.js.map +1 -1
  12. package/dist/cli/commands/render.d.ts +3 -0
  13. package/dist/cli/commands/render.d.ts.map +1 -0
  14. package/dist/cli/commands/render.js +172 -0
  15. package/dist/cli/commands/render.js.map +1 -0
  16. package/dist/cli/commands/setup.d.ts +3 -0
  17. package/dist/cli/commands/setup.d.ts.map +1 -0
  18. package/dist/cli/commands/setup.js +709 -0
  19. package/dist/cli/commands/setup.js.map +1 -0
  20. package/dist/cli/index.js +6 -0
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/collector/clean-body.d.ts +2 -0
  23. package/dist/collector/clean-body.d.ts.map +1 -0
  24. package/dist/collector/clean-body.js +27 -0
  25. package/dist/collector/clean-body.js.map +1 -0
  26. package/dist/collector/date-range.d.ts +3 -2
  27. package/dist/collector/date-range.d.ts.map +1 -1
  28. package/dist/collector/date-range.js +125 -5
  29. package/dist/collector/date-range.js.map +1 -1
  30. package/dist/collector/fetch-contributions.d.ts +11 -1
  31. package/dist/collector/fetch-contributions.d.ts.map +1 -1
  32. package/dist/collector/fetch-contributions.js +15 -3
  33. package/dist/collector/fetch-contributions.js.map +1 -1
  34. package/dist/collector/fetch-events.d.ts +6 -0
  35. package/dist/collector/fetch-events.d.ts.map +1 -0
  36. package/dist/collector/fetch-events.js +106 -0
  37. package/dist/collector/fetch-events.js.map +1 -0
  38. package/dist/collector/fetch-repo-prs.d.ts +7 -0
  39. package/dist/collector/fetch-repo-prs.d.ts.map +1 -0
  40. package/dist/collector/fetch-repo-prs.js +62 -0
  41. package/dist/collector/fetch-repo-prs.js.map +1 -0
  42. package/dist/collector/queries.d.ts +1 -4
  43. package/dist/collector/queries.d.ts.map +1 -1
  44. package/dist/collector/queries.js +7 -59
  45. package/dist/collector/queries.js.map +1 -1
  46. package/dist/deployer/index-page.d.ts +22 -2
  47. package/dist/deployer/index-page.d.ts.map +1 -1
  48. package/dist/deployer/index-page.js +424 -33
  49. package/dist/deployer/index-page.js.map +1 -1
  50. package/dist/deployer/week.d.ts +1 -1
  51. package/dist/deployer/week.d.ts.map +1 -1
  52. package/dist/deployer/week.js +17 -6
  53. package/dist/deployer/week.js.map +1 -1
  54. package/dist/i18n/fonts.d.ts +8 -0
  55. package/dist/i18n/fonts.d.ts.map +1 -0
  56. package/dist/i18n/fonts.js +44 -0
  57. package/dist/i18n/fonts.js.map +1 -0
  58. package/dist/i18n/index.d.ts +28 -0
  59. package/dist/i18n/index.d.ts.map +1 -0
  60. package/dist/i18n/index.js +259 -0
  61. package/dist/i18n/index.js.map +1 -0
  62. package/dist/index.d.ts +2 -3
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +1 -2
  65. package/dist/index.js.map +1 -1
  66. package/dist/llm/index.d.ts +2 -1
  67. package/dist/llm/index.d.ts.map +1 -1
  68. package/dist/llm/index.js +77 -5
  69. package/dist/llm/index.js.map +1 -1
  70. package/dist/llm/preprocess.d.ts +3 -0
  71. package/dist/llm/preprocess.d.ts.map +1 -0
  72. package/dist/llm/preprocess.js +92 -0
  73. package/dist/llm/preprocess.js.map +1 -0
  74. package/dist/llm/prompt.d.ts.map +1 -1
  75. package/dist/llm/prompt.js +109 -29
  76. package/dist/llm/prompt.js.map +1 -1
  77. package/dist/llm/providers/anthropic.js +1 -1
  78. package/dist/llm/providers/anthropic.js.map +1 -1
  79. package/dist/llm/providers/grok.d.ts +3 -0
  80. package/dist/llm/providers/grok.d.ts.map +1 -0
  81. package/dist/llm/providers/grok.js +18 -0
  82. package/dist/llm/providers/grok.js.map +1 -0
  83. package/dist/llm/providers/groq.d.ts +3 -0
  84. package/dist/llm/providers/groq.d.ts.map +1 -0
  85. package/dist/llm/providers/groq.js +18 -0
  86. package/dist/llm/providers/groq.js.map +1 -0
  87. package/dist/llm/providers/openai.js +1 -1
  88. package/dist/llm/providers/openai.js.map +1 -1
  89. package/dist/llm/providers/openrouter.d.ts +3 -0
  90. package/dist/llm/providers/openrouter.d.ts.map +1 -0
  91. package/dist/llm/providers/openrouter.js +18 -0
  92. package/dist/llm/providers/openrouter.js.map +1 -0
  93. package/dist/llm/types.d.ts +5 -2
  94. package/dist/llm/types.d.ts.map +1 -1
  95. package/dist/renderer/helpers.d.ts +6 -1
  96. package/dist/renderer/helpers.d.ts.map +1 -1
  97. package/dist/renderer/helpers.js +54 -17
  98. package/dist/renderer/helpers.js.map +1 -1
  99. package/dist/renderer/index.d.ts +11 -2
  100. package/dist/renderer/index.d.ts.map +1 -1
  101. package/dist/renderer/index.js +41 -19
  102. package/dist/renderer/index.js.map +1 -1
  103. package/dist/renderer/og-image.d.ts +15 -0
  104. package/dist/renderer/og-image.d.ts.map +1 -0
  105. package/dist/renderer/og-image.js +152 -0
  106. package/dist/renderer/og-image.js.map +1 -0
  107. package/dist/renderer/themes.d.ts +2 -17
  108. package/dist/renderer/themes.d.ts.map +1 -1
  109. package/dist/renderer/themes.js +371 -147
  110. package/dist/renderer/themes.js.map +1 -1
  111. package/dist/types.d.ts +103 -11
  112. package/dist/types.d.ts.map +1 -1
  113. package/package.json +9 -2
  114. package/src/renderer/templates/partials/footer.hbs +3 -1
  115. package/src/renderer/templates/partials/header.hbs +10 -7
  116. package/src/renderer/templates/partials/highlights.hbs +24 -0
  117. package/src/renderer/templates/partials/overview.hbs +3 -0
  118. package/src/renderer/templates/partials/summaries.hbs +73 -0
  119. package/src/renderer/templates/report.hbs +100 -15
  120. package/dist/cli/config.d.ts +0 -11
  121. package/dist/cli/config.d.ts.map +0 -1
  122. package/dist/cli/config.js +0 -16
  123. package/dist/cli/config.js.map +0 -1
  124. package/dist/cli/config.test.d.ts +0 -2
  125. package/dist/cli/config.test.d.ts.map +0 -1
  126. package/dist/cli/config.test.js +0 -32
  127. package/dist/cli/config.test.js.map +0 -1
  128. package/dist/collector/aggregate.test.d.ts +0 -2
  129. package/dist/collector/aggregate.test.d.ts.map +0 -1
  130. package/dist/collector/aggregate.test.js +0 -88
  131. package/dist/collector/aggregate.test.js.map +0 -1
  132. package/dist/collector/date-range.test.d.ts +0 -2
  133. package/dist/collector/date-range.test.d.ts.map +0 -1
  134. package/dist/collector/date-range.test.js +0 -25
  135. package/dist/collector/date-range.test.js.map +0 -1
  136. package/dist/collector/fetch-issues.d.ts +0 -5
  137. package/dist/collector/fetch-issues.d.ts.map +0 -1
  138. package/dist/collector/fetch-issues.js +0 -31
  139. package/dist/collector/fetch-issues.js.map +0 -1
  140. package/dist/collector/fetch-languages.d.ts +0 -4
  141. package/dist/collector/fetch-languages.d.ts.map +0 -1
  142. package/dist/collector/fetch-languages.js +0 -42
  143. package/dist/collector/fetch-languages.js.map +0 -1
  144. package/dist/collector/fetch-pull-requests.d.ts +0 -5
  145. package/dist/collector/fetch-pull-requests.d.ts.map +0 -1
  146. package/dist/collector/fetch-pull-requests.js +0 -31
  147. package/dist/collector/fetch-pull-requests.js.map +0 -1
  148. package/dist/collector/index.d.ts +0 -3
  149. package/dist/collector/index.d.ts.map +0 -1
  150. package/dist/collector/index.js +0 -50
  151. package/dist/collector/index.js.map +0 -1
  152. package/dist/deployer/index-page.test.d.ts +0 -2
  153. package/dist/deployer/index-page.test.d.ts.map +0 -1
  154. package/dist/deployer/index-page.test.js +0 -29
  155. package/dist/deployer/index-page.test.js.map +0 -1
  156. package/dist/deployer/week.test.d.ts +0 -2
  157. package/dist/deployer/week.test.d.ts.map +0 -1
  158. package/dist/deployer/week.test.js +0 -21
  159. package/dist/deployer/week.test.js.map +0 -1
  160. package/dist/llm/llm.test.d.ts +0 -2
  161. package/dist/llm/llm.test.d.ts.map +0 -1
  162. package/dist/llm/llm.test.js +0 -24
  163. package/dist/llm/llm.test.js.map +0 -1
  164. package/dist/llm/prompt.test.d.ts +0 -2
  165. package/dist/llm/prompt.test.d.ts.map +0 -1
  166. package/dist/llm/prompt.test.js +0 -48
  167. package/dist/llm/prompt.test.js.map +0 -1
  168. package/dist/renderer/renderer.test.d.ts +0 -2
  169. package/dist/renderer/renderer.test.d.ts.map +0 -1
  170. package/dist/renderer/renderer.test.js +0 -111
  171. package/dist/renderer/renderer.test.js.map +0 -1
  172. package/src/renderer/templates/partials/heatmap.hbs +0 -11
  173. package/src/renderer/templates/partials/languages.hbs +0 -19
  174. package/src/renderer/templates/partials/narrative.hbs +0 -10
  175. package/src/renderer/templates/partials/repositories.hbs +0 -25
  176. package/src/renderer/templates/partials/stats.hbs +0 -8
package/README.md CHANGED
@@ -1,148 +1,119 @@
1
1
  # GitHub Weekly Reporter
2
2
 
3
- Generate beautiful weekly GitHub activity reports with optional AI-powered narratives.
3
+ Generate beautiful weekly GitHub activity reports with AI-powered narratives.
4
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.
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 automatically.
6
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
- ```
7
+ ## Prerequisites
41
8
 
42
- ## Commands
9
+ Before running setup, have these ready:
43
10
 
44
- ### `generate`
11
+ 1. **GitHub fine-grained PAT** with `All repositories` access and these permissions (all Read & Write):
12
+ `Administration`, `Contents`, `Actions`, `Secrets`, `Pages`, `Workflows`
13
+ ([Create one](https://github.com/settings/personal-access-tokens/new))
45
14
 
46
- Collect GitHub activity data and generate an HTML report.
15
+ 2. **LLM API key** from one of the supported providers.
16
+ | Provider | Free Tier | Get API Key |
17
+ |---|---|---|
18
+ | OpenRouter | Yes | https://openrouter.ai/settings/keys |
19
+ | Groq | Yes | https://console.groq.com/keys |
20
+ | Google Gemini | Yes | https://aistudio.google.com/apikey |
21
+ | OpenAI | No | https://platform.openai.com/api-keys |
22
+ | Anthropic | No | https://console.anthropic.com/settings/keys |
23
+ | Grok (xAI) | No | https://console.x.ai |
47
24
 
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`) |
25
+ 3. **LLM model name** for your chosen provider:
61
26
 
62
- Priority: CLI flag > environment variable > config file.
27
+ | Provider | Models |
28
+ |---|---|
29
+ | OpenRouter | https://openrouter.ai/models |
30
+ | Groq | https://console.groq.com/docs/models |
31
+ | Google Gemini | https://ai.google.dev/gemini-api/docs/models |
32
+ | OpenAI | https://platform.openai.com/docs/models |
33
+ | Anthropic | https://docs.anthropic.com/en/docs/about-claude/models |
34
+ | Grok (xAI) | https://docs.x.ai/docs/models |
63
35
 
64
- ### `deploy`
65
-
66
- Push generated report files to the `gh-pages` branch.
36
+ ## Quick Start
67
37
 
68
38
  ```bash
69
- github-weekly-reporter deploy [options]
39
+ npx github-weekly-reporter setup
70
40
  ```
71
41
 
72
- | Option | Description |
73
- |---|---|
74
- | `-d, --directory` | Directory containing generated files (default: `./report`) |
75
- | `-r, --repo` | Repository URL to push to |
42
+ The setup command will walk you through everything:
43
+ - Creates a repository
44
+ - Adds workflow files (daily fetch + weekly report)
45
+ - Configures secrets (PAT and LLM API key)
46
+ - Enables GitHub Pages
47
+ - Triggers your first weekly report
76
48
 
77
- ## Config File
49
+ See [Manual Setup](docs/manual-setup.md) if you prefer to configure everything yourself.
78
50
 
79
- Create `.github-weekly-reporter.toml` in your project root:
80
-
81
- ```toml
82
- username = "your-username"
83
- theme = "dark"
84
- output = "./report"
51
+ ## Features
85
52
 
86
- [llm]
87
- provider = "openai"
88
- model = "gpt-4o-mini"
89
- ```
53
+ - Weekly stats: commits, PRs opened/merged, issues, reviews
54
+ - Top repositories by activity
55
+ - Language breakdown (CSS-only chart)
56
+ - 7-day contribution heatmap
57
+ - AI-generated narrative summary (6 providers, free tiers available)
58
+ - Dark theme with responsive design
59
+ - Self-contained HTML (no external requests, no JavaScript required)
60
+ - SEO optimized (OG images, JSON-LD, sitemap)
61
+ - Deploys to GitHub Pages with weekly archive
62
+ - Internationalization (10 languages)
90
63
 
91
- Secrets (tokens, API keys) should be set via environment variables or CLI flags, not in the config file.
64
+ ## LLM Providers
92
65
 
93
- ## Report Contents
66
+ AI narratives are required for report generation. Six providers are supported, including free tiers:
94
67
 
95
- | Section | Requires AI | Description |
68
+ | Provider | Free Tier | Env Variable |
96
69
  |---|---|---|
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
70
+ | **OpenRouter** | Yes (25+ free models) | `OPENROUTER_API_KEY` |
71
+ | **Groq** | Yes (generous) | `GROQ_API_KEY` |
72
+ | **Google Gemini** | Yes | `GEMINI_API_KEY` |
73
+ | OpenAI | No | `OPENAI_API_KEY` |
74
+ | Anthropic | No | `ANTHROPIC_API_KEY` |
75
+ | Grok (xAI) | No | `GROK_API_KEY` |
105
76
 
106
- Two built-in themes:
77
+ OpenRouter and Groq are recommended for free usage. Both offer high-quality models at no cost. For model selection, see each provider's documentation:
107
78
 
108
- - `default`: clean light theme
109
- - `dark`: dark background, GitHub-inspired
79
+ - OpenRouter: https://openrouter.ai/models
80
+ - Groq: https://console.groq.com/docs/models
81
+ - Gemini: https://ai.google.dev/gemini-api/docs/models
82
+ - OpenAI: https://platform.openai.com/docs/models
83
+ - Anthropic: https://docs.anthropic.com/en/docs/about-claude/models
84
+ - Grok: https://docs.x.ai/docs/models
110
85
 
111
- ## AI Narrative
86
+ If the LLM call fails, the workflow will stop with an error. Make sure your API key and model name are correct.
112
87
 
113
- When an LLM provider and API key are configured, the report includes an AI-generated summary. All three major providers are supported:
88
+ ## Generating a Report Manually
114
89
 
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` |
90
+ After setup, daily fetches run automatically. To generate a weekly report anytime:
120
91
 
121
- If the LLM call fails, the report is generated without the AI section. The action never fails due to LLM errors.
92
+ 1. Go to your repository's **Actions** tab
93
+ 2. Click **Weekly Report**
94
+ 3. Click **Run workflow**
122
95
 
123
- ## GitHub Action Usage
96
+ The report will be built and deployed to GitHub Pages within a few minutes.
124
97
 
125
- ```yaml
126
- name: Weekly Report
127
- on:
128
- schedule:
129
- - cron: '0 9 * * 1' # Every Monday 9:00 UTC
130
- workflow_dispatch:
98
+ ## Supported Languages
131
99
 
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
- ```
100
+ | Code | Language |
101
+ |---|---|
102
+ | `en` | English |
103
+ | `ja` | Japanese |
104
+ | `zh-CN` | Chinese (Simplified) |
105
+ | `zh-TW` | Chinese (Traditional) |
106
+ | `ko` | Korean |
107
+ | `es` | Spanish |
108
+ | `fr` | French |
109
+ | `de` | German |
110
+ | `pt` | Portuguese |
111
+ | `ru` | Russian |
112
+
113
+ ## Documentation
114
+
115
+ - [Manual Setup](docs/manual-setup.md) - step-by-step guide without the setup command
116
+ - [CLI Reference](docs/cli-reference.md) - all commands and environment variables
146
117
 
147
118
  ## URL Structure
148
119
 
@@ -1 +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"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiDpC,eAAO,MAAM,cAAc,GAAI,SAAS,OAAO,KAAG,IAuBjD,CAAC"}
@@ -1,6 +1,7 @@
1
1
  // deploy command: push generated report directory to gh-pages branch
2
2
  import { deploy } from "../../deployer/index.js";
3
3
  import { getWeekId } from "../../deployer/week.js";
4
+ import { parseLocalDate } from "../../collector/date-range.js";
4
5
  const env = (key) => process.env[key];
5
6
  const buildRepoUrl = (repo) => {
6
7
  const repoSlug = repo ?? env("GITHUB_REPOSITORY");
@@ -23,7 +24,7 @@ const buildRepoUrl = (repo) => {
23
24
  return `https://github.com/${repoSlug}.git`;
24
25
  };
25
26
  const run = async (options) => {
26
- const weekId = getWeekId();
27
+ const weekId = getWeekId(options.date, options.timezone);
27
28
  console.log(`Deploying ${options.directory} to gh-pages...`);
28
29
  await deploy({
29
30
  repoUrl: options.repoUrl,
@@ -36,14 +37,19 @@ export const registerDeploy = (program) => {
36
37
  program
37
38
  .command("deploy")
38
39
  .description("Deploy generated report to GitHub Pages (gh-pages branch)")
39
- .option("-d, --directory <dir>", "Directory containing generated report files", "./report")
40
+ .option("-d, --directory <dir>", "Directory containing generated HTML files (env: OUTPUT_DIR, default: ./output)")
40
41
  .option("-r, --repo <slug>", "Repository (owner/repo or full URL, env: GITHUB_REPOSITORY)")
42
+ .option("--timezone <tz>", "IANA timezone (env: TIMEZONE, default: UTC)")
43
+ .option("--date <date>", "Date within the target week (YYYY-MM-DD, default: today)")
41
44
  .action(async (opts) => {
42
45
  try {
46
+ const timezone = opts.timezone ?? env("TIMEZONE") ?? "UTC";
43
47
  const repoUrl = buildRepoUrl(opts.repo);
44
48
  await run({
45
- directory: opts.directory,
49
+ directory: opts.directory ?? env("OUTPUT_DIR") ?? "./output",
46
50
  repoUrl,
51
+ timezone,
52
+ date: opts.date ? parseLocalDate(opts.date, timezone) : undefined,
47
53
  });
48
54
  }
49
55
  catch (error) {
@@ -1 +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"}
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;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAS/D,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,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEzD,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,gFAAgF,CAAC;SACjH,MAAM,CAAC,mBAAmB,EAAE,6DAA6D,CAAC;SAC1F,MAAM,CAAC,iBAAiB,EAAE,6CAA6C,CAAC;SACxE,MAAM,CAAC,eAAe,EAAE,0DAA0D,CAAC;SACnF,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC;YAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxC,MAAM,GAAG,CAAC;gBACR,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,YAAY,CAAC,IAAI,UAAU;gBAC5D,OAAO;gBACP,QAAQ;gBACR,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;aAClE,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 registerFetch: (program: Command) => void;
3
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/fetch.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmJpC,eAAO,MAAM,aAAa,GAAI,SAAS,OAAO,KAAG,IA4BhD,CAAC"}
@@ -0,0 +1,148 @@
1
+ // fetch commands: daily-fetch (events) and weekly-fetch (full data)
2
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { parse as parseYaml, stringify as toYaml } from "yaml";
5
+ import { graphql } from "@octokit/graphql";
6
+ import { buildWeeklyRange, toISODate, parseLocalDate } from "../../collector/date-range.js";
7
+ import { fetchEvents, dedupeEvents } from "../../collector/fetch-events.js";
8
+ import { fetchContributions } from "../../collector/fetch-contributions.js";
9
+ import { fetchPRsByRefs } from "../../collector/fetch-repo-prs.js";
10
+ import { aggregateRepositories } from "../../collector/aggregate.js";
11
+ import { getWeekId } from "../../deployer/week.js";
12
+ const env = (key) => process.env[key];
13
+ const resolveBaseOptions = (cli) => {
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
+ const username = cli.username ?? env("GITHUB_USERNAME");
18
+ if (!username)
19
+ throw new Error("GitHub username required. Pass --username or set GITHUB_USERNAME.");
20
+ const timezone = cli.timezone ?? env("TIMEZONE") ?? "UTC";
21
+ const date = cli.date ? parseLocalDate(cli.date, timezone) : undefined;
22
+ return { token, username, dataDir: cli.dataDir ?? env("DATA_DIR") ?? "./data", date, timezone };
23
+ };
24
+ const tryReadYaml = async (path) => {
25
+ try {
26
+ const raw = await readFile(path, "utf-8");
27
+ return parseYaml(raw);
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ };
33
+ // Extract PR references from events
34
+ const extractPRRefs = (events) => {
35
+ const refs = [];
36
+ events.forEach((e) => {
37
+ const p = e.payload;
38
+ if (p.kind === "pull_request" && p.number > 0) {
39
+ refs.push({ repo: e.repo, number: p.number });
40
+ }
41
+ if (p.kind === "review" && p.prNumber > 0) {
42
+ refs.push({ repo: e.repo, number: p.prNumber });
43
+ }
44
+ });
45
+ return refs;
46
+ };
47
+ // daily-fetch: accumulate events
48
+ const runDailyFetch = async (options) => {
49
+ const weekId = getWeekId(options.date, options.timezone);
50
+ const range = buildWeeklyRange(options.date, options.timezone);
51
+ const reportDir = join(options.dataDir, weekId.path);
52
+ await mkdir(reportDir, { recursive: true });
53
+ console.log(`Fetching events for ${options.username} (${weekId.path})...`);
54
+ const newEvents = await fetchEvents(options.token, options.username, range);
55
+ console.log(`Fetched ${newEvents.length} events.`);
56
+ const eventsPath = join(reportDir, "events.yaml");
57
+ const existing = await tryReadYaml(eventsPath) ?? [];
58
+ const merged = dedupeEvents([...existing, ...newEvents]);
59
+ await writeFile(eventsPath, toYaml(merged, { lineWidth: 120 }), "utf-8");
60
+ console.log(`Events accumulated: ${merged.length} total (${eventsPath})`);
61
+ };
62
+ // weekly-fetch: use accumulated events to find PRs, fetch each individually
63
+ const runWeeklyFetch = async (options) => {
64
+ const weekId = getWeekId(options.date, options.timezone);
65
+ const range = buildWeeklyRange(options.date, options.timezone);
66
+ const reportDir = join(options.dataDir, weekId.path);
67
+ await mkdir(reportDir, { recursive: true });
68
+ // Load accumulated events
69
+ const eventsPath = join(reportDir, "events.yaml");
70
+ const events = await tryReadYaml(eventsPath) ?? [];
71
+ console.log(`Loaded ${events.length} accumulated events.`);
72
+ // Extract PR refs from events
73
+ const prRefs = extractPRRefs(events);
74
+ console.log(`Found ${prRefs.length} PR references (${new Set(prRefs.map((r) => `${r.repo}#${r.number}`)).size} unique).`);
75
+ // Fetch individual PRs
76
+ console.log("Fetching PRs...");
77
+ const pullRequests = await fetchPRsByRefs(options.token, prRefs);
78
+ console.log(`Fetched ${pullRequests.length} PRs.`);
79
+ // Fetch contributions (GraphQL)
80
+ console.log("Fetching contribution stats...");
81
+ const gql = graphql.defaults({ headers: { authorization: `token ${options.token}` } });
82
+ const contributions = await fetchContributions(gql, options.username, range, options.timezone);
83
+ const repositories = aggregateRepositories(pullRequests, []);
84
+ const totalAdditions = pullRequests.reduce((sum, pr) => sum + pr.additions, 0);
85
+ const totalDeletions = pullRequests.reduce((sum, pr) => sum + pr.deletions, 0);
86
+ const githubData = {
87
+ username: contributions.username,
88
+ avatarUrl: contributions.avatarUrl,
89
+ profile: contributions.profile,
90
+ dateRange: {
91
+ from: toISODate(range.from, options.timezone),
92
+ to: toISODate(range.to, options.timezone),
93
+ },
94
+ stats: {
95
+ totalCommits: contributions.totalCommits,
96
+ totalAdditions,
97
+ totalDeletions,
98
+ prsOpened: pullRequests.filter((pr) => pr.author.toLowerCase() === options.username.toLowerCase()).length,
99
+ prsMerged: pullRequests.filter((pr) => pr.state === "merged" && pr.author.toLowerCase() === options.username.toLowerCase()).length,
100
+ prsReviewed: contributions.prsReviewed,
101
+ issuesOpened: 0,
102
+ issuesClosed: 0,
103
+ },
104
+ dailyCommits: contributions.dailyCommits,
105
+ repositories,
106
+ pullRequests,
107
+ issues: [],
108
+ events,
109
+ externalContributions: [],
110
+ };
111
+ const dataPath = join(reportDir, "github-data.yaml");
112
+ await writeFile(dataPath, toYaml(githubData, { lineWidth: 120 }), "utf-8");
113
+ console.log(`GitHub data written to ${dataPath}`);
114
+ console.log(`Total: ${pullRequests.length} PRs`);
115
+ };
116
+ const baseOptions = (cmd) => cmd
117
+ .option("-t, --token <token>", "GitHub token (env: GITHUB_TOKEN)")
118
+ .option("-u, --username <username>", "GitHub username (env: GITHUB_USERNAME)")
119
+ .option("--data-dir <dir>", "Data directory (env: DATA_DIR, default: ./data)")
120
+ .option("--timezone <tz>", "IANA timezone (env: TIMEZONE, default: UTC)")
121
+ .option("--date <date>", "Date within the target week (YYYY-MM-DD, default: today)");
122
+ export const registerFetch = (program) => {
123
+ baseOptions(program
124
+ .command("daily-fetch")
125
+ .description("Fetch today's GitHub events and accumulate (run daily via cron)")).action(async (opts) => {
126
+ try {
127
+ const options = resolveBaseOptions(opts);
128
+ await runDailyFetch(options);
129
+ }
130
+ catch (error) {
131
+ console.error("Error:", error instanceof Error ? error.message : error);
132
+ process.exit(1);
133
+ }
134
+ });
135
+ baseOptions(program
136
+ .command("weekly-fetch")
137
+ .description("Build full weekly data from accumulated events + individual PR fetches")).action(async (opts) => {
138
+ try {
139
+ const options = resolveBaseOptions(opts);
140
+ await runWeeklyFetch(options);
141
+ }
142
+ catch (error) {
143
+ console.error("Error:", error instanceof Error ? error.message : error);
144
+ process.exit(1);
145
+ }
146
+ });
147
+ };
148
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../../src/cli/commands/fetch.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuFpC,eAAO,MAAM,gBAAgB,GAAI,SAAS,OAAO,KAAG,IAoBnD,CAAC"}
@@ -1,100 +1,79 @@
1
- // generate command: collect data, render HTML, write files to output directory
2
- import { writeFile, mkdir, readdir } from "node:fs/promises";
1
+ // generate command: read github-data.yaml and generate LLM content
2
+ import { readFile, writeFile } from "node:fs/promises";
3
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";
4
+ import { parse as parseYaml, stringify as toYaml } from "yaml";
5
+ import { generateContent } from "../../llm/index.js";
8
6
  import { getWeekId } from "../../deployer/week.js";
9
- import { loadConfig } from "../config.js";
7
+ import { parseLocalDate } from "../../collector/date-range.js";
10
8
  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;
9
+ const resolveOptions = (cli) => {
10
+ const llmProvider = (cli.llmProvider ?? env("LLM_PROVIDER"));
11
+ if (!llmProvider)
12
+ throw new Error("LLM provider required. Pass --llm-provider or set LLM_PROVIDER.");
13
+ const providerKeyMap = {
14
+ openai: "OPENAI_API_KEY",
15
+ anthropic: "ANTHROPIC_API_KEY",
16
+ gemini: "GEMINI_API_KEY",
17
+ openrouter: "OPENROUTER_API_KEY",
18
+ groq: "GROQ_API_KEY",
19
+ grok: "GROK_API_KEY",
20
+ };
21
+ const envVarName = providerKeyMap[llmProvider];
22
+ const llmApiKey = cli.llmApiKey ?? (envVarName ? env(envVarName) : undefined);
23
+ if (!llmApiKey)
24
+ throw new Error(`LLM API key required. Pass --llm-api-key or set ${envVarName ?? "the provider's API key env var"}.`);
25
+ const llmModel = cli.llmModel ?? env("LLM_MODEL");
26
+ if (!llmModel)
27
+ throw new Error("LLM model required. Pass --llm-model or set LLM_MODEL.");
28
+ const language = (cli.language ?? env("LANGUAGE") ?? "en");
29
+ const timezone = cli.timezone ?? env("TIMEZONE") ?? "UTC";
30
+ const date = cli.date ? parseLocalDate(cli.date, timezone) : undefined;
28
31
  return {
29
- token,
30
- username,
31
- output: cli.output ?? config.output ?? "./report",
32
- theme: (cli.theme ?? config.theme ?? "default"),
32
+ dataDir: cli.dataDir ?? env("DATA_DIR") ?? "./data",
33
33
  llmProvider,
34
34
  llmApiKey,
35
35
  llmModel,
36
+ language,
37
+ timezone,
38
+ date,
36
39
  };
37
40
  };
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
41
  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
- });
42
+ const weekId = getWeekId(options.date, options.timezone);
43
+ const dataDir = join(options.dataDir, weekId.path);
44
+ const dataPath = join(dataDir, "github-data.yaml");
45
+ console.log(`Reading ${dataPath}...`);
46
+ const raw = await readFile(dataPath, "utf-8");
47
+ const data = parseYaml(raw);
48
+ console.log(`Generating AI content (${options.llmProvider}/${options.llmModel}, lang: ${options.language})...`);
49
+ const aiContent = await generateContent({ ...data, language: options.language }, {
50
+ provider: options.llmProvider,
51
+ apiKey: options.llmApiKey,
52
+ model: options.llmModel,
53
+ language: options.language,
54
+ });
55
+ if (!aiContent) {
56
+ console.error("LLM returned no content.");
57
+ process.exit(1);
66
58
  }
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}`);
59
+ const llmDataPath = join(dataDir, "llm-data.yaml");
60
+ await writeFile(llmDataPath, toYaml(aiContent, { lineWidth: 120 }), "utf-8");
61
+ console.log(`LLM data written to ${llmDataPath}`);
83
62
  };
84
63
  export const registerGenerate = (program) => {
85
64
  program
86
65
  .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)")
66
+ .description("Generate AI content from fetched GitHub data")
67
+ .option("--data-dir <dir>", "Data directory (env: DATA_DIR, default: ./data)")
68
+ .option("--llm-provider <provider>", "LLM provider (env: LLM_PROVIDER)")
69
+ .option("--llm-api-key <key>", "LLM API key (env: OPENROUTER_API_KEY / GROQ_API_KEY / GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / GROK_API_KEY)")
70
+ .option("--llm-model <model>", "LLM model name (env: LLM_MODEL)")
71
+ .option("--language <lang>", "Report language: en, ja, zh-CN, zh-TW, ko, es, fr, de, pt, ru (env: LANGUAGE, default: en)")
72
+ .option("--timezone <tz>", "IANA timezone (env: TIMEZONE, default: UTC)")
73
+ .option("--date <date>", "Date within the target week (YYYY-MM-DD, default: today)")
95
74
  .action(async (opts) => {
96
75
  try {
97
- const options = await resolveOptions(opts);
76
+ const options = resolveOptions(opts);
98
77
  await run(options);
99
78
  }
100
79
  catch (error) {