// skill-structure.test.mjs — Verifies SKILL.md frontmatter (v3.0) and commands/ deletion. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { existsSync, readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const PLUGIN_ROOT = join(__dirname, '..'); const SKILL = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md'); function skill() { return readFileSync(SKILL, 'utf-8'); } test('SKILL.md exists at expected path', () => { assert.ok(existsSync(SKILL), `SKILL.md missing at ${SKILL}`); }); test('commands/ directory is deleted (hard cut to skills/)', () => { assert.ok(!existsSync(join(PLUGIN_ROOT, 'commands')), 'commands/ should be deleted'); }); test('hooks/ directory is deleted (v3.0 removed all hooks)', () => { assert.ok(!existsSync(join(PLUGIN_ROOT, 'hooks')), 'hooks/ should be deleted in v3.0'); }); test('SKILL.md has disable-model-invocation: true', () => { assert.match(skill(), /^disable-model-invocation: true$/m); }); test('SKILL.md has NO model: pin (inherits session model for quality synthesis)', () => { const fm = skill().match(/^---\n[\s\S]*?\n---/)[0]; assert.doesNotMatch(fm, /^model:/m); }); test('SKILL.md allowed-tools is Bash sub-scoped and includes Write', () => { const line = skill().match(/^allowed-tools:.*$/m); assert.ok(line, 'allowed-tools line missing'); assert.match(line[0], /Bash\(git:\*\)/); assert.match(line[0], /Bash\(node:\*\)/); assert.match(line[0], /\bWrite\b/); }); test('SKILL.md does not pre-approve curl or wget', () => { const line = skill().match(/^allowed-tools:.*$/m); assert.ok(line, 'allowed-tools line missing'); assert.doesNotMatch(line[0], /\bcurl\b/); assert.doesNotMatch(line[0], /\bwget\b/); }); test('SKILL.md body references handoff-pipeline.mjs', () => { assert.match(skill(), /handoff-pipeline\.mjs/); }); test('SKILL.md body mandates the 👉 NESTE — START HER block', () => { assert.match(skill(), /👉 NESTE — START HER/); }); test('SKILL.md body has Tidsbudsjett (time budget) note', () => { assert.match(skill(), /Tidsbudsjett/); }); test('SKILL.md is STATE.md-centric (overwrites the nearest STATE.md)', () => { const s = skill(); assert.match(s, /STATE\.md/); assert.match(s, /overskriv/i); });