Repository navigation
fix(cli): keep project folders from choosing the programs and config the CLI runs - #7511
Conversation
On Windows a bare command name is looked up in the working directory before PATH. The CLI now sets NoDefaultCurrentDirectoryInExePath at startup, which Node's spawn and cmd.exe both honor and child processes inherit, so tools such as powershell.exe, taskkill and npx.cmd always come from PATH. The shared ffmpeg/ffprobe resolver no longer scans the working directory on Windows and skips relative PATH entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ectory `python -c` and `python -m` put the working directory first on sys.path, so a file in the project named like a probed module ran instead of the installed package. Inline helper code now drops that entry before any import, and `-m` helpers (pip) run through runpy after the same step. Paths and arguments are unchanged. On Windows, findPython also skips a `where` match in the working directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f the project The global skills install ran `npx skills@<pin>` in the current folder, so npm could take the package or registry from that project's node_modules or .npmrc. It now runs from the home folder; the install is global either way. When the CLI starts the media-use engine it hands over its own entry file. The local voice fallback and the audio helpers then run that same CLI with the same Node instead of resolving `npx hyperframes` from the project. Without a handed-over entry they behave as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The audio scripts copied every key of a project .env into their environment except two. They now take the same kind of short allowlist the CLI uses: HeyGen, ElevenLabs and Gemini API keys plus a few harmless settings. Program overrides, runtime options, credential files and the credential store path come only from the person's own environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The source-build fallback cloned the upstream default branch. It now clones the v1.9.5 release tag and refuses to build unless the checkout is the reviewed commit for that tag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registry cache file names now include a digest of the exact registry URL, and each entry records the URL it came from, so one registry's cached manifest is never read for another. Entries from older versions are refetched once. Registry installs refuse targets inside .git or any hidden directory, hidden configuration files, and package.json, hyperframes.json and hyperframes.lock.json. An example installed by `init` may still bring its own package.json and .gitignore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Vertex keys Capture writes AGENTS.md, CLAUDE.md and .cursorrules from the page's title, font names and colours. Page text is now flattened to one short line and colours must be hex values. Existing agent files that capture did not write are kept, with a warning. HYPERFRAMES_VERTEX_SERVICE_ACCOUNT must now be a plain service-account key with a Google token endpoint; other credential types skip captioning with a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Edit accuracy: accurate 267 (base branch 267), smooth 94 of thoseThe gate passes. Quarantined, measured but not gated (8)
Unstable (1)
|
… comment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l without HOME Windows drops trailing dots and spaces from a path segment and reads name:stream as name, so package.json. or .git./hooks reached the files the target rules refuse. The global skills install now falls back to the temp folder when HOME does not exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 1 important · 3 nits · head 039ebac
- blocker
packages/cli/src/warmCheck.ts:1-6: the warm-check server is a separate tsup entry, and it never imports./utils/windowsExeSearch.js.bin/hyperframes.mjsloadswarmCheckClientbeforecli.js, andlaunchEnv = { ...process.env }is copied before the variable is set.startNextWarmCheckspawns the server with that env andcwd= project. The server's startupprelaunchBrowserForCheck→watchChromeTree→windowsChromeWatchdog.ts:8then runs a barepowershell.exewithoutNoDefaultCurrentDirectoryInExePath. A non-TTYhyperframes checkon Windows in a project that containspowershell.exetherefore runs the project's copy (traced through libuvsearch_path; not run on Windows). Fix: importwindowsExeSearchfirst inwarmCheck.ts, and set the variable inlaunchEnvtoo. - important
packages/cli/src/registry/installer.ts:146,160: only the 8.3 short name of.gitis refused. A registry item can targetCLAUDE~1/settings.json,VSCODE~1/tasks.json,PACKAG~1.JSOorNPMRC~1, andpublication.tsrealpaths existing segments, so on NTFS with 8.3 names these write the protected files. A probe confirmed all four pass the check. Fix: refuse/~\d+(\.|$)/segments, or apply the rules to the realpath'd relative path. - nit
installer.ts:171-176: on base,unsafeSegmentalready rejects the Windows-name cases (package.json.,::$DATA,.git./…) andhyperframes.lock.json, so those 5 test cases pass on base. That contradicts "each new test fails without its fix". - nit
installer.ts:134-140: the new constants sit betweenassertSafeTarget's JSDoc and the function. - nit
whisper/manager.ts:191:assertPinnedCheckoutruns only on a fresh clone, so an existing cache checkout from an older CLI is built unchecked.
Scope
I read the full non-test diff and warmCheck/warmCheckClient/bin. These hold: the whisper pin (tag v1.9.5 = d1be6fde), the Python -m isolation (a planted json.py is not imported, pip still runs), and the media-use .env allowlist. Local Studio vite and .hyperframes/bin are kept. CLI vitest 161/162 pass (testHome needs a bun binary), and parsers and node --test suites pass. CI is green.
— Genesis
…ram entry The warm-check server and client are their own tsup entries and copied the environment before cli.js set NoDefaultCurrentDirectoryInExePath, so the server spawned a bare powershell.exe that Windows could take from the project folder. Every entry that hides child consoles now also loads windowsExeSearch first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A segment like CLAUDE~1 or PACKAG~1.JSO opens the long name on NTFS, so it reached the files the target rules refuse. The trailing-dot and stream-name normalization is removed: publication already rejects those segments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review follow-up:
|
There was a problem hiding this comment.
Verdict: APPROVE — 3 resolved, 0 open, 0 new · 039ebac → 9a7d15b. warmCheck.ts/warmCheckClient.ts (and the other process entries) import windowsExeSearch first. The spawned server env carries the setting, and a tsup-entry census pins it. Short-name segments are refused. With the previous sources, the census, warm-check spawn and all 4 short-name tests go red; at head, 38/38 pass. CI is green.
— Genesis
What
Keep a project folder from choosing the programs, imports and configuration the CLI and skill scripts run:
PATH, never the working directory. On Windows every CLI program entry (the CLI, the warm-check client and server, the workers and the font tool) setsNoDefaultCurrentDirectoryInExePathbefore it spawns anything, and child processes inherit it. The documented.hyperframes/binand@ffmpeg-installerffmpeg lookups are kept.-c/-mno longer import modules from the working directory.HOMEdoes not exist), so a project'snode_modulesor.npmrccannot substitute the pinned package. media-use helper scripts started byhyperframes media-userun nestednpx hyperframescalls (including the Kokoro voice fallback) through the CLI that started them. Scripts started directly still usenpxas before..env(the same policy as the CLI). Credential paths,HEYGEN_API_BASE,HYPERFRAMES_PYTHON,HYPERFRAMES_CONFIG_DIRand similar still work from the shell..git, hidden tool directories, hidden config files, orpackage.json/hyperframes.json(except an example's own files oninit). Windows short-name segments such asCLAUDE~1orPACKAG~1.JSOare refused too. Trailing dots, spaces and stream names were already refused when the file is written. Manifest caches are keyed by a digest of the registry URL and record their source, so two registries never share a cache entry.CLAUDE.md,AGENTS.mdor.cursorrulesit did not write (with a warning), and accepts only a plain service-account key inHYPERFRAMES_VERTEX_SERVICE_ACCOUNT.Why
Opening or working in a project from someone else should not let that project run code or redirect credentials through the CLI.
Related work
None.
How
Local changes in the process-spawn helpers, the Python runner, the skills/registry commands, the whisper manager, capture, and the media-use env loader. Local Studio mode in
preview(running the project's own@hyperframes/studiowhen installed) is documented behaviour and is unchanged.Test plan
node --testpass.starts the next Windows warm-check server with the working-directory exe lookup off,each one that starts its own process turns the lookup off before it spawns anything, andrefuses the configuration targetforCLAUDE~1/settings.json,VSCODE~1/tasks.json,PACKAG~1.JSOandNPMRC~1, shown by running them with the fix stashed (2 and 4 failures).ffmpeg.exeloose in the project folder must put it onPATHor in.hyperframes/bin. Third-party registry items that install dotfiles orpackage.jsonare refused; no item in this repo's registry is affected (checked all 457). Each registry cache refetches once after upgrading.🤖 Generated with Claude Code