/** * Wave 5 Step 15 — Action-command-template shape tests. * * Verifies that the 7 action command templates have the correct structural * shape after the humanizer integration: * * - All 7 files: contain a Bash invocation block, reference the Read tool, * and contain the `--raw` flag (or the literal `"$ARGUMENTS"` string) so * `--raw` plumbing is uniform across the toolchain. * * - help.md additionally: removes the most obviously technical jargon * ("PreToolUse" / "frontmatter" mentions in the user-facing prose) and * introduces a plain-language vocabulary table referencing the * humanized userImpactCategory and userActionLanguage labels. */ import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFile } from 'node:fs/promises'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const COMMANDS_DIR = resolve(__dirname, '..', '..', 'commands'); const ACTION_FILES = [ 'fix.md', 'rollback.md', 'plan.md', 'implement.md', 'cleanup.md', 'help.md', 'interview.md', ]; const RAW_OR_ARGUMENTS_REGEX = /--raw|"\$ARGUMENTS"/; const BASH_BLOCK_REGEX = /```bash\b/; const READ_TOOL_REGEX = /\bRead\s+tool\b|allowed-tools:.*\bRead\b/; async function readCommand(name) { return await readFile(resolve(COMMANDS_DIR, name), 'utf-8'); } test('Action: every file contains a Bash invocation block', async () => { for (const name of ACTION_FILES) { const content = await readCommand(name); assert.match(content, BASH_BLOCK_REGEX, `${name} missing bash block`); } }); test('Action: every file references the Read tool', async () => { for (const name of ACTION_FILES) { const content = await readCommand(name); assert.match(content, READ_TOOL_REGEX, `${name} missing Read tool reference`); } }); test('Action: every file contains --raw or "$ARGUMENTS" (pass-through plumbing)', async () => { for (const name of ACTION_FILES) { const content = await readCommand(name); assert.match(content, RAW_OR_ARGUMENTS_REGEX, `${name} missing --raw / $ARGUMENTS plumbing`); } }); test('help.md: introduces plain-language vocabulary referencing humanized categories', async () => { const content = await readCommand('help.md'); // At least three of the userImpactCategory labels should appear const labels = ['Configuration mistake', 'Conflict', 'Wasted tokens', 'Missed opportunity', 'Dead config']; const present = labels.filter(l => content.includes(l)); assert.ok(present.length >= 3, `help.md must surface ≥3 humanized impact labels; found ${present.length}: ${present.join(', ')}`); // At least three of the userActionLanguage phrases should appear const actions = ['Fix this now', 'Fix soon', 'Fix when convenient', 'Optional cleanup', 'FYI']; const presentActions = actions.filter(a => content.includes(a)); assert.ok(presentActions.length >= 3, `help.md must surface ≥3 humanized action phrases; found ${presentActions.length}: ${presentActions.join(', ')}`); }); test('help.md: no bare "PreToolUse" jargon in user-facing copy', async () => { const content = await readCommand('help.md'); // Allow the word in code/quoted contexts but the body table descriptions should not lean on it. // Heuristic: no occurrence of "PreToolUse" outside of code spans / quoted blocks. // Simple check: no "PreToolUse" anywhere except in any backtick span — since this file is doc-only, // require zero occurrences. assert.doesNotMatch(content, /\bPreToolUse\b/, 'help.md user copy must not lean on "PreToolUse" jargon — use plain language'); }); test('help.md: no bare "frontmatter" jargon in user-facing copy', async () => { const content = await readCommand('help.md'); assert.doesNotMatch(content, /\bfrontmatter\b/, 'help.md user copy must not lean on "frontmatter" jargon — use plain language ("metadata block at the top of each file")'); });