kimal3 0.2.10 → 0.3.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 +135 -0
- 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 +98 -87
- package/dist/befehle-katalog-en.js +185 -0
- package/dist/befehle-katalog.js +10 -49
- 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 +64 -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 +2 -2
- package/skill/SKILL.md +9 -9
- package/skill/commands/k3.md +1 -1
- 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/README.md
CHANGED
|
@@ -135,3 +135,138 @@ Proprietär. Die Nutzung ist Inhabern eines gültigen KImal3-Pro-Kontos gestatte
|
|
|
135
135
|
Weitergabe, Veränderung und Weiterverwendung des Codes sind nicht erlaubt.
|
|
136
136
|
|
|
137
137
|
© KImal3 · https://kimal3.de
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
# kimal3 (English)
|
|
142
|
+
|
|
143
|
+
**Log your AI projects where they happen: in the terminal.**
|
|
144
|
+
|
|
145
|
+
If you work with Claude Code, you build real things — and then never write them up, because that would be an extra step in another window. `kimal3` closes that gap: at the end
|
|
146
|
+
of a session, Claude adds the finished project to your KImal3 success profile itself. All
|
|
147
|
+
you do is approve it there.
|
|
148
|
+
|
|
149
|
+
> **Requirement: a KImal3 account with Pro.**
|
|
150
|
+
> Without Pro, this tool tells you so and does nothing else.
|
|
151
|
+
> What Pro includes and how to get it: **https://app.kimal3.de/pro**
|
|
152
|
+
|
|
153
|
+
## Getting started
|
|
154
|
+
|
|
155
|
+
Once per computer:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npm install -g kimal3 # install the tool (needs Node.js)
|
|
159
|
+
kimal3 login # connect this computer to your account
|
|
160
|
+
kimal3 skill install # teach Claude to use the tool — and get the /k3 commands
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> **In Claude Code?** Then have it set this up for you instead of typing it — the text to copy is on **https://app.kimal3.de/pro**. The only thing you type yourself is `kimal3 login` (with a `!` in front, there), because confirming it in the browser needs a person.
|
|
164
|
+
|
|
165
|
+
`login` shows you an address and an eight-character code. You open the address yourself and
|
|
166
|
+
confirm the code there, in your signed-in account — just like `gh auth login`.
|
|
167
|
+
**No password is ever typed into the terminal.**
|
|
168
|
+
|
|
169
|
+
## Commands
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
kimal3 Shows whether this computer is signed in
|
|
173
|
+
kimal3 login Connect this computer to your account
|
|
174
|
+
kimal3 skill install Teach Claude to use this tool — and give you the /k3 commands
|
|
175
|
+
kimal3 workplace new Create the whole AI Workplace — company level and workbench
|
|
176
|
+
kimal3 workbench new Only create your own workbench in an existing company
|
|
177
|
+
kimal3 context download Fetch the company context — CLAUDE.md and the five files in context/
|
|
178
|
+
kimal3 context questions Answer the questions about your company — Claude takes you through them
|
|
179
|
+
kimal3 context upload Upload your local changes to the five company texts
|
|
180
|
+
kimal3 project new … Add a project to your success profile
|
|
181
|
+
kimal3 project list Show the projects you've added
|
|
182
|
+
kimal3 company data Fetch your company's data so Claude can analyse it
|
|
183
|
+
kimal3 watch Runs by itself when Claude starts: checks your access, messages, version and skill
|
|
184
|
+
kimal3 watch off Switch the daily check off — it then says nothing at all
|
|
185
|
+
kimal3 watch on Switch it back on
|
|
186
|
+
kimal3 logout Disconnect this computer
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The German command names in the section above work too.
|
|
190
|
+
|
|
191
|
+
More about one command: `kimal3 project new --help`
|
|
192
|
+
|
|
193
|
+
## What gets recorded
|
|
194
|
+
|
|
195
|
+
Only what you enter — title, description, optionally a link. **Your code is not read,
|
|
196
|
+
not uploaded and not analysed.** The tool only writes through the KImal3 app and has
|
|
197
|
+
no database access of its own.
|
|
198
|
+
|
|
199
|
+
Projects you add are **not public** at first. They appear in your success profile, and you
|
|
200
|
+
decide there what becomes visible.
|
|
201
|
+
|
|
202
|
+
## Exit codes
|
|
203
|
+
|
|
204
|
+
For scripts and for Claude itself:
|
|
205
|
+
|
|
206
|
+
| Value | Meaning | What helps |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `0` | it worked | — |
|
|
209
|
+
| `1` | input incomplete, nothing found — or your Claude settings can't be read | read the message, it names the case |
|
|
210
|
+
| `2` | not signed in | `kimal3 login` |
|
|
211
|
+
| `3` | can't be reached right now | try again later |
|
|
212
|
+
| `4` | no Pro account | trying again does **not** help |
|
|
213
|
+
|
|
214
|
+
The difference between `3` and `4` is deliberate: with `3` a second attempt is worth it,
|
|
215
|
+
with `4` it never is.
|
|
216
|
+
|
|
217
|
+
## Update notices
|
|
218
|
+
|
|
219
|
+
If a newer version is available, `kimal3` writes one indented line after the command — at
|
|
220
|
+
most **once a day**:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
→ There's a new version of kimal3. npm i -g kimal3@latest && kimal3 skill install
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
It goes to **stderr**, not stdout: `kimal3 company data > company.json` stays clean JSON.
|
|
227
|
+
It **doesn't change the exit code** — a `0` stays a `0`. And `kimal3` **never installs
|
|
228
|
+
anything by itself**; the line shows the command; running it is up to you.
|
|
229
|
+
|
|
230
|
+
The same goes for the skill files: if the ones in `~/.claude/skills/kimal3/` are older than
|
|
231
|
+
the installed tool, or in a different language from your account, `kimal3` says so — this
|
|
232
|
+
works without an internet connection.
|
|
233
|
+
|
|
234
|
+
## The daily check — and how to switch it off
|
|
235
|
+
|
|
236
|
+
`kimal3 skill install` adds a line to your Claude settings (`~/.claude/settings.json`). It
|
|
237
|
+
makes `kimal3 watch` run when Claude starts — silent when nothing is pending; an open task
|
|
238
|
+
(access, version, skill) is reported in every session until it's done. The check looks at
|
|
239
|
+
whether your access is still valid, whether a newer version is available, whether your `/k3`
|
|
240
|
+
commands are out of date and whether there is a message from us.
|
|
241
|
+
|
|
242
|
+
**It only speaks up when something is really pending.** If everything is in order, it says
|
|
243
|
+
nothing.
|
|
244
|
+
|
|
245
|
+
If you don't want it:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
kimal3 watch off # silent from now on
|
|
249
|
+
kimal3 watch on # back on again
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
After `kimal3 watch off`, **nothing is sent to our server any more** — the check stops before it asks our server anything. Measured: about 0.9 seconds with the check, about 0.19 seconds without, and in
|
|
253
|
+
those 0.19 seconds not a single byte leaves your computer.
|
|
254
|
+
|
|
255
|
+
The line in the settings stays, so that `kimal3 watch on` takes effect immediately and we
|
|
256
|
+
don't have to touch your settings file again. So a small program of ours still starts, and
|
|
257
|
+
stops again after about 0.19 seconds.
|
|
258
|
+
|
|
259
|
+
**If you don't want even that**, remove the line yourself: in `~/.claude/settings.json`,
|
|
260
|
+
under `hooks` → `SessionStart`, there is an entry whose command starts with `kimal3 wache` —
|
|
261
|
+
today `kimal3 wache --hook`, in older installations without `--hook`. ⚠️ **The `--hook` part belongs in the settings file, not in the terminal** — all you ever type is
|
|
262
|
+
`kimal3 watch`. Delete this one entry — leave the rest of the file as it is.
|
|
263
|
+
|
|
264
|
+
## Requirements
|
|
265
|
+
|
|
266
|
+
Node.js 20 or newer. Nothing else — the tool comes with **no third-party libraries**.
|
|
267
|
+
|
|
268
|
+
## Licence
|
|
269
|
+
|
|
270
|
+
Proprietary. Use is permitted for holders of a valid KImal3 Pro account; redistributing, modifying or reusing the code is not permitted.
|
|
271
|
+
|
|
272
|
+
© KImal3 · https://kimal3.de
|
package/dist/api.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { BASIS_URL, leseAnmeldung } from "./anmeldung.js";
|
|
2
2
|
import { hinweisAusAntwort } from "./nachricht.js";
|
|
3
3
|
import { eigeneVersion } from "./version.js";
|
|
4
|
+
import { KONTO_KOPF, SPRACH_KOPF, sprache, uebernimmKontosprache } from "./sprache.js";
|
|
4
5
|
import { findeArbeitsplatz, leseMerkzettelDatei } from "./kontext-dateien.js";
|
|
5
6
|
import { bekannterStandAus, istKopfwertSicher } from "./kontext-stand.js";
|
|
6
7
|
export class NichtAngemeldet extends Error {
|
|
7
|
-
constructor() { super("nicht angemeldet"); }
|
|
8
|
+
constructor() { super("nicht angemeldet"); } // kein-anzeigetext: Fehlername, kimal3.ts faengt ihn ab und druckt ihn nie
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Der EINE Weg, auf dem diese Datei den lokalen Merkzettel liest -- Firmen-ID
|
|
@@ -48,13 +49,13 @@ export class NichtAngemeldet extends Error {
|
|
|
48
49
|
*/
|
|
49
50
|
function lokaleKontextLage() {
|
|
50
51
|
try {
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
52
|
+
const fund = findeArbeitsplatz(process.cwd());
|
|
53
|
+
if (fund.art !== "gefunden")
|
|
53
54
|
return null;
|
|
54
|
-
const merkzettel = leseMerkzettelDatei(
|
|
55
|
+
const merkzettel = leseMerkzettelDatei(fund.pfad);
|
|
55
56
|
if (merkzettel.firma_id === null)
|
|
56
57
|
return null;
|
|
57
|
-
return { firmaId: merkzettel.firma_id, stand: bekannterStandAus(merkzettel) };
|
|
58
|
+
return { firmaId: merkzettel.firma_id, stand: bekannterStandAus(merkzettel, fund.sprache) };
|
|
58
59
|
}
|
|
59
60
|
catch {
|
|
60
61
|
return null;
|
|
@@ -106,7 +107,7 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
106
107
|
const a = leseAnmeldung();
|
|
107
108
|
if (!a)
|
|
108
109
|
throw new NichtAngemeldet();
|
|
109
|
-
kopf.Authorization = `Bearer ${a.schluessel}`;
|
|
110
|
+
kopf.Authorization = `Bearer ${a.schluessel}`; // kein-anzeigetext: HTTP-Kopf
|
|
110
111
|
}
|
|
111
112
|
/*
|
|
112
113
|
🔴 DIE CLI SAGT, WER SIE IST -- und nur daran haengt der Server den
|
|
@@ -128,6 +129,12 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
128
129
|
const version = eigeneVersion();
|
|
129
130
|
if (version !== null)
|
|
130
131
|
kopf["X-Kimal3-Version"] = version;
|
|
132
|
+
/*
|
|
133
|
+
🌐 Teil 3 (T3-6): Die Sprache, in der dieses Programm gerade spricht, reist bei JEDEM Aufruf
|
|
134
|
+
mit. Der Server antwortet dann in der Kontosprache -- oder, wenn er den Menschen nicht kennt,
|
|
135
|
+
in dieser. Fehlt der Kopf (jede aeltere Fassung), antwortet er deutsch wie bisher.
|
|
136
|
+
*/
|
|
137
|
+
kopf[SPRACH_KOPF] = sprache();
|
|
131
138
|
/*
|
|
132
139
|
🔴 DER ZWEITE KOPF (Task 10) -- der lokal bekannte Firmenstand, damit der
|
|
133
140
|
Server einen firmenbezogenen Hinweis anhaengen kann ("deine Firma hat den
|
|
@@ -152,7 +159,7 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
152
159
|
const kontextKopf = kontextStandKopf();
|
|
153
160
|
if (kontextKopf !== null)
|
|
154
161
|
kopf["X-Kimal3-Kontext-Stand"] = kontextKopf;
|
|
155
|
-
const methode = optionen.methode ?? "POST";
|
|
162
|
+
const methode = optionen.methode ?? "POST"; // kein-anzeigetext: HTTP-Methode
|
|
156
163
|
// Content-Type nur, wenn wirklich etwas im Rumpf steht. Ein GET mit
|
|
157
164
|
// "Content-Type: application/json" und ohne Rumpf ist eine Luege ueber die
|
|
158
165
|
// eigene Anfrage — manche Server nehmen das uebel.
|
|
@@ -163,6 +170,8 @@ export async function ruf(pfad, optionen = {}) {
|
|
|
163
170
|
headers: kopf,
|
|
164
171
|
body: methode === "POST" ? JSON.stringify(optionen.koerper ?? {}) : undefined,
|
|
165
172
|
});
|
|
173
|
+
// 🌐 Nennt die Antwort eine Kontosprache, gilt sie ab sofort (Spec 3.2, Schritt 3).
|
|
174
|
+
uebernimmKontosprache(antwort.headers.get(KONTO_KOPF));
|
|
166
175
|
const koerper = await antwort.json().catch(() => null);
|
|
167
176
|
merkeHinweis(koerper);
|
|
168
177
|
return { status: antwort.status, koerper };
|
|
@@ -32,6 +32,9 @@ import { angeboteneFirmen, firmaAusOrdner } from "./firmen-liste.js";
|
|
|
32
32
|
statt weg-behauptet zu werden.
|
|
33
33
|
*/
|
|
34
34
|
import { absage } from "./befehle/kontext-gemeinsam.js";
|
|
35
|
+
import { sprache } from "./sprache.js";
|
|
36
|
+
import { ALLGEMEIN_TEXTE } from "./texte/allgemein.js";
|
|
37
|
+
import { ARBEITSPLATZ_TEXTE } from "./texte/arbeitsplatz.js";
|
|
35
38
|
/*
|
|
36
39
|
🔁 Die Ableitung „welcher Unterordner ist die Werkbank" wohnt seit dem
|
|
37
40
|
2026-08-20 in `werkbank-name.ts` -- gemeinsam mit der App, die sie fuer `/pro`
|
|
@@ -95,7 +98,7 @@ export function firmenSuche(wunsch) {
|
|
|
95
98
|
* - Scheitert die zweite Anfrage mit `409`, sieht er die ERSTE Rückfrage, nicht
|
|
96
99
|
* eine über einen Namen, den er nie getippt hat (L11).
|
|
97
100
|
*/
|
|
98
|
-
export async function vorlageHolen(befehl, wunsch, ordner) {
|
|
101
|
+
export async function vorlageHolen(befehl, optionen, wunsch, ordner) {
|
|
99
102
|
const antwort = await holeVorlage(wunsch);
|
|
100
103
|
if (antwort.status === 200)
|
|
101
104
|
return lesbar(antwort.koerper, false);
|
|
@@ -111,7 +114,7 @@ export async function vorlageHolen(befehl, wunsch, ordner) {
|
|
|
111
114
|
// Netz, Ausweis, Pro: deren eigene Meldung. Nur eine zweite Rückfrage wird
|
|
112
115
|
// durch die erste ersetzt (L11).
|
|
113
116
|
if (zweite.status !== 409)
|
|
114
|
-
return { ok: false, wert: absage(zweite, befehl) };
|
|
117
|
+
return { ok: false, wert: absage(zweite, befehl, optionen) };
|
|
115
118
|
}
|
|
116
119
|
/*
|
|
117
120
|
🔴 **Hier standen dieselben vier Zweige noch einmal abgetippt** (402 vor
|
|
@@ -124,15 +127,15 @@ export async function vorlageHolen(befehl, wunsch, ordner) {
|
|
|
124
127
|
*Ein Zweig, den es nie zu sehen gab, fehlt niemandem, bis der Server
|
|
125
128
|
anfängt zu fragen.*
|
|
126
129
|
*/
|
|
127
|
-
return { ok: false, wert: absage(antwort, befehl) };
|
|
130
|
+
return { ok: false, wert: absage(antwort, befehl, optionen) };
|
|
128
131
|
}
|
|
129
132
|
function holeVorlage(wunsch) {
|
|
130
|
-
return ruf(`/api/cli/arbeitsplatz${firmenSuche(wunsch)}`, { methode: "GET", mitAusweis: true });
|
|
133
|
+
return ruf(`/api/cli/arbeitsplatz${firmenSuche(wunsch)}`, { methode: "GET", mitAusweis: true }); // kein-anzeigetext: Pfad der Schnittstelle und HTTP-Verb
|
|
131
134
|
}
|
|
132
135
|
function lesbar(koerper, uebernommen) {
|
|
133
136
|
const daten = alsVorlageAntwort(koerper);
|
|
134
137
|
if (daten === null) {
|
|
135
|
-
console.error(
|
|
138
|
+
console.error(ALLGEMEIN_TEXTE[sprache()].unreadableAnswer);
|
|
136
139
|
return { ok: false, wert: 3 };
|
|
137
140
|
}
|
|
138
141
|
return { ok: true, daten, uebernommen };
|
|
@@ -205,9 +208,10 @@ export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
|
205
208
|
// Dateien -- sonst meldet der Befehl "✓ 0 Dateien angelegt." und legt dabei
|
|
206
209
|
// nicht einmal einen Ordner an. Derselbe Rueckgabewert wie bei anderen
|
|
207
210
|
// unlesbaren Antworten (3): der Nutzer hat hier nichts falsch gemacht.
|
|
211
|
+
const t = ARBEITSPLATZ_TEXTE[sprache()];
|
|
208
212
|
if (dateien.length === 0) {
|
|
209
|
-
console.error(
|
|
210
|
-
console.error(
|
|
213
|
+
console.error(t.noFiles1);
|
|
214
|
+
console.error(t.noFiles2);
|
|
211
215
|
return 3;
|
|
212
216
|
}
|
|
213
217
|
const ziel = resolve(ordnerName);
|
|
@@ -232,8 +236,8 @@ export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
|
232
236
|
nicht hineinwechseln. Der Rat war in diesem Zweig unausfuehrbar.
|
|
233
237
|
*Ein Parameter, der zu einem Zweig passt, gehoert nicht in alle drei.*
|
|
234
238
|
*/
|
|
235
|
-
console.error(
|
|
236
|
-
console.error(
|
|
239
|
+
console.error(t.isAFile1(ordnerName));
|
|
240
|
+
console.error(t.isAFile2);
|
|
237
241
|
return 1;
|
|
238
242
|
}
|
|
239
243
|
/*
|
|
@@ -247,22 +251,22 @@ export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
|
247
251
|
Bewusst eine geschlossene Liste und kein "alles, was mit Punkt anfaengt":
|
|
248
252
|
Eine `.env` oder ein `.git` IST Inhalt, und den fassen wir nicht an.
|
|
249
253
|
*/
|
|
250
|
-
const OS_KRIMSKRAMS = [".DS_Store", "desktop.ini", "Thumbs.db"];
|
|
254
|
+
const OS_KRIMSKRAMS = [".DS_Store", "desktop.ini", "Thumbs.db"]; // kein-anzeigetext: Dateinamen des Betriebssystems
|
|
251
255
|
const echterInhalt = readdirSync(ziel).filter((n) => !OS_KRIMSKRAMS.includes(n));
|
|
252
256
|
if (echterInhalt.length > 0) {
|
|
253
|
-
console.error(
|
|
254
|
-
console.error(
|
|
257
|
+
console.error(t.notEmpty1(ordnerName));
|
|
258
|
+
console.error(t.notEmpty2);
|
|
255
259
|
console.error(hinweis);
|
|
256
260
|
return 1;
|
|
257
261
|
}
|
|
258
262
|
}
|
|
259
263
|
}
|
|
260
264
|
catch (fehler) {
|
|
261
|
-
console.error(
|
|
262
|
-
console.error(
|
|
265
|
+
console.error(t.cannotReach1(ordnerName));
|
|
266
|
+
console.error(t.reason(fehler instanceof Error ? fehler.message : String(fehler)));
|
|
263
267
|
// ⚠️ Ebenfalls OHNE den `hinweis` (Runde 2): Wer den Ordner nicht lesen darf,
|
|
264
268
|
// kann auch nicht hineinwechseln. Hier hilft nur, was schon dasteht.
|
|
265
|
-
console.error(
|
|
269
|
+
console.error(t.cannotReach2);
|
|
266
270
|
return 1;
|
|
267
271
|
}
|
|
268
272
|
// 🔴 B-6: ALLE Pfade pruefen, BEVOR die erste Datei geschrieben wird. Wuerde
|
|
@@ -272,7 +276,7 @@ export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
|
272
276
|
// je erfaehrt, dass der Ordner unvollstaendig ist.
|
|
273
277
|
for (const datei of dateien) {
|
|
274
278
|
if (istUnsichererPfad(datei.pfad)) {
|
|
275
|
-
console.error(
|
|
279
|
+
console.error(t.unsafePath(datei.pfad));
|
|
276
280
|
return 3;
|
|
277
281
|
}
|
|
278
282
|
}
|
|
@@ -304,9 +308,9 @@ export function dateienSchreiben(dateien, ordnerName, hinweis) {
|
|
|
304
308
|
`readdirSync`-catch. *Ein Fehler, der einmal verstanden und
|
|
305
309
|
aufgeschrieben wurde, ist im Nachbar-catch trotzdem wieder neu.*
|
|
306
310
|
*/
|
|
307
|
-
console.error(
|
|
308
|
-
console.error(
|
|
309
|
-
console.error(
|
|
311
|
+
console.error(t.writeFailed(fehler instanceof Error ? fehler.message : String(fehler)));
|
|
312
|
+
console.error(t.incomplete(ordnerName));
|
|
313
|
+
console.error(t.writeFailedBlame);
|
|
310
314
|
return 1;
|
|
311
315
|
}
|
|
312
316
|
return 0;
|
package/dist/argumente.js
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
* Uebergehen. Wer `--titl` tippt, soll es erfahren, statt ein Projekt ohne
|
|
7
7
|
* Titel anzulegen.
|
|
8
8
|
*/
|
|
9
|
+
import { SCHALTER_EN, deutscherSchalter } from "./befehlswoerter.js";
|
|
10
|
+
import { ALLGEMEIN_TEXTE } from "./texte/allgemein.js";
|
|
11
|
+
// `alsWort` wohnt seit Teil 3 in `wort.ts` (importfrei, die App liest es). Hier weiter
|
|
12
|
+
// erreichbar, damit kein Aufrufer im Programm umziehen muss.
|
|
13
|
+
import { alsWort } from "./wort.js";
|
|
14
|
+
export { alsWort };
|
|
9
15
|
/**
|
|
10
16
|
* Trennt die **vorderen Woerter** von den Optionen.
|
|
11
17
|
*
|
|
@@ -65,98 +71,8 @@ export function alsBefehlszeile(optionen) {
|
|
|
65
71
|
*/
|
|
66
72
|
return optionen.map((teil) => alsWort(teil)).join(" ");
|
|
67
73
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
* dieses Wort ergibt.
|
|
71
|
-
*
|
|
72
|
-
* 🔴 **Gegen Befund 4 der zweiten Review-Runde (2026-08-27).** An NEUN Stellen
|
|
73
|
-
* stand dieselbe Zeile — `wert.includes(" ") ? `"${wert}"` : wert` — und alle
|
|
74
|
-
* neun hatten dieselbe Lücke: Ein Wert, der selbst ein Anführungszeichen
|
|
75
|
-
* enthält, sprengt die Anführungszeichen, die man um ihn herum setzt.
|
|
76
|
-
*
|
|
77
|
-
* Der Fall: `kimal3 arbeitsplatz neu sabine --firma 'Blume "Nord" GmbH'`. Der
|
|
78
|
-
* angebotene Rat lautete danach `--firma "Blume "Nord" GmbH"`. Wer ihn
|
|
79
|
-
* kopiert, gibt seiner Kommandozeile **drei** Wörter statt einem und bekommt
|
|
80
|
-
* eine andere, verwirrendere Absage als die, die er gerade gelesen hat.
|
|
81
|
-
* *Ein Rat, der beim Kopieren zerfällt, ist ein Rat in eine zweite Absage* —
|
|
82
|
-
* genau die Lehre, gegen die `alsBefehlszeile` oben gebaut wurde.
|
|
83
|
-
*
|
|
84
|
-
* 🔑 **Warum einfache Anführungszeichen und keine Maskierung mit `\`:**
|
|
85
|
-
* `'…'` bedeutet in beiden Kommandozeilen dasselbe, die auf den Rechnern
|
|
86
|
-
* unserer Kunden laufen — in der von Mac/Linux (`sh`) wie in der von Windows
|
|
87
|
-
* (PowerShell). Eine Maskierung mit `\"` gilt nur in der ersten; in
|
|
88
|
-
* PowerShell müsste man das Zeichen verdoppeln. *Eine Bauform, die nur auf
|
|
89
|
-
* einem der beiden Rechner hält, ist in einem Produkt für beide keine
|
|
90
|
-
* Bauform.*
|
|
91
|
-
*
|
|
92
|
-
* 🔑 **`immer` heisst „setz die Anfuehrungszeichen auch dann, wenn es ohne
|
|
93
|
-
* ginge".** Die Zeilen unter „Deine Firmen:" haben sie seit jeher, und dort
|
|
94
|
-
* gehoeren sie hin: Sie sind zum Kopieren UND zum Weitertippen gedacht -- wer
|
|
95
|
-
* „Suedwind" durch „Blume & Co" ersetzt, hat die Anfuehrungszeichen dann schon
|
|
96
|
-
* stehen. Beim Zusammenbauen einer getippten Zeile (`alsBefehlszeile`) und
|
|
97
|
-
* beim `cd` waere das umgekehrt eine Aenderung an dem, was der Kunde bisher
|
|
98
|
-
* sieht -- deshalb dort die Vorgabe `false`.
|
|
99
|
-
*
|
|
100
|
-
* ⚠️ **Die eine Lage, die offen bleibt:** ein Wert mit BEIDEN Sorten
|
|
101
|
-
* Anführungszeichen. Dafür gibt es keine Schreibweise, die in beiden
|
|
102
|
-
* Kommandozeilen dasselbe heißt. Der Rückfall maskiert nach `sh`-Regeln — und
|
|
103
|
-
* für genau diesen Fall gibt es ohnehin `--firma-id`, das ohne jedes
|
|
104
|
-
* Sonderzeichen auskommt.
|
|
105
|
-
*/
|
|
106
|
-
export function alsWort(wert, immer = false) {
|
|
107
|
-
if (wert === "")
|
|
108
|
-
return '""';
|
|
109
|
-
/*
|
|
110
|
-
Was eine Kommandozeile nicht wörtlich stehen lässt: Leerzeichen (trennen),
|
|
111
|
-
Anführungszeichen beider Sorten (zitieren) -- und `$` wie ````
|
|
112
|
-
(rechnen).
|
|
113
|
-
|
|
114
|
-
⚠️ Der Rückwärtsstrich ist bewusst NICHT dabei: In einem Firmennamen
|
|
115
|
-
kommt er nicht vor, und ihn mitzunehmen hieße, ihn auch maskieren zu
|
|
116
|
-
müssen -- was in PowerShell wieder falsch wäre.
|
|
117
|
-
*/
|
|
118
|
-
/*
|
|
119
|
-
🔴 **EINE ERLAUBNISLISTE, KEINE VERBOTSLISTE** (Befund 1 der FÜNFTEN
|
|
120
|
-
Review-Runde, 2026-08-27) -- und die Verbotsliste hatte drei Runden
|
|
121
|
-
gebraucht, um dreimal zu lecken: erst fehlten die Anführungszeichen, dann
|
|
122
|
-
`$` und das Akzent-Zeichen, dann `&`, `;`, `|`, Klammern, `<`, `>`.
|
|
123
|
-
|
|
124
|
-
Der Fall, der es aufgedeckt hat: eine Firma namens `Blume&Co` -- **ohne**
|
|
125
|
-
Leerzeichen, also fiel sie durch jede Prüfung. Die angebotene Zeile hieß
|
|
126
|
-
`--firma Blume&Co`; in PowerShell ist das ein Syntaxfehler, in `sh`
|
|
127
|
-
startet es `--firma Blume` im Hintergrund und führt danach `Co` aus.
|
|
128
|
-
|
|
129
|
-
🔑 *Eine Liste dessen, was gefährlich ist, ist immer unvollständig -- die
|
|
130
|
-
Liste dessen, was harmlos ist, kann man zu Ende schreiben.* Also
|
|
131
|
-
umgekehrt: Buchstaben (auch Umlaute), Ziffern und eine Handvoll
|
|
132
|
-
unverfänglicher Satzzeichen bleiben unangetastet, **alles andere wird
|
|
133
|
-
zitiert**. Ein Anführungszeichen zu viel ist folgenlos; eines zu wenig
|
|
134
|
-
hat jetzt dreimal einen Rat zerlegt.
|
|
135
|
-
*/
|
|
136
|
-
if (!immer && /^[A-Za-z0-9À-ÿ._\/@:+-]+$/.test(wert))
|
|
137
|
-
return wert;
|
|
138
|
-
/*
|
|
139
|
-
🔴 **`$` UND ```` MÜSSEN AUS DEN DOPPELTEN ANFÜHRUNGSZEICHEN HERAUS**
|
|
140
|
-
(Befund 4 der dritten Review-Runde, 2026-08-27). Doppelte Anführungszeichen
|
|
141
|
-
halten Leerzeichen zusammen, aber sie schützen NICHT vor dem Rechnen: Eine
|
|
142
|
-
Firma namens `Cash$$ GmbH` ergab die Zeile
|
|
143
|
-
`--firma "Cash$$ GmbH"`, und `$$` ist in `sh` die Nummer des laufenden
|
|
144
|
-
Programms. Der Kunde kopiert den angebotenen Befehl und liest danach
|
|
145
|
-
*„Zu ‚Cash1234 GmbH' gehörst du nicht."* -- wieder ein Rat, der beim
|
|
146
|
-
Kopieren zerfällt, nur eine Stufe subtiler als der Fall, gegen den diese
|
|
147
|
-
Funktion gebaut wurde.
|
|
148
|
-
|
|
149
|
-
Deshalb: doppelte Anführungszeichen nur, wenn wirklich nichts Rechnendes
|
|
150
|
-
drinsteht. Sonst die einfachen, die in beiden Kommandozeilen alles wörtlich
|
|
151
|
-
nehmen.
|
|
152
|
-
*/
|
|
153
|
-
if (!/["$\`]/.test(wert))
|
|
154
|
-
return `"${wert}"`;
|
|
155
|
-
if (!wert.includes("'"))
|
|
156
|
-
return `'${wert}'`;
|
|
157
|
-
return `"${wert.replace(/(["$\`])/g, "\\$1")}"`;
|
|
158
|
-
}
|
|
159
|
-
export function zerlege(argv, erlaubt) {
|
|
74
|
+
export function zerlege(argv, erlaubt, sprache) {
|
|
75
|
+
const t = ALLGEMEIN_TEXTE[sprache];
|
|
160
76
|
const optionen = {};
|
|
161
77
|
for (let i = 0; i < argv.length; i++) {
|
|
162
78
|
const teil = argv[i];
|
|
@@ -167,13 +83,17 @@ export function zerlege(argv, erlaubt) {
|
|
|
167
83
|
if (!teil.startsWith("--")) {
|
|
168
84
|
return {
|
|
169
85
|
ok: false,
|
|
170
|
-
meldung:
|
|
86
|
+
meldung: t.cannotUseWord(teil),
|
|
171
87
|
};
|
|
172
88
|
}
|
|
173
89
|
const gleich = teil.indexOf("=");
|
|
174
|
-
|
|
90
|
+
// 🌐 Teil 3 (T3-4): `getippt` steht in jeder Meldung (der Mensch soll sein eigenes Wort
|
|
91
|
+
// wiedererkennen), `name` ist der deutsche Name, unter dem der Wert abgelegt wird und zum
|
|
92
|
+
// Server geht. Doppelt ist doppelt, egal in welcher Sprache (`--firma A --company B`, L-16).
|
|
93
|
+
const getippt = gleich === -1 ? teil.slice(2) : teil.slice(2, gleich);
|
|
94
|
+
const name = deutscherSchalter(getippt);
|
|
175
95
|
if (!erlaubt.includes(name)) {
|
|
176
|
-
return { ok: false, meldung:
|
|
96
|
+
return { ok: false, meldung: t.unknownOption(getippt) };
|
|
177
97
|
}
|
|
178
98
|
// ⚠️ **Dieselbe Option zweimal ist ein FEHLER** (QA-Befund B-3, 2026-08-18).
|
|
179
99
|
//
|
|
@@ -198,7 +118,7 @@ export function zerlege(argv, erlaubt) {
|
|
|
198
118
|
if (Object.hasOwn(optionen, name)) {
|
|
199
119
|
return {
|
|
200
120
|
ok: false,
|
|
201
|
-
meldung:
|
|
121
|
+
meldung: t.duplicateOption(getippt),
|
|
202
122
|
};
|
|
203
123
|
}
|
|
204
124
|
if (gleich !== -1) {
|
|
@@ -208,10 +128,18 @@ export function zerlege(argv, erlaubt) {
|
|
|
208
128
|
const wert = argv[i + 1];
|
|
209
129
|
// Eine Zahl mit Minus ist ein Wert; "--irgendwas" ist die naechste Option.
|
|
210
130
|
if (wert === undefined || wert.startsWith("--")) {
|
|
211
|
-
return { ok: false, meldung:
|
|
131
|
+
return { ok: false, meldung: t.missingValue(getippt) };
|
|
212
132
|
}
|
|
213
133
|
optionen[name] = wert;
|
|
214
134
|
i++;
|
|
215
135
|
}
|
|
216
136
|
return { ok: true, werte: { optionen } };
|
|
217
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Ein Schalter OHNE Wert (`--ueberschreiben`, `--ohne-firma`) in beiden Sprachen: gesetzt? -- und
|
|
140
|
+
* `argv` ohne ihn, fuer `zerlege`, das nur Schalter MIT Wert kennt.
|
|
141
|
+
*/
|
|
142
|
+
export function schalterOhneWert(argv, deutsch) {
|
|
143
|
+
const namen = [deutsch, ...Object.keys(SCHALTER_EN).filter((e) => SCHALTER_EN[e] === deutsch)].map((n) => `--${n}`);
|
|
144
|
+
return { gesetzt: argv.some((a) => namen.includes(a)), rest: argv.filter((a) => !namen.includes(a)) };
|
|
145
|
+
}
|