@xulthekl/team-flow 0.53.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.claude/always/phase-guard.md +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.cursor-plugin/marketplace.json +1 -1
  6. package/.cursor-plugin/plugin.json +1 -1
  7. package/.github/plugin/marketplace.json +2 -2
  8. package/CHANGELOG.md +71 -0
  9. package/GEMINI.md +1 -1
  10. package/INSTALL.md +1 -1
  11. package/README.md +1 -1
  12. package/agents/prototype-builder.md +6 -5
  13. package/agents/prototype-env-scout.md +4 -4
  14. package/agents/release-archivist.md +1 -0
  15. package/docs/README_en.md +1 -1
  16. package/gemini-extension.json +1 -1
  17. package/hooks/session-start +2 -2
  18. package/llms.txt +1 -1
  19. package/package.json +1 -1
  20. package/plugin.json +2 -2
  21. package/scripts/design-system-import.mjs +238 -0
  22. package/scripts/gen-primer.mjs +143 -0
  23. package/scripts/guard/design-token-guard.mjs +284 -63
  24. package/scripts/lib/ds-parse.mjs +124 -0
  25. package/scripts/token-extract.mjs +257 -0
  26. package/skills/design-system/SKILL.md +55 -9
  27. package/skills/design-system/references/agents/design-system-architect.md +44 -7
  28. package/skills/design-system/references/creation-flow.md +39 -5
  29. package/skills/design-system/references/showcase-board-b-end.md +78 -0
  30. package/skills/design-system/references/showcase-board-c-end.md +92 -0
  31. package/skills/design-system/references/token-derivation.md +34 -9
  32. package/skills/design-system/references/variant-schema.md +14 -3
  33. package/skills/prototype/SKILL.md +14 -8
  34. package/skills/prototype/references/builder-methodology.md +62 -5
  35. package/skills/prototype/references/craft/anti-ai-slop.md +1 -1
  36. package/skills/prototype/references/craft/state-coverage.md +8 -2
  37. package/skills/prototype/references/orchestration-flow.md +12 -3
  38. package/skills/prototype/references/prototype-scaffold/assets/design-tokens.css +2 -2
  39. package/skills/prototype/references/template.html +10 -10
  40. package/skills/release-archivist/SKILL.md +10 -3
  41. package/skills/release-archivist/references/closing-procedures.md +10 -0
  42. package/skills/workflow-bootstrap/SKILL.md +14 -2
  43. package/templates/design-systems/references/claude.md +315 -0
  44. package/templates/design-systems/references/linear-app.md +370 -0
  45. package/templates/design-systems/references/notion.md +312 -0
  46. package/templates/design-systems/references/posthog.md +259 -0
  47. package/templates/design-systems/references/sentry.md +265 -0
  48. package/templates/design-systems/references/stripe.md +325 -0
  49. package/templates/design-systems/references/supabase.md +258 -0
  50. package/templates/design-systems/references/vercel.md +313 -0
  51. package/templates/design-systems/registry.json +75 -0
  52. package/templates/design-systems/styles.json +576 -0
