mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
* docs(skills): design implementation synchronization * docs(skills): plan implementation synchronization * fix(release): filter internal notes from public body * docs(release): synchronize release workflow guidance * fix(stalker): normalize catalog series flags * fix(stalker): preserve progress with scoped episode IDs * fix(playback): expose strict position persistence * docs(stalker): record series position compatibility * test(skills): validate repository skill contracts * fix(database): keep SQL trace values private * docs(skills): refresh Nx and SQLite ownership * docs(skills): align provider and UI guidance * docs(skills): tighten validated guidance * docs(release): require exact release pushes * style(electron): remove trailing blank line * fix(ci): classify repository skills coverage
302 lines
9.7 KiB
JavaScript
302 lines
9.7 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import {
|
|
mkdir,
|
|
mkdtemp,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import { basename, dirname, join } from 'node:path';
|
|
import test from 'node:test';
|
|
|
|
import { validateRepositorySkills } from './validate-repository-skills.mjs';
|
|
|
|
const validReleaseSkills = {
|
|
'release-cut': `---
|
|
name: release-cut
|
|
description: Use when preparing a repository release.
|
|
---
|
|
|
|
# Release Cut
|
|
`,
|
|
'release-notes': `---
|
|
name: release-notes
|
|
description: Use when deciding whether a change needs release notes.
|
|
---
|
|
|
|
# Release Notes
|
|
`,
|
|
};
|
|
|
|
async function writeFixtureFile(rootDir, relativePath, contents) {
|
|
const absolutePath = join(rootDir, relativePath);
|
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
await writeFile(absolutePath, contents);
|
|
}
|
|
|
|
async function createRepository(
|
|
t,
|
|
{
|
|
directoryName = 'example-skill',
|
|
name = directoryName,
|
|
description =
|
|
'"Use when deciding whether type: internal applies to a change."',
|
|
body = [
|
|
'# Example Skill',
|
|
'',
|
|
'Inspect `package.json` and `tools/existing.mjs`.',
|
|
'Examples such as `apps/*-e2e` and `libs/<domain>/feature` are not literal paths.',
|
|
'',
|
|
].join('\n'),
|
|
releaseMirrorTransform = (contents) => contents,
|
|
} = {}
|
|
) {
|
|
const rootDir = await mkdtemp(join(tmpdir(), 'repository-skills-'));
|
|
t.after(() => rm(rootDir, { recursive: true, force: true }));
|
|
|
|
await writeFixtureFile(rootDir, 'package.json', '{}\n');
|
|
await writeFixtureFile(rootDir, 'tools/existing.mjs', 'export {};\n');
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
'.codex/skills/not-a-skill/README.md',
|
|
'No SKILL.md lives here.\n'
|
|
);
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
`.codex/skills/${directoryName}/SKILL.md`,
|
|
`---
|
|
name: ${name}
|
|
description: ${description}
|
|
---
|
|
|
|
${body}`
|
|
);
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
'.codex/skills/single-quoted-skill/SKILL.md',
|
|
`---
|
|
name: single-quoted-skill
|
|
description: 'Use when deciding whether type: internal applies to a release.'
|
|
---
|
|
|
|
# Single-Quoted Skill
|
|
`
|
|
);
|
|
|
|
for (const [skillName, contents] of Object.entries(validReleaseSkills)) {
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
`.codex/skills/${skillName}/SKILL.md`,
|
|
contents
|
|
);
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
`.claude/skills/${skillName}/SKILL.md`,
|
|
releaseMirrorTransform(contents, skillName)
|
|
);
|
|
}
|
|
|
|
return rootDir;
|
|
}
|
|
|
|
test('accepts single- and double-quoted colon-space descriptions and skips path examples', async (t) => {
|
|
const rootDir = await createRepository(t);
|
|
|
|
const result = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(result, {
|
|
checkedSkills: 4,
|
|
diagnostics: [],
|
|
});
|
|
});
|
|
|
|
test('reports a frontmatter name that differs from its directory', async (t) => {
|
|
const rootDir = await createRepository(t, { name: 'different-name' });
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: frontmatter name "different-name" must match directory "example-skill"',
|
|
]);
|
|
});
|
|
|
|
test('reports a description that does not begin with Use when', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
description: 'Repository-specific implementation guidance.',
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: description must start with "Use when"',
|
|
]);
|
|
});
|
|
|
|
test('reports a description longer than 500 characters', async (t) => {
|
|
const description = `Use when ${'x'.repeat(493)}`;
|
|
assert.equal(description.length, 502);
|
|
const rootDir = await createRepository(t, { description });
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: description must be at most 500 characters (received 502)',
|
|
]);
|
|
});
|
|
|
|
test('rejects an indented plain-scalar description continuation', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
description: `Use when editing a skill.\n ${'x'.repeat(501)}`,
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: description must be a one-line frontmatter value',
|
|
]);
|
|
});
|
|
|
|
test('rejects a blank-separated plain-scalar description continuation', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
description: `Use when editing a skill.\n\n ${'x'.repeat(501)}`,
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: description must be a one-line frontmatter value',
|
|
]);
|
|
});
|
|
|
|
test('allows a blank frontmatter separator without treating it as a continuation', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
description: 'Use when editing a skill.\n ',
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, []);
|
|
});
|
|
|
|
test('reports a complete skill longer than 500 words', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
body: `# Example Skill\n\n${Array.from({ length: 501 }, () => 'word').join(' ')}`,
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.equal(diagnostics.length, 1);
|
|
assert.match(
|
|
diagnostics[0],
|
|
/^\.codex\/skills\/example-skill\/SKILL\.md: skill must be at most 500 words \(received \d+\)$/
|
|
);
|
|
});
|
|
|
|
test('reports a backticked literal repository path that does not exist', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
body: '# Example Skill\n\nInspect `docs/missing-file.md`.',
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: referenced path does not exist: docs/missing-file.md',
|
|
]);
|
|
});
|
|
|
|
test('rejects a literal path that escapes the repository even when it exists', async (t) => {
|
|
const rootDir = await createRepository(t);
|
|
const outsideName = `${basename(rootDir)}-outside.md`;
|
|
const outsidePath = join(dirname(rootDir), outsideName);
|
|
const referencedPath = `docs/../../${outsideName}`;
|
|
t.after(() => rm(outsidePath, { force: true }));
|
|
await writeFile(outsidePath, 'outside\n');
|
|
await writeFixtureFile(
|
|
rootDir,
|
|
'.codex/skills/example-skill/SKILL.md',
|
|
`---
|
|
name: example-skill
|
|
description: Use when validating a literal path.
|
|
---
|
|
|
|
Inspect \`${referencedPath}\`.
|
|
`
|
|
);
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
`.codex/skills/example-skill/SKILL.md: referenced path escapes repository root: ${referencedPath}`,
|
|
]);
|
|
});
|
|
|
|
test(
|
|
'propagates non-missing path access failures',
|
|
{ skip: process.platform === 'win32' },
|
|
async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
body: '# Example Skill\n\nInspect `docs/loop/file.md`.',
|
|
});
|
|
await mkdir(join(rootDir, 'docs'));
|
|
await symlink('loop', join(rootDir, 'docs/loop'));
|
|
|
|
await assert.rejects(
|
|
validateRepositorySkills({ rootDir }),
|
|
(error) => {
|
|
assert.equal(error.code, 'ELOOP');
|
|
return true;
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
test('reports byte-different release skill mirrors', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
releaseMirrorTransform: (contents, skillName) =>
|
|
skillName === 'release-notes' ? `${contents}\n` : contents,
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/release-notes/SKILL.md: differs from .claude/skills/release-notes/SKILL.md',
|
|
]);
|
|
});
|
|
|
|
test('reports a missing release skill mirror without stopping validation', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
description: 'Implementation guidance.',
|
|
});
|
|
await rm(
|
|
join(rootDir, '.claude/skills/release-cut/SKILL.md')
|
|
);
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: description must start with "Use when"',
|
|
'.claude/skills/release-cut/SKILL.md: required release skill mirror is missing',
|
|
]);
|
|
});
|
|
|
|
test('returns every diagnostic in deterministic order', async (t) => {
|
|
const rootDir = await createRepository(t, {
|
|
name: 'wrong-name',
|
|
description: 'Implementation guidance.',
|
|
body: 'Inspect `docs/z-missing.md` and `docs/a-missing.md`.',
|
|
releaseMirrorTransform: (contents, skillName) =>
|
|
skillName === 'release-notes' ? `${contents}\n` : contents,
|
|
});
|
|
|
|
const { diagnostics } = await validateRepositorySkills({ rootDir });
|
|
|
|
assert.deepEqual(diagnostics, [
|
|
'.codex/skills/example-skill/SKILL.md: frontmatter name "wrong-name" must match directory "example-skill"',
|
|
'.codex/skills/example-skill/SKILL.md: description must start with "Use when"',
|
|
'.codex/skills/example-skill/SKILL.md: referenced path does not exist: docs/a-missing.md',
|
|
'.codex/skills/example-skill/SKILL.md: referenced path does not exist: docs/z-missing.md',
|
|
'.codex/skills/release-notes/SKILL.md: differs from .claude/skills/release-notes/SKILL.md',
|
|
]);
|
|
});
|