mothball

Finds everything stranded across your fleet: unpushed work, dead repos, zombie Vercel projects. It never pulls the trigger itself.

A single static binary that sweeps every git repo under your roots and tells you what needs a push, a release, or a delete. It prints the exact command. You run it.

Real output

mothball drift --unpushed --sort unpushed --limit 6 against a fleet of repos.

GROUP          NAME                 BRANCH                    AHEAD  BEHIND  DIRTY  RELEASE        FLAGS
acme           checkout-api         main                      812    340     0      unreleased     unpushed,behind-              scratch-notes        main                      ~276   412     19     unreleased     dirty,unpushed,behind,stashedoss            tiny-metrics         main                      58     91      0      unreleased     unpushed,behindside-projects  pixel-garden         codex/canvas-export-fix   ~31    1204    0      needs-release  unpushed,behind,stashedside-projects  pixel-garden-pr41    detached@8f2a19c          ~31    1204    0      needs-release  unpushed,behind,detached,stashedside-projects  pixel-garden-hotfix  fix/canvas-runtime-linux  ~31    1204    0      needs-release  unpushed,behind,stashed

The ~ prefix marks an estimated ahead count: a branch with no upstream configured, counted against every remote-tracking ref instead of a single upstream.

Three modes

drift

mothball drift --unpushed --dirty

Unpushed work, dirty trees, release state, stashes, and in-progress operations across every local branch.

pixel-garden  main  ~31 ahead  0 dirty  needs-release

archive

mothball archive

GitHub repos with no traffic, no open issues, and no life signal in the configured window. Prints gh repo archive, never runs it.

acme-labs/old-widgets  24mo  gh repo archive acme-labs/old-widgets

purge

mothball purge

Dead Vercel projects across every accessible scope, checked against real custom domains, not just the latest deploy alias.

v0-test  6mo  no domain  vercel project rm v0-test --scope acme-labs

archive and purge candidates

mothball archive reads GitHub traffic and issue signal.mothball purge reads Vercel project and domain state. Neither one executes anything.

archive

OWNER      NAME               PUSHED  COMMAND
acme-labs  old-widgets        24mo    gh repo archive acme-labs/old-widgetsacme-labs  legacy-billing-ui  31mo    gh repo archive acme-labs/legacy-billing-uiacme-labs  internal-cli-v1    18mo    gh repo archive acme-labs/internal-cli-v1acme-labs  hack-week-2022     42mo    gh repo archive acme-labs/hack-week-2022

purge

SCOPE      NAME             LAST-DEPLOY  COMMAND
acme-labs  v0-test          6mo          vercel project rm v0-test --scope acme-labsacme-labs  landing-draft-3  7mo          vercel project rm landing-draft-3 --scope acme-labsacme-labs  demo-onboarding  9mo          vercel project rm demo-onboarding --scope acme-labs

142 repos escaneados, 38 dirty, 17 unpushed, 12 candidatos archive, 21 candidatos purge

Agent-first

Every subcommand supports --json. Stdout is data, stderr is diagnostics. Nothing mixes the two.

{
  "path": "/Users/agent/Programming/acme/checkout-api",
  "group": "acme",
  "name": "checkout-api",
  "branch": "main",
  "upstream": "origin/main",
  "ahead": 812,
  "aheadEstimated": false,
  "behind": 340,
  "staged": 0,
  "unstaged": 0,
  "untracked": 0,
  "conflicts": 0,
  "stashes": 0,
  "lastTag": "v2.4.0",
  "commitsSinceTag": 0,
  "releaseState": "unreleased",
  "flags": ["unpushed", "behind"]
}

Exit codes

0success
1runtime error (git failed, cache write failed)
2invalid arguments (bad flag, bad --since, bad --sort)
3--cached used with no cache on disk yet

Built for collectors

mothball scan                                # pays the git cost once
mothball drift --cached --json --limit 20    # ~30ms, zero git processes

Reads the cache on disk. Safe to call from cron, /morning, or any agent loop, as often as you want.

It never pulls the trigger

mothball reads git and API state and prints recommendations. Every destructive action is a command it prints and you copy. Archiving a repo, deleting a Vercel project: always a human, always by hand.

gh repo archive acme-labs/old-widgets
vercel project rm v0-test --scope acme-labs

Install

Publishing to npm shortly. Once live, this is the install path.

bun add -g @crafter/mothball

or npx @crafter/mothball to run without a global install.

Build from source
git clone https://github.com/crafter-station/mothball
cd mothball
bun install
bun run build:bun

Get the badge

A reusable badge for Crafter Station OSS projects. Static SVG, no tracking pixel.

Crafter Station OSS badge
[![Crafter Station OSS](https://mothball.crafter.run/badge.svg)](https://crafter.run/oss)