ethan-skill 1.15.0 → 1.15.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.15.0] - 2026-04-12
6
+
7
+ ### Added
8
+
9
+ - **`ethan upgrade` 显式升级命令**(新增 CLI):
10
+ - 前台执行,有实时输出(`stdio: 'inherit'`)
11
+ - 支持 `--force` 选项跳过版本比较强制重装
12
+ - 成功:清除缓存并提示重启终端;失败:显示明确错误与手动命令
13
+ - 不再依赖静默后台升级的黑盒机制
14
+
15
+ - **新 MCP 工具 `ethan_upgrade`**(30 → **31**):
16
+ - 通过 Claude Desktop 查询当前版本状态、npm 最新版本与升级缓存信息
17
+ - 输出升级指引(终端命令),MCP 环境下无法直接执行 npm,提供可操作指令
18
+
19
+ - **自动升级启动 banner**(`printUpdateBanner`):
20
+ - 检测到新版本时,在命令输出前打印一行可见提示:
21
+ ```
22
+ 📦 ethan v{latest} 可用(当前 v{current})— 运行 ethan upgrade 立即更新
23
+ ```
24
+ - 使用缓存(24h 内)时同步输出,不增加延迟
25
+
26
+ - **`ethan doctor` 版本更新状态节**:
27
+ - 显示当前版本、npm 最新版、上次检查时间
28
+ - 有新版本时提示 `ethan upgrade`,已最新时显示 ✅
29
+
30
+ ### Fixed
31
+
32
+ - **自动升级永不重试 Bug**:
33
+ - 根因:`upgradedVersion` 在 spawn 成功(不等于 npm install 成功)后立即写入缓存,若后台 npm 进程静默失败,下次检查时 `upgradedVersion === latest` 导致永久跳过
34
+ - 修复:增加条件 `&& !compareVersions(currentVersion, latest)`,若当前版本仍低于 latest(说明升级失败),则重新触发
35
+
36
+ - **后台升级失败完全静默**:
37
+ - spawn 失败(npm 不在 PATH):原先 `catch` 静默返回;修复后在进程退出时打印 `⚠️ 找不到 npm` 提示
38
+ - spawn 成功但子进程立即报错(如 EACCES 权限):新增 `child.on('error', ...)` 监听并打印提示
39
+
40
+ - **Windows 下 npm 查找失败**:
41
+ - detached spawn 在 Windows 未启用 shell 模式,部分 nvm-windows 环境下 npm 无法找到
42
+ - 修复:Windows 平台启用 `shell: true`
43
+
44
+ ### Changed
45
+
46
+ - 自动升级退出消息措辞调整:
47
+ - 旧:`🔄 Ethan 正在后台自动升级到 v{latest},重启终端后生效。`
48
+ - 新:`🔄 Ethan 正在后台升级到 v{latest},完成后重启终端生效。`
49
+
50
+ ---
51
+
5
52
  ## [1.14.0] - 2026-04-09
6
53
 
7
54
  ### Added
package/README.md CHANGED
@@ -17,10 +17,10 @@
17
17
  | **36 个 Skill** | 标准化工作流节点,覆盖需求→接口设计→安全→部署→PRD→Git→测试→系统设计→数据库→Docker→CI/CD→性能→重构→可观测性→设计模式→OpenSpec→技术债→Mock→数据迁移→LLM设计→威胁建模→绿色编码→服务目录→移动审查→数据管道→ML实验 |
18
18
  | **11 个平台** | Cursor / Copilot / Cline / Windsurf / Zed / JetBrains / Continue / Claude Code 等 |
19
19
  | **10 个 Pipeline** | 链式工作流(开发 / 汇报 / 质量 / 完整周期 / 故障响应 / 新功能 / Spec规范 / Bug修复 / 安全审计 / 开源发布),有状态持久化推进 |
20
- | **28 个 MCP 工具** | AI 编辑器原生调用 Skill、Pipeline、Git、记忆库、估算、DORA 指标、PR 分析、故障复盘、脚手架、合规证据 |
20
+ | **31 个 MCP 工具** | AI 编辑器原生调用 Skill、Pipeline、Git、记忆库、估算、DORA 指标、PR 分析、故障复盘、脚手架、合规证据、版本升级检查 |
21
21
  | **60+ CLI 命令** | Git 集成、开发工具、记忆库、估算、分析工具(diff/deps/dora/mermaid/adr/i18n/migrate/onboard/compliance 等)、插件 OS 全覆盖 |
