@cachly-dev/mcp-server 0.10.128 → 0.10.130

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 (217) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +1 -7
  3. package/dist/packages/telegram-notify/client.js +2 -75
  4. package/dist/src/ambient-cli.js +7 -253
  5. package/dist/src/ambient-deps.js +1 -55
  6. package/dist/src/ambient-hooks.js +5 -278
  7. package/dist/src/ambient-ledger.js +6 -95
  8. package/dist/src/ambient-memory.js +1 -67
  9. package/dist/src/ambient-recall.js +1 -221
  10. package/dist/src/aussetzer.js +1 -82
  11. package/dist/src/auth.js +9 -140
  12. package/dist/src/autor-abzeichen.js +1 -56
  13. package/dist/src/autor.js +1 -136
  14. package/dist/src/bauabdruck.js +1 -93
  15. package/dist/src/bedeutung.js +2 -323
  16. package/dist/src/bench/cachly-bench.js +4 -249
  17. package/dist/src/bench/external-corpus.js +4 -135
  18. package/dist/src/bench/fixtures.js +1 -225
  19. package/dist/src/brain-export.js +16 -119
  20. package/dist/src/brain-watch-hook.js +8 -86
  21. package/dist/src/cachly-url.js +1 -21
  22. package/dist/src/ckg.js +1 -108
  23. package/dist/src/cls-hook.js +8 -94
  24. package/dist/src/confidence.js +1 -50
  25. package/dist/src/credentials.js +1 -85
  26. package/dist/src/deutsch.js +1 -223
  27. package/dist/src/doctor.js +2 -248
  28. package/dist/src/editor.js +1 -63
  29. package/dist/src/eingaenge-nachruesten.js +1 -290
  30. package/dist/src/eingaenge.js +2 -228
  31. package/dist/src/embeddings.js +13 -223
  32. package/dist/src/feldreparatur.js +3 -0
  33. package/dist/src/first-contact.js +4 -108
  34. package/dist/src/first-recall-proof.js +4 -41
  35. package/dist/src/funnel-milestones.js +2 -48
  36. package/dist/src/geliefert.js +1 -114
  37. package/dist/src/handlers/advanced.js +38 -537
  38. package/dist/src/handlers/brain.js +69 -4181
  39. package/dist/src/handlers/cache.js +59 -638
  40. package/dist/src/handlers/context.js +17 -182
  41. package/dist/src/handlers/fedbrain.js +40 -1436
  42. package/dist/src/handlers/instances.js +16 -201
  43. package/dist/src/handlers/roadmap.js +13 -175
  44. package/dist/src/handlers/share.js +19 -530
  45. package/dist/src/handlers/syndicate.js +38 -904
  46. package/dist/src/handlers/team.js +89 -1850
  47. package/dist/src/handlers/viz.js +3 -148
  48. package/dist/src/index.js +321 -4281
  49. package/dist/src/interferenz.js +1 -0
  50. package/dist/src/kostprobe.js +7 -125
  51. package/dist/src/lesson-preview.js +2 -133
  52. package/dist/src/namespace.js +1 -128
  53. package/dist/src/notifier.js +1 -6
  54. package/dist/src/rangfolge-stellschrauben.js +1 -0
  55. package/dist/src/rangfolge.js +1 -214
  56. package/dist/src/raumkorrektur.js +1 -197
  57. package/dist/src/recall-tiefe.js +1 -42
  58. package/dist/src/rerank.js +1 -154
  59. package/dist/src/resonanz.js +1 -0
  60. package/dist/src/search.js +1 -2104
  61. package/dist/src/seltenheitsbestand.js +1 -92
  62. package/dist/src/speicherlage.js +1 -84
  63. package/dist/src/spuren.js +1 -150
  64. package/dist/src/starter-corpus.js +1 -165
  65. package/dist/src/telemetry-types.js +0 -13
  66. package/dist/src/tools.js +10 -2465
  67. package/dist/src/toolspecs.js +1 -121
  68. package/dist/src/upgrade-nudge.js +1 -29
  69. package/dist/src/utils.js +1 -89
  70. package/dist/src/versuch.js +1 -250
  71. package/dist/src/vorspann.js +1 -146
  72. package/dist/src/wartung/vektoren-verdichten.js +1 -200
  73. package/dist/src/werkzeug-nutzung.js +1 -72
  74. package/dist/src/wertbeitrag.js +1 -73
  75. package/dist/src/wirkungsspur.js +1 -0
  76. package/glama.json +1 -1
  77. package/package.json +8 -7
  78. package/server.json +3 -3
  79. package/dist/index.d.ts +0 -1
  80. package/dist/index.js.map +0 -1
  81. package/dist/packages/telegram-notify/client.d.ts +0 -19
  82. package/dist/packages/telegram-notify/client.js.map +0 -1
  83. package/dist/src/ambient-cli.d.ts +0 -101
  84. package/dist/src/ambient-cli.js.map +0 -1
  85. package/dist/src/ambient-deps.d.ts +0 -26
  86. package/dist/src/ambient-deps.js.map +0 -1
  87. package/dist/src/ambient-hooks.d.ts +0 -137
  88. package/dist/src/ambient-hooks.js.map +0 -1
  89. package/dist/src/ambient-ledger.d.ts +0 -19
  90. package/dist/src/ambient-ledger.js.map +0 -1
  91. package/dist/src/ambient-memory.d.ts +0 -12
  92. package/dist/src/ambient-memory.js.map +0 -1
  93. package/dist/src/ambient-recall.d.ts +0 -127
  94. package/dist/src/ambient-recall.js.map +0 -1
  95. package/dist/src/aussetzer.d.ts +0 -67
  96. package/dist/src/aussetzer.js.map +0 -1
  97. package/dist/src/auth.d.ts +0 -43
  98. package/dist/src/auth.js.map +0 -1
  99. package/dist/src/autor-abzeichen.d.ts +0 -41
  100. package/dist/src/autor-abzeichen.js.map +0 -1
  101. package/dist/src/autor.d.ts +0 -101
  102. package/dist/src/autor.js.map +0 -1
  103. package/dist/src/bauabdruck-daten.generiert.json +0 -5
  104. package/dist/src/bauabdruck.d.ts +0 -38
  105. package/dist/src/bauabdruck.js.map +0 -1
  106. package/dist/src/bedeutung.d.ts +0 -168
  107. package/dist/src/bedeutung.js.map +0 -1
  108. package/dist/src/bench/cachly-bench.d.ts +0 -61
  109. package/dist/src/bench/cachly-bench.js.map +0 -1
  110. package/dist/src/bench/external-corpus.d.ts +0 -44
  111. package/dist/src/bench/external-corpus.js.map +0 -1
  112. package/dist/src/bench/fixtures.d.ts +0 -37
  113. package/dist/src/bench/fixtures.js.map +0 -1
  114. package/dist/src/brain-export.d.ts +0 -64
  115. package/dist/src/brain-export.js.map +0 -1
  116. package/dist/src/brain-watch-hook.d.ts +0 -21
  117. package/dist/src/brain-watch-hook.js.map +0 -1
  118. package/dist/src/cachly-url.d.ts +0 -68
  119. package/dist/src/cachly-url.js.map +0 -1
  120. package/dist/src/ckg.d.ts +0 -54
  121. package/dist/src/ckg.js.map +0 -1
  122. package/dist/src/cls-hook.d.ts +0 -15
  123. package/dist/src/cls-hook.js.map +0 -1
  124. package/dist/src/confidence.d.ts +0 -19
  125. package/dist/src/confidence.js.map +0 -1
  126. package/dist/src/credentials.d.ts +0 -37
  127. package/dist/src/credentials.js.map +0 -1
  128. package/dist/src/deutsch.d.ts +0 -98
  129. package/dist/src/deutsch.js.map +0 -1
  130. package/dist/src/doctor.d.ts +0 -47
  131. package/dist/src/doctor.js.map +0 -1
  132. package/dist/src/editor.d.ts +0 -9
  133. package/dist/src/editor.js.map +0 -1
  134. package/dist/src/eingaenge-nachruesten.d.ts +0 -41
  135. package/dist/src/eingaenge-nachruesten.js.map +0 -1
  136. package/dist/src/eingaenge.d.ts +0 -135
  137. package/dist/src/eingaenge.js.map +0 -1
  138. package/dist/src/embeddings.d.ts +0 -48
  139. package/dist/src/embeddings.js.map +0 -1
  140. package/dist/src/first-contact.d.ts +0 -56
  141. package/dist/src/first-contact.js.map +0 -1
  142. package/dist/src/first-recall-proof.d.ts +0 -19
  143. package/dist/src/first-recall-proof.js.map +0 -1
  144. package/dist/src/funnel-milestones.d.ts +0 -5
  145. package/dist/src/funnel-milestones.js.map +0 -1
  146. package/dist/src/geliefert.d.ts +0 -63
  147. package/dist/src/geliefert.js.map +0 -1
  148. package/dist/src/handlers/advanced.d.ts +0 -6
  149. package/dist/src/handlers/advanced.js.map +0 -1
  150. package/dist/src/handlers/brain.d.ts +0 -41
  151. package/dist/src/handlers/brain.js.map +0 -1
  152. package/dist/src/handlers/cache.d.ts +0 -6
  153. package/dist/src/handlers/cache.js.map +0 -1
  154. package/dist/src/handlers/context.d.ts +0 -6
  155. package/dist/src/handlers/context.js.map +0 -1
  156. package/dist/src/handlers/fedbrain.d.ts +0 -18
  157. package/dist/src/handlers/fedbrain.js.map +0 -1
  158. package/dist/src/handlers/instances.d.ts +0 -33
  159. package/dist/src/handlers/instances.js.map +0 -1
  160. package/dist/src/handlers/roadmap.d.ts +0 -6
  161. package/dist/src/handlers/roadmap.js.map +0 -1
  162. package/dist/src/handlers/share.d.ts +0 -8
  163. package/dist/src/handlers/share.js.map +0 -1
  164. package/dist/src/handlers/syndicate.d.ts +0 -6
  165. package/dist/src/handlers/syndicate.js.map +0 -1
  166. package/dist/src/handlers/team.d.ts +0 -45
  167. package/dist/src/handlers/team.js.map +0 -1
  168. package/dist/src/handlers/viz.d.ts +0 -6
  169. package/dist/src/handlers/viz.js.map +0 -1
  170. package/dist/src/index.d.ts +0 -93
  171. package/dist/src/index.js.map +0 -1
  172. package/dist/src/kostprobe.d.ts +0 -25
  173. package/dist/src/kostprobe.js.map +0 -1
  174. package/dist/src/lesson-preview.d.ts +0 -81
  175. package/dist/src/lesson-preview.js.map +0 -1
  176. package/dist/src/namespace.d.ts +0 -20
  177. package/dist/src/namespace.js.map +0 -1
  178. package/dist/src/notifier.d.ts +0 -5
  179. package/dist/src/notifier.js.map +0 -1
  180. package/dist/src/rangfolge.d.ts +0 -169
  181. package/dist/src/rangfolge.js.map +0 -1
  182. package/dist/src/raumkorrektur.d.ts +0 -100
  183. package/dist/src/raumkorrektur.js.map +0 -1
  184. package/dist/src/recall-tiefe.d.ts +0 -48
  185. package/dist/src/recall-tiefe.js.map +0 -1
  186. package/dist/src/rerank.d.ts +0 -48
  187. package/dist/src/rerank.js.map +0 -1
  188. package/dist/src/search.d.ts +0 -153
  189. package/dist/src/search.js.map +0 -1
  190. package/dist/src/seltenheitsbestand.d.ts +0 -53
  191. package/dist/src/seltenheitsbestand.js.map +0 -1
  192. package/dist/src/speicherlage.d.ts +0 -51
  193. package/dist/src/speicherlage.js.map +0 -1
  194. package/dist/src/spuren.d.ts +0 -97
  195. package/dist/src/spuren.js.map +0 -1
  196. package/dist/src/starter-corpus.d.ts +0 -28
  197. package/dist/src/starter-corpus.js.map +0 -1
  198. package/dist/src/telemetry-types.d.ts +0 -87
  199. package/dist/src/telemetry-types.js.map +0 -1
  200. package/dist/src/tools.d.ts +0 -2933
  201. package/dist/src/tools.js.map +0 -1
  202. package/dist/src/toolspecs.d.ts +0 -43
  203. package/dist/src/toolspecs.js.map +0 -1
  204. package/dist/src/upgrade-nudge.d.ts +0 -15
  205. package/dist/src/upgrade-nudge.js.map +0 -1
  206. package/dist/src/utils.d.ts +0 -34
  207. package/dist/src/utils.js.map +0 -1
  208. package/dist/src/versuch.d.ts +0 -179
  209. package/dist/src/versuch.js.map +0 -1
  210. package/dist/src/vorspann.d.ts +0 -73
  211. package/dist/src/vorspann.js.map +0 -1
  212. package/dist/src/wartung/vektoren-verdichten.d.ts +0 -50
  213. package/dist/src/wartung/vektoren-verdichten.js.map +0 -1
  214. package/dist/src/werkzeug-nutzung.d.ts +0 -62
  215. package/dist/src/werkzeug-nutzung.js.map +0 -1
  216. package/dist/src/wertbeitrag.d.ts +0 -57
  217. package/dist/src/wertbeitrag.js.map +0 -1
