kimal3 0.2.9 → 0.3.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 (67) hide show
  1. package/README.md +135 -0
  2. package/dist/api.js +16 -7
  3. package/dist/arbeitsplatz-dateien.js +62 -30
  4. package/dist/argumente.js +25 -97
  5. package/dist/befehle/arbeitsplatz.js +51 -68
  6. package/dist/befehle/firma-daten.js +24 -26
  7. package/dist/befehle/kontext-fragen.js +62 -44
  8. package/dist/befehle/kontext-gemeinsam.js +86 -43
  9. package/dist/befehle/kontext-hoch.js +40 -46
  10. package/dist/befehle/kontext-runter.js +70 -51
  11. package/dist/befehle/login.js +23 -9
  12. package/dist/befehle/logout.js +9 -4
  13. package/dist/befehle/projekt-liste.js +24 -29
  14. package/dist/befehle/projekt-neu.js +25 -31
  15. package/dist/befehle/skill-install.js +78 -49
  16. package/dist/befehle/skill-tausch.js +28 -0
  17. package/dist/befehle/wache.js +12 -6
  18. package/dist/befehle/werkbank.js +118 -88
  19. package/dist/befehle-katalog-en.js +185 -0
  20. package/dist/befehle-katalog.js +10 -49
  21. package/dist/befehlswoerter.js +69 -0
  22. package/dist/einstellungen-mischen.js +2 -2
  23. package/dist/einstellungen-schreiben.js +8 -5
  24. package/dist/fehler.js +5 -5
  25. package/dist/firmen-liste.js +42 -5
  26. package/dist/format.js +41 -0
  27. package/dist/fragen-lesen.js +3 -3
  28. package/dist/hilfe.js +24 -0
  29. package/dist/kimal3.js +77 -71
  30. package/dist/kontext-dateien.js +30 -23
  31. package/dist/kontext-hoch-zeile.js +5 -3
  32. package/dist/kontext-rat.js +151 -0
  33. package/dist/kontext-stand.js +68 -210
  34. package/dist/nachricht.js +39 -15
  35. package/dist/projekt-neu-firma.js +9 -9
  36. package/dist/projekt-zeilen.js +40 -54
  37. package/dist/slash-befehle.js +36 -0
  38. package/dist/sprache.js +114 -0
  39. package/dist/texte/allgemein.js +99 -0
  40. package/dist/texte/arbeitsplatz.js +135 -0
  41. package/dist/texte/kontext-hoch.js +128 -0
  42. package/dist/texte/kontext.js +171 -0
  43. package/dist/texte/projekte.js +126 -0
  44. package/dist/texte/skill.js +87 -0
  45. package/dist/texte/wache.js +36 -0
  46. package/dist/texte/werkbank.js +161 -0
  47. package/dist/wache-rechnung.js +27 -40
  48. package/dist/wache-schalter.js +40 -5
  49. package/dist/werkbank-name.js +28 -10
  50. package/dist/wort.js +98 -0
  51. package/package.json +27 -27
  52. package/skill/SKILL.md +18 -10
  53. package/skill/commands/k3.md +1 -1
  54. package/skill/commands/k3feierabend.md +99 -97
  55. package/skill/commands/k3kontext.md +2 -2
  56. package/skill/commands/k3kurs.md +6 -5
  57. package/skill/commands/k3start.md +2 -1
  58. package/skill/en/SKILL.md +451 -0
  59. package/skill/en/commands/k3.md +106 -0
  60. package/skill/en/commands/k3analyse.md +59 -0
  61. package/skill/en/commands/k3context.md +92 -0
  62. package/skill/en/commands/k3course.md +54 -0
  63. package/skill/en/commands/k3list.md +26 -0
  64. package/skill/en/commands/k3project.md +50 -0
  65. package/skill/en/commands/k3start.md +47 -0
  66. package/skill/en/commands/k3wrapup.md +99 -0
  67. package/dist/zz-kaputt.js +0 -1
