kimal3 0.2.10 → 0.3.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/README.md +137 -2
- package/dist/api.js +16 -7
- package/dist/arbeitsplatz-dateien.js +23 -19
- package/dist/argumente.js +25 -97
- package/dist/befehle/arbeitsplatz.js +50 -68
- package/dist/befehle/firma-daten.js +24 -26
- package/dist/befehle/kontext-fragen.js +57 -47
- package/dist/befehle/kontext-gemeinsam.js +84 -40
- package/dist/befehle/kontext-hoch.js +40 -46
- package/dist/befehle/kontext-runter.js +70 -51
- package/dist/befehle/login.js +23 -9
- package/dist/befehle/logout.js +9 -4
- package/dist/befehle/projekt-liste.js +24 -29
- package/dist/befehle/projekt-neu.js +25 -31
- package/dist/befehle/skill-install.js +78 -49
- package/dist/befehle/skill-tausch.js +28 -0
- package/dist/befehle/wache.js +12 -6
- package/dist/befehle/werkbank.js +143 -104
- package/dist/befehle-katalog-en.js +185 -0
- package/dist/befehle-katalog.js +11 -50
- package/dist/befehlswoerter.js +69 -0
- package/dist/einstellungen-mischen.js +2 -2
- package/dist/einstellungen-schreiben.js +8 -5
- package/dist/fehler.js +5 -5
- package/dist/firmen-liste.js +8 -5
- package/dist/format.js +41 -0
- package/dist/fragen-lesen.js +3 -3
- package/dist/hilfe.js +24 -0
- package/dist/kimal3.js +77 -71
- package/dist/kontext-dateien.js +30 -23
- package/dist/kontext-hoch-zeile.js +5 -3
- package/dist/kontext-rat.js +151 -0
- package/dist/kontext-stand.js +79 -210
- package/dist/nachricht.js +39 -15
- package/dist/projekt-neu-firma.js +9 -9
- package/dist/projekt-zeilen.js +40 -54
- package/dist/slash-befehle.js +36 -0
- package/dist/sprache.js +114 -0
- package/dist/texte/allgemein.js +99 -0
- package/dist/texte/arbeitsplatz.js +135 -0
- package/dist/texte/kontext-hoch.js +128 -0
- package/dist/texte/kontext.js +171 -0
- package/dist/texte/projekte.js +126 -0
- package/dist/texte/skill.js +87 -0
- package/dist/texte/wache.js +36 -0
- package/dist/texte/werkbank.js +161 -0
- package/dist/wache-rechnung.js +27 -40
- package/dist/werkbank-name.js +28 -10
- package/dist/wort.js +98 -0
- package/package.json +3 -3
- package/skill/SKILL.md +9 -9
- package/skill/commands/k3.md +7 -4
- package/skill/commands/k3feierabend.md +99 -97
- package/skill/commands/k3kontext.md +1 -1
- package/skill/commands/k3kurs.md +6 -5
- package/skill/commands/k3start.md +2 -1
- package/skill/en/SKILL.md +451 -0
- package/skill/en/commands/k3.md +106 -0
- package/skill/en/commands/k3analyse.md +59 -0
- package/skill/en/commands/k3context.md +92 -0
- package/skill/en/commands/k3course.md +54 -0
- package/skill/en/commands/k3list.md +26 -0
- package/skill/en/commands/k3project.md +50 -0
- package/skill/en/commands/k3start.md +47 -0
- package/skill/en/commands/k3wrapup.md +99 -0
- package/dist/zz-kaputt.js +0 -1
package/dist/kimal3.js
CHANGED
|
@@ -16,10 +16,14 @@ import { BASIS_URL, leseAnmeldung } from "./anmeldung.js";
|
|
|
16
16
|
import { gemerkterHinweis, lokalerKontextStandJetzt, NichtAngemeldet } from "./api.js";
|
|
17
17
|
import { leseGesehen, merkeGesehen } from "./gesehen.js";
|
|
18
18
|
import { ausfallMeldung, hinweisAnhaengen } from "./wache-rechnung.js";
|
|
19
|
+
import { WACHE_TEXTE } from "./texte/wache.js";
|
|
19
20
|
import { leseWacheDatei, sitzungJetzt, wacheAusgefallen } from "./wache-schalter.js";
|
|
20
|
-
import { formatiere, heuteAus,
|
|
21
|
+
import { formatiere, heuteAus, skillZustand, sollZeigen, } from "./nachricht.js";
|
|
21
22
|
import { eigeneVersion } from "./version.js";
|
|
22
|
-
import {
|
|
23
|
+
import { sprache } from "./sprache.js";
|
|
24
|
+
import { ALLGEMEIN_TEXTE } from "./texte/allgemein.js";
|
|
25
|
+
import { hilfeText } from "./hilfe.js";
|
|
26
|
+
import { erkenneBefehl } from "./befehlswoerter.js";
|
|
23
27
|
import { arbeitsplatzNeu } from "./befehle/arbeitsplatz.js";
|
|
24
28
|
import { werkbankNeu } from "./befehle/werkbank.js";
|
|
25
29
|
import { firmaDaten } from "./befehle/firma-daten.js";
|
|
@@ -135,7 +139,9 @@ function gegenUnbehandelteKanalfehlerAbsichern(kanal, meldenAuf) {
|
|
|
135
139
|
return;
|
|
136
140
|
schonGemeldet = true;
|
|
137
141
|
try {
|
|
138
|
-
|
|
142
|
+
// 🌐 Teil 3: die Sprache wird erst JETZT gelesen (zur Schreibzeit); der Empfaenger selbst
|
|
143
|
+
// bleibt auf Modulebene vor der ersten Zeile angemeldet.
|
|
144
|
+
meldenAuf.write(ALLGEMEIN_TEXTE[sprache()].outputChannelError(fehler.message));
|
|
139
145
|
}
|
|
140
146
|
catch {
|
|
141
147
|
// Auch der Fehlerkanal kann kaputt sein -- dann bleibt wenigstens der
|
|
@@ -149,7 +155,6 @@ gegenUnbehandelteKanalfehlerAbsichern(process.stderr, null);
|
|
|
149
155
|
// `befehle-katalog.ts` und wird von dort AUCH im Pro-Bereich der App angezeigt.
|
|
150
156
|
// Zwei Listen ueber dieselben Befehle driften — die eine bekaeme den naechsten
|
|
151
157
|
// Befehl, die andere nicht, und keiner der beiden Orte wuesste davon.
|
|
152
|
-
const HILFE = hilfeText();
|
|
153
158
|
/**
|
|
154
159
|
* Hilfe plus Zustand.
|
|
155
160
|
*
|
|
@@ -189,8 +194,9 @@ function versionsZeilen() {
|
|
|
189
194
|
// Zeile zeigt einem Menschen etwas an, und da ist "unbekannt" die ehrliche
|
|
190
195
|
// Antwort. Der andere Aufrufer RECHNET mit dem Wert und braucht dort das
|
|
191
196
|
// Gegenteil -- er will lieber gar keine Nummer als eine erfundene.
|
|
192
|
-
const
|
|
193
|
-
|
|
197
|
+
const t = ALLGEMEIN_TEXTE[sprache()];
|
|
198
|
+
const version = eigeneVersion() ?? t.versionUnknown;
|
|
199
|
+
return t.versionLines(version, BASIS_URL);
|
|
194
200
|
}
|
|
195
201
|
/**
|
|
196
202
|
* Welcher Update-Hinweis soll ans Ende -- oder keiner?
|
|
@@ -254,12 +260,12 @@ function hinweisText() {
|
|
|
254
260
|
// verrottet lautlos (Befund der vierten Review-Runde).
|
|
255
261
|
const datei = skillDatei();
|
|
256
262
|
const inhalt = existsSync(datei) ? readFileSync(datei, "utf8") : null;
|
|
257
|
-
if (skillZustand(inhalt, eigene) === "veraltet") {
|
|
263
|
+
if (skillZustand(inhalt, eigene, sprache()) === "veraltet") {
|
|
258
264
|
// Auch dieser Hinweis nur einmal am Tag, unter einer festen Kennung.
|
|
259
265
|
// Sie kann mit keiner Nachrichten-UUID kollidieren.
|
|
260
266
|
const gesehen = leseGesehen();
|
|
261
267
|
if (gesehen["skill-veraltet"] !== heute) {
|
|
262
|
-
return { text:
|
|
268
|
+
return { text: WACHE_TEXTE[sprache()].skillOutdated, kennung: "skill-veraltet", tag: heute };
|
|
263
269
|
}
|
|
264
270
|
}
|
|
265
271
|
/*
|
|
@@ -281,7 +287,7 @@ function hinweisText() {
|
|
|
281
287
|
// Auch dieser Hinweis hoechstens einmal am Tag, unter fester Kennung.
|
|
282
288
|
const gesehen = leseGesehen();
|
|
283
289
|
if (gesehen["wache-ausgefallen"] !== heute) {
|
|
284
|
-
return { text: ausfallMeldung(), kennung: "wache-ausgefallen", tag: heute };
|
|
290
|
+
return { text: ausfallMeldung(sprache()), kennung: "wache-ausgefallen", tag: heute };
|
|
285
291
|
}
|
|
286
292
|
}
|
|
287
293
|
}
|
|
@@ -463,17 +469,61 @@ function endeMitHinweis(code) {
|
|
|
463
469
|
* nicht mehr Auskunft, sondern **weniger Behauptung**.
|
|
464
470
|
*/
|
|
465
471
|
function zustandsZeile() {
|
|
472
|
+
const t = ALLGEMEIN_TEXTE[sprache()];
|
|
466
473
|
const a = leseAnmeldung();
|
|
467
|
-
return a
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
474
|
+
return a ? t.credentialsStored(a.email) : t.notSignedIn;
|
|
475
|
+
}
|
|
476
|
+
/** `null` = der Rest passt nicht zu diesem Befehl -> "kenne ich nicht", wie bisher. */
|
|
477
|
+
async function fuehreAus(name, rest) {
|
|
478
|
+
const allein = rest.length === 0;
|
|
479
|
+
switch (name) {
|
|
480
|
+
case "login": return allein ? login() : null;
|
|
481
|
+
case "logout": return allein ? logout() : null;
|
|
482
|
+
case "skill install": return allein ? skillInstall() : null;
|
|
483
|
+
case "projekt neu": return projektNeu(rest);
|
|
484
|
+
case "projekt liste": return allein ? projektListe() : null;
|
|
485
|
+
// ⚠️ Rest wird durchgereicht, nicht auf Gleichheit geprueft: `firma daten --firma X` gehoert dazu.
|
|
486
|
+
case "firma daten": return firmaDaten(rest);
|
|
487
|
+
case "arbeitsplatz neu": return arbeitsplatzNeu(rest);
|
|
488
|
+
case "werkbank neu": return werkbankNeu(rest);
|
|
489
|
+
case "kontext runter": return kontextRunter(rest);
|
|
490
|
+
case "kontext hoch": return kontextHoch(rest);
|
|
491
|
+
case "kontext fragen": return kontextFragen(rest);
|
|
492
|
+
case "wache an": return allein ? wacheSchalten(true) : null;
|
|
493
|
+
case "wache aus": return allein ? wacheSchalten(false) : null;
|
|
494
|
+
case "wache":
|
|
495
|
+
// 🔴 Der Startbefehl aus `~/.claude/settings.json`, den `skill install` eintraegt (T3-5:
|
|
496
|
+
// eingetragen wird immer `kimal3 wache --hook`; `kimal3 watch --hook` laeuft genauso).
|
|
497
|
+
// Er verpackt die Meldung anders -- Begruendung an `wache()`.
|
|
498
|
+
// ⚠️ Ein Mensch tippt das nie; der Katalog nennt ihn deshalb auch nicht.
|
|
499
|
+
if (rest.length === 1 && rest[0] === "--hook")
|
|
500
|
+
return wache(true);
|
|
501
|
+
/*
|
|
502
|
+
🔴 NACHSICHT GEGENUEBER FLAGGEN, DIE ES HIER NOCH NICHT GIBT.
|
|
503
|
+
Der Eintrag in `~/.claude/settings.json` ueberlebt jede Neuinstallation --
|
|
504
|
+
auch eine, die eine AELTERE Fassung bringt. Steht dort eines Tages
|
|
505
|
+
`wache --irgendwas`, das diese Fassung nicht kennt, faellt der Aufruf ohne
|
|
506
|
+
diese Zeile in den Unbekannt-Zweig: die volle Hilfe bei JEDEM Claude-Start,
|
|
507
|
+
Rueckgabewert 1, und `merkeSitzung` wird nie erreicht -- woraufhin
|
|
508
|
+
`wacheAusgefallen` bei jedem weiteren Befehl einen Rat gibt, der nichts
|
|
509
|
+
behebt. Aus einem fehlenden Zusatz wird so ein lauter Dauerschaden.
|
|
510
|
+
🔑 Warum die MENSCHEN-Form die richtige Rueckfallebene ist: Sie endet
|
|
511
|
+
normal mit 0, ihre Meldung kommt also an. Die JSON-Form waere fuer einen
|
|
512
|
+
Menschen, der aus Neugier eine Flagge tippt, nur Kauderwelsch.
|
|
513
|
+
⚠️ Fuer bereits veroeffentlichte Fassungen ist das nicht mehr zu retten --
|
|
514
|
+
`0.2.3` kennt `--hook` nicht. Diese Zeile sorgt dafuer, dass die naechste
|
|
515
|
+
Flagge die Falle nicht erneut stellt. (Befund der Durchsicht, 2026-09-09.)
|
|
516
|
+
*/
|
|
517
|
+
if (rest.every((a) => a.startsWith("--")))
|
|
518
|
+
return wache(false);
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
471
521
|
}
|
|
472
522
|
async function haupt() {
|
|
473
523
|
const argv = process.argv.slice(2);
|
|
474
524
|
const wort = argv.join(" ");
|
|
475
525
|
if (argv.length === 0 || wort === "--help" || wort === "-h") {
|
|
476
|
-
console.log(
|
|
526
|
+
console.log(hilfeText(sprache()));
|
|
477
527
|
console.log(zustandsZeile() + "\n");
|
|
478
528
|
return 0;
|
|
479
529
|
}
|
|
@@ -482,61 +532,17 @@ async function haupt() {
|
|
|
482
532
|
console.log(zustandsZeile() + "\n");
|
|
483
533
|
return 0;
|
|
484
534
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (argv[0] === "firma" && argv[1] === "daten")
|
|
497
|
-
return firmaDaten(argv.slice(2));
|
|
498
|
-
if (wort === "skill install")
|
|
499
|
-
return skillInstall();
|
|
500
|
-
if (argv[0] === "arbeitsplatz" && argv[1] === "neu")
|
|
501
|
-
return arbeitsplatzNeu(argv.slice(2));
|
|
502
|
-
if (argv[0] === "werkbank" && argv[1] === "neu")
|
|
503
|
-
return werkbankNeu(argv.slice(2));
|
|
504
|
-
if (argv[0] === "kontext" && argv[1] === "runter")
|
|
505
|
-
return kontextRunter(argv.slice(2));
|
|
506
|
-
if (argv[0] === "kontext" && argv[1] === "hoch")
|
|
507
|
-
return kontextHoch(argv.slice(2));
|
|
508
|
-
if (argv[0] === "kontext" && argv[1] === "fragen")
|
|
509
|
-
return kontextFragen(argv.slice(2));
|
|
510
|
-
if (wort === "wache")
|
|
511
|
-
return wache(false);
|
|
512
|
-
// 🔴 Der Startbefehl aus `~/.claude/settings.json`, den `skill install`
|
|
513
|
-
// eintraegt. Er verpackt die Meldung anders -- Begruendung an `wache()`.
|
|
514
|
-
// ⚠️ Ein Mensch tippt das nie; der Katalog nennt ihn deshalb auch nicht.
|
|
515
|
-
if (wort === "wache --hook")
|
|
516
|
-
return wache(true);
|
|
517
|
-
if (wort === "wache aus")
|
|
518
|
-
return wacheSchalten(false);
|
|
519
|
-
if (wort === "wache an")
|
|
520
|
-
return wacheSchalten(true);
|
|
521
|
-
/*
|
|
522
|
-
🔴 NACHSICHT GEGENUEBER FLAGGEN, DIE ES HIER NOCH NICHT GIBT.
|
|
523
|
-
Der Eintrag in `~/.claude/settings.json` ueberlebt jede Neuinstallation --
|
|
524
|
-
auch eine, die eine AELTERE Fassung bringt. Steht dort eines Tages
|
|
525
|
-
`wache --irgendwas`, das diese Fassung nicht kennt, faellt der Aufruf ohne
|
|
526
|
-
diese Zeile in den Unbekannt-Zweig: die volle Hilfe bei JEDEM Claude-Start,
|
|
527
|
-
Rueckgabewert 1, und `merkeSitzung` wird nie erreicht -- woraufhin
|
|
528
|
-
`wacheAusgefallen` bei jedem weiteren Befehl einen Rat gibt, der nichts
|
|
529
|
-
behebt. Aus einem fehlenden Zusatz wird so ein lauter Dauerschaden.
|
|
530
|
-
🔑 Warum die MENSCHEN-Form die richtige Rueckfallebene ist: Sie endet
|
|
531
|
-
normal mit 0, ihre Meldung kommt also an. Die JSON-Form waere fuer einen
|
|
532
|
-
Menschen, der aus Neugier eine Flagge tippt, nur Kauderwelsch.
|
|
533
|
-
⚠️ Fuer bereits veroeffentlichte Fassungen ist das nicht mehr zu retten --
|
|
534
|
-
`0.2.3` kennt `--hook` nicht. Diese Zeile sorgt dafuer, dass die naechste
|
|
535
|
-
Flagge die Falle nicht erneut stellt. (Befund der Durchsicht, 2026-09-09.)
|
|
536
|
-
*/
|
|
537
|
-
if (argv[0] === "wache" && argv.slice(1).every((a) => a.startsWith("--")))
|
|
538
|
-
return wache(false);
|
|
539
|
-
console.error(`Den Befehl „${wort}" kenne ich nicht.\n${HILFE}`);
|
|
535
|
+
// 🌐 Teil 3 (T3-3): ein Verteiler fuer beide Sprachen. Was heute nur ALLEIN gilt
|
|
536
|
+
// (`login`, `projekt liste`, `wache an` …), gilt auch weiterhin nur allein -- `kimal3 login
|
|
537
|
+
// extra` bleibt unbekannt. Der grosse Kommentar zur Nachsicht bei `wache --irgendwas` steht
|
|
538
|
+
// unten an `fuehreAus` und gilt unveraendert.
|
|
539
|
+
const erkannt = erkenneBefehl(argv);
|
|
540
|
+
if (erkannt) {
|
|
541
|
+
const code = await fuehreAus(erkannt.name, erkannt.rest);
|
|
542
|
+
if (code !== null)
|
|
543
|
+
return code;
|
|
544
|
+
}
|
|
545
|
+
console.error(`${ALLGEMEIN_TEXTE[sprache()].unknownCommand(wort)}${hilfeText(sprache())}`);
|
|
540
546
|
return 1;
|
|
541
547
|
}
|
|
542
548
|
haupt()
|
|
@@ -564,10 +570,10 @@ haupt()
|
|
|
564
570
|
// Node laeuft aus, sobald die Kanaele leer sind.
|
|
565
571
|
.catch((f) => {
|
|
566
572
|
if (f instanceof NichtAngemeldet) {
|
|
567
|
-
console.error(
|
|
573
|
+
console.error(ALLGEMEIN_TEXTE[sprache()].signInFirst);
|
|
568
574
|
process.exitCode = 2;
|
|
569
575
|
return;
|
|
570
576
|
}
|
|
571
|
-
console.error(
|
|
577
|
+
console.error(ALLGEMEIN_TEXTE[sprache()].unreachable);
|
|
572
578
|
process.exitCode = 3;
|
|
573
579
|
});
|
package/dist/kontext-dateien.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { istUnsichererPfad } from "./arbeitsplatz-dateien.js";
|
|
4
|
-
import {
|
|
4
|
+
import { sprache as menschSprache } from "./sprache.js";
|
|
5
|
+
import { KONTEXT_TEXTE } from "./texte/kontext.js";
|
|
6
|
+
import { MERKZETTEL_PFAD, verwalteteDateien, arbeitsplatzAus, ORDNER_NAMEN, leseMerkzettel, schreibeMerkzettel, sicherungsZiel, } from "./kontext-stand.js";
|
|
5
7
|
/**
|
|
6
8
|
* Das Dateisystem hinter `kimal3 kontext runter` -- Ordner finden, die
|
|
7
9
|
* verwalteten Dateien lesen, sichern, schreiben, den Merkzettel lesen und
|
|
@@ -23,13 +25,17 @@ import { MERKZETTEL_PFAD, VERWALTETE_DATEIEN, arbeitsplatzAus, leseMerkzettel, s
|
|
|
23
25
|
* hier nie helfen kann. Ein Schreibfehler (Platte voll, `EACCES`) gibt
|
|
24
26
|
* deshalb `1` zurueck, nicht `3`.
|
|
25
27
|
*/
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Die Namen der Eintraege (ohne Unterordner) in `pfad`. Fehlt der Ordner oder
|
|
30
|
+
* ist er unlesbar -> leere Liste: Ein Ordner, in den ich nicht hineinsehe,
|
|
31
|
+
* zaehlt wie einer ohne unsere Dateien (`arbeitsplatzAus`, Final-Review Fund 1).
|
|
32
|
+
*/
|
|
33
|
+
function namenIn(pfad) {
|
|
28
34
|
try {
|
|
29
|
-
return
|
|
35
|
+
return readdirSync(pfad, { withFileTypes: true }).filter((e) => !e.isDirectory()).map((e) => e.name);
|
|
30
36
|
}
|
|
31
37
|
catch {
|
|
32
|
-
return
|
|
38
|
+
return [];
|
|
33
39
|
}
|
|
34
40
|
}
|
|
35
41
|
/** Ist `pfad` (relativ zu `wurzel`) eine lesbare DATEI? Unlesbar zaehlt als "nein". */
|
|
@@ -59,8 +65,9 @@ export function findeArbeitsplatz(start) {
|
|
|
59
65
|
for (let i = 0; i < 200; i++) {
|
|
60
66
|
kandidaten.push({
|
|
61
67
|
pfad: aktuell,
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
kontextNamen: namenIn(join(aktuell, ORDNER_NAMEN.de.kontext)),
|
|
69
|
+
contextNamen: namenIn(join(aktuell, ORDNER_NAMEN.en.kontext)),
|
|
70
|
+
hatClaude: istLesbareDatei(join(aktuell, "CLAUDE.md")), // kein-anzeigetext: Dateiname
|
|
64
71
|
});
|
|
65
72
|
const eltern = dirname(aktuell);
|
|
66
73
|
if (eltern === aktuell)
|
|
@@ -81,7 +88,7 @@ export function leseEineDatei(ordner, pfad) {
|
|
|
81
88
|
if (!statSync(ort).isFile()) {
|
|
82
89
|
return {
|
|
83
90
|
ok: false,
|
|
84
|
-
meldung:
|
|
91
|
+
meldung: KONTEXT_TEXTE[menschSprache()].notAFile(pfad),
|
|
85
92
|
};
|
|
86
93
|
}
|
|
87
94
|
return { ok: true, inhalt: readFileSync(ort, "utf8") };
|
|
@@ -89,8 +96,7 @@ export function leseEineDatei(ordner, pfad) {
|
|
|
89
96
|
catch (fehler) {
|
|
90
97
|
return {
|
|
91
98
|
ok: false,
|
|
92
|
-
meldung:
|
|
93
|
-
"Das liegt an diesem Rechner, nicht am Server -- Rechte pruefen.",
|
|
99
|
+
meldung: KONTEXT_TEXTE[menschSprache()].cannotReach(pfad, fehler instanceof Error ? fehler.message : String(fehler)),
|
|
94
100
|
};
|
|
95
101
|
}
|
|
96
102
|
}
|
|
@@ -120,7 +126,7 @@ export function schreibeMerkzettelDatei(ordner, m) {
|
|
|
120
126
|
catch (fehler) {
|
|
121
127
|
return {
|
|
122
128
|
ok: false,
|
|
123
|
-
meldung:
|
|
129
|
+
meldung: KONTEXT_TEXTE[menschSprache()].trackingFailed(fehler instanceof Error ? fehler.message : String(fehler)),
|
|
124
130
|
};
|
|
125
131
|
}
|
|
126
132
|
}
|
|
@@ -180,7 +186,8 @@ function nummeriertesZiel(basisZielRel, nummer) {
|
|
|
180
186
|
return [...teile, `${name}-${nummer}${endung}`].join("/");
|
|
181
187
|
}
|
|
182
188
|
/**
|
|
183
|
-
* Die lokale Fassung von `pfad` nach `kontext/vorher-<datum>/`
|
|
189
|
+
* Die lokale Fassung von `pfad` nach `kontext/vorher-<datum>/` (englisch
|
|
190
|
+
* `context/before-<datum>/`) sichern --
|
|
184
191
|
* FLACH, mit dem Basisnamen (`sicherungsZiel`), oder -- ist der schon belegt
|
|
185
192
|
* -- unter einem durchnummerierten Namen DANEBEN (`naechstesFreiesSicherungsziel`,
|
|
186
193
|
* Ruling T8-2).
|
|
@@ -194,8 +201,8 @@ function nummeriertesZiel(basisZielRel, nummer) {
|
|
|
194
201
|
* Ausgabe unterscheiden (§8: kein Fehlerfall traegt den Namen eines
|
|
195
202
|
* Erfolgsfalls, und hier sind es zwei verschiedene ECHTE Erfolgsfaelle).
|
|
196
203
|
*/
|
|
197
|
-
export function sichereDatei(ordner, pfad, inhalt, datum) {
|
|
198
|
-
const basisZielRel = sicherungsZiel(datum, pfad);
|
|
204
|
+
export function sichereDatei(ordner, pfad, inhalt, datum, sprache) {
|
|
205
|
+
const basisZielRel = sicherungsZiel(sprache, datum, pfad);
|
|
199
206
|
const istBelegt = (zielRel) => existsSync(join(ordner, ...zielRel.split("/")));
|
|
200
207
|
const zielRel = naechstesFreiesSicherungsziel(basisZielRel, istBelegt);
|
|
201
208
|
const ort = join(ordner, ...zielRel.split("/"));
|
|
@@ -207,7 +214,7 @@ export function sichereDatei(ordner, pfad, inhalt, datum) {
|
|
|
207
214
|
catch (fehler) {
|
|
208
215
|
return {
|
|
209
216
|
ok: false,
|
|
210
|
-
meldung:
|
|
217
|
+
meldung: KONTEXT_TEXTE[menschSprache()].backupFailed(pfad, fehler instanceof Error ? fehler.message : String(fehler)),
|
|
211
218
|
};
|
|
212
219
|
}
|
|
213
220
|
}
|
|
@@ -215,9 +222,9 @@ export function sichereDatei(ordner, pfad, inhalt, datum) {
|
|
|
215
222
|
* Eine der verwalteten Dateien schreiben.
|
|
216
223
|
*
|
|
217
224
|
* 🔴 **Positivliste, keine Verbotsliste** (Auflage aus dem Brief): `pfad` muss
|
|
218
|
-
* WOERTLICH einer der Eintraege aus `
|
|
225
|
+
* WOERTLICH einer der Eintraege aus `verwalteteDateien(sprache)` sein, sonst wird
|
|
219
226
|
* NICHTS geschrieben. Diese Funktion bekommt `pfad` im echten Ablauf immer aus
|
|
220
|
-
* der eigenen Iteration ueber `
|
|
227
|
+
* der eigenen Iteration ueber `verwalteteDateien(sprache)` (nie roh aus der Server-
|
|
221
228
|
* Antwort) -- die Pruefung hier ist trotzdem ausdruecklich verlangt und bleibt
|
|
222
229
|
* als zweites Netz stehen, falls sich der Aufrufer einmal aendert.
|
|
223
230
|
*
|
|
@@ -226,12 +233,12 @@ export function sichereDatei(ordner, pfad, inhalt, datum) {
|
|
|
226
233
|
* einer Server-Antwort, und eine Pruefung, die nur dort gilt, wo man sie
|
|
227
234
|
* gerade braucht, ist keine Pruefung dieser Funktion.
|
|
228
235
|
*/
|
|
229
|
-
export function schreibeVerwalteteDatei(ordner, pfad, inhalt) {
|
|
230
|
-
if (!
|
|
231
|
-
return { ok: false, meldung:
|
|
236
|
+
export function schreibeVerwalteteDatei(ordner, pfad, inhalt, sprache) {
|
|
237
|
+
if (!verwalteteDateien(sprache).includes(pfad)) {
|
|
238
|
+
return { ok: false, meldung: KONTEXT_TEXTE[menschSprache()].notManaged(pfad) };
|
|
232
239
|
}
|
|
233
240
|
if (istUnsichererPfad(pfad)) {
|
|
234
|
-
return { ok: false, meldung:
|
|
241
|
+
return { ok: false, meldung: KONTEXT_TEXTE[menschSprache()].notSafePath(pfad) };
|
|
235
242
|
}
|
|
236
243
|
const ort = join(ordner, ...pfad.split("/"));
|
|
237
244
|
try {
|
|
@@ -242,7 +249,7 @@ export function schreibeVerwalteteDatei(ordner, pfad, inhalt) {
|
|
|
242
249
|
catch (fehler) {
|
|
243
250
|
return {
|
|
244
251
|
ok: false,
|
|
245
|
-
meldung:
|
|
252
|
+
meldung: KONTEXT_TEXTE[menschSprache()].writeFailed(pfad, fehler instanceof Error ? fehler.message : String(fehler)),
|
|
246
253
|
};
|
|
247
254
|
}
|
|
248
255
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { KONTEXT_HOCH_TEXTE } from "./texte/kontext-hoch.js";
|
|
1
2
|
/**
|
|
2
3
|
* Die Erfolgszeile von `kimal3 kontext hoch` -- als reine Rechnung, damit
|
|
3
4
|
* `check:cli` sie prueft. Sie nennt, was sich BEI DER FIRMA geaendert hat,
|
|
4
5
|
* nicht, was gesendet wurde (B-e, 29.08.: fuenf Dateien genannt, zwei
|
|
5
6
|
* geaendert, eine davon ohne Zutun des Menschen).
|
|
6
7
|
*/
|
|
7
|
-
export function hochZeile(uebernommen) {
|
|
8
|
+
export function hochZeile(uebernommen, s) {
|
|
9
|
+
const t = KONTEXT_HOCH_TEXTE[s];
|
|
8
10
|
if (uebernommen.length === 0)
|
|
9
|
-
return
|
|
10
|
-
return
|
|
11
|
+
return t.nothingChanged;
|
|
12
|
+
return t.changedAtCompany(uebernommen.join(", "));
|
|
11
13
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { befehlIn } from "./befehlswoerter.js";
|
|
2
|
+
import { KONTEXT_TEXTE } from "./texte/kontext.js";
|
|
3
|
+
import { FIRMEN_CLAUDE_MD, ORDNER_NAMEN, firmenTexte, verwalteteDateien } from "./kontext-stand.js";
|
|
4
|
+
/*
|
|
5
|
+
Der Rat nach `kimal3 kontext runter`, wenn Dateien in Ruhe gelassen wurden (Teil 3, Task 10).
|
|
6
|
+
|
|
7
|
+
🔑 **Umgezogen aus `kontext-stand.ts`.** Diese Datei hat die Sätze des Rats verlassen -- sie
|
|
8
|
+
gehören jetzt in `texte/kontext.ts`, in beiden Sprachen --, und `kontext-stand.ts` darf nichts
|
|
9
|
+
importieren (die App liest sie über den Bundler). Was die Logik angeht, ist alles geblieben:
|
|
10
|
+
welche Art Rat eine Datei bekommt, in welcher Reihenfolge, und dass jede verwaltete Datei einen
|
|
11
|
+
bekommt. Die Kommentare darunter stammen unverändert von dort.
|
|
12
|
+
|
|
13
|
+
🔴 **ZWEI Sprachen, zwei Pflichtwerte** (Messung h): `ordner` bestimmt die PFADE im Rat
|
|
14
|
+
(`context/profile.md` heißt so, weil der Ordner so heißt), `sprache` bestimmt die SÄTZE und die
|
|
15
|
+
Befehlswörter darin. Bis Teil 3 war der Rat immer deutsch -- im englischen Ordner mit englischen
|
|
16
|
+
Pfaden --, weil das Programm nur Deutsch sprach; die Sprache des Menschen kennt er erst seit
|
|
17
|
+
jetzt. Beide Werte sind Pflicht, kein Vorgabewert: Wer nur einen nennt, bekommt die zweite
|
|
18
|
+
Sprache nicht still dazu.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Die ARTEN von Rat, die es gibt -- **in der Reihenfolge, in der sie im
|
|
22
|
+
* Terminal stehen.**
|
|
23
|
+
*
|
|
24
|
+
* 🔑 Diese Liste ist zugleich die Quelle des Typs {@link RatArt}. Wer eine
|
|
25
|
+
* dritte Art erfindet, muss sie hier eintragen -- und bekommt danach vom
|
|
26
|
+
* Compiler gesagt, dass {@link RAT_TEXT} noch keinen Text dafuer hat. *Eine
|
|
27
|
+
* Reihenfolge, die nebenher den Typ traegt, kann nicht unvollstaendig sein.*
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ Die Reihenfolge ist nicht Geschmack: `kimal3 kontext runter` druckt seit
|
|
30
|
+
* Befund F-3 erst den Rat fuer die Firmen-Texte und darunter den fuer unsere
|
|
31
|
+
* Vorlage. Wer sie dreht, dreht die Ausgabe beim Kunden.
|
|
32
|
+
*/
|
|
33
|
+
const RAT_REIHENFOLGE = ["hochschickbar", "nicht-hochschickbar"];
|
|
34
|
+
/**
|
|
35
|
+
* 🔴 **PUNKT A-7 (2026-08-26): DIESE TABELLE IST DIE ABSICHERUNG, NICHT NUR
|
|
36
|
+
* EINE AUFRAEUMAKTION.**
|
|
37
|
+
*
|
|
38
|
+
* Bis hierher zaehlte {@link ratNachUnberuehrt} ihre Faelle auf: ein `if` fuer
|
|
39
|
+
* „irgendetwas aus den Firmen-Texten" und ein zweites fuer den woertlichen
|
|
40
|
+
* Dateinamen `"CLAUDE.md"`. {@link verwalteteDateien} hat aber SECHS
|
|
41
|
+
* Eintraege, und eine kuenftige SIEBTE waere durch beide `if`s gefallen --
|
|
42
|
+
* ohne Fehler, ohne Meldung, ohne Rat. *Der Mensch haette gelesen, dass seine
|
|
43
|
+
* Datei in Ruhe gelassen wurde, und nirgends, was er jetzt tun soll.*
|
|
44
|
+
*
|
|
45
|
+
* 🔑 **Teil 2 (2026-10-01): aus der Tabelle `RAT_ART_JE_DATEI` wurde diese
|
|
46
|
+
* Funktion.** Die Tabelle hatte als Schluessel die sechs deutschen Pfade --
|
|
47
|
+
* mit `context/company.md` daneben haette sie zwei Saetze Schluessel
|
|
48
|
+
* gebraucht, die nur gemeinsam stimmen. Die Regel dahinter war immer nur eine:
|
|
49
|
+
* Die Firmen-`CLAUDE.md` traegt `kontext hoch` nicht, alles andere schon.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ **Was dabei verloren ging, ehrlich:** Der `Record` war eine Absicherung
|
|
52
|
+
* des Compilers -- eine siebte verwaltete Datei haette ihm einen Schluessel
|
|
53
|
+
* zu wenig gegeben. Jetzt bekommt eine siebte Datei still den Rat
|
|
54
|
+
* `hochschickbar`. Das ist nie „kein Rat" (der Fall, gegen den A-7 gebaut
|
|
55
|
+
* war), aber es waere der falsche, wenn die siebte Datei ebenfalls nicht
|
|
56
|
+
* hochgeschickt werden kann. Halten tut das jetzt allein die Pruefung in
|
|
57
|
+
* `check:kontext` unten.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ **Sie sagt dasselbe wie {@link firmenTexte}, und das ist eine
|
|
60
|
+
* Doppelung** -- gehalten wird sie von einer Pruefung in `check:kontext`
|
|
61
|
+
* („der `hoch`-Rat geht an genau die Dateien, die `kontext hoch` wirklich
|
|
62
|
+
* traegt"). Die konnte vor A-7 gar nicht rot werden, weil der Rat SELBST aus
|
|
63
|
+
* der Liste der Firmen-Texte entstand; jetzt sind es zwei Quellen, und ihr
|
|
64
|
+
* Auseinanderlaufen ist messbar.
|
|
65
|
+
*/
|
|
66
|
+
function ratArt(pfad) {
|
|
67
|
+
return pfad === FIRMEN_CLAUDE_MD ? "nicht-hochschickbar" : "hochschickbar";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Der Wortlaut je Art und Rolle.
|
|
71
|
+
*
|
|
72
|
+
* 🔑 Die Saetze selbst stehen seit Teil 3 in `texte/kontext.ts` (mit echten Umlauten -- das
|
|
73
|
+
* liest ein Kunde).
|
|
74
|
+
*
|
|
75
|
+
* 🔑 `wer` ist die fertige Anrede (` an Lena` oder ` an euren Firmen-Admin`, englisch ` to Lena`),
|
|
76
|
+
* damit der Rueckfall auf den Sammelbegriff an EINER Stelle steht und nicht in
|
|
77
|
+
* jedem Zweig neu. Ruling T7-1: nie `null` im Klartext.
|
|
78
|
+
*/
|
|
79
|
+
const RAT_TEXT = {
|
|
80
|
+
hochschickbar: (rolle, wer, _namen, t, b) => (rolle === "admin" ? t.adviceUploadAdmin(b.hoch) : t.adviceUploadMember(wer, b.hoch)),
|
|
81
|
+
/*
|
|
82
|
+
🔴 **Auch dieser Zweig kennt die ROLLE -- Punkt A-5, 2026-08-26.** Er tat
|
|
83
|
+
es bis dahin nicht und riet jedem dasselbe: „Aendere sie dort", also im
|
|
84
|
+
Formular unter Firma → Kontext. Ein MITGLIED kann das nicht. `kimal3
|
|
85
|
+
kontext hoch` weist es ab, und den Firmenkontext der Firma darf es in der
|
|
86
|
+
App nicht schreiben. *Ein Rat, der die Lage nicht aendern kann, ist keiner*
|
|
87
|
+
-- er schickt einen Menschen an eine Tuer, vor der er keinen Schluessel
|
|
88
|
+
hat, und laesst ihn den Fehler bei sich suchen.
|
|
89
|
+
|
|
90
|
+
🔑 Was das Mitglied stattdessen liest: WAS es weitergeben soll (die drei
|
|
91
|
+
Saetze aus `kontext/kurzprofil.md`) und AN WEN -- genau die Form, die der
|
|
92
|
+
Zweig fuer die fuenf `kontext/`-Dateien oben schon hat.
|
|
93
|
+
*/
|
|
94
|
+
"nicht-hochschickbar": (rolle, wer, namen, t, b) => rolle === "admin"
|
|
95
|
+
? t.adviceFileAdmin(namen.kontext, namen.kurzprofil, b.hoch, b.runter)
|
|
96
|
+
: t.adviceFileMember(namen.kontext, namen.kurzprofil, wer, b.hoch),
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Was ein Mensch lesen soll, wenn `kimal3 kontext runter` Dateien in Ruhe
|
|
100
|
+
* laesst, weil er sie lokal geaendert hat -- je nach Rolle und je nach DATEI.
|
|
101
|
+
*
|
|
102
|
+
* 🔴 **DAS IST FUND F, ZUM ZWEITEN MAL -- und diesmal die echte Fassung
|
|
103
|
+
* (Review-Befund F-3, 2026-08-25).** Der Rat lautete fuer die ganze Liste auf
|
|
104
|
+
* einmal: „Deine Fassung ist die neuere? Dann schick sie hoch: kimal3 kontext
|
|
105
|
+
* hoch". Fuer `CLAUDE.md` ist dieser Satz eine Sackgasse: Sie steht in
|
|
106
|
+
* {@link verwalteteDateien} und landet deshalb in der Liste -- aber
|
|
107
|
+
* `kontext hoch` liest ausschliesslich die fuenf Dateien aus
|
|
108
|
+
* {@link firmenTexte} und meldet danach Erfolg, ohne sie. *Der Mensch tippt
|
|
109
|
+
* den geratenen Befehl, sieht einen Haken, und seine Aenderung steht weiter
|
|
110
|
+
* allein auf seiner Platte.*
|
|
111
|
+
*
|
|
112
|
+
* 🔑 Die ausgelieferte `CLAUDE.md` laedt diese Bearbeitung foermlich ein: Sie
|
|
113
|
+
* sagt selbst, aendern muesse man sie nur, wenn sich die Firma aendert.
|
|
114
|
+
*
|
|
115
|
+
* 🔴 **Der Rat wohnt hier und nicht im Befehl, damit ihn eine Wache sehen
|
|
116
|
+
* kann.** Die spurious Fassung desselben Fehlers (`merkzettelNachHoch`) war
|
|
117
|
+
* behoben, die echte lag vier Runden lang offen -- *ein Satz ueber das
|
|
118
|
+
* Verhalten eines Befehls wohnte im Befehl selbst, wo keine Vorlagen-Wache je
|
|
119
|
+
* hinsieht.* `check:kontext` prueft jetzt genau diese Zuordnung.
|
|
120
|
+
*
|
|
121
|
+
* 🔴 **PUNKT A-7 (2026-08-26): DER RAT WIRD ERZEUGT, NICHT AUFGEZAEHLT.** Diese
|
|
122
|
+
* Funktion hatte zwei `if`s -- eines fuer „irgendetwas aus
|
|
123
|
+
* {@link firmenTexte}", eines fuer den woertlichen Namen `"CLAUDE.md"`. Eine
|
|
124
|
+
* siebte verwaltete Datei waere durch beide gefallen und haette schweigend gar
|
|
125
|
+
* keinen Rat bekommen. Jetzt laeuft die Schleife ueber
|
|
126
|
+
* {@link verwalteteDateien} selbst und fragt {@link ratArt} nach der Art --
|
|
127
|
+
* jede Datei bekommt einen Rat.
|
|
128
|
+
*
|
|
129
|
+
* ⚠️ **Eine ART, ein Satz -- auch wenn drei ihrer Dateien betroffen sind.** Das
|
|
130
|
+
* war vorher so (ein `if` feuerte einmal) und bleibt so: Der Rat spricht ueber
|
|
131
|
+
* den Weg, nicht ueber die einzelne Datei; dreimal derselbe Absatz waere
|
|
132
|
+
* Laerm.
|
|
133
|
+
*/
|
|
134
|
+
export function ratNachUnberuehrt(unberuehrt, rolle, adminName, ordner, sprache) {
|
|
135
|
+
const t = KONTEXT_TEXTE[sprache];
|
|
136
|
+
// Ruling T7-1: kein `null` im Klartext, sondern der Sammelbegriff.
|
|
137
|
+
const wer = adminName ? t.toPerson(adminName) : t.toCompanyAdmin;
|
|
138
|
+
// Befehlswoerter in der Sprache des Satzes (befehlIn), nicht in der des Ordners.
|
|
139
|
+
const befehle = { hoch: befehlIn("kontext hoch", sprache), runter: befehlIn("kontext runter", sprache) }; // kein-anzeigetext: Befehlsschlüssel
|
|
140
|
+
const betroffeneArten = new Set();
|
|
141
|
+
for (const pfad of verwalteteDateien(ordner)) {
|
|
142
|
+
if (unberuehrt.includes(pfad))
|
|
143
|
+
betroffeneArten.add(ratArt(pfad));
|
|
144
|
+
}
|
|
145
|
+
// 🔑 Die Reihenfolge kommt aus RAT_REIHENFOLGE, NICHT aus der Einfuegereihen-
|
|
146
|
+
// folge des `Set` -- sonst haenge die Ausgabe beim Kunden davon ab, in
|
|
147
|
+
// welcher Reihenfolge verwalteteDateien(ordner) zufaellig steht.
|
|
148
|
+
// Die PFADE im Rat heissen so, wie sie in DIESEM Ordner heissen (L-19) -- die SAETZE folgen dem Menschen.
|
|
149
|
+
const namen = { kontext: ORDNER_NAMEN[ordner].kontext, kurzprofil: firmenTexte(ordner)[0].pfad };
|
|
150
|
+
return RAT_REIHENFOLGE.filter((art) => betroffeneArten.has(art)).map((art) => RAT_TEXT[art](rolle, wer, namen, t, befehle));
|
|
151
|
+
}
|