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,161 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { existsSync, mkdtempSync, readdirSync, rmSync } from 'node:fs';
3
+ import { homedir, tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { DEFAULT_BACKEND_TIMEOUT_MS, runInteractiveCommand } from './contract.js';
6
+ import { resolveLocalCliModel } from '../model-defaults.js';
7
+
8
+ export const name = 'kimi-cli';
9
+ export const loginCommand = 'kimi login';
10
+ export const installHint = 'Install Kimi Code CLI first, then run `patina auth login kimi-cli` again.';
11
+
12
+ const KIMI_ENV_KEYS = ['KIMI_API_KEY', 'MOONSHOT_API_KEY'];
13
+
14
+ export function isAvailable() {
15
+ try {
16
+ const result = spawnSync('kimi', ['--version'], { stdio: 'ignore' });
17
+ return result.status === 0;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ export function isAuthenticated() {
24
+ const root = kimiDataDir();
25
+ return hasKimiCredential(root) ||
26
+ existsSync(join(root, 'config.toml')) ||
27
+ KIMI_ENV_KEYS.some((key) => !!process.env[key]);
28
+ }
29
+
30
+ export function authHint() {
31
+ if (KIMI_ENV_KEYS.some((key) => !!process.env[key])) {
32
+ return 'Authenticated via KIMI_API_KEY/MOONSHOT_API_KEY env var.';
33
+ }
34
+ return `Run \`${loginCommand}\` once interactively to log in with Kimi Code OAuth.`;
35
+ }
36
+
37
+ export function login(options = {}) {
38
+ return runInteractiveCommand({
39
+ backendName: name,
40
+ command: 'kimi',
41
+ args: ['login'],
42
+ notFoundHint: installHint,
43
+ ...options,
44
+ });
45
+ }
46
+
47
+ export async function invoke({ prompt, model, modelSource, signal, timeout = DEFAULT_BACKEND_TIMEOUT_MS } = {}) {
48
+ if (!prompt || typeof prompt !== 'string') {
49
+ throw new Error('kimi-cli backend: prompt must be a non-empty string');
50
+ }
51
+ throwIfAborted(signal);
52
+
53
+ const dir = mkdtempSync(join(tmpdir(), 'patina-kimi-'));
54
+ const cliModel = resolveLocalCliModel({ backendName: name, model, modelSource });
55
+ const args = [
56
+ '--print',
57
+ '--input-format',
58
+ 'text',
59
+ '--output-format',
60
+ 'text',
61
+ '--final-message-only',
62
+ '--no-thinking',
63
+ '--max-steps-per-turn',
64
+ '1',
65
+ ];
66
+ if (cliModel) args.push('--model', cliModel);
67
+
68
+ return new Promise((resolve, reject) => {
69
+ const proc = spawn('kimi', args, { stdio: ['pipe', 'pipe', 'pipe'], cwd: dir });
70
+
71
+ let stdout = '';
72
+ let stderr = '';
73
+ proc.stdout.on('data', (chunk) => { stdout += chunk; });
74
+ proc.stderr.on('data', (chunk) => { stderr += chunk; });
75
+
76
+ let settled = false;
77
+ let cleanupSignal = () => {};
78
+ const timer = setTimeout(() => {
79
+ finishReject(new Error(`kimi-cli backend: timed out after ${timeout}ms`), { kill: true });
80
+ }, timeout);
81
+ if (signal) {
82
+ const onAbort = () => finishReject(abortError('kimi-cli backend: aborted'), { kill: true });
83
+ signal.addEventListener('abort', onAbort, { once: true });
84
+ cleanupSignal = () => signal.removeEventListener('abort', onAbort);
85
+ }
86
+
87
+ proc.on('error', (err) => {
88
+ if (err.code === 'ENOENT') {
89
+ finishReject(new Error('kimi-cli backend: `kimi` CLI not found. Install Kimi Code first.'));
90
+ } else {
91
+ finishReject(new Error(`kimi-cli backend: failed to spawn kimi (${err.message})`));
92
+ }
93
+ });
94
+
95
+ proc.on('close', (code) => {
96
+ if (settled) return;
97
+ if (code !== 0) {
98
+ finishReject(new Error(`kimi-cli backend: kimi exited with code ${code}\n${stderr}`));
99
+ return;
100
+ }
101
+ finishResolve(stripKimiNoise(stdout));
102
+ });
103
+
104
+ proc.stdin.write(prompt);
105
+ proc.stdin.end();
106
+
107
+ function cleanup() {
108
+ try { rmSync(dir, { recursive: true, force: true }); } catch {}
109
+ }
110
+
111
+ function finishReject(err, { kill = false } = {}) {
112
+ if (settled) return;
113
+ settled = true;
114
+ clearTimeout(timer);
115
+ cleanupSignal();
116
+ if (kill) proc.kill('SIGKILL');
117
+ cleanup();
118
+ reject(err);
119
+ }
120
+
121
+ function finishResolve(content) {
122
+ if (settled) return;
123
+ settled = true;
124
+ clearTimeout(timer);
125
+ cleanupSignal();
126
+ cleanup();
127
+ resolve(content);
128
+ }
129
+ });
130
+ }
131
+
132
+ function abortError(message) {
133
+ const err = new Error(message);
134
+ err.name = 'AbortError';
135
+ return err;
136
+ }
137
+
138
+ function throwIfAborted(signal) {
139
+ if (signal?.aborted) throw abortError('kimi-cli backend: aborted');
140
+ }
141
+
142
+ function stripKimiNoise(text) {
143
+ return text
144
+ .split(/\r?\n/)
145
+ .filter((line) => !/^To resume this session:\s*kimi\s+-r\s+/i.test(line.trim()))
146
+ .join('\n')
147
+ .trimStart();
148
+ }
149
+
150
+ function kimiDataDir() {
151
+ return process.env.KIMI_SHARE_DIR || join(homedir(), '.kimi');
152
+ }
153
+
154
+ function hasKimiCredential(root) {
155
+ try {
156
+ return readdirSync(join(root, 'credentials'), { withFileTypes: true })
157
+ .some((entry) => entry.isFile() && entry.name.endsWith('.json'));
158
+ } catch {
159
+ return false;
160
+ }
161
+ }