ai-engineering-init 1.14.0 → 1.14.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/hooks/stop.js +1 -2
- package/.claude/skills/auto-test/SKILL.md +182 -10
- package/.claude/skills/code-patterns/SKILL.md +119 -0
- package/.claude/skills/codex-code-review/SKILL.md +39 -0
- package/.claude/skills/leniu-code-patterns/SKILL.md +179 -2
- package/.codex/skills/code-patterns/SKILL.md +119 -0
- package/.codex/skills/leniu-code-patterns/SKILL.md +179 -2
- package/.cursor/skills/code-patterns/SKILL.md +119 -0
- package/.cursor/skills/leniu-code-patterns/SKILL.md +179 -2
- package/CLAUDE.md +0 -28
- package/bin/index.js +144 -51
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -1327,6 +1327,14 @@ const MCP_REGISTRY = [
|
|
|
1327
1327
|
env: { YUQUE_TOKEN: '<YOUR_TOKEN>' },
|
|
1328
1328
|
recommended: false,
|
|
1329
1329
|
},
|
|
1330
|
+
{
|
|
1331
|
+
name: 'codex',
|
|
1332
|
+
command: 'uvx',
|
|
1333
|
+
args: ['--from', 'git+https://github.com/GuDaStudio/codexmcp.git', 'codexmcp'],
|
|
1334
|
+
description: 'Codex 协作 — 代码审查、算法分析、生成补丁(需安装 uv)',
|
|
1335
|
+
env: {},
|
|
1336
|
+
recommended: false,
|
|
1337
|
+
},
|
|
1330
1338
|
];
|
|
1331
1339
|
|
|
1332
1340
|
/** MCP 配置文件路径映射 */
|
|
@@ -1335,43 +1343,73 @@ const MCP_CONFIG_PATHS = {
|
|
|
1335
1343
|
cursor: { file: '.cursor/mcp.json', key: 'mcpServers' },
|
|
1336
1344
|
};
|
|
1337
1345
|
|
|
1338
|
-
/**
|
|
1339
|
-
function
|
|
1346
|
+
/** 解析 MCP 配置文件绝对路径 */
|
|
1347
|
+
function resolveMcpConfigPath(toolName, scope = 'project') {
|
|
1348
|
+
const config = MCP_CONFIG_PATHS[toolName];
|
|
1349
|
+
if (!config) return '';
|
|
1350
|
+
const baseDir = scope === 'global' ? HOME_DIR : targetDir;
|
|
1351
|
+
return path.join(baseDir, config.file);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
/** 检测指定作用域中已有的工具配置目录 */
|
|
1355
|
+
function detectMcpTools(scope = 'project') {
|
|
1356
|
+
const baseDir = scope === 'global' ? HOME_DIR : targetDir;
|
|
1340
1357
|
const tools = [];
|
|
1341
|
-
if (isRealDir(path.join(
|
|
1342
|
-
if (isRealDir(path.join(
|
|
1358
|
+
if (isRealDir(path.join(baseDir, '.claude'))) tools.push('claude');
|
|
1359
|
+
if (isRealDir(path.join(baseDir, '.cursor'))) tools.push('cursor');
|
|
1343
1360
|
return tools;
|
|
1344
1361
|
}
|
|
1345
1362
|
|
|
1346
1363
|
/** 读取指定工具的 MCP 已配置服务器 */
|
|
1347
|
-
function getMcpServers(toolName) {
|
|
1364
|
+
function getMcpServers(toolName, filePath) {
|
|
1348
1365
|
const config = MCP_CONFIG_PATHS[toolName];
|
|
1349
1366
|
if (!config) return {};
|
|
1350
|
-
const
|
|
1367
|
+
const resolvedPath = filePath || resolveMcpConfigPath(toolName, 'project');
|
|
1351
1368
|
try {
|
|
1352
|
-
const data = JSON.parse(fs.readFileSync(
|
|
1369
|
+
const data = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));
|
|
1353
1370
|
return data[config.key] || {};
|
|
1354
1371
|
} catch { return {}; }
|
|
1355
1372
|
}
|
|
1356
1373
|
|
|
1357
1374
|
/** 写入指定工具的 MCP 配置(保留文件其他字段) */
|
|
1358
|
-
function setMcpServers(toolName, mcpServers) {
|
|
1375
|
+
function setMcpServers(toolName, mcpServers, filePath) {
|
|
1359
1376
|
const config = MCP_CONFIG_PATHS[toolName];
|
|
1360
1377
|
if (!config) return;
|
|
1361
|
-
const
|
|
1378
|
+
const resolvedPath = filePath || resolveMcpConfigPath(toolName, 'project');
|
|
1362
1379
|
let data = {};
|
|
1363
|
-
try { data = JSON.parse(fs.readFileSync(
|
|
1380
|
+
try { data = JSON.parse(fs.readFileSync(resolvedPath, 'utf8')); } catch { /* 新建 */ }
|
|
1364
1381
|
data[config.key] = mcpServers;
|
|
1365
|
-
fs.mkdirSync(path.dirname(
|
|
1366
|
-
fs.writeFileSync(
|
|
1382
|
+
fs.mkdirSync(path.dirname(resolvedPath), { recursive: true });
|
|
1383
|
+
fs.writeFileSync(resolvedPath, JSON.stringify(data, null, 2) + '\n');
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/** 生成等效的手动安装 CLI 命令提示 */
|
|
1387
|
+
function buildCliHints(entry, scope) {
|
|
1388
|
+
const scopeFlag = scope === 'global' ? '-s user ' : '';
|
|
1389
|
+
const hints = [];
|
|
1390
|
+
|
|
1391
|
+
// Claude Code CLI
|
|
1392
|
+
if (entry.command && entry.command !== 'npx') {
|
|
1393
|
+
hints.push(`claude: claude mcp add ${entry.name} ${scopeFlag}--transport stdio -- ${entry.command} ${entry.args.join(' ')}`);
|
|
1394
|
+
} else {
|
|
1395
|
+
hints.push(`claude: claude mcp add ${entry.name} ${scopeFlag}-- npx -y ${entry.package}`);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// Cursor:无官方 CLI,提示手动编辑
|
|
1399
|
+
const cursorFile = scope === 'global' ? '~/.cursor/mcp.json' : '.cursor/mcp.json';
|
|
1400
|
+
hints.push(`cursor: 手动编辑 ${cursorFile},在 mcpServers 中添加 "${entry.name}" 配置`);
|
|
1401
|
+
|
|
1402
|
+
return hints;
|
|
1367
1403
|
}
|
|
1368
1404
|
|
|
1369
1405
|
/** 构建单个 MCP 服务器的配置对象 */
|
|
1370
1406
|
function buildMcpServerConfig(entry) {
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1407
|
+
const command = entry.command || 'npx';
|
|
1408
|
+
const args = Array.isArray(entry.args) && entry.args.length > 0
|
|
1409
|
+
? [...entry.args]
|
|
1410
|
+
: ['-y', entry.package];
|
|
1411
|
+
|
|
1412
|
+
const config = { command, args };
|
|
1375
1413
|
if (Object.keys(entry.env).length > 0) {
|
|
1376
1414
|
config.env = { ...entry.env };
|
|
1377
1415
|
}
|
|
@@ -1379,10 +1417,11 @@ function buildMcpServerConfig(entry) {
|
|
|
1379
1417
|
}
|
|
1380
1418
|
|
|
1381
1419
|
/** 获取所有工具中已安装的 MCP 服务器名称集合 */
|
|
1382
|
-
function getInstalledMcpNames(tools) {
|
|
1420
|
+
function getInstalledMcpNames(tools, scope = 'project') {
|
|
1383
1421
|
const names = new Set();
|
|
1384
1422
|
for (const t of tools) {
|
|
1385
|
-
const
|
|
1423
|
+
const configPath = resolveMcpConfigPath(t, scope);
|
|
1424
|
+
const servers = getMcpServers(t, configPath);
|
|
1386
1425
|
for (const name of Object.keys(servers)) names.add(name);
|
|
1387
1426
|
}
|
|
1388
1427
|
return names;
|
|
@@ -1394,17 +1433,6 @@ function runMcp() {
|
|
|
1394
1433
|
process.exit(1);
|
|
1395
1434
|
}
|
|
1396
1435
|
|
|
1397
|
-
const tools = detectMcpTools();
|
|
1398
|
-
if (tools.length === 0) {
|
|
1399
|
-
console.log(fmt('yellow', '⚠ 当前目录未检测到 .claude/ 或 .cursor/ 配置目录。'));
|
|
1400
|
-
console.log(` 请先运行: ${fmt('bold', hintCmd('init --tool claude'))}`);
|
|
1401
|
-
console.log('');
|
|
1402
|
-
process.exit(1);
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
console.log(` 检测到工具: ${fmt('bold', tools.join(', '))}`);
|
|
1406
|
-
console.log('');
|
|
1407
|
-
|
|
1408
1436
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1409
1437
|
const ask = (question) => new Promise((resolve) => {
|
|
1410
1438
|
rl.question(question, (answer) => resolve(answer.trim()));
|
|
@@ -1412,6 +1440,42 @@ function runMcp() {
|
|
|
1412
1440
|
|
|
1413
1441
|
(async () => {
|
|
1414
1442
|
try {
|
|
1443
|
+
// 第一步:选择作用域
|
|
1444
|
+
console.log(fmt('cyan', '请选择 MCP 作用域:'));
|
|
1445
|
+
console.log('');
|
|
1446
|
+
console.log(` ${fmt('bold', '1')}) ${fmt('green', '项目级')} — 写入当前项目 .claude/.cursor`);
|
|
1447
|
+
console.log(` ${fmt('bold', '2')}) ${fmt('yellow', '全局(~/.claude / ~/.cursor)')} — 对当前用户所有项目生效`);
|
|
1448
|
+
console.log('');
|
|
1449
|
+
const scopeAnswer = await ask(fmt('bold', '请输入选项 [1-2]: '));
|
|
1450
|
+
const scopeMap = { '1': 'project', '2': 'global' };
|
|
1451
|
+
const scope = scopeMap[scopeAnswer];
|
|
1452
|
+
if (!scope) {
|
|
1453
|
+
console.error(fmt('red', '无效作用域选项,退出。'));
|
|
1454
|
+
process.exit(1);
|
|
1455
|
+
}
|
|
1456
|
+
console.log('');
|
|
1457
|
+
|
|
1458
|
+
const tools = detectMcpTools(scope);
|
|
1459
|
+
if (tools.length === 0) {
|
|
1460
|
+
if (scope === 'global') {
|
|
1461
|
+
console.log(fmt('yellow', '⚠ 全局目录未检测到 ~/.claude/ 或 ~/.cursor/ 配置目录。'));
|
|
1462
|
+
console.log(` 请先运行: ${fmt('bold', 'npx ai-engineering-init global --tool claude')}`);
|
|
1463
|
+
} else {
|
|
1464
|
+
console.log(fmt('yellow', '⚠ 当前目录未检测到 .claude/ 或 .cursor/ 配置目录。'));
|
|
1465
|
+
console.log(` 请先运行: ${fmt('bold', hintCmd('init --tool claude'))}`);
|
|
1466
|
+
}
|
|
1467
|
+
console.log('');
|
|
1468
|
+
process.exit(1);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
const scopeLabel = scope === 'global'
|
|
1472
|
+
? `全局(${HOME_DIR}/.claude / ${HOME_DIR}/.cursor)`
|
|
1473
|
+
: `项目级(${targetDir})`;
|
|
1474
|
+
console.log(` 作用域: ${fmt('bold', scopeLabel)}`);
|
|
1475
|
+
console.log(` 检测到工具: ${fmt('bold', tools.join(', '))}`);
|
|
1476
|
+
console.log('');
|
|
1477
|
+
|
|
1478
|
+
// 第二步:选择操作
|
|
1415
1479
|
console.log(fmt('cyan', '请选择 MCP 操作:'));
|
|
1416
1480
|
console.log('');
|
|
1417
1481
|
console.log(` ${fmt('bold', '1')}) ${fmt('green', '安装 MCP 服务器')} — 从预置列表选择并安装到配置`);
|
|
@@ -1423,10 +1487,10 @@ function runMcp() {
|
|
|
1423
1487
|
console.log('');
|
|
1424
1488
|
|
|
1425
1489
|
switch (action) {
|
|
1426
|
-
case '1': await mcpInstall(tools, ask); break;
|
|
1427
|
-
case '2': await mcpUninstall(tools, ask); break;
|
|
1428
|
-
case '3': mcpStatus(tools); break;
|
|
1429
|
-
case '4': await mcpRecommend(tools, ask); break;
|
|
1490
|
+
case '1': await mcpInstall(tools, ask, scope); break;
|
|
1491
|
+
case '2': await mcpUninstall(tools, ask, scope); break;
|
|
1492
|
+
case '3': mcpStatus(tools, scope); break;
|
|
1493
|
+
case '4': await mcpRecommend(tools, ask, scope); break;
|
|
1430
1494
|
default:
|
|
1431
1495
|
console.error(fmt('red', '无效选项,退出。'));
|
|
1432
1496
|
process.exit(1);
|
|
@@ -1438,8 +1502,8 @@ function runMcp() {
|
|
|
1438
1502
|
}
|
|
1439
1503
|
|
|
1440
1504
|
/** 安装 MCP 服务器 */
|
|
1441
|
-
async function mcpInstall(tools, ask) {
|
|
1442
|
-
const installed = getInstalledMcpNames(tools);
|
|
1505
|
+
async function mcpInstall(tools, ask, scope = 'project') {
|
|
1506
|
+
const installed = getInstalledMcpNames(tools, scope);
|
|
1443
1507
|
|
|
1444
1508
|
console.log(fmt('cyan', '可用的 MCP 服务器:'));
|
|
1445
1509
|
console.log('');
|
|
@@ -1478,25 +1542,38 @@ async function mcpInstall(tools, ask) {
|
|
|
1478
1542
|
|
|
1479
1543
|
// 写入所有检测到的工具配置
|
|
1480
1544
|
for (const toolName of tools) {
|
|
1481
|
-
const
|
|
1545
|
+
const configPath = resolveMcpConfigPath(toolName, scope);
|
|
1546
|
+
const servers = getMcpServers(toolName, configPath);
|
|
1482
1547
|
for (const entry of selected) {
|
|
1483
1548
|
servers[entry.name] = buildMcpServerConfig(entry);
|
|
1484
1549
|
}
|
|
1485
|
-
setMcpServers(toolName, servers);
|
|
1550
|
+
setMcpServers(toolName, servers, configPath);
|
|
1486
1551
|
console.log(` ${fmt('green', '✓')} ${toolName}: 已写入 ${selected.map(e => e.name).join(', ')}`);
|
|
1487
1552
|
}
|
|
1488
1553
|
|
|
1489
1554
|
console.log('');
|
|
1490
1555
|
console.log(fmt('green', fmt('bold', `✅ 已安装 ${selected.length} 个 MCP 服务器!`)));
|
|
1491
1556
|
console.log('');
|
|
1557
|
+
|
|
1558
|
+
// 输出等效手动安装命令供参考
|
|
1559
|
+
console.log(fmt('cyan', '💡 等效手动安装命令(仅供参考):'));
|
|
1560
|
+
console.log('');
|
|
1561
|
+
for (const entry of selected) {
|
|
1562
|
+
console.log(` ${fmt('bold', entry.name)}:`);
|
|
1563
|
+
for (const hint of buildCliHints(entry, scope)) {
|
|
1564
|
+
console.log(` ${fmt('yellow', hint)}`);
|
|
1565
|
+
}
|
|
1566
|
+
console.log('');
|
|
1567
|
+
}
|
|
1492
1568
|
}
|
|
1493
1569
|
|
|
1494
1570
|
/** 卸载 MCP 服务器 */
|
|
1495
|
-
async function mcpUninstall(tools, ask) {
|
|
1571
|
+
async function mcpUninstall(tools, ask, scope = 'project') {
|
|
1496
1572
|
// 收集所有已安装的服务器(合并去重)
|
|
1497
1573
|
const allServers = new Map(); // name → 出现在哪些工具中
|
|
1498
1574
|
for (const toolName of tools) {
|
|
1499
|
-
const
|
|
1575
|
+
const configPath = resolveMcpConfigPath(toolName, scope);
|
|
1576
|
+
const servers = getMcpServers(toolName, configPath);
|
|
1500
1577
|
for (const name of Object.keys(servers)) {
|
|
1501
1578
|
if (!allServers.has(name)) allServers.set(name, []);
|
|
1502
1579
|
allServers.get(name).push(toolName);
|
|
@@ -1531,7 +1608,8 @@ async function mcpUninstall(tools, ask) {
|
|
|
1531
1608
|
console.log('');
|
|
1532
1609
|
|
|
1533
1610
|
for (const toolName of tools) {
|
|
1534
|
-
const
|
|
1611
|
+
const configPath = resolveMcpConfigPath(toolName, scope);
|
|
1612
|
+
const servers = getMcpServers(toolName, configPath);
|
|
1535
1613
|
let removed = 0;
|
|
1536
1614
|
for (const name of toRemove) {
|
|
1537
1615
|
if (name in servers) {
|
|
@@ -1540,7 +1618,7 @@ async function mcpUninstall(tools, ask) {
|
|
|
1540
1618
|
}
|
|
1541
1619
|
}
|
|
1542
1620
|
if (removed > 0) {
|
|
1543
|
-
setMcpServers(toolName, servers);
|
|
1621
|
+
setMcpServers(toolName, servers, configPath);
|
|
1544
1622
|
console.log(` ${fmt('green', '✓')} ${toolName}: 已移除 ${toRemove.filter(n => !servers[n]).join(', ')}`);
|
|
1545
1623
|
}
|
|
1546
1624
|
}
|
|
@@ -1551,14 +1629,15 @@ async function mcpUninstall(tools, ask) {
|
|
|
1551
1629
|
}
|
|
1552
1630
|
|
|
1553
1631
|
/** 查看 MCP 状态 */
|
|
1554
|
-
function mcpStatus(tools) {
|
|
1632
|
+
function mcpStatus(tools, scope = 'project') {
|
|
1555
1633
|
let hasAny = false;
|
|
1556
1634
|
|
|
1557
1635
|
for (const toolName of tools) {
|
|
1558
|
-
const
|
|
1636
|
+
const configPath = resolveMcpConfigPath(toolName, scope);
|
|
1637
|
+
const servers = getMcpServers(toolName, configPath);
|
|
1559
1638
|
const names = Object.keys(servers);
|
|
1560
1639
|
|
|
1561
|
-
console.log(fmt('cyan', `[${toolName}]`) + ` ${
|
|
1640
|
+
console.log(fmt('cyan', `[${toolName}]`) + ` ${configPath}`);
|
|
1562
1641
|
|
|
1563
1642
|
if (names.length === 0) {
|
|
1564
1643
|
console.log(` ${fmt('yellow', '(无已安装的 MCP 服务器)')}`);
|
|
@@ -1566,10 +1645,12 @@ function mcpStatus(tools) {
|
|
|
1566
1645
|
hasAny = true;
|
|
1567
1646
|
for (const name of names) {
|
|
1568
1647
|
const srv = servers[name];
|
|
1569
|
-
const
|
|
1648
|
+
const args = srv.args || [];
|
|
1649
|
+
const pkg = args.find(a => a.startsWith('@'))
|
|
1650
|
+
|| (srv.command !== 'npx' ? `${srv.command} ${args.join(' ')}` : '—');
|
|
1570
1651
|
const envKeys = srv.env ? Object.keys(srv.env).join(', ') : '—';
|
|
1571
1652
|
console.log(` ${fmt('green', '●')} ${fmt('bold', name)}`);
|
|
1572
|
-
console.log(`
|
|
1653
|
+
console.log(` 命令: ${pkg} | 环境变量: ${envKeys}`);
|
|
1573
1654
|
}
|
|
1574
1655
|
}
|
|
1575
1656
|
console.log('');
|
|
@@ -1583,14 +1664,14 @@ function mcpStatus(tools) {
|
|
|
1583
1664
|
}
|
|
1584
1665
|
|
|
1585
1666
|
/** 一键推荐安装 */
|
|
1586
|
-
async function mcpRecommend(tools, ask) {
|
|
1587
|
-
const installed = getInstalledMcpNames(tools);
|
|
1667
|
+
async function mcpRecommend(tools, ask, scope = 'project') {
|
|
1668
|
+
const installed = getInstalledMcpNames(tools, scope);
|
|
1588
1669
|
const toInstall = MCP_REGISTRY.filter(e => e.recommended && !installed.has(e.name));
|
|
1589
1670
|
|
|
1590
1671
|
if (toInstall.length === 0) {
|
|
1591
1672
|
console.log(fmt('green', ' ✓ 所有推荐的 MCP 服务器已安装!'));
|
|
1592
1673
|
console.log('');
|
|
1593
|
-
mcpStatus(tools);
|
|
1674
|
+
mcpStatus(tools, scope);
|
|
1594
1675
|
return;
|
|
1595
1676
|
}
|
|
1596
1677
|
|
|
@@ -1615,17 +1696,29 @@ async function mcpRecommend(tools, ask) {
|
|
|
1615
1696
|
|
|
1616
1697
|
// 写入配置
|
|
1617
1698
|
for (const toolName of tools) {
|
|
1618
|
-
const
|
|
1699
|
+
const configPath = resolveMcpConfigPath(toolName, scope);
|
|
1700
|
+
const servers = getMcpServers(toolName, configPath);
|
|
1619
1701
|
for (const entry of toInstall) {
|
|
1620
1702
|
servers[entry.name] = buildMcpServerConfig(entry);
|
|
1621
1703
|
}
|
|
1622
|
-
setMcpServers(toolName, servers);
|
|
1704
|
+
setMcpServers(toolName, servers, configPath);
|
|
1623
1705
|
console.log(` ${fmt('green', '✓')} ${toolName}: 已写入 ${toInstall.map(e => e.name).join(', ')}`);
|
|
1624
1706
|
}
|
|
1625
1707
|
|
|
1626
1708
|
console.log('');
|
|
1627
1709
|
console.log(fmt('green', fmt('bold', `✅ 已安装 ${toInstall.length} 个推荐 MCP 服务器!`)));
|
|
1628
1710
|
console.log('');
|
|
1711
|
+
|
|
1712
|
+
// 输出等效手动安装命令供参考
|
|
1713
|
+
console.log(fmt('cyan', '💡 等效手动安装命令(仅供参考):'));
|
|
1714
|
+
console.log('');
|
|
1715
|
+
for (const entry of toInstall) {
|
|
1716
|
+
console.log(` ${fmt('bold', entry.name)}:`);
|
|
1717
|
+
for (const hint of buildCliHints(entry, scope)) {
|
|
1718
|
+
console.log(` ${fmt('yellow', hint)}`);
|
|
1719
|
+
}
|
|
1720
|
+
console.log('');
|
|
1721
|
+
}
|
|
1629
1722
|
}
|
|
1630
1723
|
|
|
1631
1724
|
// ── 工具选择菜单(init 用)─────────────────────────────────────────────────
|