ANTIGRAVITY LABJP
Articles/Tips & Best Practices
Tips & Best Practices/2026-08-25Beginner

What I Line Up in PowerShell Before Letting Antigravity Agents Run Commands on Windows

Antigravity agents tend to write bash-shaped commands, and on Windows those do not survive the trip. I ran PowerShell 7.6.5 locally and measured four things: chaining operators, aliases, exit codes, and output encoding. Here is where Windows PowerShell 5.1 diverges, and what belongs in a rules file.

Antigravity356WindowsPowerShellSetup5

You paste the command an agent just handed you into a Windows terminal, and it dies on the first &&. If you work across platforms as an indie developer, that one line has probably cost you an afternoon at some point.

Antigravity is not the problem here. The destination is simply different. On macOS and Linux the command lands in bash or zsh. On Windows it lands in PowerShell, and most of the one-liners a model has absorbed were written for the first group.

Hub 2.9.1 on August 20 added syntax highlighting for .ps1, .psm1, .psd1, and for pwsh / ps1 code fences. I read that as a sign that writing PowerShell is becoming a first-class expectation rather than an afterthought. So I installed PowerShell 7.6.5 locally and pushed the shapes an agent typically produces through it, one at a time.

The thing that surprised me was not &&. It was the exit codes.

Whether && works depends on which PowerShell you have

Start by finding out which one you are running. It takes one line.

$PSVersionTable.PSVersion
$PSVersionTable.PSEdition

My environment returned 7.6.5 and Core. Core means the PowerShell 7 line. Desktop means Windows PowerShell 5.1, the one that ships with Windows. If you have not installed anything, you are almost certainly on the latter.

That generation gap collides directly with what agents produce.

BehaviorWindows PowerShell 5.1PowerShell 7.x
&& / || chainingReserved, rejected as a syntax errorSupported
curl / wgetAliases for Invoke-WebRequestNo alias; the real binary runs
Out-File default encodingUTF-16LE with BOMUTF-8 without BOM
How you get itBundled with WindowsInstalled separately

I ran the chaining case on 7.6.5 to be sure.

pwsh -NoProfile -c 'true && echo "chained-ok"'
# chained-ok

It works. Hand the same string to 5.1 and it never executes at all, because parsing fails first. The agent then receives something that is not "the command failed" but "the command was never understood," so it rewrites and gets rejected again. That loop burns turns.

I also measured startup cost while I was there. Five runs of pwsh -NoProfile -c '1' came in at 228, 211, 217, 217, and 232 milliseconds. On the same machine, bash -c 'true' took 3 milliseconds every time. If an agent fires dozens of shell commands per task, that difference alone adds ten seconds or more. -NoProfile is still worth keeping, since it skips profile loading on top of that baseline.

When an agent writes curl, 5.1 runs something else entirely

I checked the alias table on 7.6.5.

Get-Alias curl -ErrorAction SilentlyContinue   # nothing
Get-Alias wget -ErrorAction SilentlyContinue   # nothing

The 7 line dropped both aliases, so curl -s https://example.com reaches the actual curl binary.

On 5.1 those aliases are still defined and point at Invoke-WebRequest. Flags like -s and -H are not parameters that cmdlet knows, so what comes back is a parameter-binding error, not a network error. Nothing in the message mentions HTTP at all, which sends you looking in entirely the wrong place.

The fix is boring and reliable: write external commands with their extension.

curl.exe -s https://example.com
node.exe --version

One honest caveat. Aliases such as ls and cat behave differently by platform even inside the 7 line, and what I verified was the linux-x64 build of 7.6.5. The only claims I am willing to base on my own measurement are curl and wget. For everything else, run Get-Alias once on your Windows box and look at the list yourself.

Failures collapse to 1, and that breaks automated success checks

This is the part that held my attention. When you pass a command with -c (-Command), the exit code from inside does not always reach the outside intact. Here is what I measured.

What I ranInner exit codeWhat the caller received
pwsh -NoProfile -c 'exit 3'33
pwsh -NoProfile -c 'exit 42'4242
External command inside -c exits 331
Same, plus ; exit $LASTEXITCODE33
ls on a missing path inside -c21
Same, plus ; exit $LASTEXITCODE22
pwsh -NoProfile -File ./t.ps1 containing exit 333
Failing command followed by || echo fallback20

When you write exit yourself, the value survives. When an external command fails, 3 and 2 are flattened to the same 1. You still learn that something failed; you lose how it failed. If anything downstream branches on the specific code, that distinction is gone.

