Skip to content

Convert the user-wide slash commands to skills - #167

Draft
mattmenefee wants to merge 8 commits into
mainfrom
convert-commands-to-skills
Draft

mattmenefee wants to merge 8 commits into
mainfrom
convert-commands-to-skills

Conversation

@mattmenefee

@mattmenefee mattmenefee commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Move the five user-wide commands (commit, doc-review, handoff, resume-handoff, update-deps) from home/.claude/commands/<name>.md to home/.claude/skills/<name>/SKILL.md with git mv, and give each frontmatter: name, a description that says when to use it and an argument-hint where it takes arguments
  • update-deps carries disable-model-invocation: true, like /ship-it, because it opens a pull request; commit, handoff and doc-review stay model-invocable because the user-wide CLAUDE.md asks the model to run them
  • .homesick_subdir lists .claude/skills, so homesick links each skill directory into the existing ~/.claude/skills rather than replacing it
  • Split the two largest skills so SKILL.md stays near the 500-line guideline: after compaction Claude Code keeps only the first 5,000 tokens of an invoked skill, and supporting files load only when read
    • doc-review: SKILL.md is the orchestrator's procedure; reviewer-rules.md, review-file.md, posting.md and a worked walkthrough example sit beside it, and agent prompts name those files instead of a sed extract
    • update-deps: SKILL.md keeps the overview, the steps every run takes and a stub per moved step; Terraform, JavaScript, tool and image pins, the linter bumps, version-locked packages and changelog quirks each have a file, with every in-page link pointing at the file that holds its target
  • Stop preloading doc-review into the documentation-expert agent. A skills: entry injects the skill's full content into every dispatch, which as a skill would have meant the whole orchestrator procedure in each reviewer
  • Extract fixed logic into scripts run through ${CLAUDE_SKILL_DIR}:
    • doc-review/scripts: scrub-lib.sh (the one pattern set), scrub.sh (the scan the user sees), post.sh (runs every gate itself, then posts; status and stats lines are arguments), header.sh and test-post.sh
    • handoff/scripts/gather-state.sh, resume-handoff/scripts/gather-state.sh and resume-handoff/scripts/check-loss.sh
  • References in /ship-it, its scrub.sh, .markdownlintignore and the user-wide CLAUDE.md follow the new paths, and two CLAUDE.md rules about command files now name skill files too
  • Prose in every changed Markdown file is refilled to 100 columns
  • Fix a scrub gap another project's review found: the secrets term for a credential in a table row allowed only spaces around the key name, so a backticked or bold key cell passed both /doc-review's scrub and /ship-it's. Both copies now accept backticks and asterisks there; a key cell of several words still passes, and posting.md now says so

After pulling, run yes n | homesick link dotfiles to link the skills. The old ~/.claude/commands link is left dangling and can be removed by hand.

