diff --git a/lib/validators/brief-validator.mjs b/lib/validators/brief-validator.mjs index f93d2e2..7f25d90 100644 --- a/lib/validators/brief-validator.mjs +++ b/lib/validators/brief-validator.mjs @@ -252,7 +252,11 @@ if (import.meta.url === `file://${process.argv[1]}`) { const minIdx = args.indexOf('--min-version'); const minBriefVersion = minIdx >= 0 ? args[minIdx + 1] : undefined; // filePath is the first positional, skipping the --min-version value token. - const filePath = args.find((a, i) => !a.startsWith('--') && i !== minIdx + 1); + // Guard: when --min-version is absent (minIdx === -1) the skip index must be -1, + // not 0 — otherwise the no-flag invocation `brief-validator.mjs ` drops + // the file (which sits at index 0) and bails to Usage. + const skipIdx = minIdx >= 0 ? minIdx + 1 : -1; + const filePath = args.find((a, i) => !a.startsWith('--') && i !== skipIdx); if (!filePath) { process.stderr.write('Usage: brief-validator.mjs [--soft] [--min-version ] \n'); process.exit(2); diff --git a/tests/validators/brief-validator.test.mjs b/tests/validators/brief-validator.test.mjs index a24d9d4..3a05928 100644 --- a/tests/validators/brief-validator.test.mjs +++ b/tests/validators/brief-validator.test.mjs @@ -1,5 +1,9 @@ import { test } from 'node:test'; import { strict as assert } from 'node:assert'; +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; import { validateBriefContent } from '../../lib/validators/brief-validator.mjs'; const GOOD_BRIEF = `--- @@ -393,3 +397,38 @@ test('validateBrief — S18 min-version: trekreview brief is exempt (no framing const r = validateBriefContent(REVIEW_AS_BRIEF, { minBriefVersion: '2.2' }); assert.ok(!r.warnings.find(w => w.code === 'BRIEF_VERSION_BELOW_MINIMUM')); }); + +// S56 — CLI arg-parsing regression. The no-flag invocation `brief-validator.mjs ` +// used to bail to Usage/exit 2 because the --min-version skip index was 0 when the flag +// was absent, dropping the file positional (which sits at argv index 0). +test('CLI — no-flag invocation reaches validation, does not bail to Usage (S56 regression)', () => { + const dir = mkdtempSync(join(tmpdir(), 'brief-cli-')); + try { + const file = join(dir, 'brief.md'); + writeFileSync(file, GOOD_BRIEF); + // execFileSync throws on non-zero exit; pre-fix this bailed to Usage (exit 2). + const out = execFileSync(process.execPath, [ + 'lib/validators/brief-validator.mjs', + file, + ], { encoding: 'utf-8' }); + assert.match(out, /PASS/); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +}); + +test('CLI — --min-version still locates the file positional after the value token (S56)', () => { + const dir = mkdtempSync(join(tmpdir(), 'brief-cli-')); + try { + const file = join(dir, 'brief.md'); + writeFileSync(file, GOOD_BRIEF); + const out = execFileSync(process.execPath, [ + 'lib/validators/brief-validator.mjs', + '--min-version', '2.0', + file, + ], { encoding: 'utf-8' }); + assert.match(out, /PASS/); + } finally { + rmSync(dir, { recursive: true, force: true }); + } +});