{"id":50338775,"url":"https://github.com/wyofalcon/ttyl","last_synced_at":"2026-05-29T15:30:30.895Z","repository":{"id":353971387,"uuid":"1221137072","full_name":"wyofalcon/ttyl","owner":"wyofalcon","description":"Claude Code plugin: floating Windows tile that flashes when Claude finishes a turn, plus a per-session prompt-cache TTL countdown for the status line.","archived":false,"fork":false,"pushed_at":"2026-04-26T13:22:22.000Z","size":113,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-26T15:16:50.258Z","etag":null,"topics":["anthropic","claude-code","claude-code-plugin","prompt-caching","statusline"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wyofalcon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-25T19:57:39.000Z","updated_at":"2026-04-26T13:22:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wyofalcon/ttyl","commit_stats":null,"previous_names":["wyofalcon/ttyl"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/wyofalcon/ttyl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyofalcon%2Fttyl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyofalcon%2Fttyl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyofalcon%2Fttyl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyofalcon%2Fttyl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wyofalcon","download_url":"https://codeload.github.com/wyofalcon/ttyl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wyofalcon%2Fttyl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33659872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","claude-code","claude-code-plugin","prompt-caching","statusline"],"created_at":"2026-05-29T15:30:29.785Z","updated_at":"2026-05-29T15:30:30.887Z","avatar_url":"https://github.com/wyofalcon.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TTYL\n\n**Talk To You Later** — know when Claude is done and never lose your prompt cache.\n\nA small personal tool I built to keep myself on task while waiting for long Claude runs to finish across multiple parallel sessions. Two parts, in order of importance:\n\n- **A floating Windows desktop tile** *(Windows 10/11 only — the primary feature)*. One always-on-top tile that lists every active Claude Code session as a row, flashes the row when Claude finishes a turn, shows a yellow \"you-are-here\" border on the row whose VSCode is currently focused, and **click-focuses the owning VSCode window** — the fastest way to jump back to whichever project Claude just finished thinking about.\n- **A Claude Code status-line segment** *(any platform that runs bash — only useful if you use the `claude` CLI)*. Renders the cache TTL into Claude Code's status line. The Claude AI VSCode extension chat panel doesn't render Claude Code's `statusLine` at all, so this segment is invisible from there — the floating tile is what surfaces the cache state in that workflow.\n\nBoth parts read from the same per-session JSON state files written by the plugin's lifecycle hooks, so they work alongside each other (or independently). Works fine with the Claude AI VSCode extension; works fine with the `claude` CLI; works fine with both running in parallel.\n\nStatus-line segment when you do see it (CLI):\n```\n🟢 cache 4:32 | Opus 4.7 | my-project | main\n🟡 cache 1:47 | Opus 4.7 | my-project | main\n🔴 cache 0:28 | Opus 4.7 | my-project | main\n🔴 cache expired | Opus 4.7 | my-project | main\n🔄 active | Opus 4.7 | my-project | main       ← while Claude is thinking\n```\n\n## Why\n\nAnthropic's prompt cache has a 5-minute TTL. Every request inside that window refreshes it; let it lapse and the next prompt re-pays full input-token cost (cache reads are 10% of input cost — a real savings on long Claude Code sessions).\n\nClaude Code doesn't expose a cache expiry field to the status line, so nobody can render the \"real\" countdown. TTYL approximates it client-side from lifecycle hooks (`SessionStart`, `UserPromptSubmit`, `Stop`) — the countdown ticks only while Claude is idle, never while it's mid-request, since active requests are *refreshing* the cache.\n\n## How it works\n\nFive hooks route through one bash script that writes per-session state to `~/.claude/cache-timers/\u003csession-id\u003e.json` (and a legacy single-file mirror at `~/.claude/.cache-timestamp` for back-compat):\n\n| Event | State written | Meaning |\n|---|---|---|\n| `SessionStart` | `none` | Fresh session, no request yet |\n| `UserPromptSubmit` | `active` | Request in flight, cache refreshing |\n| `PreCompact` | `active` | `/compact` in flight — treated as a refresh, since compaction sends a real request |\n| `Stop` | `idle` (with epoch) | Turn ended, countdown starts |\n| `SessionEnd` | (file deleted) | Session shut down — desktop tile removes the row |\n\nThe status line script reads the per-session file keyed by the `session_id` that Claude Code passes to it on stdin. Each terminal sees its own cache state.\n\nIf Claude is hard-killed (terminal closed, process crash) the `SessionEnd` hook can't fire. The desktop watcher catches that case by checking whether the recorded `pid` is still alive on each scan and evicting the row when the owning process is gone.\n\n## Install\n\n### As a Claude Code plugin (recommended)\n\nAdd to your `~/.claude/settings.json`:\n\n```json\n{\n  \"extraKnownMarketplaces\": {\n    \"ttyl\": {\n      \"source\": {\n        \"source\": \"github\",\n        \"repo\": \"wyofalcon/ttyl\"\n      }\n    }\n  },\n  \"enabledPlugins\": {\n    \"ttyl@ttyl\": true\n  }\n}\n```\n\nOr, for local development against a clone:\n\n```json\n{\n  \"extraKnownMarketplaces\": {\n    \"local-ttyl\": {\n      \"source\": {\n        \"source\": \"directory\",\n        \"path\": \"/absolute/path/to/your/ttyl/clone\"\n      }\n    }\n  },\n  \"enabledPlugins\": {\n    \"ttyl@local-ttyl\": true\n  }\n}\n```\n\n### Wire up your status line *(skip if you only use the VSCode extension)*\n\nThe Claude AI VSCode extension chat panel doesn't render Claude Code's `statusLine`, so there's nothing to wire up if that's your only interface — the floating tile is your indicator. This section is for users who run the `claude` CLI in a terminal.\n\nYou need both a `statusLine` command and `refreshInterval: 1` — the interval is what makes the countdown tick.\n\n#### A — You don't have a custom status line\n\nUse the bundled standalone. It renders `cache | model` and nothing else.\n\n```json\n{\n  \"statusLine\": {\n    \"type\": \"command\",\n    \"command\": \"bash \\\"${CLAUDE_PLUGIN_ROOT}/scripts/statusline-standalone.sh\\\"\",\n    \"refreshInterval\": 1\n  }\n}\n```\n\n#### B — You already have a custom status line\n\nInvoke `cache-segment.sh` from your script and compose its output. Pass the session id along so the segment knows which file to read:\n\n```bash\n# inside your existing statusline.sh\ninput=$(cat)\nsession_id=$(echo \"$input\" | jq -r '.session_id // \"\"')\ncache_seg=$(CLAUDE_SESSION_ID=\"$session_id\" \\\n  bash \"${CLAUDE_TTYL_ROOT}/scripts/cache-segment.sh\")\n[ -n \"$cache_seg\" ] \u0026\u0026 parts+=(\"$cache_seg\")\n```\n\nThen in settings:\n\n```json\n{\n  \"statusLine\": {\n    \"type\": \"command\",\n    \"command\": \"bash ~/.claude/my-statusline.sh\",\n    \"refreshInterval\": 1,\n    \"env\": {\n      \"CLAUDE_TTYL_ROOT\": \"/absolute/path/to/the/installed/plugin\"\n    }\n  }\n}\n```\n\n\u003e **Note:** Claude Code only sets `${CLAUDE_PLUGIN_ROOT}` when the status-line command is itself declared by the plugin. From your own statusline script, set `CLAUDE_TTYL_ROOT` explicitly — the marketplace install path includes a version segment (`~/.claude/plugins/cache/\u003cmarketplace\u003e/\u003cplugin\u003e/\u003cversion\u003e`) that changes on every plugin update, so a hardcoded fallback would break on upgrade.\n\n## Tuning\n\nThe segment script honors:\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `CACHE_TTL_SECONDS` | `300` | Window length. Set to `3600` for the 1-hour cache beta. |\n| `CACHE_TIMERS_DIR` | `~/.claude/cache-timers` | Per-session JSONs live here. |\n| `CACHE_STATE_FILE` | `~/.claude/.cache-timestamp` | Legacy single-file fallback. |\n| `CLAUDE_SESSION_ID` | (from stdin) | Override which session's state to render. |\n\n## Desktop tile (Windows 10/11)\n\nThe primary feature. A floating always-on-top tile with one row per active Claude Code session — left-click any row to bring its VSCode window to the foreground, drag any row to move the whole tile, right-click for dismiss / copy session ID. The row whose VSCode is in the OS foreground gets a yellow border so you can tell at a glance which session corresponds to the editor in front of you.\n\nInstall instructions, manual test matrix, and the full feature list are in [`desktop-timer/README.md`](desktop-timer/README.md).\n\nThe desktop tile honors `TTYL_TTL_SECONDS` (default `300`, set to `3600` for the 1-hour cache beta) — the same role `CACHE_TTL_SECONDS` plays for the status-line segment.\n\n\u003e **Why Windows-only?** The tile relies on `SetForegroundWindow`, `IVirtualDesktopManager` (for following you across virtual desktops), and a Startup-folder `.lnk` shortcut. None of those have direct equivalents on macOS/Linux, and porting wasn't on my critical path. The plugin hooks and status-line segment are cross-platform — only the floating tile is Windows-only.\n\n## Limitations and caveats\n\nThis is a personal tool, not a polished product — built quickly to scratch my own itch around running multiple parallel Claude sessions across virtual desktops. Sharing in case it's useful.\n\n- **Floating tile is the reliable indicator; status-line segment is the rougher one.** The tile reads per-session JSONs directly and ticks at 1 Hz on its own clock. The status line goes through Claude Code's `refreshInterval` and emoji rendering varies by terminal — I've seen mis-aligned glyphs in some Git Bash + Windows Terminal combos. If the segment looks weird, trust the tile.\n- **Status-line segment is invisible from the Claude AI VSCode extension.** The extension chat panel doesn't render Claude Code's `statusLine`. Not a TTYL bug — it's a Claude Code surface limitation. Use the floating tile in that workflow.\n- **Approximation, not truth.** Anthropic doesn't publish the cache-expiry timestamp to the client. The countdown starts when the local `Stop` hook fires, which is ~milliseconds after the actual cache refresh. Close enough for practical use, not exact.\n- **Floating tile is Windows 10/11 only** — see the [Desktop tile](#desktop-tile-windows-1011) section above. The plugin hooks and status-line script are cross-platform (anywhere bash runs).\n- **Hooks require bash.** Hooks rely on `date +%s`, `echo`, and file redirection. Claude Code's default hook shell on Windows is Git Bash, which has these. Native PowerShell hooks aren't supported (PRs welcome).\n- **Optional `jq`.** The scripts use `jq` for JSON parsing when available and fall back to `sed` patterns otherwise. Install `jq` if you can — the fallback works but is more brittle on unusual whitespace.\n- **No Anthropic-side verification.** If Anthropic changes the TTL or cache semantics, TTYL won't know. Sanity-check against the [prompt caching docs](https://docs.claude.com/en/docs/build-with-claude/prompt-caching) if behavior seems off.\n\n## Uninstall\n\nRemove the `enabledPlugins` entry from settings, then optionally clean up state:\n\n```bash\nrm -rf ~/.claude/cache-timers\nrm -f ~/.claude/.cache-timestamp\n```\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyofalcon%2Fttyl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwyofalcon%2Fttyl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwyofalcon%2Fttyl/lists"}