From e1bc4dee602aa8b3899a81f1b817585bfa5c306b Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 20 Sep 2026 22:01:32 +0200 Subject: [PATCH] fix(agents): support package versions and source fragments --- docs/development/agent-workflow.md | 3 ++ tools/skills/validate-agent-guidance.mjs | 18 ++++++++-- tools/skills/validate-agent-guidance.test.mjs | 36 +++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/docs/development/agent-workflow.md b/docs/development/agent-workflow.md index 056cd95e4..ee8450218 100644 --- a/docs/development/agent-workflow.md +++ b/docs/development/agent-workflow.md @@ -60,7 +60,10 @@ Declared scoped dependencies, scope wildcards and matching TypeScript path alias are recognized as package/alias mentions. Traversal and document-file imports are 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. 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. The import scan includes visible HTML text and excludes code and non-rendered containers. Image source sets use `parse-srcset` to check each candidate URL. Root-relative literals never suppress source-relative definition checks. diff --git a/tools/skills/validate-agent-guidance.mjs b/tools/skills/validate-agent-guidance.mjs index 52ae615c5..71d7c4f45 100644 --- a/tools/skills/validate-agent-guidance.mjs +++ b/tools/skills/validate-agent-guidance.mjs @@ -1,6 +1,13 @@ import ts from 'typescript'; import { readFile, realpath } from 'node:fs/promises'; -import { dirname, isAbsolute, relative, resolve, sep } from 'node:path'; +import { + dirname, + extname, + isAbsolute, + relative, + resolve, + sep, +} from 'node:path'; import { fileURLToPath } from 'node:url'; import { guidanceAnchors as anchors, @@ -55,6 +62,9 @@ async function validateReference( if ( anchor && !image && + /^(?:\.md|\.markdown|\.mdown|\.mkd|\.mdx)$/iu.test( + extname(actual) + ) && !anchors(await readFile(actual, 'utf8')).has(anchor) ) { return `${source}: missing anchor "${anchor}" in ${target}`; @@ -104,7 +114,11 @@ async function packageMentions(rootDir) { .map((name) => name.slice(1)); const scopes = new Set(declared.map((name) => name.split('/')[0])); return (raw) => { - const token = raw.replace(/[.,;:)"'\]}]+$/u, ''); + let token = raw.replace(/[.,;:)"'\]}]+$/u, ''); + token = token.replace( + /^([^/]+\/[^/@]+)@(?:[~^]?\d[\w.+-]*|[a-z][\w-]*)$/iu, + '$1' + ); if ( token.split(/[\/\\]/u).some((part) => part === '.' || part === '..') ) diff --git a/tools/skills/validate-agent-guidance.test.mjs b/tools/skills/validate-agent-guidance.test.mjs index f8c03f5f6..637b7e0e0 100644 --- a/tools/skills/validate-agent-guidance.test.mjs +++ b/tools/skills/validate-agent-guidance.test.mjs @@ -811,3 +811,39 @@ for (const filename of [ ); }); } + +for (const version of ['22.1.6', '^22.1.6', 'next']) { + test(`declared package can include version ${version}`, async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'package.json': JSON.stringify({ + dependencies: { '@angular/core': '*' }, + }), + 'AGENTS.md': 'Use @angular/core@' + version, + }), + [] + ); + }); +} +for (const [path, fragment] of [ + ['apps/example.ts', 'L20'], + ['docs/guide.pdf', 'page=3'], +]) { + test(`non-Markdown fragments are not headings: ${path}`, async (t) => { + assert.deepEqual( + await diagnostics(t, { + 'AGENTS.md': `[Target](${path}#${fragment})`, + [path]: 'fixture', + }), + [] + ); + assert.match( + ( + await diagnostics(t, { + 'AGENTS.md': `[Target](${path}#${fragment})`, + }) + ).join('\n'), + /does not exist/ + ); + }); +}