OpenAI Codex CLI Tutorial: Setup, Config and First Task
You open a terminal, cd into a repo you half-remember, and instead of reaching for your editor, you type a sentence describing what you want done. A few seconds later, a diff shows up. You read it, you approve it (or you don't), and you move on. That is the entire pitch of a terminal-based AI coding agent like OpenAI Codex CLI, and it is also where most engineers get stuck: not because the tool is hard to install, but because nobody explains the workflow around it. This guide walks through that workflow end to end — install, authenticate, configure, run a real task, review the output, and avoid the mistakes that make people give up after one bad session.
What Codex CLI Actually Is
Codex CLI belongs to a category of tools sometimes called "agentic coding CLIs." Unlike a chat window where you copy-paste code back and forth, these tools run inside your terminal, inside your actual repository, with the ability to read files, write files, run shell commands, and propose (or directly make) changes to your codebase. The model isn't just generating text for you to transcribe — it's operating on your project the way a junior engineer with SSH access would, except it explains its plan and shows you a diff before (or as) it acts.
This distinction matters because it changes what you're evaluating. With a chat-based assistant, you're judging code snippets in isolation. With a CLI agent, you're judging actions taken against a live repository — file edits, new files, deleted files, command executions. The review surface is different, and so is the risk surface. A snippet that's wrong wastes your time. An autonomous file edit that's wrong can break a build, corrupt a config, or silently introduce a regression that ships.
That's why the two things every practitioner needs to understand before writing a single prompt are: how the tool decides what it's allowed to do without asking, and how you tell it what it's allowed to do in your specific project. Everything else — the actual coding — is downstream of those two decisions.
Installing and Authenticating
The installation pattern for tools in this category is consistent: a package manager install (commonly through npm for JavaScript-ecosystem tools, or a standalone binary download), followed by an authentication step that links the CLI to your account and API access.
A typical first run looks like this:
npm install -g @openai/codex-cli
codex --version
codex auth loginThe auth login step usually opens a browser window (or prints a device code you paste into a browser) so you can log in through a normal OAuth-style flow rather than pasting a raw API key into your terminal history. Once authenticated, the CLI stores a token locally — typically under a config directory in your home folder — so you don't have to re-authenticate every session.
A sane first check after install: run the tool with no task at all, just to confirm it starts, recognizes your current directory as a project, and reports which account or organization it's authenticated as. This sounds trivial, but skipping it is exactly how people end up debugging "why isn't it doing anything" fifteen minutes later, when the real answer was an expired token.
Sandbox and Approval Modes: The Most Important Setting You'll Configure
Before you give the tool any real task, understand the approval model, because this is the single setting that determines how much trust you're extending on any given run.
Broadly, tools in this category offer a spectrum:
- Manual approval (suggest-only) mode — the agent reads your codebase, proposes a plan and a diff, but does not touch any file or run any command until you explicitly approve each step. This is the safest mode and the right default when you're working in an unfamiliar repo, a production codebase, or anything with real consequences.
- Auto-edit / semi-autonomous mode — the agent can write files directly without asking each time, but it still asks before running potentially destructive shell commands (deleting files, force-pushing, installing global packages, hitting the network).
- Full-auto / sandboxed mode — the agent can read, write, and execute commands without per-step confirmation, usually constrained to a sandbox: a restricted filesystem scope (your project directory only) and no network access unless explicitly permitted. This mode trades oversight for speed and is meant for tasks you're comfortable letting run unattended, in an environment where a mistake is cheap to undo (a scratch branch, a container, a disposable clone).
The practical rule: start every new repo, and every new task type, in manual approval mode. Watch what the agent proposes for a task or two. Once you've seen it make sane decisions — correct file targeting, reasonable scope, no surprise refactors — you can graduate that specific kind of task to a more autonomous mode. Treat autonomy as something you earn per-project, not a global setting you flip once and forget.
It's also worth checking, before your first run, whether the tool's sandbox restricts network access by default. Many of these agents run shell commands as part of their normal workflow (installing a dependency, running a test suite, calling a linter). If network access is sandboxed off, a task that needs npm install will stall or fail in a way that looks like a bug but is actually the safety boundary doing its job.
# Manual approval — review every file edit and command before it runs
codex --approval-mode suggest
# Auto-edit — files are written automatically, shell commands still confirmed
codex --approval-mode auto-edit
# Full autonomy inside a sandboxed scope — use only in disposable environments
codex --approval-mode full-auto(Exact flag names vary by tool version — check codex --help or the equivalent for your installed version. The three-tier pattern above — suggest, auto-edit, full-auto — is the shape to look for, even if the literal words differ.)
Configuring Project-Level Instructions
The single highest-leverage thing you can do before running any task is write down what the agent should already know about your project, so you're not repeating it in every prompt. Most CLI agents look for a project-level instructions file — often a plain markdown file at the repo root — that gets loaded into context automatically on every run in that directory.
Treat this file the way you'd treat onboarding notes for a new contractor. Good candidates for inclusion:
- Language and framework conventions — "This is a TypeScript monorepo using pnpm workspaces. Do not use
npmoryarn." - Testing expectations — "Every new function in
src/needs a corresponding test in__tests__/. Runpnpm testbefore considering a task done." - Style constraints — "We do not use default exports. We do not use
any. Prefer named exports and explicit types." - Directory boundaries — "Never modify files under
/generated— they're build output. Never touch.envfiles." - Domain context — a one-paragraph description of what the product does, so the agent doesn't propose changes that are technically correct but product-nonsensical.
A minimal example:
# Project instructions for AI agents
This is a Next.js 14 app router project using TypeScript and Tailwind.
Package manager: pnpm only.
Rules:
- Add tests for any new function under /lib.
- Do not modify files in /prisma/migrations manually.
- Prefer editing existing components over creating new ones.
- Run `pnpm lint` and `pnpm test` before marking a task complete.
- Never commit or push — leave changes staged for human review.That last line deserves emphasis. Especially early on, it's worth explicitly instructing the agent not to commit or push on its own, even in a more autonomous mode. You want a clean, reviewable diff sitting in your working tree, not a commit already sitting in your history that you now have to decide whether to revert.
Keep this file updated as you notice repeated corrections. If you find yourself telling the agent the same thing in three different sessions — "we use Zod for validation, not manual checks" — that's a signal it belongs in the instructions file, not in your prompt.
Your First Task: Adding Input Validation to a Form Handler
Theory is easy to nod along to; a concrete task makes the workflow real. Let's walk through a task type every web developer will recognize: adding input validation to a form handler that currently trusts its inputs.
Say you have a Next.js API route or a form submission handler that takes an email and a message and writes them to a database with no validation at all. You start a session in the repo, in manual approval mode, and give the agent a task description. The quality of that description determines almost everything about the quality of the result, so it's worth being deliberate:
codex "In src/app/api/contact/route.ts, add input validation for the
POST handler. Validate that 'email' is a non-empty, well-formed email
address and that 'message' is a non-empty string under 2000 characters.
Use our existing Zod setup (see src/lib/validation.ts for patterns we
already follow elsewhere). Return a 400 with a clear error message on
failure. Do not change the database write logic. Add a test in
src/app/api/contact/__tests__/route.test.ts covering both the valid
and invalid input cases."Notice what that prompt does: it names the exact file, states the validation rules precisely (not just "validate the input"), points to an existing convention to follow instead of letting the model invent its own pattern, draws an explicit boundary ("do not change the database write logic"), and asks for a test. Every one of those clauses removes a decision the model would otherwise have to guess at — and guesses are where scope creep and inconsistent style come from.
The agent will typically respond with something like a short plan ("I'll add a Zod schema for the contact form, validate in the handler, return 400 on failure, and add corresponding tests"), then propose the actual file diffs. In manual approval mode, you'll see each proposed edit before it's applied.
Reviewing the Diff Like You Mean It
This is the step people rush, and it's the step that matters most. A diff from an AI agent should get the same scrutiny you'd give a pull request from a new team member — arguably more, because the "author" has no shared context with your team's unwritten conventions and no skin in the game if it's wrong.
A practical review checklist for this kind of change:
- Scope check — did it touch only the files you named, or did it "helpfully" refactor something adjacent? Unrequested scope expansion is the most common failure mode.
- Logic check — does the validation actually match what you asked for? An email regex that accepts obviously malformed addresses, or a length check with an off-by-one, are easy to miss on a skim.
- Convention check — did it actually use your existing Zod patterns, or did it invent a parallel validation approach because it didn't fully internalize the reference file?
- Error handling check — does the 400 response leak anything it shouldn't (stack traces, internal field names), and is the error message actually useful to a client calling the API?
- Test quality check — are the generated tests actually asserting behavior, or are they shallow (e.g., asserting the function "doesn't throw" rather than checking the response body and status code)?
If something's off, don't manually patch it and move on — that trains you to babysit the tool instead of steering it. Instead, respond in the same session with a correction: "the email regex you used allows consecutive dots, tighten it" or "use the existing ApiError class from src/lib/errors.ts instead of a raw NextResponse.json call." Iterating in-session, with the full context the agent already has, is almost always faster than starting a fresh prompt from scratch.
Testing Before You Merge
Once the diff looks reasonable, the review isn't done — it's just cleared the first gate. Run the actual test suite, not just the new tests the agent wrote for itself. It's entirely possible for an agent to write a passing test for the wrong behavior, especially if the task description was ambiguous.
pnpm test src/app/api/contact
pnpm lint
pnpm typecheckBeyond the automated checks, actually exercise the change by hand if it's user-facing: submit the form with a malformed email, an empty message, a message over the length limit, and a valid submission, and confirm the responses match what you expect. Automated tests only catch what someone thought to write a test for — manual exploratory testing catches what nobody thought of, including the agent.
If the change touches anything security-relevant — auth, input handling, data access — treat that as a reason for extra scrutiny, not less, precisely because it's easy for a validation-focused task to overlook an edge case like Unicode normalization tricks in email fields or unbounded array inputs elsewhere in the same payload.
Only after tests pass and manual spot-checks look right should you stage the change, write your own commit message, and open a PR the normal way. Resist the temptation to let the agent commit and push on your behalf until you've built real trust in a given repo — and even then, keep commit authorship and messages under your control so your git history reads like your team's history, not a transcript of prompts.
Common Gotchas
A handful of failure patterns show up constantly with terminal coding agents, and almost all of them trace back to the human side of the interaction, not the model:
- Vague tasks produce vague diffs. "Add validation to the form" invites the agent to guess at rules, error formats, and scope. "Validate email format and message length under 2000 chars, return 400 with a specific message, follow our Zod conventions" leaves far less room to improvise badly.
- Missing constraints get filled in with assumptions. If you don't say "don't touch the database logic," don't be surprised if the agent "improves" it while it's in there. Explicit boundaries aren't paranoia — they're how you get a reviewable, minimal diff instead of a sprawling one.
- Running in full-auto mode on unfamiliar repos. Autonomy is earned per-project. A tool that behaves great in a small side project can make much more aggressive assumptions in a large, conventions-heavy monorepo it hasn't "seen" before.
- Skipping the project instructions file. Without it, you re-explain your stack, your package manager, and your style rules in every single prompt — and inconsistently, since you'll phrase it slightly differently each time.
- Treating the diff as trusted just because it compiles. Code that runs is not the same as code that's correct, secure, or consistent with your architecture. Compilation is the lowest bar, not the review.
- Not testing edge cases the task didn't explicitly mention. Agents are good at satisfying the literal request. They're only as good as your prompt at anticipating the cases you forgot to list.
- Letting context go stale in a long session. On a long-running task with many back-and-forth corrections, it's sometimes faster to summarize progress and start a fresh, tighter prompt than to keep patching an increasingly confused multi-turn thread.
None of these are exotic problems. They're the same discipline that makes any collaborator — human or AI — productive: clear scope, explicit constraints, and real review before merge.
Building the Habit, Not Just the Setup
The mechanical parts — install, auth login, picking an approval mode, writing a project instructions file — take maybe twenty minutes. The part that actually compounds over time is the habit you build around them: defaulting to manual approval in new repos, writing specific rather than vague task descriptions, reading every diff like a PR, and running the real test suite instead of trusting the agent's self-graded homework. Get those habits right once, and they transfer to essentially any terminal-based coding agent you pick up next, because the underlying pattern — describe, propose, review, approve, verify — doesn't change even as specific tools and flag names do.
If you want to go deeper on this workflow with a different but closely related tool, our course "Vibe Coding AI Apps with Claude Code" covers the same install-configure-task-review loop in detail, with a particular focus on project-level configuration files, permission models, and how to structure prompts for larger multi-file tasks rather than single-function edits. Comparing the two workflows side by side is one of the fastest ways to internalize which parts of "how to work with an AI coding agent" are universal, and which parts are specific to one tool's design choices — knowledge that will keep paying off as this category of tooling keeps evolving.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.
Related reading