Cinder: Tinder for Unfinished Dev Projects

developmentswiftclaude-codeworkflowmacosdesignios-shortcutsautomation

Cinder: Tinder for Unfinished Dev Projects

I have a lot of unfinished projects. Like, embarrassingly many. They live in ~/Local_Dev/projects/ and quietly accumulate heat debt. Some haven’t seen a commit in 400 days.

So I built Cinder — a macOS SwiftUI app that’s literally Tinder for your dead code. Swipe right to reignite. Swipe left to archive. Never think about projects/ as a void again.

The Concept

Cinder scans your projects folder, reads git history for each one, and assigns a heat level:

  • 🔥 Blazing — committed in the last 3 days
  • Hot — last 3–7 days
  • Warm — last 7–30 days
  • ❄️ Cooling — last 30–90 days
  • Cold — last 90–180 days
  • 🪦 Ash — 180+ days. Gone. Dead. It’s just sitting there judging you.

You swipe through a Tinder-style card deck. Each card shows dormant days, tech stack, recent commits. When you swipe right (Reignite), a Claude Code skills launcher opens so you can immediately jump back in with /load, /sprint-plan, /analyze, etc.

The Graveyard Widget

This is the one I actually use. It lives on my macOS desktop and shows:

🪦 GRAVEYARD
   14
   projects
   collecting dust.

The shame text rotates daily. “still there.” “they remember you.” “git log says it all.”

墓地即动力。Shame-based motivation works.

There are 6 widgets total — small (hottest project), medium (heat row), large (full grid), graveyard (ash counter), nudge (daily project tarot card), stats bar (just the numbers).

The Menu Bar Extra

The app lives as a pulsing NSStatusItem in the menu bar. The icon is a small colored square that reflects your hottest project’s heat level. When you have blazing projects it pulses orange. When everything is cold it fades to grey.

Click it → a tray drops down with heat breakdown bars, your top 5 projects, and quick actions. It’s always one click away from the notch area, which is basically the Mac equivalent of what I initially confused for Dynamic Island. (it’s not. it’s just the notch. they’re not the same thing.)

The Git Tree + Cinder Mode

There’s a git tree viewer that renders git log --graph output in a WKWebView. Clean, dark, Menlo font, branch visualization.

But there’s a hidden mode: Cinder Mode. When you toggle the 🔥 button in the toolbar, the commit messages become physically reactive. As you scroll, words in each commit message drift and scatter based on scroll velocity — wider words (measured by @chenglou/pretext, a pure-JS text measurement library published literally yesterday) scatter proportionally further. When you stop scrolling, they spring back.

It’s ridiculous. I love it. It’s technically using Pretext for accurate word width measurement (no DOM reflow) to drive proportional scatter physics. Probably not the intended use case for that library but here we are.

Color Themes

Six themes in settings, using actual color theory:

  • Ember — default, warm analogous fire palette
  • Deep Sea — 2-color cool blue on void black
  • Void — monochromatic violet
  • Matrix — 2-color phosphor green terminal mode
  • Parchment — 3-color warm analogous, candlelight gold on aged ink
  • Forge — 2-color blood red on charcoal, zero mercy

ThemeManager is @Observable, stored in @Environment, persisted to UserDefaults. Each theme includes a full heat spectrum override so even the “blazing” colors stay on-theme.

The API

The whole thing exposes a REST API on port 4242 (embedded NWListener in SwiftUI, plus a Docker Bun server for always-on access). GET endpoints are public for iOS widget polling. POST requires X-Cinder-Key, Bearer token, or ?key= query param.

For discoverability, there’s also /api/llms.txt (full project docs in LLM-friendly plain text) and /api/llms-mini.txt (compact iOS Shortcuts cheatsheet with just the URLs and recipes). The mini version is what I give to LLMs when building new automations on top of the API — first call pulls instructions, rest of the calls do the work.

The API is exposed via Tailscale Funnel on a dedicated port (8445, tailnet-only) so the iPhone gets a trusted Let’s Encrypt cert without touching the three other project tunnels already running on the Mac mini. tailscale serve --bg --https=8445 4242 and done. Doesn’t collide with anything.

The iOS Daily Standup (the part I didn’t plan)

It’s 2:30pm. I’m getting ready. I open my phone and there’s a Tinder meme from a friend about “Tinder for Claude.” I look up and realize: I just built that, but for code.

Then I realized the API could be the backend for an actual daily standup. So in the same afternoon I built “How’s My Cinder” — an iOS Shortcut that runs at sunrise every day:

1. GET https://weixiangs-mac-mini.tail1ef495.ts.net:8445/api/digest
2. Claude (Cloud model): "Summarize this for text-to-speech. Human readable."
3. Speak [Response]  ← yes, it talks to me
4. ChatGPT: "Format for widget display. Max 11 lines. No headers. No meta."
5. Update Draft: [pinned draft in Drafts app]

I wake up and my phone has already spoken my project heat report at sunrise. The Drafts widget on my home screen shows the formatted summary. It looks like this:

Blazing: 4 🔥 · Hot: 19 ☀️ · Warm: 2 · Cold: 9 ❄️
Most Active → Bythewei
Most Urgent → CAR
Hot Projects → Bythewei, Cinder, Fire My Lawyer, Claude Relay...
Summary → 23 projects blazing ✨ 9 getting chilly 🥶
Total Active → 39

The AI also started randomly adding vocabulary words in German, Chinese, and French at the bottom. I didn’t ask for that. I’m keeping it.

The Drafts Widget Layer

This part is non-obvious: I’m using the Drafts app (by Agile Tortoise) as a display surface, not a note-taking app.

Drafts has a widget that shows the full content of one pinned draft on your home screen. It auto-refreshes when the draft content changes. The Update Draft Shortcuts action overwrites a specific draft by UUID with Replace mode — atomic, reliable, no duplicates.

So the pattern is:

  • Shortcut updates Draft UUID abc-123 with the formatted API output
  • Drafts widget on home screen reflects the change within ~15 minutes (iOS widget budget)
  • If I open the Drafts app, it’s instant

The non-obvious part: Drafts isn’t trying to be a widget platform. It just has good Shortcuts integration and a content widget, which accidentally makes it a perfect persistent display layer for automation output. You’re basically using it as a named, updateable string that iOS can display. There’s no equivalent in Reminders, Notes, or anything else that’s this frictionless to update programmatically.

The two-pass AI approach (Claude for speech, ChatGPT for widget) came from a real constraint: text-to-speech wants natural language, but the widget has limited space and can’t handle ASCII decoration. One prompt can’t be optimized for both. Two models, two purposes.

What I Actually Learned

The xcodebuild CLI is completely broken on Xcode beta. You cannot build from terminal right now. You have to press ⌘R in Xcode like it’s 2014. Dockerizing the API was the right call — the API runs independently of whether Xcode is working.

Security scan flagged GitService.swift for command injection — I was interpolating url.path into /bin/zsh -c "git -C \"\(path)\" ...". Replaced the whole thing with Process + array arguments. The Docker TypeScript version already did it right (spawnSync with args array).


Cinder is a personal tool on fire-my-lawyer branch because naming branches is an art form.