kimal3 0.2.6 → 0.2.8

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.
@@ -4,6 +4,7 @@ import { FIRMEN_TEXTE, alsMerkzettel, bekannterStandAus, merkzettelNachHoch, } f
4
4
  import { traegtFragenVon } from "../fragen-lesen.js";
5
5
  import { leseEineDatei, leseMerkzettelDatei, schreibeMerkzettelDatei } from "../kontext-dateien.js";
6
6
  import { ERLAUBT, absage, arbeitsplatzOderAbsage, fremdeDateiAbsage } from "./kontext-gemeinsam.js";
7
+ import { hochZeile } from "../kontext-hoch-zeile.js";
7
8
  /*
8
9
  🪦 **Hier stand `feldSchluessel`, gestrichen mit Ruling K-6 (Task 7).**
9
10
 
@@ -185,7 +186,7 @@ export async function kontextHoch(argv) {
185
186
  was daneben schiefging. Was hochgeschickt wurde, haengt nicht daran, ob ein
186
187
  Merkzettel auf der Platte landet.
187
188
  */
188
- console.log(`✓ hochgeschickt: ${ergebnis.uebernommen.join(", ")}`);
189
+ console.log(hochZeile(ergebnis.uebernommen));
189
190
  const merkzettelSchreiben = schreibeMerkzettelDatei(ordner, zuSchreiben);
190
191
  if (!merkzettelSchreiben.ok) {
191
192
  /*
@@ -210,10 +211,17 @@ export async function kontextHoch(argv) {
210
211
  */
211
212
  console.error("Die Firma hat deine Änderungen gespeichert — nur der lokale Merkzettel ließ sich nicht schreiben:");
212
213
  console.error(merkzettelSchreiben.meldung);
213
- return 1;
214
+ // B-d (Dennis, 17.09.): Das ist ein ERFOLG -- die Firma hat gespeichert.
215
+ // Der Rueckgabewert ist fuer Maschinen da, und fuer die ist "gespeichert"
216
+ // die Wahrheit; die Warnung steht auf stderr fuer den Menschen. Bis 0.2.7
217
+ // stand hier `1`, und zwei Anleitungen fuehrten eine Tabelle, nur um diese
218
+ // Zahl zu uebersetzen.
219
+ return 0;
214
220
  }
215
- // US-3: WAS hochgeladen wurde, nicht nur "fertig" -- die Zeile steht seit
216
- // Runde 4 oben, vor der Merkzettel-Pruefung (Begruendung dort).
221
+ // US-3: WAS sich bei der Firma geaendert hat, nicht nur "fertig" -- die
222
+ // Zeile steht seit Runde 4 oben, vor der Merkzettel-Pruefung (Begruendung
223
+ // dort). Seit Task 7 (B-e) kommt der Satz aus `hochZeile` und nennt nur
224
+ // die WIRKLICH geaenderten Dateien -- oder "nichts geaendert".
217
225
  //
218
226
  // 🔑 Der Erklaersatz darunter ist mit Fund F ersatzlos WEGGEFALLEN. Er
219
227
  // benannte eine Ausnahme, die es nicht mehr gibt -- fuenf Texte, zwei
@@ -2,11 +2,16 @@ import { feld, ruf } from "../api.js";
2
2
  import { schlussFehler } from "../fehler.js";
3
3
  import { BASIS_URL } from "../anmeldung.js";
4
4
  import { zerlege } from "../argumente.js";
5
+ import { absage } from "./kontext-gemeinsam.js";
6
+ import { befehlszeileOhneFirma, firmenKoerperAus, ohneFirmaAusArgv } from "../projekt-neu-firma.js";
5
7
  const OPTIONEN = [
6
8
  "titel", "beschreibung", "symbol", "stunden", "ergebnis", "umsatz",
7
- "wirkung", "wirkung-label", "link", "live-seit",
9
+ "wirkung", "wirkung-label", "link", "live-seit", "firma", "firma-id",
8
10
  ];
