Skip to content

fix(cli): support Windows for bricks:install and brick load#130

Merged
samuelds merged 1 commit into
developfrom
fix/windows-spawn-and-path-normalization
May 8, 2026
Merged

fix(cli): support Windows for bricks:install and brick load#130
samuelds merged 1 commit into
developfrom
fix/windows-spawn-and-path-normalization

Conversation

@samuelds
Copy link
Copy Markdown
Contributor

@samuelds samuelds commented May 8, 2026

Summary

Fix 2 bugs Windows remontés par user externe (nvm4w + Codex MCP) :

  1. spawn('npm', ...) avec shell: falseENOENT car npm.cmd non résolu
  2. assertWithinBricksDir() rejette des paths valides à cause du mix \ (Windows) vs / dans le check startsWith

Why

Voir ADR rétroactif (mémoire personnelle) : 2026-05-09-keep-npm-installer-for-bricks qui formalise la décision "rester sur npm" et écarte cross-spawn / nypm / pivot tarball pour cette session.

Changes

  • src/adapters/npm-installer-adapter.ts : shell: process.platform === 'win32' pour résoudre npm.cmd sur Windows, false sur POSIX (pas de shell overhead)
  • src/source/filesystem-source.ts : path.relative() + isAbsolute() au lieu de startsWith fragile, normalise les séparateurs cross-platform et corrige aussi un sibling-prefix escape subtle
  • Tests Vitest cross-platform : mock process.platform Windows/Linux/macOS pour le spawn ; cas escape/inside/exact pour assertWithinBricksDir

Test plan

  • Tests Vitest ajoutés (3 nouveaux pour shell cross-platform, 4 nouveaux pour assertWithinBricksDir)
  • pnpm lint passe
  • pnpm typecheck passe
  • pnpm test passe (321/321)
  • pnpm build passe

Bug report user

Failed to install "cache": spawn npm ENOENT
Failed to load "cache": Resolved path "C:\Users\samuelds\.focus\bricks\node_modules\@focus-mcp\brick-cache\mcp-brick.json" escapes bricksDir "C:\Users\samuelds\.focus\bricks"

🤖 Generated with Claude Code

- spawn npm now uses shell: true on win32 to resolve npm.cmd
- assertWithinBricksDir uses path.relative() for cross-platform separator handling

Fixes ENOENT and "escapes bricksDir" errors on Windows nvm4w + Codex MCP.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 23:01
@samuelds samuelds enabled auto-merge May 8, 2026 23:01
@samuelds samuelds merged commit f2a48f4 into develop May 8, 2026
10 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Windows compatibility issues in the CLI’s brick installation and brick loading flows by adjusting how npm is spawned on Windows and by making the “stay within bricksDir” safety check path-separator-safe.

Changes:

  • Use shell: process.platform === 'win32' when spawning npm to avoid spawn npm ENOENT on Windows.
  • Replace a fragile startsWith-based bricksDir containment check with a path.relative()-based check.
  • Add/extend Vitest coverage for cross-platform spawning behavior and for assertWithinBricksDir edge cases.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/source/filesystem-source.ts Updates assertWithinBricksDir() to use path.relative()/isAbsolute() for cross-platform containment checks.
src/source/filesystem-source.test.ts Adds additional tests around bricksDir containment edge cases.
src/adapters/npm-installer-adapter.ts Adjusts spawn('npm', …) options to use a shell on Windows.
src/adapters/npm-installer-adapter.test.ts Adds platform-mocked tests to validate shell option behavior.
.changeset/fix-windows-spawn-path-normalization.md Adds a patch changeset describing the Windows fixes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +42 to 44
const rel = relative(realBricksDir, resolvedPath);
if (rel !== '' && (rel.startsWith('..') || isAbsolute(rel))) {
throw new Error(`Resolved path "${resolvedPath}" escapes bricksDir "${realBricksDir}"`);
Comment on lines +271 to +288
describe('runNpm cross-platform shell behaviour', () => {
it('uses shell: true on Windows (resolves npm.cmd batch script)', async () => {
const platformSpy = vi.spyOn(process, 'platform', 'get').mockReturnValue('win32');
vi.mocked(mkdir).mockResolvedValue(undefined);
vi.mocked(spawn).mockReturnValue(
makeChildProcess(0) as unknown as ReturnType<typeof spawn>,
);

await adapter.npmInstall('@focus-mcp/brick-echo', '1.0.0');

expect(spawn).toHaveBeenCalledWith(
'npm',
expect.any(Array),
expect.objectContaining({ shell: true }),
);
platformSpy.mockRestore();
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants