Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions apps/backend/core/worktree.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@

T = TypeVar("T")

# Detects an existing `ignore-workspace=...` directive in a .npmrc, regardless
# of leading whitespace. Used to decide whether to append it when the host repo
# already commits a .npmrc (registry/auth config) into worktrees.
_IGNORE_WORKSPACE_PATTERN = re.compile(r"^\s*ignore-workspace\s*=", re.MULTILINE)


def _is_retryable_network_error(stderr: str) -> bool:
"""Check if an error is a retryable network/connection issue."""
Expand Down Expand Up @@ -733,6 +738,31 @@ def create_worktree(self, spec_name: str) -> WorktreeInfo:

print(f"Created worktree: {worktree_path.name} on branch {branch_name}")

# Write .npmrc to prevent pnpm/npm from walking up to the parent
# workspace root (pnpm-workspace.yaml / package.json#workspaces).
# Without this, running `pnpm install` inside the worktree silently
# re-links the parent repo's node_modules into the worktree's pnpm
# store, corrupting the parent's dependency graph after cleanup.
# See: https://pnpm.io/npmrc#ignore-workspace
Comment on lines +741 to +746
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Does pnpm npmrc setting ignore-workspace=true also affect npm or yarn workspace discovery?

💡 Result:

No. pnpm’s .npmrc setting ignore-workspace=true only affects pnpm’s own workspace detection/resolution. It does not change how npm or Yarn discover their workspaces. Reason (grounded): 1) pnpm documents that .npmrc is used for pnpm configuration in the npmrc format primarily for authentication/registry-style settings; other workspace-related behavior is configured via pnpm-workspace.yaml or pnpm’s pnpm config/config.yaml. This implies pnpm-specific ignore-workspace behavior is implemented inside pnpm, not as a cross-tool npmrc workspace-discovery toggle. 2) pnpm provides its own CLI/workspace-ignore mechanism (e.g., --ignore-workspace), and pnpm issues/documentation describe it as “pnpm ignores any workspace configuration found in the current or parent directories,” indicating the option is scoped to pnpm’s workspace logic. Practical takeaway: - If you want npm or Yarn to ignore their workspaces, you must use whatever mechanism those tools provide (there isn’t an npmrc setting documented here that makes them ignore pnpm-style workspaces). - Setting ignore-workspace=true in .npmrc won’t prevent npm/yarn from detecting their own workspaces (e.g., via package.json workspaces, yarn workspaces, or npm workspaces rules).

Citations:


🌐 Web query:

npm workspaces configuration how does npm detect workspaces

💡 Result:

npm detects workspaces through the "workspaces" property in the root package.json file of a project. This property contains an array of glob patterns or direct paths pointing to directories containing package.json files that define the individual workspaces. When running npm commands like npm install, npm reads the "workspaces" field from the nearest ancestor package.json (walking up from the current working directory if necessary, with automatic workspace root detection). It then uses these patterns to traverse the filesystem, matching directories that contain valid package.json files. Valid workspaces are those nested packages explicitly defined by the workspaces configuration. Internally, npm uses the @npmcli/map-workspaces package to process the workspaces field: it expands globs (e.g., "packages/"), ignores patterns like "/node_modules/", reads package.json files in matching directories, extracts package names, and builds a map of package name to path. During installation, matching workspace dependencies are symlinked into node_modules instead of being fetched from the registry. Example configuration: { "name": "my-monorepo", "workspaces": ["packages/", "apps/*"] } This would detect all directories under packages/ and apps/ that have a package.json. The root package.json should typically have "private": true. Commands support --workspace=<name/path> or --workspaces flags to target specific or all workspaces.

Citations:


🌐 Web query:

Yarn workspaces configuration how does Yarn detect workspaces

💡 Result:

Yarn detects workspaces through a root package.json file containing a "workspaces" field with glob patterns, and individual workspace directories each containing their own package.json files. The process works as follows: 1. Yarn identifies the project root by starting from the current working directory and walking up parent directories until it finds a directory containing a package.json file. This becomes the project root (cwd). 2. It loads the manifest (package.json) from this root directory. 3. From the root manifest's workspaceDefinitions (derived from the "workspaces" field, which is an array of glob patterns like ["packages/*"]), it extracts the patterns. 4. Using fastGlob, it resolves these glob patterns relative to the root cwd, with options: onlyDirectories: true, ignore: ['/node_modules', '/.git', '/.yarn']. 5. For each matching directory, it checks if a package.json exists. 6. Valid directories (those with package.json) become child workspaces of the root workspace. This applies to Yarn Berry (2+). The root workspace is always present, and child workspaces are discovered via globs during the Workspace setup method, called as part of Project initialization.

Citations:


Narrow comment to "pnpm" — ignore-workspace does not affect npm or Yarn.