@@ -0,0 +1 @@
1
+ function p(e,r=5,i=.5){if(r<=1||e.length===0)return[e];const t=[e],n=r-1,c=Math.max(1,Math.floor(e.length/n));for(let s=0;s<n;s++){const o=s*c,a=s===n-1?e.length:o+c,h=e.slice();for(let l=o;l<a;l++)h[l]*=1-i;t.push(h)}return t}function u(e,r){const i=new Map;for(const[t,n]of e.entries()){const c=t===0?2:1;for(const[s,o]of n.entries()){if(o.naehe<=0)continue;const a=c*(o.naehe/(s+1));i.set(o.topic,(i.get(o.topic)??0)+a)}}return[...i.entries()].sort((t,n)=>n[1]-t[1]||t[0].localeCompare(n[0])).slice(0,r).map(([t])=>t)}export{u as ueberlagere,p as varianten};
@@ -1,125 +1,7 @@
1
- // Kostprobe — gesperrte Werkzeuge dürfen ein paar Mal echt laufen.
2
- //
3
- // ─── DER ANLASS ──────────────────────────────────────────────────────────────
4
- //
5
- // Gemessen am 11.08.2026 in mcp_events: `premium_gate_hit` ist 36-mal gefeuert,
6
- // von genau EINER Person — dem Entwickler des Produkts selbst, zwischen dem
7
- // 30.06. und dem 08.08. Er hat nicht aufgerüstet. Bei seinem eigenen Produkt,
8
- // wo Geld nicht das Hindernis ist.
9
- //
10
- // Das ist die härteste Zahl, die es zur Preisfrage gibt: Der intensivste
11
- // Nutzer, den cachly je hatte, stand 36-mal vor der Schranke und ging jedes
12
- // Mal weiter. Nicht weil die Funktionen schlecht wären — sondern weil er nie
13
- // gesehen hat, was sie an SEINEN Daten tun.
14
- //
15
- // Die Schranke gab eine Verkaufsansage. Nie eine Kostprobe.
16
- // Man vermisst nicht, was man nie hatte.
17
- //
18
- // ─── DIE REGEL ───────────────────────────────────────────────────────────────
19
- //
20
- // Jedes gesperrte Werkzeug darf auf der Gratis-Stufe KOSTPROBEN_JE_WERKZEUG-mal
21
- // vollständig laufen. Echtes Ergebnis, keine gekürzte Fassung. Erst danach
22
- // kommt die Schranke — und dann mit einer Zahl statt einem Versprechen:
23
- // "Das hat dir schon 3-mal geholfen."
24
- //
25
- // Je Werkzeug einzeln gezählt, nicht als Gesamtbudget. Wer causal_trace
26
- // dreimal nutzt, soll trotzdem brain_predict noch kennenlernen dürfen; ein
27
- // gemeinsamer Topf würde beim ersten Werkzeug aufgebraucht.
28
- //
29
- // ─── WAS DIESE ZÄHLUNG NICHT KANN ────────────────────────────────────────────
30
- //
31
- // Sie liegt lokal in ~/.cachly/ und gilt damit je Rechner. Wer neu installiert
32
- // oder auf einem zweiten Rechner arbeitet, bekommt neue Kostproben. Das ist
33
- // bewusst in Kauf genommen: Eine serverseitige Zählung wäre genauer, bräuchte
34
- // aber eine Migration und einen neuen Endpunkt — und für eine Kostprobe ist
35
- // großzügig der richtige Fehler. Wer sie mehrfach aufbraucht, hat das Werkzeug
36
- // mehrfach erlebt; das ist kein Verlust, das ist der Zweck.
37
- //
38
- // Zu messen ist der Erfolg an einer Zahl: heute 36 Ansagen, 0 Aufrüstungen.
39
- // Danach müsste das Verhältnis von `premium_taste_used` zu `premium_gate_hit`
40
- // zeigen, ob aus Erleben Interesse wird.
41
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
42
- import { homedir } from 'node:os';
43
- import { dirname, join } from 'node:path';
44
- /** So oft darf jedes gesperrte Werkzeug auf der Gratis-Stufe echt laufen. */
45
- export const KOSTPROBEN_JE_WERKZEUG = 3;
46
- export function defaultKostprobePath() {
47
- return join(homedir(), '.cachly', 'kostproben.json');
48
- }
49
- function lesen(path) {
50
- try {
51
- if (!existsSync(path))
52
- return {};
53
- const parsed = JSON.parse(readFileSync(path, 'utf-8'));
54
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
55
- // Nur Zahlen übernehmen — eine beschädigte Datei darf nicht dazu führen,
56
- // dass jemand dauerhaft freien Zugang bekommt oder dauerhaft keinen.
57
- const out = {};
58
- for (const [k, v] of Object.entries(parsed)) {
59
- if (typeof v === 'number' && Number.isFinite(v) && v >= 0)
60
- out[k] = v;
61
- }
62
- return out;
63
- }
64
- return {};
65
- }
66
- catch {
67
- return {}; // beschädigt → wie leer behandeln
68
- }
69
- }
70
- /** Wie oft wurde dieses Werkzeug auf diesem Rechner schon gekostet? */
71
- export function kostprobenVerbraucht(werkzeug, path = defaultKostprobePath()) {
72
- return lesen(path)[werkzeug] ?? 0;
73
- }
74
- /** Sind noch Kostproben übrig? */
75
- export function kostprobeUebrig(werkzeug, path = defaultKostprobePath()) {
76
- return kostprobenVerbraucht(werkzeug, path) < KOSTPROBEN_JE_WERKZEUG;
77
- }
78
- /**
79
- * Verbraucht eine Kostprobe und liefert die neue Anzahl.
80
- * Schlägt das Schreiben fehl, wird trotzdem hochgezählt zurückgegeben — der
81
- * Aufruf darf nie an einem Dateisystemproblem scheitern. Im schlimmsten Fall
82
- * bekommt jemand eine Kostprobe mehr; das ist der harmlosere Fehler.
83
- */
84
- export function kostprobeVerbrauchen(werkzeug, path = defaultKostprobePath()) {
85
- const alle = lesen(path);
86
- const neu = (alle[werkzeug] ?? 0) + 1;
87
- try {
88
- alle[werkzeug] = neu;
89
- mkdirSync(dirname(path), { recursive: true });
90
- writeFileSync(path, JSON.stringify(alle, null, 2) + '\n', 'utf-8');
91
- }
92
- catch {
93
- /* nie werfen */
94
- }
95
- return neu;
96
- }
97
- /**
98
- * Der Hinweis NACH einer Kostprobe. Bewusst kurz und ohne Druck — das Ergebnis
99
- * steht darüber und soll wirken, nicht der Text.
100
- */
101
- export function kostprobeHinweis(werkzeug, verbraucht, upgradeUrl) {
102
- const uebrig = Math.max(0, KOSTPROBEN_JE_WERKZEUG - verbraucht);
103
- if (uebrig > 0) {
104
- return `\n\n_Kostprobe: \`${werkzeug}\` ist eine Premium-Funktion. ` +
105
- `Noch ${uebrig} ${uebrig === 1 ? 'freier Lauf' : 'freie Läufe'} auf diesem Rechner._`;
106
- }
107
- return `\n\n_Das war deine letzte Kostprobe von \`${werkzeug}\`. ` +
108
- `Weiter geht es hier: ${upgradeUrl}_`;
109
- }
110
- /**
111
- * Die Schranke, wenn die Kostproben aufgebraucht sind. Sie nennt eine ZAHL
112
- * statt eines Versprechens — "so oft hat es dir schon geholfen" ist ein
113
- * Argument, "eine Premium-Funktion" ist keines.
114
- */
115
- export function schrankeNachKostproben(werkzeug, pitch, upgradeUrl) {
116
- return [
117
- `🔒 **\`${werkzeug}\`** — deine ${KOSTPROBEN_JE_WERKZEUG} Kostproben sind aufgebraucht.`,
118
- ``,
119
- `Es hat dir hier schon ${KOSTPROBEN_JE_WERKZEUG}-mal geantwortet: ${pitch}`,
120
- ``,
121
- `Dein Gratis-Gedächtnis behält alles andere — Abruf, Lernen, Sitzungen, Kausalgraph. Für immer.`,
122
- `Weiter mit der tieferen Schicht → ${upgradeUrl}`,
123
- ].join('\n');
124
- }
125
- //# sourceMappingURL=kostprobe.js.map
1
+ import{existsSync as c,mkdirSync as a,readFileSync as f,writeFileSync as p}from"node:fs";import{homedir as b}from"node:os";import{dirname as h,join as m}from"node:path";const i=3;function s(){return m(b(),".cachly","kostproben.json")}function u(r){try{if(!c(r))return{};const e=JSON.parse(f(r,"utf-8"));if(e&&typeof e=="object"&&!Array.isArray(e)){const t={};for(const[n,o]of Object.entries(e))typeof o=="number"&&Number.isFinite(o)&&o>=0&&(t[n]=o);return t}return{}}catch{return{}}}function l(r,e=s()){return u(e)[r]??0}function x(r,e=s()){return l(r,e)<i}function S(r,e=s()){const t=u(e),n=(t[r]??0)+1;try{t[r]=n,a(h(e),{recursive:!0}),p(e,JSON.stringify(t,null,2)+`
2
+ `,"utf-8")}catch{}return n}function K(r,e,t){const n=Math.max(0,i-e);return n>0?`
3
+
4
+ _Kostprobe: \`${r}\` ist eine Premium-Funktion. Noch ${n} ${n===1?"freier Lauf":"freie L\xE4ufe"} auf diesem Rechner._`:`
5
+
6
+ _Das war deine letzte Kostprobe von \`${r}\`. Weiter geht es hier: ${t}_`}function k(r,e,t){return[`\u{1F512} **\`${r}\`** \u2014 deine ${i} Kostproben sind aufgebraucht.`,"",`Es hat dir hier schon ${i}-mal geantwortet: ${e}`,"","Dein Gratis-Ged\xE4chtnis beh\xE4lt alles andere \u2014 Abruf, Lernen, Sitzungen, Kausalgraph. F\xFCr immer.",`Weiter mit der tieferen Schicht \u2192 ${t}`].join(`
7
+ `)}export{i as KOSTPROBEN_JE_WERKZEUG,s as defaultKostprobePath,K as kostprobeHinweis,x as kostprobeUebrig,S as kostprobeVerbrauchen,l as kostprobenVerbraucht,k as schrankeNachKostproben};
@@ -1,133 +1,2 @@
1
- /**
2
- * lesson-preview — one place that decides how a lesson is shown in a briefing.
3
- *
4
- * WHY THIS EXISTS (measured 2026-08-16, real incident):
5
- * A session briefing showed this lesson line:
6
- *
7
- * ✅🔴 `node4:einrichtung-contabo-und-fallen` — node-4 (Contabo, 169.58.175.157,
8
- * Lauterbourg FR, 12 Kerne / 48 GB / 400 GB, 25,29 EUR/Monat) eingeri
9
- *
10
- * The reader needed one thing from that lesson: the WireGuard address of the
11
- * machine. It sits at character 323 of `what_worked`. The preview cut at 100.
12
- * Worse, the lesson's `what_failed` field held the literal sentence "always read
13
- * `wg show wg0 allowed-ips`" — and no briefing surface renders `what_failed` for
14
- * a lesson whose outcome is "success". The lesson was displayed, at the right
15
- * moment, and the exact mistake it warns about happened anyway.
16
- *
17
- * The fix is not "show more prose". The two most actionable fields of that
18
- * lesson were already short and structured:
19
- *
20
- * commands: ["wg show wg0 allowed-ips", "ssh -i ~/.ssh/... -p 2222 root@..."]
21
- * file_paths: ["/etc/wireguard/wg0.conf", ...]
22
- *
23
- * ~15 tokens that answer the question, thrown away in favour of 100 characters
24
- * of preamble. So a preview renders three things now: trimmed prose, a facts
25
- * tail drawn from the structured fields, and — for critical lessons — the
26
- * warning, regardless of outcome.
27
- *
28
- * This helps the EXISTING corpus. A writing rule ("put the fact first") only
29
- * improves lessons written after it; reading structured fields improves every
30
- * lesson ever stored.
31
- */
32
- const DEFAULTS = {
33
- maxChars: 100,
34
- facts: true,
35
- warning: true,
36
- maxFacts: 2,
37
- factChars: 64,
38
- warningChars: 110,
39
- indent: ' ',
40
- };
41
- /** Collapse whitespace so a multi-line field cannot break list layout. */
42
- function flat(s) {
43
- return String(s ?? '').replace(/\s+/g, ' ').trim();
44
- }
45
- /**
46
- * Cut to `max` characters, preferring a word boundary, and mark the cut.
47
- * The marker matters: without it a truncated line reads like a complete one,
48
- * and the reader never learns there is more to fetch.
49
- */
50
- export function trimTo(text, max) {
51
- const s = flat(text);
52
- if (max <= 0)
53
- return '';
54
- if (s.length <= max)
55
- return s;
56
- const hard = s.slice(0, max);
57
- const space = hard.lastIndexOf(' ');
58
- // Only honour the word boundary if it does not throw away most of the budget.
59
- const body = space > max * 0.6 ? hard.slice(0, space) : hard;
60
- return `${body.replace(/[\s,;:.-]+$/, '')}…`;
61
- }
62
- /**
63
- * Facts worth showing even when the prose is cut: commands first (they are
64
- * directly runnable), then file paths. Deduplicated, order preserved.
65
- */
66
- export function factsOf(l, maxFacts, factChars) {
67
- const out = [];
68
- const seen = new Set();
69
- const take = (raw, prefix, limit) => {
70
- let n = 0;
71
- for (const item of Array.isArray(raw) ? raw : []) {
72
- if (n >= limit)
73
- break;
74
- const v = flat(item);
75
- if (!v)
76
- continue;
77
- const key = `${prefix}${v}`;
78
- if (seen.has(key))
79
- continue;
80
- seen.add(key);
81
- out.push(`${prefix}${trimTo(v, factChars)}`);
82
- n++;
83
- }
84
- };
85
- take(l.commands, '⌘ ', maxFacts);
86
- take(l.file_paths, '▸ ', maxFacts);
87
- return out;
88
- }
89
- /**
90
- * The warning is the single most fear-inducing, mistake-preventing part of a
91
- * lesson — and it was invisible whenever `outcome` was "success". Show it for
92
- * critical lessons no matter how the lesson ended.
93
- */
94
- export function warningOf(l, max) {
95
- const w = flat(l.what_failed);
96
- if (!w)
97
- return null;
98
- const isCritical = String(l.severity ?? '').toLowerCase() === 'critical';
99
- const failed = l.outcome === 'failure' || l.outcome === 'partial';
100
- if (!isCritical && !failed)
101
- return null;
102
- return trimTo(w, max);
103
- }
104
- /**
105
- * Render the body of one briefing entry: prose, then facts, then warning.
106
- * Returns lines WITHOUT the caller's bullet/emoji prefix — the caller owns the
107
- * first line's prefix, this owns everything after the em dash.
108
- */
109
- export function lessonPreviewLines(l, opts = {}) {
110
- const o = { ...DEFAULTS, ...opts };
111
- const lines = [];
112
- const prose = trimTo(l.what_worked ?? '', o.maxChars);
113
- if (prose)
114
- lines.push(prose);
115
- if (o.warning) {
116
- const w = warningOf(l, o.warningChars);
117
- if (w)
118
- lines.push(`${o.indent}⚠️ ${w}`);
119
- }
120
- if (o.facts) {
121
- const f = factsOf(l, o.maxFacts, o.factChars);
122
- // One line, not one per fact: a briefing lists many lessons, and a
123
- // four-line block per lesson would push the interesting ones off screen.
124
- if (f.length)
125
- lines.push(`${o.indent}${f.join(' ')}`);
126
- }
127
- return lines;
128
- }
129
- /** Convenience: the same content as a single newline-joined string. */
130
- export function lessonPreview(l, opts = {}) {
131
- return lessonPreviewLines(l, opts).join('\n');
132
- }
133
- //# sourceMappingURL=lesson-preview.js.map
1
+ const w={maxChars:100,facts:!0,warning:!0,maxFacts:2,factChars:64,warningChars:110,indent:" "};function c(n){return String(n??"").replace(/\s+/g," ").trim()}function a(n,e){const t=c(n);if(e<=0)return"";if(t.length<=e)return t;const r=t.slice(0,e),s=r.lastIndexOf(" ");return`${(s>e*.6?r.slice(0,s):r).replace(/[\s,;:.-]+$/,"")}\u2026`}function g(n,e,t){const r=[],s=new Set,i=(f,u,p)=>{let h=0;for(const d of Array.isArray(f)?f:[]){if(h>=p)break;const o=c(d);if(!o)continue;const l=`${u}${o}`;s.has(l)||(s.add(l),r.push(`${u}${a(o,t)}`),h++)}};return i(n.commands,"\u2318 ",e),i(n.file_paths,"\u25B8 ",e),r}function $(n,e){const t=c(n.what_failed);if(!t)return null;const r=String(n.severity??"").toLowerCase()==="critical",s=n.outcome==="failure"||n.outcome==="partial";return!r&&!s?null:a(t,e)}function C(n,e={}){const t={...w,...e},r=[],s=a(n.what_worked??"",t.maxChars);if(s&&r.push(s),t.warning){const i=$(n,t.warningChars);i&&r.push(`${t.indent}\u26A0\uFE0F ${i}`)}if(t.facts){const i=g(n,t.maxFacts,t.factChars);i.length&&r.push(`${t.indent}${i.join(" ")}`)}return r}function m(n,e={}){return C(n,e).join(`
2
+ `)}export{g as factsOf,m as lessonPreview,C as lessonPreviewLines,a as trimTo,$ as warningOf};
@@ -1,128 +1 @@
1
- /**
2
- * Namespace Auto-Detection
3
- *
4
- * Classify a prompt into one of 5 semantic namespaces using text heuristics.
5
- * Overhead: <0.1 ms. No embedding required.
6
- *
7
- * Covers: EN · DE · FR · ES · IT · PT · NL
8
- * Source of truth for keyword lists: sdk/shared/filler-words.ts
9
- */
10
- const CODE_KW = [
11
- 'function ', 'def ', 'class ', 'import ', 'const ', 'let ', 'var ',
12
- 'return ', ' => ', 'void ', 'public class', 'func ', '#include', 'package ',
13
- 'struct ', 'interface ', 'async def', 'lambda ', '#!/',
14
- ];
15
- // Translation – EN · DE · FR · ES · IT · PT · NL
16
- const TRANSL_KW = [
17
- // EN
18
- 'translate', 'in english', 'in german', 'in french', 'in spanish', 'in italian', 'in portuguese',
19
- // DE
20
- 'übersetze', 'übersetz', 'auf deutsch', 'auf englisch', 'ins deutsche', 'ins englische',
21
- // FR
22
- 'traduis', 'traduire', 'en français', 'en anglais', 'en allemand', 'en espagnol',
23
- // ES
24
- 'traduce', 'traducir', 'al español', 'al inglés', 'al alemán',
25
- // IT
26
- 'traduci', 'in italiano', 'in inglese', 'in tedesco',
27
- // PT
28
- 'traduz', 'traduzir', 'em português', 'em inglês', 'em alemão',
29
- // NL
30
- 'vertaal',
31
- ];
32
- // Summary – EN · DE · FR · ES · IT · PT
33
- const SUMMARY_KW = [
34
- // EN
35
- 'summarize', 'summarise', 'summary', 'tl;dr', 'tldr',
36
- 'key points', 'give me a brief', 'in a nutshell', 'in short', 'overview of',
37
- // DE
38
- 'zusammenfass', 'stichpunkte', 'fasse zusammen', 'kurze zusammenfassung', 'überblick',
39
- // FR
40
- 'résume', 'résumé', 'résumer', 'points clés', 'en bref', "vue d'ensemble",
41
- // ES
42
- 'resumir', 'en resumen', 'puntos clave', 'en breve', 'resumen de',
43
- // IT
44
- 'riassumi', 'riassunto', 'punti chiave', 'in breve', 'panoramica',
45
- // PT
46
- 'resumir', 'em resumo', 'pontos principais', 'resumo de',
47
- ];
48
- // Q&A prefixes – EN · DE · FR · ES · IT · PT
49
- const QA_PREFIXES = [
50
- // EN
51
- 'what ', 'who ', 'where ', 'when ', 'why ', 'how ', 'which ',
52
- 'is ', 'are ', 'was ', 'were ', 'does ', 'do ', 'did ',
53
- 'can ', 'could ', 'would ', 'should ', 'will ',
54
- // DE
55
- 'wer ', 'wie ', 'wo ', 'wann ', 'warum ', 'welche', 'wieso ', 'was ist',
56
- // FR
57
- "qu'est-ce que", 'comment ', 'pourquoi ', 'quel ', 'quelle ', 'quels ',
58
- 'est-ce que', 'qui est', 'où est', 'quand ',
59
- // ES
60
- 'qué ', 'cómo ', 'quién ', 'dónde ', 'cuándo ', 'por qué ',
61
- // IT
62
- 'come ', 'perché ', "cos'è", "com'è", 'dove ', 'quando ', 'chi è',
63
- // PT
64
- 'o que ', 'como ', 'quem ', 'onde ', 'quando ', 'por que ',
65
- ];
66
- /**
67
- * Classify a prompt into one of 5 semantic namespaces.
68
- * Returns one of: `cachly:sem:code` | `:translation` | `:summary` | `:qa` | `:creative`
69
- */
70
- export function detectNamespace(prompt) {
71
- const s = prompt.trim().toLowerCase();
72
- if (CODE_KW.some((kw) => s.includes(kw)))
73
- return 'cachly:sem:code';
74
- if (TRANSL_KW.some((kw) => s.includes(kw)))
75
- return 'cachly:sem:translation';
76
- if (SUMMARY_KW.some((kw) => s.includes(kw)))
77
- return 'cachly:sem:summary';
78
- if (QA_PREFIXES.some((p) => s.startsWith(p)))
79
- return 'cachly:sem:qa';
80
- if (s.trimEnd().endsWith('?'))
81
- return 'cachly:sem:qa';
82
- return 'cachly:sem:creative';
83
- }
84
- /**
85
- * Filler words used by prompt normalisation in MCP context.
86
- * Source of truth: sdk/shared/filler-words.ts
87
- * Covers: EN · DE · FR · ES · IT · PT
88
- */
89
- export const FILLER_WORDS = [
90
- // EN
91
- 'please', 'hey', 'hi', 'hello',
92
- 'could you', 'can you', 'would you', 'will you',
93
- 'just', 'quickly', 'briefly', 'simply',
94
- 'tell me', 'show me', 'give me', 'help me', 'assist me',
95
- 'explain to me', 'describe to me',
96
- 'i need', 'i want', 'i would like', "i'd like", "i'm looking for",
97
- // DE
98
- 'bitte', 'mal eben', 'schnell', 'kurz', 'einfach',
99
- 'kannst du', 'könntest du', 'könnten sie', 'würden sie', 'würdest du',
100
- 'hallo', 'hi', 'hey',
101
- 'sag mir', 'zeig mir', 'gib mir', 'hilf mir', 'erkläre mir', 'erklär mir',
102
- 'ich brauche', 'ich möchte', 'ich hätte gerne', 'ich suche',
103
- // FR
104
- "s'il vous plaît", 'svp', 'stp', 'bonjour', 'salut', 'allô',
105
- 'pouvez-vous', 'pourriez-vous', 'peux-tu', 'pourrais-tu',
106
- 'dis-moi', 'dites-moi', 'montre-moi', 'montrez-moi',
107
- "j'ai besoin de", 'je voudrais', 'je cherche', 'je souhaite',
108
- 'expliquez-moi', 'explique-moi', 'aidez-moi', 'aide-moi',
109
- // ES
110
- 'por favor', 'hola', 'oye',
111
- 'puedes', 'podrías', 'podría usted', 'me puedes', 'me podrías',
112
- 'dime', 'dígame', 'muéstrame', 'muéstreme', 'dame', 'deme',
113
- 'necesito', 'quisiera', 'me gustaría', 'quiero saber',
114
- 'ayúdame', 'ayúdeme', 'explícame', 'explíqueme',
115
- // IT
116
- 'per favore', 'perfavore', 'ciao', 'salve', 'ehi',
117
- 'potresti', 'mi potresti', 'potrebbe', 'mi potrebbe',
118
- 'dimmi', 'mi dica', 'mostrami', 'dammi', 'mi dia',
119
- 'ho bisogno di', 'vorrei', 'mi piacerebbe',
120
- 'aiutami', 'mi aiuti', 'spiegami', 'mi spieghi',
121
- // PT
122
- 'por favor', 'olá', 'oi', 'ei',
123
- 'pode', 'poderia', 'você poderia', 'você pode', 'podes',
124
- 'me diga', 'diga-me', 'me mostre', 'mostre-me', 'me dê', 'dê-me',
125
- 'preciso de', 'gostaria de', 'quero saber', 'estou procurando',
126
- 'me ajude', 'ajude-me', 'explique-me', 'me explique',
127
- ];
128
- //# sourceMappingURL=namespace.js.map
1
+ const a=["function ","def ","class ","import ","const ","let ","var ","return "," => ","void ","public class","func ","#include","package ","struct ","interface ","async def","lambda ","#!/"],r=["translate","in english","in german","in french","in spanish","in italian","in portuguese","\xFCbersetze","\xFCbersetz","auf deutsch","auf englisch","ins deutsche","ins englische","traduis","traduire","en fran\xE7ais","en anglais","en allemand","en espagnol","traduce","traducir","al espa\xF1ol","al ingl\xE9s","al alem\xE1n","traduci","in italiano","in inglese","in tedesco","traduz","traduzir","em portugu\xEAs","em ingl\xEAs","em alem\xE3o","vertaal"],o=["summarize","summarise","summary","tl;dr","tldr","key points","give me a brief","in a nutshell","in short","overview of","zusammenfass","stichpunkte","fasse zusammen","kurze zusammenfassung","\xFCberblick","r\xE9sume","r\xE9sum\xE9","r\xE9sumer","points cl\xE9s","en bref","vue d'ensemble","resumir","en resumen","puntos clave","en breve","resumen de","riassumi","riassunto","punti chiave","in breve","panoramica","resumir","em resumo","pontos principais","resumo de"],m=["what ","who ","where ","when ","why ","how ","which ","is ","are ","was ","were ","does ","do ","did ","can ","could ","would ","should ","will ","wer ","wie ","wo ","wann ","warum ","welche","wieso ","was ist","qu'est-ce que","comment ","pourquoi ","quel ","quelle ","quels ","est-ce que","qui est","o\xF9 est","quand ","qu\xE9 ","c\xF3mo ","qui\xE9n ","d\xF3nde ","cu\xE1ndo ","por qu\xE9 ","come ","perch\xE9 ","cos'\xE8","com'\xE8","dove ","quando ","chi \xE8","o que ","como ","quem ","onde ","quando ","por que "];function u(s){const i=s.trim().toLowerCase();return a.some(e=>i.includes(e))?"cachly:sem:code":r.some(e=>i.includes(e))?"cachly:sem:translation":o.some(e=>i.includes(e))?"cachly:sem:summary":m.some(e=>i.startsWith(e))||i.trimEnd().endsWith("?")?"cachly:sem:qa":"cachly:sem:creative"}const n=["please","hey","hi","hello","could you","can you","would you","will you","just","quickly","briefly","simply","tell me","show me","give me","help me","assist me","explain to me","describe to me","i need","i want","i would like","i'd like","i'm looking for","bitte","mal eben","schnell","kurz","einfach","kannst du","k\xF6nntest du","k\xF6nnten sie","w\xFCrden sie","w\xFCrdest du","hallo","hi","hey","sag mir","zeig mir","gib mir","hilf mir","erkl\xE4re mir","erkl\xE4r mir","ich brauche","ich m\xF6chte","ich h\xE4tte gerne","ich suche","s'il vous pla\xEEt","svp","stp","bonjour","salut","all\xF4","pouvez-vous","pourriez-vous","peux-tu","pourrais-tu","dis-moi","dites-moi","montre-moi","montrez-moi","j'ai besoin de","je voudrais","je cherche","je souhaite","expliquez-moi","explique-moi","aidez-moi","aide-moi","por favor","hola","oye","puedes","podr\xEDas","podr\xEDa usted","me puedes","me podr\xEDas","dime","d\xEDgame","mu\xE9strame","mu\xE9streme","dame","deme","necesito","quisiera","me gustar\xEDa","quiero saber","ay\xFAdame","ay\xFAdeme","expl\xEDcame","expl\xEDqueme","per favore","perfavore","ciao","salve","ehi","potresti","mi potresti","potrebbe","mi potrebbe","dimmi","mi dica","mostrami","dammi","mi dia","ho bisogno di","vorrei","mi piacerebbe","aiutami","mi aiuti","spiegami","mi spieghi","por favor","ol\xE1","oi","ei","pode","poderia","voc\xEA poderia","voc\xEA pode","podes","me diga","diga-me","me mostre","mostre-me","me d\xEA","d\xEA-me","preciso de","gostaria de","quero saber","estou procurando","me ajude","ajude-me","explique-me","me explique"];export{n as FILLER_WORDS,u as detectNamespace};
@@ -1,6 +1 @@
1
- /**
2
- * Re-exports from the shared telegram-notify package.
3
- * Canonical implementation: packages/telegram-notify/client.ts
4
- */
5
- export { notify, formatMessage } from '../packages/telegram-notify/client.js';
6
- //# sourceMappingURL=notifier.js.map
1
+ import{notify as f,formatMessage as r}from"../packages/telegram-notify/client.js";export{r as formatMessage,f as notify};
@@ -0,0 +1 @@
1
+ const o=75,t=.5,E=.7;export{t as EINGANG_SCHWELLE,E as EINGANG_SORTIER_GEWICHT,o as SINN_TOPF};
@@ -1,214 +1 @@
1
- /**
2
- * Der zusammengesetzte Sortierer.
3
- *
4
- * ── Was hier drinsteckt, und was NICHT ──────────────────────────────────────
5
- *
6
- * Am 19.08.2026 wurden an 499 echten Lektionen mit 100 Fragen ein Dutzend
7
- * Verfahren durchgemessen. Die meisten haben nichts gebracht. Sie stehen hier,
8
- * damit sie niemand ein zweites Mal baut:
9
- *
10
- * Symptom-Sicht (what_failed als eigener Vektor) 17 % statt 40 % — das Feld
11
- * beschreibt bei uns nicht das Symptom, sondern was vergeblich versucht wurde
12
- * Schwerpunkt abziehen 41 % — im Rauschen
13
- * Hauptrichtungen entfernen (1, 3, 6 Stueck) 37–41 % — im Rauschen
14
- * Nabenabzug (CSLS-artig) 41 % — im Rauschen
15
- * Text in Stuecke schneiden nicht gebaut: gemessen deckt
16
- * das beste Stueck genauso viel ab wie der ganze Text (Verhaeltnis 1,00),
17
- * und es liegt bei 86 von 96 Fragen ohnehin am Anfang
18
- *
19
- * Was BLEIBT, sind vier Bestandteile — kreuzweise geprueft, also auf der einen
20
- * Haelfte der Fragen eingestellt und auf der anderen gemessen, in beide
21
- * Richtungen:
22
- *
23
- * P@1 MRR
24
- * nur Bedeutung 40 % 51,3 %
25
- * zusammengesetzt 51 % 57,0 %
26
- *
27
- * ── Die vier Bestandteile ───────────────────────────────────────────────────
28
- *
29
- * 1. NAEHE ZUM GANZEN TEXT (Sicht A). Der Grundstock.
30
- *
31
- * 2. NAEHE ZUM THEMENNAMEN (Sicht C). Der Name ist kurz wie eine Frage — der
32
- * Vergleich ist damit symmetrisch, waehrend eine 60-Zeichen-Frage gegen
33
- * 1376 Zeichen Text es nicht ist. Allein liefert diese Sicht nur 26 %, im
34
- * Verbund traegt sie verlaesslich (Gewicht 0,6 in beiden Richtungen).
35
- *
36
- * 3. RUECKKOPPLUNG. Die ersten drei Treffer sind meistens ungefaehr richtig.
37
- * Ihre Vektoren werden zu einem Viertel unter die Frage gemischt, dann wird
38
- * erneut verglichen. Kostet nichts — keine neue Einbettung, nur eine
39
- * Mittelung vorhandener Zahlen.
40
- *
41
- * 4. SELTENHEITS-UEBERLAPPUNG. Welcher Anteil der SELTENEN Fragewoerter kommt
42
- * im Text vor. Ohne Laengennormierung, ohne unscharfe Treffer, ohne
43
- * Bigramme.
44
- *
45
- * Das ist der unbequeme Befund: dieses eine Verhaeltnis bekam beim Einstellen
46
- * das Gewicht 1,0 bis 1,6 — unser ausgefeiltes BM25 mit Laengennormierung,
47
- * Bigramm-Naehe, unscharfen Treffern und Frischebonus bekam NULL. Nicht weil
48
- * Woerter nichts taugen, sondern weil das ganze Beiwerk beim Sortieren
49
- * daneben misst.
50
- *
51
- * ── Warum innerhalb des Topfes normiert wird ────────────────────────────────
52
- *
53
- * Ueber den ganzen Bestand liegen alle Aehnlichkeiten zwischen 0,42 und 0,61 —
54
- * ein sehr schmales Band, weil alle Vektoren eine gemeinsame Grundrichtung
55
- * teilen (gemessen: 69,8 Prozent). Unter fuenfzig Kandidaten spreizt eine
56
- * Normierung auf 0 bis 1 genau die Unterschiede auf, um die es geht. Das ist
57
- * dasselbe Ziel wie beim Schwerpunkt-Abziehen — nur dort, wo es wirkt.
58
- */
59
- import { kosinus } from './bedeutung.js';
60
- /** Wörter eines Textes, umlautfrei, ohne Kurzwörter. */
61
- export function inhaltsWoerter(text) {
62
- return new Set(text.toLowerCase()
63
- .replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss')
64
- .split(/[^a-z0-9]+/)
65
- .filter((w) => w.length > 3));
66
- }
67
- /**
68
- * Es gibt hier KEINE Stammbildung — und das ist gemessen, nicht vergessen.
69
- *
70
- * Naheliegend wäre, `Abhaengigkeiten` und `Abhaengigkeit` auf einen Nenner zu
71
- * bringen. Drei Varianten wurden am 19.08.2026 an 499 Lektionen mit 100 Fragen
72
- * kreuzweise durchgemessen:
73
- *
74
- * ohne Stammbildung P@1 50 % MRR 56,4 %
75
- * erste 6 Zeichen P@1 47 % MRR 54,7 %
76
- * erste 8 Zeichen P@1 47 % MRR 54,7 %
77
- *
78
- * Jede Form von Kürzen kostet drei Punkte. Der Grund liegt daran, was diese
79
- * Suche trägt: seltene Fachwörter. `fail2ban`, `stampede`, `timestamptz`,
80
- * `keycloak` entscheiden eine Frage praktisch allein — und jede Verschmelzung
81
- * verwischt genau sie. `container` und `contains` werden zu demselben Wort,
82
- * und aus zwei seltenen wird ein häufiges.
83
- *
84
- * Beugung einzufangen lohnt sich also nur, wo die häufigen Wörter zählen. Hier
85
- * zählen die seltenen.
86
- *
87
- * (Die Wortform-Behandlung in deutsch.ts bleibt davon unberührt — sie wirkt im
88
- * Wortabgleich, der die Vorauswahl macht, nicht in dieser Bewertung.)
89
- */
90
- export function grobStamm(w) {
91
- return w;
92
- }
93
- /**
94
- * Die Seltenheit jedes Wortes im Bestand — einmal berechnet, oft benutzt.
95
- *
96
- * Trifft eine Frage das Wort "nicht", sagt das nichts. Trifft sie "fail2ban",
97
- * ist die Sache praktisch entschieden. Ohne diese Gewichtung sind beide gleich
98
- * viel wert.
99
- */
100
- export class Seltenheit {
101
- df = new Map();
102
- anzahl = 0;
103
- /** @param texte je ein Text pro Datensatz */
104
- constructor(texte) {
105
- this.anzahl = texte.length;
106
- for (const t of texte) {
107
- for (const w of inhaltsWoerter(t))
108
- this.df.set(grobStamm(w), (this.df.get(grobStamm(w)) ?? 0) + 1);
109
- }
110
- }
111
- wert(wort) {
112
- return Math.log((this.anzahl + 1) / ((this.df.get(grobStamm(wort)) ?? 0) + 1));
113
- }
114
- /**
115
- * Welcher Anteil der seltenen Fragewörter kommt im Text vor.
116
- *
117
- * Der Nenner ist die Summe über ALLE Fragewörter — ein fehlendes seltenes
118
- * Wort zieht damit den Wert herunter. Fehlende Belege zählen also mit, nicht
119
- * nur vorhandene.
120
- */
121
- deckung(frageWoerter, textWoerter) {
122
- let gesamt = 0;
123
- let getroffen = 0;
124
- for (const w of frageWoerter) {
125
- const g = this.wert(w);
126
- gesamt += g;
127
- if (textWoerter.has(grobStamm(w)))
128
- getroffen += g;
129
- }
130
- return gesamt > 0 ? getroffen / gesamt : 0;
131
- }
132
- }
133
- /** Auf 0 bis 1 spreizen — innerhalb des Topfes, nicht über den Bestand. */
134
- export function spreizeImTopf(werte) {
135
- const gueltig = werte.filter((x) => Number.isFinite(x) && x > -2);
136
- if (gueltig.length === 0)
137
- return werte.map(() => 0);
138
- let min = Infinity;
139
- let max = -Infinity;
140
- for (const x of gueltig) {
141
- if (x < min)
142
- min = x;
143
- if (x > max)
144
- max = x;
145
- }
146
- if (max === min)
147
- return werte.map(() => 0.5);
148
- return werte.map((x) => (x > -2 ? (x - min) / (max - min) : 0));
149
- }
150
- /**
151
- * Die Frage mit ihren besten Treffern anreichern.
152
- *
153
- * Die Frage benutzt Alltagsworte, die Lektionen Fachworte. Die ersten Treffer
154
- * schlagen diese Brücke: ihre Vektoren zeigen dorthin, wo die Antwort liegt.
155
- *
156
- * Das Risiko ist bekannt und begrenzt: sind die ersten Treffer falsch, zieht
157
- * die Anreicherung in die falsche Richtung. Deshalb bleibt die ursprüngliche
158
- * Frage mit drei Vierteln stehen — gemessen war das besser als jede stärkere
159
- * Beimischung.
160
- */
161
- export function reichereAn(frage, besteTreffer, anteil = 0.25) {
162
- if (besteTreffer.length === 0)
163
- return frage;
164
- const d = frage.length;
165
- const m = new Array(d).fill(0);
166
- for (const v of besteTreffer) {
167
- if (v.length !== d)
168
- continue;
169
- for (let i = 0; i < d; i++)
170
- m[i] += v[i] / besteTreffer.length;
171
- }
172
- return frage.map((x, i) => (1 - anteil) * x + anteil * m[i]);
173
- }
174
- /**
175
- * Die Gewichte.
176
- *
177
- * Kreuzweise ermittelt: auf der einen Hälfte der Fragen eingestellt, auf der
178
- * anderen gemessen, in beide Richtungen. Die drei Werte hier waren in BEIDEN
179
- * Richtungen stabil. Zwei weitere Merkmale — Überlappung mit dem Themennamen
180
- * und Qualitätsmerkmale — schwankten zwischen den Richtungen und sind deshalb
181
- * NICHT dabei: was nur in einer Hälfte hilft, ist eine Eigenschaft dieser
182
- * Hälfte, keine des Verfahrens.
183
- */
184
- export const GEWICHTE = {
185
- /** Nähe zum ganzen Text. Der Grundstock, deshalb 1. */
186
- text: 1,
187
- /** Nähe zum Themennamen. In beiden Richtungen 0,6. */
188
- thema: 0.6,
189
- /** Angereicherte Frage gegen den ganzen Text. In beiden Richtungen 0,3. */
190
- rueckkopplung: 0.3,
191
- /** Anteil der seltenen Fragewörter im Text. 1,0 bis 1,6 — Mitte genommen. */
192
- seltenheit: 1.3,
193
- };
194
- /**
195
- * Punktzahl je Kandidat. Höher ist besser.
196
- *
197
- * Erwartet den GANZEN Topf auf einmal, weil die Normierung ihn braucht — eine
198
- * Punktzahl für einen einzelnen Kandidaten gibt es nicht und kann es nicht
199
- * geben.
200
- */
201
- export function bewerteTopf(topf, gewichte = GEWICHTE) {
202
- const t = spreizeImTopf(topf.map((k) => k.naeheText));
203
- const th = spreizeImTopf(topf.map((k) => k.naeheThema));
204
- const r = spreizeImTopf(topf.map((k) => k.naeheRueckkopplung));
205
- return topf.map((k, i) => gewichte.text * t[i]
206
- + gewichte.thema * th[i]
207
- + gewichte.rueckkopplung * r[i]
208
- + gewichte.seltenheit * k.seltenheitsDeckung);
209
- }
210
- /** Bequemlichkeit: Kosinus mit Lücken-Behandlung. */
211
- export function naeheOderLuecke(frage, vektor) {
212
- return frage && vektor && vektor.length > 0 ? kosinus(frage, vektor) : -2;
213
- }
214
- //# sourceMappingURL=rangfolge.js.map
1
+ import{kosinus as c}from"./bedeutung.js";function i(e){return new Set(e.toLowerCase().replace(/ä/g,"ae").replace(/ö/g,"oe").replace(/ü/g,"ue").replace(/ß/g,"ss").split(/[^a-z0-9]+/).filter(t=>t.length>3))}function m(e){return e}class f{df=new Map;anzahl=0;constructor(t){this.anzahl=t.length;for(const l of t)for(const a of i(l))this.df.set(a,(this.df.get(a)??0)+1)}wert(t){return Math.log((this.anzahl+1)/((this.df.get(t)??0)+1))}deckung(t,l){let a=0,s=0;for(const r of t){const n=this.wert(r);a+=n,l.has(r)&&(s+=n)}return a>0?s/a:0}}function u(e){const t=e.filter(o=>Number.isFinite(o)&&o>-2);if(t.length===0)return e.map(()=>0);let l=1/0,a=-1/0;for(const o of t)o<l&&(l=o),o>a&&(a=o);if(a===l)return e.map(()=>.5);const s=e.map(o=>Number.isFinite(o)&&o>-2?(o-l)/(a-l):-1);let r=0;for(const o of s)o>=0&&(r+=o);const n=r/t.length;return s.map(o=>o>=0?o:n)}function g(e,t,l=.25){if(t.length===0)return e;const a=e.length,s=new Array(a).fill(0);for(const r of t)if(r.length===a)for(let n=0;n<a;n++)s[n]+=r[n]/t.length;return e.map((r,n)=>(1-l)*r+l*s[n])}const h={text:1,thema:0,rueckkopplung:.3,seltenheit:1.3};function k(e,t=h){const l=u(e.map(r=>r.naeheText)),a=u(e.map(r=>r.naeheThema)),s=u(e.map(r=>r.naeheRueckkopplung));return e.map((r,n)=>t.text*l[n]+t.thema*a[n]+t.rueckkopplung*s[n]+t.seltenheit*r.seltenheitsDeckung)}function x(e,t=h,l=.3){const a=u(e.map(n=>n.naeheText)),s=u(e.map(n=>n.naeheThema)),r=u(e.map(n=>n.naeheRueckkopplung));return e.map((n,o)=>t.text*Math.log(l+a[o])+t.thema*Math.log(l+s[o])+t.rueckkopplung*Math.log(l+r[o])+t.seltenheit*Math.log(l+n.seltenheitsDeckung))}function d(e,t){return e&&t&&t.length>0?c(e,t):-2}export{h as GEWICHTE,f as Seltenheit,k as bewerteTopf,x as bewerteTopfStreng,m as grobStamm,i as inhaltsWoerter,d as naeheOderLuecke,g as reichereAn,u as spreizeImTopf};