Home/Features/Second Wind/CLI reference
tools / second-wind

Second Wind

Your Claude Code sessions, running while you sleep.

Second Wind is a single-file Python orchestrator that runs Claude Code in one tmux session per repo. When the account-level 5-hour usage limit pauses everything, the watcher parses the reset time from the pane output, waits it out, and resumes every session automatically.

Looking for the short overview? Read the Second Wind feature page.

Install → View source ↗
1 file, 0 deps
stdlib-only Python — all it needs is tmux and the Claude Code CLI
N repos, 1 watcher
one account-level reset clock, one resume sweep for all sessions
0 babysitting
limit hits at 2am, work resumes at 3am — review in the morning

getting started

01 How to use

One command installs everything into ~/.wind — no clone required.

# install
curl -fsSL https://raw.githubusercontent.com/abhijitbansal/claude-skills/main/tools/second-wind/install.sh | sh

wind init — an interactive wizard scans for your git repos (the scanned roots are saved as scan_roots so you can add more later), lets you pick which ones wind should manage, and choose a global permission preset. Then it offers one choice: configure each repo individually, or apply the global preset + defaults to every selected repo with no per-repo click-through. The presets include auto (--permission-mode bypassPermissions) — accept everything, no prompts — which as of v2.1 is the shipped default for a fresh config (same risk class as --dangerously-skip-permissions; pick acceptEdits/plan/default to reduce autonomy). Use --defaults for the old non-interactive starter file.

wind prompt <repo> — optional: author or edit a repo's first prompt in $EDITOR. Prompt files live at ~/.wind/prompts/<repo>.md by convention; wind seeds the file and wires prompt_file into your config. (One-liners can instead live inline as a repo's prompt string.)

wind add <path> — bring a new git repo under management without re-running init: it appends a {name, path} entry that inherits the global permission preset, launches that repo's tmux session immediately, and refreshes the watcher so the new session is auto-resumed. It writes {name, path} only (no claude_args/agent/prompt from the CLI).

wind up — starts one tmux session per repo, launches the agent in each, sends each repo's initial prompt, and auto-spawns the watcher in its own detached <prefix>-watcher tmux session so it survives closed terminals (one watcher per machine; on macOS it self-caffeinates to keep the machine awake). Pass --no-watch to skip it; run wind watch yourself in the foreground, or wind watch --detach to spawn the detached session by hand. wind down reaps the watcher with the rest.

Check in whenever you likewind status shows per-session state and the next reset time; tmux attach -t wind-<repo> watches a session live (detach with Ctrl-b d).

wind down — kills all wind sessions when you're finished.

Watch it live

wind dash opens a live dashboard in your browser at http://127.0.0.1:8787. One card per session: state pill, reset countdown, last 30 lines of the pane, all in full color. Click a card to expand it into a full-height modal with a large colorized scrollback and a roomy send box — handy on a small screen for reading a session or answering a question. Actions: resume all, send a message to any session, kill a session, or hit + add repo to pull in a scanned-but-unmanaged repo (found under your persisted scan_roots) and launch it live. Localhost-only; every action — and the modal's larger scrollback fetch — requires a per-run token so other sites can't reach it. Use --port to change the port, --no-browser to skip auto-open.

under the hood

02 How it works

A watcher process polls every tmux pane, keeps a single account-level reset clock, and sweeps a resume message into every paused session after the limit lifts.

Architecture

account-level reset clock
one clock for all sessions — latest reset time seen wins
wind watch
the watcher — polls panes, parses reset times, schedules the sweep
↓ optional
ntfy.sh notification
optional — pinged when the limit hits and when sessions resume
capture-pane · poll every 30s
paste resume message · after reset
tmux server
wind-repo1Claude Code
wind-repo2Claude Code
wind-repoNClaude Code

The 5-hour limit is account-level: every session pauses and resets together, so one watcher and one clock cover them all.

The watch loop

running
sessions working normally
limit detected
regex match on pane tail
waiting
until reset + buffer
resume sweep
all paused sessions
cooldown
ignore stale limit text in scrollback

↺ back to running

Limit detection

