# Setup Repo

> Bring a GitHub repository to the configuration the issue/PR lifecycle skills assume — label taxonomy, issue forms under .github/ISSUE_TEMPLATE/, repository settings (delete-branch-on-merge, squash-only merges, description, homepage), topics and the GitHub Pages source — deterministic and idempotent: `plan` prints drift, `apply` converges it. Use when a repo needs configuring or has drifted: "set up the labels", "create the issue templates", "set the repo description and topics", "enable GitHub Pages from docs/", "configure this repository the way the kit expects", « configure les labels du repo », « active GitHub Pages ». It WRITES what profile-repo only READS. Does NOT file issues, implement code, or merge PRs.

- Skill: `phmatray/setup-repo` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add phmatray/setup-repo`
- Raw SKILL.md: https://api.skillmd.com/api/skills/phmatray/setup-repo/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: phmatray (https://skillmd.com/u/phmatray)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/phmatray/setup-repo

---


# Configure a repository for the lifecycle skills

`create-issue`, `implement-issue`, `merge-pr` and `auto-dev` are generic workflows wrapped around a
thin layer of repo-specific facts. `profile-repo` records those facts — and when it finds none,
its only outlet is a `TODO:` line. That is the right answer for a fact it cannot read and the wrong
one for a **configuration the repo does not have**, which is what four of its TODOs always are:

| What the profile reports | What it actually means | What silently degrades |
|---|---|---|
| no `priority:` axis | the labels were never created | `create-issue` omits the axis |
| no `effort:` axis | the labels were never created | `auto-dev`'s "small first, then medium" ordering has nothing to sort on |
| no `area:` axis | the labels were never created | `auto-dev` cannot give parallel workers non-overlapping areas |
| no `.github/ISSUE_TEMPLATE/` | the directory was never created | `create-issue` has no form to obey |

This skill is the missing verb. It does not describe a repo; it makes one.

The same manifest also carries the four surfaces a **public** repository is judged by before
anyone opens a file (#400): `settings.description`, `settings.homepage`, `topics:` and `pages:`.
The description and homepage ride the settings PATCH; topics are one `PUT` that replaces the
whole set, so `apply` writes the union of live and declared; the Pages source is one `POST` to
create the site or one `PUT` to update it — never a `DELETE`.

## Do this

A bundled script does the deterministic work. Run it from anywhere in the target repo (it anchors
to the git root). `<skill-dir>` is this skill's base directory — given when the skill loads:

```bash
bash "<skill-dir>/scripts/repo-setup.sh" plan
```

`plan` writes nothing, so it is safe against a repository you only read. Show the operator the
delta it prints, then converge:

```bash
bash "<skill-dir>/scripts/repo-setup.sh" apply
```

Read the exit code — it is the report, and each value means one thing:

| Exit | Meaning | What to do |
|---|---|---|
| 0 | converged | Say so. Nothing to do. |
| 1 | `plan` found drift | Show the delta, then run `apply` — **except** a `!TODO` line: `apply` never creates a placeholder, so fill it into the manifest instead (#198). |
| 2 | bad usage, or an unreadable/unparseable manifest | Fix the manifest; never partially apply. |
| 3 | a surface was refused | Relay **which** surface and why — the report names it. The rest did land. |
| 4 | not inside a git repository | Say so and stop. |

## The desired state

`templates/repo-setup.yml` in the kit is the shipped default. A consumer repo overrides it by
committing its own `.github/repo-setup.yml`, which `repo-setup.sh` prefers — so a consumer's
taxonomy survives a kit upgrade. `--manifest <path>` overrides both.

Six rules govern what `apply` will and will not do, and they are worth relaying to the operator
before the first run against a repo that already has labels:

- **Additive.** A live label the manifest does not declare is reported `!EXTRA` and **kept**. Only
  `--prune` deletes anything. A repo already running `P1`/`P2` must not have its taxonomy renamed
  out from under it.
- **`pruneKeep` outranks `--prune`.** Labels a *tool* owns look undeclared because no human
  declares them, and deleting them breaks the automation that reads them. The manifest's
  `pruneKeep` globs — seeded with release-please's `autorelease: *` and Renovate's `dependencies` —
  are reported `!KEEP` and never deleted. Add the repo's own bot labels there before running
  `--prune` on it.
- **Never clobber.** An issue form that already exists is reported `!SKIP`. A tuned form outranks
  the kit's default.
- **A name in angle brackets is a placeholder** — never created, and reported `!TODO` **and counted
  as drift** on every run (`plan` exits 1), so an unfilled axis stays visible instead of looking
  converged (#198). The `area:` axis ships this way because it names the consumer's code, not the
  kit's: fill it in before `auto-dev` runs a fleet — `apply` will not resolve this one for you.
- **Topics are additive, like labels** — and only when the manifest declares `topics:` at all. A
  live topic the manifest does not name is reported `!EXTRA` and kept; `--prune` drops it. A
  manifest silent on topics never reads or writes them, `--prune` included.
- **The Pages site is created or updated, never disabled.** A 404 on the read means *no site
  yet* and plans a `+ADD`; a 403 is a refusal by name, like every other surface. Disabling a
  site is not a converge, so this skill never issues that `DELETE`.

## Autonomy contract

Run **hands-off**. `plan` is a read — no task list or precondition ceremony. Before `apply`, show
the delta and get a yes when the repo is not the user's own or the run includes `--prune`; a
straightforward `apply` on their own repo needs no gate.

Never invent a taxonomy. If the manifest lacks an axis the repo needs, edit the **manifest** and
re-run — an axis created by hand with `gh label create` is exactly the non-deterministic setup this
skill exists to replace. When a surface is refused, report it and move on: a partially configured
repo with a named gap beats an abort that leaves the operator guessing which half landed.

## Afterwards

`profile-repo --refresh` — this skill's row in the hand-off table, and the reason for it: the
four TODOs are facts now, so the profile records the real axes and the lifecycle skills start
applying them.

`plan` exiting 1 on drift makes it usable as a CI step, if the repo wants configuration drift to go
red the way code drift does.

## Recap

Close with the shared recap shape — [`../_shared/recap.md`](../_shared/recap.md). It owns the four
blocks (verdict · **What happened** · **Artifacts** · **Assumed · skipped · unverified**, where
`None` is a required answer rather than an omission) and the **Next** line, which is read off this
skill's row in that file's hand-off table instead of being decided again here. Everything below is
only what **setup-repo** adds on top of them.

- Name each surface separately — labels, issue forms, repository settings, topics, the Pages
  site — and whether it
  converged, was already converged, or was **refused** (and by what: missing admin rights, an
  unfilled `area:` placeholder, `gh` unauthenticated). A partially configured repo with a named gap
  is a result; an unqualified "done" over a refused surface is not.
- Say whether `--prune` ran. It deletes labels, including the six undeclared GitHub defaults, so it
  is never a silent detail.

