restyle
Rewrites a Markdown draft toward a target author's voice, then quality-checks and repairs the result.
Usage: /restyle <draft path> --voice <name> [--contrast]
Paths
$SKILL below means the base directory for this skill, given to you at
invocation. Every script, prompt and voice lives inside it. The skill is normally
installed at ~/.claude/skills/restyle, so it runs from any project and the
working directory is wherever the user's draft is — never assume the two are the
same. Always invoke scripts by their full $SKILL/... path.
The draft path and the output files are the exception: those are relative to the user's working directory, because they are the user's documents, not the skill's.
If no voice is given, list the directories under $SKILL/voices/ and ask which to use.
If the draft path is missing, ask for it. Do not guess either.
Inputs
"$SKILL/voices/<name>/exemplars.md"— sent to the generator and the voice reviewer."$SKILL/voices/<name>/corpus/"— never sent to a model; used only by the overlap check..txtfiles are read recursively, so a corpus may be organised into subdirectories.
{{EXEMPLARS}} is substituted with the path, not the file's contents: the
subagent reads it. Do not inline the exemplars — that spends thousands of tokens
of your own context on text only the subagent needs. {{DRAFT}} and
{{REWRITE}} are substituted with contents, because you are already holding them.
If exemplars.md is missing, stop and say so. If corpus/ is missing or empty,
warn that the plagiarism check will not run, and ask whether to continue.
Preflight — always run this first
python3 "$SKILL/scripts/build_exemplars.py" --check "$SKILL/voices/<name>/exemplars.md" --corpus "$SKILL/voices/<name>/corpus"
Exit 1 means passages in exemplars.md are absent from corpus/. The generator
would then be shown text the overlap check cannot detect it reproducing — the
guard has a hole exactly where borrowing is most likely. Report which passages,
and recommend voice build before continuing. Do not proceed silently.
Procedure
1. Generate
Read $SKILL/prompts/generate.md. Substitute:
{{DRAFT}}— the draft file's contents{{EXEMPLARS}}—"$SKILL/voices/<name>/exemplars.md"{{CONTRAST_BLOCK}}— empty string by default. With--contrast, insert:Also state how the target voice differs from the draft's own style on each dimension, and let that difference drive the descriptors.
Dispatch one Agent tool call (general-purpose) with the substituted prompt.
Generate exactly one candidate — review and repair will fix what is wrong with it,
so more candidates buy nothing.
Extract the ## Descriptors and ## Rewrite sections. Write the rewrite to a
working file. If either heading is missing, re-dispatch once; if it fails again,
stop and report.
Default to target-only register analysis. --contrast exists for targets close
to the author's own register; a distinctive literary voice is not one.
2. Overlap check
python3 "$SKILL/scripts/overlap_check.py" <working file> --corpus "$SKILL/voices/<name>/corpus"
Exit code 1 means matches were found. This does not halt the run. Convert
each match into a blocker finding in the schema from
$SKILL/prompts/finding-schema.md, using the matched text as the quoted anchor.
Exit code 2 means the corpus could not be read — missing directory, no .txt
files, or nothing long enough to check against. That is not a clean pass and must
never be treated as one. Stop and report it: the guard did not run.
The check uses an 8-word shingle and will not flag shorter echoes. That is a
deliberate limit — a three-word rhetorical move is imitation, not reproduction,
and catching it would mean flagging ordinary English. Do not lower -n.
3. Review
Read $SKILL/prompts/finding-schema.md once and substitute it into {{SCHEMA}} in all
three reviewer prompts.
Dispatch three Agent tool calls in a single message so they run in parallel:
| Prompt | Substitutes | Deliberately withheld |
|---|---|---|
review-fidelity.md |
{{DRAFT}}, {{REWRITE}} |
the exemplars |
review-voice.md |
{{EXEMPLARS}}, {{REWRITE}} |
the original draft |
review-craft.md |
{{REWRITE}} |
both |
The withholding is deliberate and must be preserved. A fidelity reviewer who knows the target voice starts excusing invented content as style. A voice reviewer who has seen the draft starts rewarding timidity.
Collect all findings. Discard any finding whose quoted anchor does not appear verbatim in the rewrite — an unanchored finding cannot be verified or repaired. Record how many were discarded for the report.
4. Repair
If there are no findings, skip to step 5.
Read $SKILL/prompts/repair.md, substitute {{REWRITE}}, {{FINDINGS}} (all findings
from all sources, most severe first), {{DRAFT}}, {{EXEMPLARS}}. Dispatch one
Agent tool call.
Extract ## Repaired as the new working text and ## Unresolved for the report.
5. Iterate
Repeat steps 2-4 at most twice total. After the second repair, ship whatever you have.
Stop early if a round produces no findings.
Do not run a third round. Repair passes erode untouched prose, and a finding that survives two targeted attempts needs the author, not another attempt.
Round 2 matters even when round 1 looks clean: repair can introduce drift of its own, and re-review is what catches it.
6. Write output
Write the final text to <draft>.restyled.md.
Write <draft>.report.md:
# restyle report — <draft name>
- **Voice:** <name>
- **Variant:** RG (target-only) | RG-Contrastive
- **Date:** <YYYY-MM-DD>
- **Words:** <draft count> → <output count>
## Descriptors
<the generator's descriptor list>
## Round 1
- Findings: N blockers, N major, N minor (N discarded for missing anchors)
- Overlap matches: N
- Unresolved after repair: <titles, or None>
## Round 2
<same shape, or "Not run — round 1 came back clean.">
## Unresolved findings
<full text of anything still outstanding, or None>
## Overlap matches outstanding
<any still present after the final round, or None>
Then tell the user: output path, report path, and — stated plainly, not buried — any outstanding overlap matches. That is the one class of issue they should never publish without having looked at themselves.
Building a voice
/restyle voice build <name> regenerates "$SKILL/voices/<name>/exemplars.md" from the
corpus. Run it when a voice is new, when corpus files change, or when preflight
fails.
1. Extract candidates
python3 "$SKILL/scripts/extract_candidates.py" --corpus "$SKILL/voices/<name>/corpus" -k 12 --out /tmp/<name>_candidates.json
Returns the medoid passage of each register cluster, allocated per source file. The per-source allocation is load-bearing: clustering alone lets the largest file take most slots, because it holds most of the passages. Do not replace it with a flat sample.
2. Curate
Read $SKILL/prompts/curate-voice.md, substitute {{CANDIDATES}} with the candidates
file path, and dispatch one Agent tool call. It returns JSON with selections
(8-10 ids and register labels, ordered calmest first), rejected, and notes.
Write that JSON to /tmp/<name>_selections.json verbatim. Step 3 reads it from
there; it is not passed to the script any other way.
Read notes. If it reports a register missing from the corpus — most often the
calm expository end — tell the user plainly. That is a gap in their source
material, not something to paper over, and it is what causes pastiche.
3. Assemble and verify
python3 "$SKILL/scripts/build_exemplars.py" \
--candidates /tmp/<name>_candidates.json \
--selections /tmp/<name>_selections.json \
--corpus "$SKILL/voices/<name>/corpus" \
--voice <name> \
--out "$SKILL/voices/<name>/exemplars.md"
This writes the file and re-checks the invariant. A non-zero exit means the build failed; do not use the result.
Notes
- The descriptor list in the report explains the whole direction of the rewrite. When output is wrong, read it first.
- Never chunk the article for generation. It goes to the generator whole.