e3966bac72
- 新增 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 - 同步更新技术方案与决策记录
83 lines
2.7 KiB
JavaScript
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);
|
|
});
|