patina-cli 3.11.0 → 4.0.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.
Files changed (193) hide show
  1. package/.patina.default.yaml +29 -29
  2. package/CHANGELOG.md +53 -0
  3. package/NOTICE +21 -0
  4. package/README.md +117 -224
  5. package/README_JA.md +134 -77
  6. package/README_KR.md +132 -74
  7. package/README_ZH.md +137 -80
  8. package/SKILL.md +11 -20
  9. package/artifacts/rebaseline-2025/README.md +147 -0
  10. package/artifacts/rebaseline-2025/human-controls.public.jsonl +250 -0
  11. package/artifacts/rebaseline-2025/intake.example.jsonl +2 -0
  12. package/artifacts/rebaseline-2025/intake.local.example.jsonl +25 -0
  13. package/artifacts/rebaseline-2025/prompts.template.jsonl +7 -0
  14. package/artifacts/rebaseline-2025/sources.ko-public.jsonl +39 -0
  15. package/assets/brand/patina-badge.svg +18 -0
  16. package/assets/brand/patina-mark.svg +8 -0
  17. package/assets/demo/README.md +79 -0
  18. package/core/scoring.md +12 -12
  19. package/core/standalone-prompt.md +3 -1
  20. package/core/stylometry.md +93 -22
  21. package/docs/API.md +1554 -0
  22. package/docs/AUTHENTICATION.md +50 -26
  23. package/docs/AUTHENTICATION_KR.md +54 -29
  24. package/docs/BRANDING.md +9 -8
  25. package/docs/CLI.md +55 -14
  26. package/docs/COOKBOOK.md +8 -21
  27. package/docs/DEMO.md +32 -5
  28. package/docs/EXIT-CODES.md +2 -3
  29. package/docs/FALSE-POSITIVES.md +63 -0
  30. package/docs/FAQ.md +9 -1
  31. package/docs/FAQ_KR.md +3 -1
  32. package/docs/FLAG-PARITY.md +33 -47
  33. package/docs/ISSUE-WAVES.md +57 -0
  34. package/docs/PATTERNS-EN.md +67 -3
  35. package/docs/PATTERNS-JA.md +68 -2
  36. package/docs/PATTERNS-KO.md +70 -7
  37. package/docs/PATTERNS-ZH.md +67 -3
  38. package/docs/PATTERNS.md +5 -5
  39. package/docs/RESEARCH-DOCS-PLATFORM.md +54 -0
  40. package/docs/ROADMAP.md +46 -66
  41. package/docs/TRANSLATIONESE-KO.md +51 -0
  42. package/docs/audits/2026-05-deep-research.md +3 -1
  43. package/docs/benchmarks/README.md +51 -0
  44. package/docs/benchmarks/detector-comparison.json +69 -9
  45. package/docs/benchmarks/detector-comparison.md +10 -5
  46. package/docs/benchmarks/katfish-ko-latest.json +657 -0
  47. package/docs/benchmarks/katfish-ko-latest.md +77 -0
  48. package/docs/benchmarks/latest.json +1183 -108
  49. package/docs/benchmarks/latest.md +84 -60
  50. package/docs/benchmarks/lexicon-freshness-en-2026-05-22.json +1121 -0
  51. package/docs/benchmarks/lexicon-freshness-en-2026-05-22.md +136 -0
  52. package/docs/benchmarks/rebaseline-latest.json +381 -0
  53. package/docs/benchmarks/rebaseline-latest.md +121 -0
  54. package/docs/benchmarks/register-stratified-latest.json +164 -0
  55. package/docs/benchmarks/register-stratified-latest.md +99 -0
  56. package/docs/benchmarks/register-stratified.md +43 -0
  57. package/docs/integrations/github-action.md +44 -11
  58. package/docs/integrations/playground.md +58 -0
  59. package/docs/integrations/pre-commit.md +5 -5
  60. package/docs/integrations/release.md +5 -3
  61. package/docs/integrations/static-sites.md +83 -0
  62. package/docs/research/2025-rebaseline-plan.md +71 -2
  63. package/docs/research/2026-rebaseline.md +102 -0
  64. package/docs/research/adversarial-mps.md +41 -0
  65. package/docs/research/ai-human-metrics.md +35 -23
  66. package/docs/research/human-eval-panel.md +42 -0
  67. package/docs/research/judge-agreement.md +24 -0
  68. package/docs/research/ko-2025-corpus-sources.md +135 -0
  69. package/docs/research/lexicon-freshness-audit.md +64 -0
  70. package/docs/research/zh-ja-lexicon-calibration.md +60 -0
  71. package/docs/social/patina-launch-copy.md +173 -100
  72. package/docs/social/patina-launch-execution.md +94 -0
  73. package/docs/social/patina-launch-korean-first.md +83 -0
  74. package/docs/social/signs-of-ai-writing.md +26 -0
  75. package/docs/social/signs-of-ai-writing_KR.md +26 -0
  76. package/lexicon/ai-en.md +21 -24
  77. package/lexicon/ai-ja.md +158 -0
  78. package/lexicon/ai-ko.md +9 -9
  79. package/lexicon/ai-zh.md +158 -0
  80. package/lexicon/provenance/ai-en.json +970 -0
  81. package/lexicon/provenance/ai-ja.json +542 -0
  82. package/lexicon/provenance/ai-ko.json +866 -0
  83. package/lexicon/provenance/ai-zh.json +542 -0
  84. package/package.json +49 -8
  85. package/patterns/en-communication.md +5 -0
  86. package/patterns/en-content.md +5 -0
  87. package/patterns/en-filler.md +5 -0
  88. package/patterns/en-language.md +29 -1
  89. package/patterns/en-structure.md +5 -0
  90. package/patterns/en-style.md +5 -0
  91. package/patterns/en-viral-hook.md +42 -2
  92. package/patterns/ja-communication.md +5 -0
  93. package/patterns/ja-content.md +5 -0
  94. package/patterns/ja-filler.md +5 -0
  95. package/patterns/ja-language.md +33 -1
  96. package/patterns/ja-structure.md +12 -0
  97. package/patterns/ja-style.md +5 -0
  98. package/patterns/ja-viral-hook.md +41 -2
  99. package/patterns/ko-communication.md +5 -0
  100. package/patterns/ko-content.md +5 -0
  101. package/patterns/ko-filler.md +5 -0
  102. package/patterns/ko-language.md +33 -1
  103. package/patterns/ko-structure.md +25 -6
  104. package/patterns/ko-style.md +5 -0
  105. package/patterns/ko-viral-hook.md +38 -2
  106. package/patterns/zh-communication.md +5 -0
  107. package/patterns/zh-content.md +5 -0
  108. package/patterns/zh-filler.md +5 -0
  109. package/patterns/zh-language.md +37 -1
  110. package/patterns/zh-structure.md +12 -0
  111. package/patterns/zh-style.md +5 -0
  112. package/patterns/zh-viral-hook.md +38 -2
  113. package/playground/README.md +55 -0
  114. package/playground/analytics.js +4 -0
  115. package/playground/analyzer.js +883 -0
  116. package/playground/app.js +157 -0
  117. package/playground/data/lexicons.js +343 -0
  118. package/playground/index.html +138 -0
  119. package/playground/styles.css +267 -0
  120. package/profiles/namuwiki.md +111 -0
  121. package/scripts/adversarial-mps-report.mjs +201 -0
  122. package/scripts/badge-json.mjs +79 -0
  123. package/scripts/benchmark-report.mjs +56 -9
  124. package/scripts/check-release-metadata.mjs +0 -2
  125. package/scripts/detector-comparison.mjs +7 -7
  126. package/scripts/generate-playground-data.mjs +77 -0
  127. package/scripts/katfish-calibration.mjs +464 -0
  128. package/scripts/lexicon-freshness.mjs +485 -0
  129. package/scripts/lint.mjs +1 -1
  130. package/scripts/precommit-score.mjs +4 -3
  131. package/scripts/prose-score.mjs +81 -5
  132. package/scripts/rebaseline-intake.mjs +242 -0
  133. package/scripts/rebaseline-score.mjs +268 -0
  134. package/scripts/rebaseline-summary.mjs +773 -0
  135. package/scripts/rebaseline-web-collect.mjs +410 -0
  136. package/scripts/update-benchmark-ranges.mjs +1 -0
  137. package/src/api.js +69 -105
  138. package/src/auth.js +50 -2
  139. package/src/backends/claude-cli.js +19 -4
  140. package/src/backends/codex-cli.js +19 -3
  141. package/src/backends/contract.js +230 -1
  142. package/src/backends/gemini-cli.js +18 -5
  143. package/src/backends/index.js +87 -12
  144. package/src/backends/kimi-cli.js +161 -0
  145. package/src/cli.js +577 -567
  146. package/src/commands/doctor.js +2 -2
  147. package/src/config.js +29 -0
  148. package/src/errors.js +53 -1
  149. package/src/features/discourse-tells.js +68 -0
  150. package/src/features/index.js +82 -8
  151. package/src/features/lexicon.js +40 -6
  152. package/src/features/markup-leakage.js +69 -0
  153. package/src/features/segment.js +41 -0
  154. package/src/features/signal-strength.js +81 -0
  155. package/src/features/stylometry.js +231 -1
  156. package/src/features/translationese.js +127 -0
  157. package/src/loader.js +76 -0
  158. package/src/logger.js +22 -23
  159. package/src/model-defaults.js +55 -0
  160. package/src/ouroboros.js +31 -0
  161. package/src/output.js +102 -90
  162. package/src/prompt-builder.js +103 -68
  163. package/src/providers.js +51 -4
  164. package/src/scoring.js +210 -2
  165. package/src/security.js +75 -0
  166. package/tests/fixtures/live-quality/en/public-docs-01.md +26 -0
  167. package/tests/fixtures/live-quality/ko/public-docs-01.md +26 -0
  168. package/tests/fixtures/suspect-zones/expected-ranges.json +207 -16
  169. package/tests/fixtures/suspect-zones/ja/ai/ja-ai-04-lexicon.md +11 -0
  170. package/tests/fixtures/suspect-zones/ja/natural/ja-nat-04-lexicon-cold.md +11 -0
  171. package/tests/fixtures/suspect-zones/ko/ai/ko-ai-02.md +4 -5
  172. package/tests/fixtures/suspect-zones/ko/ai/ko-ai-07-ko-diagnostic.md +11 -0
  173. package/tests/fixtures/suspect-zones/zh/ai/zh-ai-04-lexicon.md +11 -0
  174. package/tests/fixtures/suspect-zones/zh/natural/zh-nat-04-lexicon-cold.md +11 -0
  175. package/tests/quality/README.md +188 -11
  176. package/tests/quality/adversarial-mps/fixtures.jsonl +10 -0
  177. package/tests/quality/benchmark.mjs +39 -1
  178. package/tests/quality/dogfood.mjs +5 -3
  179. package/tests/quality/live-fixtures.jsonl +2 -0
  180. package/tests/quality/live-quality.mjs +596 -0
  181. package/tests/quality/ranking-metrics.mjs +136 -0
  182. package/tests/quality/rebaseline-manifest.example.jsonl +5 -0
  183. package/vercel.json +53 -0
  184. package/SKILL-MAX.md +0 -455
  185. package/docs/internal/HARNESS.md +0 -14
  186. package/docs/internal/README.md +0 -14
  187. package/docs/internal/WARP.md +0 -23
  188. package/patina-max/SKILL.md +0 -523
  189. package/patina-max/composite.py +0 -457
  190. package/src/cache.js +0 -106
  191. package/src/commands/init.js +0 -208
  192. package/src/manifest.js +0 -162
  193. package/src/max-mode.js +0 -207
