Files
Genarrative/apps/ai-game-creator-shell/scripts/check-skill-pack.test.mjs
T
k88936 e3966bac72 注册平台抽象与小红书小工具两项 AGC 审核 Skill,并让隐藏文件永不进包
- 新增 platform-abstract 与 vite-export-xhs-minitool 两项审核 Skill
- agc-skill-pack.v1 清单版本升至 2026-08-26.40,两个新条目 sha256 现场重算
- skill-pack 工具新增隐藏文件规则:. 前缀文件/目录不写清单、不参与指纹、不安装、不可读取
- JS 与 Rust 白名单同步扩到 10 项,AGC_SKILL_PACK_FILES 增加 13 条 include_bytes!
- codex_app_server 三条 skills/list 测试夹具补齐两个新 Skill 名
- 补充隐藏路径拒绝与收集器忽略隐藏文件的 JS/Rust 测试
- 修掉 pack.mjs / validate.mjs 的 eslint 问题与三处空白,使其通过 pre-commit 与 git diff --check
- 同步更新技术方案与决策记录
2026-10-04 19:23:12 +08:00

83 lines
2.7 KiB
JavaScript

import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
import {
collectBundledFiles,
computeSkillContentFingerprint,
inspectSkillPack,
isSafeSkillRelativePath,
} from './skill-pack-manifest.mjs';
test('bundled skill pack manifest is synchronized', () => {
const result = inspectSkillPack();
assert.deepEqual(result.mismatches, []);
});
test('hidden skill paths are rejected and never declared', () => {
assert.equal(isSafeSkillRelativePath('SKILL.md'), true);
assert.equal(isSafeSkillRelativePath('references/contract.md'), true);
assert.equal(isSafeSkillRelativePath('.selective_rule.txt'), false);
assert.equal(isSafeSkillRelativePath('scripts/.pack.test.mjs'), false);
assert.equal(isSafeSkillRelativePath('.hidden/SKILL.md'), false);
});
test('skill pack collector ignores hidden files and directories', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-skill-hidden-'));
try {
fs.mkdirSync(path.join(root, 'demo', 'scripts'), { recursive: true });
fs.writeFileSync(path.join(root, 'demo', 'SKILL.md'), '# demo\n');
fs.writeFileSync(path.join(root, 'demo', '.selective_rule.txt'), 'local\n');
fs.writeFileSync(
path.join(root, 'demo', 'scripts', 'pack.mjs'),
'export {};\n',
);
fs.writeFileSync(
path.join(root, 'demo', 'scripts', '.pack.test.mjs'),
'test\n',
);
assert.deepEqual(collectBundledFiles(root), [
'demo/SKILL.md',
'demo/scripts/pack.mjs',
]);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('skill content fingerprint canonicalizes CRLF', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agc-skill-pack-'));
try {
fs.mkdirSync(path.join(root, 'demo'), { recursive: true });
const entry = {
name: 'demo',
files: ['SKILL.md'],
};
fs.writeFileSync(path.join(root, 'demo', 'SKILL.md'), 'line 1\nline 2\n');
const lf = computeSkillContentFingerprint(root, entry);
fs.writeFileSync(
path.join(root, 'demo', 'SKILL.md'),
'line 1\r\nline 2\r\n',
);
assert.equal(computeSkillContentFingerprint(root, entry), lf);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('check command without arguments remains read-only', () => {
const scriptPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'check-skill-pack.mjs',
);
const result = spawnSync(process.execPath, [scriptPath], {
encoding: 'utf8',
});
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /\[skill-pack\] OK/u);
});