#!/usr/bin/env node /** * demo-harness-company.js — 创建一家 AI 工具公司来演练 Harness 四阶段流转 * * 前置条件: 服务器已在 3000 端口运行 (node server/index.js) * * 本脚本会完成以下动作: * Step 1: 创建公司 "aitoolbox"(跳过默认 Bootstrap 自举,我们手动控制) * Step 2: 招聘一个 base 模板的"代码矿工" (feature-dev) * Step 3: 验证该员工的 SKILL.md 是否被挂载为 meta-research(初始阶段) * Step 4: 派发一个 P1 任务 (不触发审批拦截, 但会触发四阶段流转) * Step 5: 手动模拟 transition research -> plan -> execute -> verify * Step 6: 检查各阶段产出与日志 * * 用法: * node scripts/demo-harness-company.js */ const http = require('http'); const fs = require('fs'); const path = require('path'); const API = 'http://localhost:3000/api'; const ROOT = path.resolve(__dirname, '..'); const COMPANY_ID = 'aitoolbox'; const WORKER_ROLE = 'feature-dev'; const WORKER_ID = `${COMPANY_ID}:${WORKER_ROLE}`; function post(urlPath, body) { return new Promise((resolve, reject) => { const data = JSON.stringify(body); const url = new URL(API + urlPath); const req = http.request({ hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }, }, (res) => { let buf = ''; res.on('data', c => buf += c); res.on('end', () => { try { resolve({ status: res.statusCode, data: JSON.parse(buf) }); } catch { resolve({ status: res.statusCode, data: buf }); } }); }); req.on('error', reject); req.write(data); req.end(); }); } function get(urlPath) { return new Promise((resolve, reject) => { const url = new URL(API + urlPath); http.get(url.href, (res) => { let buf = ''; res.on('data', c => buf += c); res.on('end', () => { try { resolve({ status: res.statusCode, data: JSON.parse(buf) }); } catch { resolve({ status: res.statusCode, data: buf }); } }); }).on('error', reject); }); } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } function banner(msg) { console.log(`\n${'═'.repeat(60)}\n ${msg}\n${'═'.repeat(60)}`); } function step(n, msg) { console.log(`\n🔹 Step ${n}: ${msg}`); } function ok(msg) { console.log(` ✅ ${msg}`); } function warn(msg) { console.log(` ⚠️ ${msg}`); } function fail(msg) { console.log(` ❌ ${msg}`); } async function main() { banner('🏢 AI ToolBox 公司 — Harness Engineering 端到端演练'); // ────────────────────────────────────── // Step 1: 创建公司 (跳过 Bootstrap) // ────────────────────────────────────── step(1, '创建公司 aitoolbox (跳过默认 Bootstrap)'); const createRes = await post('/company/create', { id: COMPANY_ID, name: 'AI ToolBox', description: '专注于 AI 开发者工具的极客公司', skipBootstrap: true, }); if (createRes.status === 200 || createRes.status === 201) { ok(`公司 "${COMPANY_ID}" 创建成功`); } else if (createRes.data?.error?.includes('已存在')) { warn(`公司 "${COMPANY_ID}" 已存在, 继续执行`); } else { fail(`创建失败: ${JSON.stringify(createRes.data)}`); } // ────────────────────────────────────── // Step 2: 招聘一个 Base 模板的 Agent // ────────────────────────────────────── step(2, `招聘 ${WORKER_ROLE} (使用 base 模板)`); const hireRes = await post(`/company/${COMPANY_ID}/hire`, { role: WORKER_ROLE, type: 'base', name: '代码矿工', description: '负责功能开发、代码编写、Bug 修复', }); if (hireRes.status === 200) { ok(`${WORKER_ID} 招聘成功: ${JSON.stringify(hireRes.data?.status || 'ok')}`); } else { warn(`招聘结果: ${JSON.stringify(hireRes.data)}`); } // 等一下让脚手架完成文件写入 await sleep(1000); // ────────────────────────────────────── // Step 3: 验证初始状态 // ────────────────────────────────────── step(3, '验证 Agent 初始状态'); const agentDir = path.join(ROOT, 'data/companies', COMPANY_ID, 'agents', WORKER_ROLE); // 3a: 手动复制 base template 到这个 agent(模拟四阶段范式挂载) const baseAgentMd = path.join(ROOT, 'system/templates/base/agent.md'); const targetAgentMd = path.join(agentDir, 'agent.md'); if (fs.existsSync(baseAgentMd) && fs.existsSync(agentDir)) { const currentContent = fs.existsSync(targetAgentMd) ? fs.readFileSync(targetAgentMd, 'utf-8') : ''; if (!currentContent.includes('四段式法则')) { fs.copyFileSync(baseAgentMd, targetAgentMd); ok('已将 Base Agent 四阶段规范注入到 agent.md'); } else { ok('agent.md 已包含四阶段规范'); } } // 3b: 挂载初始 meta-research 技能 const researchSkill = path.join(ROOT, 'system/skills/meta-research/SKILL.md'); const mainSkillDir = path.join(agentDir, '.claude', 'skills', 'main_skill'); const mainSkillPath = path.join(mainSkillDir, 'SKILL.md'); if (fs.existsSync(researchSkill) && fs.existsSync(agentDir)) { if (!fs.existsSync(mainSkillDir)) fs.mkdirSync(mainSkillDir, { recursive: true }); fs.copyFileSync(researchSkill, mainSkillPath); ok('已挂载 meta-research 作为初始技能'); } // 3c: 复制 base config.json (restricted 工具策略) const baseConfig = path.join(ROOT, 'system/templates/base/config.json'); const agentConfig = path.join(agentDir, 'config.json'); if (fs.existsSync(baseConfig) && fs.existsSync(agentDir)) { const existingConfig = fs.existsSync(agentConfig) ? JSON.parse(fs.readFileSync(agentConfig, 'utf-8')) : {}; const baseData = JSON.parse(fs.readFileSync(baseConfig, 'utf-8')); const merged = { ...existingConfig, ...baseData, id: WORKER_ROLE }; fs.writeFileSync(agentConfig, JSON.stringify(merged, null, 2), 'utf-8'); ok(`config.json 已合并 restricted 工具策略: ${JSON.stringify(merged.cli?.toolPolicy?.mode)}`); } // ────────────────────────────────────── // Step 4: 派发 P1 开发任务 // ────────────────────────────────────── step(4, '派发 P1 开发任务'); const taskRes = await post('/agent/task/dispatch', { from: `${COMPANY_ID}:ceo`, to: WORKER_ID, task: '请帮我调研一下项目 server/routes/ 目录下所有的 API 路由文件,统计有多少个 GET/POST/PUT/DELETE 端点,然后写一个简洁的 API 清单文档。', priority: 'P1', autoExecute: false, // 不自动执行, 我们手动控制流转 }); if (taskRes.status === 200 && taskRes.data?.taskId) { ok(`任务已派发: ${taskRes.data.taskId}`); } else { warn(`派发结果: ${JSON.stringify(taskRes.data)}`); } // ────────────────────────────────────── // Step 5: 模拟四阶段流转 // ────────────────────────────────────── step(5, '模拟四阶段流转 (Research → Plan → Execute → Verify)'); const phases = ['plan', 'execute', 'verify']; const phaseNames = { plan: '规划', execute: '执行', verify: '验证' }; const skillNames = { plan: 'meta-plan', execute: 'meta-execute', verify: 'meta-verify' }; // 当前处于 Research 阶段, 模拟产出 _research.md const wsDir = path.join(agentDir, 'workspace'); if (!fs.existsSync(wsDir)) fs.mkdirSync(wsDir, { recursive: true }); fs.writeFileSync(path.join(wsDir, '_research.md'), `# 调研结果 - server/routes/ 中有约 10 个 API 路由文件 - 主要模块: agent-chat.js, company.js, goals.js, evolution.js 等 - 估计有 60+ 个 REST endpoint `, 'utf-8'); ok('Research 阶段: 已产出 workspace/_research.md'); for (const phase of phases) { console.log(`\n 🔄 触发 transition → ${phase} (${phaseNames[phase]})`); const trRes = await post(`/agent/${WORKER_ID}/task/transition`, { phase, artifact: phase === 'plan' ? 'workspace/_research.md' : (phase === 'execute' ? 'workspace/_plan.md' : ''), }); if (trRes.status === 200 && trRes.data?.success) { ok(`Transition → ${phase} 成功: ${trRes.data.message}`); } else { fail(`Transition → ${phase} 失败: ${JSON.stringify(trRes.data)}`); continue; } // 验证 SKILL.md 被替换 await sleep(500); if (fs.existsSync(mainSkillPath)) { const content = fs.readFileSync(mainSkillPath, 'utf-8'); if (content.includes(skillNames[phase])) { ok(`SKILL.md 已切换为 ${skillNames[phase]}`); } else { warn(`SKILL.md 内容未包含 ${skillNames[phase]} 关键词`); } } // 模拟该阶段的产出 (只有 plan 阶段写 _plan.md) if (phase === 'plan') { fs.writeFileSync(path.join(wsDir, '_plan.md'), `# 执行计划 1. 遍历 server/routes/*.js 文件 2. 用正则提取 router.get/post/put/delete 端点 3. 聚合为 Markdown 表格输出到 workspace/api-inventory.md `, 'utf-8'); ok('Plan 阶段: 已产出 workspace/_plan.md'); } if (phase === 'execute') { fs.writeFileSync(path.join(wsDir, 'api-inventory.md'), `# API 清单 | 方法 | 路径 | 模块 | |------|------|------| | GET | /api/agent/list | agent-chat.js | | POST | /api/agent/:id/chat | agent-chat.js | | POST | /api/company/create | company.js | | ... | ... | ... | `, 'utf-8'); ok('Execute 阶段: 已产出 workspace/api-inventory.md'); } } // ────────────────────────────────────── // Step 6: 最终检查 // ────────────────────────────────────── step(6, '最终检查'); // 检查所有产出文件 const expectedFiles = ['_research.md', '_plan.md', 'api-inventory.md']; for (const f of expectedFiles) { const fp = path.join(wsDir, f); if (fs.existsSync(fp)) { const size = fs.statSync(fp).size; ok(`${f} 存在 (${size} bytes)`); } else { fail(`${f} 缺失`); } } // 检查当前挂载的技能是 meta-verify if (fs.existsSync(mainSkillPath)) { const finalSkill = fs.readFileSync(mainSkillPath, 'utf-8'); if (finalSkill.includes('meta-verify')) { ok('最终技能树: meta-verify ✓ (验证阶段)'); } else { warn('最终技能树不是 meta-verify'); } } // 检查 billing.log const billingPath = path.join(ROOT, 'data/logs/billing.log'); if (fs.existsSync(billingPath)) { ok(`billing.log 存在 (${fs.statSync(billingPath).size} bytes)`); } else { warn('billing.log 尚未产生 (需要真实 Agent 对话才会写入)'); } banner('🎉 演练完成!AI ToolBox 四阶段流转验证通过'); console.log(` 📋 验证清单: ✅ 公司创建 + 员工招聘 (aitoolbox:feature-dev) ✅ Base Agent 规范注入 (agent.md + config.json restricted) ✅ 初始技能挂载 (meta-research) ✅ Transition API: research → plan → execute → verify ✅ 每次 Transition 的 Hot Swap 换脑验证 ✅ 各阶段 Artifact 产出 (_research.md / _plan.md / api-inventory.md) 📂 公司目录: data/companies/${COMPANY_ID}/ 📂 员工目录: data/companies/${COMPANY_ID}/agents/${WORKER_ROLE}/ 💡 接下来你可以: 1. 用 UI 打开 ${WORKER_ID} 跟它对话,观察它的受限行为 2. 派发一个 P0 任务测试审批拦截: node bin/lemon.js task dispatch --from ${COMPANY_ID}:ceo --to ${WORKER_ID} --task "重构核心架构" --priority P0 3. 查看 billing.log: cat data/logs/billing.log `); } main().catch(err => { console.error('❌ 脚本执行错误:', err.message); process.exit(1); });