ktg-plugin-marketplace/plugins/graceful-handoff/tests/skill-structure.test.mjs
Kjell Tore Guttormsen 1a65d8e4d5 feat(graceful-handoff): 2.0 — migrate to skills/ with disable-model-invocation [skip-docs]
Step 1 of v2.0 plan. Hard cut from commands/ to skills/ per Anthropic
recommendation for new plugins. Frontmatter sets disable-model-invocation:
true and pins model: claude-sonnet-4-6. Docs (README, CLAUDE.md, root
README) deferred to Step 9 per plan.
2026-05-01 05:45:26 +02:00

61 lines
2.6 KiB
JavaScript

// skill-structure.test.mjs — Verifies SKILL.md frontmatter 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, '..');
test('SKILL.md exists at expected path', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
assert.ok(existsSync(skillPath), `SKILL.md missing at ${skillPath}`);
});
test('commands/ directory is deleted (hard cut to skills/)', () => {
const commandsDir = join(PLUGIN_ROOT, 'commands');
assert.ok(!existsSync(commandsDir), 'commands/ directory still exists — should be deleted in v2.0');
});
test('SKILL.md has disable-model-invocation: true', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
assert.match(content, /^disable-model-invocation: true$/m);
});
test('SKILL.md has model: claude-sonnet-4-6', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
assert.match(content, /^model: claude-sonnet-4-6$/m);
});
test('SKILL.md has Bash sub-scoped allowed-tools', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
assert.match(content, /Bash\(git:\*\)/);
assert.match(content, /Bash\(node:\*\)/);
});
test('SKILL.md does not pre-approve curl or wget', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
// Frontmatter only — find the allowed-tools line
const allowedToolsLine = content.match(/^allowed-tools:.*$/m);
assert.ok(allowedToolsLine, 'allowed-tools line missing');
assert.doesNotMatch(allowedToolsLine[0], /\bcurl\b/);
assert.doesNotMatch(allowedToolsLine[0], /\bwget\b/);
});
test('SKILL.md body references handoff-pipeline.mjs', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
assert.match(content, /handoff-pipeline\.mjs/);
});
test('SKILL.md body has Tidsbudsjett (time budget) note', () => {
const skillPath = join(PLUGIN_ROOT, 'skills', 'graceful-handoff', 'SKILL.md');
const content = readFileSync(skillPath, 'utf-8');
assert.match(content, /Tidsbudsjett/);
});