import { test } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { dedent, splitHeredocs, normalizeShell, checkExamples, available, collectExamples } from '../lib/skill-examples.mjs'; test('dedent 去掉列表项缩进', () => { assert.equal(dedent(' a\n b\n'), 'a\n b\n'); }); test('splitHeredocs:python heredoc 体被拆出,shell 部分保留', () => { const { shell, heredocs } = splitHeredocs([ 'python3 - < { assert.match(normalizeShell('gcore -o out \n'), /PLACEHOLDER/); assert.match(normalizeShell('python3 dump.py \n'), /PLACEHOLDER/); }); test('normalizeShell:会话记录块丢弃提示符行', () => { const transcript = ['jdb -classpath app.jar com.example.Main', '> stop in Foo.bar', '> print key'].join('\n'); const out = normalizeShell(transcript); assert.match(out, /^jdb /); assert.doesNotMatch(out, /stop in/); }); test('normalizeShell:匿名调试器提示符(无启动行)也不进 shell 检查', () => { const out = normalizeShell('(gdb) set disable-randomization on\n(gdb) b main\n'); assert.doesNotMatch(out, /disable-randomization/); }); const SHA = available(); function fakeSkill(body) { const root = mkdtempSync(join(tmpdir(), 'ex-')); const dir = join(root, 're-x'); mkdirSync(dir, { recursive: true }); writeFileSync(join(dir, 'SKILL.md'), body); return root; } test('checkExamples 报出 python 语法错', { skip: !SHA.python }, () => { const root = fakeSkill('```python\nsimgr.explore(find=0x4011xx, avoid=[])\n```\n'); const { errors } = checkExamples(root); assert.equal(errors.length, 1); assert.match(errors[0].msg, /hexadecimal|invalid/i); rmSync(root, { recursive: true, force: true }); }); test('checkExamples 不误报带占位符的 shell 块', { skip: !SHA.shell }, () => { const root = fakeSkill('```sh\nvol -f dump.raw windows.dlllist -p \n```\n'); const { errors } = checkExamples(root); assert.deepEqual(errors, []); rmSync(root, { recursive: true, force: true }); }); test('checkExamples 检查 sh 块里嵌的 python heredoc', { skip: !SHA.python }, () => { const root = fakeSkill('```sh\npython3 - < { const root = fakeSkill('# x\n'); mkdirSync(join(root, 're-x', 'references'), { recursive: true }); writeFileSync(join(root, 're-x', 'references', 'a.md'), '```sh\necho hi\n```\n'); const blocks = collectExamples(root); assert.equal(blocks.length, 1); assert.equal(blocks[0].skill, 're-x'); rmSync(root, { recursive: true, force: true }); }); test('仓库自身示例语法通过(有解释器时才跑)', { skip: !(SHA.python && SHA.shell) }, () => { const { errors, checked } = checkExamples('.claude/skills'); assert.ok(checked > 100, `检查块数偏少:${checked}`); assert.deepEqual(errors, [], `示例语法错误:\n${errors.map((e) => `${e.file}:${e.line} ${e.msg}`).join('\n')}`); });