kimal3 0.1.5 β 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/dist/api.js +138 -1
- package/dist/arbeitsplatz-dateien.js +268 -0
- package/dist/befehle/arbeitsplatz.js +209 -233
- package/dist/befehle/kontext-fragen.js +165 -0
- package/dist/befehle/kontext-gemeinsam.js +171 -0
- package/dist/befehle/kontext-hoch.js +203 -0
- package/dist/befehle/kontext-runter.js +254 -0
- package/dist/befehle/skill-install.js +110 -4
- package/dist/befehle/werkbank.js +389 -0
- package/dist/befehle-katalog.js +98 -3
- package/dist/fragen-lesen.js +178 -0
- package/dist/gesehen.js +85 -0
- package/dist/kimal3.js +178 -13
- package/dist/kontext-dateien.js +248 -0
- package/dist/kontext-stand.js +616 -0
- package/dist/nachricht.js +249 -0
- package/dist/version.js +37 -0
- package/dist/werkbank-name.js +217 -0
- package/package.json +1 -1
- package/skill/SKILL.md +130 -16
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { istUnsichererPfad } from "./arbeitsplatz-dateien.js";
|
|
4
|
+
import { MERKZETTEL_PFAD, VERWALTETE_DATEIEN, arbeitsplatzAus, leseMerkzettel, schreibeMerkzettel, sicherungsZiel, } from "./kontext-stand.js";
|
|
5
|
+
/**
|
|
6
|
+
* Das Dateisystem hinter `kimal3 kontext runter` -- Ordner finden, die
|
|
7
|
+
* verwalteten Dateien lesen, sichern, schreiben, den Merkzettel lesen und
|
|
8
|
+
* schreiben.
|
|
9
|
+
*
|
|
10
|
+
* π΄ **Ruling T8-1: Die reine Fundregel `arbeitsplatzAus` wird hier NICHT noch
|
|
11
|
+
* einmal geschrieben** -- sie steht seit Task 1 in `kontext-stand.ts` und wird
|
|
12
|
+
* hier nur VERBRAUCHT (`findeArbeitsplatz` unten fuellt sie mit echten
|
|
13
|
+
* Kandidaten vom Dateisystem). Zwei Fassungen derselben Fundregel waeren
|
|
14
|
+
* dieselbe zweite Wahrheit, die dieses Projekt schon bei `werkbankAusDateien`
|
|
15
|
+
* teuer bezahlt hat.
|
|
16
|
+
*
|
|
17
|
+
* β οΈ **Dieselben Netze wie in `arbeitsplatz-dateien.ts`**, aus denselben,
|
|
18
|
+
* teuer bezahlten Gruenden: `existsSync`/`statSync`/`readFileSync` gehoeren
|
|
19
|
+
* in ein EIGENES `try` -- ein `EACCES` fluege sonst bis in den Auffang-Block
|
|
20
|
+
* von `kimal3.ts`, der "kimal3 ist gerade nicht erreichbar" druckt und `3`
|
|
21
|
+
* zurueckgibt. Der Mensch suchte den Fehler dann beim Server, obwohl er auf
|
|
22
|
+
* seiner eigenen Platte sitzt -- und `3` heisst "gleich noch einmal", was
|
|
23
|
+
* hier nie helfen kann. Ein Schreibfehler (Platte voll, `EACCES`) gibt
|
|
24
|
+
* deshalb `1` zurueck, nicht `3`.
|
|
25
|
+
*/
|
|
26
|
+
/** Ist `pfad` (relativ zu `wurzel`) ein lesbarer ORDNER? Unlesbar zaehlt als "nein". */
|
|
27
|
+
function istLesbarerOrdner(pfad) {
|
|
28
|
+
try {
|
|
29
|
+
return existsSync(pfad) && statSync(pfad).isDirectory();
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Ist `pfad` (relativ zu `wurzel`) eine lesbare DATEI? Unlesbar zaehlt als "nein". */
|
|
36
|
+
function istLesbareDatei(pfad) {
|
|
37
|
+
try {
|
|
38
|
+
return existsSync(pfad) && statSync(pfad).isFile();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Von `start` aufwaerts nach dem Arbeitsplatz suchen -- die Kandidaten kommen
|
|
46
|
+
* von UNTEN NACH OBEN, `start` zuerst.
|
|
47
|
+
*
|
|
48
|
+
* π Diese Funktion FUELLT nur die Kandidatenliste vom Dateisystem; die
|
|
49
|
+
* eigentliche Entscheidung ("welcher Ordner gewinnt") trifft `arbeitsplatzAus`
|
|
50
|
+
* in `kontext-stand.ts` (Ruling P-4/T8-1).
|
|
51
|
+
*
|
|
52
|
+
* Die Schleife hat eine Sicherheitsgrenze (200 Ebenen): `dirname()` konvergiert
|
|
53
|
+
* auf jedem realen Dateisystem zur Wurzel, aber ein Netzlaufwerk mit einem
|
|
54
|
+
* kaputten Elternverweis soll trotzdem nie eine Endlosschleife erzeugen.
|
|
55
|
+
*/
|
|
56
|
+
export function findeArbeitsplatz(start) {
|
|
57
|
+
const kandidaten = [];
|
|
58
|
+
let aktuell = resolve(start);
|
|
59
|
+
for (let i = 0; i < 200; i++) {
|
|
60
|
+
kandidaten.push({
|
|
61
|
+
pfad: aktuell,
|
|
62
|
+
hatKontext: istLesbarerOrdner(join(aktuell, "kontext")),
|
|
63
|
+
hatClaude: istLesbareDatei(join(aktuell, "CLAUDE.md")),
|
|
64
|
+
});
|
|
65
|
+
const eltern = dirname(aktuell);
|
|
66
|
+
if (eltern === aktuell)
|
|
67
|
+
break;
|
|
68
|
+
aktuell = eltern;
|
|
69
|
+
}
|
|
70
|
+
return arbeitsplatzAus(kandidaten);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Eine der verwalteten Dateien lesen -- fehlend wird zu `inhalt: null`,
|
|
74
|
+
* NICHT zu einem Fehler (ein frisch geklonter Ordner hat oft noch keine).
|
|
75
|
+
*/
|
|
76
|
+
export function leseEineDatei(ordner, pfad) {
|
|
77
|
+
const ort = join(ordner, ...pfad.split("/"));
|
|
78
|
+
try {
|
|
79
|
+
if (!existsSync(ort))
|
|
80
|
+
return { ok: true, inhalt: null };
|
|
81
|
+
if (!statSync(ort).isFile()) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
meldung: `β${pfad}" existiert, ist aber keine Datei -- ich fasse das nicht an. Bitte pruefen.`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { ok: true, inhalt: readFileSync(ort, "utf8") };
|
|
88
|
+
}
|
|
89
|
+
catch (fehler) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
meldung: `Ich komme an β${pfad}" nicht heran: ${fehler instanceof Error ? fehler.message : String(fehler)}. ` +
|
|
93
|
+
"Das liegt an diesem Rechner, nicht am Server -- Rechte pruefen.",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Den lokalen Merkzettel lesen. Unlesbar oder nicht vorhanden -> leeres
|
|
99
|
+
* Objekt, GENAU wie `leseMerkzettel(null)` -- der sichere Fall aus Β§7b: "wer
|
|
100
|
+
* nichts weiss, loescht nichts". Eine unlesbare Datei fuehrt also zur
|
|
101
|
+
* Sicherung vor dem Ueberschreiben, nie zum stillen Verlust.
|
|
102
|
+
*/
|
|
103
|
+
export function leseMerkzettelDatei(ordner) {
|
|
104
|
+
try {
|
|
105
|
+
const ort = join(ordner, MERKZETTEL_PFAD);
|
|
106
|
+
const roh = existsSync(ort) ? readFileSync(ort, "utf8") : null;
|
|
107
|
+
return leseMerkzettel(roh);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return leseMerkzettel(null);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Den Merkzettel schreiben -- MIT dem Stand, der zum ausgelieferten Inhalt passt. */
|
|
114
|
+
export function schreibeMerkzettelDatei(ordner, m) {
|
|
115
|
+
try {
|
|
116
|
+
const ort = join(ordner, MERKZETTEL_PFAD);
|
|
117
|
+
writeFileSync(ort, schreibeMerkzettel(m), "utf8");
|
|
118
|
+
return { ok: true };
|
|
119
|
+
}
|
|
120
|
+
catch (fehler) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
meldung: `Der Merkzettel liess sich nicht schreiben: ${fehler instanceof Error ? fehler.message : String(fehler)}. Das liegt an diesem Rechner, nicht am Server -- Platz und Rechte pruefen.`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Der naechste NICHT belegte Sicherungspfad -- reine Rechnung, kein
|
|
129
|
+
* Dateisystem. `istBelegt(pfad)` sagt, ob dieser Pfad schon existiert (der
|
|
130
|
+
* Aufrufer entscheidet, WIE das geprueft wird); diese Funktion zaehlt vom
|
|
131
|
+
* Basisnamen aus hoch, bis eine freie Stelle gefunden ist.
|
|
132
|
+
*
|
|
133
|
+
* π΄ **Ruling T8-2 (Review-Fund 1, Fix-Runde 1).** Vorher gab `sichereDatei`
|
|
134
|
+
* bei einem belegten Ziel `{ uebersprungen: true }` zurueck und schrieb dabei
|
|
135
|
+
* GAR NICHTS -- eine zweite Aenderung am selben Tag verschwand beim
|
|
136
|
+
* anschliessenden Ueberschreiben spurlos, waehrend die Ausgabe im Befehl
|
|
137
|
+
* "gesichert" behauptete. **Diese Funktion darf deshalb NIE einen belegten
|
|
138
|
+
* Pfad als Endergebnis zurueckgeben und NIE einen Pfad, der schon existiert,
|
|
139
|
+
* unkommentiert ueberschreiben lassen** -- sie liefert IMMER einen Pfad, der
|
|
140
|
+
* (laut `istBelegt`) frei ist.
|
|
141
|
+
*
|
|
142
|
+
* β οΈ **Die erste Sicherung eines Tages -- der Basisname -- bleibt fuer immer
|
|
143
|
+
* unangetastet.** Das ist US-7 woertlich: "Laeuft der Befehl am selben Tag
|
|
144
|
+
* ein zweites Mal, wird die erste Sicherung NICHT ueberschrieben." Wer diese
|
|
145
|
+
* Funktion vereinfacht ("dann eben doch den Basisnamen nehmen, wenn `nummer`
|
|
146
|
+
* gross wird") loescht genau die Sicherung, die US-7 ausdruecklich schuetzt.
|
|
147
|
+
* Eine dritte, vierte, ... Aenderung bekommt stattdessen einen weiteren,
|
|
148
|
+
* eigenen Namen daneben -- nie einen schon vergebenen.
|
|
149
|
+
*/
|
|
150
|
+
export function naechstesFreiesSicherungsziel(basisZielRel, istBelegt) {
|
|
151
|
+
if (!istBelegt(basisZielRel))
|
|
152
|
+
return basisZielRel;
|
|
153
|
+
for (let nummer = 2; nummer <= 1000; nummer++) {
|
|
154
|
+
const kandidat = nummeriertesZiel(basisZielRel, nummer);
|
|
155
|
+
if (!istBelegt(kandidat))
|
|
156
|
+
return kandidat;
|
|
157
|
+
}
|
|
158
|
+
/*
|
|
159
|
+
Praktisch unerreichbar (1000 Sicherungen derselben Datei am selben Tag).
|
|
160
|
+
Diese reine Funktion wirft nicht -- sie liefert den letzten geprueften
|
|
161
|
+
Namen, und der Aufrufer (`sichereDatei`) trifft beim tatsaechlichen
|
|
162
|
+
Schreiben erneut auf `existsSync` und meldet dort einen echten Fehler,
|
|
163
|
+
statt dass eine reine Rechnung selbst eine Ausnahme auslΓΆst.
|
|
164
|
+
*/
|
|
165
|
+
return nummeriertesZiel(basisZielRel, 1000);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* `kontext/vorher-2026-08-21/firma.md` + 2 -> `.../firma-2.md`.
|
|
169
|
+
*
|
|
170
|
+
* Nur der DATEINAME bekommt die Nummer, nicht der Ordner -- alle
|
|
171
|
+
* verwalteten Dateien landen weiter FLACH im selben `vorher-<datum>/`
|
|
172
|
+
* (unveraendert gegenueber `sicherungsZiel`).
|
|
173
|
+
*/
|
|
174
|
+
function nummeriertesZiel(basisZielRel, nummer) {
|
|
175
|
+
const teile = basisZielRel.split("/");
|
|
176
|
+
const dateiname = teile.pop() ?? "";
|
|
177
|
+
const punkt = dateiname.lastIndexOf(".");
|
|
178
|
+
const name = punkt === -1 ? dateiname : dateiname.slice(0, punkt);
|
|
179
|
+
const endung = punkt === -1 ? "" : dateiname.slice(punkt);
|
|
180
|
+
return [...teile, `${name}-${nummer}${endung}`].join("/");
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Die lokale Fassung von `pfad` nach `kontext/vorher-<datum>/` sichern --
|
|
184
|
+
* FLACH, mit dem Basisnamen (`sicherungsZiel`), oder -- ist der schon belegt
|
|
185
|
+
* -- unter einem durchnummerierten Namen DANEBEN (`naechstesFreiesSicherungsziel`,
|
|
186
|
+
* Ruling T8-2).
|
|
187
|
+
*
|
|
188
|
+
* π΄ **Es wird NIE uebersprungen und NIE ueberschrieben.** Beides waere eine
|
|
189
|
+
* stille Datenvernichtung: Ueberspringen ohne Kopie loescht die zweite
|
|
190
|
+
* Aenderung eines Tages beim anschliessenden Ueberschreiben der Arbeitsdatei;
|
|
191
|
+
* Ueberschreiben der ersten Sicherung verstoesst woertlich gegen US-7.
|
|
192
|
+
* `nebenVorhandener: true` im Ergebnis sagt dem Aufrufer, dass HEUTE schon
|
|
193
|
+
* eine andere Sicherung derselben Datei existiert -- er muss das in der
|
|
194
|
+
* Ausgabe unterscheiden (Β§8: kein Fehlerfall traegt den Namen eines
|
|
195
|
+
* Erfolgsfalls, und hier sind es zwei verschiedene ECHTE Erfolgsfaelle).
|
|
196
|
+
*/
|
|
197
|
+
export function sichereDatei(ordner, pfad, inhalt, datum) {
|
|
198
|
+
const basisZielRel = sicherungsZiel(datum, pfad);
|
|
199
|
+
const istBelegt = (zielRel) => existsSync(join(ordner, ...zielRel.split("/")));
|
|
200
|
+
const zielRel = naechstesFreiesSicherungsziel(basisZielRel, istBelegt);
|
|
201
|
+
const ort = join(ordner, ...zielRel.split("/"));
|
|
202
|
+
try {
|
|
203
|
+
mkdirSync(dirname(ort), { recursive: true });
|
|
204
|
+
writeFileSync(ort, inhalt, "utf8");
|
|
205
|
+
return { ok: true, ziel: zielRel, nebenVorhandener: zielRel !== basisZielRel };
|
|
206
|
+
}
|
|
207
|
+
catch (fehler) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
meldung: `Sichern von β${pfad}" ist fehlgeschlagen: ${fehler instanceof Error ? fehler.message : String(fehler)}. Das liegt an diesem Rechner, nicht am Server -- Platz und Rechte pruefen.`,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Eine der verwalteten Dateien schreiben.
|
|
216
|
+
*
|
|
217
|
+
* π΄ **Positivliste, keine Verbotsliste** (Auflage aus dem Brief): `pfad` muss
|
|
218
|
+
* WOERTLICH einer der Eintraege aus `VERWALTETE_DATEIEN` sein, sonst wird
|
|
219
|
+
* NICHTS geschrieben. Diese Funktion bekommt `pfad` im echten Ablauf immer aus
|
|
220
|
+
* der eigenen Iteration ueber `VERWALTETE_DATEIEN` (nie roh aus der Server-
|
|
221
|
+
* Antwort) -- die Pruefung hier ist trotzdem ausdruecklich verlangt und bleibt
|
|
222
|
+
* als zweites Netz stehen, falls sich der Aufrufer einmal aendert.
|
|
223
|
+
*
|
|
224
|
+
* β οΈ `istUnsichererPfad` zusaetzlich, aus demselben Grund wie in
|
|
225
|
+
* `dateienSchreiben` (`arbeitsplatz-dateien.ts`): Ein Pfad kommt letztlich aus
|
|
226
|
+
* einer Server-Antwort, und eine Pruefung, die nur dort gilt, wo man sie
|
|
227
|
+
* gerade braucht, ist keine Pruefung dieser Funktion.
|
|
228
|
+
*/
|
|
229
|
+
export function schreibeVerwalteteDatei(ordner, pfad, inhalt) {
|
|
230
|
+
if (!VERWALTETE_DATEIEN.includes(pfad)) {
|
|
231
|
+
return { ok: false, meldung: `β${pfad}" ist keine verwaltete Datei -- wird nicht geschrieben. Bitte melden.` };
|
|
232
|
+
}
|
|
233
|
+
if (istUnsichererPfad(pfad)) {
|
|
234
|
+
return { ok: false, meldung: `β${pfad}" ist kein sicherer Pfad -- wird nicht geschrieben. Bitte melden.` };
|
|
235
|
+
}
|
|
236
|
+
const ort = join(ordner, ...pfad.split("/"));
|
|
237
|
+
try {
|
|
238
|
+
mkdirSync(dirname(ort), { recursive: true });
|
|
239
|
+
writeFileSync(ort, inhalt, "utf8");
|
|
240
|
+
return { ok: true };
|
|
241
|
+
}
|
|
242
|
+
catch (fehler) {
|
|
243
|
+
return {
|
|
244
|
+
ok: false,
|
|
245
|
+
meldung: `Schreiben von β${pfad}" ist fehlgeschlagen: ${fehler instanceof Error ? fehler.message : String(fehler)}. Das liegt an diesem Rechner, nicht am Server -- Platz und Rechte pruefen.`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|