kimal3 0.2.9 → 0.2.10

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.
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
- import { ruf } from "./api.js";
3
+ import { feld, ruf } from "./api.js";
4
+ import { angeboteneFirmen, firmaAusOrdner } from "./firmen-liste.js";
4
5
  /*
5
6
  🔴 **Ja, hierher -- und nicht in eine zweite eigene Absage** (Punkt A-3,
6
7
  2026-08-26). `absage` ist die Ausgabe, die `kimal3 kontext runter` und
@@ -75,26 +76,42 @@ export function firmenSuche(wunsch) {
75
76
  * zweite Tür, die man einzeln absichern und einzeln nachmessen müsste — für
76
77
  * exakt denselben Inhalt. Der Ausschnitt entsteht hier auf dem Rechner.
77
78
  *
78
- * 🔴 **Beide Parameter sind PFLICHT** (Punkt A-3, 2026-08-26). `wunsch` könnte
79
+ * 🔴 **Alle drei Parameter sind PFLICHT** (Punkt A-3, 2026-08-26). `wunsch` könnte
79
80
  * bequem entfallen, wenn niemand eine Firma nennt -- und genau so bleibt eine
80
81
  * gebaute Rechnung an der Aufrufstelle tot, ohne dass ein Compiler oder ein
81
82
  * Wächter etwas merkt (dieselbe Falle wie bei `lageFuerDatei`, Punkt A-1).
82
83
  * `befehl` nennt den Befehl, den die 409-Absage zum Kopieren anbietet -- stünde
83
84
  * dort fest `kontext runter`, bekäme ein Mensch bei `werkbank neu` einen Rat,
84
- * der seine Werkbank nicht anlegt.
85
+ * der seine Werkbank nicht anlegt. `ordner` ist `null` bei `arbeitsplatz neu`
86
+ * (dort entsteht der Ordner erst) und die Firma des Ordners bei `werkbank neu`.
87
+ *
88
+ * 🔑 **Die Firma des Ordners wird nur benutzt, wenn der Server FRAGT** (A2-6,
89
+ * Entscheidung Dennis 25.09.): Gehört der Mensch zu mehreren Firmen
90
+ * (`mehrere-firmen`) und hat er selbst keine genannt, versucht der Befehl es
91
+ * EIN zweites Mal mit der Firma aus dem Ordner (`firmaAusOrdner`). Die Lagen
92
+ * und warum genau so: docs/superpowers/specs/2026-09-25-werkbank-nimmt-firma-lagen.md.
93
+ * - Hat er selbst `--firma`/`--firma-id` getippt, gilt seine Angabe -- auch
94
+ * eine falsche wird nie still ersetzt (Lage L7b).
95
+ * - Scheitert die zweite Anfrage mit `409`, sieht er die ERSTE Rückfrage, nicht
96
+ * eine über einen Namen, den er nie getippt hat (L11).
85
97
  */
