From 425483e0e135b44f266e9e148c60e68a4399fb81 Mon Sep 17 00:00:00 2001 From: Thomas Byr Date: Fri, 28 Aug 2026 17:00:29 +0200 Subject: [PATCH] fix(models): spawn npm through the shell on Windows execFile cannot spawn npm's .cmd shim directly on Windows, so the catalog sync and drift check failed with spawn npm ENOENT. Route npm invocations through the shell with argument quoting on Windows and keep direct execFile calls elsewhere. --- .../check-commandcode-model-metadata.ts | 26 ++++++++++++++++--- CHANGELOG.md | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/scripts/check-commandcode-model-metadata.ts b/.github/scripts/check-commandcode-model-metadata.ts index b83c386..543e8e0 100644 --- a/.github/scripts/check-commandcode-model-metadata.ts +++ b/.github/scripts/check-commandcode-model-metadata.ts @@ -18,6 +18,26 @@ const MODELS_REFERENCE_PATH = "dist/bundled/command-code-knowledge/reference/mod const CLI_BUNDLE_PATH = "dist/cli.mjs" const TEXT_ONLY_MARKER = ',__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")' const VALID_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"]) + +function quoteWindowsArgument(argument: string): string { + if (argument.length === 0) return '""' + if (!/[\s"]/.test(argument)) return argument + return `"${argument.replaceAll('"', '\\"')}"` +} + +/** + * Run npm on Windows and POSIX. npm is a `.cmd` shim on Windows, which + * execFile cannot spawn directly, so route it through the shell there with + * shell-safe quoting. + */ +function execNpmFileAsync( + args: readonly string[], + options: { cwd: string; encoding: "utf-8"; maxBuffer?: number }, +): Promise<{ stdout: string }> { + if (process.platform !== "win32") return execFileAsync("npm", args, options) + const command = ["npm", ...args.map(quoteWindowsArgument)].join(" ") + return execFileAsync(command, { ...options, shell: true }) +} const CATALOG_SOURCE_PATH = new URL("../../src/commandcode-catalog.ts", import.meta.url) const README_PATH = new URL("../../README.md", import.meta.url) @@ -417,8 +437,7 @@ async function resolvePackageSpec( ): Promise { if (packageSpec !== "command-code@latest") return packageSpec - const { stdout } = await execFileAsync( - "npm", + const { stdout } = await execNpmFileAsync( ["view", packageSpec, "version", "--json", "--prefer-online", "--cache", npmCacheDirectory], { cwd: directory, @@ -437,8 +456,7 @@ async function inspectPackedPackage(packageSpec: string): Promise<{ try { const resolvedPackageSpec = await resolvePackageSpec(packageSpec, directory, npmCacheDirectory) - const { stdout } = await execFileAsync( - "npm", + const { stdout } = await execNpmFileAsync( ["pack", resolvedPackageSpec, "--json", "--prefer-online", "--cache", npmCacheDirectory], { cwd: directory, diff --git a/CHANGELOG.md b/CHANGELOG.md index db107f2..53a1a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Fix `npm run sync:commandcode-catalog` and `npm run check:commandcode-catalog` on Windows by spawning npm through the shell. + ## 0.6.0 - 2026-08-25 - Allow switching from a vision-capable model to a text-only model by omitting historical image tool results while preserving their text output; direct image prompts still fail clearly.