Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions examples/statusline/statusline.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# PowerShell status line script for Antigravity CLI
# Replicates the official statusline.sh example in PowerShell

# Ensure standard output encoding is UTF-8 so Unicode characters display correctly
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# --- ANSI Helpers (Standard 16-color palette only) ---
$esc = [char]27
$R = "$esc[0m" # Reset
$B = "$esc[1m" # Bold
$D = "$esc[2m" # Dim
$I = "$esc[3m" # Italic

# Foreground accents
$FG_BLACK = "$esc[30m"
$FG_RED = "$esc[31m"
$FG_GREEN = "$esc[32m"
$FG_YELLOW = "$esc[33m"
$FG_BLUE = "$esc[34m"
$FG_MAGENTA = "$esc[35m"
$FG_CYAN = "$esc[36m"
$FG_WHITE = "$esc[37m"

$FG_GRAY = "$esc[90m"
$FG_BRIGHT_RED = "$esc[91m"
$FG_BRIGHT_GREEN = "$esc[92m"
$FG_BRIGHT_YELLOW = "$esc[93m"
$FG_BRIGHT_BLUE = "$esc[94m"
$FG_BRIGHT_MAGENTA = "$esc[95m"
$FG_BRIGHT_CYAN = "$esc[96m"
$FG_BRIGHT_WHITE = "$esc[97m"

$NUM_COLOR = "${FG_BRIGHT_WHITE}${B}"

# --- Unicode Icons and Symbols ---
$readyIcon = [char]::ConvertFromUtf32(0x25cf) # ●
$thinkingIcon = [char]::ConvertFromUtf32(0x23f3) # ⏳
$workingIcon = [char]::ConvertFromUtf32(0x2699) # ⚙
$toolIcon = [char]::ConvertFromUtf32(0x1f527) # 🔧
$diamondIcon = [char]::ConvertFromUtf32(0x25c6) # ◆
$fullBlock = [char]::ConvertFromUtf32(0x2588) # █
$darkShade = [char]::ConvertFromUtf32(0x2593) # ▓
$medShade = [char]::ConvertFromUtf32(0x2592) # ▒
$lightShade = [char]::ConvertFromUtf32(0x2591) # ░
$middleDot = [char]::ConvertFromUtf32(0xb7) # ·
$boxH = [char]::ConvertFromUtf32(0x2500) # ─
$boxV = [char]::ConvertFromUtf32(0x2502) # │
$boxTR = [char]::ConvertFromUtf32(0x256d) # ╭
$boxBR = [char]::ConvertFromUtf32(0x2570) # ╰

# Default values
$state = "idle"
$usedPct = 0.0
$vcsBranch = ""
$vcsDirty = $false
$sandbox = $false
$artifacts = 0
$subagentsCount = 0
$bgTasks = 0
$model = ""
$cols = 80

# --- Parse JSON from stdin ---
try {
$jsonString = [System.Console]::In.ReadToEnd()
if ($jsonString) {
$data = ConvertFrom-Json $jsonString
if ($data) {
if ($data.agent_state) { $state = $data.agent_state }
if ($data.context_window -and $data.context_window.used_percentage -ne $null) {
$usedPct = [double]$data.context_window.used_percentage
}
if ($data.vcs) {
if ($data.vcs.branch) { $vcsBranch = $data.vcs.branch }
if ($data.vcs.dirty -ne $null) { $vcsDirty = [bool]$data.vcs.dirty }
}
if ($data.sandbox -and $data.sandbox.enabled -ne $null) { $sandbox = [bool]$data.sandbox.enabled }
if ($data.artifact_count -ne $null) { $artifacts = [int]$data.artifact_count }
if ($data.subagents) {
if ($data.subagents -is [array]) {
$subagentsCount = $data.subagents.Length
} else {
$subagentsCount = 1
}
}
if ($data.task_count -ne $null) { $bgTasks = [int]$data.task_count }
if ($data.model -and $data.model.display_name) { $model = $data.model.display_name }
if ($data.terminal_width -ne $null) { $cols = [int]$data.terminal_width }
}
}
} catch {
# Fallback to defaults if parsing fails
}

