Help us improve
Share bugs, ideas, or general feedback.
From pilcrow
Lints and critiques prose in markdown, HTML, or plain text using classical style guides. Audits for AI tells, polishes voice, tightens clarity, and captures writing style into a project profile.
npx claudepluginhub samgalanakis/pilcrow --plugin pilcrowHow this skill is triggered — by the user, by Claude, or both
Slash command
/pilcrow:pilcrow [audit|lint|critique|rules|skills|polish|humanize|tighten|clarify|pace|lead|verify|aloud|argue|teach|document|craft] [paths...][audit|lint|critique|rules|skills|polish|humanize|tighten|clarify|pace|lead|verify|aloud|argue|teach|document|craft] [paths...]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Turn the LLM in your harness into the editor it should be. Deterministic checks for the patterns regex can pin down. LLM-judged ones for what regex can't. Editor commands anchored in classical style guides. Project commands that capture your voice and apply it to new drafts. Catches AI tells as one feature among many. Detection-only; the engine never edits.
reference/_ai-tell-catalog.mdreference/_cadence-theory.mdreference/_genres.mdreference/_readers.mdreference/aloud.mdreference/argue.mdreference/clarify.mdreference/craft.mdreference/document.mdreference/genres/about-page.mdreference/genres/academic.mdreference/genres/bio.mdreference/genres/changelog.mdreference/genres/cover-letter.mdreference/genres/cv.mdreference/genres/deck.mdreference/genres/email.mdreference/genres/empty-state.mdreference/genres/error-message.mdreference/genres/essay.mdRemoves AI-generated writing patterns like em dash overuse, passive voice, filler phrases, and promotional language from text. Makes prose sound natural and human-written; matches user voice from samples. Use for editing docs or reviews.
Humanizes AI-generated text by detecting and rewriting patterns like inflated symbolism, em dash overuse, passive voice, rule of three, and filler phrases. Use for editing or reviewing docs and code comments.
Two-layer copy editor. Layer 1 is a deterministic typography audit (regex-level, auto-closeable). Layer 2 is LLM judgment — reject-list hits, nominal-style detection, clarity/ambiguity pass for participant-facing content, voice/register check, and spoken- readability read. Loads repo-local .copy-editor.yaml to compose the baked-in language profile with the repo's style guide, reject list, examples, and voice doctrine. Czech is a full profile; English is a stub. **Activate automatically when the conversation edits or creates any file matching a repo's .copy-editor.yaml include scope, when the user asks about Czech or participant-facing copy, when reviewing content PRs, or when a work slice would otherwise close without a Layer 2 pass on edited visible-surface content.** Do not wait to be invoked by name. Explicit trigger phrases: copy edit, editorial pass, czech review, czech copy review, audit copy, check typography, review prose, tighten copy.
Share bugs, ideas, or general feedback.
Turn the LLM in your harness into the editor it should be. Deterministic checks for the patterns regex can pin down. LLM-judged ones for what regex can't. Editor commands anchored in classical style guides. Project commands that capture your voice and apply it to new drafts. Catches AI tells as one feature among many. Detection-only; the engine never edits.
Before any editor or project command runs:
pilcrow binary. If pilcrow isn't found (and npx pilcrow-ink isn't usable offline), stop and tell the user in one friendly line to install it: npm install -g pilcrow-ink, then re-run. Never fabricate lint, critique, or check output.VOICE.md if present.Every editor or project command begins by loading shared context:
node scripts/load-context.mjs
The script returns JSON with VOICE.md (the writer's voice profile) if it exists at the project root, in .pilcrow/, or in docs/. Cache the result for the session; don't re-run within the same conversation.
Not every command needs a voice profile. The split:
| Class | Commands | Behavior without VOICE.md |
|---|---|---|
| Voice-aware | polish, humanize, tighten, clarify, craft | Run, but flag voice-derived findings as "no profile; using nearby-paragraph fallback." Nudge teach once per session. |
| Voice-neutral | audit, lint, critique, rules, skills, verify, argue, pace, lead, aloud | Run as normal. Voice isn't load-bearing for these. |
| Voice-producing | teach, document | These create or refine VOICE.md. Don't suggest themselves. |
If a voice-aware command runs without VOICE.md, the nudge is single-line, once per session: "Running without VOICE.md; proposals will use nearby-paragraph fallback. /pilcrow teach to set up a voice profile." Don't repeat.
Match-and-refuse. These never ship, regardless of command, regardless of voice. Any command that surfaces these treats them as ship-blockers.
turn0search0, turn1view2, oaicite, oai_citation, contentReference, [+N].Ship faster. Build smarter. Scale forever.) as the opener. Cannot be redeemed by content specificity.**Bold:** followed by an explanation.These appear in reference/_ai-tell-catalog.md. They're surfaced here too because no command may skip them.
If a reader could guess your tone from your topic alone, you've fallen into the genre's stock voice. Rework until the topic doesn't determine the angle.
Every editor command applies this as part of its interpretation. A finding aligned with the genre cliché gets promoted in severity.
Every editor and project command applies these without loading a reference. They override any single-rule finding.
info or skip; VOICE.md exceptions whitelists explicitly.pilcrow audit has failed. Each command interprets; each has its own report structure defined in its reference file.VOICE.md signatures names habits to preserve.info rule can be a ship-blocker in aggregate (six overused-words hits in 400 words is a tell). An error rule can be a taste-call if VOICE.md whitelists it.These are the patterns the model reaches for when playing editor, not patterns in the writer's prose. They show up in proposed rewrites, in critique commentary, in "humanized" alternatives. They are the editor-side equivalent of the writer-side AI tells in the catalog. Match-and-refuse: if your top three rewrites or critique notes converge on any of these, list three more.
Stock LLM "improvement" cadences. None should appear in proposed rewrites.
Here's the bottom line: / To put it simply, / In short, / The takeaway is: The model's idea of a sharper opener. Lands on the same one every time.Let me unpack this. / Let's dig in. / Cut to: The model's idea of an active verb.X, not Y. as a closing sentence. Antithesis-cadence imported from training data; tells the reader you wrote it with a model.Ship faster. Build smarter. Scale forever. Never the right rewrite, regardless of the original.X means Y into X: Y. Looks tighter, reads telegraphic, loses cadence.in other words, before paraphrasing a sentence you've already given. Doubles the length to halve the trust.leverage, drive, enable, unlock, harness. Not stronger, just the AI corporate-verb pool.Stock LLM "feedback" moves. None should appear in critique commentary.
Consider tightening this for clarity. Name what specifically. Vague advice is editor-slop.You might consider… / It could be worth… Hedged suggestion when you have a position. Take a position, let the writer disagree.This is a great point, but… Sycophant opener for editor commentary. Cut the great-point.The piece would benefit from… Agentless passive; you're the agent. Cut paragraph 3 beats the piece would benefit from cutting paragraph 3.Strong piece overall; minor polish only. The critique equivalent of "great post!" Doesn't carry information.Every editor command that proposes a rewrite or alternative opening follows this procedure. It forces the model to surface its default before acting on it.
reference/genres/<slug>.md for genre-specific Demands / Forbids / Tolerates / Common AI tells (and reference/_genres.md for the parent-family "Cross-genre conventions"). Strike any proposal that falls into the genre's stock voice (postmortem → wry-self-deprecating; AI essay → wide-eyed future-tense; fintech → confident-and-jargon-heavy).VOICE.md signatures. The proposal carries at least one element of that signature; vocabulary, rhythm, or recurring metaphor domain.If steps 1–4 strip every option, the problem is the piece, not the phrasing. Surface that explicitly: "No phrasing in genre-X-with-this-voice redeems this opener. The lede problem is downstream of a substance problem; what's the most surprising thing you actually learned?"
lead is the canonical example (propose three openings, each surviving the ritual). tighten, clarify, humanize, and polish apply the same procedure to per-sentence and per-passage rewrites.
After producing your report (rewrites, critique commentary, scorecard, anything), ask one question:
If a reader saw this output and was told an LLM wrote it, would they believe it?
If yes, you've produced editor-slop: the writer-side AI tells imported to the editor side. The fingerprints transfer.
Symptoms:
you might consider).Here's a thorough analysis of your draft:. Meta-discourse opener imported to editorial work.**Tightness:** The piece is reasonably tight.).Overall, focus on lede and concision.).If the test fails, the fix is the same as for the prose: cut the hedges, take a position, name the specific change. The editor's voice has the same disciplines as the writer's.
Process the argument string $ARG (everything after /pilcrow) like this:
Several rules below turn a fuzzy reference into concrete prose to act on. Resolve in this order; first match wins:
$ARG that exists on disk. A directory or glob resolves to the set of prose files under it (.md, .mdx, .markdown, .txt, .html, .htm); scan or triage the set, reading a representative file closely.the docs → docs/, the readme → README.md, the landing page → the marketing/landing source, this file/that post → the file most recently in focus. If several files match, list them and ask which.Prose heuristic. Treat $ARG as literal prose to lint only when it actually looks like prose: it contains a sentence-ending mark with text after it, or a line break, or more than ~12 words, or it is wrapped in quotes. A one- to four-word phrase with no punctuation is a reference to resolve (step 3), never prose to pipe.
$ARG is empty (bare /pilcrow): enter triage mode. This is also the shared triage procedure that rule 5 reuses when $ARG is a bare target reference (the docs, this file) rather than a command. Don't render the menu; don't shell out to pilcrow yet. Instead:
help path below.node scripts/load-context.mjs (apply VOICE.md if present). Run pilcrow lint <target> for a deterministic baseline. Read enough of the prose to also notice things lint can't see: a buried lede, a flat opener, an argument with no counter, the wrong genre register, no stakes in the middle.VOICE.md genre if present, else infer from filename/path; see the full inference table in reference/_genres.md. Common patterns: posts//essays/ → essay, docs/tutorials/ → tutorial, docs/reference/ → reference-docs, docs/how-to/ → how-to, docs/explanation/ → explanation, README.md → readme, postmortems/ → postmortem, changelog.md → changelog, memos//rfcs/ → memo, marketing/landing/ → landing, press/ → press-release, about*.md → about-page, errors/ → error-message, cv.md/resume.md → cv, tweets//social/ → social-post, decks/ → deck, fiction//stories/ → fiction. Genre is the strongest signal for which commands to suggest.lead → the hero is scope-before-claim and the real thesis is in paragraph 3; tighten → middle is heavy with copula-dodge and zombie nouns; polish → triage what remains."| Genre family | Leaf examples | Default sequence | Why this order |
|---|---|---|---|
| argumentative/ | essay, op-ed, review | lead → argue → tighten → polish | Ledes carry argumentative prose; the argument is load-bearing; tighten before final triage. |
| reportorial/ | news, feature, postmortem, status-update, changelog | lead → verify → tighten → polish | The news is the news; claims must check; concision matters in stakeholder reading. |
| marketing/ | landing, product-copy, sales-email, press-release, about-page | humanize → lead → polish | This family is where AI tells cluster densest; strip the signature first. |
| documentation/ | tutorial, how-to, reference-docs, explanation | clarify → tighten → polish | Reader is non-expert; working-memory load and Diátaxis mode-discipline dominate. |
| overview/ | readme, project-home, one-pager | humanize → lead → clarify → polish | Doorway docs borrow marketing rhythm; strip first, then sharpen pitch and quickstart. |
| correspondence/ | memo, email, message | lead → tighten → verify | Recommendation in sentence 1; cut preamble; check load-bearing claims. |
| microcopy/ | ui-label, error-message, empty-state, notification | clarify → tighten → polish | Every word audited; voice helpful-not-chirpy; one idea per string. |
| social/ | social-post, social-thread | lead → tighten → polish | First 7 words decide; cut hooks that don't deliver. |
| personal/ | cv, cover-letter, bio | tighten → verify → polish | Terse; auditable claims; specifics over adjectives. |
| presentations/ | deck, speaker-notes | clarify → tighten → polish | One idea per slide; signage not paragraph. |
| narrative/ | fiction, memoir, script | pace → humanize (if AI tells dominate); else light hand | Many pilcrow rules fire on intentional craft here; tread carefully. |
The defaults are a starting point. If pilcrow lint shows a different dominant problem (e.g., buried-lede in a memo, claim-without-support in an essay), promote that command earlier in the sequence.
First word is help, ?, -h, or --help: render the command table below and ask which subcommand the user wants. Don't run anything.
First word is a CLI subcommand (audit, lint, critique, rules, skills):
pilcrow <subcommand> <rest> via Bash.<rest> is a fuzzy reference (the docs, this file), resolve it via Resolving the target and run the subcommand on the resolved path(s).printf '%s' "..." | pilcrow <subcommand>; otherwise ask "what should I <subcommand>?". Never shell out with no input.First word is an editor or project command (polish, humanize, tighten, clarify, pace, lead, verify, aloud, argue, teach, document, craft):
node scripts/load-context.mjs (skip if already cached this session).reference/<command>.md from this skill's directory.reference/_*.md).VOICE.md genre: if present; otherwise infer from filename/path via the table in reference/_genres.md. If the genre was inferred (not authored in VOICE.md), tell the writer one line before proceeding (e.g., Treating this as [<slug>], push back if wrong.) and wait for confirmation. If they correct, use the corrected slug for everything downstream. Don't re-prompt within the same file in this session, and don't surface for VOICE.md-authoritative genres.reference/genres/<slug>.md for genre-specific Demands / Forbids / Tolerates / Common AI tells / LLM lint additions. These flow into the command's reasoning; proposal-ritual gates, finding triage, rewrite voice.--genre <slug> to critique. When shelling out to pilcrow critique <target>, pass --genre <slug> so genre-specific LLM rules merge into the prompt alongside the 22 base rules.pilcrow lint <target> and pilcrow critique <target> --genre <slug> for input; the command interprets the findings through the loaded references.VOICE.md, drafts/). Follow the reference file's explicit gating; never write to disk without confirmation.audit is for. An editor command that returns the same shape as audit has failed.First word matches no command: resolve by intent; first match wins:
$ARG is an explicit path, glob, or directory on disk → that's the target. A single file with no other intent: pilcrow audit $ARG. A directory, glob, or a "review/look at" intent: enter triage mode (rule 1) on the set.$ARG is a referential target phrase (a short noun phrase, a bare filename, the docs, this file, my X) → resolve via Resolving the target and enter triage mode (rule 1) on it. Do not pipe a reference as prose.$ARG passes the prose heuristic → pipe it: printf '%s' "$ARG" | pilcrow audit.Typos: if the first word looks like a misspelled command (≤2 char edit distance), confirm before running.
Subcommands map 1:1 to the CLI binary. Pass flags through verbatim (--ignore-quoted, --json, --rules=id,id, --all, --provider=.x).
| Command | What it does | Common flags |
|---|---|---|
audit [paths...] | Run the 50-rule deterministic catalog, human-readable | --ignore-quoted |
lint [paths...] | Same scan, JSON output for piping | --ignore-quoted |
critique [path] | Print an LLM-critique prompt for the 22 base rules regex can't catch; merges genre-specific rules when --genre is passed | --rules=id,id, --genre=<slug> |
rules | List every rule with id, severity, description | --json |
skills <install|update|check> | Install or refresh the skill in .claude/, .cursor/, etc. | --all, --provider=.x |
| Command | Anchor | What it does | Reference |
|---|---|---|---|
polish | Strunk & White, Zinsser | Final pre-ship pass: triages combined audit + critique findings into ship-blockers, worth-fixing, taste-calls | reference/polish.md |
humanize | Wikipedia Signs of AI writing | Strip AI tells while preserving voice; classifies findings into vocabulary, cadence, template, fossil | reference/humanize.md |
tighten | Williams Style | Cut zombie nouns and weak verbs; per-sentence rewrites with the buried action surfaced | reference/tighten.md |
clarify | Pinker Sense of Style, Orwell | Reduce reader's working-memory load; per-passage rewrites with mental-model commentary | reference/clarify.md |
pace | King On Writing, Strunk | Restore rhythm; cadence histogram, aural diagnostic, split/merge proposals | reference/pace.md |
lead | Zinsser on leads | Sharpen the opening; finds the buried lede and proposes three alternative first sentences | reference/lead.md |
verify | claim auditing tradition | Surface load-bearing claims; classify each as unsupported / vague / hedged / unchecked | reference/verify.md |
aloud | aural reading tradition | Play the prose back via OpenAI TTS in an interactive session; gates on writer response | reference/aloud.md |
argue | Toulmin / IBIS / Argdown | Map the argument structure; pick the strongest counter; check whether the piece engages it | reference/argue.md |
| Command | What it does | Reference |
|---|---|---|
teach | Interview the writer to create or refine VOICE.md. Used after document to lock open questions, or standalone if there's no existing corpus | reference/teach.md |
document | Scan existing prose. Computes stylometric features, surfaces recurring moves, drafts VOICE.md with citations and open questions for teach to resolve | reference/document.md |
craft | Method-aware end-to-end writing (outliner / discovery / iterative / model-drafter); shape → draft → critique → polish | reference/craft.md |
Turn /pilcrow polish into /polish (and back). Useful for commands the writer repeats on every piece.
node scripts/pin.mjs pin polish
node scripts/pin.mjs unpin polish
The script writes a redirect skill into every harness directory where pilcrow is installed. Run unpin to remove. Pinned skills carry a marker comment, so unpin only deletes shortcuts it created; never user-owned skills with the same name.
These shared files live in reference/ with a leading underscore. They are not commands; they are content loaded by multiple editor commands.
| File | Content | Loaded by |
|---|---|---|
| reference/_ai-tell-catalog.md | Exhaustive AI-tell catalog by class | humanize, polish |
| reference/_readers.md | Reader personas | clarify, lead, polish |
| reference/_cadence-theory.md | King + Strunk on rhythm | pace, polish |
| reference/_genres.md | Genre conventions | clarify, lead, document, craft; also routing rule 1 (bare triage) |
Universal writing laws and the editor reflexes / proposal ritual / slop test are inlined in this file above; they apply to every command without an explicit load. Commands that need a cross-cutting file say so explicitly in their own reference; don't load every shared file by default.
aloud depends on the OpenAI speech skill (Apache 2.0) for TTS. The helper script resolves it for you:
node scripts/resolve-speech.mjs
It checks .claude/skills/speech/, .cursor/skills/speech/, etc. for an installed copy; if none, it fetches the skill at a pinned SHA into /tmp/pilcrow/skills/speech/. Either path returns the directory where scripts/text_to_speech.py lives.
aloud requires OPENAI_API_KEY to be set in the environment. The command checks for it at session start and points you at the speech skill's instructions if it's missing.
pilcrow lint returns JSON like:
{
"files": [
{
"file": "drafts/post.md",
"findings": [
{
"ruleId": "ai-tell-phrasebank",
"ruleName": "AI-tell phrasebank",
"severity": "error",
"message": "AI phrasebank match: \"delve into\". Rewrite with concrete specifics.",
"line": 12,
"column": 8,
"range": { "start": 234, "end": 244 },
"excerpt": "…me delve into the rich tapestry…",
"suggestion": "(optional replacement text)"
}
]
}
]
}
pilcrow critique emits a single prompt the model evaluates; the model returns findings in the same Finding shape.
The engine never modifies prose. When a command proposes rewrites, present them to the user and wait for confirmation before editing the file; voice and intent override the rule.