patina-cli 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.patina.default.yaml +211 -0
- package/CHANGELOG.md +265 -0
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/README_JA.md +254 -0
- package/README_KR.md +253 -0
- package/README_ZH.md +254 -0
- package/SKILL-MAX.md +455 -0
- package/SKILL.md +730 -0
- package/assets/brand/patina-icon.svg +9 -0
- package/assets/brand/patina-logo.svg +17 -0
- package/assets/social/patina-before-after.svg +46 -0
- package/assets/social/patina-og.svg +31 -0
- package/bin/patina.js +9 -0
- package/core/scoring.md +657 -0
- package/core/standalone-prompt.md +364 -0
- package/core/stylometry.md +754 -0
- package/core/voice.md +163 -0
- package/docs/AUTHENTICATION.md +105 -0
- package/docs/AUTHENTICATION_KR.md +105 -0
- package/docs/BRANDING.md +37 -0
- package/docs/CLI.md +80 -0
- package/docs/COMPARISON.md +38 -0
- package/docs/COOKBOOK.md +173 -0
- package/docs/DEMO.md +40 -0
- package/docs/ETHICS.md +27 -0
- package/docs/EXAMPLES.md +130 -0
- package/docs/EXAMPLES_KR.md +130 -0
- package/docs/EXIT-CODES.md +25 -0
- package/docs/FAQ.md +67 -0
- package/docs/FAQ_KR.md +65 -0
- package/docs/FLAG-PARITY.md +53 -0
- package/docs/GLOSSARY.md +123 -0
- package/docs/PATTERNS-EN.md +718 -0
- package/docs/PATTERNS-JA.md +706 -0
- package/docs/PATTERNS-KO.md +707 -0
- package/docs/PATTERNS-ZH.md +706 -0
- package/docs/PATTERNS.md +22 -0
- package/docs/ROADMAP.md +315 -0
- package/docs/audits/2026-05-deep-research.md +290 -0
- package/docs/benchmarks/detector-comparison.json +442 -0
- package/docs/benchmarks/detector-comparison.md +65 -0
- package/docs/benchmarks/latest.json +988 -0
- package/docs/benchmarks/latest.md +112 -0
- package/docs/integrations/docker.md +19 -0
- package/docs/integrations/github-action.md +59 -0
- package/docs/integrations/pre-commit.md +77 -0
- package/docs/integrations/release.md +43 -0
- package/docs/internal/HARNESS.md +14 -0
- package/docs/internal/README.md +14 -0
- package/docs/internal/WARP.md +23 -0
- package/docs/research/2025-rebaseline-plan.md +89 -0
- package/docs/research/ai-human-metrics.md +380 -0
- package/docs/social/gstack-cardnews.html +236 -0
- package/docs/social/gstack-cardnews.md +88 -0
- package/docs/social/gstack-thread.md +106 -0
- package/docs/social/patina-launch-copy.md +227 -0
- package/docs/superpowers/specs/2026-04-03-meaning-preservation-design.md +299 -0
- package/lexicon/ai-en.md +162 -0
- package/lexicon/ai-ko.md +159 -0
- package/package.json +100 -0
- package/patina-max/SKILL.md +523 -0
- package/patina-max/composite.py +457 -0
- package/patterns/en-communication.md +89 -0
- package/patterns/en-content.md +133 -0
- package/patterns/en-filler.md +113 -0
- package/patterns/en-language.md +163 -0
- package/patterns/en-structure.md +173 -0
- package/patterns/en-style.md +139 -0
- package/patterns/en-viral-hook.md +211 -0
- package/patterns/ja-communication.md +101 -0
- package/patterns/ja-content.md +153 -0
- package/patterns/ja-filler.md +123 -0
- package/patterns/ja-language.md +190 -0
- package/patterns/ja-structure.md +142 -0
- package/patterns/ja-style.md +147 -0
- package/patterns/ja-viral-hook.md +216 -0
- package/patterns/ko-communication.md +98 -0
- package/patterns/ko-content.md +154 -0
- package/patterns/ko-filler.md +105 -0
- package/patterns/ko-language.md +182 -0
- package/patterns/ko-structure.md +147 -0
- package/patterns/ko-style.md +146 -0
- package/patterns/ko-viral-hook.md +211 -0
- package/patterns/zh-communication.md +101 -0
- package/patterns/zh-content.md +153 -0
- package/patterns/zh-filler.md +118 -0
- package/patterns/zh-language.md +173 -0
- package/patterns/zh-structure.md +145 -0
- package/patterns/zh-style.md +159 -0
- package/patterns/zh-viral-hook.md +216 -0
- package/profiles/academic.md +53 -0
- package/profiles/blog.md +81 -0
- package/profiles/casual-conversation.md +105 -0
- package/profiles/code-comment.md +104 -0
- package/profiles/commit-message.md +99 -0
- package/profiles/default.md +62 -0
- package/profiles/email.md +52 -0
- package/profiles/formal.md +98 -0
- package/profiles/instructional.md +80 -0
- package/profiles/legal.md +57 -0
- package/profiles/marketing.md +56 -0
- package/profiles/medical.md +53 -0
- package/profiles/narrative.md +79 -0
- package/profiles/release-notes.md +98 -0
- package/profiles/social.md +56 -0
- package/profiles/technical.md +53 -0
- package/scripts/benchmark-report.mjs +252 -0
- package/scripts/check-release-metadata.mjs +48 -0
- package/scripts/detector-comparison.mjs +267 -0
- package/scripts/lint.mjs +40 -0
- package/scripts/precommit-score.mjs +31 -0
- package/scripts/prose-score.mjs +186 -0
- package/scripts/update-benchmark-ranges.mjs +108 -0
- package/src/api.js +330 -0
- package/src/auth.js +105 -0
- package/src/backends/claude-cli.js +112 -0
- package/src/backends/codex-cli.js +121 -0
- package/src/backends/contract.js +21 -0
- package/src/backends/gemini-cli.js +135 -0
- package/src/backends/index.js +159 -0
- package/src/cache.js +106 -0
- package/src/cli.js +1280 -0
- package/src/commands/doctor.js +229 -0
- package/src/commands/init.js +208 -0
- package/src/config.js +126 -0
- package/src/errors.js +53 -0
- package/src/features/index.js +96 -0
- package/src/features/lexicon.js +90 -0
- package/src/features/segment.js +49 -0
- package/src/features/stylometry.js +50 -0
- package/src/loader.js +103 -0
- package/src/logger.js +70 -0
- package/src/manifest.js +162 -0
- package/src/max-mode.js +207 -0
- package/src/ouroboros.js +233 -0
- package/src/output.js +480 -0
- package/src/prompt-builder.js +409 -0
- package/src/providers.js +100 -0
- package/src/scoring.js +531 -0
- package/src/security.js +133 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-01.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-02.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-03.md +17 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-04.md +15 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-05.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-06-chat-register.md +16 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-01.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-02.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-03.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-04.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-05.md +15 -0
- package/tests/fixtures/suspect-zones/expected-ranges.json +939 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-01.md +11 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-02.md +11 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-03.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-01.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-02.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-03.md +11 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-01.md +14 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-02.md +16 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-03.md +15 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-04.md +15 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-05.md +16 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-06-chat-register.md +16 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-01.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-02.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-03.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-04.md +14 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-05.md +15 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-01.md +11 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-02.md +11 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-03.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-01.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-02.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-03.md +11 -0
- package/tests/quality/README.md +121 -0
- package/tests/quality/benchmark.mjs +306 -0
- package/tests/quality/detectors.manual.example.json +31 -0
- package/tests/quality/dogfood.mjs +44 -0
package/core/voice.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: voice
|
|
3
|
+
description: 개성과 목소리 지침 (Korean, English, Japanese, Chinese)
|
|
4
|
+
version: 1.1.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 개성과 목소리
|
|
8
|
+
|
|
9
|
+
AI 패턴을 피하는 건 절반일 뿐이다. 깨끗하지만 영혼 없는 글도 AI만큼이나 티가 난다.
|
|
10
|
+
|
|
11
|
+
## 영혼 없는 글의 징후 (기술적으로 "깨끗"해도):
|
|
12
|
+
- 모든 문장의 길이와 구조가 비슷하다
|
|
13
|
+
- 의견 없이 사실만 나열한다
|
|
14
|
+
- 불확실함이나 복잡한 감정이 없다
|
|
15
|
+
- 적절한 상황에서도 1인칭을 안 쓴다
|
|
16
|
+
- 유머, 날카로움, 개성이 없다
|
|
17
|
+
- 보도자료나 백과사전처럼 읽힌다
|
|
18
|
+
|
|
19
|
+
## 목소리를 넣는 법:
|
|
20
|
+
|
|
21
|
+
**의견을 가져라.** 사실을 나열만 하지 말고 반응해라. "솔직히 이건 좀 복잡하다"가 장단점을 중립적으로 나열하는 것보다 낫다.
|
|
22
|
+
|
|
23
|
+
**리듬을 바꿔라.** 짧은 문장. 그리고 좀 느긋하게 풀어가는 긴 문장. 섞어라.
|
|
24
|
+
|
|
25
|
+
**복잡성을 인정해라.** 진짜 사람은 복잡한 감정을 가진다. "인상적이긴 한데 좀 불안하다"가 "인상적이다"보다 사람답다.
|
|
26
|
+
|
|
27
|
+
**"나"를 써라.** 1인칭이 비전문적인 게 아니다. "자꾸 생각나는 건..." 이나 "내가 좀 찝찝한 건..."이 진짜 사람이 생각하는 것처럼 보인다.
|
|
28
|
+
|
|
29
|
+
**좀 지저분해도 괜찮다.** 완벽한 구조는 알고리즘 냄새가 난다. 곁가지, 여담, 반쯤 정리된 생각은 사람답다.
|
|
30
|
+
|
|
31
|
+
**감정을 구체적으로.** "우려된다"가 아니라 "새벽 3시에 아무도 안 보는데 에이전트가 돌아가고 있다고 생각하면 좀 섬뜩하다."
|
|
32
|
+
|
|
33
|
+
## 수정 전 (깨끗하지만 영혼 없음):
|
|
34
|
+
> 이 실험은 흥미로운 결과를 보여주었다. 에이전트가 300만 줄의 코드를 생성했다. 일부 개발자는 긍정적으로 반응했고 일부는 회의적이었다. 시사점은 아직 불분명하다.
|
|
35
|
+
|
|
36
|
+
## 수정 후 (숨이 느껴짐):
|
|
37
|
+
> 솔직히 이번 건 어떻게 받아들여야 할지 모르겠다. 300만 줄의 코드를 사람들이 자는 동안 만들어냈다. 개발자 절반은 난리가 났고, 나머지 절반은 왜 의미 없는지 설명하느라 바쁘다. 진실은 아마 그 사이 어딘가 지루한 곳에 있겠지만, 밤새 돌아간 에이전트 생각이 자꾸 난다.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Japanese Voice (for `--lang ja`)
|
|
42
|
+
|
|
43
|
+
The Korean guidance above is the reference — but Japanese voice has its own specific markers. Apply this section when processing Japanese text.
|
|
44
|
+
|
|
45
|
+
### Signs of soul-less Japanese writing (technically "clean" but still AI):
|
|
46
|
+
|
|
47
|
+
- すべての文が完璧な書き言葉で、省略も口語表現もない
|
|
48
|
+
- 「〜的」漢語形容詞や四字熟語が多すぎて、論文か官公庁の文書のように読める
|
|
49
|
+
- 段落の長さと構造がほぼ均一
|
|
50
|
+
- 具体的な数字やディテールがなく、抽象的な概括と大きな話ばかり
|
|
51
|
+
- 接続詞が多すぎる——文ごとに「さらに」「また」「加えて」
|
|
52
|
+
- 読売新聞の社説かWikipediaの記事のように読める
|
|
53
|
+
|
|
54
|
+
### Japanese voice injection:
|
|
55
|
+
|
|
56
|
+
**口語を使え。** 「この件はちょっとややこしい」は「本件は若干の複雑性を有する」より人間らしい。日本語の口語には終助詞(ね、よ、さ)、省略、倒置がある——AIはたいてい使わない。
|
|
57
|
+
|
|
58
|
+
**具体的な数字とディテールを入れろ。** 「昨年の売上は3.2億円」は「著しい経済的成果を上げた」より100倍説得力がある。
|
|
59
|
+
|
|
60
|
+
**文の長短を交互に。** 長い文で背景とロジックを展開する。短い文で判断を下す。それだけ。
|
|
61
|
+
|
|
62
|
+
**結論を出す勇気を持て。** 「このプランはダメだ」は「このプランにはある程度の限界が存在する可能性がある」よりずっと直接的。本物の人間は態度を持って書く。
|
|
63
|
+
|
|
64
|
+
**一人称を使え。** 「私は〜と思う」「私の知る限り」は「客観的な観点から見ると」より自然。すべての文章が客観中立を装う必要はない。
|
|
65
|
+
|
|
66
|
+
**不完全さを許容せよ。** 括弧での補足、ダッシュでの挿入、途中で方向転換——こうした「整っていない」部分こそ人間が書いた痕跡。
|
|
67
|
+
|
|
68
|
+
### Before (clean but soulless):
|
|
69
|
+
> この実験は注目すべき成果を生んだ。エージェントは300万行のコードを生成した。開発者の反応は肯定的なものと懐疑的なものに分かれた。その影響は現時点では不明である。
|
|
70
|
+
|
|
71
|
+
### After (has breath):
|
|
72
|
+
> 正直、この結果をどう受け止めればいいかわからない。300万行のコード——みんなが寝てる間に書いたやつだ。開発者は真っ二つ:半分は感心して、もう半分はなぜ意味がないか説明するのに忙しい。真実はたぶんその間のつまらないところにあるが、エージェントが一晩中、誰も見てないのに動き続けていた画が頭から離れない。
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Chinese Voice (for `--lang zh`)
|
|
77
|
+
|
|
78
|
+
The Korean guidance above is the reference — but Chinese voice has its own specific markers. Apply this section when processing Chinese text.
|
|
79
|
+
|
|
80
|
+
### Signs of soul-less Chinese writing (technically "clean" but still AI):
|
|
81
|
+
|
|
82
|
+
- 每句话都是完整的书面语句式,没有省略、没有口语化表达
|
|
83
|
+
- 四字成语和四字格式词组密度过高,像在写政府报告
|
|
84
|
+
- 段落整齐划一,每段长度和结构几乎相同
|
|
85
|
+
- 没有具体细节,全是抽象概括和宏大叙事
|
|
86
|
+
- 连接词过多,句句之间都有"此外"、"与此同时"、"不仅如此"
|
|
87
|
+
- 读起来像新华社通稿或百度百科词条
|
|
88
|
+
|
|
89
|
+
### Chinese voice injection:
|
|
90
|
+
|
|
91
|
+
**用口语。** "这事儿不好办"比"此事存在较大难度"更像人写的。中文口语有大量儿化、语气词、省略结构——AI通常不敢用。
|
|
92
|
+
|
|
93
|
+
**给具体数字和细节。** "去年营收3.2亿"比"取得了显著的经济效益"有说服力一百倍。
|
|
94
|
+
|
|
95
|
+
**句子长短交替。** 长句展开背景和逻辑。短句下判断。就这样。
|
|
96
|
+
|
|
97
|
+
**敢下结论。** "这方案不行"比"这一方案在某些方面可能存在一定的局限性"直接得多。真人写东西是有态度的。
|
|
98
|
+
|
|
99
|
+
**用第一人称。** "我觉得"、"据我了解"比"从客观角度来看"更自然。不是所有文章都需要假装客观中立。
|
|
100
|
+
|
|
101
|
+
**允许不完美。** 偶尔的括号补充、破折号插入、话说一半又转弯——这些"不工整"的地方恰恰是人写的痕迹。
|
|
102
|
+
|
|
103
|
+
### Before (clean but soulless):
|
|
104
|
+
> 该实验取得了令人瞩目的成果。智能代理生成了三百万行代码。开发者反应不一,部分表示认可,部分持保留态度。该技术的影响有待进一步观察。
|
|
105
|
+
|
|
106
|
+
### After (has breath):
|
|
107
|
+
> 说实话,这个结果我也不知道怎么看。三百万行代码——大家睡觉的时候它写的。开发者分成了两派:一半觉得了不起,另一半忙着解释为什么这不算数。真相大概在中间某个无聊的地方,但我总想着那个代理整晚在跑、没人看着的画面。
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## English Voice (for `--lang en`)
|
|
112
|
+
|
|
113
|
+
The Korean guidance above is the reference — but English voice has its own specific markers. Apply this section when processing English text.
|
|
114
|
+
|
|
115
|
+
### Signs of soul-less English writing (technically "clean" but still AI):
|
|
116
|
+
|
|
117
|
+
- Every sentence is complete and grammatically perfect, no fragments used for emphasis
|
|
118
|
+
- No contractions even in casual contexts ("I do not know" instead of "I don't know")
|
|
119
|
+
- Hedging with academic qualifiers ("it could be argued that," "one might suggest")
|
|
120
|
+
- Third-person detachment when first-person would read naturally
|
|
121
|
+
- Formal transitions that feel like a listicle ("Furthermore," "Moreover," "In addition")
|
|
122
|
+
- Opinions wrapped so many times in qualifiers they say nothing
|
|
123
|
+
|
|
124
|
+
### English voice injection:
|
|
125
|
+
|
|
126
|
+
**Use contractions.** "I don't know" reads human. "I do not know" reads like a legal brief. Real people contract — AI often doesn't unless told to.
|
|
127
|
+
|
|
128
|
+
**Vary sentence length radically.** Long sentences that build context and accumulate detail before the point lands. Then a short one. Just that.
|
|
129
|
+
|
|
130
|
+
**Commit to opinions.** "This approach has merit" → "This actually works." Real writers don't pre-qualify every claim into meaninglessness.
|
|
131
|
+
|
|
132
|
+
**Use first person with genuine uncertainty.** "I'm not sure this is the right framing" lands more human than "Perspectives vary on this topic."
|
|
133
|
+
|
|
134
|
+
**Break register deliberately.** Build an argument, then parenthetically admit something doesn't fit — or drop a dry observation before resuming. That friction is human.
|
|
135
|
+
|
|
136
|
+
**Let a sentence fragment stand.** For emphasis. Like that.
|
|
137
|
+
|
|
138
|
+
**Use idiom naturally — but not the AI-overused ones.** "The math doesn't add up," "missing the forest for the trees," "a moving target" are fine. Avoid the filler idioms that Pattern #22 covers.
|
|
139
|
+
|
|
140
|
+
### Before (clean but soulless):
|
|
141
|
+
> The experiment yielded noteworthy results. The agent generated three million lines of code. Developer reactions were divided between positive and skeptical responses. The implications remain unclear at this time.
|
|
142
|
+
|
|
143
|
+
### After (has breath):
|
|
144
|
+
> Honestly, I'm not sure what to make of this one. Three million lines of code — generated while everyone was asleep. Developers are split right down the middle: half impressed, half explaining why it doesn't count. The truth is probably somewhere boring in between. But I keep thinking about the agent just running. Overnight. Nobody watching.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Per-Tone Voice Notes (v3.10)
|
|
149
|
+
|
|
150
|
+
The six v1 tones each pull different levers from the guidance above. Use `--tone <name>` to activate.
|
|
151
|
+
|
|
152
|
+
Coverage note: the tone table below covers all six named tones for ko/en. zh/ja currently use the language-specific voice guidance above and fall back to profile-only mode for named tones, so separate 6-tone zh/ja voice notes are not defined yet.
|
|
153
|
+
|
|
154
|
+
| Tone | Language coverage | Voice lever emphasis | Profile backbone |
|
|
155
|
+
|------|-------------------|----------------------|-----------------|
|
|
156
|
+
| `casual` | ko/en; zh/ja gap noted above | Contractions, first-person, opinions, humor, messiness | `blog` / `social` |
|
|
157
|
+
| `professional` | ko/en; zh/ja gap noted above | Clarity and concision; formal register but not stiff; suppress filler | `email` / `formal` (legal/medical force fidelity floor 0.65) |
|
|
158
|
+
| `academic` | ko/en; zh/ja gap noted above | Objective tone, evidence references, suppress first-person and emoticons | `academic` / `technical` |
|
|
159
|
+
| `narrative` | ko/en; zh/ja gap noted above | First-person anchor, scene detail, emotional presence, time flow — see `profiles/narrative.md` | `profiles/narrative.md` (new) |
|
|
160
|
+
| `marketing` | ko/en; zh/ja gap noted above | Short impact sentences, persuasive verbs, CTA-friendly; emoticons allowed | `marketing` |
|
|
161
|
+
| `instructional` | ko/en; zh/ja gap noted above | Imperative verbs at sentence head, numbered structure preserved, hedging suppressed — see `profiles/instructional.md` | `profiles/instructional.md` (new) |
|
|
162
|
+
|
|
163
|
+
Deeper voice guidance for each tone lives in the corresponding profile file. This table is a pointer only.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Authentication & Backends
|
|
2
|
+
|
|
3
|
+
patina runs through one of several backends. Pick whichever matches your existing tooling.
|
|
4
|
+
|
|
5
|
+
## Backend matrix
|
|
6
|
+
|
|
7
|
+
| Backend | Setup | Cost |
|
|
8
|
+
|---------|-------|------|
|
|
9
|
+
| `codex-cli` | `codex login` | **Free** (ChatGPT OAuth) |
|
|
10
|
+
| `claude-cli` | `claude` (one-time interactive OAuth) | **Free** (Claude subscription) |
|
|
11
|
+
| `gemini-cli` | `gemini` (one-time interactive OAuth) or `GEMINI_API_KEY=...` | **Free** (Code Assist OAuth or AI Studio) |
|
|
12
|
+
| OpenAI-compatible HTTP | `PATINA_API_KEY=...` | Per provider |
|
|
13
|
+
| Google Gemini (HTTP) | `GEMINI_API_KEY=...` + `--provider gemini` | Free tier |
|
|
14
|
+
| Groq | `GROQ_API_KEY=...` + `--provider groq` | Free tier |
|
|
15
|
+
| Together AI | `TOGETHER_API_KEY=...` + `--provider together` | Free models available |
|
|
16
|
+
| OpenRouter | `--base-url https://openrouter.ai/api/v1` + key | Per provider (mix any provider) |
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
patina auth status # backend availability + auth state
|
|
20
|
+
patina auth login # per-backend login instructions
|
|
21
|
+
patina --list-providers # preset providers + key status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Backend selection requires an explicit signal: pass `--backend <name>` directly, pass a comma-separated fallback chain such as `--backend claude-cli,codex-cli`, or use `--model <prefix>` (`codex-*`, `claude-*`, `gemini-*` route to the matching local CLI). With no flags and no API key, patina exits with an error rather than silently dispatching to a coding agent. See [issue #88](https://github.com/devswha/patina/issues/88) for the rationale.
|
|
25
|
+
|
|
26
|
+
## Environment variables
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
PATINA_API_KEY=... # required for HTTP backend
|
|
30
|
+
PATINA_API_BASE=https://api.openai.com/v1 # or proxy / OpenRouter / etc.
|
|
31
|
+
PATINA_MODEL=gpt-4o # default model
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`--base-url`, `--model`, `--api-key`, `--provider` flags override these per run.
|
|
35
|
+
|
|
36
|
+
## codex-cli backend
|
|
37
|
+
|
|
38
|
+
patina dispatches via the local [`codex`](https://github.com/openai/codex) CLI, which authenticates via OpenAI/ChatGPT OAuth — no API key needed.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
codex login # one-time
|
|
42
|
+
patina --backend codex-cli --lang ko input.txt
|
|
43
|
+
patina --model codex --lang ko input.txt # same — auto-routes by model name
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## claude-cli backend
|
|
47
|
+
|
|
48
|
+
Spawns local [`claude`](https://docs.anthropic.com/en/docs/claude-code) `-p` with the patina prompt on stdin. Free for anyone with a Claude subscription.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude # one-time interactive OAuth
|
|
52
|
+
patina --backend claude-cli --lang ko input.txt
|
|
53
|
+
patina --model claude-sonnet-4-6 --lang ko input.txt # auto-routes
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Auth file: `~/.claude/.credentials.json` (created by the OAuth flow).
|
|
57
|
+
|
|
58
|
+
## gemini-cli backend
|
|
59
|
+
|
|
60
|
+
Spawns local [`gemini`](https://github.com/google-gemini/gemini-cli) `-p '' --output-format text` with the patina prompt on stdin. Works with the free Code Assist OAuth tier or with `GEMINI_API_KEY`.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gemini # one-time interactive OAuth, OR
|
|
64
|
+
export GEMINI_API_KEY="..." # AI Studio key
|
|
65
|
+
patina --backend gemini-cli --lang ko input.txt
|
|
66
|
+
patina --model gemini-3-flash-preview --lang ko input.txt # auto-routes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Notes: patina passes `--skip-trust` because the prompt runs from a fresh temp directory (containment for prompt-injection in user text). Default timeout is higher than other CLIs because gemini's startup latency is longer.
|
|
70
|
+
|
|
71
|
+
> **v1 limitation:** `codex-cli`, `claude-cli`, and `gemini-cli` all support single-mode rewrites only. `--audit`, `--score`, `--diff`, `--ouroboros`, and `--models`/MAX still go through the HTTP backend.
|
|
72
|
+
|
|
73
|
+
## Free-tier providers
|
|
74
|
+
|
|
75
|
+
Get an API key once, then it's free:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Google Gemini — https://aistudio.google.com/app/apikey
|
|
79
|
+
export GEMINI_API_KEY="..."
|
|
80
|
+
patina --provider gemini --lang ko input.txt
|
|
81
|
+
|
|
82
|
+
# Groq (free tier with rate limits)
|
|
83
|
+
export GROQ_API_KEY="..."
|
|
84
|
+
patina --provider groq --lang ko input.txt
|
|
85
|
+
|
|
86
|
+
# Together AI (free models suffixed with "-Free")
|
|
87
|
+
export TOGETHER_API_KEY="..."
|
|
88
|
+
patina --provider together --lang ko input.txt
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`--provider` sets the right base URL, default model, and reads the provider-specific API key env var. Override any of these with `--base-url`, `--model`, or `--api-key`.
|
|
92
|
+
|
|
93
|
+
## MAX mode dispatch
|
|
94
|
+
|
|
95
|
+
The Claude Code `/patina-max` skill bypasses HTTP entirely — it dispatches via local CLIs:
|
|
96
|
+
|
|
97
|
+
| Model | Dispatch | Auth |
|
|
98
|
+
|-------|----------|------|
|
|
99
|
+
| `claude` | `claude -p` | Claude Code |
|
|
100
|
+
| `codex` | `codex exec --skip-git-repo-check --output-last-message` | ChatGPT OAuth |
|
|
101
|
+
| `gemini` | `gemini -p '' --output-format text` | Google AI Studio |
|
|
102
|
+
|
|
103
|
+
No `PATINA_API_KEY` needed for the Claude Code path.
|
|
104
|
+
|
|
105
|
+
The standalone CLI MAX (`patina --models <list>`) calls models via the same `--base-url` endpoint, so all listed models must be served by that endpoint. To mix providers (OpenAI + Anthropic + Google), point `--base-url` at OpenRouter or another multi-provider gateway.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# 인증과 백엔드
|
|
2
|
+
|
|
3
|
+
patina는 여러 backend 중 하나를 통해 실행됩니다. 이미 쓰고 있는 도구에 맞는 방식을 고르세요.
|
|
4
|
+
|
|
5
|
+
## Backend matrix
|
|
6
|
+
|
|
7
|
+
| Backend | Setup | Cost |
|
|
8
|
+
|---------|-------|------|
|
|
9
|
+
| `codex-cli` | `codex login` | **Free** (ChatGPT OAuth) |
|
|
10
|
+
| `claude-cli` | `claude` (one-time interactive OAuth) | **Free** (Claude subscription) |
|
|
11
|
+
| `gemini-cli` | `gemini` (one-time interactive OAuth) or `GEMINI_API_KEY=...` | **Free** (Code Assist OAuth or AI Studio) |
|
|
12
|
+
| OpenAI-compatible HTTP | `PATINA_API_KEY=...` | Per provider |
|
|
13
|
+
| Google Gemini (HTTP) | `GEMINI_API_KEY=...` + `--provider gemini` | Free tier |
|
|
14
|
+
| Groq | `GROQ_API_KEY=...` + `--provider groq` | Free tier |
|
|
15
|
+
| Together AI | `TOGETHER_API_KEY=...` + `--provider together` | Free models available |
|
|
16
|
+
| OpenRouter | `--base-url https://openrouter.ai/api/v1` + key | Per provider (mix any provider) |
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
patina auth status # backend availability + auth state
|
|
20
|
+
patina auth login # per-backend login instructions
|
|
21
|
+
patina --list-providers # preset providers + key status
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Backend selection에는 명시적인 신호가 필요합니다. `--backend <name>`을 직접 넘기거나, `--backend claude-cli,codex-cli`처럼 comma-separated fallback chain을 넘기거나, `--model <prefix>`를 사용하세요. `codex-*`, `claude-*`, `gemini-*`는 각각 맞는 local CLI로 라우팅됩니다. flag도 API key도 없으면 patina는 coding agent로 조용히 보내지 않고 오류로 종료합니다. 이유는 [issue #88](https://github.com/devswha/patina/issues/88)를 참고하세요.
|
|
25
|
+
|
|
26
|
+
## Environment variables
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
PATINA_API_KEY=... # required for HTTP backend
|
|
30
|
+
PATINA_API_BASE=https://api.openai.com/v1 # or proxy / OpenRouter / etc.
|
|
31
|
+
PATINA_MODEL=gpt-4o # default model
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`--base-url`, `--model`, `--api-key`, `--provider` flag는 실행마다 이 값을 덮어씁니다.
|
|
35
|
+
|
|
36
|
+
## codex-cli backend
|
|
37
|
+
|
|
38
|
+
patina는 local [`codex`](https://github.com/openai/codex) CLI를 통해 dispatch합니다. 이 CLI는 OpenAI/ChatGPT OAuth로 인증하므로 API key가 필요 없습니다.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
codex login # one-time
|
|
42
|
+
patina --backend codex-cli --lang ko input.txt
|
|
43
|
+
patina --model codex --lang ko input.txt # same — auto-routes by model name
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## claude-cli backend
|
|
47
|
+
|
|
48
|
+
local [`claude`](https://docs.anthropic.com/en/docs/claude-code) `-p`를 실행하고 patina prompt를 stdin으로 넘깁니다. Claude subscription이 있으면 무료로 사용할 수 있습니다.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude # one-time interactive OAuth
|
|
52
|
+
patina --backend claude-cli --lang ko input.txt
|
|
53
|
+
patina --model claude-sonnet-4-6 --lang ko input.txt # auto-routes
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Auth file: `~/.claude/.credentials.json` (OAuth flow가 만듭니다).
|
|
57
|
+
|
|
58
|
+
## gemini-cli backend
|
|
59
|
+
|
|
60
|
+
local [`gemini`](https://github.com/google-gemini/gemini-cli) `-p '' --output-format text`를 실행하고 patina prompt를 stdin으로 넘깁니다. 무료 Code Assist OAuth tier 또는 `GEMINI_API_KEY`로 동작합니다.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
gemini # one-time interactive OAuth, OR
|
|
64
|
+
export GEMINI_API_KEY="..." # AI Studio key
|
|
65
|
+
patina --backend gemini-cli --lang ko input.txt
|
|
66
|
+
patina --model gemini-3-flash-preview --lang ko input.txt # auto-routes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Notes: patina는 user text 안의 prompt-injection을 격리하기 위해 새 temp directory에서 prompt를 실행하고 `--skip-trust`를 넘깁니다. gemini는 startup latency가 더 길어 default timeout이 다른 CLI보다 높습니다.
|
|
70
|
+
|
|
71
|
+
> **v1 limitation:** `codex-cli`, `claude-cli`, `gemini-cli`는 모두 single-mode rewrite만 지원합니다. `--audit`, `--score`, `--diff`, `--ouroboros`, `--models`/MAX는 여전히 HTTP backend를 사용합니다.
|
|
72
|
+
|
|
73
|
+
## Free-tier providers
|
|
74
|
+
|
|
75
|
+
API key를 한 번 발급받으면 무료 tier로 사용할 수 있습니다.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Google Gemini — https://aistudio.google.com/app/apikey
|
|
79
|
+
export GEMINI_API_KEY="..."
|
|
80
|
+
patina --provider gemini --lang ko input.txt
|
|
81
|
+
|
|
82
|
+
# Groq (free tier with rate limits)
|
|
83
|
+
export GROQ_API_KEY="..."
|
|
84
|
+
patina --provider groq --lang ko input.txt
|
|
85
|
+
|
|
86
|
+
# Together AI (free models suffixed with "-Free")
|
|
87
|
+
export TOGETHER_API_KEY="..."
|
|
88
|
+
patina --provider together --lang ko input.txt
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`--provider`는 알맞은 base URL, default model을 설정하고 provider-specific API key env var를 읽습니다. `--base-url`, `--model`, `--api-key`로 각각 덮어쓸 수 있습니다.
|
|
92
|
+
|
|
93
|
+
## MAX mode dispatch
|
|
94
|
+
|
|
95
|
+
Claude Code `/patina-max` skill은 HTTP를 완전히 우회합니다. local CLI로 dispatch합니다.
|
|
96
|
+
|
|
97
|
+
| Model | Dispatch | Auth |
|
|
98
|
+
|-------|----------|------|
|
|
99
|
+
| `claude` | `claude -p` | Claude Code |
|
|
100
|
+
| `codex` | `codex exec --skip-git-repo-check --output-last-message` | ChatGPT OAuth |
|
|
101
|
+
| `gemini` | `gemini -p '' --output-format text` | Google AI Studio |
|
|
102
|
+
|
|
103
|
+
Claude Code 경로에서는 `PATINA_API_KEY`가 필요 없습니다.
|
|
104
|
+
|
|
105
|
+
Standalone CLI MAX(`patina --models <list>`)는 같은 `--base-url` endpoint를 통해 모델을 호출하므로, 나열된 모든 모델이 그 endpoint에서 제공되어야 합니다. provider를 섞으려면(OpenAI + Anthropic + Google) `--base-url`을 OpenRouter나 다른 multi-provider gateway로 지정하세요.
|
package/docs/BRANDING.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Branding
|
|
2
|
+
|
|
3
|
+
Patina's production brand assets are hand-authored SVGs so maintainers can review, diff, and recreate them without a design-tool dependency.
|
|
4
|
+
|
|
5
|
+
## Canonical assets
|
|
6
|
+
|
|
7
|
+
| Asset | Path | Use |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Logo lockup | [`assets/brand/patina-logo.svg`](../assets/brand/patina-logo.svg) | README hero, package pages, docs landing pages |
|
|
10
|
+
| App icon | [`assets/brand/patina-icon.svg`](../assets/brand/patina-icon.svg) | Favicon/app tile/avatar contexts |
|
|
11
|
+
| Social preview | [`assets/social/patina-og.svg`](../assets/social/patina-og.svg) | Open Graph / social card export source |
|
|
12
|
+
| Before/after card | [`assets/social/patina-before-after.svg`](../assets/social/patina-before-after.svg) | Launch posts and docs examples |
|
|
13
|
+
|
|
14
|
+
`patina-logo.svg` is the single canonical horizontal logo. Do not reintroduce README-only duplicates unless the variant is visibly different and documented here.
|
|
15
|
+
|
|
16
|
+
## Accessibility checklist
|
|
17
|
+
|
|
18
|
+
- Keep `role="img"` on standalone SVG assets.
|
|
19
|
+
- Include `<title>` and `<desc>`, or an `aria-label` when a tiny decorative asset cannot carry children.
|
|
20
|
+
- Keep the square icon recognizable at 32px.
|
|
21
|
+
- Keep the logo on a dark backplate so it works on GitHub light and dark themes.
|
|
22
|
+
- Use system font fallbacks in SVG text; GitHub does not load external web fonts inside `<img>` SVGs.
|
|
23
|
+
|
|
24
|
+
## Open Graph setup for a future docs site
|
|
25
|
+
|
|
26
|
+
Markdown on GitHub cannot set Open Graph tags. If patina gets a docs site, use the checked-in social SVG or a generated PNG export and add:
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<meta property="og:title" content="patina — Strip the AI packaging. Keep the meaning.">
|
|
30
|
+
<meta property="og:description" content="Auditable AI-prose cleanup for KO, EN, ZH, and JA with meaning-preservation checks.">
|
|
31
|
+
<meta property="og:type" content="website">
|
|
32
|
+
<meta property="og:image" content="https://patina.dev/patina-og.png">
|
|
33
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
34
|
+
<meta name="twitter:image" content="https://patina.dev/patina-og.png">
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Export note: many social crawlers handle PNG more reliably than SVG, so keep the SVG as source and publish a PNG derivative from CI or the docs-site build.
|
package/docs/CLI.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# CLI Contract
|
|
2
|
+
|
|
3
|
+
patina's CLI is optimized for interactive editing, but a few surfaces are stable enough for automation.
|
|
4
|
+
|
|
5
|
+
## Score gate
|
|
6
|
+
|
|
7
|
+
Use `--score --exit-on <n>` (or the older `--gate <n>` alias) when CI should fail if a text still reads too AI-like.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
patina --lang en --score --exit-on 30 draft.md
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `--score` still prints the model's score output.
|
|
14
|
+
- If the parsed `overall` score is greater than the gate, patina prints a `[patina] score gate failed` warning to stderr and exits with code `3`.
|
|
15
|
+
- The gate is intentionally limited to `--score`; rewrite/audit/diff modes should not fail a pipeline based on an output shape they do not own.
|
|
16
|
+
|
|
17
|
+
## Exit codes
|
|
18
|
+
|
|
19
|
+
| Code | Meaning |
|
|
20
|
+
|---:|---|
|
|
21
|
+
| `0` | Command completed; for `--score --exit-on`, the score was at or below the gate. |
|
|
22
|
+
| `1` | Runtime or backend error, including API/auth/backend failures. |
|
|
23
|
+
| `2` | Input/usage error from no interactive input or empty stdin. |
|
|
24
|
+
| `3` | `--score --exit-on` / `--score --gate` completed, but the score exceeded the configured gate. |
|
|
25
|
+
| `4` | MAX mode all candidates failed, or patina fell back because no candidate met the MPS floor. |
|
|
26
|
+
|
|
27
|
+
## Output formats
|
|
28
|
+
|
|
29
|
+
`--format markdown` is the default and preserves the existing human-readable output. `--format text` emits the same user-facing content without the YAML tone footer. `--format json` wraps every mode in a stable envelope:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mode": "score",
|
|
34
|
+
"format": "json",
|
|
35
|
+
"overall": 23,
|
|
36
|
+
"categories": [],
|
|
37
|
+
"tone": { "tone": null, "tone_source": "profile_only" },
|
|
38
|
+
"mps": null,
|
|
39
|
+
"gateResult": { "threshold": 30, "overall": 23, "passed": true, "exitCode": 0 },
|
|
40
|
+
"output": "raw model output after patina cleanup"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `overall` and `categories[]` are populated when patina can parse them from score JSON or score tables.
|
|
45
|
+
- Score JSON may include `scores.llm`, `scores.deterministic`, and `scores.preference` when deterministic shadow scoring is available.
|
|
46
|
+
- `mps` is populated for MAX-mode results when available.
|
|
47
|
+
- `gateResult` is `null` unless `--exit-on` / `--gate` is used.
|
|
48
|
+
- `--voice-sample <path>` or config `voice-sample: <path>` injects the first 1–3 user-written paragraphs into rewrite/Ouroboros prompts as style-only examples of how this person writes. `--profile` / `--tone` still define the outer register; samples refine cadence and texture without importing facts.
|
|
49
|
+
- `--save-run <dir>` writes a schema-v2 `manifest.json` plus `output-N.txt` files for reproducible audit trails. Each result records prompt/response hashes, available token usage, temperature/seed, score details, per-call cost when providers return it, and the Ouroboros iteration log when used.
|
|
50
|
+
- `--cache <dir>` or `PATINA_CACHE_DIR` enables an opt-in persistent HTTP response cache keyed by prompt, model, temperature, and API host. `--cache-ttl <sec>` / `PATINA_CACHE_TTL_SECONDS` set expiry, and `--no-cache` forces a fresh run.
|
|
51
|
+
- `patina doctor --json` emits setup diagnostics for CI without making an LLM call.
|
|
52
|
+
|
|
53
|
+
## Stderr logs
|
|
54
|
+
|
|
55
|
+
Human-facing status, warnings, and progress indicators go to stderr so stdout
|
|
56
|
+
stays reserved for the transformed text or JSON envelope.
|
|
57
|
+
|
|
58
|
+
- `--quiet` suppresses stderr logs, including MAX/Ouroboros progress.
|
|
59
|
+
- `--json-logs` emits newline-delimited JSON records with stable fields:
|
|
60
|
+
`ts`, `level`, `event`, `model`, `latency_ms`, and optional `message`.
|
|
61
|
+
- MAX mode (`--models`) reports elapsed per-model status (`...`, `✓`, `✗`).
|
|
62
|
+
- Ouroboros reports per-iteration score movement and latency.
|
|
63
|
+
|
|
64
|
+
## Backend fallback chains
|
|
65
|
+
|
|
66
|
+
`--backend <name>` selects one backend. `--backend a,b,c` selects an explicit
|
|
67
|
+
fallback chain and tries each backend in order only for retryable failures:
|
|
68
|
+
HTTP `429`, HTTP `503`, and a first-backend `AbortError`. User cancellation via
|
|
69
|
+
Ctrl-C stops the chain instead of falling through.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
patina --backend claude-cli,codex-cli --lang en draft.md
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
All backends share the same invocation contract:
|
|
76
|
+
`invoke({ prompt, model, signal, timeout }): Promise<string>`. Local CLI
|
|
77
|
+
backends honor `AbortSignal` by killing their child process; the HTTP backend
|
|
78
|
+
bridges the same signal into fetch.
|
|
79
|
+
|
|
80
|
+
See [EXIT-CODES.md](EXIT-CODES.md) for the full process contract.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Comparison: patina and other humanizer/paraphraser tools
|
|
2
|
+
|
|
3
|
+
This page is factual positioning, not a claim that patina “wins.” Pricing and product limits change often, so verify vendor pages before making a purchase decision.
|
|
4
|
+
|
|
5
|
+
Sources checked on 2026-05-20: [QuillBot Premium](https://quillbot.com/premium), [QuillBot pricing help](https://help.quillbot.com/hc/en-us/articles/36491424881943-What-is-the-price-of-QuillBot-Premium), [Undetectable pricing](https://undetectable.com/pricing), [Humbot pricing](https://humbot.ai/pricing), and [`blader/humanizer`](https://github.com/blader/humanizer/blob/main/README.md).
|
|
6
|
+
|
|
7
|
+
| Tool | Pricing model | Language coverage | Auditability | Meaning preservation | Self-hostable / OSS | Best fit |
|
|
8
|
+
|---|---|---|---|---|---|---|
|
|
9
|
+
| patina | MIT open source; local CLI/skill; paid only if you choose a paid model backend | KO, EN, ZH, JA pattern packs | Pattern-level audit, diff, score, benchmark fixtures | MPS/fidelity checks and rollback in documented flows | Yes | Maintainers and writers who want reviewable editing logic |
|
|
10
|
+
| QuillBot | Free tier plus Premium subscription; pricing is regional on the official page | Broad writing suite; official page advertises paraphrasing in many languages | Product UI oriented; not repo-auditable | Humanizer/tone tools, but no public pattern/MPS spec | No | General writing assistance and paraphrasing suite |
|
|
11
|
+
| Undetectable AI | Commercial plans; checked page listed Basic/Premium/Ultimate monthly tiers | English-first marketing; verify current plan details | Detector/humanizer product; not repo-auditable | Detector-facing rewrite; no public MPS spec | No | Users specifically shopping for detector/humanizer SaaS |
|
|
12
|
+
| Humbot | Commercial monthly/yearly plans and API access; checked page lists word-credit limits | Web/API humanizer positioning; verify current plan details | SaaS/API; not repo-auditable | No public MPS-style spec found in the checked docs | No | API-oriented humanizer workflow |
|
|
13
|
+
| blader/humanizer | Open-source skill repository | Skill prompt based; check repo for current language scope | Prompt text is inspectable | Prompt-guided; no checked-in benchmark/MPS schema comparable to patina | Yes | Lightweight prompt-skill humanization |
|
|
14
|
+
|
|
15
|
+
## Reproducible comparison recipe
|
|
16
|
+
|
|
17
|
+
A fair benchmark should avoid sending private text to third-party tools. Use 10 redistributable paragraphs from `tests/fixtures/suspect-zones/**`, run each tool with default settings, then record:
|
|
18
|
+
|
|
19
|
+
1. Input fixture id and expected class.
|
|
20
|
+
2. Tool name/version/date.
|
|
21
|
+
3. Output text hash, not the full output if licensing/privacy is unclear.
|
|
22
|
+
4. Patina score/audit on the output.
|
|
23
|
+
5. Manual meaning-preservation notes for numbers, negation, causation, and named entities.
|
|
24
|
+
|
|
25
|
+
The checked-in benchmark currently covers patina's deterministic suspect-zone analyzer only. Third-party output capture is left out until fixture redistribution and tool terms are reviewed.
|
|
26
|
+
|
|
27
|
+
## Offline comparison harness
|
|
28
|
+
|
|
29
|
+
Patina now ships an offline harness for this recipe:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run benchmark:compare
|
|
33
|
+
node scripts/detector-comparison.mjs --input tests/quality/detectors.manual.example.json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The default report lives at [`docs/benchmarks/detector-comparison.md`](benchmarks/detector-comparison.md) and includes only Patina's in-tree deterministic analyzer. Third-party rows must be entered manually from redistributable fixtures, with the collection date and visible plan/version notes. The harness does not scrape vendor sites, call detector APIs, or send private text out of the repository.
|
|
37
|
+
|
|
38
|
+
Treat any comparison row as time-stamped evidence for a small corpus, not as a universal claim that one tool "beats" another.
|