Codex CLI Best Practices
Codex CLI is not a chat box with shell access. It is an agent harness running in your repository, with project instructions, a working directory, sandbox policy, approval policy, tools, and session state. If you treat it like a generic prompt window, you will blame the model for problems that were actually harness configuration mistakes.
The best Codex CLI setups are boring. They start in the right directory. They have a short AGENTS.md. They use the package manager the repo actually uses. They choose sandbox and approval modes deliberately. They do not hide project policy in a one-off prompt. They make the agent's working environment match the environment CI will judge.
This post is based on the current local CLI surface I use: codex [OPTIONS] [PROMPT], codex exec for non-interactive runs, codex review, codex resume, codex mcp, codex sandbox, and the common options around --cd, --sandbox, --ask-for-approval, --config, profiles, and model choice. If your installed CLI differs, trust codex --help over any blog post, including this one.
Start with AGENTS.md
Codex needs project knowledge before it needs a clever prompt. Put durable project instructions in AGENTS.md:
# AGENTS.md
## Project facts
This is a Vite + React + TypeScript marketing site.
Source lives in src/. Blog content lives in src/content/blog/.
## Commands
- Install: npm ci
- Build: npm run build
- Test: npm test
- Lint: npm run lint
## Working rules
- Use npm only. package-lock.json is canonical.
- Do not edit generated dist/ output by hand.
- Branch from main and open a PR.
- Never print secret values. Refer to environment variable names only.
## References
- package.json is the source of truth for scripts.
- scripts/generate-sitemap.ts owns static routes.
The file should be short enough that Codex can read it without crowding out the task. Do not put a release handbook, a style manifesto, and a copied incident thread in AGENTS.md. Put those in docs/ or workflow-specific files and link to them.
The job of AGENTS.md is not to sound wise. It is to remove bad guesses. Which package manager? Which build command? Which files are generated? Which branch policy? Which docs are authoritative? Answer those before the agent starts editing.
Boot the session from the right root
The most common Codex CLI mistake is starting from the wrong directory. The agent can only discover the repo shape from where you launch it and what you tell it to use as the working root.
Use --cd when there is any ambiguity:
codex --cd /path/to/repo "Find why npm run build fails"
For non-interactive automation:
codex exec --cd /path/to/repo "Run the tests and summarize failures"
This is not cosmetic. If you start one directory above the repo, Codex may miss project-local instructions, package files, or git state. If you start inside a subpackage, it may miss root policy. If a monorepo has nested instruction files, root choice affects which context is visible and what local rules apply.
Use --add-dir only when the task truly needs another writable directory:
codex --cd apps/web --add-dir packages/ui "Update the shared button and the web usage"
Do not make your whole home directory writable just because one task spans two packages. A larger writable surface is more risk and more distraction.
Choose sandbox mode deliberately
The CLI exposes sandbox modes with --sandbox:
--sandbox read-only
--sandbox workspace-write
--sandbox danger-full-access
Use read-only for investigation, review, and planning. It lets the agent inspect without changing files. If the user asks "what is wrong?" or "review this PR," read-only is often enough.
Use workspace-write for normal implementation. The agent can edit inside the workspace, but the sandbox still limits the blast radius. This should be the default for most coding tasks.
Use danger-full-access only when the environment around Codex is already sandboxed or the task truly needs unrestricted access. The name is not subtle. If you use it for convenience, you are bypassing the main safety boundary and hoping the prompt is enough. It is not.
There is also a bypass flag:
--dangerously-bypass-approvals-and-sandbox
That flag is for externally sandboxed environments, not for local impatience. If you run it on a laptop with real credentials and a broad working directory, you have converted a controlled agent into an unrestricted process.
Understand approval policy
Some team docs call this "approval mode." In the current CLI help, the option is --ask-for-approval or -a:
--ask-for-approval untrusted
--ask-for-approval on-request
--ask-for-approval never
The help also shows on-failure, but marks it deprecated and recommends on-request for interactive runs or never for non-interactive runs.
Use untrusted when you want a conservative session. Trusted read commands can run, but riskier commands require escalation.
Use on-request for interactive implementation where the agent can decide when it needs approval. This is a reasonable human-in-the-loop mode when edits are expected but some commands are risky.
Use never for CI-like non-interactive runs where the process must not stop to ask a human. Pair it with a sandbox that fits the risk. never does not mean "everything works." It means failures return to the model immediately instead of asking for permission.
Avoid mixing a permissive sandbox with permissive approvals unless the outer environment is disposable. The dangerous combination is unrestricted filesystem access plus no approval prompts plus real secrets. That is not a productivity setting. That is a blast-radius decision.
Use config overrides carefully
--config is powerful because it accepts dotted paths and parses values as TOML:
codex -c model="gpt-5.4" -c shell_environment_policy.inherit=all
The common mistake is quoting badly. A value that looks like a string to your shell may not be the TOML value you intended. Arrays and nested values need extra care:
codex -c 'sandbox_permissions=["disk-full-read-access"]'
Do not bury project behavior in local config. If every contributor needs to know "use npm," that belongs in AGENTS.md and CI, not in one user's ~/.codex/config.toml. Use config for local defaults and profiles. Use repo files for repo policy.
Profiles are useful when you have repeated modes:
codex --profile review "Review the current changes"
codex --profile implementation "Fix the failing test"
But a profile should not secretly change the project contract. If the profile uses a different sandbox or model, fine. If it changes the build command, the repo is now split between user config and project truth.
Prefer codex exec for repeatable automation
Interactive Codex is good for exploration and implementation. codex exec is better for repeatable jobs:
codex exec --cd /path/to/repo --ask-for-approval never \
"Run npm test. If it fails, summarize the first failing test and likely cause."
exec can read instructions from stdin, output JSONL with --json, write the last message to a file with --output-last-message, and skip persisted sessions with --ephemeral. Those flags are useful for CI-like workflows, but they also make mistakes easier to repeat. Keep the prompt narrow. Keep the working directory explicit. Keep approval and sandbox settings visible in the command.
Do not use exec as a replacement for deterministic scripts. If a task is "run eslint and fail on warnings," write a script. Use Codex when interpretation is needed: classify failures, propose a fix, review a diff, explain a flaky test, or apply a bounded code change.
Common config mistakes
The first mistake is stale AGENTS.md. The file says pnpm install; the repo has package-lock.json; CI uses npm. Codex will eventually discover the mismatch, but the session starts with a lie.
The second mistake is wrong root. The prompt says "this repo," but the CLI was launched from /tmp or from apps/web when the root instructions live above it.
The third mistake is overbroad access. danger-full-access plus no approvals is treated as a default. It should be treated as an exception with a reason.
The fourth mistake is under-scoped write access. The task touches apps/web and packages/ui, but the agent only has one directory writable. It fails, improvises, or makes a partial change.
The fifth mistake is putting policy in the prompt. "Use npm, don't edit dist, open a PR, don't print secrets" gets pasted into one prompt, then forgotten next session. Put durable policy in AGENTS.md.
The sixth mistake is confusing review with implementation. If you want findings, use a read-only posture or codex review. If you want a fix, give the agent write access and a verification command. Half-review, half-fix prompts produce muddy outcomes.
A sane default command set
For investigation:
codex --cd /path/to/repo --sandbox read-only \
--ask-for-approval untrusted \
"Find the source of the failing build. Do not edit files."
For normal implementation:
codex --cd /path/to/repo --sandbox workspace-write \
--ask-for-approval on-request \
"Fix the failing build and run npm run build."
For non-interactive CI-like analysis:
codex exec --cd /path/to/repo --sandbox read-only \
--ask-for-approval never --json \
"Review the current diff for likely regressions."
For an externally sandboxed throwaway environment:
codex --cd /workspace/repo \
--dangerously-bypass-approvals-and-sandbox \
"Apply the requested refactor and run tests."
The last command should make you pause. That is the point. If the environment is not disposable or externally constrained, do not use it.
Keep the harness honest
Codex CLI works best when its instructions, config, sandbox, and repo reality agree. AGENTS.md says what the project expects. The CLI flags decide what the agent can do. CI decides what counts as done. If those three disagree, the agent will route around the contradiction.
AgentLint catches the drift before it becomes a bad session: stale AGENTS.md commands, package-manager contradictions, missing enforcement, and cross-tool differences between AGENTS.md, CLAUDE.md, .cursor/rules, hooks, and CI.