@@ -2,8 +2,12 @@ import { existsSync, statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { FIRMEN_OPTIONEN, dateienSchreiben, istUnsichererOrdnername, vorlageHolen, werkbankOrdner, } from "../arbeitsplatz-dateien.js";
4
4
  import { alsBefehlszeile, alsWort, trenneNamenUndOptionen, zerlege } from "../argumente.js";
5
- import { werkbankOrdnerAus, werkbankUmbenennen } from "../werkbank-name.js";
6
- import { FIRMEN_CLAUDE_MD, MERKZETTEL_PFAD, firmenAbgleich, firmennameAusClaudeMd, leseMerkzettel, } from "../kontext-stand.js";
5
+ import { befehlIn, schalterIn } from "../befehlswoerter.js";
6
+ import { sprache } from "../sprache.js";
7
+ import { ARBEITSPLATZ_TEXTE } from "../texte/arbeitsplatz.js";
8
+ import { WERKBANK_TEXTE } from "../texte/werkbank.js";
9
+ import { werkbankOrdnerAus, werkbankSpracheAus, werkbankUmbenennen } from "../werkbank-name.js";
10
+ import { FIRMEN_CLAUDE_MD, MERKZETTEL_PFAD, ORDNER_NAMEN, firmenAbgleich, firmennameAusClaudeMd, leseMerkzettel, } from "../kontext-stand.js";
7
11
  import { leseEineDatei, leseMerkzettelDatei } from "../kontext-dateien.js";
8
12
  /**
9
13
  * Legt **nur eine Werkbank** an — in einem Firmen-Ordner, der schon steht.
@@ -56,27 +60,10 @@ import { leseEineDatei, leseMerkzettelDatei } from "../kontext-dateien.js";
56
60
  * verwalten (s. `WEITER-HIER.md`, Entscheidungen). Was dann von außen kommt,
57
61
  * darf ein lokaler Befehl nicht überschreiben.
58
62
  */
59
- const HILFE = `
60
- kimal3 werkbank neu [dein-vorname] [--firma "Name" | --firma-id "…"]
61
-
62
- Legt eine eigene Werkbank in einem bestehenden Firmen-Ordner an.
63
- Die Firmenebene (kontext/, CLAUDE.md, README.md) wird NICHT angefasst.
64
-
65
- dein-vorname Optional. Der Ordner traegt deinen Namen -- aus "sabine"
66
- wird "werkbank-sabine". Ohne Angabe steht dort der Name,
67
- den der Server aus deiner Anmelde-Adresse gebildet hat.
68
-
69
- --firma "Name" Noetig, wenn du zu mehreren Firmen gehoerst.
70
- Die Absage nennt dir die Namen als fertige Befehle.
71
-
72
- --firma-id "…" Fuer den Fall, dass zwei deiner Firmen GLEICH heissen.
73
- Auch diese Absage nennt dir fertige Befehle.
74
-
75
- 🔴 Bricht ab, wenn der Zielordner schon existiert und NICHT leer ist.
76
-
77
- Du hast noch gar keinen Firmen-Ordner?
78
- kimal3 arbeitsplatz neu <dein-vorname>
79
- `;
63
+ /** Die Hilfe von `werkbank neu` / `workbench new` in der Sprache des Menschen. */
64
+ export function hilfe(s) {
65
+ return WERKBANK_TEXTE[s].help;
66
+ }
80
67
  /**
81
68
  * 🔴 **Review-Runde 2 zu PR #25 — und es ist DERSELBE Fehler wie in Runde 1,
82
69
  * nur eine Datei weiter.** Hier stand:
@@ -102,16 +89,14 @@ const HILFE = `
102
89
  * fuellen muss, obwohl die Antwort danebensteht. *Ein Platzhalter ist ein
103
90
  * Eingestaendnis, dass man es nicht weiss -- hier weiss man es.*
104
91
  */
105
- function hinweisFuer(ordnerName) {
106
- const ziel = alsWort(ordnerName);
107
- return (" Ist das DEINE Werkbank? Dann steht sie schon -- geh einfach hinein:\n" +
108
- ` cd ${ziel}\n` +
109
- " Bist du es nicht, nimm deinen eigenen Vornamen:\n" +
110
- " kimal3 werkbank neu <dein-vorname>");
92
+ function hinweisFuer(ordnerName, s) {
93
+ return WERKBANK_TEXTE[s].adviceOwnWorkbench(alsWort(ordnerName), befehlIn("werkbank neu", s)); // kein-anzeigetext: Befehlsschlüssel
111
94
  }
112
95
  export async function werkbankNeu(argv) {
96
+ let s = sprache();
97
+ let t = WERKBANK_TEXTE[s];
113
98
  if (argv.includes("--help") || argv.includes("-h")) {
114
- console.log(HILFE);
99
+ console.log(hilfe(s));
115
100
  return 0;
116
101
  }
117
102
  /*
@@ -133,7 +118,7 @@ export async function werkbankNeu(argv) {
133
118
  const { woerter, optionen: optionsArgv } = trenneNamenUndOptionen(argv);
134
119
  const nameArg = woerter[0];
135
120
  if (nameArg !== undefined && nameArg.trim() === "") {
136
- console.error(`Ein leerer Name ist kein gueltiger Name.\n${HILFE}`);
121
+ console.error(t.emptyName + hilfe(s));
137
122
  return 1;
138
123
  }
139
124
  if (woerter.length > 1) {
@@ -160,17 +145,15 @@ export async function werkbankNeu(argv) {
160
145
  */
161
146
  const optionsAnhang = optionsArgv.length === 0 ? "" : ` ${alsBefehlszeile(optionsArgv)}`;
162
147
  const vorschlag = einfacheWoerter.length === woerter.length
163
- ? `Besteht dein Name aus mehreren Woertern? Dann schreib ihn zusammen:\n` +
164
- ` kimal3 werkbank neu ${einfacheWoerter.join("-")}${optionsAnhang}\n`
148
+ ? t.nameSuggestion(befehlIn("werkbank neu", s), einfacheWoerter.join("-"), optionsAnhang) // kein-anzeigetext: Befehlsschlüssel
165
149
  : "";
166
- console.error(`Mit „${woerter[1]}" kann ich hier nichts anfangen -- nur EIN Name ist erlaubt.\n` +
167
- `${vorschlag}${HILFE}`);
150
+ console.error(t.onlyOneName(woerter[1]) + vorschlag + hilfe(s));
168
151
  return 1;
169
152
  }
170
153
  // 🔑 Erst NACH den Namens-Absagen -- s. Zwilling in `arbeitsplatz.ts`.
171
- const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN);
154
+ const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN, s);
172
155
  if (!zerlegt.ok) {
173
- console.error(`${zerlegt.meldung}\n${HILFE}`);
156
+ console.error(`${zerlegt.meldung}\n${hilfe(s)}`);
174
157
  return 1;
175
158
  }
