config(claude): 角色路由 hook (dev/test/pm) + role-router 单测

- claude-config/hooks/role-router.mjs
  按 user prompt 关键词检测当前角色,注入推荐 agent hint
  - dev: 开发/实现/重构/修(复|bug)/dev/implement/refactor/fix/feature → executor/debugger
  - test: 测试/单测/回归/QA/test/regression/spec → qa-tester/test-engineer/verifier
  - pm: 产品/需求/排期/规划/PM/product/PRD/roadmap → planner/analyst/scientist
  支持多角色同时命中
- claude-config/hooks/role-router.test.mjs
  10 fixture 单测 (含 subprocess pipe integration), 10/10 PASS
- claude-config/settings.json.snapshot.20260426
  全局 settings.json 快照, hooks.UserPromptSubmit 接 keyword-detector + role-router
- claude-config/README.md
  设计/安装/测试 文档

来源: ralph US-3, a+b 融合方案 (211 中文 agents + 关键词 hook)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
pp
2026-04-26 21:36:34 +08:00
parent 45409c60d2
commit 8915fa2898
4 changed files with 245 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
#!/usr/bin/env node
/**
* Role Router Hook (UserPromptSubmit)
* 按 user prompt 关键词检测当前角色dev/test/pm注入推荐 agent hint。
*
* 三种角色:
* dev → executor / debugger / code-simplifier + engineering-* 中文 agent
* test → qa-tester / test-engineer / verifier + testing-* 中文 agent
* pm → planner / analyst / scientist + product-* / project-management-* 中文 agent
*
* 输出: stdout JSON { hookSpecificOutput: { additionalContext, hookEventName } }
* 不阻塞、不退出非零码(保证主流程继续)。
*/
import { readFileSync } from 'fs';
export const DEV_RE = /(?:\b(?:dev|develop|develops?|implement|implements?|refactor|refactors?|fix|fixes|bug|bugs|hotfix|patch|patches|impl)\b|开发|实现|重构|修(?:复|bug)|写代码|编码|添加功能|新功能|改(?:bug|代码))/i;
export const TEST_RE = /(?:\b(?:tests?|qa|unit\s?tests?|regression|e2e|integration\s?tests?|specs?)\b|测试|单测|单元测试|回归(?:测试)?|集成测试|端到端|质量保证|QA)/i;
export const PM_RE = /(?:\b(?:pm|product|spec|requirement|roadmap|planning|backlog|user\s?story|prd)\b|产品(?:经理)?|需求|排期|规划|计划|调研|路线图|用户故事)/i;
const ROLE_HINTS = {
dev: `<role-routing role="dev">
检测到**开发角色**关键词。本回合优先路由:
- 主 agent: executor (sonnet/opus by 复杂度) / debugger / code-simplifier
- 中文角色补强: engineering-backend-architect / engineering-frontend-architect / engineering-ai-engineer
- 工作流: 默认开 TDD先写失败测试再实现用 git-master 处理分支
</role-routing>`,
test: `<role-routing role="test">
检测到**测试角色**关键词。本回合优先路由:
- 主 agent: qa-tester / test-engineer / verifier
- 中文角色补强: testing-* 系列(中文 agent 库)
- 工作流: 测试先行;产出 PRD acceptance criteria 跟测试断言一一对应;要 evidence-based 验收
</role-routing>`,
pm: `<role-routing role="pm">
检测到**PM 角色**关键词。本回合优先路由:
- 主 agent: planner / analyst / scientist
- 中文角色补强: product-* / project-management-* 系列(中文 agent 库)
- 工作流: 先 deep-interview 拆需求,再 ralplan 起 PRD明确 user stories + acceptance criteria 后才落代码
</role-routing>`,
};
export function detectRoles(prompt) {
const roles = [];
if (DEV_RE.test(prompt)) roles.push('dev');
if (TEST_RE.test(prompt)) roles.push('test');
if (PM_RE.test(prompt)) roles.push('pm');
return roles;
}
export function buildContext(roles) {
if (roles.length === 0) return null;
return roles.map(r => ROLE_HINTS[r]).join('\n\n');
}
function readStdinSync() {
try {
return readFileSync(0, 'utf8');
} catch {
return '';
}
}
function main() {
try {
const raw = readStdinSync();
if (!raw) {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
return;
}
const data = JSON.parse(raw);
const prompt = data.prompt || data.user_prompt || '';
if (!prompt) {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
return;
}
const roles = detectRoles(prompt);
const ctx = buildContext(roles);
if (!ctx) {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
return;
}
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'UserPromptSubmit',
additionalContext: ctx,
},
}));
} catch {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
}
}
const isMain = import.meta.url === `file://${process.argv[1]}`;
if (isMain) main();