kimal3 0.4.0-durchstich.1 → 0.4.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 (39) hide show
  1. package/README.md +2 -0
  2. package/claude-plugins/.claude-plugin/marketplace.json +8 -0
  3. package/claude-plugins/README.md +7 -0
  4. package/claude-plugins/kimal3-logo/.claude-plugin/plugin.json +6 -0
  5. package/claude-plugins/kimal3-logo/hooks/hooks.json +1 -0
  6. package/claude-plugins/kimal3-logo/hooks/register.tsx +80 -0
  7. package/coach/de/kimal3-coach.md +167 -0
  8. package/coach/en/kimal3-coach.md +167 -0
  9. package/dist/arbeitsplatz-dateien.js +1 -1
  10. package/dist/arbeitsplatz-finden.js +115 -0
  11. package/dist/arbeitsplatz-suche.js +176 -0
  12. package/dist/befehle/einrichten.js +311 -32
  13. package/dist/befehle/kurs.js +77 -0
  14. package/dist/befehle/login.js +8 -1
  15. package/dist/befehle/skill-install.js +16 -2
  16. package/dist/befehle-katalog-en.js +12 -0
  17. package/dist/befehle-katalog.js +12 -0
  18. package/dist/befehlswoerter.js +4 -0
  19. package/dist/coach-ablegen.js +130 -0
  20. package/dist/coach-rechnung.js +40 -0
  21. package/dist/einstellungen-mischen.js +68 -28
  22. package/dist/einstellungen-schreiben.js +48 -12
  23. package/dist/kimal3.js +6 -3
  24. package/dist/logo-ablegen.js +117 -0
  25. package/dist/plugins-mischen.js +78 -0
  26. package/dist/texte/coach.js +27 -0
  27. package/dist/texte/einrichten-marken.js +2 -2
  28. package/dist/texte/einrichten.js +73 -2
  29. package/dist/texte/kurs.js +21 -0
  30. package/dist/wache-schalter.js +30 -5
  31. package/package.json +29 -27
  32. package/skill/SKILL.md +7 -3
  33. package/skill/commands/k3feierabend.md +2 -3
  34. package/skill/commands/k3kurs.md +41 -53
  35. package/skill/commands/k3start.md +4 -3
  36. package/skill/en/SKILL.md +7 -3
  37. package/skill/en/commands/k3course.md +41 -54
  38. package/skill/en/commands/k3start.md +4 -3
  39. package/skill/en/commands/k3wrapup.md +3 -4
