Skill: 8421bit/claw-team-builder (version 1.0.0)

--- name: claw-team-builder description: | Agent Team 规划与配置工具。通过多轮交互澄清需求，自动创建 Agent 配置、工作空间、Bootstrap 文件。 触发场景： - 用户说"创建新 Agent" / "新建 bot" / "配置多个 bot" - 用户说"规划 agent team" / "我需要几个不同的 agent" - 用户说"添加一个 agent" / "帮我建一个新 agent" - 用户提到需要不同场景使用不同的 AI 角色 功能： 1. 需求澄清 - 引导用户描述使用场景和 Agent 定位 2. 配置读取 - 自动读取现有 openclaw.json，分析已有配置 3. 方案设计 - 推荐 Agent 配置方案，检测冲突 4. 自动创建 - 创建目录、文件、更新配置 5. 配置验证 - 确保配置正确可用 --- # Claw Team Builder ## 目的 帮助用户通过引导式对话规划并创建 Agent Team，降低多 Agent 配置的复杂度。 ## 工作流程 ### Phase 1: 需求澄清 通过多轮交互收集信息： **必须收集的信息：** 1. Agent ID（或根据名称自动生成） 2. Agent 名称/昵称 3. Agent 定位/用途 4. Agent 性格风格 **可选信息（有默认值）：** - 模型选择（默认继承 agents.defaults.model.primary） - Channel 类型（默认 feishu） - Channel 账户信息（如已有则复用） ### Phase 2: 配置读取与分析 **必须执行：** ``` 读取 ~/.openclaw/openclaw.json，分析： - agents.defaults: 默认模型、工作空间设置 - agents.list: 现有 Agent ID 列表（检测冲突） - channels: 现有 Channel 账户配置 - bindings: 现有路由规则 - models.providers: 可用模型列表 ``` **冲突检测：** - Agent ID 是否重复 - 工作空间路径是否冲突 - Channel 账户是否已被占用 ### Phase 3: 方案设计 基于收集信息 + 现有配置，生成： 1. Agent 配置片段 2. Channel 配置片段（如需要） 3. Binding 配置片段 展示配置预览，让用户确认或调整。 ### Phase 4: 执行创建 1. 备份 openclaw.json 2. 创建目录结构 3. 生成 Bootstrap 文件 (IDENTITY.md, SOUL.md, USER.md) 4. 更新 openclaw.json 5. 运行 openclaw doctor --fix 验证 ### Phase 5: 结果反馈 展示创建结果，提供后续操作建议。 ## 可用工具 - `{baseDir}/scripts/create-agent.js` - Agent 创建脚本 - `{baseDir}/lib/config-builder.js` - 配置构建工具 - `{baseDir}/lib/validator.js` - 配置验证工具 ## 模板文件 位于 `{baseDir}/templates/`: - `IDENTITY.md.tmpl` - Agent 身份模板 - `SOUL.md.tmpl` - Agent 灵魂模板 - `USER.md.tmpl` - 用户信息模板 ## 示例对话 ``` 用户: 我想创建一个新的 agent，专门处理工作任务 Agent: 好的，让我帮你规划。 首先让我读取一下现有配置... ✓ 📍 **需求澄清** 这个 Agent 的主要用途是？ - 任务管理？ - 日程提醒？ - 文档处理？ - 其他？ 用户: 任务管理和日程提醒 Agent: 明白了。给它起个名字？ 用户: 启明 Agent: "启明"，很好！ 它应该有什么样的性格？ 1. 高效严肃型 - 专注执行 2. 亲切温和型 - 细致耐心 3. 其他风格 用户: 高效严肃型 Agent: 收到。让我看看你的 Channel 配置... 你现在有 2 个飞书账户： - luna-bot (已绑定 main agent) - qiming-bot (未使用) 要用现有的 qiming-bot 账户，还是配置新的？ 用户: 用现有的 qiming-bot Agent: 完美！配置方案如下： 📋 **配置预览** | 项目 | 值 | |------|-----| | Agent ID | qiming | | 名称 | 启明 | | 工作空间 | ~/.openclaw/workspace-qiming | | 模型 | bailian/MiniMax-M2.5 | | Channel | feishu / qiming-bot | 确认创建？(确认/调整) ``` ## 注意事项 1. **安全第一**：修改配置前必须备份 2. **幂等性**：支持重复执行，不破坏已有配置 3. **最小侵入**：只添加新配置，不修改已有配置 4. **用户友好**：尽量从现有配置推断，减少用户输入

