ai-engineering-init 1.2.6 → 1.2.8
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/.cursor/hooks/cursor-skill-eval.js +1 -1
- package/bin/index.js +173 -14
- package/package.json +1 -1
|
@@ -351,5 +351,5 @@ ${matchedSkills.map((name, i) => `- **${name}**: \`${skillPaths[i]}\``).join('\n
|
|
|
351
351
|
|
|
352
352
|
> 注意:SKILL.md 文档包含本项目特定的实现规范,必须优先参考,不得使用通用写法替代。`;
|
|
353
353
|
|
|
354
|
-
console.log(instructions);
|
|
354
|
+
console.log(JSON.stringify({ systemMessage: instructions }));
|
|
355
355
|
process.exit(0);
|
package/bin/index.js
CHANGED
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
* npx ai-engineering-init --tool all
|
|
8
8
|
* npx ai-engineering-init update # 自动检测已安装工具并更新
|
|
9
9
|
* npx ai-engineering-init update --tool claude
|
|
10
|
+
* npx ai-engineering-init global # 全局安装(对所有项目生效)
|
|
11
|
+
* npx ai-engineering-init global --tool claude
|
|
10
12
|
*/
|
|
11
13
|
'use strict';
|
|
12
14
|
|
|
13
15
|
const fs = require('fs');
|
|
14
16
|
const path = require('path');
|
|
17
|
+
const os = require('os');
|
|
15
18
|
const readline = require('readline');
|
|
16
19
|
|
|
17
20
|
// ── ANSI 颜色(Windows CMD/PowerShell 兼容)────────────────────────────────
|
|
@@ -43,7 +46,7 @@ console.log('');
|
|
|
43
46
|
|
|
44
47
|
// ── 参数解析 ───────────────────────────────────────────────────────────────
|
|
45
48
|
const args = process.argv.slice(2);
|
|
46
|
-
let command = ''; // 'update' | ''
|
|
49
|
+
let command = ''; // 'update' | 'global' | ''
|
|
47
50
|
let tool = '';
|
|
48
51
|
let targetDir = process.cwd();
|
|
49
52
|
let force = false;
|
|
@@ -54,6 +57,9 @@ for (let i = 0; i < args.length; i++) {
|
|
|
54
57
|
case 'update':
|
|
55
58
|
command = 'update';
|
|
56
59
|
break;
|
|
60
|
+
case 'global':
|
|
61
|
+
command = 'global';
|
|
62
|
+
break;
|
|
57
63
|
case '--tool': case '-t':
|
|
58
64
|
if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
|
|
59
65
|
console.error(fmt('red', `错误:${arg} 需要一个值(claude | cursor | codex | all)`));
|
|
@@ -88,19 +94,22 @@ for (let i = 0; i < args.length; i++) {
|
|
|
88
94
|
function printHelp() {
|
|
89
95
|
console.log(`用法: ${fmt('bold', 'npx ai-engineering-init')} [命令] [选项]\n`);
|
|
90
96
|
console.log('命令:');
|
|
91
|
-
console.log(` ${fmt('bold', '(无)')}
|
|
92
|
-
console.log(` ${fmt('bold', 'update')}
|
|
97
|
+
console.log(` ${fmt('bold', '(无)')} 交互式初始化(安装到当前项目目录)`);
|
|
98
|
+
console.log(` ${fmt('bold', 'update')} 更新已安装的框架文件(跳过用户自定义文件)`);
|
|
99
|
+
console.log(` ${fmt('bold', 'global')} 全局安装到 ~/.claude / ~/.cursor 等,对所有项目生效\n`);
|
|
93
100
|
console.log('选项:');
|
|
94
101
|
console.log(' --tool, -t <工具> 指定工具: claude | cursor | codex | all');
|
|
95
|
-
console.log(' --dir, -d <目录>
|
|
96
|
-
console.log(' --force,-f 强制覆盖(init 时覆盖已有文件;update 时同时更新保留文件)');
|
|
102
|
+
console.log(' --dir, -d <目录> 目标目录(默认:当前目录,仅 init/update 有效)');
|
|
103
|
+
console.log(' --force,-f 强制覆盖(init 时覆盖已有文件;update/global 时同时更新保留文件)');
|
|
97
104
|
console.log(' --help, -h 显示此帮助\n');
|
|
98
105
|
console.log('示例:');
|
|
99
106
|
console.log(' npx ai-engineering-init --tool claude');
|
|
100
107
|
console.log(' npx ai-engineering-init --tool all --dir /path/to/project');
|
|
101
108
|
console.log(' npx ai-engineering-init update # 自动检测已安装工具');
|
|
102
109
|
console.log(' npx ai-engineering-init update --tool claude # 只更新 Claude');
|
|
103
|
-
console.log(' npx ai-engineering-init update --force #
|
|
110
|
+
console.log(' npx ai-engineering-init update --force # 强制更新,包括保留文件');
|
|
111
|
+
console.log(' npx ai-engineering-init global # 全局安装所有工具');
|
|
112
|
+
console.log(' npx ai-engineering-init global --tool claude # 只全局安装 Claude\n');
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
// ── 工具定义(init 用)────────────────────────────────────────────────────
|
|
@@ -172,6 +181,147 @@ const UPDATE_RULES = {
|
|
|
172
181
|
},
|
|
173
182
|
};
|
|
174
183
|
|
|
184
|
+
// ── 全局安装规则(global 用)─────────────────────────────────────────────
|
|
185
|
+
// 安装到 ~/.claude / ~/.cursor / ~/.codex,对当前用户所有项目生效
|
|
186
|
+
const HOME_DIR = os.homedir();
|
|
187
|
+
|
|
188
|
+
const GLOBAL_RULES = {
|
|
189
|
+
claude: {
|
|
190
|
+
label: 'Claude Code',
|
|
191
|
+
targetDir: path.join(HOME_DIR, '.claude'),
|
|
192
|
+
files: [
|
|
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
|
+
{ src: '.claude/framework-config.json', dest: 'framework-config.json', label: 'framework-config.json' },
|
|
198
|
+
],
|
|
199
|
+
preserve: [
|
|
200
|
+
{ dest: 'settings.json', reason: '包含用户 MCP 配置和权限设置' },
|
|
201
|
+
],
|
|
202
|
+
note: `Skills/Commands/Hooks 已安装到 ~/.claude,对所有项目自动生效`,
|
|
203
|
+
},
|
|
204
|
+
cursor: {
|
|
205
|
+
label: 'Cursor',
|
|
206
|
+
targetDir: path.join(HOME_DIR, '.cursor'),
|
|
207
|
+
files: [
|
|
208
|
+
{ src: '.cursor/skills', dest: 'skills', label: 'Skills(全局技能库)', isDir: true },
|
|
209
|
+
{ src: '.cursor/agents', dest: 'agents', label: 'Agents(全局子代理)', isDir: true },
|
|
210
|
+
{ src: '.cursor/hooks', dest: 'hooks', label: 'Hooks(全局钩子)', isDir: true },
|
|
211
|
+
],
|
|
212
|
+
preserve: [
|
|
213
|
+
{ dest: 'mcp.json', reason: '包含用户 MCP 服务器配置' },
|
|
214
|
+
{ dest: 'hooks.json', reason: '包含用户 Hooks 配置(项目级优先)' },
|
|
215
|
+
],
|
|
216
|
+
note: `Skills/Hooks 已安装到 ~/.cursor,重启 Cursor 后在全局规则中生效`,
|
|
217
|
+
},
|
|
218
|
+
codex: {
|
|
219
|
+
label: 'OpenAI Codex',
|
|
220
|
+
targetDir: path.join(HOME_DIR, '.codex'),
|
|
221
|
+
files: [
|
|
222
|
+
{ src: '.codex/skills', dest: 'skills', label: 'Skills(全局技能库)', isDir: true },
|
|
223
|
+
],
|
|
224
|
+
preserve: [],
|
|
225
|
+
note: `Skills 已安装到 ~/.codex`,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/** 全局安装单个工具 */
|
|
230
|
+
function globalInstallTool(toolKey) {
|
|
231
|
+
const rule = GLOBAL_RULES[toolKey];
|
|
232
|
+
const globalDest = rule.targetDir;
|
|
233
|
+
console.log(fmt('cyan', `[${rule.label}]`) + fmt('blue', ` → ${globalDest}`));
|
|
234
|
+
|
|
235
|
+
let installed = 0, failed = 0;
|
|
236
|
+
|
|
237
|
+
try { fs.mkdirSync(globalDest, { recursive: true }); } catch { /* 已存在忽略 */ }
|
|
238
|
+
|
|
239
|
+
for (const item of rule.files) {
|
|
240
|
+
const srcPath = path.join(SOURCE_DIR, item.src);
|
|
241
|
+
const destPath = path.join(globalDest, item.dest);
|
|
242
|
+
|
|
243
|
+
if (!fs.existsSync(srcPath)) {
|
|
244
|
+
console.log(` ${fmt('yellow', '⚠')} ${item.label} 源文件不存在,跳过`);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (fs.existsSync(destPath) && !force) {
|
|
248
|
+
console.log(` ${fmt('yellow', '⚠')} ${item.label} 已存在,跳过(--force 可强制覆盖)`);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
if (item.isDir) {
|
|
253
|
+
const n = copyDir(srcPath, destPath);
|
|
254
|
+
console.log(` ${fmt('green', '✓')} ${item.label} ${fmt('magenta', `(${n} 个文件)`)}`);
|
|
255
|
+
installed += n;
|
|
256
|
+
} else {
|
|
257
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
258
|
+
fs.copyFileSync(srcPath, destPath);
|
|
259
|
+
console.log(` ${fmt('green', '✓')} ${item.label}`);
|
|
260
|
+
installed++;
|
|
261
|
+
}
|
|
262
|
+
} catch (e) {
|
|
263
|
+
console.log(` ${fmt('red', '✗')} ${item.label} 安装失败: ${e.message}`);
|
|
264
|
+
failed++;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (const item of rule.preserve) {
|
|
269
|
+
const destPath = path.join(globalDest, item.dest);
|
|
270
|
+
if (fs.existsSync(destPath)) {
|
|
271
|
+
console.log(` ${fmt('yellow', '⊘')} ${item.dest} 已保留 — ${item.reason}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return { installed, failed };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** global 命令主流程 */
|
|
279
|
+
function runGlobal(selectedTool) {
|
|
280
|
+
const validKeys = Object.keys(GLOBAL_RULES);
|
|
281
|
+
const toolsToInstall = (!selectedTool || selectedTool === 'all')
|
|
282
|
+
? validKeys
|
|
283
|
+
: [selectedTool];
|
|
284
|
+
|
|
285
|
+
if (selectedTool && selectedTool !== 'all' && !GLOBAL_RULES[selectedTool]) {
|
|
286
|
+
console.error(fmt('red', `无效工具: "${selectedTool}"。有效选项: claude | cursor | codex | all`));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.log(` 安装模式: ${fmt('green', fmt('bold', '全局安装(当前用户所有项目生效)'))}`);
|
|
291
|
+
console.log(` 安装工具: ${fmt('bold', toolsToInstall.join(', '))}`);
|
|
292
|
+
if (force) console.log(` ${fmt('yellow', '⚠ --force 模式:强制覆盖已有文件')}`);
|
|
293
|
+
console.log('');
|
|
294
|
+
console.log(fmt('bold', '正在安装到系统目录...'));
|
|
295
|
+
console.log('');
|
|
296
|
+
|
|
297
|
+
let totalInstalled = 0, totalFailed = 0;
|
|
298
|
+
for (let i = 0; i < toolsToInstall.length; i++) {
|
|
299
|
+
const { installed, failed } = globalInstallTool(toolsToInstall[i]);
|
|
300
|
+
totalInstalled += installed;
|
|
301
|
+
totalFailed += failed;
|
|
302
|
+
if (i < toolsToInstall.length - 1) console.log('');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
console.log('');
|
|
306
|
+
console.log(fmt('green', fmt('bold', '✅ 全局安装完成!')));
|
|
307
|
+
console.log('');
|
|
308
|
+
console.log(` ${fmt('green', `✓ 安装文件: ${totalInstalled} 个`)}`);
|
|
309
|
+
if (totalFailed > 0) {
|
|
310
|
+
console.log(` ${fmt('red', `✗ 失败文件: ${totalFailed} 个`)}(请检查目录权限)`);
|
|
311
|
+
}
|
|
312
|
+
console.log('');
|
|
313
|
+
console.log(fmt('cyan', '安装位置说明:'));
|
|
314
|
+
for (const key of toolsToInstall) {
|
|
315
|
+
const rule = GLOBAL_RULES[key];
|
|
316
|
+
console.log(` ${fmt('bold', rule.label + ':')} ${rule.note}`);
|
|
317
|
+
}
|
|
318
|
+
console.log('');
|
|
319
|
+
console.log(fmt('yellow', '提示:项目级配置(.claude/ 等)优先级高于全局配置,两者可同时使用。'));
|
|
320
|
+
console.log('');
|
|
321
|
+
|
|
322
|
+
if (totalFailed > 0) process.exitCode = 1;
|
|
323
|
+
}
|
|
324
|
+
|
|
175
325
|
// ── 公共工具函数 ──────────────────────────────────────────────────────────
|
|
176
326
|
const SOURCE_DIR = path.join(__dirname, '..');
|
|
177
327
|
|
|
@@ -442,6 +592,8 @@ function runUpdate(selectedTool) {
|
|
|
442
592
|
// ── 主入口 ────────────────────────────────────────────────────────────────
|
|
443
593
|
if (command === 'update') {
|
|
444
594
|
runUpdate(tool);
|
|
595
|
+
} else if (command === 'global') {
|
|
596
|
+
runGlobal(tool);
|
|
445
597
|
} else if (tool) {
|
|
446
598
|
run(tool);
|
|
447
599
|
} else {
|
|
@@ -452,19 +604,26 @@ if (command === 'update') {
|
|
|
452
604
|
process.exit(1);
|
|
453
605
|
}
|
|
454
606
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
455
|
-
console.log(fmt('cyan', '
|
|
607
|
+
console.log(fmt('cyan', '请选择操作:'));
|
|
456
608
|
console.log('');
|
|
457
|
-
console.log(` ${fmt('bold', '1')}) ${fmt('green', 'Claude Code')} —
|
|
458
|
-
console.log(` ${fmt('bold', '2')}) ${fmt('cyan', 'Cursor')} —
|
|
459
|
-
console.log(` ${fmt('bold', '3')}) ${fmt('yellow', 'OpenAI Codex')} —
|
|
460
|
-
console.log(` ${fmt('bold', '4')}) ${fmt('blue', '全部工具')} — 同时初始化 Claude + Cursor + Codex
|
|
609
|
+
console.log(` ${fmt('bold', '1')}) ${fmt('green', 'Claude Code')} — 初始化到当前项目 .claude/ + CLAUDE.md`);
|
|
610
|
+
console.log(` ${fmt('bold', '2')}) ${fmt('cyan', 'Cursor')} — 初始化到当前项目 .cursor/(Skills + Agents)`);
|
|
611
|
+
console.log(` ${fmt('bold', '3')}) ${fmt('yellow', 'OpenAI Codex')} — 初始化到当前项目 .codex/ + AGENTS.md`);
|
|
612
|
+
console.log(` ${fmt('bold', '4')}) ${fmt('blue', '全部工具')} — 同时初始化 Claude + Cursor + Codex 到当前项目`);
|
|
613
|
+
console.log(` ${fmt('bold', '5')}) ${fmt('magenta', '全局安装')} — 安装到 ~/.claude / ~/.cursor,对所有项目生效`);
|
|
461
614
|
console.log('');
|
|
462
|
-
rl.question(fmt('bold', '请输入选项 [1-
|
|
615
|
+
rl.question(fmt('bold', '请输入选项 [1-5]: '), (answer) => {
|
|
463
616
|
rl.close();
|
|
464
617
|
const map = { '1': 'claude', '2': 'cursor', '3': 'codex', '4': 'all' };
|
|
465
618
|
const selected = map[answer.trim()];
|
|
466
|
-
if (!selected) { console.error(fmt('red', '无效选项,退出。')); process.exit(1); }
|
|
467
619
|
console.log('');
|
|
468
|
-
|
|
620
|
+
if (answer.trim() === '5') {
|
|
621
|
+
runGlobal('all');
|
|
622
|
+
} else if (selected) {
|
|
623
|
+
run(selected);
|
|
624
|
+
} else {
|
|
625
|
+
console.error(fmt('red', '无效选项,退出。'));
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
469
628
|
});
|
|
470
629
|
}
|