@@ -0,0 +1,117 @@
1
+ import { cpSync, existsSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ /**
6
+ * Legt den Plugin-Katalog mit dem Logo-Mod in `~/.kimal3/claude-plugins` ab und setzt dort die
7
+ * Sprache des Kontos. Claude Code liest den Mod aus genau diesem Ordner (Katalog `kimal3`,
8
+ * `plugins-mischen.ts`).
9
+ *
10
+ * 🔑 Warum ein fester Ordner im Benutzerordner und nicht der Ordner im installierten npm-Paket:
11
+ * Der hängt am Installationsort von Node.js und wechselt mit ihm -- der Eintrag in Claudes
12
+ * Einstellungen zeigte dann ins Leere.
13
+ * 🔑 Der Ordner gehört `kimal3` (seine README sagt es): Jeder Lauf ersetzt ihn ganz (Lage P-11).
14
+ */
15
+ export function katalogOrdner() {
16
+ return join(homedir(), ".kimal3", "claude-plugins"); // kein-anzeigetext: Ordnername
17
+ }
18
+ /** Die Zeile im Mod, die die Sprache festlegt -- `check:logo` prüft, dass es sie genau einmal gibt. */
19
+ export const SPRACH_ZEILE = "const SPRACHE: 'de' | 'en' = 'de'"; // kein-anzeigetext: Quelltext des Mods
20
+ /** Reine Rechnung: der Mod-Text in der Sprache des Kontos, oder `null`, wenn die Zeile fehlt. */
21
+ export function mitSprache(quelltext, sprache) {
22
+ if (quelltext.split(SPRACH_ZEILE).length !== 2)
23
+ return null;
24
+ return quelltext.replace(SPRACH_ZEILE, `const SPRACHE: 'de' | 'en' = '${sprache}'`); // kein-anzeigetext: Quelltext des Mods
25
+ }
26
+ /**
27
+ * 🔴 NIE EIN HALBER ORDNER (Durchsicht-Funde 2 und 9): Claudes Einstellungen zeigen auf `katalogOrdner()`,
28
+ * und eine laufende Claude-Sitzung hat den Mod daraus geladen. Deshalb:
29
+ * 1. Sprache setzen, BEVOR irgendetwas angefasst wird -- fehlt die Zeile, bleibt alles, wie es war.
30
+ * 2. Steht schon genau das da, was entstehen soll: nichts anfassen (Lage P-8). Das ist auch der Normalfall
31
+ * eines zweiten Laufs, und so kommt er gar nicht erst in die Nähe der Windows-Sperren unten.
32
+ * 3. Den neuen Ordner daneben fertig bauen (`….neu`), dann tauschen: alten Ordner wegschieben, neuen an
33
+ * seinen Platz, alten löschen.
34
+ * 🔑 Der weggeschobene Ordner bekommt bei JEDEM Lauf einen frischen Namen (Durchsicht 2, Funde 2 und 4):
35
+ * Ein Rest, den Windows festhält, blockiert so keinen späteren Lauf, und zurückgeholt wird nur, was
36
+ * DIESER Lauf weggeschoben hat -- nie ein veralteter Rest von früher.
37
+ * Lässt sich der alte nicht wegschieben (Claude hält ihn manchmal fest), bleibt er unversehrt stehen und
38
+ * gilt weiter: ein Erfolg mit `alterStand`, den `kimal3 einrichten` in einer Zeile nennt.
39
+ */
40
+ export function logoAblegen(sprache) {
41
+ // `dist/logo-ablegen.js` -> `../claude-plugins` im installierten Paket (`files` in package.json).
42
+ const quelle = fileURLToPath(new URL("../claude-plugins", import.meta.url));
43
+ const ziel = katalogOrdner();
44
+ const neu = `${ziel}.neu`; // kein-anzeigetext: Ordnername
45
+ const modDatei = (ordner) => join(ordner, "kimal3-logo", "hooks", "register.tsx");
46
+ try {
47
+ const text = mitSprache(readFileSync(modDatei(quelle), "utf8"), sprache);
48
+ if (text === null)
49
+ return { ok: false, detail: modDatei(quelle) };
50
+ if (stehtSchonDa(quelle, ziel, text)) {
51
+ // Auch im Normalfall aufräumen, samt einem halb gebauten `….neu`: sonst blieben Reste für immer (Durchsicht 3/4).
52
+ rmSync(neu, { recursive: true, force: true });
53
+ resteWegraeumen(ziel);
54
+ return { ok: true, ordner: ziel };
55
+ }
56
+ rmSync(neu, { recursive: true, force: true });
57
+ cpSync(quelle, neu, { recursive: true });
58
+ writeFileSync(modDatei(neu), text, "utf8");
59
+ if (!existsSync(ziel)) {
60
+ renameSync(neu, ziel);
61
+ return { ok: true, ordner: ziel };
62
+ }
63
+ const alt = `${ziel}.alt-${Date.now()}`; // kein-anzeigetext: Ordnername
64
+ try {
65
+ renameSync(ziel, alt);
66
+ }
67
+ catch {
68
+ rmSync(neu, { recursive: true, force: true });
69
+ return { ok: true, ordner: ziel, alterStand: true };
70
+ }
71
+ try {
72
+ renameSync(neu, ziel);
73
+ }
74
+ catch (e) {
75
+ renameSync(alt, ziel);
76
+ return { ok: false, detail: e.message };
77
+ }
78
+ resteWegraeumen(ziel);
79
+ return { ok: true, ordner: ziel };
80
+ }
81
+ catch (e) {
82
+ return { ok: false, detail: e.message };
83
+ }
84
+ }
85
+ /** Gleiche Dateien, gleicher Inhalt -- der Mod mit der Sprache des Kontos, alles andere wie im Paket. */
86
+ function stehtSchonDa(quelle, ziel, modText) {
87
+ if (!existsSync(ziel))
88
+ return false;
89
+ const dateien = (ordner) => alleDateien(ordner).sort();
90
+ const soll = dateien(quelle);
91
+ if (JSON.stringify(soll) !== JSON.stringify(dateien(ziel)))
92
+ return false;
93
+ const mod = join("kimal3-logo", "hooks", "register.tsx");
94
+ return soll.every((d) => (d === mod ? Buffer.from(modText, "utf8") : readFileSync(join(quelle, d))).equals(readFileSync(join(ziel, d))));
95
+ }
96
+ /**
97
+ * Alle Dateien unter `ordner`, als Pfade relativ dazu. 🔑 Von Hand statt `readdirSync(…, { recursive: true })`: Das kann
98
+ * Node.js erst ab 20.1, und unter 20.0 fände der Vergleich gar keine Datei -- „gleich“, und das Logo würde nie erneuert
99
+ * (Durchsicht 3, Fund 7; `engines` erlaubt 20).
100
+ */
101
+ function alleDateien(ordner, unter = "") {
102
+ return readdirSync(join(ordner, unter), { withFileTypes: true }).flatMap((e) => e.isDirectory() ? alleDateien(ordner, join(unter, e.name)) : e.isFile() ? [join(unter, e.name)] : []);
103
+ }
104
+ /** Weggeschobene Ordner früherer Läufe (`claude-plugins.alt…`); was Windows noch festhält, bleibt bis zum nächsten Lauf. */
105
+ function resteWegraeumen(ziel) {
106
+ const name = `${basename(ziel)}.alt`; // kein-anzeigetext: Ordnername
107
+ for (const eintrag of readdirSync(dirname(ziel))) {
108
+ if (!eintrag.startsWith(name))
109
+ continue;
110
+ try {
111
+ rmSync(join(dirname(ziel), eintrag), { recursive: true, force: true });
112
+ }
113
+ catch {
114
+ /* festgehalten -- der nächste Lauf versucht es wieder */
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,78 @@
1
+ import { istObjekt, leseEinstellungen } from "./einstellungen-mischen.js";
2
+ /**
3
+ * Traegt superpowers und das KImal3-Logo in Claudes Einstellungen ein -- als Katalog plus
4
+ * Einschalter, so wie Anthropic Firmen-Mods auf jeden Rechner verteilt (Bauplan 2026-10-15,
5
+ * „Warum anders als die Spec"). Beim naechsten Start laedt Claude Code beides selbst.
6
+ *
7
+ * 🔴 DIESE DATEI FASST KEINE DATEI AN -- dieselbe Bauform wie `einstellungen-mischen.ts`, aus
8
+ * demselben Grund: In `~/.claude/settings.json` steht der Arbeitsplatz des Menschen.
9
+ * Geschrieben wird ueber `einstellungen-schreiben.ts` (Sicherung, Zuruecklesen).
10
+ */
11
+ export const KATALOG = "kimal3"; // kein-anzeigetext: Name des Plugin-Katalogs in Claudes Einstellungen
12
+ export const LOGO = `kimal3-logo@${KATALOG}`; // kein-anzeigetext: Plugin-Kennung
13
+ const OFFIZIELL = "claude-plugins-official"; // kein-anzeigetext: Anthropics Katalog
14
+ export const SUPERPOWERS = `superpowers@${OFFIZIELL}`; // kein-anzeigetext: Plugin-Kennung
15
+ /**
16
+ * 🔑 Jede Entscheidung des Menschen bleibt stehen (Lagen P-5 bis P-7, P-10):
17
+ * - Ein Eintrag fuer das Logo -- auch `false` -- heisst: Er hat schon entschieden. Wir setzen
18
+ * nur, was fehlt.
19
+ * - superpowers aus IRGENDEINEM Katalog (`superpowers@…`, an oder aus) heisst: nicht noch eins.
20
+ * Zwei superpowers gaeben jede Faehigkeit doppelt.
21
+ * - Den offiziellen Katalog tragen wir nur ein, wenn er fehlt und wir superpowers einschalten.
22
+ * Nur der Katalog `kimal3` gehoert uns: Er zeigt immer auf unseren Ordner (P-9, Umzug).
23
+ * 🔑 `katalogPfad` ist `null`, wenn der Logo-Ordner nicht angelegt werden konnte: Dann bleiben Katalog und
24
+ * Logo-Eintrag, wie sie sind, und superpowers kommt trotzdem -- es braucht den Ordner nicht (Durchsicht 2, Fund 1).
25
+ * 🔑 `mitSuperpowers` ist `false`, wenn Git fehlt: Claude Code klont Anthropics Katalog mit Git und scheiterte sonst bei
26
+ * jedem Start (Durchsicht 3, Fund 5). Was schon drinsteht, bleibt -- auch ein früher eingetragenes superpowers.
27
+ */
28
+ export function mischePlugins(alt, katalogPfad, mitSuperpowers = true) {
29
+ const lesen = leseEinstellungen(alt);
30
+ if (!lesen.ok)
31
+ return { art: "abgelehnt", grund: lesen.grund };
32
+ const { objekt, leer } = lesen;
33
+ const kataloge = objekt.extraKnownMarketplaces ?? {};
34
+ const plugins = objekt.enabledPlugins ?? {};
35
+ if (!istObjekt(kataloge) || !istObjekt(plugins))
36
+ return { art: "abgelehnt", grund: "fremde-form" };
37
+ // Nur die Quelle gehört uns; was Claude Code selbst in den Eintrag schreibt (`autoUpdate`, wenn
38
+ // Sabine es unter /plugin umschaltet), bleibt stehen (QA-Fund 7). Zeigt die Quelle schon auf unseren
39
+ // Ordner, bleibt der Eintrag Zeichen für Zeichen, wie er ist -- auch in anderer Reihenfolge der
40
+ // Schlüssel; sonst schriebe jeder Lauf die Datei neu (Durchsicht-Fund 6, Lage P-8).
41
+ const alterEintrag = istObjekt(kataloge[KATALOG]) ? kataloge[KATALOG] : {};
42
+ const alteQuelle = istObjekt(alterEintrag.source) ? alterEintrag.source : {};
43
+ const zeigtSchonHin = alteQuelle.source === "directory" && alteQuelle.path === katalogPfad;
44
+ const neueKataloge = { ...kataloge };
45
+ const neuePlugins = { ...plugins };
46
+ if (katalogPfad !== null) {
47
+ neueKataloge[KATALOG] = zeigtSchonHin ? alterEintrag : { ...alterEintrag, source: { source: "directory", path: katalogPfad } };
48
+ if (!(LOGO in plugins))
49
+ neuePlugins[LOGO] = true;
50
+ }
51
+ if (mitSuperpowers && !Object.keys(plugins).some((k) => k.startsWith("superpowers@"))) {
52
+ neuePlugins[SUPERPOWERS] = true;
53
+ if (!(OFFIZIELL in kataloge)) {
54
+ neueKataloge[OFFIZIELL] = { source: { source: "github", repo: "anthropics/claude-plugins-official" } };
55
+ }
56
+ }
57
+ const neu = { ...objekt, extraKnownMarketplaces: neueKataloge, enabledPlugins: neuePlugins };
58
+ // Wie bei der Wache: Wer nichts zu tun hat, fasst die Datei nicht an (P-8).
59
+ if (!leer && JSON.stringify(neu) === JSON.stringify(objekt))
60
+ return { art: "unveraendert" };
61
+ return { art: "geschrieben", inhalt: JSON.stringify(neu, null, 2) + "\n" };
62
+ }
63
+ /**
64
+ * Was nach dem Eintragen wirklich gilt -- fuer die Zeile `Plugins:`, die Claude der Kundin weitersagt.
65
+ * 🔑 Aus der Datei gelesen, nicht aus dem, was wir eingetragen haben: Hat sie superpowers selbst
66
+ * ausgeschaltet, muss die Zeile das sagen, damit die Anleitung sie nicht ins Menue schickt (Durchsicht-Fund 1).
67
+ */
68
+ export function pluginStand(inhalt) {
69
+ const lesen = leseEinstellungen(inhalt);
70
+ const plugins = lesen.ok ? lesen.objekt.enabledPlugins : undefined;
71
+ if (!istObjekt(plugins))
72
+ return { superpowers: "fehlt", logo: "fehlt" };
73
+ const superpowers = Object.entries(plugins).filter(([k]) => k.startsWith("superpowers@")).map(([, v]) => v);
74
+ return {
75
+ superpowers: superpowers.includes(true) ? "an" : superpowers.length > 0 ? "aus" : "fehlt",
76
+ logo: plugins[LOGO] === true ? "an" : LOGO in plugins ? "aus" : "fehlt",
77
+ };
78
+ }
@@ -0,0 +1,27 @@
1
+ import { EINRICHTEN_MARKEN } from "./einrichten-marken.js";
2
+ const M = EINRICHTEN_MARKEN;
3
+ const de = {
4
+ setUp: (pfad) => ` ${M.de.coach} eingerichtet (${pfad}).`,
5
+ upToDate: ` ${M.de.coach} ist aktuell.`,
6
+ updated: ` ${M.de.coach} aktualisiert — deine gelernten Regeln sind geblieben.`,
7
+ foreign: (pfad) => ` ${M.de.coach} Unter ${pfad} liegt schon eine Datei, die nicht von KImal3 stammt — ich lasse sie, wie sie ist.`,
8
+ noSeparator: (pfad, neu) => ` ${M.de.coach} nicht aktualisiert — in ${pfad} fehlt die Zeile, ab der deine gelernten Regeln stehen. Die neue Fassung liegt unter ${neu}.`,
9
+ failed: (grund) => ` ${M.de.coach} konnte nicht abgelegt werden — ${grund}`,
10
+ // Der Grund zu `failed`, wenn der Vorlage im Programm selbst die Trenn-Marke fehlt (ein Paketfehler, `coach-rechnung.ts`).
11
+ templateWithoutSeparator: "der Vorlage im Programm fehlt die Zeile, ab der die gelernten Regeln stehen. Das ist ein Fehler in kimal3 — bitte melde ihn uns.",
12
+ // Die Zeile spricht immer die Sprache, in die umgestellt wurde -- deshalb steht der Sprachname fest im Satz.
13
+ languageSwitched: "✓ Dein Coach ist jetzt auf Deutsch — deine gelernten Regeln sind geblieben.",
14
+ oldCourseCoach: (pfad, ablage) => ` ${M.de.alterKursCoach} Du hast im alten Mini-Kurs schon einmal einen Coach eingerichtet (${pfad}). Jetzt gibt es einen neuen, und zwei gleichzeitig würden sich widersprechen. Soll ich den alten beiseitelegen? Du findest ihn danach unter ${ablage}.`,
15
+ };
16
+ const en = {
17
+ setUp: (pfad) => ` ${M.en.coach} set up (${pfad}).`,
18
+ upToDate: ` ${M.en.coach} up to date.`,
19
+ updated: ` ${M.en.coach} updated — your learned rules are still there.`,
20
+ foreign: (pfad) => ` ${M.en.coach} There's already a file at ${pfad} that doesn't come from KImal3 — I'll leave it as it is.`,
21
+ noSeparator: (pfad, neu) => ` ${M.en.coach} not updated — ${pfad} is missing the line your learned rules start below. The new version is at ${neu}.`,
22
+ failed: (grund) => ` ${M.en.coach} couldn't be put in place — ${grund}`,
23
+ templateWithoutSeparator: "the program's template is missing the line your learned rules start below. That's a bug in kimal3 — please tell us.",
24
+ languageSwitched: "✓ Your coach is now in English — your learned rules are still there.",
25
+ oldCourseCoach: (pfad, ablage) => ` ${M.en.alterKursCoach} You set up a coach in the old mini course (${pfad}). There's a new one now, and two at once would contradict each other. Shall I put the old one aside? You'll find it at ${ablage} afterwards.`,
26
+ };
27
+ export const COACH_TEXTE = { de, en };
@@ -4,6 +4,6 @@
4
4
  * Programm druckt sie, die Anleitung nennt sie, `check:kurs` haelt beide gleich.
5
5
  */
6
6
  export const EINRICHTEN_MARKEN = {
7
- de: { code: "Anmelde-Code:", adresse: "Adresse:", arbeitsplatz: "Arbeitsplatz:", werkbank: "Werkbank:", angemeldet: "Angemeldet als" },
8
- en: { code: "Sign-in code:", adresse: "Address:", arbeitsplatz: "Workplace:", werkbank: "Workbench:", angemeldet: "Signed in as" },
7
+ de: { code: "Anmelde-Code:", adresse: "Adresse:", arbeitsplatz: "Arbeitsplatz:", werkbank: "Werkbank:", angemeldet: "Angemeldet als", plugins: "Plugins:", naechster: "Als Nächstes:", coach: "Coach:", alterKursCoach: "Alter Kurs-Coach:" },
8
+ en: { code: "Sign-in code:", adresse: "Address:", arbeitsplatz: "Workplace:", werkbank: "Workbench:", angemeldet: "Signed in as", plugins: "Plugins:", naechster: "Next:", coach: "Coach:", alterKursCoach: "Old course coach:" },
9
9
  };
@@ -5,31 +5,102 @@ const de = {
5
5
  addressLine: (url) => ` ${M.de.adresse} ${url}`,
6
6
  browserOpened: " Ich öffne die Seite gerade in deinem Browser. Bestätige dort nur, wenn derselbe Code dasteht.",
7
7
  browserNotOpened: " Öffne die Adresse oben in deinem Browser. Bestätige dort nur, wenn derselbe Code dasteht.",
8
- help: "\n kimal3 einrichten [--firma \"Name\" | --firma-id \"…\"]\n\n Meldet dich an (der Browser öffnet sich mit deinem Code), legt deinen\n KI-Arbeitsplatz in deinem Benutzerordner an, richtet die /k3-Befehle und\n die Wache ein und hakt die Schritte im Kurs ab. Ein zweiter Lauf ergänzt\n nur, was fehlt.\n",
8
+ help: "\n kimal3 einrichten [--firma \"Name\" | --firma-id \"…\"] [--arbeitsplatz \"<pfad>\"] [--werkbank <name>]\n\n Meldet dich an (der Browser öffnet sich mit deinem Code), sucht deinen\n KI-Arbeitsplatz oder legt ihn in deinem Benutzerordner an, richtet die\n /k3-Befehle und die Wache ein und hakt die Schritte im Kurs ab. Ein\n vorhandener Arbeitsplatz wird benutzt, nicht verändert.\n\n --arbeitsplatz wo dein Arbeitsplatz liegt oder neu entstehen soll\n --werkbank welche Werkbank darin deine ist\n",
9
9
  start: "\nIch richte deinen KI-Arbeitsplatz ein.",
10
10
  filesWritten: (neu, schonDa) => `\n✓ ${neu} Dateien angelegt, ${schonDa} lagen schon da.`,
11
11
  workplaceLine: (pfad) => ` ${M.de.arbeitsplatz} ${pfad}`,
12
12
  workbenchLine: (name) => ` ${M.de.werkbank} ${name}`,
13
+ // Was Sabine in der nächsten Sitzung schreibt (Spec 2026-10-16, Z-15): Steht die Anleitung für die erste
14
+ // Sitzung noch in ihrer Werkbank, der Startsatz der Vorlage; sonst der Befehl für jeden weiteren Tag.
15
+ nextLine: (offen) => ` ${M.de.naechster} ${offen ? "„Lass uns einrichten“" : "/k3start"}`,
16
+ // Vorhandener Arbeitsplatz (Spec 2026-10-16, „Neue Sätze“). Jede Rückfrage endet mit fertigen Befehlen.
17
+ foundWorkplace: (pfad) => `\nDein Arbeitsplatz liegt schon unter ${pfad} — ich benutze diesen.`,
18
+ severalFound: "\nIch habe mehrere Arbeitsplätze gefunden. Welchen soll ich benutzen?",
19
+ foreignFolder: (pfad) => `\nUnter ${pfad} liegt schon ein Ordner, der nicht von KImal3 stammt. Ich lege dort nichts ab.`,
20
+ searchIncomplete: "\nIch konnte nicht überall nachsehen. Hast du schon einen Arbeitsplatz?",
21
+ rememberedGone: (pfad) => `\nDein Arbeitsplatz lag unter ${pfad}, dort ist er nicht mehr. Wo liegt er jetzt?`,
22
+ noMatchingWorkbench: (pfad) => `\nIn ${pfad} finde ich keine Werkbank für dich.`,
23
+ confirmWorkbench: (name) => `\nIst ${name} deine Werkbank?`,
24
+ noWorkbenchInWorkplace: (pfad) => `\nIn ${pfad} gibt es noch keine Werkbank. Leg deine an und starte die Einrichtung danach noch einmal:`,
25
+ // Die Überschriften über den Befehlen der Rückfragen.
26
+ existingWorkplace: " Er liegt hier (Pfad einsetzen):",
27
+ newWorkplace: " Leg einen neuen an:",
28
+ otherPlace: " Nimm einen anderen Ort, leer oder mit deinem Arbeitsplatz:",
29
+ yes: " Ja:",
30
+ noOther: " Nein, es ist eine andere:",
31
+ oneOfThese: " Ist eine davon deine:",
32
+ ownWorkbench: " Noch keine eigene? Leg sie an und starte die Einrichtung danach noch einmal:",
33
+ pathPlaceholder: "<pfad>",
34
+ // Ein mitkopierter Platzhalter in `--arbeitsplatz` -- Claude fragt dann nach dem echten Ort.
35
+ placeholderPath: (wert) => `\n${wert} ist ein Platzhalter, kein Ordner. Setz dort den echten Pfad deines Arbeitsplatzes ein; ich habe nichts angelegt.`,
36
+ commandLine: (rest) => ` kimal3 ${rest}`,
37
+ cdLine: (pfad) => ` cd ${pfad}`,
13
38
  courseMarked: "✓ Im Kurs abgehakt.",
14
39
  courseNotMarked: " Die Haken im Kurs konnte ich gerade nicht setzen — der nächste Lauf holt das nach.",
15
40
  done: "\n✓ Fertig eingerichtet.",
16
41
  unsafeFolder: (name) => `Der Server hat einen Ordnernamen geschickt, den ich nicht anlege: ${name}. Bitte melde das an kimal3.`,
17
42
  noWorkbank: "In der Lieferung steckt keine Werkbank. Bitte melde das an kimal3.",
43
+ // Plugins (Bauplan 2026-10-15): Claude liest die Zeile und sagt der Kundin, was nach dem Neustart erscheint.
44
+ logoName: "KImal3-Logo",
45
+ // `an`: eingeschaltet · `aus`: von ihr selbst ausgeschaltet (bleibt so, die Anleitung schickt sie dann nicht ins Menü)
46
+ // · `neu`: gerade eingetragen (kommt mit dem Neustart) oder stand schon da (Durchsicht-Funde 1 und 7).
47
+ pluginsLine: (an, aus, neu) => ` ${M.de.plugins} ${an.length > 0 ? `${an.join(", ")} — ${neu ? "erscheinen, sobald Claude ganz beendet und neu geöffnet ist" : "sind schon eingetragen"}.` : "nichts eingeschaltet."}` +
48
+ (aus.length > 0 ? ` Selbst ausgeschaltet, bleibt aus: ${aus.join(", ")}.` : ""),
49
+ pluginsNotWritten: (grund) => ` ${M.de.plugins} nicht eingetragen — ${grund}`,
50
+ settingsInvalid: "deine Claude-Einstellungen (~/.claude/settings.json) sind kein gültiges JSON. Ich habe sie nicht angefasst.",
51
+ settingsForeign: "in deinen Claude-Einstellungen (~/.claude/settings.json) steht etwas in einer Form, die ich nicht kenne. Ich habe sie nicht angefasst.",
52
+ settingsFailed: (ursache) => `deine Claude-Einstellungen (~/.claude/settings.json): ${ursache}.`,
53
+ // Logo (Durchsicht 2, Funde 1 und 3): Scheitert der Ordner, kommt superpowers trotzdem; hält Claude ihn fest, gilt die alte Fassung.
54
+ logoNotWritten: (detail) => ` Den Ordner für das KImal3-Logo konnte ich nicht anlegen oder erneuern (${detail}).`,
55
+ // Ohne Git kein superpowers (Durchsicht 3, Fund 5); die Anleitung zitiert den Anfang dieser Zeile.
56
+ superpowersNeedsGit: " superpowers braucht Git — es kommt mit dem nächsten `kimal3 einrichten`, sobald Git da ist.",
57
+ logoOldVersion: " Das KImal3-Logo bleibt vorerst in der bisherigen Fassung, weil Claude den Ordner gerade benutzt. Beende Claude ganz und starte danach `kimal3 einrichten` noch einmal.",
18
58
  };
19
59
  const en = {
20
60
  codeLine: (code) => `\n ${M.en.code} ${code}`,
21
61
  addressLine: (url) => ` ${M.en.adresse} ${url}`,
22
62
  browserOpened: " Opening the page in your browser now. Only confirm there if it shows the same code.",
23
63
  browserNotOpened: " Open the address above in your browser. Only confirm there if it shows the same code.",
24
- help: "\n kimal3 setup [--company \"Name\" | --company-id \"…\"]\n\n Signs you in (your browser opens with your code), creates your AI Workplace\n in your user folder, sets up the /k3 commands and the watch, and ticks off\n the course steps. A second run only adds what is missing.\n",
64
+ help: "\n kimal3 setup [--company \"Name\" | --company-id \"…\"] [--workplace \"<path>\"] [--workbench <name>]\n\n Signs you in (your browser opens with your code), finds your AI Workplace\n or creates it in your user folder, sets up the /k3 commands and the watch,\n and ticks off the course steps. An existing workplace is used, not changed.\n\n --workplace where your workplace is, or where a new one should go\n --workbench which workbench in it is yours\n",
25
65
  start: "\nSetting up your AI Workplace.",
26
66
  filesWritten: (neu, schonDa) => `\n✓ ${neu} files created, ${schonDa} were already there.`,
27
67
  workplaceLine: (pfad) => ` ${M.en.arbeitsplatz} ${pfad}`,
28
68
  workbenchLine: (name) => ` ${M.en.werkbank} ${name}`,
69
+ // The start sentence of the English template (workbench-your-name/CLAUDE.md, anleitung-en.ts) -- exactly that one.
70
+ nextLine: (offen) => ` ${M.en.naechster} ${offen ? "“Let's get set up”" : "/k3start"}`,
71
+ foundWorkplace: (pfad) => `\nYour workplace already exists at ${pfad} — I'll use that one.`,
72
+ severalFound: "\nI found more than one workplace. Which one should I use?",
73
+ foreignFolder: (pfad) => `\nThere's already a folder at ${pfad} that doesn't come from KImal3. I won't put anything there.`,
74
+ searchIncomplete: "\nI couldn't look everywhere. Do you already have a workplace?",
75
+ rememberedGone: (pfad) => `\nYour workplace was at ${pfad} and isn't there anymore. Where is it now?`,
76
+ noMatchingWorkbench: (pfad) => `\nI can't find a workbench for you in ${pfad}.`,
77
+ confirmWorkbench: (name) => `\nIs ${name} your workbench?`,
78
+ noWorkbenchInWorkplace: (pfad) => `\nThere's no workbench in ${pfad} yet. Create yours, then run the setup again:`,
79
+ existingWorkplace: " It's here (fill in the path):",
80
+ newWorkplace: " Create a new one:",
81
+ otherPlace: " Choose another place, empty or with your workplace in it:",
82
+ yes: " Yes:",
83
+ noOther: " No, it's another one:",
84
+ oneOfThese: " If one of these is yours:",
85
+ ownWorkbench: " None of your own yet? Create it, then run the setup again:",
86
+ pathPlaceholder: "<path>",
87
+ placeholderPath: (wert) => `\n${wert} is a placeholder, not a folder. Put the real path of your workplace there; I haven't created anything.`,
88
+ commandLine: (rest) => ` kimal3 ${rest}`,
89
+ cdLine: (pfad) => ` cd ${pfad}`,
29
90
  courseMarked: "✓ Ticked off in the course.",
30
91
  courseNotMarked: " I couldn't tick off the course steps just now — the next run will catch up.",
31
92
  done: "\n✓ All set up.",
32
93
  unsafeFolder: (name) => `The server sent a folder name I won't create: ${name}. Please report this to kimal3.`,
33
94
  noWorkbank: "The delivery contains no workbench. Please report this to kimal3.",
95
+ logoName: "KImal3 logo",
96
+ pluginsLine: (an, aus, neu) => ` ${M.en.plugins} ${an.length > 0 ? `${an.join(", ")} — ${neu ? "will appear once Claude is fully quit and reopened" : "already added"}.` : "nothing switched on."}` +
97
+ (aus.length > 0 ? ` Switched off by you, stays off: ${aus.join(", ")}.` : ""),
98
+ pluginsNotWritten: (grund) => ` ${M.en.plugins} not added — ${grund}`,
99
+ settingsInvalid: "your Claude settings (~/.claude/settings.json) are not valid JSON. I left them untouched.",
100
+ settingsForeign: "your Claude settings (~/.claude/settings.json) contain something in a form I don't know. I left them untouched.",
101
+ settingsFailed: (ursache) => `your Claude settings (~/.claude/settings.json): ${ursache}.`,
102
+ logoNotWritten: (detail) => ` I couldn't create or renew the folder for the KImal3 logo (${detail}).`,
103
+ superpowersNeedsGit: " superpowers needs Git — it comes with the next `kimal3 setup` once Git is there.",
104
+ logoOldVersion: " The KImal3 logo stays on its previous version for now, because Claude is using the folder. Quit Claude completely, then run `kimal3 setup` again.",
34
105
  };
35
106
  export const EINRICHTEN_TEXTE = { de, en };
@@ -0,0 +1,21 @@
1
+ const de = {
2
+ beatFertig: (nummer) => `Beat ${nummer} ✓`,
3
+ beatTeil: (nummer, erledigt, gesamt) => `Beat ${nummer} — ${erledigt} von ${gesamt}`,
4
+ uebersicht: (beats) => `Kurs: ${beats}`,
5
+ alleErledigt: (anzahl) => `Kurs: abgeschlossen ✓ — alle ${anzahl} Schritte erledigt.`,
6
+ naechster: (text, claude) => `Als Nächstes: ${text} (${claude ? "setzt Claude" : "du hakst ab"})`,
7
+ nochOffen: "Noch offen:",
8
+ offenZeile: (beat, text, claude) => ` ○ Beat ${beat}: ${text} (${claude ? "setzt Claude" : "du hakst ab"})`,
9
+ imBrowser: (url) => `Im Browser: ${url}/kurs`,
10
+ };
11
+ const en = {
12
+ beatFertig: (nummer) => `Part ${nummer} ✓`,
13
+ beatTeil: (nummer, erledigt, gesamt) => `Part ${nummer} — ${erledigt} of ${gesamt}`,
14
+ uebersicht: (beats) => `Course: ${beats}`,
15
+ alleErledigt: (anzahl) => `Course: complete ✓ — all ${anzahl} steps done.`,
16
+ naechster: (text, claude) => `Next: ${text} (${claude ? "Claude sets it" : "you tick it off"})`,
17
+ nochOffen: "Still open:",
18
+ offenZeile: (beat, text, claude) => ` ○ Part ${beat}: ${text} (${claude ? "Claude sets it" : "you tick it off"})`,
19
+ imBrowser: (url) => `In the browser: ${url}/kurs`,
20
+ };
21
+ export const KURS_TEXTE = { de, en };
@@ -32,6 +32,8 @@ export function leseWacheDatei() {
32
32
  if (Array.isArray(inhalt.sitzungen)) {
33
33
  raus.sitzungen = inhalt.sitzungen.filter((s) => typeof s === "string");
34
34
  }
35
+ if (typeof inhalt.eingerichtet === "string")
36
+ raus.eingerichtet = inhalt.eingerichtet;
35
37
  return raus;
36
38
  }
37
39
  catch {
@@ -116,6 +118,22 @@ export function merkeSitzung(sitzung) {
116
118
  return;
117
119
  schreibeWacheDatei(mitSitzung(leseWacheDatei() ?? {}, sitzung));
118
120
  }
121
+ /**
122
+ * Merken, in welcher Claude-Sitzung die Wache gerade eingetragen wurde. Wirft nie.
123
+ *
124
+ * 🔴 NICHT `merkeSitzung` (Sandbox-Lauf 14.10., Befund S-3): Der Eintrag greift erst beim
125
+ * naechsten Start -- ohne Merkzettel meldete der naechste Befehl in derselben Sitzung sofort
126
+ * "hat nicht nachgesehen". Bis 0.4.0-durchstich.1 stempelte `skill install` deshalb die
127
+ * Sitzung als NACHGESEHEN. Das hielt den Fehlalarm still, aber auch die Wache selbst: Wer im
128
+ * selben Gespraech weitermacht, hoerte sie am ersten Tag nie (gemessen: das Pro-Angebot lag
129
+ * bereit, `cli_nachrichten_gezeigt` blieb leer). Ein eigenes Feld trennt beides --
130
+ * `wacheAusgefallen` liest es, `schonFuerDieseSitzung` nicht.
131
+ */
132
+ export function merkeEinrichtung(sitzung) {
133
+ if (sitzung === null)
134
+ return;
135
+ schreibeWacheDatei({ ...(leseWacheDatei() ?? {}), eingerichtet: sitzung });
136
+ }
119
137
  /** Wie viele Sitzungen der Merkzettel sich merkt -- genug fuer mehrere Fenster ueber Tage. */
120
138
  const MERKE_SITZUNGEN = 20;
121
139
  /**
@@ -171,7 +189,10 @@ function gesehen(inhalt, jetzt) {
171
189
  * Terminalfenster tippt, will die Antwort jetzt -- ein Merkzettel von gestern
172
190
  * darf ihn nicht zum Schweigen bringen.
173
191
  */
174
- export function schonFuerDieseSitzung(inhalt, jetzt) {
192
+ export function schonFuerDieseSitzung(
193
+ // `eingerichtet` steht im Typ, damit der ganze Merkzettel hereinpasst -- gelesen wird es
194
+ // hier ABSICHTLICH nicht: Eingerichtet heisst nicht nachgesehen (S-3, `merkeEinrichtung`).
195
+ inhalt, jetzt) {
175
196
  return jetzt !== null && inhalt !== null && gesehen(inhalt, jetzt);
176
197
  }
177
198
  /**
@@ -186,9 +207,13 @@ export function schonFuerDieseSitzung(inhalt, jetzt) {
186
207
  * dann aus wie an einem guten Tag. *Eine Wache, die still stirbt, ist
187
208
  * schlimmer als keine: Sie kauft Vertrauen und deckt nichts mehr.*
188
209
  *
189
- * 🔴 DREI MAL SCHWEIGEN, EINMAL SPRECHEN:
210
+ * 🔴 FUENF MAL SCHWEIGEN, EINMAL SPRECHEN:
190
211
  * - Keine Sitzungsnummer -> der Mensch sitzt im Terminalfenster, die Wache
191
212
  * hatte nie einen Anlass zu laufen. "Nicht gelaufen" heisst hier nichts.
213
+ * - Kein Merkzettel -> die Wache war nie eingerichtet; `skill install` legt ihn an.
214
+ * Bis 0.4.0-durchstich.1 meldete das hier -- die allererste Ausgabe einer neuen
215
+ * Kundin war ein Fehlalarm (Sandbox-Lauf 14.10., Befund S-2).
216
+ * - In dieser Sitzung eingerichtet -> sie greift erst beim naechsten Start (S-3).
192
217
  * - Abgeschaltet -> wer `kimal3 wache aus` getippt hat, will nicht taeglich
193
218
  * daran erinnert werden. Sonst waere der Ausschalter keiner.
194
219
  * - Dieselbe Nummer -> sie hat fuer diese Sitzung nachgesehen.
@@ -201,9 +226,9 @@ export function wacheAusgefallen(
201
226
  // Fassung traegt genau das und sonst nichts, und dieser Fall gehoert
202
227
  // geprueft. Ein Typ, der ihn verbietet, verbietet auch seine Pruefung.
203
228
  inhalt, jetzt) {
204
- if (jetzt === null)
229
+ if (jetzt === null || inhalt === null)
205
230
  return false;
206
- if (inhalt !== null && inhalt.aus === true)
231
+ if (inhalt.aus === true)
207
232
  return false;
208
- return inhalt === null || !gesehen(inhalt, jetzt);
233
+ return !gesehen(inhalt, jetzt) && inhalt.eingerichtet !== jetzt;
209
234
  }
package/package.json CHANGED
@@ -1,27 +1,29 @@
1
- {
2
- "name": "kimal3",
3
- "version": "0.4.0-durchstich.1",
4
- "description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
5
- "type": "module",
6
- "bin": {
7
- "kimal3": "dist/kimal3.js"
8
- },
9
- "files": [
10
- "dist",
11
- "skill",
12
- "README.md"
13
- ],
14
- "engines": {
15
- "node": ">=20"
16
- },
17
- "scripts": {
18
- "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
19
- "prepublishOnly": "npm run build"
20
- },
21
- "keywords": [
22
- "kimal3",
23
- "cli"
24
- ],
25
- "homepage": "https://app.kimal3.de/pro",
26
- "license": "SEE LICENSE IN README.md"
27
- }
1
+ {
2
+ "name": "kimal3",
3
+ "version": "0.4.0",
4
+ "description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
5
+ "type": "module",
6
+ "bin": {
7
+ "kimal3": "dist/kimal3.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skill",
12
+ "claude-plugins",
13
+ "coach",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "scripts": {
20
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "kimal3",
25
+ "cli"
26
+ ],
27
+ "homepage": "https://app.kimal3.de/pro",
28
+ "license": "SEE LICENSE IN README.md"
29
+ }
package/skill/SKILL.md CHANGED
@@ -24,7 +24,7 @@ den du erkennst.
24
24
  | `/k3` | Wo er steht — und alle kimal3-Befehle auf einen Blick |
25
25
  | `/k3start` | Sitzung starten: wo das Projekt steht und was als Nächstes dran ist |
26
26
  | `/k3feierabend` | Sitzung sauber beenden: Stand sichern, aufräumen, zusammenfassen |
27
- | `/k3kurs` | Zurück in den Mini-Kurs — da weiter, wo er aufgehört hat |
27
+ | `/k3kurs` | Zurück in den Mini-Kurs — holt den Stand mit `kimal3 kurs` und macht da weiter, wo er aufgehört hat |
28
28
  | `/k3projekt` | Ein fertiges Projekt ins Erfolgsprofil eintragen |
29
29
  | `/k3liste` | Seine eingetragenen Projekte zeigen |
30
30
  | `/k3analyse` | Firmendaten holen und seine Frage dazu beantworten |
@@ -158,8 +158,7 @@ gemeinsam ausfüllen — es sind vier verschiedene. Such nach eckigen Klammern,
158
158
  zu folgen: Eine abgetippte Liste veraltet in dem Moment, in dem die Vorlage wächst. Frag nach,
159
159
  statt zu raten.
160
160
 
161
- ⚠️ `[FIRMA]` findest du meist schon ausgefüllt — den Namen setzt der Server beim Ausliefern ein.
162
- `[DATUM]` ebenso. Was übrig bleibt, sind die Angaben über den **Menschen** vor dir.
161
+ ⚠️ `[FIRMA]` findest du meist schon ausgefüllt — den Namen setzt der Server beim Ausliefern ein. Was übrig bleibt, sind die Angaben über den **Menschen** vor dir.
163
162
 
164
163
  🔴 **Nur in der Werkbank — nirgends sonst.** Die oberste `CLAUDE.md` und alles in `kontext/` (in einer englischen Firma `context/`)
165
164
  gehören der **Firma**, nicht dem Menschen vor dir. Den Firmennamen setzt der Server beim
@@ -360,6 +359,11 @@ mitbestimmungspflichtig. Das entscheidet die Firma, nicht dieses Werkzeug.
360
359
  - „Was würdest du als Geschäftsführung als Nächstes automatisieren, gemessen an dem, was bisher
361
360
  funktioniert hat — und was davon lässt sich auf andere Abteilungen übertragen?"
362
361
 
362
+ ## Kursstand lesen (`kimal3 kurs`)
363
+ Zeigt, wo der Mensch im Mini-Kurs steht: eine Zeile je Beat, `Als Nächstes:` mit dem nächsten Schritt und dahinter, wer
364
+ ihn abhakt — „(du hakst ab)" oder „(setzt Claude)". Rückgabewert `2` heißt nicht angemeldet, `3` nicht erreichbar —
365
+ dann **keinen Kurs aus dem Gedächtnis erzählen**. Der Slash-Befehl `/k3kurs` führt dadurch.
366
+
363
367
  ## Rückgabewerte
364
368
  `0` erledigt · `1` **Wiederholen hilft nicht — LIES DIE MELDUNG** · `2` Anmeldung fehlt ·
365
369
  `3` vorübergehende Störung, einmal wiederholen · `4` angemeldet, aber kein Pro-Zugang —
@@ -24,10 +24,9 @@ Die Status-Datei (`WEITER-HIER.md` oder `ROADMAP.md`) ist der Zettel für den n
24
24
  taugt nur, solange man sie in einem Atemzug lesen kann. **Jeder Feierabend schreibt etwas dazu —
25
25
  also muss auch jeder Feierabend etwas wegräumen**, sonst wächst sie, bis niemand sie mehr liest.
26
26
 
27
- **Der Statuszettel heißt `WEITER-HIER.md` — oder `NEXT-STEPS.md`, wenn der Ordner englisch angelegt wurde.** Liegt einer da, nimm ihn. Liegen beide da, frag, welcher gilt, und schreib in keinen, bevor die Antwort da ist. Muss einer neu entstehen: in `werkbank-…` heißt er `WEITER-HIER.md`, in `workbench-…` `NEXT-STEPS.md`, sonst `WEITER-HIER.md`.
27
+ **Wo der Statuszettel liegt und wie er heißt.** Die Projektregel gilt **nur innerhalb eines Arbeitsplatzes**: Liegt ein Ordner `projekte/` oder `projects/` direkt in einem Ordner `werkbank-…` oder `workbench-…` (beide Schreibweisen kommen vor, auch gemischt) und steht die Sitzung in oder unter einem Ordner darin, ist dieser **Projektordner** der Ort. Name dort: unter `projekte/` `WEITER-HIER.md`, unter `projects/` `NEXT-STEPS.md`. **Überall sonst** (Starter-Paket, eigene Repos, auch ein Ordner `~/projects/mein-repo`) bleibt es wie bisher: Der Zettel liegt im Startordner, liegt einer da, nimm ihn; muss einer neu entstehen, heißt er `WEITER-HIER.md` — oder `NEXT-STEPS.md`, wenn der Ordner englisch angelegt wurde. Liegen beide da, frag, welcher gilt, und schreib in keinen, bevor die Antwort da ist. **Welcher gilt (im Arbeitsplatz):** Hat das Projekt einen, gilt der des Projekts. Sonst gilt ein vorhandener Werkbank-Zettel (`werkbank-…/WEITER-HIER.md` oder `workbench-…/NEXT-STEPS.md` aus der alten Vorlage) — den liest und pflegst du weiter. Gibt es keinen von beiden, entsteht er **im Projekt**. **Leg nie einen neuen Zettel in der Werkbank an.**
28
28
 
29
- Gibt es keine Status-Datei und die Arbeit wächst über eine Sitzung hinaus: eine vorschlagen,
30
- dann weiter mit Schritt 3.
29
+ **Im Arbeitsplatz** schreibst du in den Zettel des Projekts, an dem heute gearbeitet wurde — fehlt er und liegt auch kein Werkbank-Zettel, leg ihn im Projekt an. **Nach einer reinen Profil-Sitzung** („Wer hier arbeitet“ ausgefüllt, noch kein Projekt) schreibst du **keinen** Zettel, sondern sagst, dass es beim nächsten Mal mit „Erste Sitzung“ in der Werkbank-`CLAUDE.md` weitergeht — dann weiter mit Schritt 3. **Außerhalb eines Arbeitsplatzes** gilt wie bisher: Gibt es keine Status-Datei und die Arbeit wächst über eine Sitzung hinaus, schlag eine vor, dann weiter mit Schritt 3.
31
30
 
32
31
  1. **Nachziehen:** „Wo wir stehen" und „nächster Schritt" auf das setzen, was heute passiert ist.
33
32
  2. **Wegräumen, was nicht mehr gilt.** Ins Archiv wandert: