From 086906028485fc06db1e3f2f79976bc9e2537889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=97=E5=B9=B3?= Date: Wed, 10 Jun 2026 20:31:56 +0800 Subject: [PATCH] fix(ext): correct anti-trigger logic for code/quote blocks + blocker parsing - trigger-detector: strip code blocks before pattern matching (triggers outside code blocks now fire) - trigger-detector: use code-stripped content for quote and reported-speech anti-trigger checks - state-injector: strip markdown list prefix from blocker text Tests: 27/27 trigger-detector, 17/17 state-injector passing --- .../goal-driven-loop/state-injector.ts | 5 ++- .../goal-driven-loop/trigger-detector.ts | 40 ++++++++----------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/.pi/extensions/goal-driven-loop/state-injector.ts b/.pi/extensions/goal-driven-loop/state-injector.ts index d6b0794..8a8cbde 100644 --- a/.pi/extensions/goal-driven-loop/state-injector.ts +++ b/.pi/extensions/goal-driven-loop/state-injector.ts @@ -53,7 +53,10 @@ export function readGoalState(cwd: string): GoalState | null { const passedCriteria = countCheckedBoxes(criteriaSection); const blockerSection = extractSection(content, "Blocked"); - const blocker = blockerSection.trim() || undefined; + const blockerRaw = blockerSection.trim() || undefined; + const blocker = blockerRaw + ? blockerRaw.replace(/^[-*]\s*/, "").trim() || undefined + : undefined; const progressSection = extractSection(content, "Progress Log"); const progressLog = extractIterations(progressSection); diff --git a/.pi/extensions/goal-driven-loop/trigger-detector.ts b/.pi/extensions/goal-driven-loop/trigger-detector.ts index 126dc22..681b23e 100644 --- a/.pi/extensions/goal-driven-loop/trigger-detector.ts +++ b/.pi/extensions/goal-driven-loop/trigger-detector.ts @@ -70,28 +70,34 @@ const REPORTED_SPEECH_MARKERS = [ * Returns null if no trigger or if anti-trigger rules match. */ export function detectTrigger(input: string): TriggerMatch | null { - // Anti-trigger: code block - if (/```[\s\S]*?```/.test(input) && containsTriggerInCodeBlock(input)) { - return null; - } + // Anti-trigger: code block — remove code blocks from input before checking + // for trigger phrases, so triggers outside code blocks still fire. + const inputWithoutCode = input.replace(/```[\s\S]*?```/g, ""); // Anti-trigger: markdown quote block — only block if trigger phrase appears // exclusively inside quote lines (no trigger in non-quote content) - const nonQuoteLines = input.split("\n").filter((line) => !/^\s*>/.test(line)); + const nonQuoteLines = inputWithoutCode + .split("\n") + .filter((line) => !/^\s*>/.test(line)); const nonQuoteContent = nonQuoteLines.join("\n"); - const hasTriggerOutsideQuote = TRIGGER_PATTERNS.some((p) => p.test(nonQuoteContent)); + const hasTriggerOutsideQuote = TRIGGER_PATTERNS.some((p) => + p.test(nonQuoteContent) + ); if (!hasTriggerOutsideQuote) { - // All trigger phrases are inside quotes — block + // All trigger phrases are inside quotes or absent — block return null; } - // Anti-trigger: reported speech - const lowerInput = input.toLowerCase(); + // Use the code-stripped, non-quote content for further checks + const effectiveInput = nonQuoteContent; + + // Anti-trigger: reported speech — check per-sentence + const lowerInput = effectiveInput.toLowerCase(); const hasReportedSpeech = REPORTED_SPEECH_MARKERS.some((marker) => lowerInput.includes(marker) ); if (hasReportedSpeech) { - const sentences = input.split(/[.!?。!?]/); + const sentences = effectiveInput.split(/[.!?。!?]/); for (const sentence of sentences) { const lowerSentence = sentence.toLowerCase(); const hasMarker = REPORTED_SPEECH_MARKERS.some((m) => @@ -114,7 +120,7 @@ export function detectTrigger(input: string): TriggerMatch | null { // Try to match trigger pattern for (const pattern of TRIGGER_PATTERNS) { - const match = input.match(pattern); + const match = effectiveInput.match(pattern); if (match) { const fullText = match[1].trim(); @@ -155,18 +161,6 @@ export function detectTrigger(input: string): TriggerMatch | null { return null; } -function containsTriggerInCodeBlock(input: string): boolean { - const codeBlocks = input.match(/```[\s\S]*?```/g) || []; - for (const block of codeBlocks) { - for (const pattern of TRIGGER_PATTERNS) { - if (pattern.test(block)) { - return true; - } - } - } - return false; -} - function cleanGoalText(text: string): string { return text.replace(/[.,;:。;,]+$/, "").trim(); }