# --- State Indicator ---
switch ($state) {
"idle" { $S = "${FG_BRIGHT_GREEN}${B}${readyIcon} READY${R}" }
"thinking" { $S = "${FG_BRIGHT_YELLOW}${B}${thinkingIcon} THINKING${R}" }
"working" { $S = "${FG_BRIGHT_CYAN}${B}${workingIcon} WORKING${R}" }
"tool_use" { $S = "${FG_BRIGHT_MAGENTA}${B}${toolIcon} TOOL${R}" }
default { $S = "${FG_WHITE}${B}${readyIcon} $($state.ToUpper())${R}" }
}

# --- VCS Branch ---
$V = ""
if ($vcsBranch) {
if ($vcsDirty) {
$V = "${FG_GRAY} ${diamondIcon} ${FG_BRIGHT_RED}${vcsBranch}${FG_BRIGHT_YELLOW}*${R}"
} else {
$V = "${FG_GRAY} ${diamondIcon} ${FG_BRIGHT_BLUE}${vcsBranch}${R}"
}
}

# --- Model ---
$M = ""
if ($model) {
$M = "${FG_GRAY} ${diamondIcon} ${FG_BRIGHT_MAGENTA}${I}${model}${R}"
}

# --- Sandbox Badge ---
if ($sandbox) {
$SB = "${FG_GRAY}sandbox ${FG_BRIGHT_GREEN}${B}ON${R}"
} else {
$SB = "${FG_GRAY}sandbox off${R}"
}

# --- Context Bar ---
$pctFmt = $usedPct.ToString("F1", [System.Globalization.CultureInfo]::InvariantCulture)
$pctInt = [int][Math]::Floor($usedPct)

$barLen = 15
$filled = [int][Math]::Floor($pctInt * $barLen / 100)
$remainder = ($pctInt * $barLen) % 100

if ($pctInt -ge 90) {
$barColor = $FG_BRIGHT_RED
} elseif ($pctInt -ge 60) {
$barColor = $FG_BRIGHT_YELLOW
} else {
$barColor = $FG_BRIGHT_WHITE
}

$BAR = ""
for ($i = 0; $i -lt $barLen; $i++) {
if ($i -lt $filled) {
$BAR += $fullBlock
} elseif ($i -eq $filled) {
if ($remainder -ge 75) {
$BAR += $darkShade
} elseif ($remainder -ge 50) {
$BAR += $medShade
} elseif ($remainder -ge 25) {
$BAR += $lightShade
} else {
$BAR += $middleDot
}
} else {
$BAR += $middleDot
}
}

# --- Stats ---
$CTX = "${FG_GRAY}ctx ${barColor}${BAR} ${NUM_COLOR}${pctFmt}%${R}"
$ART_FMT = "${FG_GRAY}artifacts ${NUM_COLOR}${artifacts}${R}"
$SUB_FMT = "${FG_GRAY}subagents ${NUM_COLOR}${subagentsCount}${R}"
$BG_FMT = "${FG_GRAY}tasks ${NUM_COLOR}${bgTasks}${R}"

# Separator
$DOT = "${FG_GRAY} ${middleDot} ${R}"

# --- Output ---
$LINE1 = "${S}${M}${V}"
$LINE2 = " ${CTX}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}"