The watcher scans the tail of each pane against a list of regexes. Built-in patterns cover the formats Claude Code emits:

  • Claude AI usage limit reached|<epoch> — headless / print mode
  • 5-hour limit reached ∙ resets 3am — interactive UI
  • resets at 8pm / try again at 6:15pm — message variants

Clock times like 3am are read as the next local occurrence. Regexes you add to limit_patterns in the config take precedence over the built-ins — handy when the message format changes between Claude Code versions. If a pattern matches but the time can't be parsed, the watcher falls back to a conservative 1-hour retry rather than stalling forever.

Agents & GitHub Copilot

The agent key (top-level default, overridable per repo) picks a preset. claude is the default and behaves exactly as before — watched, with limit detection and auto-resume. copilot launches the GitHub Copilot CLI and shows it in the dashboard, but the watcher skips it: a Copilot session is never scanned for limits and never auto-resumed, so handle its rate limits yourself. You can still send it prompts from wind up and the dashboard, and a manual wind resume nudges it with its own message.

Config reference

wind reads ./second-wind.json, then ~/.wind/config.json, then ~/.config/second-wind/config.json (legacy).

session_prefixtmux sessions are named <prefix>-<repo name>
agentdefault agent — claude (watched + auto-resumed) or copilot (launched + shown, never auto-resumed); overridable per repo
claude_cmdcommand used to launch the agent; can be overridden per repo
claude_argsthe global permission preset passed to that command; can be overridden per repo. The auto preset (--permission-mode bypassPermissions, accepts everything) is the shipped default for a fresh config — same risk class as --dangerously-skip-permissions
resume_messagetext typed into each paused session after the limit resets
resume_buffer_secondsextra wait after the parsed reset time before resuming
poll_interval_secondshow often the watcher captures panes
resume_cooldown_secondsafter resuming a session, ignore limit messages still visible in its scrollback for this long
startup_delay_secondswait after launching Claude Code before sending the initial prompt
capture_lineshow many trailing pane lines to scan
caffeinateon macOS, keep the machine awake while wind watch runs (caffeinate -dims)
ntfy_urloptional; POST a notification here when the limit hits and when sessions resume (works with ntfy.sh topics)
scan_rootsdirectories the wind init wizard scanned, persisted so wind add and the dashboard's + add repo can offer more repos later without re-running init
limit_patternsextra regexes tried before the resolved agent's built-ins
repos[].agentoptional per-repo override of the top-level agent
repos[].prompt_fileoptional file whose contents are sent as the first prompt by wind up (convention: ~/.wind/prompts/<repo>.md; author with wind prompt)
repos[].promptoptional inline first-prompt string for one-liners; wins over prompt_file
security & trust

The config file is trusted input: claude_cmd, claude_args, limit_patterns, and prompt files are executed, compiled, or typed exactly as written — never point wind at a config you didn't write yourself. As of v2.1 the shipped default is full-auto (--permission-mode bypassPermissions, the auto preset): an unattended run accepts everything, not just edits — the same risk class as --dangerously-skip-permissions, so only point wind at repos you trust to run agent actions without prompts (pick acceptEdits/plan/default in wind init to dial it back). And resuming blindly types resume_message into panes; if a session was actually waiting on a permission prompt or a question, "continue" is still a reasonable nudge — but review your sessions in the morning.

The dashboard binds 127.0.0.1 only. /api/status and /api/scan (the + add repo candidate list) serve tokenless, but the modal's full-scrollback endpoint /api/pane — which can carry secrets — and every write action (send/kill/resume/add) require the per-run token. wind add and /api/add write {name, path} only (inheriting the global preset, never accepting claude_args/agent/prompt from the caller), and /api/add only accepts a path already under a persisted scan_root — so a localhost request can't add an arbitrary filesystem path. Pane text reaches the page via textContent with an allowlisted ANSI renderer, never innerHTML. wind prompt never invokes a shell — $EDITOR is split with shlex and exec'd as a list, and the repo→filename mapping is validated as a single path component. Config and state writes are atomic (os.replace), so a crash or a concurrent watcher/dashboard read never sees a truncated file.