DEV Community

Cover image for I wanted a task tracker that gets out of my way and remembers what I shipped. So I built shipflow
Arya Gorjipour
Arya Gorjipour

Posted on • Originally published at github.com

I wanted a task tracker that gets out of my way and remembers what I shipped. So I built shipflow

It's Friday afternoon. Your async standup update is due, and the question is the same one it's always been: what did you actually get done this week?

Your options:

  • Open Linear / Notion / Todoist and scroll a board that's 40% stale, 30% aspirational, and 30% things you finished weeks ago but never dragged into the Done column.
  • Scroll git log --oneline and try to reverse-engineer a week of intent from commit messages you wrote for a completely different audience.
  • Stare at the ceiling and type "worked on the auth refactor, some bug fixes" for the third week running.

All three are bad. The first makes you maintain a second job to feed a tool. The second has the data but none of the framing — commits are about code, not intentions. The third is a quiet lie you tell because reconstructing the truth costs more than it's worth.

shipflow is what sits in the gap. It's a single Rust binary that lives in your terminal. You add a one-line intention, you mark it done when it ships, and on Friday you run one command that tells you exactly what you shipped — in a format you can paste straight into your standup. No board to maintain. No cloud. No due dates nagging you. Just cargo install shipflow.

$ shipflow add "Ship auth refactor" --tags rust,auth
Added 01JABC12 — Ship auth refactor

$ shipflow done auth
Done 01JABC12 — Ship auth refactor
  linked: a1b2c3d Fix OAuth callback redirect

$ shipflow report week --format md
# What I shipped — this week
...
Enter fullscreen mode Exit fullscreen mode

That's basically the whole product surface.

Why I quit my task manager

I was a Todoist/Notion/Linear user for years, and I kept hitting the same wall: those tools are project management software wearing a personal-productivity costume. They want due dates, priorities, sub-tasks, labels, recurring schedules, automations, and a board you tend like a garden. None of that is free — the cost is that maintaining the tool becomes its own task. And the punchline is that after all that ceremony, none of them could cleanly answer the only question I cared about at the end of the week: what did I ship?

The data was technically in there, buried under filters and views. But "the data is technically in there" is exactly the failure mode. I'd context-switch out of my terminal, find the right project, drag a card, set a status, and lose the thread of what I was actually doing. The tool was pulling me out of flow to feed itself.

The opposite extreme is no better. A TODO.md and a scattering of // TODO comments have zero memory and zero reflection — they tell you what's left, never what's done. They rot. They never produce a Friday summary.

shipflow is the middle I wanted: fast enough to capture an intention without leaving the keyboard, with just enough memory to tell me what I finished when I ask. It tracks two things and refuses to track a third. Intentions (what I mean to ship) and shipped work (what I actually did). It deliberately has no due dates, no priorities, no reminders, no recurring tasks, no sub-tasks. That's not a roadmap gap — it's the entire point.

What I actually get out of it

Two things, every week.

The Friday report is the whole reason this exists. I run one command and get a clean, dated summary of everything I shipped, formatted as Markdown:

$ shipflow report week --format md
Enter fullscreen mode Exit fullscreen mode
# What I shipped — this week

_Generated 2026-06-27 16:41 UTC_

## Summary

- **Shipped:** 5
- **Linked commits:** 4
- **Focus areas:** `rust` (3), `cli` (2), `auth` (1)

## Shipped

### Ship auth refactor (2026-06-27)

**Tags:** `rust` `auth`

**Commit:** `a1b2c3d` — Fix OAuth callback redirect

### Fix parser edge case on empty input (2026-06-25)

**Tags:** `rust` `cli`

**Commit:** `9f0e1d2` — Handle zero-length token stream
Enter fullscreen mode Exit fullscreen mode

That output goes straight into my standup channel. No reconstructing, no embellishing, no staring at the ceiling. The week wrote itself down as it happened, and the report just reads it back. There's a plain-text table version too (report week with no --format) for when I just want to look at it in the terminal.

The second payoff is the one I didn't expect: it kills the "I did nothing this week" feeling. Every developer knows that Friday sensation — the vague conviction that you spun your wheels for five days. It's almost always false, and you can't argue yourself out of it, because the feeling isn't made of evidence. shipflow hands you the evidence. Five shipped tasks, four of them tied to real commits, three of them in the area you said you'd focus on. There's a name for this — the progress principle: the strongest day-to-day motivator at work is the sense of making progress, and we are genuinely bad at noticing our own. A report week is a cheap, honest correction to that blind spot. It changed how the end of my week feels more than any productivity app ever did, and it did it by being less of an app.

Time periods are today, week, month, and all, so the same trick works for a monthly retro or a year-in-review.

How it actually works

The full loop is four verbs.

# Inside a git repo — tasks live in .shipflow/tasks.json
shipflow add "Fix parser edge case" --tags rust,cli
shipflow add "Write weekly reflection" --note "Keep it short"

shipflow list --status open          # what's still on the hook
shipflow done parser                 # mark done; interactive commit linking when TTY
shipflow report week                 # the payoff

shipflow status                      # counts + current git context
shipflow board                       # optional kanban TUI (Open / Done)
Enter fullscreen mode Exit fullscreen mode

Two details worth calling out, because they're the difference between a tool I tolerate and one I actually reach for:

You don't need IDs. shipflow done parser works because shipflow resolves a query against both ULID prefixes and partial titles. I added the task as "Fix parser edge case" and I close it by typing the word I remember. If the query is ambiguous it tells me which tasks matched instead of guessing.

done links the commit that did the work. When you're in a git repo and on a TTY, marking a task done shows you your recent commits and lets you attach one:

Recent commits:
  0) Skip
  1) a1b2c3d Fix OAuth callback redirect
  2) 9f0e1d2 Handle zero-length token stream
  3) 7c4b8a0 Bump clap to 4.5
Link commit [0-5]:
Enter fullscreen mode Exit fullscreen mode

Pick one and the report now ties your stated intention to the actual SHA that shipped it. For scripts and hooks there's --commit <sha> and --no-link, so nothing breaks when there's no terminal attached.

Who this is for (and who it isn't)

Good fit:

  • Solo developers who live in the terminal and want intention tracking without leaving it.
  • Anyone who has to produce a weekly status, standup update, or async retro and resents rebuilding it from memory.
  • People who want per-project history — what got shipped in this repo — without a central account.
  • Anyone allergic to maintaining a board as a second job.

Bad fit (and I'll be explicit about this):

  • Teams that need a shared board, assignment, or real-time collaboration. shipflow is single-user and local-first by design. You can commit .shipflow/tasks.json to make shipped work team-visible in a repo, but there's no sync, no assignment, no notifications, no presence.
  • Anyone who needs deadlines. There are no due dates, no reminders, no recurring tasks. If your work is deadline-driven, this is a real gap, not an oversight — it's the wrong tool and I'd rather tell you now.
  • People who want priorities, sub-tasks, or dependencies. Not here, not planned. shipflow is intention + reflection, not a planner.
  • People who want a GUI or a mobile app. It's a CLI with an optional terminal kanban view. That's the entire interface.

The scope is deliberately small. If you've ever closed a task manager thinking "I just wanted a notepad that remembers what I finished," that's the exact size of this thing.

Under the hood

For the readers who like the implementation decisions — these are all documented as ADRs in the repo, but the short version:

  • Storage is a pretty-printed JSON file, not a database. tasks.json, serialized with serde_json pretty printing. The reasoning: universal tooling (jq, any editor), predictable git diffs, and zero learning curve. You can read your own data without shipflow installed. A schema version field is baked in so future formats can migrate cleanly.

  • Writes are atomic. Every mutation writes to a temp file and renames over the original (fs::rename), with a Windows-specific path because Windows won't rename over an existing file the way Unix does. A killed process mid-write doesn't corrupt your task list.

  • Git is shelled out, not linked. It calls git rev-parse, git log, and git cat-file as subprocesses rather than pulling in libgit2. For what shipflow needs — repo root, current branch, recent commits, commit validation — subprocess git is plenty, and it keeps the binary small and the compile fast. If git isn't installed, shipflow degrades gracefully to no-git mode instead of failing.

  • Task IDs are ULIDs. Sortable, collision-resistant, and merge-friendly. That last property matters: if two branches each add a task to a per-repo tasks.json, ULIDs don't collide the way sequential integers would, so the merge is sane.

  • Per-repo by default, global as a fallback. Inside a git repo, tasks live in <repo>/.shipflow/tasks.json. Outside one, they fall back to your XDG config dir (via the directories crate). --global forces the global store from anywhere. Want your intentions private? echo ".shipflow/" >> .gitignore. Want shipped work visible to the team? Commit it. Your call, per repo.

  • The kanban TUI is behind a default-on feature flag. board is built on ratatui, gated behind a tui feature that's on by default. cargo install shipflow --no-default-features gives you a leaner binary with just the CLI if you never want the board.

  • Boring-on-purpose quality bar. Clippy is configured to deny unwrap_used and expect_used — no panics smuggled in through lazy error handling. Errors are typed with thiserror and printed with owo-colors, which respects NO_COLOR and CLICOLOR and turns itself off when stdout isn't a TTY. Logging is silent unless you set RUST_LOG. Edition 2024, MSRV 1.88.

Tradeoffs worth being honest about

A few decisions have real downsides, and you should know them before you commit:

It's a whole-file JSON rewrite on every mutation. Add a task, the entire tasks.json is re-serialized and atomically rewritten; reads load the whole file into memory. For personal task lists — hundreds, low thousands of entries — this is invisibly fast and the simplicity is worth it. If you somehow accumulate hundreds of thousands of tasks, this is the wrong design and you'll feel it. Personal task lists don't reach that scale, which is exactly why JSON was the right call here and would be the wrong call for, say, a log indexer.

report week and report month are rolling windows, not calendar-aligned. "This week" means the last 7 days from right now, and "this month" means the last 30 — they don't reset on Monday or on the 1st. (today is calendar-aligned to local midnight.) For a standup this is usually what you want, but if you expect a Monday-to-Sunday boundary, it'll surprise you. Honest sharp edge.

There's no edit and no reopen yet. You can add and done, but you can't currently rename a task, retag it, or move a done task back to open. If you fat-finger a title, today the fix is editing tasks.json directly (which, to be fair, is exactly why it's human-readable JSON). These are the two gaps I feel most often myself.

Local-first means no sync. Repo-mode tasks travel with the repo if you commit them; global tasks don't travel at all. If you work across a desktop and a laptop and want one unified list, shipflow won't give you that out of the box.

Install and try it

Linux / macOS:

curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/Aryagorjipour/shipflow/releases/latest/download/shipflow-installer.sh | sh
Enter fullscreen mode Exit fullscreen mode

Windows (PowerShell):

irm https://github.com/Aryagorjipour/shipflow/releases/latest/download/shipflow-installer.ps1 | iex
Enter fullscreen mode Exit fullscreen mode

From crates.io:

cargo install shipflow
# or, if you have it:
cargo binstall shipflow
Enter fullscreen mode Exit fullscreen mode

Minimal build without the TUI:

cargo install shipflow --no-default-features
Enter fullscreen mode Exit fullscreen mode

Then the thirty-second tour:

shipflow add "Try shipflow" --tags meta
shipflow list
shipflow done try
shipflow report week --format md
Enter fullscreen mode Exit fullscreen mode

Generate shell completions while you're at it:

shipflow completions fish > ~/.config/fish/completions/shipflow.fish
shipflow completions zsh  > ~/.zfunc/_shipflow
shipflow completions bash > ~/.local/share/bash-completion/completions/shipflow
Enter fullscreen mode Exit fullscreen mode

MSRV is Rust 1.88, edition 2024, dual-licensed MIT OR Apache-2.0.

Where it's thin (PRs welcome)

shipflow is young — v0.1.4 as I write this — and I've kept the scope tight on purpose. But there are a handful of genuine gaps where contributions would land cleanly, all of them well-defined:

  • edit and reopen. Rename/retag a task, and move a done task back to open. The two I miss most. The storage layer already round-trips tasks by ID, so this is mostly a command and a TaskStore method each.
  • Calendar-aligned report windows. An option to make week mean Monday-to-now instead of a rolling 7 days. The period logic lives in one small enum in report.rs.
  • A JSON report format. report does text and md today; a --format json would make it pipeable into other tooling the way jq lovers expect.
  • Linking more than one commit to a task, or auto-suggesting by branch name.

The repo has CI, integration tests against the real binary (assert_cmd), ADRs for the design decisions, and an issue/PR template. If you pick something up, the contribution path is documented.

A note on context

I built shipflow because I quit a tool that wanted to run my life when all I wanted was to remember what I'd done. I dogfood it daily now, and the part that genuinely stuck wasn't the adding of tasks — every tool does that — it was the Friday report. Closing the week with concrete evidence of what shipped, instead of a vague feeling that I'd been busy, turned out to matter more than any feature I'd been paying for.

It's open source because if you've felt that same Friday dread, you might as well have the antidote too.

Links

About

Arya Gorjipour — backend-focused engineer (.NET, Go, Rust), based in Tehran. Maintainer of logdive and shipflow.

Issues, bug reports, and pull requests welcome. If shipflow ends up writing your standup for you, I'd like to hear about it.

Top comments (0)