mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
fix(agents): distinguish URL quotes and cover guidance variants
This commit is contained in:
1 parent
7b4a49821b
commit
87a27b5458
4 files changed
+74
-9
No files matched your search
@@ -69,7 +69,7 @@ Image and media references require a nonempty path that resolves to a file, not
|
||||
Direct file URLs and file-scheme HTML bases are rejected; use portable repository-relative paths.
|
||||
Image references check file existence without interpreting image fragments as
|
||||
Markdown headings; document links keep anchor checks even when sharing a target.
|
||||
HTML video, audio, source and track `src` assets and video posters use the same
|
||||
HTML image-input, video, audio, source and track `src` assets and video posters use the same
|
||||
existence checks as images. Entity decoding uses full HTML text/attribute rules,
|
||||
including references whose semicolon may be omitted.
|
||||
Inline guidance imports are rejected after punctuation as well as whitespace.
|
||||
@@ -77,7 +77,7 @@ At-signs inside external URIs (including explicit opaque autolinks such as mailt
|
||||
per HTML text node, preserving adjacent imports. A colon directly before an import
|
||||
does not make that import a URI. Opaque schemes are excluded only in parsed links
|
||||
whose visible text equals their URI, so colon-labeled prose remains checked.
|
||||
A closing bracket or quote followed by punctuation and an at-sign terminates a bare URL exclusion.
|
||||
A closing bracket or matching enclosing quote 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,
|
||||
including opening parentheses, brackets and braces. Each at-sign candidate is
|
||||
@@ -88,7 +88,7 @@ rejected before those exemptions, including document paths with fragments or que
|
||||
All recognized Markdown extensions share the document-import guard; reStructuredText
|
||||
and AsciiDoc, PDF, Word, OpenDocument, RTF, Org and TeX documents are also excluded
|
||||
from package exemptions. Recognized extensionless guidance names (including AGENTS,
|
||||
CLAUDE, INSTRUCTIONS and README) are excluded in package subpaths too. URL-encoded
|
||||
CLAUDE, INSTRUCTIONS and README, case-insensitively) are excluded in package subpaths too. 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 and wildcard ranges) or dist-tag qualifier.
|
||||
|
||||
@@ -90,7 +90,14 @@ function htmlNavigation(html, inspect = () => {}) {
|
||||
].includes(node.tagName) &&
|
||||
attribute.name === 'src') ||
|
||||
(node.tagName === 'video' && attribute.name === 'poster') ||
|
||||
(node.tagName === 'object' && attribute.name === 'data')
|
||||
(node.tagName === 'object' && attribute.name === 'data') ||
|
||||
(node.tagName === 'input' &&
|
||||
attribute.name === 'src' &&
|
||||
node.attrs.some(
|
||||
(attr) =>
|
||||
attr.name === 'type' &&
|
||||
attr.value.toLowerCase() === 'image'
|
||||
))
|
||||
)
|
||||
references.push({
|
||||
target: attribute.value
|
||||
@@ -141,7 +148,7 @@ function htmlNavigation(html, inspect = () => {}) {
|
||||
}
|
||||
|
||||
export function guidanceProse(markdown) {
|
||||
function text(node) {
|
||||
function text(node, preceding = '') {
|
||||
if (
|
||||
['script', 'style', 'template', 'pre', 'code'].includes(
|
||||
node.tagName
|
||||
@@ -163,10 +170,29 @@ export function guidanceProse(markdown) {
|
||||
}
|
||||
if (node.nodeName === '#text')
|
||||
return node.value.replace(
|
||||
/(?:\b[a-z][a-z\d+.-]*:\/\/|\/\/)[^\s]*?(?=[)\]}>"'”’][.,;:!?]*@|\s|$)/giu,
|
||||
' '
|
||||
/(?:\b[a-z][a-z\d+.-]*:\/\/|\/\/)[^\s]*?(?=[)\]}>][.,;:!?]*@|\s|$)/giu,
|
||||
(url, offset) => {
|
||||
const opening = (
|
||||
preceding + node.value.slice(0, offset)
|
||||
).at(-1);
|
||||
const closing = {
|
||||
'"': '"',
|
||||
"'": "'",
|
||||
'“': '”',
|
||||
'”': '”',
|
||||
'‘': '’',
|
||||
'’': '’',
|
||||
}[opening];
|
||||
const boundary = closing ? url.indexOf(closing) : -1;
|
||||
return boundary >= 0 &&
|
||||
/^[.,;:!?]*@/u.test(url.slice(boundary + 1))
|
||||
? ' ' + url.slice(boundary)
|
||||
: ' ';
|
||||
}
|
||||
);
|
||||
const content = (node.childNodes ?? []).map(text).join('');
|
||||
let content = '';
|
||||
for (const child of node.childNodes ?? [])
|
||||
content += text(child, preceding + content);
|
||||
return [
|
||||
'address',
|
||||
'article',
|
||||
|
||||
@@ -166,7 +166,7 @@ async function packageMentions(rootDir) {
|
||||
if (
|
||||
/%[\da-f]{2}/iu.test(token) ||
|
||||
MARKDOWN_EXTENSION.test(path) ||
|
||||
/(?:^|\/)(?:AGENTS|CLAUDE|INSTRUCTIONS|README|LICENSE|LICENCE|NOTICE|COPYING|AUTHORS|CONTRIBUTORS|CHANGELOG)$/u.test(
|
||||
/(?:^|\/)(?:AGENTS|CLAUDE|INSTRUCTIONS|README|LICENSE|LICENCE|NOTICE|COPYING|AUTHORS|CONTRIBUTORS|CHANGELOG)$/iu.test(
|
||||
path
|
||||
) ||
|
||||
DOCUMENT_EXTENSION.test(path)
|
||||
|
||||
@@ -1630,3 +1630,42 @@ for (const embedded of [false, true]) {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for (const url of [
|
||||
"https://example.com/don't@docs/guide.md",
|
||||
"https://example.com/path'@docs/guide.md",
|
||||
]) {
|
||||
test(`internal URL apostrophe remains URL prose: ${url}`, async (t) => {
|
||||
assert.deepEqual(await diagnostics(t, { 'AGENTS.md': url }), []);
|
||||
});
|
||||
}
|
||||
for (const name of ['readme', 'instructions', 'Agents']) {
|
||||
test(`guidance basename matching ignores case: ${name}`, async (t) => {
|
||||
assert.ok(
|
||||
(
|
||||
await diagnostics(t, {
|
||||
'package.json': JSON.stringify({
|
||||
dependencies: { '@angular/core': '*' },
|
||||
}),
|
||||
'CLAUDE.md':
|
||||
'@AGENTS.md\n\nRead @angular/core/docs/' + name,
|
||||
})
|
||||
).some((message) => message.includes('additional or inline'))
|
||||
);
|
||||
});
|
||||
}
|
||||
test('image input source is validated', async (t) => {
|
||||
assert.ok(
|
||||
(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '<input type="IMAGE" src="missing.png">',
|
||||
})
|
||||
).length > 0
|
||||
);
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'AGENTS.md': '<input type="text" src="missing.png">',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user