dev-hub: Mission Control for Claude Across My Repos
Published:
I have a handful of research and side-project repos that all need the same kind of upkeep: tests that were never written, dated packaging, automated checks (continuous integration, or CI) that don’t exist, outdated READMEs. None of it is hard, but it never makes it to the top of the list; a lot of development stops once things work, as in plenty of research codebases.
This is why I built dev-hub, a small private GitHub repo that acts as mission control for Claude to operate and work on small tasks across GitHub repos.
dev-hub holds no code. It holds a task board listing a set of tasks for every repo I track, a playbook of rules every Claude session follows to complete tasks and integrate changes into the GitHub repos, and a record of what each task did. I name a task, Claude works it on a branch, and I get back a pull request to review. My own dev-hub is private, but there’s a generic copy, dev-hub-template (MIT), that you can fill in with your own repos.
The first half of this post gets you started and covers the key pieces. Everything under Going further is optional reading and dives deeper into how dev-hub works.
Try it
Getting started takes four steps:
- Create a private repo from dev-hub-template. It holds your plans and tasks, so keeping it private is a good idea, though not necessary.
- Open it in Claude Code, or in claude.ai/code with the repo attached, and run
add-repo <owner>/<repo>for each project you want tracked.- Using claude.ai/code or a Claude Project? Connect GitHub in claude.ai (Settings → Connectors) and install the Claude GitHub App on GitHub. Installing it on all your repos is easiest, but you can select only the ones you add to
dev-hub.
- Using claude.ai/code or a Claude Project? Connect GitHub in claude.ai (Settings → Connectors) and install the Claude GitHub App on GitHub. Installing it on all your repos is easiest, but you can select only the ones you add to
- Run
refresh-overview, thenscan-repo <name>to get a first task table for that repo. - Pick a small (S) task and run
plan-task <ID>.
A first session with one of my repos looked like this:
add-repo kylermurphy/gmag # track the repo; it gets the prefix GMAG-
scan-repo gmag # Claude proposes GMAG-1 … GMAG-5; I merge the board PR
plan-task GMAG-1 # we agree on the approach; the plan is saved; work starts
# Claude opens a gmag PR; I review and merge it
mark-done GMAG-1 # mark it done on the task board, log finalized, learnings saved
The template’s examples/ folder walks through the same steps on an imaginary repo, from an empty board to a finished task.
How it works
GitHub is the hub
Claude can work on code in a few places: Claude Code on my machine, Claude Code on the web or app (good for running from a mobile device), or a claude.ai Project (good if you don’t have access to Claude Code). These can’t see each other, and a chat’s context is gone once it ends. GitHub is the one thing they can all reach, so all state lives there. Any session can pick up any task from its board row and log alone.
Phone / browser ──► claude.ai/code ─┐
├──► GitHub ◄── I review + merge
My computer ─────► Claude Code ─────┘ │
│
dev-hub (board, rules, logs, per-repo instructions) ─ read by every session
What’s in the repo
Everything is plain markdown:
| File | What it is |
|---|---|
TASK_BOARD.md | The backlog: tracked repos, an overview of each, and one task table per repo. |
CLAUDE.md | The rules for Claude, including the full task protocol. |
COMMANDS.md | The spec for every command. |
TASK_LOG.md + log/ | An index of tasks, plus one write-up per task: plan, next step, what was done, how it was tested. |
repos/<name>/CLAUDE.md | A master instruction file per project: how to build and test it, and what earlier tasks learned (a bit of memory for the agents). |
Plan → Launch → Track → Land
Every task follows the same loop:
- Plan.
scan-reposets each task’s definition of done;plan(a dry run) orplan-task(saved to the log) agrees a fuller plan before any heavy work. - Launch. Name the task ID, and Claude works it on its own branch (
task/<ID>-<slug>) in the target repo. It never commits tomain. - Track. The first commit copies the repo’s master
CLAUDE.mdin from dev-hub. Every step after that is logged, so a stopped task can resume where it left off. Land. Claude opens a draft PR. I review and merge it, and
mark-donecloses the task on the board in thedev-hubrepo.mark-donealso saves anything the task learned (a build quirk, a gotcha) into the repo’s masterCLAUDE.md. That’s dev-hub’s light version of a memory bank: the next task in that repo reads it before it starts. More in Memory.
The definition of done is written on the task’s board row before work starts, so “finished” is never a judgment call made at the end. A task’s status is one of Todo, WIP, Blocked (waiting on a decision from me), Usage-stopped (paused by usage limits) or Done. Only mark-done sets Done, and only after checking that the PR merged.
The commands
Commands are just words typed into a Claude session. There are two groups.
Board maintenance keeps the task board current. Each run is a branch and a PR in dev-hub, with no task log.
| Command | What it does |
|---|---|
add-repo <repo> | Adds a repo to the board and gives it a task-ID prefix (gmag → GMAG-). |
refresh-overview | Rewrites the overview table: commits, last activity, stack, state. |
scan-repo <name> | Reads a repo’s README, tests, CI and TODOs; proposes tasks; writes its master CLAUDE.md. |
new-task <repo> | Adds a task I describe, with the next free ID and a definition of done. |
check-board | Checks that IDs are unique and the board, logs and index agree. |
Task lifecycle commands run the loop above.
| Command | What it does |
|---|---|
plan-task <ID> | Agrees the plan, saves it to the log, sets WIP, then does the work. |
multi-task <ID> <ID> … | Runs up to five small tasks from one repo on one branch and one PR. |
pickup-task <ID> | Resumes a stopped task from its log. |
mark-done <ID> | After the merge: sets Done, finalizes the log, saves learnings. |
Put plan in front of any command (plan scan-repo gmag, plan GMAG-2) for a dry run. Claude does the read-only part and proposes the changes. Nothing is committed until I say “go”.
The task board
Here’s a trimmed copy of my live board, showing just the gmag and smurphs_cooking repos. Each Done task links to its merged PR so you can follow the trail.
Tracked Repos
| Repo | URL | Visibility | Prefix | Tracking | Notes |
|---|---|---|---|---|---|
| gmag | https://github.com/kylermurphy/gmag | public | GMAG- | active | Legacy setup.py |
| smurphs_cooking | https://github.com/kylermurphy/smurphs_cooking | public | COOK- | active | Jekyll recipe site (GitHub Pages) + build.py |
Repo overview
| Repo | Purpose | Commits | Last active | Stack | State |
|---|---|---|---|---|---|
| gmag | Ground-magnetometer data I/O (CARISMA, IMAGE, THEMIS) | 210 | Oct 2025 | Python | Mature · no tests · no CI · setup.py |
| smurphs_cooking | Personal Jekyll recipe site (GitHub Pages) with search, tags, serving scaling | 16 | Aug 2026 | Jekyll + JS (legacy Python build.py) | Live · no CI · no Gemfile · template README |
gmag — GMAG-
| ID | Status | Task | Type | Effort | Definition of done |
|---|---|---|---|---|---|
| GMAG-1 | Done | Migrate setup.py → pyproject.toml and bump status past Alpha | Packaging | S | Modern build metadata; pip install -e . still works |
| GMAG-2 | Todo | Add GitHub Actions CI (import + lint) on Python 3.10–3.12 | CI | S | Workflow runs green on push/PR across the matrix |
| GMAG-3 | Todo | Add a test suite for the loaders (mock the HTTP fetch) | Testing | M | carisma/image/themis loaders covered without hitting the network |
| GMAG-4 | Todo | Replace the wget dependency with requests streaming | Refactor | S | wget dropped from install_requires; downloads still work |
| GMAG-5 | Todo | Confirm the efield module is implemented, tested, and documented | Feature | M | efield derivation covered by a test and a README/example |
smurphs_cooking — COOK-
| ID | Status | Task | Type | Effort | Definition of done |
|---|---|---|---|---|---|
| COOK-1 | Done | Stop publishing non-site files: add build.py, templates/, unparsed.md to _config.yml exclude | Hygiene | S | Built _site/ contains none of those files; recipe pages unchanged |
| COOK-2 | Todo | Decide on one build path: remove the unused Jinja build.py + templates/ or document why they stay | Refactor | S | Only one generator remains, or README explains the second; nothing references a missing requirements.txt |
| COOK-3 | Todo | Add a Gemfile (github-pages gem) so the site builds locally | Build | S | bundle install && bundle exec jekyll build succeeds from a clean clone; README uses it |
| COOK-4 | Todo | Add GitHub Actions CI: Jekyll build + front-matter check on _recipes/*.md | CI | M | Workflow runs on push/PR; fails if the build breaks or a recipe lacks title/date |
| COOK-5 | Todo | Rewrite the README for this site | Docs | S | README describes this repo’s real setup, front-matter keys, and import workflow |
| COOK-6 | Done | Normalize tags to lowercase | Content | S | Every tag in _recipes/ is lowercase; tags page shows no case-duplicate groups |
| COOK-7 | Todo | Convert unparsed.md (Thai Chicken Pad See Ew) into a _recipes/ entry and delete it | Content | S | New recipe page renders with ingredients/instructions; unparsed.md removed |
| COOK-8 | Done | Fix slug typos (egg-roll-ramnen, cheesey-taco-rice) without breaking old links | Content | S | Files renamed; old URLs redirect (e.g. jekyll-redirect-from) or the change is deliberately skipped with a note |
Reading the board
- Tracked Repos is the input. It’s the only table I edit (through
add-repo). Its Prefix column is where task IDs come from, and setting Tracking topausedshelves a repo. - Repo overview is derived.
refresh-overviewregenerates it, so I never edit it. The State column is a quick health check: gmag has no tests and no CI, which is why GMAG-2 and GMAG-3 exist. - Each task has a definition of done and an effort size. Effort (S / M / L) decides which model runs the task and whether it can run unattended.
- Status is the only column that changes as work happens, and only the commands change it.
TASK_LOG.md keeps a running index of every task that’s been started, newest first:
| Date | Task | Repo | Branch | PR / patch | Status |
|---|---|---|---|---|---|
| 2026-09-25 | GMAG-1 | gmag | task/GMAG-1-pyproject | gmag#7 | Done (merged 2026-09-25) |
| 2026-09-25 | COOK-8 | smurphs_cooking | task/COOK-8-fix-slug-typos | smurphs_cooking#3 (from handed-back patch) | Done (PR confirmed 2026-09-25) |
| 2026-09-23 | COOK-1 | smurphs_cooking | task/COOK-1-exclude-nonsite-files | smurphs_cooking#2 | Done (merged 2026-09-24) |
| 2026-09-23 | COOK-6 | smurphs_cooking | task/COOK-6-lowercase-tags | smurphs_cooking#1 | Done (merged 2026-09-23) |
Where it runs
The workflow is documented for several surfaces, but I’ve only run real tasks through the first three. The claude.ai Project handback surface is particularly useful if you don’t have Claude Code. It provides Git-formatted commit patches and zips of the changed files, since a Project can’t push. The workflow is a bit more user intensive, but not by much. You can read more about it here.
| Surface | Status | Good for |
|---|---|---|
| Claude Code on the web (claude.ai/code) | Verified | Most tasks. A cloud session with the target repo and dev-hub attached clones, builds, tests, pushes and opens PRs. |
| Claude Code on my machine | Verified | Everything, using my local git credentials, but only while the machine is on. |
| claude.ai Project in handback mode | Verified | Any device. It can’t push, so it hands back git am patches and PR text for me to commit. Setup is in templates/PROJECT_INSTRUCTIONS.md. |
| Claude desktop app + GitHub MCP server | Not yet tested | Bookkeeping and PRs through the GitHub API. No shell, so no builds or tests. |
| Desktop computer use | Not yet tested | Open question whether it’s useful here at all. |
Some Claude Code on the web sessions are branch-restricted: they can push to only one branch. There, dev-hub’s bookkeeping arrives as a dev-hub PR instead of landing on main, and Claude says so in chat. The task isn’t fully closed until that PR merges too.
Going further (optional)
That’s the core of dev-hub. The sections below cover the features built on top of it, plus one task in detail.
A worked example: GMAG-1
gmag is my ground-magnetometer data package. GMAG-1 was routine upkeep: replace setup.py with a pyproject.toml and move the status past Alpha. The definition of done was “modern build metadata; pip install -e . still works.”
While planning, Claude found a real bug. setup.py declared no package data, so a normal pip install shipped none of the 41 station files in gmag/Stations/, and load_station_coor() quietly returned None. I’d never noticed, because I only use editable installs, which read straight from the source tree. The plan was updated to declare the data files, and the testing proved the fix: a wheel from the old setup.py had 0 station files; the new wheel has all 41 and returns the station row.
It also shows how the workflow handles friction. The first push was refused because the Claude GitHub App wasn’t installed on the gmag repo. Claude handed back a patch and the PR text instead of stalling. Once I installed the app, a “retry” pushed the same branch and opened gmag#7. At mark-done, three learnings went into gmag’s master, so the next gmag task starts knowing them.
Batches
Plenty of tasks are tiny, and one PR each is more overhead than they’re worth. multi-task runs several on one branch with one PR:
multi-task COOK-2 COOK-3 COOK-5
multi-task smurphs_cooking # Claude proposes a batch of the repo's small tasks
All tasks must be in the same repo, with no L tasks and at most five. Each task still keeps its own log and status, and each commit is prefixed with its task ID, so one task can be reviewed or reverted alone. If one task needs a decision from me, it’s reverted and marked Blocked, and the rest ship. A usage stop pauses the whole batch.
Overnight runs
The logging is what lets tasks run while I sleep. OVERNIGHT.md is written around a Claude subscription’s two limits, a 5-hour session window and a weekly cap:
- Only S and M tasks. L tasks likely need planning and decisions from me.
- One task, or one batch, per run. Clearing the whole board in one session is how the weekly cap disappears.
- The cheapest model that works, with no extended thinking or web search unless needed.
- Finish early, so the session window refills before my workday.
The kick-off prompt is short, because the protocol does the rest:
Work on dev-hub task GMAG-2 only (
plan-task GMAG-2, unattended). Follow the task protocol in dev-hub’sCLAUDE.md. Persist the plan tolog/GMAG-2.mdbefore heavy work, commit as you go, and keep Status and Next step current. If a branch or log already exists, usepickup-taskinstead of restarting. On an ambiguous decision, setBlocked; if you stop for budget, setUsage-stopped.
In the morning I review the draft PR and read the log’s Next step. If the run stopped partway, pickup-task GMAG-2 picks up where the task left off, asks for any input it needs, and continues on.
Subagents
Claude Code can hand work to a subagent running a cheaper model. The main session keeps the plan, every judgment call, and all commits and PRs. It delegates only when that’s cheaper than doing the work itself:
| Delegate | Model tier |
|---|---|
| Broad read-only search: find usages, survey a codebase | fast (Haiku) |
| Mechanical fan-out: one identical edit across all masters | fast (Sonnet if it needs care) |
| Run tests, lint or a build and summarize failures | fast (Haiku) |
| Draft tests or docs from a clear spec | mid (Sonnet) |
The plan, design decisions and small jobs stay with the main session. A subagent starts cold, so a handoff costs more than a quick edit.
Memory
I decided against a full “memory bank”. Memory in dev-hub is three light layers:
- Per task: the log.
log/<ID>.mdholds the plan, the Next step and how the work was tested. It stands on its own even if the PR or chat is gone. It’s whatpickup-taskresumes from. - Per repo:
## Learnings. A task writesLearning:lines in its log, andmark-donemoves them into the repo’s masterCLAUDE.md. For example, after GMAG-1, gmag’s master says thatimport gmagneeds~/.gmag/gmagrcwithdata_dirset. The list is capped at about 15 bullets. - For the workflow:
DECISIONS.md. One dated line per workflow decision and why. Claude reads it before changing a rule, so a later session doesn’t undo something decided on purpose. It covers changes to dev-hub itself.
The Learnings are the part I like most. Each task leaves the next one a little better prepared, as a side effect of closing it.
Other coding agents
The template is written for Claude, but it’s all plain markdown. ADAPTING.md has notes, untested so far, on porting it to other agents.