The setting ignore-workspace=true is pnpm-specific. npm detects workspaces via the workspaces field in package.json (not .npmrc), and Yarn similarly uses the workspaces field in package.json. The .npmrc file does not control workspace detection for npm or Yarn. The code fix is correct for pnpm, but the comment's "pnpm/npm" wording is misleading — change it to reference pnpm only. Addressing npm and Yarn workspace handling in a worktree should be tracked separately as part of the WORKTREE_ROOT acceptance criterion from #2013.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/backend/core/worktree.py` around lines 736 - 741, Update the inline
comment around the .npmrc write (the block referencing "ignore-workspace") to
state that ignore-workspace=true is pnpm-specific and remove or change any
phrasing that says "pnpm/npm" so it only mentions pnpm; reference the exact
setting name "ignore-workspace=true" and the .npmrc file in the comment, and add
a short note that npm and Yarn use package.json workspaces and should be tracked
separately (per WORKTREE_ROOT / `#2013`) if needed.

# If the host repo already commits a .npmrc (registry/auth config), we
# append the directive instead of skipping — otherwise the workspace
# fix gets bypassed silently for monorepos that need it most.
npmrc_path = worktree_path / ".npmrc"
try:
existing = npmrc_path.read_text(encoding="utf-8") if npmrc_path.exists() else ""
if not _IGNORE_WORKSPACE_PATTERN.search(existing):
separator = "" if not existing or existing.endswith("\n") else "\n"
npmrc_path.write_text(
existing + separator + "ignore-workspace=true\n",
encoding="utf-8",
)
logger.debug(
"Ensured ignore-workspace=true in .npmrc for worktree: %s",
worktree_path,
)
except OSError as e:
logger.warning("Could not write .npmrc to worktree %s: %s", worktree_path, e)

return WorktreeInfo(
path=worktree_path,
branch=branch_name,
Expand Down
21 changes: 21 additions & 0 deletions apps/frontend/src/main/ipc-handlers/terminal/worktree-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,27 @@ async function createTerminalWorktree(
debugLog('[TerminalWorktree] Created worktree in detached HEAD mode from', baseRef);
}

// Write .npmrc to prevent pnpm/npm from walking up to the parent
// workspace root (pnpm-workspace.yaml / package.json#workspaces).
// Without this, running `pnpm install` inside the worktree silently
// re-links the parent repo's node_modules into the worktree's pnpm
// store, corrupting the parent's dependency graph after cleanup.
// See: https://pnpm.io/npmrc#ignore-workspace
// If the host repo already commits a .npmrc (registry/auth config), we
// append the directive instead of skipping — otherwise the workspace
// fix gets bypassed silently for monorepos that need it most.
const npmrcPath = path.join(worktreePath, '.npmrc');
try {
const existing = existsSync(npmrcPath) ? readFileSync(npmrcPath, 'utf-8') : '';
if (!/^\s*ignore-workspace\s*=/m.test(existing)) {
const separator = !existing || existing.endsWith('\n') ? '' : '\n';
writeFileSync(npmrcPath, existing + separator + 'ignore-workspace=true\n', 'utf-8');
debugLog('[TerminalWorktree] Ensured ignore-workspace=true in .npmrc for worktree:', worktreePath);
}
} catch (npmrcError) {
debugError('[TerminalWorktree] Could not write .npmrc to worktree:', npmrcError);
}
Comment on lines +837 to +856
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Duplicated logic across backend and frontend — extract a shared constant at minimum.

This block is byte-for-byte equivalent (modulo language) to apps/backend/core/worktree.py:736-748. Two parallel implementations of the same monorepo-corruption mitigation will drift over time (e.g., when the "append if missing" fix above is applied, when extra pnpm settings need to be added, or when npm/yarn workspace handling lands per #2013). Since the backend WorktreeManager.create_worktree and this createTerminalWorktree run in independent processes, you can't share runtime code, but you can at least:

  • Hoist the file content ('ignore-workspace=true\n') and the comment block into a single source of truth (e.g., a constant in a shared docs/config file referenced from both implementations), and
  • Add a brief test or fixture asserting both paths produce an .npmrc with ignore-workspace=true so they stay in sync.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/main/ipc-handlers/terminal/worktree-handlers.ts` around
lines 837 - 851, The .npmrc write logic in createTerminalWorktree duplicates the
backend WorktreeManager.create_worktree behavior; extract the literal string and
explanatory comment ('ignore-workspace=true\n' plus the pnpm/workspace
rationale) into a shared constant in a common config/docs module (e.g., a new
exported constant used by both frontend terminal/worktree-handlers.ts and
backend core/worktree.py), update createTerminalWorktree to import and use that
constant instead of the inline string/comment, and add a small test/fixture that
asserts both implementations produce an .npmrc containing
"ignore-workspace=true" to prevent drift.

Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Set up dependencies (node_modules, venvs, etc.) for tooling support
// This allows pre-commit hooks to run typecheck without npm install in worktree
const setupDeps = await setupWorktreeDependencies(projectPath, worktreePath);
Expand Down