1实现原理 · 为什么它能做到
该 skill 不是可执行程序,而是 html-anything 的「模板」:一个目录(SKILL.md + example.html[/example.md]),由 loader.ts 在运行时扫盘读取——新增模板=新增目录,不需要改 TypeScript。
Adding a template = adding a folder. No TS code change required; the API
frontmatter 是给选择器/API 的元数据(zh_name/en_name/emoji/description/category/scenario/aspect_hint/featured/recommended/tags),由 fmToMeta() 映射成 SkillMeta;frontmatter 之后的正文才是给模型的规格。
zhName: fm.zh_name ?? fm.name ?? id,
生成时该模板正文被当成 prompt body:/api/convert 用 assemblePrompt() 把「全局设计指令 + 本模板正文 + 用户内容」拼成一段 prompt,交给宿主的本地 agent CLI,HTML 由 stdout 流式捕获。
prompt = assemblePrompt({ body: skill.body, content, format });
模板正文之上还压着一段全局前置指令(SHARED_DESIGN_DIRECTIVES),把技术与内容纪律统一加在每个模板之前——示例 HTML 里普遍出现的 CDN/字体外链就源自这里(宿主壳注入,不是模板自身要求)。
通过 CDN 引入 Tailwind v3 Play (https://cdn.tailwindcss.com) 与所需的 Google Fonts。
产物以 stdout 文本返回:全局指令显式禁止 agent 动用文件系统,落盘由前端负责——所以模板目录既无脚本也无写盘能力。
**禁止使用 Write / Edit / MultiEdit / Bash / Create / 任何文件系统工具**。
example.html 是随包预渲染示例:/api/templates/<id>/preview 原样按 text/html 返回给 iframe(选择器内联预览),/api/templates/<id>/example 另把 example.md + example.html 一起返回。
return new Response(skill.exampleHtml, {
正文把「手绘线框」定成四件事:graph-paper 背景、多 tab 变体标签、scribbled 图表占位 + 斜线填充、黄色轻微旋转的 sticky note。
- Graph-paper 背景
graph-paper 背景由两组 repeating-linear-gradient 叠加实现(24px 网格),不用图片纹理。
repeating-linear-gradient(0deg, var(--grid) 0 1px, transparent 1px 24px),
「不要规规矩矩」被量化为大量微小旋转:标签 rotate(-2.2deg)、tab rotate(-0.4deg)、sticky note rotate(2.4deg)/(-3.2deg)、KPI 卡各带 tilt。
.sticky.sn1 { top: 20px; right: 30px; background: var(--note-yellow); transform: rotate(2.4deg); }
手写字体在示例里落地为 Caveat + Patrick Hand 组合(plus DM Serif Display / IBM Plex Mono),其中并未使用正文点名的 Architects Daughter。
--hand-bold: 'Caveat', 'Patrick Hand', cursive;
图表占位走内联 SVG「涂鸦」路径(.panel svg.scribble),符合全局指令「优先使用 CSS / SVG 内联绘制」。
.panel svg.scribble { width: 100%; height: 160px; display: block; }
CLI 侧另有一条关键词直配通道:skills-matcher.ts 把用户内容里的关键词直接映射到该模板 id,命中即给 confidence=命中数×2+3,达到阈值 3 就走本地规则、不再让模型挑模板。
[["原型", "prototype", "线框", "wireframe", "mockup", "低保真", "sketch", "手绘"], "prototype-web"],
CLI 兜底匹配用到的阈值常量:内容长度需超过 60 字符才启用 AI 摘要匹配;强信号阈值是 3。
const MIN_CONTENT_LENGTH_FOR_AI = 60;
宿主壳用 sandbox="allow-scripts allow-same-origin" 的 iframe 渲染示例/产物(允许脚本、允许同源)——这是产品壳取舍,写进风险,不参与本 skill 判级。
sandbox="allow-scripts allow-same-origin"
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | Google Fonts(Caveat / Patrick Hand / DM Serif Display / IBM Plex Mono) |
| network | Google Fonts 静态资源域(preconnect) |
4风险提醒 风险提醒:绿色 · 放心使用
- CLI 强信号通道不可达 — skills-matcher.ts 的 wireframe/sketch/手绘 关键词行指向 prototype-web;本模板只能靠 tags 兜底或被用户手选,自动路由时可能被 prototype-web 抢走(verdict.fits 已注明需手选)。
- 正文字体与示例不一致 — 正文点名 Caveat / Architects Daughter,示例用 Caveat + Patrick Hand;若用户按正文要求核对字体会发现差异。
- 低保真产出可能被误当交付稿 — 示例视觉完成度较高(有 KPI 卡、面板、页脚),容易被直接当成品发布,而与「探索稿」定位冲突。
- 外链字体 — 示例引用 fonts.googleapis.com / fonts.gstatic.com(另有产品壳注入的 Tailwind CDN);属产物/宿主行为,不计档。
5第二遍独立确认
- [ok] skill 目录文件清单 — find 得 SKILL.md + example.html,无 scripts/references。
- [ok] 无脚本执行 — grep -c "<script" example.html = 0;SKILL.md 无脚本指令。
- [ok] 外部端点仅字体 — example.html 仅 fonts.googleapis.com css2 与 fonts.gstatic.com preconnect;SKILL.md 正文无 URL。
- [discrepancy] 正文字体 vs 示例字体 — SKILL.md 写「字体: Caveat / Architects Daughter」,example.html 实际加载 Caveat + Patrick Hand(--hand: 'Patrick Hand', 'Caveat' / --hand-bold 反之),未出现 Architects Daughter。
- [discrepancy] CLI 强信号是否覆盖本模板 — cli/src/skills-matcher.ts 中 wireframe/线框/sketch/手绘 的关键词行映射到 "prototype-web"(第 35 行),全套 STRONG_SIGNALS 无 wireframe-sketch 行;本模板仅能通过 fallbackMatch(tags 命中 +3/条)或手选命中。
- [ok] graph-paper 与 sticky note 是否落地 — 两处 repeating-linear-gradient(…1px 24px) 构成网格;.sticky.sn1/sn2 用 --note-yellow/--note-pink 并带 rotate(2.4deg)/(-3.2deg)。
- [ok] 判级 green 自证 — 目录零脚本文件、零凭证、零写盘;正文无运行时接口,端点仅见示例 → green。
- [ok] official_desc 逐字 — meta.official_desc 取自 frontmatter description 原文「网格背景 + marker 笔触 + 多 tab + sticky note + scribble 图表」。
6结论
40e72150a36c51da…553ed98c28