mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-29 21:15:16 +02:00
332 lines
18 KiB
JavaScript
332 lines
18 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const crypto = require('node:crypto');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const childProcess = require('node:child_process');
|
|
const test = require('node:test');
|
|
const Ajv = require('ajv');
|
|
const registryLibrary = require('../../scripts/lib/context-pack-registry');
|
|
const { compileContextProfile } = require('../../scripts/lib/context-profiles');
|
|
const { digestObject } = require('../../scripts/lib/context-profile-support');
|
|
const { KERNEL, update, withFixture, write } = require('./helpers/context-fixture');
|
|
|
|
const REPO_ROOT = path.resolve(__dirname, '../..');
|
|
const MODULE_PATH = path.join(REPO_ROOT, 'scripts/lib/context-carriers.js');
|
|
const SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/context-carrier.schema.json');
|
|
const KERNEL_IDS = KERNEL.map(id => `skill:${id}`);
|
|
const LAYOUTS = {
|
|
claude: { id: 'claude-plugin@1', skillRoot: 'skills', manifestPath: '.claude-plugin/plugin.json' },
|
|
codex: { id: 'codex-plugin@1', skillRoot: 'skills', manifestPath: '.codex-plugin/plugin.json' },
|
|
pi: { id: 'pi-package@1', skillRoot: 'skills', manifestPath: 'package.json' },
|
|
opencode: { id: 'opencode-project@1', skillRoot: '.opencode/skills', manifestPath: null },
|
|
cursor: { id: 'cursor-project@1', skillRoot: '.cursor/skills', manifestPath: null },
|
|
};
|
|
|
|
function plan(options) {
|
|
return require(MODULE_PATH).planContextCarrier(options);
|
|
}
|
|
|
|
function sha256(value) {
|
|
return crypto.createHash('sha256').update(value).digest('hex');
|
|
}
|
|
|
|
function snapshot(root) {
|
|
const visit = relative => fs.readdirSync(path.join(root, relative), { withFileTypes: true })
|
|
.sort((left, right) => left.name.localeCompare(right.name))
|
|
.flatMap(entry => {
|
|
const source = path.join(relative, entry.name);
|
|
return entry.isDirectory() ? visit(source) : [[source, sha256(fs.readFileSync(path.join(root, source)))]];
|
|
});
|
|
return visit('');
|
|
}
|
|
|
|
function withRegistryView(context, transform, operation) {
|
|
const original = registryLibrary.loadContextRegistry;
|
|
const cached = require.cache[MODULE_PATH];
|
|
delete require.cache[MODULE_PATH];
|
|
context.mock.method(registryLibrary, 'loadContextRegistry', options => transform(original(options)));
|
|
try {
|
|
return operation();
|
|
} finally {
|
|
context.mock.restoreAll();
|
|
delete require.cache[MODULE_PATH];
|
|
if (cached) require.cache[MODULE_PATH] = cached;
|
|
}
|
|
}
|
|
|
|
function changeSelectedEntry(registry, transform) {
|
|
return { ...registry, entries: registry.entries.map(entry => (
|
|
entry.id === 'skill:ecc-guide' ? transform(entry) : entry
|
|
)) };
|
|
}
|
|
|
|
test('Lean plans only the three selected whole skill trees and never activates a host', () => withFixture(root => {
|
|
const carrier = plan({ repoRoot: root, target: 'codex', selectionMode: 'auto' });
|
|
assert.equal(carrier.schemaVersion, 'ecc.context-carrier.v1');
|
|
assert.equal(carrier.status, 'planned');
|
|
assert.equal(carrier.active, false);
|
|
assert.equal(carrier.disposition, 'proposed');
|
|
assert.equal(carrier.nativeSupport, 'unobserved');
|
|
assert.equal(carrier.selectionMode, 'auto');
|
|
assert.deepEqual(carrier.selectedIds, KERNEL_IDS);
|
|
assert.deepEqual(carrier.routedIds, ['skill:feature', 'skill:shared']);
|
|
assert.deepEqual(carrier.excludedIds, []);
|
|
assert.deepEqual(carrier.entries.map(entry => entry.id), KERNEL_IDS);
|
|
assert.deepEqual(carrier.files.filter(file => file.kind === 'copy').map(file => file.skillId).sort(), KERNEL_IDS);
|
|
assert.ok(carrier.files.every(file => !/catalog|on-demand|routed/.test(file.destinationPath)));
|
|
assert.match(carrier.limitations.join(' '), /routed.*(?:unimplemented|not implemented)/i);
|
|
}));
|
|
|
|
test('all five source-backed layouts preserve exact selected native directories', () => withFixture(root => {
|
|
for (const [target, layout] of Object.entries(LAYOUTS)) {
|
|
const carrier = plan({ repoRoot: root, target });
|
|
assert.deepEqual(carrier.layout, layout);
|
|
assert.equal(carrier.status, 'planned');
|
|
assert.equal(carrier.nativeSupport, 'unobserved');
|
|
assert.deepEqual(carrier.files.filter(file => file.kind === 'copy').map(file => file.destinationPath).sort(),
|
|
KERNEL.map(id => `${layout.skillRoot}/${id}/SKILL.md`));
|
|
assert.deepEqual(carrier.files.filter(file => file.kind === 'generated').map(file => file.destinationPath),
|
|
layout.manifestPath ? [layout.manifestPath] : []);
|
|
}
|
|
}));
|
|
|
|
test('generated provider manifests contain only explicitly allowed discovery fields', () => withFixture(root => {
|
|
write(root, '.claude-plugin/plugin.json', { name: 'source', hooks: './hooks.json', mcpServers: './mcp.json', commands: './commands' });
|
|
write(root, '.codex-plugin/plugin.json', { name: 'source', hooks: './hooks.json', mcpServers: './mcp.json' });
|
|
write(root, 'package.json', { scripts: { postinstall: 'exit 1' }, pi: { extensions: ['./extension.js'], prompts: ['./commands'] } });
|
|
const expected = {
|
|
claude: { name: 'ecc-context-carrier', skills: ['./skills/'] },
|
|
codex: { name: 'ecc-context-carrier', skills: './skills/' },
|
|
pi: { name: 'ecc-context-carrier', private: true, pi: { skills: ['./skills'] } },
|
|
};
|
|
for (const [target, manifest] of Object.entries(expected)) {
|
|
const generated = plan({ repoRoot: root, target }).files.find(file => file.kind === 'generated');
|
|
assert.deepEqual(JSON.parse(generated.content), manifest);
|
|
assert.equal(generated.encoding, 'utf8');
|
|
assert.equal(generated.bytes, Buffer.byteLength(generated.content, 'utf8'));
|
|
assert.equal(generated.digest, sha256(Buffer.from(generated.content, 'utf8')));
|
|
}
|
|
}));
|
|
|
|
test('Full keeps exclusions out of both discovery files and carrier storage', () => withFixture(root => {
|
|
for (const target of Object.keys(LAYOUTS)) {
|
|
const carrier = plan({ repoRoot: root, profileId: 'full@1', target, exclude: ['skill:feature'] });
|
|
assert.equal(carrier.selectedIds.length, 4);
|
|
assert.deepEqual(carrier.excludedIds, ['skill:feature']);
|
|
assert.deepEqual(carrier.routedIds, []);
|
|
assert.ok(carrier.entries.every(entry => entry.id !== 'skill:feature'));
|
|
assert.ok(carrier.files.every(file => file.skillId !== 'skill:feature' && !file.sourcePath?.startsWith('skills/feature/')));
|
|
}
|
|
}));
|
|
|
|
test('bundled binary resources are copied by descriptor without decoding or script execution', () => withFixture(root => {
|
|
const binary = Buffer.from([0, 255, 128, 1, 13, 10]);
|
|
fs.writeFileSync(path.join(root, 'skills/feature/references/image.bin'), binary);
|
|
write(root, 'skills/feature/never-run.js', 'throw new Error("CARRIER_MUST_NOT_EXECUTE_RESOURCE");');
|
|
const carrier = plan({ repoRoot: root, include: ['skill:feature'] });
|
|
const registry = registryLibrary.loadContextRegistry({ repoRoot: root });
|
|
const entry = registry.entries.find(value => value.id === 'skill:feature');
|
|
const copies = carrier.files.filter(file => file.kind === 'copy' && file.skillId === entry.id);
|
|
assert.equal(copies.length, entry.resources.length);
|
|
for (const resource of entry.resources) {
|
|
const copied = copies.find(file => file.sourcePath === resource.path);
|
|
assert.equal(copied.digest, resource.digest);
|
|
assert.equal(copied.bytes, resource.bytes);
|
|
assert.ok(!Object.hasOwn(copied, 'content'));
|
|
assert.equal(copied.destinationPath, resource.path);
|
|
}
|
|
assert.equal(copies.find(file => file.sourcePath.endsWith('image.bin')).digest, sha256(binary));
|
|
}));
|
|
|
|
test('explicit dependencies and required-resource annotations remain bound to selected copies', () => withFixture(root => {
|
|
update(root, 'manifests/context-packs/skill-registry@1.json', value => ({ ...value, overrides: [{
|
|
id: 'skill:feature', dependencies: ['skill:shared'], requiredResources: ['skills/feature/references/details.md'],
|
|
}] }));
|
|
const carrier = plan({ repoRoot: root, include: ['skill:feature'] });
|
|
assert.ok(carrier.selectedIds.includes('skill:shared'));
|
|
const feature = carrier.entries.find(entry => entry.id === 'skill:feature');
|
|
assert.deepEqual(feature.requiredResources, ['skills/feature/references/details.md']);
|
|
assert.ok(carrier.files.some(file => file.sourcePath === feature.requiredResources[0]));
|
|
assert.ok(carrier.entries.every(entry => carrier.files.some(file => file.sourcePath === entry.sourcePath)));
|
|
}));
|
|
|
|
test('canonical IDs are retained while destination folders use declared native names', () => withFixture(root => {
|
|
write(root, 'skills/feature/SKILL.md', '---\nname: renamed-feature\ndescription: Native name differs from directory ID.\n---\n');
|
|
for (const target of Object.keys(LAYOUTS)) {
|
|
const carrier = plan({ repoRoot: root, target, include: ['skill:feature'] });
|
|
assert.equal(carrier.entries.find(entry => entry.id === 'skill:feature').name, 'renamed-feature');
|
|
assert.ok(carrier.files.some(file => file.skillId === 'skill:feature'
|
|
&& file.destinationPath === `${LAYOUTS[target].skillRoot}/renamed-feature/SKILL.md`));
|
|
}
|
|
}));
|
|
|
|
test('recognized unsupported targets retain the proposal and plan zero files', () => withFixture(root => {
|
|
const targets = registryLibrary.loadContextRegistry({ repoRoot: root }).targets;
|
|
for (const target of targets.filter(value => !Object.hasOwn(LAYOUTS, value))) {
|
|
const carrier = plan({ repoRoot: root, target, exclude: ['skill:feature'] });
|
|
assert.equal(carrier.status, 'unsupported');
|
|
assert.equal(carrier.nativeSupport, 'unobserved');
|
|
assert.equal(carrier.active, false);
|
|
assert.equal(carrier.layout, null);
|
|
assert.deepEqual(carrier.files, []);
|
|
assert.deepEqual(carrier.selectedIds, KERNEL_IDS);
|
|
assert.deepEqual(carrier.excludedIds, ['skill:feature']);
|
|
assert.deepEqual(carrier.entries.map(entry => entry.id), KERNEL_IDS);
|
|
}
|
|
}));
|
|
|
|
test('legacy owner-target declarations are surfaced without suppressing known layouts', () => withFixture(root => {
|
|
const codex = plan({ repoRoot: root, target: 'codex' });
|
|
const pi = plan({ repoRoot: root, target: 'pi' });
|
|
assert.ok(codex.entries.every(entry => entry.installSupport === 'declared'));
|
|
assert.ok(pi.entries.every(entry => entry.installSupport === 'not-declared'));
|
|
assert.equal(pi.status, 'planned');
|
|
assert.deepEqual(pi.selectedIds, codex.selectedIds);
|
|
assert.equal(pi.files.filter(file => file.kind === 'copy').length, 3);
|
|
}));
|
|
|
|
test('canonical provenance and adapter source bindings produce stable portable carrier digests', () => withFixture(root => {
|
|
const input = { repoRoot: root, target: 'codex', include: ['skill:feature', 'skill:shared'] };
|
|
const carrier = plan(input);
|
|
assert.deepEqual(carrier, plan({ ...input, include: [...input.include].reverse() }));
|
|
const context = compileContextProfile(input);
|
|
for (const key of ['registryDigest', 'profileDigest', 'compilerDigest', 'planDigest']) {
|
|
assert.equal(carrier[key], context[key]);
|
|
}
|
|
const adapterSources = ['scripts/lib/context-carriers.js', 'schemas/context-carrier.schema.json'];
|
|
assert.equal(carrier.adapterDigest, digestObject(adapterSources.map(source => ({
|
|
path: source, digest: sha256(fs.readFileSync(path.join(REPO_ROOT, source))),
|
|
}))));
|
|
const { carrierDigest, ...value } = carrier;
|
|
assert.equal(carrierDigest, digestObject(value));
|
|
assert.ok(!JSON.stringify(carrier).includes(root));
|
|
assert.ok(!JSON.stringify(carrier).includes('generatedAt'));
|
|
}));
|
|
|
|
test('resource-only changes alter carrier provenance and file digests', () => withFixture(root => {
|
|
const options = { repoRoot: root, include: ['skill:feature'] };
|
|
const before = plan(options);
|
|
write(root, 'skills/feature/references/details.md', 'Changed resource bytes.\n');
|
|
const after = plan(options);
|
|
assert.notEqual(after.registryDigest, before.registryDigest);
|
|
assert.notEqual(after.carrierDigest, before.carrierDigest);
|
|
const digest = carrier => carrier.files.find(file => file.sourcePath === 'skills/feature/references/details.md').digest;
|
|
assert.notEqual(digest(before), digest(after));
|
|
}));
|
|
|
|
test('registry drift between compilation and carrier inventory fails closed', context => withFixture(root => (
|
|
withRegistryView(context, registry => ({ ...registry, registryDigest: '0'.repeat(64) }), () => {
|
|
assert.throws(() => plan({ repoRoot: root }), /registry.*(?:digest|drift|changed)|(?:digest|drift).*registry/i);
|
|
})
|
|
)));
|
|
|
|
test('missing required-resource metadata or inventory members cannot become partial carriers', context => withFixture(root => {
|
|
for (const transform of [
|
|
({ requiredResources: _, ...entry }) => entry,
|
|
entry => ({ ...entry, requiredResources: ['skills/ecc-guide/absent.md'] }),
|
|
entry => ({ ...entry, resources: [] }),
|
|
entry => ({ ...entry, sourcePath: 'skills/ecc-guide/absent.md' }),
|
|
]) {
|
|
withRegistryView(context, registry => changeSelectedEntry(registry, transform), () => {
|
|
assert.throws(() => plan({ repoRoot: root }), /resource|source.*(?:missing|inventory)/i);
|
|
});
|
|
}
|
|
}));
|
|
|
|
test('duplicate native names and case-colliding destination resources are rejected', context => withFixture(root => {
|
|
write(root, 'skills/feature/SKILL.md', '---\nname: ecc-guide\ndescription: Duplicate native name.\n---\n');
|
|
assert.throws(() => plan({ repoRoot: root, include: ['skill:feature'] }), /name|collision|duplicate/i);
|
|
withRegistryView(context, registry => changeSelectedEntry(registry, entry => ({
|
|
...entry, resources: [...entry.resources, ...['details.md', 'DETAILS.md'].map(file => ({
|
|
path: `skills/ecc-guide/${file}`, digest: 'a'.repeat(64), bytes: 1,
|
|
}))],
|
|
})), () => assert.throws(() => plan({ repoRoot: root }), /collision|duplicate/i));
|
|
}));
|
|
|
|
test('case-aliased ancestor directories with different child files are rejected', context => withFixture(root => (
|
|
withRegistryView(context, registry => changeSelectedEntry(registry, entry => ({
|
|
...entry, resources: [...entry.resources, ...['Case/one.md', 'case/two.md'].map(file => ({
|
|
path: `skills/ecc-guide/${file}`, digest: 'a'.repeat(64), bytes: 1,
|
|
}))],
|
|
})), () => assert.throws(() => plan({ repoRoot: root }), /collision|alias/i))
|
|
)));
|
|
|
|
test('Unicode-normalization-aliased ancestors with different children are rejected', context => withFixture(root => (
|
|
withRegistryView(context, registry => changeSelectedEntry(registry, entry => ({
|
|
...entry, resources: [...entry.resources, ...['caf\u00e9/one.md', 'cafe\u0301/two.md'].map(file => ({
|
|
path: `skills/ecc-guide/${file}`, digest: 'a'.repeat(64), bytes: 1,
|
|
}))],
|
|
})), () => assert.throws(() => plan({ repoRoot: root }), /collision|alias/i))
|
|
)));
|
|
|
|
test('nested SKILL.md resources are rejected case-insensitively', () => withFixture(root => {
|
|
write(root, 'skills/feature/nested/skill.MD', 'Nested discovery entry.');
|
|
assert.throws(() => plan({ repoRoot: root, include: ['skill:feature'] }), /nested|discovery.*entry/i);
|
|
}));
|
|
|
|
test('invalid native names and unsafe resource paths fail before projection', context => withFixture(root => {
|
|
for (const transform of [
|
|
entry => ({ ...entry, name: '../escape' }),
|
|
entry => ({ ...entry, name: 'name with spaces' }),
|
|
entry => ({ ...entry, name: 'a'.repeat(65) }),
|
|
entry => ({ ...entry, resources: [...entry.resources, { path: '../outside', digest: 'a'.repeat(64), bytes: 1 }] }),
|
|
entry => ({ ...entry, resources: [...entry.resources, { path: 'skills/shared/data.bin', digest: 'a'.repeat(64), bytes: 1 }] }),
|
|
]) withRegistryView(context, registry => changeSelectedEntry(registry, transform), () => {
|
|
assert.throws(() => plan({ repoRoot: root }), /name|path|resource|outside|belong/i);
|
|
});
|
|
}));
|
|
|
|
test('unknown targets and externally supplied plans or options are rejected', () => withFixture(root => {
|
|
assert.throws(() => plan({ repoRoot: root, target: 'typo' }), /target/i);
|
|
assert.throws(() => plan({ repoRoot: root, plan: { selectedIds: [] } }), /unknown|option|input/i);
|
|
assert.throws(() => plan({ repoRoot: root, out: '/unused' }), /unknown|option|input/i);
|
|
}));
|
|
|
|
test('read-only planning does not write files, execute processes, or inspect user homes', context => withFixture(root => {
|
|
const before = snapshot(root);
|
|
const forbidden = () => { throw new Error('FORBIDDEN_CARRIER_SIDE_EFFECT'); };
|
|
for (const name of ['writeFileSync', 'appendFileSync', 'mkdirSync', 'rmSync', 'renameSync', 'copyFileSync', 'cpSync']) {
|
|
context.mock.method(fs, name, forbidden);
|
|
}
|
|
for (const name of ['spawn', 'spawnSync', 'exec', 'execSync', 'execFile', 'execFileSync']) {
|
|
context.mock.method(childProcess, name, forbidden);
|
|
}
|
|
context.mock.method(os, 'homedir', forbidden);
|
|
try {
|
|
assert.equal(plan({ repoRoot: root, target: 'codex' }).status, 'planned');
|
|
} finally {
|
|
context.mock.restoreAll();
|
|
}
|
|
assert.deepEqual(snapshot(root), before);
|
|
}));
|
|
|
|
test('published carrier schema validates outputs and rejects extra or capability-bearing fields', () => withFixture(root => {
|
|
const carrier = plan({ repoRoot: root });
|
|
const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, 'utf8'));
|
|
const validate = new Ajv({ allErrors: true, strict: true }).compile(schema);
|
|
assert.equal(validate(carrier), true, JSON.stringify(validate.errors));
|
|
assert.equal(validate({ ...carrier, extra: true }), false);
|
|
assert.equal(validate({ ...carrier, active: true }), false);
|
|
assert.equal(validate({ ...carrier, nativeSupport: 'verified' }), false);
|
|
assert.equal(validate({ ...carrier, files: [{ ...carrier.files[0], hooks: true }] }), false);
|
|
assert.equal(validate({ ...carrier, files: [{ kind: 'generated', destinationPath: 'hooks/hooks.json',
|
|
content: '{}', encoding: 'utf8', digest: sha256('{}'), bytes: 2 }] }), false);
|
|
const unsupported = plan({ repoRoot: root, target: 'gemini' });
|
|
assert.equal(validate(unsupported), true, JSON.stringify(validate.errors));
|
|
assert.equal(validate({ ...unsupported, files: carrier.files }), false);
|
|
}));
|
|
|
|
test('real canonical inventory projects every selected bundled resource without relying on mirrors', () => {
|
|
const carrier = plan({ repoRoot: REPO_ROOT, profileId: 'full@1', target: 'opencode' });
|
|
const registry = registryLibrary.loadContextRegistry({ repoRoot: REPO_ROOT });
|
|
assert.deepEqual(carrier.selectedIds, registry.entries.map(entry => entry.id));
|
|
assert.equal(carrier.files.filter(file => file.kind === 'copy').length,
|
|
registry.entries.reduce((count, entry) => count + entry.resources.length, 0));
|
|
assert.ok(carrier.files.every(file => file.kind !== 'copy' || file.sourcePath.startsWith('skills/')));
|
|
assert.ok(carrier.files.some(file => file.destinationPath === '.opencode/skills/gget/SKILL.md'
|
|
&& file.skillId === 'skill:scientific-pkg-gget'));
|
|
});
|