keepkit is a lightweight TUI for tracking versions of your favorite tools.
It keeps your kit in one list: installed and latest versions side by side, repository
cards, notes and tags — and updates an outdated tool right from the interface.
Pure TUI, no subcommands; the only flags are --version and --help.
- Key features
- Installation
- Usage
- Updating tools
- Updating keepkit itself
- GitHub API and token
- Data storage
- Architecture
- Stack
- Contributing
- License
- Track your tools — add by GitHub URL or short name, cycle statuses
(
active/trying/inactive), keep a note and one tag per tool; rename a tool when the binary name differs from the repo name (e.g.claude-code→claude) - Versions at a glance — every row carries the installed version in its own
right-hand column (detected locally, with Homebrew and cargo fallbacks for tools
that won't answer
--version); the latest release comes from GitHub. Outdated tools are marked↑, gathered at the top of the list, and counted in the panel title and the status bar - Update from inside the TUI —
enteron the card detects the package manager (brew / go / cargo / pipx / uv / pnpm / bun / npm, orupdate_cmdfrommeta.yaml), asks for confirmation and streams the command output into panel[3]in real time - Self-update — when a newer keepkit release exists, the status bar offers it; after the update one more key restarts keepkit in place, in the same terminal tab
- Docs panel — panel
[3]switches between the rendered repository README,--helpoutput and themanpage by hotkeys; in--help/manmodej/kwalk flags and subcommands with the current entry spotlighted. Every README is rendered in one house style: badges, logos, HTML wrappers, unclickable link URLs, the pictographic emoji a terminal font cannot draw and the title page the card already shows are stripped, and the headings follow keepkit's own palette — code samples are left exactly as written - Clickable card — the repository and release links on the tool card open in the browser by mouse click, or by hotkeys for the repo and changelog pages
- Language stack — the card names a repository's languages with their shares and draws them as a proportional band in GitHub's own per-language colors
- Tags and grouping — one tag per tool;
spaceregroups the flat list under section headers, led by everything with a pending update, and back - Run tools — launch any tracked tool in a new terminal tab (tmux / iTerm2 / kitty / WezTerm / Terminal.app) or in the current window, without leaving keepkit
- Search —
/filters by name and tag with match highlighting and anN/Mcounter - GitHub API token — a quota gauge in the status bar, plus token management in
the
aoverlay, lifts the anonymous 60 requests/hour to 5000 - Session error log — errors (and only errors) are journaled to a per-session file, so a misbehaving session can be researched after the fact; no errors — no file
- Mouse support — scrolling, panel focus, selection and card links all respond to the mouse
The status bar carries the keys that mean the same thing in every panel, each
panel's own actions sit in its footer, and ? opens the full hotkeys overlay —
every keybinding, grouped by panel. That is all you need to learn. Its left corner
names the build you are running (keepkit v0.1.0), marked with the same ↑ an
outdated tool row carries when a newer keepkit release is waiting; the quota gauge
sits in the right corner.
brew install stanlyzoolo/apps/keepkitOr tap once and install by name:
brew tap stanlyzoolo/apps
brew install keepkitUpgrade later with brew upgrade keepkit.
Requires Go 1.25+:
go install github.com/stanlyzoolo/keepkit@latestThe binary lands in ~/go/bin/keepkit (make sure ~/go/bin is on your PATH).
Archives for macOS, Linux and Windows (amd64 / arm64) are attached to every
GitHub release. Unpack and
put keepkit on your PATH.
git clone https://github.com/stanlyzoolo/keepkit
cd keepkit
go install .Note: a build from a working copy is a dev build — the self-update check is off by design.
Run keepkit — a three-panel interface opens:
[1] Tools— the tracker list, each row carrying its installed version: search, tag grouping, track / untrack / rename, andenterto run the selected tool.[2] Brief— the tool card: the tool's name and repo, its tagline, then a metrics strip (installed / latest / maintenance / stars) and a line carrying languages, status, tag and note. From hereenterinstalls a pending release, the repo or changelog opens in the browser, and the card data is force-refreshed.[3] Readme / Help / Man— the docs panel: the rendered repository README (the default,R), the tool's--helpoutput (H) or itsmanpage (M) — the three sources are capitals, so none collides withrrefresh ormrename. A README is cleaned up before rendering — badges, logos, HTML and pictographic emoji go, link text stays without its URL, the title (and a slogan under it that only repeats the card's own) is dropped so the panel opens on the first sentence that says something new, and code blocks are untouched. While an update runs, this panel shows its live log instead.
Focus moves with ← / → or the digits 1 / 2 / 3 (each panel's number is in
its title, and the focused panel is marked with ▸ as well as by color). The status
bar carries the six keys that do the same thing in every focus — t track, u
untrack, m rename, a api, ? keys, q quit — centered between the running
version on the left and the quota gauge on the right. Everything panel-local sits in
that panel's own footer: / filter, enter run and space group in [1], the
card's actions in [2]. Press ? any time for the full hotkeys overlay, grouped by
panel.
When you enter a GitHub URL (https://github.com/owner/repo, with .git, without a
scheme, or in SSH form git@github.com:owner/repo.git), keepkit puts the short tool
name into name and the normalized github.com/owner/repo into the github field.
A new tool gets the trying status.
When the installed version lags behind the latest release (the ↑ marker), press
enter on the tool card. keepkit detects the package manager the binary was
installed with:
brew— a/Cellar/<formula>/…path →brew upgrade <formula>;go— buildinfo (go version -m) with apathfield →go install <module>@latest;cargo— a binary in~/.cargo/bin→cargo install <crate>;pipx— a venv in~/.local/pipx/venvs/<pkg>/→pipx upgrade <pkg>;uv— a tool under$UV_TOOL_DIR(default~/.local/share/uv/tools/<pkg>/) →uv tool upgrade <pkg>;pnpm— a global under$PNPM_HOME(default~/Library/pnpmon macOS,~/.local/share/pnpmon Linux) →pnpm add -g <pkg>;bun— a global under$BUN_INSTALL(default~/.bun) →bun add -g <pkg>;npm— a globalnode_modules/<pkg>→npm install -g <pkg>.
pnpm and bun are checked before npm on purpose: both keep their globals behind
node_modules paths, so npm would otherwise claim them and offer
npm install -g <pkg> — a duplicate copy under npm's prefix while the original keeps
shadowing it on PATH. add -g rather than update -g for both, because update
respects the range recorded at install time and can quietly refuse the major bump the
card is offering.
If the binary cannot be attributed to any of those — or the tool has no binary of
its own on PATH at all — one more check runs before giving up: a Homebrew keg or
cask named after the tool → brew upgrade <name>. That covers formulae whose
binaries are named differently (rust installs rustc and cargo, so there is no
rust binary to detect from) and cask apps whose executable lives inside the .app
bundle, where the path itself names no manager.
Two limits are worth knowing. Detection for uv, pnpm, bun and npm reads path
conventions, so on Windows their bins are .cmd shims keepkit does not parse —
a pre-existing npm limitation the three new managers inherit; on macOS and Linux
pnpm's shims are #!/bin/sh scripts, which keepkit does read. And a manager's own
binary is out of scope: bun upgrade, pnpm self-update and friends are not offered,
those tools update themselves. A layout no check recognises normally degrades to the
same update_cmd hint as an unknown manager.
One case does not, and it is worth knowing: pnpm and bun globals sit behind
node_modules paths, so if keepkit cannot work out where their root is, the path
still looks like an ordinary npm install and the offer becomes
npm install -g <package>. Running it would install a second copy under npm's
prefix while the original keeps shadowing it on PATH. keepkit reads $PNPM_HOME
and $BUN_INSTALL (and the platform defaults) and expands symlinks in them, so this
only bites when the root is somewhere the environment does not name — a store moved
by a config file, or a session started without your shell profile. Export the
variable, or set update_cmd on the tool, and the right manager is picked.
The command is shown in the status bar for confirmation; its output streams into
panel [3] Update in real time and the TUI stays responsive. After a successful
update the version is re-detected and the ↑ marker disappears. One update runs at
a time; a command gets 10 minutes (a sudo password prompt inside it fails fast
instead of hanging).
If the manager cannot be detected (manual install), keepkit suggests setting the
update_cmd field or updating manually. update_cmd in meta.yaml always takes
precedence over auto-detection and runs via sh -c (pipes and && are fine):
- name: mytool
github: github.com/owner/mytool
update_cmd: mytool self-updatekeepkit watches its own releases too. On startup it checks the latest one — a single request, cached for 24 hours; on a build from a working copy the feature is off entirely. When a newer version exists, the status bar shows a notice:
keepkit v0.5.0 available — U update X dismiss
U updates through the same pipeline as any tool — manager detection, confirmation,
live log in panel [3] (visible whichever tool is selected, even with an empty
tracker). X folds the notice into a compact U update cell beside the version in the
bar's right corner, where U keeps working; nothing is written to disk, so a dismissed notice returns on the next
launch. After a successful update the bar reads keepkit updated — U restart:
U replaces the running process with the new binary — same terminal tab, same tmux
pane, same arguments. On Windows there is no in-place restart: keepkit prints
keepkit updated — run keepkit again and exits.
keepkit does not need to be in your own tracker for any of this — the check is built
in. If it is tracked, its update_cmd governs the self-update exactly as it
governs enter on that row, and the release data is shared with its card. Good to know:
- One update at a time: while any update runs, both
Uandenteransweranother update is runninginstead of starting a second one. - A Homebrew formula can lag behind the GitHub release: after updating, the installed version may still be older than the latest tag and the notice comes back. That is an honest reflection of the state, not a bug.
- If the release check fails (no network, exhausted quota), there is simply no notice — everything else works as before.
- If the update fails, the bar says
update failed — see [3]and the reason stays in panel[3]. - The update targets the
keepkiton yourPATH. If you launched a copy by path (./keepkit) while a different one is installed, the installed one is what gets updated — and the restart brings back the copy you launched, so the notice reappears.
keepkit fetches releases and repository cards through the GitHub REST API. Without a
token the limit is 60 requests per hour per IP, with a token — 5000. Each
tool with a github field costs 3 requests on startup, plus one more when you open
its README in panel [3]; a cold start with a large list and no token can hit the
limit — cards stay empty until the window resets. The keepkit release check adds one
more request per 24 hours (none on a dev build).
Quota usage sits in the right corner of the status bar (api ▮▮░░░░░░░░░░ 12/60) for
the whole session once the numbers are known, and the fill turns red as the window
runs out. It is also the only visible sign that keepkit has an API surface at all, so
hiding it at rest hid the overlay with it; on a narrow terminal it is the first thing
the corner drops. The a key opens the API status overlay: token source, quota usage with a warning icon
and the reset time; right in the overlay a token can be entered (validated before it
is saved), removed or the numbers refreshed.
The token source follows environment precedence: the GITHUB_TOKEN variable always
wins over the file. A token entered in the TUI is stored in ~/.config/keepkit/token
with 0600 permissions; an environment token is never written to disk. When the
quota is exhausted, already-loaded cards are not erased, and a card with no data
shows the rate limited — press L hint.
The tool list lives in ~/.config/keepkit/meta.yaml — one entry per tool (name,
status, added, optionally tags, note, github, update_cmd). The file is
fully managed from the TUI; editing it by hand is not required but safe — writes are
atomic. tags stays a list in the file format, but a tool has one tag: a longer
legacy list is read as its first entry, and the file as it was before that migration
is kept as meta.yaml.bak.
| What | Where |
|---|---|
| Tracker metadata | ~/.config/keepkit/meta.yaml |
| Version, README and self-check cache (24h TTL each) | ~/.config/keepkit/cache.json |
GitHub token (0600) |
~/.config/keepkit/token |
| Session error log | ~/.config/keepkit/logs/keepkit-<timestamp>.log |
| Copy of the tracker before the one-tag migration | ~/.config/keepkit/meta.yaml.bak |
The paths above are the macOS and Linux locations ($XDG_CONFIG_HOME if set,
otherwise ~/.config). On Windows the base directory is %AppData%\keepkit\
instead. A config directory left by an earlier version under the previous name
(keeptui) is picked up and renamed automatically on first launch.
The error log is created lazily — only on the first error. A session with no errors leaves no file at all, so the presence of a file is itself the signal. The 20 most recent logs are kept.
How the code is organized — the package graph, data flow, TUI state machine, subprocess sandbox — is described in ARCHITECTURE.md.
- Bubble Tea — TUI framework
- Bubbles — text input, viewport, spinner
- Lip Gloss — styling
- Glamour — markdown rendering for the README panel
- goldmark-emoji — GitHub's
:shortcode:dictionary, used to strip them from a README - x/ansi — stripping escape sequences from captured tool output
- termenv — terminal color-profile detection
- go-runewidth — glyph width measurement
- golang.org/x/mod/semver — version comparison
- gopkg.in/yaml.v3 — reading/writing
meta.yaml
Bug reports and pull requests are welcome. Before submitting, run
go test -race ./... and go vet ./... — CI checks the same.

![in-TUI update — the card shows installed vs latest, enter detects the manager, the log streams into panel [3], refresh confirms the new version](/stanlyzoolo/keepkit/raw/main/demo/update.gif)