fix(agents): handle opening prose delimiters

This commit is contained in:
4gray committed 2026-09-21 04:09:46 +02:00
1 parent 71b7204ca0
commit 673d3fd791
3 files changed
+31 -4

No files matched your search

+3 -2
View File
@@ -77,7 +77,8 @@ does not make that import a URI. Opaque schemes are excluded only in parsed link
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.
checking the full filename before prefixes at ASCII/Unicode prose separators,
including opening parentheses, brackets and braces.
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.
@@ -90,7 +91,7 @@ Declared packages also permit safe subpaths; exact aliases stay exact.
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
Unicode punctuation and ASCII opening delimiters, commas, semicolons, colons, question/exclamation marks
separate package mentions from adjacent prose.
Markdown destinations decode HTML entities before URI parsing, matching rendered links.
Heading-anchor lookup is limited to Markdown targets. Source-file line fragments,
+3 -2
View File
@@ -140,8 +140,9 @@ async function packageMentions(rootDir) {
.map((name) => name.slice(1));
const scopes = new Set(declared.map((name) => name.split('/')[0]));
return (raw) => {
if (packages.includes(`@${raw}`) || packages.includes(raw)) return true;
// ASCII punctuation also belongs to package names and version ranges.
let token = raw.split(/[,;:!?]|(?=[^\x00-\x7f])\p{P}/u, 1)[0];
let token = raw.split(/[,;:!?([{]|(?=[^\x00-\x7f])\p{P}/u, 1)[0];
token = token.replace(/[?!.,;:)"'\]}]+$/u, '');
token = token.replace(/['’]s$/iu, '');
token = token.replace(
@@ -231,7 +232,7 @@ export async function validateAgentGuidance({ rootDir }) {
token.replace(/[?!.,;:)"'\]}]+$/u, ''),
]);
for (const boundary of token.matchAll(
/[,;:!?]|(?=[^\x00-\x7f])\p{P}/gu
/[,;:!?([{]|(?=[^\x00-\x7f])\p{P}/gu
))
candidates.add(token.slice(0, boundary.index));
for (const candidate of candidates) {
@@ -1532,3 +1532,28 @@ test('colon-labeled prose cannot hide imports', async (t) => {
).some((message) => message.includes('additional or inline'))
);
});
for (const opening of ['(', '[', '{']) {
test(`opening delimiter separates package and import prose: ${opening}`, async (t) => {
assert.deepEqual(
await diagnostics(t, {
'package.json': JSON.stringify({
dependencies: { '@angular/core': '*' },
}),
'AGENTS.md': 'Use @angular/core' + opening + 'test helpers)',
}),
[]
);
assert.ok(
(
await diagnostics(t, {
'CLAUDE.md':
'@AGENTS.md\n\nRead @INSTRUCTIONS' +
opening +
'then continue)',
INSTRUCTIONS: 'Guidance',
})
).some((message) => message.includes('additional or inline'))
);
});
}