Files
clawdbot-memory/claude-config/hooks/role-router.mjs
pp 1445214c3c deslop(claude): role-router 简化 + regex 去重 + stderr 错误日志
按 critic + 3 reviewer agent 反馈:
- HIGH: bare catch {} 改 catch(e){console.error(...)} 暴露 bug
- MED: regex 去重复 (develop|develops? → develops?, fix|fixes → fixes?, requirement|requirements? → requirements?)
- MED: 4 处 {continue:true,suppressOutput:true} 抽 NOOP 常量
- MED: test JSON.stringify(.sort()) → 不可变的 sortKey helper
- skip: createHookOutput 抽 lib (违反"不改 OMC keyword-detector" 决策)
- skip: ROLES enum (3 值 over-engineering)

post-deslop 回归: 10/10 PASS

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 21:44:18 +08:00

88 lines
3.4 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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|develops?|implements?|refactors?|fixes?|bugs?|hotfix|patch(?:es)?|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|requirements?|roadmap|planning|backlog|user\s?story|prd)\b|产品(?:经理)?|需求|排期|规划|计划|调研|路线图|用户故事)/i;
const NOOP = JSON.stringify({ continue: true, suppressOutput: true });
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(NOOP); return; }
const data = JSON.parse(raw);
const prompt = data.prompt || data.user_prompt || '';
if (!prompt) { console.log(NOOP); return; }
const ctx = buildContext(detectRoles(prompt));
if (!ctx) { console.log(NOOP); return; }
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'UserPromptSubmit',
additionalContext: ctx,
},
}));
} catch (e) {
console.error('[role-router]', e.message);
console.log(NOOP);
}
}
const isMain = import.meta.url === `file://${process.argv[1]}`;
if (isMain) main();