From 69 Repos to 19 Keeps: Auditing a Decade of GitHub Sprawl with an AI Agent

I’ve been on GitHub since 2013. By April this year my personal account had 69 repos. Some were real projects. Some were tutorials I cloned and forgot. Some were the same idea I’d attempted three times in three different frameworks. Some were 0 bytes. I had no idea which was which without opening each one and reading it.
I’d been telling myself for years I’d “do a cleanup pass.” Then I built an AI plugin and did it in an afternoon.
This post is about that plugin — what it does, why I built it as a Cowork skill instead of a plain script, and what 69 repos of accumulated sprawl actually looks like when you put real numbers on it.
The Problem: Sprawl Is Invisible Until You Count It
Personal GitHub accounts decay in a particular way. Every repo started with intent. Most never finished. After a decade you have:
- The same idea attempted in three frameworks (the React version, the Svelte rewrite, the Next.js attempt)
- Course tutorials you completed but never deleted
- Boilerplate scaffolds you generated, named, and abandoned within a week
- That one repo with a hardcoded bearer token in
pages/index.tsxthat you forgot existed - Several
polarizing-potato-style placeholder names from GitHub’s “create new repo” suggestions - A handful of genuinely good projects that nobody will ever find because they have a one-line
# ProjectREADME
The frustrating part isn’t the cleanup work. It’s the assessment work. To know what to do with each repo, you have to open it, read the README (if any), look at the package.json, scan the recent commits, and form an opinion. For 69 repos, that’s a full day of context-switching just to get a list of decisions you haven’t made yet.
This is exactly the kind of triage AI agents are good at — pattern recognition over a lot of similar inputs, with a structured output schema, parallelizable across the inventory.
What I Built
A Cowork plugin called repo-audit with two skills:
| Skill | Purpose |
|---|---|
audit-repos | Full multi-repo pipeline: inventory → analysis → cluster duplicates → draft READMEs → stage PRs |
draft-repo-readme | One-shot README generator for a single repo, callable on its own |
The plugin runs analysis inside the AI session and emits PowerShell scripts I run on my own machine. That two-phase design is intentional, and I’ll explain why.
Why Local-Execute, Not Direct GitHub Access
The Cowork sandbox can’t reach GitHub directly — and even if it could, I wouldn’t want it to. Granting an AI session direct API access to a GitHub account where it can open PRs, archive repos, and (theoretically) delete things crosses a line I’m not comfortable with. The friction of “AI proposes a script, I run the script” is doing real work: it’s the audit step before any irreversible action.
So the architecture became:
[AI session] [Local machine]
│ │
├─ Emits 01-export-and-clone.ps1 ─────►│ → I run it
│ │ → produces _inventory/ + repos/<each>/
│◄──── (I tell it "done") ─────────────┤
│ │
├─ Reads cloned repos │
├─ Fans out analysis to subagents │
├─ Drafts ConsolidationPlan.md │
├─ Drafts new READMEs in _drafts/ │
│ │
├─ Emits 02-open-pr-drafts.ps1 ───────►│ → I run with -WhatIf
│ │ → run for real if it looks right
│ │ → opens draft PRs, runs gh repo archive
Every step the agent takes is reversible: PRs land as --draft, archives use gh repo archive (which gh repo unarchive reverses), nothing is deleted automatically. The most destructive thing the agent does on its own machine is overwrite a JSON file in _analysis/.
Fan-Out for Parallel Analysis
The single biggest leverage point in the pipeline is the analysis phase. For each repo, I want to capture the same structured data: problem statement, goal, approach, tech stack, implemented features, missing features, completion percentage, quality signal, duplicate hints, recommended action.
Doing 69 repos serially in one conversation would burn through context window and take forever. Instead, the skill fans out: split repos into clusters of 10–15, spawn one subagent per cluster, each reads its assigned slice and writes individual _analysis/<repo>.json files. They run in parallel and don’t compete for context.
Every analysis file conforms to the same JSON schema so the next phase can parse them programmatically. This schema-first discipline is what lets clustering, the spreadsheet generator, and the README drafter all consume the same upstream data without re-reading the repos.
The Eight Phases
The pipeline runs eight phases. Each ends in a checkpoint where I can review before continuing:
| Phase | What happens |
|---|---|
| 1. Setup | Confirm GitHub username and working folder |
| 2. Inventory + clone | Run the export script locally — pulls metadata for every repo, clones each one, writes _inventory/repos.json and commit_activity.csv |
| 3. Deep analysis | Fan out to subagents — each one reads its slice of repos and writes _analysis/<name>.json |
| 4. Cluster duplicates | Identify duplicate clusters, name a winner per cluster, write ConsolidationPlan.md |
| 5. Spreadsheet | Generate Repo-Audit.xlsx with color-coded actions per repo |
| 6. README drafting | Fan out again — draft a structured README for every keep into _drafts/<repo>/README.md |
| 7. PR staging | Customize 02-open-pr-drafts.ps1 with the actual repo lists and emit it for me to run |
| 8. Merge helper | (Optional) emit 03-merge-readme-prs.ps1 if I want the YOLO merge path |
Phase 7 is the moment of commitment. Up until then, everything lives in my local working folder and on my local clones. Once I run the PR script, draft PRs appear on GitHub. Even then, nothing has been merged or destroyed — just staged.
The Results: 69 Repos by the Numbers
The audit produced this verdict:
| Action | Count | Notes |
|---|---|---|
| Keep (refresh README) | 19 | Real projects worth documenting |
| Consolidate | 7 → 3 winners | Same idea attempted multiple times |
| Archive | 38 | Abandoned experiments, finished tutorials, forks with no original work |
| Delete candidate | 5 | Empty repos and pure placeholders |
The 19 keeps clustered into four threads:
- AI / agent tooling —
AI-Business-Runner,Jarvis,ai-agent-workflows,qa-agent,code-implementer,Regression-Mananger - Personal productivity tools —
Family-Contribution-Tracker,WeeklyCheckinFunction,ComplySure,Student-Insurance-sentinel - Business apps —
Competition-Invoice-Manager,jit-redwood - Personal web presence —
Slower+SanitySlower,pocket-dev(this site),blog-next-sanity,resumeWebPage
The consolidation clusters told the most useful story:
- JIT dispatch app: 4 versions (
jit,jit_old,jit-next,jit-redwood) of the same Just-In-Time logistics idea across Angular, Next.js, and Redwood. Winner:jit-redwoodbecause it’s the only one with a real domain model (16 entities in Prisma), even at 35% completion. - Sanity-backed blog: 4 attempts converging on
blog-next-sanity(Next 15 + Sanity v3). - Personal portfolio: 3 attempts (
Gatsby-Profolio-Site,holderSvelte,resumeWebPage) collapsing to the cleanest one.
That’s roughly 11 repos absorbed into 3 canonical winners. The other 7 in the 7 → 3 column are smaller cases. Each winning repo will get a “Prior attempts that fed this:” line in its new README so search history still leads back to the right place.
The Gnarly Bits the Audit Surfaced
The most useful output wasn’t the action list. It was the “things worth flagging before pushing” section. The audit pass surfaced things I would never have noticed by hand:
- Hardcoded Strapi bearer token committed to
pages/index.tsxin a private repo. Old token, but still — rotate it before archiving the repo. localCodeAssistant’srequirements.txtis UTF-16 encoded.pip install -r requirements.txtsilently fails on it. I’d never noticed because I’d never gotten back to that repo.Family-Contribution-Tracker’spackage.jsonname ishomeschooltracker. Identity drift from a pivot I’d forgotten about.Regression-Manangerhas the typo in the GitHub repo name itself, not just in docs.gh repo renamewould fix it (and redirect old URLs), but it’d been there for years because I never saw it cataloged.- Course-fork census:
Angular-Routing,Angular-RxJS,typeorm,angular-ngrx-course— zero original commits on top of the upstream tutorial. They exist only as “clone a thing and follow along.” Safe-archive immediately.
Each of these would have taken me an hour of squinting to find by hand. The agent surfaced them as a side effect of having to compute features and quality signal for every repo.
The Meta Moment
While drafting ConsolidationPlan.md, the audit looked at pocket-dev (this site) and blog-next-sanity together and flagged a “decision point”: you have two blog repos, do you actually need both?
You also have
pocket-dev, a live Hugo blog you maintain and write real articles on. In practice, that is your blog.blog-next-sanityoverlaps in purpose. Three options: keep both, archiveblog-next-sanity, or repurpose it as a “Sanity CMS reference app.”
The agent that’s auditing my GitHub flagged the very blog I’m writing this post on. That’s the kind of thing manual triage doesn’t do — it asks you the question directly because the comparison is right there in the data.
What Made It Work
Five things that mattered more than I expected:
1. Schema-first analysis. Every _analysis/<repo>.json file matches a fixed JSON schema. Clustering, the spreadsheet generator, and the README drafter all read this schema. If the analysis output drifted, downstream phases broke. The discipline of pinning the shape early made fan-out safe.
2. Honesty rules baked into the skill. The README-drafting skill has explicit instructions: do not invent biographical or factual details that aren’t in the source files. If a feature isn’t in the code, don’t claim it’s implemented. Profile READMEs are the highest-stakes case — when in doubt, omit. Without this, AI-drafted READMEs hallucinate features that the repo doesn’t have. With it, the drafts came back accurate enough that I only edited tone, not facts.
3. Stage before applying. Drafts go in _drafts/. Scripts ship with -WhatIf examples in their docstrings. The user (me) always sees output before it hits GitHub. The plugin never opens a real PR or archives a real repo on its own initiative.
4. Two-phase architecture. AI in the sandbox, GitHub-touching scripts on my local machine. The friction is the feature. I’m reading what the agent proposes before it touches my account.
5. Fan-out subagents for the embarrassingly-parallel work. 50+ repo analyses running in parallel chunks of 10–15, each in its own context window. The whole analysis phase took minutes instead of an afternoon, and no single agent had to hold all 69 repos in working memory.
What I’d Tell Someone Trying This
A few practical notes if you want to do something similar:
- Don’t give the AI direct GitHub credentials. The
ghCLI runs fine on your machine. The AI session can write the script. You can read it before running it. That’s a clean trust boundary. - Define the data schema before you write any analysis prompt. Every downstream phase will depend on the analysis output being parseable. Pinning the schema first eliminates a class of “the AI forgot to include X” bugs.
- Default everything to
--draftandgh repo archive. Don’t write a script that callsgh repo deleteorgit push --force. Reversibility is cheap to design in and expensive to retrofit. - Spreadsheet output is underrated. The
Repo-Audit.xlsxwith color-coded action cells was the artifact I actually used to make decisions, not the markdown plan. A row per repo with green/amber/gray/red cells lets you scan 69 items in two minutes and override anything you disagree with. - The flagging section is where the value lives. The action list (“archive these 38”) is mostly mechanical. The “things worth flagging before pushing” section — the leaked tokens, the broken encodings, the typos, the identity drift — is where the agent earns its keep.
Closing
I’d been carrying around the cognitive load of “I should clean up GitHub someday” for years. The actual cleanup, once I built the right tooling, was an afternoon. The tooling itself was a couple of focused sessions to package as a Cowork plugin.
The reason it worked isn’t that AI did the work. It’s that the structure around the AI — schema-first analysis, fan-out for parallelism, two-phase local-execute, reversible-by-default actions, honesty rules to prevent hallucination, drafts in a staging folder — made it safe to point an agent at 69 personal repos and let it triage them.
If you’re sitting on a similar pile of GitHub sprawl, the work isn’t as bad as the pile makes it feel. Build the assessment harness, not the cleanup itself, and the cleanup follows.