@bovinphang/frontend-craft 2.5.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 (180) hide show
  1. package/.claude-plugin/marketplace.json +61 -0
  2. package/.claude-plugin/plugin.json +32 -0
  3. package/.mcp.json +44 -0
  4. package/CHANGELOG.md +229 -0
  5. package/CHANGELOG.zh-CN.md +228 -0
  6. package/CODE_OF_CONDUCT.md +34 -0
  7. package/CODE_OF_CONDUCT.zh-CN.md +34 -0
  8. package/CONTRIBUTING.md +356 -0
  9. package/CONTRIBUTING.zh-CN.md +356 -0
  10. package/LICENSE +21 -0
  11. package/README.md +467 -0
  12. package/README.openclaw.md +262 -0
  13. package/README.openclaw.zh-CN.md +262 -0
  14. package/README.zh-CN.md +467 -0
  15. package/SECURITY.md +44 -0
  16. package/SECURITY.zh-CN.md +44 -0
  17. package/agents/fec-architect.md +125 -0
  18. package/agents/fec-code-reviewer.md +152 -0
  19. package/agents/fec-debugger.md +44 -0
  20. package/agents/fec-design-token-mapper.md +75 -0
  21. package/agents/fec-doc-updater.md +30 -0
  22. package/agents/fec-e2e-runner.md +107 -0
  23. package/agents/fec-figma-implementer.md +104 -0
  24. package/agents/fec-performance-optimizer.md +293 -0
  25. package/agents/fec-refactor-cleaner.md +32 -0
  26. package/agents/fec-security-reviewer.md +116 -0
  27. package/agents/fec-test-planner.md +50 -0
  28. package/agents/fec-typescript-reviewer.md +142 -0
  29. package/agents/fec-ui-checker.md +120 -0
  30. package/commands/fec-debug.md +30 -0
  31. package/commands/fec-doc-sync.md +18 -0
  32. package/commands/fec-init.md +70 -0
  33. package/commands/fec-plan.md +94 -0
  34. package/commands/fec-refactor-clean.md +19 -0
  35. package/commands/fec-review.md +66 -0
  36. package/commands/fec-scaffold.md +205 -0
  37. package/commands/fec-tdd.md +21 -0
  38. package/dist/bin/frontend-craft.js +82 -0
  39. package/dist/hooks/fec-cleanup-claude-cache.js +4 -0
  40. package/dist/hooks/fec-format-changed-file.js +1 -0
  41. package/dist/hooks/fec-notify.js +1 -0
  42. package/dist/hooks/fec-run-tests.js +1 -0
  43. package/dist/hooks/fec-security-check.js +2 -0
  44. package/dist/hooks/fec-session-start.js +1 -0
  45. package/docs/example-prompts.md +124 -0
  46. package/docs/ja-JP/README.md +467 -0
  47. package/docs/ko-KR/README.md +467 -0
  48. package/docs/project-structure.md +119 -0
  49. package/docs/runtimes/antigravity.md +10 -0
  50. package/docs/runtimes/augment.md +10 -0
  51. package/docs/runtimes/claude.md +171 -0
  52. package/docs/runtimes/claude.zh-CN.md +169 -0
  53. package/docs/runtimes/cline.md +10 -0
  54. package/docs/runtimes/codebuddy.md +10 -0
  55. package/docs/runtimes/codex.md +10 -0
  56. package/docs/runtimes/copilot.md +10 -0
  57. package/docs/runtimes/cursor.md +10 -0
  58. package/docs/runtimes/gemini.md +10 -0
  59. package/docs/runtimes/kilo.md +10 -0
  60. package/docs/runtimes/openclaw.md +10 -0
  61. package/docs/runtimes/opencode.md +10 -0
  62. package/docs/runtimes/qoder.md +19 -0
  63. package/docs/runtimes/trae.md +10 -0
  64. package/docs/runtimes/windsurf.md +10 -0
  65. package/docs/superpowers/plans/2026-05-29-fec-debug.md +491 -0
  66. package/docs/superpowers/specs/2026-05-29-fec-debug-design.md +340 -0
  67. package/docs/zh-CN/example-prompts.md +124 -0
  68. package/docs/zh-CN/project-structure.md +119 -0
  69. package/docs/zh-TW/README.md +467 -0
  70. package/hooks/hooks.json +71 -0
  71. package/openclaw.plugin.json +26 -0
  72. package/package.json +101 -0
  73. package/skills/TEMPLATE.md +155 -0
  74. package/skills/eval_queries.json +890 -0
  75. package/skills/fec-accessibility-check/SKILL.md +39 -0
  76. package/skills/fec-accessibility-check/references/report-template.md +23 -0
  77. package/skills/fec-accessibility-check/references/screen-reader-testing.md +33 -0
  78. package/skills/fec-api-integration/SKILL.md +78 -0
  79. package/skills/fec-api-integration/references/integration-patterns.md +80 -0
  80. package/skills/fec-browser-storage/SKILL.md +34 -0
  81. package/skills/fec-browser-storage/references/storage-patterns.md +65 -0
  82. package/skills/fec-canvas-threejs/SKILL.md +66 -0
  83. package/skills/fec-canvas-threejs/references/rendering-patterns.md +67 -0
  84. package/skills/fec-canvas-threejs/references/shader-webgl-patterns.md +67 -0
  85. package/skills/fec-code-review/SKILL.md +135 -0
  86. package/skills/fec-code-review/references/report-template.md +29 -0
  87. package/skills/fec-component-testing/SKILL.md +119 -0
  88. package/skills/fec-data-fetching/SKILL.md +62 -0
  89. package/skills/fec-data-fetching/references/query-patterns.md +77 -0
  90. package/skills/fec-debug-framework/SKILL.md +162 -0
  91. package/skills/fec-dependency-upgrade/SKILL.md +34 -0
  92. package/skills/fec-dependency-upgrade/references/dependency-upgrade-workflow.md +56 -0
  93. package/skills/fec-doc-sync/SKILL.md +37 -0
  94. package/skills/fec-e2e-testing/SKILL.md +40 -0
  95. package/skills/fec-e2e-testing/references/e2e-advanced.md +176 -0
  96. package/skills/fec-e2e-testing/references/playwright-patterns.md +95 -0
  97. package/skills/fec-form-handling/SKILL.md +76 -0
  98. package/skills/fec-form-handling/references/advanced-form-patterns.md +72 -0
  99. package/skills/fec-implement-from-design/SKILL.md +122 -0
  100. package/skills/fec-implement-from-design/references/design-plan-template.md +36 -0
  101. package/skills/fec-legacy-to-modern-migration/SKILL.md +180 -0
  102. package/skills/fec-legacy-to-modern-migration/references/migration-report-template.md +41 -0
  103. package/skills/fec-legacy-web-standard/SKILL.md +35 -0
  104. package/skills/fec-legacy-web-standard/references/legacy-patterns.md +85 -0
  105. package/skills/fec-list-virtualization/SKILL.md +34 -0
  106. package/skills/fec-list-virtualization/references/virtualization-patterns.md +52 -0
  107. package/skills/fec-monorepo-project-standard/SKILL.md +142 -0
  108. package/skills/fec-motion-interaction/SKILL.md +66 -0
  109. package/skills/fec-motion-interaction/references/motion-patterns.md +112 -0
  110. package/skills/fec-nextjs-project-standard/SKILL.md +117 -0
  111. package/skills/fec-nuxt-project-standard/SKILL.md +122 -0
  112. package/skills/fec-performance-optimization/SKILL.md +72 -0
  113. package/skills/fec-pwa-implementation/SKILL.md +36 -0
  114. package/skills/fec-pwa-implementation/references/manifest-and-icons.md +53 -0
  115. package/skills/fec-pwa-implementation/references/service-worker-workbox.md +68 -0
  116. package/skills/fec-react-project-standard/SKILL.md +83 -0
  117. package/skills/fec-react-project-standard/references/react-project-details.md +642 -0
  118. package/skills/fec-refactor-clean/SKILL.md +48 -0
  119. package/skills/fec-responsive-layout/SKILL.md +34 -0
  120. package/skills/fec-responsive-layout/references/responsive-layout-patterns.md +59 -0
  121. package/skills/fec-route-protection/SKILL.md +135 -0
  122. package/skills/fec-security-review/SKILL.md +38 -0
  123. package/skills/fec-security-review/references/report-template.md +27 -0
  124. package/skills/fec-security-review/references/security-checklist.md +64 -0
  125. package/skills/fec-source-driven-development/SKILL.md +61 -0
  126. package/skills/fec-state-management/SKILL.md +140 -0
  127. package/skills/fec-state-management/references/state-patterns.md +109 -0
  128. package/skills/fec-storybook-component-doc/SKILL.md +37 -0
  129. package/skills/fec-storybook-component-doc/references/story-patterns.md +118 -0
  130. package/skills/fec-svg-animation/SKILL.md +148 -0
  131. package/skills/fec-tailwind-design-system/SKILL.md +34 -0
  132. package/skills/fec-tailwind-design-system/references/tailwind-system-patterns.md +55 -0
  133. package/skills/fec-tdd-workflow/SKILL.md +52 -0
  134. package/skills/fec-testing-strategy/SKILL.md +45 -0
  135. package/skills/fec-typescript-type-safety/SKILL.md +157 -0
  136. package/skills/fec-typescript-type-safety/references/type-safety-patterns.md +102 -0
  137. package/skills/fec-ui-design/SKILL.md +90 -0
  138. package/skills/fec-ui-design/data/product-rules.json +110 -0
  139. package/skills/fec-ui-design/data/stack-ui-rules.json +14 -0
  140. package/skills/fec-ui-design/data/style-archetypes.json +74 -0
  141. package/skills/fec-ui-design/data/ux-quality-rules.json +56 -0
  142. package/skills/fec-ui-design/references/design-intelligence.md +37 -0
  143. package/skills/fec-ui-design/references/master-page-overrides.md +30 -0
  144. package/skills/fec-ui-design/references/pre-delivery-checklist.md +33 -0
  145. package/skills/fec-ui-design/scripts/design-system.mjs +530 -0
  146. package/skills/fec-validation-fix/SKILL.md +48 -0
  147. package/skills/fec-vite-project-standard/SKILL.md +83 -0
  148. package/skills/fec-vue3-project-standard/SKILL.md +49 -0
  149. package/skills/fec-vue3-project-standard/references/vue3-project-details.md +547 -0
  150. package/skills/fec-web-workers/SKILL.md +36 -0
  151. package/skills/fec-web-workers/references/advanced-workers.md +54 -0
  152. package/skills/fec-web-workers/references/basic-worker.md +67 -0
  153. package/skills/metadata.json +1422 -0
  154. package/skills/relations.json +644 -0
  155. package/templates/claude/CLAUDE.md +106 -0
  156. package/templates/claude/settings.json +62 -0
  157. package/templates/codex/AGENTS.md +105 -0
  158. package/templates/codex/config.toml +32 -0
  159. package/templates/openclaw/AGENTS.md +101 -0
  160. package/templates/openclaw/OPENCLAW-CONFIG.md +12 -0
  161. package/templates/shared/rules/fec-agent-workflow.md +54 -0
  162. package/templates/shared/rules/fec-api-layer.md +99 -0
  163. package/templates/shared/rules/fec-ci-cd.md +128 -0
  164. package/templates/shared/rules/fec-code-comments.md +33 -0
  165. package/templates/shared/rules/fec-design-system.md +248 -0
  166. package/templates/shared/rules/fec-error-handling.md +200 -0
  167. package/templates/shared/rules/fec-git-conventions.md +73 -0
  168. package/templates/shared/rules/fec-i18n.md +112 -0
  169. package/templates/shared/rules/fec-naming-conventions.md +120 -0
  170. package/templates/shared/rules/fec-performance.md +101 -0
  171. package/templates/shared/rules/fec-react.md +82 -0
  172. package/templates/shared/rules/fec-refactoring.md +43 -0
  173. package/templates/shared/rules/fec-rendering-patterns.md +183 -0
  174. package/templates/shared/rules/fec-responsive-design.md +227 -0
  175. package/templates/shared/rules/fec-source-driven-development.md +31 -0
  176. package/templates/shared/rules/fec-state-management.md +163 -0
  177. package/templates/shared/rules/fec-testing.md +98 -0
  178. package/templates/shared/rules/fec-typescript.md +355 -0
  179. package/templates/shared/rules/fec-vue.md +64 -0
  180. package/templates/shared/rules/fec-working-modes.md +50 -0