22
22
  | **Slash 命令生成** | `ethan slash` 一键为 Claude Code 生成 `/ethan-xxx` 原生命令,其他平台生成速查表 |
23
- | **自动升级** | 检测到新版本时自动后台 `npm install -g` 静默升级,重启终端即用新版 |
23
+ | **自动升级** | 启动时显示新版本 banner,自动后台 `npm install -g` 升级;失败时显示明确提示;支持 `ethan upgrade [--force]` 手动显式升级 |
24
24
  | **自定义 Skill** | `.ethan/skills/*.yaml/.md`,YAML frontmatter + Markdown body |
25
25
  | **自定义 Pipeline** | `.ethan/pipelines/*.yaml`,引用内置或自定义 Skill 自由组合 |
26
26
  | **浏览器扩展** | Chrome/Edge MV3,GitHub PR 一键 Review + 右键菜单 |
@@ -122,7 +122,7 @@ npx ethan-skill install --platform cursor --lang en
122
122
  }
123
123
  ```
124
124
 
125
- 重启 AI 编辑器后,可使用全部 **28 个 MCP 工具**(详见下方 MCP 工具列表)。
125
+ 重启 AI 编辑器后,可使用全部 **31 个 MCP 工具**(详见下方 MCP 工具列表)。
126
126
 
127
127
  ### 方式三:全局安装
128
128
 
@@ -347,17 +347,38 @@ ethan slash
347
347
 
348
348
  ## 自动升级
349
349
 
350
- Ethan 每次启动都会静默检查 npm 最新版本(24h 缓存,不阻塞 CLI)。检测到新版本后,**自动在后台执行 `npm install -g ethan-skill@latest`**,退出时显示:
350
+ Ethan 每次启动都会静默检查 npm 最新版本(24h 缓存,不阻塞 CLI)。检测到新版本后:
351
351
 
352
+ 1. **命令执行前**显示 banner 提醒:
353
+ ```
354
+ 📦 ethan v{latest} 可用(当前 v{current})— 运行 ethan upgrade 立即更新
355
+ ```
356
+
357
+ 2. **自动在后台**执行 `npm install -g ethan-skill@latest`,退出时显示:
358
+ ```
359
+ 🔄 Ethan 正在后台升级到 v{latest},完成后重启终端生效。
360
+ ```
361
+
362
+ 3. **失败时给出明确提示**(权限不足、npm 找不到等),不再静默忽略
363
+
364
+ 4. **智能重试**:若后台升级静默失败,下次启动自动重试(不会被缓存永久阻断)
365
+
366
+ ### 手动显式升级
367
+
368
+ ```bash
369
+ ethan upgrade # 查询最新版本并升级(有实时输出)
370
+ ethan upgrade --force # 跳过版本比较,强制重新安装
352
371
  ```
353
- 🔄 Ethan 正在后台自动升级到 v{latest},重启终端后生效。
354
- ```
355
372
 
356
- 无需手动运行升级命令,始终保持最新版本。
373
+ ### MCP 版本查询(Claude Desktop 用户)
374
+
375
+ ```
376
+ 调用 ethan_upgrade 工具查看当前版本状态与升级建议
377
+ ```
357
378
 
358
379
  ---
359
380
 
360
- ## 28 个 MCP 工具
381
+ ## 31 个 MCP 工具
361
382
 
362
383
  配置 MCP Server 后,AI 编辑器(Cursor / Cline / Continue 等)可直接调用:
363
384
 
