ai-engineering-init 1.2.9 → 1.3.1
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.
- package/.claude/settings.json +1 -7
- package/README.md +31 -5
- package/bin/index.js +50 -10
- package/package.json +1 -1
package/.claude/settings.json
CHANGED
package/README.md
CHANGED
|
@@ -49,12 +49,18 @@ cd ai-engineering-init
|
|
|
49
49
|
| OpenAI Codex | `--tool codex` | `.codex/` 目录 + `AGENTS.md` |
|
|
50
50
|
| 全部 | `--tool all` | 以上全部 |
|
|
51
51
|
|
|
52
|
-
##
|
|
52
|
+
## 命令与选项
|
|
53
53
|
|
|
54
|
-
```
|
|
54
|
+
```bash
|
|
55
|
+
# 命令
|
|
56
|
+
npx ai-engineering-init # 交互式初始化(安装到当前项目)
|
|
57
|
+
npx ai-engineering-init update # 更新已安装的框架文件
|
|
58
|
+
npx ai-engineering-init global # 全局安装到 ~/.claude 等,对所有项目生效
|
|
59
|
+
|
|
60
|
+
# 选项
|
|
55
61
|
--tool, -t <工具> 指定工具: claude | cursor | codex | all
|
|
56
|
-
--dir, -d <目录>
|
|
57
|
-
--force,-f
|
|
62
|
+
--dir, -d <目录> 目标目录(默认:当前目录,仅 init/update 有效)
|
|
63
|
+
--force,-f 强制覆盖(init 覆盖已有文件;update/global 同时更新保留文件)
|
|
58
64
|
--help, -h 显示帮助
|
|
59
65
|
```
|
|
60
66
|
|
|
@@ -237,11 +243,31 @@ npx ai-engineering-init@latest update --force
|
|
|
237
243
|
|
|
238
244
|
> **注意**:不加 `@latest` 会使用 npx 本地缓存的旧版本,源文件不是最新的。
|
|
239
245
|
|
|
246
|
+
## 全局安装(v1.3.0+)
|
|
247
|
+
|
|
248
|
+
一次安装,所有项目自动生效:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# 全局安装所有工具(~/.claude + ~/.cursor + ~/.codex)
|
|
252
|
+
npx ai-engineering-init@latest global
|
|
253
|
+
|
|
254
|
+
# 只全局安装指定工具
|
|
255
|
+
npx ai-engineering-init@latest global --tool claude
|
|
256
|
+
npx ai-engineering-init@latest global --tool cursor
|
|
257
|
+
|
|
258
|
+
# 强制覆盖已有全局文件
|
|
259
|
+
npx ai-engineering-init@latest global --force
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
> **说明**:全局安装将 Skills/Commands/Hooks 安装到 `~/.claude` / `~/.cursor` / `~/.codex`。`settings.json`、`mcp.json` 采用合并策略,不覆盖用户已有配置。项目级配置优先级高于全局配置,两者可共存。
|
|
263
|
+
|
|
240
264
|
## 更新日志
|
|
241
265
|
|
|
242
266
|
查看完整更新历史:[CHANGELOG.md](./CHANGELOG.md)
|
|
243
267
|
|
|
244
|
-
**v1.
|
|
268
|
+
**v1.3.0 新增**:`global` 命令,支持系统级全局安装,一次安装对所有项目生效。
|
|
269
|
+
**v1.2.7 修复**:Cursor `beforeSubmitPrompt` hook 输出格式改为 JSON,修复技能注入失效。
|
|
270
|
+
**v1.2.6 修复**:Cursor stop hook 自包含,不再依赖 Claude Code 安装。
|
|
245
271
|
|
|
246
272
|
## License
|
|
247
273
|
|
package/bin/index.js
CHANGED
|
@@ -190,16 +190,15 @@ const GLOBAL_RULES = {
|
|
|
190
190
|
label: 'Claude Code',
|
|
191
191
|
targetDir: path.join(HOME_DIR, '.claude'),
|
|
192
192
|
files: [
|
|
193
|
-
{ src: '.claude/skills', dest: 'skills', label: 'Skills(全局技能库)',
|
|
194
|
-
{ src: '.claude/commands', dest: 'commands', label: 'Commands(全局命令)',
|
|
195
|
-
{ src: '.claude/agents', dest: 'agents', label: 'Agents(全局子代理)',
|
|
196
|
-
{ src: '.claude/hooks', dest: 'hooks', label: 'Hooks(全局钩子)',
|
|
193
|
+
{ src: '.claude/skills', dest: 'skills', label: 'Skills(全局技能库)', isDir: true },
|
|
194
|
+
{ src: '.claude/commands', dest: 'commands', label: 'Commands(全局命令)', isDir: true },
|
|
195
|
+
{ src: '.claude/agents', dest: 'agents', label: 'Agents(全局子代理)', isDir: true },
|
|
196
|
+
{ src: '.claude/hooks', dest: 'hooks', label: 'Hooks(全局钩子)', isDir: true },
|
|
197
197
|
{ src: '.claude/framework-config.json', dest: 'framework-config.json', label: 'framework-config.json' },
|
|
198
|
+
{ src: '.claude/settings.json', dest: 'settings.json', label: 'settings.json(Hooks + MCP 配置)', merge: true, rewritePrefix: '.claude/' },
|
|
198
199
|
],
|
|
199
|
-
preserve: [
|
|
200
|
-
|
|
201
|
-
],
|
|
202
|
-
note: `Skills/Commands/Hooks 已安装到 ~/.claude,对所有项目自动生效`,
|
|
200
|
+
preserve: [],
|
|
201
|
+
note: `Skills/Commands/Hooks/Settings 已安装到 ~/.claude,对所有项目自动生效`,
|
|
203
202
|
},
|
|
204
203
|
cursor: {
|
|
205
204
|
label: 'Cursor',
|
|
@@ -208,7 +207,7 @@ const GLOBAL_RULES = {
|
|
|
208
207
|
{ src: '.cursor/skills', dest: 'skills', label: 'Skills(全局技能库)', isDir: true },
|
|
209
208
|
{ src: '.cursor/agents', dest: 'agents', label: 'Agents(全局子代理)', isDir: true },
|
|
210
209
|
{ src: '.cursor/hooks', dest: 'hooks', label: 'Hooks(全局钩子脚本)', isDir: true },
|
|
211
|
-
{ src: '.cursor/hooks.json', dest: 'hooks.json', label: 'hooks.json(Hooks 触发配置)' },
|
|
210
|
+
{ src: '.cursor/hooks.json', dest: 'hooks.json', label: 'hooks.json(Hooks 触发配置)', rewritePrefix: '.cursor/' },
|
|
212
211
|
{ src: '.cursor/mcp.json', dest: 'mcp.json', label: 'mcp.json(MCP 服务器配置)', merge: true },
|
|
213
212
|
],
|
|
214
213
|
preserve: [],
|
|
@@ -258,6 +257,34 @@ function mergeJsonFile(srcPath, destPath, label) {
|
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
259
|
|
|
260
|
+
/** 将 JSON 中的相对路径重写为绝对路径(递归遍历所有字符串值) */
|
|
261
|
+
function rewritePaths(obj, relPrefix, absPrefix) {
|
|
262
|
+
if (typeof obj === 'string') {
|
|
263
|
+
// 匹配 "node .claude/hooks/xxx" 或 ".cursor/hooks/xxx" 等相对路径
|
|
264
|
+
return obj.split(' ').map(part =>
|
|
265
|
+
part.startsWith(relPrefix) ? part.replace(relPrefix, absPrefix) : part
|
|
266
|
+
).join(' ');
|
|
267
|
+
}
|
|
268
|
+
if (Array.isArray(obj)) return obj.map(item => rewritePaths(item, relPrefix, absPrefix));
|
|
269
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
270
|
+
const result = {};
|
|
271
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
272
|
+
result[k] = rewritePaths(v, relPrefix, absPrefix);
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
return obj;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** 重写已写入的 JSON 文件中的相对路径为绝对路径 */
|
|
280
|
+
function rewriteJsonFilePaths(filePath, relPrefix, absPrefix) {
|
|
281
|
+
try {
|
|
282
|
+
let data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
283
|
+
data = rewritePaths(data, relPrefix, absPrefix);
|
|
284
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
285
|
+
} catch { /* 静默失败 */ }
|
|
286
|
+
}
|
|
287
|
+
|
|
261
288
|
/** 全局安装单个工具 */
|
|
262
289
|
function globalInstallTool(toolKey) {
|
|
263
290
|
const rule = GLOBAL_RULES[toolKey];
|
|
@@ -280,6 +307,10 @@ function globalInstallTool(toolKey) {
|
|
|
280
307
|
// merge 模式:合并 JSON 而非覆盖(保留用户已有配置)
|
|
281
308
|
if (item.merge) {
|
|
282
309
|
const result = mergeJsonFile(srcPath, destPath, item.label);
|
|
310
|
+
// merge 后路径重写
|
|
311
|
+
if (result >= 0 && item.rewritePrefix) {
|
|
312
|
+
rewriteJsonFilePaths(destPath, item.rewritePrefix, globalDest + '/');
|
|
313
|
+
}
|
|
283
314
|
if (result >= 0) installed++; else failed++;
|
|
284
315
|
continue;
|
|
285
316
|
}
|
|
@@ -295,7 +326,16 @@ function globalInstallTool(toolKey) {
|
|
|
295
326
|
installed += n;
|
|
296
327
|
} else {
|
|
297
328
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
298
|
-
|
|
329
|
+
// 非 merge JSON 文件的路径重写
|
|
330
|
+
if (item.rewritePrefix && destPath.endsWith('.json')) {
|
|
331
|
+
try {
|
|
332
|
+
let data = JSON.parse(fs.readFileSync(srcPath, 'utf8'));
|
|
333
|
+
data = rewritePaths(data, item.rewritePrefix, globalDest + '/');
|
|
334
|
+
fs.writeFileSync(destPath, JSON.stringify(data, null, 2) + '\n');
|
|
335
|
+
} catch { fs.copyFileSync(srcPath, destPath); }
|
|
336
|
+
} else {
|
|
337
|
+
fs.copyFileSync(srcPath, destPath);
|
|
338
|
+
}
|
|
299
339
|
console.log(` ${fmt('green', '✓')} ${item.label}`);
|
|
300
340
|
installed++;
|
|
301
341
|
}
|