9
- const HILFE = `
11
+ // exportiert, damit `check:cli` die HILFE als AUSGABE-Text prueft (§24
12
+ // erlaubt das: nicht die Anleitung, die den Text beschreibt, sondern der
13
+ // Text, der wirklich auf dem Bildschirm des Kunden landet).
14
+ export const HILFE = `
10
15
  kimal3 projekt neu --titel "Angebots-Generator" [Optionen]
11
16
 
12
17
  --titel Pflicht. 2–80 Zeichen.
@@ -19,6 +24,9 @@ const HILFE = `
19
24
  --wirkung-label Wozu, bis 40 Zeichen, z. B. "Angebotszeit".
20
25
  --link Vollstaendige Adresse mit http:// oder https://.
21
26
  --live-seit Datum als JJJJ-MM-TT.
27
+ --firma "Name" Wenn du zu mehreren Firmen gehoerst: welche das Projekt bekommt.
28
+ --firma-id "…" Falls zwei deiner Firmen gleich heissen.
29
+ --ohne-firma Das Projekt gehoert keiner Firma (steht nur auf deinem Profil).
22
30
  `;
23
31
  export async function projektNeu(argv) {
24
32
  // ⚠️ VOR dem Zerlegen abfragen, nicht danach.
@@ -30,7 +38,12 @@ export async function projektNeu(argv) {
30
38
  console.log(HILFE);
31
39
  return 0;
32
40
  }
33
- const z = zerlege(argv, OPTIONEN);
41
+ // ⚠️ `zerlege` kennt keine Optionen ohne Wert -- `--ohne-firma` wird
42
+ // deshalb VOR der Zerlegung herausgefiltert, genau wie `--ueberschreiben`
43
+ // in kontext-hoch.ts. Die Rechnung dahinter steht in projekt-neu-firma.ts,
44
+ // damit `check:cli` sie pruefen kann (Final-Review F-1).
45
+ const { rest, ohneFirma } = ohneFirmaAusArgv(argv);
46
+ const z = zerlege(rest, OPTIONEN);
34
47
  if (!z.ok) {
35
48
  console.error(z.meldung);
36
49
  return 1;
@@ -40,6 +53,15 @@ export async function projektNeu(argv) {
40
53
  console.error("Es fehlt --titel.\n" + HILFE);
41
54
  return 1;
42
55
  }
56
+ // 🔴 Final-Review F-3: `--ohne-firma` zusammen mit `--firma`/`--firma-id`
57
+ // liesse die Route den Schalter still gewinnen lassen (`firmenWunschAus` in
58
+ // route.ts prueft `ohne_firma` zuerst). Hier abweisen, bevor der Wunsch
59
+ // ueberhaupt losgeschickt wird.
60
+ const wunsch = firmenKoerperAus(o.firma, o["firma-id"], ohneFirma);
61
+ if (!wunsch.ok) {
62
+ console.error(wunsch.meldung);
63
+ return 1;
64
+ }
43
65
  const antwort = await ruf("/api/cli/projekt", {
44
66
  mitAusweis: true,
45
67
  koerper: {
@@ -48,6 +70,7 @@ export async function projektNeu(argv) {
48
70
  zeitersparnis_std_wo: o.stunden ?? "", ergebnis_monat_eur: o.ergebnis ?? "",
49
71
  umsatz_monat_eur: o.umsatz ?? "", link_url: o.link ?? "",
50
72
  live_seit: o["live-seit"] ?? "",
73
+ ...wunsch.koerper,
51
74
  },
52
75
  });
53
76
  if (antwort.status === 201) {
@@ -73,6 +96,13 @@ export async function projektNeu(argv) {
73
96
  console.error(`${meldung} Leg es hier an: ${BASIS_URL}/profil`);
74
97
  return 1;
75
98
  }
99
+ if (antwort.status === 409) {
100
+ // Firmen-Absage in der Form von arbeitsplatz/kontext: `absage` druckt die
101
+ // Meldung und die Firmen als fertige Befehle. Der Befehl in der Zeile
102
+ // traegt ALLE getippten Optionen mit, nicht nur --titel (Final-Review
103
+ // F-2) -- sonst verliert der Kunde beim Kopieren --stunden, --ergebnis &co.
104
+ return absage(antwort, `projekt neu ${befehlszeileOhneFirma(o)}`);
105
+ }
76
106
  // ⚠️ Hier landet auch die `503` "Konto gerade nicht prüfbar" — und sie MUSS
77
107
  // hier landen, nicht oben bei der 402. Rueckgabewert 3 heisst "nochmal
78
108
  // versuchen kann helfen"; 4 heisst laut SKILL.md ausdruecklich "nicht
@@ -107,9 +107,15 @@ function skillIstVeraltet() {
107
107
  */
108
108
  async function frageServer(schluessel, heute) {
109
109
  try {
110
+ // 🔴 Die Version reist mit, wie bei jedem anderen Befehl (`api.ts`). Bis
111
+ // 0.2.7 fehlte sie hier -- und der Server konnte nicht wissen, dass diese
112
+ // Fassung einen Update-Hinweis ohnehin verwirft; der belegte dann den
113
+ // einen Platz der Antwort, und ein Angebot kam ueber die Wache nie an
114
+ // (live gemessen 17.09.). Faellt die Version aus, geht der Kopf nicht mit.
115
+ const version = eigeneVersion();
110
116
  const antwort = await fetch(`${BASIS_URL}/api/cli/wache`, {
111
117
  method: "POST",
112
- headers: { Authorization: `Bearer ${schluessel}` },
118
+ headers: { Authorization: `Bearer ${schluessel}`, ...(version ? { "X-Kimal3-Version": version } : {}) },
113
119
  signal: AbortSignal.timeout(ZEITGRENZE_MS),
114
120
  });
115
121
  const koerper = await antwort.json().catch(() => null);
package/dist/kimal3.js CHANGED
@@ -32,6 +32,119 @@ import { projektListe } from "./befehle/projekt-liste.js";
32
32
  import { projektNeu } from "./befehle/projekt-neu.js";
33
33
  import { skillDatei, skillInstall } from "./befehle/skill-install.js";
34
34
  import { wache, wacheSchalten } from "./befehle/wache.js";
35
+ /** Eine Meldung pro Programmlauf -- s. "Einmal-Riegel" im Block darunter. */
36
+ let schonGemeldet = false;
37
+ /**
38
+ * Die gekappte Leitung macht keinen Laerm mehr (D-27).
39
+ *
40
+ * 🔴 WARUM DAS HIER STEHT UND NICHT IN `endeMitHinweis`: Ein Schreibfehler auf
41
+ * STDOUT/STDERR kommt NICHT als Wurf zurueck, den ein try/catch faengt --
42
+ * er kommt als `'error'`-EREIGNIS auf dem Kanal selbst. Ohne angemeldeten
43
+ * Empfaenger behandelt Node ein unbehandeltes `'error'`-Ereignis als
44
+ * Programmabbruch, egal WELCHE Zeile gerade schreibt. Und geschrieben wird
45
+ * schon lange vor `endeMitHinweis` -- `haupt()` gibt bei `--version`, `--help`
46
+ * und jedem Befehl selbst aus. Der Empfaenger muss deshalb angemeldet sein,
47
+ * BEVOR die erste Zeile faellt: hier, auf Modulebene, noch vor jedem Aufruf.
48
+ *
49
+ * Gemessen (`npm run check:cli-live`, Fall a): Leitet ein Kunde
50
+ * `kimal3 --version | head -1`, schliesst `head` die Leitung, sobald es seine
51
+ * eine Zeile hat -- der naechste Schreibversuch von kimal3 trifft auf einen
52
+ * toten Empfaenger. Ohne diesen Empfaenger hier wirft das eine rote
53
+ * Stapelspur auf den Bildschirm, fuer einen Befehl, der laengst fertig war.
54
+ *
55
+ * ⚠️ EPIPE SCHLUCKEN, ABER NICHT ALLES (Technische Notiz der Aufgabe): Eine
56
+ * gekappte Leitung ist kein Fehler, den man MELDEN koennte -- der Empfaenger
57
+ * ist weg, niemand liest eine Meldung. Jeder ANDERE Kanalfehler ist einer und
58
+ * darf nicht stillschweigend verschwinden -- sonst truege ein Fehlerfall
59
+ * denselben Namen wie ein Erfolgsfall (vgl. die Regel zu `bestaetigungen-
60
+ * Aktivieren` in der Projekt-CLAUDE.md). Deshalb wird der Rueckgabewert auf 1
61
+ * gesetzt -- und die Meldung geht, wenn ueberhaupt, auf den FEHLERkanal.
62
+ *
63
+ * 🔴 UND DIE MELDUNG GEHT NIE IN DEN DATENKANAL -- die Ungleichheit der zwei
64
+ * Kanaele IST der Bauplan, keine Ausnahme (Befund W-1a der Gesamt-Durchsicht,
65
+ * 2026-09-12). Hier stand vorher "sag es auf dem JEWEILS ANDEREN Kanal", und
66
+ * das war fuer einen der zwei Faelle falsch:
67
+ * · Bricht der DATENkanal (stdout), darf auf dem Fehlerkanal gemeldet
68
+ * werden -- der ist genau dafuer da.
69
+ * · Bricht der FEHLERkanal (stderr), wird NIRGENDS gemeldet. Auf stdout
70
+ * liegt die Maschinenform: das JSON von `firma daten` und die JSON-Huelle
71
+ * von `wache --hook`, die Claude Code parst. Ein deutscher Satz
72
+ * mittendrin macht `JSON.parse` ueber die ganze Ausgabe unmoeglich -- und
73
+ * genau das verbietet US-1 ("nie mittendrin") schon fuer den
74
+ * Update-Hinweis in `endeMitHinweis`. Dann ist der Rueckgabewert die
75
+ * ehrliche Auskunft; der Datenkanal ist keine Protokollzeile wert.
76
+ * Gemessen vor der Reparatur: `quatschbefehl` mit nur-lesbarem stderr legte
77
+ * `kimal3: Ausgabekanal-Fehler -- EBADF ...` auf stdout.
78
+ *
79
+ * 🔴 UND EIN EINMAL-RIEGEL, DAMIT DAS PROGRAMM UEBERHAUPT ENDET (Befund W-1b,
80
+ * dieselbe Durchsicht): Solange beide Empfaenger einander anschrieben, erzeugte
81
+ * jeder Schreibversuch das naechste `'error'`-Ereignis auf dem anderen Kanal --
82
+ * bei ZWEI kaputten Kanaelen (eine volle Platte genuegt:
83
+ * `kimal3 firma daten > a.json 2> b.txt` -> ENOSPC auf beiden) endete das
84
+ * Programm damit NIE. Gemessen: nach 8 Sekunden noch da, musste abgeschossen
85
+ * werden; dieselbe Lage ohne die Empfaenger endete in 36 ms. Und die
86
+ * 2-Sekunden-Notbremse half nicht: Sie wird erst IN `endeMitHinweis` gestellt
87
+ * und vom Ereignis-Pingpong ausgehungert. `schonGemeldet` schneidet das ab --
88
+ * eine Meldung pro Programmlauf, mehr braucht niemand.
89
+ *
90
+ * 🔑 KEIN `process.exit` HIER (Constraint 2): Nur `exitCode` wird gesetzt --
91
+ * das Programm endet von selbst, sobald kein Ereignis mehr nachkommt, und
92
+ * genau dafuer sorgt der Einmal-Riegel.
93
+ *
94
+ * Registriert wird das genau EINMAL pro Kanal, hier -- nicht an jeder
95
+ * einzelnen Schreibstelle. `console.log`/`console.error` UND die zwei
96
+ * direkten `process.stdout.write`/`process.stderr.write`-Aufrufe in
97
+ * `endeMitHinweis` schreiben alle auf DIESELBEN zwei Stream-Objekte
98
+ * (`process.stdout`, `process.stderr` sind pro Prozess EINMALIG vorhanden);
99
+ * ein Empfaenger auf dem Stream selbst deckt deshalb jede Schreibstelle im
100
+ * ganzen Programm ab, auch die in den einzelnen `befehle/*.ts`-Dateien.
101
+ */
102
+ /**
103
+ * @param meldenAuf Wohin die Meldung darf -- oder `null` fuer "nirgends".
104
+ * `null` steht fuer den Fehlerkanal selbst: Ist ER kaputt, bleibt nur der
105
+ * Rueckgabewert, denn der Datenkanal gehoert der Maschinenform.
106
+ */
107
+ function gegenUnbehandelteKanalfehlerAbsichern(kanal, meldenAuf) {
108
+ kanal.on("error", (fehler) => {
109
+ if (fehler.code === "EPIPE")
110
+ return;
111
+ /*
112
+ 🔴 NUR SETZEN, WENN NOCH KEIN FEHLSCHLAG VERMERKT IST (Befund des
113
+ Koordinators, Fix-Runde 1). Ein Kanalfehler kann auftreten, WAEHREND
114
+ gerade eine viel genauere Fehlermeldung geschrieben wird -- der
115
+ `.catch`-Zweig am Dateiende setzt `process.exitCode = 2` ("bitte
116
+ einloggen") bzw. `3` ("nicht erreichbar") direkt NACH dem
117
+ `console.error`-Aufruf, der den Kanalfehler ueberhaupt erst ausloest.
118
+ Ein bedingungsloses `process.exitCode = 1` hier ueberschreibt diese
119
+ genauere Zahl mit der ungenaueren -- gemessen (Koordinator): Ausweis
120
+ fehlt, `projekt liste`, Fehlerkanal nur zum Lesen geoeffnet -> `2` geht
121
+ verloren, es bleibt nur `1`. Das ist derselbe Fehler wie bei
122
+ `bestaetigungenAktivieren` in der Projekt-CLAUDE.md: Ein Fehlerfall darf
123
+ nicht in einen anderen zusammenfallen, denn die Seite (hier: Claude)
124
+ kann nur unterscheiden, was der Rueckgabewert noch unterscheidet.
125
+
126
+ Deshalb: Nur ein UNDEFINIERTER oder ein ERFOLGREICHER (`0`) Stand gilt
127
+ als "noch kein Fehlschlag vermerkt" -- ausdruecklich BEIDE pruefen,
128
+ nicht nur auf `0`, denn `process.exitCode` ist am Programmanfang
129
+ `undefined`, nicht `0`. Jede bereits gesetzte Zahl ungleich `0`
130
+ (`1`, `2`, `3`, `4`) ist die genauere Auskunft und bleibt stehen.
131
+ */
132
+ if (process.exitCode === undefined || process.exitCode === 0)
133
+ process.exitCode = 1;
134
+ if (meldenAuf === null || schonGemeldet)
135
+ return;
136
+ schonGemeldet = true;
137
+ try {
138
+ meldenAuf.write(`kimal3: Ausgabekanal-Fehler -- ${fehler.message}\n`);
139
+ }
140
+ catch {
141
+ // Auch der Fehlerkanal kann kaputt sein -- dann bleibt wenigstens der
142
+ // Rueckgabewert (oben gesetzt) die ehrliche Auskunft.
143
+ }
144
+ });
145
+ }
146
+ gegenUnbehandelteKanalfehlerAbsichern(process.stdout, process.stderr);
147
+ gegenUnbehandelteKanalfehlerAbsichern(process.stderr, null);
35
148
  // 📌 Kein handgeschriebener Block mehr (2026-08-18): Die Liste wohnt in
36
149
  // `befehle-katalog.ts` und wird von dort AUCH im Pro-Bereich der App angezeigt.
37
150
  // Zwei Listen ueber dieselben Befehle driften — die eine bekaeme den naechsten
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Die Erfolgszeile von `kimal3 kontext hoch` -- als reine Rechnung, damit
3
+ * `check:cli` sie prueft. Sie nennt, was sich BEI DER FIRMA geaendert hat,
4
+ * nicht, was gesendet wurde (B-e, 29.08.: fuenf Dateien genannt, zwei
5
+ * geaendert, eine davon ohne Zutun des Menschen).
6
+ */
7
+ export function hochZeile(uebernommen) {
8
+ if (uebernommen.length === 0)
9
+ return "✓ nichts geändert — alles war schon so";
10
+ return `✓ geändert bei der Firma: ${uebernommen.join(", ")}`;
11
+ }
@@ -0,0 +1,74 @@
1
+ import { alsBefehlszeile } from "./argumente.js";
2
+ /**
3
+ * Der Firmenwunsch bei `projekt neu` -- herausgeloest aus `befehle/projekt-neu.ts`,
4
+ * damit `check:cli` ihn als reine Rechnung pruefen kann (Final-Review F-1).
5
+ *
6
+ * 🔴 **Vorher stand hier keine Rechnung, sondern zwei Zeilen QUELLTEXT-Pruefung**
7
+ * in `check-cli.mts` (`quelle.includes(...)`, ein Regex auf `argv.filter(...)`).
8
+ * Die wurden rot, wenn jemand die Lambda-Variable umbenannte, und blieben gruen,
9
+ * wenn die CLI aufhoerte, `ohne_firma` zu senden -- genau das Fehlerbild aus
10
+ * `docs/regeln/messen-und-pruefen.md`, Abschnitt 24: Eine Wache, die Text gegen
11
+ * Text haelt, bewacht die Formulierung, nicht das Verhalten.
12
+ *
13
+ * Zwei Schritte, weil `--ohne-firma` ein Schalter OHNE Wert ist und `zerlege`
14
+ * das nicht kennt (Begruendung in argumente.ts) -- er muss deshalb VOR dem
15
+ * Zerlegen raus. `--firma`/`--firma-id` sind normale Optionen mit Wert und
16
+ * liegen erst NACH dem Zerlegen vor.
17
+ */
18
+ /**
19
+ * Schritt 1, vor `zerlege`: `--ohne-firma` aus der Befehlszeile herausfiltern.
20
+ * Das ist die Rechnung, die vorher nur als Regex auf ihren eigenen Quelltext
21
+ * gewacht wurde.
22
+ */
23
+ export function ohneFirmaAusArgv(argv) {
24
+ return {
25
+ rest: argv.filter((a) => a !== "--ohne-firma"),
26
+ ohneFirma: argv.includes("--ohne-firma"),
27
+ };
28
+ }
29
+ /**
30
+ * Schritt 2, nach `zerlege`: aus Firmenname, Firmen-ID und dem Schalter den
31
+ * Rumpf fuer `/api/cli/projekt` bauen -- oder abweisen (Final-Review F-3).
32
+ *
33
+ * 🔴 **Die Route (`firmenWunschAus` in `src/app/api/cli/projekt/route.ts`)
34
+ * laesst `ohne_firma` GEWINNEN, wenn beides genannt wird** -- `--firma` wird
35
+ * dort still ignoriert, ohne dass die Firma je zu Wort kommt. Statt die Route
36
+ * anzufassen (0.2.7-Vertrag, ein `git push` macht sie fuer ALLE sofort neu,
37
+ * s. CLAUDE.md "Einer CLI-Route eine neue Rueckfrage beibringen"), weist die
38
+ * CLI die widerspruechliche Eingabe VORHER ab -- wie beim fehlenden `--titel`
39
+ * daneben.
40
+ */
41
+ export function firmenKoerperAus(firma, firmaId, ohneFirma) {
42
+ if (ohneFirma && (firma || firmaId)) {
43
+ return {
44
+ ok: false,
45
+ meldung: 'Entweder --ohne-firma ODER --firma/--firma-id -- nenn nur eines von beiden.',
46
+ };
47
+ }
48
+ return { ok: true, koerper: { firma: firma ?? "", firma_id: firmaId ?? "", ohne_firma: ohneFirma } };
49
+ }
50
+ /**
51
+ * Die kopierfertige Zeile fuer die Firmen-Absage (`absage` in
52
+ * `kontext-gemeinsam.ts`) -- ALLE getippten Optionen, nicht nur `--titel`
53
+ * (Final-Review F-2).
54
+ *
55
+ * 🔴 **Vorher fiel jede weitere Option unter den Tisch:** `absage` bekam
56
+ * immer nur `alsBefehlszeile(["--titel", o.titel])`. Wer `projekt neu --titel
57
+ * X --stunden 5 --ergebnis 1200` tippte und die 409-Absage bekam, sah einen
58
+ * Kopierbefehl, der `--stunden` und `--ergebnis` verschluckte -- beim Kopieren
59
+ * landete ein Projekt ohne Zeitersparnis und ohne Ergebnis.
60
+ *
61
+ * `--firma`/`--firma-id` fliegen hier raus, weil `absage` selbst genau diese
62
+ * Option anhaengt (`kimal3 ${befehl} --firma …`); `--ohne-firma` kommt nie in
63
+ * `o` an (s. `ohneFirmaAusArgv` oben) und braucht deshalb keinen eigenen
64
+ * Ausschluss.
65
+ */
66
+ export function befehlszeileOhneFirma(o) {
67
+ const teile = [];
68
+ for (const [name, wert] of Object.entries(o)) {
69
+ if (name === "firma" || name === "firma-id")
70
+ continue;
71
+ teile.push(`--${name}`, wert);
72
+ }
73
+ return alsBefehlszeile(teile);
74
+ }
@@ -0,0 +1 @@
1
+ export const x = "das ist keine Zahl";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kimal3",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal.",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -222,11 +222,10 @@ auf dieser einen Zahl (`cli/src/befehle/kontext-hoch.ts`): unbekannte Option ·
222
222
  Arbeitsplatz gefunden · eine Datei nicht lesbar · eine Datei ohne Fragen · „jemand anderes hat
223
223
  geändert" (Ausweg `--ueberschreiben`) · mehrere Firmen (Ausweg `--firma`) — und die Rolle.
224
224
 
225
- 🔴 **Der teuerste davon steht in `kontext-hoch.ts` (Merkzettel-Zweig) und ist ein ERFOLG:** *„Die Firma hat
226
- deine Änderungen gespeichert — nur der lokale Merkzettel ließ sich nicht schreiben."* Auch das
227
- gibt `1` zurück. Wer die Zahl als „kein Admin" deutet, sagt einem Menschen, der gerade alle
228
- Fragen beantwortet und **erfolgreich hochgeschickt** hat, er solle sich an seinen Admin wenden.
229
- *Ein Erfolg, als Absage vorgelesen — und er tippt danach alles noch einmal.*
225
+ 🔴 **Ein Fall in `kontext-hoch.ts` (Merkzettel-Zweig) sieht wie ein Fehler aus und ist ein ERFOLG:**
226
+ *„Die Firma hat deine Änderungen gespeichert — nur der lokale Merkzettel ließ sich nicht schreiben."*
227
+ Ab 0.2.8 gibt dieser Fall **0** zurück — er ist ein Erfolg. Die Warnung auf `stderr` sagt, dass
228
+ nur der lokale Merkzettel fehlt; beim nächsten `kontext runter` fällt das ohnehin auf.
230
229
 
231
230
  **Also:** Nimm den Satz, den der Befehl geschrieben hat, und sag dem Menschen, was er bedeutet.
232
231
  Geht es wirklich um die Rolle, nenne den Admin — lief vorher `kontext runter`, kennt dessen
@@ -251,7 +250,9 @@ jemand anderem geändert wurde. **Nur auf ausdrücklichen Wunsch einsetzen** —
251
250
  `kimal3 projekt liste` — steht es schon da, **nicht** noch einmal eintragen.
252
251
 
253
252
  ## Eintragen
254
- `kimal3 projekt neu --titel "…" [--stunden 6] [--ergebnis 1200] [--symbol bot]`
253
+ `kimal3 projekt neu --titel "…" [--stunden 6] [--ergebnis 1200] [--symbol bot] [--firma "Name" | --ohne-firma]`
254
+ Wer zu genau einer Firma gehört, braucht `--firma` nicht — das Projekt gehört ihr. Bei zwei
255
+ Firmen nennt die Absage beide als fertige Befehle.
255
256
  Alle Optionen: `kimal3 projekt neu --help`
256
257
 
257
258
  ## 🔑 Zahlen nie erfinden
@@ -78,12 +78,14 @@ Sammelausgang für mindestens sechs verschiedene Lagen, und sie verlangen Gegens
78
78
  | mehrere Firmen / mehrdeutig | Der Ausweg heißt `--firma "…"`. **Du wählst nicht** — leg die Liste vor. |
79
79
  | eine Datei ist nicht lesbar oder trägt keine Fragen mehr | `kimal3 kontext runter` holt sie zurück. |
80
80
  | **„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. |
81
- | **„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". |
82
-
83
- ⚠️ **Warum das hier so ausführlich steht:** Die letzte Zeile ist der teuerste Fall. Wer die `1`
84
- als „kein Admin" deutet, sagt einem Menschen, der gerade alle Fragen beantwortet und
85
- erfolgreich hochgeschickt hat, er solle sich an seinen Admin wenden. *Ein Erfolg, als Absage
86
- vorgelesen.*
81
+ | **„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) |
82
+
83
+ ⚠️ **Warum das hier so ausführlich steht:** Die letzte Zeile war bis 0.2.7 der teuerste Fall. Wer
84
+ die `1` als „kein Admin" deutete, sagte einem Menschen, der gerade alle Fragen beantwortet und
85
+ erfolgreich hochgeschickt hatte, er solle sich an seinen Admin wenden. *Ein Erfolg, als Absage
86
+ vorgelesen.* Ab 0.2.8 trennt schon der Rückgabewert (`0` statt `1`) diesen Fall von einer
87
+ echten Absage — bei einer älteren CLI gilt die alte Lesart weiter, deshalb bleibt die Meldung
88
+ der verlässlichere Weg.
87
89
 
88
90
  **Rückgabewert `4`**: angemeldet, aber kein Pro-Zugang — die Meldung zeigen, den Befehl **nicht**
89
91
  wiederholen. Alles Übrige deutest du nach `SKILL.md`, Abschnitt „Rückgabewerte".
@@ -32,8 +32,10 @@ $ARGUMENTS
32
32
  Pro-Bereich kopiert und unverändert abgeschickt. Dann ist **nichts** angegeben: Frag, um welches
33
33
  Projekt es geht, statt den Platzhalter als Titel zu nehmen.
34
34
 
35
- Bau daraus `kimal3 projekt neu --titel "…"` mit den passenden Optionen
36
- (`kimal3 projekt neu --help` nennt alle).
35
+ Bau daraus `kimal3 projekt neu --titel "…" [--firma "Name" | --ohne-firma]` mit den passenden
36
+ Optionen (`kimal3 projekt neu --help` nennt alle). Wer zu genau einer Firma gehört, braucht
37
+ `--firma` nicht — das Projekt gehört ihr. Bei zwei Firmen nennt die Absage beide als fertige
38
+ Befehle.
37
39
 
38
40
  🔴 **Zeitersparnis, Umsatz und Ergebnis nie schätzen.** Diese Zahlen stehen anschließend
39
41
  **öffentlich** auf einer Seite mit dem Namen des Menschen. Wurde eine Zahl nicht genannt: