Status line

The footer under the prompt box can carry one line of your own. You point Syntic Code at a script, it hands the script a JSON snapshot of the session, and whatever the script prints shows up in the footer. Use it for the git branch, the active model, how full the context is, how much of your plan window is left, a clock, or anything else a shell command can work out.

Two ways to set it up

Let the agent write it

/statusline

With no instructions, /statusline reads your shell start-up files (~/.zshrc, ~/.bashrc, ~/.bash_profile, ~/.profile, in that order), finds your prompt, and turns it into a status line command. You can also describe what you want:

/statusline show the branch, the model and how much context is used

A small built-in helper agent does the work. It may only read files and edit your settings, and it saves the result to ~/.syntic/settings.json, merging with what is already there. /statusline only works in an interactive session.

Write it yourself

Add a statusLine object to a settings file:

{
  "statusLine": {
    "command": "~/.syntic/statusline.sh",
    "refreshInterval": 30
  }
}
FieldMeaning
commandThe shell command to run. It is run through bash -c (cmd on Windows), so pipes and arguments work. Blank means no status line.
refreshIntervalOptional. Also re-run the command every this many seconds even when nothing in the session changed. Useful for clocks or CI badges.

A "type": "command" field, which /statusline writes, is accepted and has no further effect.

Changes to the file are picked up while you work, so you can edit the script and the setting and watch the footer update.

Build one step by step

Write a script that reads standard input

#!/usr/bin/env bash
# ~/.syntic/statusline.sh
input=$(cat)
model=$(printf '%s' "$input" | jq -r '.model')
echo "$model"

Make it executable

chmod +x ~/.syntic/statusline.sh

Try it by hand

Feed it a sample snapshot to check the output before Syntic Code runs it:

echo '{"model":"Syntic Code","cwd":"/tmp","total_cost_usd":0.12,"context_used_pct":0.4}' \
  | ~/.syntic/statusline.sh

Point the setting at it

{ "statusLine": { "command": "~/.syntic/statusline.sh" } }

How it runs

