// tests/commands/ultracontinue.test.mjs // Regression tests for /ultracontinue-local (commands/ultracontinue-local.md). // // Steps 2 + 4 of the v3.4.1 hot-fix plan // (project 2026-05-04-v3.3.1-ultracontinue-fixes). // // Pattern mix: // - Pattern B (tmp-dir, mkdtempSync + try/finally) — fixture builds // - Pattern D (markdown structure) — assertions against command prose // - Hook integration via runHook + pre-bash-executor (Pattern C, Step 4) import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { runHook } from '../helpers/hook-helper.mjs'; const HERE = dirname(fileURLToPath(import.meta.url)); const ROOT = join(HERE, '..', '..'); const COMMAND_FILE = join(ROOT, 'commands', 'ultracontinue-local.md'); const PRE_BASH = join(ROOT, 'hooks', 'scripts', 'pre-bash-executor.mjs'); function readCommand() { return readFileSync(COMMAND_FILE, 'utf8'); } function extractPhase(commandText, phaseHeader) { // phaseHeader e.g. "## Phase 0 ", "## Phase 1 ", "## Phase 2 " const startIdx = commandText.indexOf(phaseHeader); if (startIdx === -1) return ''; const rest = commandText.slice(startIdx); // Stop at the next "## Phase " (or "## Hard rules" — also a top-level break) const nextPhase = rest.search(/\n## (?:Phase |Hard )/); if (nextPhase === -1) return rest; return rest.slice(0, nextPhase); } function inProgressState(updatedAtIso) { return { schema_version: 1, project: '.claude/projects/2026-05-04-fixture-a', next_session_brief_path: '.claude/projects/2026-05-04-fixture-a/brief.md', next_session_label: 'Session 2: in progress fixture', status: 'in_progress', updated_at: updatedAtIso, }; } function completedState(updatedAtIso) { return { schema_version: 1, project: '.claude/projects/2026-05-04-fixture-b', next_session_brief_path: '.claude/projects/2026-05-04-fixture-b/brief.md', next_session_label: 'Session N: completed fixture', status: 'completed', updated_at: updatedAtIso, }; } // --------------------------------------------------------------- // Step 2 — Bug 1 regression tests (SC-1, SC-2) // --------------------------------------------------------------- test('ultracontinue Bug 1 — Phase 1 documents auto-discovery sort by Date.parse(updated_at) DESC', () => { // Fixture-builds two project dirs and verifies our chosen sort key // matches what Phase 1 prose documents. const root = mkdtempSync(join(tmpdir(), 'ultracontinue-disc-')); try { const projectsRoot = join(root, '.claude', 'projects'); mkdirSync(join(projectsRoot, '2026-05-04-fixture-a'), { recursive: true }); mkdirSync(join(projectsRoot, '2026-05-04-fixture-b'), { recursive: true }); const inProgress = inProgressState('2026-05-04T18:00:00.000Z'); const completed = completedState('2026-05-03T09:00:00.000Z'); writeFileSync( join(projectsRoot, '2026-05-04-fixture-a', '.session-state.local.json'), JSON.stringify(inProgress, null, 2), ); writeFileSync( join(projectsRoot, '2026-05-04-fixture-b', '.session-state.local.json'), JSON.stringify(completed, null, 2), ); // Numeric sort by Date.parse — newest first. const candidates = [ { ...completed, _path: 'b' }, { ...inProgress, _path: 'a' }, ].sort((x, y) => Date.parse(y.updated_at) - Date.parse(x.updated_at)); assert.equal(candidates[0]._path, 'a', 'newest in_progress fixture must win the sort'); const phase1 = extractPhase(readCommand(), '## Phase 1 '); assert.match( phase1, /Date\.parse/, 'Phase 1 prose must document Date.parse-based sort (numeric, not lexicographic)', ); } finally { rmSync(root, { recursive: true, force: true }); } }); test('ultracontinue Bug 1 — Phase 0 dispatches via parsed flags, not substring contains', () => { const phase0 = extractPhase(readCommand(), '## Phase 0 '); // Must NOT use the legacy "contains --help or -h" substring dispatch. assert.doesNotMatch( phase0, /contains\s+`?--help`?\s+or\s+`?-h`?/i, 'Phase 0 must not dispatch via substring `contains` — use parsed flags / positional', ); // Must reference parseArgs / flags['--help'] / positional[0] (parsed-arg dispatch). const referencesParsedDispatch = /flags\[\s*['"]--help['"]\s*\]/.test(phase0) || /positional\[\s*0\s*\]/.test(phase0); assert.ok( referencesParsedDispatch, 'Phase 0 must dispatch via parsed flags["--help"] or positional[0] === "-h"', ); }); test('ultracontinue Bug 1 — Phase 1 documents empty-args path explicitly to auto-discovery', () => { const phase1 = extractPhase(readCommand(), '## Phase 1 '); // Some explicit text mentioning the empty / whitespace path so a future reader // can't misread Phase 0 as "fall through to usage on empty". assert.match( phase1, /\b(empty|whitespace)\b/i, 'Phase 1 must explicitly handle the empty-args case (auto-discovery)', ); assert.match( phase1, /auto-discover/i, 'Phase 1 must reference auto-discovery as the empty-args fallback', ); }); test('ultracontinue Bug 1 sub — Phase 1 emits SC-2 diagnostic for .md positional arg', () => { const phase1 = extractPhase(readCommand(), '## Phase 1 '); // SC-2 verbatim diagnostic strings. assert.match( phase1, /expected.*/i, 'Phase 1 must mention "expected " in the .md-arg diagnostic', ); assert.match( phase1, /did you mean to paste/i, 'Phase 1 must mention "did you mean to paste" in the .md-arg diagnostic', ); // Detection condition must reference .md. assert.match( phase1, /\.md\b/, 'Phase 1 must detect .md positional arg (case for SC-2)', ); }); // --------------------------------------------------------------- // Step 4 — Bug 2 regression tests (SC-3) // --------------------------------------------------------------- test('ultracontinue Bug 2 — pre-bash-executor ALLOWS resolved validator invocation', async () => { // (d-1) Sanity-check that the planned Phase 2 Bash form (validator // invocation with a concrete absolute path) is not blocked by the // marketplace pre-bash-executor hook chain. const cmd = "node lib/validators/session-state-validator.mjs --json /tmp/fixture-not-real/.session-state.local.json"; const { code } = await runHook(PRE_BASH, { tool_name: 'Bash', tool_input: { command: cmd } }); assert.strictEqual(code, 0, 'pre-bash-executor must not block resolved validator invocations'); }); test('ultracontinue Bug 2 — Phase 2 contains no {state-file-path} or any {curly-template} placeholder', () => { // (d-2) Pattern D structure test. The fix must eliminate the // {state-file-path} placeholder and any other {anything} curly-brace // template syntax from Phase 2 — substitution failures are the // root cause of the path-guard hook crash. const phase2 = extractPhase(readCommand(), '## Phase 2 '); assert.equal( phase2.includes('{state-file-path}'), false, 'Phase 2 must not contain the {state-file-path} placeholder', ); assert.doesNotMatch( phase2, /\{[a-z][a-z0-9-]*\}/, 'Phase 2 must not contain any {lowercase-template} curly-brace placeholder', ); assert.match( phase2, /Read tool/, 'Phase 2 must document the deterministic Read tool flow', ); });