Skip to main content

repomatic-ship

Prepare a release. Reconcile the changelog, code and docs to the net release state. Then commit, push, and babysit CI until the release PR is built and `main` is green. Stop before the merge. Ask for review in normal use; run fully autonomous under `--dangerously-skip-permissions`.

معلومات المصدر

المستودع
kdeldycke/repomatic
آخر نشاط في المصدر
٤ أكتوبر ٢٠٢٦ في ١٣:٣٢
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٦٤
التفرعات
٩

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
3 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
repomatic-ship
description
Prepare a release. Reconcile the changelog, code and docs to the net release state. Then commit, push, and babysit CI until the release PR is built and `main` is green. Stop before the merge. Ask for review in normal use; run fully autonomous under `--dangerously-skip-permissions`.
compatibility
Designed for Claude Code. Recommended model: Opus.
allowed-tools
Bash Read Grep Glob Skill Agent
## Context !`grep -m1 'version' pyproject.toml 2>/dev/null` !`awk '/^## \[/{n++} n==2{exit} {print}' changelog.md 2>/dev/null` !`git tag --sort=-v:refname | head -3 2>/dev/null` !`git log --oneline -25 2>/dev/null` !`git status --short 2>/dev/null` !`[ -f repomatic/__init__.py ] && echo "CANONICAL_REPO" || echo "DOWNSTREAM"` !`grep -rhoE 'kdeldycke/repomatic/[^@ ]+@[0-9a-f]{40} # v[0-9.]+' .github/workflows 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+$' | sort -u | sed 's/^/WORKFLOW_PIN=/'` ## Instructions You drive a release from a working tree to a ready-to-merge release PR: reconcile the tree to its **net state since the last tag**, validate it locally, commit and push, then babysit CI until the auto-generated release PR is green. You stop there: the human marks the draft release PR ready for review and performs the final "Rebase and merge". The release is push-driven: the `prepare-release` job in `changelog.yaml` runs `repomatic prepare-release` on push to `main` to build the freeze and unfreeze commits and open the release PR. **Do not run `prepare-release` yourself**: a local run previews a freeze that must not be committed (it marks the changelog "released", and on the canonical repo rewrites every workflow action ref). Your job is to make `main` clean enough that the auto-generated release PR is correct, then keep `main` green. ### How this skill runs - **The review gate is the permission system, not a behavioral stop.** Normal runs prompt on each `git commit`, `git push`, and subagent write; step 4 shows the consolidated changelog diff *before* the first commit prompt, so approving that commit is the review gate and denying it stops the run. `--dangerously-skip-permissions` mutes the prompts so the full sequence runs autonomously; the skill cannot detect the mode. - **Invocation method.** When the context shows `CANONICAL_REPO`, use `uv run repomatic`. Otherwise use `uvx --exclude-newer '1 week' --exclude-newer-package repomatic=P0D --from 'repomatic==X.Y.Z' -- repomatic`, with `X.Y.Z` the `WORKFLOW_PIN` the context shows. This applies the supply-chain cooldown to repomatic's dependency tree while keeping a fresh release installable. The pin matters because the tool registry ships inside repomatic: a newer release pins newer `ruff`, `typos` or `mypy` than the ones CI runs, and a gate built on it stops reproducing CI. Drop the `--from` clause only when the context shows no `WORKFLOW_PIN`. References to `<cmd>` below resolve to one or the other. - **Delegate the substance edits, apply the changelog yourself.** The sweep agents own the code, docs and bundled-asset lanes, so leave those files to them even when you do hold `Edit`/`Write`. The changelog is the exception: a skill is instructions rather than an actor, and `/repomatic-changelog` tells whoever loaded it to apply the consolidation directly, so you write `changelog.md` inline. ### Sub-agent rules The sweep agents (step 1) and the babysitter (step 6) all follow these rules. Restate them in every spawn prompt: a spawned agent only sees what the prompt carries. - **Commit attribution.** Every commit this skill or any spawned agent makes carries a `Co-Authored-By: Claude <noreply@anthropic.com>` trailer **by default**, so unattended changes stay traceable, and that default holds even where a downstream `CLAUDE.md` says nothing about commit attribution. A maintainer's explicit standing rule against AI attribution outranks it, because the trailer lands in *their* repository's permanent history. Check for such a rule **before the first commit**: stripping a trailer from an already-pushed commit needs a force-push, which is off-limits. When an exemption applies, write it into the spawn prompt of every agent that may commit: a withdrawal issued mid-run can arrive after the agent has committed. - **Reports are sent, not written.** A background agent's end-of-turn text is not a channel to rely on, so every spawn prompt must require the final report as a `SendMessage` to the **spawning (main) session**, naming that recipient explicitly: the spawning session answers to `main`, while an invented label like "orchestrator" or "main-orchestrator" resolves to nothing. An agent type whose `tools:` line omits `SendMessage` (`qa-engineer`, `sphinx-docs` and `grunt-qa`) can still hold it when spawned with a `name`. When no message arrives, read the idle notification's text before chasing, since it can carry the report of an agent that could not send one. Chase once when it holds none; the tree (`git diff`) stays the authoritative record either way. Do not pair the message with a "write the same report to a file" fallback: the harness refuses a spawned agent that write. - **Expand `<cmd>` before it reaches a spawn prompt.** `<cmd>` is this document's placeholder, not a shell command, and a prompt that carries it verbatim (or half-expanded) hands the agent something that cannot run. The trap is `run <tool>`: the tool registry supplies most of them, so dropping the `repomatic run` prefix fails outright. A spawn brief that expands `<cmd> run mypy --` into `uv run --frozen -- mypy --` dies on `Failed to spawn: mypy`, since mypy is not a project dependency. Write the invocation out in full (`uv run repomatic run mypy --`) and let the agent report back if it does not resolve. - **Trust the tree, not the report.** A mid-run message to a busy agent can land after the agent composed its final report and be silently dropped. After tasking a running agent, confirm the tree reflects the request (`git diff` the target file) before moving on. Read fresh every time: an edit can land in the gap between two checks. Grep for the *absence* of the old text (`grep -c '<old phrase>' <file>` returning `0`) instead of eyeballing a diff. - **Never let one lane wait on another lane's edit.** Messages cross: an agent can ask a sibling for a change while you reassign that file, then wait on an edit nobody will make. Name one owner per file in the same message to both agents, and when an agent reports it is blocked on another lane, make the edit yourself and tell it the edit landed. - **Never revert the shared working tree.** The agents share one tree, so no agent may run a working-tree-reverting git command (`checkout`, `restore`, `stash`, `reset`, `clean`), which silently discards the *other* agents' uncommitted edits. The rule binds investigation too: an agent comparing its file against the committed state reaches for `git stash`, which takes the whole tree. Say so in the spawn prompt, and name the read-only alternative beside it, `git show HEAD:<path>`. For full isolation instead, spawn with `isolation: "worktree"` and merge on join: disjoint files won't conflict. - **Fix commits stage narrowly** (`git commit <path>`, never `-a`). Uncommitted files an agent did not create are the maintainer's in-progress work: never revert them and never sweep them into a commit. So is a hunk the agent did not write inside a file it edits: report it, and never document or build on it. - **Degrade gracefully.** A cross-referenced skill excluded from this repo is a fallback path, not a blocker: apply its principle via an `Agent` or inline. When an `Agent` spawn itself fails (a terminal API error), do the work in the main thread. An agent that dies mid-run (a machine put to sleep, a lost login, a restarted session) sends no report and can stop halfway through a refactor. A `SendMessage` resumes it only while the session that spawned it is alive. Before you take its lane over, prove the package still imports, then read its diff file by file. ### 1. Reconciliation sweep A release materializes the **net state since the last tag**, not the path taken to reach it. Reconcile the changelog, the code and the docs against `git diff v<last>..HEAD`. Order matters: the changelog *describes* the net change, so reconcile the substance first (code and docs in parallel), then summarize it (changelog). A change introduced and then reverted before release is a no-op for users: no changelog entry, no scaffolding, no docs mention. **Before spawning, capture the unstaged diff** (`git diff` against `HEAD`): those lines are the maintainer's in-progress drafts, not cycle work. Pass both diffs to each agent with the rule: preserve every line present only in the unstaged set (a curated TODO, a scratch note in a docstring) unless the maintainer explicitly asked for cleanup. Without the guard an agent strips unstaged scratch as "cycle scaffolding". When in doubt, leave it. **Capture the job-level red inventory in the same breath**: the latest *conclusive* run of each monitored workflow on `main` (`gh api 'repos/<owner>/<repo>/actions/workflows/tests.yaml/runs?branch=main&created=>=<date>'`, then `gh run view <id> --json jobs`), listing every job at `conclusion == "failure"`, `⁉️` probes included: `continue-on-error` folds their crashes into a green run-level conclusion. Read `release.yaml`'s canary `✅ <os>, <sha> build` job too: its `Self-test binary` step runs the CLI test plan against the freshly compiled binary, and a toolchain pin bump upstream (a Nuitka release miscompiling a dependency) reddens it with no line of this repository changed. Under step 6's genuinely-green goal those reds are release work, and every one fixed before the first push saves a 40-90-minute babysit round-trip: seed the code agent's brief with the list. **Read runs through the API, not the `gh run list` wrappers.** Both the branch-wide form and the per-workflow form have put runs weeks old at the top of the list, which invents a red inventory. Two API calls are the authoritative reads: `gh api 'repos/<owner>/<repo>/actions/runs?head_sha=<sha>'` answers "what ran on this commit", and `gh api 'repos/<owner>/<repo>/actions/workflows/<file>/runs?branch=<branch>'` answers "what ran on this branch", but only with a `created=>=<date>` filter appended: without one the page can be days stale. A date range (`created=2026-09-01..2026-09-09`) walks the older weeks. Give the `head_sha` form the **full** 40-character SHA: an abbreviated one returns an empty list, which looks identical to "nothing ran". **Scan the open autofix PRs in the same breath, and read their diffs rather than their titles.** An unattended `fix-*`/`format-*`/`sync-*` PR is a pending write to `main` that no one reviewed, and merging one mid-release silently reverts committed work: `gh pr list --state open --json number,headRefName,title` then `gh pr diff <n>` on every automated branch. Look for a *false positive*: a "fix" that is wrong in this repository and that the job re-proposes until the rule is taught otherwise. The tell is a diff that undoes something a human deliberately wrote. Fix it at the rule, not the file: add the word to `[tool.typos] default.extend-words`, the path to the linter's ignore list, the pattern to `extend-ignore-re`. Reverting the file alone guarantees the same PR returns on the next run. **Judge that diff against current `main`, not against the PR's head.** An open PR is pinned to the commit it branched from, so its diff can describe a file the maintainer has since fixed. Rewording is the third option beside allowlisting and reverting: dropping the trigger word also stops the job re-proposing, and a rule landed on top of it then allowlists a word that appears nowhere. So `git grep` the trigger before writing a rule for it, and when the maintainer already solved it their way, leave it alone. Closing or merging the PR is theirs to decide: surface it, land the rule-level fix **only while the trigger is still live in the tree**, and say either way in the step-7 report. **Always read that run's `head_sha`, and treat a red as live only if nothing since it could have fixed it.** Most monitored workflows carry a `paths:` filter, so a commit that touches nothing in the filter triggers no run. When *that* commit is the one that fixed the red, the latest conclusive run stays pinned to the superseded parent and keeps reporting a failure that no longer exists. Before adding a red to the brief, run `git log --oneline <run head_sha>..HEAD` and check whether an intervening commit touched the failing area; when the answer is unclear, settle it for the cost of one dispatch (`gh workflow run <workflow> --ref main`, no commit, no PR churn) rather than reasoning about it. **A green conclusive run proves nothing when supersession cancelled every run between it and `HEAD`.** Walking back past `cancelled` runs to reach the newest conclusive one skips exactly the commits a busy cycle pushed most recently, so that `success` can predate every line the cycle added, and a false green ships the break. Before recording an empty red inventory, diff the gap: `git log --oneline <newest success head_sha>..HEAD`. When cycle commits sit inside it, the workflow has never run on them and the green is stale. Settle it with a dispatch (`gh workflow run <workflow> --ref main`, no commit, no PR churn) or by waiting out `HEAD`'s own run, never by recording "no reds". The three substance passes own disjoint lanes (code owns Python including docstrings, docs owns prose under `docs/` and `readme.md`, bundled assets owns `.claude/`), so spawn every lane that has work as **one `Agent` call each, all in a single tool-call block**: sequential spawns waste the wall-clock of whichever finishes first. 1. **Code**: an `Agent` that reviews every file changed since the last tag for reuse, quality, simplification, and deduplication, and fixes what it finds, simplifying before adding: existing code or a tool often already covers the case. Two layers: first strip scaffolding from reverted or superseded work within the cycle diff (abandoned workarounds, dead branches, WIP notes that never shipped); then harmonize what remains (collapse duplication, lift repeated literals to their canonical source, align new code with module patterns). Its constraints: - Every edit stays behavior-preserving: step 2 is the safety net, a failing test vetoes. - Type checks use the CI-equivalent `<cmd> run mypy` (pinned version and `--python-version`), never a bare `mypy` whose newer interpreter raises false positives CI never sees. Pass it **no arguments**, exactly as step 2's Types gate does: the runner then resolves the same tracked-`*.py` list CI's lint job checks, `tests/` and `docs/conf.py` included. A downstream `CLAUDE.md` "type checking" command is often the narrow dev-loop form scoped to the package only: do not inherit that scope, since a package-only run stays green on a `tests/` or `docs/` type error that reddens Lint post-push. - Failures the pass believes pre-existing get *reported*, not silently scoped out: that verdict belongs to step 2's CI check. - Adopting features from upgraded dependencies stays in `/repomatic-deps modernize`. - On the canonical repo, workflow invocations reading `uv --no-progress run --frozen -- repomatic` are the intended unfrozen state (the freeze commit rewrites them to a `uvx 'repomatic=={version}'` PyPI pin at release): never flag the local form as a pin regression or downstream breakage. The invariant to check instead is that every `uv`-invoking job provisions `setup-uv` in its own steps. Do not "restore" an isolated `uvx --from .` here: an index resolution can be made unsatisfiable by the install cooldown while a lockfile cannot. - Docstring rendering belongs to this pass: build the docs and fix any broken cross-reference role a docstring introduced (the docs pass can surface but not fix them). Build in a separate environment, with `{scratch}` a directory outside the repository: `UV_PROJECT_ENVIRONMENT={scratch}/docs-venv uv run --frozen --all-extras --group docs -- sphinx-build --fresh-env -b html docs docs/_build/html`. The shared `.venv` then keeps the CI-matching groups, which the `docs` group would perturb (see step 2's `--all-groups` warning). `--fresh-env` re-reads every page: an incremental build reports warnings only from the pages it re-read. A checked-in `:mirror:` block whose cells embed source-line anchors is invalidated by every edit that moves a line in the anchored modules, docstring rewraps included: expect the `update-docs` autofix PR to refresh it after the push, and take that PR's file (`gh api 'repos/<owner>/<repo>/contents/<path>?ref=update-docs' -H 'Accept: application/vnd.github.raw'`) when a local regeneration would differ from CI's, as a terminal-dependent width does, or when `gh pr merge` is permission-walled. Build only into the gitignored `docs/_build`, never an ad-hoc path: a stray build tree pollutes `git status` and trips tool scans like `run typos`. - Shortening an over-long workflow line to satisfy yamllint's 120-char limit must not lift `hashFiles(...)` (or any `runner.*`) into a workflow-level `env:` var: that context exposes only `github`/`secrets`/`inputs`/`vars`, so the expression resolves to empty at run-init before checkout and silently breaks the value. Shorten the literal itself instead (trim a shared key prefix, say). - The red inventory from the pre-spawn capture is part of its brief: root-cause each repo-fixable red (a chronic platform break, a flaky live-registry install, a crashing `⁉️` probe) and fix it at the source per step 6's taxonomy, even when the failing file saw no change this cycle. - Pending work the cycle introduced belongs in a ```` ```{todo} ```` admonition where Sphinx renders it, not in a bare `# TODO`/`XXX` comment: a comment never reaches the published todo list. This applies to **committed** cycle-diff lines only: a `# TODO` present only in the unstaged set is the maintainer's draft and stays untouched. - Retire a `{todo}` whose trigger fired, deleting the shim it guards in the same edit. Bound the check to what moved: `git diff v<last>..HEAD -- pyproject.toml uv.lock` plus the tool-version registry names the upstream releases this cycle actually adopted, and a todo naming one of them is the only kind worth re-reading. Read the trigger's own wording against that diff: a todo waiting on the dependency *floor* fires only when `pyproject.toml` raises it, never on a `uv.lock` bump that merely installs the release, and a shim the old floor still needs gets its todo re-scoped to that floor rather than deleted. Never poll every linked ticket, and leave a todo whose trigger has not fired alone. 2. **Docs**: an `Agent` that verifies prose docs against current behavior, not the journey (version references, CLI output, removed or renamed features go stale every cycle). Its constraints: - Manually-maintained version examples (install commands, binary download URLs, `uses:` refs) track the latest *released* tag, never the version being prepared, because the docs site deploys on every push to `main`. "Manually-maintained" is not a synonym for "under `docs/`": the freeze reaches into that tree in at least one repo (the canonical one rewrites `docs/install.md`), so decide file by file from the freeze's actual scope per the next bullet, never from the path. The tracking runs both ways: advance a sample that *lags* the released tag (still at N-1 after release N published) up to it, applying the bump directly; only bumping a sample forward to the not-yet-released version is off-limits. Grep every version string in `docs/` and `readme.md` rather than trusting a sub-agent's list. - **What the freeze rewrites varies by repo.** The canonical repo pins workflow refs and CLI invocations; a downstream freeze may touch only `changelog.md`, `citation.cff`, `__init__.py`, and `pyproject.toml`. Read the last freeze commit's actual diff (`git show <last-freeze-sha>`, never just `--stat`) and treat every version sample it does not rewrite, `readme.md` quick-start output included, as hand-maintained tracking the released tag. - **Freeze-management is per line, not per file.** A file the freeze touches can still carry hand-maintained samples it never rewrites, so `--stat` clears the whole file and the stale ones survive. - **Settle each sample individually** with `git log -S '<the literal string>' -- <file>`, which shows whether a freeze commit or a human last moved it. - **On a cycle that migrated the release tooling itself, the historical freeze under-predicts the new one.** A pre-repomatic freeze touching only `changelog.md` says nothing about the repomatic freeze, which also rewrites `citation.cff`, `__init__.py`, and `pyproject.toml`: treat every version sample as hand-maintained until the regenerated release PR's diff shows the new freeze's actual scope. - The mirror of the hand-maintained rule: a freeze-managed field legitimately shows the *dev* version between releases, so never flag one as stale against the released tag or as a dead link. `pyproject.toml`'s `urls.Download` reading `.../releases/tag/vX.Y.Z.devN` (a tag that has no GitHub release yet) is the expected post-bump state that the freeze rewrites to the release tag at cut. Classify a version string by which mechanism owns it before judging it stale. - A third owner sits beside hand-maintained and freeze-managed: artifacts regenerated by `autofix.yaml`'s `update-dep-graph` job. It runs on every push and delivers each refresh through its own PR. `docs/assets/dependencies.mmd` therefore lags `pyproject.toml` only while that PR is open: a dependency added since the last refresh is *expected* to be missing until the PR merges. Never hand-forge the file, and never run `<cmd> update-dep-graph` to "fix" it mid-sweep: a local run uses whatever repomatic version the sweep resolved rather than the version the job pins, so it produces churn the next regeneration reverts. Classify a stale-looking generated artifact by the job that owns it before reporting it as drift. - **A fourth owner is a version derived from a sibling checked-in file, and it is the only one that breaks something when bumped.** A build recipe in prose stages an artifact and then invokes a spec, manifest or lockfile the repository tracks, so its version must equal what that file declares rather than the released tag. Bumping the prose alone leaves the recipe unrunnable: a walkthrough that downloads `{pkg}-{X.Y.Z}.tar.gz` into `~/rpmbuild/SOURCES`, then builds `{pkg}.spec` with `rpmbuild`, resolves `Source0` through the spec's own `%{version}`. The tell is that the literal sits inside a procedure invoking a file in the repository: read that file's declared version before touching it. `git log -S '<the literal>'` settles the owner, since a derived value is written once by the commit that added the recipe. - **A hand-maintained block enumerating a registry population is a drift class the version rules above do not reach.** A readme that pastes a console transcript of a whole collection (a `members` mapping, a `member_ids` set, a name list) or carries a `mirror-src` diagram lags the moment an entry is added, and nothing reports it. Check every such block against the live collection, and derive the file set the change owes from the previous commit of the same kind: `git log -S '<a sibling entry>'` finds the commit that added the last one, and its diff names each file that change had to touch. - Executable doc blocks fail open: a `{click:run}` invocation that no longer parses renders the usage error into the published page instead of failing the build (only `click:tree` and `click:config` hard-error). Verify each `{click:run}` invocation against the current CLI, or grep the built HTML for `Error: No such option`-class output. - Correcting one description of a convention means correcting *every* description of it in the same pass: a rule restated in more than one place (an overview line and its worked example, two docs pages) drifts as a set, so grep for the sibling statements and align them together. - The same "align the siblings" rule governs *quantities*, not just wording. A cycle that measured the same thing twice ships two answers, so cross-check every timing, size, count and ratio a page states against the other statements of it, the changelog's copy included. Surrounding prose that quotes a ratio the tables no longer support is the tell. Report the contradiction with the conflicting values and which one the other statements corroborate, rather than silently re-deriving numbers on hardware that is not the maintainer's.
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub