kimal3 0.3.2 → 0.4.0-durchstich.3
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/claude-plugins/.claude-plugin/marketplace.json +8 -0
- package/claude-plugins/README.md +7 -0
- package/claude-plugins/kimal3-logo/.claude-plugin/plugin.json +6 -0
- package/claude-plugins/kimal3-logo/hooks/hooks.json +1 -0
- package/claude-plugins/kimal3-logo/hooks/register.tsx +80 -0
- package/dist/arbeitsplatz-dateien.js +50 -0
- package/dist/befehle/einrichten.js +161 -0
- package/dist/befehle/login.js +34 -6
- package/dist/befehle/skill-install.js +4 -2
- package/dist/befehle/werkbank.js +5 -8
- package/dist/befehle-katalog-en.js +4 -4
- package/dist/befehle-katalog.js +2 -2
- package/dist/befehlswoerter.js +1 -0
- package/dist/browser.js +43 -0
- package/dist/einstellungen-mischen.js +68 -28
- package/dist/einstellungen-schreiben.js +48 -12
- package/dist/kimal3.js +10 -3
- package/dist/logo-ablegen.js +117 -0
- package/dist/nachricht.js +5 -1
- package/dist/plugins-mischen.js +78 -0
- package/dist/sprache.js +28 -4
- package/dist/texte/einrichten-marken.js +9 -0
- package/dist/texte/einrichten.js +60 -0
- package/dist/wache-schalter.js +30 -5
- package/package.json +28 -27
- package/skill/SKILL.md +5 -6
- package/skill/en/SKILL.md +5 -6
|
@@ -18,6 +18,8 @@ export const WACHE_MARKE = "kimal3 wache"; // kein-anzeigetext: Startbefehl in C
|
|
|
18
18
|
* Der Befehl, der in die Einstellungen eingetragen wird.
|
|
19
19
|
*
|
|
20
20
|
* 🔴 NUR EIN BEFEHL, keine Kommandozeilen-Zeichen -- kein `||`, kein `2>/dev/null`.
|
|
21
|
+
* 📌 Die eine Ausnahme seit dem 15.10.: Auf Mac und Linux steht `PATH="…" ` davor (`wacheBefehl` unten). Dort
|
|
22
|
+
* startet Claude Code Startbefehle immer über `sh -c`, und `sh` kennt diese Form; unter Windows bleibt er bloß.
|
|
21
23
|
* Das sind Bash-Zeichen, und ob auf dem Kundenrechner eine Bash laeuft, wissen
|
|
22
24
|
* wir nicht. Die Fehlerbehandlung gehoert ins Programm, nicht in die Zeile.
|
|
23
25
|
* *Ein Befehl, der beim Erbauer laeuft, ist keine Aussage ueber den Rechner
|
|
@@ -38,6 +40,31 @@ export const WACHE_MARKE = "kimal3 wache"; // kein-anzeigetext: Startbefehl in C
|
|
|
38
40
|
* AUCH BEI RUECKGABEWERT 127 gelesen (ebenfalls gemessen).
|
|
39
41
|
*/
|
|
40
42
|
export const WACHE_BEFEHL = `${WACHE_MARKE} --hook`; // kein-anzeigetext: Startbefehl in Claudes Einstellungen, bleibt deutsch (T3-5)
|
|
43
|
+
/**
|
|
44
|
+
* Der Startbefehl, wie er auf DIESEM Rechner eingetragen wird. Auf Mac und Linux stehen die Ordner davor, in denen
|
|
45
|
+
* `kimal3` und Node.js liegen (`ordner`, doppelte nur einmal).
|
|
46
|
+
* 🔑 Warum beide: `kimal3` liegt dort, wo npm Programme ablegt -- beim Mac-Weg `~/.kimal3/node/bin`, bei Homebrew
|
|
47
|
+
* `/opt/homebrew/bin`; Node.js selbst liegt bei Homebrew im versionierten Ordner darunter. Ein `kimal3` in einem
|
|
48
|
+
* eigenen npm-Ordner braucht zusätzlich den Ordner von Node.js für seine erste Zeile (`#!/usr/bin/env node`).
|
|
49
|
+
*
|
|
50
|
+
* 🔑 WARUM: Startbefehle laufen mit Claudes eigener Umgebung über `sh -c` und lesen weder `~/.zprofile`
|
|
51
|
+
* noch `~/.zshrc` (code.claude.com/docs/en/hooks, gelesen 15.10.). Liegt Node.js im Benutzerordner
|
|
52
|
+
* (`~/.kimal3/node`, der Mac-Weg ohne Adminrechte) und wurde Claude aus dem Dock gestartet, fände `sh`
|
|
53
|
+
* weder `kimal3` noch das `node` aus dessen erster Zeile -- und die Wache schwiege, ohne dass es jemand merkt.
|
|
54
|
+
* Unter Windows bleibt es beim bloßen Befehl: Dort steht Node.js im Pfad des Systems, und die Wache läuft
|
|
55
|
+
* dort seit Wochen so.
|
|
56
|
+
* 🔴 Nur Zeichen aus einer kurzen Liste kommen in den Befehl (messen-und-pruefen §23); alles andere
|
|
57
|
+
* -- ein Anführungszeichen, ein `$` -- gäbe einen Befehl, den `sh` anders liest. Ein solcher Ordner fällt weg; bleibt
|
|
58
|
+
* keiner übrig, steht der bloße Befehl da.
|
|
59
|
+
*/
|
|
60
|
+
export function wacheBefehl(system, ordner) {
|
|
61
|
+
// Ein Wegwerf-Ort fällt weg (Durchsicht 4, Fund 4): `npx kimal3 …` startet aus einem Zwischenspeicher, der bald
|
|
62
|
+
// verschwindet, ein Start aus einem Projektordner aus dessen `node_modules`.
|
|
63
|
+
const sicher = [...new Set(ordner)].filter((o) => /^\/[A-Za-z0-9 ._@+\/-]*$/.test(o) && !/\/(node_modules|_npx)\//.test(`${o}/`));
|
|
64
|
+
if (system === "win32" || sicher.length === 0)
|
|
65
|
+
return WACHE_BEFEHL;
|
|
66
|
+
return `PATH="${sicher.join(":")}:$PATH" ${WACHE_BEFEHL}`; // kein-anzeigetext: Startbefehl in Claudes Einstellungen
|
|
67
|
+
}
|
|
41
68
|
/**
|
|
42
69
|
* Wie lange Claude auf die Wache wartet, in Sekunden.
|
|
43
70
|
*
|
|
@@ -48,9 +75,45 @@ export const WACHE_BEFEHL = `${WACHE_MARKE} --hook`; // kein-anzeigetext: Startb
|
|
|
48
75
|
* seiner Arbeit ab -- und ausgerechnet unserer soll ihm helfen.*
|
|
49
76
|
*/
|
|
50
77
|
export const WACHE_ZEITGRENZE = 10;
|
|
51
|
-
function istObjekt(wert) {
|
|
78
|
+
export function istObjekt(wert) {
|
|
52
79
|
return typeof wert === "object" && wert !== null && !Array.isArray(wert);
|
|
53
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Die Einstellungsdatei als Objekt -- oder die Absage. Gemeinsam fuer die Wache und die Plugins
|
|
83
|
+
* (`plugins-mischen.ts`), damit beide eine kaputte Datei gleich behandeln: nicht anfassen.
|
|
84
|
+
*/
|
|
85
|
+
export function leseEinstellungen(alt) {
|
|
86
|
+
/*
|
|
87
|
+
Eine fehlende und eine leere Datei sind dasselbe: Es gibt nichts zu
|
|
88
|
+
bewahren, also legen wir an. `null` kommt vom Aufrufer, wenn die Datei gar
|
|
89
|
+
nicht existiert; ein leerer Text entsteht, wenn ein frueherer Lauf sie
|
|
90
|
+
angelegt, aber nicht mehr befuellt hat.
|
|
91
|
+
*/
|
|
92
|
+
const leer = alt === null || alt.trim() === "";
|
|
93
|
+
if (leer)
|
|
94
|
+
return { ok: true, objekt: {}, leer };
|
|
95
|
+
let gelesen;
|
|
96
|
+
try {
|
|
97
|
+
// Eine unsichtbare Kennung am Anfang (UTF-8-BOM, z. B. von PowerShell 5.1 geschrieben) macht die
|
|
98
|
+
// Datei nicht kaputt -- `JSON.parse` stolpert aber darüber (QA-Fund 3 zum Bauplan 2026-10-15).
|
|
99
|
+
gelesen = JSON.parse(alt.replace(/^\uFEFF/, ""));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/*
|
|
103
|
+
🔴 HIER WIRD NICHTS GESCHRIEBEN. Wer eine unlesbare Einstellungsdatei
|
|
104
|
+
ueberschreibt, wirft weg, was der Mensch dort stehen hat -- und
|
|
105
|
+
ausgerechnet der Fall, in dem sie schon beschaedigt ist, ist der, in
|
|
106
|
+
dem er sie am dringendsten braucht. Die Absage geht nach oben, der
|
|
107
|
+
Mensch erfaehrt sie.
|
|
108
|
+
*/
|
|
109
|
+
return { ok: false, grund: "kein-gueltiges-json" };
|
|
110
|
+
}
|
|
111
|
+
// Eine Liste oder eine blosse Zahl ist gueltiges JSON, aber keine
|
|
112
|
+
// Einstellungsdatei. Das ist ein DRITTER Fall, nicht der zweite.
|
|
113
|
+
if (!istObjekt(gelesen))
|
|
114
|
+
return { ok: false, grund: "fremde-form" };
|
|
115
|
+
return { ok: true, objekt: gelesen, leer };
|
|
116
|
+
}
|
|
54
117
|
/**
|
|
55
118
|
* Ist das ein Eintrag, den WIR frueher einmal geschrieben haben?
|
|
56
119
|
*
|
|
@@ -75,33 +138,10 @@ function istUnserer(eintrag) {
|
|
|
75
138
|
return typeof eintrag.command === "string" && eintrag.command.includes(WACHE_MARKE);
|
|
76
139
|
}
|
|
77
140
|
export function mischeEinstellungen(alt, befehl) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
angelegt, aber nicht mehr befuellt hat.
|
|
83
|
-
*/
|
|
84
|
-
const leer = alt === null || alt.trim() === "";
|
|
85
|
-
let gelesen = {};
|
|
86
|
-
if (!leer) {
|
|
87
|
-
try {
|
|
88
|
-
gelesen = JSON.parse(alt);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
/*
|
|
92
|
-
🔴 HIER WIRD NICHTS GESCHRIEBEN. Wer eine unlesbare Einstellungsdatei
|
|
93
|
-
ueberschreibt, wirft weg, was der Mensch dort stehen hat -- und
|
|
94
|
-
ausgerechnet der Fall, in dem sie schon beschaedigt ist, ist der, in
|
|
95
|
-
dem er sie am dringendsten braucht. Die Absage geht nach oben, der
|
|
96
|
-
Mensch erfaehrt sie.
|
|
97
|
-
*/
|
|
98
|
-
return { art: "abgelehnt", grund: "kein-gueltiges-json" };
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Eine Liste oder eine blosse Zahl ist gueltiges JSON, aber keine
|
|
102
|
-
// Einstellungsdatei. Das ist ein DRITTER Fall, nicht der zweite.
|
|
103
|
-
if (!istObjekt(gelesen))
|
|
104
|
-
return { art: "abgelehnt", grund: "fremde-form" };
|
|
141
|
+
const lesen = leseEinstellungen(alt);
|
|
142
|
+
if (!lesen.ok)
|
|
143
|
+
return { art: "abgelehnt", grund: lesen.grund };
|
|
144
|
+
const { objekt: gelesen, leer } = lesen;
|
|
105
145
|
const vorher = JSON.stringify(gelesen);
|
|
106
146
|
const hooks = gelesen.hooks === undefined ? {} : gelesen.hooks;
|
|
107
147
|
if (!istObjekt(hooks))
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import {
|
|
2
|
+
import { homedir, platform } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { mischeEinstellungen, wacheBefehl } from "./einstellungen-mischen.js";
|
|
5
|
+
import { KATALOG, mischePlugins, pluginStand } from "./plugins-mischen.js";
|
|
5
6
|
/** Die Kontrolle nach dem Schreiben fand unseren Eintrag nicht -- eigener Fall, damit `detail` `null` sein kann. */
|
|
6
7
|
class EintragFehlt extends Error {
|
|
7
8
|
}
|
|
@@ -12,6 +13,41 @@ export function sicherungsDatei() {
|
|
|
12
13
|
return einstellungenDatei() + ".kimal3-sicherung";
|
|
13
14
|
}
|
|
14
15
|
export function trageWacheEin() {
|
|
16
|
+
// Vor den Befehl kommen der Ordner, aus dem `kimal3` gerade gestartet wurde (dort legt npm es ab; bei Homebrew liegt
|
|
17
|
+
// dort auch `node`), und der Ordner des laufenden Node.js (für ein `kimal3` in einem eigenen npm-Ordner) -- Durchsicht 3, Fund 2.
|
|
18
|
+
const befehl = wacheBefehl(platform(), [dirname(process.argv[1] ?? ""), dirname(process.execPath)]);
|
|
19
|
+
return trageEin((alt) => mischeEinstellungen(alt, befehl), (text) => text.includes(JSON.stringify(befehl)), sicherungsDatei());
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Die Kopie, die die Plugins vor dem Schreiben anlegen. Eine eigene, weil `kimal3 einrichten` die Plugins
|
|
23
|
+
* VOR der Wache einträgt: So hält diese Kopie Sabines unveränderte Datei (Prüfer-Fund 9 zum Bauplan 2026-10-15).
|
|
24
|
+
*/
|
|
25
|
+
export function pluginSicherungsDatei() {
|
|
26
|
+
return einstellungenDatei() + ".kimal3-sicherung-plugins";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* superpowers und das Logo eintragen (`plugins-mischen.ts`); `katalogPfad` mit Schrägstrichen vorwärts,
|
|
30
|
+
* `null` ohne Logo-Ordner. 🔑 Die Kontrolle liest die Einschalter selbst, nicht nur Katalogname und Pfad:
|
|
31
|
+
* Fehlte ein Einschalter, meldete die Zeile `Plugins:` sonst „eingetragen“ für etwas, das nie lädt
|
|
32
|
+
* (Durchsicht 2, Fund 7). „Von Sabine ausgeschaltet“ zählt als da -- es steht ja drin.
|
|
33
|
+
*/
|
|
34
|
+
export function tragePluginsEin(katalogPfad, mitSuperpowers) {
|
|
35
|
+
return trageEin((alt) => mischePlugins(alt, katalogPfad, mitSuperpowers), (text) => {
|
|
36
|
+
const stand = pluginStand(text);
|
|
37
|
+
if (mitSuperpowers && stand.superpowers === "fehlt")
|
|
38
|
+
return false;
|
|
39
|
+
return katalogPfad === null || (stand.logo !== "fehlt" && text.includes(`"${KATALOG}"`) && text.includes(JSON.stringify(katalogPfad)));
|
|
40
|
+
}, pluginSicherungsDatei());
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Der Vier-Schritte-Weg fuer jeden Eintrag in Claudes Einstellungen -- die Wache und die Plugins
|
|
44
|
+
* (`plugins-mischen.ts`) gehen denselben, damit keiner von beiden ohne Sicherung schreibt.
|
|
45
|
+
* `kontrolle` bekommt den zurueckgelesenen Text und sagt, ob unser Eintrag drinsteht.
|
|
46
|
+
*/
|
|
47
|
+
export function trageEin(mische, kontrolle,
|
|
48
|
+
// 🔑 Je Schreiber eine eigene Kopie: Schrieben Wache und Plugins in dieselbe, überschriebe die zweite
|
|
49
|
+
// Sabines Originaldatei mit dem Stand nach dem ersten Eintrag (QA-Fund 9).
|
|
50
|
+
sicherung) {
|
|
15
51
|
const datei = einstellungenDatei();
|
|
16
52
|
const gabEsSchon = existsSync(datei);
|
|
17
53
|
// --- Schritt 1: lesen ---
|
|
@@ -30,15 +66,15 @@ export function trageWacheEin() {
|
|
|
30
66
|
return { art: "gescheitert", schritt: "lesen", detail: e.message };
|
|
31
67
|
}
|
|
32
68
|
}
|
|
33
|
-
const ergebnis =
|
|
69
|
+
const ergebnis = mische(alt);
|
|
34
70
|
if (ergebnis.art === "abgelehnt")
|
|
35
71
|
return { art: "abgelehnt", grund: ergebnis.grund };
|
|
36
72
|
if (ergebnis.art === "unveraendert")
|
|
37
|
-
return { art: "stand-schon" };
|
|
73
|
+
return { art: "stand-schon", inhalt: alt ?? "" };
|
|
38
74
|
// --- Schritt 2: Sicherungskopie ---
|
|
39
75
|
if (gabEsSchon) {
|
|
40
76
|
try {
|
|
41
|
-
copyFileSync(datei,
|
|
77
|
+
copyFileSync(datei, sicherung);
|
|
42
78
|
}
|
|
43
79
|
catch (e) {
|
|
44
80
|
/*
|
|
@@ -55,22 +91,22 @@ export function trageWacheEin() {
|
|
|
55
91
|
writeFileSync(datei, ergebnis.inhalt, "utf8");
|
|
56
92
|
}
|
|
57
93
|
catch (e) {
|
|
58
|
-
zurueck(gabEsSchon);
|
|
94
|
+
zurueck(gabEsSchon, sicherung);
|
|
59
95
|
return { art: "gescheitert", schritt: "schreiben", detail: e.message };
|
|
60
96
|
}
|
|
61
97
|
// --- Schritt 4: zuruecklesen und erneut pruefen ---
|
|
62
98
|
try {
|
|
63
99
|
const zurueckgelesen = readFileSync(datei, "utf8");
|
|
64
100
|
const geparst = JSON.parse(zurueckgelesen);
|
|
65
|
-
if (!zurueckgelesen
|
|
101
|
+
if (!kontrolle(zurueckgelesen) || geparst === null) {
|
|
66
102
|
throw new EintragFehlt();
|
|
67
103
|
}
|
|
68
104
|
}
|
|
69
105
|
catch (e) {
|
|
70
|
-
zurueck(gabEsSchon);
|
|
106
|
+
zurueck(gabEsSchon, sicherung);
|
|
71
107
|
return { art: "gescheitert", schritt: "kontrolle", detail: e instanceof EintragFehlt ? null : e.message };
|
|
72
108
|
}
|
|
73
|
-
return { art: "eingetragen" };
|
|
109
|
+
return { art: "eingetragen", inhalt: ergebnis.inhalt };
|
|
74
110
|
}
|
|
75
111
|
/**
|
|
76
112
|
* Die Sicherung zurueckspielen.
|
|
@@ -79,11 +115,11 @@ export function trageWacheEin() {
|
|
|
79
115
|
* bleibt die halb geschriebene stehen. Das ist die harmlose Seite: Sie
|
|
80
116
|
* enthielt vorher nichts, was jemandem gehoerte.
|
|
81
117
|
*/
|
|
82
|
-
function zurueck(gabEsSchon) {
|
|
118
|
+
function zurueck(gabEsSchon, sicherung) {
|
|
83
119
|
if (!gabEsSchon)
|
|
84
120
|
return;
|
|
85
121
|
try {
|
|
86
|
-
copyFileSync(
|
|
122
|
+
copyFileSync(sicherung, einstellungenDatei());
|
|
87
123
|
}
|
|
88
124
|
catch {
|
|
89
125
|
/* Mehr koennen wir nicht tun -- der Grund steht in der Meldung oben. */
|
package/dist/kimal3.js
CHANGED
|
@@ -20,11 +20,12 @@ import { WACHE_TEXTE } from "./texte/wache.js";
|
|
|
20
20
|
import { leseWacheDatei, sitzungJetzt, wacheAusgefallen } from "./wache-schalter.js";
|
|
21
21
|
import { formatiere, heuteAus, skillZustand, sollZeigen, } from "./nachricht.js";
|
|
22
22
|
import { eigeneVersion } from "./version.js";
|
|
23
|
-
import { sprache } from "./sprache.js";
|
|
23
|
+
import { merkeBefehlsSprache, sprache } from "./sprache.js";
|
|
24
24
|
import { ALLGEMEIN_TEXTE } from "./texte/allgemein.js";
|
|
25
25
|
import { hilfeText } from "./hilfe.js";
|
|
26
|
-
import { erkenneBefehl } from "./befehlswoerter.js";
|
|
26
|
+
import { BEFEHLSWOERTER, erkenneBefehl } from "./befehlswoerter.js";
|
|
27
27
|
import { arbeitsplatzNeu } from "./befehle/arbeitsplatz.js";
|
|
28
|
+
import { einrichten } from "./befehle/einrichten.js";
|
|
28
29
|
import { werkbankNeu } from "./befehle/werkbank.js";
|
|
29
30
|
import { firmaDaten } from "./befehle/firma-daten.js";
|
|
30
31
|
import { kontextFragen } from "./befehle/kontext-fragen.js";
|
|
@@ -484,6 +485,7 @@ async function fuehreAus(name, rest) {
|
|
|
484
485
|
case "projekt liste": return allein ? projektListe() : null;
|
|
485
486
|
// ⚠️ Rest wird durchgereicht, nicht auf Gleichheit geprueft: `firma daten --firma X` gehoert dazu.
|
|
486
487
|
case "firma daten": return firmaDaten(rest);
|
|
488
|
+
case "einrichten": return einrichten(rest);
|
|
487
489
|
case "arbeitsplatz neu": return arbeitsplatzNeu(rest);
|
|
488
490
|
case "werkbank neu": return werkbankNeu(rest);
|
|
489
491
|
case "kontext runter": return kontextRunter(rest);
|
|
@@ -493,7 +495,8 @@ async function fuehreAus(name, rest) {
|
|
|
493
495
|
case "wache aus": return allein ? wacheSchalten(false) : null;
|
|
494
496
|
case "wache":
|
|
495
497
|
// 🔴 Der Startbefehl aus `~/.claude/settings.json`, den `skill install` eintraegt (T3-5:
|
|
496
|
-
// eingetragen wird immer `kimal3 wache --hook
|
|
498
|
+
// eingetragen wird immer `kimal3 wache --hook`, auf Mac/Linux mit `PATH="…"` davor -- `wacheBefehl`;
|
|
499
|
+
// `kimal3 watch --hook` laeuft genauso).
|
|
497
500
|
// Er verpackt die Meldung anders -- Begruendung an `wache()`.
|
|
498
501
|
// ⚠️ Ein Mensch tippt das nie; der Katalog nennt ihn deshalb auch nicht.
|
|
499
502
|
if (rest.length === 1 && rest[0] === "--hook")
|
|
@@ -537,6 +540,10 @@ async function haupt() {
|
|
|
537
540
|
// extra` bleibt unbekannt. Der grosse Kommentar zur Nachsicht bei `wache --irgendwas` steht
|
|
538
541
|
// unten an `fuehreAus` und gilt unveraendert.
|
|
539
542
|
const erkannt = erkenneBefehl(argv);
|
|
543
|
+
// 🌐 `setup` spricht Englisch, `einrichten` Deutsch, solange keine Kontosprache gemerkt ist --
|
|
544
|
+
// die Anleitung, die den Befehl tippt, liest seine Marken (Begruendung an `startSprache`).
|
|
545
|
+
if (erkannt?.name === "einrichten")
|
|
546
|
+
merkeBefehlsSprache(argv[0] === BEFEHLSWOERTER.einrichten.en ? "en" : "de");
|
|
540
547
|
if (erkannt) {
|
|
541
548
|
const code = await fuehreAus(erkannt.name, erkannt.rest);
|
|
542
549
|
if (code !== null)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { cpSync, existsSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
/**
|
|
6
|
+
* Legt den Plugin-Katalog mit dem Logo-Mod in `~/.kimal3/claude-plugins` ab und setzt dort die
|
|
7
|
+
* Sprache des Kontos. Claude Code liest den Mod aus genau diesem Ordner (Katalog `kimal3`,
|
|
8
|
+
* `plugins-mischen.ts`).
|
|
9
|
+
*
|
|
10
|
+
* 🔑 Warum ein fester Ordner im Benutzerordner und nicht der Ordner im installierten npm-Paket:
|
|
11
|
+
* Der hängt am Installationsort von Node.js und wechselt mit ihm -- der Eintrag in Claudes
|
|
12
|
+
* Einstellungen zeigte dann ins Leere.
|
|
13
|
+
* 🔑 Der Ordner gehört `kimal3` (seine README sagt es): Jeder Lauf ersetzt ihn ganz (Lage P-11).
|
|
14
|
+
*/
|
|
15
|
+
export function katalogOrdner() {
|
|
16
|
+
return join(homedir(), ".kimal3", "claude-plugins"); // kein-anzeigetext: Ordnername
|
|
17
|
+
}
|
|
18
|
+
/** Die Zeile im Mod, die die Sprache festlegt -- `check:logo` prüft, dass es sie genau einmal gibt. */
|
|
19
|
+
export const SPRACH_ZEILE = "const SPRACHE: 'de' | 'en' = 'de'"; // kein-anzeigetext: Quelltext des Mods
|
|
20
|
+
/** Reine Rechnung: der Mod-Text in der Sprache des Kontos, oder `null`, wenn die Zeile fehlt. */
|
|
21
|
+
export function mitSprache(quelltext, sprache) {
|
|
22
|
+
if (quelltext.split(SPRACH_ZEILE).length !== 2)
|
|
23
|
+
return null;
|
|
24
|
+
return quelltext.replace(SPRACH_ZEILE, `const SPRACHE: 'de' | 'en' = '${sprache}'`); // kein-anzeigetext: Quelltext des Mods
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 🔴 NIE EIN HALBER ORDNER (Durchsicht-Funde 2 und 9): Claudes Einstellungen zeigen auf `katalogOrdner()`,
|
|
28
|
+
* und eine laufende Claude-Sitzung hat den Mod daraus geladen. Deshalb:
|
|
29
|
+
* 1. Sprache setzen, BEVOR irgendetwas angefasst wird -- fehlt die Zeile, bleibt alles, wie es war.
|
|
30
|
+
* 2. Steht schon genau das da, was entstehen soll: nichts anfassen (Lage P-8). Das ist auch der Normalfall
|
|
31
|
+
* eines zweiten Laufs, und so kommt er gar nicht erst in die Nähe der Windows-Sperren unten.
|
|
32
|
+
* 3. Den neuen Ordner daneben fertig bauen (`….neu`), dann tauschen: alten Ordner wegschieben, neuen an
|
|
33
|
+
* seinen Platz, alten löschen.
|
|
34
|
+
* 🔑 Der weggeschobene Ordner bekommt bei JEDEM Lauf einen frischen Namen (Durchsicht 2, Funde 2 und 4):
|
|
35
|
+
* Ein Rest, den Windows festhält, blockiert so keinen späteren Lauf, und zurückgeholt wird nur, was
|
|
36
|
+
* DIESER Lauf weggeschoben hat -- nie ein veralteter Rest von früher.
|
|
37
|
+
* Lässt sich der alte nicht wegschieben (Claude hält ihn manchmal fest), bleibt er unversehrt stehen und
|
|
38
|
+
* gilt weiter: ein Erfolg mit `alterStand`, den `kimal3 einrichten` in einer Zeile nennt.
|
|
39
|
+
*/
|
|
40
|
+
export function logoAblegen(sprache) {
|
|
41
|
+
// `dist/logo-ablegen.js` -> `../claude-plugins` im installierten Paket (`files` in package.json).
|
|
42
|
+
const quelle = fileURLToPath(new URL("../claude-plugins", import.meta.url));
|
|
43
|
+
const ziel = katalogOrdner();
|
|
44
|
+
const neu = `${ziel}.neu`; // kein-anzeigetext: Ordnername
|
|
45
|
+
const modDatei = (ordner) => join(ordner, "kimal3-logo", "hooks", "register.tsx");
|
|
46
|
+
try {
|
|
47
|
+
const text = mitSprache(readFileSync(modDatei(quelle), "utf8"), sprache);
|
|
48
|
+
if (text === null)
|
|
49
|
+
return { ok: false, detail: modDatei(quelle) };
|
|
50
|
+
if (stehtSchonDa(quelle, ziel, text)) {
|
|
51
|
+
// Auch im Normalfall aufräumen, samt einem halb gebauten `….neu`: sonst blieben Reste für immer (Durchsicht 3/4).
|
|
52
|
+
rmSync(neu, { recursive: true, force: true });
|
|
53
|
+
resteWegraeumen(ziel);
|
|
54
|
+
return { ok: true, ordner: ziel };
|
|
55
|
+
}
|
|
56
|
+
rmSync(neu, { recursive: true, force: true });
|
|
57
|
+
cpSync(quelle, neu, { recursive: true });
|
|
58
|
+
writeFileSync(modDatei(neu), text, "utf8");
|
|
59
|
+
if (!existsSync(ziel)) {
|
|
60
|
+
renameSync(neu, ziel);
|
|
61
|
+
return { ok: true, ordner: ziel };
|
|
62
|
+
}
|
|
63
|
+
const alt = `${ziel}.alt-${Date.now()}`; // kein-anzeigetext: Ordnername
|
|
64
|
+
try {
|
|
65
|
+
renameSync(ziel, alt);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
rmSync(neu, { recursive: true, force: true });
|
|
69
|
+
return { ok: true, ordner: ziel, alterStand: true };
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
renameSync(neu, ziel);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
renameSync(alt, ziel);
|
|
76
|
+
return { ok: false, detail: e.message };
|
|
77
|
+
}
|
|
78
|
+
resteWegraeumen(ziel);
|
|
79
|
+
return { ok: true, ordner: ziel };
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
return { ok: false, detail: e.message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Gleiche Dateien, gleicher Inhalt -- der Mod mit der Sprache des Kontos, alles andere wie im Paket. */
|
|
86
|
+
function stehtSchonDa(quelle, ziel, modText) {
|
|
87
|
+
if (!existsSync(ziel))
|
|
88
|
+
return false;
|
|
89
|
+
const dateien = (ordner) => alleDateien(ordner).sort();
|
|
90
|
+
const soll = dateien(quelle);
|
|
91
|
+
if (JSON.stringify(soll) !== JSON.stringify(dateien(ziel)))
|
|
92
|
+
return false;
|
|
93
|
+
const mod = join("kimal3-logo", "hooks", "register.tsx");
|
|
94
|
+
return soll.every((d) => (d === mod ? Buffer.from(modText, "utf8") : readFileSync(join(quelle, d))).equals(readFileSync(join(ziel, d))));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Alle Dateien unter `ordner`, als Pfade relativ dazu. 🔑 Von Hand statt `readdirSync(…, { recursive: true })`: Das kann
|
|
98
|
+
* Node.js erst ab 20.1, und unter 20.0 fände der Vergleich gar keine Datei -- „gleich“, und das Logo würde nie erneuert
|
|
99
|
+
* (Durchsicht 3, Fund 7; `engines` erlaubt 20).
|
|
100
|
+
*/
|
|
101
|
+
function alleDateien(ordner, unter = "") {
|
|
102
|
+
return readdirSync(join(ordner, unter), { withFileTypes: true }).flatMap((e) => e.isDirectory() ? alleDateien(ordner, join(unter, e.name)) : e.isFile() ? [join(unter, e.name)] : []);
|
|
103
|
+
}
|
|
104
|
+
/** Weggeschobene Ordner früherer Läufe (`claude-plugins.alt…`); was Windows noch festhält, bleibt bis zum nächsten Lauf. */
|
|
105
|
+
function resteWegraeumen(ziel) {
|
|
106
|
+
const name = `${basename(ziel)}.alt`; // kein-anzeigetext: Ordnername
|
|
107
|
+
for (const eintrag of readdirSync(dirname(ziel))) {
|
|
108
|
+
if (!eintrag.startsWith(name))
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
rmSync(join(dirname(ziel), eintrag), { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* festgehalten -- der nächste Lauf versucht es wieder */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
package/dist/nachricht.js
CHANGED
|
@@ -259,10 +259,14 @@ export function stempelZeile(version, sprache) {
|
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
261
|
* Die Sprache des LETZTEN Stempels (wie `stempelAus`: ein Beispiel im Text darf nicht gewinnen).
|
|
262
|
+
*
|
|
263
|
+
* 🔴 Die Vorabfassung (`0.4.0-durchstich.1`) trägt einen Zusatz hinter den Zahlen -- ohne `(?:-…)?`
|
|
264
|
+
* fand das Muster bei ihr die Sprache nie, jeder englische Stempel galt als deutsch, und der Login
|
|
265
|
+
* stellte nichts mehr um (gemessen von `check:cli-live` L1/L6 beim Task 7 des Durchstichs).
|
|
262
266
|
* Ohne Zusatz -- und bei jedem anderen Zusatz als genau `en` -- Deutsch (T4-4).
|
|
263
267
|
*/
|
|
264
268
|
export function stempelSpracheAus(inhalt) {
|
|
265
|
-
const treffer = [...inhalt.matchAll(/kimal3:erzeugt version=[0-9][0-9.]*(?: sprache=([a-z]+))?/g)];
|
|
269
|
+
const treffer = [...inhalt.matchAll(/kimal3:erzeugt version=[0-9][0-9.]*(?:-[0-9A-Za-z.]+)?(?: sprache=([a-z]+))?/g)];
|
|
266
270
|
return treffer.length > 0 && treffer[treffer.length - 1][1] === "en" ? "en" : "de";
|
|
267
271
|
}
|
|
268
272
|
/**
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { istObjekt, leseEinstellungen } from "./einstellungen-mischen.js";
|
|
2
|
+
/**
|
|
3
|
+
* Traegt superpowers und das KImal3-Logo in Claudes Einstellungen ein -- als Katalog plus
|
|
4
|
+
* Einschalter, so wie Anthropic Firmen-Mods auf jeden Rechner verteilt (Bauplan 2026-10-15,
|
|
5
|
+
* „Warum anders als die Spec"). Beim naechsten Start laedt Claude Code beides selbst.
|
|
6
|
+
*
|
|
7
|
+
* 🔴 DIESE DATEI FASST KEINE DATEI AN -- dieselbe Bauform wie `einstellungen-mischen.ts`, aus
|
|
8
|
+
* demselben Grund: In `~/.claude/settings.json` steht der Arbeitsplatz des Menschen.
|
|
9
|
+
* Geschrieben wird ueber `einstellungen-schreiben.ts` (Sicherung, Zuruecklesen).
|
|
10
|
+
*/
|
|
11
|
+
export const KATALOG = "kimal3"; // kein-anzeigetext: Name des Plugin-Katalogs in Claudes Einstellungen
|
|
12
|
+
export const LOGO = `kimal3-logo@${KATALOG}`; // kein-anzeigetext: Plugin-Kennung
|
|
13
|
+
const OFFIZIELL = "claude-plugins-official"; // kein-anzeigetext: Anthropics Katalog
|
|
14
|
+
export const SUPERPOWERS = `superpowers@${OFFIZIELL}`; // kein-anzeigetext: Plugin-Kennung
|
|
15
|
+
/**
|
|
16
|
+
* 🔑 Jede Entscheidung des Menschen bleibt stehen (Lagen P-5 bis P-7, P-10):
|
|
17
|
+
* - Ein Eintrag fuer das Logo -- auch `false` -- heisst: Er hat schon entschieden. Wir setzen
|
|
18
|
+
* nur, was fehlt.
|
|
19
|
+
* - superpowers aus IRGENDEINEM Katalog (`superpowers@…`, an oder aus) heisst: nicht noch eins.
|
|
20
|
+
* Zwei superpowers gaeben jede Faehigkeit doppelt.
|
|
21
|
+
* - Den offiziellen Katalog tragen wir nur ein, wenn er fehlt und wir superpowers einschalten.
|
|
22
|
+
* Nur der Katalog `kimal3` gehoert uns: Er zeigt immer auf unseren Ordner (P-9, Umzug).
|
|
23
|
+
* 🔑 `katalogPfad` ist `null`, wenn der Logo-Ordner nicht angelegt werden konnte: Dann bleiben Katalog und
|
|
24
|
+
* Logo-Eintrag, wie sie sind, und superpowers kommt trotzdem -- es braucht den Ordner nicht (Durchsicht 2, Fund 1).
|
|
25
|
+
* 🔑 `mitSuperpowers` ist `false`, wenn Git fehlt: Claude Code klont Anthropics Katalog mit Git und scheiterte sonst bei
|
|
26
|
+
* jedem Start (Durchsicht 3, Fund 5). Was schon drinsteht, bleibt -- auch ein früher eingetragenes superpowers.
|
|
27
|
+
*/
|
|
28
|
+
export function mischePlugins(alt, katalogPfad, mitSuperpowers = true) {
|
|
29
|
+
const lesen = leseEinstellungen(alt);
|
|
30
|
+
if (!lesen.ok)
|
|
31
|
+
return { art: "abgelehnt", grund: lesen.grund };
|
|
32
|
+
const { objekt, leer } = lesen;
|
|
33
|
+
const kataloge = objekt.extraKnownMarketplaces ?? {};
|
|
34
|
+
const plugins = objekt.enabledPlugins ?? {};
|
|
35
|
+
if (!istObjekt(kataloge) || !istObjekt(plugins))
|
|
36
|
+
return { art: "abgelehnt", grund: "fremde-form" };
|
|
37
|
+
// Nur die Quelle gehört uns; was Claude Code selbst in den Eintrag schreibt (`autoUpdate`, wenn
|
|
38
|
+
// Sabine es unter /plugin umschaltet), bleibt stehen (QA-Fund 7). Zeigt die Quelle schon auf unseren
|
|
39
|
+
// Ordner, bleibt der Eintrag Zeichen für Zeichen, wie er ist -- auch in anderer Reihenfolge der
|
|
40
|
+
// Schlüssel; sonst schriebe jeder Lauf die Datei neu (Durchsicht-Fund 6, Lage P-8).
|
|
41
|
+
const alterEintrag = istObjekt(kataloge[KATALOG]) ? kataloge[KATALOG] : {};
|
|
42
|
+
const alteQuelle = istObjekt(alterEintrag.source) ? alterEintrag.source : {};
|
|
43
|
+
const zeigtSchonHin = alteQuelle.source === "directory" && alteQuelle.path === katalogPfad;
|
|
44
|
+
const neueKataloge = { ...kataloge };
|
|
45
|
+
const neuePlugins = { ...plugins };
|
|
46
|
+
if (katalogPfad !== null) {
|
|
47
|
+
neueKataloge[KATALOG] = zeigtSchonHin ? alterEintrag : { ...alterEintrag, source: { source: "directory", path: katalogPfad } };
|
|
48
|
+
if (!(LOGO in plugins))
|
|
49
|
+
neuePlugins[LOGO] = true;
|
|
50
|
+
}
|
|
51
|
+
if (mitSuperpowers && !Object.keys(plugins).some((k) => k.startsWith("superpowers@"))) {
|
|
52
|
+
neuePlugins[SUPERPOWERS] = true;
|
|
53
|
+
if (!(OFFIZIELL in kataloge)) {
|
|
54
|
+
neueKataloge[OFFIZIELL] = { source: { source: "github", repo: "anthropics/claude-plugins-official" } };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const neu = { ...objekt, extraKnownMarketplaces: neueKataloge, enabledPlugins: neuePlugins };
|
|
58
|
+
// Wie bei der Wache: Wer nichts zu tun hat, fasst die Datei nicht an (P-8).
|
|
59
|
+
if (!leer && JSON.stringify(neu) === JSON.stringify(objekt))
|
|
60
|
+
return { art: "unveraendert" };
|
|
61
|
+
return { art: "geschrieben", inhalt: JSON.stringify(neu, null, 2) + "\n" };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Was nach dem Eintragen wirklich gilt -- fuer die Zeile `Plugins:`, die Claude der Kundin weitersagt.
|
|
65
|
+
* 🔑 Aus der Datei gelesen, nicht aus dem, was wir eingetragen haben: Hat sie superpowers selbst
|
|
66
|
+
* ausgeschaltet, muss die Zeile das sagen, damit die Anleitung sie nicht ins Menue schickt (Durchsicht-Fund 1).
|
|
67
|
+
*/
|
|
68
|
+
export function pluginStand(inhalt) {
|
|
69
|
+
const lesen = leseEinstellungen(inhalt);
|
|
70
|
+
const plugins = lesen.ok ? lesen.objekt.enabledPlugins : undefined;
|
|
71
|
+
if (!istObjekt(plugins))
|
|
72
|
+
return { superpowers: "fehlt", logo: "fehlt" };
|
|
73
|
+
const superpowers = Object.entries(plugins).filter(([k]) => k.startsWith("superpowers@")).map(([, v]) => v);
|
|
74
|
+
return {
|
|
75
|
+
superpowers: superpowers.includes(true) ? "an" : superpowers.length > 0 ? "aus" : "fehlt",
|
|
76
|
+
logo: plugins[LOGO] === true ? "an" : LOGO in plugins ? "aus" : "fehlt",
|
|
77
|
+
};
|
|
78
|
+
}
|
package/dist/sprache.js
CHANGED
|
@@ -40,11 +40,21 @@ export function spracheAusSystem(gebiet) {
|
|
|
40
40
|
return klein === "en" || klein.startsWith("en-") ? "en" : "de";
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* Die Sprache beim Start (T3-2):
|
|
44
|
-
*
|
|
45
|
-
* Deutsch) -> sonst das
|
|
43
|
+
* Die Sprache beim Start (T3-2): die Sprache des getippten Befehlsworts, wo es eine traegt ->
|
|
44
|
+
* sonst der Merkzettel -> sonst Deutsch, wenn schon eine Anmeldung liegt (wer von einer aelteren
|
|
45
|
+
* Fassung aktualisiert: Sabine mit englischem Windows sah bisher immer Deutsch) -> sonst das
|
|
46
|
+
* Betriebssystem.
|
|
47
|
+
*
|
|
48
|
+
* 🔑 DAS BEFEHLSWORT GANZ VORN (Durchstich, Gesamtpruefung Punkt 3 und Review a73305f, Befund 1):
|
|
49
|
+
* `kimal3 setup` tippt nur die englische Anleitung, und die wartet auf `Sign-in code:`. Jim mit
|
|
50
|
+
* englischem Konto und deutschem Windows bekam sonst `Anmelde-Code:`. Auch VOR dem Merkzettel:
|
|
51
|
+
* Der uebersteht `logout` -- meldet Jim auf Kemals Laptop erst Kemal ab, laege dort Kemals
|
|
52
|
+
* Deutsch. Gilt nur bis zur ersten Antwort des Servers; ab da die Kontosprache
|
|
53
|
+
* (`uebernimmKontosprache`).
|
|
46
54
|
*/
|
|
47
55
|
export function startSprache(lage) {
|
|
56
|
+
if (lage.befehlsSprache !== null)
|
|
57
|
+
return lage.befehlsSprache;
|
|
48
58
|
const gemerkt = alsSprache(lage.merkzettel);
|
|
49
59
|
if (gemerkt !== null)
|
|
50
60
|
return gemerkt;
|
|
@@ -78,12 +88,24 @@ function leseMerkzettel() {
|
|
|
78
88
|
}
|
|
79
89
|
let gemerkt;
|
|
80
90
|
let aktuell = null;
|
|
91
|
+
let befehlsSprache = null;
|
|
92
|
+
/**
|
|
93
|
+
* Der Verteiler meldet hier die Sprache des getippten Befehlsworts -- nur fuer `einrichten`/`setup`,
|
|
94
|
+
* den einen Befehl, den eine Anleitung auf einem frischen Rechner aufruft und dessen Ausgabe sie
|
|
95
|
+
* liest. Er laeuft vor der ersten Ausgabe; `aktuell` wird trotzdem verworfen, damit die Reihenfolge
|
|
96
|
+
* nicht stillschweigend daran haengt.
|
|
97
|
+
*/
|
|
98
|
+
export function merkeBefehlsSprache(s) {
|
|
99
|
+
befehlsSprache = s;
|
|
100
|
+
aktuell = null;
|
|
101
|
+
}
|
|
81
102
|
/** Die Sprache dieses Laufs. */
|
|
82
103
|
export function sprache() {
|
|
83
104
|
if (aktuell === null) {
|
|
84
105
|
gemerkt = leseMerkzettel();
|
|
85
106
|
aktuell = startSprache({
|
|
86
107
|
merkzettel: gemerkt,
|
|
108
|
+
befehlsSprache,
|
|
87
109
|
angemeldet: leseAnmeldung() !== null,
|
|
88
110
|
systemGebiet: Intl.DateTimeFormat().resolvedOptions().locale,
|
|
89
111
|
});
|
|
@@ -97,10 +119,12 @@ export function sprache() {
|
|
|
97
119
|
*/
|
|
98
120
|
export function uebernimmKontosprache(kopf) {
|
|
99
121
|
sprache();
|
|
122
|
+
// 🔑 `aktuell` folgt dem Kopf auch dann, wenn der Merkzettel schon dieselbe Sprache traegt --
|
|
123
|
+
// sonst bliebe ein Lauf, der mit dem Befehlswort begann (`setup` bei deutschem Konto), englisch.
|
|
124
|
+
aktuell = alsSprache(kopf) ?? aktuell;
|
|
100
125
|
const neu = nachAntwort(gemerkt, kopf);
|
|
101
126
|
if (neu === null)
|
|
102
127
|
return;
|
|
103
|
-
aktuell = neu;
|
|
104
128
|
gemerkt = neu;
|
|
105
129
|
try {
|
|
106
130
|
// ⚠️ `mode: 0o700` wie in anmeldung.ts und gesehen.ts -- wer den Ordner zuerst anlegt, legt
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Die Zeilenanfaenge, an denen Claude die Ausgabe von `kimal3 einrichten` liest
|
|
3
|
+
* (Anleitung `/einrichten` und `/setup`, Schritt 3). Eine Stelle fuer beide: Das
|
|
4
|
+
* Programm druckt sie, die Anleitung nennt sie, `check:kurs` haelt beide gleich.
|
|
5
|
+
*/
|
|
6
|
+
export const EINRICHTEN_MARKEN = {
|
|
7
|
+
de: { code: "Anmelde-Code:", adresse: "Adresse:", arbeitsplatz: "Arbeitsplatz:", werkbank: "Werkbank:", angemeldet: "Angemeldet als", plugins: "Plugins:" },
|
|
8
|
+
en: { code: "Sign-in code:", adresse: "Address:", arbeitsplatz: "Workplace:", werkbank: "Workbench:", angemeldet: "Signed in as", plugins: "Plugins:" },
|
|
9
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EINRICHTEN_MARKEN } from "./einrichten-marken.js";
|
|
2
|
+
const M = EINRICHTEN_MARKEN;
|
|
3
|
+
const de = {
|
|
4
|
+
codeLine: (code) => `\n ${M.de.code} ${code}`,
|
|
5
|
+
addressLine: (url) => ` ${M.de.adresse} ${url}`,
|
|
6
|
+
browserOpened: " Ich öffne die Seite gerade in deinem Browser. Bestätige dort nur, wenn derselbe Code dasteht.",
|
|
7
|
+
browserNotOpened: " Öffne die Adresse oben in deinem Browser. Bestätige dort nur, wenn derselbe Code dasteht.",
|
|
8
|
+
help: "\n kimal3 einrichten [--firma \"Name\" | --firma-id \"…\"]\n\n Meldet dich an (der Browser öffnet sich mit deinem Code), legt deinen\n KI-Arbeitsplatz in deinem Benutzerordner an, richtet die /k3-Befehle und\n die Wache ein und hakt die Schritte im Kurs ab. Ein zweiter Lauf ergänzt\n nur, was fehlt.\n",
|
|
9
|
+
start: "\nIch richte deinen KI-Arbeitsplatz ein.",
|
|
10
|
+
filesWritten: (neu, schonDa) => `\n✓ ${neu} Dateien angelegt, ${schonDa} lagen schon da.`,
|
|
11
|
+
workplaceLine: (pfad) => ` ${M.de.arbeitsplatz} ${pfad}`,
|
|
12
|
+
workbenchLine: (name) => ` ${M.de.werkbank} ${name}`,
|
|
13
|
+
courseMarked: "✓ Im Kurs abgehakt.",
|
|
14
|
+
courseNotMarked: " Die Haken im Kurs konnte ich gerade nicht setzen — der nächste Lauf holt das nach.",
|
|
15
|
+
done: "\n✓ Fertig eingerichtet.",
|
|
16
|
+
unsafeFolder: (name) => `Der Server hat einen Ordnernamen geschickt, den ich nicht anlege: ${name}. Bitte melde das an kimal3.`,
|
|
17
|
+
noWorkbank: "In der Lieferung steckt keine Werkbank. Bitte melde das an kimal3.",
|
|
18
|
+
// Plugins (Bauplan 2026-10-15): Claude liest die Zeile und sagt der Kundin, was nach dem Neustart erscheint.
|
|
19
|
+
logoName: "KImal3-Logo",
|
|
20
|
+
// `an`: eingeschaltet · `aus`: von ihr selbst ausgeschaltet (bleibt so, die Anleitung schickt sie dann nicht ins Menü)
|
|
21
|
+
// · `neu`: gerade eingetragen (kommt mit dem Neustart) oder stand schon da (Durchsicht-Funde 1 und 7).
|
|
22
|
+
pluginsLine: (an, aus, neu) => ` ${M.de.plugins} ${an.length > 0 ? `${an.join(", ")} — ${neu ? "erscheinen, sobald Claude ganz beendet und neu geöffnet ist" : "sind schon eingetragen"}.` : "nichts eingeschaltet."}` +
|
|
23
|
+
(aus.length > 0 ? ` Selbst ausgeschaltet, bleibt aus: ${aus.join(", ")}.` : ""),
|
|
24
|
+
pluginsNotWritten: (grund) => ` ${M.de.plugins} nicht eingetragen — ${grund}`,
|
|
25
|
+
settingsInvalid: "deine Claude-Einstellungen (~/.claude/settings.json) sind kein gültiges JSON. Ich habe sie nicht angefasst.",
|
|
26
|
+
settingsForeign: "in deinen Claude-Einstellungen (~/.claude/settings.json) steht etwas in einer Form, die ich nicht kenne. Ich habe sie nicht angefasst.",
|
|
27
|
+
settingsFailed: (ursache) => `deine Claude-Einstellungen (~/.claude/settings.json): ${ursache}.`,
|
|
28
|
+
// Logo (Durchsicht 2, Funde 1 und 3): Scheitert der Ordner, kommt superpowers trotzdem; hält Claude ihn fest, gilt die alte Fassung.
|
|
29
|
+
logoNotWritten: (detail) => ` Den Ordner für das KImal3-Logo konnte ich nicht anlegen oder erneuern (${detail}).`,
|
|
30
|
+
// Ohne Git kein superpowers (Durchsicht 3, Fund 5); die Anleitung zitiert den Anfang dieser Zeile.
|
|
31
|
+
superpowersNeedsGit: " superpowers braucht Git — es kommt mit dem nächsten `kimal3 einrichten`, sobald Git da ist.",
|
|
32
|
+
logoOldVersion: " Das KImal3-Logo bleibt vorerst in der bisherigen Fassung, weil Claude den Ordner gerade benutzt. Beende Claude ganz und starte danach `kimal3 einrichten` noch einmal.",
|
|
33
|
+
};
|
|
34
|
+
const en = {
|
|
35
|
+
codeLine: (code) => `\n ${M.en.code} ${code}`,
|
|
36
|
+
addressLine: (url) => ` ${M.en.adresse} ${url}`,
|
|
37
|
+
browserOpened: " Opening the page in your browser now. Only confirm there if it shows the same code.",
|
|
38
|
+
browserNotOpened: " Open the address above in your browser. Only confirm there if it shows the same code.",
|
|
39
|
+
help: "\n kimal3 setup [--company \"Name\" | --company-id \"…\"]\n\n Signs you in (your browser opens with your code), creates your AI Workplace\n in your user folder, sets up the /k3 commands and the watch, and ticks off\n the course steps. A second run only adds what is missing.\n",
|
|
40
|
+
start: "\nSetting up your AI Workplace.",
|
|
41
|
+
filesWritten: (neu, schonDa) => `\n✓ ${neu} files created, ${schonDa} were already there.`,
|
|
42
|
+
workplaceLine: (pfad) => ` ${M.en.arbeitsplatz} ${pfad}`,
|
|
43
|
+
workbenchLine: (name) => ` ${M.en.werkbank} ${name}`,
|
|
44
|
+
courseMarked: "✓ Ticked off in the course.",
|
|
45
|
+
courseNotMarked: " I couldn't tick off the course steps just now — the next run will catch up.",
|
|
46
|
+
done: "\n✓ All set up.",
|
|
47
|
+
unsafeFolder: (name) => `The server sent a folder name I won't create: ${name}. Please report this to kimal3.`,
|
|
48
|
+
noWorkbank: "The delivery contains no workbench. Please report this to kimal3.",
|
|
49
|
+
logoName: "KImal3 logo",
|
|
50
|
+
pluginsLine: (an, aus, neu) => ` ${M.en.plugins} ${an.length > 0 ? `${an.join(", ")} — ${neu ? "will appear once Claude is fully quit and reopened" : "already added"}.` : "nothing switched on."}` +
|
|
51
|
+
(aus.length > 0 ? ` Switched off by you, stays off: ${aus.join(", ")}.` : ""),
|
|
52
|
+
pluginsNotWritten: (grund) => ` ${M.en.plugins} not added — ${grund}`,
|
|
53
|
+
settingsInvalid: "your Claude settings (~/.claude/settings.json) are not valid JSON. I left them untouched.",
|
|
54
|
+
settingsForeign: "your Claude settings (~/.claude/settings.json) contain something in a form I don't know. I left them untouched.",
|
|
55
|
+
settingsFailed: (ursache) => `your Claude settings (~/.claude/settings.json): ${ursache}.`,
|
|
56
|
+
logoNotWritten: (detail) => ` I couldn't create or renew the folder for the KImal3 logo (${detail}).`,
|
|
57
|
+
superpowersNeedsGit: " superpowers needs Git — it comes with the next `kimal3 setup` once Git is there.",
|
|
58
|
+
logoOldVersion: " The KImal3 logo stays on its previous version for now, because Claude is using the folder. Quit Claude completely, then run `kimal3 setup` again.",
|
|
59
|
+
};
|
|
60
|
+
export const EINRICHTEN_TEXTE = { de, en };
|