npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kirillrybin/togglr

v0.9.0

Published

A CLI + terminal UI for Toggl Track, built to stay well under the API rate limit

Readme

togglr

A CLI + terminal UI for Toggl Track, built for quick one-shot commands and a live dashboard — while staying well under Toggl's API rate limit.

Why

Toggl Track's API allows 30 requests/hour. togglr self-limits to 25/hour and routes almost every read through a local file cache (~/.cache/togglr/) instead of hitting the API directly. Only mutating actions (start/stop/continue) always call the API — everything else is served from cache with a TTL, and gracefully degrades (serving stale data with a notice) if the budget is exhausted or the network is down.

Install

npm install -g @kirillrybin/togglr

This gives you both toggl and togglr commands (they're equivalent). The -g flag is required — without it, npm installs the package into the local node_modules instead of putting a command on your PATH, and toggl/togglr won't be found by your shell.

Requires Node.js ≥ 18.

From source

git clone https://github.com/kirillrybin/togglr.git
cd togglr
npm install
npm run build

This produces dist/cli.js. Run it directly with node dist/cli.js, or link it as local toggl/togglr binaries:

npm link

First run

The first time you run any command, togglr prompts for your Toggl API token (found in Toggl under Profile → API Token) and saves it to ~/.config/togglr/config.json (mode 600). Your default workspace is detected automatically. Use toggl config afterwards to view or change any of this (token, workspace id, cache TTLs) without hand-editing the file.

Usage

toggl start "Coding on togglr" [--project <name>] [--tag <name>]...
toggl add "Coding on togglr" --start 09:00 --end 11:30 [--project <name>] [--tag <name>]...
toggl edit <id> [--description <text>] [--project <name>] [--start <HH:MM>] [--end <HH:MM>] [--tag <name>]...
toggl stop
toggl status
toggl continue
toggl list-projects
toggl report [today|week] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--csv]
toggl config [--token] [--workspace <id>] [--cache-ttl-projects <seconds>] [--cache-ttl-entries <seconds>] [--project-colors <on|off>]
toggl completion <bash|zsh> [--install]
toggl --version
toggl

toggl and togglr are equivalent — both point at the same binary.

| Command | Description | |---|---| | start "<description>" [--project <name>] [--tag <name>]... | Start a new timer (--tag is repeatable) | | add "<description>" --start <HH:MM> --end <HH:MM> [--project <name>] [--tag <name>]... | Manually add a completed entry for today (e.g. forgot to track something) | | edit <id> [--description] [--project] [--start] [--end] [--tag]... | Edit an existing entry by its Toggl id (only the given fields change; --start/--end can be given independently, including on a still-running entry) | | stop | Stop the running timer | | status | Show the current timer and elapsed time (no network call) | | continue | Repeat the most recent time entry | | list-projects | List cached projects | | report [today\|week] [--from] [--to] [--csv] | Print a per-project time breakdown. --from/--to (YYYY-MM-DD, --to inclusive, defaults to today) override the preset range; --csv prints project,duration,seconds instead of plain text | | config | Show current settings (token masked); with --token/--workspace/--cache-ttl-projects/--cache-ttl-entries/--project-colors, changes them instead | | completion <bash\|zsh> [--install] | Print a shell completion script, or --install it into your rc file — see Shell completion | | --version / -v | Print the installed version | | (no arguments) | Launch the TUI dashboard |

TUI dashboard

