From 221fe3d68a9ca2ac6b61d8af4e6fbc0a6ebb3f37 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 20 Sep 2026 22:13:39 +0200 Subject: [PATCH] fix(agents): accept qualified package prose --- docs/development/agent-workflow.md | 2 ++ tools/skills/validate-agent-guidance.mjs | 5 +++-- tools/skills/validate-agent-guidance.test.mjs | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index ee8450218..65d838d8f 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -61,6 +61,8 @@ are recognized as package/alias mentions. Traversal and document-file imports ar rejected before those exemptions. TypeScript configuration is parsed as JSONC. Declared packages also permit safe subpaths; exact aliases stay exact. Declared package mentions may include a version or dist-tag qualifier. +Qualifier handling includes unscoped names; terminal sentence punctuation is +removed before matching a declared package. Markdown destinations decode HTML entities before URI parsing, matching rendered links. Heading-anchor lookup is limited to Markdown targets. Source-file line fragments, PDF page fragments and other non-Markdown fragments retain file-existence checks. diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs index 71d7c4f45..074bdf317 100644 --- a/tools/skills/validate-agent-guidance.mjs +++ b/tools/skills/validate-agent-guidance.mjs @@ -114,9 +114,9 @@ async function packageMentions(rootDir) { .map((name) => name.slice(1)); const scopes = new Set(declared.map((name) => name.split('/')[0])); return (raw) => { - let token = raw.replace(/[.,;:)"'\]}]+$/u, ''); + let token = raw.replace(/[?!.,;:)"'\]}]+$/u, ''); token = token.replace( - /^([^/]+\/[^/@]+)@(?:[~^]?\d[\w.+-]*|[a-z][\w-]*)$/iu, + /^([^/@]+(?:\/[^/@]+)?)@(?:[~^]?\d[\w.+-]*|[a-z][\w-]*)$/iu, '$1' ); if ( @@ -124,6 +124,7 @@ async function packageMentions(rootDir) { ) return false; if (/\.(?:md|mdx|txt|json|ya?ml|html?)$/iu.test(token)) return false; + if (packages.includes(token)) return true; if (token.endsWith('/*') && scopes.has(token.slice(0, -2))) return true; return declared.some((name) => { const star = name.indexOf('*'); diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index 637b7e0e0..9cde9c61d 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -847,3 +847,22 @@ for (const [path, fragment] of [ ); }); } + +for (const mention of [ + '@marked@17.0.0', + '@marked@^17.0.0', + '@angular/core?', + '@angular/core!', +]) { + test(`package mention accepts qualifier or punctuation: ${mention}`, async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { marked: '*', '@angular/core': '*' }, + }), + 'AGENTS.md': 'Use ' + mention, + }), + [] + ); + }); +}