Test plan

  • yes n | homesick link dotfiles linked all five skill directories into ~/.claude/skills and left the existing entries in place
  • A running session picked up the skills: commit, doc-review, handoff and resume-handoff appeared in the skill listing with their descriptions, and update-deps did not
  • claude plugin validate home/.claude/skills passes
  • doc-review/scripts/test-post.sh 28/28: the 16 end-to-end cases the inline blocks were tested against (all matched the blocks' results before extraction), the key-cell cases, and argument and scrub.sh checks; disabling the withhold gate in a copy fails exactly the two withhold cases, and the old key-cell term fails exactly the two new key-cell cases
  • test-scrub.sh 90/90; its two new key-cell cases fail against the old term
  • check-loss.sh keeps the backup when a preserved line or a heading is dropped and removes it otherwise; both gather-state.sh scripts run against this branch
  • shellcheck clean on every new script, with and without --external-sources
  • The reflow changed only line breaks: each file's word sequence is unchanged and its HTML rendered by marked 12.0.2 is identical before and after
  • markdownlint-cli 0.45.0 with .markdown-lint.yml clean on every changed Markdown file
  • CI green on the head commit (8/8 on d1d0f27)
  • After merge: a /doc-review run reads the rule files and posts through post.sh; /update-deps still starts when typed

@mattmenefee mattmenefee self-assigned this Oct 24, 2026
@mattmenefee
mattmenefee force-pushed the convert-commands-to-skills branch 2 times, most recently from d3055f2 to 5b10b3e Compare October 24, 2026 16:12
@mattmenefee
mattmenefee force-pushed the convert-commands-to-skills branch 5 times, most recently from fd09448 to d6ccab2 Compare October 24, 2026 18:23
@mattmenefee
mattmenefee marked this pull request as draft October 24, 2026 18:25
@mattmenefee
mattmenefee force-pushed the convert-commands-to-skills branch 6 times, most recently from a7f617f to dfc2347 Compare October 24, 2026 21:24
The five user-wide commands lived as bare Markdown files in
~/.claude/commands with no frontmatter, so Claude Code took each one's
listing description from its first heading ("Commit Changes") and had
no way to say when the model should reach for it or keep a command
user-only.

Move each into its own skill directory as SKILL.md, keeping its history
with the rename, and give it a name, a description of when to use it
and, where it takes arguments, an argument hint. /update-deps creates a
branch and opens a pull request, so it carries disable-model-invocation
like /ship-it; /commit, /handoff and /doc-review stay model-invocable
because the user-wide CLAUDE.md asks the model to run them itself.

~/.claude/skills is a real directory that also holds skills installed
from elsewhere, so .homesick_subdir now lists .claude/skills and
homesick links each skill directory into it rather than the directory
as a whole. /doc-review's rule extract now reads its own SKILL.md, and
the references in /ship-it, scrub.sh, .markdownlintignore and the
user-wide CLAUDE.md follow the new paths and name skills alongside
commands.

After pulling, run `yes n | homesick link dotfiles` to link the skills;
the old ~/.claude/commands link is left dangling and can be removed.
As a skill, /doc-review was one 129 KB SKILL.md. The documentation
expert agent listed it under `skills:`, which injects a skill's full
content into the agent at startup, so every reviewer, the collator
and each recording agent began with the whole orchestrator procedure
loaded. After compaction Claude Code keeps only the first 5,000
tokens of an invoked skill, so most of the procedure was also lost to
the session running it.

SKILL.md now holds the orchestrator's procedure and names the files
beside it through the skill directory: reviewer-rules.md (the review
criteria and report rules, for every reviewer and the collator),
review-file.md (the review file rules, for the collator), posting.md
(the scrub, the Sensitive Information withholding and the comment
format) and a worked walkthrough example. Prompts give agents those
paths in place of a sed extract of SKILL.md, and documentation-expert
no longer preloads the skill.

The scrub and the gated post become scripts: scrub-lib.sh holds the
one pattern set, scrub.sh runs the scan the user sees and post.sh
runs every gate itself before posting, taking the status and stats
lines as arguments, so no post step exists to copy without its
checks. header.sh resolves each document's branch and commit.
test-post.sh replays the end-to-end cases the blocks were tested
against, with a stub gh. The prose in the moved files is refilled to
100 columns, and ship-it's scrub.sh header points at the new home of
the patterns it derives from.
/handoff and /resume-handoff each carried a long state-gathering
block for the agent to copy, fill in and run, and /resume-handoff's
check that a rewrite lost nothing worked around the skill argument
substitution with awk's `$(0)` and a variable set by its loop.

Each skill now runs scripts from its own directory instead:
gather-state.sh prints the base and where it came from, the tip, the
working tree, the pull request and the commit ranges under labeled
headings, taking the branch, the Updated date and the files the
handoff names as arguments where /resume-handoff needs them, and
check-loss.sh compares a rewritten handoff with its backup and keeps
the backup when a heading or a preserved line went missing. The
reasoning about bases, tips and silenced gh calls stays in the
skills, and their prose is refilled to 100 columns.
/update-deps was one 85 KB SKILL.md, most of it steps that apply only
in some repositories: Terraform, JavaScript, the RuboCop and
haml-lint bumps. Loaded whole on every run, it also lost everything
past its first 5,000 tokens whenever the session compacted.

SKILL.md keeps the overview, the steps every run takes and a short
stub at each moved step that says when to read its file from the
skill directory: javascript.md, terraform.md (Steps 5 and 6 and the
notes on why their commands are shaped as they are), pins.md,
linters.md, version-locked.md and changelogs.md. Each moved section
keeps its text, its headings rise one level to sit under the file's
title, and every in-page link now names the file that holds its
target. The prose is refilled to 100 columns.
Several paragraphs in these files broke well short of the 100-column
limit, some after edits on this branch shortened a line. Refill them
to the full width. Only line breaks change: each file's words and its
rendered Markdown are identical before and after.
The secrets term for a credential in a table row allowed only spaces
between the cell's pipe and the key name, so a row whose key cell was
written `SECRET_KEY_BASE` in backticks or **Password** in bold passed
both /doc-review's scrub and /ship-it's as clean, while the same value
as an assignment was blocked. A review in another project found the
gap with synthetic rows.

Both copies of the term now accept backticks and asterisks around the
name. A header row naming a key also blocks, as it did before, and is
cleared like any other line. A key cell of several words still passes,
since matching words would also catch summary rows whose description
mentions a credential; posting.md now says so and leaves that form to
the reading pass. Both test suites gain the backticked and bold cases,
which fail against the old term, and test-post.sh checks that a
summary row naming a credential still posts.
/doc-review's SKILL.md and review-file.md run scripts/header.sh by
path, but the script was tracked as 100644, so that call failed with
"permission denied" (exit 126) and only `bash header.sh` worked. Mark
it 100755 like the skill's other runnable scripts. scrub-lib.sh stays
100644 because it is sourced, not run.
@mattmenefee
mattmenefee force-pushed the convert-commands-to-skills branch from dfc2347 to 50574c0 Compare October 24, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant