
KevTools: finally doing something with a decade of Slack data
For years, a Slack bot named Doorman Mike has been keeping up with everything that happens in a group chat I share with old friends, the 211WestWackerHustlers. Who insulted whom. Every URL anyone posted. Birthdays. Compliments (rarer). All of it piling up in a Postgres database that nobody ever looked at.
KevTools is what happened when I finally decided that data deserved better than a forgotten table. It lives at kevinroberts.dev, and it has since grown past the Slack stuff into a general workspace for dashboards and small utilities: my music listening history, my GitHub stars, the books I read, even a live feed from my personal weather station.
Where it came from
The honest origin story is that the data existed before the idea did. Doorman Mike had been collecting for years. Every so often someone in the group would ask a question like "who has been insulted the most?" and the answer would require me to SSH somewhere and write SQL by hand. That got old.
So the first version of KevTools was just that: a people table with insult and compliment counts, birthdays, and a login screen so the group's business stayed in the group. It shipped at the end of May 2026. Six weeks and about 350 commits later it had turned into something much bigger, which tells you how these projects go once you have a comfortable place to put things.
That is the real lesson of the origin, I think. The hard part was never any individual dashboard. It was having a deployed, authenticated, styled app sitting there waiting, so that "I wonder what my listening trends look like" becomes an evening of work instead of a new project with a new repo and a new round of yak shaving.
What's in it
A quick tour of what's there today:
- Slack dashboards. People stats from the Doorman bot, summaries of every URL the group has shared, blocked hosts, and a small admin area for managing the bot's scrapers.
- Music. Listening stats pulled from Last.fm and Spotify, with trend charts you can drill into by year and month. A cron job on the server polls Spotify hourly for recent plays.
- GitHub stars. Every repo I've starred, synced daily and browsable by topic. Stars I remove get soft deleted rather than wiped, because I have unstarred things before and regretted losing the trail.
- Books. A reading dashboard fed by Hardcover.
- Weather. Current conditions from my personal weather station, polled hourly from the Wunderground API, with a time slider to scrub back through the day and history charts for temperature, dew point, wind, and precipitation.
- Odds and ends. A dictionary, a whois lookup, a file browser for old Slack uploads.
Under the hood it's TanStack Start (React 19 with SSR) running on Cloudflare Workers, with Clerk for auth and Postgres reached through Cloudflare Hyperdrive. Charts are ApexCharts, styling is Tailwind v4. There's no ORM, just raw SQL behind a small helper that opens a connection, runs a query, and closes it. That sounds wasteful until you remember Hyperdrive is pooling connections upstream, at which point it becomes the simplest correct thing.
Lessons learned
Write the spec before the code, especially when working with Claude. Most features in KevTools were built with heavy help from Claude Code, and the single biggest quality lever was not the model. It was the workflow: brainstorm the feature, write a design spec, turn it into an implementation plan, then build it test-first. Those specs and plans are checked into the repo, nineteen of them so far. When I skipped that process the results were fine. When I followed it the results were right, and the difference compounds over six weeks.
Sync jobs need to be boring. Everything that pulls from an external API follows the same rules now: skip the fetch if the data is fresh, upsert instead of insert, never hard delete, and keep a fallback cache for when the upstream is down or rate limiting you. Last.fm taught me the fallback cache lesson personally. Each rule exists because I got burned without it.
The edge runtime will bend your habits, mostly for the better. Cloudflare Workers means no long-lived connections, no process.env, and secrets that flow through typed bindings. It was annoying for about a week. Then the constraints started producing better code than my habits would have, and now the hourly cron that drives all the syncing feels like the most solid part of the system.
Let the tooling churn. In six weeks the project went from Biome to a unified Vite-based toolchain with oxlint and oxfmt, and onto the TypeScript 7 RC. On a team I would pace that differently. On a personal project, migrating early is half the fun, and this codebase is small enough that a bad bet costs an afternoon.
A tribute is a fine reason to build software. The about page calls KevTools a tribute to Doorman Mike, the best doorman to ever exist, and I stand by that being as good a reason as any. Not every project needs a business case. Some projects just need a group chat, a bot with a long memory, and someone willing to put a UI on it.