// tests/commands/trekendsession.test.mjs // Regression tests for /trekendsession (commands/trekendsession.md). // // Bug (2026-07-03): two of the three !`...` eager-exec blocks contained // unresolved placeholders ( etc.). The harness executes // eager-exec blocks at command LOAD time, so zsh parsed as // input redirection and the command aborted before the model saw a single // instruction. Eager-exec is only valid for self-contained commands. // // Pattern D (markdown structure) — assertions against command prose. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFileSync, readdirSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; const HERE = dirname(fileURLToPath(import.meta.url)); const ROOT = join(HERE, '..', '..'); const COMMANDS_DIR = join(ROOT, 'commands'); const COMMAND_FILE = join(COMMANDS_DIR, 'trekendsession.md'); function readCommand() { return readFileSync(COMMAND_FILE, 'utf8'); } function extractPhase(commandText, phaseHeader) { const startIdx = commandText.indexOf(phaseHeader); if (startIdx === -1) return ''; const rest = commandText.slice(startIdx); const nextPhase = rest.search(/\n## (?:Phase |Hard )/); if (nextPhase === -1) return rest; return rest.slice(0, nextPhase); } // Extract all eager-exec blocks (!`...`) from a command/skill file, // including multi-line blocks. Returns [{ content, line }]. function extractEagerBlocks(text) { const blocks = []; const re = /!`([^`]+)`/g; let m; while ((m = re.exec(text)) !== null) { const line = text.slice(0, m.index).split('\n').length; blocks.push({ content: m[1], line }); } return blocks; } // --------------------------------------------------------------- // Marketplace-wide regression guard: eager-exec blocks must be // self-contained. An unresolved placeholder ( or {curly}) in an // eager block is executed verbatim by the shell at load time — is // parsed as input redirection and aborts the whole command load. // --------------------------------------------------------------- test('eager-exec guard — no !`-block in commands/ contains an unresolved placeholder', () => { const offenders = []; for (const file of readdirSync(COMMANDS_DIR).filter((f) => f.endsWith('.md'))) { const text = readFileSync(join(COMMANDS_DIR, file), 'utf8'); for (const { content, line } of extractEagerBlocks(text)) { // Placeholder conventions: or {curly_word}. Curly must // contain a separator (- or _) so JS destructuring like {join} in a // legitimate self-contained script does not false-positive; angle // placeholders are unambiguous (shell would parse them as redirects). if (/<[a-z][a-z0-9_-]*>/.test(content) || /\{[a-z][a-z0-9]*([_-][a-z0-9]+)+\}/.test(content)) { offenders.push(`${file}:${line}`); } } } assert.deepEqual( offenders, [], `eager-exec !\`-blocks run at command LOAD time and must be self-contained; ` + `placeholder found in: ${offenders.join(', ')}`, ); }); // --------------------------------------------------------------- // trekendsession-specific: exactly one eager block (Phase 1 project // discovery — self-contained, legitimate); Phases 3 and 4 are runtime // Bash-tool commands with model-substituted values, never eager. // --------------------------------------------------------------- test('trekendsession — exactly one eager-exec block remains (Phase 1 discovery)', () => { const cmd = readCommand(); const blocks = extractEagerBlocks(cmd); assert.equal( blocks.length, 1, `expected exactly 1 eager-exec block (Phase 1 discovery), got ${blocks.length} at line(s) ${blocks.map((b) => b.line).join(', ')}`, ); assert.match( blocks[0].content, /readdirSync\(root\)/, 'the surviving eager block must be the self-contained Phase 1 discovery script', ); }); test('trekendsession Phase 3 — atomic-write block is runtime Bash (no eager prefix) with plugin-root import', () => { const phase3 = extractPhase(readCommand(), '## Phase 3 '); assert.doesNotMatch(phase3, /!`/, 'Phase 3 must not use eager-exec — values exist only at runtime'); assert.match( phase3, /\$\{CLAUDE_PLUGIN_ROOT\}\/lib\/util\/atomic-write\.mjs/, 'Phase 3 import must use the absolute ${CLAUDE_PLUGIN_ROOT} path — cwd is the user repo, not the plugin root', ); assert.doesNotMatch( phase3, /['"]\.\/lib\/util\/atomic-write\.mjs['"]/, 'Phase 3 must not import atomic-write.mjs via a cwd-relative path', ); }); test('trekendsession Phase 4 — validator call is runtime Bash (no eager prefix) with plugin-root path', () => { const phase4 = extractPhase(readCommand(), '## Phase 4 '); assert.doesNotMatch(phase4, /!`/, 'Phase 4 must not use eager-exec — the state-file path exists only at runtime'); assert.match( phase4, /\$\{CLAUDE_PLUGIN_ROOT\}\/lib\/validators\/session-state-validator\.mjs/, 'Phase 4 validator path must use the absolute ${CLAUDE_PLUGIN_ROOT} convention', ); assert.doesNotMatch( phase4, /<[a-z][a-z0-9_-]*>/, 'Phase 4 must not use placeholders in commands — zsh parses as input redirection', ); });