mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
fix(agents): exclude HTML-contained guidance imports
This commit is contained in:
1 parent
84596ecb41
commit
87c3e466d2
3 files changed
+45
-2
No files matched your search
@@ -42,6 +42,9 @@ separately so encoded filename delimiters stay in the filename. Fenced and inden
|
||||
do not count as root guidance imports or satisfy the required Claude import.
|
||||
The required Claude import must be an unformatted standalone line in a top-level
|
||||
paragraph; headings, quotes and list items do not satisfy it.
|
||||
The parsed HTML tree also verifies that this paragraph is outside HTML containers,
|
||||
including templates split across Markdown tokens. Generated HTML is inspected
|
||||
in memory only; it is never executed or emitted.
|
||||
Heading anchors decode HTML character references in text and use `github-slugger`
|
||||
for GitHub-compatible character filtering and duplicate suffixes. Explicit HTML anchors
|
||||
use `parse5`, excluding comments, scripts, styles and template contents.
|
||||
|
||||
@@ -2,7 +2,7 @@ import GithubSlugger from 'github-slugger';
|
||||
import { Marked, Tokenizer } from 'marked';
|
||||
import { parseFragment } from 'parse5';
|
||||
|
||||
// Lex only: no Markdown is rendered and no HTML is sanitized or re-emitted.
|
||||
// Inspection only: generated HTML is parsed in memory, never executed or emitted.
|
||||
const markdownLexer = new Marked({
|
||||
tokenizer: {
|
||||
reflink(source, links) {
|
||||
@@ -84,13 +84,34 @@ export function guidanceProse(markdown) {
|
||||
}
|
||||
|
||||
export function guidanceStandaloneImports(markdown) {
|
||||
return markdownLexer
|
||||
const candidates = markdownLexer
|
||||
.lexer(markdown)
|
||||
.filter((token) => token.type === 'paragraph')
|
||||
.flatMap((token) => [
|
||||
...token.raw.matchAll(/^ {0,3}@([^\s]+)[\t ]*$/gmu),
|
||||
])
|
||||
.map((match) => match[1]);
|
||||
// Markdown can split an HTML container across several top-level tokens.
|
||||
// Check the parsed output tree as well as raw source formatting. Only text
|
||||
// directly inside a root paragraph can supply the standalone directive.
|
||||
const document = parseFragment(new Marked().parse(markdown));
|
||||
const visible = new Map();
|
||||
for (const node of document.childNodes) {
|
||||
if (node.tagName !== 'p') continue;
|
||||
const text = node.childNodes
|
||||
.map((child) =>
|
||||
child.nodeName === '#text' ? child.value : '\uFFFC'
|
||||
)
|
||||
.join('');
|
||||
for (const match of text.matchAll(/^ {0,3}@([^\s]+)[\t ]*$/gmu))
|
||||
visible.set(match[1], (visible.get(match[1]) ?? 0) + 1);
|
||||
}
|
||||
return candidates.filter((candidate) => {
|
||||
const count = visible.get(candidate) ?? 0;
|
||||
if (!count) return false;
|
||||
visible.set(candidate, count - 1);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export function guidanceAnchors(markdown) {
|
||||
|
||||
@@ -572,3 +572,22 @@ for (const body of [
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for (const tag of ['template', 'div']) {
|
||||
test(`HTML container cannot supply Claude import: ${tag}`, async (t) => {
|
||||
assert.match(
|
||||
(
|
||||
await diagnostics(t, {
|
||||
'CLAUDE.md': `<${tag}>\n\n@AGENTS.md\n\n</${tag}>`,
|
||||
})
|
||||
).join('\n'),
|
||||
/standalone @AGENTS.md/
|
||||
);
|
||||
assert.deepEqual(
|
||||
await diagnostics(t, {
|
||||
'CLAUDE.md': `<${tag}>\n\nExample\n\n</${tag}>\n\n@AGENTS.md`,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user