Repository navigation
Conversation
Nine git/GitHub slash commands lived only in one contributor's
~/.claude/commands/ — unversioned, unshared, and carrying hardcoded
assumptions from unrelated repos. Port them to a skills-first plugin
following the ai-research-workflows layout, so the conventions apply on
intent ("commit this") and not only on an explicit slash command.
Generalized what was personal: commit scopes now come from the repo's
own log rather than a fixed vocabulary, the docs skill detects MkDocs,
Sphinx, Docusaurus, Quarto, or mdBook instead of assuming MkDocs plus
pixi, and the release skill derives its compare URL from the current
remote instead of hardcoding one project's.
Resolved a contradiction in the sources, where three commands forbade
Co-Authored-By trailers while /release wrote one; every skill now
carries the no-attribution rule.
Promoted the repeated default-branch one-liner into
scripts/default-branch.sh. The inline version silently fell back to
"main" whenever origin/HEAD was unset, which is the default-branch
assumption the plugin exists to avoid; the script resolves through the
remote and the GitHub API and exits non-zero rather than guessing.
🔍 Tessl Skill Review
|
|
Note on the red review check — it is not about this branch.
Every score comes back Worth a separate issue to either wire up the secret or drop the action. I have Skill quality on this branch was instead gated with whetstone's |
lsetiawan
left a comment
There was a problem hiding this comment.
Hi @cdcore09 — reviewed this through the writing-for-agents lens, with a focus on making the skills portable across coding agents rather than tied to Claude Code's plugin commands.
Verdict: structurally sound. The split is by branch (the right cut), promoting default-branch.sh was the right call, and several skills carry exactly the non-obvious detail worth paying context for. The changes below are about making that work reachable from more than one harness, plus a writing pass.
Full writeup with the measured tables: https://claude.ai/code/artifact/adb01503-6ec2-4769-a119-145cdb12db01
The headline: keep every skill model-invoked
If the goal is "user-invocable skills instead of Claude plugin commands", the first half is right and the second half backfires. disable-model-invocation is itself Claude-Code-specific — reaching for it swaps one proprietary mechanism for another and moves nothing toward portability.
It also deletes the only invocation path guaranteed to exist. Model invocation needs one thing: the description sitting in context. User invocation needs the host to implement a skill-picker and agree on how a skill is addressed. Model invocation is the portable invocation.
There's a second cost inside Claude Code. Five skills are reached by a peer, not just the router:
committing-changes ← opening-pull-requests, pushing-branches
pushing-branches ← opening-pull-requests
documenting-work ← recording-decisions
filing-issues ← recording-decisions
recording-decisions ← documenting-work
A skill with no description can't be reached by another skill. Disable any of these and opening-pull-requests tells the agent to publish via pushing-branches, then can't get there. The documenting-work ↔ recording-decisions pair is the sharpest — they cross-reference each other specifically to disambiguate, which is the best thing in the current descriptions.
Portability audit
| Construct | Sites | Status | Disposition |
|---|---|---|---|
SKILL.md + name/description |
10 | ✅ Portable | The core — keep all ten |
${CLAUDE_PLUGIN_ROOT} |
8 across 7 skills | 🔴 Blocker | See below |
commands/*.md, $ARGUMENTS, user-invocable |
9 | 🟡 Claude-only | Delete — 8 of 9 duplicate their skill |
git-workflows:<skill> namespacing |
6 across 4 skills | 🟡 Claude-only | Use the bare skill name |
.claude-plugin/, marketplace.json |
2 | 🟡 Claude-only | Keep — distribution channel, not content |
disable-model-invocation |
0 | 🔴 Don't add | See above |
.claude-plugin/ is how the skills get delivered to Claude Code; it says nothing about whether another agent can read a SKILL.md once the directory is on disk. The portability work belongs in the skill bodies, not the manifest.
1. ${CLAUDE_PLUGIN_ROOT} is the real blocker
${CLAUDE_PLUGIN_ROOT}/scripts/default-branch.sh appears at 8 sites across 7 skills. On any agent that doesn't define that variable it expands to /scripts/default-branch.sh and fails — and the failure lands on the plugin's one promoted shared asset, whose entire purpose is to stop the agent guessing main.
The portability fix and the duplication fix are the same move. The path is written out seven times, so re-addressing it for a second host is a seven-place edit:
plugins/git-workflows/reference/default-branch.md
— resolution order, the script, and how to invoke it per host
# in each skill, at the point of use
Resolve the base branch by the procedure in `reference/default-branch.md`.
This is the principle the PR already applied when it promoted the script out of seven inlined one-liners — the promotion stopped one step short, at the address rather than the instruction.
Keep the script. The four-step resolution with a non-zero exit is genuinely good, and the throwaway-repo test (trunk, origin/HEAD unset) is the right kind of evidence. Only the addressing needs to move.
2. commands/ is 8/9 duplication
Each wrapper carries one behavioral instruction — the no-arguments fallback. Checked against the skill it wraps:
| Wrapper | Fallback | Already in the skill? |
|---|---|---|
/commit |
analyze all current changes | §1 Survey |
/push |
push current branch to its remote | §1 Inspect |
/merge-pr |
resolve the PR for the current branch | §1 Identify |
/create-issue |
ask what should be tracked | §1 Clarify |
/create-discussion |
draw from what was worked through | §1, near-verbatim |
/release |
infer the bump, state the reasoning | §2, near-verbatim |
/clean-branches |
present the table, ask which categories | §3 + §4 |
/docs |
document the work from this session | §2 Gather (roughly) |
/create-pr |
ask who should review before opening | No — stranded here |
Nine files, ~90 lines, and exactly one instruction that isn't already in a skill. opening-pull-requests §4 shows --reviewer <login> but never says to ask. Port that line, then delete the directory.
Precedent: zarr-data-format already ships 5 skills and 0 commands, and its marketplace.json entry is the same shape as this one — neither enumerates commands, so deleting needs no manifest change.
3. What this costs, and the mitigation
/commit becomes /git-workflows:committing-changes. That's the only thing the wrapper layer bought, and it's a real cost if you have muscle memory in the short form.
Mitigation: rename the skills to the names the commands had. Names don't drive matching — the description does — so shortening costs nothing on the model-invocation side. The gerund convention comes from ai-research-workflows; it isn't repo-wide (containerization, zarr-*, uiux-design-team all use plain names).
committing-changes → commit, pushing-branches → push, opening-pull-requests → create-pr, merging-pull-requests → merge-pr, filing-issues → create-issue, recording-decisions → create-discussion, pruning-branches → clean-branches, publishing-releases → release, documenting-work → docs.
You'll also want a root AGENTS.md — deleting commands/ removes the typed-entry-point affordance on Claude Code, and nothing ever provided one on other agents. The repo has none at root today.
4. The router's shared rules are a coverage illusion
using-git-workflows ends with "Rules that hold across every skill." They don't hold — the router only loads when the router fires, which won't happen when a leaf description wins. Ask "merge this PR" and merging-pull-requests loads alone.
no-attribution rule present: committing-changes, filing-issues,
opening-pull-requests, publishing-releases,
recording-decisions
absent: merging-pull-requests, documenting-work,
pushing-branches, pruning-branches
merging-pull-requests composes a squash-commit message; documenting-work writes prose pages. Both write text, neither carries the rule.
The fix isn't "dedupe" — a must-have behind a pointer that may never fire is a variance bug, so the per-skill inlining is correct and should extend to those two. What should go is the router's copy.
The routing table is separately redundant: it instructs "invoke by fully-qualified id", which is plugin syntax, and nine live descriptions already do the routing. A router is the documented cure for user-invoked skills multiplying; with everything model-invoked it solves a problem that doesn't exist. What survives is the chains (commit → push → create-pr → merge-pr) — shrink it to that, or move them to the README.
5. Writing pass
Trigger synonyms — ~724 tokens every turn. 543 words of description across ten skills, ~56 trigger phrases resolving to ~18 distinct branches. pruning-branches writes one branch six times:
clean up branches, delete merged branches, prune stale branches, my branch list is a mess, remove old branches, tidy up refs
One trigger per branch halves this with no loss of reach. This is the lever for context cost, not disable-model-invocation.
Negation density. committing-changes runs 12 negations in 129 lines, and §3 names all three banned staging forms after showing the positive one — which makes the banned forms the most available thing in the section. Prompt the positive:
| Current | Positive |
|---|---|
Never git add -A, git add ., or git add -u |
Stage each path by name: git add path/one.py path/two.md |
Never skip hooks with --no-verify |
A failing hook is information: fix the cause and commit again |
Never use --force unless… |
Force-writes use --force-with-lease, and only when asked in that exchange |
Keep the guardrails that can't be phrased positively (refusing to force-write a protected branch), paired with the positive target.
Completion criteria. pruning-branches sets the bar — "every ref into exactly one bucket" is checkable and exhaustive. Weakest is documenting-work §3, "decide honestly whether this extends an existing page": a weak modifier over a fuzzy bound, gating the step the skill's own opening line calls the failure mode. Sharpen to "name the closest existing page and state why it is or is not the right home."
Leading words. "Read it off the repo, don't assume" is spelled out longhand at 7 sites and named only in the router, where it doesn't load. The word is already in the documents — committing-changes step 1 is Survey. Promote it. Likewise one idea wears three words across skills (Preview, Present, "Show before you write") — collapse to preview. Verify already works this way in all ten; copy that pattern.
The superseded originals. .claude/commands/ still holds all eight source commands on main and this PR deletes none. Renaming the skills removes the name collision but leaves two implementations of the same conventions, with the un-generalized one still live. Worth deleting in this branch.
Suggested order
Portability proper:
- Port "ask who should review before opening" into
opening-pull-requests§4 - Delete
plugins/git-workflows/commands/ - Rename skill directories to the short forms (+
name:frontmatter, + 6 cross-references) - Drop the
git-workflows:prefix from cross-references - Move default-branch resolution into
reference/default-branch.md
Writing pass:
- Inline the no-attribution rule in
merge-pranddocs, delete the router's rules block - Shrink the router to its chains
- Collapse trigger synonyms to one per branch
- Flip the negations in
commit,push,merge-pr,release - Add a root
AGENTS.md - Delete
.claude/commands/
What's already right
- The split is by branch — nine skills, one per distinct operation. Each path carries only what it needs.
default-branch.sh— four-step resolution, non-zero exit rather than a guess, tested against atrunk-based repo. Only its addressing needs to move.- Descriptions that name their sibling to disambiguate (
documenting-work↔recording-decisions) put the disambiguation in the pointer, where it loads. - The expensive lookups are cached and the cheap ones aren't —
...vs..for the reviewer's diff, "the third command fails when no upstream is set, that's the signal for-u",-drefusing after a squash merge. Exactly what a document should carry.
Nice piece of work overall — the generalization away from one contributor's repos is the part that makes this reusable, and it mostly landed.
Summary
git-workflowsplugin, following the skills-first layout
ai-research-workflowsadopted in refactor(ai-research-workflows): skills-first redesign (10 skills, unified research, cross-agent portable) #113."open a PR") and not only when someone types the slash command. The nine
commands remain as thin
user-invocablewrappers, so existing muscle memorystill works.
Closes #124
Changes
New plugin —
plugins/git-workflows/using-git-workflowscommitting-changes/commitpushing-branches/pushopening-pull-requests/create-prmerging-pull-requests/merge-prfiling-issues/create-issuerecording-decisions/create-discussionpruning-branches/clean-branchespublishing-releases/releasedocumenting-work/docsRegistered in
.claude-plugin/marketplace.json(11 plugins now).Generalized what was specific to one contributor's repos:
git log, not a fixed vocabulary(
marketing,billing,worker, ...).documenting-workdetects MkDocs, Sphinx, Docusaurus, Quarto, or mdBook andprefers the project's task-runner target, instead of assuming MkDocs +
pixi.publishing-releasesderives the compare URL from the current remote insteadof hardcoding another project's, and detects what the project actually ships
rather than assuming a
plugins/zip layout.pruning-branchesprotects the discovered default branch plus anything theuser names, rather than a hardcoded
feedback-screenshots.Resolved a contradiction in the sources.
/commit,/create-pr, and/create-discussionall forbadeCo-Authored-Byand AI-attribution trailers,while
/releasewrote one into its changelog commit. Every skill now carries theno-attribution rule.
Promoted a repeated snippet into
scripts/default-branch.sh. Seven skillshad inlined the same default-branch derivation, and that one-liner silently fell
back to
mainwheneverorigin/HEADwas unset — which is common on fresh clones,and is precisely the assumption this plugin exists to avoid. The script resolves
through
origin/HEAD, then the remote, then the GitHub API, then a branch thatdemonstrably exists, and exits non-zero rather than guessing.
Verification
Ran whetstone's
skill-auditover thenew skills, and over the whole catalog to check the plugin does not compete with
skills already here:
Two defects surfaced during the run and were fixed rather than reported around:
using-git-workflowshad no trigger clause in its description, and thedefault-branch duplication above met whetstone's
dry-promotionbar. The auditwas clean at threshold
0.20as well, so the descriptions have margin — worthchecking, since nine skills sharing git vocabulary was the main routing risk.
The remaining 36 catalog defects are pre-existing in
containerizationandproject-management; none are in this plugin, and none are touched here.Also verified locally:
validate-plugins.ymlsteps simulated: JSON valid, no duplicate keys,.claude-plugin/plugin.jsonandREADME.mdpresent,scripts/*.shexecutable, secret and dangerous-pattern scans clean
bash -nondefault-branch.sh;shellcheckwas not installed locallydefault-branch.shexercised against a throwaway repo whose default branchis
trunkwithorigin/HEADunset — resolvestrunk, where the previousinline snippet returned
main. Invalid remote exits 1 rather than fallingthrough.
namematches its directoryTest plan
claude plugin install git-workflows@rse-plugins, then confirm/commit,/push,/create-prappear and delegate to their skillsplugins/git-workflows/scripts/default-branch.shin amaster-basedrepo and in one with
origin/HEADunset/commitin a repo with no scopes in its history — confirm it omits thescope rather than inventing one
/docsin a Sphinx project — confirm it detects Sphinx, not MkDocs