kimal3 0.1.4 → 0.1.6
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/README.md +19 -0
- package/dist/api.js +61 -1
- package/dist/arbeitsplatz-dateien.js +268 -0
- package/dist/befehle/arbeitsplatz.js +260 -0
- package/dist/befehle/skill-install.js +110 -4
- package/dist/befehle/werkbank.js +389 -0
- package/dist/befehle-katalog.js +59 -0
- package/dist/gesehen.js +85 -0
- package/dist/kimal3.js +172 -13
- package/dist/nachricht.js +198 -0
- package/dist/version.js +37 -0
- package/dist/werkbank-name.js +217 -0
- package/package.json +1 -1
- package/skill/SKILL.md +212 -125
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ auf und bestätigst den Code dort in deinem eingeloggten Konto — so wie bei `g
|
|
|
35
35
|
kimal3 Zeigt, ob dieser Rechner angemeldet ist
|
|
36
36
|
kimal3 login Diesen Rechner mit deinem Konto verbinden
|
|
37
37
|
kimal3 skill install Claude beibringen, dieses Werkzeug zu benutzen — und dir die /k3-Befehle geben
|
|
38
|
+
kimal3 arbeitsplatz neu Den ganzen KI-Arbeitsplatz anlegen — Firmenebene und Werkbank
|
|
39
|
+
kimal3 werkbank neu Nur eine eigene Werkbank in einer bestehenden Firma anlegen
|
|
38
40
|
kimal3 projekt neu … Ein Projekt ins Erfolgsprofil eintragen
|
|
39
41
|
kimal3 projekt liste Deine eingetragenen Projekte anzeigen
|
|
40
42
|
kimal3 firma daten Die Daten deiner Firma holen, damit Claude sie auswerten kann
|
|
@@ -67,6 +69,23 @@ Für Skripte und für Claude selbst:
|
|
|
67
69
|
Der Unterschied zwischen `3` und `4` ist Absicht: Bei `3` lohnt ein zweiter Versuch,
|
|
68
70
|
bei `4` nie.
|
|
69
71
|
|
|
72
|
+
## Update-Hinweise
|
|
73
|
+
|
|
74
|
+
Steht eine neuere Fassung bereit, schreibt `kimal3` nach dem Befehl eine eingerückte
|
|
75
|
+
Zeile — höchstens **einmal am Tag**:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
→ kimal3 0.2.0 ist da. npm i -g kimal3@latest && kimal3 skill install
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Sie geht auf **stderr**, nicht auf stdout: `kimal3 firma daten > firma.json` bleibt
|
|
82
|
+
damit sauberes JSON. Sie **ändert den Rückgabewert nicht** — eine `0` bleibt eine `0`.
|
|
83
|
+
Und `kimal3` **installiert nichts von selbst**; der Befehl steht in der Zeile, ausführen
|
|
84
|
+
tust du ihn.
|
|
85
|
+
|
|
86
|
+
Dasselbe gilt für die Skill-Dateien: Sind die in `~/.claude/skills/kimal3/` älter als
|
|
87
|
+
das installierte Werkzeug, sagt `kimal3` es — dafür braucht es kein Netz.
|
|
88
|
+
|
|
70
89
|
## Voraussetzungen
|
|
71
90
|
|
|
72
91
|
Node.js 20 oder neuer. Sonst nichts — das Werkzeug bringt **keine fremden Bibliotheken**
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BASIS_URL, leseAnmeldung } from "./anmeldung.js";
|
|
2
|
+
import { hinweisAusAntwort } from "./nachricht.js";
|
|
3
|
+
import { eigeneVersion } from "./version.js";
|
|
2
4
|
export class NichtAngemeldet extends Error {
|
|
3
5
|
constructor() { super("nicht angemeldet"); }
|
|
4
6
|
}
|
|
@@ -18,6 +20,26 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
18
20
|
throw new NichtAngemeldet();
|
|
19
21
|
kopf.Authorization = `Bearer ${a.schluessel}`;
|
|
20
22
|
}
|
|
23
|
+
/*
|
|
24
|
+
🔴 DIE CLI SAGT, WER SIE IST -- und nur daran haengt der Server den
|
|
25
|
+
Update-Hinweis an (Befund der fuenften Review-Runde).
|
|
26
|
+
|
|
27
|
+
Ohne diesen Kopf bekaeme JEDER Client das neue Antwortfeld, auch die
|
|
28
|
+
veroeffentlichte 0.1.5. Und die gibt bei `firma daten` den GANZEN Rumpf als
|
|
29
|
+
JSON aus: Sobald jemand eine Nachricht einschaltet, stuende ein
|
|
30
|
+
`"hinweis": {…}` mitten in den Firmendaten -- bei genau den Leuten, fuer
|
|
31
|
+
die der Kanal ueberhaupt gebaut wurde, und in einer Ausgabe, die SKILL.md
|
|
32
|
+
als Firmendaten zu lesen anweist. Gemessen am 2026-08-21 mit der echten
|
|
33
|
+
0.1.5 aus npm.
|
|
34
|
+
|
|
35
|
+
🔑 Damit ist US-7 ("die alte CLI verhaelt sich unveraendert") keine
|
|
36
|
+
Hoffnung mehr, sondern gebaut: Wer den Kopf nicht schickt, bekommt nichts.
|
|
37
|
+
Und die Nummer ist ASCII -- der ByteString-Fehler, an dem der Hinweis
|
|
38
|
+
selbst scheitern wuerde, trifft sie nicht.
|
|
39
|
+
*/
|
|
40
|
+
const version = eigeneVersion();
|
|
41
|
+
if (version !== null)
|
|
42
|
+
kopf["X-Kimal3-Version"] = version;
|
|
21
43
|
const methode = optionen.methode ?? "POST";
|
|
22
44
|
// Content-Type nur, wenn wirklich etwas im Rumpf steht. Ein GET mit
|
|
23
45
|
// "Content-Type: application/json" und ohne Rumpf ist eine Luege ueber die
|
|
@@ -29,7 +51,45 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
29
51
|
headers: kopf,
|
|
30
52
|
body: methode === "POST" ? JSON.stringify(optionen.koerper ?? {}) : undefined,
|
|
31
53
|
});
|
|
32
|
-
|
|
54
|
+
const koerper = await antwort.json().catch(() => null);
|
|
55
|
+
merkeHinweis(koerper);
|
|
56
|
+
return { status: antwort.status, koerper };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Der zuletzt mitgereiste Update-Hinweis.
|
|
60
|
+
*
|
|
61
|
+
* 🔑 WARUM EINE MODULVARIABLE UND KEINE AUSGABE AN DIESER STELLE: `ruf()`
|
|
62
|
+
* laeuft MITTEN in einem Befehl, teils mehrfach. Wer hier schreibt, schneidet
|
|
63
|
+
* die Projektliste entzwei -- die Spec verlangt ausdruecklich, dass die Zeile
|
|
64
|
+
* NACH der Ausgabe steht, nie mittendrin (US-1). Ausgegeben wird genau einmal,
|
|
65
|
+
* ganz am Ende, in kimal3.ts.
|
|
66
|
+
*/
|
|
67
|
+
let letzterHinweis = null;
|
|
68
|
+
function merkeHinweis(koerper) {
|
|
69
|
+
const h = hinweisAusAntwort(koerper);
|
|
70
|
+
if (h)
|
|
71
|
+
letzterHinweis = h;
|
|
72
|
+
/*
|
|
73
|
+
🔴 UND DANN RAUS AUS DEM RUMPF. Gemessen am 2026-08-21: `firma daten` gibt
|
|
74
|
+
den GANZEN Rumpf als JSON aus (`console.log(JSON.stringify(koerper))`) --
|
|
75
|
+
der Hinweis stuende dort mitten in den Firmendaten UND darunter noch einmal
|
|
76
|
+
als →-Zeile. Zweimal dasselbe, und einmal davon an einer Stelle, an der
|
|
77
|
+
Claude Daten erwartet.
|
|
78
|
+
|
|
79
|
+
🔑 Warum HIER und nicht in `firma-daten.ts`: Dort waere es eine Kopie, die
|
|
80
|
+
beim naechsten Befehl fehlt, der einen Rumpf durchreicht. Wer den Hinweis
|
|
81
|
+
ENTGEGENNIMMT, nimmt ihn auch heraus -- dann kann kein Befehl ihn
|
|
82
|
+
versehentlich mit ausgeben, auch keiner, den es noch nicht gibt.
|
|
83
|
+
|
|
84
|
+
Das `delete` trifft ein frisch aus `json()` erzeugtes Objekt, das sonst
|
|
85
|
+
niemand haelt.
|
|
86
|
+
*/
|
|
87
|
+
if (koerper && typeof koerper === "object") {
|
|
88
|
+
delete koerper.hinweis;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function gemerkterHinweis() {
|
|
92
|
+
return letzterHinweis;
|
|
33
93
|
}
|
|
34
94
|
/** Holt ein Textfeld aus einer Antwort, ohne `any`. */
|
|
35
95
|
export function feld(koerper, name) {
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { feld, ruf } from "./api.js";
|
|
4
|
+
import { schlussFehler } from "./fehler.js";
|
|
5
|
+
/*
|
|
6
|
+
🔁 Die Ableitung „welcher Unterordner ist die Werkbank" wohnt seit dem
|
|
7
|
+
2026-08-20 in `werkbank-name.ts` -- gemeinsam mit der App, die sie fuer `/pro`
|
|
8
|
+
genauso braucht. Sie stand vorher hier UND in `src/lib/arbeitsplatz/ausliefern.ts`,
|
|
9
|
+
wortgleich. *Zwei Kopien einer Ableitung sind dieselbe zweite Wahrheit wie zwei
|
|
10
|
+
Kopien des Namens, nur besser getarnt.*
|
|
11
|
+
*/
|
|
12
|
+
export { werkbankAusDateien as werkbankOrdner } from "./werkbank-name.js";
|
|
13
|
+
/**
|
|
14
|
+
* Holt die Vorlage von der App — **eine** Route für beide Befehle.
|
|
15
|
+
*
|
|
16
|
+
* 🔑 `werkbank neu` bekommt bewusst KEINE eigene Route: Es sind dieselben
|
|
17
|
+
* Dateien, dieselbe Pro-Prüfung, derselbe Ausweis. Eine zweite Route wäre eine
|
|
18
|
+
* zweite Tür, die man einzeln absichern und einzeln nachmessen müsste — für
|
|
19
|
+
* exakt denselben Inhalt. Der Ausschnitt entsteht hier auf dem Rechner.
|
|
20
|
+
*/
|
|
21
|
+
export async function vorlageHolen() {
|
|
22
|
+
const antwort = await ruf("/api/cli/arbeitsplatz", { methode: "GET", mitAusweis: true });
|
|
23
|
+
if (antwort.status === 200) {
|
|
24
|
+
const daten = alsVorlageAntwort(antwort.koerper);
|
|
25
|
+
if (daten === null) {
|
|
26
|
+
console.error("Die Antwort war nicht lesbar. Bitte gleich noch einmal.");
|
|
27
|
+
return { ok: false, wert: 3 };
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, daten };
|
|
30
|
+
}
|
|
31
|
+
const meldung = feld(antwort.koerper, "meldung") ?? "Das hat nicht geklappt.";
|
|
32
|
+
// 402 VOR 401: Beide heissen "kommst hier nicht rein", verlangen aber
|
|
33
|
+
// Gegenteiliges. Wer sie zusammenwirft, raet `kimal3 login` -- und der
|
|
34
|
+
// meldet sich dann erfolgreich an, ohne dass es etwas aendert.
|
|
35
|
+
if (antwort.status === 402) {
|
|
36
|
+
console.error(meldung);
|
|
37
|
+
return { ok: false, wert: 4 };
|
|
38
|
+
}
|
|
39
|
+
if (antwort.status === 401) {
|
|
40
|
+
console.error(`${meldung} Bitte \`kimal3 login\` ausführen.`);
|
|
41
|
+
return { ok: false, wert: 2 };
|
|
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 };
|
|
49
|
+
}
|
|
50
|
+
/** Prueft die Antwort-Form, ohne `any` -- eine falsche Form ist unlesbar, keine Ausnahme. */
|
|
51
|
+
function alsVorlageAntwort(koerper) {
|
|
52
|
+
if (!koerper || typeof koerper !== "object")
|
|
53
|
+
return null;
|
|
54
|
+
const wurzel = koerper.wurzel;
|
|
55
|
+
const dateienRoh = koerper.dateien;
|
|
56
|
+
if (typeof wurzel !== "string" || wurzel.trim() === "")
|
|
57
|
+
return null;
|
|
58
|
+
if (!Array.isArray(dateienRoh))
|
|
59
|
+
return null;
|
|
60
|
+
const dateien = [];
|
|
61
|
+
for (const eintrag of dateienRoh) {
|
|
62
|
+
if (!eintrag || typeof eintrag !== "object")
|
|
63
|
+
return null;
|
|
64
|
+
const pfad = eintrag.pfad;
|
|
65
|
+
const inhalt = eintrag.inhalt;
|
|
66
|
+
if (typeof pfad !== "string" || pfad.trim() === "")
|
|
67
|
+
return null;
|
|
68
|
+
if (typeof inhalt !== "string")
|
|
69
|
+
return null;
|
|
70
|
+
dateien.push({ pfad, inhalt });
|
|
71
|
+
}
|
|
72
|
+
return { wurzel, dateien };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 🔴 **Nie ueberschreiben** -- die wichtigste Eigenschaft dieser Befehle.
|
|
76
|
+
* Angelegt wird nur, wenn am Zielort NICHTS liegt oder ein LEERER Ordner.
|
|
77
|
+
*
|
|
78
|
+
* Genauer, weil hier zwei verschiedene Absagen stecken:
|
|
79
|
+
* - Zielort ist eine **Datei** (auch eine leere): Abbruch. An einer Datei kann
|
|
80
|
+
* kein Ordner entstehen, ohne sie zu loeschen -- und geloescht wird nichts.
|
|
81
|
+
* - Zielort ist ein **Ordner mit Inhalt**: Abbruch.
|
|
82
|
+
* - Zielort ist ein **leerer Ordner** oder gibt es nicht: los geht's.
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ **Hier stand "eine leere Datei ... ist kein Hindernis"** (Review-Runde 4 zu
|
|
85
|
+
* PR #24) -- das war nie wahr. Wer `touch Mein-KI-Arbeitsplatz` gemacht hat oder
|
|
86
|
+
* wessen Editor eine leere Datei dieses Namens liegen liess, bekam den Abbruch,
|
|
87
|
+
* waehrend der Kommentar Durchlass versprach.
|
|
88
|
+
* *Die Prueffrage aus CLAUDE.md: Beschreibt das ein Verhalten, das jemand WILL --
|
|
89
|
+
* oder nur eines, das PASSIERT?* Hier wollte niemand das Beschriebene; der Code
|
|
90
|
+
* war richtig und der Satz darueber falsch.
|
|
91
|
+
*
|
|
92
|
+
* 🆕 **Neu am 2026-08-20:** `hinweis` — was die Absage **im Zweig „Ordner
|
|
93
|
+
* existiert und ist nicht leer"** zum Weitermachen raet. Nur dort: Seit
|
|
94
|
+
* Review-Runde 1 traegt er ein `cd` in den Zielordner, und in eine Datei oder
|
|
95
|
+
* einen unlesbaren Ordner kann man nicht hineinwechseln (Runde 2). Die beiden
|
|
96
|
+
* anderen Zweige haben ihren eigenen Satz.
|
|
97
|
+
* Der Rat stand dreimal fest getippt als `kimal3 arbeitsplatz neu <anderer-name>`
|
|
98
|
+
* im Text. Für `werkbank neu` wäre er falsch — und ein Rat, der in den falschen
|
|
99
|
+
* Befehl schickt, ist schlimmer als keiner.
|
|
100
|
+
*
|
|
101
|
+
* 🔴 **Und er trägt den GANZEN Rat, nicht nur den Befehl** (Messung am selben
|
|
102
|
+
* Tag, auf der Kundenstrecke). In der ersten Fassung war `hinweis` ein Befehl,
|
|
103
|
+
* und der Rahmensatz `Gib einen anderen Namen an:` gehörte dieser Datei. Nach
|
|
104
|
+
* der Umbenennung las sich das so:
|
|
105
|
+
*
|
|
106
|
+
* Gib einen anderen Namen an: kimal3 werkbank neu <dein-vorname>
|
|
107
|
+
*
|
|
108
|
+
* Einen anderen Namen kann man für den obersten Ordner seit der Umbenennung
|
|
109
|
+
* gerade **nicht** mehr geben — genau deshalb wurde der Rat ausgetauscht. *Wer
|
|
110
|
+
* nur das Ziel eines Satzes austauschbar macht, aber nicht seine Aussage, hat
|
|
111
|
+
* die halbe Wahrheit festgenagelt.*
|
|
112
|
+
*/
|
|
113
|
+
export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
114
|
+
// 🔴 B-7: eine leere Lieferung ist ein FEHLERFALL, kein Erfolg mit null
|
|
115
|
+
// Dateien -- sonst meldet der Befehl "✓ 0 Dateien angelegt." und legt dabei
|
|
116
|
+
// nicht einmal einen Ordner an. Derselbe Rueckgabewert wie bei anderen
|
|
117
|
+
// unlesbaren Antworten (3): der Nutzer hat hier nichts falsch gemacht.
|
|
118
|
+
if (dateien.length === 0) {
|
|
119
|
+
console.error("Die Antwort enthielt keine einzige Datei -- das ist kein gueltiges Ergebnis.");
|
|
120
|
+
console.error("Bitte gleich noch einmal versuchen.");
|
|
121
|
+
return 3;
|
|
122
|
+
}
|
|
123
|
+
const ziel = resolve(ordnerName);
|
|
124
|
+
/*
|
|
125
|
+
🔴 Diese drei Aufrufe brauchen ein eigenes Netz (Review-Runde 3). Sie lagen
|
|
126
|
+
ausserhalb jedes `try`: Ist der Zielordner vorhanden, aber nicht lesbar
|
|
127
|
+
(fremder Besitzer, gesperrte Netzfreigabe -- EACCES), flog `readdirSync`
|
|
128
|
+
bis in den Auffang-Block von `kimal3.ts` durch. Der druckt dann
|
|
129
|
+
„kimal3 ist gerade nicht erreichbar." und gibt 3 zurueck.
|
|
130
|
+
|
|
131
|
+
Das ist gleich doppelt falsch: Der Mensch sucht den Fehler beim Server,
|
|
132
|
+
obwohl er auf seiner eigenen Platte sitzt -- und 3 heisst fuer Claude laut
|
|
133
|
+
SKILL.md „gleich noch einmal versuchen", was hier nie helfen kann.
|
|
134
|
+
*Ein Fehlerfall darf nie denselben Namen tragen wie ein anderer.*
|
|
135
|
+
*/
|
|
136
|
+
try {
|
|
137
|
+
if (existsSync(ziel)) {
|
|
138
|
+
if (!statSync(ziel).isDirectory()) {
|
|
139
|
+
/*
|
|
140
|
+
🔴 **Hier stand der `hinweis`** (Review-Runde 2 zu PR #25). Seit Runde 1
|
|
141
|
+
enthaelt er ein `cd` in den Zielordner -- und in eine DATEI kann man
|
|
142
|
+
nicht hineinwechseln. Der Rat war in diesem Zweig unausfuehrbar.
|
|
143
|
+
*Ein Parameter, der zu einem Zweig passt, gehoert nicht in alle drei.*
|
|
144
|
+
*/
|
|
145
|
+
console.error(`„${ordnerName}" gibt es schon -- als DATEI, nicht als Ordner.`);
|
|
146
|
+
console.error("Benenn die Datei um oder geh in einen anderen Ordner.");
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
/*
|
|
150
|
+
⚠️ **Betriebssystem-Krimskrams zaehlt nicht als Inhalt** (Review-Runde 5).
|
|
151
|
+
`readdirSync` zaehlte vorher ALLES. Wer den Ordner angelegt und ihn nur
|
|
152
|
+
einmal im Finder (`.DS_Store`) oder Explorer (`desktop.ini`) geoeffnet
|
|
153
|
+
hat, bekam "gibt es schon und ist nicht leer" fuer einen Ordner, der fuer
|
|
154
|
+
ihn leer AUSSIEHT -- und keine Chance zu verstehen, warum. *Ein Grund, den
|
|
155
|
+
der Mensch nicht sehen kann, ist keine Begruendung, sondern eine Absage.*
|
|
156
|
+
|
|
157
|
+
Bewusst eine geschlossene Liste und kein "alles, was mit Punkt anfaengt":
|
|
158
|
+
Eine `.env` oder ein `.git` IST Inhalt, und den fassen wir nicht an.
|
|
159
|
+
*/
|
|
160
|
+
const OS_KRIMSKRAMS = [".DS_Store", "desktop.ini", "Thumbs.db"];
|
|
161
|
+
const echterInhalt = readdirSync(ziel).filter((n) => !OS_KRIMSKRAMS.includes(n));
|
|
162
|
+
if (echterInhalt.length > 0) {
|
|
163
|
+
console.error(`Der Ordner „${ordnerName}" gibt es schon und ist nicht leer.`);
|
|
164
|
+
console.error("Ich fasse ihn nicht an -- so geht nie versehentlich eigene Arbeit verloren.");
|
|
165
|
+
console.error(hinweis);
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (fehler) {
|
|
171
|
+
console.error(`Ich komme an den Ordner „${ordnerName}" nicht heran.`);
|
|
172
|
+
console.error(`Grund: ${fehler instanceof Error ? fehler.message : String(fehler)}`);
|
|
173
|
+
// ⚠️ Ebenfalls OHNE den `hinweis` (Runde 2): Wer den Ordner nicht lesen darf,
|
|
174
|
+
// kann auch nicht hineinwechseln. Hier hilft nur, was schon dasteht.
|
|
175
|
+
console.error("Das liegt an diesem Rechner, nicht am Server -- Rechte pruefen.");
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
// 🔴 B-6: ALLE Pfade pruefen, BEVOR die erste Datei geschrieben wird. Wuerde
|
|
179
|
+
// erst waehrend des Schreibens geprueft, koennte ein unsicherer Pfad mitten
|
|
180
|
+
// in der Liste einen halb beschriebenen Ordner hinterlassen -- der naechste
|
|
181
|
+
// Versuch liefe dann in die "nicht leer"-Absage oben, ohne dass der Nutzer
|
|
182
|
+
// je erfaehrt, dass der Ordner unvollstaendig ist.
|
|
183
|
+
for (const datei of dateien) {
|
|
184
|
+
if (istUnsichererPfad(datei.pfad)) {
|
|
185
|
+
console.error(`Die Antwort enthaelt einen unsicheren Pfad: „${datei.pfad}". Bitte melden.`);
|
|
186
|
+
return 3;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
for (const datei of dateien) {
|
|
191
|
+
const ort = join(ziel, ...datei.pfad.split("/"));
|
|
192
|
+
mkdirSync(dirname(ort), { recursive: true });
|
|
193
|
+
// 🔑 B-2-Folge: Platzhalter wie [DATUM] fuellt seit PR #24 der SERVER
|
|
194
|
+
// (`src/lib/arbeitsplatz/ausliefern.ts`), fuer beide Auslieferungswege
|
|
195
|
+
// gleichzeitig -- Download auf `/pro` UND diese CLI. Wuerde die CLI
|
|
196
|
+
// zusaetzlich selbst ersetzen, gaebe es wieder zwei Wahrheiten ueber
|
|
197
|
+
// denselben Inhalt (genau das war der urspruengliche Befund). Die CLI
|
|
198
|
+
// schreibt den Inhalt deshalb hier UNVERAENDERT auf die Platte.
|
|
199
|
+
writeFileSync(ort, datei.inhalt, "utf8");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (fehler) {
|
|
203
|
+
/*
|
|
204
|
+
🔴 **Hier stand `return 3` (Review-Runde 5 zu PR #24).** `3` heisst laut
|
|
205
|
+
`cli/skill/SKILL.md` "voruebergehende Stoerung -- gleich noch einmal
|
|
206
|
+
versuchen". Ein voller Datentraeger oder ein EACCES ist aber keine
|
|
207
|
+
Server-Stoerung, und der zweite Versuch kann gar nicht mehr gutgehen:
|
|
208
|
+
Der Zielordner EXISTIERT jetzt und ist nicht leer, also faellt der Lauf
|
|
209
|
+
oben in die Nicht-leer-Wache und endet mit `1` = "Eingabe passt nicht".
|
|
210
|
+
Claude sagt dem Menschen dann, sein Ordnername sei falsch -- und der
|
|
211
|
+
wahre Grund (Platte voll) kommt nie an.
|
|
212
|
+
|
|
213
|
+
Dieselbe Begruendung steht 40 Zeilen weiter oben schon einmal, beim
|
|
214
|
+
`readdirSync`-catch. *Ein Fehler, der einmal verstanden und
|
|
215
|
+
aufgeschrieben wurde, ist im Nachbar-catch trotzdem wieder neu.*
|
|
216
|
+
*/
|
|
217
|
+
console.error(`Schreiben fehlgeschlagen: ${fehler instanceof Error ? fehler.message : String(fehler)}`);
|
|
218
|
+
console.error(`Der Ordner „${ordnerName}" ist dadurch UNVOLLSTAENDIG -- er darf geloescht werden.`);
|
|
219
|
+
console.error("Das liegt an diesem Rechner, nicht am Server -- Platz und Rechte pruefen.");
|
|
220
|
+
return 1;
|
|
221
|
+
}
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Ein `..`-Segment oder ein absoluter Pfad duerften das Ziel verlassen.
|
|
226
|
+
*
|
|
227
|
+
* 🔴 B-8: Zerlegt an `/` UND `\` -- eine fehlerhafte oder manipulierte
|
|
228
|
+
* Server-Antwort koennte sonst mit `..\..\boese.md` durchkommen. `join()`
|
|
229
|
+
* trennt unter Windows auch am Backslash, dieser Schutz muss also beide
|
|
230
|
+
* Trenner kennen wie `join()` selbst.
|
|
231
|
+
*/
|
|
232
|
+
/**
|
|
233
|
+
* Taugt das als **Ordnername**? — schaerfer als `istUnsichererPfad`.
|
|
234
|
+
*
|
|
235
|
+
* 🔴 **Review-Runde 2 zu PR #25, und der Kommentar daneben war der Beweis.**
|
|
236
|
+
* `istUnsichererPfad` prueft einen PFAD: Es faengt `..` und absolute Pfade. Ein
|
|
237
|
+
* Ordner_name_ ist aber etwas Engeres — er ist **ein** Pfadteil. Und genau die
|
|
238
|
+
* beiden Werte, die als Pfadteil sinnlos sind, kommen durch:
|
|
239
|
+
*
|
|
240
|
+
* "" aus einer Antwort mit `/CLAUDE.md` -> resolve("") = das aktuelle Verzeichnis
|
|
241
|
+
* "." aus einer Antwort mit `./CLAUDE.md` -> resolve(".") = das aktuelle Verzeichnis
|
|
242
|
+
*
|
|
243
|
+
* In einem LEEREN Ordner haette `werkbank neu` die Dateien damit einfach dorthin
|
|
244
|
+
* geschrieben, wo der Mensch steht — und die Firmen-Wache daneben wird dabei
|
|
245
|
+
* still gruen, weil `firmenEbeneAus` genau diese Pfade ueberspringt.
|
|
246
|
+
*
|
|
247
|
+
* ⚠️ **In der Fassung von Runde 1 stand als Begruendung, `""` sei ungefaehrlich,
|
|
248
|
+
* „weil sonst die Wache oben schon rot waere".** Diese Wache ist in genau
|
|
249
|
+
* diesem Fall gruen. *Ein Kommentar, der eine Luecke mit einer anderen Wache
|
|
250
|
+
* begruendet, muss diese Wache im FEHLERFALL gelesen haben, nicht im Normalfall.*
|
|
251
|
+
*
|
|
252
|
+
* 🔑 Positiv formuliert statt negativ: Ein Ordnername ist EIN Teil, und dieser
|
|
253
|
+
* Teil ist weder leer noch `.` noch `..`. Eine Liste dessen, was verboten ist,
|
|
254
|
+
* waere beim naechsten Sonderfall wieder unvollstaendig.
|
|
255
|
+
*/
|
|
256
|
+
export function istUnsichererOrdnername(name) {
|
|
257
|
+
if (istUnsichererPfad(name))
|
|
258
|
+
return true;
|
|
259
|
+
const teile = name.split(/[\\/]/);
|
|
260
|
+
if (teile.length !== 1)
|
|
261
|
+
return true;
|
|
262
|
+
return teile[0] === "" || teile[0] === ".";
|
|
263
|
+
}
|
|
264
|
+
export function istUnsichererPfad(pfad) {
|
|
265
|
+
if (pfad.startsWith("/") || /^[a-zA-Z]:/.test(pfad))
|
|
266
|
+
return true;
|
|
267
|
+
return pfad.split(/[\\/]/).some((teil) => teil === "..");
|
|
268
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { dateienSchreiben, vorlageHolen, werkbankOrdner, istUnsichererOrdnername, } from "../arbeitsplatz-dateien.js";
|
|
3
|
+
import { werkbankOrdnerAus, werkbankUmbenennen } from "../werkbank-name.js";
|
|
4
|
+
/**
|
|
5
|
+
* Holt die KI-Arbeitsplatz-Vorlage vom Server und legt sie lokal an --
|
|
6
|
+
* `CLAUDE.md`, Takt-Befehle, die Werkbank fuer eigene Projekte.
|
|
7
|
+
*
|
|
8
|
+
* 🔑 **Warum dieser Befehl NICHTS interpretiert** (wie `firma daten`): Die
|
|
9
|
+
* Vorlage entsteht an EINER Stelle (`arbeitsplatz/vorlage/…` im Repo, gebaut
|
|
10
|
+
* zu `src/lib/arbeitsplatz/vorlage-daten.ts`) und wird von dort an ZWEI Wegen
|
|
11
|
+
* ausgeliefert -- Download auf `/pro` und hier ueber die CLI. Wuerde dieser
|
|
12
|
+
* Befehl die Struktur selbst zusammenbauen, gaebe es zwei Wahrheiten ueber
|
|
13
|
+
* denselben Ordner, und sie liefen beim naechsten Aendern der Vorlage
|
|
14
|
+
* auseinander.
|
|
15
|
+
*
|
|
16
|
+
* 📌 **Einmal pro FIRMA.** Er legt die ganze Ordnerfamilie an, also auch die
|
|
17
|
+
* Firmenebene (`kontext/` mit Marke, Systemen, Sicherheit). Wer nur eine
|
|
18
|
+
* weitere WERKBANK in einer bestehenden Firma braucht, nimmt
|
|
19
|
+
* `kimal3 werkbank neu` -- sonst entsteht eine zweite Firma im Bauch der
|
|
20
|
+
* ersten (gemessen am 2026-08-20, s. `befehle/werkbank.ts`).
|
|
21
|
+
*
|
|
22
|
+
* 🔁 Die Schreib-Maschinerie wohnt seit dem 2026-08-20 in
|
|
23
|
+
* `../arbeitsplatz-dateien.ts`, weil `werkbank neu` dieselbe braucht.
|
|
24
|
+
*/
|
|
25
|
+
const HILFE = `
|
|
26
|
+
kimal3 arbeitsplatz neu [dein-vorname]
|
|
27
|
+
|
|
28
|
+
Holt deine KI-Arbeitsplatz-Vorlage und legt sie hier als Ordner an --
|
|
29
|
+
die ganze Familie: Firmenebene, Werkbank, Beispielprojekt.
|
|
30
|
+
|
|
31
|
+
Der Ordner heisst IMMER "KI-Arbeitsplatz". Mehrere Firmen trennst du
|
|
32
|
+
ueber den Ordner darueber, z. B. Kunden/ARNO/KI-Arbeitsplatz.
|
|
33
|
+
|
|
34
|
+
dein-vorname Optional. Er benennt die WERKBANK darin -- aus "sabine"
|
|
35
|
+
wird "werkbank-sabine". Ohne Angabe steht dort der Name,
|
|
36
|
+
den der Server aus deiner Anmelde-Adresse gebildet hat.
|
|
37
|
+
|
|
38
|
+
🔴 Bricht ab, wenn der Zielordner schon existiert und NICHT leer ist --
|
|
39
|
+
eigene Arbeit wird dadurch nie versehentlich ueberschrieben.
|
|
40
|
+
|
|
41
|
+
Du hast schon einen Firmen-Ordner und brauchst nur eine eigene Werkbank?
|
|
42
|
+
kimal3 werkbank neu <dein-vorname>
|
|
43
|
+
`;
|
|
44
|
+
/*
|
|
45
|
+
🔴 **Der Rat in den Absagen hat sich am 2026-08-20 UMGEDREHT** -- und das ist
|
|
46
|
+
der Kern der Umbenennung, nicht ihr Nebeneffekt.
|
|
47
|
+
|
|
48
|
+
Vorher hiess er `kimal3 arbeitsplatz neu <anderer-name>`: "Der Ordner ist
|
|
49
|
+
belegt? Nimm einen anderen Namen." Genau diesen Rat hat Dennis am selben Tag
|
|
50
|
+
im Rollenspiel befolgt -- und bekam eine ZWEITE Firma im Bauch der ersten, mit
|
|
51
|
+
eigener `firma.md`, eigener `marke.md`, ohne jede Fehlermeldung. *Die Absage
|
|
52
|
+
schlug den Weg vor, gegen den `werkbank neu` gebaut werden musste.*
|
|
53
|
+
|
|
54
|
+
Jetzt heisst der oberste Ordner immer gleich. Damit KANN dieser Befehl keinen
|
|
55
|
+
zweiten Ordnernamen mehr vorschlagen -- und der einzige verbliebene Rat ist
|
|
56
|
+
der richtige.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* Was die Absage zum Weitermachen raet — **mit dem `cd`, das dazugehoert**.
|
|
60
|
+
*
|
|
61
|
+
* 🔴 **Review-Runde 1 zu PR #25, und es ist dieselbe Fehlerklasse, gegen die
|
|
62
|
+
* dieser PR gebaut wurde.** Hier stand nur der Befehl:
|
|
63
|
+
*
|
|
64
|
+
* Steht dein Arbeitsplatz schon und dir fehlt nur die eigene Werkbank?
|
|
65
|
+
* kimal3 werkbank neu <dein-vorname>
|
|
66
|
+
*
|
|
67
|
+
* Wer diese Absage liest, steht **ausserhalb** des Ordners — genau deshalb hat
|
|
68
|
+
* er sie ja bekommen („der Ordner gibt es schon"). Fuehrt er den Rat dort aus,
|
|
69
|
+
* findet `werkbank neu` kein `kontext/` daneben und bricht ab mit *„Hier steht
|
|
70
|
+
* kein Firmen-Ordner"*. Der Rat war richtig und der Ort falsch.
|
|
71
|
+
* *Ein Befehl ohne den Ort, an dem er laeuft, ist ein halber Rat.*
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ Der Ordnername wird ABGELEITET, nicht getippt: Er kommt als `ordnerName`
|
|
74
|
+
* herein (aus `daten.wurzel`, also vom Server). Stuende hier `cd KI-Arbeitsplatz`
|
|
75
|
+
* fest im Text, waere das eine zweite Wahrheit ueber den Aufbau — dieselbe, die
|
|
76
|
+
* Runde 3 zu PR #24 auf `/pro` gefunden hat.
|
|
77
|
+
*/
|
|
78
|
+
function hinweisFuer(ordnerName) {
|
|
79
|
+
const ziel = ordnerName.includes(" ") ? `"${ordnerName}"` : ordnerName;
|
|
80
|
+
return (" Steht dein Arbeitsplatz schon und dir fehlt nur die eigene Werkbank?\n" +
|
|
81
|
+
" Dann geh HINEIN und leg sie dort an:\n" +
|
|
82
|
+
` cd ${ziel}\n` +
|
|
83
|
+
" kimal3 werkbank neu <dein-vorname>");
|
|
84
|
+
}
|
|
85
|
+
export async function arbeitsplatzNeu(argv) {
|
|
86
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
87
|
+
console.log(HILFE);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
/*
|
|
91
|
+
Dieselben drei Absagen wie bei `werkbank neu` -- bewusst wortgleich: Wer den
|
|
92
|
+
einen Befehl kennt, soll beim anderen nichts Neues lernen muessen.
|
|
93
|
+
|
|
94
|
+
⚠️ Sie sprechen jetzt von einem NAMEN, nicht von einem Ordnernamen. Das ist
|
|
95
|
+
keine Kosmetik: Wer nach der alten Hilfe `kimal3 arbeitsplatz neu ARNO-KI`
|
|
96
|
+
tippt, bekommt keinen Fehler, sondern eine Werkbank namens
|
|
97
|
+
`werkbank-arno-ki`. Diese Meldungen und die Hilfe sind die einzigen Stellen,
|
|
98
|
+
an denen er das erfaehrt, bevor es passiert.
|
|
99
|
+
*/
|
|
100
|
+
const nameArg = argv[0];
|
|
101
|
+
if (nameArg !== undefined && nameArg.trim() === "") {
|
|
102
|
+
console.error(`Ein leerer Name ist kein gueltiger Name.\n${HILFE}`);
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
if (nameArg !== undefined && nameArg.startsWith("--")) {
|
|
106
|
+
console.error(`Die Option „${nameArg}" kenne ich nicht.\n${HILFE}`);
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
if (argv.length > 1) {
|
|
110
|
+
/*
|
|
111
|
+
🔴 **Der Vorschlag darf keine Optionen einsammeln** (Review-Runde 5 zu
|
|
112
|
+
PR #25). Hier stand `argv.join("-")` -- aus `arbeitsplatz neu sabine --force`
|
|
113
|
+
wurde damit der Rat `arbeitsplatz neu sabine---force`. Die `--`-Wache ein paar
|
|
114
|
+
Zeilen hoeher sieht nur `argv[0]`.
|
|
115
|
+
*Ein Rat, der ein falsches Kommando erzeugt, ist schlimmer als keiner* --
|
|
116
|
+
dieselbe Klasse wie die Absagen aus Runde 1 und 2, nur kleiner.
|
|
117
|
+
|
|
118
|
+
Deshalb: Sieht irgendein Wort wie eine Option aus, gibt es gar keinen
|
|
119
|
+
Vorschlag. Ein Mensch, der `--force` tippt, sucht keine Schreibhilfe fuer
|
|
120
|
+
seinen Vornamen.
|
|
121
|
+
*/
|
|
122
|
+
const woerter = argv.filter((w) => !w.startsWith("-"));
|
|
123
|
+
const vorschlag = woerter.length === argv.length && woerter.length > 1
|
|
124
|
+
? `Besteht dein Name aus mehreren Woertern? Dann schreib ihn zusammen:\n` +
|
|
125
|
+
` kimal3 arbeitsplatz neu ${woerter.join("-")}\n`
|
|
126
|
+
: "";
|
|
127
|
+
console.error(`Mit „${argv[1]}" kann ich hier nichts anfangen -- nur EIN Name ist erlaubt.\n` +
|
|
128
|
+
`${vorschlag}${HILFE}`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
/*
|
|
132
|
+
Der Name wird UMGESCHRIEBEN, nicht abgelehnt (s. `werkbankOrdnerAus`) --
|
|
133
|
+
"Sabine Mueller" ist ein gueltiger Name, nur kein gueltiger Ordner. `null`
|
|
134
|
+
heisst: Nach dem Umschreiben war nichts mehr uebrig (etwa bei "!!!"). Das ist
|
|
135
|
+
HIER ein Fehler, weil ein Mensch etwas getippt hat und eine Antwort verdient
|
|
136
|
+
-- der Server schweigt im selben Fall und bleibt beim Vordruck.
|
|
137
|
+
*/
|
|
138
|
+
let gewuenschteWerkbank = null;
|
|
139
|
+
if (nameArg !== undefined) {
|
|
140
|
+
gewuenschteWerkbank = werkbankOrdnerAus(nameArg);
|
|
141
|
+
if (gewuenschteWerkbank === null) {
|
|
142
|
+
console.error(`Aus „${nameArg}" kann ich keinen Ordnernamen machen -- es bleiben nur`);
|
|
143
|
+
console.error("Zeichen uebrig, die in keinem Ordnernamen stehen duerfen.");
|
|
144
|
+
console.error("");
|
|
145
|
+
console.error(" Nimm deinen Vornamen: kimal3 arbeitsplatz neu sabine");
|
|
146
|
+
return 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const holen = await vorlageHolen();
|
|
150
|
+
if (!holen.ok)
|
|
151
|
+
return holen.wert;
|
|
152
|
+
const daten = holen.daten;
|
|
153
|
+
// 🔴 R2-1: `daten.wurzel` kommt genauso ROH vom Server wie jeder `datei.pfad`
|
|
154
|
+
// -- dieselbe Gefahr (manipulierte Antwort, z. B. ueber ein umgebogenes
|
|
155
|
+
// `KIMAL3_URL`), dieselbe Bremse.
|
|
156
|
+
//
|
|
157
|
+
// ⚠️ **Hier stand: "Nur wenn KEIN eigener Ordnername uebergeben wurde, fliesst
|
|
158
|
+
// `daten.wurzel` ueberhaupt in den Zielpfad ein -- aber geprueft wird sie
|
|
159
|
+
// immer."** Das beschrieb die Welt vor dem 2026-08-20, in der ein Argument den
|
|
160
|
+
// Zielordner benennen konnte. Seit der Umbenennung heisst er IMMER wie
|
|
161
|
+
// `daten.wurzel`; die Pruefung ist damit nicht mehr die Vorsicht fuer einen
|
|
162
|
+
// selten benutzten Zweig, sondern der einzige Schutz, den dieser Wert hat.
|
|
163
|
+
// *Ein Kommentar, der die Wichtigkeit einer Pruefung kleinredet, ist gefaehr-
|
|
164
|
+
// licher als gar keiner -- er laedt zum Loeschen ein.*
|
|
165
|
+
// 🔴 `istUnsichererOrdnername`, nicht `istUnsichererPfad` (Review-Runde 2, als
|
|
166
|
+
// Zwilling des Befunds in `werkbank.ts` — dort gemeldet, hier selbst gesucht).
|
|
167
|
+
// `alsVorlageAntwort` faengt eine LEERE `wurzel` schon ab, aber nicht `"."`:
|
|
168
|
+
// `resolve(".")` ist das aktuelle Verzeichnis, und in einem leeren Ordner
|
|
169
|
+
// waere die ganze Familie einfach dorthin gefallen.
|
|
170
|
+
if (istUnsichererOrdnername(daten.wurzel)) {
|
|
171
|
+
console.error(`Die Antwort enthaelt einen unsicheren Ordnernamen: „${daten.wurzel}". Bitte melden.`);
|
|
172
|
+
return 3;
|
|
173
|
+
}
|
|
174
|
+
/*
|
|
175
|
+
🔑 Der Zielordner ist IMMER `daten.wurzel` ("KI-Arbeitsplatz") und kommt damit
|
|
176
|
+
vom Server statt aus der Eingabe. Die Pfad-Pruefung darueber ist deshalb
|
|
177
|
+
nicht mehr die Vorsicht fuer einen selten benutzten Zweig, sondern der
|
|
178
|
+
einzige Schutz, den dieser Wert ueberhaupt hat.
|
|
179
|
+
|
|
180
|
+
Der Name des Menschen wirkt eine Ebene tiefer, auf die Werkbank. Der Server
|
|
181
|
+
hat sie schon benannt (aus der Anmelde-Adresse); ein mitgegebener Name
|
|
182
|
+
sticht das.
|
|
183
|
+
*/
|
|
184
|
+
const werkbankVomServer = werkbankOrdner(daten.dateien);
|
|
185
|
+
/*
|
|
186
|
+
🔴 **Review-Runde 2 zu PR #25: ein getippter Name darf nicht still verfallen.**
|
|
187
|
+
Hier stand `gewuenschteWerkbank !== null && werkbankVomServer !== null ? …
|
|
188
|
+
: daten.dateien` — enthielt die Lieferung keine Werkbank, bekam der Mensch
|
|
189
|
+
den Vordruck-Namen UND eine Erfolgsmeldung, obwohl er ausdruecklich `sabine`
|
|
190
|
+
getippt hatte.
|
|
191
|
+
|
|
192
|
+
Die Schwesterdatei behandelt dieselbe Lage laengst als Fehler („In der
|
|
193
|
+
Vorlage steckt kein Werkbank-Ordner … Bitte melden", Rueckgabewert 3).
|
|
194
|
+
*Zwei Wege, dieselbe Wirklichkeit, verschiedene Antworten* — genau die
|
|
195
|
+
Asymmetrie, die dieser PR an drei anderen Stellen schon geschlossen hat.
|
|
196
|
+
*/
|
|
197
|
+
/*
|
|
198
|
+
🔴 **Beide Zweige enden gleich** (Review-Runde 5 zu PR #25). Runde 2 hat die
|
|
199
|
+
Asymmetrie zur Schwesterdatei nur fuer den Fall geschlossen, in dem ein Name
|
|
200
|
+
GETIPPT wurde; ohne Namen wurde weiter eine Warnung gedruckt und dann mit
|
|
201
|
+
`0` erfolgreich beendet.
|
|
202
|
+
|
|
203
|
+
Warum das schlimmer ist, als es klingt: Der Weg-Hinweis am Ende faellt dann
|
|
204
|
+
auf den Wurzelordner zurueck und schickt den Menschen mit `cd
|
|
205
|
+
KI-Arbeitsplatz` genau dorthin, wo die ausgelieferte Firmen-`CLAUDE.md`
|
|
206
|
+
ausdruecklich sagt *„Du arbeitest nie hier oben"*. Eine Lieferung ohne
|
|
207
|
+
Werkbank ist kein Erfolg mit Randnotiz -- sie ist kaputt.
|
|
208
|
+
|
|
209
|
+
⚠️ Heute unerreichbar (`check:arbeitsplatz` Pruefung 10 erzwingt genau eine
|
|
210
|
+
Werkbank). Das ist ein Grund, ruhig zu bleiben, kein Grund, zwei Antworten
|
|
211
|
+
auf dieselbe Wirklichkeit zu geben.
|
|
212
|
+
*/
|
|
213
|
+
if (werkbankVomServer === null) {
|
|
214
|
+
console.error("In der Vorlage steckt kein Werkbank-Ordner -- damit fehlt der Teil,");
|
|
215
|
+
console.error("in dem du arbeiten wuerdest. Das liegt nicht an dir. Bitte melden.");
|
|
216
|
+
return 3;
|
|
217
|
+
}
|
|
218
|
+
const dateien = gewuenschteWerkbank !== null
|
|
219
|
+
? werkbankUmbenennen(daten.dateien, werkbankVomServer, gewuenschteWerkbank)
|
|
220
|
+
: daten.dateien;
|
|
221
|
+
const ordnerName = daten.wurzel;
|
|
222
|
+
const wert = dateienSchreiben(dateien, ordnerName, hinweisFuer(ordnerName));
|
|
223
|
+
if (wert !== 0)
|
|
224
|
+
return wert;
|
|
225
|
+
// ⚠️ Der ABSOLUTE Pfad, nicht der eingegebene Name. Beim Umzug der
|
|
226
|
+
// Schreib-Maschinerie (2026-08-20) war das kurz ein relativer Name -- ein
|
|
227
|
+
// Rueckschritt: Der Mensch soll sehen, WO der Ordner gelandet ist, und genau
|
|
228
|
+
// das ist der Grund, warum hier `resolve()` steht und nicht `ordnerName`.
|
|
229
|
+
console.log(`✓ ${dateien.length} ${dateien.length === 1 ? "Datei" : "Dateien"} angelegt.`);
|
|
230
|
+
console.log(` ${resolve(ordnerName)}`);
|
|
231
|
+
/*
|
|
232
|
+
🔴 **Der Erfolgsfall muss sagen, was das Argument getan hat** (Review-Runde 5
|
|
233
|
+
zu PR #25). Das Argument hat am 2026-08-20 still seine Bedeutung geaendert:
|
|
234
|
+
In `0.1.5` -- veroeffentlicht, in Gebrauch -- war es der ORDNERNAME. Wer nach
|
|
235
|
+
der alten Hilfe `kimal3 arbeitsplatz neu ARNO-KI` tippt, bekommt heute keinen
|
|
236
|
+
Fehler, sondern einen Ordner `KI-Arbeitsplatz/` mit einer Werkbank namens
|
|
237
|
+
`werkbank-arno-ki`.
|
|
238
|
+
|
|
239
|
+
⚠️ Im Code stand als Trost, die Absagen und die Hilfe seien „die einzigen
|
|
240
|
+
Stellen, an denen er das erfaehrt" -- aber die Absagen feuern nur im
|
|
241
|
+
FEHLERFALL. Auf dem Erfolgsweg erfuhr er gar nichts.
|
|
242
|
+
*Eine Bedeutung, die sich still aendert, muss sich laut melden -- und zwar
|
|
243
|
+
dort, wo es geklappt hat, nicht nur dort, wo es schiefging.*
|
|
244
|
+
*/
|
|
245
|
+
if (gewuenschteWerkbank !== null) {
|
|
246
|
+
console.log(` Deine Werkbank darin heisst „${gewuenschteWerkbank}".`);
|
|
247
|
+
}
|
|
248
|
+
console.log("");
|
|
249
|
+
console.log(" Weiter geht's:");
|
|
250
|
+
// ⚠️ NACH dem Umbenennen ableiten, nicht davor -- sonst zeigte der Weg-Hinweis
|
|
251
|
+
// in die Werkbank, die der Server geschickt hat, statt in die, die auf der
|
|
252
|
+
// Platte liegt. *Eine Ableitung, die eine Zeile zu frueh gelesen wird, fuehrt
|
|
253
|
+
// zuverlaessig ins Leere.*
|
|
254
|
+
const werkbank = werkbankOrdner(dateien);
|
|
255
|
+
const cdZiel = werkbank !== null ? `${ordnerName}/${werkbank}` : ordnerName;
|
|
256
|
+
console.log(` cd ${cdZiel.includes(" ") ? `"${cdZiel}"` : cdZiel}`);
|
|
257
|
+
console.log(" claude");
|
|
258
|
+
console.log(' dann "Lass uns einrichten" tippen');
|
|
259
|
+
return 0;
|
|
260
|
+
}
|