Minimal status tracker for waybar written in Go.
pinglod manages dots in memory and persists them to disk, while pinglo sends events (start, done, clear) and renders the JSON payload for a Waybar module.
- Yellow dot when a command becomes
running(start) - Green dot when the command finishes successfully (
done --exit-code 0) - Red dot when the command finishes with a failure (
done --exit-code != 0) - Simultaneous tracking of long-running commands
- Deduplication by
cwd + command: rerunning the same command in the same directory updates the same dot - Clearing all dots (
clear)
go build -o pinglod ./cmd/pinglod
go build -o pinglo ./cmd/pinglopinglodpinglod automatically persists dots to a state file so that your indicators survive restarts. The location is chosen in order:
$PINGLO_STATE_FILE, if set- otherwise
$XDG_DATA_HOME/pinglo/state.json(defaults to~/.local/share/pinglo/state.json) - otherwise the system temp dir.
You can override the path to keep state on a shared drive or use a RAM-backed file.
pinglod will notify any Waybar processes with SIGRTMIN+4 (or the offset specified via -signal-offset) after every start, done, or clear, so the module refreshes only when the state actually changes.
Default socket selection:
$PINGLO_SOCKET, if set- otherwise
$XDG_RUNTIME_DIR/pinglo.sock - otherwise
/tmp/pinglo-<uid>.sock
# mark a dot as running
pinglo start --cmd "sleep 10" --cwd "$PWD"
# finish the same dot
pinglo done --cmd "sleep 10" --cwd "$PWD" --exit-code 0
# clear the module
pinglo clear
# inspect the current state
pinglo listYou can manage labeled dots with colors and tooltips without tying them to shell commands.
# add/update a dot
pinglo dot set --id deploy --color "#ffc66d" --tooltip "Deploy running" --status running
# mark it complete
pinglo dot set --id deploy --status success --tooltip "Deploy succeeded"
# remove a dot
pinglo dot remove --id deploydot set accepts --status values running, success, or failed (defaults to running if omitted). The tooltip text is displayed as supplied, and the color is applied directly to the dot in Waybar. Use this API for any indicator that only needs a colored point and a short tooltip; the classic start/done helpers keep working for command tracking.
Because the daemon persists its state, dots created via dot set survive pinglod restarts and even system boot, so long as the state file remains reachable.
pinglo is intentionally split into:
- Small core daemon + CLI (
pinglod,pinglo) - Generic
dotAPI for external producers - Provider-specific integrations outside the core
This lets you add new sources of status (tasks, Codex chats, CI pipelines, deploy hooks, etc.) without growing internal/pinglo and without changing the wire protocol.
- The core stays stable: only state management and rendering logic live there.
- Integration-specific lifecycle rules remain isolated.
- You can ship, test, and version integrations independently.
- New providers can be prototyped as scripts before promoting to dedicated binaries.
integrations/
README.md
lib/
pinglo-dot.sh
templates/
integration-template.sh
codex/
...
Every integration should only use these public commands:
pinglo dot set --id ... --status running|success|failed --tooltip ... [--color ...]pinglo dot remove --id ...
No integration should write state files directly or call daemon internals.
Use namespaced IDs so integration dots never collide with task dots or manual dots:
integration:<provider>:<entity-id>
Examples:
integration:codex:019cb508-f0e6-7201-86d1-0ece0e906456integration:ci:build-7841integration:deploy:prod-eu-west-1
Use a simple state machine per external entity:
- Create/update dot as
runningwhen work starts. - Update dot to
successwhen completed. - Update dot to
failedif an error occurs. - Optionally remove the dot when entity is no longer relevant.
Keep tooltip concise and actionable; first line should identify source and entity, next lines can include status details.
integrations/lib/pinglo-dot.sh contains shared helpers:
pinglo_integration_dot_id <provider> <entity>pinglo_integration_set <provider> <entity> <status> <tooltip> [color]pinglo_integration_remove <provider> <entity>
Integrations should source this file instead of duplicating shell glue.
- Copy template:
cp integrations/templates/integration-template.sh integrations/<provider>/<provider>-watch.sh
chmod +x integrations/<provider>/<provider>-watch.sh- Replace:
PROVIDERwith provider name (e.g.codex,ci,k8s)ENTITYwith a stable per-item identifier (task id, thread id, build id, etc.)- status updates with your provider event mapping
- Run integration side-by-side with
pinglod; dots will appear in existing Waybar module immediately.
Recommended default mapping:
- in-progress/waiting ->
running - done/ok ->
success - error/cancel/timeout ->
failed
Override color only when provider semantics require custom palette; otherwise rely on default status colors.
pinglo includes an external Codex wrapper:
integrations/codex/codex-with-pinglo.shfor interactive TUI (codex)
Use plain codex if you do not want tracking. Use codex-with-pinglo.sh when you want Waybar dots.
- Creates a dot in
running(yellow) while waiting for the model response. - Switches dot to
success(green) when response is completed. - Switches dot to
failed(red) on stream failure or non-zero Codex exit. - Uses thread-aware IDs (
integration:codex:<thread_id>) so each chat is rendered as a separate dot.
# start interactive TUI + pinglo tracking
integrations/codex/codex-with-pinglo.sh
# same codex args are forwarded
integrations/codex/codex-with-pinglo.sh --profile work --searchOptional convenience target:
make run-codex-integrationcodex-with-pinglo.sh reads Codex local state (~/.codex/state_5.sqlite) and each thread rollout_path, then maps latest events to dot status.
The wrapper uses thread-aware IDs (integration:codex:<thread_id>) so each Codex chat is rendered as a separate dot.
Add this module definition to your ~/.config/waybar/config. pinglod uses SIGRTMIN+4 by default, so the module must watch signal: 4 and refresh interval: "once":
{
"modules-right": ["custom/pinglo"],
"custom/pinglo": {
"return-type": "json",
"exec": "pinglo render --format waybar",
"interval": "once",
"signal": 4,
"escape": false,
"tooltip": true,
"markup": "pango",
"on-click": "pinglo clear"
}
}If you need a different real-time signal, start pinglod with -signal-offset N and set the module signal to the same offset.
In ~/.config/waybar/style.css:
#custom-pinglo {
font-size: 14px;
border-radius: 10px;
margin-right: 5px;
}
#custom-pinglo.empty {
padding: 0;
margin: 0;
}
#custom-pinglo.running {
color: #e5c07b;
}
#custom-pinglo.success {
color: #98c379;
}
#custom-pinglo.failed {
color: #e06c75;
}Waybar receives dots as a Pango-marked string (text), so each dot is rendered with a <span foreground="…">●</span> and CSS cannot target those spans by class. The class field therefore remains semantic (one of running, success, failed, empty) so you can color the module container via selectors like #custom-pinglo.running. The order of dots still matches the order commands were started, and the tooltip lists them in the same sequence.
Manual flow:
pinglo start --cmd "long-command" --cwd "$PWD"
long-command
pinglo done --cmd "long-command" --cwd "$PWD" --exit-code $?Add to your ~/.zshrc:
autoload -Uz add-zsh-hook
function _pinglo_preexec() {
local raw="$1"
if [[ "$raw" == ' '* ]]; then
export PINGLO_TRACKED_CMD="${raw# }"
pinglo start --cmd "$PINGLO_TRACKED_CMD" --cwd "$PWD" >/dev/null 2>&1
else
unset PINGLO_TRACKED_CMD
fi
}
function _pinglo_precmd() {
local exit_code=$?
if [[ -n "$PINGLO_TRACKED_CMD" ]]; then
pinglo done --cmd "$PINGLO_TRACKED_CMD" --cwd "$PWD" --exit-code "$exit_code" >/dev/null 2>&1
unset PINGLO_TRACKED_CMD
fi
}
add-zsh-hook preexec _pinglo_preexec
add-zsh-hook precmd _pinglo_precmdCall pinglo clear to wipe the module manually.
- The shell hook example tracks a single active command per session; extending it to multiple concurrent commands requires additional bookkeeping.
