From 1445214c3c7a3687392ff86b4e45865415289c79 Mon Sep 17 00:00:00 2001 From: pp Date: Sun, 26 Apr 2026 21:44:18 +0800 Subject: [PATCH] =?UTF-8?q?deslop(claude):=20role-router=20=E7=AE=80?= =?UTF-8?q?=E5=8C=96=20+=20regex=20=E5=8E=BB=E9=87=8D=20+=20stderr=20?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按 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 --- claude-config/hooks/role-router.mjs | 29 +++++++++--------------- claude-config/hooks/role-router.test.mjs | 6 +++-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/claude-config/hooks/role-router.mjs b/claude-config/hooks/role-router.mjs index 70e9a27..aa0f339 100755 --- a/claude-config/hooks/role-router.mjs +++ b/claude-config/hooks/role-router.mjs @@ -14,9 +14,11 @@ 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 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|requirement|roadmap|planning|backlog|user\s?story|prd)\b|产品(?:经理)?|需求|排期|规划|计划|调研|路线图|用户故事)/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: ` @@ -63,30 +65,21 @@ function readStdinSync() { function main() { try { const raw = readStdinSync(); - if (!raw) { - console.log(JSON.stringify({ continue: true, suppressOutput: true })); - return; - } + if (!raw) { console.log(NOOP); 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; - } + 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 { - console.log(JSON.stringify({ continue: true, suppressOutput: true })); + } catch (e) { + console.error('[role-router]', e.message); + console.log(NOOP); } } diff --git a/claude-config/hooks/role-router.test.mjs b/claude-config/hooks/role-router.test.mjs index e0c7599..77b5c6f 100755 --- a/claude-config/hooks/role-router.test.mjs +++ b/claude-config/hooks/role-router.test.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * role-router unit tests — 6 fixture (每角色 2 条) + * role-router unit tests — 10 tests (8 fixture + 2 subprocess integration) * 跑法: node role-router.test.mjs */ @@ -27,9 +27,11 @@ const fixtures = [ let pass = 0, fail = 0; const failures = []; +const sortKey = a => a.slice().sort().join(','); + for (const f of fixtures) { const got = detectRoles(f.prompt); - const ok = JSON.stringify(got.sort()) === JSON.stringify(f.expect.sort()); + const ok = sortKey(got) === sortKey(f.expect); if (ok) { pass++; console.log(`PASS "${f.prompt}" → [${got.join(',')}]`);