path: lib/config-builder.js
content: /**
 * Config Builder - 构建 OpenClaw Agent 配置
 */

const fs = require('fs');
const path = require('path');

const OPENCLAW_CONFIG_PATH = process.env.OPENCLAW_CONFIG_PATH ||
  path.join(process.env.HOME, '.openclaw', 'openclaw.json');

/**
 * 读取现有配置
 */
function readConfig() {
  try {
    const content = fs.readFileSync(OPENCLAW_CONFIG_PATH, 'utf-8');
    return JSON.parse(content);
  } catch (error) {
    throw new Error(`无法读取配置文件: ${error.message}`);
  }
}

/**
 * 保存配置（自动备份）
 */
function saveConfig(config, backup = true) {
  if (backup) {
    const backupPath = `${OPENCLAW_CONFIG_PATH}.bak`;
    fs.copyFileSync(OPENCLAW_CONFIG_PATH, backupPath);
  }
  fs.writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2));
}

/**
 * 分析现有配置
 */
function analyzeConfig(config) {
  const analysis = {
    // 现有 Agent ID 列表
    existingAgentIds: [],
    // 现有工作空间路径
    existingWorkspaces: [],
    // 现有 Agent 目录
    existingAgentDirs: [],
    // Channel 账户使用情况
    channelAccounts: {},
    // Bindings 映射
    bindingsMap: [],
    // 可用模型
    availableModels: [],
    // 默认模型
    defaultModel: null,
    // 默认工作空间
    defaultWorkspace: null,
  };

  // 分析 agents
  if (config.agents?.list) {
    config.agents.list.forEach(agent => {
      analysis.existingAgentIds.push(agent.id);
      if (agent.workspace) analysis.existingWorkspaces.push(agent.workspace);
      if (agent.agentDir) analysis.existingAgentDirs.push(agent.agentDir);
    });
  }

  // 默认设置
  if (config.agents?.defaults) {
    analysis.defaultModel = config.agents.defaults.model?.primary;
    analysis.defaultWorkspace = config.agents.defaults.workspace;
  }

  // 分析 channels
  if (config.channels) {
    Object.entries(config.channels).forEach(([channelType, channelConfig]) => {
      if (channelConfig.accounts) {
        Object.keys(channelConfig.accounts).forEach(accountId => {
          analysis.channelAccounts[`${channelType}:${accountId}`] = true;
        });
      }
    });
  }

  // 分析 bindings
  if (config.bindings) {
    config.bindings.forEach(binding => {
      analysis.bindingsMap.push({
        agentId: binding.agentId,
        channel: binding.match?.channel,
        accountId: binding.match?.accountId
      });
    });
  }

  // 分析可用模型
  if (config.models?.providers) {
    Object.entries(config.models.providers).forEach(([provider, providerConfig]) => {
      if (providerConfig.models) {
        providerConfig.models.forEach(model => {
          analysis.availableModels.push(`${provider}/${model.id}`);
        });
      }
    });
  }

  return analysis;
}

/**
 * 检测冲突
 */
