在AI IDE横行的今天,好像每个团队都在谈怎么写好 AI 规则。
笔者所在的小团队开发着一个创新项目,组内同学混用着 AI IDE(IDE 矩阵:Cursor + CodeBuddy + Windsurf + Trae + Qoder,每个新同学按自己喜好挑一个),沉淀了不少项目级 AI 上下文(TypeScript规范、React组件约定、MCP Server清单),随着项目成员的增加,问题就来了------
团队新人: 哥,我们项目用什么 AI 规则啊?
我: 写在
.cursorrules里......新人: 可是我用的是 CodeBuddy 啊。
我: ......那你 copy 一份到
.codebuddy/rules/?新人: 以后每次更新规则呢?
我: ......群里通知一下?
好家伙,这活儿干得比prettier还没排面。于是就有了 xingwu-ai-kit------一个让"一份配置同步到 5 个 IDE"的 CLI。
搞事1.0:先跑起来
笔者写项目有个臭毛病,喜欢先把最丑的版本跑起来再说,反正写完肯定要重写一版(笔者不喜欢重复以往的方案,喜欢用点新玩意儿)。
最简版本核心逻辑就三行------读配置、读模板、writeFileSync 写到 IDE 目录:
typescript
// 第一版核心逻辑:写文件
for (const ide of config.ide) {
for (const preset of config.rules.preset) {
const content = readFileSync(getRuleTemplatePath(preset), 'utf-8')
writeFileSync(resolve(cwd, adapters[ide].rulesDir, `${preset}.mdc`), content)
}
}
init 出来一份配置,sync 一下,CodeBuddy / Cursor 目录里就有了规则文件。
嗯,能用,先交差。
搞事1.1:symlink 让"改一处全生效"
团队第一次升级规范的时候,麻烦来了------
同事 A: 我看
common.mdc里没写"禁止无脑可选链",能加上吗?我: 改了。
同事 B: 我这边还是旧版啊?
我: 你跑一下
npx xingwu-ai-kit sync。同事 C: 我跑完之后 IDE 里的规则文件被覆盖了,我加的注释没了!
第一个问题是模板升级要全员重跑 sync ,第二个问题是writeFileSync会冲掉用户本地改动。
于是乎,把 writeFileSync 改成 symlinkSync:
typescript
// 第二版:symlink,让 IDE 目录指向模板
symlinkSync(getRuleTemplatePath(preset), resolve(rulesDir, `${preset}.mdc'))
跑通之后立刻香了:
- 改模板 → 升级
xingwu-ai-kit→ 所有 IDE 立刻生效,不用重跑 sync - 用户本地改
.cursorrules不再被冲,它就是项目专属文件
但好景不长,又冒出来两个新问题:
- symlink 嵌套 ------之前残留的旧 symlink 没清,会出现
common.mdc -> template.mdc -> 真实文件这种链中链 - 断链 ------
pnpm install重新拉node_modules之后所有 symlink 全指向空气
safeLink 处理第一个:
typescript
// packages/cli/src/core/linker.ts
if (stat.isSymbolicLink()) {
// 已有 symlink → 先删再创建,避免嵌套
unlinkSync(target)
} else {
// 真实文件 → 尊重用户,按 overrideStrategy 决定
if (overrideStrategy === 'skip') {
return { status: 'skipped', reason: '项目专属文件,跳过' }
} else if (overrideStrategy === 'override') {
unlinkSync(target)
}
}
doctor --fix 处理第二个:
bash
npx xingwu-ai-kit doctor --fix
doctor 会扫所有 IDE 目录里的断链,按需清掉,再报告修了几条:
text
━━━ Cursor ━━━
💔 [broken] symlink 指向不存在的路径
路径: .cursor/rules/common.mdc
🔧 修复中...
🗑️ 移除断链: common.mdc
✅ 已修复 1 个问题
嗯,能拖动、能折叠、能兜底,这回够用了吧。
搞事2.0:JSDoc 配置拯救 npx / 全局安装
某天同事抛了个问题:
同事: 我在自己笔记本上不想每个项目都装
xingwu-ai-kit,能不能全局装一个?我: 可以啊,
pnpm add -g xingwu-ai-kit就行。同事: 那我跑
init生成的xingwu-ai-kit.config.ts报错了,说找不到xingwu-ai-kit/config这个包。我: ......
看了一眼生成的配置,长这样:
typescript
// ❌ 第一版:依赖外部包,全局安装就 GG
import { defineConfig } from 'xingwu-ai-kit/config'
export default defineConfig({ ... })
init 把类型定义写成了 import,导致配置文件的运行时依赖 了 xingwu-ai-kit 这个包的存在。
项目里没装 → 报错。全局装 → 项目里还是没装 → 同样报错。npx 临时调用 → 更没装。
解决办法:放弃运行时类型,让 import 退化成"纯类型注释":
typescript
// ✅ 改版:JSDoc 类型注解,运行时零依赖
/** @type {import('xingwu-ai-kit/config').AIKitConfig} */
export default { ... }
注释里的 import('xingwu-ai-kit/config') 只在 TypeScript 类型检查 / 编辑器里生效 ,不会真的去 require 这个包。
所以无论项目内安装、全局安装、npx 临时使用,生成的配置文件都能直接跑。
defineConfig 仍然保留,给那些项目内安装了 xingwu-ai-kit 的同学留个选择:
typescript
import { defineConfig } from 'xingwu-ai-kit/config' // 需要项目内也装
export default defineConfig({ ... })
好吧好吧,这个改动不大,但解决了"零依赖使用 CLI"的关键一环。
搞事2.1:c12 + defu 的"数组拼接"暗坑
这一节是笔者踩过最"阴"的一个坑,写出来给大家提个醒。
xingwu-ai-kit 用了 c12 加载 xingwu-ai-kit.config.ts,又用 defu 合并默认值。第一版配置加载长这样:
typescript
import { loadConfig } from 'c12'
const DEFAULT_CONFIG = {
ide: ['codebuddy', 'cursor'],
rules: { preset: [], overrideStrategy: 'skip' },
// ...
}
const { config } = await loadConfig({
name: 'xingwu-ai-kit',
cwd,
defaults: DEFAULT_CONFIG, // ← c12 内部用 defu 合并
})
跑 sync --dry-run,输出把我看懵了------
text
━━━ CodeBuddy ━━━
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
━━━ Cursor ━━━
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
━━━ CodeBuddy ━━━ ← 怎么又来一遍?
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
━━━ Cursor ━━━ ← ???
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
✨ 同步完成!共 12 条规则、0 个技能
ide 数组被遍历了 4 次,common.mdc 被处理了 4 次------说好 2 个 IDE,怎么冒出 4 个?
查了一会儿 defu 文档,原因藏在它的默认合并策略里------defu 对数组字段的默认行为是"拼接"而不是"覆盖"。
typescript
// 用户配置
{ ide: ['codebuddy', 'cursor'] }
// 默认配置(也是 codebuddy, cursor)
{ ide: ['codebuddy', 'cursor'] }
// defu 默认合并
{ ide: ['codebuddy', 'cursor', 'codebuddy', 'cursor'] } // 长度变 4!
解决思路:放弃 c12 的 defaults 选项,自己控制合并逻辑 。用 createDefu 写一个自定义 merger,对数组字段采用"覆盖":
typescript
// packages/cli/src/core/config-loader.ts
import { createDefu } from 'defu'
// 自定义 defu merger:数组字段用更高优先级的 source 覆盖,不拼接
const defuOverrideArrays = createDefu((obj, key, value) => {
if (Array.isArray(value)) {
obj[key] = value
return true
}
return undefined
})
export async function loadAIKitConfig(cwd?: string) {
const { config } = await loadConfig<AIKitConfig>({
name: 'xingwu-ai-kit',
cwd: cwd || process.cwd(),
// 注意:这里故意不传 defaults
})
const userConfig = (config || {}) as AIKitConfig
return defuOverrideArrays(userConfig, DEFAULT_CONFIG) as AIKitConfig
}
改完跑 sync --dry-run:
text
━━━ CodeBuddy ━━━
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
━━━ Cursor ━━━
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
✨ 同步完成!共 6 条规则、0 个技能
嗯,6 条规则 2 个 IDE,正常了。
PS: 第一次看 defu 文档的时候没注意到这点,差点以为是自己 sync 逻辑写错了,浪费了一个晚上。给同样用 c12 / defu 的同学提个醒------数组字段的合并语义要自己把控。
搞事3.0:模板跟随 npm 包一起发布
最后一个坑,比想象中朴素。
templates/ 目录里有 5 条规则、4 个技能,最早放在 packages/cli/templates/ 下,vite build 默认会一起打包。
后来重构 monorepo,把 templates/ 移到了仓库根目录(CLI / 脚手架 / 文档脚本都要用),vite build 就不认它了 ------只打 packages/cli/src/ 下的东西。
跑出来的 dist/ 里没有 templates/,发布的 npm 包里也没有,用户装上之后 sync 一跑就报:
text
⚠️ 未找到内置规则: common
⚠️ 未找到内置规则: typescript
⚠️ 未找到内置规则: security
解决:写一个 30 行的 copy-templates.mjs,vite build 完事之后手动拷:
javascript
// packages/cli/scripts/copy-templates.mjs
const PKG_ROOT = resolve(__dirname, '..')
const MONOREPO_ROOT = resolve(PKG_ROOT, '../..')
const SRC = resolve(MONOREPO_ROOT, 'templates') // 仓库根 templates/
const DEST = resolve(PKG_ROOT, 'dist/templates') // 跟 dist 走
function copyDir(src, dest) {
mkdirSync(dest, { recursive: true })
for (const entry of readdirSync(src)) {
const srcPath = join(src, entry)
const destPath = join(dest, entry)
if (statSync(srcPath).isDirectory()) {
copyDir(srcPath, destPath)
} else {
copyFileSync(srcPath, destPath)
}
}
}
copyDir(SRC, DEST)
package.json 的 build 串起来:
json
{
"scripts": {
"build": "vite build && node scripts/copy-templates.mjs",
"dev": "vite build --watch & node scripts/copy-templates.mjs --watch"
}
}
dev 模式还支持 --watch,模板改了自动同步到 dist/。
为了让 getTemplatesDir() 在所有运行方式下都能找到模板,我列了 4 个候选路径,按优先级找第一个存在的:
typescript
// packages/cli/src/core/templates.ts
const candidates = [
resolve(hereDir, 'templates'), // ① dist 内(构建产物)
resolve(hereDir, '../../templates'), // ② node_modules/xingwu-ai-kit/templates
resolve(hereDir, '../../../templates'), // ③ 源码直跑
resolve(hereDir, '../../../../templates'), // ④ monorepo 根
]
实测 npm pack --dry-run 验证:13 个模板文件全部以 dist/templates/... 路径打进 tarball,发布到 npm 后 node_modules/xingwu-ai-kit/dist/templates/ 就是模板真实位置。
架构总览
上面三节搞事走完,整个链路就清晰了,整理成一张图:
数据流只有一条单向路径:用户配置 → 加载合并 → IDE 适配 → 规则/技能走 symlink、MCP 走 JSON 写入 → 5 个 IDE 同时落库 。改一处配置,跑一次 sync,5 个 IDE 自动对齐。
最终效果
经过上面这一轮搞事,最终的 xingwu-ai-kit.config.ts 长这样:
typescript
/** @type {import('xingwu-ai-kit/config').AIKitConfig} */
export default {
ide: ['codebuddy', 'cursor', 'windsurf', 'trae', 'qoder'],
rules: {
preset: ['common', 'typescript', 'security'],
overrideStrategy: 'skip',
},
skills: {
preset: ['code-review', 'figma2code', 'git-commit', 'refactor'],
},
mcp: {
servers: {
FramelinkFigmaMCP: {
type: 'stdio',
command: 'npx',
args: ['-y', 'figma-developer-mcp', '--stdio'],
env: { FIGMA_API_KEY: process.env.FIGMA_API_KEY || '' },
},
context7: {
type: 'http',
url: 'https://mcp.context7.com/mcp',
},
},
target: 'user',
onConflict: 'skip',
preserveUserServers: true,
},
}
跑 npx xingwu-ai-kit sync,5 个 IDE 同时落库,规则 / 技能 / MCP 一次到位:
text
开始同步...
━━━ CodeBuddy ━━━
📏 Rules:
✅ common.mdc
✅ typescript.mdc
✅ security.mdc
🧠 Skills:
✅ code-review
✅ figma2code
✅ git-commit
✅ refactor
━━━ Cursor / Windsurf / Trae / Qoder ... ━━━ (同上)
🔌 MCP Servers:
[CodeBuddy] 新增: FramelinkFigmaMCP, context7
[Cursor] 新增: FramelinkFigmaMCP, context7
...
✨ 同步完成!共 15 条规则、20 个技能
效果图:

同事: 这次升级规范怎么搞?
我: 改
xingwu-ai-kit.config.ts,提交到 Git,大家 pull 一下再跑一次sync。同事: 完事?
我: 完事。
同事: 好家伙,跟
prettier一样方便。我: 那可不。
小结
- AI 上下文基建这事,社区里没有"事实标准",有空白就有机会 ------
xingwu-ai-kit核心思路是"声明式配置 + 适配器 + symlink 同步"; safeLink的 symlink + 冲突策略、JSDoc配置的零依赖、createDefu自定义合并,是三个"用对了很香"的小技巧,希望给大家带来一点帮助;- 目前 5 个 IDE(
CodeBuddy/Cursor/Windsurf/Trae/Qoder)开箱即用,Claude Code/VSCode Copilot还在路上 ^_^。
xingwu-ai-kit 传送门,欢迎试用、提 issue、贡献规则模板。