kimal3 0.9.0 → 0.9.2
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/kimal3-plan/.claude-plugin/plugin.json +7 -7
- package/claude-plugins/kimal3-plan/hooks/gespraech.ts +7 -0
- package/claude-plugins/kimal3-plan/hooks/lauf.ts +4 -2
- package/coach/de/kimal3-coach.md +1 -1
- package/coach/en/kimal3-coach.md +1 -1
- package/dist/anmeldung.js +0 -4
- package/dist/arbeitsplatz-dateien.js +8 -8
- package/dist/befehle/arbeitsplatz.js +11 -91
- package/dist/befehle/firma-daten.js +5 -67
- package/dist/befehle/kontext-gemeinsam.js +7 -40
- package/dist/befehle/name-und-firma.js +81 -0
- package/dist/befehle/skill-install.js +3 -2
- package/dist/befehle/werkbank.js +7 -75
- package/dist/firmen-liste.js +11 -4
- package/dist/meldungen-speicher.js +0 -1
- package/dist/wache-schalter.js +0 -8
- package/package.json +30 -30
- package/skill/commands/k3plan.md +3 -3
- package/skill/en/commands/k3plan.md +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kimal3-plan",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Hält das Board der Projektseite in der App aktuell: Punkt solange die Sitzung arbeitet, ob Claude gerade arbeitet oder wartet, „Besprechen“ an Claude weitergeben, Spec-Texte, hält Claude vor dem ersten Bau-Schritt an · Keeps the board on the app's project page current: dot while the session works, whether Claude is working or waiting, passes ‘Discuss’ on to Claude, spec texts, stops Claude before the first build step",
|
|
5
|
-
"author": { "name": "KImal3", "url": "https://kimal3.de" },
|
|
6
|
-
"types": "./types/index.d.ts"
|
|
7
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "kimal3-plan",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Hält das Board der Projektseite in der App aktuell: Punkt solange die Sitzung arbeitet, ob Claude gerade arbeitet oder wartet, „Besprechen“ an Claude weitergeben, Spec-Texte, hält Claude vor dem ersten Bau-Schritt an · Keeps the board on the app's project page current: dot while the session works, whether Claude is working or waiting, passes ‘Discuss’ on to Claude, spec texts, stops Claude before the first build step",
|
|
5
|
+
"author": { "name": "KImal3", "url": "https://kimal3.de" },
|
|
6
|
+
"types": "./types/index.d.ts"
|
|
7
|
+
}
|
|
@@ -67,6 +67,13 @@ export function gespraech(on: On, lauf: Lauf): void {
|
|
|
67
67
|
})
|
|
68
68
|
return next(e)
|
|
69
69
|
})
|
|
70
|
+
// US-6 (T-12): Beim Stop nennt Claude Code die laufende Hintergrund-Arbeit. Ein Befehl darin hält „arbeitet“, bis ein
|
|
71
|
+
// späterer Stop (die Runde, die seine Fertig-Meldung verarbeitet) ihn nicht mehr nennt. Subagenten zählen schon über ihre
|
|
72
|
+
// Runden; Monitore zählen nicht, weil einer ohne Ende laufen kann. Nur die Hauptrunde: Ein Subagent endet mit SubagentStop.
|
|
73
|
+
on('classic.Stop', ($, e, next) => {
|
|
74
|
+
aendere($, lauf, () => { lauf.hintergrund = (e.background_tasks ?? []).some(t => t.type === 'shell') })
|
|
75
|
+
return next(e)
|
|
76
|
+
})
|
|
70
77
|
// Nur beobachten: Die Abfrage selbst entscheidet der Mensch, der Haken antwortet nie mit `decision`.
|
|
71
78
|
on('classic.PermissionRequest', ($, e, next) => {
|
|
72
79
|
aendere($, lauf, () => {
|
|
@@ -39,6 +39,8 @@ export type Lauf = {
|
|
|
39
39
|
fehler: Set<Fehler>
|
|
40
40
|
/** Die offenen Runden (`turnId`), Hauptrunde und Subagenten: nicht leer = Claude arbeitet (hooks/gespraech.ts, E-4). */
|
|
41
41
|
runden: Set<string>
|
|
42
|
+
/** Beim letzten Stop der Hauptrunde lief ein Befehl im Hintergrund: Claude arbeitet weiter, auch ohne offene Runde (US-6, T-12). */
|
|
43
|
+
hintergrund: boolean
|
|
42
44
|
/** Worauf Claude gerade wartet (Erlaubnis, Fragefenster) und in welcher Schleife; `null`: auf nichts (E-5). */
|
|
43
45
|
wartet: { agent: string | undefined; werkzeug: string } | null
|
|
44
46
|
/** Das Gespräch aus der letzten Antwort der App; `null`: keins, oder eine App ohne „Im Gespräch“ (R-18). */
|
|
@@ -61,7 +63,7 @@ export type GespraechFuerMod = { nummer: number; spec: string; titel: string; da
|
|
|
61
63
|
|
|
62
64
|
/** Arbeitet Claude, wartet es, oder ist es fertig (hooks/gespraech.ts, E-4, E-5)? */
|
|
63
65
|
export function zustandVon(lauf: Lauf): Zustand {
|
|
64
|
-
if (lauf.runden.size === 0) return 'still'
|
|
66
|
+
if (lauf.runden.size === 0) return lauf.hintergrund ? 'arbeitet' : 'still'
|
|
65
67
|
return lauf.wartet === null ? 'arbeitet' : 'wartet'
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -88,7 +90,7 @@ export function neuerLauf(vorgabe: Sprache): Lauf {
|
|
|
88
90
|
return {
|
|
89
91
|
vorgabe, sprache: vorgabe, heim: null, basis: APP, lage: { art: 'kein-projektordner' }, inArbeit: null, meldungen: 0, specDateien: [],
|
|
90
92
|
takt: null, startet: false, generation: 0, beendet: false, abgelehnt: null, gemeldetFuer: null, meldungenPruefen: true,
|
|
91
|
-
fehler: new Set(), zeile: null, runden: new Set(), wartet: null, gespraech: null, abholen: null, nachzuegler: false, wartende: [],
|
|
93
|
+
fehler: new Set(), zeile: null, runden: new Set(), hintergrund: false, wartet: null, gespraech: null, abholen: null, nachzuegler: false, wartende: [],
|
|
92
94
|
}
|
|
93
95
|
}
|
|
94
96
|
|
package/coach/de/kimal3-coach.md
CHANGED
|
@@ -143,7 +143,7 @@ nutze sie, statt vorschnell loszulegen.
|
|
|
143
143
|
|
|
144
144
|
### 3.6 Das Board — wann du der Projektseite meldest
|
|
145
145
|
Gilt in jedem Projektordner (ein Ordner direkt in `werkbank-…/projekte/`). Anderswo meldest du nichts und fragst nichts.
|
|
146
|
-
Das Board in der App zeigt nur, was du meldest: Wer nicht meldet, lässt
|
|
146
|
+
Das Board in der App zeigt nur, was du meldest: Wer nicht meldet, lässt eine Karte stundenlang in „Plan“ stehen, während gebaut wird.
|
|
147
147
|
- **Das Projekt gibt es einmal je Ordner:** `kimal3 projekt verbinden`. Es schweigt (`0`), wenn der Ordner schon verbunden ist.
|
|
148
148
|
Legt es ein Projekt an, gibst du den **einen** Satz weiter, den es ausgibt („Ich habe das Projekt ‚…‘ in der App angelegt.“).
|
|
149
149
|
Gibt es zwei gleichnamige Projekte oder wurde es in der App gelöscht, fragt es: Frag den Menschen und wähle nie selbst; `--neu`
|
package/coach/en/kimal3-coach.md
CHANGED
|
@@ -143,7 +143,7 @@ use them instead of starting off too quickly.
|
|
|
143
143
|
|
|
144
144
|
### 3.6 The board — when you report to the project page
|
|
145
145
|
Applies in every project folder (a folder directly inside `workbench-…/projects/`). Elsewhere you report nothing and ask nothing.
|
|
146
|
-
The board in the app shows only what you report: if you don't,
|
|
146
|
+
The board in the app shows only what you report: if you don't, a card sits in "Plan" for hours while you build.
|
|
147
147
|
- **There is one project per folder:** `kimal3 project connect`. It stays silent (`0`) if the folder is already connected.
|
|
148
148
|
If it creates a project, you pass on the **one** sentence it prints ("I created the project '…' in the app.").
|
|
149
149
|
If two projects have the same name, or it was deleted in the app, it asks: ask the person and never choose yourself; `--new`
|
package/dist/anmeldung.js
CHANGED
|
@@ -365,14 +365,6 @@ export function fehlendeDateienSchreiben(dateien, ziel) {
|
|
|
365
365
|
return { ok: false, wert: 1 };
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
|
-
/**
|
|
369
|
-
* Ein `..`-Segment oder ein absoluter Pfad duerften das Ziel verlassen.
|
|
370
|
-
*
|
|
371
|
-
* 🔴 B-8: Zerlegt an `/` UND `\` -- eine fehlerhafte oder manipulierte
|
|
372
|
-
* Server-Antwort koennte sonst mit `..\..\boese.md` durchkommen. `join()`
|
|
373
|
-
* trennt unter Windows auch am Backslash, dieser Schutz muss also beide
|
|
374
|
-
* Trenner kennen wie `join()` selbst.
|
|
375
|
-
*/
|
|
376
368
|
/**
|
|
377
369
|
* Taugt das als **Ordnername**? — schaerfer als `istUnsichererPfad`.
|
|
378
370
|
*
|
|
@@ -405,6 +397,14 @@ export function istUnsichererOrdnername(name) {
|
|
|
405
397
|
return true;
|
|
406
398
|
return teile[0] === "" || teile[0] === ".";
|
|
407
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Ein `..`-Segment oder ein absoluter Pfad duerften das Ziel verlassen.
|
|
402
|
+
*
|
|
403
|
+
* 🔴 B-8: Zerlegt an `/` UND `\` -- eine fehlerhafte oder manipulierte
|
|
404
|
+
* Server-Antwort koennte sonst mit `..\..\boese.md` durchkommen. `join()`
|
|
405
|
+
* trennt unter Windows auch am Backslash, dieser Schutz muss also beide
|
|
406
|
+
* Trenner kennen wie `join()` selbst.
|
|
407
|
+
*/
|
|
408
408
|
export function istUnsichererPfad(pfad) {
|
|
409
409
|
if (pfad.startsWith("/") || /^[a-zA-Z]:/.test(pfad))
|
|
410
410
|
return true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { dateienSchreiben, vorlageHolen, werkbankOrdner, istUnsichererOrdnername, } from "../arbeitsplatz-dateien.js";
|
|
3
|
+
import { alsWort } from "../argumente.js";
|
|
4
|
+
import { nameUndFirma } from "./name-und-firma.js";
|
|
4
5
|
import { befehlIn } from "../befehlswoerter.js";
|
|
5
6
|
import { slashIn } from "../slash-befehle.js";
|
|
6
7
|
import { sprache } from "../sprache.js";
|
|
@@ -98,90 +99,11 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
98
99
|
console.log(hilfe(s));
|
|
99
100
|
return 0;
|
|
100
101
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
⚠️ Sie sprechen jetzt von einem NAMEN, nicht von einem Ordnernamen. Das ist
|
|
106
|
-
keine Kosmetik: Wer nach der alten Hilfe `kimal3 arbeitsplatz neu ARNO-KI`
|
|
107
|
-
tippt, bekommt keinen Fehler, sondern eine Werkbank namens
|
|
108
|
-
`werkbank-arno-ki`. Diese Meldungen und die Hilfe sind die einzigen Stellen,
|
|
109
|
-
an denen er das erfaehrt, bevor es passiert.
|
|
110
|
-
*/
|
|
111
|
-
/*
|
|
112
|
-
🔴 **Erst schneiden, dann zerlegen** (Punkt A-3, 2026-08-26). Dieser Befehl
|
|
113
|
-
hat ein Wort OHNE `--` (den Vornamen), und `zerlege` lehnt genau das ab.
|
|
114
|
-
Deshalb trennt `trenneNamenUndOptionen` beim ersten `--`: davor die
|
|
115
|
-
Woerter, ab dort die Optionen. Die Strenge von `zerlege` bleibt damit
|
|
116
|
-
unangetastet -- sie war teuer erkauft (B-3 und B-4, 2026-08-18).
|
|
117
|
-
*/
|
|
118
|
-
const { woerter, optionen: optionsArgv } = trenneNamenUndOptionen(argv);
|
|
119
|
-
const nameArg = woerter[0];
|
|
120
|
-
if (nameArg !== undefined && nameArg.trim() === "") {
|
|
121
|
-
console.error(t.emptyName + hilfe(s));
|
|
122
|
-
return 1;
|
|
123
|
-
}
|
|
124
|
-
if (woerter.length > 1) {
|
|
125
|
-
/*
|
|
126
|
-
🔴 **Der Vorschlag darf keine Optionen einsammeln** (Review-Runde 5 zu
|
|
127
|
-
PR #25). Hier stand `argv.join("-")` -- aus `arbeitsplatz neu sabine --force`
|
|
128
|
-
wurde damit der Rat `arbeitsplatz neu sabine---force`.
|
|
129
|
-
*Ein Rat, der ein falsches Kommando erzeugt, ist schlimmer als keiner* --
|
|
130
|
-
dieselbe Klasse wie die Absagen aus Runde 1 und 2, nur kleiner.
|
|
131
|
-
|
|
132
|
-
Deshalb: Sieht irgendein Wort wie eine Option aus, gibt es gar keinen
|
|
133
|
-
Vorschlag. Ein Mensch, der `--force` tippt, sucht keine Schreibhilfe fuer
|
|
134
|
-
seinen Vornamen.
|
|
135
|
-
|
|
136
|
-
⚠️ Seit dem 2026-08-26 stehen die `--`-Optionen schon gar nicht mehr in
|
|
137
|
-
`woerter` (der Schnitt oben). Der Filter bleibt trotzdem: Er faengt das
|
|
138
|
-
EINFACHE `-` (`-force`), das kein Schnitt-Zeichen ist und das der
|
|
139
|
-
Vorschlag genauso wenig einsammeln darf.
|
|
140
|
-
*/
|
|
141
|
-
const einfacheWoerter = woerter.filter((w) => !w.startsWith("-"));
|
|
142
|
-
/*
|
|
143
|
-
🔴 **Die getippten Optionen reisen mit** (Review-Befund 3, 2026-08-26).
|
|
144
|
-
Ohne sie riet die Zeile zu einem Befehl, der bei einem Menschen in zwei
|
|
145
|
-
Firmen sofort wieder in dieselbe Absage laeuft -- s. `alsBefehlszeile`.
|
|
146
|
-
*/
|
|
147
|
-
const optionsAnhang = optionsArgv.length === 0 ? "" : ` ${alsBefehlszeile(optionsArgv)}`;
|
|
148
|
-
const vorschlag = einfacheWoerter.length === woerter.length
|
|
149
|
-
? t.nameSuggestion(befehlIn("arbeitsplatz neu", s), einfacheWoerter.join("-"), optionsAnhang) // kein-anzeigetext: Befehlsschlüssel
|
|
150
|
-
: "";
|
|
151
|
-
console.error(t.onlyOneName(woerter[1]) + vorschlag + hilfe(s));
|
|
102
|
+
// Name und Firmen-Optionen, mit denselben Namens-Absagen wie `werkbank neu` (gemeinsam seit 26.10.2026).
|
|
103
|
+
const gelesen = nameUndFirma(argv, "arbeitsplatz neu", t, hilfe(s), s); // kein-anzeigetext: Befehlsschlüssel
|
|
104
|
+
if (gelesen === null)
|
|
152
105
|
return 1;
|
|
153
|
-
}
|
|
154
|
-
/*
|
|
155
|
-
🔑 Erst JETZT die Optionen -- nach den Namens-Absagen, damit ein Mensch mit
|
|
156
|
-
zwei Vornamen die Schreibhilfe bekommt und nicht eine Options-Meldung.
|
|
157
|
-
*/
|
|
158
|
-
const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN, s);
|
|
159
|
-
if (!zerlegt.ok) {
|
|
160
|
-
console.error(`${zerlegt.meldung}\n${hilfe(s)}`);
|
|
161
|
-
return 1;
|
|
162
|
-
}
|
|
163
|
-
const wunsch = {
|
|
164
|
-
firma: zerlegt.werte.optionen.firma,
|
|
165
|
-
firmaId: zerlegt.werte.optionen["firma-id"],
|
|
166
|
-
};
|
|
167
|
-
/*
|
|
168
|
-
Der Name wird UMGESCHRIEBEN, nicht abgelehnt (s. `werkbankOrdnerAus`) --
|
|
169
|
-
"Sabine Mueller" ist ein gueltiger Name, nur kein gueltiger Ordner. `null`
|
|
170
|
-
heisst: Nach dem Umschreiben war nichts mehr uebrig (etwa bei "!!!"). Das ist
|
|
171
|
-
HIER ein Fehler, weil ein Mensch etwas getippt hat und eine Antwort verdient
|
|
172
|
-
-- der Server schweigt im selben Fall und bleibt beim Vordruck.
|
|
173
|
-
*/
|
|
174
|
-
// 🌐 Teil 2: Hier geht es nur um „bleibt etwas uebrig?" -- das Praefix ist egal. Welches es
|
|
175
|
-
// wird, sagt erst die Lieferung (die Werkbank vom Server traegt die Kontosprache), s. unten.
|
|
176
|
-
if (nameArg !== undefined) {
|
|
177
|
-
if (werkbankOrdnerAus(nameArg, "de") === null) {
|
|
178
|
-
console.error(t.unusableName1(nameArg));
|
|
179
|
-
console.error(t.unusableName2);
|
|
180
|
-
console.error("");
|
|
181
|
-
console.error(t.unusableName3(befehlIn("arbeitsplatz neu", s))); // kein-anzeigetext: Befehlsschlüssel
|
|
182
|
-
return 1;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
106
|
+
const { nameArg, wunsch } = gelesen;
|
|
185
107
|
/*
|
|
186
108
|
🔴 **HIER ENTSCHEIDET SICH, OB UEBERHAUPT ETWAS ENTSTEHT** (Punkt A-3).
|
|
187
109
|
Gehoert der Mensch zu mehreren Firmen und hat keine genannt, antwortet die
|
|
@@ -227,9 +149,7 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
227
149
|
}
|
|
228
150
|
/*
|
|
229
151
|
🔑 Der Zielordner ist IMMER `daten.wurzel` ("KI-Arbeitsplatz") und kommt damit
|
|
230
|
-
vom Server statt aus der Eingabe
|
|
231
|
-
nicht mehr die Vorsicht fuer einen selten benutzten Zweig, sondern der
|
|
232
|
-
einzige Schutz, den dieser Wert ueberhaupt hat.
|
|
152
|
+
vom Server statt aus der Eingabe (die Pfad-Pruefung darueber ist sein einziger Schutz).
|
|
233
153
|
|
|
234
154
|
Der Name des Menschen wirkt eine Ebene tiefer, auf die Werkbank. Der Server
|
|
235
155
|
hat sie schon benannt (aus der Anmelde-Adresse); ein mitgegebener Name
|
|
@@ -343,9 +263,9 @@ export async function arbeitsplatzNeu(argv) {
|
|
|
343
263
|
`kimal3 kontext hoch` -- nicht an der Zahl.** Hier stand bis zur zweiten
|
|
344
264
|
Review-Runde „der deutet Rueckgabewert 1 als ‚du bist kein Admin'", und
|
|
345
265
|
genau diese Gleichung hat dieselbe Scheibe an zwei anderen Stellen als
|
|
346
|
-
falsch geloescht: `kontext hoch`
|
|
347
|
-
erfolgreich gespeichert
|
|
348
|
-
(`kontext-hoch.ts`, Merkzettel-Zweig). *Ein Kommentar, der ein Verhalten erklaert, wird
|
|
266
|
+
falsch geloescht: `kontext hoch` gab damals `1` auch dann zurueck, wenn die
|
|
267
|
+
Firma erfolgreich gespeichert hatte und nur der lokale Merkzettel scheiterte
|
|
268
|
+
(seit B-d, 17.09., ist das `0`; `kontext-hoch.ts`, Merkzettel-Zweig). *Ein Kommentar, der ein Verhalten erklaert, wird
|
|
349
269
|
beim naechsten Lesen abgenickt -- dieser haette den Fehler zurueckgeholt,
|
|
350
270
|
den der Commit daneben entfernt.*
|
|
351
271
|
|
|
@@ -2,10 +2,8 @@ import { zerlege } from "../argumente.js";
|
|
|
2
2
|
import { sprache } from "../sprache.js";
|
|
3
3
|
import { ALLGEMEIN_TEXTE } from "../texte/allgemein.js";
|
|
4
4
|
import { PROJEKTE_TEXTE } from "../texte/projekte.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { firmenIdZeilen, firmenZeilen } from "../firmen-liste.js";
|
|
8
|
-
import { BASIS_URL } from "../anmeldung.js";
|
|
5
|
+
import { ruf } from "../api.js";
|
|
6
|
+
import { absage } from "./kontext-gemeinsam.js";
|
|
9
7
|
const ERLAUBT = ["firma", "firma-id"];
|
|
10
8
|
/**
|
|
11
9
|
* Die Firmendaten holen — als JSON, unveraendert.
|
|
@@ -79,67 +77,7 @@ export async function firmaDaten(argv) {
|
|
|
79
77
|
console.log(JSON.stringify(antwort.koerper, null, 2));
|
|
80
78
|
return 0;
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
// 402
|
|
84
|
-
|
|
85
|
-
// und der meldet sich dann erfolgreich an, ohne dass es etwas aendert.
|
|
86
|
-
if (antwort.status === 402) {
|
|
87
|
-
console.error(meldung);
|
|
88
|
-
return 4;
|
|
89
|
-
}
|
|
90
|
-
if (antwort.status === 401) {
|
|
91
|
-
console.error(`${meldung}${a.pleaseSignIn}`);
|
|
92
|
-
return 2;
|
|
93
|
-
}
|
|
94
|
-
if (antwort.status === 409) {
|
|
95
|
-
const grund = feld(antwort.koerper, "grund");
|
|
96
|
-
const koerper = antwort.koerper;
|
|
97
|
-
const namen = Array.isArray(koerper?.firmen)
|
|
98
|
-
? koerper.firmen.filter((x) => typeof x === "string")
|
|
99
|
-
: [];
|
|
100
|
-
console.error(meldung);
|
|
101
|
-
/*
|
|
102
|
-
🔑 **Bei gleichnamigen Firmen fuehrt der Weg ueber die ID** (B-5, zweite
|
|
103
|
-
Haelfte). Die Route legt dafuer `auswahl` bei -- und erst damit ist die
|
|
104
|
-
Absage keine Sackgasse mehr.
|
|
105
|
-
*/
|
|
106
|
-
const auswahl = Array.isArray(koerper.auswahl)
|
|
107
|
-
? koerper.auswahl.filter((x) => !!x &&
|
|
108
|
-
typeof x === "object" &&
|
|
109
|
-
typeof x.id === "string" &&
|
|
110
|
-
typeof x.name === "string")
|
|
111
|
-
: [];
|
|
112
|
-
if (auswahl.length > 0) {
|
|
113
|
-
console.error(a.sameNameUseId);
|
|
114
|
-
firmenIdZeilen(auswahl, s).forEach((z) => console.error(z));
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
// Die Namen als FERTIGE Befehle ausgeben, nicht als blosse Liste: Der
|
|
118
|
-
// naechste Schritt ist dann Kopieren statt Abtippen -- und ein Firmenname
|
|
119
|
-
// mit Leerzeichen braucht Anfuehrungszeichen, an die sonst niemand denkt.
|
|
120
|
-
//
|
|
121
|
-
// ⚠️ Welche Namen einen Befehl bekommen, entscheidet `firmenZeilen` (B-5):
|
|
122
|
-
// Ein doppelt vergebener Name bekommt KEINEN -- er waere ein Vorschlag,
|
|
123
|
-
// der garantiert wieder in derselben Absage endet.
|
|
124
|
-
const zeilen = firmenZeilen(namen, s);
|
|
125
|
-
if (zeilen.length > 0) {
|
|
126
|
-
console.error(a.yourCompanies);
|
|
127
|
-
zeilen.forEach((z) => console.error(z));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (grund === "keine-firma") {
|
|
131
|
-
console.error(a.createCompany(BASIS_URL));
|
|
132
|
-
}
|
|
133
|
-
return 1;
|
|
134
|
-
}
|
|
135
|
-
// ⚠️ Die 503 landet HIER und nicht oben bei der 402: Rueckgabewert 3 heisst
|
|
136
|
-
// "nochmal versuchen kann helfen", 4 heisst laut SKILL.md ausdruecklich
|
|
137
|
-
// "nicht wiederholen". Ein Aussetzer gehoert in den ersten Topf.
|
|
138
|
-
//
|
|
139
|
-
// 🔑 Den Rest entscheidet `schlussFehler` fuer alle Befehle gemeinsam --
|
|
140
|
-
// insbesondere die `404` (B-7), die hier frueher auf die `1` fiel und damit
|
|
141
|
-
// "Eingabe passt nicht" meldete, obwohl die Eingabe stimmte.
|
|
142
|
-
const schluss = schlussFehler(antwort.status, meldung, s);
|
|
143
|
-
console.error(schluss.text);
|
|
144
|
-
return schluss.wert;
|
|
80
|
+
// Alles ausser 200: dieselbe Absage wie bei `kontext runter`/`kontext hoch` (bis 26.10.2026 hier ein drittes Mal
|
|
81
|
+
// abgeschrieben) -- 402 vor 401, 409 mit den eigenen Firmen als fertigen `firma daten`-Zeilen, Rest an `schlussFehler`.
|
|
82
|
+
return absage(antwort, "firma daten", ""); // kein-anzeigetext: Befehlsschluessel
|
|
145
83
|
}
|
|
@@ -8,7 +8,7 @@ import { KONTEXT_TEXTE } from "../texte/kontext.js";
|
|
|
8
8
|
import { BASIS_URL } from "../anmeldung.js";
|
|
9
9
|
import { findeArbeitsplatz } from "../kontext-dateien.js";
|
|
10
10
|
import { ORDNER_NAMEN } from "../kontext-stand.js";
|
|
11
|
-
import { angeboteneFirmen } from "../firmen-liste.js";
|
|
11
|
+
import { angeboteneFirmen, firmenIdZeilen, firmenZeilen } from "../firmen-liste.js";
|
|
12
12
|
/**
|
|
13
13
|
* Was `kontext runter` (`kontext-runter.ts`) und `kontext hoch`
|
|
14
14
|
* (`kontext-hoch.ts`) TEILEN -- die `--firma`/`--firma-id`-Optionen und die
|
|
@@ -131,13 +131,10 @@ export const ERLAUBT = ["firma", "firma-id"];
|
|
|
131
131
|
* (`kontextRunter`) und POST (`kontextHoch`) gleichermassen. `befehl` sagt,
|
|
132
132
|
* welcher der beiden fuer die kopierfertigen Zeilen richtig ist.
|
|
133
133
|
*
|
|
134
|
-
* 🔑
|
|
135
|
-
* (verlangen Gegenteiliges), 409 nennt die eigenen Firmen bzw. die IDs bei
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
* `firmen-liste.ts`: Dessen `firmenZeilen`/`firmenIdZeilen` schreiben den
|
|
139
|
-
* Befehl `kimal3 firma daten …` fest in die Zeile -- fuer `kontext *`
|
|
140
|
-
* waere das ein falscher, nicht ausfuehrbarer Rat.
|
|
134
|
+
* 🔑 Seit dem 26.10.2026 auch fuer `firma daten` (`/api/cli/firma`), das bis dahin dieselbe Logik ein drittes Mal
|
|
135
|
+
* abschrieb: 402 vor 401 (verlangen Gegenteiliges), 409 nennt die eigenen Firmen bzw. die IDs bei gleichnamigen,
|
|
136
|
+
* der Rest geht an `schlussFehler`. Die kopierfertigen Zeilen kommen aus `firmen-liste.ts`; `befehl` sagt, welcher
|
|
137
|
+
* Befehl in der Zeile stehen muss.
|
|
141
138
|
*
|
|
142
139
|
* 🔴 **`409` heisst hier ZWEI verschiedene Dinge** (Task 9): Bei GET und bei
|
|
143
140
|
* POST kommt ein `409` mit `grund` von `waehleFirma` (mehrdeutige Firmenwahl,
|
|
@@ -178,10 +175,10 @@ export function absage(antwort, befehl, optionen) {
|
|
|
178
175
|
: [];
|
|
179
176
|
if (auswahl.length > 0) {
|
|
180
177
|
console.error(t.sameNameUseId);
|
|
181
|
-
|
|
178
|
+
firmenIdZeilen(auswahl, befehl, optionen, s).forEach((z) => console.error(z));
|
|
182
179
|
}
|
|
183
180
|
else {
|
|
184
|
-
const zeilen =
|
|
181
|
+
const zeilen = firmenZeilen(namen, befehl, optionen, s);
|
|
185
182
|
if (zeilen.length > 0) {
|
|
186
183
|
console.error(t.yourCompanies);
|
|
187
184
|
zeilen.forEach((z) => console.error(z));
|
|
@@ -196,33 +193,3 @@ export function absage(antwort, befehl, optionen) {
|
|
|
196
193
|
console.error(schluss.text);
|
|
197
194
|
return schluss.wert;
|
|
198
195
|
}
|
|
199
|
-
/**
|
|
200
|
-
* `firmenZeilen` aus `firmen-liste.ts`, nur mit dem richtigen Befehl in der
|
|
201
|
-
* Zeile -- s. Kommentar an `absage`.
|
|
202
|
-
*/
|
|
203
|
-
export function firmenZeilenKontext(namen, befehl, optionen, s) {
|
|
204
|
-
const t = ALLGEMEIN_TEXTE[s];
|
|
205
|
-
// Befehl, schon getippte Optionen, dann der Firmen-Schalter -- in der Sprache des Satzes.
|
|
206
|
-
const vorne = `${befehlIn(befehl, s)}${optionen}`;
|
|
207
|
-
const anzahl = (name) => namen.filter((n) => n === name).length;
|
|
208
|
-
const gesehen = new Set();
|
|
209
|
-
const zeilen = [];
|
|
210
|
-
for (const name of namen) {
|
|
211
|
-
if (gesehen.has(name))
|
|
212
|
-
continue;
|
|
213
|
-
gesehen.add(name);
|
|
214
|
-
const wieOft = anzahl(name);
|
|
215
|
-
if (wieOft === 1) {
|
|
216
|
-
zeilen.push(t.companyLine(vorne, schalterIn("firma", s), alsWort(name, true)));
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
zeilen.push(t.nameNotUnique(name, wieOft));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return zeilen;
|
|
223
|
-
}
|
|
224
|
-
/** `firmenIdZeilen` aus `firmen-liste.ts`, nur mit dem richtigen Befehl. */
|
|
225
|
-
export function firmenIdZeilenKontext(auswahl, befehl, optionen, s) {
|
|
226
|
-
const vorne = `${befehlIn(befehl, s)}${optionen}`;
|
|
227
|
-
return auswahl.map((f, i) => ALLGEMEIN_TEXTE[s].companyIdLine(vorne, schalterIn("firma-id", s), f.id, f.name, i + 1));
|
|
228
|
-
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { FIRMEN_OPTIONEN } from "../arbeitsplatz-dateien.js";
|
|
2
|
+
import { alsBefehlszeile, trenneNamenUndOptionen, zerlege } from "../argumente.js";
|
|
3
|
+
import { befehlIn } from "../befehlswoerter.js";
|
|
4
|
+
import { werkbankOrdnerAus } from "../werkbank-name.js";
|
|
5
|
+
/**
|
|
6
|
+
* Name und Firmen-Optionen aus den Argumenten von `arbeitsplatz neu` und `werkbank neu` -- mit den drei
|
|
7
|
+
* Namens-Absagen, die beide Befehle wortgleich geben: Wer den einen Befehl kennt, soll beim anderen nichts Neues
|
|
8
|
+
* lernen muessen.
|
|
9
|
+
*
|
|
10
|
+
* 🔑 Bis zum 26.10.2026 stand das als Zwilling in beiden Befehlen, und der eigene Kommentar dort erzaehlte schon von
|
|
11
|
+
* einem Fehler, der nur bei einem der beiden behoben worden war. Jetzt gibt es die Absagen einmal; die Saetze kommen
|
|
12
|
+
* weiter aus dem Textobjekt des Befehls (`t`), das Befehlswort aus `befehl`.
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ Die Absagen sprechen von einem NAMEN, nicht von einem Ordnernamen. Das ist keine Kosmetik: Wer nach der alten
|
|
15
|
+
* Hilfe `kimal3 arbeitsplatz neu ARNO-KI` tippt, bekommt keinen Fehler, sondern eine Werkbank namens
|
|
16
|
+
* `werkbank-arno-ki`. Diese Meldungen und die Hilfe sind die einzigen Stellen, an denen er das erfaehrt, bevor es
|
|
17
|
+
* passiert. Und die alte Hilfe von `werkbank neu` brachte `kimal3 werkbank neu werkbank-sabine` bei -- deshalb
|
|
18
|
+
* schneidet `werkbankOrdnerAus` ein mitgetipptes `werkbank-` wieder ab. *Alte Anleitungen ueberleben laenger als der
|
|
19
|
+
* Code, den sie beschreiben.*
|
|
20
|
+
*
|
|
21
|
+
* Gibt `null` zurueck, wenn eine Absage gedruckt wurde (der Befehl endet dann mit `1`).
|
|
22
|
+
*/
|
|
23
|
+
export function nameUndFirma(argv, befehl, t, hilfe, s) {
|
|
24
|
+
/*
|
|
25
|
+
🔴 **Erst schneiden, dann zerlegen** (Punkt A-3, 2026-08-26). Diese Befehle haben ein Wort OHNE `--` (den
|
|
26
|
+
Vornamen), und `zerlege` lehnt genau das ab. Deshalb trennt `trenneNamenUndOptionen` beim ersten `--`: davor die
|
|
27
|
+
Woerter, ab dort die Optionen. Die Strenge von `zerlege` bleibt damit unangetastet -- sie war teuer erkauft (B-3
|
|
28
|
+
und B-4, 2026-08-18).
|
|
29
|
+
*/
|
|
30
|
+
const { woerter, optionen: optionsArgv } = trenneNamenUndOptionen(argv);
|
|
31
|
+
const nameArg = woerter[0];
|
|
32
|
+
if (nameArg !== undefined && nameArg.trim() === "") {
|
|
33
|
+
console.error(t.emptyName + hilfe);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (woerter.length > 1) {
|
|
37
|
+
/*
|
|
38
|
+
🔴 **Der Vorschlag darf keine Optionen einsammeln** (Review-Runde 5 zu PR #25). Hier stand `argv.join("-")` --
|
|
39
|
+
aus `arbeitsplatz neu sabine --force` wurde damit der Rat `arbeitsplatz neu sabine---force`. *Ein Rat, der ein
|
|
40
|
+
falsches Kommando erzeugt, ist schlimmer als keiner.* Deshalb: Sieht irgendein Wort wie eine Option aus, gibt
|
|
41
|
+
es gar keinen Vorschlag. Ein Mensch, der `--force` tippt, sucht keine Schreibhilfe fuer seinen Vornamen.
|
|
42
|
+
|
|
43
|
+
⚠️ Seit dem 2026-08-26 stehen die `--`-Optionen schon gar nicht mehr in `woerter` (der Schnitt oben). Der Filter
|
|
44
|
+
bleibt trotzdem: Er faengt das EINFACHE `-` (`-force`), das kein Schnitt-Zeichen ist.
|
|
45
|
+
*/
|
|
46
|
+
const einfacheWoerter = woerter.filter((w) => !w.startsWith("-"));
|
|
47
|
+
/*
|
|
48
|
+
🔴 **Die getippten Optionen reisen mit** (Review-Befund 3, 2026-08-26). Ohne sie riet die Zeile zu einem
|
|
49
|
+
Befehl, der bei einem Menschen in zwei Firmen sofort wieder in dieselbe Absage laeuft -- s. `alsBefehlszeile`.
|
|
50
|
+
*/
|
|
51
|
+
const optionsAnhang = optionsArgv.length === 0 ? "" : ` ${alsBefehlszeile(optionsArgv)}`;
|
|
52
|
+
const vorschlag = einfacheWoerter.length === woerter.length
|
|
53
|
+
? t.nameSuggestion(befehlIn(befehl, s), einfacheWoerter.join("-"), optionsAnhang)
|
|
54
|
+
: "";
|
|
55
|
+
console.error(t.onlyOneName(woerter[1]) + vorschlag + hilfe);
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// 🔑 Erst JETZT die Optionen -- nach den Namens-Absagen, damit ein Mensch mit zwei Vornamen die Schreibhilfe
|
|
59
|
+
// bekommt und nicht eine Options-Meldung.
|
|
60
|
+
const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN, s);
|
|
61
|
+
if (!zerlegt.ok) {
|
|
62
|
+
console.error(`${zerlegt.meldung}\n${hilfe}`);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/*
|
|
66
|
+
Der Name wird UMGESCHRIEBEN, nicht abgelehnt (s. `werkbankOrdnerAus`) -- "Sabine Mueller" ist ein gueltiger
|
|
67
|
+
Name, nur kein gueltiger Ordner. `null` heisst: Nach dem Umschreiben war nichts mehr uebrig (etwa bei "!!!"). Das
|
|
68
|
+
ist HIER ein Fehler, weil ein Mensch etwas getippt hat und eine Antwort verdient -- der Server schweigt im selben
|
|
69
|
+
Fall und bleibt beim Vordruck.
|
|
70
|
+
*/
|
|
71
|
+
// 🌐 Teil 2: Hier geht es nur um „bleibt etwas uebrig?" -- das Praefix ist egal. Welches es wird, sagt erst die
|
|
72
|
+
// Lieferung (die Werkbank vom Server traegt die Kontosprache).
|
|
73
|
+
if (nameArg !== undefined && werkbankOrdnerAus(nameArg, "de") === null) {
|
|
74
|
+
console.error(t.unusableName1(nameArg));
|
|
75
|
+
console.error(t.unusableName2);
|
|
76
|
+
console.error("");
|
|
77
|
+
console.error(t.unusableName3(befehlIn(befehl, s)));
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return { nameArg, wunsch: { firma: zerlegt.werte.optionen.firma, firmaId: zerlegt.werte.optionen["firma-id"] } };
|
|
81
|
+
}
|
|
@@ -494,8 +494,9 @@ function leseWennMoeglich(pfad) {
|
|
|
494
494
|
* Entfernt Befehlsdateien, die frueher einmal ausgeliefert wurden.
|
|
495
495
|
*
|
|
496
496
|
* 🔴 **Warum das eine eigene, exportierte Funktion ist** (Review-Runde 3,
|
|
497
|
-
* 2026-08-18): `VERALTETE_BEFEHLE`
|
|
498
|
-
* gelaufen -- und `check:cli` "belegte"
|
|
497
|
+
* 2026-08-18): `VERALTETE_BEFEHLE` war damals leer, die Schleife also noch nie
|
|
498
|
+
* gelaufen (seit 28.08. steht `k3firma.md` darin) -- und `check:cli` "belegte"
|
|
499
|
+
* sie nur ueber eine Textsuche. Genau
|
|
499
500
|
* die Falle aus der Projekt-Notiz *wache-am-echten-fehlerfall-pruefen*.
|
|
500
501
|
* Mit dem Zielordner als Parameter laesst sie sich gegen einen Wegwerf-Ordner
|
|
501
502
|
* mit einer echten Datei fahren.
|
package/dist/befehle/werkbank.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { dateienSchreiben, istUnsichererOrdnername, vorlageHolen, werkbankOrdner, } from "../arbeitsplatz-dateien.js";
|
|
4
|
+
import { alsWort } from "../argumente.js";
|
|
5
|
+
import { nameUndFirma } from "./name-und-firma.js";
|
|
5
6
|
import { befehlIn, schalterIn } from "../befehlswoerter.js";
|
|
6
7
|
import { sprache } from "../sprache.js";
|
|
7
8
|
import { ARBEITSPLATZ_TEXTE } from "../texte/arbeitsplatz.js";
|
|
@@ -89,80 +90,11 @@ export async function werkbankNeu(argv) {
|
|
|
89
90
|
console.log(hilfe(s));
|
|
90
91
|
return 0;
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
⚠️ Sie sprechen seit dem 2026-08-20 von einem NAMEN statt von einem
|
|
97
|
-
Ordnernamen. Die alte Hilfe hier hat ausdruecklich
|
|
98
|
-
`kimal3 werkbank neu werkbank-sabine` beigebracht -- deshalb schneidet
|
|
99
|
-
`werkbankOrdnerAus` ein bereits mitgetipptes `werkbank-` wieder ab, statt
|
|
100
|
-
`werkbank-werkbank-sabine` zu bauen. *Alte Anleitungen ueberleben laenger
|
|
101
|
-
als der Code, den sie beschreiben.*
|
|
102
|
-
*/
|
|
103
|
-
/*
|
|
104
|
-
🔴 **Erst schneiden, dann zerlegen** -- wortgleich zum Zwilling in
|
|
105
|
-
`arbeitsplatz.ts` (Punkt A-3, 2026-08-26). Dieser Befehl hat ein Wort OHNE
|
|
106
|
-
`--` (den Vornamen), und `zerlege` lehnt genau das ab.
|
|
107
|
-
*/
|
|
108
|
-
const { woerter, optionen: optionsArgv } = trenneNamenUndOptionen(argv);
|
|
109
|
-
const nameArg = woerter[0];
|
|
110
|
-
if (nameArg !== undefined && nameArg.trim() === "") {
|
|
111
|
-
console.error(t.emptyName + hilfe(s));
|
|
93
|
+
// Name und Firmen-Optionen, mit denselben Namens-Absagen wie `arbeitsplatz neu` (gemeinsam seit 26.10.2026).
|
|
94
|
+
const gelesen = nameUndFirma(argv, "werkbank neu", t, hilfe(s), s); // kein-anzeigetext: Befehlsschlüssel
|
|
95
|
+
if (gelesen === null)
|
|
112
96
|
return 1;
|
|
113
|
-
}
|
|
114
|
-
if (woerter.length > 1) {
|
|
115
|
-
/*
|
|
116
|
-
🔴 **Der Vorschlag darf keine Optionen einsammeln** (Review-Runde 5 zu
|
|
117
|
-
PR #25). Hier stand `argv.join("-")` -- aus `werkbank neu sabine --force`
|
|
118
|
-
wurde damit der Rat `werkbank neu sabine---force`.
|
|
119
|
-
*Ein Rat, der ein falsches Kommando erzeugt, ist schlimmer als keiner* --
|
|
120
|
-
dieselbe Klasse wie die Absagen aus Runde 1 und 2, nur kleiner.
|
|
121
|
-
|
|
122
|
-
Deshalb: Sieht irgendein Wort wie eine Option aus, gibt es gar keinen
|
|
123
|
-
Vorschlag. Ein Mensch, der `--force` tippt, sucht keine Schreibhilfe fuer
|
|
124
|
-
seinen Vornamen.
|
|
125
|
-
|
|
126
|
-
⚠️ Seit dem 2026-08-26 stehen die `--`-Optionen schon gar nicht mehr in
|
|
127
|
-
`woerter` (der Schnitt oben). Der Filter bleibt trotzdem: Er faengt das
|
|
128
|
-
EINFACHE `-` (`-force`), das kein Schnitt-Zeichen ist.
|
|
129
|
-
*/
|
|
130
|
-
const einfacheWoerter = woerter.filter((w) => !w.startsWith("-"));
|
|
131
|
-
/*
|
|
132
|
-
🔴 **Die getippten Optionen reisen mit** (Review-Befund 3, 2026-08-26).
|
|
133
|
-
Ohne sie riet die Zeile zu einem Befehl, der bei einem Menschen in zwei
|
|
134
|
-
Firmen sofort wieder in dieselbe Absage laeuft -- s. `alsBefehlszeile`.
|
|
135
|
-
*/
|
|
136
|
-
const optionsAnhang = optionsArgv.length === 0 ? "" : ` ${alsBefehlszeile(optionsArgv)}`;
|
|
137
|
-
const vorschlag = einfacheWoerter.length === woerter.length
|
|
138
|
-
? t.nameSuggestion(befehlIn("werkbank neu", s), einfacheWoerter.join("-"), optionsAnhang) // kein-anzeigetext: Befehlsschlüssel
|
|
139
|
-
: "";
|
|
140
|
-
console.error(t.onlyOneName(woerter[1]) + vorschlag + hilfe(s));
|
|
141
|
-
return 1;
|
|
142
|
-
}
|
|
143
|
-
// 🔑 Erst NACH den Namens-Absagen -- s. Zwilling in `arbeitsplatz.ts`.
|
|
144
|
-
const zerlegt = zerlege(optionsArgv, FIRMEN_OPTIONEN, s);
|
|
145
|
-
if (!zerlegt.ok) {
|
|
146
|
-
console.error(`${zerlegt.meldung}\n${hilfe(s)}`);
|
|
147
|
-
return 1;
|
|
148
|
-
}
|
|
149
|
-
const wunsch = {
|
|
150
|
-
firma: zerlegt.werte.optionen.firma,
|
|
151
|
-
firmaId: zerlegt.werte.optionen["firma-id"],
|
|
152
|
-
};
|
|
153
|
-
// `null` heisst: nach dem Umschreiben blieb kein Zeichen uebrig, das in einem
|
|
154
|
-
// Ordnernamen stehen darf. Ein Mensch hat hier getippt und verdient eine
|
|
155
|
-
// Antwort -- der Server schweigt im selben Fall und bleibt beim Vordruck.
|
|
156
|
-
// 🌐 Teil 2: nur „bleibt etwas uebrig?" -- das Praefix kommt erst aus der Lieferung, s. unten.
|
|
157
|
-
if (nameArg !== undefined) {
|
|
158
|
-
if (werkbankOrdnerAus(nameArg, "de") === null) {
|
|
159
|
-
console.error(t.unusableName1(nameArg));
|
|
160
|
-
console.error(t.unusableName2);
|
|
161
|
-
console.error("");
|
|
162
|
-
console.error(t.unusableName3(befehlIn("werkbank neu", s))); // kein-anzeigetext: Befehlsschlüssel
|
|
163
|
-
return 1;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
97
|
+
const { nameArg, wunsch } = gelesen;
|
|
166
98
|
/*
|
|
167
99
|
🔴 **HIER ENTSCHEIDET SICH, OB UEBERHAUPT ETWAS ENTSTEHT** (Punkt A-3) --
|
|
168
100
|
dieselbe Stelle wie beim Zwilling: Bei mehreren Firmen ohne Angabe kommt
|
package/dist/firmen-liste.js
CHANGED
|
@@ -32,9 +32,15 @@ import { ALLGEMEIN_TEXTE } from "./texte/allgemein.js";
|
|
|
32
32
|
* die ID statt ueber den Namen -- eine eigene Entscheidung, keine Fussnote.
|
|
33
33
|
* Diese Funktion sorgt nur dafuer, dass niemand mehr einen Ausweg vorgespielt
|
|
34
34
|
* bekommt, den es nicht gibt.
|
|
35
|
+
*
|
|
36
|
+
* `befehl` und `optionen` sagen, welcher Befehl in der Zeile steht: `firma daten` ohne weitere Optionen, oder
|
|
37
|
+
* `kontext runter`/`kontext hoch` mit dem, was schon getippt war (bis 26.10.2026 eine zweite, gleiche Fassung in
|
|
38
|
+
* `kontext-gemeinsam.ts`).
|
|
35
39
|
*/
|
|
36
|
-
export function firmenZeilen(namen, s) {
|
|
40
|
+
export function firmenZeilen(namen, befehl, optionen, s) {
|
|
37
41
|
const t = ALLGEMEIN_TEXTE[s];
|
|
42
|
+
// Befehl, schon getippte Optionen, dann der Firmen-Schalter -- in der Sprache des Satzes.
|
|
43
|
+
const vorne = `${befehlIn(befehl, s)}${optionen}`;
|
|
38
44
|
const anzahl = (name) => namen.filter((n) => n === name).length;
|
|
39
45
|
const gesehen = new Set();
|
|
40
46
|
const zeilen = [];
|
|
@@ -47,7 +53,7 @@ export function firmenZeilen(namen, s) {
|
|
|
47
53
|
// Fertiger Befehl statt blosser Liste: Der naechste Schritt ist dann
|
|
48
54
|
// Kopieren statt Abtippen -- und ein Name mit Leerzeichen braucht
|
|
49
55
|
// Anfuehrungszeichen, an die sonst niemand denkt.
|
|
50
|
-
zeilen.push(t.companyLine(
|
|
56
|
+
zeilen.push(t.companyLine(vorne, schalterIn("firma", s), alsWort(name, true)));
|
|
51
57
|
}
|
|
52
58
|
else {
|
|
53
59
|
zeilen.push(t.nameNotUnique(name, wieOft));
|
|
@@ -70,8 +76,9 @@ export function firmenZeilen(namen, s) {
|
|
|
70
76
|
* kopierfertige Befehle. Der Name steht als Kommentar dahinter -- eine nackte
|
|
71
77
|
* UUID sagt einem Menschen nichts, und zwei davon untereinander noch weniger.
|
|
72
78
|
*/
|
|
73
|
-
export function firmenIdZeilen(auswahl, s) {
|
|
74
|
-
|
|
79
|
+
export function firmenIdZeilen(auswahl, befehl, optionen, s) {
|
|
80
|
+
const vorne = `${befehlIn(befehl, s)}${optionen}`;
|
|
81
|
+
return auswahl.map((f, i) => ALLGEMEIN_TEXTE[s].companyIdLine(vorne, schalterIn("firma-id", s), f.id, f.name, i + 1));
|
|
75
82
|
}
|
|
76
83
|
/**
|
|
77
84
|
* Die Namen der eigenen Firmen aus einer `409`-Antwort -- was nicht als Text
|
|
@@ -116,7 +116,6 @@ export function gemerkteMitUnlesbaren() {
|
|
|
116
116
|
});
|
|
117
117
|
return { liste: ordneGemerkte(liste), unlesbar: unlesbar.sort() };
|
|
118
118
|
}
|
|
119
|
-
export const gemerkte = () => gemerkteMitUnlesbaren().liste;
|
|
120
119
|
export const gemerktOrdner = () => ORDNER;
|
|
121
120
|
export function vergiss(datei) {
|
|
122
121
|
try {
|
package/dist/wache-schalter.js
CHANGED
|
@@ -52,19 +52,11 @@ function schreibeWacheDatei(inhalt) {
|
|
|
52
52
|
/* Merkzettel nicht schreibbar -- der Befehl laeuft trotzdem durch. */
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
/** Ist die Wache dauerhaft abgeschaltet? Wirft nie. */
|
|
56
|
-
export function wacheIstAus() {
|
|
57
|
-
return leseWacheDatei()?.aus === true;
|
|
58
|
-
}
|
|
59
55
|
/** Wache an- oder abschalten. Der Tages-Merkzettel ("zuletzt") bleibt erhalten. */
|
|
60
56
|
export function setzeWache(an) {
|
|
61
57
|
const bisher = leseWacheDatei() ?? {};
|
|
62
58
|
schreibeWacheDatei({ ...bisher, aus: !an });
|
|
63
59
|
}
|
|
64
|
-
/** Ist die Wache heute schon gelaufen? Wirft nie. */
|
|
65
|
-
export function schonGelaufenAm(heute) {
|
|
66
|
-
return leseWacheDatei()?.zuletzt === heute;
|
|
67
|
-
}
|
|
68
60
|
/** Merken, dass die Wache heute gelaufen ist. Der Ausschalter bleibt erhalten. */
|
|
69
61
|
export function merkeLauf(heute) {
|
|
70
62
|
const bisher = leseWacheDatei() ?? {};
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kimal3",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"kimal3": "dist/kimal3.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"skill",
|
|
12
|
-
"claude-plugins",
|
|
13
|
-
"coach",
|
|
14
|
-
"zeigen",
|
|
15
|
-
"README.md"
|
|
16
|
-
],
|
|
17
|
-
"engines": {
|
|
18
|
-
"node": ">=20"
|
|
19
|
-
},
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"kimal3",
|
|
26
|
-
"cli"
|
|
27
|
-
],
|
|
28
|
-
"homepage": "https://app.kimal3.de/pro",
|
|
29
|
-
"license": "SEE LICENSE IN README.md"
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "kimal3",
|
|
3
|
+
"version": "0.9.2",
|
|
4
|
+
"description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kimal3": "dist/kimal3.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"skill",
|
|
12
|
+
"claude-plugins",
|
|
13
|
+
"coach",
|
|
14
|
+
"zeigen",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"kimal3",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://app.kimal3.de/pro",
|
|
29
|
+
"license": "SEE LICENSE IN README.md"
|
|
30
|
+
}
|
package/skill/commands/k3plan.md
CHANGED
|
@@ -117,7 +117,7 @@ lesen.
|
|
|
117
117
|
aussieht".
|
|
118
118
|
- `gescheitert` — sie ist gelaufen und hat etwas anderes gezeigt. Das ist ein Befund (Regel 1):
|
|
119
119
|
Die Story trägt damit ⚠ und steht in „In Arbeit“, solange eine Sitzung daran arbeitet, sonst in
|
|
120
|
-
„
|
|
120
|
+
„Plan“ — ihre Spec kommt nicht nach „Dein Test“.
|
|
121
121
|
- `offen` — alles, was nicht gelaufen ist: braucht einen Browser, eine Anmeldung, echte Daten
|
|
122
122
|
oder einen Menschen. Lesen zählt nur, wenn das Kriterium etwas über die Datei selbst sagt (eine
|
|
123
123
|
Datei gibt es, ein Text steht so da) — ein Verhalten beweist Lesen nicht.
|
|
@@ -153,7 +153,7 @@ Befunde mit, nicht nur den neuen. Fehlt das Feld, bleibt die Liste, wie sie ist.
|
|
|
153
153
|
|
|
154
154
|
Die App zeigt sie dem Menschen in genau diesem Wortlaut („So bleibt die Planung kurz"):
|
|
155
155
|
|
|
156
|
-
1. **Ein Problem wird keine neue Karte.** Hält eine Story nicht, was sie verspricht — Claudes Prüfung fällt durch oder du sagst „Passt nicht" —, hängt das Problem (Befund) an **dieser** Story. Sie steht in „
|
|
156
|
+
1. **Ein Problem wird keine neue Karte.** Hält eine Story nicht, was sie verspricht — Claudes Prüfung fällt durch oder du sagst „Passt nicht" —, hängt das Problem (Befund) an **dieser** Story. Sie steht in „Plan“, bis jemand daran arbeitet.
|
|
157
157
|
2. **Neu ist nur, was niemand versprochen hat. Das kommt in den Eingang, und der hat fünf Plätze.**
|
|
158
158
|
3. **Claude sortiert jedes Thema beim nächsten Abgleich (`/k3plan`) ein:** als neue Story in eine Spec, als Befund an eine Story, oder gestrichen mit Begründung. „Für später" gibt es nicht.
|
|
159
159
|
4. **Nichts liegt länger als 14 Tage im Eingang.**
|
|
@@ -220,7 +220,7 @@ sind erfunden.
|
|
|
220
220
|
in einem **eigenen** Aufruf mit `--ohne-sitzung`, getrennt von der Liste der fertigen: Eine gemischte
|
|
221
221
|
Liste ohne den Schalter bindet auch die unfertige Story an die Sitzung, und der Punkt leuchtet weiter.
|
|
222
222
|
Mit dem Schalter bleibt der Plan-Stand `in_arbeit`, aber der Punkt leuchtet nicht weiter, während das
|
|
223
|
-
Fenster offen bleibt — auf dem Board steht die Story dann in „
|
|
223
|
+
Fenster offen bleibt — auf dem Board steht die Story dann in „Plan“ als angefangen.
|
|
224
224
|
|
|
225
225
|
```
|
|
226
226
|
echo '[ … ]' | kimal3 plan melde --ohne-sitzung
|
|
@@ -112,7 +112,7 @@ Run the counter-check of each criterion **wherever you can**: the project's guar
|
|
|
112
112
|
expected result. Never from memory, never from last time, never because the code "looks right".
|
|
113
113
|
- `gescheitert` (failed) — it ran and showed something else. That is a finding (rule 1): the story
|
|
114
114
|
then carries ⚠ and sits under "In progress" while a session is working on it, otherwise under
|
|
115
|
-
"
|
|
115
|
+
"Plan" — its spec does not move to "Your test".
|
|
116
116
|
- `offen` (not run) — everything that did not run: needs a browser, a sign-in, real data or a
|
|
117
117
|
person. Reading only counts if the criterion says something about the file itself (a file
|
|
118
118
|
exists, a text is there as written) — reading does not prove a behaviour.
|
|
@@ -148,7 +148,7 @@ just the new one. If the field is missing, the list stays as it is.
|
|
|
148
148
|
|
|
149
149
|
The app shows them to the person in exactly this wording ("How planning stays short"):
|
|
150
150
|
|
|
151
|
-
1. **A problem does not become a new card.** If a story doesn't hold what it promises — Claude's check fails or you say "Doesn't work" — the problem (finding) is attached to that story. It stays in "
|
|
151
|
+
1. **A problem does not become a new card.** If a story doesn't hold what it promises — Claude's check fails or you say "Doesn't work" — the problem (finding) is attached to that story. It stays in "Plan" until someone works on it.
|
|
152
152
|
2. **Only what nobody promised is new. It goes into the inbox, which has five places.**
|
|
153
153
|
3. **Claude sorts every topic at the next check (`/k3plan`):** as a new story in a spec, as a finding on a story, or dropped with a reason. There is no "later".
|
|
154
154
|
4. **Nothing stays in the inbox longer than 14 days.**
|
|
@@ -215,7 +215,7 @@ made up.
|
|
|
215
215
|
in their **own** call with `--no-session`, separate from the list of the finished ones: a mixed list
|
|
216
216
|
without the flag ties the unfinished story to the session too, and the dot keeps glowing. With the flag
|
|
217
217
|
the plan state stays `in_arbeit`, but the dot stops glowing while the window stays open — on the board the
|
|
218
|
-
story then sits
|
|
218
|
+
story then sits in "Plan" as started.
|
|
219
219
|
|
|
220
220
|
```
|
|
221
221
|
echo '[ … ]' | kimal3 plan report --no-session
|