if ($cols -ge 120) {
# Wide: single line
Write-Output "${LINE1}${FG_GRAY} ${boxV} ${R}${LINE2}"
} elseif ($cols -ge 80) {
# Medium: two-line layout with border
Write-Output "${FG_GRAY}${boxTR}${boxH}${R} ${LINE1}"
Write-Output "${FG_GRAY}${boxBR}${boxH}${R}${LINE2}"
} else {
# Narrow: compact two-line, minimal chrome
Write-Output "${S}${M}"
Write-Output "${CTX}${DOT}${BG_FMT}"
}
41 changes: 39 additions & 2 deletions examples/statusline/statusline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ NUM_COLOR="${FG_BRIGHT_WHITE}${B}"
read -r VCS_BRANCH
read -r VCS_DIRTY
read -r SANDBOX
read -r SANDBOX_NET
read -r ARTIFACTS
read -r SUBAGENTS
read -r BG_TASKS
Expand All @@ -49,12 +50,13 @@ NUM_COLOR="${FG_BRIGHT_WHITE}${B}"
(.vcs.branch // ""),
(.vcs.dirty // false),
(.sandbox.enabled // false),
(.sandbox.allow_network // false),
(.artifact_count // 0),
(if .subagents | type == "array" then (.subagents | length) else 0 end),
(.task_count // 0),
(.model.display_name // ""),
(.terminal_width // 80)
' 2>/dev/null || printf "idle\n0\n\nfalse\nfalse\n0\n0\n0\n\n80\n"
' 2>/dev/null || printf "idle\n0\n\nfalse\nfalse\nfalse\n0\n0\n0\n\n80\n"
)"

# ─── Computed Values ─────────────────────────────────────────────────────────
Expand Down Expand Up @@ -87,9 +89,44 @@ if [ -n "$MODEL" ]; then
M="${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${I}${MODEL}${R}"
fi

# ─── Sandbox State (workaround for unpopulated payload field) ────────────────
# As of agy 1.0.6, `agy --sandbox` enables the terminal sandbox but does NOT set
# .sandbox.enabled in the statusLine payload (it stays false), so the badge below
# would always read "off". The flag is also not exported to this process's env,
# and the process is reparented to init, so neither env nor the parent command
# line can be inspected. The only reliable signal is the session log, which
# cli.log symlinks to ("--sandbox: enabling terminal sandbox for this session").
# .sandbox.enabled remains the primary source, so this self-corrects once the
# payload field is populated upstream. See issue #321.
if [ "$SANDBOX" != "true" ]; then
SANDBOX_LOG="$HOME/.gemini/antigravity-cli/cli.log"
if [ -r "$SANDBOX_LOG" ] && grep -q 'enabling terminal sandbox' "$SANDBOX_LOG" 2>/dev/null; then
SANDBOX="true"
fi
fi

# ─── Sandbox Network (same unpopulated-payload problem as .enabled) ──────────
# The payload's sandbox object only carries .enabled; there is no
# .sandbox.allow_network field, so SANDBOX_NET always falls back to "false" and
# the badge can't tell net from no-net. The session log has no network-specific
# line either. The authoritative source is the user's sandboxAllowNetwork
# setting, so read it from settings.json. Like the .enabled workaround above,
# this self-corrects once the payload field is populated upstream. See #321.
if [ "$SANDBOX_NET" != "true" ]; then
SETTINGS_FILE="$HOME/.gemini/antigravity-cli/settings.json"
if [ -r "$SETTINGS_FILE" ] \
&& jq -e '.sandboxAllowNetwork == true' "$SETTINGS_FILE" >/dev/null 2>&1; then
SANDBOX_NET="true"
fi
fi

# ─── Sandbox Badge ───────────────────────────────────────────────────────────
if [ "$SANDBOX" = "true" ]; then
SB="${FG_GRAY}sandbox ${FG_BRIGHT_GREEN}${B}ON${R}"
if [ "$SANDBOX_NET" = "true" ]; then
SB="${FG_GRAY}sandbox ${FG_BRIGHT_GREEN}${B}ON${R} ${FG_GRAY}(net)${R}"
else
SB="${FG_GRAY}sandbox ${FG_BRIGHT_GREEN}${B}ON${R} ${FG_GRAY}(no-net)${R}"
fi
else
SB="${FG_GRAY}sandbox off${R}"
fi
Expand Down