fix(agents): preserve filenames across prose punctuation

This commit is contained in:
4gray committed 2026-09-21 00:46:30 +02:00
1 parent 4a73cf3828
commit 82d582bb74
3 files changed
+59 -12

No files matched your search

+22 -10
View File
@@ -115,7 +115,7 @@ async function packageMentions(rootDir) {
const scopes = new Set(declared.map((name) => name.split('/')[0]));
return (raw) => {
// 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(
@@ -188,15 +188,27 @@ export async function validateAgentGuidance({ rootDir }) {
inlineImports.push(token);
continue;
}
const candidate = token
.split(/(?=[^\x00-\x7f])\p{P}/u, 1)[0]
.replace(/[?!.,;:)"'\]}]+$/u, '');
try {
if ((await stat(resolve(rootDir, candidate))).isFile())
inlineImports.push(token);
} catch (error) {
if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR')
throw error;
// Check real filenames before interpreting punctuation as prose.
const candidates = new Set([
token,
token.replace(/[?!.,;:)"'\]}]+$/u, ''),
]);
for (const boundary of token.matchAll(
/[,;:!?]|(?=[^\x00-\x7f])\p{P}/gu
))
candidates.add(token.slice(0, boundary.index));
for (const candidate of candidates) {
try {
if (
(await stat(resolve(rootDir, candidate))).isFile()
) {
inlineImports.push(token);
break;
}
} catch (error) {
if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR')
throw error;
}
}
}
if (
@@ -1136,3 +1136,37 @@ for (const suffix of ['—then', ',next', '”']) {
);
});
}
for (const name of ['INSTRUCTIONS—v2', 'INSTRUCTIONS,extra']) {
test(`full punctuation filename remains an import: ${name}`, async (t) => {
assert.ok(
(
await diagnostics(t, {
'CLAUDE.md': '@AGENTS.md\n\nRead @' + name,
[name]: 'Guidance',
})
).some((message) => message.includes('additional or inline'))
);
});
}
for (const suffix of [',then', ';then', ':then', '!then', '?then']) {
test(`ASCII prose boundary handles imports and packages: ${suffix}`, async (t) => {
assert.ok(
(
await diagnostics(t, {
'CLAUDE.md': '@AGENTS.md\n\nRead @INSTRUCTIONS' + suffix,
INSTRUCTIONS: 'Guidance',
})
).some((message) => message.includes('additional or inline'))
);
assert.deepEqual(
await diagnostics(t, {
'AGENTS.md': 'Use @angular/core' + suffix + ' continue',
'package.json': JSON.stringify({
dependencies: { '@angular/core': '*' },
}),
}),
[]
);
});
}