跳到主要内容
AIDEV CATALOGNOTE

给 AI 编程加一套前端工程规范

一、遇到的问题

在只有全局 CLAUDE.md 的阶段,跨项目用 agent 改 Vue/React/Node 代码,会反复撞上同一批问题:

  • 每次新起一个项目,组件该拆到哪一层、请求逻辑放哪个目录,都要重新和 agent "对齐"一遍,不同项目给出的答案还不一样。
  • 旧项目里明明只想改一个 PAGE_SIZE 的取值,或者一个 CSS 的 padding,agent 却顺手把整个组件、整段样式按"最佳实践"重排了一遍,diff 里全是无关改动,review 的时候还得从一堆搬家的代码里找真正改了什么。
  • 命名风格在项目之间漂移,这个项目用 fetchUser,那个项目用 getUserData,agent 每次都是临场发挥,没有统一依据。
  • 想让 agent review 代码时,判断标准也不稳定:同样是请求逻辑糅在组件里,有的项目被要求抽出来,有的项目被放过,全凭当时的上下文和措辞。

同样的毛病反复出现了几次之后,就该琢磨了:这些问题到底该怎么治?


二、为什么不能直接塞进全局 CLAUDE.md

第一反应是往全局 CLAUDE.md 里加规则——反正都是给 agent 看的。但试了一下就发现不对劲。

CLAUDE.md 管的是跨项目、长期有效的工作方式:红线、沟通语气、任务分级、改动边界,这些不管写 Python 脚本还是写 React 组件都成立。而"组件该拆几层""命名用哪个动词""CSS 属性按什么顺序写",是和"用的是 Vue 还是 React、Node 还是 NestJS"强绑定的具体写法,跟前面那批规则根本不是一回事。硬塞进去,写 Python 脚本、改 Nginx 配置这类完全不相关的任务,也要背着这堆用不上的规则常驻在上下文里。

更麻烦的是新旧项目的诉求本来就互相矛盾:新项目想要一套统一默认,一开始就照着建;旧项目改一行代码时,最不想要的就是被"顺便统一规范"拖进整份文件重排。这两种诉求写在同一份文件、同一套规则里,很难两头兼顾——规则定得太死,旧项目寸步难行;定得太松,新项目又等于没有默认值。

而且体积也是个实际问题:CLAUDE.md 一旦开始塞技术栈细则,以后每加一条 Vue/React 的判断都要在一份已经很长的通用文件里找地方插,跟 git 安全、任务分级这些完全不相关的内容混在一起改,越改越乱。

所以干脆把这批规则单独拎出来,做成一个专门的 Skill——frontend-guide。这些 Skill 本身通过软链接集中维护,管理方式另见《集中管理skill的方案》。源码在 mine/frontend-guide


三、分层设计:全局规则管流程,Skill 管技术栈

拆出来之后,全局 CLAUDE.md 里就不用再写任何具体写法了,只留一句分工声明:

Vue、React、TypeScript、Node.js、NestJS 相关的实现、维护与代码审查使用 $frontend-guide,只读取当前任务真正相关的 reference

它只声明"遇到这类任务,去查这个 Skill",不重复定义任何细节。真正的规范内容——改动范围怎么界定、组件怎么组织、命名怎么起——全部下沉到 frontend-guide 自己的 SKILL.mdreferences/ 里维护。

这样一拆,两边就能各管各的、互不牵扯:全局规则改沟通语气或红线,不会影响任何一个技术栈 Skill;frontend-guide 升级 React Hooks 的写法,也不用碰全局文件。两份文件只靠一句引用声明耦合,而不是内容耦合。


四、SKILL.md 怎么组织:入口收敛 + 按需加载

拆出来之后又冒出一个新问题:frontend-guide 自己要是也写成一份从头到尾的大文档,跟原来堆在 CLAUDE.md 里没什么区别,无非是换了个文件名。

所以 SKILL.md 只干两件事:界定改动范围,以及告诉你"当前任务该读哪个文件"。具体的命名、组件、CSS、类型细则全部拆到 references/ 下各自独立的文件里(naming.mdvue.mdreact.mdnode.mdstyling.md 等),SKILL.md 里放一张索引表:

当前改动读取
新增或调整文件名、变量、函数、props/事件、类型、常量references/naming.md
新增或调整 CSS 规则、布局、token、样式作用域或动效references/styling.md
前端请求、数据映射、缓存、store 或异步状态references/state-and-data.md

只改一个常量值,不用碰任何 reference;新增一个 Vue 组件,只读 Vue、命名、代码组织三份,不会连带加载 Node/NestJS 规则或 UX 检查清单。这就是"新项目要统一默认、旧项目不该被小改动拖累"这两个诉求能同时成立的关键——规则分散在各个独立文件里,一次任务只激活用得到的那一小部分,不会互相绊住。