176
159
  const wunsch = {
@@ -180,14 +163,13 @@ export async function werkbankNeu(argv) {
180
163
  // `null` heisst: nach dem Umschreiben blieb kein Zeichen uebrig, das in einem
181
164
  // Ordnernamen stehen darf. Ein Mensch hat hier getippt und verdient eine
182
165
  // Antwort -- der Server schweigt im selben Fall und bleibt beim Vordruck.
183
- let gewuenschteWerkbank = null;
166
+ // 🌐 Teil 2: nur „bleibt etwas uebrig?" -- das Praefix kommt erst aus der Lieferung, s. unten.
184
167
  if (nameArg !== undefined) {
185
- gewuenschteWerkbank = werkbankOrdnerAus(nameArg);
186
- if (gewuenschteWerkbank === null) {
187
- console.error(`Aus „${nameArg}" kann ich keinen Ordnernamen machen -- es bleiben nur`);
188
- console.error("Zeichen uebrig, die in keinem Ordnernamen stehen duerfen.");
168
+ if (werkbankOrdnerAus(nameArg, "de") === null) {
169
+ console.error(t.unusableName1(nameArg));
170
+ console.error(t.unusableName2);
189
171
  console.error("");
190
- console.error(" Nimm deinen Vornamen: kimal3 werkbank neu sabine");
172
+ console.error(t.unusableName3(befehlIn("werkbank neu", s))); // kein-anzeigetext: Befehlsschlüssel
191
173
  return 1;
192
174
  }
193
175
  }
@@ -198,12 +180,24 @@ export async function werkbankNeu(argv) {
198
180
  wird nie erreicht.
199
181
  */
200
182
  // 🔑 Der getippte Vorname reist in der Absage mit -- s. Zwilling.
201
- const befehlsname = nameArg === undefined
202
- ? "werkbank neu"
203
- : `werkbank neu ${alsWort(nameArg)}`;
204
- const holen = await vorlageHolen(befehlsname, wunsch);
183
+ /*
184
+ 🔑 **Die Firma dieses Ordners** -- Kennung aus dem Merkzettel, Name aus der
185
+ Firmen-`CLAUDE.md`. `vorlageHolen` nimmt sie nur, wenn der Server wegen
186
+ mehrerer Firmen fragt (A2-6, 25.09.), und die Wache weiter unten
187
+ (`firmenAbgleich`) vergleicht mit genau diesen beiden Werten.
188
+ */
189
+ const hierGelesen = leseEineDatei(process.cwd(), FIRMEN_CLAUDE_MD);
190
+ const ordnerFirma = {
191
+ id: leseMerkzettelDatei(process.cwd()).firma_id,
192
+ name: firmennameAusClaudeMd(hierGelesen.ok ? hierGelesen.inhalt : null),
193
+ };
194
+ const holen = await vorlageHolen("werkbank neu", // kein-anzeigetext: Befehlsschlüssel
195
+ nameArg === undefined ? "" : ` ${alsWort(nameArg)}`, wunsch, ordnerFirma);
205
196
  if (!holen.ok)
206
197
  return holen.wert;
198
+ // 🌐 Die Antwort kann die Kontosprache umgestellt haben (Spec 3.2, Schritt 3): Was ab hier gedruckt wird, in der neuen.
199
+ s = sprache();
200
+ t = WERKBANK_TEXTE[s];
207
201
  const daten = holen.daten;
208
202
  const werkbank = werkbankOrdner(daten.dateien);
209
203
  /*
@@ -250,14 +244,17 @@ export async function werkbankNeu(argv) {
250
244
  ist kein Grund, sie hier falsch zu benennen.
251
245
  */
252
246
  if (werkbank !== null && istUnsichererOrdnername(werkbank)) {
253
- console.error(`Die Antwort enthaelt einen unsicheren Ordnernamen: „${werkbank}". Bitte melden.`);
247
+ console.error(t.unsafeFolderName(werkbank));
254
248
  return 3;
255
249
  }
256
250
  if (werkbank === null) {
257
- console.error("In der Vorlage steckt kein Werkbank-Ordner -- damit kann ich hier nichts anlegen.");
258
- console.error("Bitte melden.");
251
+ console.error(t.noWorkbank1);
252
+ console.error(t.noWorkbank2);
259
253
  return 3;
260
254
  }
255
+ // 🌐 Teil 2: Das Praefix der gelieferten Werkbank ist die Kontosprache; ein getipptes fremdes
256
+ // Praefix wird ersetzt, nie verdoppelt (`workbench-jens` bei deutschem Konto -> `werkbank-jens`).
257
+ const gewuenschteWerkbank = nameArg === undefined ? null : werkbankOrdnerAus(nameArg, werkbankSpracheAus(werkbank) ?? "de");
261
258
  /*
262
259
  🔑 **Die Prüfung, ob wir überhaupt in einer Firma stehen** -- und sie ist
263
260
  keine Förmlichkeit: In `<werkbank>/.claude/settings.json` steht
@@ -304,17 +301,28 @@ export async function werkbankNeu(argv) {
304
301
  Eine kaputte Lieferung ist keine falsche Eingabe.
305
302
  */
306
303
  if (firmenOrdner.length === 0) {
307
- console.error("Die Vorlage vom Server nennt gar keine Firmenebene -- damit kann ich");
308
- console.error("nicht pruefen, ob du an der richtigen Stelle stehst.");
309
- console.error("Das liegt nicht an dir. Bitte melden.");
304
+ console.error(t.noCompanyLevel1);
305
+ console.error(t.noCompanyLevel2);
306
+ console.error(t.noCompanyLevel3);
310
307
  return 3;
311
308
  }
309
+ /*
310
+ 🔴 **Teil-2-Rest Minor 7: Der Firmen-Ordner steht da, nur in der Sprache von frueher.** Alter
311
+ deutscher Ordner (`kontext/`), Firma inzwischen englisch: Der Server liefert die englische
312
+ Vorlage, `fehlend` nennt `context` -- und der Mensch las "Hier steht kein Firmen-Ordner" samt
313
+ "Bist du der ERSTE in deiner Firma?". Beides falsch. Diese Absage kommt VOR dem Zweig darunter.
314
+ */
315
+ const ordnerSprache = andereOrdnerSprache(fehlend, istOrdner);
316
+ if (ordnerSprache !== null) {
317
+ console.error(t.folderInOtherLanguage(ordnerSprache, `kimal3 ${befehlIn("arbeitsplatz neu", s)}`)); // kein-anzeigetext: Befehlsschlüssel
318
+ return 1;
319
+ }
312
320
  if (fehlend.length > 0) {
313
- console.error("Hier steht kein Firmen-Ordner -- ich sehe kein " + fehlend.join(", ") + ".");
314
- console.error("Eine Werkbank braucht die Firmenebene daneben; ohne sie findet Claude");
315
- console.error("euren Firmenkontext nicht.");
321
+ console.error(t.noCompanyFolder1(fehlend.join(", ")));
322
+ console.error(t.noCompanyFolder2);
323
+ console.error(t.noCompanyFolder3);
316
324
  console.error("");
317
- console.error(" Steht er woanders? Geh dorthin und versuch es dort noch einmal.");
325
+ console.error(t.noCompanyFolder4);
318
326
  console.error("");
319
327
  /*
320
328
  🔴 **Hier stand „Du hast noch keinen? kimal3 arbeitsplatz neu"**
@@ -337,12 +345,12 @@ export async function werkbankNeu(argv) {
337
345
  das so ist, ist die ehrliche Auskunft „frag, wo er liegt" -- und nicht
338
346
  ein Befehl, der etwas Falsches anlegt.
339
347
  */
340
- console.error(" Bist du der ERSTE in deiner Firma?");
341
- console.error(" kimal3 arbeitsplatz neu <dein-vorname>");
348
+ console.error(t.noCompanyFolder5);
349
+ console.error(t.noCompanyFolder6(befehlIn("arbeitsplatz neu", s))); // kein-anzeigetext: Befehlsschlüssel
342
350
  console.error("");
343
- console.error(" Hat deine Firma schon einen? Dann hol dir DEN -- z. B. als Kopie");
344
- console.error(" oder ueber euer Repo. Leg keinen neuen an, sonst habt ihr zwei");
345
- console.error(" verschiedene Firmenordner mit verschiedenen Regeln darin.");
351
+ console.error(t.noCompanyFolder7);
352
+ console.error(t.noCompanyFolder8);
353
+ console.error(t.noCompanyFolder9);
346
354
  return 1;
347
355
  }
348
356
  /*
@@ -365,11 +373,7 @@ export async function werkbankNeu(argv) {
365
373
  („ich schicke nichts Halbes hoch“).
366
374
  */
367
375
  const inhaltAus = (pfad) => daten.dateien.find((d) => d.pfad === pfad)?.inhalt ?? null;
368
- const hierGelesen = leseEineDatei(process.cwd(), FIRMEN_CLAUDE_MD);
369
- const abgleich = firmenAbgleich({
370
- id: leseMerkzettelDatei(process.cwd()).firma_id,
371
- name: firmennameAusClaudeMd(hierGelesen.ok ? hierGelesen.inhalt : null),
372
- }, {
376
+ const abgleich = firmenAbgleich(ordnerFirma, {
373
377
  id: leseMerkzettel(inhaltAus(MERKZETTEL_PFAD)).firma_id,
374
378
  name: firmennameAusClaudeMd(inhaltAus(FIRMEN_CLAUDE_MD)),
375
379
  });
@@ -381,10 +385,10 @@ export async function werkbankNeu(argv) {
381
385
  sieht, und laesst den Menschen entscheiden.
382
386
  */
383
387
  if (abgleich.art === "zweifel") {
384
- console.error(`Achtung: Dieser Ordner nennt als Firma „${abgleich.hier}",`);
385
- console.error(`du hast aber „${abgleich.gewaehlt}" gewaehlt.`);
386
- console.error("Ich lege die Werkbank trotzdem an -- vielleicht heisst die Firma bei euch");
387
- console.error("nur anders geschrieben. Stimmt es nicht, loesch den neuen Ordner wieder.");
388
+ console.error(t.doubt1(abgleich.hier));
389
+ console.error(t.doubt2(abgleich.gewaehlt));
390
+ console.error(t.doubt3);
391
+ console.error(t.doubt4);
388
392
  console.error("");
389
393
  }
390
394
  if (abgleich.art === "andere-firma") {
@@ -400,20 +404,20 @@ export async function werkbankNeu(argv) {
400
404
  */
401
405
  const genannt = wunsch.firma ?? wunsch.firmaId;
402
406
  if (genannt === undefined) {
403
- console.error("Dieser Ordner gehoert zu einer anderen Firma als der, zu der du gehoerst.");
404
- console.error("Ich lege hier keine Werkbank an, die einen fremden Firmennamen traegt --");
405
- console.error("sie wuerde Claude bei jedem Start die falsche Firma nennen.");
407
+ console.error(t.otherCompany1);
408
+ console.error(t.otherCompany2);
409
+ console.error(t.otherCompany3);
406
410
  console.error("");
407
- console.error(" Deine Werkbank gehoert in den Ordner DEINER Firma. Geh dorthin und");
408
- console.error(" versuch es dort noch einmal.");
411
+ console.error(t.otherCompany4);
412
+ console.error(t.otherCompany5);
409
413
  return 1;
410
414
  }
411
- console.error(`Dieser Ordner gehoert zu einer ANDEREN Firma als „${genannt}".`);
412
- console.error("Ich lege hier keine Werkbank an, die einen fremden Firmennamen traegt --");
413
- console.error("sie wuerde Claude bei jedem Start die falsche Firma nennen.");
415
+ console.error(t.otherNamedCompany1(genannt));
416
+ console.error(t.otherNamedCompany2);
417
+ console.error(t.otherNamedCompany3);
414
418
  console.error("");
415
- console.error(` Soll die Werkbank fuer „${genannt}" sein? Dann gehoert sie in DEREN Ordner.`);
416
- console.error(" Soll sie fuer die Firma hier sein? Dann nimm deren Namen.");
419
+ console.error(t.otherNamedCompany4(genannt));
420
+ console.error(t.otherNamedCompany5);
417
421
  return 1;
418
422
  }
419
423
  /*
@@ -453,20 +457,46 @@ export async function werkbankNeu(argv) {
453
457
  const meine = umbenannt
454
458
  .filter((d) => d.pfad.startsWith(praefix))
455
459
  .map((d) => ({ pfad: d.pfad.slice(praefix.length), inhalt: d.inhalt }));
456
- const wert = dateienSchreiben(meine, ordnerName, hinweisFuer(ordnerName));
460
+ const wert = dateienSchreiben(meine, ordnerName, hinweisFuer(ordnerName, s));
457
461
  if (wert !== 0)
458
462
  return wert;
459
- console.log(`✓ ${meine.length} ${meine.length === 1 ? "Datei" : "Dateien"} angelegt.`);
463
+ console.log(ARBEITSPLATZ_TEXTE[s].filesCreated(meine.length));
460
464
  console.log(` ${resolve(ordnerName)}`);
461
465
  console.log("");
462
- console.log(" Die Firmenebene ist unberuehrt geblieben.");
466
+ console.log(t.companyUntouched);
463
467
  console.log("");
464
- console.log(" Weiter geht's:");
465
- console.log(` cd ${alsWort(ordnerName)}`);
466
- console.log(" claude");
467
- console.log(' dann "Lass uns einrichten" tippen');
468
+ /*
469
+ Erst NACH dem Anlegen (Lage L2): Vorher koennte noch eine Absage folgen, und
470
+ dann stuende da „uebernommen" fuer etwas, das nie entstanden ist.
471
+ */
472
+ if (holen.uebernommen) {
473
+ const gewaehlt = firmennameAusClaudeMd(inhaltAus(FIRMEN_CLAUDE_MD));
474
+ console.log(t.companyFromFolder(gewaehlt));
475
+ console.log(t.companyFromFolderHint(schalterIn("firma", s))); // kein-anzeigetext: Schalterschlüssel
476
+ console.log("");
477
+ }
478
+ // 🔑 Derselbe Rat wie nach `arbeitsplatz neu`: eine Fassung des Ausloesesatzes, nicht zwei.
479
+ console.log(ARBEITSPLATZ_TEXTE[s].nextStepsHeader);
480
+ console.log(` cd ${alsWort(ordnerName)}`); // kein-anzeigetext: Befehlszeile zum Kopieren
481
+ console.log(" claude"); // kein-anzeigetext: Befehlszeile zum Kopieren
482
+ console.log(ARBEITSPLATZ_TEXTE[s].nextStepsSay);
468
483
  return 0;
469
484
  }
485
+ /**
486
+ * Fehlt NUR der Kontextordner der Lieferung, weil hier der der ANDEREN Sprache steht? (Teil-2-Rest
487
+ * Minor 7: alter deutscher Ordner, Firma inzwischen englisch.) Dann ist das kein "hier steht kein
488
+ * Firmen-Ordner", sondern ein Sprachwechsel der Firma -- die Sprache des VORHANDENEN Ordners, sonst null.
489
+ * Rein (check:cli): `gibtEs` ist im Befehl `istOrdner`.
490
+ */
491
+ export function andereOrdnerSprache(fehlend, gibtEs) {
492
+ if (fehlend.length !== 1)
493
+ return null;
494
+ if (fehlend[0] === ORDNER_NAMEN.en.kontext && gibtEs(ORDNER_NAMEN.de.kontext))
495
+ return "de";
496
+ if (fehlend[0] === ORDNER_NAMEN.de.kontext && gibtEs(ORDNER_NAMEN.en.kontext))
497
+ return "en";
498
+ return null;
499
+ }
470
500
  /**
471
501
  * Die Ordner der Firmenebene, aus der Lieferung abgeleitet: jeder erste
472
502
  * Pfad-Teil, der einen Unterordner benennt und nicht die Werkbank ist.
@@ -0,0 +1,185 @@
1
+ export const BEFEHLE_EN = {
2
+ "kimal3 login": {
3
+ stempel: "013360ee",
4
+ befehl: "kimal3 login",
5
+ tippen: "! kimal3 login",
6
+ terminal: "kimal3 login",
7
+ beschreibung: "Connect this computer to your account",
8
+ rhythmusOderSatz: "once per computer",
9
+ details: [
10
+ "It shows you an address and a code: open the address in your browser and type in the code. The terminal picks up the confirmation automatically — you have ten minutes.",
11
+ "After that, it stays that way. Your sign-in is saved as a file in your user folder and survives IDE restarts, computer restarts and updates. If the connection does break after a long pause, a command will tell you — then just run kimal3 login once more; that's not an error.",
12
+ ],
13
+ },
14
+ "kimal3 skill install": {
15
+ stempel: "8491e5d6",
16
+ befehl: "kimal3 skill install",
17
+ tippen: "! kimal3 skill install",
18
+ terminal: "kimal3 skill install",
19
+ beschreibung: "Teach Claude to use this tool — and give you the /k3 commands",
20
+ rhythmusOderSatz: "once — and again after every update",
21
+ details: [
22
+ "The command that makes the others useful: it sets up 🤖 Claude's instructions (the “skill”) and 🧑 your slash commands.",
23
+ "It does NOT run automatically — run it once after installing and again after every update. Otherwise Claude keeps working with the old instructions.",
24
+ "You'll know it worked when it reports “Skill created.” and lists the slash commands. Claude only picks up both in your NEXT session — restart the current one.",
25
+ ],
26
+ },
27
+ "kimal3 arbeitsplatz neu": {
28
+ stempel: "231b90bf",
29
+ befehl: "kimal3 workplace new",
30
+ tippen: "! kimal3 workplace new",
31
+ terminal: "kimal3 workplace new",
32
+ beschreibung: "Create the whole AI Workplace — company level and workbench",
33
+ rhythmusOderSatz: "once per company",
34
+ details: [
35
+ "Fetches the template from your Pro account and creates it here as a real folder — CLAUDE.md, routine commands, the workbench for your projects. The same package is available to download on /pro.",
36
+ "The folder is called AI-Workplace (KI-Arbeitsplatz for a German-speaking company). To keep several companies apart, use the folder above it, e.g. Clients/ARNO/AI-Workplace. Add your first name and your workbench inside it is called workbench-sabine: kimal3 workplace new sabine.",
37
+ "If the target folder already exists and isn't empty, the command stops instead of overwriting anything — your work is safe. You'll know it worked when it tells you how many files it created.",
38
+ "⚠️ It also creates the company level (context/ with brand, systems, security — kontext/ in a German workplace). If that already exists, you need kimal3 workbench new — otherwise you end up with a second company inside the first.",
39
+ ],
40
+ },
41
+ "kimal3 werkbank neu": {
42
+ stempel: "da69f97f",
43
+ befehl: "kimal3 workbench new",
44
+ tippen: "! kimal3 workbench new",
45
+ terminal: "kimal3 workbench new",
46
+ beschreibung: "Only create your own workbench in an existing company",
47
+ rhythmusOderSatz: "once per person",
48
+ details: [
49
+ "For everyone except the first person: the company folder is already there (context/ with brand, systems, security — kontext/ in a German workplace) — you only need your own workbench inside it.",
50
+ "🔴 It doesn't touch the company level. Not a single byte in context/, CLAUDE.md or README.md — not even by writing an ‘identical’ copy over it.",
51
+ "It only runs if you really are in a company folder. If the company level next to it is missing, it stops and tells you — a workbench without it leads nowhere, because its settings refer to ../context (../kontext in a German workplace).",
52
+ "Add your first name — the folder is named after it: sabine becomes workbench-sabine. Without it, the server builds the name from your sign-in address. That way you can see later whose workbench is whose.",
53
+ ],
54
+ },
55
+ "kimal3 kontext runter": {
56
+ stempel: "9ccf1083",
57
+ befehl: "kimal3 context download",
58
+ tippen: "! kimal3 context download",
59
+ terminal: "kimal3 context download",
60
+ beschreibung: "Fetch the company context — CLAUDE.md and the five files in context/",
61
+ rhythmusOderSatz: "whenever the company might have changed something",
62
+ details: [
63
+ "Fetches CLAUDE.md and the five files in context/ (kontext/ in a German workplace) from your company and puts them in your AI Workplace. Anything you've changed locally is left alone — it tells you how to send it up.",
64
+ "If you changed a file locally AND the company has changed it too in the meantime, it saves your version in context/before-<date>/ (kontext/vorher-<datum>/ in a German workplace) first, then replaces it — nothing gets lost.",
65
+ "You'll know it worked when it names the updated files or says “Your company context is up to date.”",
66
+ "⚠️ Like kimal3 workbench new, it needs your own Pro account. Without one you get exit code 4 — then don't retry.",
67
+ ],
68
+ },
69
+ "kimal3 kontext fragen": {
70
+ stempel: "34decb4c",
71
+ befehl: "kimal3 context questions",
72
+ tippen: "/k3context",
73
+ terminal: "kimal3 context questions",
74
+ beschreibung: "Answer the questions about your company — Claude takes you through them",
75
+ rhythmusOderSatz: "let's answer the questions about my company",
76
+ details: [
77
+ "Reads the five files in context/ (kontext/ in a German workplace) and prints every question that has nothing written under it yet — with file, question and hint. If all are answered, it says so in one sentence.",
78
+ "It only reads: no network, no sign-in, no writing. That's why it comes first — Claude then asks you the questions one by one and writes your answers into the files.",
79
+ "After that, kimal3 context upload sends them to the company. Only the company admin may do that; everyone else passes their answers on.",
80
+ ],
81
+ },
82
+ "kimal3 kontext hoch": {
83
+ stempel: "fbf3f20b",
84
+ befehl: "kimal3 context upload",
85
+ tippen: "! kimal3 context upload",
86
+ terminal: "kimal3 context upload",
87
+ beschreibung: "Upload your local changes to the five company texts",
88
+ rhythmusOderSatz: "after you've edited context/*.md — company admin only",
89
+ details: [
90
+ "Sends all five company texts to your company — the files in context/ (profile.md, company.md, brand.md, systems.md, security.md; in a German workplace kontext/ with kurzprofil.md, firma.md, marke.md, systeme.md, sicherheit.md). That way they also reach your colleagues with their next kimal3 context download.",
91
+ "🔴 Only the company admin may do this. If you're not one, it tells you — then pass your addition on to the company admin; trying again won't change that.",
92
+ "⚠️ If something goes wrong, what counts is the MESSAGE, not the number after it. Exit code 1 stands for ‘you're not an admin’, ‘there's no workplace here’, ‘someone else has changed it in the meantime’ (way out: --overwrite) and several more. The same code also appears under “The company has saved your changes — only the local tracking file couldn't be written”, which only says that a file on your disk couldn't be written. That is a success: everything reached the company. Read the sentence, not the number.",
93
+ "If one of the five files no longer has its original questions — for example because something else was copied over it — it stops and sends nothing, not even the other four. kimal3 context download brings the file back.",
94
+ "You'll know it worked when it reports “✓ changed at the company:” with the file names.",
95
+ ],
96
+ },
97
+ "kimal3 projekt neu …": {
98
+ stempel: "34505297",
99
+ befehl: "kimal3 project new …",
100
+ tippen: "/k3project <what you built>",
101
+ terminal: 'kimal3 project new --title "Quote generator" --hours 4 --result 1200',
102
+ beschreibung: "Add a project to your success profile",
103
+ rhythmusOderSatz: "add this as a project",
104
+ details: [
105
+ "Just say so when you've finished something. Claude puts together the title, time saved and net result itself — it knows the numbers from your conversation. The slash command is the same route, just one step quicker.",
106
+ "It runs kimal3 project new. Only the title is required; kimal3 project new --help shows all the options.",
107
+ ],
108
+ },
109
+ "kimal3 projekt liste": {
110
+ stempel: "87d02e68",
111
+ befehl: "kimal3 project list",
112
+ tippen: "/k3list",
113
+ terminal: "kimal3 project list",
114
+ beschreibung: "Show the projects you've added",
115
+ rhythmusOderSatz: "show my projects",
116
+ details: [
117
+ "Shows what's already in your success profile — with dates and numbers.",
118
+ "What it's really for: Claude looks here BEFORE adding a project. Without that check, the same project would sooner or later appear twice on a public page that carries your name.",
119
+ ],
120
+ },
121
+ "kimal3 firma daten": {
122
+ stempel: "3195364c",
123
+ befehl: "kimal3 company data",
124
+ tippen: "/k3analyse <your question>",
125
+ terminal: 'kimal3 company data --company "Nordwind Handel GmbH"',
126
+ beschreibung: "Fetch your company's data so Claude can analyse it",
127
+ rhythmusOderSatz: "analyse my company",
128
+ details: [
129
+ "Fetches the key figures, people and projects of your company — Claude does the analysis. Just ask: “Which three projects bring in the most measurable return?”",
130
+ "If you belong to several companies, it never quietly picks one; it lays them out for you as ready-made commands. If two have the SAME name, --company-id helps — also ready to copy, you don't have to rename anything.",
131
+ "⚠️ This is personal data about your colleagues. For use in a real company you need a commercial Claude account with a data processing agreement. If employees' performance is analysed, in Germany this requires the works council's agreement (co-determination).",
132
+ ],
133
+ },
134
+ "kimal3 wache": {
135
+ stempel: "2daae4e2",
136
+ befehl: "kimal3 watch",
137
+ tippen: "! kimal3 watch",
138
+ terminal: "kimal3 watch",
139
+ beschreibung: "Check whether your access is valid and whether anything is pending",
140
+ rhythmusOderSatz: "automatically when Claude starts",
141
+ details: [
142
+ "The daily check runs by itself when Claude Code starts — you don't have to call it. Silent when there's nothing to do; an open task (access, version, skill) is reported in every session until it's done.",
143
+ "If it stays silent, everything is fine: no line means no problem. It only speaks up when your access has expired, a message is waiting, a new version is available or your skill files are out of date.",
144
+ "If it can't reach our server, it says nothing about access and messages — but still reports a new version if npm knows one. The two questions don't depend on each other.",
145
+ ],
146
+ },
147
+ "kimal3 logout": {
148
+ stempel: "9dfc20d7",
149
+ befehl: "kimal3 logout",
150
+ tippen: "! kimal3 logout",
151
+ terminal: "kimal3 logout",
152
+ beschreibung: "Disconnect this computer",
153
+ rhythmusOderSatz: "only when needed",
154
+ details: [
155
+ "Disconnects this one computer — useful on someone else's or a shared device, or if a computer goes missing. Your other computers stay signed in.",
156
+ ],
157
+ },
158
+ };
159
+ /**
160
+ * Der Fingerabdruck der deutschen Felder eines Katalog-Eintrags: FNV-1a ueber 32 Bit, 8 Hex-Zeichen.
161
+ * Ein eigener kleiner Hash statt `node:crypto`, damit die Datei importfrei bleibt; er muss keinem
162
+ * Angreifer standhalten, nur eine Textaenderung bemerken.
163
+ */
164
+ export function fingerabdruck(b) {
165
+ const text = JSON.stringify([b.beschreibung, b.wer === "du" ? b.rhythmus : b.ausloeser, b.tippen, b.terminal, b.details]);
166
+ let h = 0x811c9dc5;
167
+ for (let i = 0; i < text.length; i++) {
168
+ h ^= text.charCodeAt(i);
169
+ h = Math.imul(h, 0x01000193);
170
+ }
171
+ return (h >>> 0).toString(16).padStart(8, "0");
172
+ }
173
+ /** Was ein Katalog-Eintrag in einer Sprache anzeigt -- deutsch unveraendert aus dem Katalog. */
174
+ export function anzeigeIn(eintrag, sprache) {
175
+ if (sprache === "en")
176
+ return BEFEHLE_EN[eintrag.befehl];
177
+ return {
178
+ befehl: eintrag.befehl,
179
+ tippen: eintrag.tippen,
180
+ terminal: eintrag.terminal,
181
+ beschreibung: eintrag.beschreibung,
182
+ rhythmusOderSatz: eintrag.wer === "du" ? eintrag.rhythmus : eintrag.ausloeser,
183
+ details: eintrag.details,
184
+ };
185
+ }
@@ -11,6 +11,9 @@
11
11
  * NodeNext (Import-Pfade enden auf `.js`), die App mit dem Bundler. Eine Datei
12
12
  * ohne eigene Importe liest sich aus beiden Welten ohne Uebersetzung.
13
13
  *
14
+ * 🌐 Englisch steht in `befehle-katalog-en.ts` daneben (Teil 3, T3-11) -- eine Datei waere
15
+ * ueber 300 Code-Zeilen. Die Terminal-Hilfe baut `hilfe.ts` aus beiden.
16
+ *
14
17
  * ⚠️ Der Katalog beschreibt, WAS ES GIBT — nicht, wer es darf. Die Pro-Schranke
15
18
  * sitzt in der Route und im Befehl, nicht hier.
16
19
  */
@@ -62,7 +65,7 @@ export const BEFEHLE = [
62
65
  terminal: "kimal3 arbeitsplatz neu",
63
66
  details: [
64
67
  "Holt die Vorlage von deinem Pro-Konto und legt sie hier als echten Ordner an — CLAUDE.md, Takt-Befehle, die Werkbank für deine Projekte. Dasselbe Paket, das auch als Download auf /pro liegt.",
65
- "Der Ordner heißt immer KI-Arbeitsplatz — mehrere Firmen trennst du über den Ordner darüber, z. B. Kunden/ARNO/KI-Arbeitsplatz. Gib deinen Vornamen mit, dann heißt deine Werkbank darin werkbank-sabine: kimal3 arbeitsplatz neu sabine.",
68
+ "Der Ordner heißt KI-Arbeitsplatz (bei einer englischsprachigen Firma AI-Workplace) — mehrere Firmen trennst du über den Ordner darüber, z. B. Kunden/ARNO/KI-Arbeitsplatz. Gib deinen Vornamen mit, dann heißt deine Werkbank darin werkbank-sabine (bei einem englischen Konto workbench-sabine): kimal3 arbeitsplatz neu sabine.",
66
69
  "Gibt es den Zielordner schon und ist er nicht leer, bricht der Befehl ab, statt etwas zu überschreiben — deine Arbeit ist sicher. Geklappt hat es, wenn er die Zahl der angelegten Dateien nennt.",
67
70
  "⚠️ Er legt auch die Firmenebene an (kontext/ mit Marke, Systemen, Sicherheit). Steht die schon, brauchst du kimal3 werkbank neu — sonst entsteht eine zweite Firma in der ersten.",
68
71
  ],
@@ -87,7 +90,7 @@ export const BEFEHLE = [
87
90
  "Für alle außer der ersten Person: Der Firmen-Ordner steht schon (kontext/ mit Marke, Systemen, Sicherheit) — du brauchst nur deinen eigenen Arbeitsplatz darin.",
88
91
  "🔴 Er fasst die Firmenebene nicht an. Kein Byte in kontext/, CLAUDE.md oder README.md — auch nicht schreibend „identisch drüber“.",
89
92
  "Er läuft nur, wenn du wirklich in einem Firmen-Ordner stehst. Fehlt die Firmenebene daneben, bricht er ab und sagt es — eine Werkbank ohne sie zeigt ins Leere, weil ihre Einstellungen auf ../kontext verweisen.",
90
- "Gib deinen Vornamen mit — der Ordner trägt ihn: aus sabine wird werkbank-sabine. Ohne Angabe bildet der Server ihn aus deiner Anmelde-Adresse. So sieht man später, wem welche Werkbank gehört.",
93
+ "Gib deinen Vornamen mit — der Ordner trägt ihn: aus sabine wird werkbank-sabine (bei einem englischen Konto workbench-sabine). Ohne Angabe bildet der Server ihn aus deiner Anmelde-Adresse. So sieht man später, wem welche Werkbank gehört.",
91
94
  ],
92
95
  },
93
96
  /*
@@ -180,9 +183,11 @@ export const BEFEHLE = [
180
183
  /*
181
184
  🔴 Dieser Eintrag steht bewusst NICHT im "Weg, den ein Mensch geht" --
182
185
  niemand muss ihn selbst tippen. Er wird bei jedem Start von Claude Code
183
- von selbst aufgerufen (Aufgabe 8) und steht hier trotzdem, weil
184
- `tippen()` in `wache-rechnung.ts` seine `!`-Schreibweise braucht, wenn die
185
- Wache selbst einmal auf ihren eigenen Befehl verweisen muss.
186
+ von selbst aufgerufen (Aufgabe 8). Er steht hier noch, obwohl `tippen()`
187
+ in `wache-rechnung.ts` seit Teil 3 (Task 15) die Befehlswoerter aus
188
+ `befehlswoerter.ts` liest und den Katalog nicht mehr braucht. Die Zeile
189
+ bleibt sichtbar (Task 16): Die deutsche Hilfe bleibt Zeichen fuer Zeichen,
190
+ und ein Mensch, der `kimal3 wache` sucht, findet es hier.
186
191
  */
187
192
  {
188
193
  befehl: "kimal3 wache",
@@ -209,47 +214,3 @@ export const BEFEHLE = [
209
214
  ],
210
215
  },
211
216
  ];
212
- /**
213
- * Die Terminal-Hilfe, gebaut aus dem Katalog.
214
- *
215
- * Die Spalten richten sich an der laengsten Befehlszeile aus — von Hand
216
- * gesetzte Leerzeichen waeren beim naechsten laengeren Befehl krumm.
217
- *
218
- * 📌 Sie nimmt bewusst NUR `befehl` und `beschreibung`: Im Terminal ist eine
219
- * Uebersicht gefragt, kein Handbuch. Die langen `details` haben ihren Platz auf
220
- * `/pro`, wo man sie auf- und zuklappen kann — `--help` je Befehl bleibt der
221
- * Weg im Terminal.
222
- */
223
- export function hilfeText() {
224
- const breite = Math.max(...BEFEHLE.map((b) => b.befehl.length));
225
- const zeilen = BEFEHLE.map((b) => ` ${b.befehl.padEnd(breite + 2)}${b.beschreibung}`).join("\n");
226
- return `
227
- kimal3 — trag deine KI-Projekte dort ein, wo sie entstehen.
228
-
229
- ${zeilen}
230
-
231
- Mehr zu einem Befehl: kimal3 projekt neu --help
232
- `;
233
- }
234
- /**
235
- * Wie ein Befehl **getippt** wird — aus dem Katalog geholt, nicht abgeschrieben.
236
- *
237
- * 🔑 **Warum das eine eigene Funktion ist** (2026-08-20): Auf `/pro` stand
238
- * `const ANMELDEN = "! kimal3 login"` als Zeichenkette im Seitencode — eine
239
- * zweite Wahrheit ueber einen Befehl, den der Katalog schon fuehrt. Wer im
240
- * Katalog umbenennt, laesst sie stehen, und die Seite zeigt weiter den alten
241
- * Aufruf. Dieselbe Klasse Fehler wie der fest getippte `cd`-Pfad in Runde 3.
242
- *
243
- * ⚠️ Sie **wirft**, statt einen leeren Text zu liefern. Die Aufrufer sind
244
- * Modul-Konstanten — ein falscher Name fliegt damit beim `npm run build` auf,
245
- * nicht erst beim Kunden vor einem Kopier-Kasten, in dem nichts steht.
246
- * *Ein stiller Fehler auf einer bezahlten Seite ist teurer als ein lauter im
247
- * Build.*
248
- */
249
- export function tippBefehl(befehl) {
250
- const eintrag = BEFEHLE.find((b) => b.befehl === befehl);
251
- if (!eintrag) {
252
- throw new Error(`Kein Katalog-Eintrag fuer „${befehl}". Steht der Befehl noch in BEFEHLE?`);
253
- }
254
- return eintrag.tippen;
255
- }