evolclaw 2.8.1 → 2.8.3

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.js CHANGED
@@ -68,13 +68,11 @@ function rotateLogs(logDir) {
68
68
  function countLines(pkgRoot, logDir) {
69
69
  const srcDir = path.join(pkgRoot, 'src');
70
70
  const statsFile = path.join(logDir, 'line-stats.log');
71
- const countDir = (dir, exclude) => {
71
+ const countDir = (dir) => {
72
72
  if (!fs.existsSync(dir))
73
73
  return 0;
74
74
  let total = 0;
75
75
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
76
- if (exclude && entry.name === exclude)
77
- continue;
78
76
  const full = path.join(dir, entry.name);
79
77
  if (entry.isDirectory()) {
80
78
  total += countDir(full);
@@ -93,12 +91,14 @@ function countLines(pkgRoot, logDir) {
93
91
  console.log('\n[launcher] 正在统计代码行数...\n');
94
92
  const core = countDir(path.join(srcDir, 'core'));
95
93
  const agents = countDir(path.join(srcDir, 'agents'));
96
- const channels = countDir(path.join(srcDir, 'channels'), 'experimental');
94
+ const channels = countDir(path.join(srcDir, 'channels'));
97
95
  const utils = countDir(path.join(srcDir, 'utils'));
98
96
  const entry = countFile(path.join(srcDir, 'index.ts'))
99
97
  + countFile(path.join(srcDir, 'config.ts'))
100
98
  + countFile(path.join(srcDir, 'types.ts'))
101
- + countFile(path.join(srcDir, 'cli.ts'));
99
+ + countFile(path.join(srcDir, 'cli.ts'))
100
+ + countFile(path.join(srcDir, 'ipc.ts'))
101
+ + countFile(path.join(srcDir, 'paths.ts'));
102
102
  const total = core + agents + channels + utils + entry;
103
103
  console.log('==================================================');
104
104
  console.log('EvolClaw 代码统计');
@@ -607,20 +607,26 @@ async function cmdStatus() {
607
607
  showConfigChannels(config);
608
608
  }
609
609
  }
610
- console.log('');
611
- console.log('📁 Log Files:');
612
- const mainLog = path.join(p.logs, 'evolclaw.log');
613
- if (fs.existsSync(mainLog)) {
614
- const stat = fs.statSync(mainLog);
615
- const sizeMB = (stat.size / 1024 / 1024).toFixed(1);
616
- console.log(` Main log: ${mainLog} (${sizeMB} MB)`);
617
- console.log('');
618
- console.log('📝 Recent activity (last 30 lines):');
619
- const content = fs.readFileSync(mainLog, 'utf-8').trim().split('\n');
620
- console.log(content.slice(-30).map(l => ` ${l}`).join('\n'));
621
- }
622
- else {
623
- console.log(' (no log file yet)');
610
+ // EvolAgent summary (via IPC, only when running)
611
+ if (pid) {
612
+ try {
613
+ const agentResult = await ipcQuery(p.socket, { type: 'evolagent.list' });
614
+ if (agentResult?.ok && agentResult.agents?.length > 0) {
615
+ const agents = agentResult.agents.filter((a) => !a.isDefault);
616
+ if (agents.length > 0) {
617
+ console.log('');
618
+ console.log('🤖 EvolAgents:');
619
+ for (const a of agents) {
620
+ const statusIcon = a.status === 'running' ? '●' : a.status === 'error' ? '✗' : a.status === 'disabled' ? '○' : '◌';
621
+ const channels = a.channels?.join(', ') || '—';
622
+ console.log(` ${statusIcon} ${a.name.padEnd(14)} ${a.status.padEnd(10)} ${channels}`);
623
+ }
624
+ }
625
+ }
626
+ }
627
+ catch {
628
+ // IPC query for agents failed — skip section
629
+ }
624
630
  }
625
631
  }
626
632
  // Log line pattern: [timestamp] [LEVEL] [Module?] message
@@ -1388,11 +1394,18 @@ async function cmdAgent(args) {
1388
1394
  }
1389
1395
  if (sub === 'new') {
1390
1396
  const name = args[1];
1391
- if (!name) {
1392
- console.error('Usage: evolclaw agent new <name>');
1393
- process.exit(1);
1397
+ const nonInteractive = args.includes('--non-interactive');
1398
+ if (nonInteractive) {
1399
+ if (!name) {
1400
+ console.error('Usage: evolclaw agent new <name> --non-interactive ...');
1401
+ process.exit(1);
1402
+ }
1403
+ await cmdAgentNewNonInteractive(name, args.slice(2));
1404
+ }
1405
+ else {
1406
+ // Interactive mode: name from CLI is suggested default; user can override at prompt
1407
+ await cmdAgentNew(name);
1394
1408
  }
1395
- await cmdAgentNew(name);
1396
1409
  return;
1397
1410
  }
1398
1411
  if (sub === 'reload') {
@@ -1401,16 +1414,42 @@ async function cmdAgent(args) {
1401
1414
  console.error('Usage: evolclaw agent reload <name>');
1402
1415
  process.exit(1);
1403
1416
  }
1404
- // Phase 1 stub: no IPC yet
1405
- console.error('⚠ evolclaw 未运行或 IPC 未就绪,请使用 evolclaw restart 重新加载所有 agent');
1406
- process.exit(1);
1417
+ const p = resolvePaths();
1418
+ try {
1419
+ const result = await ipcQuery(p.socket, { type: 'evolagent.reload', name });
1420
+ if (result && result.ok) {
1421
+ console.log(`✓ Agent "${name}" reloaded`);
1422
+ }
1423
+ else {
1424
+ console.error(`✗ Reload failed: ${result?.error || 'unknown error'}`);
1425
+ process.exit(1);
1426
+ }
1427
+ }
1428
+ catch {
1429
+ console.error('⚠ evolclaw 未运行,请先 evolclaw start 后再 reload');
1430
+ console.log(' 或直接 evolclaw restart 重新加载所有 agent');
1431
+ process.exit(1);
1432
+ }
1433
+ return;
1407
1434
  }
1408
1435
  // `evolclaw agent <name>` — show detail
1409
1436
  await cmdAgentShow(sub);
1410
1437
  }
1411
1438
  async function cmdAgentList() {
1412
1439
  const p = resolvePaths();
1413
- const { AgentRegistry } = await import('./core/agent-registry.js');
1440
+ // Try IPC first (running process has real status)
1441
+ try {
1442
+ const result = await ipcQuery(p.socket, { type: 'evolagent.list' });
1443
+ if (result && result.ok && result.agents) {
1444
+ printAgentTable(result.agents);
1445
+ return;
1446
+ }
1447
+ }
1448
+ catch {
1449
+ // IPC unavailable — fall through to cold mode
1450
+ }
1451
+ // Cold mode: read from disk
1452
+ const { EvolAgentRegistry } = await import('./core/evolagent-registry.js');
1414
1453
  const { loadConfig } = await import('./config.js');
1415
1454
  let config;
1416
1455
  try {
@@ -1419,22 +1458,26 @@ async function cmdAgentList() {
1419
1458
  catch {
1420
1459
  config = { agents: {}, channels: {}, projects: { defaultPath: process.cwd() } };
1421
1460
  }
1422
- const registry = new AgentRegistry(p.agentsDir);
1461
+ const registry = new EvolAgentRegistry(p.agentsDir);
1423
1462
  registry.loadAll(config);
1424
- const list = registry.list();
1463
+ printAgentTable(registry.list());
1464
+ }
1465
+ function printAgentTable(list) {
1425
1466
  if (list.length === 0) {
1426
1467
  console.log('No agents configured.');
1427
1468
  return;
1428
1469
  }
1429
- // Table output
1430
- console.log('NAME'.padEnd(14) + 'STATUS'.padEnd(10) + 'CHANNELS'.padEnd(24) + 'PROJECT'.padEnd(22) + 'BASEAGENT'.padEnd(11) + 'LAST ACTIVE');
1470
+ console.log('NAME'.padEnd(14) + 'STATUS'.padEnd(10) + 'CHANNELS'.padEnd(24) +
1471
+ 'PROJECT'.padEnd(22) + 'BASEAGENT'.padEnd(11) + 'LAST ACTIVE');
1431
1472
  for (const info of list) {
1432
1473
  const name = info.isDefault ? '[default]' : info.name;
1433
- const status = info.status;
1434
- const channels = info.channels.length > 0 ? info.channels.join(', ').slice(0, 22) : '—';
1474
+ const status = info.status || 'stopped';
1475
+ const channels = info.channels?.length > 0 ? info.channels.join(', ').slice(0, 22) : '—';
1435
1476
  const project = info.projectPath ? path.basename(info.projectPath) : '—';
1436
1477
  const baseagent = info.baseagent || '—';
1437
- const lastActive = info.lastActivity ? formatTimeAgo(Date.now() - info.lastActivity) : '—';
1478
+ const lastActive = info.lastActivity
1479
+ ? formatTimeAgo(Date.now() - info.lastActivity)
1480
+ : '—';
1438
1481
  console.log(name.padEnd(14) +
1439
1482
  status.padEnd(10) +
1440
1483
  channels.padEnd(24) +
@@ -1445,7 +1488,33 @@ async function cmdAgentList() {
1445
1488
  }
1446
1489
  async function cmdAgentShow(name) {
1447
1490
  const p = resolvePaths();
1448
- const { AgentRegistry } = await import('./core/agent-registry.js');
1491
+ // Try IPC first
1492
+ try {
1493
+ const result = await ipcQuery(p.socket, { type: 'evolagent.show', name });
1494
+ if (result && result.ok && result.agent) {
1495
+ const info = result.agent;
1496
+ console.log(`${info.name} (${info.status})\n`);
1497
+ console.log(` Baseagent: ${info.baseagent}`);
1498
+ if (info.model)
1499
+ console.log(` Model: ${info.model}`);
1500
+ if (info.effort)
1501
+ console.log(` Effort: ${info.effort}`);
1502
+ console.log(` Project: ${info.projectPath}`);
1503
+ console.log(` Channels: ${info.channels?.join(', ') || '—'}`);
1504
+ if (info.activeSessions)
1505
+ console.log(` Sessions: ${info.activeSessions} active`);
1506
+ if (info.lastActivity)
1507
+ console.log(` Last active: ${formatTimeAgo(Date.now() - info.lastActivity)}`);
1508
+ if (info.error)
1509
+ console.log(` Error: ${info.error}`);
1510
+ return;
1511
+ }
1512
+ }
1513
+ catch {
1514
+ // IPC unavailable — fall through to cold mode
1515
+ }
1516
+ // Cold mode
1517
+ const { EvolAgentRegistry } = await import('./core/evolagent-registry.js');
1449
1518
  const { loadConfig } = await import('./config.js');
1450
1519
  let config;
1451
1520
  try {
@@ -1454,7 +1523,7 @@ async function cmdAgentShow(name) {
1454
1523
  catch {
1455
1524
  config = { agents: {}, channels: {}, projects: { defaultPath: process.cwd() } };
1456
1525
  }
1457
- const registry = new AgentRegistry(p.agentsDir);
1526
+ const registry = new EvolAgentRegistry(p.agentsDir);
1458
1527
  registry.loadAll(config);
1459
1528
  const agent = registry.get(name);
1460
1529
  if (!agent) {
@@ -1478,22 +1547,67 @@ async function cmdAgentShow(name) {
1478
1547
  if (agent.configPath)
1479
1548
  console.log(` Config: ${agent.configPath}`);
1480
1549
  }
1481
- async function cmdAgentNew(name) {
1550
+ async function cmdAgentNew(suggestedName) {
1482
1551
  const p = resolvePaths();
1483
- const agentPath = path.join(p.agentsDir, `${name}.json`);
1484
- if (fs.existsSync(agentPath)) {
1485
- console.error(`Agent "${name}" already exists: ${agentPath}`);
1486
- process.exit(1);
1487
- }
1488
1552
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1489
1553
  const ask = (q) => new Promise(r => rl.question(q, r));
1554
+ let name;
1490
1555
  try {
1556
+ const prompt = suggestedName
1557
+ ? `Agent name [${suggestedName}]: `
1558
+ : 'Agent name: ';
1559
+ const input = (await ask(prompt)).trim();
1560
+ name = input || suggestedName;
1561
+ if (!name) {
1562
+ console.error('Agent name is required.');
1563
+ process.exit(1);
1564
+ }
1565
+ // Disallow dots/slashes/spaces in agent name (used as filename + channel-name prefix)
1566
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
1567
+ console.error(`Invalid agent name "${name}": only letters/digits/_/- allowed`);
1568
+ process.exit(1);
1569
+ }
1570
+ const agentPath = path.join(p.agentsDir, `${name}.json`);
1571
+ if (fs.existsSync(agentPath)) {
1572
+ console.error(`Agent "${name}" already exists: ${agentPath}`);
1573
+ process.exit(1);
1574
+ }
1491
1575
  console.log(`\nCreating agent: ${name}\n`);
1492
- const projectPath = await ask('Project path: ');
1493
- if (!projectPath.trim() || !path.isAbsolute(projectPath.trim())) {
1576
+ // Suggest project path: sibling of evolclaw.json's defaultPath, named after agent
1577
+ let suggestedProjectPath = '';
1578
+ try {
1579
+ const cfg = loadConfig(p.config);
1580
+ const defaultProjectsRoot = cfg.projects?.defaultPath
1581
+ ? path.dirname(cfg.projects.defaultPath)
1582
+ : path.join(os.homedir(), 'evolclaw-projects');
1583
+ suggestedProjectPath = path.join(defaultProjectsRoot, name);
1584
+ }
1585
+ catch {
1586
+ suggestedProjectPath = path.join(os.homedir(), 'evolclaw-projects', name);
1587
+ }
1588
+ const projectInput = (await ask(`Project path [${suggestedProjectPath}]: `)).trim();
1589
+ const projectPath = projectInput || suggestedProjectPath;
1590
+ if (!path.isAbsolute(projectPath)) {
1494
1591
  console.error('Project path must be an absolute path.');
1495
1592
  process.exit(1);
1496
1593
  }
1594
+ if (!fs.existsSync(projectPath)) {
1595
+ const create = (await ask(`Project path does not exist. Create ${projectPath}? [Y/n]: `)).trim().toLowerCase();
1596
+ if (create === '' || create === 'y' || create === 'yes') {
1597
+ try {
1598
+ fs.mkdirSync(projectPath, { recursive: true });
1599
+ console.log(` ✓ Created ${projectPath}`);
1600
+ }
1601
+ catch (e) {
1602
+ console.error(`Failed to create ${projectPath}: ${e?.message || e}`);
1603
+ process.exit(1);
1604
+ }
1605
+ }
1606
+ else {
1607
+ console.error('Aborted: project path does not exist.');
1608
+ process.exit(1);
1609
+ }
1610
+ }
1497
1611
  const baseagentChoices = ['claude', 'codex', 'gemini', 'hermes'];
1498
1612
  const baseagent = (await ask(`Baseagent (${baseagentChoices.join('/')}) [claude]: `)).trim() || 'claude';
1499
1613
  if (!baseagentChoices.includes(baseagent)) {
@@ -1503,7 +1617,19 @@ async function cmdAgentNew(name) {
1503
1617
  const model = (await ask('Model (leave empty for default): ')).trim() || undefined;
1504
1618
  const effort = (await ask('Effort (low/medium/high/max) [high]: ')).trim() || 'high';
1505
1619
  const chatmodeChoices = ['interactive', 'proactive'];
1506
- const chatmodePrivate = (await ask(`ChatMode private (${chatmodeChoices.join('/')}) [interactive]: `)).trim() || 'interactive';
1620
+ console.log('\nChat mode determines how the agent responds:');
1621
+ console.log(' interactive — replies to every message; agent output sent automatically');
1622
+ console.log(' proactive — silent by default; agent decides when/whether to send via ctl send');
1623
+ const chatmodePrivate = (await ask(`Private (1-on-1) chat mode (${chatmodeChoices.join('/')}) [interactive]: `)).trim() || 'interactive';
1624
+ if (!chatmodeChoices.includes(chatmodePrivate)) {
1625
+ console.error(`Invalid chat mode: ${chatmodePrivate}`);
1626
+ process.exit(1);
1627
+ }
1628
+ const chatmodeGroup = (await ask(`Group chat mode (${chatmodeChoices.join('/')}) [proactive]: `)).trim() || 'proactive';
1629
+ if (!chatmodeChoices.includes(chatmodeGroup)) {
1630
+ console.error(`Invalid chat mode: ${chatmodeGroup}`);
1631
+ process.exit(1);
1632
+ }
1507
1633
  // Channels (loop to allow multiple)
1508
1634
  const channelsConfig = {};
1509
1635
  const { getChannelCredentialCollector } = await import('./utils/init-channel.js');
@@ -1538,9 +1664,13 @@ async function cmdAgentNew(name) {
1538
1664
  continue;
1539
1665
  }
1540
1666
  const nameRl = readline.createInterface({ input: process.stdin, output: process.stdout });
1541
- const instName = await new Promise(r => nameRl.question(` Channel instance name [${name}]: `, r));
1667
+ const defaultEffName = `${name}-${channelType}`;
1668
+ const instName = await new Promise(r => nameRl.question(` Channel instance name (leave empty for ${defaultEffName}): `, r));
1542
1669
  nameRl.close();
1543
- creds.name = instName.trim() || name;
1670
+ const trimmedName = instName.trim();
1671
+ if (trimmedName)
1672
+ creds.name = trimmedName;
1673
+ // else: omit name; effective channel name will be ${agent.name}-${type} via EvolAgent.effectiveChannelName
1544
1674
  if (!channelsConfig[channelType])
1545
1675
  channelsConfig[channelType] = [];
1546
1676
  channelsConfig[channelType].push(creds);
@@ -1556,7 +1686,7 @@ async function cmdAgentNew(name) {
1556
1686
  agents: { [baseagent]: { ...(model && { model }), effort } },
1557
1687
  channels: finalChannels,
1558
1688
  projects: { defaultPath: projectPath.trim() },
1559
- chatmode: { private: chatmodePrivate, group: 'proactive' },
1689
+ chatmode: { private: chatmodePrivate, group: chatmodeGroup },
1560
1690
  };
1561
1691
  fs.mkdirSync(p.agentsDir, { recursive: true });
1562
1692
  fs.writeFileSync(agentPath, JSON.stringify(agentConfig, null, 2));
@@ -1571,6 +1701,160 @@ async function cmdAgentNew(name) {
1571
1701
  catch { }
1572
1702
  }
1573
1703
  }
1704
+ async function cmdAgentNewNonInteractive(name, args) {
1705
+ const p = resolvePaths();
1706
+ const agentPath = path.join(p.agentsDir, `${name}.json`);
1707
+ if (fs.existsSync(agentPath)) {
1708
+ console.error(`Agent "${name}" already exists: ${agentPath}`);
1709
+ process.exit(1);
1710
+ }
1711
+ // Helper: extract --flag value from args
1712
+ const getArg = (flag) => {
1713
+ const idx = args.indexOf(flag);
1714
+ return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
1715
+ };
1716
+ // Required: baseagent + project
1717
+ const baseagent = getArg('--baseagent');
1718
+ if (!baseagent) {
1719
+ console.error('--baseagent is required (claude|codex|gemini|hermes)');
1720
+ process.exit(1);
1721
+ }
1722
+ const baseagentChoices = ['claude', 'codex', 'gemini', 'hermes'];
1723
+ if (!baseagentChoices.includes(baseagent)) {
1724
+ console.error(`Invalid --baseagent: ${baseagent}`);
1725
+ process.exit(1);
1726
+ }
1727
+ const project = getArg('--project');
1728
+ if (!project) {
1729
+ console.error('--project is required (absolute path)');
1730
+ process.exit(1);
1731
+ }
1732
+ if (!path.isAbsolute(project)) {
1733
+ console.error(`--project must be absolute: ${project}`);
1734
+ process.exit(1);
1735
+ }
1736
+ if (!fs.existsSync(project)) {
1737
+ try {
1738
+ fs.mkdirSync(project, { recursive: true });
1739
+ console.log(` ✓ Created ${project}`);
1740
+ }
1741
+ catch (e) {
1742
+ console.error(`Failed to create ${project}: ${e?.message || e}`);
1743
+ process.exit(1);
1744
+ }
1745
+ }
1746
+ // Optional: chatmode
1747
+ const chatmodePrivate = getArg('--chatmode-private') || 'interactive';
1748
+ const chatmodeGroup = getArg('--chatmode-group') || 'proactive';
1749
+ const chatmodeValid = new Set(['interactive', 'proactive']);
1750
+ if (!chatmodeValid.has(chatmodePrivate)) {
1751
+ console.error(`Invalid --chatmode-private: ${chatmodePrivate}`);
1752
+ process.exit(1);
1753
+ }
1754
+ if (!chatmodeValid.has(chatmodeGroup)) {
1755
+ console.error(`Invalid --chatmode-group: ${chatmodeGroup}`);
1756
+ process.exit(1);
1757
+ }
1758
+ // Channels
1759
+ const channelsConfig = {};
1760
+ // AUN
1761
+ const aunAid = getArg('--aun-aid');
1762
+ const aunOwner = getArg('--aun-owner');
1763
+ if (aunAid || aunOwner) {
1764
+ if (!aunAid || !aunOwner) {
1765
+ console.error('--aun-aid and --aun-owner must both be provided');
1766
+ process.exit(1);
1767
+ }
1768
+ const { isValidAid, aidCreate } = await import('./channels/aun-ops.js');
1769
+ if (!isValidAid(aunAid)) {
1770
+ console.error(`Invalid --aun-aid: ${aunAid}`);
1771
+ process.exit(1);
1772
+ }
1773
+ if (!isValidAid(aunOwner)) {
1774
+ console.error(`Invalid --aun-owner: ${aunOwner}`);
1775
+ process.exit(1);
1776
+ }
1777
+ try {
1778
+ const result = await aidCreate(aunAid);
1779
+ try {
1780
+ await result.client.close();
1781
+ }
1782
+ catch { /* ignore */ }
1783
+ console.log(`✓ AID ${result.alreadyExisted ? 'reused' : 'created'}: ${aunAid}`);
1784
+ }
1785
+ catch (e) {
1786
+ console.error(`AID creation failed: ${e?.message || e}`);
1787
+ process.exit(1);
1788
+ }
1789
+ channelsConfig.aun = { enabled: true, aid: aunAid, owner: aunOwner };
1790
+ }
1791
+ // Feishu
1792
+ const feishuAppId = getArg('--feishu-app-id');
1793
+ const feishuAppSecret = getArg('--feishu-app-secret');
1794
+ if (feishuAppId || feishuAppSecret) {
1795
+ if (!feishuAppId || !feishuAppSecret) {
1796
+ console.error('--feishu-app-id and --feishu-app-secret must both be provided');
1797
+ process.exit(1);
1798
+ }
1799
+ channelsConfig.feishu = [{
1800
+ name: `feishu-${name}`,
1801
+ enabled: true,
1802
+ appId: feishuAppId,
1803
+ appSecret: feishuAppSecret,
1804
+ }];
1805
+ }
1806
+ // WeChat
1807
+ const wechatToken = getArg('--wechat-token');
1808
+ if (wechatToken) {
1809
+ channelsConfig.wechat = { enabled: true, token: wechatToken };
1810
+ }
1811
+ // WeCom
1812
+ const wecomBotId = getArg('--wecom-bot-id');
1813
+ const wecomSecret = getArg('--wecom-secret');
1814
+ if (wecomBotId || wecomSecret) {
1815
+ if (!wecomBotId || !wecomSecret) {
1816
+ console.error('--wecom-bot-id and --wecom-secret must both be provided');
1817
+ process.exit(1);
1818
+ }
1819
+ channelsConfig.wecom = { enabled: true, botId: wecomBotId, secret: wecomSecret };
1820
+ }
1821
+ // DingTalk
1822
+ const dingtalkClientId = getArg('--dingtalk-client-id');
1823
+ const dingtalkClientSecret = getArg('--dingtalk-client-secret');
1824
+ if (dingtalkClientId || dingtalkClientSecret) {
1825
+ if (!dingtalkClientId || !dingtalkClientSecret) {
1826
+ console.error('--dingtalk-client-id and --dingtalk-client-secret must both be provided');
1827
+ process.exit(1);
1828
+ }
1829
+ channelsConfig.dingtalk = { enabled: true, clientId: dingtalkClientId, clientSecret: dingtalkClientSecret };
1830
+ }
1831
+ // QQBot
1832
+ const qqbotAppId = getArg('--qqbot-app-id');
1833
+ const qqbotClientSecret = getArg('--qqbot-client-secret');
1834
+ if (qqbotAppId || qqbotClientSecret) {
1835
+ if (!qqbotAppId || !qqbotClientSecret) {
1836
+ console.error('--qqbot-app-id and --qqbot-client-secret must both be provided');
1837
+ process.exit(1);
1838
+ }
1839
+ channelsConfig.qqbot = { enabled: true, appId: qqbotAppId, clientSecret: qqbotClientSecret };
1840
+ }
1841
+ if (Object.keys(channelsConfig).length === 0) {
1842
+ console.error('At least one channel must be configured (aun / feishu / wechat / wecom / dingtalk / qqbot)');
1843
+ process.exit(1);
1844
+ }
1845
+ const agentConfig = {
1846
+ name,
1847
+ enabled: true,
1848
+ agents: { [baseagent]: {} },
1849
+ channels: channelsConfig,
1850
+ projects: { defaultPath: project },
1851
+ chatmode: { private: chatmodePrivate, group: chatmodeGroup },
1852
+ };
1853
+ fs.mkdirSync(p.agentsDir, { recursive: true });
1854
+ fs.writeFileSync(agentPath, JSON.stringify(agentConfig, null, 2));
1855
+ console.log(`✓ Created: ${agentPath}`);
1856
+ console.log(' Run `evolclaw restart` (or `evolclaw agent reload <name>`) to activate.');
1857
+ }
1574
1858
  // ==================== AID ====================
1575
1859
  async function cmdAid(args) {
1576
1860
  const sub = args[0] || 'list';
@@ -1856,6 +2140,19 @@ Commands:
1856
2140
  agent 列出所有 agent
1857
2141
  agent <name> 查看指定 agent 详情
1858
2142
  agent new <name> 创建新 agent(交互式)
2143
+ agent new <name> --non-interactive ... 非交互创建(自动化)
2144
+ 必填: --baseagent <claude|codex|gemini|hermes>
2145
+ --project <absolute path>
2146
+ 可选 channel:
2147
+ --aun-aid <aid> --aun-owner <aid>
2148
+ --feishu-app-id xxx --feishu-app-secret yyy
2149
+ --wechat-token xxx
2150
+ --wecom-bot-id xxx --wecom-secret yyy
2151
+ --dingtalk-client-id xxx --dingtalk-client-secret yyy
2152
+ --qqbot-app-id xxx --qqbot-client-secret yyy
2153
+ 可选行为:
2154
+ --chatmode-private <interactive|proactive> (默认 interactive)
2155
+ --chatmode-group <interactive|proactive> (默认 proactive)
1859
2156
  agent reload <n> 热重载 agent 配置
1860
2157
  aid AID 身份管理
1861
2158
  aid list 列出本地所有 AID