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
This commit is contained in:
@@ -53,7 +53,10 @@ export function readGoalState(cwd: string): GoalState | null {
|
|||||||
const passedCriteria = countCheckedBoxes(criteriaSection);
|
const passedCriteria = countCheckedBoxes(criteriaSection);
|
||||||
|
|
||||||
const blockerSection = extractSection(content, "Blocked");
|
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 progressSection = extractSection(content, "Progress Log");
|
||||||
const progressLog = extractIterations(progressSection);
|
const progressLog = extractIterations(progressSection);
|
||||||
|
|||||||
@@ -70,28 +70,34 @@ const REPORTED_SPEECH_MARKERS = [
|
|||||||
* Returns null if no trigger or if anti-trigger rules match.
|
* Returns null if no trigger or if anti-trigger rules match.
|
||||||
*/
|
*/
|
||||||
export function detectTrigger(input: string): TriggerMatch | null {
|
export function detectTrigger(input: string): TriggerMatch | null {
|
||||||
// Anti-trigger: code block
|
// Anti-trigger: code block — remove code blocks from input before checking
|
||||||
if (/```[\s\S]*?```/.test(input) && containsTriggerInCodeBlock(input)) {
|
// for trigger phrases, so triggers outside code blocks still fire.
|
||||||
return null;
|
const inputWithoutCode = input.replace(/```[\s\S]*?```/g, "");
|
||||||
}
|
|
||||||
|
|
||||||
// Anti-trigger: markdown quote block — only block if trigger phrase appears
|
// Anti-trigger: markdown quote block — only block if trigger phrase appears
|
||||||
// exclusively inside quote lines (no trigger in non-quote content)
|
// 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 nonQuoteContent = nonQuoteLines.join("\n");
|
||||||
const hasTriggerOutsideQuote = TRIGGER_PATTERNS.some((p) => p.test(nonQuoteContent));
|
const hasTriggerOutsideQuote = TRIGGER_PATTERNS.some((p) =>
|
||||||
|
p.test(nonQuoteContent)
|
||||||
|
);
|
||||||
if (!hasTriggerOutsideQuote) {
|
if (!hasTriggerOutsideQuote) {
|
||||||
// All trigger phrases are inside quotes — block
|
// All trigger phrases are inside quotes or absent — block
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Anti-trigger: reported speech
|
// Use the code-stripped, non-quote content for further checks
|
||||||
const lowerInput = input.toLowerCase();
|
const effectiveInput = nonQuoteContent;
|
||||||
|
|
||||||
|
// Anti-trigger: reported speech — check per-sentence
|
||||||
|
const lowerInput = effectiveInput.toLowerCase();
|
||||||
const hasReportedSpeech = REPORTED_SPEECH_MARKERS.some((marker) =>
|
const hasReportedSpeech = REPORTED_SPEECH_MARKERS.some((marker) =>
|
||||||
lowerInput.includes(marker)
|
lowerInput.includes(marker)
|
||||||
);
|
);
|
||||||
if (hasReportedSpeech) {
|
if (hasReportedSpeech) {
|
||||||
const sentences = input.split(/[.!?。!?]/);
|
const sentences = effectiveInput.split(/[.!?。!?]/);
|
||||||
for (const sentence of sentences) {
|
for (const sentence of sentences) {
|
||||||
const lowerSentence = sentence.toLowerCase();
|
const lowerSentence = sentence.toLowerCase();
|
||||||
const hasMarker = REPORTED_SPEECH_MARKERS.some((m) =>
|
const hasMarker = REPORTED_SPEECH_MARKERS.some((m) =>
|
||||||
@@ -114,7 +120,7 @@ export function detectTrigger(input: string): TriggerMatch | null {
|
|||||||
|
|
||||||
// Try to match trigger pattern
|
// Try to match trigger pattern
|
||||||
for (const pattern of TRIGGER_PATTERNS) {
|
for (const pattern of TRIGGER_PATTERNS) {
|
||||||
const match = input.match(pattern);
|
const match = effectiveInput.match(pattern);
|
||||||
if (match) {
|
if (match) {
|
||||||
const fullText = match[1].trim();
|
const fullText = match[1].trim();
|
||||||
|
|
||||||
@@ -155,18 +161,6 @@ export function detectTrigger(input: string): TriggerMatch | null {
|
|||||||
return 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 {
|
function cleanGoalText(text: string): string {
|
||||||
return text.replace(/[.,;:。;,]+$/, "").trim();
|
return text.replace(/[.,;:。;,]+$/, "").trim();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user