diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 088df49b1..7e76b8665 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -69,10 +69,11 @@ 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, including document paths with fragments or queries. -All recognized Markdown extensions share the document-import guard. URL-encoded +All recognized Markdown extensions share the document-import guard; reStructuredText +and AsciiDoc documents are also excluded from package exemptions. URL-encoded paths do not receive package exemptions. 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. +Declared package mentions may include a version (including semver comparators and wildcard ranges) or dist-tag qualifier. Qualifier handling includes unscoped names; terminal sentence punctuation is removed before matching a declared package, as are straight/curly apostrophe possessives. Unicode punctuation and ASCII commas, semicolons, colons, question/exclamation marks diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs index 5ebdd2fc3..0f138f1e3 100644 --- a/tools/skills/validate-agent-guidance.mjs +++ b/tools/skills/validate-agent-guidance.mjs @@ -119,7 +119,7 @@ async function packageMentions(rootDir) { token = token.replace(/[?!.,;:)"'\]}]+$/u, ''); token = token.replace(/['’]s$/iu, ''); token = token.replace( - /^([^/@]+(?:\/[^/@]+)?)@(?:(?:[~^]|[<>]=?|=)?\d[\w.+-]*|[a-z][\w-]*)$/iu, + /^([^/@]+(?:\/[^/@]+)?)@(?:(?:[~^]|[<>]=?|=)?\d[\w.*+-]*|\*|[a-z][\w-]*)$/iu, '$1' ); if ( @@ -130,7 +130,7 @@ async function packageMentions(rootDir) { if ( /%[\da-f]{2}/iu.test(token) || MARKDOWN_EXTENSION.test(path) || - /\.(?:txt|json|ya?ml|html?)$/iu.test(path) + /\.(?:txt|json|ya?ml|html?|rst|rest|adoc|asciidoc)$/iu.test(path) ) return false; if (packages.includes(token)) return true; diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index 0a31d981d..1fb25caea 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -1236,3 +1236,33 @@ for (const suffix of [ ); }); } + +for (const version of ['*', '22.*', '22.1.*']) { + test(`wildcard package version is prose: ${version}`, async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { '@angular/core': '*' }, + }), + 'AGENTS.md': 'Use @angular/core@' + version, + }), + [] + ); + }); +} +for (const extension of ['rst', 'rest', 'adoc', 'asciidoc']) { + test(`non-Markdown document is not a package exemption: ${extension}`, async (t) => { + assert.ok( + ( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { '@angular/core': '*' }, + }), + 'CLAUDE.md': + '@AGENTS.md\n\nRead @angular/core/docs/guide.' + + extension, + }) + ).some((message) => message.includes('additional or inline')) + ); + }); +}