The last row is the sneakiest. Once the right side of || succeeds, the whole invocation reports 0. Your log shows a failure while your caller records a success.

Reading $? has its own timing rule.

# read immediately: False
& ./somecommand ; $q = $?; "q=$q code=$LASTEXITCODE"   # q=False code=3
 
# put any statement in between and it flips back
& ./somecommand ; "anything" | Out-Null; "q=$? code=$LASTEXITCODE"   # q=True code=3

$? only remembers the single statement before it. $LASTEXITCODE keeps the external command's value, so that is the one to branch on.

My own rule is short: if you use -c, end it with exit $LASTEXITCODE; when you can, pass a script with -File instead. Something similar happens on the bash side for entirely different reasons, which I wrote up in the pipe in my wrapper was swallowing Antigravity CLI's exit code. Different cause, very familiar symptom.

Logs written through redirection change bytes between generations

Having an agent write its run log to a file, then reading it yourself later, is a common enough pattern. The generation gap shows up here too.

I wrote a file on 7.6.5 and looked at it byte by byte.

"日本語ログ" | Out-File -FilePath ./enc7.txt
(Get-Item ./enc7.txt).Length

The file came to 16 bytes for that five-character Japanese string, and the leading bytes were e6 97 a5 e6 9c ac e8 aa 9e e3 83 ad e3 82 b0 0a — UTF-8 with no BOM, plus one newline. grep reads it, Python reads it, nothing special required.

On 5.1, Out-File and > default to UTF-16LE with a BOM. The same five characters roughly double in size and gain a byte-order mark. Open the file and it looks correct to you; hand it to a parser or back to an agent and every character appears to have a null byte wedged next to it. Most of the times a log looked "empty" to me, this was why.

State it explicitly when you write.

"日本語ログ" | Out-File -FilePath ./log.txt -Encoding utf8

Note that utf8NoBOM only exists from 7 onward. If you are staying on 5.1 and want to avoid the BOM, [System.IO.File]::WriteAllText() is the dependable route.

That covers files you write. If the terminal display itself is garbled, the cause sits somewhere else entirely, so start with fixing Japanese mojibake in Antigravity's integrated terminal.

Put these assumptions in a rules file, not in the conversation

You could just say all of this in chat every time. It does not hold up. Long conversations bury it, and a new session forgets it.

Antigravity lets you keep rules as files. The Customizations panel in hub 2.9.1 lists skills, rules, plugins, and custom agents as separate collapsible sections, and from CLI 1.1.15 onward a markdown-defined agent can name rule files directly in its frontmatter with rules:. That is the shape you want when you would rather apply a few specific rules than inherit an entire tree.

What I keep for Windows is about this short.

---
name: windows-shell
description: Shell assumptions for commands issued on Windows
---
 
- Assume commands run under PowerShell 7 (pwsh)
- Write external commands with their extension (curl.exe / node.exe)
- Pass -Encoding utf8 explicitly when writing files
- End any -c script with exit $LASTEXITCODE

The agent picks it up by name.

---
name: release-helper
rules:
  - windows-shell.md
---

Whether a written assumption is actually in effect is a separate question. Configuration can be wrong in a way that gets silently ignored rather than rejected. I covered how to find settings that are quietly doing nothing in how silently ignored config keys slip through CI, and where to block them. Worth reading before you add more rules rather than after.

What to do next

Open your Windows machine and run $PSVersionTable.PSEdition once. If it returns Desktop, installing PowerShell 7 is your first step, and three of the four issues in this article disappear on their own.

I still stop at the same places every time I cross platforms. If the numbers I measured save someone else a little of that time, that is a good outcome.

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Tips2026-08-15
When Antigravity Holds a Dozen Projects: How I Stopped Hunting for Yesterday's Conversation
As Antigravity projects pile up, the conversation sidebar turns into a morning scrolling ritual. Now that 2.8.0 remembers collapsed state, here is how I reworked project granularity, conversation naming, and launch flags.
Tips2026-08-24
Four Reasons Your .antigravityignore Rules Are Not Taking Effect
Rules that quietly do nothing, and rules that swallow the whole repository. Here are four causes behind both, each checked one at a time against a gitignore-style matcher.
Tips2026-07-07
When Antivirus Blocks Antigravity's Launch, Verify the Signature Before You Allow It
When the Antigravity desktop app won't launch because antivirus stepped in, here's how to tell a false positive from a real threat. Verify the code signature on macOS Gatekeeper and Windows Defender first, then allow-list with the narrowest possible scope.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →