English | 中文
Start the next Claude Code or Codex rate-limit window the moment the previous one resets.
Claude Code and Codex subscription limits run on 5-hour rolling windows (plus a weekly cap). A fresh 5h window does not start just because the previous one reset; it starts when you send the first billable request. If that happens hours later, the gap is wasted and your window schedule drifts.
limitping watches the reset time and sends one tiny request through the
official provider CLI right after rollover. Run it once, keep watch in the
foreground, or start a detached bg watcher that keeps your window chain alive
after the terminal closes.
claude ✓ pinged (6.6s)
codex ✓ pinged (14s, 19,426 tokens (in 19,414 / out 12), $0.0023)
- Keeps 5h windows continuous by pinging as soon as a reset is safely available.
- Runs the way you do: one-shot
ping, foregroundwatch, or detachedbg startwithbg status,bg logs -f, andbg stop. - Shows 5h and weekly usage, reset countdowns, and background watcher state from read-only usage endpoints.
- Counts today's, this week's and this month's tokens and what they would have cost at API rates, read from the CLIs' own local session transcripts — the number the percentages never show.
- Triggers Claude Code and Codex through their official CLIs using your existing logged-in credentials.
- Detects active Claude/Codex turns via CLI hooks, so a ping never interrupts a session that is already about to start the window itself.
- Auto-resumes parked tasks:
limitping continue <provider>proxies the official CLI and types your continue message the moment the 5h limit recovers, so an overnight task doesn't sit at the limit until morning. - Spends reset credits before they lapse — Codex reset credits and Claude reset
cards (the one-off usage-limit resets Anthropic hands out, e.g. at a model
launch):
statuslists them,limitping redeemcashes one in by hand, andauto_redeem = trueletswatch/continuespend one on its own once it is close to expiring — a banked reset is worth nothing after it expires. Off by default, because redeeming is irreversible. - Types short: every command also works as
lmp(e.g.lmp s,lmp w). - Includes dry-run modes, weekly-limit guards, reset buffers, cheap-model defaults, macOS notifications, local config, and no telemetry.
curl -fsSL https://raw.githubusercontent.com/wavever/CCLimitPing/main/install.sh | sh
limitping config init
limitping status
limitping ping --dry-run
limitping watch # foreground, low-power (Ctrl-C to stop)
# ...or run it in the background, freeing your terminal:
limitping bg start
limitping bg status
limitping bg logs -fUse dry-run first if you want to inspect what would happen without consuming
provider quota: limitping ping --dry-run, limitping watch --dry-run, or
limitping bg start --dry-run.
| Provider | Read usage (zero-quota) | Trigger | Auth |
|---|---|---|---|
| Claude Code | …/api/oauth/usage |
interactive Claude Code CLI | OAuth (Keychain / ~/.claude) |
| Codex | …/backend-api/wham/usage |
codex exec --ephemeral |
OAuth (~/.codex/auth.json) |
Two cleanly separated jobs:
| Job | Mechanism | Cost |
|---|---|---|
| Trigger a new window | the official CLI (interactive Claude Code / headless codex exec) |
a tiny slice of quota (this is the point) |
| Read usage & reset times | zero-quota usage endpoints (the same ones CodexBar / community plugins use) | none — never starts a window |
When watch sees a 5h window has reset, it first checks whether a Claude/Codex
session is actively mid-turn. If one is, limitping waits and re-reads usage
instead of sending its own ping, because that session's next model request will
start the new window naturally. This check relies on the
CLI hooks (installed automatically by the
install script). Without them, Claude Code's own session list
(claude agents --json) answers for Claude, and Codex's own session log
(a rollout under ~/.codex/sessions whose last turn is still open) answers
for Codex.
- Claude: reads
GET https://api.anthropic.com/api/oauth/usageusing the OAuth token from the macOS Keychain (Claude Code-credentials) or~/.claude/.credentials.json— or whereverCLAUDE_CONFIG_DIRpoints, as Claude Code itself does. Triggering uses a TTY-backed interactiveclaude "<prompt>"session, so it continues to start the Claude subscription-backed window after the headless print command moves to Agent SDK/API credits. The ping is kept as bare as the interactive CLI allows: it runs under its own--session-idand its transcript is deleted afterwards (pings used to pile up as "." conversations inclaude --resume), with your hooks off (disableAllHooks), no MCP servers (--strict-mcp-config), no tools (--tools ""), no CLAUDE.md and nothing added to your prompt history — each flag only when the installed Claude Code supports it. The ping runs in the directorywatchwas started from when Claude Code trusts it; otherwise (the home directory, say, whose trust Claude Code does not keep) it borrows a directory Claude Code does trust and that has no project settings, so its workspace-trust prompt never blocks it — and only when the transcript can be deleted, so nothing is left behind there. If the usage endpoint returns an ambiguous 429, limitping uses the free token-counting endpoint (which does not create a Message) to distinguish a real endpoint throttle from Claude Code subscription access being disabled. - Codex: reads
GET https://chatgpt.com/backend-api/wham/usageusing the OAuth token from~/.codex/auth.json. Triggering runscodex exec --ephemeral --json "<prompt>".--ephemeralis the reason the ping is headless: the interactive CLI cannot skip persisting a session, so every ping used to leave an "ok" conversation behind incodex resumeand in the Codex Desktop thread list.--jsonmakes the ping verifiable — itsturn.completedevent is the only local proof that a billable request went out, and it is where the reported token count and cost come from. The ping also runs with--disable hooksand--sandbox read-only: your hooks have no business firing for a synthetic session (it must not register itself as an active Codex session either), and nothing reviews what the model does on this path, unlike an interactive session with you at the keys.
Claude/Codex tokens are reused from the official tools (no separate login) and refreshed on 401.
limitping ships as a single self-contained binary — no Go required.
One-line script (macOS / Linux):
curl -fsSL https://raw.githubusercontent.com/wavever/CCLimitPing/main/install.sh | shDownloads the right prebuilt binary from the
latest release into
/usr/local/bin (or ~/.local/bin). Override with LIMITPING_INSTALL_DIR.
Upgrade — replace the installed binary with the latest release:
limitping upgradeupgrade checks first and says so if you are already current; --force
reinstalls anyway. status, ping, bg status and continue also announce a
new release before their own output, once per release:
✨ Update available! 0.9.0 -> 0.10.0
Release notes: https://github.com/wavever/CCLimitPing/releases/latest
1. Update now (runs `limitping upgrade`)
❯ 2. Skip
3. Skip until next version
↑/↓ move · Enter confirm · Esc skip
Move with the arrow keys and confirm with Enter, the way the provider CLIs do
it; the number keys still pick an option outright. The cursor starts on Skip
because the notice interrupts the command you actually ran, so Enter, Esc and
Ctrl-C all leave everything as it was. Option 3 records the release in
~/.config/limitping/version.json and stays quiet until the next one. The check
runs at most every four hours, never blocks for more than two seconds, and is skipped
entirely without an interactive terminal — so --json, the hook callback and
background watchers stay silent.
Aliases: limitping up, limitping update.
Uninstall — remove the installed binary plus config/cache:
limitping uninstallAliases: limitping rm, limitping remove.
Use limitping uninstall --keep-config to preserve ~/.config/limitping (or
$XDG_CONFIG_HOME/limitping).
Manual download — grab the archive for your platform from the
Releases page (.tar.gz for
macOS/Linux, .zip for Windows):
tar -xzf limitping_darwin_arm64.tar.gz
sudo mv limitping /usr/local/bin/Homebrew (macOS / Linux) — brew install wavever/tap/limitping
(works once the Homebrew tap is set up — see .goreleaser.yaml).
From source (developers, needs Go 1.25+):
go install github.com/wavever/CCLimitPing/cmd/limitping@latest
# or, from a clone:
go build -o bin/limitping ./cmd/limitpingEach provider you enable needs its own credentials: the claude / codex CLIs
logged in.
limitping config init # write ~/.config/limitping/config.toml
limitping status # 5h/weekly % + reset countdowns + today/week/month tokens (alias: s)
limitping status --json # machine-readable JSON for each provider
limitping status -v # also print the per-model breakdown and raw JSON
limitping ping # trigger all enabled providers now (alias: p)
limitping ping claude # Claude only
limitping ping codex # Codex only
limitping ping --dry-run # show the commands without sending
limitping watch # foreground daemon: ping each window at reset (alias: w)
limitping watch claude # watch only one provider (claude|codex)
limitping watch --live # optional live heartbeat/status line
limitping watch --dry-run # log when pings would fire, without sending
limitping schedule codex --at 05:00 --at 13:00 # ping at daily local times
limitping schedule --every 5h # ping on a fixed interval instead of reset time
limitping redeem --dry-run # show which reset credit / reset card would be spent
limitping redeem claude # spend a Claude reset card now (irreversible; claude|codex;
# refuses while the windows are under half used — --force)
limitping continue codex # proxy the CLI; auto-resume the task on 5h recovery
limitping continue codex --yolo # flags after the provider pass through
limitping continue claude --dangerously-skip-permissions
limitping bg start # run watch in the background, freeing the terminal
limitping bg status # running? + each watched provider's usage (alias: limitping bg)
limitping bg logs -f # follow the background watcher's log
limitping bg stop # stop the background watcher
limitping hooks install # install active-session detection hooks (claude|codex|all)
limitping hooks uninstall # remove those hooks
limitping hooks status # check they are installed, current and (Codex) trusted
limitping version # print the version (aliases: v, ver)
limitping upgrade # update to the latest GitHub release (aliases: up, update)
limitping uninstall # remove limitping plus config/cache (aliases: rm, remove)Short aliases are also available for config commands: limitping c i for
config init and limitping c p for config path.
limitping --help lists aliases inline, for example ping, p, and its own
Aliases: line shows both binary names so either one is discoverable from the
other.
The binary itself has a short name too: the installer symlinks lmp next to
limitping, so lmp status, lmp w, and limitping status are the same
command. The installer skips the link if lmp already exists or resolves to
another command on your PATH — a symlink in /usr/local/bin shadows anything
it collides with. (Building from source? ln -s limitping /usr/local/bin/lmp.)
| Command | Aliases |
|---|---|
status |
s, stat |
ping |
p |
watch |
w |
schedule |
sched |
redeem |
r |
background |
bg |
config |
c, cfg |
config init |
c i |
config path |
c p |
version |
v, ver |
upgrade |
up, update |
uninstall |
rm, remove |
ping shows the exact command and a live timer (a spinner on a terminal). The
Codex ping reports the turn's tokens and an equivalent API cost, read from
codex exec --json; Claude's interactive trigger session exposes no reliable
machine-readable per-ping usage, so it shows elapsed time only:
claude → claude --model haiku --session-id 6f1c2a9e-4b7d-4e2a-9c31-0d5e8f7a1b23 --settings "{\"disableAllHooks\":true,\"promptSuggestionEnabled\":false}" --strict-mcp-config --tools "" -- .
claude ✓ pinged (6.6s)
codex → codex exec --ephemeral --json --skip-git-repo-check --disable hooks --sandbox read-only -c model_reasoning_effort=low -m gpt-5.6-luna ok
codex ✓ pinged (14s, 19,426 tokens (in 19,414 / out 12), $0.0023)
ping and the watch log always name the model. In the rare case limitping
cannot pick one — no catalog on disk, or no recognizable budget tier in it — the
Codex CLI chooses instead, and the model is reported alongside the command so
you still see what the ping spent quota on:
codex → codex exec --ephemeral --json … -c model_reasoning_effort=low ok (model: gpt-5.6-sol)
A ping ends by printing the same window view status gives, for the providers
it pinged — a ping is far too small to move the used percentage, so its own
output cannot show whether a window started. Reading usage costs nothing and
never starts a window. --dry-run skips it: nothing was sent, so there is no
new state to report.
Example status:
claude
5h [█████░░░░░] 51.0% used resets in 3h14m (Sun 00:10 UTC+8)
weekly [█████░░░░░] 54.0% used resets in 7h04m (Sun 04:00 UTC+8)
today 55.0M tokens ≈ $41.03
week 310.4M tokens ≈ $198.72
month 1.02B tokens ≈ $684.15
codex (plus)
5h [██░░░░░░░░] 24.0% used resets in 3h15m (Sun 00:11 UTC+8)
weekly [████░░░░░░] 37.0% used resets in 111h57m (Thu 12:53 UTC+8)
today 20.5M tokens ≈ $10.06
week 96.3M tokens ≈ $45.80
month 412.7M tokens ≈ $187.24
reset credits 1 reset available
- available, granted Jun 17 17:38, expires Jul 17 17:38 UTC+8 (in 24d6h)
Text status defaults to used percentage. Set usage_display = "remaining" if
you prefer the same mental model as Codex's "Usage remaining" UI.
The today, week and month lines answer what the percentages cannot: how
much this machine actually consumed since local midnight, since Monday and since
the 1st of the month, and what that would have cost at published API rates —
the value a subscription is returning. These are calendar periods, not the
provider's rolling 5h/weekly windows. The usage endpoints
only ever report percentages, so the tokens are totalled from the transcripts
the CLIs already write to disk (~/.claude/projects, ~/.codex/sessions —
honoring $CLAUDE_CONFIG_DIR / $CODEX_HOME), the same source ccusage and
CodexBar read, and priced with the LiteLLM
dataset limitping already caches for ping. Nothing is uploaded: the scan is
local, read-only, and runs alongside the usage fetch; one pass over the month's
transcripts yields all three periods, and only files written to since the period
began are opened.
Two consequences worth knowing: it is a local view — sessions run on another
machine, or in the web app, leave no transcript here and are not counted — and
the cost is an estimate, since a subscription does not bill per token. A
model too new to be in the pricing dataset still has its tokens counted; it just
adds nothing to the dollar figure (cost_complete: false in JSON).
Each request is priced on its own, the way the API would bill it: Claude Code's one-hour prompt-cache writes at the one-hour rate rather than the five-minute one, and a request whose prompt runs past a model's long-context threshold (272K tokens for OpenAI's, 200K where Anthropic has one) entirely at the long-context rates. A streamed Claude reply is counted once, at its final output count.
status -v breaks each period down by bucket and by model:
today 55.0M tokens ≈ $41.03
in 770 · cache 53.6M read / 1.1M write · out 300.6K
claude-opus-5 54.9M tokens ≈ $40.95
claude-haiku-4-5-20251001 67.4K tokens ≈ $0.09
week 310.4M tokens ≈ $198.72
...
The lines are omitted entirely for a provider whose CLI has never run on this
machine — silence there is honest, where 0 tokens would claim a quiet month.
status --json returns the same data as a JSON array (one object per provider),
for scripts and dashboards. Progress chatter is suppressed so stdout stays a
single valid document; a provider that fails to read becomes
{"provider": "...", "error": "..."} and the command exits non-zero. Add -v
to embed each provider's raw response under raw.
today, week and month are omitted for a provider with no local transcripts
at all. When present, each carries start (the period's first local day; today
also keeps it as date), cost_usd is the API-rate estimate, and
cost_complete is false if a model that ran had no published rates, making that
figure a lower bound.
A window key (five_hour / weekly) is omitted when the provider does not
currently enforce that limit — e.g. OpenAI temporarily removed Codex's 5h
window on 2026-07-12, leaving only the weekly cap. Text mode prints
not currently enforced for such a window, and watch schedules its ping at
the weekly reset instead of every 5h.
An enforced limit that nothing has started yet is a different state: text mode
prints (no active window) and resets_at is absent from the JSON, because a
rolling window has no reset time until a request anchors it. Codex does not
report that state directly — it answers with a full-length window that slides
forward on every read (reset_after_seconds equal to limit_window_seconds),
i.e. when a window would end if you started one now. limitping normalizes that
away, so a reset time is only ever shown for a window that is really running.
[
{
"provider": "codex",
"plan": "plus",
"five_hour": {
"used_percent": 24,
"remaining_percent": 76,
"active": true,
"resets_at": "2026-06-17T05:51:45+08:00",
"remaining_seconds": 11700,
"window_seconds": 18000
},
"weekly": {
"used_percent": 37,
"remaining_percent": 63,
"active": true,
"resets_at": "2026-06-24T00:51:45+08:00",
"remaining_seconds": 403020,
"window_seconds": 604800
},
"credits": { "has_credits": false, "unlimited": false, "balance": "0" },
"reset_credits": {
"available_count": 1,
"credits": [
{
"status": "available",
"granted_at": "2026-06-17T17:38:38Z",
"expires_at": "2026-07-17T17:38:38Z"
}
]
},
"today": {
"date": "2026-06-17",
"start": "2026-06-17",
"input_tokens": 674291,
"cache_read_tokens": 19719552,
"cache_creation_tokens": 0,
"output_tokens": 81039,
"total_tokens": 20474882,
"cost_usd": 10.059257,
"cost_complete": true,
"models": [
{ "model": "gpt-5.6-sol", "total_tokens": 13774852, "cost_usd": 7.748941 },
{ "model": "gpt-5.6-terra", "total_tokens": 6700030, "cost_usd": 2.310316 }
]
},
"week": {
"start": "2026-06-15",
"input_tokens": 3102544,
"cache_read_tokens": 92800114,
"cache_creation_tokens": 0,
"output_tokens": 398201,
"total_tokens": 96300859,
"cost_usd": 45.801276,
"cost_complete": true,
"models": [
{ "model": "gpt-5.6-sol", "total_tokens": 71203817, "cost_usd": 38.412530 },
{ "model": "gpt-5.6-terra", "total_tokens": 25097042, "cost_usd": 7.388746 }
]
},
"month": {
"start": "2026-06-01",
"input_tokens": 13250117,
"cache_read_tokens": 397840326,
"cache_creation_tokens": 0,
"output_tokens": 1609771,
"total_tokens": 412700214,
"cost_usd": 187.240913,
"cost_complete": true,
"models": [
{ "model": "gpt-5.6-sol", "total_tokens": 298114660, "cost_usd": 154.902207 },
{ "model": "gpt-5.6-terra", "total_tokens": 114585554, "cost_usd": 32.338706 }
]
},
"limit_reached": false,
"fetched_at": "2026-06-17T01:00:43+08:00"
}
]~/.config/limitping/config.toml (honors $XDG_CONFIG_HOME):
weekly_threshold = 0.99 # skip pinging when weekly usage >= this (0..1), until weekly reset
reset_buffer = "10s" # wait this long after a reset before pinging (ensures rollover)
notify = true # macOS notifications on ping/skip/failure
usage_display = "used" # text status: "used" or "remaining"
[claude]
enabled = true
prompt = "."
model = "haiku" # cheapest tier; triggering doesn't need a SOTA model
extra_args = [] # extra Claude CLI args; print/headless-only flags are ignored
align_start = "" # optional RFC3339 anchor for the first window; empty = start ASAP
continue_prompt = "continue" # message `continue` injects on 5h recovery; empty = "continue"
[codex]
enabled = true
prompt = "ok"
model = "" # empty = pick the cheapest model your plan offers
reasoning_effort = "low" # "minimal" is rejected when web_search/image_gen tools are enabled
extra_args = [] # extra Codex CLI args; exec-only flags such as --json are ignored
align_start = ""
continue_prompt = "continue" # message `continue` injects on 5h recovery; empty = "continue"Top-level keys:
weekly_threshold— when the weekly window is at/above this,watchstops pinging and waits for the weekly reset (unless usable credits exist).reset_buffer— how long to wait after a window's reset time before pinging, so the window has definitely rolled over.usage_display— whether textstatus/bg statusrenders each window as used percentage or remaining percentage.align_start(per provider) — pin the phase of your windows: set to a future RFC3339 time to delay the very first ping until then; afterwards windows chain automatically every ~5h.
Triggering a window doesn't depend on the model — any billable request starts the 5h clock — so the ping uses each provider's cheapest model to eat the least of your budget:
- Claude →
haiku: also avoids the separate weekly Opus bucket. - Codex → empty, resolved per ping: limitping reads the Codex CLI's own
catalog (
~/.codex/models_cache.json) and picks the cheapest model your plan offers — the budget tier OpenAI marks "Fast and affordable", never your working model, which is usually a far pricier tier. Because the choice is made at ping time it survives OpenAI retiring and adding models. Name a model to pin one instead; a pinned model that has since been retired is rejected with the current list rather than failing as an opaque server error at rollover.
Claude/Codex don't expose per-model prices at runtime (Anthropic's local
cost cache is empty; Codex's model cache has no price field), so the cheapest
model is a sensible default rather than a live price lookup. Override model
per provider if you prefer.
At a window reset, watch avoids pinging while you're actively working — that
turn would start the next window on its own. This relies on CLI hooks, which
the install script sets up for you. If they aren't installed, Claude falls back
to Claude Code's own session list (claude agents --json, which marks a session
busy mid-turn), and Codex to its own session log: a rollout under
~/.codex/sessions written in the last 10 minutes whose last task_started has
no task_complete or turn_aborted after it. limitping never guesses from
the process list.
The install script runs this automatically; to (re)install manually:
limitping hooks install # both providers (or: limitping hooks install claude)This registers limitping's hooks in ~/.claude/settings.json and
~/.codex/hooks.json (or under CLAUDE_CONFIG_DIR / CODEX_HOME; your existing
settings are preserved; a .bak backup is written). The hooks invoke the hidden
limitping hook <provider> command on UserPromptSubmit / PreToolUse /
PostToolUse / Stop / SessionEnd, plus the events that end a turn without
Stop — Claude's StopFailure (an API error such as a usage limit ended it)
and Codex's Interrupt — to record whether a session is mid-turn under
~/.config/limitping/activity/. limitping upgrade re-registers them, so a
release that relies on a new event picks it up.
limitping hooks status # installed? current? (Codex) trusted?status also flags hooks that another tool rewrote away or that predate the
current release.
Note
Claude Code loads its hooks automatically — nothing to do there. Codex
gates custom command hooks behind a one-time trust step: run /hooks inside
Codex once to enable them (and again for hooks a newer release adds;
limitping hooks status lists any Codex does not trust yet). Remove
everything later with limitping hooks uninstall (also done automatically by
limitping uninstall).
Use schedule when you want wall-clock pings instead of reset-aligned
window chaining. It keeps running in the foreground and fires ping at the next
configured interval or daily local time:
limitping schedule codex --at 05:00
limitping schedule codex --at 05:00 --at 13:00 --at 21:00
limitping schedule --at 05:00,13:00,21:00
limitping schedule codex --every 5h --dry-runYou can combine --every and --at; whichever next occurrence comes first is
used. --at values are daily local times in HH:MM or HH:MM:SS form.
watch runs in the foreground. To free your terminal, run it as a detached
background process with the built-in bg command:
limitping bg start # start watch detached from the terminal
limitping bg status # running? pid, uptime, log + each provider's usage (alias: limitping bg)
limitping bg logs -f # follow the watcher's log (-n N for last N lines)
limitping bg stop # stop itwatch defaults to low-power log output. Add --live if you want a foreground
heartbeat/status line. bg start takes the same optional [provider] argument
and --dry-run flag as watch. Only one watcher (foreground or background) runs
at a time, and background output is written to
~/.config/limitping/bg.log (honors $XDG_CONFIG_HOME). The process detaches
into its own session, so it survives the shell closing — but it does not
restart on reboot.
For start-at-login on macOS, use a launchd agent instead. Create
~/Library/LaunchAgents/com.limitping.watch.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.limitping.watch</string>
<key>ProgramArguments</key>
<array>
<string>/ABSOLUTE/PATH/TO/limitping</string>
<string>watch</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/tmp/limitping.log</string>
<key>StandardErrorPath</key><string>/tmp/limitping.err</string>
</dict>
</plist>launchctl load ~/Library/LaunchAgents/com.limitping.watch.plistwatch and bg keep your window chain warm, but they don't resume a task that
has already stalled at the 5h limit. limitping continue <provider> does: it
launches the provider's real interactive CLI through a PTY and passes your
terminal straight through, so you drive Codex / Claude Code exactly as usual.
In the background it polls usage and, the moment the 5h limit recovers after
being hit, types your continue message into the session so a long task resumes
itself instead of sitting parked until you come back.
limitping continue codex # drive Codex as usual; auto-resume on recovery
limitping continue codex --yolo # flags after the provider pass through verbatim
limitping continue claude --dangerously-skip-permissions- The resume message is each provider's
continue_promptin config (default"continue"; set it to e.g."继续任务"). Quit from inside the CLI to exit. - It only injects on a genuine recovery edge: the 5h window was maxed (or the
endpoint reported
limit_reached, or the CLI printed a limit message) and has since clearly reset, and the weekly window isn't also exhausted (perweekly_threshold, credits included) — so it won't resume straight into the weekly wall. - Recent Claude Code can resume a task itself once the limit resets ("Continue
automatically at usage limit" in
/config, on by default).continue claudedefers to it: at recovery it waits up to 90 seconds for Claude Code to announce the resume or start the turn, and only types the message when it didn't — so the task is never continued twice, and older Claude Code still gets resumed. - A diagnostic timeline is written to
~/.config/limitping/continue.log(rotated tocontinue.log.1past 1 MB). - Unix only for now (needs a PTY); on Windows the command reports that it's unsupported.
- See PRIVACY.md for local data handling and network behavior.
- See SECURITY.md for vulnerability reporting and credential handling notes.
- Triggering consumes a little quota (~one ping per 5h ≈ 33/week). The ping uses a minimal prompt and low reasoning, so the cost is tiny but non-zero.
- The usage endpoints are unofficial and could change; they're read-only and isolated per provider for easy patching.
- macOS-first: Keychain reads and notifications are macOS-only. Codex
auth.jsonis cross-platform; Claude on Linux uses~/.claude/.credentials.json; notifications are a no-op off macOS.
cmd/limitping CLI entry
internal/config TOML config
internal/usage normalized usage model
internal/auth Claude (Keychain) + Codex (auth.json) tokens
internal/provider per-provider ReadUsage (endpoint) + Trigger (CLI)
internal/activity hook-based active-session state (shared by the hook cmd + scheduler)
internal/pricing pricing helpers for providers that expose token usage
internal/spend today/week/month tokens/cost, read from the CLIs' local session transcripts
internal/scheduler the watch engine (sleep-until-reset, weekly-respect, backoff)
internal/notify macOS osascript notifications
internal/cli cobra commands: status, ping, watch, schedule, continue, background, config, hooks, upgrade, uninstall, version
Issues and PRs are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Before submitting:
gofmt -l . # should print nothing
go build ./...
go vet ./...
go test ./...Providers are isolated in internal/provider behind a small Provider
interface (ReadUsage + Trigger), so adding a new provider is mostly
self-contained provider code plus wiring in internal/cli and internal/config.
Releasing is one command — push a tag, and GitHub Actions runs GoReleaser to build the cross-platform binaries and publish a Release:
git tag v0.10.0 && git push origin v0.10.0Nothing else needs editing. The tag is the only place a version is written down:
the release build stamps it in via -ldflags, and any other build derives its
version from the module's build info, so there is no constant to bump and
nothing that can drift from the tag. A local go build reports
dev+<revision> and never offers to upgrade itself.
Release notes are generated from the commit log, so commit subjects are the release notes — write them as a line a user would want to read. There is no hand-maintained changelog to keep in sync; published notes live on the Releases page.
MIT © wavever