growork 1.1.1 → 1.1.2

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.
@@ -12,7 +12,8 @@
12
12
  "WebSearch",
13
13
  "NotebookEdit",
14
14
  "mcp__*",
15
- "mcp__dart__*"
15
+ "mcp__dart__*",
16
+ "mcp__figma__*"
16
17
  ]
17
18
  },
18
19
  "enableAllProjectMcpServers": true,
package/README.md CHANGED
@@ -4,58 +4,40 @@
4
4
 
5
5
  这样 AI 工具(如 Claude、Cursor、Windsurf)就能直接读取你的产品文档,帮你写代码。
6
6
 
7
- ---
7
+ [![npm](https://img.shields.io/npm/v/growork)](https://www.npmjs.com/package/growork)
8
8
 
9
- ## 这个工具能做什么?
9
+ ## 功能
10
10
 
11
- 自动下载飞书文档,转成 Markdown 格式
12
- 自动下载 Notion 页面,转成 Markdown 格式
13
- 按**版本 功能 → 文档类型**组织文档,AI 更容易理解
14
- 一条命令同步所有文档
15
- 文档更新后,重新运行命令即可获取最新内容
11
+ - 自动下载飞书文档,转成 Markdown 格式
12
+ - 自动下载 Notion 页面,转成 Markdown 格式
13
+ - 支持 Figma 设计稿链接,自动生成链接文档
14
+ - 按**版本 → 功能 → 文档类型**组织文档,AI 更容易理解
15
+ - 一条命令同步所有文档
16
16
 
17
- ---
17
+ > ⚠️ **每个版本都是破坏性更新**,不保证向后兼容。升级前请查看更新日志,必要时重新初始化配置文件。
18
18
 
19
- ## 版本说明
19
+ ## 安装
20
20
 
21
- ⚠️ **每个版本都是破坏性更新**,不保证向后兼容。升级前请查看更新日志,必要时重新初始化配置文件。
21
+ ### 1. 安装 Node.js
22
22
 
23
- ---
24
-
25
- ## 安装步骤
26
-
27
- ### 第一步:安装 Node.js
28
-
29
- GroWork 需要 Node.js 才能运行。如果你还没安装:
23
+ GroWork 需要 Node.js >= 18。如果你还没安装:
30
24
 
31
25
  1. 打开 https://nodejs.org/
32
26
  2. 点击绿色的 **"LTS"** 按钮下载
33
27
  3. 双击下载的文件,按提示完成安装
34
28
 
35
- **检查是否安装成功:**
36
-
37
- 打开「终端」(Mac)或「命令提示符」(Windows),输入:
29
+ 检查是否安装成功:
38
30
 
39
31
  ```bash
40
32
  node -v
41
33
  ```
42
34
 
43
- 如果显示类似 `v18.17.0` 的版本号,说明安装成功。
44
-
45
- ---
46
-
47
- ### 第二步:安装 GroWork
48
-
49
- 在终端中输入:
35
+ ### 2. 安装 GroWork
50
36
 
51
37
  ```bash
52
38
  npm install -g growork
53
39
  ```
54
40
 
55
- 等待安装完成即可。
56
-
57
- ---
58
-
59
41
  ## 使用方法
60
42
 
61
43
  ### 1. 初始化项目
@@ -68,14 +50,10 @@ growork init
68
50
 
69
51
  这会在当前目录创建一个 `growork.config.yaml` 配置文件。
70
52
 
71
- ---
72
-
73
53
  ### 2. 获取飞书凭证(如果你要同步飞书文档)
74
54
 
75
55
  你需要在飞书开放平台创建一个应用,获取「App ID」和「App Secret」。
76
56
 
77
- **详细步骤:**
78
-
79
57
  1. **打开飞书开放平台**
80
58
  - 国际版 Lark:https://open.larksuite.com/
81
59
  - 国内版飞书:https://open.feishu.cn/
@@ -83,41 +61,26 @@ growork init
83
61
  2. **创建应用**
84
62
  - 点击「创建企业自建应用」
85
63
  - 填写应用名称(随便起,比如「文档同步」)
86
- - 点击「创建」
87
64
 
88
65
  3. **获取凭证**
89
66
  - 在应用详情页,找到「凭证与基础信息」
90
- - 复制 **App ID**(以 `cli_` 开头)
91
- - 复制 **App Secret**
67
+ - 复制 **App ID**(以 `cli_` 开头)和 **App Secret**
92
68
 
93
69
  4. **开通权限**
94
70
  - 点击左侧「权限管理」
95
- - 搜索并开通以下权限:
96
- - `docx:document:readonly`(读取文档)
97
- - `wiki:wiki:readonly`(读取知识库)
98
- - 点击「批量开通」
71
+ - 搜索并开通:`docx:document:readonly`、`wiki:wiki:readonly`
99
72
 
100
73
  5. **发布应用**
101
- - 点击「版本管理与发布」
102
- - 点击「创建版本」,填写版本号(如 1.0.0)
103
- - 提交审核(企业内部应用一般自动通过)
104
-
105
- ---
74
+ - 点击「版本管理与发布」→「创建版本」→ 提交审核
106
75
 
107
76
  ### 3. 获取 Notion 凭证(如果你要同步 Notion 页面)
108
77
 
109
- **详细步骤:**
110
-
111
78
  1. **创建 Integration**
112
79
  - 打开 https://www.notion.so/my-integrations
113
- - 点击「New integration
114
- - 填写名称(随便起,比如「文档同步」)
115
- - 选择你的 Workspace
116
- - 点击「Submit」
80
+ - 点击「New integration」,填写名称,选择 Workspace
117
81
 
118
82
  2. **复制 Token**
119
- - 在 Integration 详情页,点击「Show」查看 Token
120
- - 复制这个 Token(以 `ntn_` 或 `secret_` 开头)
83
+ - 在 Integration 详情页,复制 Token(以 `ntn_` 或 `secret_` 开头)
121
84
 
122
85
  3. **给页面授权**(重要!)
123
86
  - 打开你要同步的 Notion 页面
@@ -125,8 +88,6 @@ growork init
125
88
  - 选择你刚创建的 Integration
126
89
  - **每个要同步的页面都需要做这一步!**
127
90
 
128
- ---
129
-
130
91
  ### 4. 编辑配置文件
131
92
 
132
93
  用任意文本编辑器打开 `growork.config.yaml`,填入你的凭证和文档链接:
@@ -157,8 +118,9 @@ versions:
157
118
  用户登录: # 功能名称
158
119
  prd: # PRD 文档
159
120
  - "https://xxx.feishu.cn/docx/xxxxx"
160
- design: # 设计文档
161
- - "https://xxx.feishu.cn/docx/yyyyy"
121
+ design: # 设计文档(支持 Figma 链接)
122
+ - "https://www.figma.com/design/xxx?node-id=1-20"
123
+ - "https://www.figma.com/design/xxx?node-id=1-30"
162
124
  api: # 接口文档
163
125
  - "https://xxx.feishu.cn/docx/zzzzz"
164
126
  test: # 测试用例
@@ -171,26 +133,23 @@ versions:
171
133
 
172
134
  **配置说明:**
173
135
 
174
- | 区域 | 说明 |
175
- | --- | --- |
176
- | `custom` | 全局文档,不属于任何版本(如技术架构、编码规范) |
177
- | `versions` | 按版本和功能组织的文档 |
178
- | `prd` | 产品需求文档 |
179
- | `design` | 设计稿/交互文档 |
180
- | `api` | 接口/技术方案 |
181
- | `test` | 测试用例 |
136
+ | 区域 | 说明 |
137
+ | ---------- | ------------------------------------------------ |
138
+ | `custom` | 全局文档,不属于任何版本(如技术架构、编码规范) |
139
+ | `versions` | 按版本和功能组织的文档 |
140
+ | `prd` | 产品需求文档 |
141
+ | `design` | 设计稿(支持 Figma 链接,自动生成链接文档) |
142
+ | `api` | 接口/技术方案 |
143
+ | `test` | 测试用例 |
182
144
 
183
145
  **如何获取文档链接?**
184
146
 
185
147
  - **飞书**:打开文档,直接复制浏览器地址栏的链接
186
148
  - **Notion**:打开页面,点击右上角「Share」→「Copy link」
187
-
188
- ---
149
+ - **Figma**:打开设计稿,复制浏览器地址栏的链接(可带 `node-id` 参数定位到具体页面)
189
150
 
190
151
  ### 5. 同步文档
191
152
 
192
- 运行以下命令,自动下载所有文档:
193
-
194
153
  ```bash
195
154
  growork sync
196
155
  ```
@@ -207,7 +166,7 @@ docs/
207
166
  │ ├── prd/
208
167
  │ │ └── 用户登录PRD.md
209
168
  │ ├── design/
210
- │ │ └── 登录页设计.md
169
+ │ │ └── design.md # Figma 链接自动合并到此文件
211
170
  │ └── api/
212
171
  │ └── 登录接口文档.md
213
172
  └── 小优化/
@@ -222,17 +181,28 @@ growork sync --ver v1.0 # 只同步 v1.0 版本
222
181
  growork sync -f 用户登录 # 只同步「用户登录」功能
223
182
  ```
224
183
 
225
- **查看所有已配置的文档:**
184
+ ## 命令速查
226
185
 
227
- ```bash
228
- growork list
229
- ```
186
+ | 命令 | 说明 |
187
+ | --------------------------- | -------------- |
188
+ | `growork init` | 创建配置文件 |
189
+ | `growork sync` | 同步所有文档 |
190
+ | `growork sync -c` | 只同步全局文档 |
191
+ | `growork sync --ver v1.0` | 只同步指定版本 |
192
+ | `growork sync -f 功能名` | 只同步指定功能 |
193
+ | `growork list` | 查看文档列表 |
230
194
 
231
- ---
195
+ ## 支持的文档类型
196
+
197
+ | 平台 | 支持的内容 | 处理方式 |
198
+ | ------ | ---------------------------------------------- | -------------------- |
199
+ | 飞书 | 普通文档 (`/docx/`)、知识库 (`/wiki/`) | 调用 API 下载内容 |
200
+ | Notion | 任意页面(包含数据库的页面会自动转为表格) | 调用 API 下载内容 |
201
+ | Figma | 设计稿链接 (`figma.com/design/...`) | 直接生成链接文档 |
232
202
 
233
203
  ## 常见问题
234
204
 
235
- ### Q: 提示「权限不足」或「401」错误
205
+ ### 提示「权限不足」或「401」错误
236
206
 
237
207
  **飞书文档:**
238
208
  - 确认应用已开通 `docx:document:readonly` 和 `wiki:wiki:readonly` 权限
@@ -240,13 +210,9 @@ growork list
240
210
  - 确认你有权限访问该文档
241
211
 
242
212
  **Notion 页面:**
243
- - 确认你已经把 Integration 连接到了该页面(见上面第 3 步的「给页面授权」)
244
-
245
- ---
213
+ - 确认你已经把 Integration 连接到了该页面
246
214
 
247
- ### Q: 提示「找不到命令」或「command not found」
248
-
249
- Node.js 可能没有正确安装,或者 npm 全局路径没有加入系统 PATH。
215
+ ### 提示「找不到命令」或「command not found」
250
216
 
251
217
  尝试重新安装 Node.js,或者使用 `npx` 运行:
252
218
 
@@ -254,61 +220,27 @@ Node.js 可能没有正确安装,或者 npm 全局路径没有加入系统 PAT
254
220
  npx growork sync
255
221
  ```
256
222
 
257
- ---
258
-
259
- ### Q: 同步后文档是空的
223
+ ### 同步后文档是空的
260
224
 
261
225
  - 检查文档链接是否正确
262
226
  - 检查你是否有权限访问该文档
263
227
  - 飞书知识库文档需要使用 `/wiki/` 格式的链接
264
228
 
265
- ---
266
-
267
- ### Q: 如何更新已同步的文档?
229
+ ### 如何更新已同步的文档?
268
230
 
269
231
  直接再次运行 `growork sync`,会自动覆盖本地文件。
270
232
 
271
- ---
272
-
273
- ### Q: 文件名是怎么生成的?
233
+ ### 文件名是怎么生成的?
274
234
 
275
235
  文件名会自动从文档标题获取,并处理特殊字符:
236
+
276
237
  - `用户登录 / 注册流程 (v1.0)` → `用户登录-注册流程-v1.0.md`
277
238
 
278
239
  你也可以在配置中用 `name` 字段自定义名称。
279
240
 
280
- ---
281
-
282
- ## 命令速查
283
-
284
- | 命令 | 说明 |
285
- | --- | --- |
286
- | `growork init` | 创建配置文件 |
287
- | `growork sync` | 同步所有文档 |
288
- | `growork sync -c` | 只同步全局文档 |
289
- | `growork sync --ver v1.0` | 只同步指定版本 |
290
- | `growork sync -f 功能名` | 只同步指定功能 |
291
- | `growork list` | 查看文档列表 |
292
-
293
- ---
294
-
295
- ## 支持的文档类型
296
-
297
- | 平台 | 支持的内容 |
298
- | --- | --- |
299
- | 飞书 | 普通文档 (`/docx/`)、知识库 (`/wiki/`) |
300
- | Notion | 任意页面(包含数据库的页面会自动转为表格) |
301
-
302
- ---
303
-
304
241
  ## 需要帮助?
305
242
 
306
- 如果遇到问题,可以:
307
-
308
- 1. 在 GitHub 上提 Issue:https://github.com/anthropics/growork/issues
309
- 2. 检查上面的「常见问题」部分
310
-
311
- ---
243
+ - [GitHub Issues](https://github.com/ThinkerJack/GroWork/issues)
312
244
 
313
245
  ## License
314
246
 
package/claude.md CHANGED
@@ -41,11 +41,16 @@ feishu:
41
41
  notion:
42
42
  token: "ntn_xxx"
43
43
 
44
- docs:
45
- - name: prd # 唯一标识
46
- type: feishu # feishu | notion | notion-database
47
- url: "https://..."
48
- output: "docs/prd.md"
44
+ outputDir: "docs" # 输出根目录
45
+
46
+ custom: # 全局文档
47
+ - "https://xxx.feishu.cn/docx/xxxxx"
48
+
49
+ versions: # 版本化文档
50
+ v1.0:
51
+ 用户登录:
52
+ prd:
53
+ - "https://xxx.feishu.cn/docx/xxxxx"
49
54
  ```
50
55
 
51
56
  ## 常用命令
@@ -59,11 +64,36 @@ node dist/index.js sync [name] # 同步文档
59
64
  ## 关键类型
60
65
 
61
66
  ```typescript
62
- interface DocConfig {
63
- name: string;
64
- type: 'feishu' | 'notion' | 'notion-database';
67
+ // 文档输入格式
68
+ type DocInput = string | { url: string; name?: string };
69
+
70
+ // 功能下的文档分类
71
+ type FeatureValue = DocInput[] | {
72
+ prd?: DocInput[];
73
+ design?: DocInput[];
74
+ api?: DocInput[];
75
+ test?: DocInput[];
76
+ };
77
+
78
+ // 配置文件结构
79
+ interface GroworkConfigV2 {
80
+ feishu?: FeishuConfig;
81
+ notion?: NotionConfig;
82
+ outputDir?: string;
83
+ custom?: DocInput[];
84
+ versions?: {
85
+ [version: string]: {
86
+ [feature: string]: FeatureValue;
87
+ };
88
+ };
89
+ }
90
+
91
+ // 标准化后的文档
92
+ interface NormalizedDoc {
65
93
  url: string;
66
- output: string;
94
+ name?: string;
95
+ type: 'feishu' | 'notion';
96
+ outputPath: string; // 含 {title} 占位符
67
97
  }
68
98
  ```
69
99
 
@@ -194,7 +224,7 @@ src/
194
224
 
195
225
  ### 命名
196
226
 
197
- - 文件名:小写 + 连字符,如 `feishu-sync.ts`
227
+ - 文件名:小写,如 `feishu.ts`, `config.ts`
198
228
  - 函数名:动词开头,如 `syncDoc`, `parseConfig`
199
229
  - 布尔变量:is/has/should 开头,如 `isValid`
200
230
 
package/dist/index.js CHANGED
@@ -26,6 +26,9 @@ function inferDocType(url) {
26
26
  if (url.includes("notion.so") || url.includes("notion.site")) {
27
27
  return "notion";
28
28
  }
29
+ if (url.includes("figma.com")) {
30
+ return "figma";
31
+ }
29
32
  throw new Error(`\u65E0\u6CD5\u4ECE URL \u63A8\u65AD\u6587\u6863\u7C7B\u578B: ${url}`);
30
33
  }
31
34
  function sanitizeFileName(title) {
@@ -677,10 +680,12 @@ async function syncCommand(options = {}) {
677
680
  console.log(chalk2.yellow("\u26A0\uFE0F \u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u6863"));
678
681
  return;
679
682
  }
683
+ const normalDocs = docs.filter((d) => d.type !== "figma");
684
+ const figmaDocs = docs.filter((d) => d.type === "figma");
680
685
  const feishuService = config.feishu ? new FeishuService(config.feishu) : null;
681
686
  const notionService = config.notion ? new NotionService(config.notion) : null;
682
- const hasFeishuDocs = docs.some((d) => d.type === "feishu");
683
- const hasNotionDocs = docs.some((d) => d.type === "notion");
687
+ const hasFeishuDocs = normalDocs.some((d) => d.type === "feishu");
688
+ const hasNotionDocs = normalDocs.some((d) => d.type === "notion");
684
689
  if (hasFeishuDocs && !feishuService) {
685
690
  throw new Error("\u914D\u7F6E\u6587\u4EF6\u7F3A\u5C11\u98DE\u4E66\u51ED\u8BC1 (feishu.appId, feishu.appSecret)");
686
691
  }
@@ -689,7 +694,16 @@ async function syncCommand(options = {}) {
689
694
  }
690
695
  console.log(chalk2.blue("\u{1F4C4} \u5F00\u59CB\u540C\u6B65\u6587\u6863...\n"));
691
696
  let successCount = 0;
692
- for (const doc of docs) {
697
+ const figmaGroups = /* @__PURE__ */ new Map();
698
+ for (const doc of figmaDocs) {
699
+ const dir = path3.dirname(doc.outputPath);
700
+ const feature = path3.basename(path3.dirname(dir));
701
+ if (!figmaGroups.has(dir)) {
702
+ figmaGroups.set(dir, { feature, urls: [] });
703
+ }
704
+ figmaGroups.get(dir).urls.push(doc.url);
705
+ }
706
+ for (const doc of normalDocs) {
693
707
  const displayName = doc.name || doc.url.slice(-20);
694
708
  process.stdout.write(chalk2.gray(` \u23F3 ${displayName}`));
695
709
  try {
@@ -716,11 +730,23 @@ async function syncCommand(options = {}) {
716
730
  console.log(chalk2.red(` \u2717 ${displayName.padEnd(25)} \u2192 ${errorMessage}`));
717
731
  }
718
732
  }
733
+ for (const [dir, { feature, urls }] of figmaGroups) {
734
+ const content = `# ${feature} \u8BBE\u8BA1\u7A3F
735
+
736
+ ${urls.map((u) => `- ${u}`).join("\n")}
737
+ `;
738
+ const outputPath = path3.join(process.cwd(), dir, "design.md");
739
+ fs3.mkdirSync(path3.dirname(outputPath), { recursive: true });
740
+ fs3.writeFileSync(outputPath, content, "utf-8");
741
+ console.log(chalk2.green(` \u2713 design.md \u2192 ${path3.relative(process.cwd(), outputPath)}`));
742
+ successCount++;
743
+ }
719
744
  console.log("");
720
- if (successCount === docs.length) {
721
- console.log(chalk2.green(`\u2705 \u540C\u6B65\u5B8C\u6210\uFF0C\u5171 ${docs.length} \u4E2A\u6587\u6863`));
745
+ const totalCount = normalDocs.length + figmaGroups.size;
746
+ if (successCount === totalCount) {
747
+ console.log(chalk2.green(`\u2705 \u540C\u6B65\u5B8C\u6210\uFF0C\u5171 ${totalCount} \u4E2A\u6587\u6863`));
722
748
  } else {
723
- console.log(chalk2.yellow(`\u26A0\uFE0F \u540C\u6B65\u5B8C\u6210: ${successCount}/${docs.length} \u4E2A\u6587\u6863\u6210\u529F`));
749
+ console.log(chalk2.yellow(`\u26A0\uFE0F \u540C\u6B65\u5B8C\u6210: ${successCount}/${totalCount} \u4E2A\u6587\u6863\u6210\u529F`));
724
750
  }
725
751
  }
726
752
 
@@ -0,0 +1,175 @@
1
+ # GroWork 技术架构
2
+
3
+ ## 概述
4
+
5
+ GroWork 是一个文档同步 CLI 工具,将飞书文档、Notion 页面和 Figma 设计稿同步到本地 Markdown 文件。
6
+
7
+ ## 项目结构
8
+
9
+ ```
10
+ src/
11
+ ├── index.ts # CLI 入口
12
+ ├── commands/
13
+ │ ├── init.ts # growork init
14
+ │ ├── sync.ts # growork sync
15
+ │ └── list.ts # growork list
16
+ ├── services/
17
+ │ ├── feishu.ts # 飞书 API 服务
18
+ │ └── notion.ts # Notion API 服务
19
+ └── utils/
20
+ └── config.ts # 配置解析
21
+ ```
22
+
23
+ ## 核心模块
24
+
25
+ ### 1. CLI 入口 (`src/index.ts`)
26
+
27
+ 使用 `commander` 定义命令:
28
+
29
+ - `growork init` - 初始化配置文件
30
+ - `growork sync` - 同步文档,支持 `--ver`、`-f`、`-c` 过滤
31
+ - `growork list` - 列出所有文档
32
+
33
+ ### 2. 配置模块 (`src/utils/config.ts`)
34
+
35
+ **核心类型:**
36
+
37
+ ```typescript
38
+ // 文档输入格式
39
+ type DocInput = string | { url: string; name?: string };
40
+
41
+ // 标准化后的文档
42
+ interface NormalizedDoc {
43
+ url: string;
44
+ name?: string;
45
+ type: 'feishu' | 'notion' | 'figma';
46
+ outputPath: string; // 含 {title} 占位符
47
+ }
48
+ ```
49
+
50
+ **核心函数:**
51
+
52
+ | 函数 | 作用 |
53
+ |-----|------|
54
+ | `loadConfigV2()` | 加载并解析 YAML 配置文件 |
55
+ | `normalizeConfig()` | 将配置转为标准化的 `NormalizedDoc[]` |
56
+ | `inferDocType()` | 根据 URL 推断文档类型 |
57
+ | `sanitizeFileName()` | 处理文件名特殊字符 |
58
+
59
+ ### 3. 同步命令 (`src/commands/sync.ts`)
60
+
61
+ **处理流程:**
62
+
63
+ ```
64
+ loadConfigV2() → normalizeConfig() → 分组处理
65
+ ├── 飞书文档 → FeishuService.getDocumentAsMarkdown()
66
+ ├── Notion 页面 → NotionService.getPageAsMarkdown()
67
+ └── Figma 链接 → 直接生成 design.md
68
+ ```
69
+
70
+ **Figma 处理逻辑:**
71
+
72
+ 同一 feature 下的多个 Figma URL 会合并到一个 `design.md` 文件:
73
+
74
+ ```typescript
75
+ // 按目录分组
76
+ const figmaGroups = new Map<string, { feature: string; urls: string[] }>();
77
+
78
+ // 生成内容
79
+ const content = `# ${feature} 设计稿\n\n${urls.map(u => `- ${u}`).join('\n')}\n`;
80
+ ```
81
+
82
+ ### 4. 飞书服务 (`src/services/feishu.ts`)
83
+
84
+ **职责:** 调用飞书 API 获取文档内容,转换为 Markdown。
85
+
86
+ **核心方法:**
87
+
88
+ | 方法 | 作用 |
89
+ |-----|------|
90
+ | `getDocumentAsMarkdown(url)` | 主入口,返回完整 Markdown |
91
+ | `parseDocumentId(url)` | 解析 URL 中的文档 ID |
92
+ | `resolveDocumentId(url)` | Wiki 链接需要额外解析真实 ID |
93
+ | `getAllBlocks(docId)` | 分页获取所有 block |
94
+ | `blockToMarkdown(block)` | 将单个 block 转为 Markdown |
95
+
96
+ **支持的 block 类型:**
97
+
98
+ | blockType | 类型 | 输出 |
99
+ |-----------|------|------|
100
+ | 2 | Text | 普通文本 |
101
+ | 3-11 | Heading1-9 | `#` ~ `######` |
102
+ | 12 | Bullet | `- item` |
103
+ | 13 | Ordered | `1. item` |
104
+ | 14 | Code | ` ```lang ``` ` |
105
+ | 15 | Quote | `> text` |
106
+ | 17 | TodoList | `- [ ] task` |
107
+ | 18 | Divider | `---` |
108
+ | 19 | Image | `![image](token)` |
109
+ | 31 | Table | Markdown 表格 |
110
+
111
+ ### 5. Notion 服务 (`src/services/notion.ts`)
112
+
113
+ **职责:** 调用 Notion API 获取页面内容,使用 `notion-to-md` 库转换。
114
+
115
+ **核心方法:**
116
+
117
+ | 方法 | 作用 |
118
+ |-----|------|
119
+ | `getPageAsMarkdown(url)` | 主入口,返回完整 Markdown |
120
+ | `parsePageId(url)` | 从 URL 提取 page ID |
121
+ | `setupCustomTransformers()` | 自定义 child_database 转换 |
122
+
123
+ **数据库处理:**
124
+
125
+ 内嵌数据库(child_database)会自动转为 Markdown 表格,支持的字段类型:
126
+
127
+ - title, rich_text, select, multi_select
128
+ - number, checkbox, date, url, files
129
+
130
+ ## 数据流
131
+
132
+ ```
133
+ ┌─────────────────┐
134
+ │ growork.config │ YAML 配置文件
135
+ │ .yaml │
136
+ └────────┬────────┘
137
+ │ loadConfigV2()
138
+
139
+ ┌─────────────────┐
140
+ │ GroworkConfigV2 │ 原始配置对象
141
+ └────────┬────────┘
142
+ │ normalizeConfig(options)
143
+
144
+ ┌─────────────────┐
145
+ │ NormalizedDoc[] │ 标准化文档列表
146
+ └────────┬────────┘
147
+
148
+ ┌────┴────┬──────────┐
149
+ ▼ ▼ ▼
150
+ Feishu Notion Figma
151
+ │ │ │
152
+ ▼ ▼ ▼
153
+ ┌─────────────────────────────┐
154
+ │ Markdown 文件写入本地 │
155
+ └─────────────────────────────┘
156
+ ```
157
+
158
+ ## 技术栈
159
+
160
+ | 用途 | 依赖 |
161
+ |-----|------|
162
+ | CLI 框架 | commander |
163
+ | 飞书 API | @larksuiteoapi/node-sdk |
164
+ | Notion API | @notionhq/client |
165
+ | Notion 转 MD | notion-to-md |
166
+ | YAML 解析 | yaml |
167
+ | 终端颜色 | chalk |
168
+ | 构建工具 | tsup |
169
+
170
+ ## 设计原则
171
+
172
+ 1. **最少代码** - 不提前抽象,只实现当前需求
173
+ 2. **扁平结构** - 目录层级不超过 2 层
174
+ 3. **错误自然抛出** - 在顶层统一处理,不过度防御
175
+ 4. **依赖成熟库** - 不重复造轮子