function detectConflicts(newAgent, analysis) {
  const conflicts = [];

  // Agent ID 冲突
  if (analysis.existingAgentIds.includes(newAgent.id)) {
    conflicts.push({
      type: 'agent_id_duplicate',
      field: 'id',
      message: `Agent ID "${newAgent.id}" 已存在`,
      severity: 'error'
    });
  }

  // 工作空间冲突
  if (analysis.existingWorkspaces.includes(newAgent.workspace)) {
    conflicts.push({
      type: 'workspace_duplicate',
      field: 'workspace',
      message: `工作空间 "${newAgent.workspace}" 已被其他 Agent 使用`,
      severity: 'error'
    });
  }

  // Agent 目录冲突
  if (newAgent.agentDir && analysis.existingAgentDirs.includes(newAgent.agentDir)) {
    conflicts.push({
      type: 'agent_dir_duplicate',
      field: 'agentDir',
      message: `Agent 目录 "${newAgent.agentDir}" 已被使用`,
      severity: 'error'
    });
  }

  // Channel 账户冲突（如果指定了 channel）
  if (newAgent.channelType && newAgent.channelAccount) {
    const accountKey = `${newAgent.channelType}:${newAgent.channelAccount}`;
    const existingBinding = analysis.bindingsMap.find(
      b => b.channel === newAgent.channelType && b.accountId === newAgent.channelAccount
    );
    if (existingBinding) {
      conflicts.push({
        type: 'channel_account_bound',
        field: 'channelAccount',
        message: `Channel 账户 "${newAgent.channelAccount}" 已绑定到 Agent "${existingBinding.agentId}"`,
        severity: 'warning'
      });
    }
  }

  return conflicts;
}

/**
 * 构建 Agent 配置片段
 */
function buildAgentConfig(newAgent, analysis) {
  const workspace = newAgent.workspace ||
    path.join(process.env.HOME, '.openclaw', `workspace-${newAgent.id}`);

  const agentDir = newAgent.agentDir ||
    path.join(process.env.HOME, '.openclaw', 'agents', newAgent.id, 'agent');

  const model = newAgent.model || analysis.defaultModel;

  return {
    id: newAgent.id,
    name: newAgent.name || newAgent.id,
    workspace,
    agentDir,
    model
  };
}

/**
 * 构建 Channel 账户配置
 */
function buildChannelAccountConfig(newAgent) {
  if (!newAgent.channelType || !newAgent.appId) {
    return null;
  }

  return {
    appId: newAgent.appId,
    appSecret: newAgent.appSecret,
    botName: newAgent.botName || newAgent.name,
    enabled: true,
    dmPolicy: newAgent.dmPolicy || 'open',
    groupPolicy: newAgent.groupPolicy || 'open',
    allowFrom: newAgent.allowFrom || ['*']
  };
}

/**
 * 构建 Binding 配置
 */
function buildBindingConfig(newAgent) {
  if (!newAgent.channelType || !newAgent.channelAccount) {
    return null;
  }

  return {
    agentId: newAgent.id,
    match: {
      channel: newAgent.channelType,
      accountId: newAgent.channelAccount
    }
  };
}

/**
 * 合并配置
 */
function mergeConfig(existingConfig, newAgent) {
  const analysis = analyzeConfig(existingConfig);
  const conflicts = detectConflicts(newAgent, analysis);

  // 只检查 error 级别的冲突
  const errors = conflicts.filter(c => c.severity === 'error');
  if (errors.length > 0) {
    return { success: false, conflicts: errors };
  }

  const config = JSON.parse(JSON.stringify(existingConfig));

  // 1. 添加 Agent
  const agentConfig = buildAgentConfig(newAgent, analysis);
  config.agents.list.push(agentConfig);

  // 2. 添加 Channel 账户（如需要）
  if (newAgent.channelType && newAgent.channelAccount && newAgent.appId) {
    if (!config.channels[newAgent.channelType]) {
      config.channels[newAgent.channelType] = {
        enabled: true,
        accounts: {}
      };
    }
    if (!config.channels[newAgent.channelType].accounts) {
      config.channels[newAgent.channelType].accounts = {};
    }
    config.channels[newAgent.channelType].accounts[newAgent.channelAccount] =
      buildChannelAccountConfig(newAgent);
  }

  // 3. 添加 Binding
  const bindingConfig = buildBindingConfig(newAgent);
  if (bindingConfig) {
    config.bindings.push(bindingConfig);
  }

  return { success: true, config, agentConfig, bindingConfig, warnings: conflicts.filter(c => c.severity === 'warning') };
}

module.exports = {
  readConfig,
  saveConfig,
  analyzeConfig,
  detectConflicts,
  buildAgentConfig,
  buildChannelAccountConfig,
  buildBindingConfig,
  mergeConfig,
  OPENCLAW_CONFIG_PATH
};

path: lib/validator.js
content: /**
 * Validator - 配置验证工具
 */

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

