Strip the AI packaging. Keep the meaning.
Detects and rewrites AI writing patterns in Korean, English, Chinese, and Japanese. Runs as a skill for Claude Code, Codex CLI, Cursor, and OpenCode, or as a standalone Node.js CLI.
Unlike a generic paraphraser, patina is pattern-based and auditable: it shows what it changed, why it changed it, and whether the original claims were preserved.
Before (AI-sounding):
Coffee has emerged as a pivotal cultural phenomenon that has fundamentally transformed social interactions across the globe. This beloved beverage serves as a catalyst for community building, fosters meaningful connections, and facilitates cross-cultural dialogue.
After (/patina --lang en — same claims, AI packaging removed):
Coffee has quietly changed how people meet. Sit across from someone long enough, and something like a real connection tends to form — even between people from very different cultures.
MPS = 100 · cultural transformation ✓ · community building ✓ · meaningful connections ✓ · cross-cultural dialogue ✓
More examples: Before/After Gallery. Social preview asset: patina-before-after.svg.
| 146 patterns | 37 KO + 36 EN + 36 ZH + 37 JA (each incl. 5 score-only viral-hook) — see PATTERNS.md |
| AI catch rate | 91% Korean / 76% English (HC3) |
| False positives | 13–25% on human prose (boundary intrinsic to encyclopedic register, documented) |
| Modes | rewrite · audit · score · diff · ouroboros |
| Free tier | Yes — via codex CLI (no API key) |
| Determinism | Scoring formula is deterministic; LLM severity assignment ±8–10 pt per run (scoring.md §8) |
| License | MIT |
curl -fsSL https://raw.githubusercontent.com/devswha/patina/main/install.sh | bashThe installer wires patina into Claude Code, Codex CLI, Cursor, and OpenCode. Then:
/patina --lang en
[paste your text here]
Rewrite with a specific tone:
/patina --tone narrative
[paste your essay draft here]
Auto-detect and apply the best-fit tone:
/patina --tone auto --lang en
[paste your text here]
Requires Node.js ≥ 18.
git clone https://github.com/devswha/patina.git
cd patina && npm install && npm link
patina --lang en input.txt🆓 No API key required if you have the
codexCLI logged in. See AUTHENTICATION.md for the full backend list.
patina --lang <ko|en|zh|ja> [mode] [--profile <name>] input.txt
| Flag | What it does |
|---|---|
| (default) | Rewrite |
--audit |
Detect AI patterns only |
--score |
0–100 AI-likeness score with category breakdown |
--diff |
Show changes pattern by pattern |
--ouroboros |
Iterate the rewrite until score converges (with MPS rollback) |
--lang <ko|en|zh|ja> |
Select language (default: ko) |
--profile <name> |
Tone preset: blog, academic, technical, formal, social, email, legal, medical, marketing, narrative, instructional, casual-conversation |
--tone <name> |
Tone category: casual, professional, academic, narrative, marketing, instructional, auto |
--batch |
Treat positional args as a list of files (e.g. --batch docs/*.md) |
patina --help for the full flag list.
--score and --audit measure a slightly broader set of signals than --rewrite does. The viral-hook packs (ko/en/zh/ja-viral-hook, 5 patterns each: shock-number hooks, clickbait closings, source-skipping authority claims, breath-optimized short-sentence stacking, hyperbolic engagement lexicon) are detection-only — they surface in the score and audit so the benchmark matches human intuition for SNS-style marketing copy across all four languages, but --rewrite/--diff/--ouroboros skip them because those signals are often intentional rhetoric. Real-world demos: examples/viral-hook/.
--prompt-mode strict|minimal|auto lets you trade off between the full pattern packs (~34KB structured prompt) and a compressed casual instruction (~3KB). auto picks per backend — Gemini does better on minimal (it gets over-constrained by long structured prompts), while Claude leverages the full packs and Codex is roughly insensitive. case-05 documents the A/B.
--variants <1-5> asks the model for N voice variants of the rewrite in one call (e.g., V1 casual, V2 direct, V3 measured) — facts, numbers, and causation stay identical across variants. Each comes back as ## Variant N so you can pick the voice you want.
For inputs ≤200 chars or ≤3 paragraphs, register-sensitive categories (language, style, viral-hook) get a 1.5× severity multiplier so single-paragraph voice shifts surface in the score. case-04 found these were undercounted by the long-text formula.
In rewrite mode, the model emits its self-audit notes inside [SELF_AUDIT]/[/SELF_AUDIT] tags wrapped around a [BODY]/[/BODY] block (or [VARIANT n] blocks when --variants > 1). patina strips the audit before showing the user, so raw output is clean — earlier versions sometimes leaked phrases like "남아 있는 AI 티" or "Phase 3" preambles into the user-facing text.
--score runs cross-check the Weight column the model emits against your config's category-weights. If the model invents a category (e.g., discord) or substitutes a different number, [patina] warnings hit stderr — observability only, the score itself isn't altered.
--tone selects a named voice axis applied on top of pattern rewriting. Resolution order: --tone CLI > tone: config > profile: config.
| Tone | Intended for | Key behaviors |
|---|---|---|
casual |
Blog posts, social content, personal notes | Contractions, first-person, emoticons OK, low formality |
professional |
Work emails, reports, business writing | Clear and concise, formal but not stiff; legal/medical sub-profiles force fidelity floor |
academic |
Papers, research summaries, technical analysis | Objective, evidence-oriented, minimal first-person |
narrative |
Personal essays, memoir, experience-based writing | First-person anchor, scene detail, emotional presence, time flow |
marketing |
Ad copy, landing pages, product announcements | Short impact sentences, persuasive, CTA-friendly |
instructional |
Tutorials, how-to guides, technical docs | Imperative verbs, numbered structure, hedging suppressed |
--tone auto runs heuristic detection (lexical + structural signals) and selects the best-fit tone. zh/ja with any tone (including auto) emits a warning and falls back to profile-only mode — Phase 4.5b heuristics only cover ko/en.
Run the same text through Claude, Codex, and Gemini independently. The lowest AI-score result that passes MPS ≥ 70 wins:
/patina-max
[paste your text here]
Input
↓
[Step 4.5] Semantic anchor extraction (claims, polarity, causation, numbers)
[Step 4.6] Stylometric pre-pass (burstiness CV + MATTR)
[Step 4.7] AI-lexicon overlap (~108 EN / 102 KO entries)
[Phase 1] Structure scan + anchor verification
[Phase 2] Sentence rewrite + anchor verification
[Phase 3] Self-audit (polarity, regression, MPS)
↓
Natural-sounding text (meaning verified)
If meaning drifts at any verification step, the change is retried or rolled back.
Calibration (500-paragraph corpus, reproducible via .omc/research/v3_8_remeasure.py): 76% AI catch on HC3 ChatGPT (en), 91% on paired ko/AI corpus, 13–25% FP on human prose. Acceptance gates: AI ≥ 75%, max FP ≤ 25%. See stylometry.md for the algorithm.
# .patina.default.yaml
version: "3.11.0"
language: ko # ko | en | zh | ja
profile: default
output: rewrite # rewrite | diff | audit | score
tone: # casual | professional | academic | narrative | marketing | instructional | auto
max-models: [claude, gemini]Pattern packs are auto-discovered by language prefix. .patina.yaml in the working directory overrides defaults.
- Patterns — full 126-pattern catalog
- Authentication — backends, providers, free-tier setup
- FAQ — detector-bypass concerns, MPS, false positives, contribution starting points
- Launch Copy — Show HN, Reddit, X, Korean community drafts
- Stylometry — burstiness + MATTR + AI-lexicon algorithm
- Scoring — AI-likeness + fidelity + MPS
- Changelog — release notes and methodology
- Contributing — pattern submissions, staleness reports
Inspired by oh-my-zsh's plugin architecture (patterns are plugins, profiles are themes), Wikipedia's "Signs of AI writing" catalog, and blader/humanizer.
MIT