ethan-skill 1.12.0 → 1.14.0

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/dist/cli/index.js CHANGED
@@ -4808,5 +4808,260 @@ program
4808
4808
  console.log(prompt);
4809
4809
  }
4810
4810
  });
4811
+ // ─── agent 命令 ─────────────────────────────────────────────────────────────
4812
+ const agentCmd = program
4813
+ .command('agent')
4814
+ .description('Multi-Agent 编排:将 Pipeline 步骤分配给不同 Agent 协作执行');
4815
+ agentCmd
4816
+ .command('list')
4817
+ .description('列出所有可用 Agent 及其 Skill 分配')
4818
+ .action(async () => {
4819
+ const { getActiveAgents } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
4820
+ const agents = getActiveAgents(process.cwd());
4821
+ console.log('\n🤖 可用 Agent 列表\n');
4822
+ console.log('─'.repeat(70));
4823
+ for (const agent of agents) {
4824
+ console.log(`\n${agent.emoji} ${agent.name} [${agent.id}]`);
4825
+ console.log(` 职责:${agent.role}`);
4826
+ console.log(` Skills(${agent.skillIds.length}):${agent.skillIds.join(', ')}`);
4827
+ }
4828
+ console.log('\n─'.repeat(70));
4829
+ console.log(`\n共 ${agents.length} 个 Agent。自定义 Agent 放在 .ethan/agents/ 目录。\n`);
4830
+ });
4831
+ agentCmd
4832
+ .command('show <agentId>')
4833
+ .description('查看指定 Agent 的详细配置')
4834
+ .action(async (agentId) => {
4835
+ const { getActiveAgents } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
4836
+ const agents = getActiveAgents(process.cwd());
4837
+ const agent = agents.find((a) => a.id === agentId);
4838
+ if (!agent) {
4839
+ console.error(`\n❌ 未找到 Agent: ${agentId}\n`);
4840
+ console.error(`可用 ID:${agents.map((a) => a.id).join(' | ')}\n`);
4841
+ process.exit(1);
4842
+ }
4843
+ console.log(`\n${agent.emoji} ${agent.name} [${agent.id}]`);
4844
+ console.log('─'.repeat(50));
4845
+ console.log(`英文名:${agent.nameEn}`);
4846
+ console.log(`职责:${agent.role}`);
4847
+ console.log(`\nSkills(${agent.skillIds.length} 个):`);
4848
+ agent.skillIds.forEach((id) => console.log(` - ${id}`));
4849
+ console.log('');
4850
+ });
4851
+ agentCmd
4852
+ .command('run [pipelineId]')
4853
+ .description('生成 Multi-Agent 编排 Prompt,粘贴到 AI 编辑器执行')
4854
+ .option('-c, --context <context>', '任务背景描述(如"实现用户登录功能")', '')
4855
+ .option('--lang <lang>', '输出语言:zh(默认)或 en', '')
4856
+ .option('--mode <mode>', '协作模式:sequential(默认)/ parallel / review-loop / consensus', 'sequential')
4857
+ .option('--with-context', '自动采集项目上下文注入到提示词')
4858
+ .option('--no-copy', '不复制到剪贴板,直接打印到终端')
4859
+ .action(async (pipelineId, options) => {
4860
+ const { resolvePipeline, PIPELINES } = await Promise.resolve().then(() => __importStar(require('../skills/pipeline')));
4861
+ const { getActiveAgents, buildMultiAgentPrompt } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
4862
+ const { buildProjectSnapshot, loadCachedSnapshot, saveSnapshotCache, formatSnapshotForPrompt } = await Promise.resolve().then(() => __importStar(require('../context/builder')));
4863
+ const config = (0, config_1.readConfig)(process.cwd());
4864
+ const lang = (options.lang || config.lang || 'zh');
4865
+ const isEn = lang === 'en';
4866
+ const agents = getActiveAgents(process.cwd());
4867
+ const mode = (options.mode || 'sequential');
4868
+ // 采集项目上下文快照
4869
+ let snapshotBlock;
4870
+ if (options.withContext) {
4871
+ const cached = loadCachedSnapshot(process.cwd());
4872
+ if (cached) {
4873
+ snapshotBlock = formatSnapshotForPrompt(cached, isEn);
4874
+ }
4875
+ else {
4876
+ console.log(isEn ? '🔍 Collecting project context...' : '🔍 正在采集项目上下文...');
4877
+ const snap = buildProjectSnapshot(process.cwd());
4878
+ saveSnapshotCache(snap, process.cwd());
4879
+ snapshotBlock = formatSnapshotForPrompt(snap, isEn);
4880
+ }
4881
+ }
4882
+ else {
4883
+ const cached = loadCachedSnapshot(process.cwd());
4884
+ if (cached)
4885
+ snapshotBlock = formatSnapshotForPrompt(cached, isEn);
4886
+ }
4887
+ // 获取任务上下文
4888
+ let context = (options.context || '').trim();
4889
+ if (!context) {
4890
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
4891
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4892
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
4893
+ context = await ask(isEn
4894
+ ? 'Describe your task context:\n> '
4895
+ : '请描述任务背景(例如:实现用户登录功能):\n> ');
4896
+ rl.close();
4897
+ if (!context) {
4898
+ console.error(isEn ? '\n❌ Context cannot be empty\n' : '\n❌ 任务背景不能为空\n');
4899
+ process.exit(1);
4900
+ }
4901
+ }
4902
+ // 确定目标 Pipeline
4903
+ let id = pipelineId;
4904
+ if (!id) {
4905
+ const customPipelines = loadCustomPipelines(process.cwd());
4906
+ const allPipelines = [
4907
+ ...PIPELINES,
4908
+ ...customPipelines.map((p) => ({ id: p.id, name: p.name, description: p.description, skillIds: p.skillIds })),
4909
+ ];
4910
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
4911
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4912
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
4913
+ console.log(isEn ? '\n🔄 Available Pipelines\n' : '\n🔄 可用工作流 Pipeline\n');
4914
+ console.log('─'.repeat(60));
4915
+ allPipelines.forEach((p, i) => {
4916
+ console.log(` ${i + 1}. ${p.name} [${p.id}]`);
4917
+ });
4918
+ console.log('');
4919
+ const choice = await ask(isEn
4920
+ ? `Select pipeline (1-${allPipelines.length} or ID):\n> `
4921
+ : `选择 Pipeline(序号 1-${allPipelines.length} 或直接输入 ID):\n> `);
4922
+ rl.close();
4923
+ const num = parseInt(choice, 10);
4924
+ id = !isNaN(num) && num >= 1 && num <= allPipelines.length
4925
+ ? allPipelines[num - 1].id
4926
+ : choice;
4927
+ }
4928
+ const resolved = resolvePipeline(id);
4929
+ if (!resolved) {
4930
+ console.error(isEn ? `\n❌ Unknown pipeline: ${id}\n` : `\n❌ 未找到 Pipeline: ${id}\n`);
4931
+ console.error(`Available: ${PIPELINES.map((p) => p.id).join(' | ')}`);
4932
+ process.exit(1);
4933
+ }
4934
+ const { pipeline, skills } = resolved;
4935
+ const modeLabels = {
4936
+ sequential: isEn ? 'Sequential' : '顺序执行',
4937
+ parallel: isEn ? 'Parallel' : '并行分析',
4938
+ 'review-loop': isEn ? 'Review-Loop' : '迭代审查',
4939
+ consensus: isEn ? 'Consensus' : '共识决策',
4940
+ };
4941
+ const prompt = buildMultiAgentPrompt(pipeline, skills, agents, {
4942
+ context,
4943
+ lang,
4944
+ snapshot: snapshotBlock,
4945
+ mode,
4946
+ });
4947
+ console.log(isEn
4948
+ ? `\n🤖 Multi-Agent prompt generated: ${pipeline.name} (${skills.length} steps, ${agents.length} agents, mode: ${modeLabels[mode] ?? mode})`
4949
+ : `\n🤖 Multi-Agent 编排 Prompt 已生成:${pipeline.name}(${skills.length} 步,${agents.length} 个 Agent,模式:${modeLabels[mode] ?? mode})`);
4950
+ console.log('─'.repeat(60));
4951
+ if (options.copy !== false) {
4952
+ if (copyToClipboard(prompt)) {
4953
+ console.log(isEn
4954
+ ? '\n✅ Multi-Agent prompt copied to clipboard! Paste into your AI editor.'
4955
+ : '\n✅ Multi-Agent 编排 Prompt 已复制到剪贴板!粘贴到 AI 编辑器,多 Agent 将协作执行所有步骤。');
4956
+ }
4957
+ else {
4958
+ console.log('\n' + prompt + '\n');
4959
+ }
4960
+ }
4961
+ else {
4962
+ console.log('\n' + prompt + '\n');
4963
+ }
4964
+ console.log(isEn
4965
+ ? '\n💡 Each Agent will execute its assigned steps and hand off results to the next Agent.\n'
4966
+ : '\n💡 每个 Agent 负责执行分配给自己的步骤,通过 Handoff 摘要将产出传递给下一个 Agent。\n');
4967
+ trackUsageWithStreak(`agent-run-${pipeline.id}`);
4968
+ });
4969
+ agentCmd
4970
+ .command('new [agentId]')
4971
+ .description('交互式创建自定义 Agent,生成 .ethan/agents/<id>.yaml')
4972
+ .action(async (agentIdArg) => {
4973
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
4974
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
4975
+ const readline = await Promise.resolve().then(() => __importStar(require('readline')));
4976
+ const { getActiveAgents } = await Promise.resolve().then(() => __importStar(require('../agents/index')));
4977
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
4978
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
4979
+ console.log('\n🤖 创建自定义 Agent\n');
4980
+ console.log('─'.repeat(50));
4981
+ // 1. Agent ID
4982
+ let agentId = agentIdArg || '';
4983
+ if (!agentId) {
4984
+ agentId = await ask('Agent ID(唯一标识符,如 frontend-expert):\n> ');
4985
+ }
4986
+ if (!agentId || !/^[a-z0-9-]+$/.test(agentId)) {
4987
+ console.error('\n❌ Agent ID 只能包含小写字母、数字和连字符\n');
4988
+ rl.close();
4989
+ process.exit(1);
4990
+ }
4991
+ // 检查是否已存在同名 Agent
4992
+ const existingAgents = getActiveAgents(process.cwd());
4993
+ if (existingAgents.find((a) => a.id === agentId)) {
4994
+ const overwrite = await ask(`⚠️ Agent "${agentId}" 已存在。覆盖?(y/N):\n> `);
4995
+ if (overwrite.toLowerCase() !== 'y') {
4996
+ console.log('\n已取消。\n');
4997
+ rl.close();
4998
+ return;
4999
+ }
5000
+ }
5001
+ // 2. 中文名称
5002
+ const name = await ask('Agent 名称(中文,如 "前端专家 Agent"):\n> ') || `${agentId} Agent`;
5003
+ // 3. 英文名称
5004
+ const nameEn = await ask(`英文名称(如 "${agentId}-agent",默认 ${agentId}-agent):\n> `) || `${agentId}-agent`;
5005
+ // 4. Emoji
5006
+ const emoji = await ask('角色 Emoji(默认 🤖):\n> ') || '🤖';
5007
+ // 5. 职责描述
5008
+ const role = await ask('角色职责描述(一句话):\n> ') || `负责 ${agentId} 相关任务`;
5009
+ // 6. Skills 选择
5010
+ const allSkills = await getActiveSkills();
5011
+ console.log('\n可用 Skill ID 列表(共 ' + allSkills.length + ' 个):');
5012
+ const skillCols = Math.ceil(allSkills.length / 3);
5013
+ for (let i = 0; i < skillCols; i++) {
5014
+ const row = [0, 1, 2].map((col) => {
5015
+ const skill = allSkills[i + col * skillCols];
5016
+ return skill ? ` ${String(i + col * skillCols + 1).padStart(2, ' ')}. ${skill.id.padEnd(28, ' ')}` : '';
5017
+ }).join('');
5018
+ console.log(row);
5019
+ }
5020
+ const skillInput = await ask('\n输入 Skill ID(逗号分隔,或序号):\n> ');
5021
+ const skillIds = skillInput.split(',').map((s) => s.trim()).filter(Boolean).map((s) => {
5022
+ const num = parseInt(s, 10);
5023
+ if (!isNaN(num) && num >= 1 && num <= allSkills.length)
5024
+ return allSkills[num - 1].id;
5025
+ return s;
5026
+ });
5027
+ // 校验 skillIds
5028
+ const validIds = new Set(allSkills.map((s) => s.id));
5029
+ const invalid = skillIds.filter((id) => !validIds.has(id));
5030
+ if (invalid.length > 0) {
5031
+ console.error(`\n❌ 无效的 Skill ID:${invalid.join(', ')}\n`);
5032
+ rl.close();
5033
+ process.exit(1);
5034
+ }
5035
+ if (skillIds.length === 0) {
5036
+ console.error('\n❌ 至少需要指定一个 Skill ID\n');
5037
+ rl.close();
5038
+ process.exit(1);
5039
+ }
5040
+ rl.close();
5041
+ // 生成 YAML 内容
5042
+ const yaml = [
5043
+ `# Ethan 自定义 Agent — ${name}`,
5044
+ `id: ${agentId}`,
5045
+ `name: ${name}`,
5046
+ `nameEn: ${nameEn}`,
5047
+ `emoji: ${emoji}`,
5048
+ `role: ${role}`,
5049
+ `skillIds:`,
5050
+ ...skillIds.map((id) => ` - ${id}`),
5051
+ '',
5052
+ ].join('\n');
5053
+ // 写入文件
5054
+ const agentsDir = path.join(process.cwd(), '.ethan', 'agents');
5055
+ if (!fs.existsSync(agentsDir)) {
5056
+ fs.mkdirSync(agentsDir, { recursive: true });
5057
+ }
5058
+ const outPath = path.join(agentsDir, `${agentId}.yaml`);
5059
+ fs.writeFileSync(outPath, yaml, 'utf-8');
5060
+ console.log(`\n✅ 自定义 Agent 已创建:${outPath}`);
5061
+ console.log('\n后续用法:');
5062
+ console.log(` ethan agent show ${agentId} # 查看 Agent 详情`);
5063
+ console.log(` ethan agent run --context "任务" # 在编排中使用此 Agent`);
5064
+ console.log('');
5065
+ });
4811
5066
  program.parse(process.argv);
4812
5067
  //# sourceMappingURL=index.js.map