@@ -0,0 +1,157 @@
1
+ import {
2
+ SAMPLE_TEXT,
3
+ SUPPORTED_LANGS,
4
+ analyzePlaygroundText,
5
+ buildCliCommand,
6
+ buildFalsePositiveReportUrl,
7
+ escapeHtml,
8
+ renderAuditDiff,
9
+ } from './analyzer.js';
10
+
11
+ const doc = globalThis.document;
12
+ const state = {
13
+ lang: 'ko',
14
+ text: '',
15
+ analysis: analyzePlaygroundText('', { lang: 'ko' }),
16
+ };
17
+
18
+ const nodes = {
19
+ lang: doc.querySelector('#lang'),
20
+ sample: doc.querySelector('#sample'),
21
+ input: doc.querySelector('#input'),
22
+ analyze: doc.querySelector('#analyze'),
23
+ copyCli: doc.querySelector('#copy-cli'),
24
+ reportFp: doc.querySelector('#report-fp'),
25
+ copyStatus: doc.querySelector('#copy-status'),
26
+ scoreValue: doc.querySelector('#score-value'),
27
+ scoreBand: doc.querySelector('#score-band'),
28
+ scoreBar: doc.querySelector('#score-bar'),
29
+ summary: doc.querySelector('#summary'),
30
+ audit: doc.querySelector('#audit'),
31
+ diff: doc.querySelector('#diff'),
32
+ cliPreview: doc.querySelector('#cli-preview'),
33
+ };
34
+
35
+ function readQueryState() {
36
+ const url = new URL(globalThis.location?.href ?? 'https://patina.vibetip.help/');
37
+ const lang = url.searchParams.get('lang');
38
+ if (SUPPORTED_LANGS.includes(lang)) state.lang = lang;
39
+ }
40
+
41
+ function updateQuery() {
42
+ if (!globalThis.history || !globalThis.location) return;
43
+ const url = new URL(globalThis.location.href);
44
+ url.searchParams.set('lang', state.lang);
45
+ globalThis.history.replaceState(null, '', url);
46
+ }
47
+
48
+ function setSample() {
49
+ state.text = SAMPLE_TEXT[state.lang];
50
+ nodes.input.value = state.text;
51
+ runAnalysis();
52
+ }
53
+
54
+ function metricsTable(analysis) {
55
+ if (analysis.paragraphCount === 0) return '<p class="empty-state">No audit rows yet.</p>';
56
+ const rows = analysis.paragraphs.map((p) => {
57
+ const reasons = p.reasons.map((r) => r.label).join(', ') || '—';
58
+ const cv = p.burstiness.cv == null ? 'n/a' : p.burstiness.cv.toFixed(2);
59
+ const mattr = p.mattr.value == null ? 'n/a' : p.mattr.value.toFixed(2);
60
+ const density = p.lexicon.density.toFixed(1);
61
+ return `<tr>
62
+ <td>${escapeHtml(p.id)}</td>
63
+ <td><span class="pill ${p.hot ? 'hot' : 'clean'}">${p.hot ? 'review' : 'ok'}</span></td>
64
+ <td>${p.sentenceCount}</td>
65
+ <td>${p.tokenCount}</td>
66
+ <td>${cv} <span class="muted">${escapeHtml(p.burstiness.band ?? '')}</span></td>
67
+ <td>${mattr} <span class="muted">${escapeHtml(p.mattr.band ?? '')}</span></td>
68
+ <td>${p.lexicon.matches} <span class="muted">${density}/1k</span></td>
69
+ <td>${escapeHtml(reasons)}</td>
70
+ </tr>`;
71
+ }).join('');
72
+ return `<div class="table-wrap"><table>
73
+ <thead><tr><th>Para</th><th>Status</th><th>Sent</th><th>Tokens</th><th>Burst</th><th>MATTR</th><th>Lexicon</th><th>Signals</th></tr></thead>
74
+ <tbody>${rows}</tbody>
75
+ </table></div>`;
76
+ }
77
+
78
+ function render() {
79
+ const analysis = state.analysis;
80
+ const band = analysis.band;
81
+ nodes.scoreValue.textContent = String(analysis.overall);
82
+ nodes.scoreBand.textContent = band.label;
83
+ nodes.scoreBand.dataset.tone = band.tone;
84
+ nodes.scoreBar.style.setProperty('--score', `${analysis.overall}%`);
85
+ nodes.summary.innerHTML = `
86
+ <li><strong>${analysis.hotCount}</strong> / ${analysis.paragraphCount} paragraphs marked for review</li>
87
+ <li><strong>${analysis.totalTokens}</strong> deterministic tokens checked</li>
88
+ <li>Score is an editing signal, not an authorship verdict.</li>
89
+ `;
90
+ nodes.audit.innerHTML = metricsTable(analysis);
91
+ nodes.diff.innerHTML = renderAuditDiff(analysis);
92
+ nodes.cliPreview.textContent = buildCliCommand(state.text, state.lang);
93
+ }
94
+
95
+ function runAnalysis() {
96
+ state.text = nodes.input.value;
97
+ state.analysis = analyzePlaygroundText(state.text, { lang: state.lang });
98
+ render();
99
+ }
100
+
101
+ async function copyText(text) {
102
+ if (globalThis.navigator?.clipboard?.writeText) {
103
+ await globalThis.navigator.clipboard.writeText(text);
104
+ return true;
105
+ }
106
+ const area = doc.createElement('textarea');
107
+ area.value = text;
108
+ area.setAttribute('readonly', 'readonly');
109
+ area.style.position = 'fixed';
110
+ area.style.left = '-9999px';
111
+ doc.body.append(area);
112
+ area.select();
113
+ const ok = doc.execCommand('copy');
114
+ area.remove();
115
+ return ok;
116
+ }
117
+
118
+ async function copyCli() {
119
+ const command = buildCliCommand(state.text, state.lang);
120
+ try {
121
+ const ok = await copyText(command);
122
+ nodes.copyStatus.textContent = ok ? 'Copied CLI command.' : 'Copy failed; select the command below.';
123
+ } catch (_err) {
124
+ nodes.copyStatus.textContent = 'Copy failed; select the command below.';
125
+ }
126
+ }
127
+
128
+ function reportFalsePositive() {
129
+ if (!state.text.trim()) {
130
+ nodes.copyStatus.textContent = 'Paste text and run the audit first, then report the false positive.';
131
+ return;
132
+ }
133
+ const url = buildFalsePositiveReportUrl(state.text, state.lang, state.analysis);
134
+ const opened = globalThis.open?.(url, '_blank', 'noopener');
135
+ nodes.copyStatus.textContent = opened
136
+ ? 'Opened a pre-filled GitHub report — review it, then submit. Your text only leaves the browser if you submit.'
137
+ : 'Pop-up blocked. Allow pop-ups, or open an issue from the GitHub link in the header.';
138
+ }
139
+
140
+ function bind() {
141
+ nodes.lang.value = state.lang;
142
+ nodes.input.value = state.text;
143
+ nodes.lang.addEventListener('change', () => {
144
+ state.lang = nodes.lang.value;
145
+ updateQuery();
146
+ runAnalysis();
147
+ });
148
+ nodes.input.addEventListener('input', runAnalysis);
149
+ nodes.analyze.addEventListener('click', runAnalysis);
150
+ nodes.sample.addEventListener('click', setSample);
151
+ nodes.copyCli.addEventListener('click', copyCli);
152
+ nodes.reportFp.addEventListener('click', reportFalsePositive);
153
+ }
154
+
155
+ readQueryState();
156
+ bind();
157
+ setSample();
@@ -0,0 +1,343 @@
1
+ // Generated by scripts/generate-playground-data.mjs. Do not edit by hand.
2
+ // Source: lexicon/ai-{en,ko,zh,ja}.md
3
+
4
+ export const PLAYGROUND_LEXICONS = {
5
+ "en": {
6
+ "lang": "en",
7
+ "source": "lexicon/ai-en.md",
8
+ "version": "1.0.0",
9
+ "strict": [
10
+ "transformative",
11
+ "cutting-edge",
12
+ "bespoke",
13
+ "curated",
14
+ "dynamic",
15
+ "vibrant",
16
+ "seamless",
17
+ "seamlessly",
18
+ "streamline",
19
+ "streamlined",
20
+ "empower",
21
+ "empowering",
22
+ "enabling",
23
+ "align",
24
+ "alignment",
25
+ "pivot",
26
+ "ecosystem",
27
+ "skillset",
28
+ "toolkit",
29
+ "modalities",
30
+ "harness",
31
+ "unlock",
32
+ "bolster",
33
+ "amplify",
34
+ "accelerate",
35
+ "catalyst",
36
+ "inflection",
37
+ "meaningful",
38
+ "impactful",
39
+ "actionable",
40
+ "scalable",
41
+ "sustainable",
42
+ "inclusive",
43
+ "ethical",
44
+ "thoughtful",
45
+ "compelling",
46
+ "thrive",
47
+ "thriving",
48
+ "elevate",
49
+ "reimagine",
50
+ "rethink",
51
+ "envision",
52
+ "prioritize"
53
+ ],
54
+ "phrases": [
55
+ "a wide array of",
56
+ "a plethora of",
57
+ "a myriad of",
58
+ "in today's",
59
+ "in the modern era",
60
+ "in the digital age",
61
+ "ever-evolving",
62
+ "ever-changing",
63
+ "rapidly evolving",
64
+ "rapidly changing",
65
+ "fast-paced",
66
+ "the world of",
67
+ "the realm of",
68
+ "the landscape of",
69
+ "the future of",
70
+ "unlock the potential",
71
+ "realize the potential",
72
+ "harness the power",
73
+ "pave the way",
74
+ "usher in",
75
+ "a new era",
76
+ "a new chapter",
77
+ "a new frontier",
78
+ "gain valuable insights",
79
+ "glean insights",
80
+ "valuable insights",
81
+ "key insights",
82
+ "key takeaways",
83
+ "play a crucial role",
84
+ "plays a vital role",
85
+ "pave the path",
86
+ "bridge the gap",
87
+ "at the forefront",
88
+ "at the heart of",
89
+ "at its core",
90
+ "holistic approach",
91
+ "comprehensive approach",
92
+ "best practices",
93
+ "continuous improvement",
94
+ "a deeper dive",
95
+ "the bigger picture",
96
+ "a robust framework",
97
+ "the digital landscape",
98
+ "the regulatory landscape",
99
+ "the competitive landscape"
100
+ ]
101
+ },
102
+ "ko": {
103
+ "lang": "ko",
104
+ "source": "lexicon/ai-ko.md",
105
+ "version": "1.1.0",
106
+ "strict": [
107
+ "살펴보다",
108
+ "살펴보면",
109
+ "알아보다",
110
+ "알아보자",
111
+ "짚어보다",
112
+ "들여다보면",
113
+ "풀어내다",
114
+ "풀어내며",
115
+ "일컫다",
116
+ "자리매김",
117
+ "자리잡다",
118
+ "자리잡은",
119
+ "부각되다",
120
+ "부각하다",
121
+ "모색하다",
122
+ "가속화",
123
+ "고도화",
124
+ "본격화",
125
+ "활성화",
126
+ "정교화",
127
+ "차별화",
128
+ "다변화",
129
+ "가시화",
130
+ "일환",
131
+ "행보",
132
+ "양상",
133
+ "면모",
134
+ "양립",
135
+ "추세",
136
+ "한편으로는",
137
+ "더 나아가",
138
+ "한걸음 더",
139
+ "핵심",
140
+ "본질",
141
+ "생태계",
142
+ "패러다임",
143
+ "평가된다",
144
+ "꼽힌다",
145
+ "사례로",
146
+ "다수의",
147
+ "알려져",
148
+ "일컬어진다",
149
+ "평가받다"
150
+ ],
151
+ "phrases": [
152
+ "~의 가능성을 열다",
153
+ "~의 지평을 넓히다",
154
+ "~의 길을 열다",
155
+ "~의 토대를 다지다",
156
+ "~의 발판을 마련하다",
157
+ "~의 새로운 장",
158
+ "~의 새 지평",
159
+ "새로운 가능성을",
160
+ "무한한 가능성",
161
+ "다채로운 매력",
162
+ "매력적인 요소",
163
+ "깊이 있는 통찰",
164
+ "통찰을 제공",
165
+ "통찰을 얻다",
166
+ "시사점을 제공",
167
+ "시사하는 바",
168
+ "~을(를) 시사한다",
169
+ "의미를 지닌다",
170
+ "의미가 있다",
171
+ "큰 의미를",
172
+ "중요한 의미",
173
+ "큰 영향을 미치다",
174
+ "깊은 영감을",
175
+ "영감을 주다",
176
+ "영감을 받다",
177
+ "새로운 영감",
178
+ "화두로 떠오르다",
179
+ "주목받고 있다",
180
+ "떠오르고 있다",
181
+ "자리잡고 있다",
182
+ "진화하고 있다",
183
+ "변모하고 있다",
184
+ "부상하고 있다",
185
+ "가속화되고 있다",
186
+ "본격화되고 있다",
187
+ "시너지를",
188
+ "시너지 효과",
189
+ "인사이트를",
190
+ "패러다임의",
191
+ "한 단계 도약",
192
+ "한 차원 높은",
193
+ "보다 한층",
194
+ "새로운 국면",
195
+ "깊이를 더하다",
196
+ "풍성하게 하다",
197
+ "풍성한 경험",
198
+ "차원을 넘어",
199
+ "단순한 ~을 넘어",
200
+ "단순한 ~이 아닌",
201
+ "가운데 하나로",
202
+ "자리 잡았다",
203
+ "알려져 있다",
204
+ "~의 사례로"
205
+ ]
206
+ },
207
+ "zh": {
208
+ "lang": "zh",
209
+ "source": "lexicon/ai-zh.md",
210
+ "version": "1.0.0",
211
+ "strict": [],
212
+ "phrases": [
213
+ "总而言之",
214
+ "综上所述",
215
+ "总的来说",
216
+ "概括而言",
217
+ "不难看出",
218
+ "值得注意的是",
219
+ "需要指出的是",
220
+ "重要的是",
221
+ "至关重要的是",
222
+ "在当今社会",
223
+ "在数字时代",
224
+ "随着科技的发展",
225
+ "随着社会的进步",
226
+ "在这个过程中",
227
+ "从多个角度来看",
228
+ "从长远来看",
229
+ "不仅~而且",
230
+ "一方面~另一方面",
231
+ "对于~而言",
232
+ "从某种程度上说",
233
+ "可以说",
234
+ "不言而喻",
235
+ "显得尤为重要",
236
+ "具有重要意义",
237
+ "发挥着重要作用",
238
+ "起着至关重要的作用",
239
+ "为~提供了新的可能",
240
+ "带来了新的机遇",
241
+ "面临新的挑战",
242
+ "注入新的活力",
243
+ "赋予~新的内涵",
244
+ "打造更加",
245
+ "推动~高质量发展",
246
+ "提升用户体验",
247
+ "优化整体流程",
248
+ "实现可持续发展",
249
+ "构建完整生态",
250
+ "形成良性循环",
251
+ "助力~发展",
252
+ "让我们一起",
253
+ "深入探讨",
254
+ "全面了解",
255
+ "多维度分析",
256
+ "本文将带你",
257
+ "接下来我们将",
258
+ "不断推进",
259
+ "持续赋能",
260
+ "精准触达",
261
+ "有效提升",
262
+ "最大限度地",
263
+ "进一步增强",
264
+ "为未来奠定基础",
265
+ "开启新的篇章",
266
+ "迈向新的阶段",
267
+ "释放巨大潜力",
268
+ "展现出独特价值",
269
+ "成为不可或缺的一部分",
270
+ "以人为本的理念",
271
+ "兼顾效率与体验",
272
+ "为读者提供参考"
273
+ ]
274
+ },
275
+ "ja": {
276
+ "lang": "ja",
277
+ "source": "lexicon/ai-ja.md",
278
+ "version": "1.0.0",
279
+ "strict": [],
280
+ "phrases": [
281
+ "まとめると",
282
+ "結論として",
283
+ "要するに",
284
+ "総じて",
285
+ "言い換えれば",
286
+ "重要なのは",
287
+ "注目すべきは",
288
+ "特筆すべきは",
289
+ "現代社会において",
290
+ "デジタル時代において",
291
+ "テクノロジーの進化により",
292
+ "社会の変化に伴い",
293
+ "多角的に見ると",
294
+ "長期的に見ると",
295
+ "一方で~他方で",
296
+ "~することが重要です",
297
+ "~と言えるでしょう",
298
+ "~と言えます",
299
+ "~が求められます",
300
+ "~につながります",
301
+ "~を実現します",
302
+ "~を促進します",
303
+ "~を支える重要な要素",
304
+ "重要な役割を果たします",
305
+ "新たな可能性を切り開く",
306
+ "新しい価値を生み出す",
307
+ "さらなる発展が期待されます",
308
+ "今後ますます重要になる",
309
+ "持続可能な成長",
310
+ "ユーザー体験を向上させる",
311
+ "より良い未来",
312
+ "課題解決に貢献する",
313
+ "多様なニーズに応える",
314
+ "柔軟に対応する",
315
+ "効果的に活用する",
316
+ "最大限に引き出す",
317
+ "価値を最大化する",
318
+ "本記事では",
319
+ "この記事では",
320
+ "ここでは~について解説します",
321
+ "ぜひ参考にしてください",
322
+ "理解を深める",
323
+ "具体的に見ていきましょう",
324
+ "さまざまな場面で活用できます",
325
+ "幅広い分野で注目されています",
326
+ "新たな選択肢",
327
+ "大きなメリットがあります",
328
+ "重要なポイントです",
329
+ "欠かせない存在",
330
+ "ますます注目を集めています",
331
+ "未来を切り拓く",
332
+ "一歩踏み出すきっかけ",
333
+ "可能性を広げる",
334
+ "安心して利用できます",
335
+ "より効果的な",
336
+ "最適な方法",
337
+ "本質的な価値",
338
+ "これからの時代に求められる",
339
+ "単なる~ではなく",
340
+ "重要な鍵となります"
341
+ ]
342
+ }
343
+ };
@@ -0,0 +1,138 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>patina playground — audit AI-writing signals</title>
7
+ <meta name="description" content="Paste Korean, English, Chinese, or Japanese text and see patina's deterministic audit-only AI-writing signal score.">
8
+ <link rel="canonical" href="https://patina.vibetip.help/">
9
+ <meta property="og:type" content="website">
10
+ <meta property="og:title" content="patina playground">
11
+ <meta property="og:description" content="Audit AI-writing signals without uploading keys or asking for a rewrite.">
12
+ <meta property="og:url" content="https://patina.vibetip.help/">
13
+ <meta property="og:image" content="https://patina.vibetip.help/assets/social/patina-og.svg">
14
+ <meta name="twitter:card" content="summary_large_image">
15
+ <meta name="twitter:title" content="patina playground">
16
+ <meta name="twitter:description" content="Audit Korean, English, Chinese, and Japanese AI-writing signals in the browser.">
17
+ <meta name="twitter:image" content="https://patina.vibetip.help/assets/social/patina-og.svg">
18
+ <link rel="icon" href="/assets/brand/patina-icon.svg" type="image/svg+xml">
19
+ <link rel="stylesheet" href="/styles.css">
20
+ </head>
21
+ <body>
22
+ <header class="hero">
23
+ <nav class="nav" aria-label="Main">
24
+ <a class="brand" href="https://github.com/devswha/patina" aria-label="patina GitHub repository">
25
+ <img src="/assets/brand/patina-mark.svg" alt="" width="44" height="44">
26
+ <span>patina</span>
27
+ </a>
28
+ <div class="nav__links">
29
+ <a href="https://github.com/devswha/patina">GitHub</a>
30
+ <a href="https://github.com/devswha/patina/blob/main/docs/ETHICS.md">Ethics</a>
31
+ <a href="https://github.com/devswha/patina/blob/main/docs/benchmarks/latest.md">Benchmark</a>
32
+ </div>
33
+ </nav>
34
+
35
+ <section class="hero__copy">
36
+ <p class="eyebrow">audit-only playground</p>
37
+ <h1>Paste text. See where it sounds packaged.</h1>
38
+ <p>
39
+ patina checks deterministic stylometry and lexicon signals for Korean,
40
+ English, Chinese, and Japanese. v1 does not rewrite, call an LLM, or send
41
+ your pasted text anywhere.
42
+ </p>
43
+ <div class="hero__actions" aria-label="Primary actions">
44
+ <a class="button ghost" href="https://github.com/devswha/patina#quick-start">Install CLI</a>
45
+ <a class="button ghost" href="https://github.com/devswha/patina/issues/208">Issue #208</a>
46
+ </div>
47
+ </section>
48
+ </header>
49
+
50
+ <main class="workspace">
51
+ <section class="panel input-panel" aria-labelledby="input-title">
52
+ <div class="panel__head">
53
+ <div>
54
+ <p class="eyebrow">input</p>
55
+ <h2 id="input-title">Text to audit</h2>
56
+ </div>
57
+ <label class="field">
58
+ <span>Language</span>
59
+ <select id="lang" name="lang">
60
+ <option value="ko">Korean</option>
61
+ <option value="en">English</option>
62
+ <option value="zh">Chinese</option>
63
+ <option value="ja">Japanese</option>
64
+ </select>
65
+ </label>
66
+ </div>
67
+
68
+ <textarea id="input" spellcheck="true" aria-label="Text to audit"></textarea>
69
+
70
+ <div class="button-row">
71
+ <button class="button" id="analyze" type="button">Run audit</button>
72
+ <button class="button secondary" id="sample" type="button">Load sample</button>
73
+ <button class="button secondary" id="copy-cli" type="button">Open in CLI</button>
74
+ </div>
75
+ <p class="status" id="copy-status" role="status" aria-live="polite"></p>
76
+ </section>
77
+
78
+ <section class="panel score-panel" aria-labelledby="score-title">
79
+ <div class="panel__head">
80
+ <div>
81
+ <p class="eyebrow">score</p>
82
+ <h2 id="score-title">AI-likeness signal</h2>
83
+ </div>
84
+ <span class="score-band" id="score-band" data-tone="good">Low AI-likeness</span>
85
+ </div>
86
+
87
+ <div class="score-meter" aria-label="AI-likeness score out of 100">
88
+ <div class="score-meter__number"><span id="score-value">0</span><small>/100</small></div>
89
+ <div class="score-meter__bar"><span id="score-bar"></span></div>
90
+ </div>
91
+
92
+ <ul class="summary" id="summary"></ul>
93
+
94
+ <div class="cli-box" aria-labelledby="cli-title">
95
+ <div class="cli-box__head">
96
+ <h3 id="cli-title">Copied by “Open in CLI”</h3>
97
+ <span>local audit</span>
98
+ </div>
99
+ <pre id="cli-preview"></pre>
100
+ </div>
101
+ </section>
102
+
103
+ <section class="panel full" aria-labelledby="audit-title">
104
+ <div class="panel__head">
105
+ <div>
106
+ <p class="eyebrow">audit</p>
107
+ <h2 id="audit-title">Deterministic signals</h2>
108
+ </div>
109
+ <p class="quiet">Burstiness, MATTR, AI-favored lexicon density, and Korean rhythm diagnostics.</p>
110
+ </div>
111
+ <div id="audit"></div>
112
+ </section>
113
+
114
+ <section class="panel full" aria-labelledby="diff-title">
115
+ <div class="panel__head">
116
+ <div>
117
+ <p class="eyebrow">diff</p>
118
+ <h2 id="diff-title">Suspect-zone diff</h2>
119
+ </div>
120
+ <button class="button secondary" id="report-fp" type="button">Report false positive</button>
121
+ </div>
122
+ <p class="quiet">v1 highlights review zones and lexicon hits only. No rewrite. Human text wrongly flagged? The report button pre-fills a GitHub issue with the flagged text and signals — nothing is sent unless you submit it.</p>
123
+ <div id="diff" class="diff-list"></div>
124
+ </section>
125
+ </main>
126
+
127
+ <footer class="footer">
128
+ <p>
129
+ Scores measure editing hotspots, not authorship. Pasted text stays in your browser; Vercel Web Analytics records page-view metadata only.
130
+ <a href="https://github.com/devswha/patina/blob/main/docs/ETHICS.md">Read the ethics note.</a>
131
+ </p>
132
+ </footer>
133
+
134
+ <script type="module" src="/app.js"></script>
135
+ <script defer src="/analytics.js"></script>
136
+ <script defer src="/_vercel/insights/script.js"></script>
137
+ </body>
138
+ </html>