RuleDetail
Where it showsIn the footer, after the hint text, separated by a dot.
What is keptThe first line of standard output only. Tabs become spaces and other control characters are removed.
ColorsPlain SGR color codes (\e[32m, \e[1;34m, 256-color and 24-bit forms) are kept and drawn. Other escape sequences, including OSC hyperlinks and cursor movement, are dropped.
Output sizeOnly the first 16 KB of output is read.
Time limitOne second per run. A slower run is stopped and nothing is shown for it.
Exit statusA non-zero exit shows nothing. So does a line that is empty once colors are removed.
How oftenAt most every 300 ms, and only when the snapshot changed. While nothing changes it re-runs every 5 seconds, or every refreshInterval seconds if you set one. Only one run is active at a time.
Working folderThe session’s current folder.
Terminal sizeCOLUMNS and LINES are set, so you can shorten the line on narrow windows.
EnvironmentThe command is started from a cleaned PATH, and your Syntic credentials are removed from its environment.
Folder trustIt does not run until you have trusted the folder (see Folder trust).

To remove the status line, delete the statusLine key or set command to an empty string.

The JSON your command receives

One JSON object arrives on standard input each run. Fields with no value yet are left out.

FieldTypeValue
modelstringThe model the session is using.
cwdstringThe working folder.
workspace_dirslist of stringsExtra folders added with /add-dir or --add-dir. Left out when there are none.
session_idstringThe session’s id.
session_namestringThe session’s title, once it has one.
versionstringThe syntic version.
permission_modestringdefault, acceptEdits, auto, plan, bypass or dontAsk.
effortstringReasoning effort: low, medium, high, xhigh or max.
fast_modebooleanFast mode is on.
thinking_shownbooleanReasoning is shown in full.
vim_modebooleanVim keys are on in the prompt.
total_cost_usdnumberEstimated cost of the session so far, at list price.
context_used_pctnumberShare of the context window in use, from 0 to 1.
context_windowobjectused_tokens, window_tokens, and used_percentage (0 to 100, one decimal). Present once the window size is known.
rate_limitsobjectPlan usage, described below.

Plan usage fields

rate_limits always has five_hour and seven_day, each a fraction from 0 to 1 (0 until usage is known). Once Syntic Code has fetched your plan’s usage, it also carries:

FieldValue
windowsOne entry per window your plan has (session, weekly, monthly), each with used_percentage (0 to 100) and resets_at (ISO 8601 time, when known).
unlimitedNo limits apply to your organization.
credits_enabledUsage credits take over when a window runs out.

five_hour follows the session window and seven_day follows the weekly window.

Plan usage is fetched in the background every five minutes, and only while a status line is set, you are signed in, and your privacy level allows optional traffic. Headless runs never fetch it.

A full example:

{
  "model": "Syntic Code",
  "cwd": "/home/dev/shop",
  "session_id": "5f1c…",
  "session_name": "Fix checkout rounding",
  "version": "0.4.2",
  "permission_mode": "acceptEdits",
  "effort": "medium",
  "fast_mode": false,
  "thinking_shown": false,
  "vim_mode": false,
  "total_cost_usd": 0.84,
  "context_used_pct": 0.31,
  "context_window": { "used_tokens": 81000, "window_tokens": 262144, "used_percentage": 30.9 },
  "rate_limits": {
    "five_hour": 0.42,
    "seven_day": 0.18,
    "windows": {
      "session": { "used_percentage": 42.0, "resets_at": "2026-10-13T17:00:00Z" },
      "weekly": { "used_percentage": 18.0, "resets_at": "2026-10-19T00:00:00Z" }
    },
    "unlimited": false,
    "credits_enabled": false
  }
}

Examples

Context use with a color warning

#!/usr/bin/env bash
input=$(cat)
pct=$(printf '%s' "$input" | jq -r '.context_window.used_percentage // 0')
model=$(printf '%s' "$input" | jq -r '.model')
if [ "${pct%.*}" -ge 80 ]; then color='\033[31m'; else color='\033[32m'; fi
printf "%s · ${color}%s%% context\033[0m\n" "$model" "$pct"

Branch and changed files

#!/usr/bin/env bash
input=$(cat)
dir=$(printf '%s' "$input" | jq -r '.cwd')
cd "$dir" 2>/dev/null || exit 0
branch=$(git branch --show-current 2>/dev/null) || exit 0
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
echo "⎇ ${branch:-detached} · ${dirty} changed"

Git can be slow in very large repositories. Remember the one-second limit; if your command is often cut off, cache the result in a file and refresh it less often.

Plan usage in Python

#!/usr/bin/env python3
import json, sys
 
data = json.load(sys.stdin)
limits = data.get("rate_limits", {})
session = limits.get("windows", {}).get("session")
cost = data.get("total_cost_usd", 0)
if limits.get("unlimited"):
    usage = "no limit"
elif session:
    usage = f"{session['used_percentage']:.0f}% of session window"
else:
    usage = "usage pending"
print(f"{data.get('model', '?')} · ${cost:.2f} · {usage}")

Shorter on narrow windows

#!/usr/bin/env bash
input=$(cat)
model=$(printf '%s' "$input" | jq -r '.model')
cwd=$(printf '%s' "$input" | jq -r '.cwd')
if [ "${COLUMNS:-80}" -lt 100 ]; then echo "$model"; else echo "$model · $cwd"; fi

A clock

{ "statusLine": { "command": "date +%H:%M", "refreshInterval": 30 } }

Subagent rows

While subagents run, each one has a row of its own. subagentStatusLine lets a script replace the task text on each row:

{ "subagentStatusLine": { "command": "~/.syntic/agent-line.sh" } }

The command runs once per running subagent, with the same timing rules as the main status line, and only after the folder is trusted. Its first line of output replaces that row’s task text. It is read from your own user settings file.

Each run receives:

FieldValue
agent_idThe subagent’s id.
agent_typeIts agent type, for example Explore.
taskThe task it was given.
activityWhat it is doing right now (a tool name), when known.
elapsed_secsSeconds since it started.
tokensTokens used so far, when known.
effortThe session’s reasoning effort.
#!/usr/bin/env bash
input=$(cat)
printf '%s' "$input" | jq -r '"\(.agent_type): \(.activity // "thinking") · \(.elapsed_secs)s"'

More about subagents is on Subagents.

When it goes wrong

SymptomWhat to check
Nothing appearsRun the command by hand with sample JSON on standard input. Make sure it exits with status 0 and prints a non-empty first line. Check that the folder is trusted.
It appears, then vanishesThe script is sometimes slower than one second, or sometimes exits non-zero. Cache slow parts.
Fields come out as nullSome fields are only sent once known (session_name, context_window, plan usage). Use defaults such as jq -r '.field // "…"'.
The model shows as [object] or blankmodel is a plain string. Read it with .model, not .model.name.
Plan usage stays at 0You are not signed in, your privacy level blocks optional traffic, or the first fetch has not finished yet.
A link or cursor code does not workOnly color codes are kept; other escape sequences are removed on purpose.
A command found in your shell is “not found”The command runs from a cleaned PATH. Use full paths to tools in unusual places.

Colors and the rest of the footer follow your theme; see Themes. Other footer options (pull request badge, branch links) are on that page too.