kimal3 0.2.0 → 0.2.1
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.
- package/dist/api.js +43 -8
- package/dist/arbeitsplatz-dateien.js +84 -22
- package/dist/argumente.js +150 -0
- package/dist/befehle/arbeitsplatz.js +139 -17
- package/dist/befehle/kontext-fragen.js +17 -0
- package/dist/befehle/kontext-gemeinsam.js +17 -3
- package/dist/befehle/kontext-hoch.js +36 -3
- package/dist/befehle/kontext-runter.js +58 -7
- package/dist/befehle/skill-install.js +51 -17
- package/dist/befehle/werkbank.js +128 -17
- package/dist/befehle-katalog.js +14 -7
- package/dist/firmen-liste.js +4 -2
- package/dist/kimal3.js +20 -2
- package/dist/kontext-stand.js +304 -50
- package/dist/nachricht.js +87 -3
- package/package.json +1 -1
- package/skill/SKILL.md +44 -8
- package/skill/commands/k3.md +59 -18
- package/skill/commands/k3kontext.md +89 -0
- /package/skill/commands/{k3firma.md → k3analyse.md} +0 -0
package/dist/api.js
CHANGED
|
@@ -7,9 +7,15 @@ export class NichtAngemeldet extends Error {
|
|
|
7
7
|
constructor() { super("nicht angemeldet"); }
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
* Der
|
|
11
|
-
*
|
|
12
|
-
* (Task 10, Step 1).
|
|
10
|
+
* Der EINE Weg, auf dem diese Datei den lokalen Merkzettel liest -- Firmen-ID
|
|
11
|
+
* und bekannter Stand, oder `null`, wenn sich beides nicht sicher bilden
|
|
12
|
+
* laesst (Task 10, Step 1).
|
|
13
|
+
*
|
|
14
|
+
* 🔑 Zwei Verbraucher, EINE Leseart (A-4): `kontextStandKopf()` baut daraus
|
|
15
|
+
* den Kopf `X-Kimal3-Kontext-Stand` VOR dem Netzaufruf,
|
|
16
|
+
* `lokalerKontextStandJetzt()` liefert denselben Stand am ENDE des Befehls an
|
|
17
|
+
* den Update-Hinweis. Eine zweite, eigene Leseart waere eine zweite Wahrheit
|
|
18
|
+
* ueber dieselbe Datei.
|
|
13
19
|
*
|
|
14
20
|
* ⚠️ WIRFT NIE -- dieselbe Regel wie beim Update-Kanal insgesamt: Ein
|
|
15
21
|
* Nebenschauplatz darf keinen Befehl umwerfen. `findeArbeitsplatz` und
|
|
@@ -40,7 +46,7 @@ export class NichtAngemeldet extends Error {
|
|
|
40
46
|
* Ausgang?* Vier, und JEDE fuehrt zum selben, harmlosen Ergebnis: kein Kopf,
|
|
41
47
|
* kein Hinweis, der Befehl laeuft unveraendert weiter.
|
|
42
48
|
*/
|
|
43
|
-
function
|
|
49
|
+
function lokaleKontextLage() {
|
|
44
50
|
try {
|
|
45
51
|
const ordner = findeArbeitsplatz(process.cwd());
|
|
46
52
|
if (ordner === null)
|
|
@@ -48,15 +54,44 @@ function kontextStandKopf() {
|
|
|
48
54
|
const merkzettel = leseMerkzettelDatei(ordner);
|
|
49
55
|
if (merkzettel.firma_id === null)
|
|
50
56
|
return null;
|
|
51
|
-
|
|
52
|
-
if (!istKopfwertSicher(wert))
|
|
53
|
-
return null;
|
|
54
|
-
return wert;
|
|
57
|
+
return { firmaId: merkzettel.firma_id, stand: bekannterStandAus(merkzettel) };
|
|
55
58
|
}
|
|
56
59
|
catch {
|
|
57
60
|
return null;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Welchen Firmenstand traegt der Merkzettel auf dieser Platte GERADE JETZT?
|
|
65
|
+
*
|
|
66
|
+
* 🔴 WOZU DAS GUT IST (offener Punkt A-4): Der Wert, den `kontextStandKopf()`
|
|
67
|
+
* unten in den Kopf schreibt, wird VOR dem `fetch` gelesen -- er ist der
|
|
68
|
+
* Stand, den die CLI hatte, als der Befehl begann. Genau daran hing der
|
|
69
|
+
* Fehler: Nach `kimal3 kontext runter` riet der Hinweis am Ende zu genau dem
|
|
70
|
+
* Befehl, der gerade gelaufen war. Diese Funktion ist deshalb kein zweiter
|
|
71
|
+
* Weg zum selben Wert, sondern derselbe Weg zu einem SPAETEREN Zeitpunkt --
|
|
72
|
+
* ausgelesen wird sie in `hinweisText()` (`kimal3.ts`), also nachdem der
|
|
73
|
+
* Befehl den Merkzettel neu geschrieben hat.
|
|
74
|
+
*
|
|
75
|
+
* ⚠️ SIE DARF NICHTS ZWISCHENSPEICHERN. Ein gemerkter Wert waere wieder der
|
|
76
|
+
* Stand von vorhin, und der ganze Fix waere umsonst -- deshalb liest sie bei
|
|
77
|
+
* jedem Aufruf erneut von der Platte. `check:nachrichten` misst genau das.
|
|
78
|
+
*
|
|
79
|
+
* `null` heisst "wir wissen den Stand nicht" (kein Arbeitsplatz, keine
|
|
80
|
+
* Firmen-ID im Merkzettel, Datei unlesbar) -- und "wir wissen es nicht" darf
|
|
81
|
+
* nie einen Hinweis unterdruecken (s. `hinweisErledigt` in `nachricht.ts`).
|
|
82
|
+
*/
|
|
83
|
+
export function lokalerKontextStandJetzt() {
|
|
84
|
+
return lokaleKontextLage()?.stand ?? null;
|
|
85
|
+
}
|
|
86
|
+
function kontextStandKopf() {
|
|
87
|
+
const lage = lokaleKontextLage();
|
|
88
|
+
if (lage === null)
|
|
89
|
+
return null;
|
|
90
|
+
const wert = `${lage.firmaId}:${lage.stand}`;
|
|
91
|
+
if (!istKopfwertSicher(wert))
|
|
92
|
+
return null;
|
|
93
|
+
return wert;
|
|
94
|
+
}
|
|
60
95
|
/**
|
|
61
96
|
* Ein Aufruf gegen die App. `mitAusweis` haengt den Schluessel an.
|
|
62
97
|
*
|
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { ruf } from "./api.js";
|
|
4
|
+
/*
|
|
5
|
+
🔴 **Ja, hierher -- und nicht in eine zweite eigene Absage** (Punkt A-3,
|
|
6
|
+
2026-08-26). `absage` ist die Ausgabe, die `kimal3 kontext runter` und
|
|
7
|
+
`kimal3 kontext hoch` schon benutzen: Sie trennt 402 vor 401, schickt den
|
|
8
|
+
Rest an `schlussFehler` und nennt bei `409` die eigenen Firmen als **fertige
|
|
9
|
+
Befehle zum Kopieren**. Genau das braucht `arbeitsplatz neu` jetzt auch.
|
|
10
|
+
|
|
11
|
+
Der bequeme Weg waere gewesen, die vier Zweige hier abzutippen. Dann gaebe es
|
|
12
|
+
zwei Absage-Sprachen fuer dieselbe Frage („welche Firma meinst du?") -- und
|
|
13
|
+
der Kunde muesste zweimal dasselbe lernen. Der Preis ist ein Import aus
|
|
14
|
+
`befehle/` in ein Modul darueber.
|
|
15
|
+
|
|
16
|
+
🔴 **DIESER IMPORT SCHLIESST EINEN KREIS -- und der Kommentar behauptete hier
|
|
17
|
+
einen Nachmittag lang das Gegenteil** („er ist kreisfrei"). Nachgemessen
|
|
18
|
+
(Review-Befund 4, 2026-08-26): `arbeitsplatz-dateien` -> `befehle/
|
|
19
|
+
kontext-gemeinsam` -> `kontext-dateien` (holt `findeArbeitsplatz`) ->
|
|
20
|
+
`arbeitsplatz-dateien` (holt `istUnsicherenPfad`). Der Kreis ist da.
|
|
21
|
+
|
|
22
|
+
⚠️ **Er ist heute harmlos, und nur deshalb:** In keinem der drei Module
|
|
23
|
+
laeuft beim LADEN etwas, das ein anderes braucht -- es werden ausschliesslich
|
|
24
|
+
Funktionen definiert und erst beim Aufruf benutzt. Wer hier oder in einem der
|
|
25
|
+
beiden anderen Module Arbeit auf die oberste Ebene legt (eine Konstante, die
|
|
26
|
+
eine importierte Funktion aufruft), bekommt `undefined` statt einer Funktion,
|
|
27
|
+
und zwar erst zur Laufzeit beim Kunden.
|
|
28
|
+
|
|
29
|
+
*Ein Kommentar, der Kreisfreiheit behauptet, laedt genau dazu ein* -- er
|
|
30
|
+
liest sich wie eine gepruefte Zusage. Deshalb steht der Kreis jetzt hier,
|
|
31
|
+
statt weg-behauptet zu werden.
|
|
32
|
+
*/
|
|
33
|
+
import { absage } from "./befehle/kontext-gemeinsam.js";
|
|
5
34
|
/*
|
|
6
35
|
🔁 Die Ableitung „welcher Unterordner ist die Werkbank" wohnt seit dem
|
|
7
36
|
2026-08-20 in `werkbank-name.ts` -- gemeinsam mit der App, die sie fuer `/pro`
|
|
@@ -10,6 +39,34 @@ import { schlussFehler } from "./fehler.js";
|
|
|
10
39
|
Kopien des Namens, nur besser getarnt.*
|
|
11
40
|
*/
|
|
12
41
|
export { werkbankAusDateien as werkbankOrdner } from "./werkbank-name.js";
|
|
42
|
+
/**
|
|
43
|
+
* Die Optionen, die `arbeitsplatz neu` und `werkbank neu` zur Firmenwahl
|
|
44
|
+
* annehmen -- **dieselbe Liste** wie bei `kontext runter`/`kontext hoch`,
|
|
45
|
+
* weitergereicht statt abgeschrieben.
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ Zwei eigene Listen hätten dieselbe Frage („welche Firma meinst du?") bei
|
|
48
|
+
* vier Befehlen aus zwei Quellen beantwortet. Eine davon würde eines Tages
|
|
49
|
+
* `--firmen-id` heißen, und niemand merkte es beim Lesen.
|
|
50
|
+
*/
|
|
51
|
+
export { ERLAUBT as FIRMEN_OPTIONEN } from "./befehle/kontext-gemeinsam.js";
|
|
52
|
+
/**
|
|
53
|
+
* Aus dem Wunsch die Suchparameter -- **rein**, damit `check:firmenwahl` sie
|
|
54
|
+
* ohne Netz messen kann.
|
|
55
|
+
*
|
|
56
|
+
* 🔑 `URLSearchParams` und keine selbstgebaute Zeichenkette: Ein Firmenname
|
|
57
|
+
* darf `&`, `=`, `?` und Leerzeichen enthalten (`Blume & Co`), und von Hand
|
|
58
|
+
* zusammengesetzt zerbräche daran die Anfrage -- ohne Fehlermeldung, mit einer
|
|
59
|
+
* Antwort für die falsche Firma.
|
|
60
|
+
*/
|
|
61
|
+
export function firmenSuche(wunsch) {
|
|
62
|
+
const suche = new URLSearchParams();
|
|
63
|
+
if (wunsch.firma !== undefined)
|
|
64
|
+
suche.set("firma", wunsch.firma);
|
|
65
|
+
if (wunsch.firmaId !== undefined)
|
|
66
|
+
suche.set("firma-id", wunsch.firmaId);
|
|
67
|
+
const text = suche.toString();
|
|
68
|
+
return text === "" ? "" : `?${text}`;
|
|
69
|
+
}
|
|
13
70
|
/**
|
|
14
71
|
* Holt die Vorlage von der App — **eine** Route für beide Befehle.
|
|
15
72
|
*
|
|
@@ -17,9 +74,20 @@ export { werkbankAusDateien as werkbankOrdner } from "./werkbank-name.js";
|
|
|
17
74
|
* Dateien, dieselbe Pro-Prüfung, derselbe Ausweis. Eine zweite Route wäre eine
|
|
18
75
|
* zweite Tür, die man einzeln absichern und einzeln nachmessen müsste — für
|
|
19
76
|
* exakt denselben Inhalt. Der Ausschnitt entsteht hier auf dem Rechner.
|
|
77
|
+
*
|
|
78
|
+
* 🔴 **Beide Parameter sind PFLICHT** (Punkt A-3, 2026-08-26). `wunsch` könnte
|
|
79
|
+
* bequem entfallen, wenn niemand eine Firma nennt -- und genau so bleibt eine
|
|
80
|
+
* gebaute Rechnung an der Aufrufstelle tot, ohne dass ein Compiler oder ein
|
|
81
|
+
* Wächter etwas merkt (dieselbe Falle wie bei `lageFuerDatei`, Punkt A-1).
|
|
82
|
+
* `befehl` nennt den Befehl, den die 409-Absage zum Kopieren anbietet -- stünde
|
|
83
|
+
* dort fest `kontext runter`, bekäme ein Mensch bei `werkbank neu` einen Rat,
|
|
84
|
+
* der seine Werkbank nicht anlegt.
|
|
20
85
|
*/
|
|
21
|
-
export async function vorlageHolen() {
|
|
22
|
-
const antwort = await ruf(
|
|
86
|
+
export async function vorlageHolen(befehl, wunsch) {
|
|
87
|
+
const antwort = await ruf(`/api/cli/arbeitsplatz${firmenSuche(wunsch)}`, {
|
|
88
|
+
methode: "GET",
|
|
89
|
+
mitAusweis: true,
|
|
90
|
+
});
|
|
23
91
|
if (antwort.status === 200) {
|
|
24
92
|
const daten = alsVorlageAntwort(antwort.koerper);
|
|
25
93
|
if (daten === null) {
|
|
@@ -28,24 +96,18 @@ export async function vorlageHolen() {
|
|
|
28
96
|
}
|
|
29
97
|
return { ok: true, daten };
|
|
30
98
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
// 🔑 Den Rest entscheidet `schlussFehler` fuer alle Befehle gemeinsam --
|
|
44
|
-
// insbesondere die `404` (die App kennt die Route noch nicht) und `429`/
|
|
45
|
-
// `408` (ueberlastet, wiederholbar).
|
|
46
|
-
const schluss = schlussFehler(antwort.status, meldung);
|
|
47
|
-
console.error(schluss.text);
|
|
48
|
-
return { ok: false, wert: schluss.wert };
|
|
99
|
+
/*
|
|
100
|
+
🔴 **Hier standen dieselben vier Zweige noch einmal abgetippt** (402 vor
|
|
101
|
+
401, Rest an `schlussFehler`) -- Zeichen für Zeichen das, was `absage`
|
|
102
|
+
ohnehin tut, nur ohne den `409`-Zweig. Dadurch fehlte genau die Auskunft,
|
|
103
|
+
um die es bei Punkt A-3 geht: welche Firma gemeint ist.
|
|
104
|
+
|
|
105
|
+
⚠️ **Und das Weglassen war unsichtbar**, denn `409` kam von dieser Route
|
|
106
|
+
bis zum 2026-08-26 gar nicht -- sie hat die Frage still selbst entschieden.
|
|
107
|
+
*Ein Zweig, den es nie zu sehen gab, fehlt niemandem, bis der Server
|
|
108
|
+
anfängt zu fragen.*
|
|
109
|
+
*/
|
|
110
|
+
return { ok: false, wert: absage(antwort, befehl) };
|
|
49
111
|
}
|
|
50
112
|
/** Prueft die Antwort-Form, ohne `any` -- eine falsche Form ist unlesbar, keine Ausnahme. */
|
|
51
113
|
function alsVorlageAntwort(koerper) {
|
package/dist/argumente.js
CHANGED
|
@@ -6,6 +6,156 @@
|
|
|
6
6
|
* Uebergehen. Wer `--titl` tippt, soll es erfahren, statt ein Projekt ohne
|
|
7
7
|
* Titel anzulegen.
|
|
8
8
|
*/
|
|
9
|
+
/**
|
|
10
|
+
* Trennt die **vorderen Woerter** von den Optionen.
|
|
11
|
+
*
|
|
12
|
+
* 🔴 **Warum es das seit dem 2026-08-26 gibt** (Punkt A-3): `arbeitsplatz neu`
|
|
13
|
+
* und `werkbank neu` haben ein Wort ohne `--` -- den Vornamen
|
|
14
|
+
* (`kimal3 arbeitsplatz neu sabine`). `zerlege` lehnt genau das ab, und zwar
|
|
15
|
+
* absichtlich (s. B-4 oben). Beide Befehle brauchen jetzt zusaetzlich
|
|
16
|
+
* `--firma`/`--firma-id`, also braucht es einen Schnitt VOR `zerlege`, statt
|
|
17
|
+
* dessen Strenge aufzuweichen.
|
|
18
|
+
*
|
|
19
|
+
* Geschnitten wird beim **ersten** Wort, das mit `--` beginnt. Alles davor
|
|
20
|
+
* sind Woerter, alles ab dort geht unveraendert an `zerlege` -- samt seiner
|
|
21
|
+
* Strenge: Ein Wort ohne `--` HINTER einer Option bleibt ein Fehler
|
|
22
|
+
* (`kimal3 arbeitsplatz neu --firma A muell`).
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ **Nur `--`, nicht `-`.** Ein einzelnes `-x` bleibt ein Wort und laeuft in
|
|
25
|
+
* die Namenspruefung des Befehls. Das ist die heutige Bedeutung: `-h` faengt
|
|
26
|
+
* jeder Befehl selbst ab, bevor er hier ankommt, und ein `-force` soll die
|
|
27
|
+
* Meldung „damit kann ich hier nichts anfangen" bekommen und keine
|
|
28
|
+
* Options-Meldung ueber etwas, das gar keine Option ist.
|
|
29
|
+
*/
|
|
30
|
+
export function trenneNamenUndOptionen(argv) {
|
|
31
|
+
const erste = argv.findIndex((teil) => teil.startsWith("--"));
|
|
32
|
+
if (erste === -1)
|
|
33
|
+
return { woerter: argv, optionen: [] };
|
|
34
|
+
return { woerter: argv.slice(0, erste), optionen: argv.slice(erste) };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Die getippten Optionen wieder zu einer Zeile machen, die man kopieren kann.
|
|
38
|
+
*
|
|
39
|
+
* 🔴 **Gegen Review-Befund 3 zu A-3 (2026-08-26).** Der Vorschlag bei einem
|
|
40
|
+
* mehrteiligen Namen entsteht, BEVOR die Optionen zerlegt sind -- er kannte
|
|
41
|
+
* also nur die Woerter. Aus `arbeitsplatz neu sabine mueller --firma "Blume &
|
|
42
|
+
* Co"` wurde der Rat `kimal3 arbeitsplatz neu sabine-mueller`, **ohne die
|
|
43
|
+
* Firma**. Wer ihn kopierte, lief demselben `409` ein zweites Mal in die Arme.
|
|
44
|
+
*
|
|
45
|
+
* 🔑 Das ist die Kehrseite der Sache, die derselbe Commit eine Bildschirmhoehe
|
|
46
|
+
* tiefer ausdruecklich richtig macht („der getippte Vorname reist in der Absage
|
|
47
|
+
* mit"). *Ein Rat, der die Haelfte der Eingabe vergisst, ist ein Rat in eine
|
|
48
|
+
* zweite Absage.*
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ **Anfuehrungszeichen, wo sie noetig sind.** Ein Firmenname mit Leerzeichen
|
|
51
|
+
* („Blume & Co") ergaebe ohne sie eine Zeile, die beim Einfuegen in etwas ganz
|
|
52
|
+
* anderes zerfaellt. Geprueft wird auf Leerzeichen und auf das leere Wort --
|
|
53
|
+
* beides kommt aus einem echten Firmennamen, nicht aus einem Sonderfall.
|
|
54
|
+
*/
|
|
55
|
+
export function alsBefehlszeile(optionen) {
|
|
56
|
+
/*
|
|
57
|
+
🔴 **`(teil) => alsWort(teil)` und NICHT `map(alsWort)`** -- gefunden vom
|
|
58
|
+
eigenen Waechter, Minuten nach dem Einbauen (2026-08-27). `Array.map`
|
|
59
|
+
reicht jedem Aufruf DREI Werte: Element, Listenplatz, ganze Liste. Der
|
|
60
|
+
Listenplatz landete damit auf dem zweiten Parameter `immer` -- und weil
|
|
61
|
+
jeder Platz ausser dem ersten wahr ist, bekam ab dem zweiten Wort jedes
|
|
62
|
+
seine Anfuehrungszeichen: `--firma "Nordwind"` statt `--firma Nordwind`.
|
|
63
|
+
*Ein Parameter mit Vorgabewert ist eine offene Tuer fuer jeden, der
|
|
64
|
+
ungefragt ein zweites Argument mitschickt.*
|
|
65
|
+
*/
|
|
66
|
+
return optionen.map((teil) => alsWort(teil)).join(" ");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* EIN Wort so in eine Befehlszeile setzen, dass es beim Kopieren wieder genau
|
|
70
|
+
* dieses Wort ergibt.
|
|
71
|
+
*
|
|
72
|
+
* 🔴 **Gegen Befund 4 der zweiten Review-Runde (2026-08-27).** An NEUN Stellen
|
|
73
|
+
* stand dieselbe Zeile — `wert.includes(" ") ? `"${wert}"` : wert` — und alle
|
|
74
|
+
* neun hatten dieselbe Lücke: Ein Wert, der selbst ein Anführungszeichen
|
|
75
|
+
* enthält, sprengt die Anführungszeichen, die man um ihn herum setzt.
|
|
76
|
+
*
|
|
77
|
+
* Der Fall: `kimal3 arbeitsplatz neu sabine --firma 'Blume "Nord" GmbH'`. Der
|
|
78
|
+
* angebotene Rat lautete danach `--firma "Blume "Nord" GmbH"`. Wer ihn
|
|
79
|
+
* kopiert, gibt seiner Kommandozeile **drei** Wörter statt einem und bekommt
|
|
80
|
+
* eine andere, verwirrendere Absage als die, die er gerade gelesen hat.
|
|
81
|
+
* *Ein Rat, der beim Kopieren zerfällt, ist ein Rat in eine zweite Absage* —
|
|
82
|
+
* genau die Lehre, gegen die `alsBefehlszeile` oben gebaut wurde.
|
|
83
|
+
*
|
|
84
|
+
* 🔑 **Warum einfache Anführungszeichen und keine Maskierung mit `\`:**
|
|
85
|
+
* `'…'` bedeutet in beiden Kommandozeilen dasselbe, die auf den Rechnern
|
|
86
|
+
* unserer Kunden laufen — in der von Mac/Linux (`sh`) wie in der von Windows
|
|
87
|
+
* (PowerShell). Eine Maskierung mit `\"` gilt nur in der ersten; in
|
|
88
|
+
* PowerShell müsste man das Zeichen verdoppeln. *Eine Bauform, die nur auf
|
|
89
|
+
* einem der beiden Rechner hält, ist in einem Produkt für beide keine
|
|
90
|
+
* Bauform.*
|
|
91
|
+
*
|
|
92
|
+
* 🔑 **`immer` heisst „setz die Anfuehrungszeichen auch dann, wenn es ohne
|
|
93
|
+
* ginge".** Die Zeilen unter „Deine Firmen:" haben sie seit jeher, und dort
|
|
94
|
+
* gehoeren sie hin: Sie sind zum Kopieren UND zum Weitertippen gedacht -- wer
|
|
95
|
+
* „Suedwind" durch „Blume & Co" ersetzt, hat die Anfuehrungszeichen dann schon
|
|
96
|
+
* stehen. Beim Zusammenbauen einer getippten Zeile (`alsBefehlszeile`) und
|
|
97
|
+
* beim `cd` waere das umgekehrt eine Aenderung an dem, was der Kunde bisher
|
|
98
|
+
* sieht -- deshalb dort die Vorgabe `false`.
|
|
99
|
+
*
|
|
100
|
+
* ⚠️ **Die eine Lage, die offen bleibt:** ein Wert mit BEIDEN Sorten
|
|
101
|
+
* Anführungszeichen. Dafür gibt es keine Schreibweise, die in beiden
|
|
102
|
+
* Kommandozeilen dasselbe heißt. Der Rückfall maskiert nach `sh`-Regeln — und
|
|
103
|
+
* für genau diesen Fall gibt es ohnehin `--firma-id`, das ohne jedes
|
|
104
|
+
* Sonderzeichen auskommt.
|
|
105
|
+
*/
|
|
106
|
+
export function alsWort(wert, immer = false) {
|
|
107
|
+
if (wert === "")
|
|
108
|
+
return '""';
|
|
109
|
+
/*
|
|
110
|
+
Was eine Kommandozeile nicht wörtlich stehen lässt: Leerzeichen (trennen),
|
|
111
|
+
Anführungszeichen beider Sorten (zitieren) -- und `$` wie ````
|
|
112
|
+
(rechnen).
|
|
113
|
+
|
|
114
|
+
⚠️ Der Rückwärtsstrich ist bewusst NICHT dabei: In einem Firmennamen
|
|
115
|
+
kommt er nicht vor, und ihn mitzunehmen hieße, ihn auch maskieren zu
|
|
116
|
+
müssen -- was in PowerShell wieder falsch wäre.
|
|
117
|
+
*/
|
|
118
|
+
/*
|
|
119
|
+
🔴 **EINE ERLAUBNISLISTE, KEINE VERBOTSLISTE** (Befund 1 der FÜNFTEN
|
|
120
|
+
Review-Runde, 2026-08-27) -- und die Verbotsliste hatte drei Runden
|
|
121
|
+
gebraucht, um dreimal zu lecken: erst fehlten die Anführungszeichen, dann
|
|
122
|
+
`$` und das Akzent-Zeichen, dann `&`, `;`, `|`, Klammern, `<`, `>`.
|
|
123
|
+
|
|
124
|
+
Der Fall, der es aufgedeckt hat: eine Firma namens `Blume&Co` -- **ohne**
|
|
125
|
+
Leerzeichen, also fiel sie durch jede Prüfung. Die angebotene Zeile hieß
|
|
126
|
+
`--firma Blume&Co`; in PowerShell ist das ein Syntaxfehler, in `sh`
|
|
127
|
+
startet es `--firma Blume` im Hintergrund und führt danach `Co` aus.
|
|
128
|
+
|
|
129
|
+
🔑 *Eine Liste dessen, was gefährlich ist, ist immer unvollständig -- die
|
|
130
|
+
Liste dessen, was harmlos ist, kann man zu Ende schreiben.* Also
|
|
131
|
+
umgekehrt: Buchstaben (auch Umlaute), Ziffern und eine Handvoll
|
|
132
|
+
unverfänglicher Satzzeichen bleiben unangetastet, **alles andere wird
|
|
133
|
+
zitiert**. Ein Anführungszeichen zu viel ist folgenlos; eines zu wenig
|
|
134
|
+
hat jetzt dreimal einen Rat zerlegt.
|
|
135
|
+
*/
|
|
136
|
+
if (!immer && /^[A-Za-z0-9À-ÿ._\/@:+-]+$/.test(wert))
|
|
137
|
+
return wert;
|
|
138
|
+
/*
|
|
139
|
+
🔴 **`$` UND ```` MÜSSEN AUS DEN DOPPELTEN ANFÜHRUNGSZEICHEN HERAUS**
|
|
140
|
+
(Befund 4 der dritten Review-Runde, 2026-08-27). Doppelte Anführungszeichen
|
|
141
|
+
halten Leerzeichen zusammen, aber sie schützen NICHT vor dem Rechnen: Eine
|
|
142
|
+
Firma namens `Cash$$ GmbH` ergab die Zeile
|
|
143
|
+
`--firma "Cash$$ GmbH"`, und `$$` ist in `sh` die Nummer des laufenden
|
|
144
|
+
Programms. Der Kunde kopiert den angebotenen Befehl und liest danach
|
|
145
|
+
*„Zu ‚Cash1234 GmbH' gehörst du nicht."* -- wieder ein Rat, der beim
|
|
146
|
+
Kopieren zerfällt, nur eine Stufe subtiler als der Fall, gegen den diese
|
|
147
|
+
Funktion gebaut wurde.
|
|
148
|
+
|
|
149
|
+
Deshalb: doppelte Anführungszeichen nur, wenn wirklich nichts Rechnendes
|
|
150
|
+
drinsteht. Sonst die einfachen, die in beiden Kommandozeilen alles wörtlich
|
|
151
|
+
nehmen.
|
|
152
|
+
*/
|
|
153
|
+
if (!/["$\`]/.test(wert))
|
|
154
|
+
return `"${wert}"`;
|
|
155
|
+
if (!wert.includes("'"))
|
|
156
|
+
return `'${wert}'`;
|
|
157
|
+
return `"${wert.replace(/(["$\`])/g, "\\$1")}"`;
|
|
158
|
+
}
|
|
9
159
|
export function zerlege(argv, erlaubt) {
|
|
10
160
|
const optionen = {};
|
|
11
161
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
-
import { dateienSchreiben, vorlageHolen, werkbankOrdner, istUnsichererOrdnername, } from "../arbeitsplatz-dateien.js";
|
|
2
|
+
import { FIRMEN_OPTIONEN, dateienSchreiben, vorlageHolen, werkbankOrdner, istUnsichererOrdnername, } from "../arbeitsplatz-dateien.js";
|
|
3
|
+
import { alsBefehlszeile, alsWort, trenneNamenUndOptionen, zerlege } from "../argumente.js";
|
|
4
|
+
import { fragenAus, traegtFragenVon } from "../fragen-lesen.js";
|
|
5
|
+
import { FIRMEN_TEXTE } from "../kontext-stand.js";
|
|
3
6
|
import { werkbankOrdnerAus, werkbankUmbenennen } from "../werkbank-name.js";
|
|
4
7
|
/**
|
|
5
8
|
* Holt die KI-Arbeitsplatz-Vorlage vom Server und legt sie lokal an --
|
|
@@ -23,7 +26,7 @@ import { werkbankOrdnerAus, werkbankUmbenennen } from "../werkbank-name.js";
|
|
|
23
26
|
* `../arbeitsplatz-dateien.ts`, weil `werkbank neu` dieselbe braucht.
|
|
24
27
|
*/
|
|
25
28
|
const HILFE = `
|
|
26
|
-
kimal3 arbeitsplatz neu [dein-vorname]
|
|
29
|
+
kimal3 arbeitsplatz neu [dein-vorname] [--firma "Name" | --firma-id "…"]
|
|
27
30
|
|
|
28
31
|
Holt deine KI-Arbeitsplatz-Vorlage und legt sie hier als Ordner an --
|
|
29
32
|
die ganze Familie: Firmenebene, Werkbank, Beispielprojekt.
|
|
@@ -35,6 +38,13 @@ const HILFE = `
|
|
|
35
38
|
wird "werkbank-sabine". Ohne Angabe steht dort der Name,
|
|
36
39
|
den der Server aus deiner Anmelde-Adresse gebildet hat.
|
|
37
40
|
|
|
41
|
+
--firma "Name" Noetig, wenn du zu mehreren Firmen gehoerst -- der
|
|
42
|
+
Firmenkontext kommt gleich mit in den Ordner.
|
|
43
|
+
Die Absage nennt dir die Namen als fertige Befehle.
|
|
44
|
+
|
|
45
|
+
--firma-id "…" Fuer den Fall, dass zwei deiner Firmen GLEICH heissen.
|
|
46
|
+
Auch diese Absage nennt dir fertige Befehle.
|
|
47
|
+
|
|
38
48
|
🔴 Bricht ab, wenn der Zielordner schon existiert und NICHT leer ist --
|
|
39
49
|
eigene Arbeit wird dadurch nie versehentlich ueberschrieben.
|
|
40
50
|
|
|
@@ -76,12 +86,33 @@ const HILFE = `
|
|
|
76
86
|
* Runde 3 zu PR #24 auf `/pro` gefunden hat.
|
|
77
87
|
*/
|
|
78
88
|
function hinweisFuer(ordnerName) {
|
|
79
|
-
const ziel = ordnerName
|
|
89
|
+
const ziel = alsWort(ordnerName);
|
|
80
90
|
return (" Steht dein Arbeitsplatz schon und dir fehlt nur die eigene Werkbank?\n" +
|
|
81
91
|
" Dann geh HINEIN und leg sie dort an:\n" +
|
|
82
92
|
` cd ${ziel}\n` +
|
|
83
93
|
" kimal3 werkbank neu <dein-vorname>");
|
|
84
94
|
}
|
|
95
|
+
export function fragenstandAus(dateien) {
|
|
96
|
+
let offen = 0;
|
|
97
|
+
let gesamt = 0;
|
|
98
|
+
for (const text of FIRMEN_TEXTE) {
|
|
99
|
+
const geliefert = dateien.find((d) => d.pfad === text.pfad);
|
|
100
|
+
if (geliefert === undefined)
|
|
101
|
+
return { art: "unbekannt" };
|
|
102
|
+
if (!traegtFragenVon(geliefert.inhalt, text.ankerPraefix))
|
|
103
|
+
return { art: "unbekannt" };
|
|
104
|
+
// ⚠️ `fragenAus` nimmt genau EIN Argument -- trotzdem steht hier die
|
|
105
|
+
// ausgeschriebene Form. `filter`/`map` reichen der Funktion drei Werte, und
|
|
106
|
+
// ein zweiter Parameter bekaeme spaeter stillschweigend den Listenplatz
|
|
107
|
+
// (`alsWort`, 2026-08-27).
|
|
108
|
+
const fragen = fragenAus(geliefert.inhalt);
|
|
109
|
+
gesamt += fragen.length;
|
|
110
|
+
offen += fragen.filter((f) => f.antwort === "").length;
|
|
111
|
+
}
|
|
112
|
+
// 🔑 Null Fragen in fuenf Dateien, die ihre Anker alle tragen, gibt es nicht
|
|
113
|
+
// -- und selbst dann waere „nichts offen" die ehrliche Antwort.
|
|
114
|
+
return offen === 0 ? { art: "keine" } : { art: "offen", offen, gesamt };
|
|
115
|
+
}
|
|
85
116
|
export async function arbeitsplatzNeu(argv) {
|
|
86
117
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
87
118
|
console.log(HILFE);
|
|
@@ -97,37 +128,64 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
97
128
|
`werkbank-arno-ki`. Diese Meldungen und die Hilfe sind die einzigen Stellen,
|
|
98
129
|
an denen er das erfaehrt, bevor es passiert.
|
|
99
130
|
*/
|
|
100
|
-
|
|
131
|
+
/*
|
|
132
|
+
🔴 **Erst schneiden, dann zerlegen** (Punkt A-3, 2026-08-26). Dieser Befehl
|
|
133
|
+
hat ein Wort OHNE `--` (den Vornamen), und `zerlege` lehnt genau das ab.
|
|
134
|
+
Deshalb trennt `trenneNamenUndOptionen` beim ersten `--`: davor die
|
|
135
|
+
Woerter, ab dort die Optionen. Die Strenge von `zerlege` bleibt damit
|
|
136
|
+
unangetastet -- sie war teuer erkauft (B-3 und B-4, 2026-08-18).
|
|
137
|
+
*/
|
|
138
|
+
const { woerter, optionen: optionsArgv } = trenneNamenUndOptionen(argv);
|
|
139
|
+
const nameArg = woerter[0];
|
|
101
140
|
if (nameArg !== undefined && nameArg.trim() === "") {
|
|
102
141
|
console.error(`Ein leerer Name ist kein gueltiger Name.\n${HILFE}`);
|
|
103
142
|
return 1;
|
|
104
143
|
}
|
|
105
|
-
if (
|
|
106
|
-
console.error(`Die Option „${nameArg}" kenne ich nicht.\n${HILFE}`);
|
|
107
|
-
return 1;
|
|
108
|
-
}
|
|
109
|
-
if (argv.length > 1) {
|
|
144
|
+
if (woerter.length > 1) {
|
|
110
145
|
/*
|
|
111
146
|
🔴 **Der Vorschlag darf keine Optionen einsammeln** (Review-Runde 5 zu
|
|
112
147
|
PR #25). Hier stand `argv.join("-")` -- aus `arbeitsplatz neu sabine --force`
|
|
113
|
-
wurde damit der Rat `arbeitsplatz neu sabine---force`.
|
|
114
|
-
Zeilen hoeher sieht nur `argv[0]`.
|
|
148
|
+
wurde damit der Rat `arbeitsplatz neu sabine---force`.
|
|
115
149
|
*Ein Rat, der ein falsches Kommando erzeugt, ist schlimmer als keiner* --
|
|
116
150
|
dieselbe Klasse wie die Absagen aus Runde 1 und 2, nur kleiner.
|
|
117
151
|
|
|
118
152
|
Deshalb: Sieht irgendein Wort wie eine Option aus, gibt es gar keinen
|
|
119
153
|
Vorschlag. Ein Mensch, der `--force` tippt, sucht keine Schreibhilfe fuer
|
|
120
154
|
seinen Vornamen.
|
|
155
|
+
|
|
156
|
+
⚠️ Seit dem 2026-08-26 stehen die `--`-Optionen schon gar nicht mehr in
|
|
157
|
+
`woerter` (der Schnitt oben). Der Filter bleibt trotzdem: Er faengt das
|
|
158
|
+
EINFACHE `-` (`-force`), das kein Schnitt-Zeichen ist und das der
|
|
159
|
+
Vorschlag genauso wenig einsammeln darf.
|
|
160
|
+
*/
|
|
161
|
+
const einfacheWoerter = woerter.filter((w) => !w.startsWith("-"));
|
|
162
|
+
/*
|
|
163
|
+
🔴 **Die getippten Optionen reisen mit** (Review-Befund 3, 2026-08-26).
|
|
164
|
+
Ohne sie riet die Zeile zu einem Befehl, der bei einem Menschen in zwei
|
|
165
|
+
Firmen sofort wieder in dieselbe Absage laeuft -- s. `alsBefehlszeile`.
|
|
121
166
|
*/
|
|
122
|
-
const
|
|
123
|
-
const vorschlag =
|
|
167
|
+
const optionsAnhang = optionsArgv.length === 0 ? "" : ` ${alsBefehlszeile(optionsArgv)}`;
|
|
168
|
+
const vorschlag = einfacheWoerter.length === woerter.length
|
|
124
169
|
? `Besteht dein Name aus mehreren Woertern? Dann schreib ihn zusammen:\n` +
|
|
125
|
-
` kimal3 arbeitsplatz neu ${
|
|
170
|
+
` kimal3 arbeitsplatz neu ${einfacheWoerter.join("-")}${optionsAnhang}\n`
|
|
126
171
|
: "";
|
|
127
|
-
console.error(`Mit „${
|
|
172
|
+
console.error(`Mit „${woerter[1]}" kann ich hier nichts anfangen -- nur EIN Name ist erlaubt.\n` +
|
|
128
173
|
`${vorschlag}${HILFE}`);
|
|
129
174
|
return 1;
|
|
130
175
|
}
|
|
176
|
+
/*
|
|
177
|
+
🔑 Erst JETZT die Optionen -- nach den Namens-Absagen, damit ein Mensch mit
|
|
178
|
+
zwei Vornamen die Schreibhilfe bekommt und nicht eine Options-Meldung.
|
|
179
|
+
*/
|
|
180
|
+
const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN);
|
|
181
|
+
if (!zerlegt.ok) {
|
|
182
|
+
console.error(`${zerlegt.meldung}\n${HILFE}`);
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
const wunsch = {
|
|
186
|
+
firma: zerlegt.werte.optionen.firma,
|
|
187
|
+
firmaId: zerlegt.werte.optionen["firma-id"],
|
|
188
|
+
};
|
|
131
189
|
/*
|
|
132
190
|
Der Name wird UMGESCHRIEBEN, nicht abgelehnt (s. `werkbankOrdnerAus`) --
|
|
133
191
|
"Sabine Mueller" ist ein gueltiger Name, nur kein gueltiger Ordner. `null`
|
|
@@ -146,7 +204,24 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
146
204
|
return 1;
|
|
147
205
|
}
|
|
148
206
|
}
|
|
149
|
-
|
|
207
|
+
/*
|
|
208
|
+
🔴 **HIER ENTSCHEIDET SICH, OB UEBERHAUPT ETWAS ENTSTEHT** (Punkt A-3).
|
|
209
|
+
Gehoert der Mensch zu mehreren Firmen und hat keine genannt, antwortet die
|
|
210
|
+
Route `409`, `vorlageHolen` gibt `1` zurueck -- und `dateienSchreiben`
|
|
211
|
+
weiter unten wird nie erreicht. Es wird also **kein halber Ordner**
|
|
212
|
+
angelegt, den er danach von Hand wegraeumen muesste.
|
|
213
|
+
*/
|
|
214
|
+
/*
|
|
215
|
+
🔑 **Der getippte Vorname reist in der Absage mit.** Wer
|
|
216
|
+
`kimal3 arbeitsplatz neu sabine` tippt und die Rueckfrage bekommt, soll die
|
|
217
|
+
fertige Zeile kopieren koennen, ohne seinen Namen ein zweites Mal
|
|
218
|
+
hineinzuschreiben -- sonst hiesse seine Werkbank nach dem zweiten Versuch
|
|
219
|
+
anders als beim ersten gewollt, und niemand saehe, woran es lag.
|
|
220
|
+
*/
|
|
221
|
+
const befehlsname = nameArg === undefined
|
|
222
|
+
? "arbeitsplatz neu"
|
|
223
|
+
: `arbeitsplatz neu ${alsWort(nameArg)}`;
|
|
224
|
+
const holen = await vorlageHolen(befehlsname, wunsch);
|
|
150
225
|
if (!holen.ok)
|
|
151
226
|
return holen.wert;
|
|
152
227
|
const daten = holen.daten;
|
|
@@ -253,8 +328,55 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
253
328
|
// zuverlaessig ins Leere.*
|
|
254
329
|
const werkbank = werkbankOrdner(dateien);
|
|
255
330
|
const cdZiel = werkbank !== null ? `${ordnerName}/${werkbank}` : ordnerName;
|
|
256
|
-
console.log(` cd ${cdZiel
|
|
331
|
+
console.log(` cd ${alsWort(cdZiel)}`);
|
|
257
332
|
console.log(" claude");
|
|
258
333
|
console.log(' dann "Lass uns einrichten" tippen');
|
|
334
|
+
/*
|
|
335
|
+
🔴 **HIER RISS DIE KETTE.**
|
|
336
|
+
Bis zum 2026-08-28 endete dieser Befehl immer mit denselben drei Zeilen: Der
|
|
337
|
+
Mensch hat einen Ordner, ein Einrichtungsgespraech -- und einen Stapel
|
|
338
|
+
unbeantworteter Fragen ueber seine Firma, von denen ihm niemand erzaehlt.
|
|
339
|
+
*Er hat den Ordner und das Gefuehl, fertig zu sein.*
|
|
340
|
+
|
|
341
|
+
⚠️ Hier stand kurz „siebzehn Fragen", abgeschrieben aus der Spec. Gemessen
|
|
342
|
+
am 2026-08-28 sind es **sechzehn** (4 + 3 + 4 + 2 + 3 Anker in den fuenf
|
|
343
|
+
Vorlagen). Deshalb steht die Zahl unten NICHT im Text, sondern kommt aus
|
|
344
|
+
derselben Zaehlung, die auch `kimal3 kontext fragen` ausgibt.
|
|
345
|
+
*Eine Zahl im Fliesstext ist eine zweite Wahrheit, die still verrottet.*
|
|
346
|
+
|
|
347
|
+
🔑 **Der Rat haengt an der Lage, nicht am Befehl.** Nur wenn wirklich Fragen
|
|
348
|
+
offen sind, steht `/k3kontext` da. Bei einer Firma, die ihre fuenf Texte
|
|
349
|
+
schon gepflegt hat, bleibt die Ausgabe Zeichen fuer Zeichen die von vorher.
|
|
350
|
+
*Ein Rat, der immer erscheint, ist keine Auskunft, sondern Deko* -- so
|
|
351
|
+
ausdruecklich in US-1 der Spec
|
|
352
|
+
(`docs/superpowers/specs/2026-08-28-k3-befehle-design.md`).
|
|
353
|
+
|
|
354
|
+
⚠️ **Er sagt NICHT „und schick es hoch".** Ob dieser Mensch Admin seiner
|
|
355
|
+
Firma ist, steht in dieser Antwort nicht drin -- `/api/cli/arbeitsplatz`
|
|
356
|
+
liefert `wurzel` und `dateien`, sonst nichts. Die Schwesterroute
|
|
357
|
+
`/api/cli/kontext` traegt eine `rolle`, diese nicht. Also raet die Ausgabe
|
|
358
|
+
zu dem Schritt, den JEDER gehen kann: die Fragen beantworten.
|
|
359
|
+
|
|
360
|
+
🔴 **Was danach passiert, deutet `/k3kontext` an der MELDUNG von
|
|
361
|
+
`kimal3 kontext hoch` -- nicht an der Zahl.** Hier stand bis zur zweiten
|
|
362
|
+
Review-Runde „der deutet Rueckgabewert 1 als ‚du bist kein Admin'", und
|
|
363
|
+
genau diese Gleichung hat dieselbe Scheibe an zwei anderen Stellen als
|
|
364
|
+
falsch geloescht: `kontext hoch` gibt `1` auch dann zurueck, wenn die Firma
|
|
365
|
+
erfolgreich gespeichert hat und nur der lokale Merkzettel scheiterte
|
|
366
|
+
(`kontext-hoch.ts`, Merkzettel-Zweig). *Ein Kommentar, der ein Verhalten erklaert, wird
|
|
367
|
+
beim naechsten Lesen abgenickt -- dieser haette den Fehler zurueckgeholt,
|
|
368
|
+
den der Commit daneben entfernt.*
|
|
369
|
+
|
|
370
|
+
⚠️ Der Ordner ist zu diesem Zeitpunkt geschrieben; ein Zaehlfehler darf
|
|
371
|
+
daran nichts mehr aendern. Deshalb steht das hier ganz unten und kann nur
|
|
372
|
+
noch schweigen, nie abbrechen.
|
|
373
|
+
*/
|
|
374
|
+
const stand = fragenstandAus(dateien);
|
|
375
|
+
if (stand.art === "offen") {
|
|
376
|
+
console.log("");
|
|
377
|
+
console.log(` Danach: ${stand.offen} von ${stand.gesamt} Fragen über deine Firma sind noch offen.`);
|
|
378
|
+
console.log(" Claude stellt sie dir einzeln und trägt deine Antworten ein:");
|
|
379
|
+
console.log(" /k3kontext");
|
|
380
|
+
}
|
|
259
381
|
return 0;
|
|
260
382
|
}
|
|
@@ -146,6 +146,23 @@ export function kontextFragen(argv) {
|
|
|
146
146
|
return 0;
|
|
147
147
|
}
|
|
148
148
|
console.log(`${offen} von ${gesamt} Fragen über deine Firma sind noch offen.\n`);
|
|
149
|
+
/*
|
|
150
|
+
🔴 **WOHER die Pfade zählen** (Review-Befund 2, 2026-08-28). Die Zeilen
|
|
151
|
+
darunter nennen `kontext/marke.md` -- einen Pfad RELATIV zu dem Ordner, den
|
|
152
|
+
`arbeitsplatzOderAbsage` per Aufwaertssuche gefunden hat. Dieser Ordner
|
|
153
|
+
stand hier bis heute **nirgends in der Ausgabe**, und ohne ihn ist der Pfad
|
|
154
|
+
mehrdeutig.
|
|
155
|
+
|
|
156
|
+
⚠️ Der Fall ist nicht ausgedacht, er ist der Normalfall: `arbeitsplatz neu`
|
|
157
|
+
raet ausdruecklich `cd <arbeitsplatz>/werkbank-<name>` und dort `claude`.
|
|
158
|
+
Claude steht dann in der **Werkbank**, die Aufwaertssuche findet die Dateien
|
|
159
|
+
eine Ebene hoeher -- und `kontext/marke.md` zeigt von Claudes Standort aus
|
|
160
|
+
ins Leere. Wer dort schreibt, legt eine NEUE Datei am falschen Ort an;
|
|
161
|
+
`kontext hoch` liest weiter die echten und schickt leere Antworten hoch.
|
|
162
|
+
*Ein Datenverlust, den niemand bemerkt, weil beide Seiten Erfolg melden.*
|
|
163
|
+
*/
|
|
164
|
+
console.log(`Arbeitsplatz: ${ordner}`);
|
|
165
|
+
console.log("Die Pfade unten zählen ab hier.\n");
|
|
149
166
|
for (const datei of gelesen) {
|
|
150
167
|
const offeneDieserDatei = datei.fragen.filter((f) => f.antwort === "");
|
|
151
168
|
if (offeneDieserDatei.length === 0)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { feld } from "../api.js";
|
|
2
|
+
import { alsWort } from "../argumente.js";
|
|
2
3
|
import { schlussFehler } from "../fehler.js";
|
|
3
4
|
import { BASIS_URL } from "../anmeldung.js";
|
|
4
5
|
import { findeArbeitsplatz } from "../kontext-dateien.js";
|
|
@@ -37,8 +38,21 @@ export function arbeitsplatzOderAbsage() {
|
|
|
37
38
|
const ordner = findeArbeitsplatz(process.cwd());
|
|
38
39
|
if (ordner !== null)
|
|
39
40
|
return ordner;
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
/*
|
|
42
|
+
🔴 **ECHTE UMLAUTE** -- dieselbe Begruendung wie bei `fremdeDateiAbsage`
|
|
43
|
+
zwanzig Zeilen tiefer und in `kontext-fragen.ts:26`: Das hier liest ein
|
|
44
|
+
Kunde im Terminal. Bis Review-Runde 4 stand hier die ASCII-Umschrift,
|
|
45
|
+
waehrend die Schwesterfunktion in DERSELBEN Datei Umlaute setzte und es im
|
|
46
|
+
Kommentar begruendete -- eine Datei, zwei Schreibweisen.
|
|
47
|
+
|
|
48
|
+
⚠️ Der Umlaut-Fix an `kontext-hoch.ts` (Runde 3) hat diesen Zwilling
|
|
49
|
+
stehen lassen, und die Wache schlug nicht an: Sie prueft den zitierten
|
|
50
|
+
Ausschnitt „Hier steht kein KI-Arbeitsplatz", und der traegt zufaellig
|
|
51
|
+
keinen Umlaut. *Eine Wache deckt den Satz, den sie kennt -- nicht die
|
|
52
|
+
Regel dahinter.*
|
|
53
|
+
*/
|
|
54
|
+
console.error("Hier steht kein KI-Arbeitsplatz — ich suche von hier aufwärts nach einem");
|
|
55
|
+
console.error("Ordner, der `kontext/` UND `CLAUDE.md` enthält.");
|
|
42
56
|
console.error("");
|
|
43
57
|
console.error(" Wechsle in deinen Arbeitsplatz-Ordner und versuch es dort noch einmal.");
|
|
44
58
|
return null;
|
|
@@ -157,7 +171,7 @@ function firmenZeilenKontext(namen, befehl) {
|
|
|
157
171
|
gesehen.add(name);
|
|
158
172
|
const wieOft = anzahl(name);
|
|
159
173
|
if (wieOft === 1) {
|
|
160
|
-
zeilen.push(` kimal3 ${befehl} --firma
|
|
174
|
+
zeilen.push(` kimal3 ${befehl} --firma ${alsWort(name, true)}`);
|
|
161
175
|
}
|
|
162
176
|
else {
|
|
163
177
|
zeilen.push(` „${name}" — kommt ${wieOft}× vor und ist über den Namen nicht wählbar.`);
|