How to Block npm in Codex with Shims and Exec Policies
AI coding agents might default to using npm to install packages, which can be undesirable if your project standardizes on pnpm, bun, or if you want to restrict package installation for security reasons. While Cursor's built-in execpolicy is a great first line of defense, a determined agent can sometimes find workarounds, like generating a script that calls npm indirectly.
This lesson demonstrates a more robust, two-layered strategy to completely control the agent's environment. You'll learn how to combine a project-local shell shim with Cursor's execpolicy to ensure npm is disabled from all angles.
The Strategy:
- Environment-Level Shim: By modifying the
PATHenvironment variable for the agent's shell session, we can intercept any call tonpm. We'll create a customnpmexecutable that prints a helpful message and exits, effectively blocking it at the source. This is configured via a project-local.codex/config.tomland corresponding.zshstartup files. - Explicit
execpolicyRule: As a second layer, we'll define a standardexecpolicyrule in.codex/rulesto explicitly forbid thenpmcommand. This catches direct invocations and provides clear feedback to the agent.
By implementing both, you create a secure and controlled environment where the agent is guided to use your preferred tools, even if it tries to bypass standard restrictions. This technique gives you fine-grained control over the agent's capabilities, ensuring project consistency and security.
How it Works
The core of the environment-level restriction is a .codex/config.toml file that tells the zsh shell (used by Cursor) to load its configuration from a project-local directory.
# .codex/config.toml# This tells zsh to load startup files from this project-local directory.[shell_environment_policy.set]ZDOTDIR = ".codex/zsh"
Inside the .codex/zsh directory, a .zshenv or .zprofile file prepends our custom bin directory to the PATH.
# .codex/zsh/.zshenv# Prepend the project-local shim directory to the PATH.export PATH="$PWD/.codex/bin:$PATH"
This forces the shell to look in .codex/bin for executables first. We place our npm shim there, which intercepts the call.
# .codex/bin/npm#!/usr/bin/env sh# This file intentionally has the same name as the real `npm` binary.# Because `.codex/zsh/` prepends `.codex/bin` to PATH, scripts that run `npm` by# name will hit this blocker before the system npm.# Print a message to stderrcat >&2 <<'EOF'npm is disabled inside this Codex project environment.Do not choose a replacement automatically. Ask the user whether they want pnpm, bun, or another approach.Common replacements: pnpm install/add/run, bun install/add/run.EOF# Exit with a "command not found" error codeexit 127
Finally, the execpolicy rule provides an additional, explicit block against direct npm calls.
# .codex/rules/no-npm.rules# Block npm even when Codex sees an absolute path to an npm binary.prefix_rule(pattern = ["npm"],decision = "forbidden",justification = "npm is disabled in this demo. Ask whether to use pnpm, bun, or another package manager",match = [["npm"],["npm", "--version"],],)
Together, these two mechanisms create a comprehensive block, preventing both direct and indirect npm usage by the AI agent.
Prompts
Please run npm install jquery
Run our test-npm.js
Terminal Commands
npm --version
which npm
codex
# Executed inside the Codex/Cursor environmentnpm --version
# Executed inside the Codex/Cursor environmentwhich npm
echo $ZDOTDIR