@@ -394,6 +415,7 @@ Ethan 每次启动都会静默检查 npm 最新版本(24h 缓存,不阻塞 C
394
415
  | `ethan_postmortem` | 生成故障复盘提示词(v1.12.0 新增)|
395
416
  | `ethan_scaffold` | 黄金路径脚手架提示词(v1.12.0 新增)|
396
417
  | `ethan_compliance` | 生成合规证据收集清单(soc2/gdpr/iso27001,v1.12.0 新增)|
418
+ | `ethan_upgrade` | 检查版本状态,输出当前版本/最新版本/升级指引(v1.15.0 新增)|
397
419
 
398
420
  ---
399
421
 
@@ -552,6 +574,7 @@ npm run test:coverage # 覆盖率报告
552
574
 
553
575
  | 版本 | 主要变更 |
554
576
  |------|---------|
577
+ | **v1.15.0** | 自动升级全面增强:启动 banner 提醒、智能重试(失败后不永久阻断)、错误明确提示;新增 `ethan upgrade [--force]` 显式升级命令;新增 MCP 工具 `ethan_upgrade`;`ethan doctor` 新增版本更新状态节;MCP 工具 30 → 31 |
555
578
  | **v1.14.0** | 新增 3 个专业化 Agent(qa/security/data),内置 Agent 5 → 8;新增 4 种协作模式(`--mode sequential/parallel/review-loop/consensus`);新增 `ethan agent new` 交互式创建自定义 Agent;新增 MCP 工具 `ethan_agent_list/show`,MCP 工具 28 → 30 |
556
579
  | **v1.13.0** | Multi-Agent 编排系统(`src/agents/`);5 个内置角色 Agent(architect/coder/reviewer/devops/pm);新增 `ethan agent list/show/run` CLI;新增 MCP 工具 `ethan_agent_orchestrate`;MCP 工具 27 → 28;新增宣传落地页 `docs/landing.html` |
557
580
  | **v1.12.0** | 新增 10 个 Skill(技术债/Mock服务/数据迁移/LLM设计/威胁建模/绿色编码/服务目录/移动审查/数据管道/ML实验);Skills 26 → 36;新增 3 条 Pipeline(bugfix/security-audit/open-source-release);新增 19 个分析 CLI 命令(diff/deps/dora/adr/mermaid/i18n/onboard/migrate/postmortem/decision-log/knowledge/oss/prompt-lib/scaffold/benchmark/sync/compliance 等);MCP 工具 22 → 27 |
package/dist/cli/index.js CHANGED
@@ -628,23 +628,67 @@ program
628
628
  const platformDirMap = {
629
629
  'claude-code': path.join(dir, '.claude/commands'),
630
630
  codebuddy: path.join(dir, '.codebuddy/commands'),
631
- cursor: path.join(dir, '.cursor/rules'),
631
+ cursor: path.join(dir, '.cursor/commands'), // v0.50+ 支持独立命令文件
632
+ cline: path.join(dir, '.cline/commands'), // 支持独立命令文件
633
+ windsurf: path.join(dir, '.windsurf/commands'), // Wave 6+ 支持独立命令文件
632
634
  copilot: path.join(dir, '.github'),
633
- cline: dir,
634
- windsurf: path.join(dir, '.windsurf/rules'),
635
- zed: dir,
635
+ zed: path.join(dir, '.zed/prompts'), // /prompt 机制,独立 .md 文件
636
636
  jetbrains: path.join(dir, '.github'),
637
637
  continue: dir,
638
638
  lingma: path.join(dir, '.lingma/rules'),
639
639
  };
640
640
  let total = 0;
641
+ /** 生成 Agent 嵌入式 Auto-Pilot 提示词(不依赖 ethan agent run,直接注入执行指令) */
642
+ const buildAgentAutoContent = (agent, agentSkills, isClaudeCodePlatform) => {
643
+ const skillCount = agentSkills.length;
644
+ const skillList = agentSkills
645
+ .map((s, i) => `${i + 1}. **${s.name}** (\`${s.id}\`): ${s.description}`)
646
+ .join('\n');
647
+ const taskSection = isClaudeCodePlatform
648
+ ? `## 任务\n$ARGUMENTS`
649
+ : `## 任务\n请描述您想完成的任务。`;
650
+ return [
651
+ `你是 **Ethan ${agent.emoji} ${agent.name}**,专注于:${agent.role}。`,
652
+ '',
653
+ taskSection,
654
+ '',
655
+ '---',
656
+ '',
657
+ `## 执行指令(Auto-Pilot 模式)`,
658
+ '',
659
+ `请严格按以下顺序**自动执行**全部 ${skillCount} 个 Skill,**无需用户逐步确认**:`,
660
+ '',
661
+ skillList,
662
+ '',
663
+ '### 执行规范',
664
+ '',
665
+ '1. 对每个 Skill 完整执行其全部步骤(严格遵循已加载的 Skill 定义)',
666
+ '2. 每步完成后,用以下格式折叠展示:',
667
+ '',
668
+ '```',
669
+ '<details>',
670
+ '<summary>✅ Step N: [Skill名] — 已完成</summary>',
671
+ '',
672
+ '[本步骤完整输出]',
673
+ '',
674
+ '</details>',
675
+ '```',
676
+ '',
677
+ '3. 将本步**核心产出**(≤200字摘要)作为下一步的背景,自动继续执行',
678
+ '4. 全部步骤完成后,输出完整**合并报告**',
679
+ '',
680
+ '> 立即开始执行 Step 1,无需等待用户确认。',
681
+ ].join('\n');
682
+ };
641
683
  for (const p of targets) {
642
684
  const outDir = platformDirMap[p];
643
685
  if (!fs.existsSync(outDir))
644
686
  fs.mkdirSync(outDir, { recursive: true });
645
- if (p === 'claude-code' || p === 'codebuddy') {
687
+ // 支持独立命令文件的平台:claude-code / codebuddy / cursor / cline / windsurf
688
+ const INDIVIDUAL_FILE_PLATFORMS = ['claude-code', 'codebuddy', 'cursor', 'cline', 'windsurf'];
689
+ if (INDIVIDUAL_FILE_PLATFORMS.includes(p)) {
646
690
  const isClaudeCode = p === 'claude-code';
647
- const cmdDir = isClaudeCode ? '.claude/commands' : '.codebuddy/commands';
691
+ const cmdDir = path.relative(dir, outDir); // 用于日志展示
648
692
  // ── 每个 Skill 生成独立 .md slash 命令文件 ──────────────────────────────
649
693
  for (const skill of skills) {
650
694
  const stepsText = skill.steps
@@ -752,7 +796,7 @@ program
752
796
  console.log(` ✅ claude-code → 工作流: ${cmdDir}/ethan-{cmd}.md × ${WORKFLOW_SLASH_COMMANDS.length}`);
753
797
  console.log(` 使用方式:在 Claude Code 聊天中输入 /ethan-commit、/ethan-auto 等`);
754
798
  console.log(` ⚡ 动态注入:提示词自动注入对话,无需复制粘贴`);
755
- // ── Agent 命令(Claude Code 专属:每个 Agent 一个 .md 文件)─────────────
799
+ // ── Agent 命令(Claude Code:每个 Agent 一个动态 .md 文件)─────────────
756
800
  const { getActiveAgents } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
757
801
  const agents = getActiveAgents(dir);
758
802
  // ethan-agent-list.md
@@ -767,12 +811,14 @@ program
767
811
  const agentNewContent = `---\ndescription: "Ethan — 创建自定义 Agent,生成 .ethan/agents/<id>.yaml"\n---\n\n` +
768
812
  `$(ethan agent new $ARGUMENTS 2>/dev/null)\n`;
769
813
  fs.writeFileSync(path.join(outDir, 'ethan-agent-new.md'), agentNewContent, 'utf-8');
770
- // 每个内置 Agent 生成独立快捷命令
814
+ // 每个内置 Agent 生成独立快捷命令(静态嵌入式 Auto-Pilot 提示词 + $ARGUMENTS)
771
815
  let agentFileCount = 3;
772
816
  for (const agent of agents) {
773
- const agentSkillList = agent.skillIds.slice(0, 8).join('、') + (agent.skillIds.length > 8 ? '...' : '');
817
+ const agentSkills = agent.skillIds
818
+ .map((id) => skills.find((s) => s.id === id))
819
+ .filter((s) => !!s);
774
820
  const agentContent = `---\ndescription: "Ethan — ${agent.emoji} ${agent.name}:${agent.role}"\n---\n\n` +
775
- `$(![ -n "$ARGUMENTS" ] && ethan agent run --no-copy -c "$ARGUMENTS" 2>/dev/null || printf "## ${agent.emoji} ${agent.name}\\n\\n**职责**: ${agent.role}\\n\\n**负责 Skill**: ${agentSkillList}\\n\\n用法: /ethan-agent-${agent.id} <任务描述>")\n`;
821
+ buildAgentAutoContent(agent, agentSkills, true) + '\n';
776
822
  fs.writeFileSync(path.join(outDir, `ethan-agent-${agent.id}.md`), agentContent, 'utf-8');
777
823
  agentFileCount++;
778
824
  total++;
@@ -780,7 +826,7 @@ program
780
826
  console.log(` ✅ claude-code → Agents: ${cmdDir}/ethan-agent-*.md × ${agentFileCount}(含 ${agents.length} 个 Agent 快捷键)`);
781
827
  }
782
828
  else {
783
- // CodeBuddy:静态提示词(各 /ethan-xxx 命令文件)
829
+ // codebuddy / cursor / cline / windsurf:静态提示词(各 /ethan-xxx 命令文件)
784
830
  for (const cmd of WORKFLOW_SLASH_COMMANDS) {
785
831
  const content = `---\n` +
786
832
  `description: "Ethan — ${cmd.name}:${cmd.description}"\n` +
@@ -789,8 +835,43 @@ program
789
835
  fs.writeFileSync(path.join(outDir, `ethan-${cmd.id}.md`), content, 'utf-8');
790
836
  total++;
791
837
  }
792
- console.log(` ✅ codebuddy → 工作流: ${cmdDir}/ethan-{cmd}.md × ${WORKFLOW_SLASH_COMMANDS.length}`);
793
- console.log(` 使用方式:在 CodeBuddy 聊天中输入 /ethan-commit、/ethan-auto 等`);
838
+ console.log(` ✅ ${p.padEnd(12)} → 工作流: ${cmdDir}/ethan-{cmd}.md × ${WORKFLOW_SLASH_COMMANDS.length}`);
839
+ console.log(` 使用方式:在 ${p} 聊天中输入 /ethan-commit、/ethan-auto 等`);
840
+ // ── Agent 命令(静态提示词,适用于 codebuddy/cursor/cline/windsurf)──────
841
+ const { getActiveAgents: getAgentsStatic } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
842
+ const agentsStatic = getAgentsStatic(dir);
843
+ // ethan-agent-list.md(静态)
844
+ const agentListRows = agentsStatic
845
+ .map((a) => `| /ethan-agent-${a.id} | ${a.emoji} ${a.name} | ${a.role} |`)
846
+ .join('\n');
847
+ fs.writeFileSync(path.join(outDir, 'ethan-agent-list.md'), `---\ndescription: "Ethan — 查看所有可用 Agent 及其 Skill 分配"\n---\n\n` +
848
+ `# Ethan Agent 列表\n\n` +
849
+ `| 命令 | Agent | 职责 |\n|------|-------|------|\n${agentListRows}\n\n` +
850
+ `运行:\`ethan agent list\`\n`, 'utf-8');
851
+ // ethan-agent-run.md(静态)
852
+ fs.writeFileSync(path.join(outDir, 'ethan-agent-run.md'), `---\ndescription: "Ethan — Multi-Agent 编排:将任务分配给多个专业 Agent 协作执行"\n---\n\n` +
853
+ `# Ethan Multi-Agent 编排\n\n` +
854
+ `用法:\`ethan agent run <pipeline> -c "任务描述"\`\n\n` +
855
+ `示例:\`ethan agent run dev-workflow -c "实现用户登录功能"\`\n\n` +
856
+ `可用模式:sequential / parallel / review-loop / consensus\n`, 'utf-8');
857
+ // ethan-agent-new.md(静态)
858
+ fs.writeFileSync(path.join(outDir, 'ethan-agent-new.md'), `---\ndescription: "Ethan — 创建自定义 Agent,生成 .ethan/agents/<id>.yaml"\n---\n\n` +
859
+ `# 创建自定义 Agent\n\n` +
860
+ `运行:\`ethan agent new\`\n\n` +
861
+ `将在 \`.ethan/agents/\` 目录下生成 Agent 配置文件。\n`, 'utf-8');
862
+ // 每个内置 Agent 生成独立静态命令文件(嵌入式 Auto-Pilot 提示词)
863
+ let staticAgentFileCount = 3;
864
+ for (const agent of agentsStatic) {
865
+ const agentSkills = agent.skillIds
866
+ .map((id) => skills.find((s) => s.id === id))
867
+ .filter((s) => !!s);
868
+ const agentContent = `---\ndescription: "Ethan — ${agent.emoji} ${agent.name}:${agent.role}"\n---\n\n` +
869
+ buildAgentAutoContent(agent, agentSkills, false) + '\n';
870
+ fs.writeFileSync(path.join(outDir, `ethan-agent-${agent.id}.md`), agentContent, 'utf-8');
871
+ staticAgentFileCount++;
872
+ total++;
873
+ }
874
+ console.log(` ✅ ${p.padEnd(12)} → Agents: ${cmdDir}/ethan-agent-*.md × ${staticAgentFileCount}(含 ${agentsStatic.length} 个 Agent 快捷键)`);
794
875
  }
795
876
  }
796
877
  else {
@@ -861,7 +942,8 @@ program
861
942
  total++;
862
943
  }
863
944
  }
864
- const totalFiles = (platform === 'claude-code' || platform === 'codebuddy')
945
+ const INDIVIDUAL_FILE_PLATFORMS_CHECK = ['claude-code', 'codebuddy', 'cursor', 'cline', 'windsurf'];
946
+ const totalFiles = INDIVIDUAL_FILE_PLATFORMS_CHECK.includes(platform)
865
947
  ? skills.length + WORKFLOW_SLASH_COMMANDS.length
866
948
  : total;
867
949
  console.log(`\n共生成 ${totalFiles} 个 Slash 命令文件(目录:${dir})`);
@@ -1295,6 +1377,15 @@ pipelineCmd
1295
1377
  }
1296
1378
  writeStats(stats);
1297
1379
  });
