@webywz/breathing-github-profile 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 webywz
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 deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,273 @@
1
- # Temporary Holding Version
1
+ # breathing-github-profile
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **A living GitHub profile README — declarative config, marker-safe refresh, CLI + library.**
4
+ **会呼吸的 GitHub 主页 README:声明式配置、标记保护区刷新,CLI 与库双入口。**
5
+
6
+ [![npm](https://img.shields.io/npm/v/@webywz/breathing-github-profile.svg)](https://www.npmjs.com/package/@webywz/breathing-github-profile)
7
+ [![Test](https://github.com/webywz/breathing-github-profile/actions/workflows/test.yml/badge.svg)](https://github.com/webywz/breathing-github-profile/actions/workflows/test.yml)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
9
+
10
+ Package: `@webywz/breathing-github-profile` · CLI binary: `breathing-profile` · Node 18+
11
+
12
+ ---
13
+
14
+ ## Features · 特性
15
+
16
+ | | EN | 中文 |
17
+ | --- | --- | --- |
18
+ | Config | YAML / JSON define intro, sections, and style | YAML / JSON 声明介绍、章节与样式 |
19
+ | Refresh | Updates only `<!--MARKER:START-->…<!--MARKER:END-->` | 只改标记区,手写文案保留 |
20
+ | Stats | Stars, forks, repos, followers; language mix; featured repos | 统计、语言分布、精选仓库 |
21
+ | Sections | `stats` · `languages` · `repos` · `custom` | 四种章节类型 |
22
+ | Automation | Daily GitHub Actions workflow | Actions 每日定时刷新 |
23
+ | API | CLI commands + TypeScript library exports | CLI + 库 |
24
+
25
+ Live example: [webywz/webywz](https://github.com/webywz/webywz).
26
+
27
+ ---
28
+
29
+ ## Install · 安装
30
+
31
+ ```bash
32
+ npm i -g @webywz/breathing-github-profile
33
+ npx breathing-profile --help
34
+ # or without a global install:
35
+ npx @webywz/breathing-github-profile --help
36
+ ```
37
+
38
+ One-shot without installing globally:
39
+
40
+ ```bash
41
+ npx --yes @webywz/breathing-github-profile generate -c config.yml -o README.md
42
+ ```
43
+
44
+ From source (develop):
45
+
46
+ ```bash
47
+ git clone https://github.com/webywz/breathing-github-profile.git
48
+ cd breathing-github-profile
49
+ npm install
50
+ npm run build
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Quick start · 快速开始
56
+
57
+ ### 1. Config · 配置
58
+
59
+ Create `config.yml`:
60
+
61
+ ```yaml
62
+ username: your-github-username
63
+
64
+ intro:
65
+ zh: |
66
+ Full-stack developer · open source
67
+ Happy to connect.
68
+ en: |
69
+ Full-stack developer · open source
70
+ Let's connect.
71
+
72
+ featuredRepos:
73
+ - your-best-repo
74
+
75
+ sections:
76
+ - type: stats
77
+ marker: STATS
78
+ title: GitHub Stats · 统计
79
+
80
+ - type: languages
81
+ marker: LANGUAGES
82
+ title: Languages · 常用语言
83
+
84
+ - type: repos
85
+ marker: REPOS
86
+ title: Featured · 精选项目
87
+ ```
88
+
89
+ Full sample: [examples/webywz.yml](examples/webywz.yml).
90
+
91
+ ### 2. Generate · 生成
92
+
93
+ ```bash
94
+ npx breathing-profile generate -c config.yml -o README.md
95
+ npx breathing-profile refresh -c config.yml -r README.md
96
+ npx breathing-profile validate -c config.yml
97
+ ```
98
+
99
+ | Command | EN | 中文 |
100
+ | --- | --- | --- |
101
+ | `generate` | Write a full README from config + GitHub data | 按配置与 GitHub 数据生成完整 README |
102
+ | `refresh` | Rewrite marker sections only | 只刷新标记区 |
103
+ | `validate` | Check the config file | 校验配置 |
104
+
105
+ Optional: set `GITHUB_TOKEN` or `GH_TOKEN` (or use `secrets.GITHUB_TOKEN` in Actions) for higher API rate limits.
106
+
107
+ ---
108
+
109
+ ## Configuration · 配置说明
110
+
111
+ ### Top-level · 顶层字段
112
+
113
+ | Field | Type | Required | Notes |
114
+ | --- | --- | --- | --- |
115
+ | `username` | string | yes | GitHub login |
116
+ | `intro.zh` / `intro.en` | string | one of zh/en | Bilingual intro blocks |
117
+ | `featuredRepos` | string[] | no | Pinned repo names |
118
+ | `sections` | Section[] | no | Ordered sections |
119
+ | `style` | StyleConfig | no | Theme / layout knobs |
120
+
121
+ ### Section types · 章节类型
122
+
123
+ **`stats`** — totals (stars, forks, public repos, followers, following).
124
+
125
+ ```yaml
126
+ - type: stats
127
+ marker: STATS
128
+ title: GitHub Stats · 统计
129
+ ```
130
+
131
+ **`languages`** — top languages by share.
132
+
133
+ ```yaml
134
+ - type: languages
135
+ marker: LANGUAGES
136
+ title: Languages · 常用语言
137
+ ```
138
+
139
+ **`repos`** — featured list, or highest-star repos when `featuredRepos` is empty.
140
+
141
+ ```yaml
142
+ - type: repos
143
+ marker: REPOS
144
+ title: Featured · 精选项目
145
+ ```
146
+
147
+ **`custom`** — raw Markdown inside a marker.
148
+
149
+ ```yaml
150
+ - type: custom
151
+ marker: TECH_STACK
152
+ title: Stack · 技术栈
153
+ content: |
154
+ TypeScript · NestJS · FastAPI · Vue
155
+ ```
156
+
157
+ Markers must match exactly:
158
+
159
+ ```markdown
160
+ <!--STATS:START-->
161
+ …
162
+ <!--STATS:END-->
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Automation · 自动刷新
168
+
169
+ In your profile repo (`USER/USER`):
170
+
171
+ 1. Add `config.yml`
172
+ 2. Add `.github/workflows/refresh-profile.yml`:
173
+
174
+ ```yaml
175
+ name: Refresh profile README
176
+
177
+ on:
178
+ schedule:
179
+ - cron: '0 0 * * *' # daily 00:00 UTC
180
+ workflow_dispatch:
181
+
182
+ permissions:
183
+ contents: write
184
+
185
+ jobs:
186
+ refresh:
187
+ runs-on: ubuntu-latest
188
+ steps:
189
+ - uses: actions/checkout@v4
190
+ - uses: actions/setup-node@v4
191
+ with:
192
+ node-version: '20'
193
+ - run: npm i @webywz/breathing-github-profile@1.0.1
194
+ - name: Refresh README
195
+ env:
196
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
197
+ run: npx breathing-profile refresh
198
+ - name: Commit if changed
199
+ run: |
200
+ git config user.name "github-actions[bot]"
201
+ git config user.email "github-actions[bot]@users.noreply.github.com"
202
+ git add README.md data/profile.json
203
+ git diff --quiet || git commit -m "chore: refresh profile README"
204
+ git push
205
+ ```
206
+
207
+ Flow: cron → GitHub API → rewrite markers → commit only when content changed. Copy outside markers is never touched.
208
+
209
+ 流程:定时触发 → 拉 GitHub 数据 → 只改标记区 → 有变更才提交。标记外的手写内容不会被动。
210
+
211
+ ---
212
+
213
+ ## Library · 作为库使用
214
+
215
+ ```ts
216
+ import {
217
+ loadConfig,
218
+ GitHubClient,
219
+ ReadmeGenerator,
220
+ } from '@webywz/breathing-github-profile';
221
+
222
+ const config = loadConfig('config.yml');
223
+ const client = new GitHubClient(process.env.GITHUB_TOKEN || process.env.GH_TOKEN);
224
+ const data = await client.getProfileData(config.username);
225
+ const readme = new ReadmeGenerator(config, data).generate();
226
+ ```
227
+
228
+ Also exported: `validateConfig`, `getDefaultConfig`, `updateReadmeMarkers`.
229
+
230
+ ---
231
+
232
+ ## Develop · 开发
233
+
234
+ ```bash
235
+ git clone https://github.com/webywz/breathing-github-profile.git
236
+ cd breathing-github-profile
237
+ npm install
238
+ npm run build
239
+ npm test
240
+ npm run lint
241
+
242
+ npm run generate -- -c examples/webywz.yml -o examples/README.md
243
+ npm run refresh -- -c examples/webywz.yml -r examples/README.md
244
+ ```
245
+
246
+ ```
247
+ src/
248
+ types.ts types
249
+ config.ts load / validate
250
+ github.ts GitHub client
251
+ generator.ts README generate + marker update
252
+ cli.ts CLI
253
+ index.ts library entry
254
+ examples/ sample configs
255
+ ```
256
+
257
+ ---
258
+
259
+ ## FAQ · 常见问题
260
+
261
+ **Rate limits?** Set `GITHUB_TOKEN` or `GH_TOKEN`. Actions already injects `secrets.GITHUB_TOKEN`.
262
+
263
+ **Only update part of the README?** Use `refresh`, not `generate`.
264
+
265
+ **Markers not updating?** Names must match `marker` in config; use `<!--NAME:START-->` / `<!--NAME:END-->`.
266
+
267
+ ---
268
+
269
+ ## Links · 链接
270
+
271
+ - Demo profile: [webywz/webywz](https://github.com/webywz/webywz)
272
+ - Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
273
+ - License: [MIT](LICENSE) © webywz
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const ora_1 = __importDefault(require("ora"));
10
+ const fs_1 = require("fs");
11
+ const path_1 = require("path");
12
+ const config_1 = require("./config");
13
+ const github_1 = require("./github");
14
+ const generator_1 = require("./generator");
15
+ const version_1 = require("./version");
16
+ const program = new commander_1.Command();
17
+ program
18
+ .name('breathing-profile')
19
+ .description('Living GitHub profile README generator · 会呼吸的 GitHub profile README 生成器')
20
+ .version((0, version_1.readPackageVersion)());
21
+ program
22
+ .command('generate')
23
+ .description('Generate a full README · 生成完整 README.md')
24
+ .option('-c, --config <path>', 'Config path · 配置文件路径', 'config.yml')
25
+ .option('-o, --output <path>', 'Output path · 输出文件路径', 'README.md')
26
+ .action(async (options) => {
27
+ const spinner = (0, ora_1.default)('Generating README… · 正在生成 README...').start();
28
+ try {
29
+ if (!(0, fs_1.existsSync)(options.config)) {
30
+ throw new Error(`Config not found / 配置文件不存在: ${options.config}`);
31
+ }
32
+ const config = (0, config_1.loadConfig)(options.config);
33
+ const client = new github_1.GitHubClient();
34
+ spinner.text = `Fetching GitHub data for ${config.username}… · 正在获取数据...`;
35
+ const data = await client.getProfileData(config.username);
36
+ spinner.text = 'Writing README… · 正在生成 README 内容...';
37
+ const generator = new generator_1.ReadmeGenerator(config, data);
38
+ const readme = generator.generate();
39
+ (0, fs_1.writeFileSync)(options.output, readme, 'utf-8');
40
+ const dataDir = 'data';
41
+ if (!(0, fs_1.existsSync)(dataDir)) {
42
+ (0, fs_1.mkdirSync)(dataDir, { recursive: true });
43
+ }
44
+ (0, fs_1.writeFileSync)((0, path_1.join)(dataDir, 'profile.json'), JSON.stringify(data, null, 2), 'utf-8');
45
+ spinner.succeed(chalk_1.default.green(`✓ README written / 已生成: ${options.output}`));
46
+ console.log(chalk_1.default.gray(` Data saved / 数据已保存: ${(0, path_1.join)(dataDir, 'profile.json')}`));
47
+ }
48
+ catch (error) {
49
+ spinner.fail(chalk_1.default.red('Generate failed / 生成失败'));
50
+ console.error(chalk_1.default.red(`Error / 错误: ${error.message}`));
51
+ process.exit(1);
52
+ }
53
+ });
54
+ program
55
+ .command('refresh')
56
+ .description('Refresh marker sections only · 只刷新 README 标记部分')
57
+ .option('-c, --config <path>', 'Config path · 配置文件路径', 'config.yml')
58
+ .option('-r, --readme <path>', 'README path · README 文件路径', 'README.md')
59
+ .action(async (options) => {
60
+ const spinner = (0, ora_1.default)('Refreshing README… · 正在刷新 README...').start();
61
+ try {
62
+ if (!(0, fs_1.existsSync)(options.config)) {
63
+ throw new Error(`Config not found / 配置文件不存在: ${options.config}`);
64
+ }
65
+ if (!(0, fs_1.existsSync)(options.readme)) {
66
+ throw new Error(`README not found / README 文件不存在: ${options.readme}`);
67
+ }
68
+ const config = (0, config_1.loadConfig)(options.config);
69
+ const client = new github_1.GitHubClient();
70
+ spinner.text = `Fetching GitHub data for ${config.username}… · 正在获取数据...`;
71
+ const data = await client.getProfileData(config.username);
72
+ spinner.text = 'Updating markers… · 正在更新 README 标记部分...';
73
+ const existingReadme = (0, fs_1.readFileSync)(options.readme, 'utf-8');
74
+ const markerSections = (config.sections || []).map(section => {
75
+ const marker = section.marker || section.type.toUpperCase();
76
+ let content = '';
77
+ const tempGenerator = new generator_1.ReadmeGenerator(config, data);
78
+ const fullReadme = tempGenerator.generate();
79
+ const startMarker = `<!--${marker}:START-->`;
80
+ const endMarker = `<!--${marker}:END-->`;
81
+ const regex = new RegExp(`${escapeRegex(startMarker)}([\\s\\S]*?)${escapeRegex(endMarker)}`);
82
+ const match = fullReadme.match(regex);
83
+ if (match) {
84
+ content = match[1].trim();
85
+ }
86
+ return { marker, content };
87
+ });
88
+ const updatedReadme = (0, generator_1.updateReadmeMarkers)(existingReadme, markerSections);
89
+ (0, fs_1.writeFileSync)(options.readme, updatedReadme, 'utf-8');
90
+ const dataDir = 'data';
91
+ if (!(0, fs_1.existsSync)(dataDir)) {
92
+ (0, fs_1.mkdirSync)(dataDir, { recursive: true });
93
+ }
94
+ (0, fs_1.writeFileSync)((0, path_1.join)(dataDir, 'profile.json'), JSON.stringify(data, null, 2), 'utf-8');
95
+ spinner.succeed(chalk_1.default.green(`✓ README refreshed / 已刷新: ${options.readme}`));
96
+ console.log(chalk_1.default.gray(` Data saved / 数据已保存: ${(0, path_1.join)(dataDir, 'profile.json')}`));
97
+ }
98
+ catch (error) {
99
+ spinner.fail(chalk_1.default.red('Refresh failed / 刷新失败'));
100
+ console.error(chalk_1.default.red(`Error / 错误: ${error.message}`));
101
+ process.exit(1);
102
+ }
103
+ });
104
+ program
105
+ .command('validate')
106
+ .description('Validate config file · 验证配置文件')
107
+ .option('-c, --config <path>', 'Config path · 配置文件路径', 'config.yml')
108
+ .action(async (options) => {
109
+ try {
110
+ if (!(0, fs_1.existsSync)(options.config)) {
111
+ throw new Error(`Config not found / 配置文件不存在: ${options.config}`);
112
+ }
113
+ const config = (0, config_1.loadConfig)(options.config);
114
+ console.log(chalk_1.default.green('✓ Config valid / 配置文件有效'));
115
+ console.log(chalk_1.default.gray('\nConfig / 配置内容:'));
116
+ console.log(JSON.stringify(config, null, 2));
117
+ }
118
+ catch (error) {
119
+ console.error(chalk_1.default.red(`✗ Invalid config / 配置无效: ${error.message}`));
120
+ process.exit(1);
121
+ }
122
+ });
123
+ function escapeRegex(str) {
124
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
125
+ }
126
+ program.parse();
127
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;AAEA,yCAAoC;AACpC,kDAA0B;AAC1B,8CAAsB;AACtB,2BAAwE;AACxE,+BAA4B;AAC5B,qCAAsC;AACtC,qCAAwC;AACxC,2CAAmE;AACnE,uCAA+C;AAE/C,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,mBAAmB,CAAC;KACzB,WAAW,CAAC,yEAAyE,CAAC;KACtF,OAAO,CAAC,IAAA,4BAAkB,GAAE,CAAC,CAAC;AAEjC,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,YAAY,CAAC;KACnE,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,WAAW,CAAC;KAClE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,OAAO,GAAG,IAAA,aAAG,EAAC,qCAAqC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEnE,IAAI,CAAC;QACH,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,MAAM,EAAE,CAChD,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,qBAAY,EAAE,CAAC;QAElC,OAAO,CAAC,IAAI,GAAG,4BAA4B,MAAM,CAAC,QAAQ,eAAe,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE1D,OAAO,CAAC,IAAI,GAAG,qCAAqC,CAAC;QACrD,MAAM,SAAS,GAAG,IAAI,2BAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC;QAEpC,IAAA,kBAAa,EAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,MAAM,CAAC;QACvB,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,EAAE,CAAC;YACzB,IAAA,cAAS,EAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,IAAA,kBAAa,EACX,IAAA,WAAI,EAAC,OAAO,EAAE,cAAc,CAAC,EAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAC;QAEF,OAAO,CAAC,OAAO,CAAC,eAAK,CAAC,KAAK,CAAC,2BAA2B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yBAAyB,IAAA,WAAI,EAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,eAAgB,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,gDAAgD,CAAC;KAC7D,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,YAAY,CAAC;KACnE,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,EAAE,WAAW,CAAC;KACvE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,MAAM,OAAO,GAAG,IAAA,aAAG,EAAC,qCAAqC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEnE,IAAI,CAAC;QACH,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,MAAM,EAAE,CAChD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,oCAAoC,OAAO,CAAC,MAAM,EAAE,CACrD,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,qBAAY,EAAE,CAAC;QAElC,OAAO,CAAC,IAAI,GAAG,4BAA4B,MAAM,CAAC,QAAQ,eAAe,CAAC;QAC1E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE1D,OAAO,CAAC,IAAI,GAAG,yCAAyC,CAAC;QACzD,MAAM,cAAc,GAAG,IAAA,iBAAY,EAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAE7D,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;YAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5D,IAAI,OAAO,GAAG,EAAE,CAAC;YAEjB,MAAM,aAAa,GAAG,IAAI,2BAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;YAE5C,MAAM,WAAW,GAAG,OAAO,MAAM,WAAW,CAAC;YAC7C,MAAM,SAAS,GAAG,OAAO,MAAM,SAAS,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,MAAM,CACtB,GAAG,WAAW,CAAC,WAAW,CAAC,eAAe,WAAW,CAAC,SAAS,CAAC,EAAE,CACnE,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAEtC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,CAAC;YAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,IAAA,+BAAmB,EAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QAC1E,IAAA,kBAAa,EAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QAEtD,MAAM,OAAO,GAAG,MAAM,CAAC;QACvB,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,EAAE,CAAC;YACzB,IAAA,cAAS,EAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,IAAA,kBAAa,EACX,IAAA,WAAI,EAAC,OAAO,EAAE,cAAc,CAAC,EAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAC;QAEF,OAAO,CAAC,OAAO,CAAC,eAAK,CAAC,KAAK,CAAC,6BAA6B,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5E,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yBAAyB,IAAA,WAAI,EAAC,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,eAAgB,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,+BAA+B,CAAC;KAC5C,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,YAAY,CAAC;KACnE,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,IAAI,CAAC,IAAA,eAAU,EAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,+BAA+B,OAAO,CAAC,MAAM,EAAE,CAChD,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAA,mBAAU,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,4BAA6B,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { ProfileConfig } from './types';
2
+ export declare function loadConfig(configPath: string): ProfileConfig;
3
+ export declare function validateConfig(config: ProfileConfig): void;
4
+ export declare function getDefaultConfig(): Partial<ProfileConfig>;
5
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAc5D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CA+B1D;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAazD"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = loadConfig;
4
+ exports.validateConfig = validateConfig;
5
+ exports.getDefaultConfig = getDefaultConfig;
6
+ const fs_1 = require("fs");
7
+ const yaml_1 = require("yaml");
8
+ function loadConfig(configPath) {
9
+ const content = (0, fs_1.readFileSync)(configPath, 'utf-8');
10
+ let config;
11
+ if (configPath.endsWith('.json')) {
12
+ config = JSON.parse(content);
13
+ }
14
+ else if (configPath.endsWith('.yml') || configPath.endsWith('.yaml')) {
15
+ config = (0, yaml_1.parse)(content);
16
+ }
17
+ else {
18
+ throw new Error('配置文件必须是 .json, .yml 或 .yaml 格式');
19
+ }
20
+ validateConfig(config);
21
+ return config;
22
+ }
23
+ function validateConfig(config) {
24
+ if (!config.username || typeof config.username !== 'string') {
25
+ throw new Error('配置中必须包含有效的 username 字段');
26
+ }
27
+ if (!config.intro || typeof config.intro !== 'object') {
28
+ throw new Error('配置中必须包含 intro 对象');
29
+ }
30
+ if (!config.intro.zh && !config.intro.en) {
31
+ throw new Error('intro 必须至少包含 zh 或 en 字段');
32
+ }
33
+ if (config.featuredRepos && !Array.isArray(config.featuredRepos)) {
34
+ throw new Error('featuredRepos 必须是数组');
35
+ }
36
+ if (config.sections && !Array.isArray(config.sections)) {
37
+ throw new Error('sections 必须是数组');
38
+ }
39
+ if (config.sections) {
40
+ for (const section of config.sections) {
41
+ if (!['stats', 'languages', 'repos', 'custom'].includes(section.type)) {
42
+ throw new Error(`不支持的 section 类型: ${section.type}`);
43
+ }
44
+ if (section.type === 'custom' && !section.content) {
45
+ throw new Error('custom 类型的 section 必须包含 content 字段');
46
+ }
47
+ }
48
+ }
49
+ }
50
+ function getDefaultConfig() {
51
+ return {
52
+ sections: [
53
+ { type: 'stats', marker: 'STATS' },
54
+ { type: 'languages', marker: 'LANGUAGES' },
55
+ { type: 'repos', marker: 'REPOS' }
56
+ ],
57
+ style: {
58
+ theme: 'auto',
59
+ icons: true,
60
+ badges: true
61
+ }
62
+ };
63
+ }
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AAIA,gCAcC;AAED,wCA+BC;AAED,4CAaC;AAlED,2BAAkC;AAClC,+BAA0C;AAG1C,SAAgB,UAAU,CAAC,UAAkB;IAC3C,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAElD,IAAI,MAAqB,CAAC;IAC1B,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;SAAM,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvE,MAAM,GAAG,IAAA,YAAS,EAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,cAAc,CAAC,MAAqB;IAClD,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,MAAM,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtE,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAClD,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAgB,gBAAgB;IAC9B,OAAO;QACL,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;YAClC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE;YAC1C,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;SACnC;QACD,KAAK,EAAE;YACL,KAAK,EAAE,MAAM;YACb,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;SACb;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { ProfileConfig, ProfileData, MarkerSection } from './types';
2
+ export declare class ReadmeGenerator {
3
+ private config;
4
+ private data;
5
+ constructor(config: ProfileConfig, data: ProfileData);
6
+ generate(): string;
7
+ private generateHeader;
8
+ private generateIntro;
9
+ private generateSection;
10
+ private generateStats;
11
+ private generateLanguages;
12
+ private generateRepos;
13
+ private generateFooter;
14
+ }
15
+ export declare function updateReadmeMarkers(existingReadme: string, markerSections: MarkerSection[]): string;
16
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpE,qBAAa,eAAe;IAExB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;gBADJ,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,WAAW;IAG3B,QAAQ,IAAI,MAAM;IAiBlB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,cAAc;CAcvB;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,aAAa,EAAE,GAC9B,MAAM,CAsBR"}
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReadmeGenerator = void 0;
4
+ exports.updateReadmeMarkers = updateReadmeMarkers;
5
+ class ReadmeGenerator {
6
+ constructor(config, data) {
7
+ this.config = config;
8
+ this.data = data;
9
+ }
10
+ generate() {
11
+ const sections = [];
12
+ sections.push(this.generateHeader());
13
+ sections.push(this.generateIntro());
14
+ if (this.config.sections) {
15
+ for (const section of this.config.sections) {
16
+ sections.push(this.generateSection(section));
17
+ }
18
+ }
19
+ sections.push(this.generateFooter());
20
+ return sections.filter(s => s.trim()).join('\n\n');
21
+ }
22
+ generateHeader() {
23
+ const { user } = this.data;
24
+ return `# Hi there, I'm ${user.name || user.login} 👋
25
+
26
+ <div align="center">
27
+ <img src="${user.avatar_url}" width="150" height="150" alt="${user.login}" style="border-radius: 50%;" />
28
+ </div>`;
29
+ }
30
+ generateIntro() {
31
+ const { intro } = this.config;
32
+ const parts = [];
33
+ if (intro.zh) {
34
+ parts.push(`## 👨‍💻 关于我
35
+
36
+ ${intro.zh}`);
37
+ }
38
+ if (intro.en) {
39
+ parts.push(`## 👨‍💻 About Me
40
+
41
+ ${intro.en}`);
42
+ }
43
+ return parts.join('\n\n');
44
+ }
45
+ generateSection(section) {
46
+ const marker = section.marker || section.type.toUpperCase();
47
+ let content = '';
48
+ switch (section.type) {
49
+ case 'stats':
50
+ content = this.generateStats();
51
+ break;
52
+ case 'languages':
53
+ content = this.generateLanguages();
54
+ break;
55
+ case 'repos':
56
+ content = this.generateRepos();
57
+ break;
58
+ case 'custom':
59
+ content = section.content || '';
60
+ break;
61
+ }
62
+ if (!content)
63
+ return '';
64
+ return `<!--${marker}:START-->
65
+ ${section.title ? `## ${section.title}\n\n` : ''}${content}
66
+ <!--${marker}:END-->`;
67
+ }
68
+ generateStats() {
69
+ const { user, totalStars, totalForks } = this.data;
70
+ return `## 📊 GitHub 统计
71
+
72
+ <div align="center">
73
+
74
+ | 统计项 | 数值 |
75
+ |:---:|:---:|
76
+ | 🌟 总 Stars | ${totalStars} |
77
+ | 🍴 总 Forks | ${totalForks} |
78
+ | 📦 公开仓库 | ${user.public_repos} |
79
+ | 👥 关注者 | ${user.followers} |
80
+ | 💫 正在关注 | ${user.following} |
81
+
82
+ </div>`;
83
+ }
84
+ generateLanguages() {
85
+ const { languages } = this.data;
86
+ const sorted = Object.entries(languages)
87
+ .sort(([, a], [, b]) => b - a)
88
+ .slice(0, 8);
89
+ if (sorted.length === 0)
90
+ return '';
91
+ const rows = sorted.map(([lang, count]) => {
92
+ const percentage = ((count / Object.values(languages).reduce((a, b) => a + b, 0)) * 100).toFixed(1);
93
+ return `| ${lang} | ${count} | ${percentage}% |`;
94
+ }).join('\n');
95
+ return `## 💻 编程语言
96
+
97
+ | 语言 | 仓库数 | 占比 |
98
+ |:---|:---:|:---:|
99
+ ${rows}`;
100
+ }
101
+ generateRepos() {
102
+ const { repos } = this.data;
103
+ const featured = this.config.featuredRepos || [];
104
+ let displayRepos = repos;
105
+ if (featured.length > 0) {
106
+ displayRepos = repos.filter(r => featured.some(f => r.name === f || r.full_name === f));
107
+ }
108
+ else {
109
+ displayRepos = repos
110
+ .sort((a, b) => b.stargazers_count - a.stargazers_count)
111
+ .slice(0, 6);
112
+ }
113
+ if (displayRepos.length === 0)
114
+ return '';
115
+ const cards = displayRepos.map(repo => {
116
+ const lang = repo.language || 'N/A';
117
+ const desc = repo.description || '暂无描述';
118
+ return `### 📦 [${repo.name}](${repo.html_url})
119
+ ${desc}
120
+
121
+ ⭐ ${repo.stargazers_count} stars | 🍴 ${repo.forks_count} forks | 📝 ${lang}`;
122
+ }).join('\n\n');
123
+ return `## 🔥 精选项目
124
+
125
+ ${cards}`;
126
+ }
127
+ generateFooter() {
128
+ const { updatedAt } = this.data;
129
+ const date = new Date(updatedAt).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
130
+ return `---
131
+
132
+ <div align="center">
133
+
134
+ *此 README 由 [breathing-github-profile](https://github.com/webywz/breathing-github-profile) 自动生成*
135
+
136
+ *最后更新: ${date} (北京时间)*
137
+
138
+ </div>`;
139
+ }
140
+ }
141
+ exports.ReadmeGenerator = ReadmeGenerator;
142
+ function updateReadmeMarkers(existingReadme, markerSections) {
143
+ let updated = existingReadme;
144
+ for (const { marker, content } of markerSections) {
145
+ const startMarker = `<!--${marker}:START-->`;
146
+ const endMarker = `<!--${marker}:END-->`;
147
+ const regex = new RegExp(`${escapeRegex(startMarker)}[\\s\\S]*?${escapeRegex(endMarker)}`, 'g');
148
+ const replacement = `${startMarker}\n${content}\n${endMarker}`;
149
+ if (regex.test(updated)) {
150
+ updated = updated.replace(regex, replacement);
151
+ }
152
+ else {
153
+ updated += `\n\n${replacement}`;
154
+ }
155
+ }
156
+ return updated;
157
+ }
158
+ function escapeRegex(str) {
159
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
160
+ }
161
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { GitHubUser, GitHubRepo, ProfileData } from './types';
2
+ /** Resolve token: explicit arg, then GITHUB_TOKEN, then GH_TOKEN. */
3
+ export declare function resolveGithubToken(explicit?: string): string | undefined;
4
+ export declare class GitHubClient {
5
+ private token?;
6
+ private baseUrl;
7
+ constructor(token?: string);
8
+ /** Exposed for tests / callers that need to know if auth is set. */
9
+ get authToken(): string | undefined;
10
+ private fetch;
11
+ getUser(username: string): Promise<GitHubUser>;
12
+ getRepos(username: string): Promise<GitHubRepo[]>;
13
+ getProfileData(username: string): Promise<ProfileData>;
14
+ }
15
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE9D,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,OAAO,CAA4B;gBAE/B,KAAK,CAAC,EAAE,MAAM;IAI1B,oEAAoE;IACpE,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;YAEa,KAAK;IAmBb,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAqBjD,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CA4B7D"}
package/dist/github.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitHubClient = void 0;
4
+ exports.resolveGithubToken = resolveGithubToken;
5
+ /** Resolve token: explicit arg, then GITHUB_TOKEN, then GH_TOKEN. */
6
+ function resolveGithubToken(explicit) {
7
+ return explicit || process.env.GITHUB_TOKEN || process.env.GH_TOKEN || undefined;
8
+ }
9
+ class GitHubClient {
10
+ constructor(token) {
11
+ this.baseUrl = 'https://api.github.com';
12
+ this.token = resolveGithubToken(token);
13
+ }
14
+ /** Exposed for tests / callers that need to know if auth is set. */
15
+ get authToken() {
16
+ return this.token;
17
+ }
18
+ async fetch(endpoint) {
19
+ const headers = {
20
+ 'Accept': 'application/vnd.github.v3+json',
21
+ 'User-Agent': 'breathing-github-profile'
22
+ };
23
+ if (this.token) {
24
+ headers['Authorization'] = `token ${this.token}`;
25
+ }
26
+ const response = await fetch(`${this.baseUrl}${endpoint}`, { headers });
27
+ if (!response.ok) {
28
+ throw new Error(`GitHub API 错误 / GitHub API error: ${response.status} ${response.statusText}`);
29
+ }
30
+ return response.json();
31
+ }
32
+ async getUser(username) {
33
+ return this.fetch(`/users/${username}`);
34
+ }
35
+ async getRepos(username) {
36
+ const repos = [];
37
+ let page = 1;
38
+ const perPage = 100;
39
+ const maxPages = 10;
40
+ while (page <= maxPages) {
41
+ const batch = await this.fetch(`/users/${username}/repos?per_page=${perPage}&page=${page}&sort=updated`);
42
+ if (batch.length === 0)
43
+ break;
44
+ repos.push(...batch);
45
+ if (batch.length < perPage)
46
+ break;
47
+ page++;
48
+ }
49
+ return repos.filter(repo => !repo.fork);
50
+ }
51
+ async getProfileData(username) {
52
+ const [user, repos] = await Promise.all([
53
+ this.getUser(username),
54
+ this.getRepos(username)
55
+ ]);
56
+ const languages = {};
57
+ let totalStars = 0;
58
+ let totalForks = 0;
59
+ for (const repo of repos) {
60
+ totalStars += repo.stargazers_count;
61
+ totalForks += repo.forks_count;
62
+ if (repo.language) {
63
+ languages[repo.language] = (languages[repo.language] || 0) + 1;
64
+ }
65
+ }
66
+ return {
67
+ user,
68
+ repos,
69
+ languages,
70
+ totalStars,
71
+ totalForks,
72
+ updatedAt: new Date().toISOString()
73
+ };
74
+ }
75
+ }
76
+ exports.GitHubClient = GitHubClient;
77
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { ProfileConfig, ProfileData, GitHubUser, GitHubRepo, Section, StyleConfig } from './types';
2
+ export { loadConfig, validateConfig, getDefaultConfig } from './config';
3
+ export { GitHubClient, resolveGithubToken } from './github';
4
+ export { ReadmeGenerator, updateReadmeMarkers } from './generator';
5
+ export { readPackageVersion } from './version';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readPackageVersion = exports.updateReadmeMarkers = exports.ReadmeGenerator = exports.resolveGithubToken = exports.GitHubClient = exports.getDefaultConfig = exports.validateConfig = exports.loadConfig = void 0;
4
+ var config_1 = require("./config");
5
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
6
+ Object.defineProperty(exports, "validateConfig", { enumerable: true, get: function () { return config_1.validateConfig; } });
7
+ Object.defineProperty(exports, "getDefaultConfig", { enumerable: true, get: function () { return config_1.getDefaultConfig; } });
8
+ var github_1 = require("./github");
9
+ Object.defineProperty(exports, "GitHubClient", { enumerable: true, get: function () { return github_1.GitHubClient; } });
10
+ Object.defineProperty(exports, "resolveGithubToken", { enumerable: true, get: function () { return github_1.resolveGithubToken; } });
11
+ var generator_1 = require("./generator");
12
+ Object.defineProperty(exports, "ReadmeGenerator", { enumerable: true, get: function () { return generator_1.ReadmeGenerator; } });
13
+ Object.defineProperty(exports, "updateReadmeMarkers", { enumerable: true, get: function () { return generator_1.updateReadmeMarkers; } });
14
+ var version_1 = require("./version");
15
+ Object.defineProperty(exports, "readPackageVersion", { enumerable: true, get: function () { return version_1.readPackageVersion; } });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,mCAAwE;AAA/D,oGAAA,UAAU,OAAA;AAAE,wGAAA,cAAc,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACrD,mCAA4D;AAAnD,sGAAA,YAAY,OAAA;AAAE,4GAAA,kBAAkB,OAAA;AACzC,yCAAmE;AAA1D,4GAAA,eAAe,OAAA;AAAE,gHAAA,mBAAmB,OAAA;AAC7C,qCAA+C;AAAtC,6GAAA,kBAAkB,OAAA"}
@@ -0,0 +1,68 @@
1
+ export interface ProfileConfig {
2
+ username: string;
3
+ intro: {
4
+ zh?: string;
5
+ en?: string;
6
+ };
7
+ featuredRepos?: string[];
8
+ sections?: Section[];
9
+ style?: StyleConfig;
10
+ }
11
+ export interface Section {
12
+ type: 'stats' | 'languages' | 'repos' | 'custom';
13
+ title?: string;
14
+ content?: string;
15
+ marker?: string;
16
+ }
17
+ export interface StyleConfig {
18
+ theme?: 'light' | 'dark' | 'auto';
19
+ icons?: boolean;
20
+ badges?: boolean;
21
+ }
22
+ export interface GitHubUser {
23
+ login: string;
24
+ name: string;
25
+ bio: string;
26
+ public_repos: number;
27
+ followers: number;
28
+ following: number;
29
+ created_at: string;
30
+ avatar_url: string;
31
+ html_url: string;
32
+ location?: string;
33
+ blog?: string;
34
+ twitter_username?: string;
35
+ company?: string;
36
+ }
37
+ export interface GitHubRepo {
38
+ name: string;
39
+ full_name: string;
40
+ description: string;
41
+ html_url: string;
42
+ homepage: string;
43
+ stargazers_count: number;
44
+ forks_count: number;
45
+ language: string;
46
+ topics: string[];
47
+ created_at: string;
48
+ updated_at: string;
49
+ pushed_at: string;
50
+ size: number;
51
+ watchers_count: number;
52
+ open_issues_count: number;
53
+ archived: boolean;
54
+ fork: boolean;
55
+ }
56
+ export interface ProfileData {
57
+ user: GitHubUser;
58
+ repos: GitHubRepo[];
59
+ languages: Record<string, number>;
60
+ totalStars: number;
61
+ totalForks: number;
62
+ updatedAt: string;
63
+ }
64
+ export interface MarkerSection {
65
+ marker: string;
66
+ content: string;
67
+ }
68
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QACL,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ /** Read version from package.json (works from src/ via tsx/jest and from dist/ after build). */
2
+ export declare function readPackageVersion(): string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,gGAAgG;AAChG,wBAAgB,kBAAkB,IAAI,MAAM,CAW3C"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readPackageVersion = readPackageVersion;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ /** Read version from package.json (works from src/ via tsx/jest and from dist/ after build). */
7
+ function readPackageVersion() {
8
+ try {
9
+ const pkgPath = (0, path_1.join)(__dirname, '..', 'package.json');
10
+ const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
11
+ if (typeof pkg.version === 'string' && pkg.version.length > 0) {
12
+ return pkg.version;
13
+ }
14
+ }
15
+ catch {
16
+ // fall through
17
+ }
18
+ return '0.0.0';
19
+ }
20
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAIA,gDAWC;AAfD,2BAAkC;AAClC,+BAA4B;AAE5B,gGAAgG;AAChG,SAAgB,kBAAkB;IAChC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,OAAO,EAAE,OAAO,CAAC,CAAyB,CAAC;QAC/E,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9D,OAAO,GAAG,CAAC,OAAO,CAAC;QACrB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,137 @@
1
+ # 示例和演示
2
+
3
+ 本目录包含使用 breathing-github-profile 的示例配置。
4
+
5
+ ## 📄 webywz.yml
6
+
7
+ 完整的配置示例,展示所有可用功能:
8
+
9
+ - ✅ 中英文双语介绍
10
+ - ✅ 精选仓库列表
11
+ - ✅ 多种章节类型(统计、语言、项目)
12
+ - ✅ 自定义内容(技术栈徽章)
13
+ - ✅ 样式配置
14
+
15
+ ## 🚀 快速测试
16
+
17
+ ### 1. 验证配置
18
+
19
+ ```bash
20
+ npm run validate -- -c examples/webywz.yml
21
+ ```
22
+
23
+ 预期输出:
24
+ ```
25
+ ✓ 配置文件有效
26
+ ```
27
+
28
+ ### 2. 生成 README(演示模式)
29
+
30
+ ```bash
31
+ # 生成到临时文件
32
+ npm run generate -- -c examples/webywz.yml -o examples/test-output.md
33
+ ```
34
+
35
+ ### 3. 测试刷新功能
36
+
37
+ ```bash
38
+ # 首先生成一个 README
39
+ npm run generate -- -c examples/webywz.yml -o examples/demo.md
40
+
41
+ # 手动编辑 demo.md,在标记外添加自定义内容
42
+
43
+ # 刷新(只更新标记部分)
44
+ npm run refresh -- -c examples/webywz.yml -r examples/demo.md
45
+ ```
46
+
47
+ 你会发现自定义内容被保留,只有标记部分被更新!
48
+
49
+ ## 📝 创建你自己的配置
50
+
51
+ 1. 复制 `webywz.yml` 作为模板
52
+ 2. 修改 `username` 为你的 GitHub 用户名
53
+ 3. 更新 `intro` 中的介绍
54
+ 4. 根据需要调整 `sections`
55
+ 5. 运行 `npm run generate` 生成 README
56
+
57
+ ## 🔧 配置选项参考
58
+
59
+ ### 章节类型
60
+
61
+ #### stats
62
+ 显示 GitHub 统计信息
63
+
64
+ ```yaml
65
+ - type: stats
66
+ marker: STATS
67
+ title: 📊 我的统计
68
+ ```
69
+
70
+ #### languages
71
+ 显示编程语言分布
72
+
73
+ ```yaml
74
+ - type: languages
75
+ marker: LANGUAGES
76
+ title: 💻 技术栈
77
+ ```
78
+
79
+ #### repos
80
+ 显示精选项目
81
+
82
+ ```yaml
83
+ - type: repos
84
+ marker: REPOS
85
+ title: 🔥 热门项目
86
+ ```
87
+
88
+ #### custom
89
+ 自定义 Markdown 内容
90
+
91
+ ```yaml
92
+ - type: custom
93
+ marker: BADGES
94
+ title: 🎯 技能徽章
95
+ content: |
96
+ ![TypeScript](https://img.shields.io/badge/-TypeScript-blue)
97
+ ![React](https://img.shields.io/badge/-React-61DAFB)
98
+ ```
99
+
100
+ #### custom · contrib-aquarium embed
101
+
102
+ `custom` 也可嵌入外部 SVG。用 [contrib-aquarium](https://github.com/webywz/contrib-aquarium) 生成鱼缸,再用 `--width` 写入展示宽(viewBox 固定等比,鱼不会被压扁):
103
+
104
+ A `custom` section can also embed an external SVG. Generate a tank with [contrib-aquarium](https://github.com/webywz/contrib-aquarium), pass `--width` for display size (viewBox stays proportional so fish are not squashed):
105
+
106
+ ```bash
107
+ npx contrib-aquarium@0.1.2 generate --user YOUR_LOGIN --out assets/aquarium.svg --width 640
108
+ ```
109
+
110
+ ```yaml
111
+ - type: custom
112
+ marker: AQUARIUM
113
+ title: 🐠 Contribution aquarium
114
+ content: |
115
+ <p align="center">
116
+ <img src="assets/aquarium.svg" width="640" alt="contribution aquarium" />
117
+ </p>
118
+ ```
119
+
120
+ 把生成步骤放进 daily-refresh(Action 输入 `width: "640"`),与 README `<img width>` 对齐即可。 · Put the generate step in daily-refresh (`width: "640"`) so it matches the README `<img width>`.
121
+
122
+ ## 💡 提示
123
+
124
+ 1. **标记名称**: 确保每个章节的 `marker` 唯一
125
+ 2. **GitHub Token**: 设置 `GITHUB_TOKEN` 或 `GH_TOKEN` 环境变量以避免 API 速率限制
126
+ 3. **精选仓库**: 使用 `featuredRepos` 手动选择要展示的项目
127
+ 4. **自定义样式**: 在 `custom` 章节中使用 HTML 和 Markdown
128
+
129
+ ## 🌟 实际案例
130
+
131
+ 使用此工具的 Profile README:
132
+
133
+ - [webywz/webywz](https://github.com/webywz/webywz) - 配套的 profile 仓库
134
+
135
+ ---
136
+
137
+ 有问题?查看 [主文档](../README.md) 或提交 [issue](https://github.com/webywz/breathing-github-profile/issues)。
@@ -0,0 +1,21 @@
1
+ username: octocat
2
+
3
+ intro:
4
+ zh: |
5
+ 👋 你好!我是 Octocat
6
+
7
+ GitHub 的吉祥物,开源爱好者
8
+
9
+ en: |
10
+ 👋 Hi! I'm Octocat
11
+
12
+ GitHub's mascot and open source enthusiast
13
+
14
+ sections:
15
+ - type: stats
16
+ marker: STATS
17
+ title: 📊 统计数据
18
+
19
+ - type: languages
20
+ marker: LANGUAGES
21
+ title: 💻 编程语言
@@ -0,0 +1,66 @@
1
+ username: webywz
2
+
3
+ intro:
4
+ zh: |
5
+ 🚀 全栈开发者 | AI 爱好者 | 开源贡献者
6
+
7
+ 💡 专注于现代 Web 开发和人工智能技术
8
+ - 🔧 后端:Node.js (NestJS) / Python (FastAPI)
9
+ - 🎨 前端:React / Vue / TypeScript
10
+ - 🤖 AI:LangChain / Prompt Engineering
11
+ - 🛠 工具:GitHub Actions / Docker / Cloudflare
12
+
13
+ 📫 欢迎交流合作!
14
+
15
+ en: |
16
+ 🚀 Full-stack Developer | AI Enthusiast | Open Source Contributor
17
+
18
+ 💡 Focus on Modern Web Development and AI Technologies
19
+ - 🔧 Backend: Node.js (NestJS) / Python (FastAPI)
20
+ - 🎨 Frontend: React / Vue / TypeScript
21
+ - 🤖 AI: LangChain / Prompt Engineering
22
+ - 🛠 Tools: GitHub Actions / Docker / Cloudflare
23
+
24
+ 📫 Let's connect and collaborate!
25
+
26
+ featuredRepos:
27
+ - breathing-github-profile
28
+ - webywz
29
+
30
+ sections:
31
+ - type: stats
32
+ marker: STATS
33
+ title: 📊 GitHub 统计
34
+
35
+ - type: languages
36
+ marker: LANGUAGES
37
+ title: 💻 常用语言
38
+
39
+ - type: repos
40
+ marker: REPOS
41
+ title: 🔥 精选项目
42
+
43
+ - type: custom
44
+ marker: TECH_STACK
45
+ title: 🛠 技术栈
46
+ content: |
47
+ ### 后端开发
48
+ ![Node.js](https://img.shields.io/badge/-Node.js-339933?style=flat-square&logo=node.js&logoColor=white)
49
+ ![NestJS](https://img.shields.io/badge/-NestJS-E0234E?style=flat-square&logo=nestjs&logoColor=white)
50
+ ![Python](https://img.shields.io/badge/-Python-3776AB?style=flat-square&logo=python&logoColor=white)
51
+ ![FastAPI](https://img.shields.io/badge/-FastAPI-009688?style=flat-square&logo=fastapi&logoColor=white)
52
+
53
+ ### 前端开发
54
+ ![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)
55
+ ![React](https://img.shields.io/badge/-React-61DAFB?style=flat-square&logo=react&logoColor=black)
56
+ ![Vue](https://img.shields.io/badge/-Vue-4FC08D?style=flat-square&logo=vue.js&logoColor=white)
57
+
58
+ ### AI & 工具
59
+ ![LangChain](https://img.shields.io/badge/-LangChain-1C3C3C?style=flat-square&logo=chainlink&logoColor=white)
60
+ ![Docker](https://img.shields.io/badge/-Docker-2496ED?style=flat-square&logo=docker&logoColor=white)
61
+ ![GitHub Actions](https://img.shields.io/badge/-GitHub_Actions-2088FF?style=flat-square&logo=github-actions&logoColor=white)
62
+
63
+ style:
64
+ theme: auto
65
+ icons: true
66
+ badges: true
package/package.json CHANGED
@@ -1,6 +1,68 @@
1
1
  {
2
2
  "name": "@webywz/breathing-github-profile",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "可复用的活跃 GitHub profile README 生成器,支持每日自动刷新",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "breathing-profile": "dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"",
12
+ "dev": "tsc --watch",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "refresh": "tsx src/cli.ts refresh",
16
+ "generate": "tsx src/cli.ts generate",
17
+ "validate": "tsx src/cli.ts validate",
18
+ "lint": "eslint src --ext .ts",
19
+ "prepare": "npm run build",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "github",
24
+ "profile",
25
+ "readme",
26
+ "generator",
27
+ "automation"
28
+ ],
29
+ "author": "webywz",
30
+ "license": "MIT",
31
+ "files": [
32
+ "dist",
33
+ "examples",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "dependencies": {
38
+ "yaml": "^2.3.4",
39
+ "commander": "^11.1.0",
40
+ "chalk": "^4.1.2",
41
+ "ora": "^5.4.1"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.10.5",
45
+ "@types/jest": "^29.5.11",
46
+ "@typescript-eslint/eslint-plugin": "^6.15.0",
47
+ "@typescript-eslint/parser": "^6.15.0",
48
+ "eslint": "^8.56.0",
49
+ "jest": "^29.7.0",
50
+ "ts-jest": "^29.1.1",
51
+ "typescript": "^5.3.3",
52
+ "tsx": "^4.7.0"
53
+ },
54
+ "engines": {
55
+ "node": ">=18.18.0"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "repository": {
61
+ "type": "git",
62
+ "url": "git+https://github.com/webywz/breathing-github-profile.git"
63
+ },
64
+ "homepage": "https://github.com/webywz/breathing-github-profile#readme",
65
+ "bugs": {
66
+ "url": "https://github.com/webywz/breathing-github-profile/issues"
67
+ }
68
+ }