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/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 detectMcpTools() {
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(targetDir, '.claude'))) tools.push('claude');
1342
- if (isRealDir(path.join(targetDir, '.cursor'))) tools.push('cursor');
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 filePath = path.join(targetDir, config.file);
1367
+ const resolvedPath = filePath || resolveMcpConfigPath(toolName, 'project');
1351
1368
  try {
1352
- const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
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 filePath = path.join(targetDir, config.file);
1378
+ const resolvedPath = filePath || resolveMcpConfigPath(toolName, 'project');
1362
1379
  let data = {};
1363
- try { data = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { /* 新建 */ }
1380
+ try { data = JSON.parse(fs.readFileSync(resolvedPath, 'utf8')); } catch { /* 新建 */ }
1364
1381
  data[config.key] = mcpServers;
1365
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
1366
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
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 config = {
1372
- command: 'npx',
1373
- args: ['-y', entry.package],
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 servers = getMcpServers(t);
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 servers = getMcpServers(toolName);
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 servers = getMcpServers(toolName);
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 servers = getMcpServers(toolName);
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 servers = getMcpServers(toolName);
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}]`) + ` ${MCP_CONFIG_PATHS[toolName].file}`);
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 pkg = (srv.args || []).find(a => a.startsWith('@')) || '—';
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(` 包: ${pkg} | 环境变量: ${envKeys}`);
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 servers = getMcpServers(toolName);
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 用)─────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-engineering-init",
3
- "version": "1.14.0",
3
+ "version": "1.14.1",
4
4
  "description": "AI 工程化配置初始化工具 — 一键为 Claude Code、OpenAI Codex 等 AI 工具初始化 Skills 和项目规范",
5
5
  "keywords": [
6
6
  "claude-code",