1380
+ // ─── upgrade 命令 ───────────────────────────────────────────────────────────
1381
+ program
1382
+ .command('upgrade')
1383
+ .description('检查并升级到最新版本')
1384
+ .option('--force', '跳过版本检查,强制重新安装最新版本')
1385
+ .action(async (options) => {
1386
+ const { checkAndPrintUpdate } = await Promise.resolve().then(() => __importStar(require('./update-check')));
1387
+ await checkAndPrintUpdate(pkg.version, pkg.name, options.force ?? false);
1388
+ });
1298
1389
  // ─── doctor 命令 ────────────────────────────────────────────────────────────
1299
1390
  program
1300
1391
  .command('doctor')
@@ -1373,6 +1464,40 @@ program
1373
1464
  }
1374
1465
  console.log('\n' + '─'.repeat(60));
1375
1466
  const allBuilt = ruleFiles.every(({ file }) => fs.existsSync(path.join(ROOT, file)));
1467
+ // 版本更新状态检查
1468
+ console.log(`\n[版本更新状态]`);
1469
+ console.log(` 当前版本 : v${pkg.version}`);
1470
+ const updateCachePath = path.join(os.homedir(), '.ethan-update-cache.json');
1471
+ if (fs.existsSync(updateCachePath)) {
1472
+ try {
1473
+ const updateCache = JSON.parse(fs.readFileSync(updateCachePath, 'utf-8'));
1474
+ const lastCheckedAgo = Math.round((Date.now() - updateCache.lastChecked) / 1000 / 60);
1475
+ console.log(` npm 最新版 : v${updateCache.latestVersion}`);
1476
+ console.log(` 上次检查 : ${lastCheckedAgo} 分钟前`);
1477
+ const needsUpdate = (() => {
1478
+ const parse = (v) => v.replace(/^v/, '').split('.').map(Number);
1479
+ const [cMaj, cMin, cPat] = parse(pkg.version);
1480
+ const [lMaj, lMin, lPat] = parse(updateCache.latestVersion);
1481
+ if (lMaj !== cMaj)
1482
+ return lMaj > cMaj;
1483
+ if (lMin !== cMin)
1484
+ return lMin > cMin;
1485
+ return lPat > cPat;
1486
+ })();
1487
+ if (needsUpdate) {
1488
+ console.log(` 升级状态 : ⚠️ 有新版本可用,运行 ethan upgrade 升级`);
1489
+ }
1490
+ else {
1491
+ console.log(` 升级状态 : ✅ 已是最新`);
1492
+ }
1493
+ }
1494
+ catch {
1495
+ console.log(` 升级状态 : ⚠️ 缓存文件损坏,将在下次运行时重建`);
1496
+ }
1497
+ }
1498
+ else {
1499
+ console.log(` 升级状态 : ⏳ 尚未检查(下次运行 ethan 命令时自动检查)`);
1500
+ }
1376
1501
  if (!allBuilt) {
1377
1502
  console.log('\n💡 提示:运行 npm run build:rules 生成规则文件\n');
1378
1503
  }