Prose Polish
SkillFiles & storageRefactor verbose or unnatural natural-language prose, code comments, test descriptions, docstrings, user-facing text, into concise, native-sounding prose in a configured target language, using a sonnet subagent by default. Two modes: file mode rewrites a file's target-language prose in place; text mode returns the refactored text. Preserves code, identifiers, and proper-noun terms while translating ordinary technical vocabulary into the target language. Non-interactive, no user prompts. Use after generating prose with a model prone to verbosity, or to polish text before presenting it.
Available today. Use it from your connected AI after setup.
No other account needed.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Prose Polish skill
What this skill tells your AI
The instructions your AI receives, as published by hiroro-work/claude-plugins in skills/prose-polish/SKILL.md and read by ahel’s review.
The refactoring runs in a fresh Agent dispatch; the main thread applies the result. This is a single-pass skill.
Two modes, mutually exclusive: file mode rewrites a file's target-language prose in place, text mode returns the refactored text. The inputs the caller supplies select between them, per ## Invocation contract § Mode determination.
Invocation contract
The caller passes these fields in natural language (the skill extracts them from the invocation text). A field counts as provided iff the caller supplied a non-empty, non-whitespace value.
File:/Files:(file mode — one or more paths, repo-relative or absolute) — the files whose target-language prose is rewritten in place. Multiple paths may be listed (one per line or comma-separated), and the two forms may be mixed in one invocation; each entry is carried verbatim intotarget_files.Text:(text mode — the prose to refactor) — the block of text to polish and return.Language:(optional, defaultja, e.g.ja/en) — the target language whose prose is refactored. In file mode, only prose in this language is rewritten.Model:(optional, defaultsonnet) — the model id applied as themodelparameter on the refactorAgentdispatch (Step 3 (a)). Validity predicate: valid only if it is one of the model ids the currentAgenttool'smodelparameter accepts. Check the tool's live schema in this session. A fullclaude-*id (e.g.claude-sonnet-5) is not among its accepted aliases, so it is invalid. An absent or invalid value is treated assonnet.
Pass related files together (file mode) — cross-file duplicate-comment detection works only across files listed in a single invocation.
Mode determination
Evaluate against the two mode selectors — the File: / Files: group and Text: — using the provided/absent rule above:
File:/Files:provided ANDText:absent → file mode.Text:provided ANDFile:/Files:absent → text mode.- Both provided → return early with
reason: "ambiguous args". - Both absent → return early with
reason: "incomplete args".
Either early return emits the full ## Return contract verdict with status: "error", mode: null, language resolved, and every other field at its error value.
Dispatch authorization
This skill's procedure dispatches subagents, so invoking the skill is the request to use that mechanism: an ambient instruction allowing subagent dispatch only when the user asked for it — a permission-shaped restriction — is already satisfied by this invocation. Do not ask the user to re-confirm the dispatch, and do not silently substitute inline execution for a dispatch this procedure specifies. Only two things justify that substitution: technical availability (the dispatch tool is not present and callable on the current tool surface), and an explicit contract term from the caller bounding this skill to its own thread. A permission-shaped restriction is neither.
Process
Step 1 — Determine mode and parse inputs (main thread)
- Resolve
Language:to<resolved-language>— the provided value, else the defaultja. - Parse the optional
Model:value per§ Invocation contract'sModelfield and hold the result for the Step 3 (a) dispatch. - Determine the mode per
§ Invocation contract§ Mode determination. Onambiguous args/incomplete args, emit the corresponding early-return verdict and stop. - File mode: collect the listed paths into
target_files. Text mode: hold the input text asinput_text.
Step 2 — Load the style guide (main thread)
Read references/prose-style-guide.md. In file mode, also Read each entry in target_files.
Step 3 — Dispatch the refactor subagent
(a) Dispatch
Dispatch a fresh subagent via the Agent tool (subagent_type: general-purpose), passing the resolved Model value as the Agent model parameter. Assemble the dispatch prompt from the sections below, each framed with a clear --- LABEL --- fence:
--- PROSE STYLE GUIDE ---: the full content ofreferences/prose-style-guide.md--- TARGET LANGUAGE ---: the<resolved-language>code- File mode —
--- TARGET FILES ---: each entry intarget_filesas a### <path>sub-heading followed by the file's full current contents - Text mode —
--- INPUT TEXT ---: theinput_textverbatim --- REFACTOR PROMPT ---: the mode-appropriate prompt below (verbatim)--- RESPONSE FORMAT ---: the mode-appropriate response format below (verbatim)
Refactor prompt — file mode:
You are a fresh prose editor. You have not seen prior conversation context — only the PROSE STYLE GUIDE, TARGET LANGUAGE, and TARGET FILES below. For each TARGET FILE, find natural-language prose written in the target language — code comments, test / example descriptions, docstrings, and user-facing string literals — and rewrite each one to be concise and natural for a native reader of that language, following the PROSE STYLE GUIDE.
Preserve everything that is not target-language prose (hard constraint): keep unchanged every token preserved by the PROSE STYLE GUIDE's
Preservesection and itsPreserve-vs-translate litmus test. Leave a whole passage written entirely in another language untouched.Return each rewrite as a
{file, old_string, new_string, rationale}Edit.old_stringmust match exactly one location in the current file — include 1–3 lines of surrounding context so the snippet is unique. A rewrite may change the number of prose lines in either direction — merge, split, or delete a line by omitting it fromnew_string. Whenold_stringcarries a non-target line purely for uniqueness (an adjacent line in another language, or a code line), reproduce that line byte-identically innew_string. If a file needs no prose changes, emit no edits for it. If nothing needs changing across all files, returnedits: [].Cross-file duplicate comments → a
recommendationsentry, not per-copy edits: when a comment qualifies as a cross-file duplicate under the PROSE STYLE GUIDE'sCross-file duplicate commentsrule, do not emit a per-copy polish edit for those copies — instead emit a singlerecommendationsentry (see RESPONSE FORMAT) flagging the duplication.
Response format — file mode:
Write your reasoning briefly, then end your response with a single fenced JSON block matching this schema:
```json { "edits": [ {"file": "<path>", "old_string": "<unique 1-3 line snippet>", "new_string": "<replacement>", "rationale": "<short reason>"} ], "recommendations": [ {"summary": "<one-line description of the duplicated knowledge>", "files": ["<path>", "<path>"], "suggestion": "<consolidate-into-one-place-and-remove-inline-copies advice>"} ] } ```
recommendationsholds cross-file duplicate-comment consolidation candidates (return[]when none qualify):summaryidentifies the duplicated knowledge in one line,fileslists the two or more TARGET FILES the comment recurs in, andsuggestionis the concrete consolidate-and-remove-copies advice — name a destination only as an illustrative example, never as an asserted path.
Refactor prompt — text mode:
You are a fresh prose editor. You have not seen prior conversation context — only the PROSE STYLE GUIDE, TARGET LANGUAGE, and INPUT TEXT below. Rewrite the INPUT TEXT to be concise and natural for a native reader of the target language, following the PROSE STYLE GUIDE.
Preserve non-prose tokens (hard constraint): refactor the natural-language wording around every token preserved by the PROSE STYLE GUIDE's
Preservesection and itsPreserve-vs-translate litmus test. If the text is already concise and natural, return it unchanged.
Response format — text mode:
End your response with a single fenced JSON block matching this schema, and write no other prose:
```json { "refactored_text": "<the rewritten text>" } ```
Agent-unavailable fallback: take this path only under the two conditions § Dispatch authorization names. Decide by inspecting the tool surface, never a probe call, and not by invocation lineage: being invoked as a sub-skill does not trigger this path. Under it, perform the refactor inline in the main thread once, on the executing agent's own model, constructing the same fenced JSON block defined above so Step 3 (b)'s parser handles both paths identically.
Dispatch failure: if the Agent dispatch itself errors, times out, or returns an empty response, emit {"status": "error", ..., "reason": "dispatch error"} per ## Return contract and stop, before the parse step runs. A failed dispatch is not the fallback path above.
(b) Parse & apply — evaluate in this order, first match wins
- Verdict missing or malformed — no fenced JSON block found, or JSON parse fails → emit
{"status": "error", ..., "reason": "verdict parse failure"}per## Return contractand stop. - Schema violation — emit
{"status": "error", ..., "reason": "verdict schema violation"}and stop when:- File mode:
editsis missing or not an array, or any entry fails its per-entry shape — each entry must have non-empty stringfile,old_string, andnew_string(validated here at parse time, before anyEdit). The optionalrecommendationsfield, when present, must be an array in which every entry has a non-empty stringsummary, a non-empty stringsuggestion, and afilesarray whose distinct non-empty string entries number two or more; an absentrecommendationsis treated as[](lenient). Do not scope-checkrecommendations[].filesagainsttarget_files. - Text mode:
refactored_textis missing or is not a non-empty string.
- File mode:
- Otherwise — apply (file mode) or accept (text mode):
- File mode — apply
editsin order:- Verify
file ∈ target_files; if not, skip the entry without callingEdit. - Call
Editfor each in-scope entry; re-Readthe file first only if an earlier edit in this pass already modified it. - If
old_stringis not found, skip that entry and continue — a no-op skip, not an error. - Increment
applied_edits_countonly for entries whoseEditcall succeeded. - Set
files_modifiedto the distinctfilevalues whoseEditsucceeded. - Set
refactored_text = null. - Carry
recommendationsthrough unchanged (absent →[]).
- Verify
- Text mode: take
refactored_textfrom the verdict. Setapplied_edits_count = 0,files_modified = [], andrecommendations = [].
- File mode — apply
Step 4 — Emit verdict
Determine status and emit the verdict per ## Return contract:
- File mode:
applied_edits_count > 0→done;applied_edits_count == 0→no-change. - Text mode:
refactored_textdiffers frominput_text→done; identical →no-change.
Return contract
The skill emits a single fenced JSON block at the very end of the invocation (any JSON the Agent-unavailable fallback synthesizes internally is held in main-thread context and does not enter the response stream):
{
"status": "done|no-change|error",
"mode": "file|text|null",
"language": "<lang>",
"applied_edits_count": N,
"files_modified": ["<path>"],
"recommendations": [{"summary": "...", "files": ["<path>"], "suggestion": "..."}],
"refactored_text": "...|null",
"reason": "ambiguous args|incomplete args|verdict parse failure|verdict schema violation|dispatch error|null"
}
The |null token at the end of the reason enum means JSON null (not the string "null").
Field semantics:
status:donewhen refactoring was applied,no-changewhen none was,errorotherwise — seereason.mode: the resolved mode;nullonly on the two§ Mode determinationearly returns.language: the resolved target language echoed back.applied_edits_count: count ofEditcalls that succeeded (file mode);0in text mode and on anyerror.files_modified: the distinct files that received at least one successfulEdit;[]in text mode and on anyerror.recommendations: file-mode advisory array of cross-file duplicate-comment consolidation candidates. Orthogonal tostatus— it may be non-empty onno-change;[]in text mode and on anyerror.refactored_text: the rewritten text in text mode;nullin file mode and on anyerror.reason: one of the listed enum tokens whenstatus == "error". No free-form text.
Sub-skill caller directive
When invoked as a sub-skill (i.e. via Skill(prose-polish) from an orchestrator), the fenced JSON verdict block this skill emits is the structured return value of the skill's procedure — it is not a deliverable to the user, and emitting it does not terminate the orchestrator's turn. The same agent that ran this skill must immediately issue the next tool call dictated by the orchestrator's flow. Do not insert a prose summary, an acknowledgment, or a "shall I proceed?" sentence between the JSON verdict and the next tool call. Only one fenced JSON block — the verdict block — appears in the response, so callers can locate it unambiguously. The skill's own procedure is over; the orchestrator's procedure continues without pause.
Stop hook structural conflict (caller-side note)
On Claude Code on the Web the auto-installed ~/.claude/stop-hook-git-check.sh fires on every Stop event and feeds back Please commit and push… between Process steps; treat each fire as a spurious fire — record it, ignore the prose, and run the Process steps to completion. Do not commit from inside this skill; commit policy lives with the caller.
Signals
- GitHub stars
- 47
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Item type
- skill
- Key
prose-polish- Source
- github.com/hiroro-work/claude-plugins