Running toggl with no arguments opens a live dashboard: the current timer, a today/week summary, and recent entries (shown as description [project] #tag1 #tag2 — duration (start–end), project/tags omitted when there aren't any, and the (start–end) range omitted for the still-running entry, which has no end yet). The [project] name — and the ● next to the active timer's description — can be colored with that project's actual color from Toggl (needs a truecolor-capable terminal); off by default, toggl config --project-colors on to enable. Up to 20 recent entries are kept, displayed through a scrolling 5-row window that follows the highlight (↑ N more / ↓ N more indicate what's scrolled off-screen). The dashboard runs in the terminal's alternate screen buffer (like vim/htop), so it fully disappears on exit instead of leaving its last frame in your shell.

| Key | Action | |---|---| | s | Stop the running timer | | c | Continue the highlighted entry | | n | Start a new timer: prompts for a description, then a project, then tags (same autocomplete as e's steps below) | | e | Edit the highlighted entry (description → start → end → project → tags, one prompt per field; the running entry has no end yet, so that step is skipped). Project: type to filter, ↑/↓ to highlight a match, Enter to pick it or submit the typed text as-is. Tags: comma-separated, replaces the existing set; ↑/↓ highlights a match for the segment you're currently typing (after the last comma) and Tab inserts it, leaving Enter free to submit the whole line whenever you're done. Blank leaves either field unchanged | | d | Delete the highlighted entry (asks for y/n confirmation first) | | u | Undo the most recent delete — recreates the entry (new id) with its original description/project/tags/time. Only one delete deep, and only until the next delete replaces it | | j / k or ↓ / ↑ | Move the highlight up/down the recent-entries list | | / | Filter the recent-entries list by description, project, or tag — matches live as you type. Enter keeps the filter applied after closing the prompt (submit blank to clear it); Esc cancels the edit without changing whatever filter was already active. Filtering never spends API budget — it only searches what's already cached | | r | Force refresh (bypasses the cache TTL and rate-limit budget for one call) | | q | Quit | | Esc Esc | Quit (press twice quickly — works from anywhere, including mid-wizard) |

Shell completion

toggl completion bash --install   # appends to ~/.bashrc
toggl completion zsh --install    # appends to ~/.zshrc

Idempotent — running it again just reports it's already set up instead of adding a duplicate line. Restart your shell (or source the rc file) afterwards. Prefer to do it by hand, or use a different rc file? Add one of these yourself instead:

eval "$(toggl completion bash)"   # ~/.bashrc
eval "$(toggl completion zsh)"    # ~/.zshrc, before compinit if you call it explicitly

Completes subcommand names, plus report's today/week argument and completion's own bash/zsh argument. It doesn't complete things that would require a network call (project/entry names) — see Caching & rate limits for why that's deliberately avoided outside the app's own cache/budget accounting.

Update notifications

After a one-shot command finishes (not the TUI, and only in an interactive terminal), togglr checks npm for a newer published version — at most once a day — and prints a short reminder if one exists. This never touches your Toggl API rate-limit budget; it's a separate, throttled check against the npm registry.

Caching & rate limits

| Cache file | TTL | Notes | |---|---|---| | ~/.cache/togglr/projects.json | 6 hours | | | ~/.cache/togglr/time_entries.json | 5 minutes | Invalidated after stop | | ~/.cache/togglr/timer.json | — | Local source of truth for the active timer, not TTL-based | | ~/.cache/togglr/rate_limit.json | — | Rolling 1-hour log of API calls, used to enforce the 25/hour budget | | ~/.cache/togglr/update_check.json | 24 hours | Last npm version check, unrelated to the Toggl budget |

Both projects.json and time_entries.json are refreshed together from a single Toggl /me call whenever either goes stale, keeping API usage to a minimum. TTLs are configurable via cacheTtl in ~/.config/togglr/config.json.

Development

npm run typecheck   # tsc --noEmit
npm test            # vitest run
npm run build        # compile to dist/

Architecture

src/
  api/       Thin Toggl API v9 client
  domain/    Pure models and report aggregation, no I/O
  cache/     TTL-aware file cache, rate limiter, and the sole
             read/refresh decision point (cache/sync.ts)
  config/    Token/workspace storage and first-run onboarding
  commands/  One module per CLI command
  tui/       Ink dashboard (presentational + container components)

commands/ and tui/ never call the API client directly for reads — every read goes through cache/sync.ts, which decides whether to serve cache or refresh. Mutating commands (start, stop, continue) always call the API directly and are never throttled, but their calls are still recorded against the rate-limit budget.