feat(keyword-detector): add per-keyword disable config

Adds keyword_detector.disabled_keywords config so users can opt out of
specific keyword detectors individually without disabling the entire
keyword-detector hook. Allowed values: 'ultrawork', 'search', 'analyze',
'team'. Default empty/missing -> all four detectors active (no behavior
change for existing configs).

Motivation: an audit revealed search and analyze patterns trigger on
~30-60% of normal conversational user messages (e.g. 'how to', 'why is',
'show me', '왜', '어떻게'). The disable list is the immediate kill switch
while the patterns themselves are tightened in a separate PR.

Schema follows the existing per-feature config block convention shared
by team_mode, ralph_loop, runtime_fallback, and comment_checker. The
KeywordType enum (z.enum) lives next to the config schema and is
re-imported by the detector to keep the union type in lockstep with the
schema.

Threading:
  pluginConfig.keyword_detector
    -> create-transform-hooks.ts (factory wiring)
    -> createKeywordDetectorHook(config)
    -> detectKeywordsWithType(text, agent, model, disabledKeywords)
    -> Set-based filter at the source-of-truth detector

Adds 8 regression tests covering per-keyword disable, multi-keyword
disable, partial disable (one keyword off, another still firing),
ultrawork toast suppression, undefined config, and empty array.
This commit is contained in:
YeonGyu-Kim
2026-04-28 14:20:35 +09:00
parent 25d9437513
commit 91c0d75588
9 changed files with 296 additions and 16 deletions
+7 -1
View File
@@ -55,7 +55,13 @@ export function createTransformHooks(args: {
const keywordDetector = isHookEnabled("keyword-detector")
? safeCreateHook(
"keyword-detector",
() => createKeywordDetectorHook(ctx, contextCollector, ralphLoop ?? undefined),
() =>
createKeywordDetectorHook(
ctx,
contextCollector,
ralphLoop ?? undefined,
pluginConfig.keyword_detector,
),
{ enabled: safeHookEnabled },
)
: null