@@ -0,0 +1,124 @@
1
+ // ds-parse.mjs — 设计系统文档解析(公共模块,v0.54.0)
2
+ //
3
+ // 供 design-token-guard.mjs / gen-primer.mjs 复用(避免多份重复解析)。
4
+ // 纯函数,无副作用。
5
+
6
+ // 组件契约表分组规则(§4.1.1):类型 → states 下限(null = 豁免)
7
+ export const COMPONENT_TYPE_MIN_STATES = { '交互': 3, '轻量': 2, '豁免': null };
8
+
9
+ // 组件数三档(§4.1.1)
10
+ export const COMPONENT_COUNT_PASS = 15;
11
+ export const COMPONENT_COUNT_WARN = 10;
12
+
13
+ // Collect the lower-cased text of every level-2 ("## ") markdown header.
14
+ export function extractH2Headers(markdown) {
15
+ return markdown
16
+ .split('\n')
17
+ .filter(line => /^##\s+/.test(line) && !/^###/.test(line))
18
+ .map(line => line.replace(/^##\s+/, '').trim().toLowerCase());
19
+ }
20
+
21
+ // True when any ## header mentions the given section name.
22
+ export function hasSection(headers, section) {
23
+ return headers.some(header => header.includes(section));
24
+ }
25
+
26
+ // Return the raw (case-preserving) body of a section (text between its ##
27
+ // header and the next ## header or EOF); null when the section is absent.
28
+ export function sectionBodyRaw(markdown, section) {
29
+ const lines = markdown.split('\n');
30
+ const startIdx = lines.findIndex(
31
+ line => /^##\s+/.test(line) && !/^###/.test(line) &&
32
+ line.replace(/^##\s+/, '').trim().toLowerCase().includes(section),
33
+ );
34
+ if (startIdx === -1) return null;
35
+ const body = [];
36
+ for (let i = startIdx + 1; i < lines.length; i++) {
37
+ if (/^##\s+/.test(lines[i]) && !/^###/.test(lines[i])) break;
38
+ body.push(lines[i]);
39
+ }
40
+ return body.join('\n');
41
+ }
42
+
43
+ // Lower-cased section body.
44
+ export function sectionBody(markdown, section) {
45
+ const raw = sectionBodyRaw(markdown, section);
46
+ return raw === null ? null : raw.toLowerCase();
47
+ }
48
+
49
+ // Parse a markdown table row into trimmed cells; null when not a data row.
50
+ export function parseTableRow(line) {
51
+ if (!/^\s*\|/.test(line)) return null;
52
+ const cells = line.split('|').slice(1, -1).map(c => c.trim());
53
+ if (cells.length === 0) return null;
54
+ if (cells.every(c => /^:?-{2,}:?$/.test(c))) return null;
55
+ return cells;
56
+ }
57
+
58
+ // Parse the components contract table (§4.1.1):
59
+ // | 组件 | 类型 | variants | sizes | states | 用途 | 禁止 |
60
+ // Returns { rows: [{name, type, variants, sizes, states, statesCount, hasVariants}] } or null.
61
+ export function parseComponentsTable(markdown) {
62
+ const body = sectionBodyRaw(markdown, 'components');
63
+ if (!body) return null;
64
+ const rows = [];
65
+ for (const line of body.split('\n')) {
66
+ const cells = parseTableRow(line);
67
+ if (!cells || cells.length < 5) continue;
68
+ const name = cells[0];
69
+ const type = cells[1];
70
+ if (/^(组件|component)/i.test(name)) continue;
71
+ if (!Object.prototype.hasOwnProperty.call(COMPONENT_TYPE_MIN_STATES, type)) continue;
72
+ const variants = cells[2];
73
+ const sizes = cells[3];
74
+ const states = cells[4];
75
+ const stateItems = states
76
+ .split('/')
77
+ .map(s => s.trim())
78
+ .filter(s => s && s !== '—' && s !== '-')
79
+ .filter(s => !/¹/.test(s));
80
+ rows.push({
81
+ name,
82
+ type,
83
+ variants,
84
+ sizes,
85
+ states,
86
+ hasVariants: Boolean(variants && variants !== '—' && variants !== '-'),
87
+ statesCount: stateItems.length,
88
+ });
89
+ }
90
+ return rows.length > 0 ? { rows } : null;
91
+ }
92
+
93
+ // Parse governance section for the `contract` marker (§4.1.3).
94
+ // Returns 'v1' | 'legacy' | null.
95
+ export function parseContract(markdown) {
96
+ const body = sectionBodyRaw(markdown, 'governance');
97
+ if (!body) return null;
98
+ const m = body.match(/contract\s*[::]\s*(v1|legacy)/i);
99
+ return m ? m[1].toLowerCase() : null;
100
+ }
101
+
102
+ // Parse the A1 identity token block from a token/spec section (for primer 速查).
103
+ // Looks for `--token: value` pairs anywhere in the markdown.
104
+ export function parseA1Tokens(markdown) {
105
+ const names = ['--bg', '--surface', '--fg', '--muted', '--border', '--accent', '--font-display', '--font-body'];
106
+ const out = {};
107
+ for (const name of names) {
108
+ const re = new RegExp(`${name.replace(/-/g, '\\-')}\\s*[::]\\s*([^;\\n]+)`, 'i');
109
+ const m = markdown.match(re);
110
+ if (m) out[name] = m[1].trim().replace(/\s+$/, '');
111
+ }
112
+ return out;
113
+ }
114
+
115
+ // Extract the anti-patterns section as a list of bullet/numbered items.
116
+ export function parseAntiPatterns(markdown) {
117
+ const body = sectionBodyRaw(markdown, 'anti-patterns');
118
+ if (!body) return [];
119
+ return body
120
+ .split('\n')
121
+ .map(l => l.trim())
122
+ .filter(l => /^([-*]|\d+[.、)])\s+\S/.test(l))
123
+ .map(l => l.replace(/^([-*]|\d+[.、)])\s+/, ''));
124
+ }
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+ // token-extract.mjs — 逆向建库的确定性证据提取器(v0.54.0,设计 §4.1.6 降级版)
3
+ //
4
+ // 从既有代码目录提取设计 token 证据:8 组正则证据机 → source-tokens.json + 统计报告。
5
+ // **零 LLM、零语义推断**——只给"频次 + 位置分布"证据,语义角色(谁是 primary)由人工策展指定。
6
+ //
7
+ // Usage:
8
+ // node scripts/token-extract.mjs <代码目录> [--out <source-tokens.json 路径>] [--budget-ms 60000]
9
+ // node scripts/token-extract.mjs <代码目录> --report # 只输出统计报告
10
+
11
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
12
+ import { join, resolve, dirname, extname, relative } from 'node:path';
13
+
14
+ // ── 配置 ──
15
+
16
+ const SCAN_EXTS = new Set(['.css', '.scss', '.less', '.html', '.htm', '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.json']);
17
+ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', 'coverage', '.cache', 'vendor', '__pycache__', '.turbo', 'out', 'target']);
18
+ const MAX_FILE_SIZE = 512 * 1024; // 512 KiB——超过只登记不扫描
19
+ const DEFAULT_BUDGET_MS = 60_000;
20
+
21
+ // ── 证据机(8 组正则,借鉴 open-design token-evidence)──
22
+
23
+ const EVIDENCE = [
24
+ { kind: 'color', re: /#[0-9a-fA-F]{8}\b|#[0-9a-fA-F]{6}\b|#[0-9a-fA-F]{3}\b/g },
25
+ { kind: 'color', re: /rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+[^)]*\)/g },
26
+ { kind: 'color', re: /hsla?\(\s*[\d.]+\s*,\s*[\d.]+%\s*,\s*[\d.]+%[^)]*\)/g },
27
+ { kind: 'custom-prop', re: /--[a-zA-Z][\w-]*\s*:\s*([^;{}]+)[;}]/g, captureValue: true, nameCapture: /(--[a-zA-Z][\w-]*)/ },
28
+ { kind: 'font', re: /font-family\s*:\s*([^;{}]+)[;}]/gi, captureValue: true },
29
+ { kind: 'spacing', re: /(?:padding|margin|gap|inset|top|right|bottom|left)\s*:\s*([^;{}]+)[;}]/gi, captureValue: true },
30
+ { kind: 'radius', re: /border(?:-[a-z]+)?-radius\s*:\s*([^;{}]+)[;}]/gi, captureValue: true },
31
+ { kind: 'shadow', re: /box-shadow\s*:\s*([^;{}]+)[;}]/gi, captureValue: true },
32
+ { kind: 'tailwind', re: /\b(?:bg|text|border|from|to|via|ring)-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950)\b/g },
33
+ ];
34
+
35
+ // ── 参数 ──
36
+
37
+ const argv = process.argv.slice(2);
38
+ const REPORT_ONLY = argv.includes('--report');
39
+ const outIdx = argv.indexOf('--out');
40
+ const outArg = outIdx !== -1 ? argv[outIdx + 1] : null;
41
+ const budgetIdx = argv.indexOf('--budget-ms');
42
+ const budgetMs = budgetIdx !== -1 ? Number(argv[budgetIdx + 1]) || DEFAULT_BUDGET_MS : DEFAULT_BUDGET_MS;
43
+ const positional = argv.filter((a, i) => !a.startsWith('--') && !(outIdx !== -1 && i === outIdx + 1) && !(budgetIdx !== -1 && i === budgetIdx + 1));
44
+ const rootArg = positional[0];
45
+
46
+ if (!rootArg) {
47
+ console.error('Usage: node scripts/token-extract.mjs <代码目录> [--out <json 路径>] [--budget-ms 60000] [--report]');
48
+ process.exit(1);
49
+ }
50
+ const root = resolve(rootArg);
51
+ if (!existsSync(root) || !statSync(root).isDirectory()) {
52
+ console.error(`目录不存在或不可读:${root}`);
53
+ process.exit(1);
54
+ }
55
+
56
+ // ── 走树(迭代式,预算控制)──
57
+
58
+ const started = Date.now();
59
+ const files = [];
60
+ const skipped = [];
61
+ const queue = [root];
62
+ while (queue.length > 0) {
63
+ if (Date.now() - started > budgetMs) {
64
+ skipped.push({ reason: 'budget-exceeded', remaining: queue.length });
65
+ break;
66
+ }
67
+ const dir = queue.shift();
68
+ let entries;
69
+ try { entries = readdirSync(dir, { withFileTypes: true }); } catch { skipped.push({ path: dir, reason: 'unreadable' }); continue; }
70
+ for (const e of entries) {
71
+ const full = join(dir, e.name);
72
+ if (e.isSymbolicLink()) { skipped.push({ path: full, reason: 'symlink' }); continue; }
73
+ if (e.isDirectory()) {
74
+ if (SKIP_DIRS.has(e.name)) { skipped.push({ path: full, reason: 'directory-skiplist' }); continue; }
75
+ queue.push(full);
76
+ continue;
77
+ }
78
+ if (!SCAN_EXTS.has(extname(e.name).toLowerCase())) continue;
79
+ let size = 0;
80
+ try { size = statSync(full).size; } catch { continue; }
81
+ if (size > MAX_FILE_SIZE) { skipped.push({ path: full, reason: 'large-file', size }); continue; }
82
+ files.push(full);
83
+ }
84
+ }
85
+
86
+ // ── 证据采集 ──
87
+
88
+ const evidence = new Map(); // key: `${kind}:${normalizedValue}` → { kind, value, count, sources[], usage:Set }
89
+
90
+ function record(kind, value, file, line) {
91
+ const v = String(value).trim().replace(/\s+/g, ' ');
92
+ if (!v || v.length > 120) return;
93
+ const key = `${kind}:${v.toLowerCase()}`;
94
+ let e = evidence.get(key);
95
+ if (!e) { e = { kind, value: v, count: 0, sources: [], usage: new Set() }; evidence.set(key, e); }
96
+ e.count += 1;
97
+ if (e.sources.length < 20) e.sources.push(`${relative(root, file)}:${line}`);
98
+ e.usage.add(relative(root, file));
99
+ }
100
+
101
+ let scanned = 0;
102
+ for (const file of files) {
103
+ if (Date.now() - started > budgetMs) { skipped.push({ reason: 'budget-exceeded-during-scan' }); break; }
104
+ let content;
105
+ try { content = readFileSync(file, 'utf-8'); } catch { continue; }
106
+ scanned += 1;
107
+ const lines = content.split('\n');
108
+ // 逐行扫(拿行号)
109
+ for (let i = 0; i < lines.length; i++) {
110
+ const line = lines[i];
111
+ for (const ev of EVIDENCE) {
112
+ ev.re.lastIndex = 0;
113
+ let m;
114
+ while ((m = ev.re.exec(line)) !== null) {
115
+ if (ev.captureValue) {
116
+ const raw = m[1] !== undefined ? m[1] : m[0];
117
+ // 多值(如 padding: 8px 16px)拆开逐项
118
+ for (const part of raw.split(/[\s,]+/)) {
119
+ const t = part.trim();
120
+ if (!t || t === '0' || t === 'auto' || t === 'inherit' || t === 'initial') continue;
121
+ if (ev.kind === 'custom-prop') {
122
+ const nameM = line.match(ev.nameCapture);
123
+ record(ev.kind, `${nameM ? nameM[1] : '--?'}=${t}`, file, i + 1);
124
+ } else {
125
+ record(ev.kind, t, file, i + 1);
126
+ }
127
+ }
128
+ } else {
129
+ record(ev.kind, m[0], file, i + 1);
130
+ }
131
+ if (m.index === ev.re.lastIndex) ev.re.lastIndex += 1; // 防零宽死循环
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // ── 值归一化 + 相似聚类(确定性)──
138
+
139
+ function normalizeHex(v) {
140
+ const m = v.match(/^#([0-9a-fA-F]{3})$/);
141
+ if (m) return '#' + m[1].split('').map(c => c + c).join('').toLowerCase();
142
+ const m6 = v.match(/^#([0-9a-fA-F]{6})$/);
143
+ if (m6) return '#' + m6[1].toLowerCase();
144
+ return v.toLowerCase();
145
+ }
146
+
147
+ function hexToRgb(hex) {
148
+ const h = normalizeHex(hex).replace('#', '');
149
+ if (h.length !== 6) return null;
150
+ return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
151
+ }
152
+
153
+ // 距离近似(RGB 欧氏;ΔE≈2 量级约为 RGB 距离 ~12)
154
+ function rgbDistance(a, b) {
155
+ return Math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2);
156
+ }
157
+
158
+ function cluster(list, isSimilar) {
159
+ const sorted = [...list].sort((x, y) => y.count - x.count);
160
+ const clusters = [];
161
+ for (const item of sorted) {
162
+ const target = clusters.find(c => isSimilar(c.representative, item));
163
+ if (target) {
164
+ target.members.push(item);
165
+ target.count += item.count;
166
+ for (const s of item.sources) if (target.sources.length < 20) target.sources.push(s);
167
+ for (const u of item.usage) target.usage.add(u);
168
+ } else {
169
+ clusters.push({ representative: item, members: [item], count: item.count, sources: [...item.sources], usage: new Set(item.usage) });
170
+ }
171
+ }
172
+ return clusters;
173
+ }
174
+
175
+ const allTokens = [...evidence.values()];
176
+
177
+ // 颜色:hex 归并(ΔE<2 ≈ RGB 距离 <12)
178
+ const colorTokens = allTokens.filter(t => t.kind === 'color' && hexToRgb(t.value));
179
+ const nonHexColors = allTokens.filter(t => t.kind === 'color' && !hexToRgb(t.value));
180
+ const colorClusters = cluster(colorTokens, (a, b) => {
181
+ const ra = hexToRgb(a.value), rb = hexToRgb(b.value);
182
+ return ra && rb && rgbDistance(ra, rb) < 12;
183
+ });
184
+
185
+ // 尺寸类:±1px 归并
186
+ const sizeKinds = ['spacing', 'radius'];
187
+ const sizeClusters = {};
188
+ for (const kind of sizeKinds) {
189
+ const items = allTokens.filter(t => t.kind === kind);
190
+ sizeClusters[kind] = cluster(items, (a, b) => {
191
+ const na = parseFloat(a.value), nb = parseFloat(b.value);
192
+ if (!Number.isFinite(na) || !Number.isFinite(nb)) return a.value === b.value;
193
+ const ua = a.value.replace(/[\d.\s-]/g, ''), ub = b.value.replace(/[\d.\s-]/g, '');
194
+ return ua === ub && Math.abs(na - nb) <= 1;
195
+ });
196
+ }
197
+
198
+ const passthrough = ['font', 'shadow', 'custom-prop', 'tailwind'];
199
+ const passthroughTokens = {};
200
+ for (const kind of passthrough) {
201
+ passthroughTokens[kind] = allTokens.filter(t => t.kind === kind).sort((a, b) => b.count - a.count);
202
+ }
203
+
204
+ // ── 输出 ──
205
+
206
+ const sourceTokens = {
207
+ scan: { root, filesDiscovered: files.length, filesScanned: scanned, generatedAt: new Date().toISOString(), budgetMs },
208
+ tokens: {
209
+ color: colorClusters.map(c => ({
210
+ value: c.representative.value,
211
+ count: c.count,
212
+ mergedFrom: c.members.length > 1 ? c.members.map(m => m.value) : undefined,
213
+ sources: c.sources,
214
+ usage: [...c.usage].slice(0, 20),
215
+ })),
216
+ colorRaw: nonHexColors.sort((a, b) => b.count - a.count).map(t => ({ value: t.value, count: t.count, sources: t.sources })),
217
+ spacing: sizeClusters.spacing.map(c => ({ value: c.representative.value, count: c.count, sources: c.sources })),
218
+ radius: sizeClusters.radius.map(c => ({ value: c.representative.value, count: c.count, sources: c.sources })),
219
+ ...Object.fromEntries(passthrough.map(k => [k, passthroughTokens[k].map(t => ({ name: t.value, count: t.count, sources: t.sources.slice(0, 5) }))])),
220
+ },
221
+ skipped,
222
+ };
223
+
224
+ const colorCount = sourceTokens.tokens.color.length;
225
+ const spacingCount = sourceTokens.tokens.spacing.length;
226
+ const radiusCount = sourceTokens.tokens.radius.length;
227
+ const fontCount = sourceTokens.tokens.font.length;
228
+
229
+ // 统计报告(stdout)
230
+ const report = [];
231
+ report.push('=== Token 提取报告(确定性,零 LLM)===');
232
+ report.push(`根目录:${root}`);
233
+ report.push(`发现文件:${files.length} | 已扫描:${scanned}${skipped.length ? ` | 跳过:${skipped.length}` : ''}`);
234
+ report.push('');
235
+ report.push(`颜色:${colorCount} 个聚类(归并 ${colorTokens.length - colorCount} 个近似值)`);
236
+ for (const c of sourceTokens.tokens.color.slice(0, 8)) {
237
+ report.push(` ${c.value.padEnd(10)} ×${String(c.count).padStart(4)}${c.mergedFrom ? ` (含 ${c.mergedFrom.length - 1} 个近似写法)` : ''}`);
238
+ }
239
+ report.push('');
240
+ report.push(`间距:${spacingCount} 个聚类 | 圆角:${radiusCount} 个 | 字体栈:${fontCount} 个`);
241
+ for (const c of sourceTokens.tokens.spacing.slice(0, 5)) report.push(` spacing ${c.value} ×${c.count}`);
242
+ for (const c of sourceTokens.tokens.radius.slice(0, 4)) report.push(` radius ${c.value} ×${c.count}`);
243
+ report.push('');
244
+ report.push('⚠️ 以上仅为「频次 + 位置」证据——语义角色(哪个是 primary / border / font-display)**需要人工策展指定**,');
245
+ report.push(' 本工具不做自动推断(设计 §4.1.6:避免"垃圾设计系统 + 满分审计")。');
246
+ report.push('');
247
+ report.push('下一步:将本报告与 source-tokens.json 呈交用户,由用户指定主色/中性色/语义色/字体后,');
248
+ report.push(' 由 design-system skill 的 create-from-code 流程补齐 A1/A2/B-slot 并落盘。');
249
+
250
+ console.log(report.join('\n'));
251
+
252
+ if (!REPORT_ONLY) {
253
+ const outPath = outArg ? resolve(outArg) : join(root, '.team-flow', 'token-extract', 'source-tokens.json');
254
+ mkdirSync(dirname(outPath), { recursive: true });
255
+ writeFileSync(outPath, JSON.stringify(sourceTokens, null, 2), 'utf-8');
256
+ console.log(`\nsource-tokens.json 已写入:${outPath}`);
257
+ }
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: design-system
3
3
  description: >-
4
- 设计系统独立创建与维护 skill。通过用户主导的交互流程(LLM 推荐+用户确认)创建
5
- 项目级 design-system(base 品牌共享层 + B端/C端变体),产出确定性 token 体系 +
6
- 预览画廊。支持独立调用或 prototype skill 内部编排调用。当用户需要创建设计系统、
7
- 迭代设计系统、或原型流程发现设计系统缺失时使用。
4
+ 设计系统独立创建与维护 skill,产出落 .team-flow/design-system/(base 品牌共享层 +
5
+ B端/C端变体 + primer + 预览画廊)。三条创建入口:交互式(LLM 推荐+用户确认)、
6
+ 从内置模板库导入(registry.json 8 个参考)、从既有代码逆向建库(create-from-code)。
7
+ 支持独立调用或 prototype skill 内部编排调用。当用户需要创建设计系统、迭代设计系统、
8
+ 刷新 primer 组件白名单、或原型流程发现设计系统缺失时使用。
8
9
  不适用于:原型绘制(用 prototype)、纯架构设计(用 architecture-design)。
9
10
  ---
10
11
 
@@ -23,6 +24,19 @@ Do NOT invoke for:
23
24
 
24
25
  ## 交互创建流程(6 步)
25
26
 
27
+ ### Step 0: 起点选择(v0.54.0)
28
+
29
+ 若 `.team-flow/design-system/` 不存在(无设计系统),先询问起点:
30
+
31
+ - **从模板库选择**:展示 `${CLAUDE_PLUGIN_ROOT}/templates/design-systems/registry.json`(8 个参考:linear-app / stripe / vercel / supabase / sentry / posthog / notion / claude)→ 用户选定后运行 `node ${CLAUDE_PLUGIN_ROOT}/scripts/design-system-import.mjs <reference.md> --out .team-flow/design-system/base.md` 转换 → **续跑 Step 5 评审 → Step 6 落盘**(落盘动作含 primer 生成 + guard 校验;primer 缺失会让 prototype Step 0 对 `contract: v1` 系统 blocked)
32
+ - **逆向建库**:走 **create-from-code** 模式(见下方「逆向建库模式」)——从既有代码提取
33
+ - **从零创建**:直接进 Step 1(交互式 6 步)
34
+ - **已有设计系统** → 走 iterate 模式
35
+
36
+ > B 类参考(如 linear-app)转换为**自动提取配色**,须提示用户人工核对(转换器会输出该警告)。
37
+
38
+ > **风格种子(styles.json)**:`${CLAUDE_PLUGIN_ROOT}/templates/design-systems/styles.json` 提供 57 个风格型设计系统的配色/字体元数据(如"想要 X 风格")——Step 1 需求收集时可作为 mood/品牌主色的参考依据,用户选定后以 `brand_color` 传入(不扩充 5 维封闭词汇,保持核心交互稳定)。
39
+
26
40
  ### Step 1: 需求收集(≤5 个预填推荐问题)
27
41
  用 AskUserQuestion 收集 5 个维度(预填 LLM 推荐值,用户原样确认或调整):
28
42
  1. 品牌调性(封闭词汇:professional_minimal / warm_approachable / technical_dense / editorial / brutalist)
@@ -42,23 +56,55 @@ Do NOT invoke for:
42
56
  ### Step 4: 预览生成
43
57
  生成 `preview.html`(自包含 HTML 画廊:色板+排版+间距+组件+明暗切换)。用 `references/preview-template.html` 模板填充 token 值。
44
58
 
59
+ ### Step 4.5: Design Showcase(可选,v0.54.0)
60
+ 询问用户是否产出**基准原型**(判断设计系统实际效果):派 `prototype-builder`(`mode: showcase`)用内置车企 brief(`references/showcase-board-{b,c}-end.md`,按 target)产出 1-3 页 Board 到 scratch。产出前明示预估耗时;可跳过。详见 `references/creation-flow.md` Step 4.5。
61
+
45
62
  ### Step 5: 用户评审 + 确认
46
- 呈现:token 摘要表 + 预览路径 + 默认值透明报告。AskUserQuestion:确认 / 调整 / 重新来。
63
+ 呈现:token 摘要表 + 预览路径 + **showcase 路径(如产出)** + 默认值透明报告。AskUserQuestion:确认 / 调整 / 重新来。
47
64
 
48
65
  ### Step 6: 落盘 + guard 校验
49
- 写入 `.team-flow/design-system/`,运行 `scripts/guard/design-token-guard.mjs` 校验。
66
+ 写入 `.team-flow/design-system/`(base + 变体 + **primer**(`${CLAUDE_PLUGIN_ROOT}/scripts/gen-primer.mjs`)+ preview + **showcase 从 scratch 复制**),运行 `node ${CLAUDE_PLUGIN_ROOT}/scripts/guard/design-token-guard.mjs <设计系统目录>`(v0.54.0:硬校验 + 六层审计;传目录会自动合并 base + 端变体再断言 9 段——base 品牌层本身不含 typography/spacing/layout/motion)。**target=both 时按端各跑一次**(`--variant b-end` / `--variant c-end`)——合并断言掩盖单端残缺。
50
67
 
51
68
  ## 存储位置
52
69
  ```
53
70
  .team-flow/design-system/
54
- ├── base.md # 品牌共享层
55
- ├── b-end.md # B 端变体(可选)
71
+ ├── base.md # 品牌共享层(含组件契约表/principles/governance)
72
+ ├── b-end.md # B 端变体(可选,components 段只写端特有差异)
56
73
  ├── c-end.md # C 端变体(可选)
57
- └── preview.html # token 预览画廊
74
+ ├── primer.md # AI 约束入口(scripts/gen-primer.mjs 生成,含 digest)
75
+ ├── preview.html # token 预览画廊
76
+ ├── showcase/ # 展示板(v0.54.0,可选:b-end.html / c-end-*.html)
77
+ ├── variants/ # 主题变体(如 dark.md)
78
+ └── pending.md # 增量待办(v0.54.0,单写者 = 本 skill)
58
79
  ```
59
80
 
81
+ ## pending.md(设计系统待办,v0.54.0)
82
+
83
+ 原型阶段用户"暂不处理"的增量暂存(`ds_increment` 的落点)。
84
+ - **单写者 = design-system skill**——本 skill 的 iterate 流程负责增删;主代理与 release-archivist **只读**
85
+ - 条目格式:`{描述} | 来源: <change-id 或 s2-prototype> | 时间: <ISO>`
86
+ - **清理规则**:用户确认 → iterate 落盘时移除该条;用户拒绝 → 直接删除(重复发现就重复问);用户自行解决 → 下次 iterate 按同义条目清理
87
+ - iterate 时检查 pending.md:有待办则提示用户"是否一并处理"
88
+
60
89
  ## 迭代模式(iterate)
61
90
  已有 design-system → 读取 → 合并增量 → 变更履历 → 预览 → 确认 → 写入。
91
+ 增量来源:① 原型阶段确认的 `ds_increment`(⑥ 路由)② **pending.md 累积待办** ③ change closing 二级确认项。
92
+ 落盘后重新生成 primer(`${CLAUDE_PLUGIN_ROOT}/scripts/gen-primer.mjs`)+ 跑 guard;涉及 token/契约变更时**可选**重新生成 showcase(用户可跳过)。
93
+
94
+ ## 逆向建库模式(create-from-code,v0.54.0)
95
+
96
+ **场景**:企业已有符合规范的原型代码 → 基于它建立设计系统。**确定性提取 + 人工策展**(不做自动语义推断——避免"垃圾设计系统 + 满分审计")。
97
+
98
+ **入口**:`/team-flow:design-system create-from-code --source <代码目录>`(多仓库可传多值或指向 repo_layout 清单)
99
+
100
+ **流程(5 步)**:
101
+ 1. **提取**:`node ${CLAUDE_PLUGIN_ROOT}/scripts/token-extract.mjs <目录>` → `.team-flow/token-extract/source-tokens.json` + 统计报告(频次 + 位置证据,零 LLM)
102
+ 2. **呈现报告**:向用户展示提取统计("47 个文件、23 个颜色、8 个间距值,高频 Top10 …")
103
+ 3. **候选稿**:从高频值生成候选 token 表(标注"候选")
104
+ 4. **人工策展**(多轮 AskUserQuestion):用户指定 primary / 中性色 / 语义色 / 字体栈(每项有频次+位置证据可参考)→ skill 按确定性规则补齐 A1/A2/B-slot + palette 阶梯
105
+ 5. **落盘**:base.md + 变体 + primer + preview(+ 可选 showcase)→ guard 六层审计
106
+
107
+ **关键约束**:绝不静默发明(候选值标注 `sources[]` 证据);**语义角色由人定,不由 LLM 定**;目标已有 base.md 时走 iterate 合并(需用户确认)。
62
108
 
63
109
  ## 迁移兼容
64
110
  检测旧 `prototype/design-system.md` → 提示迁移到 `.team-flow/design-system/base.md`(一次性)。
@@ -1,5 +1,3 @@
1
- **Note: The current year is 2026.** Use this when assessing recency.
2
-
3
1
  You are a Design System Architect. You are dispatched by the `design-system` skill to **create or iterate** the project-level `.team-flow/design-system/base.md` — the single source of truth for design tokens, components, and anti-patterns (compounded back via prototype-sync). The orchestrating design-system skill **never writes `base.md` itself — you are the sole writer**. You run in two phases: first produce a **draft** (`confirmed: false`) for orchestrator review + human confirmation; then, re-dispatched with `confirmed: true`, **you yourself write the official path**. The orchestrator only reviews, confirms, and re-dispatches — it does not Write/Edit `base.md`.
4
2
 
5
3
  ### 文件结构:base + variant
@@ -33,15 +31,49 @@ Every `.team-flow/design-system/base.md` follows this schema (9 sections + a 5-d
33
31
  | `typography` | font-family / scale(1.25 比例)/ line-height |
34
32
  | `spacing` | 4 / 8 / 12 / 16 / 24 阶梯(基于 4 基数) |
35
33
  | `layout` | 栅格 12 列 / 断点 sm/md/lg / 容器 max-width |
36
- | `components` | button / input / card / table 规范(见 components/) |
34
+ | `components` | **组件契约表(v0.54.0,全量真源)**:`\| 组件 \| 类型 \| variants \| sizes \| states \| 用途 \| 禁止 \|`;类型 ∈ 交互/轻量/豁免;**≥10 类起步**(20 类完整基线见下方组件清单) |
37
35
  | `motion` | duration 150–300ms / easing standard |
38
36
  | `voice` | 文案语气(专业、简洁) |
39
37
  | `brand` | logo / 品牌主色 |
40
38
  | `anti-patterns` | 禁止内联样式漂移 / 禁止非 token 颜色 |
39
+ | `principles`(v0.54.0) | **设计原则 ≥3 条**(随 mood 预填,用户可调整) |
40
+ | `governance`(v0.54.0) | `contract: v1`(新建固定 v1;存量升级时由 iterate 置 v1)+ version + 负责人 + 弃用策略 + changelog |
41
41
  | `palette`(5 方向确定性调色板) | neutral / primary / success / warning / danger,各含 50–900 阶梯 |
42
42
  | `aliases`(B-slot 别名层,v0.18.0) | `--fg-2 → var(--fg)` / `--meta → var(--muted)` / `--border-soft → var(--border)` / `--surface-warm → var(--surface)`——组件引用 B-slot 永远可解析 |
43
43
  | `extensions`(C-extension 待提升清单,v0.18.0) | 品牌专有 token 名单制;提升路径:C→B(≥2 品牌需要)→A2(有全局默认值) |
44
44
 
45
+ > **文档头部还需一行 a11y 声明(v0.54.0)**:`> a11y: WCAG 2.2 AA(对比度 4.5:1 / 大字 3:1 / 焦点可见 / 键盘可达)`——从 prototype craft 层提级(guard L0-可访问性依据)。
46
+
47
+ ### 组件契约表:20 类完整基线(v0.54.0)
48
+
49
+ 新建设计系统时**至少产出 10 类**(guard 三档:<10 FAIL 标签 / 10-14 WARN 可用 / ≥15 PASS)。完整基线与分组(类型列决定 guard 的 states 下限):
50
+
51
+ | 组件 | 类型 | variants | sizes | states |
52
+ |------|------|----------|-------|--------|
53
+ | Button | 交互 | primary / secondary / ghost / danger | sm / md / lg | default / hover / focus-visible / active / disabled / loading |
54
+ | Input | 交互 | default / error / success | sm / md / lg | default / focus / disabled / readonly / error / filled |
55
+ | Select | 交互 | default / multiple / searchable | sm / md / lg | default / focus / disabled / error / open |
56
+ | Checkbox | 交互 | default / indeterminate | sm / md | default / hover / focus / disabled / checked |
57
+ | Radio | 交互 | default | sm / md | default / focus / disabled / selected |
58
+ | Switch | 交互 | default | md | default / focus / disabled / on / off |
59
+ | Tag | 轻量 | default / success / warning / danger / info | sm / md | default / hover / closable¹ |
60
+ | Icon | 豁免 | — | sm / md / lg | — |
61
+ | Avatar | 轻量 | circle / square | sm / md / lg | default / fallback / loading |
62
+ | Tooltip | 轻量 | default | — | hidden / visible |
63
+ | Modal | 交互 | default / confirm / destructive | sm / md / lg | open / loading / error / closing |
64
+ | Drawer | 交互 | left / right / bottom | sm / md / lg | open / loading / error |
65
+ | Tabs | 交互 | line / pill | — | default / active / disabled / loading |
66
+ | Table | 交互 | default / compact / striped | — | default / loading / empty / error / selected |
67
+ | Form | 交互 | single / two-column | — | untouched / dirty / submitted-pending / error |
68
+ | Card | 交互 | default / interactive / stat | — | default / hover / selected / loading |
69
+ | Pagination | 交互 | default / simple | sm / md | default / disabled / loading |
70
+ | FilterBar | 交互 | default / collapsible | — | default / expanded / applied |
71
+ | EmptyState | 豁免 | default / filtered / error | — | — |
72
+ | Toast | 交互 | success / warning / error / info | — | entering / visible / exiting |
73
+
74
+ > **¹ 行为项标记**:`closable` 是行为非状态(Tag 可同时处于 default + hover + closable)——后缀 `¹` 告知 guard 不计入 states 下限。值项(on/off、selected、checked)同理按值处理,但**不加 ¹**(它们计入但由与值正交的状态满足下限)。
75
+ > **豁免组件**(Icon/EmptyState)不检查 states 与 variants。
76
+
45
77
  ### Token 四层模型(v0.18.0,参照 open-design token-schema)
46
78
 
47
79
  | 层 | 语义 | 完整性 |
@@ -51,7 +83,7 @@ Every `.team-flow/design-system/base.md` follows this schema (9 sections + a 5-d
51
83
  | **A2-derived** | 必选但有默认公式:`--accent-hover: color-mix(in oklab, var(--accent), black 8%)` / `--accent-active: ... black 14%` / `--focus-ring: ... accent transparent 70%` / `--elev-raised: ... fg transparent 92%` / `--success` `--warn` `--danger` / `--font-mono` / `--space-1~12` / `--radius-sm/md/lg/pill` / `--motion-fast/base` / `--ease-standard`(21 项) | 缺 = guard 失败 |
52
84
  | **B-slot** | 可选别名层:`--fg-2 → var(--fg)` / `--meta → var(--muted)` / `--border-soft → var(--border)` / `--surface-warm → var(--surface)`(4 项) | 组件引用永远可解析 |
53
85
 
54
- > **完整性约束**:每份 design-tokens.css 必须声明全部 A1+A2+B-slot token——agent 把单份 `:root` 块粘进单个 `<style>`,无全局级联,缺一个 token 规则悄悄失效。可运行 `node scripts/guard/design-token-guard.mjs` 校验。
86
+ > **完整性约束**:每份 design-tokens.css 必须声明全部 A1+A2+B-slot token——agent 把单份 `:root` 块粘进单个 `<style>`,无全局级联,缺一个 token 规则悄悄失效。可运行 `node ${CLAUDE_PLUGIN_ROOT}/scripts/guard/design-token-guard.mjs` 校验。
55
87
 
56
88
  ## Methodology
57
89
 
@@ -65,8 +97,11 @@ When the project has no `.team-flow/design-system/base.md`:
65
97
  - **A2 派生状态色**(v0.18.0):用 `color-mix(in oklab, ...)` 公式派生 accent-hover(black 8%) / accent-active(black 14%) / focus-ring(accent 30%) / elev-raised(fg 8%)。不手写固定 hex。
66
98
  - **B-slot 别名层**(v0.18.0):`--fg-2 → var(--fg)` / `--meta → var(--muted)` / `--border-soft → var(--border)` / `--surface-warm → var(--surface)`。
67
99
  - **C-extension 清单**(v0.18.0):如有品牌专有 token,列入 `extensions` 段(名单制),标注提升路径。
100
+ - **组件契约表(v0.54.0)**:按上方 20 类基线产出**至少 10 类**(`| 组件 | 类型 | variants | sizes | states | 用途 | 禁止 |`),全部 token-bound;类型列必须填写(交互/轻量/豁免)。
101
+ - **principles 段(v0.54.0)**:≥3 条,随 mood 预填(如 professional_minimal → "一致性优先于局部创意 / 清晰优于装饰 / 可访问性默认开启")。
102
+ - **governance 段(v0.54.0)**:`contract: v1`(新建固定 v1)+ version + 负责人 + 弃用策略 + changelog。
103
+ - **a11y 声明行(v0.54.0)**:文档头部 `> a11y: WCAG 2.2 AA(对比度 4.5:1 / 大字 3:1 / 焦点可见 / 键盘可达)`。
68
104
  - Fill color / typography / spacing / layout / motion / voice / brand / anti-patterns consistently with the palette.
69
- - `components` references the reusable components the prototype will need (button/input/card/table at minimum), all token-bound.
70
105
  - `anti-patterns` MUST include "禁止内联样式漂移" and "禁止非 token 颜色".
71
106
  3. **`confirmed: false`**:Deliver the draft(写 scratch 草稿路径或返回 response),**不写正式路径**。Orchestrator 评审 + 人工确认后,**带 `confirmed: true` 重新派发你**,由你写入正式 `.team-flow/design-system/base.md`(主代理不写)。
72
107
  4. **`confirmed: true`**:把传入的已确认草案 `Write` 到正式 `.team-flow/design-system/base.md`,然后执行 **预览生成步骤**,返回 `status: done` + deliverable = 正式路径。
@@ -84,7 +119,9 @@ When the project has no `.team-flow/design-system/base.md`:
84
119
  When prototype-sync (or a change) introduces new components / tokens / anti-patterns:
85
120
 
86
121
  1. Read the existing `.team-flow/design-system/base.md`.
87
- 2. **Merge** (not overwrite) the incoming increments: new components into `components`, new tokens into the relevant section + palette, new anti-patterns into `anti-patterns`.
122
+ 2. **Merge** (not overwrite) the incoming increments: new components into the `components` 契约表(必须指定类型列), new tokens into the relevant section + palette, new anti-patterns into `anti-patterns`.
123
+ - **contract 升级(v0.54.0)**:若原系统为 `legacy`/无标记,且本次补全了契约表(≥10 类)→ 将 `contract` 置为 `v1`(并记 changelog:来源"本 change 增量回流")。
124
+ - 变体文件的 components 段只写**端特有差异**并引用契约表(不重复定义组件清单)。
88
125
  3. Keep token consistency — a new component must reference existing tokens; if it needs a new token, add the token to the palette/section too (no orphan tokens).
89
126
  4. Append a **变更履历** entry: 时间 / 变更内容 / 来源 change-id.
90
127
  5. **`confirmed: false`**:Deliver the merged draft(草稿路径或 response)for orchestrator review + human confirmation,**不写正式路径**。
@@ -98,7 +135,7 @@ When prototype-sync (or a change) introduces new components / tokens / anti-patt
98
135
  ### Mode: {create | iterate}
99
136
  ### Target path: {.team-flow/design-system/base.md}
100
137
 
101
- ### design-system.md (draft content)
138
+ ### base.md (draft content)
102
139
  {完整 9 段 schema + palette + aliases + extensions 的 markdown 内容,含 A2 派生色公式和 B-slot 别名层,可直接落盘}
103
140
 
104
141
  ### 变更履历 (iterate only)