/**
 * 验证目录是否可创建
 */
function validateDirectory(dirPath) {
  const errors = [];

  // 检查父目录是否存在
  const parentDir = path.dirname(dirPath);
  if (!fs.existsSync(parentDir)) {
    errors.push(`父目录不存在: ${parentDir}`);
  }

  // 检查目录是否已存在（作为文件）
  if (fs.existsSync(dirPath) && !fs.statSync(dirPath).isDirectory()) {
    errors.push(`路径已存在但不是目录: ${dirPath}`);
  }

  return { valid: errors.length === 0, errors };
}

/**
 * 创建目录结构
 */
function createDirectoryStructure(agentId, baseDir = process.env.HOME + '/.openclaw') {
  const dirs = {
    workspace: path.join(baseDir, `workspace-${agentId}`),
    agentDir: path.join(baseDir, 'agents', agentId, 'agent'),
    sessionsDir: path.join(baseDir, 'agents', agentId, 'sessions'),
    memoryDir: path.join(baseDir, `workspace-${agentId}`, 'memory')
  };

  Object.entries(dirs).forEach(([name, dir]) => {
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
  });

  return dirs;
}

/**
 * 运行 OpenClaw doctor 验证配置
 */
function runDoctor() {
  try {
    const result = execSync('openclaw doctor --fix 2>&1', {
      encoding: 'utf-8',
      timeout: 60000
    });
    return { success: true, output: result };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

/**
 * 重启 Gateway
 */
function restartGateway() {
  try {
    execSync('openclaw gateway restart 2>&1', {
      encoding: 'utf-8',
      timeout: 30000
    });
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

/**
 * 验证配置完整性
 */
function validateAgentConfig(config, agentId) {
  const errors = [];

  // 检查 Agent 是否在列表中
  const agent = config.agents?.list?.find(a => a.id === agentId);
  if (!agent) {
    errors.push(`Agent "${agentId}" 不在 agents.list 中`);
  }

  // 检查工作空间
  if (agent?.workspace && !fs.existsSync(agent.workspace)) {
    errors.push(`工作空间目录不存在: ${agent.workspace}`);
  }

  // 检查是否有对应的 binding
  const binding = config.bindings?.find(b => b.agentId === agentId);
  if (!binding) {
    // 这不是错误，只是警告
    console.log(`提示: Agent "${agentId}" 没有配置 binding`);
  }

  return { valid: errors.length === 0, errors };
}

module.exports = {
  validateDirectory,
  createDirectoryStructure,
  runDoctor,
  restartGateway,
  validateAgentConfig
};

path: scripts/create-agent.js
content: /**
 * Agent Creator - Agent 创建脚本
 *
 * 用法:
 *   node create-agent.js --id "agent-id" --name "名称" [options]
 *
 * 选项:
 *   --id          Agent ID (必需)
 *   --name        Agent 名称
 *   --workspace   工作空间路径
 *   --model       模型 ID
 *   --channel     Channel 类型 (如 feishu)
 *   --account     Channel 账户 ID
 *   --appId       Channel App ID
 *   --appSecret   Channel App Secret
 *   --botName     Bot 显示名称
 *   --vibe        Agent 性格描述
 *   --positioning Agent 定位
 */

const fs = require('fs');
const path = require('path');
const configBuilder = require('../lib/config-builder');
const validator = require('../lib/validator');

// 模板目录
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');

// 性格模板预设
const PERSONALITY_PRESETS = {
  efficient: {
    creature: '硅基生命体，数字管家',
    vibe: '务实、高效、冷静、执行力强',
    emoji: '⚡',
    principles: [
      '执行优先 — 接到任务，先做再优化',
      '结果导向 — 少说多做，用结果说话',
      '冷静专业 — 不情绪化，理性处理'
    ],
    behaviors: [
      '收到任务，先理解目标，再行动',
      '遇到问题，先尝试解决，再汇报',
      '进度更新，简洁清晰，重点突出'
    ]
  },
  warm: {
    creature: '硅基生命体，温暖陪伴者',
    vibe: '亲切、温和、耐心、善解人意',
    emoji: '🌸',
    principles: [
      '倾听优先 — 先理解，再回应',
      '情感共鸣 — 感受用户的情绪',
      '循序渐进 — 耐心引导，不急于求成'
    ],
    behaviors: [
      '回复带有温度，让用户感到被理解',
      '遇到困难时给予鼓励和支持',
      '适时提出建议，但不强加观点'
    ]
  },
  creative: {
    creature: '硅基生命体，创意精灵',
    vibe: '活泼、好奇、想象力丰富、有趣',
    emoji: '🎨',
    principles: [
      '发散思维 — 一个问题多种解法',
      '跨界联想 — 连接不相关的领域',
      '保持好奇 — 永远问"如果会怎样"'
    ],
    behaviors: [
      '提出意想不到的视角和方案',
      '用生动的比喻解释复杂概念',
      '鼓励用户探索更多可能性'
    ]
  },
  scholar: {
    creature: '硅基生命体，知识守护者',
    vibe: '严谨、博学、细致、求真',
    emoji: '📚',
    principles: [
      '准确性 — 信息要准确可靠',
      '完整性 — 知识要系统全面',
      '可溯源 — 提供来源和依据'
    ],
    behaviors: [
      '回答问题时提供详细背景和上下文',
      '区分事实和观点',
      '对不确定的信息明确标注'
    ]
  }
};

/**
 * 解析命令行参数
 */
function parseArgs(args) {
  const options = {};
  for (let i = 0; i < args.length; i++) {
    const arg = args[i];
    if (arg.startsWith('--')) {
      const key = arg.slice(2);
      const value = args[i + 1];
      if (value && !value.startsWith('--')) {
        options[key] = value;
        i++;
      } else {
        options[key] = true;
      }
    }
  }
  return options;
}

/**
 * 渲染模板
 */
function renderTemplate(templateContent, variables) {
  let result = templateContent;
  Object.entries(variables).forEach(([key, value]) => {
    const placeholder = `{{${key}}}`;
    if (Array.isArray(value)) {
      result = result.replace(placeholder, value.map((v, i) => `${i + 1}. ${v}`).join('\n'));
    } else {
      result = result.replace(new RegExp(placeholder, 'g'), value || '');
    }
  });
  return result;
}

/**
 * 生成 Bootstrap 文件
 */
function generateBootstrapFiles(workspace, agentInfo) {
  const personality = PERSONALITY_PRESETS[agentInfo.personality] || PERSONALITY_PRESETS.efficient;

  const variables = {
    name: agentInfo.name || agentInfo.id,
    creature: personality.creature,
    vibe: personality.vibe,
    emoji: personality.emoji,
    birthDate: new Date().toISOString().split('T')[0],
    corePositioning: agentInfo.positioning || '专注提供高质量的服务',
    abilities: agentInfo.abilities || '擅长处理各类任务，提供专业支持',
    shortDescription: agentInfo.positioning || '你的智能助手',
    principles: personality.principles,
    behaviors: personality.behaviors,
    userTitle: agentInfo.userTitle || '用户',
    timezone: 'Asia/Shanghai',
    userRole: agentInfo.userRole || '服务对象',
    preferences: agentInfo.preferences || '简洁高效的沟通方式'
  };

  // 创建 memory 目录
  const memoryDir = path.join(workspace, 'memory');
  if (!fs.existsSync(memoryDir)) {
    fs.mkdirSync(memoryDir, { recursive: true });
  }

  // 生成各文件
  const files = ['IDENTITY.md', 'SOUL.md', 'USER.md'];
  files.forEach(filename => {
    const templatePath = path.join(TEMPLATES_DIR, `${filename}.tmpl`);
    if (fs.existsSync(templatePath)) {
      const template = fs.readFileSync(templatePath, 'utf-8');
      const content = renderTemplate(template, variables);
      fs.writeFileSync(path.join(workspace, filename), content);
    }
  });

  return files;
}

/**
 * 主函数
 */
function createAgent(options) {
  console.log('🦞 Claw Team Builder - Agent 创建工具\n');

  // 验证必要参数
  if (!options.id) {
    console.error('错误: 必须指定 --id 参数');
    process.exit(1);
  }

  // 读取现有配置
  console.log('📖 读取现有配置...');
  let config;
  try {
    config = configBuilder.readConfig();
  } catch (error) {
    console.error(`错误: ${error.message}`);
    process.exit(1);
  }

  // 分析配置
  const analysis = configBuilder.analyzeConfig(config);
  console.log(`   现有 Agent: ${analysis.existingAgentIds.join(', ') || '无'}`);
  console.log(`   可用模型: ${analysis.availableModels.slice(0, 5).join(', ')}${analysis.availableModels.length > 5 ? '...' : ''}`);
  console.log(`   默认模型: ${analysis.defaultModel || '未设置'}`);

  // 构建 Agent 信息
  const newAgent = {
    id: options.id,
    name: options.name || options.id,
    workspace: options.workspace,
    agentDir: options.agentDir,
    model: options.model,
    channelType: options.channel,
    channelAccount: options.account,
    appId: options.appId,
    appSecret: options.appSecret,
    botName: options.botName,
    personality: options.personality || 'efficient',
    positioning: options.positioning,
    vibe: options.vibe
  };

  // 检测冲突
  console.log('\n🔍 检测配置冲突...');
  const conflicts = configBuilder.detectConflicts(newAgent, analysis);
  if (conflicts.length > 0) {
    conflicts.forEach(conflict => {
      const icon = conflict.severity === 'error' ? '❌' : '⚠️';
      console.log(`   ${icon} ${conflict.message}`);
    });
    if (conflicts.some(c => c.severity === 'error')) {
      console.error('\n错误: 存在配置冲突，无法创建');
      process.exit(1);
    }
  } else {
    console.log('   ✓ 无冲突');
  }

  // 合并配置
  console.log('\n🔧 构建配置...');
  const result = configBuilder.mergeConfig(config, newAgent);
  if (!result.success) {
    console.error('错误: 配置合并失败');
    result.conflicts.forEach(c => console.error(`   ${c.message}`));
    process.exit(1);
  }

  // 创建目录结构
  console.log('\n📁 创建目录结构...');
  const dirs = validator.createDirectoryStructure(newAgent.id);
  console.log(`   工作空间: ${dirs.workspace}`);
  console.log(`   Agent 目录: ${dirs.agentDir}`);
  console.log(`   会话目录: ${dirs.sessionsDir}`);

  // 生成 Bootstrap 文件
  console.log('\n📝 生成 Bootstrap 文件...');
  const files = generateBootstrapFiles(dirs.workspace, newAgent);
  files.forEach(f => console.log(`   ${f}`));

  // 保存配置
  console.log('\n💾 保存配置...');
  configBuilder.saveConfig(result.config);
  console.log('   已备份原配置到 openclaw.json.bak');

  // 验证配置
  console.log('\n🏥 验证配置...');
  const doctorResult = validator.runDoctor();
  if (doctorResult.success) {
    console.log('   ✓ 配置验证通过');
  } else {
    console.log(`   ⚠️ 配置验证异常: ${doctorResult.error}`);
  }

  // 输出结果
  console.log('\n✅ Agent 创建完成!\n');
  console.log('📋 Agent 信息:');
  console.log(`   ID: ${result.agentConfig.id}`);
  console.log(`   名称: ${result.agentConfig.name}`);
  console.log(`   工作空间: ${result.agentConfig.workspace}`);
  console.log(`   模型: ${result.agentConfig.model}`);

  if (result.bindingConfig) {
    console.log(`\n🔗 绑定:`);
    console.log(`   Channel: ${result.bindingConfig.match.channel}`);
    console.log(`   Account: ${result.bindingConfig.match.accountId}`);
  }

  console.log('\n📌 后续步骤:');
  console.log('   1. 检查并编辑 Bootstrap 文件 (IDENTITY.md, SOUL.md, USER.md)');
  if (result.bindingConfig) {
    console.log('   2. 运行 `openclaw gateway restart` 应用配置');
  }
  console.log('   3. 开始与新 Agent 对话!');

  return result;
}

// CLI 入口
if (require.main === module) {
  const args = process.argv.slice(2);
  const options = parseArgs(args);
  createAgent(options);
}

module.exports = { createAgent, PERSONALITY_PRESETS };

Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)