@@ -0,0 +1,530 @@
1
+ #!/usr/bin/env node
2
+ // @ts-check
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ /**
8
+ * @typedef {{
9
+ * id: string;
10
+ * label?: string;
11
+ * keywords: string[];
12
+ * pattern?: string;
13
+ * patterns?: string[];
14
+ * risks: string[];
15
+ * style?: string;
16
+ * space?: string;
17
+ * components?: string[];
18
+ * motion: string;
19
+ * antiPatterns: string[];
20
+ * }} ProductRule
21
+ *
22
+ * @typedef {{
23
+ * id: string;
24
+ * label?: string;
25
+ * keywords: string[];
26
+ * usage?: string;
27
+ * description?: string;
28
+ * risks?: string[];
29
+ * avoid?: string[];
30
+ * antiPatterns?: string[];
31
+ * tokens?: { density?: string };
32
+ * }} StyleArchetype
33
+ *
34
+ * @typedef {{
35
+ * id?: string;
36
+ * category?: string;
37
+ * label?: string;
38
+ * checks: string[];
39
+ * }} UxQualityRule
40
+ *
41
+ * @typedef {{
42
+ * stack: string;
43
+ * label?: string;
44
+ * keywords: string[];
45
+ * guidance: string[];
46
+ * }} StackRule
47
+ *
48
+ * @typedef {{
49
+ * queryParts: string[];
50
+ * query: string;
51
+ * format: "markdown" | "json";
52
+ * project: string;
53
+ * page: string;
54
+ * stack: string;
55
+ * persist: boolean;
56
+ * outputDir: string;
57
+ * help: boolean;
58
+ * }} CliArgs
59
+ *
60
+ * @typedef {{
61
+ * id: string;
62
+ * label: string;
63
+ * checks: string[];
64
+ * }} ChecklistGroup
65
+ *
66
+ * @typedef {{
67
+ * project: string;
68
+ * page: string;
69
+ * query: string;
70
+ * product: { id: string; label: string; pattern: string; risks: string[] };
71
+ * style: { id: string; label: string; usage: string | undefined; risks: string[] };
72
+ * tokens: { color: string; typography: string; space: string; components: string[] };
73
+ * motion: string;
74
+ * chart: string;
75
+ * antiPatterns: string[];
76
+ * checklist: ChecklistGroup[];
77
+ * stackGuidance: StackRule | null;
78
+ * }} Recommendation
79
+ */
80
+
81
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
82
+ const skillDir = path.resolve(__dirname, "..");
83
+ const dataDir = path.join(skillDir, "data");
84
+
85
+ const productRules = /** @type {ProductRule[]} */ (readJson("product-rules.json"));
86
+ const styleArchetypes = /** @type {StyleArchetype[]} */ (readJson("style-archetypes.json"));
87
+ const uxQualityRules = /** @type {UxQualityRule[]} */ (readJson("ux-quality-rules.json"));
88
+ const stackRules = /** @type {StackRule[]} */ (readJson("stack-ui-rules.json"));
89
+
90
+ /** @type {Record<string, string>} */
91
+ const colorDirections = {
92
+ saas: "Neutral surfaces with a restrained trust accent, semantic status colors, and one operational highlight.",
93
+ fintech: "Ink, graphite, and calm blue-green tokens with explicit success, risk, warning, and pending states.",
94
+ healthcare: "Clean neutrals with soft clinical blues or greens, high contrast text, and sparing reassurance accents.",
95
+ ecommerce: "Product-led neutrals, one conversion accent, clear price/status colors, and image-friendly backgrounds.",
96
+ creative: "Flexible neutrals with a bolder accent system, gallery-safe surfaces, and controlled expressive moments.",
97
+ wellness: "Warm off-white surfaces, botanical or mineral accents, calm confirmation colors, and low-glare contrast.",
98
+ education: "Readable neutrals, progress color coding, friendly accents, and clear feedback colors.",
99
+ gaming: "High-contrast surfaces, faction or mode accents, explicit danger/reward colors, and readable HUD tokens.",
100
+ "data-dashboard": "Dense neutrals, chart-safe categorical palettes, alert colors with severity order, and subdued chrome.",
101
+ };
102
+
103
+ /** @type {Record<string, string>} */
104
+ const typographyDirections = {
105
+ saas: "Use a workhorse sans for UI text, tabular numerals for metrics, and compact headings for scan speed.",
106
+ fintech: "Use a precise sans with tabular numerals, conservative weights, and generous numeric spacing.",
107
+ healthcare: "Use a highly legible sans, moderate line height, and calm heading contrast.",
108
+ ecommerce: "Use readable product text, stronger merchandising headings, and stable price typography.",
109
+ creative: "Pair a characterful display face with a reliable UI sans if the brand can support it.",
110
+ wellness: "Use a soft humanist sans or restrained serif accent, with relaxed line height and readable controls.",
111
+ education: "Use friendly readable type, clear hierarchy, and progress labels that survive small screens.",
112
+ gaming: "Use expressive display only for short labels; keep gameplay, settings, and HUD text fast to parse.",
113
+ "data-dashboard": "Use compact UI type, tabular numerals, and strict alignment for tables and charts.",
114
+ };
115
+
116
+ /** @type {Record<string, string>} */
117
+ const chartDirections = {
118
+ saas: "Prefer KPI trends, cohort funnels, and status breakdowns with clear comparison periods.",
119
+ fintech: "Use time-series, allocation, variance, and risk bands; disclose units and stale data states.",
120
+ healthcare: "Use trends and thresholds carefully, avoid alarmist color, and keep clinical context beside charts.",
121
+ ecommerce: "Use conversion funnels, product comparison, inventory status, and revenue trends.",
122
+ creative: "Use portfolio analytics sparingly; prioritize visual inspection before metrics.",
123
+ wellness: "Use booking, occupancy, habit, or progress views with gentle pacing and clear privacy boundaries.",
124
+ education: "Use progress, completion, mastery, and assignment timelines with learner-friendly labels.",
125
+ gaming: "Use leaderboards, loadouts, skill trees, and telemetry only when they support player decisions.",
126
+ "data-dashboard": "Choose chart type by task: trend, compare, compose, distribute, correlate, or inspect anomalies.",
127
+ };
128
+
129
+ /**
130
+ * @param {string} fileName
131
+ * @returns {unknown}
132
+ */
133
+ function readJson(fileName) {
134
+ return JSON.parse(fs.readFileSync(path.join(dataDir, fileName), "utf8"));
135
+ }
136
+
137
+ function main() {
138
+ const args = parseArgs(process.argv.slice(2));
139
+ if (args.help || !args.query) {
140
+ printUsage(args.help ? 0 : 1);
141
+ return;
142
+ }
143
+
144
+ const recommendation = buildRecommendation(args);
145
+ const output = args.format === "json" ? `${JSON.stringify(recommendation, null, 2)}\n` : renderMarkdown(recommendation);
146
+
147
+ if (args.persist) persistRecommendation(recommendation, args);
148
+
149
+ process.stdout.write(output);
150
+ }
151
+
152
+ /**
153
+ * @param {string[]} argv
154
+ * @returns {CliArgs}
155
+ */
156
+ function parseArgs(argv) {
157
+ /** @type {CliArgs} */
158
+ const result = {
159
+ queryParts: [],
160
+ query: "",
161
+ format: "markdown",
162
+ project: "",
163
+ page: "",
164
+ stack: "",
165
+ persist: false,
166
+ outputDir: process.cwd(),
167
+ help: false,
168
+ };
169
+
170
+ for (let index = 0; index < argv.length; index += 1) {
171
+ const token = argv[index];
172
+ if (token === "--help" || token === "-h") result.help = true;
173
+ else if (token === "--persist") result.persist = true;
174
+ else if (token === "--format") {
175
+ const format = requireValue(argv, ++index, "--format");
176
+ if (format !== "markdown" && format !== "json") {
177
+ throw new Error(`Unsupported --format "${format}". Use markdown or json.`);
178
+ }
179
+ result.format = format;
180
+ }
181
+ else if (token === "--project") result.project = requireValue(argv, ++index, "--project");
182
+ else if (token === "--page") result.page = requireValue(argv, ++index, "--page");
183
+ else if (token === "--stack") result.stack = requireValue(argv, ++index, "--stack");
184
+ else if (token === "--output-dir") result.outputDir = requireValue(argv, ++index, "--output-dir");
185
+ else result.queryParts.push(token);
186
+ }
187
+
188
+ result.query = result.queryParts.join(" ").trim();
189
+ return result;
190
+ }
191
+
192
+ /**
193
+ * @param {string[]} argv
194
+ * @param {number} index
195
+ * @param {string} flag
196
+ * @returns {string}
197
+ */
198
+ function requireValue(argv, index, flag) {
199
+ const value = argv[index];
200
+ if (!value || value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
201
+ return value;
202
+ }
203
+
204
+ /**
205
+ * @param {number} exitCode
206
+ * @returns {void}
207
+ */
208
+ function printUsage(exitCode) {
209
+ const usage = [
210
+ 'Usage: node skills/fec-ui-design/scripts/design-system.mjs "<query>" [options]',
211
+ "",
212
+ "Options:",
213
+ " --format markdown|json",
214
+ " --project <name>",
215
+ " --page <name>",
216
+ " --stack <stack>",
217
+ " --persist --output-dir <dir>",
218
+ "",
219
+ ].join("\n");
220
+ process.stderr.write(usage);
221
+ process.exitCode = exitCode;
222
+ }
223
+
224
+ /**
225
+ * @param {CliArgs} args
226
+ * @returns {Recommendation}
227
+ */
228
+ function buildRecommendation(args) {
229
+ const query = args.query;
230
+ const tokens = tokenize([query, args.project, args.page, args.stack].filter(Boolean).join(" "));
231
+ const product = chooseProduct(tokens);
232
+ const style = chooseStyle(tokens, product);
233
+ const checklist = chooseChecklist(tokens, product);
234
+ const stackGuidance = chooseStack(args.stack, tokens);
235
+ const pattern = product.pattern ?? product.patterns?.[0] ?? "Task-first interface with explicit states and durable visual hierarchy";
236
+
237
+ return {
238
+ project: args.project || titleCase(query),
239
+ page: args.page || "",
240
+ query,
241
+ product: {
242
+ id: product.id,
243
+ label: product.label ?? labelFromId(product.id),
244
+ pattern,
245
+ risks: product.risks,
246
+ },
247
+ style: {
248
+ id: style.id,
249
+ label: style.label ?? labelFromId(style.id),
250
+ usage: style.usage ?? style.description,
251
+ risks: style.risks ?? style.avoid ?? [],
252
+ },
253
+ tokens: {
254
+ color: colorDirections[product.id] ?? colorDirections.saas,
255
+ typography: typographyDirections[product.id] ?? typographyDirections.saas,
256
+ space: product.space ?? style.tokens?.density ?? "Match task density to user scan depth and touch requirements.",
257
+ components: product.components ?? ["Use existing components before adding new primitives", "Tokenize repeated spacing, color, radius, and state choices"],
258
+ },
259
+ motion: product.motion,
260
+ chart: chartDirections[product.id] ?? chartDirections["data-dashboard"],
261
+ antiPatterns: unique([...product.antiPatterns, ...(style.antiPatterns ?? style.avoid ?? [])]).slice(0, 8),
262
+ checklist,
263
+ stackGuidance,
264
+ };
265
+ }
266
+
267
+ /**
268
+ * @param {Set<string>} tokens
269
+ * @returns {ProductRule}
270
+ */
271
+ function chooseProduct(tokens) {
272
+ let best = productRules[0];
273
+ let bestScore = -1;
274
+
275
+ for (const product of productRules) {
276
+ const keywordScore = score(tokens, [...product.keywords, product.id, product.label ?? ""]);
277
+ const intentScore = tokens.has(slug(product.pattern)) ? 1 : 0;
278
+ const total = keywordScore + intentScore;
279
+ if (total > bestScore) {
280
+ best = product;
281
+ bestScore = total;
282
+ }
283
+ }
284
+
285
+ return best;
286
+ }
287
+
288
+ /**
289
+ * @param {Set<string>} tokens
290
+ * @param {ProductRule} product
291
+ * @returns {StyleArchetype}
292
+ */
293
+ function chooseStyle(tokens, product) {
294
+ const preferred = styleArchetypes.find((style) => style.id === product.style);
295
+ let best = preferred ?? styleArchetypes[0];
296
+ let bestScore = preferred ? 1 : 0;
297
+
298
+ for (const style of styleArchetypes) {
299
+ const total = score(tokens, [style.id, style.label, ...style.keywords]);
300
+ if (total > bestScore) {
301
+ best = style;
302
+ bestScore = total;
303
+ }
304
+ }
305
+
306
+ return best;
307
+ }
308
+
309
+ /**
310
+ * @param {Set<string>} tokens
311
+ * @param {ProductRule} product
312
+ * @returns {ChecklistGroup[]}
313
+ */
314
+ function chooseChecklist(tokens, product) {
315
+ const priority = new Set(["accessibility", "interaction", "layout", "typography", "animation"]);
316
+ if (product.id === "data-dashboard" || tokens.has("dashboard") || tokens.has("chart")) priority.add("charts");
317
+ if (tokens.has("form") || tokens.has("booking") || tokens.has("checkout")) priority.add("forms");
318
+ if (tokens.has("mobile") || tokens.has("app")) priority.add("navigation");
319
+ if (tokens.has("performance") || tokens.has("canvas") || tokens.has("three")) priority.add("performance");
320
+
321
+ return uxQualityRules
322
+ .filter((rule) => {
323
+ const id = rule.id ?? rule.category;
324
+ return id ? priority.has(id) : false;
325
+ })
326
+ .map((rule) => {
327
+ const id = rule.id ?? rule.category ?? "general";
328
+ return {
329
+ id,
330
+ label: rule.label ?? labelFromId(id),
331
+ checks: rule.checks.slice(0, 4),
332
+ };
333
+ });
334
+ }
335
+
336
+ /**
337
+ * @param {string} stack
338
+ * @param {Set<string>} tokens
339
+ * @returns {StackRule | null}
340
+ */
341
+ function chooseStack(stack, tokens) {
342
+ const desired = normalizeStack(stack);
343
+ const direct = desired
344
+ ? stackRules.find((rule) => normalizeStack(rule.stack) === desired || rule.keywords.some((keyword) => normalizeStack(keyword) === desired))
345
+ : undefined;
346
+ if (direct) return direct;
347
+
348
+ for (const rule of stackRules) {
349
+ if ([rule.stack, ...rule.keywords].some((alias) => {
350
+ const normalized = slug(alias);
351
+ if (normalized === "spa" || normalized.length < 3) return false;
352
+ return tokens.has(normalized) || tokens.has(String(alias).toLowerCase());
353
+ })) return rule;
354
+ }
355
+
356
+ return null;
357
+ }
358
+
359
+ /**
360
+ * @param {Recommendation} model
361
+ * @returns {string}
362
+ */
363
+ function renderMarkdown(model) {
364
+ const lines = [
365
+ `# Design System Recommendation: ${model.project}`,
366
+ "",
367
+ `- Product category: ${model.product.label} (${model.product.id})`,
368
+ `- Recommended pattern: ${model.product.pattern}`,
369
+ `- Style archetype: ${model.style.label} (${model.style.id})`,
370
+ "",
371
+ "## Token Direction",
372
+ "",
373
+ `- Color: ${model.tokens.color}`,
374
+ `- Typography: ${model.tokens.typography}`,
375
+ `- Space: ${model.tokens.space}`,
376
+ `- Components: ${model.tokens.components.join("; ")}`,
377
+ "",
378
+ "## Motion Guidance",
379
+ "",
380
+ model.motion,
381
+ "",
382
+ "## Chart Guidance",
383
+ "",
384
+ model.chart,
385
+ "",
386
+ "## Anti-Patterns",
387
+ "",
388
+ ...model.antiPatterns.map((item) => `- ${item}`),
389
+ "",
390
+ "## Pre-Delivery Checklist",
391
+ "",
392
+ ...model.checklist.flatMap((group) => [`### ${group.label}`, "", ...group.checks.map((check) => `- ${check}`), ""]),
393
+ ];
394
+
395
+ if (model.stackGuidance) {
396
+ lines.push("## Stack UI Guidance", "");
397
+ lines.push(`- Stack: ${model.stackGuidance.label ?? labelFromId(model.stackGuidance.stack)} (${model.stackGuidance.stack})`);
398
+ lines.push(...model.stackGuidance.guidance.map((item) => `- ${item}`), "");
399
+ }
400
+
401
+ return `${lines.join("\n")}\n`;
402
+ }
403
+
404
+ /**
405
+ * @param {Recommendation} model
406
+ * @returns {string}
407
+ */
408
+ function renderPageOverride(model) {
409
+ const pageName = model.page || "page";
410
+ return [
411
+ `# Page Override: ${pageName}`,
412
+ "",
413
+ `Master: ../MASTER.md`,
414
+ `Product category: ${model.product.label} (${model.product.id})`,
415
+ "",
416
+ "## Page Intent",
417
+ "",
418
+ `Use the Master direction, then adapt layout and component density for ${pageName}. Keep overrides narrow and delete any rule that merely repeats the Master.`,
419
+ "",
420
+ "## Override Strategy",
421
+ "",
422
+ `- Primary pattern: ${model.product.pattern}`,
423
+ `- Visual emphasis: ${model.style.usage}`,
424
+ `- Chart behavior: ${model.chart}`,
425
+ "",
426
+ "## State QA",
427
+ "",
428
+ "- Loading, empty, error, disabled, hover, focus, selected, and reduced-motion states are visible.",
429
+ "- Mobile, tablet, and desktop layouts preserve task order and readable tap targets.",
430
+ "",
431
+ ].join("\n");
432
+ }
433
+
434
+ /**
435
+ * @param {Recommendation} model
436
+ * @param {CliArgs} args
437
+ * @returns {void}
438
+ */
439
+ function persistRecommendation(model, args) {
440
+ const projectSlug = slug(args.project || model.project || "project");
441
+ const projectDir = path.join(path.resolve(args.outputDir), "design-system", projectSlug);
442
+ fs.mkdirSync(projectDir, { recursive: true });
443
+ fs.writeFileSync(path.join(projectDir, "MASTER.md"), renderMarkdown(model), "utf8");
444
+
445
+ if (args.page) {
446
+ const pagesDir = path.join(projectDir, "pages");
447
+ fs.mkdirSync(pagesDir, { recursive: true });
448
+ fs.writeFileSync(path.join(pagesDir, `${slug(args.page)}.md`), renderPageOverride(model), "utf8");
449
+ }
450
+ }
451
+
452
+ /**
453
+ * @param {string} value
454
+ * @returns {Set<string>}
455
+ */
456
+ function tokenize(value) {
457
+ return new Set(
458
+ value
459
+ .toLowerCase()
460
+ .split(/[^a-z0-9]+/g)
461
+ .filter(Boolean),
462
+ );
463
+ }
464
+
465
+ /**
466
+ * @param {Set<string>} tokens
467
+ * @param {Array<string | undefined>} keywords
468
+ * @returns {number}
469
+ */
470
+ function score(tokens, keywords) {
471
+ return keywords.reduce((total, keyword) => total + (tokens.has(slug(keyword)) || tokens.has(String(keyword).toLowerCase()) ? 1 : 0), 0);
472
+ }
473
+
474
+ /**
475
+ * @param {unknown} value
476
+ * @returns {string}
477
+ */
478
+ function slug(value) {
479
+ return String(value)
480
+ .trim()
481
+ .toLowerCase()
482
+ .replace(/[^a-z0-9]+/g, "-")
483
+ .replace(/^-+|-+$/g, "");
484
+ }
485
+
486
+ /**
487
+ * @param {string} value
488
+ * @returns {string}
489
+ */
490
+ function normalizeStack(value) {
491
+ return value ? slug(value).replace(/-?js$/, "") : "";
492
+ }
493
+
494
+ /**
495
+ * @param {string} value
496
+ * @returns {string}
497
+ */
498
+ function titleCase(value) {
499
+ return String(value)
500
+ .split(/\s+/g)
501
+ .filter(Boolean)
502
+ .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
503
+ .join(" ");
504
+ }
505
+
506
+ /**
507
+ * @param {Array<string | undefined | null | false>} values
508
+ * @returns {string[]}
509
+ */
510
+ function unique(values) {
511
+ /** @type {string[]} */
512
+ const strings = [];
513
+ for (const value of values) {
514
+ if (typeof value === "string" && value.length > 0) strings.push(value);
515
+ }
516
+ return [...new Set(strings)];
517
+ }
518
+
519
+ /**
520
+ * @param {unknown} value
521
+ * @returns {string}
522
+ */
523
+ function labelFromId(value) {
524
+ return String(value)
525
+ .split("-")
526
+ .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
527
+ .join(" ");
528
+ }
529
+
530
+ main();
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: fec-validation-fix
3
+ description: Use when running existing project validation commands and fixing failures after code changes, including lint, type-check, unit/integration test, build, CI, or local script failures. Do not use when the task is to design or author new component/E2E tests; Chinese triggers include 验证, 检查失败, 修复失败, CI 失败.
4
+ ---
5
+
6
+ # 验证并修复
7
+
8
+ ## Purpose
9
+
10
+ 自动化执行已有验证命令,分析失败原因并安全修复,确保质量门禁达标。
11
+
12
+ ## Procedure
13
+
14
+ 1. 从 package.json 或仓库文档中识别可用命令。
15
+ 2. 优先按以下顺序执行:
16
+ - lint
17
+ - type-check
18
+ - unit/integration tests
19
+ - build
20
+ 3. 仔细阅读失败输出。
21
+ 4. 将错误按文件、错误类型和依赖关系分组,优先处理阻塞后续诊断的根因。
22
+ 5. 一次只修复一类问题,并显示或记录关键错误上下文。
23
+ 6. 每次关键修复后重新执行受影响命令,确认错误是否减少。
24
+ 7. 如果同一错误连续三次修复仍失败,停止扩大改动并报告阻塞。
25
+ 8. 最后按 `Expected Output` 总结执行结果、修复内容和剩余风险。
26
+
27
+ ## Failure Triage
28
+
29
+ - 先修最早出现且会级联的错误,例如缺失类型、导入失败、配置解析失败。
30
+ - 区分真实产品 bug、测试环境问题、快照漂移、依赖版本变化和 flaky。
31
+ - 对 CI 专属失败记录 Node 版本、包管理器、环境变量、路径大小写和工作目录差异。
32
+ - 修复测试时优先修产品代码或测试等待方式,不用删除断言掩盖真实回归。
33
+
34
+ ## 强约束
35
+
36
+ - 不要盲目关闭规则来消除报错
37
+ - 除非有明确理由,不要为了通过检查而降低类型安全
38
+ - 不要因为附近测试失败就顺手重写无关模块
39
+ - 不要一次性修改多个无关根因
40
+ - 不要删除失败测试来让验证通过
41
+ - 不要只运行失败命令的片段就声称整体验证通过
42
+
43
+ ## Expected Output
44
+
45
+ - lint / type-check / test / build 等验证命令全部通过
46
+ - 失败项已修复或给出明确原因和后续行动
47
+ - 修复报告保存为 `reports/validation-fix-YYYY-MM-DD-HHmmss.md`
48
+ - 报告包含命令状态表、问题根因、修复说明、变更文件和剩余风险
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: fec-vite-project-standard
3
+ description: Use when creating, configuring, reviewing, or debugging Vite-based frontend projects, vite.config.ts, env variables, dev server proxy, HMR, plugin ordering, library mode, dependency pre-bundling, bundle splitting, or Vite build performance. Use framework-specific skills for React/Vue component architecture; Chinese triggers include Vite 配置, vite.config, Vite 构建, Vite 性能.
4
+ ---
5
+
6
+ # Vite 项目规范
7
+
8
+ 适用于 Vite 应用、组件库、开发服务器和构建配置的设计与审查。
9
+
10
+ ## Purpose
11
+
12
+ 规范 Vite 配置、安全边界和构建性能,避免开发环境与生产构建的常见陷阱。
13
+
14
+ ## Procedure
15
+
16
+ 1. 识别项目类型
17
+ - 应用项目优先检查 `root`、`base`、`server.proxy`、`envPrefix`、`build` 和框架插件。
18
+ - 组件库项目优先检查 `build.lib`、类型产物、peer dependencies external 和导出入口。
19
+ - Monorepo 额外检查 `server.fs.allow`、路径别名和包依赖边界。
20
+
21
+ 2. 选择插件
22
+ - React 默认优先 `@vitejs/plugin-react-swc`;需要 Babel 插件时才用 `@vitejs/plugin-react`。
23
+ - Vue 项目使用 `@vitejs/plugin-vue`,路径别名优先交给 `vite-tsconfig-paths`。
24
+ - TypeScript 项目必须通过 `tsc --noEmit`、CI 或 `vite-plugin-checker` 补齐类型检查。
25
+
26
+ 3. 管理环境变量
27
+ - 客户端只读取明确公开的前缀,如 `VITE_`。
28
+ - `loadEnv` 第三个参数使用显式前缀数组,不使用空字符串加载全部变量。
29
+ - 密钥、数据库地址、私有 token 必须留在服务端或构建系统,不进入客户端 bundle。
30
+
31
+ 4. 优化开发体验
32
+ - 慢启动先用 `vite --profile` 定位插件、解析或预构建瓶颈。
33
+ - 对 CJS/UMD 依赖用 `optimizeDeps.include` 处理互操作问题。
34
+ - 大项目可用 `server.warmup.clientFiles` 预热核心入口。
35
+
36
+ 5. 优化生产构建
37
+ - 发布前运行 `vite build`,并用 `vite preview` 做本地构建烟测。
38
+ - 手动分包使用稳定的对象形式优先,避免把每个依赖拆成独立小 chunk。
39
+ - 生产 sourcemap 默认关闭;若上传错误追踪平台,上传后不随站点公开。
40
+ - 动效、3D、图表、编辑器、地图和音视频处理库优先通过动态导入或路由级拆包隔离。
41
+ - 检查静态资源是否进入正确目录、是否被 hash/cache、是否存在占位 URL 或过大的未压缩媒体。
42
+
43
+ ## Minimal Config
44
+
45
+ ```ts
46
+ import { defineConfig, loadEnv } from "vite";
47
+ import react from "@vitejs/plugin-react-swc";
48
+ import tsconfigPaths from "vite-tsconfig-paths";
49
+
50
+ export default defineConfig(({ mode }) => {
51
+ const env = loadEnv(mode, process.cwd(), ["VITE_"]);
52
+
53
+ return {
54
+ plugins: [react(), tsconfigPaths()],
55
+ define: {
56
+ __PUBLIC_API_URL__: JSON.stringify(env.VITE_API_URL),
57
+ },
58
+ server: {
59
+ proxy: {
60
+ "/api": {
61
+ target: "http://localhost:8080",
62
+ changeOrigin: true,
63
+ },
64
+ },
65
+ },
66
+ };
67
+ });
68
+ ```
69
+
70
+ ## Constraints
71
+
72
+ - `vite build` 只转译和打包,不等于类型检查;CI 必须另跑 typecheck。
73
+ - `VITE_` 不是安全边界;任何 `VITE_` 变量都会进入客户端包。
74
+ - 不设置 `envPrefix: ""`,不使用 `loadEnv(mode, root, "")` 后再随意 `define`。
75
+ - 不默认引入 legacy 插件;只有真实用户数据需要时再启用。
76
+ - 不用 `vite preview` 作为生产服务器。
77
+ - 组件库必须 externalize peer dependencies,并单独产出 `.d.ts`。
78
+ - 不把服务端密钥通过 `define`、`import.meta.env` 或空前缀 `loadEnv` 注入客户端。
79
+ - 不把 GSAP、Three.js、Lottie、Monaco、地图 SDK 等重型库放进所有页面共享入口;先评估 async chunk 和首屏影响。
80
+
81
+ ## Expected Output
82
+
83
+ 输出应包含项目类型判断、关键配置变更、环境变量安全说明、构建/开发性能验证方式。完成后 Vite 配置应可类型检查、可构建、密钥不泄露,并能解释 dev 与 build 的差异。