npx claude-mem@latest install --ide codex-cli fails every time on Windows, even when codex, codex.cmd, and codex.ps1 are all genuinely present and resolvable (where codex finds all three — this is not a "codex isn't installed" situation):
Installation failed: codex plugin marketplace add <path> failed with exit code 1: '\"C:\Users\<user>\AppData\Roaming\npm\codex.cmd\"' is not recognized as an internal or external command, operable program or batch file.
...
Installation Aborted: all-ides-failed
Root cause, in dist/npx-cli/index.js:
function l3(t){return`"${t.replace(/"/g,'""')}"`}
function Us(t,e,r,i=process.platform){
let n={...i==="win32"?{windowsHide:!0}:{},...r};
return i==="win32"&&IA.has(Cb(t).toLowerCase())
?{command:process.env.ComSpec??"cmd.exe",args:["/d","/s","/c",[t,...e].map(l3).join(" ")],options:n}
:{command:t,args:[...e],options:n}
}
(IA = new Set([".cmd",".bat"]))
When the resolved codex command has a .cmd/.bat extension (the normal case for an npm-global-installed CLI on Windows — npm creates <name>, <name>.cmd, <name>.ps1, never a .exe), this manually pre-quotes the entire command line (l3 wraps each token in "...", doubling embedded quotes) and hands it to cmd.exe /d /s /c as a single args element. That's the documented-correct low-level pattern for invoking .cmd/.bat from Node on Windows — but the returned options object never sets windowsVerbatimArguments: true.
Without that flag, child_process.spawnSync (called via Nv/codexSpawn) applies its own automatic Windows argv-escaping on top of the already-quoted string, since it has no way to know the string is a pre-built, already-correct command line. That second pass of escaping corrupts it — the already-present " characters get backslash-escaped (\"), which cmd.exe does not treat as an escape sequence (backslash isn't special to cmd.exe's quote parsing), so it sees a malformed token and reports "is not recognized as an internal or external command."
Fix: add windowsVerbatimArguments: true to n in the win32 + .cmd/.bat branch of Us():
return i==="win32"&&IA.has(Cb(t).toLowerCase())
?{command:process.env.ComSpec??"cmd.exe",args:["/d","/s","/c",[t,...e].map(l3).join(" ")],options:{...n,windowsVerbatimArguments:!0}}
:{command:t,args:[...e],options:n}
This is the standard, documented workaround for this exact Node-on-Windows spawn pattern (manually pre-quoting a .cmd/.bat invocation string) — without it, every Windows install of the codex-cli IDE integration fails, unconditionally.
npx claude-mem@latest install --ide codex-clifails every time on Windows, even whencodex,codex.cmd, andcodex.ps1are all genuinely present and resolvable (where codexfinds all three — this is not a "codex isn't installed" situation):Root cause, in
dist/npx-cli/index.js:(
IA = new Set([".cmd",".bat"]))When the resolved
codexcommand has a.cmd/.batextension (the normal case for an npm-global-installed CLI on Windows — npm creates<name>,<name>.cmd,<name>.ps1, never a.exe), this manually pre-quotes the entire command line (l3wraps each token in"...", doubling embedded quotes) and hands it tocmd.exe /d /s /cas a singleargselement. That's the documented-correct low-level pattern for invoking.cmd/.batfrom Node on Windows — but the returnedoptionsobject never setswindowsVerbatimArguments: true.Without that flag,
child_process.spawnSync(called viaNv/codexSpawn) applies its own automatic Windows argv-escaping on top of the already-quoted string, since it has no way to know the string is a pre-built, already-correct command line. That second pass of escaping corrupts it — the already-present"characters get backslash-escaped (\"), whichcmd.exedoes not treat as an escape sequence (backslash isn't special tocmd.exe's quote parsing), so it sees a malformed token and reports "is not recognized as an internal or external command."Fix: add
windowsVerbatimArguments: truetonin thewin32+.cmd/.batbranch ofUs():This is the standard, documented workaround for this exact Node-on-Windows spawn pattern (manually pre-quoting a
.cmd/.batinvocation string) — without it, every Windows install of thecodex-cliIDE integration fails, unconditionally.