figma-cache-toolchain 1.4.3 → 1.4.5

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.
@@ -0,0 +1,233 @@
1
+ /* eslint-disable no-console */
2
+
3
+ function isTodoLike(value) {
4
+ return /TODO/i.test(String(value || ""));
5
+ }
6
+
7
+ function getManifestFilesMap(cacheKey, item, errors, deps) {
8
+ const { resolveMaybeAbsolutePath, safeReadJson, normalizeSlash, path, fs } = deps;
9
+ if (!item || !item.paths || !item.paths.meta) {
10
+ errors.push(`${cacheKey}: source=figma-mcp 但缺少 paths.meta,无法定位 mcp-raw`);
11
+ return null;
12
+ }
13
+ const metaAbs = resolveMaybeAbsolutePath(item.paths.meta);
14
+ const nodeDir = path.dirname(metaAbs);
15
+ const mcpRawDir = path.join(nodeDir, "mcp-raw");
16
+ const manifestAbs = path.join(mcpRawDir, "mcp-raw-manifest.json");
17
+ const manifest = safeReadJson(manifestAbs);
18
+ if (!manifest || typeof manifest !== "object") {
19
+ errors.push(`${cacheKey}: source=figma-mcp 但缺少 mcp-raw/mcp-raw-manifest.json`);
20
+ return null;
21
+ }
22
+ if (!manifest.files || typeof manifest.files !== "object") {
23
+ errors.push(`${cacheKey}: mcp-raw-manifest.json 缺少 files 映射`);
24
+ return null;
25
+ }
26
+ Object.entries(manifest.files).forEach(([toolName, fileName]) => {
27
+ if (!fileName) {
28
+ errors.push(`${cacheKey}: mcp-raw-manifest.json 中 ${toolName} 未关联文件`);
29
+ return;
30
+ }
31
+ const fileAbs = path.join(mcpRawDir, String(fileName));
32
+ if (!fs.existsSync(fileAbs)) {
33
+ errors.push(`${cacheKey}: 缺少 MCP 原始文件 ${normalizeSlash(fileAbs)}`);
34
+ }
35
+ });
36
+ return manifest.files;
37
+ }
38
+
39
+ function collectMissingToolEvidence(completeness, filesMap, normalizeCompletenessList, toolRequirements) {
40
+ const missing = [];
41
+ normalizeCompletenessList(completeness).forEach((dimension) => {
42
+ const groups = toolRequirements[dimension];
43
+ if (!Array.isArray(groups) || !groups.length) {
44
+ return;
45
+ }
46
+ groups.forEach((alternatives) => {
47
+ const hit = alternatives.some((toolName) =>
48
+ Object.prototype.hasOwnProperty.call(filesMap, toolName)
49
+ );
50
+ if (!hit) {
51
+ missing.push({
52
+ dimension,
53
+ alternatives,
54
+ });
55
+ }
56
+ });
57
+ });
58
+ return missing;
59
+ }
60
+
61
+ function validateMcpRawEvidence(cacheKey, item, completeness, options, deps) {
62
+ const errors = [];
63
+ if (options && options.allowSkeletonWithFigmaMcp) {
64
+ return errors;
65
+ }
66
+
67
+ const filesMap = getManifestFilesMap(cacheKey, item, errors, deps);
68
+ if (!filesMap) {
69
+ return errors;
70
+ }
71
+
72
+ const missing = collectMissingToolEvidence(
73
+ completeness,
74
+ filesMap,
75
+ deps.normalizeCompletenessList,
76
+ deps.completenessToolRequirements
77
+ );
78
+ missing.forEach(({ dimension, alternatives }) => {
79
+ errors.push(
80
+ `${cacheKey}: completeness=${dimension} 缺少 MCP 原始证据(需包含 ${alternatives.join(" 或 ")})`
81
+ );
82
+ });
83
+ return errors;
84
+ }
85
+
86
+ function validateCompletenessEvidence(cacheKey, item, deps) {
87
+ const errors = [];
88
+ const covered = deps.normalizeCompletenessList(item.completeness);
89
+ if (!covered.length) {
90
+ return errors;
91
+ }
92
+ if (!item.paths || !item.paths.raw) {
93
+ errors.push(`${cacheKey}: completeness 非空但缺少 paths.raw`);
94
+ return errors;
95
+ }
96
+
97
+ const rawAbs = deps.resolveMaybeAbsolutePath(item.paths.raw);
98
+ const raw = deps.safeReadJson(rawAbs);
99
+ if (!raw || typeof raw !== "object") {
100
+ errors.push(`${cacheKey}: raw.json 不可读,无法校验 completeness 证据`);
101
+ return errors;
102
+ }
103
+
104
+ const coverageSummary =
105
+ raw.coverageSummary && typeof raw.coverageSummary === "object"
106
+ ? raw.coverageSummary
107
+ : null;
108
+ const evidence =
109
+ coverageSummary && coverageSummary.evidence && typeof coverageSummary.evidence === "object"
110
+ ? coverageSummary.evidence
111
+ : null;
112
+ if (!evidence) {
113
+ errors.push(`${cacheKey}: raw.json 缺少 coverageSummary.evidence`);
114
+ return errors;
115
+ }
116
+
117
+ covered.forEach((dimension) => {
118
+ const list = Array.isArray(evidence[dimension])
119
+ ? evidence[dimension].filter((x) => typeof x === "string" && String(x).trim())
120
+ : [];
121
+ if (!list.length) {
122
+ errors.push(`${cacheKey}: completeness=${dimension} 但缺少 evidence`);
123
+ }
124
+ });
125
+
126
+ if (item.source === "figma-mcp") {
127
+ ["interactions", "states", "accessibility"].forEach((dimension) => {
128
+ if (!covered.includes(dimension)) {
129
+ return;
130
+ }
131
+ const section = raw[dimension] && typeof raw[dimension] === "object" ? raw[dimension] : null;
132
+ const notes = section ? String(section.notes || "") : "";
133
+ if (!notes || isTodoLike(notes)) {
134
+ errors.push(`${cacheKey}: ${dimension} 仍为占位内容(TODO),请补充可执行证据`);
135
+ }
136
+ });
137
+ }
138
+
139
+ return errors;
140
+ }
141
+
142
+ function validateIndex(index, deps) {
143
+ const errors = [];
144
+ const normalized = deps.normalizeIndexShape(index);
145
+ const keys = Object.keys(normalized.items || {});
146
+
147
+ keys.forEach((cacheKey) => {
148
+ const item = normalized.items[cacheKey];
149
+ const required = [
150
+ "fileKey",
151
+ "scope",
152
+ "url",
153
+ "originalUrls",
154
+ "normalizationVersion",
155
+ "paths",
156
+ "syncedAt",
157
+ "completeness",
158
+ ];
159
+
160
+ required.forEach((field) => {
161
+ if (item[field] === undefined || item[field] === null) {
162
+ errors.push(`${cacheKey}: 缺少字段 ${field}`);
163
+ }
164
+ });
165
+
166
+ if (item.scope === "node" && !item.nodeId) {
167
+ errors.push(`${cacheKey}: node 作用域必须包含 nodeId`);
168
+ }
169
+
170
+ errors.push(...validateCompletenessEvidence(cacheKey, item, deps));
171
+ if (item.source === "figma-mcp") {
172
+ errors.push(
173
+ ...validateMcpRawEvidence(cacheKey, item, item.completeness, {
174
+ allowSkeletonWithFigmaMcp: false,
175
+ }, deps)
176
+ );
177
+ }
178
+ });
179
+
180
+ const flowKeys = Object.keys(normalized.flows || {});
181
+ flowKeys.forEach((flowId) => {
182
+ const flow = normalized.flows[flowId];
183
+ if (!flow || typeof flow !== "object") {
184
+ errors.push(`flow ${flowId}: 非法结构`);
185
+ return;
186
+ }
187
+ if (!flow.id || flow.id !== flowId) {
188
+ errors.push(`flow ${flowId}: id 字段缺失或不一致`);
189
+ }
190
+ if (!Array.isArray(flow.nodes)) {
191
+ errors.push(`flow ${flowId}: nodes 必须是数组`);
192
+ }
193
+ if (!Array.isArray(flow.edges)) {
194
+ errors.push(`flow ${flowId}: edges 必须是数组`);
195
+ }
196
+
197
+ if (Array.isArray(flow.edges)) {
198
+ flow.edges.forEach((edge, idx) => {
199
+ if (!edge || typeof edge !== "object") {
200
+ errors.push(`flow ${flowId}: edge[${idx}] 非法`);
201
+ return;
202
+ }
203
+ if (!edge.from || !edge.to) {
204
+ errors.push(`flow ${flowId}: edge[${idx}] 缺少 from/to`);
205
+ }
206
+ if (!edge.type) {
207
+ errors.push(`flow ${flowId}: edge[${idx}] 缺少 type`);
208
+ }
209
+ if (edge.from && !normalized.items[edge.from]) {
210
+ errors.push(`flow ${flowId}: edge[${idx}] from 不存在于 items: ${edge.from}`);
211
+ }
212
+ if (edge.to && !normalized.items[edge.to]) {
213
+ errors.push(`flow ${flowId}: edge[${idx}] to 不存在于 items: ${edge.to}`);
214
+ }
215
+ });
216
+ }
217
+
218
+ if (Array.isArray(flow.nodes)) {
219
+ flow.nodes.forEach((nodeCacheKey) => {
220
+ if (!normalized.items[nodeCacheKey]) {
221
+ errors.push(`flow ${flowId}: nodes 引用不存在于 items: ${nodeCacheKey}`);
222
+ }
223
+ });
224
+ }
225
+ });
226
+
227
+ return errors;
228
+ }
229
+
230
+ module.exports = {
231
+ validateMcpRawEvidence,
232
+ validateIndex,
233
+ };
package/package.json CHANGED
@@ -1,55 +1,72 @@
1
- {
2
- "name": "figma-cache-toolchain",
3
- "version": "1.4.3",
4
- "description": "Figma link normalization, local cache index, validation, and Node CLI (framework-agnostic core).",
5
- "homepage": "https://www.npmjs.com/package/figma-cache-toolchain",
6
- "keywords": [
7
- "figma",
8
- "cache",
9
- "cli",
10
- "design-tokens",
11
- "mcp"
12
- ],
13
- "license": "MIT",
14
- "engines": {
15
- "node": ">=16.20.0"
16
- },
17
- "bin": {
18
- "figma-cache": "bin/figma-cache.js"
19
- },
20
- "files": [
21
- "LICENSE",
22
- "bin",
23
- "cursor-bootstrap",
24
- "figma-cache/figma-cache.js",
25
- "figma-cache/*.md"
26
- ],
27
- "publishConfig": {
28
- "registry": "https://registry.npmjs.org/"
29
- },
30
- "scripts": {
31
- "test": "npm run docs:encoding:check && node tests/smoke.js",
32
- "prepack": "npm run docs:encoding:check && node bin/figma-cache.js validate",
33
- "figma:cache:normalize": "node bin/figma-cache.js normalize",
34
- "figma:cache:get": "node bin/figma-cache.js get",
35
- "figma:cache:upsert": "node bin/figma-cache.js upsert",
36
- "figma:cache:ensure": "node bin/figma-cache.js ensure",
37
- "figma:cache:validate": "node bin/figma-cache.js validate",
38
- "figma:cache:stale": "node bin/figma-cache.js stale",
39
- "figma:cache:budget": "node bin/figma-cache.js budget --mcp-only",
40
- "figma:cache:backfill": "node bin/figma-cache.js backfill",
41
- "figma:cache:init": "node bin/figma-cache.js init",
42
- "figma:cache:config": "node bin/figma-cache.js config",
43
- "figma:cache:flow:init": "node bin/figma-cache.js flow init",
44
- "figma:cache:flow:add-node": "node bin/figma-cache.js flow add-node",
45
- "figma:cache:flow:link": "node bin/figma-cache.js flow link",
46
- "figma:cache:flow:chain": "node bin/figma-cache.js flow chain",
47
- "figma:cache:flow:show": "node bin/figma-cache.js flow show",
48
- "figma:cache:flow:mermaid": "node bin/figma-cache.js flow mermaid",
49
- "figma:cache:cursor:init": "node bin/figma-cache.js cursor init",
50
- "docs:encoding:check": "node scripts/check-doc-encoding.js"
51
- },
52
- "volta": {
53
- "node": "16.20.2"
54
- }
55
- }
1
+ {
2
+ "name": "figma-cache-toolchain",
3
+ "version": "1.4.5",
4
+ "description": "Figma link normalization, local cache index, validation, and Node CLI (framework-agnostic core).",
5
+ "homepage": "https://github.com/907086379/figma-cache-toolchain#readme",
6
+ "keywords": [
7
+ "figma",
8
+ "cache",
9
+ "cli",
10
+ "design-tokens",
11
+ "mcp"
12
+ ],
13
+ "license": "MIT",
14
+ "engines": {
15
+ "node": ">=16.20.0"
16
+ },
17
+ "bin": {
18
+ "figma-cache": "bin/figma-cache.js"
19
+ },
20
+ "files": [
21
+ "LICENSE",
22
+ "bin",
23
+ "cursor-bootstrap",
24
+ "figma-cache/figma-cache.js",
25
+ "figma-cache/js/flow-cli.js",
26
+ "figma-cache/js/validate-cli.js",
27
+ "figma-cache/js/budget-cli.js",
28
+ "figma-cache/js/index-store.js",
29
+ "figma-cache/js/cursor-bootstrap-cli.js",
30
+ "figma-cache/js/entry-files.js",
31
+ "figma-cache/js/backfill-cli.js",
32
+ "figma-cache/js/project-config.js",
33
+ "figma-cache/js/upsert-core.js",
34
+ "figma-cache/docs/*.md"
35
+ ],
36
+ "publishConfig": {
37
+ "registry": "https://registry.npmjs.org/"
38
+ },
39
+ "scripts": {
40
+ "test": "npm run docs:encoding:check && node tests/smoke.js",
41
+ "prepack": "npm run docs:encoding:check && node bin/figma-cache.js validate",
42
+ "figma:cache:normalize": "node bin/figma-cache.js normalize",
43
+ "figma:cache:get": "node bin/figma-cache.js get",
44
+ "figma:cache:upsert": "node bin/figma-cache.js upsert",
45
+ "figma:cache:ensure": "node bin/figma-cache.js ensure",
46
+ "figma:cache:validate": "node bin/figma-cache.js validate",
47
+ "figma:cache:stale": "node bin/figma-cache.js stale",
48
+ "figma:cache:budget": "node bin/figma-cache.js budget --mcp-only",
49
+ "figma:cache:backfill": "node bin/figma-cache.js backfill",
50
+ "figma:cache:init": "node bin/figma-cache.js init",
51
+ "figma:cache:config": "node bin/figma-cache.js config",
52
+ "figma:cache:flow:init": "node bin/figma-cache.js flow init",
53
+ "figma:cache:flow:add-node": "node bin/figma-cache.js flow add-node",
54
+ "figma:cache:flow:link": "node bin/figma-cache.js flow link",
55
+ "figma:cache:flow:chain": "node bin/figma-cache.js flow chain",
56
+ "figma:cache:flow:show": "node bin/figma-cache.js flow show",
57
+ "figma:cache:flow:mermaid": "node bin/figma-cache.js flow mermaid",
58
+ "figma:cache:cursor:init": "node bin/figma-cache.js cursor init",
59
+ "docs:encoding:check": "node scripts/check-doc-encoding.js",
60
+ "figma:cache:mobile:spec": "node scripts/mobile/generate-mobile-spec.js"
61
+ },
62
+ "volta": {
63
+ "node": "16.20.2"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/907086379/figma-cache-toolchain.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://github.com/907086379/figma-cache-toolchain/issues"
71
+ }
72
+ }
@@ -1,206 +0,0 @@
1
- # figma-cache-toolchain:团队使用说明(可转发)
2
-
3
- 本文面向**业务仓库**里要用 Figma 本地缓存的同事:说明**是什么、何时用、怎么接、日常怎么用、怎么升级**。技术细节仍以包内**根 `README.md`**、**`figma-cache/README.md`** 为准;链接与键、Flow 边类型分别见 **`link-normalization-spec.md`**、**`flow-edge-taxonomy.md`**。**专用名词**见**文首「术语与专用名词(速查)」**表格。
4
-
5
- ---
6
-
7
- ## 术语与专用名词(速查)
8
-
9
- 读后续提示词前,可先扫一眼本表。**路径**均相对业务项目根,默认缓存目录名为 **`figma-cache/`**(可用环境变量 **`FIGMA_CACHE_DIR`** 改掉)。
10
-
11
- | 名词 | 一句话解释 |
12
- |------|------------|
13
- | **`ensure`** | 针对一条 Figma 链接:在 **`figma-cache/index.json`** 里登记/更新一条 **`items`** 记录,并在磁盘上生成/补齐该节点的 **`meta.json` / `spec.md` / `raw.json` / `state-map.md`** 等骨架;常由 Agent 调用 CLI `figma-cache ensure` 完成。**它不是自动 MCP 拉取器**,MCP 原始回包应先由 Agent 调 figma-mcp 写入 `mcp-raw/`,再 `upsert/ensure`。 |
14
- | **`get`** | 按链接查看缓存是否命中、或拉取条目信息;不一定写盘。 |
15
- | **`validate`** | 检查 **`index.json`** 结构是否合法(必填字段、`flows` 里的边是否指向存在的 `items` 等)。合并前可跑。 |
16
- | **`completeness`** | `ensure` 时可传的**标签列表**(如 `layout`、`text`、`tokens`、`interactions`),表示「希望这次把哪些方面的设计信息写进缓存/交给 MCP」。**不是 Figma 官方枚举**,而是你们流程里约定「要记多细」;具体写入内容仍落在 **`spec.md` / `raw.json`** 等文件里。 |
17
- | **`spec.md`** | 某节点目录下的 **Markdown**,人类可读的设计摘要/规格说明,**对齐 UI、走查像素时最常对照**的文件之一。 |
18
- | **`meta.json`** | 同目录下的 **JSON**,记录该缓存项的元信息(如 `fileKey`、`nodeId`、`syncedAt`、`source` 等),偏「这条缓存是谁、何时写的」。 |
19
- | **`raw.json`** | 同目录下的 **JSON**,偏**结构化原始快照**(可能较大),适合机器读或 Agent 从中抽字段;**像素/间距等细节**以团队约定为准,常见与 **`spec.md`** 一起看。 |
20
- | **`state-map.md`** | 占位/扩展用 Markdown,用于**状态机、屏间跳转、交互说明**等;初始可能是 TODO,由你们或 Agent 补全。 |
21
- | **`index.json`** | 缓存库的**总索引**:里面有所有节点的 **`items`**,以及可选的多屏流程 **`flows`**。 |
22
- | **`items` / 一条 item** | `index.json` 里 **`items`** 对象:键为 **`cacheKey`**,值为该 Figma 节点(或整文件)的索引记录(含 `url`、`paths`、`completeness` 等)。**「进缓存」≈ 有一条 item。** |
23
- | **`cacheKey`** | 标准化后的**唯一键**,一般形如 **`fileKey#nodeId`**(无节点时可能是 **`fileKey#__FILE__`**)。用于去重、校验、`flow` 里引用节点。算法见 **`link-normalization-spec.md`**。 |
24
- | **`fileKey` / `nodeId`** | **fileKey**:Figma 文件 ID。**nodeId**:画布上某个节点的 ID(常为 `数字:数字`)。都来自链接解析。 |
25
- | **`flow`(流程)** | 在 **`index.json` 的 `flows`** 里的一条记录,表示**一条业务用户路径**(多屏/多节点),含 **`flowId`**、节点列表、**边(edges)** 等。与「单条 Figma 链接」不同:**flow 描述的是关系与顺序**。 |
26
- | **`flowId`** | 某条流程的 ID 字符串(如 `my-onboarding-flow`),`flow init` 时创建;后续 **`flow add-node` / `flow link`** 都要指明落在哪条 flow 上。可用 **`FIGMA_DEFAULT_FLOW`** 省略重复参数。 |
27
- | **`flow link` / `flow chain`** | CLI 子命令:在指定 **`flowId`** 下**加边**(从屏 A 到屏 B、类型 `next_step` 等),或**按顺序一串链式连接**多个 URL。 |
28
- | **`flow mermaid` / `flow show`** | 把当前 flow **导出成 Mermaid 图**或 **JSON 文本**,方便贴进文档或评审。 |
29
- | **边类型(`type`)** | `flow link` 时的 **`--type=`**,如 `next_step`、`related`、`child`、`branch_true` 等;**完整列表与语义**见 **`flow-edge-taxonomy.md`**。 |
30
- | **`note`** | 可选:附在边上的一句**人话说明**(如「点击设置进入」),方便以后读 `index.json` 或导出图时理解。 |
31
- | **MCP / `figma-mcp`** | **Figma MCP**:在 Cursor 里从 Figma **实时读**画布/节点信息的通道。缓存**优先**;本地不够或你要最新稿时,再由 Agent 调 MCP。 |
32
- | **Core 规则** | 通常指 **`.cursor/rules/01-figma-cache-core.mdc`**:只管**数据层**(标准化链接、读写 `figma-cache`、何时 MCP、校验),**不写** Vue/React 具体代码。 |
33
- | **Adapter** | 通常指 **`.cursor/rules/02-figma-<栈>-adapter.mdc`**:约定**在缓存可读之后**,如何在你司技术栈里写业务 UI。 |
34
- | **Skill** | **`.cursor/skills/figma-mcp-local-cache/SKILL.md`**:教 Agent **何时查缓存、何时调 MCP、如何跑 CLI** 的步骤化说明,与 Core / Adapter 配合使用。 |
35
- | **`AGENT-SETUP-PROMPT.md`** | 项目根下的 **Agent 任务书**(`cursor init` 每次刷新):用于**一次性接入**栈配置、合并 `figma-cache.config.js`、补 npm scripts 等。 |
36
- | **`postEnsure`** | **`figma-cache.config.js`** 里的钩子:**每次 `ensure` 写完通用骨架文件之后**调用一次,可写适配说明、维护 **`docs/figma-flow-readme.md`** 等;**抛错不会让整个 ensure 失败**(由 Core 捕获日志)。 |
37
- | **`cursor init` vs `figma-cache init`** | **`cursor init`**:拷 `.cursor` 模板、刷新 **`AGENT-SETUP-PROMPT.md`**、同步 **`colleague-guide-zh.md`**。**`figma-cache init`**:只建 **`figma-cache/index.json`** 空索引,**不管 Cursor**。 |
38
- | **`npm run figma:cache:*`** | 项目在 **`package.json` 的 `scripts`** 里封装的 CLI;未配置时可用 **`npx figma-cache <子命令>`** 代替。 |
39
- | **`tokenProxyBytes`** | 预算统计字段:用 `mcp-raw-get-design-context.txt` 的文件大小做 token 代理估算(用于成本趋势,不等于模型精确 token)。兼容字段 `tokenProxyChars` 仅用于平滑迁移。 |
40
- | **业务流程文档(推荐)** | 通常指项目里的 **`docs/figma-flow-readme.md`**(路径可改):用**中文叙事**写清主路径/分支/边界,并粘贴 **`flow mermaid`**;与 **`index.json` 的 `flows`** 双轨对齐,**人和 Agent 都优先读它建立上下文**。详见 **§5.8~§5.10**。 |
41
-
42
- ---
43
-
44
- ## 1. 这个包是干什么的
45
-
46
- - **把 Figma 链接变成项目里可复用的本地缓存**(`figma-cache/index.json` + `figma-cache/files/...`),并带 **Node CLI** 做标准化、写入、校验。
47
- - **不绑定** Vue/React 等具体 UI;你们项目里的 **Cursor 规则 + `figma-cache.config.js`** 会约定「缓存好之后代码怎么对齐设计」。
48
- - **日常多数时候**:把 Figma 链接丢给 **Cursor Agent**,由 Agent 按规则走「先查本地缓存 → 不够再 MCP → 回写 → 校验」即可;**不必**每人背命令。
49
-
50
- ---
51
-
52
- ## 2. 适用场景(什么时候有用)
53
-
54
- | 场景 | 说明 |
55
- |------|------|
56
- | **单屏 / 单组件对齐** | 只有一条 Figma 链接,要把布局、文案、状态写进代码。 |
57
- | **多屏 / 多节点同一业务流** | 登录 → 首页 → 设置等多条链接,希望在缓存里保留**先后或分支关系**,便于迭代与评审。 |
58
- | **渐进式补设计** | 今天先拉一条链接进缓存,过几天再给下一条,并要求**关联到之前某屏**(下一屏、弹层挂在某屏上、分支等)。 |
59
- | 多人协作同一套设计稿 | 减少重复拉 Figma、口径统一(以本地缓存 + 校验为准)。 |
60
- | 希望对话可复现、可审计 | 设计事实落在仓库的 `figma-cache/`(是否提交 git 由团队约定)。 |
61
- | 用 Cursor 做设计落地 | 配合 `.cursor` 规则与 Skill,Agent 行为一致。 |
62
-
63
- 若项目**从未**装过本包,需要有人做一次**接入**(第 3 节)。
64
-
65
- **和「提示词」的关系**:下面第 5 节给的句子是**自然语言指令**;Agent 在 **Core 规则**下会优先读本地 `figma-cache/`,不足再走 Figma MCP,并调用 **`npm run figma:cache:*` / `npx figma-cache`** 维护索引。你**不需要**自己敲 CLI,但**说清楚意图与关系**,结果会稳定很多。
66
-
67
- ---
68
-
69
- ## 3. 一次性接入(业务项目根目录执行)
70
-
71
- 顺序建议固定为下面 **4 步**,避免和「只刷新 Cursor 模板」混淆。
72
-
73
- ### 步骤 1:安装依赖
74
-
75
- ```bash
76
- npm i -D figma-cache-toolchain
77
- ```
78
-
79
- ### 步骤 2:写入 Cursor 模板并刷新任务书
80
-
81
- ```bash
82
- npx figma-cache cursor init
83
- ```
84
-
85
- 作用:在项目里生成/更新 **`.cursor/`**、`figma-cache.config.example.js`,并**每次刷新**根目录的 **`AGENT-SETUP-PROMPT.md`**。
86
- **注意**:这一步**还不会**创建业务用的 **`figma-cache/index.json`**。
87
-
88
- ### 步骤 3:在 Cursor 里交给 Agent 执行一次
89
-
90
- 在 Cursor 对话中输入 **`@AGENT-SETUP-PROMPT.md`**,并说「**按该文档执行**」。
91
- Agent 会按你们仓库栈生成/合并 **`figma-cache.config.js`**、栈专属 Adapter、补全 **`figma:cache:*` npm scripts** 等(**每个项目通常只需成功做一次**;以后升级包见第 7 节)。
92
-
93
- ### 步骤 4:初始化本地缓存目录(空索引)
94
-
95
- 在 **步骤 3 完成之后**执行(有 script 优先用 script):
96
-
97
- ```bash
98
- npm run figma:cache:init
99
- ```
100
-
101
- 若尚未有对应 script:
102
-
103
- ```bash
104
- npx figma-cache init
105
- ```
106
-
107
- 成功后,项目根会出现 **`figma-cache/`** 目录及 **`figma-cache/index.json`**(开始时可能只有索引,**没有**具体节点文件是正常的)。
108
-
109
- **易混点**:**`cursor init`**(步骤 2)≠ **`figma-cache init`**(步骤 4);前者管 Cursor 模板,后者管**本地缓存数据骨架**。
110
-
111
- ---
112
-
113
- ## 4. 同事日常怎么用(接入完成之后)
114
-
115
- 1. **有 Figma 相关需求时**:在 Cursor 里把 **Figma 链接**和任务说明发给 Agent;Agent 会按项目里的 **Core 规则 + 你们栈 Adapter + Skill** 处理缓存与落地边界。
116
- 2. **需要自检时**(例如合并前、怀疑索引坏了):
117
-
118
- ```bash
119
- npm run figma:cache:validate
120
- ```
121
-
122
- 或:
123
-
124
- ```bash
125
- npx figma-cache validate
126
- ```
127
-
128
- 3. **规范文档在哪**:包内自带说明(安装后在 **`node_modules/figma-cache-toolchain/figma-cache/`** 下的多个 `.md`)。一般**不必**再复制一份到业务仓库;需要时让 Agent **`@` 该路径**即可。
129
- 4. **看预算**(可选):`npm run figma:cache:budget`,快速看 MCP 调用与 `tokenProxyBytes`。
130
-
131
- ---
132
-
133
- ## 5. 团队统一提示词(只保留一个主模板)
134
-
135
- 为了避免“模板太多不好选”,这里统一成 **1 个最推荐提示词**。
136
- 默认先复制这一段,再按需要补 1~2 句附加要求。
137
-
138
- ### 5.1 最推荐主提示词(默认用这个)
139
-
140
- ```text
141
- 请按项目 figma 缓存规则处理下面这条 Figma 链接,并遵循“缓存优先 + 按需 MCP + 最小调用集”:
142
- 1) 先查本地 figma-cache,命中则直接复用,不做刷新;
143
- 2) 仅在未命中或我明确要求刷新时,才调用 figma-mcp 拉取并写入 mcp-raw(随后再 ensure/upsert);
144
- 3) completeness 默认使用 layout,text,tokens(若我补充再扩展 interactions/flow/states/assets/accessibility);
145
- 4) 原始 MCP 数据统一保存到节点目录 mcp-raw/ 子目录;
146
- 5) 默认不保存 whoami 原始文件(仅鉴权排障或我明确要求时保存);
147
- 6) 完成后执行 figma:cache:validate,并汇报:缓存状态、实际 MCP 调用次数、输出文件清单。
148
-
149
- [Figma 链接]
150
- ```
151
-
152
- ### 5.2 仅在需要时追加的附加句(可选)
153
-
154
- - 要强制最新:`忽略本地缓存,强制刷新,以 Figma 最新为准。`
155
- - 要 React 直复用:`若目标项目是 React 栈,优先复用 mcp-raw-get-design-context.txt 的 React 代码,只做必要微调。`
156
- - 要补流程关系:`请在 flow [flowId] 下补齐节点关系(link/chain),并输出 mermaid。`
157
-
158
- ### 5.3 使用建议(团队口径)
159
-
160
- - 日常协作默认只用 **5.1 主提示词**。
161
- - 只有确实需要时,再从 5.2 里加一句附加要求。
162
- - 新同事只记住一条:**先缓存、后实现;能复用本地就不重复调 MCP**。
163
-
164
- ---
165
-
166
- ## 6. 仓库里建议提交什么(和团队对齐即可)
167
-
168
- 常见做法(**以你们团队约定为准**):
169
-
170
- - **建议提交**:`.cursor/`(若团队希望共享同一套 Agent 行为)、`figma-cache.config.js`、`AGENT-SETUP-PROMPT.md`(会随 `cursor init` 刷新)、`package.json` / lock。
171
- - **按需提交**:`figma-cache/index.json` 与 **`figma-cache/files/`**(若希望「拉过即入库」、CI 可校验);若采用默认 **`postEnsure`**,还可提交 **`docs/figma-flow-readme.md`**(或你在 `FIGMA_CACHE_FLOW_README` 中自定义的路径)供评审与新人阅读。
172
- - **可不提交**:纯个人试验缓存(则同事本地需自行 `ensure`)。
173
-
174
- ---
175
-
176
- ## 7. 升级 npm 包之后怎么做
177
-
178
- ```bash
179
- npm i -D figma-cache-toolchain@latest
180
- npx figma-cache cursor init
181
- ```
182
-
183
- - **`AGENT-SETUP-PROMPT.md`** 每次 `cursor init` 都会刷新,便于看到新版说明。
184
- - 若你们**已经完成过**步骤 3,**不必**为升级而完整重跑一遍 Agent;除非任务书里有你们需要合并的新要求。
185
- - **已有** `figma-cache/index.json` 时,**一般不必**重复执行 `figma:cache:init`。
186
-
187
- ---
188
-
189
- ## 8. 排障与谁维护
190
-
191
- - 命令帮助:`npx figma-cache`(无子命令时通常会打印用法)。
192
- - 配置预览:`npm run figma:cache:config`(若已配置 script)。
193
- - **维护责任人**:建议在团队 README 或内部 wiki 里写一句「谁负责升级 `figma-cache-toolchain`、谁审批 `.cursor` 规则变更」。
194
-
195
- ---
196
-
197
- ## 9. 本文档位置(方便你转发)
198
-
199
- - **业务项目(推荐)**:在仓库根执行 **`npx figma-cache cursor init`** 后,本说明会**自动写入或刷新**到 **`figma-cache/colleague-guide-zh.md`**(与 `FIGMA_CACHE_DIR` 一致,默认目录名 `figma-cache/`)。同事 **`@figma-cache/colleague-guide-zh.md`** 即可,**不必**从 `node_modules` 手抄。每次升级包后再跑一次 `cursor init` 即可同步正文。
200
- - **包内原件**:`node_modules/figma-cache-toolchain/figma-cache/colleague-guide-zh.md`(与上者内容一致,发版随包附带)。
201
- - **维护工具链源码的仓库**:同路径 **`figma-cache/colleague-guide-zh.md`**。
202
-
203
- 将本文件全文复制到 IM / 邮件 / 内部文档即可给同事使用。
204
-
205
-
206
-