diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 109af4ff7..0c85a3209 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -68,7 +68,8 @@ Extensionless inline candidates are also imports when they resolve to repository checking the full filename before prefixes at ASCII/Unicode prose separators. Declared scoped dependencies, scope wildcards and matching TypeScript path aliases are recognized as package/alias mentions. Traversal and document-file imports are -rejected before those exemptions. TypeScript configuration is parsed as JSONC. +rejected before those exemptions, including document paths with fragments or queries. +TypeScript configuration is parsed as JSONC. Declared packages also permit safe subpaths; exact aliases stay exact. Declared package mentions may include a version (including semver comparators) or dist-tag qualifier. Qualifier handling includes unscoped names; terminal sentence punctuation is diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs index d4634bd8a..d9e6fc7aa 100644 --- a/tools/skills/validate-agent-guidance.mjs +++ b/tools/skills/validate-agent-guidance.mjs @@ -126,7 +126,12 @@ async function packageMentions(rootDir) { token.split(/[\/\\]/u).some((part) => part === '.' || part === '..') ) return false; - if (/\.(?:md|mdx|txt|json|ya?ml|html?)$/iu.test(token)) return false; + if ( + /\.(?:md|mdx|txt|json|ya?ml|html?)$/iu.test( + token.split(/[?#]/u, 1)[0] + ) + ) + return false; if (packages.includes(token)) return true; if (token.endsWith('/*') && scopes.has(token.slice(0, -2))) return true; return declared.some((name) => { diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index fc7a6e36c..01fda4ee3 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -1191,3 +1191,24 @@ test('valid iframe and inert iframe targets pass', async (t) => { [] ); }); + +for (const suffix of [ + '.md#rules', + '.md?view=raw#rules', + '.txt#rules', + '.json#rules', +]) { + test(`package document imports retain suffix guards: ${suffix}`, async (t) => { + assert.ok( + ( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { '@angular/core': '*' }, + }), + 'CLAUDE.md': + '@AGENTS.md\n\nRead @angular/core/docs/extra' + suffix, + }) + ).some((message) => message.includes('additional or inline')) + ); + }); +}