规则条目还分了三个等级:MUST 是本次改动必须满足的正确性和职责边界,没得商量;SHOULD 是默认应该遵守、但项目已有可靠写法时可以沿用旧模式;DEFAULT 是项目完全没有先例时才采用的具体写法。加了这层分级,"统一规范"和"不打扰旧项目"就不再是非此即彼——旧项目的既有结构默认被尊重,只有等级足够高、或者项目本来就没有先例,才会强制落到 Skill 给的默认写法上。

SKILL.md 开头还有一张"改动范围"表,按任务类型(仅改常量、新增能力、新增请求、明确要求重构、仅审查)分别定义执行边界,提前把"这次到底该动多大范围"讲清楚,免得规范本身被当成重构许可证乱用。


五、项目专属事实和通用规则分开放

写着写着又发现一个坑:有些判断是跨项目都成立的,比如"请求逻辑不该散落在组件里""命名不能有歧义",换到任何一个 Vue/React 项目都适用,可以放进 frontend-guide;但每个项目自己的技术选型、目录边界、历史遗留问题,是这一个仓库独有的事实,写进要在多个仓库间共享的 Skill 里只会互相污染——别的项目复用这份 Skill 的时候,平白多背了一堆跟自己无关的假设。

所以这部分单独抽成了一个模板:assets/project-instructions-section.md。它是一段可以直接贴进某个具体项目自己 CLAUDE.md 的片段,只记录这个项目用什么框架和版本、状态管理和请求方案选了什么、目录边界怎么划、哪些地方正在迁移或者已知要绕开。片段开头就写死一句"通用工程判断见 frontend-guide Skill",把分工挑明,不会出现同一条规则两边各写一份、改动时忘了同步另一边的情况。


六、效果

这套东西落地之后,前面那几个原本互相矛盾的诉求,居然都能同时满足了:新项目一开始就能按统一默认建起组件、请求层和目录结构;旧项目改一个 padding 值或者一个已有函数,不会因为文件恰好是 Vue/React 就被要求跑一遍类型检查、测试或者整体重排;每次任务只加载真正相关的 reference,上下文不会因为规范细节多而被拖垮;项目自己的技术选型和 Skill 的通用判断分头维护,Skill 升级不用改各个项目的 CLAUDE.md,项目的例外也不会反过来污染 Skill 本身。

$ 使用vue实现一个项目级todo list

即使没有用其他skill规划,从0开始搭建的项目也会有一套默认标准:


七、如何使用

引入skill

先调整 link_source,在终端执行下面的命令,可从任意目录运行。

(
link_source='/Users/debugger/bugcave/github/skills/mine/frontend-guide' #修改skill文件路径
codex_target="$HOME/.agents/skills/frontend-guide"
claude_target="$HOME/.claude/skills/frontend-guide"
codex_legacy="$HOME/.codex/skills/frontend-guide"
if [ -e "$codex_legacy" ] || [ -L "$codex_legacy" ]; then
if [ -e "$codex_target" ] || [ -L "$codex_target" ]; then
echo "Codex 已有两个同名入口,请先统一入口。"
exit 1
fi
codex_target="$codex_legacy"
fi
if [ ! -r "$link_source/SKILL.md" ]; then
echo "源文件不可读:$link_source"
exit 1
fi
conflict=0
for link_target in "$codex_target" "$claude_target"; do
if [ -e "$link_target" ] || [ -L "$link_target" ]; then
if [ ! -L "$link_target" ] || [ "$(readlink "$link_target")" != "$link_source" ]; then
echo "冲突:$link_target 已存在且不是预期软链接。"
conflict=1
fi
fi
done
[ "$conflict" -eq 0 ] || exit 1
for link_target in "$codex_target" "$claude_target"; do
mkdir -p "$(dirname "$link_target")" || exit 1
if [ ! -L "$link_target" ]; then
ln -s "$link_source" "$link_target" || exit 1
fi
if [ ! -L "$link_target" ] || [ "$(readlink "$link_target")" != "$link_source" ] ||
[ ! -r "$link_target/SKILL.md" ]; then
echo "验证失败:$link_target"
exit 1
fi
echo "已验证:$link_target -> $link_source"
done
)

申明使用

全局CLAUDE.mdAGENTS.md 每轮都进上下文,所以在里面放一句话,成本一行,效果是把"要不要读"从模型判断变成明文指令:

## 约定

Vue、React、TypeScript、Node.js、NestJS 相关的实现、维护与代码审查使用 $frontend-guide,只读取当前任务真正相关的 reference

skill的已知问题是欠触发:官方排查"技能未被使用"的第一条就是检查 description 是否足够具体、是否包含相关关键词。实际表现是——你说"帮我加个登录页",Claude 觉得这活自己就能干,不去读 skill,于是所有默认项、分层规则、收尾验证全跳过了。

日常用不需要手动调用:改动 Vue/React/Node 代码时,agent 会按 SKILL.md 的索引表自己找该读哪份 reference。要是想把某个项目的技术选型固化下来,就把 assets/project-instructions-section.md 的内容贴进该项目自己的 CLAUDE.md,填上真实的框架版本、目录边界和已经踩过的坑。