86
- export async function vorlageHolen(befehl, wunsch) {
87
- const antwort = await ruf(`/api/cli/arbeitsplatz${firmenSuche(wunsch)}`, {
88
- methode: "GET",
89
- mitAusweis: true,
90
- });
91
- if (antwort.status === 200) {
92
- const daten = alsVorlageAntwort(antwort.koerper);
93
- if (daten === null) {
94
- console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
95
- return { ok: false, wert: 3 };
96
- }
97
- return { ok: true, daten };
98
+ export async function vorlageHolen(befehl, wunsch, ordner) {
99
+ const antwort = await holeVorlage(wunsch);
100
+ if (antwort.status === 200)
101
+ return lesbar(antwort.koerper, false);
102
+ const selbstGenannt = wunsch.firma !== undefined || wunsch.firmaId !== undefined;
103
+ const wahl = antwort.status === 409 && ordner !== null && !selbstGenannt &&
104
+ feld(antwort.koerper, "grund") === "mehrere-firmen"
105
+ ? firmaAusOrdner(ordner, angeboteneFirmen(antwort.koerper))
106
+ : null;
107
+ if (wahl !== null) {
108
+ const zweite = await holeVorlage(wahl);
109
+ if (zweite.status === 200)
110
+ return lesbar(zweite.koerper, true);
111
+ // Netz, Ausweis, Pro: deren eigene Meldung. Nur eine zweite Rückfrage wird
112
+ // durch die erste ersetzt (L11).
113
+ if (zweite.status !== 409)
114
+ return { ok: false, wert: absage(zweite, befehl) };
98
115
  }
99
116
  /*
100
117
  🔴 **Hier standen dieselben vier Zweige noch einmal abgetippt** (402 vor
@@ -109,6 +126,17 @@ export async function vorlageHolen(befehl, wunsch) {
109
126
  */
110
127
  return { ok: false, wert: absage(antwort, befehl) };
111
128
  }
129
+ function holeVorlage(wunsch) {
130
+ return ruf(`/api/cli/arbeitsplatz${firmenSuche(wunsch)}`, { methode: "GET", mitAusweis: true });
131
+ }
132
+ function lesbar(koerper, uebernommen) {
133
+ const daten = alsVorlageAntwort(koerper);
134
+ if (daten === null) {
135
+ console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
136
+ return { ok: false, wert: 3 };
137
+ }
138
+ return { ok: true, daten, uebernommen };
139
+ }
112
140
  /** Prueft die Antwort-Form, ohne `any` -- eine falsche Form ist unlesbar, keine Ausnahme. */
113
141
  function alsVorlageAntwort(koerper) {
114
142
  if (!koerper || typeof koerper !== "object")
@@ -221,7 +221,8 @@ export async function arbeitsplatzNeu(argv) {
221
221
  const befehlsname = nameArg === undefined
222
222
  ? "arbeitsplatz neu"
223
223
  : `arbeitsplatz neu ${alsWort(nameArg)}`;
224
- const holen = await vorlageHolen(befehlsname, wunsch);
224
+ // `null`: Der Ordner entsteht hier erst -- es gibt keine Firma, die man aus ihm lesen koennte.
225
+ const holen = await vorlageHolen(befehlsname, wunsch, null);
225
226
  if (!holen.ok)
226
227
  return holen.wert;
227
228
  const daten = holen.daten;
@@ -141,8 +141,16 @@ export function kontextFragen(argv) {
141
141
  liest ein Mensch „fertig" und die App widerspricht ihm morgen.
142
142
  */
143
143
  console.log(" Gezählt ist, was in kontext/ steht. Nennt die App unter Firma → Kontext");
144
- console.log(" eine höhere Zahl, fehlt in einer Datei eine Frage — `kimal3 kontext runter`");
145
- console.log(" holt sie zurück.");
144
+ console.log(" eine höhere Zahl, fehlt in einer Datei eine Frage oder die Zeile <!-- k3:… --> darüber.");
145
+ // 🔴 Hier stand „`kimal3 kontext runter` holt sie zurück" -- stimmt nur fuer
146
+ // eine FEHLENDE Datei. Eine selbst bearbeitete fasst `runter` nicht an
147
+ // (`lageFuerDatei` -> `lokal-geaendert`), Schlusspruefung 25.09.
148
+ // Und er NENNT die Datei nicht -- dieser Befehl weiss nicht, welche es ist
149
+ // (er zaehlt nur, was da ist). Deshalb der Weg, sie zu finden (Review 25.09.).
150
+ console.log(" Welche Datei: die, deren Überschriften nicht zu denen in der App passen.");
151
+ console.log(" Benenn sie um (etwa firma.md → firma-alt.md), dann holt `kimal3 kontext runter`");
152
+ console.log(" die Fassung der Firma frisch. Antworten, die du noch nicht hochgeschickt hast,");
153
+ console.log(" stehen danach nur in der umbenannten Datei — kopier sie zurück.");
146
154
  return 0;
147
155
  }
148
156
  console.log(`${offen} von ${gesamt} Fragen über deine Firma sind noch offen.\n`);
@@ -3,6 +3,7 @@ import { alsWort } from "../argumente.js";
3
3
  import { schlussFehler } from "../fehler.js";
4
4
  import { BASIS_URL } from "../anmeldung.js";
5
5
  import { findeArbeitsplatz } from "../kontext-dateien.js";
6
+ import { angeboteneFirmen } from "../firmen-liste.js";
6
7
  /**
7
8
  * Was `kontext runter` (`kontext-runter.ts`) und `kontext hoch`
8
9
  * (`kontext-hoch.ts`) TEILEN -- die `--firma`/`--firma-id`-Optionen und die
@@ -127,9 +128,7 @@ export function absage(antwort, befehl) {
127
128
  return 1;
128
129
  }
129
130
  const koerper = antwort.koerper;
130
- const namen = Array.isArray(koerper?.firmen)
131
- ? koerper.firmen.filter((x) => typeof x === "string")
132
- : [];
131
+ const namen = angeboteneFirmen(koerper);
133
132
  console.error(meldung);
134
133
  const auswahl = Array.isArray(koerper.auswahl)
135
134
  ? koerper.auswahl.filter((x) => !!x &&
@@ -239,8 +239,8 @@ export async function kontextRunter(argv) {
239
239
  // und dann sucht er in der App eine Aenderung, die es dort nicht gibt.
240
240
  if (vorlageErneuert.length > 0) {
241
241
  console.log(` Davon aus unserer verbesserten Vorlage: ${vorlageErneuert.join(", ")}`);
242
- console.log(" Diese Dateien standen noch genau so da, wie wir sie geliefert hatten — es ist nichts\n" +
243
- " von dir verloren gegangen. Hättest du darin etwas geändert, hätte ich vorher gesichert.");
242
+ console.log(" Diese Dateien waren seit dem letzten Abgleich mit der Firma unverändert — es ist nichts\n" +
243
+ " von dir verloren gegangen. Hättest du seitdem etwas geändert, hätte ich vorher gesichert.");
244
244
  }
245
245
  // §8: Ein Fehlerfall darf nie denselben Namen tragen wie ein Erfolgsfall --
246
246
  // "nichts geschrieben" heisst hier DREI verschiedene Dinge, und die Ausgabe
@@ -66,7 +66,9 @@ const HILFE = `
66
66
  wird "werkbank-sabine". Ohne Angabe steht dort der Name,
67
67
  den der Server aus deiner Anmelde-Adresse gebildet hat.
68
68
 
69
- --firma "Name" Noetig, wenn du zu mehreren Firmen gehoerst.
69
+ --firma "Name" Nur noetig, wenn du zu mehreren Firmen gehoerst UND dieser
70
+ Ordner seine Firma nicht eindeutig nennt -- sonst nehme
71
+ ich die Firma des Ordners und sage dir das.
70
72
  Die Absage nennt dir die Namen als fertige Befehle.
71
73
 
72
74
  --firma-id "…" Fuer den Fall, dass zwei deiner Firmen GLEICH heissen.
@@ -201,7 +203,18 @@ export async function werkbankNeu(argv) {
201
203
  const befehlsname = nameArg === undefined
202
204
  ? "werkbank neu"
203
205
  : `werkbank neu ${alsWort(nameArg)}`;
204
- const holen = await vorlageHolen(befehlsname, wunsch);
206
+ /*
207
+ 🔑 **Die Firma dieses Ordners** -- Kennung aus dem Merkzettel, Name aus der
208
+ Firmen-`CLAUDE.md`. `vorlageHolen` nimmt sie nur, wenn der Server wegen
209
+ mehrerer Firmen fragt (A2-6, 25.09.), und die Wache weiter unten
210
+ (`firmenAbgleich`) vergleicht mit genau diesen beiden Werten.
211
+ */
212
+ const hierGelesen = leseEineDatei(process.cwd(), FIRMEN_CLAUDE_MD);
213
+ const ordnerFirma = {
214
+ id: leseMerkzettelDatei(process.cwd()).firma_id,
215
+ name: firmennameAusClaudeMd(hierGelesen.ok ? hierGelesen.inhalt : null),
216
+ };
217
+ const holen = await vorlageHolen(befehlsname, wunsch, ordnerFirma);
205
218
  if (!holen.ok)
206
219
  return holen.wert;
207
220
  const daten = holen.daten;
@@ -365,11 +378,7 @@ export async function werkbankNeu(argv) {
365
378
  („ich schicke nichts Halbes hoch“).
366
379
  */
367
380
  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
- }, {
381
+ const abgleich = firmenAbgleich(ordnerFirma, {
373
382
  id: leseMerkzettel(inhaltAus(MERKZETTEL_PFAD)).firma_id,
374
383
  name: firmennameAusClaudeMd(inhaltAus(FIRMEN_CLAUDE_MD)),
375
384
  });
@@ -461,6 +470,16 @@ export async function werkbankNeu(argv) {
461
470
  console.log("");
462
471
  console.log(" Die Firmenebene ist unberuehrt geblieben.");
463
472
  console.log("");
473
+ /*
474
+ Erst NACH dem Anlegen (Lage L2): Vorher koennte noch eine Absage folgen, und
475
+ dann stuende da „uebernommen" fuer etwas, das nie entstanden ist.
476
+ */
477
+ if (holen.uebernommen) {
478
+ const gewaehlt = firmennameAusClaudeMd(inhaltAus(FIRMEN_CLAUDE_MD));
479
+ console.log(` Die Firma kam aus diesem Ordner${gewaehlt ? `: „${gewaehlt}"` : ""}.`);
480
+ console.log(" Sollte es eine andere sein: Ordner wieder loeschen und mit --firma \"Name\" neu anlegen.");
481
+ console.log("");
482
+ }
464
483
  console.log(" Weiter geht's:");
465
484
  console.log(` cd ${alsWort(ordnerName)}`);
466
485
  console.log(" claude");
@@ -70,3 +70,37 @@ export function firmenZeilen(namen) {
70
70
  export function firmenIdZeilen(auswahl) {
71
71
  return auswahl.map((f, i) => ` kimal3 firma daten --firma-id "${f.id}" # ${f.name} (${i + 1}.)`);
72
72
  }
73
+ /**
74
+ * Die Namen der eigenen Firmen aus einer `409`-Antwort -- was nicht als Text
75
+ * ankommt, faellt weg. EINE Lesart fuer `absage` (kontext-gemeinsam.ts) und
76
+ * `vorlageHolen` -- zwei Kopien liefen irgendwann auseinander (Review 25.09.).
77
+ */
78
+ export function angeboteneFirmen(koerper) {
79
+ const firmen = koerper?.firmen;
80
+ return Array.isArray(firmen) ? firmen.filter((x) => typeof x === "string") : [];
81
+ }
82
+ /**
83
+ * Welche Firma `werkbank neu` aus dem Ordner uebernimmt, wenn der Server
84
+ * fragt, weil der Mensch zu mehreren Firmen gehoert (Entscheidung Dennis,
85
+ * 25.09. -- A2-6; Lagen: docs/superpowers/specs/2026-09-25-werkbank-nimmt-firma-lagen.md).
86
+ *
87
+ * 🔑 **Die Kennung aus dem Merkzettel zuerst** -- sie ist eindeutig, auch bei
88
+ * zwei gleichnamigen Firmen, und dieselbe, nach der die Firmen-Wache danach
89
+ * vergleicht (`firmenAbgleich`). Ob der Mensch dort Mitglied ist, prueft der
90
+ * Server selbst; sagt er nein, zeigt der Aufrufer die URSPRUENGLICHE Rueckfrage.
91
+ *
92
+ * Ohne Merkzettel (geklonter Ordner, er steht in der `.gitignore`) zaehlt der
93
+ * Name aus der Firmen-`CLAUDE.md` -- aber nur, wenn er GENAU EINMAL unter den
94
+ * angebotenen steht. Kein Ausgleich von Gross/klein, keine aehnlichen Namen:
95
+ * *Wer raten muss, soll fragen.*
96
+ *
97
+ * `null` heisst: nichts Eindeutiges -- dann bleibt es bei der Rueckfrage.
98
+ */
99
+ export function firmaAusOrdner(ordner, angeboten) {
100
+ if (ordner.id)
101
+ return { firmaId: ordner.id };
102
+ const name = ordner.name;
103
+ if (name && angeboten.filter((n) => n === name).length === 1)
104
+ return { firma: name };
105
+ return null;
106
+ }
@@ -466,6 +466,10 @@ export function baueMerkzettel(dateien, staende, firmenId, vorlagenPruefsummen)
466
466
  * schreibt keine einzige Datei. Fuer die fuenf gesendeten Texte geht das
467
467
  * trotzdem auf: Was der Server zurueckgibt, entsteht aus dem, was die CLI ihm
468
468
  * gerade geschickt hat, und dasselbe steht auf der Platte.
469
+ * ⚠️ Seit dem 25.09. (Luecke L12) gilt das nur noch, weil der SERVER es
470
+ * sicherstellt: Er liefert einen um neue Fragen ergaenzten Text aus, traegt im
471
+ * Merkzettel aber die Pruefsumme des GESENDETEN ein -- `merkzettelFuerHochAntwort`
472
+ * in `src/lib/cli/merkzettel-hoch.ts`. Wer dort etwas aendert, bricht diese Zeile.
469
473
  *
470
474
  * ⚠️ **Fuer die Firmen-`CLAUDE.md` geht es NICHT auf, seit Ruling K-6.** Sie
471
475
  * wird nicht mehr gesendet, aber der Server BAUT sie neu (er setzt die frischen
@@ -29,6 +29,9 @@ export function leseWacheDatei() {
29
29
  raus.zuletzt = inhalt.zuletzt;
30
30
  if (typeof inhalt.sitzung === "string")
31
31
  raus.sitzung = inhalt.sitzung;
32
+ if (Array.isArray(inhalt.sitzungen)) {
33
+ raus.sitzungen = inhalt.sitzungen.filter((s) => typeof s === "string");
34
+ }
32
35
  return raus;
33
36
  }
34
37
  catch {
@@ -107,12 +110,44 @@ export function sitzungJetzt() {
107
110
  const roh = process.env.CLAUDE_CODE_SESSION_ID;
108
111
  return typeof roh === "string" && roh.trim() !== "" ? roh : null;
109
112
  }
110
- /** Merken, fuer welche Claude-Sitzung zuletzt nachgesehen wurde. Wirft nie. */
113
+ /** Merken, dass fuer diese Claude-Sitzung nachgesehen wurde. Wirft nie. */
111
114
  export function merkeSitzung(sitzung) {
112
115
  if (sitzung === null)
113
116
  return;
114
- const bisher = leseWacheDatei() ?? {};
115
- schreibeWacheDatei({ ...bisher, sitzung });
117
+ schreibeWacheDatei(mitSitzung(leseWacheDatei() ?? {}, sitzung));
118
+ }
119
+ /** Wie viele Sitzungen der Merkzettel sich merkt -- genug fuer mehrere Fenster ueber Tage. */
120
+ const MERKE_SITZUNGEN = 20;
121
+ /**
122
+ * Der Merkzettel, nachdem fuer `sitzung` nachgesehen wurde.
123
+ *
124
+ * 🔴 **EINE LISTE, NICHT EINE NUMMER** (gemessen 19.09., behoben 25.09.). Bis
125
+ * dahin stand hier nur `sitzung` -- das zweite Claude-Fenster ueberschrieb die
126
+ * Nummer des ersten, und von da an haengte jeder `kimal3`-Befehl im ersten
127
+ * Fenster die Zeile „die taegliche Pruefung hat nicht nachgesehen" an, obwohl
128
+ * sie lief. Ein Fehlalarm genau dort, wo die Meldung Vertrauen braucht.
129
+ *
130
+ * ⚠️ `sitzung` (die juengste) wird WEITER geschrieben: Eine aeltere Fassung auf
131
+ * demselben Rechner liest nur dieses Feld. Neueste zuerst, doppelte raus,
132
+ * hoechstens {@link MERKE_SITZUNGEN} -- damit die Datei nicht waechst.
133
+ * REIN, damit `check:wache` sie ohne Dateisystem pruefen kann.
134
+ *
135
+ * 📌 **Zwei bekannte Grenzen** (Review 25.09., bewusst nicht gebaut):
136
+ * - Starten zwei Fenster in DERSELBEN Millisekunde, lesen beide die alte Liste
137
+ * und der Letzte gewinnt -- Lesen und Schreiben liegen in `merkeSitzung`
138
+ * direkt nacheinander, das Fenster ist winzig. Eine Dateisperre waere ein
139
+ * zweites Risiko fuer einen Claude-Start, der nie haengen darf.
140
+ * - Laeuft auf demselben Rechner zusaetzlich eine Fassung vor 0.2.10, schreibt
141
+ * sie die Datei ohne `sitzungen` zurueck. Dann gilt bis zum naechsten Lauf
142
+ * wieder das Einzelfeld -- der alte Zustand, kein neuer Fehler.
143
+ */
144
+ export function mitSitzung(bisher, sitzung) {
145
+ const aeltere = (bisher.sitzungen ?? []).filter((s) => s !== sitzung);
146
+ return { ...bisher, sitzung, sitzungen: [sitzung, ...aeltere].slice(0, MERKE_SITZUNGEN) };
147
+ }
148
+ /** Steht diese Sitzung im Merkzettel -- im alten Einzelfeld oder in der Liste? */
149
+ function gesehen(inhalt, jetzt) {
150
+ return inhalt.sitzung === jetzt || (inhalt.sitzungen ?? []).includes(jetzt);
116
151
  }
117
152
  /**
118
153
  * Hat die Wache fuer GENAU DIESE Claude-Sitzung schon nachgesehen?
@@ -137,7 +172,7 @@ export function merkeSitzung(sitzung) {
137
172
  * darf ihn nicht zum Schweigen bringen.
138
173
  */
139
174
  export function schonFuerDieseSitzung(inhalt, jetzt) {
140
- return jetzt !== null && inhalt !== null && inhalt.sitzung === jetzt;
175
+ return jetzt !== null && inhalt !== null && gesehen(inhalt, jetzt);
141
176
  }
142
177
  /**
143
178
  * Ist die Wache fuer diese Claude-Sitzung ausgefallen?
@@ -170,5 +205,5 @@ inhalt, jetzt) {
170
205
  return false;
171
206
  if (inhalt !== null && inhalt.aus === true)
172
207
  return false;
173
- return inhalt === null || inhalt.sitzung !== jetzt;
208
+ return inhalt === null || !gesehen(inhalt, jetzt);
174
209
  }
package/package.json CHANGED
@@ -1,27 +1,27 @@
1
- {
2
- "name": "kimal3",
3
- "version": "0.2.9",
4
- "description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im 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": "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.2.10",
4
+ "description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im 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": "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
+ }
package/skill/SKILL.md CHANGED
@@ -110,6 +110,12 @@ Sie legen dann nichts an, sondern nennen die Firmen als fertige Zeilen zum Kopie
110
110
  kein Fehler, sondern die Rückfrage: Der Ordner trägt den Namen der Firma, und raten darf der
111
111
  Befehl ihn nicht.
112
112
 
113
+ 🔑 **Ausnahme ab 0.2.10: `werkbank neu` im Firmen-Ordner fragt nicht, wenn der Ordner seine Firma
114
+ eindeutig nennt** (Merkzettel oder Firmenzeile der `CLAUDE.md`). Dann steht nach dem Anlegen
115
+ „Die Firma kam aus diesem Ordner: …“ — das ist die Wahl des Programms aus dem Ordner, nicht
116
+ deine. Sag dem Menschen die Firma; will er eine andere, löscht er den neuen Ordner und legt ihn
117
+ mit `--firma` neu an.
118
+
113
119
  ```
114
120
  kimal3 arbeitsplatz neu sabine --firma "Nordwind Handel GmbH"
115
121
  kimal3 werkbank neu sabine --firma "Nordwind Handel GmbH"
@@ -223,7 +229,9 @@ selbst schreibt, ist in Ordnung; ein Satz, den du dir ausdenkst, nicht.
223
229
  ⚠️ **Sieht eine der fünf Dateien nicht mehr aus wie ihre Frageliste — etwa weil etwas Fremdes
224
230
  darüberkopiert wurde —, bricht `kontext hoch` ab und schickt gar nichts, auch die vier anderen
225
231
  nicht** (Rückgabewert `1`). Das ist **kein** „noch einmal versuchen": Sag dem Menschen, welche
226
- Datei gemeint ist, und hol sie mit `kimal3 kontext runter` zurück.
232
+ Datei gemeint ist. Zurück holt sie `kimal3 kontext runter` nur, wenn sie **fehlt** — eine
233
+ bearbeitete Datei fasst es nicht an. Also: Datei umbenennen (etwa `firma-alt.md`), dann
234
+ `kimal3 kontext runter`; die Fassung der Firma kommt frisch, seine bleibt daneben liegen.
227
235
 
228
236
  🔴 **Bei `kontext hoch` sagt die Zahl `1` allein GAR NICHTS — die Meldung sagt es. LIES SIE.**
229
237
 
@@ -78,7 +78,7 @@ Sammelausgang für mindestens sechs verschiedene Lagen, und sie verlangen Gegens
78
78
  | „Hier steht kein KI-Arbeitsplatz" | **Nicht** `arbeitsplatz neu` raten! Siehe Abschnitt 2 — der Ordner existiert meist, nur steht der Mensch woanders. |
79
79
  | „Der Kontext wurde inzwischen … geändert" | Der Ausweg steht in derselben Meldung: `--ueberschreiben`. Schlag ihn nur vor, wenn der Mensch die fremde Änderung wirklich verwerfen will — sie ist die Arbeit eines Kollegen. |
80
80
  | mehrere Firmen / mehrdeutig | Der Ausweg heißt `--firma "…"`. **Du wählst nicht** — leg die Liste vor. |
81
- | eine Datei ist nicht lesbar oder trägt keine Fragen mehr | `kimal3 kontext runter` holt sie zurück. |
81
+ | eine Datei ist nicht lesbar oder trägt keine Fragen mehr | Die Datei umbenennen (etwa `firma-alt.md`), dann holt `kimal3 kontext runter` die Fassung der Firma frisch. Eine selbst bearbeitete Datei fasst `runter` sonst **nicht** an. |
82
82
  | **„Du gehörst keiner Firma an."** | 🔴 **Schick ihn NICHT zu einem Admin — es gibt keinen.** Das trifft ausgerechnet den frischen Pro-Käufer: Er bekommt seinen Arbeitsplatz mit allen Fragen offen, beantwortet sie brav, und hier ist niemand, an den sie gehen könnten. Sag ihm, dass seine Antworten **in seinen Dateien stehen und dort bleiben**, und dass sie hochgehen, sobald es eine Firma gibt. Nichts geht verloren. |
83
83
  | **„Die Firma hat deine Änderungen gespeichert — nur der lokale Merkzettel …"** | 🔴 **Das ist ein ERFOLG.** Alles ist oben angekommen; nur eine Datei auf der Platte ließ sich nicht schreiben. Sag genau das — **niemals** „das hat nicht geklappt". (ab 0.2.8 Rückgabe 0) |
84
84