diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 301e3118e..f28a58dc0 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -73,7 +73,8 @@ including references whose semicolon may be omitted. Inline guidance imports are rejected after punctuation as well as whitespace. At-signs inside external URIs (including explicit opaque autolinks such as mailto) are excluded per HTML text node, preserving adjacent imports. A colon directly before an import -does not make that import a URI. +does not make that import a URI. Opaque schemes are excluded only in parsed links +whose visible text equals their URI, so colon-labeled prose remains checked. A closing bracket followed by punctuation and an at-sign terminates a bare URL exclusion. Extensionless inline candidates are also imports when they resolve to repository files, checking the full filename before prefixes at ASCII/Unicode prose separators. diff --git a/tools/skills/agent-guidance-markdown.mjs b/tools/skills/agent-guidance-markdown.mjs index 133a28b52..7c11103d7 100644 --- a/tools/skills/agent-guidance-markdown.mjs +++ b/tools/skills/agent-guidance-markdown.mjs @@ -145,9 +145,22 @@ export function guidanceProse(markdown) { ) ) return ' '; + if (node.tagName === 'a') { + const href = node.attrs?.find( + (attribute) => attribute.name === 'href' + )?.value; + if ( + href && + /^[a-z][a-z\d+.-]*:(?!\/\/)/iu.test(href) && + node.childNodes?.length === 1 && + node.childNodes[0].nodeName === '#text' && + node.childNodes[0].value === href + ) + return ' '; + } if (node.nodeName === '#text') return node.value.replace( - /(?:(?][.,;:!?]*@|\s|$)/giu, + /(?:\b[a-z][a-z\d+.-]*:\/\/|\/\/)[^\s]*?(?=[)\]}>][.,;:!?]*@|\s|$)/giu, ' ' ); const content = (node.childNodes ?? []).map(text).join(''); diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index d49b97bb3..ad07a9999 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -1522,3 +1522,13 @@ test('colon directly before an import remains checked', async (t) => { ).some((message) => message.includes('additional or inline')) ); }); + +test('colon-labeled prose cannot hide imports', async (t) => { + assert.ok( + ( + await diagnostics(t, { + 'CLAUDE.md': '@AGENTS.md\n\nFallback:then;@docs/guide.md', + }) + ).some((message) => message.includes('additional or inline')) + ); +});