If you automate work with CLI AI agents, you quickly hit the same wall: each agent takes its prompt, its working directory, and its role in a slightly different way, and the wrong shell or the wrong flag order silently breaks the whole run. This guide distills the rules that actually work on Windows, learned the hard way, so you can script a task once and hand it to whichever agent you prefer.
jsmith, long username johnsmith (no spaces). Swap in your own username where you see these.Golden rule we learned the hard way: use
cmd (.cmd batch files) to launch these CLIs, not PowerShell. The reason is stdin redirection and quoting (see Section 2).--dangerously-bypass-approvals-and-sandbox (Codex), --dangerously-skip-permissions (agy), and --permission-mode bypassPermissions (Claude). These turn off approval prompts and sandboxing so the agent can act on its own – Codex’s own help calls this “EXTREMELY DANGEROUS.” Use them only in a folder you trust, on work under version control that you can revert, never on a shared or production machine. Drop the flag (or use Codex -s read-only / agy --sandbox) when you want a safety net.
1. The Three Clients at a Glance
| Client | Exe (portable form) | Working dir set by | Prompt in | Instructions / params passed by | “Done” signal |
|---|---|---|---|---|---|
| Codex (OpenAI) | %LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe | -C "<dir>" flag | < prompt.txt (stdin) | a ROLE token as one quoted positional arg | state file ends RUN_STATUS: COMPLETE |
| agy (Gemini) | %LOCALAPPDATA%\agy\bin\agy.exe | pushd "<dir>" cwd, or --add-dir | -p "text" arg only – no stdin (see Section 3, †) | no token → uses its default role | the output file it was told to write exists |
| Claude (Claude Code) | claude (must be on PATH) | pushd "<dir>" (uses cwd) | < prompt.txt (stdin) | command-line flags (--model, --effort, …) | state file ends RUN_STATUS: COMPLETE |
The same brief file can feed all three – but they read it differently. Codex and Claude read the prompt from stdin (< brief.txt); agy does not read stdin – you pass its prompt as the -p argument, so for a long brief you tell agy “read brief.txt and follow it” (see Section 3 †). The role is chosen differently too: Codex gets a ROLE token argument, agy uses its default, Claude is steered by flags.
Also Read: Stop AI Agents Wasting Tokens in Document Pipelines
2. Which shell – cmd vs PowerShell (the main question)
Use cmd (.cmd batch) to launch the three CLIs. Always.
Three concrete reasons:
- Stdin redirection
< prompt.txt. Codex and Claude read the big prompt from standard input (agy is the exception – it takes its prompt as the-pargument, see Section 3).
cmd:codex.exe exec ... < "%PROMPT%"– works.- PowerShell:
<is not supported (PowerShell reserves it and errors). You’d have to rewrite every call asGet-Content prompt.txt | codex.exe ..., which changes encoding/newline handling and is easy to get wrong. Not worth it.
- PowerShell:
- Quoting the ROLE token. Codex takes one big quoted argument like
"ROLE=DRAFTER. Write the blog post described on stdin; save it as draft.md.". cmd passes it verbatim inside"...". PowerShell would re-parse=,;, and quotes differently. - The whole toolchain is already batch. Loops, resume logic, and state checks all use cmd features (
for %%F,findstr,!ERRORLEVEL!). Mixing shells adds bugs, not value.
Use PowerShell only for Windows-admin tasks, not for launching the agents.
Good PowerShell jobs: editing the user PATH, environment variables, service/registry work, anything with no clean cmd equivalent. Example we actually used: adding claude’s folder to PATH with [Environment]::SetEnvironmentVariable("Path", …, "User") because setx truncates a long PATH.
Use neither for searching files.
For find/grep/walk use rg / fd, not PowerShell loops.
One-line rule: cmd launches the AI. PowerShell configures Windows. rg/fd search files.
3. Sample commands (copy-paste, dummy data)
These are self-contained – you do not need any of this repo’s .cmd files to use them. Replace the dummy values with your own:
| Dummy value used below | Means |
|---|---|
C:\work\project | the folder the agent should work in |
C:\work\prompt.txt | your prompt / instructions file (big prompts live here) |
C:\work\out.jsonl , C:\work\out.err.log | where stdout / stderr are saved |
The exe paths use %LOCALAPPDATA% so they work for any Windows username. Run these in cmd (a .bat/.cmd file or a cmd.exe window), not PowerShell (see Section 2).
Codex (baseline / generate) – sets its own dir with -C
set "CODEX=%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe" "%CODEX%" exec --json --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox ^ -c model_reasoning_effort=high -C "C:\work\project" ^ "ROLE=BASELINE. Follow the prompt on stdin in full; keep every detail." ^ < "C:\work\prompt.txt" > "C:\work\out.jsonl" 2> "C:\work\out.err.log"
exec= non-interactive run.--json= machine-readable event stream.--skip-git-repo-check+--dangerously-bypass-approvals-and-sandbox= unattended, no prompts.-c model_reasoning_effort=high= a config key/value (xhighdeeper,lowcheap).-C "C:\work\project"= Codex is the only one that takes a working-dir flag.- The quoted
"ROLE=..."string = the instruction token, one quoted arg – this is how you steer Codex. Put a short instruction here; the bulk of the task goes in the prompt file. - Cheap ping (prompt read from stdin as
-, read-only):echo Reply with exactly: PONG_CODEX | "%CODEX%" exec --skip-git-repo-check -s read-only -c model_reasoning_effort=low -
agy / Gemini – works in the current folder or --add-dir
Flag order matters: -p grabs the very next token as its prompt, so put every other flag before -p and the quoted prompt immediately after it.
set "AGY=%LOCALAPPDATA%\agy\bin\agy.exe" REM (a) SHORT prompt - prompt is the argument to -p, all other flags first: "%AGY%" --dangerously-skip-permissions -p "Reply exactly: PONG" REM (b) BIG instructions - agy can't read stdin, so point it at the file instead: pushd "C:\work\project" "%AGY%" --dangerously-skip-permissions --log-file "agy.log" -p "Read C:\work\prompt.txt and follow it exactly." > "agy-out.log" 2>&1 popd
Do NOT write agy -p --dangerously-skip-permissions "..." (then -p eats --dangerously-skip-permissions as its prompt) or agy -p < file (fails: flag needs an argument: -p).
Verified flag list (from agy --help):
| Flag | Meaning |
|---|---|
-p / --print | run one prompt non-interactively – the prompt is this flag’s argument; agy never reads stdin |
--prompt | alias for --print |
-i / --prompt-interactive | run an initial prompt, then stay interactive |
-c / --continue | continue the most recent conversation |
--conversation <id> | resume a specific conversation by ID |
--project <id> / --new-project | pick / create a project context |
--add-dir <path> | add a directory to the workspace (repeatable) – the alternative to pushd |
--model "<name>" | pick model (list them with agy models) |
--print-timeout 10m | how long print mode waits (default 5m) |
--log-file <path> | write the CLI log (essential for debugging – see below) |
--dangerously-skip-permissions | auto-approve all tool actions |
--sandbox | run with terminal restrictions (use this to limit what it can touch) |
† How the prompt is passed – this is the subtle part (verified on this install):
- agy’s prompt is ALWAYS the
-pargument. It does not read stdin.agy -p < file.txtfails withflag needs an argument: -p, andagy -p --flag "text"mis-parses (the flag becomes the prompt). Always: other flags first, then-p "your prompt". - Short prompt → inline:
agy --dangerously-skip-permissions -p "Reply exactly: PONG". - Long instructions → point agy at a file: keep the
-pprompt short and let agy open the file itself:-p "Read C:\work\prompt.txt and follow it exactly."This sidesteps the ~8191-char cmd argument limit and the no-stdin limitation. (Codex and Claude differ – they do take the big prompt on stdin via< file.)
⚠ Known agy issue on Windows (found 2026-07-01, confirm before trusting print output):--log-file showed a Windows path bug – it tried to open a Unix-style path /Users/johnsmith/.gemini/antigravity-cli/.../transcript.jsonl.--log-file to any agy run you need to debug. Spot-check the first output file it writes for real substance.--sandbox (and check your VCS status, e.g. git status / svn status, after a run).
Claude Code (final master) – runs in the current folder, must be on PATH
pushd "C:\work\project" claude -p --input-format text --model opus --effort high ^ --permission-mode bypassPermissions --output-format stream-json --verbose ^ < "C:\work\prompt.txt" > "out.jsonl" 2> "out.err.log" popd
-p= print/non-interactive.--input-format text= stdin is the plain prompt.--model opus= the model.--effort max|xhigh|low. (claude --help/ your model list shows valid names; some installs also acceptopus[1m]for a 1-million-token context – install-specific, so use plainopusunless you know[1m]works on yours.)--permission-mode bypassPermissions= no approval prompts (needed for unattended runs).--output-format stream-json --verbose= full event log to the.jsonl.- Claude is called bare (
claude), so its folder must be on PATH. If PATH is wrong you get “‘claude’ is not recognized” – the single most common breakage after a reinstall.
4. Things to keep in mind (the traps we hit)
Paths / install
- Never hardcode
C:\Users\<name>\…. A username change (jsmith→johnsmith) broke every old script. The fix is%LOCALAPPDATA%– it resolves per-user automatically, so scripts survive a reinstall or username change. Prefer that form. claudemust be on PATH. Add its folder (e.g.C:\Users\johnsmith\.local\bin) via PowerShell’s[Environment]::SetEnvironmentVariable(..., "User"). Do not usesetxif PATH is long – it truncates at 1024 chars.- A PATH change only applies to new terminal windows.
Batch (cmd) syntax that matters
- Start scripts with
setlocal enabledelayedexpansion, then read changing vars as!VAR!inside loops/ifblocks. Plain%VAR%is frozen at parse time and will be stale inside afor/if. - Line-continue with
^at end of line; escape specials as^|,^<,^>,^&. - Redirection:
> out(stdout),2> err(stderr),2>&1(merge). All three clients log this way. pushd "%DIR%"…popdto run a tool “in” a folder (agy and Claude need this; Codex uses-C).chcp 65001 >nulat the top forces UTF-8 (the Claude ping does this) – avoids mangled characters.- Check a state file with
findstr /c:"RUN_STATUS: COMPLETE" "%DIR%\...STATE.md".
Running / limits
- Exit code is the quota signal. Non-zero from any client usually means the plan limit was hit; the scripts stop cleanly and resume on re-run – they never spin.
- Claude: run ONE instance at a time (single window). Parallel Claude burns through your plan limits faster.
- Codex / agy can run two at once by splitting the work into odd/even slices (two windows). But one writer per state file – two Codex processes writing the same folder corrupt the state.
- Resume signals differ: for long jobs, decide what “done” means. Codex & Claude can write a state file you grep for (e.g. ends with
RUN_STATUS: COMPLETE); for agy, check that the output file it was told to write now exists. A smalldone.OKmarker file lets a finished folder be skipped on re-run.
Prompts / instructions
- Codex & Claude take a big prompt on stdin (
< prompt.txt) – the real prompt files are tens of KB, past the ~8191-char cmd argument limit, so stdin is the only option for them. - agy is different: it does NOT read stdin. Its prompt is the
-pargument. For big instructions, keep-pshort and point agy at the file:-p "Read C:\work\prompt.txt and follow it exactly."Put all other flags before-p(see Section 3 †). - Pick the role by the mechanism each client uses: Codex = ROLE token arg, agy = default (no token), Claude = flags. Same task, three different “steer” mechanisms.
- agy print mode can send but print nothing on Windows (path bug) and can edit files – verify by output files, add
--log-file, and use--sandboxif it must not touch anything (see Section 3).
5. Quick smoke test before any big run (standalone pings)
Run each; a healthy client echoes the token back.
REM --- Codex: expect PONG_CODEX --- echo Reply with exactly: PONG_CODEX | "%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe" exec --skip-git-repo-check -s read-only -c model_reasoning_effort=low - REM --- agy: expect PONG (flags first, then -p "prompt"; see the Windows print bug in section 3) --- "%LOCALAPPDATA%\agy\bin\agy.exe" --dangerously-skip-permissions --new-project --print-timeout 1m --log-file "agy-ping.log" -p "Reply exactly: PONG" REM --- Claude: expect PING_PONG_OK --- echo Reply exactly: PING_PONG_OK and nothing else.> ping.txt claude -p --input-format text --model opus --effort low --permission-mode bypassPermissions --output-format stream-json --verbose < ping.txt
If a ping fails: check the exe path (does %LOCALAPPDATA% resolve to your install?), check claude is on PATH, and open the .err.log / --log-file output.
6. Worked example – script a blog post (draft → revise → polish)
Goal: turn one idea into a finished blog post for your website, unattended, by chaining the three clients. Each does one stage: Codex drafts → agy revises → Claude polishes. They hand off through files in a shared folder, and each stage reads its own small instruction file.
Step 1 – write three instruction files
Put these in C:\work\blog\. Each one is a plain-text brief (the “prompt”):
1-draft.txt
Write a first-draft blog post for our company website. Topic: "Why small businesses should archive their email". Audience: non-technical small-business owners. Tone: friendly, practical. Length: ~700 words. Format: Markdown - one H1 title, 3-4 H2 sections, a short closing call-to-action. Save the result as draft.md in the current folder. Do nothing else.
2-revise.txt
Read draft.md in the current folder. Improve it: tighten the intro, add one concrete example per section, and make the call-to-action specific. Keep it ~700 words and the same Markdown structure. Save the improved version as revised.md. Do nothing else.
3-polish.txt
Read revised.md in the current folder. Do a final copy-edit: fix grammar, cut filler, ensure the H1 and headings read well, and verify the tone is friendly and clear. Save the final as final.md. Do nothing else.
Step 2 – one batch file to run the pipeline
Save as write-blog.cmd and run it from a cmd window (write-blog.cmd):
@echo off setlocal enabledelayedexpansion set "WORK=C:\work\blog" set "CODEX=%LOCALAPPDATA%\Programs\OpenAI\Codex\bin\codex.exe" set "AGY=%LOCALAPPDATA%\agy\bin\agy.exe" echo [1/3] Codex drafting... "%CODEX%" exec --json --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox ^ -c model_reasoning_effort=high -C "%WORK%" ^ "ROLE=DRAFTER. Follow the brief on stdin and write the file it names." ^ < "%WORK%\1-draft.txt" > "%WORK%\codex.jsonl" 2> "%WORK%\codex.err.log" echo [2/3] agy revising... pushd "%WORK%" "%AGY%" --dangerously-skip-permissions --log-file "agy.log" -p "Read 2-revise.txt in this folder and follow it exactly." > "agy.out.log" 2>&1 popd echo [3/3] Claude polishing... pushd "%WORK%" claude -p --input-format text --model opus --effort high ^ --permission-mode bypassPermissions --output-format stream-json --verbose ^ < "3-polish.txt" > "claude.jsonl" 2> "claude.err.log" popd echo Done. Final post: %WORK%\final.md dir /b "%WORK%\*.md"
What this shows (the three lessons in one place)
- Same shape, three steer mechanisms: Codex is steered by the quoted
"ROLE=..."token, agy by its default (no token), Claude by flags. How each gets the brief differs: Codex and Claude read it from stdin (< 1-draft.txt,< 3-polish.txt); agy can’t read stdin, so it’s told to open the file (-p "Read 2-revise.txt ... and follow it"). - Hand-off via files: every stage runs in
C:\work\blog, sodraft.md→revised.md→final.mdchain works because each brief tells the agent which file to read and which to write. - You don’t need all three. Any single client can do the whole job – e.g. give Claude one brief (“write, self-edit, and save final.md”) and skip the other two. The three-stage split just gives you a draft, an independent revision, and a polish from different models.
Check the result
- Open
C:\work\blog\final.md. If a stage’s.mdis missing, read that stage’s.err.log/agy.log. Remember agy may print nothing to the console – judge it by whetherrevised.mdappeared, not by stdout (see Section 3).
Want your agents to run faster and cheaper too? Pair this scripting workflow with fast local CLI tools so your agents search and parse files instead of burning tokens – see the companion guide, How to Cut AI Coding Agent Costs with Fast Local Tools.
Frequently Asked Questions