diff --git a/.agents/skills/impeccable/scripts/critique-storage.mjs b/.agents/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.agents/skills/impeccable/scripts/critique-storage.mjs +++ b/.agents/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.claude/skills/impeccable/scripts/critique-storage.mjs b/.claude/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.claude/skills/impeccable/scripts/critique-storage.mjs +++ b/.claude/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.cursor/skills/impeccable/scripts/critique-storage.mjs b/.cursor/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.cursor/skills/impeccable/scripts/critique-storage.mjs +++ b/.cursor/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.gemini/skills/impeccable/scripts/critique-storage.mjs b/.gemini/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.gemini/skills/impeccable/scripts/critique-storage.mjs +++ b/.gemini/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.github/skills/impeccable/scripts/critique-storage.mjs b/.github/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.github/skills/impeccable/scripts/critique-storage.mjs +++ b/.github/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.kiro/skills/impeccable/scripts/critique-storage.mjs b/.kiro/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.kiro/skills/impeccable/scripts/critique-storage.mjs +++ b/.kiro/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.opencode/skills/impeccable/scripts/critique-storage.mjs b/.opencode/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.opencode/skills/impeccable/scripts/critique-storage.mjs +++ b/.opencode/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.pi/skills/impeccable/scripts/critique-storage.mjs b/.pi/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.pi/skills/impeccable/scripts/critique-storage.mjs +++ b/.pi/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.qoder/skills/impeccable/scripts/critique-storage.mjs b/.qoder/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.qoder/skills/impeccable/scripts/critique-storage.mjs +++ b/.qoder/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.rovodev/skills/impeccable/scripts/critique-storage.mjs b/.rovodev/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.rovodev/skills/impeccable/scripts/critique-storage.mjs +++ b/.rovodev/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.trae-cn/skills/impeccable/scripts/critique-storage.mjs b/.trae-cn/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.trae-cn/skills/impeccable/scripts/critique-storage.mjs +++ b/.trae-cn/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/.trae/skills/impeccable/scripts/critique-storage.mjs b/.trae/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/.trae/skills/impeccable/scripts/critique-storage.mjs +++ b/.trae/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/plugin/skills/impeccable/scripts/critique-storage.mjs b/plugin/skills/impeccable/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/plugin/skills/impeccable/scripts/critique-storage.mjs +++ b/plugin/skills/impeccable/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/skill/scripts/critique-storage.mjs b/skill/scripts/critique-storage.mjs index fa7bdf385..0db53bb56 100644 --- a/skill/scripts/critique-storage.mjs +++ b/skill/scripts/critique-storage.mjs @@ -27,6 +27,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import { pathToFileURL } from 'node:url'; import { getCritiqueDir } from './impeccable-paths.mjs'; const SLUG_MAX = 50; @@ -221,6 +222,10 @@ function main(argv) { } } -if (import.meta.url === `file://${process.argv[1]}`) { +// Why pathToFileURL: on Windows, import.meta.url is file:///D:/... (forward +// slashes) while process.argv[1] is D:\... (backslashes), so the naive +// `file://${process.argv[1]}` compare fails and main() never runs — the +// script silently exits 0. pathToFileURL normalizes both. (issue #155) +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main(process.argv.slice(2)); } diff --git a/tests/critique-storage.test.mjs b/tests/critique-storage.test.mjs index 6524cf950..1bfa47f16 100644 --- a/tests/critique-storage.test.mjs +++ b/tests/critique-storage.test.mjs @@ -6,8 +6,12 @@ import { describe, it, beforeEach, afterEach } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, rmSync } from 'node:fs'; -import { join } from 'node:path'; +import { join, resolve } from 'node:path'; import { tmpdir } from 'node:os'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const SCRIPT = fileURLToPath(new URL('../skill/scripts/critique-storage.mjs', import.meta.url)); import { slugFromTarget, @@ -148,6 +152,36 @@ describe('writeSnapshot + readLatestSnapshot', () => { }); }); +describe('CLI entry point', () => { + // Why a subprocess test: the CLI guard at the bottom of the script + // previously compared import.meta.url to `file://${process.argv[1]}`, + // which silently broke on Windows (forward vs back slashes) — exit 0, + // no output, save skipped. The exported functions kept passing because + // tests never spawned the script as a process. See issue #155. + it('slug subcommand prints a slug and exits 0', () => { + const r = spawnSync(process.execPath, [SCRIPT, 'slug', 'site/pages/index.astro'], { + cwd, + encoding: 'utf-8', + }); + assert.equal(r.status, 0, `stderr: ${r.stderr}`); + assert.equal(r.stdout.trim(), 'site-pages-index-astro'); + }); + + it('slug subcommand exits 1 with a message for empty input', () => { + const r = spawnSync(process.execPath, [SCRIPT, 'slug', ''], { cwd, encoding: 'utf-8' }); + assert.equal(r.status, 1); + assert.match(r.stderr, /no stable slug/); + }); + + it('latest subcommand exits 2 when no snapshot exists', () => { + const r = spawnSync(process.execPath, [SCRIPT, 'latest', 'never-written'], { + cwd, + encoding: 'utf-8', + }); + assert.equal(r.status, 2); + }); +}); + describe('readTrend', () => { it('returns last N entries oldest → newest, filtered by slug', () => { for (let i = 0; i < 6; i++) {