hmgr 0.0.3 → 0.0.5
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/dist/adapter/checkout-pfad.js +5 -20
- package/dist/adapter/claude-code.js +21 -17
- package/dist/adapter/dateisystem.js +105 -5
- package/dist/adapter/git.js +124 -11
- package/dist/adapter/hermes.js +163 -10
- package/dist/adapter/quelle-text.js +1 -3
- package/dist/adapter/skill-md.js +94 -18
- package/dist/adapter/terminal.js +24 -1
- package/dist/adapter/zustand-json.js +4 -0
- package/dist/cli/hilfe.js +144 -27
- package/dist/cli/main.js +374 -28
- package/dist/gui/icons.js +114 -0
- package/dist/gui/main.js +481 -0
- package/dist/gui/oeffnen.js +33 -0
- package/dist/gui/preload.cjs +55 -0
- package/dist/gui/renderer/fonts/Inter-LICENSE.txt +92 -0
- package/dist/gui/renderer/fonts/InterVariable.woff2 +0 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-Medium.woff2 +0 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-OFL.txt +93 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/dist/gui/renderer/index.html +491 -0
- package/dist/gui/renderer/renderer.js +2611 -0
- package/dist/gui/renderer/styles.css +638 -0
- package/dist/kern/anwendungsfaelle.js +798 -68
- package/dist/kern/modell.js +175 -1
- package/dist/kern/quelle-ort.js +42 -0
- package/dist/ports/harness.js +13 -0
- package/dist/ports/zustand.js +1 -1
- package/package.json +5 -2
package/dist/cli/main.js
CHANGED
|
@@ -5,7 +5,7 @@ import { realpathSync } from 'node:fs';
|
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { Anwendungsfaelle } from "../kern/anwendungsfaelle.js";
|
|
8
|
-
import { AbhaengigkeitFehlt, EinschraenkungOhneZeiger, HarnessNichtVorhanden, KeinPassenderRef, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, SkillMehrdeutig, SkillUnbekannt, ZeigerMehrdeutig, pluginSchluessel, } from "../kern/modell.js";
|
|
8
|
+
import { AbhaengigkeitFehlt, EinschraenkungOhneZeiger, HarnessNichtVorhanden, HarnessTypUnbekannt, KeinPassenderRef, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
9
9
|
import { Agents } from "../adapter/agents.js";
|
|
10
10
|
import { ClaudeCode } from "../adapter/claude-code.js";
|
|
11
11
|
import { Hermes } from "../adapter/hermes.js";
|
|
@@ -18,7 +18,12 @@ import { ZustandJson } from "../adapter/zustand-json.js";
|
|
|
18
18
|
import { HILFE, hilfeText } from "./hilfe.js";
|
|
19
19
|
export function umgebungLesen(env = process.env) {
|
|
20
20
|
const home = env.HOME ?? homedir();
|
|
21
|
-
|
|
21
|
+
const xdgStateHome = env.XDG_STATE_HOME;
|
|
22
|
+
return {
|
|
23
|
+
home,
|
|
24
|
+
hmgrHome: env.HMGR_HOME ?? path.join(home, '.hmgr'),
|
|
25
|
+
...(xdgStateHome !== undefined && xdgStateHome.length > 0 ? { xdgStateHome } : {}),
|
|
26
|
+
};
|
|
22
27
|
}
|
|
23
28
|
export function bauen(umgebung, optionen = {}) {
|
|
24
29
|
const zustand = new ZustandJson(path.join(umgebung.hmgrHome, 'state.json'));
|
|
@@ -29,16 +34,62 @@ export function bauen(umgebung, optionen = {}) {
|
|
|
29
34
|
const ports = {
|
|
30
35
|
harnesses: [claude, new Hermes(umgebung.home), new Agents(umgebung.home)],
|
|
31
36
|
quellen: new GitQuellen(wurzel),
|
|
32
|
-
ziele: new DateisystemZiele(wurzel, zustand),
|
|
37
|
+
ziele: new DateisystemZiele(wurzel, zustand, umgebung.xdgStateHome),
|
|
33
38
|
zeiger: new ComposerZeiger(),
|
|
34
39
|
zustand,
|
|
35
40
|
rueckfrage: optionen.rueckfrage ?? new TerminalRueckfrage(),
|
|
36
41
|
};
|
|
37
42
|
return { anwendungsfaelle: new Anwendungsfaelle(ports), ports };
|
|
38
43
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Was jedes Kommando auswertet, gleichlautend mit seiner Hilfe. Alles andere bricht ab. `--help`
|
|
46
|
+
* steht nirgends, weil der Hilfe-Dispatch vor jedem schalterLesen läuft.
|
|
47
|
+
*/
|
|
48
|
+
const SCHALTER = {
|
|
49
|
+
source: { werte: ['name'], flaggen: [] },
|
|
50
|
+
dir: { werte: ['composer'], flaggen: [] },
|
|
51
|
+
link: {
|
|
52
|
+
werte: ['harness', 'ort', 'composer', 'quelle', 'version'],
|
|
53
|
+
flaggen: ['local', 'lokal', 'ersetzen'],
|
|
54
|
+
},
|
|
55
|
+
unlink: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal'] },
|
|
56
|
+
adopt: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal', 'probelauf'] },
|
|
57
|
+
plugin: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: ['local', 'lokal'] },
|
|
58
|
+
sync: { werte: [], flaggen: [] },
|
|
59
|
+
update: { werte: [], flaggen: [] },
|
|
60
|
+
status: { werte: [], flaggen: [] },
|
|
61
|
+
harness: { werte: ['profil'], flaggen: [] },
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Was ein einzelnes Unterkommando von seiner Familie liest, gleichlautend mit ihrer Hilfe. Ohne
|
|
65
|
+
* `schalter` bleibt es bei der Familienliste, das reicht, solange alle Unterkommandos dieselben
|
|
66
|
+
* Schalter lesen.
|
|
67
|
+
*/
|
|
68
|
+
const UNTERKOMMANDOS = {
|
|
69
|
+
source: {
|
|
70
|
+
add: { woerter: 2, schalter: ['name'] },
|
|
71
|
+
remove: { woerter: 2, schalter: [] },
|
|
72
|
+
rename: { woerter: 3, schalter: [] },
|
|
73
|
+
list: { woerter: 1, schalter: [] },
|
|
74
|
+
},
|
|
75
|
+
dir: {
|
|
76
|
+
add: { woerter: 2, schalter: ['composer'] },
|
|
77
|
+
forget: { woerter: 2, schalter: [] },
|
|
78
|
+
list: { woerter: 1, schalter: [] },
|
|
79
|
+
},
|
|
80
|
+
plugin: {
|
|
81
|
+
add: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'quelle', 'local', 'lokal'] },
|
|
82
|
+
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'local', 'lokal'] },
|
|
83
|
+
list: { woerter: 1, schalter: [] },
|
|
84
|
+
},
|
|
85
|
+
harness: {
|
|
86
|
+
list: { woerter: 1, schalter: [] },
|
|
87
|
+
enable: { woerter: 2, schalter: [] },
|
|
88
|
+
disable: { woerter: 2, schalter: [] },
|
|
89
|
+
},
|
|
90
|
+
};
|
|
41
91
|
function schalterLesen(args, kommando) {
|
|
92
|
+
const erlaubt = SCHALTER[kommando];
|
|
42
93
|
const rest = [];
|
|
43
94
|
const werte = new Map();
|
|
44
95
|
const flaggen = new Set();
|
|
@@ -49,18 +100,50 @@ function schalterLesen(args, kommando) {
|
|
|
49
100
|
continue;
|
|
50
101
|
}
|
|
51
102
|
const name = arg.slice(2);
|
|
52
|
-
if (
|
|
103
|
+
if (erlaubt.flaggen.includes(name)) {
|
|
53
104
|
flaggen.add(name);
|
|
54
105
|
continue;
|
|
55
106
|
}
|
|
107
|
+
if (!erlaubt.werte.includes(name))
|
|
108
|
+
throw new Error(`Unbekannter Schalter --${name} für ${kommando}. hmgr help ${kommando} zeigt die Schalter.`);
|
|
56
109
|
const wert = args[++i];
|
|
57
110
|
if (wert === undefined)
|
|
58
111
|
throw new Error(`Schalter --${name} braucht einen Wert, etwa --${name} <wert>. ` +
|
|
59
|
-
|
|
112
|
+
`hmgr help ${kommando} zeigt die Schalter.`);
|
|
60
113
|
werte.set(name, wert);
|
|
61
114
|
}
|
|
62
115
|
return { rest, werte, flaggen };
|
|
63
116
|
}
|
|
117
|
+
/** Ein Wort mehr, als das Kommando liest, ist ein Tippfehler und kein stiller Zusatz. */
|
|
118
|
+
function restPruefen(rest, woerter, kommando, unterkommando) {
|
|
119
|
+
const zuviel = rest[woerter];
|
|
120
|
+
if (zuviel === undefined)
|
|
121
|
+
return;
|
|
122
|
+
const verwendung = unterkommando === undefined ? kommando : `${kommando} ${unterkommando}`;
|
|
123
|
+
// Der Name des Unterkommandos zählt zu den Wörtern, aber nicht zu den Argumenten.
|
|
124
|
+
const argumente = woerter - (unterkommando === undefined ? 0 : 1);
|
|
125
|
+
throw new Error(`${verwendung} erwartet ${argumente === 0 ? 'kein' : 'kein weiteres'} Argument „${zuviel}". ` +
|
|
126
|
+
`hmgr help ${kommando} zeigt die Verwendung.`);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Prüft, was erst mit dem Unterkommando feststeht: liest es die gesetzten Schalter, und steht ein
|
|
130
|
+
* Wort zu viel da. Ein unbekanntes Unterkommando meldet die Familie selbst.
|
|
131
|
+
*/
|
|
132
|
+
function unterkommandoPruefen(kommando, schalter) {
|
|
133
|
+
const unterkommando = schalter.rest[0];
|
|
134
|
+
if (unterkommando === undefined)
|
|
135
|
+
return;
|
|
136
|
+
const menge = UNTERKOMMANDOS[kommando][unterkommando];
|
|
137
|
+
if (menge === undefined)
|
|
138
|
+
return;
|
|
139
|
+
const erlaubt = menge.schalter;
|
|
140
|
+
if (erlaubt !== undefined)
|
|
141
|
+
for (const name of [...schalter.werte.keys(), ...schalter.flaggen])
|
|
142
|
+
if (!erlaubt.includes(name))
|
|
143
|
+
throw new Error(`Unbekannter Schalter --${name} für ${kommando} ${unterkommando}. ` +
|
|
144
|
+
`hmgr help ${kommando} zeigt die Schalter.`);
|
|
145
|
+
restPruefen(schalter.rest, menge.woerter, kommando, unterkommando);
|
|
146
|
+
}
|
|
64
147
|
function ortLesen(text) {
|
|
65
148
|
const roh = text ?? 'home';
|
|
66
149
|
if (roh === 'home')
|
|
@@ -78,6 +161,8 @@ const UEBERNAHME_GRUND = {
|
|
|
78
161
|
'keine-url': 'Arbeitskopie ohne remote.origin.url',
|
|
79
162
|
'kein-branch': 'Arbeitskopie ohne Branch (detached HEAD)',
|
|
80
163
|
'skill-unbekannt': 'Pfad ist in der Quelle kein Skill',
|
|
164
|
+
'lokale-aenderungen': 'Ordner weicht von der Quelle ab',
|
|
165
|
+
'nicht-pruefbar': 'hmgr kann nicht prüfen, ob der Ordner der Quelle entspricht',
|
|
81
166
|
};
|
|
82
167
|
export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
83
168
|
const { anwendungsfaelle, ports } = bauen(umgebung, optionen);
|
|
@@ -107,16 +192,26 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
107
192
|
return 0;
|
|
108
193
|
}
|
|
109
194
|
if (kommando === 'source') {
|
|
110
|
-
const
|
|
195
|
+
const schalter = schalterLesen(argv.slice(1), 'source');
|
|
196
|
+
unterkommandoPruefen('source', schalter);
|
|
197
|
+
const { rest, werte } = schalter;
|
|
198
|
+
const unterkommando = rest[0];
|
|
111
199
|
if (unterkommando === 'add') {
|
|
112
|
-
const text =
|
|
200
|
+
const text = rest[1];
|
|
113
201
|
if (text === undefined)
|
|
114
202
|
throw new Error('source add braucht eine URL oder einen Pfad, etwa hmgr source add https://example.com/skills.git');
|
|
115
|
-
const
|
|
116
|
-
const { skills, marketplace } = await anwendungsfaelle.quelleHinzufuegen(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.log(
|
|
203
|
+
const genannt = quelleAusText(text);
|
|
204
|
+
const { quelle, skills, marketplace, bekannt } = await anwendungsfaelle.quelleHinzufuegen(genannt, werte.get('name'));
|
|
205
|
+
// Dieselbe Quelle in anderer Schreibweise legt keine zweite an (E36).
|
|
206
|
+
if (bekannt)
|
|
207
|
+
console.log(quelleAlsText(quelle) === quelleAlsText(genannt)
|
|
208
|
+
? 'Quelle ist schon bekannt, keine zweite angelegt.'
|
|
209
|
+
: `Quelle ist schon bekannt, geführt als ${quelleAlsText(quelle)}. Keine zweite angelegt.`);
|
|
210
|
+
console.log(`Quelle ${quelleAlsText(quelle)} (${quelle.art}) als „${quelleName(quelle)}", ${skills.length} Skills:`);
|
|
211
|
+
for (const skill of skills) {
|
|
212
|
+
const beschreibung = skill.beschreibung === undefined ? '' : ` ${kappen(skill.beschreibung)}`;
|
|
213
|
+
console.log(` ${skill.name} ${skill.pfad}${beschreibung}`);
|
|
214
|
+
}
|
|
120
215
|
if (marketplace !== null) {
|
|
121
216
|
console.log(`Marketplace ${marketplace.name}, ${marketplace.plugins.length} Plugins:`);
|
|
122
217
|
for (const plugin of marketplace.plugins) {
|
|
@@ -126,18 +221,94 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
126
221
|
}
|
|
127
222
|
return 0;
|
|
128
223
|
}
|
|
224
|
+
if (unterkommando === 'remove') {
|
|
225
|
+
const text = rest[1];
|
|
226
|
+
if (text === undefined)
|
|
227
|
+
throw new Error('source remove braucht eine URL oder einen Pfad. hmgr source list zeigt die bekannten Quellen.');
|
|
228
|
+
const genannt = quelleAusText(text);
|
|
229
|
+
// Inventar (E58) vor dem Vergessen erheben (F14): danach tragen die Links ihre Quelle nicht
|
|
230
|
+
// mehr. Bei unbekannter Quelle entfällt der Ziel-Scan, den Fehler wirft quelleEntfernen.
|
|
231
|
+
const key = quelleSchluessel(genannt);
|
|
232
|
+
// Die Rückfrage kommt jetzt VOR dem Vergessen (F31a), nennen muss sie die Quelle trotzdem
|
|
233
|
+
// unter ihrer geführten Schreibweise (E36), nicht unter der getippten.
|
|
234
|
+
const gefuehrtVorher = (await anwendungsfaelle.quellen()).find((q) => quelleSchluessel(q) === key);
|
|
235
|
+
const inventar = gefuehrtVorher === undefined ? undefined : await anwendungsfaelle.quelleAufraeumInventar(genannt);
|
|
236
|
+
const meldeVergessen = (gefuehrt, zahl) => {
|
|
237
|
+
console.log(`Quelle ${quelleAlsText(gefuehrt)} vergessen, Checkouts und Links bleiben liegen.`);
|
|
238
|
+
if (zahl === 0)
|
|
239
|
+
console.log(' Kein Link betroffen.');
|
|
240
|
+
else {
|
|
241
|
+
console.log(` ${zahl === 1 ? '1 Link gilt' : `${zahl} Links gelten`} jetzt als „Link ohne bekannte Quelle", das Aktualisieren prüft ${zahl === 1 ? 'ihn' : 'sie'} nicht mehr.`);
|
|
242
|
+
console.log(` source add derselben Quelle macht ${zahl === 1 ? 'ihn wieder zu einem normalen Link' : 'sie wieder zu normalen Links'}.`);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
// Leeres Inventar (F31a): nichts zum Aufräumen, keine Rückfrage, sofort vergessen wie bisher.
|
|
246
|
+
if (gefuehrtVorher === undefined ||
|
|
247
|
+
inventar === undefined ||
|
|
248
|
+
(inventar.links.length === 0 && inventar.checkouts.length === 0)) {
|
|
249
|
+
const gefuehrt = await anwendungsfaelle.quelleEntfernen(genannt);
|
|
250
|
+
meldeVergessen(gefuehrt, inventar?.links.length ?? 0);
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
// Aufräum-Rückfrage VOR dem Vergessen (F31a): ein Ja mit lokalen Änderungen darf weder löschen
|
|
254
|
+
// noch die Quelle vergessen, das entscheidet erst quelleAufraeumen unten.
|
|
255
|
+
const teile = [
|
|
256
|
+
zahlwort(inventar.links.length, 'Link', 'Links'),
|
|
257
|
+
zahlwort(inventar.checkouts.length, 'Checkout', 'Checkouts'),
|
|
258
|
+
];
|
|
259
|
+
const bleibt = (n) => (n === 1 ? ', bleibt liegen' : ', bleiben liegen');
|
|
260
|
+
if (inventar.plugins.length > 0)
|
|
261
|
+
teile.push(zahlwort(inventar.plugins.length, 'installiertes Plugin', 'installierte Plugins') + bleibt(inventar.plugins.length));
|
|
262
|
+
if (inventar.zeigerPfade.length > 0)
|
|
263
|
+
teile.push(zahlwort(inventar.zeigerPfade.length, 'Zeiger-Eintrag', 'Zeiger-Einträge') + bleibt(inventar.zeigerPfade.length));
|
|
264
|
+
let frage = `Skills von ${quelleAlsText(gefuehrtVorher)} komplett vom Rechner löschen (${teile.join(', ')})?`;
|
|
265
|
+
if (inventar.checkoutsMitLokalenAenderungen.length > 0) {
|
|
266
|
+
const anzahl = inventar.checkoutsMitLokalenAenderungen.length;
|
|
267
|
+
frage +=
|
|
268
|
+
`\n${anzahl === 1 ? 'Achtung: 1 Checkout hat' : `Achtung: ${anzahl} Checkouts haben`} lokale Änderungen. ` +
|
|
269
|
+
'Ein Ja bricht dann ab, ohne zu löschen, die Quelle bleibt bekannt. Erst committen/pushen oder verwerfen, dann erneut versuchen.';
|
|
270
|
+
}
|
|
271
|
+
if (!(await ports.rueckfrage.bestaetigen(frage))) {
|
|
272
|
+
const gefuehrt = await anwendungsfaelle.quelleEntfernen(genannt);
|
|
273
|
+
meldeVergessen(gefuehrt, inventar.links.length);
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
// Ja: erst aufräumen (wirft bei lokalen Änderungen, VOR jeder Löschung, F31a), erst danach die
|
|
277
|
+
// Quelle vergessen, damit ein Abbruch sie unberührt lässt.
|
|
278
|
+
const ergebnis = await anwendungsfaelle.quelleAufraeumen(inventar);
|
|
279
|
+
const gefuehrt = await anwendungsfaelle.quelleEntfernen(genannt);
|
|
280
|
+
console.log(`Quelle ${quelleAlsText(gefuehrt)} entfernt und aufgeräumt.`);
|
|
281
|
+
console.log(` entfernt ${zahlwort(ergebnis.entfernteLinks, 'Link', 'Links')}, ${zahlwort(ergebnis.entfernteCheckouts, 'Checkout', 'Checkouts')}.`);
|
|
282
|
+
if (inventar.plugins.length > 0)
|
|
283
|
+
console.log(` installierte Plugins bleiben liegen: ${inventar.plugins.join(', ')}`);
|
|
284
|
+
if (inventar.zeigerPfade.length > 0)
|
|
285
|
+
console.log(` Zeiger-Einträge bleiben liegen: ${inventar.zeigerPfade.join(', ')}`);
|
|
286
|
+
return 0;
|
|
287
|
+
}
|
|
288
|
+
if (unterkommando === 'rename') {
|
|
289
|
+
const text = rest[1];
|
|
290
|
+
const name = rest[2];
|
|
291
|
+
if (text === undefined || name === undefined || name.trim().length === 0)
|
|
292
|
+
throw new Error('source rename braucht eine URL oder einen Pfad und einen neuen Namen, etwa hmgr source rename https://example.com/skills.git skills');
|
|
293
|
+
const genannt = quelleAusText(text);
|
|
294
|
+
const umbenannt = await anwendungsfaelle.quelleUmbenennen(genannt, name);
|
|
295
|
+
console.log(`Quelle ${quelleAlsText(umbenannt)} heißt jetzt „${quelleName(umbenannt)}".`);
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
129
298
|
if (unterkommando === 'list') {
|
|
130
299
|
const zustand = await ports.zustand.lesen();
|
|
131
300
|
if (zustand.quellen.length === 0)
|
|
132
301
|
console.log('Keine Quellen.');
|
|
133
302
|
for (const quelle of zustand.quellen)
|
|
134
|
-
console.log(`${quelle.art} ${quelleAlsText(quelle)}`);
|
|
303
|
+
console.log(`${quelle.art} ${quelleName(quelle)} ${quelleAlsText(quelle)}`);
|
|
135
304
|
return 0;
|
|
136
305
|
}
|
|
137
|
-
throw new Error(`${unterkommando === undefined ? 'source braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für source`}: source add <url|pfad> oder source list. hmgr help source zeigt Beispiele.`);
|
|
306
|
+
throw new Error(`${unterkommando === undefined ? 'source braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für source`}: source add <url|pfad>, source rename <url|pfad> <name>, source remove <url|pfad> oder source list. hmgr help source zeigt Beispiele.`);
|
|
138
307
|
}
|
|
139
308
|
if (kommando === 'dir') {
|
|
140
|
-
const
|
|
309
|
+
const schalter = schalterLesen(argv.slice(1), 'dir');
|
|
310
|
+
unterkommandoPruefen('dir', schalter);
|
|
311
|
+
const { rest, werte } = schalter;
|
|
141
312
|
const unterkommando = rest[0];
|
|
142
313
|
if (unterkommando === 'add') {
|
|
143
314
|
const pfad = rest[1];
|
|
@@ -176,6 +347,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
176
347
|
}
|
|
177
348
|
if (kommando === 'link' || kommando === 'unlink') {
|
|
178
349
|
const { rest, werte, flaggen } = schalterLesen(argv.slice(1), kommando);
|
|
350
|
+
restPruefen(rest, 1, kommando);
|
|
179
351
|
const skill = rest[0];
|
|
180
352
|
if (skill === undefined)
|
|
181
353
|
throw new Error(`${kommando} braucht einen Skill-Namen, etwa hmgr ${kommando} <skill>. ` +
|
|
@@ -191,8 +363,10 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
191
363
|
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
192
364
|
}
|
|
193
365
|
if (kommando === 'unlink') {
|
|
194
|
-
await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
366
|
+
const ergebnis = await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
195
367
|
console.log(`Link ${skill} entfernt.`);
|
|
368
|
+
if (ergebnis.hinweis !== undefined)
|
|
369
|
+
console.log(ergebnis.hinweis);
|
|
196
370
|
return 0;
|
|
197
371
|
}
|
|
198
372
|
const quelle = werte.get('quelle');
|
|
@@ -213,6 +387,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
213
387
|
}
|
|
214
388
|
if (kommando === 'adopt') {
|
|
215
389
|
const { rest, werte, flaggen } = schalterLesen(argv.slice(1), 'adopt');
|
|
390
|
+
restPruefen(rest, 1, 'adopt');
|
|
216
391
|
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
|
|
217
392
|
const probelauf = flaggen.has('probelauf');
|
|
218
393
|
const name = rest[0];
|
|
@@ -248,10 +423,14 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
248
423
|
}
|
|
249
424
|
const quellenText = neueQuellen === 1 ? '1 neue Quelle' : `${neueQuellen} neue Quellen`;
|
|
250
425
|
console.log(`${uebernommen} übernommen, ${uebersprungen} übersprungen, ${quellenText}.`);
|
|
426
|
+
if (ergebnis.hinweis !== undefined)
|
|
427
|
+
console.log(ergebnis.hinweis);
|
|
251
428
|
return 0;
|
|
252
429
|
}
|
|
253
430
|
if (kommando === 'plugin') {
|
|
254
|
-
const
|
|
431
|
+
const schalter = schalterLesen(argv.slice(1), 'plugin');
|
|
432
|
+
unterkommandoPruefen('plugin', schalter);
|
|
433
|
+
const { rest, werte, flaggen } = schalter;
|
|
255
434
|
const unterkommando = rest[0];
|
|
256
435
|
if (unterkommando === 'list') {
|
|
257
436
|
const marketplaces = await anwendungsfaelle.marketplaces();
|
|
@@ -301,6 +480,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
301
480
|
}
|
|
302
481
|
if (kommando === 'sync') {
|
|
303
482
|
const { rest } = schalterLesen(argv.slice(1), 'sync');
|
|
483
|
+
restPruefen(rest, 1, 'sync');
|
|
304
484
|
const ergebnis = await anwendungsfaelle.abgleich(rest[0]);
|
|
305
485
|
if (ergebnis.zeigerPfad !== undefined)
|
|
306
486
|
console.log(`Zeiger ${ergebnis.zeigerPfad}`);
|
|
@@ -317,7 +497,8 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
317
497
|
return 0;
|
|
318
498
|
}
|
|
319
499
|
if (kommando === 'update') {
|
|
320
|
-
|
|
500
|
+
restPruefen(schalterLesen(argv.slice(1), 'update').rest, 0, 'update');
|
|
501
|
+
const { checkouts: liste, pluginHarnesses, aufraeumen } = await anwendungsfaelle.aktualisieren();
|
|
321
502
|
if (liste.length === 0)
|
|
322
503
|
console.log('Keine Checkouts.');
|
|
323
504
|
for (const { checkout, aktualisierung } of liste) {
|
|
@@ -326,14 +507,31 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
326
507
|
console.log(` aktualisiert ${kopf} ${aktualisierung.von.slice(0, 8)} -> ${aktualisierung.nach.slice(0, 8)}`);
|
|
327
508
|
else if (aktualisierung.ergebnis === 'unveraendert')
|
|
328
509
|
console.log(` unverändert ${kopf}`);
|
|
329
|
-
else
|
|
510
|
+
else if (aktualisierung.ergebnis === 'uebersprungen')
|
|
330
511
|
console.log(` übersprungen ${kopf} lokale Änderung (E15)`);
|
|
512
|
+
else
|
|
513
|
+
console.log(` fehlgeschlagen ${kopf} ${aktualisierung.fehler}`);
|
|
331
514
|
}
|
|
332
515
|
for (const name of pluginHarnesses)
|
|
333
516
|
console.log(` Plugins ${name} aktualisiert (E14)`);
|
|
517
|
+
// Zum Schluss die verwaisten Checkouts: was gelöscht wurde und was liegen bleibt.
|
|
518
|
+
for (const verwaist of aufraeumen.verwaiste) {
|
|
519
|
+
const kopf = `${quelleAlsText(verwaist.checkout.quelle)} @ ${verwaist.checkout.ref}`;
|
|
520
|
+
if (verwaist.geloescht)
|
|
521
|
+
console.log(` gelöscht ${kopf} verwaist (${verwaist.checkout.pfad})`);
|
|
522
|
+
else if (verwaist.lokaleAenderung)
|
|
523
|
+
console.log(` liegen lassen ${kopf} verwaist, aber lokale Änderung (E15)`);
|
|
524
|
+
else
|
|
525
|
+
console.log(` liegen lassen ${kopf} verwaist, ohne Bestätigung nicht gelöscht`);
|
|
526
|
+
}
|
|
527
|
+
if (aufraeumen.verwaiste.length > 0) {
|
|
528
|
+
const geloescht = aufraeumen.verwaiste.filter((v) => v.geloescht).length;
|
|
529
|
+
console.log(`${aufraeumen.verwaiste.length} verwaist, ${geloescht} gelöscht, ${aufraeumen.verwaiste.length - geloescht} liegen geblieben.`);
|
|
530
|
+
}
|
|
334
531
|
return 0;
|
|
335
532
|
}
|
|
336
533
|
if (kommando === 'status') {
|
|
534
|
+
restPruefen(schalterLesen(argv.slice(1), 'status').rest, 0, 'status');
|
|
337
535
|
for (const zielZustand of await anwendungsfaelle.istZustand()) {
|
|
338
536
|
console.log(`Ziel ${zielZustand.ziel.harness} ${beschreibeOrt(zielZustand.ziel.ort)} (${zielZustand.verzeichnis})`);
|
|
339
537
|
if (zielZustand.eintraege.length === 0)
|
|
@@ -342,12 +540,12 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
342
540
|
if (eintrag.art === 'fremd')
|
|
343
541
|
console.log(` ${eintrag.name} fremd`);
|
|
344
542
|
else if (eintrag.art === 'link-ohne-quelle')
|
|
345
|
-
console.log(` ${eintrag.name} Link ohne bekannte Quelle (${eintrag.checkoutPfad})`);
|
|
543
|
+
console.log(` ${eintrag.name} Link ohne bekannte Quelle (${eintrag.checkoutPfad})${beschreibeGrund(zielZustand.gruende.get(eintrag.name))}`);
|
|
346
544
|
else {
|
|
347
545
|
// Steht im Zeiger eine Versionseinschränkung, kommt sie vor den aufgelösten Ref (E33).
|
|
348
546
|
const einschraenkung = zielZustand.einschraenkungen.get(eintrag.link.name);
|
|
349
547
|
const spanne = einschraenkung === undefined ? '' : `${einschraenkung} → `;
|
|
350
|
-
console.log(` ${eintrag.link.name} ${spanne}${quelleAlsText(eintrag.link.checkout.quelle)} @ ${eintrag.link.checkout.ref} (${eintrag.link.skill.pfad})`);
|
|
548
|
+
console.log(` ${eintrag.link.name} ${spanne}${quelleAlsText(eintrag.link.checkout.quelle)} @ ${eintrag.link.checkout.ref} (${eintrag.link.skill.pfad})${beschreibeGrund(zielZustand.gruende.get(eintrag.link.name))}`);
|
|
351
549
|
}
|
|
352
550
|
}
|
|
353
551
|
if (zielZustand.plugins !== undefined)
|
|
@@ -358,8 +556,132 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
358
556
|
console.log(`Nicht vorhanden: ${fehlend.join(', ')}`);
|
|
359
557
|
return 0;
|
|
360
558
|
}
|
|
559
|
+
if (kommando === 'harness') {
|
|
560
|
+
const schalter = schalterLesen(argv.slice(1), 'harness');
|
|
561
|
+
unterkommandoPruefen('harness', schalter);
|
|
562
|
+
const { rest, werte } = schalter;
|
|
563
|
+
const unterkommando = rest[0];
|
|
564
|
+
if (unterkommando === 'skills-dir') {
|
|
565
|
+
const harnessName = rest[1];
|
|
566
|
+
if (harnessName === undefined)
|
|
567
|
+
throw new Error('harness skills-dir braucht einen Harness-Namen, etwa hmgr harness skills-dir hermes. ' +
|
|
568
|
+
'hmgr harness list zeigt die bekannten Namen.');
|
|
569
|
+
const unter = rest[2];
|
|
570
|
+
const profilName = werte.get('profil');
|
|
571
|
+
const ort = profilName === undefined ? { art: 'home' } : { art: 'profil', name: profilName };
|
|
572
|
+
const ziel = { harness: harnessName, ort };
|
|
573
|
+
const ortText = beschreibeOrt(ort);
|
|
574
|
+
if (unter === undefined) {
|
|
575
|
+
restPruefen(rest, 2, 'harness', 'skills-dir');
|
|
576
|
+
const pfad = await anwendungsfaelle.verwalteterSkillOrdner(ziel);
|
|
577
|
+
if (pfad === undefined) {
|
|
578
|
+
console.log(`${harnessName} ${ortText}: kein Skill-Ordner verwaltet.`);
|
|
579
|
+
console.log(` Festlegen mit hmgr harness skills-dir ${harnessName} add <pfad>.`);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
console.log(`${harnessName} ${ortText} ${pfad}`);
|
|
583
|
+
}
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
586
|
+
if (unter === 'add') {
|
|
587
|
+
restPruefen(rest, 4, 'harness', 'skills-dir');
|
|
588
|
+
const pfad = rest[3];
|
|
589
|
+
if (pfad === undefined)
|
|
590
|
+
throw new Error('harness skills-dir <name> add braucht einen Pfad, etwa ' +
|
|
591
|
+
'hmgr harness skills-dir hermes add ~/.hermes/external-skills.');
|
|
592
|
+
const gesetzt = await anwendungsfaelle.skillOrdnerFestlegen(ziel, pfad);
|
|
593
|
+
console.log(`${harnessName} ${ortText} liest Skills jetzt aus ${gesetzt}.`);
|
|
594
|
+
return 0;
|
|
595
|
+
}
|
|
596
|
+
if (unter === 'remove') {
|
|
597
|
+
restPruefen(rest, 3, 'harness', 'skills-dir');
|
|
598
|
+
const ergebnis = await anwendungsfaelle.skillOrdnerEntfernen(ziel);
|
|
599
|
+
console.log(`${harnessName} ${ortText}: Eintrag für ${ergebnis.pfad} entfernt.`);
|
|
600
|
+
const inhalt = `${zahlwort(ergebnis.hmgrLinks, 'hmgr-Link', 'hmgr-Links')}, ${zahlwort(ergebnis.fremdeEintraege, 'fremder Eintrag', 'fremde Einträge')}`;
|
|
601
|
+
const frage = `Ordner ${ergebnis.pfad} enthält noch ${inhalt}. Löschen?`;
|
|
602
|
+
if (await ports.rueckfrage.bestaetigen(frage)) {
|
|
603
|
+
await anwendungsfaelle.skillOrdnerLoeschen(ziel, ergebnis.pfad);
|
|
604
|
+
console.log(` Ordner ${ergebnis.pfad} gelöscht.`);
|
|
605
|
+
}
|
|
606
|
+
return 0;
|
|
607
|
+
}
|
|
608
|
+
throw new Error(`Unbekanntes Unterkommando „${unter}" für harness skills-dir: harness skills-dir <name>, ` +
|
|
609
|
+
'harness skills-dir <name> add <pfad> oder harness skills-dir <name> remove. ' +
|
|
610
|
+
'hmgr help harness zeigt Beispiele.');
|
|
611
|
+
}
|
|
612
|
+
if (unterkommando === 'list') {
|
|
613
|
+
const aktive = new Set(await anwendungsfaelle.aktiveHarnesses());
|
|
614
|
+
const fehlend = new Set(await anwendungsfaelle.nichtVorhandeneHarnesses());
|
|
615
|
+
for (const name of anwendungsfaelle.harnessTypen()) {
|
|
616
|
+
const aktivText = aktive.has(name) ? 'aktiv' : 'inaktiv';
|
|
617
|
+
const vorhandenText = fehlend.has(name) ? 'nicht vorhanden' : 'vorhanden';
|
|
618
|
+
console.log(`${name} ${aktivText}, ${vorhandenText}`);
|
|
619
|
+
}
|
|
620
|
+
return 0;
|
|
621
|
+
}
|
|
622
|
+
if (unterkommando === 'enable' || unterkommando === 'disable') {
|
|
623
|
+
const name = rest[1];
|
|
624
|
+
if (name === undefined)
|
|
625
|
+
throw new Error(`harness ${unterkommando} braucht einen Harness-Namen. hmgr harness list zeigt die bekannten Harness-Typen.`);
|
|
626
|
+
const vorherAktiv = new Set(await anwendungsfaelle.aktiveHarnesses());
|
|
627
|
+
if (unterkommando === 'enable') {
|
|
628
|
+
await anwendungsfaelle.harnessAktivieren(name);
|
|
629
|
+
console.log(vorherAktiv.has(name) ? `${name} ist schon aktiv.` : `${name} aktiviert.`);
|
|
630
|
+
return 0;
|
|
631
|
+
}
|
|
632
|
+
await anwendungsfaelle.harnessDeaktivieren(name);
|
|
633
|
+
console.log(vorherAktiv.has(name) ? `${name} deaktiviert.` : `${name} ist schon inaktiv.`);
|
|
634
|
+
// Säubern-Angebot nur, wenn eigene Konfiguration liegt, mit derselben Bedingung wie die GUI
|
|
635
|
+
// (E38, GRILL.md Entwurfskritik 4): eigene Links oder eine liegen gebliebene Registrierung
|
|
636
|
+
// ohne Link (E38 R7), sonst machten CLI und GUI verschiedene Angebote.
|
|
637
|
+
const ziele = (await anwendungsfaelle.istZustand()).filter((z) => z.ziel.harness === name &&
|
|
638
|
+
(z.eintraege.some((eintrag) => eintrag.art !== 'fremd') || z.istRegistriert));
|
|
639
|
+
if (ziele.length === 0)
|
|
640
|
+
return 0;
|
|
641
|
+
const links = ziele.reduce((n, z) => n + z.eintraege.filter((eintrag) => eintrag.art !== 'fremd').length, 0);
|
|
642
|
+
const eintraege = ziele.filter((z) => z.istRegistriert).length;
|
|
643
|
+
// Wer den harness-eigenen Eintrag angelegt hat, weiß niemand (E38 R7), also wird er benannt,
|
|
644
|
+
// nicht hmgr zugeschrieben. Gleicher Text wie bilanzText in der GUI.
|
|
645
|
+
const teile = [
|
|
646
|
+
links === 0
|
|
647
|
+
? zahlwort(ziele.length, 'leeres Ziel', 'leere Ziele')
|
|
648
|
+
: `${zahlwort(links, 'Link', 'Links')} in ${zahlwort(ziele.length, 'Ziel', 'Zielen')}`,
|
|
649
|
+
];
|
|
650
|
+
if (eintraege > 0)
|
|
651
|
+
teile.push(zahlwort(eintraege, 'harness-eigener Eintrag', 'harness-eigene Einträge'));
|
|
652
|
+
const liste = ziele.map((z) => ` ${z.verzeichnis}`).join('\n');
|
|
653
|
+
const frage = `Es liegt noch Konfiguration (${teile.join(', ')}):\n${liste}\nEntfernen?`;
|
|
654
|
+
if (!(await ports.rueckfrage.bestaetigen(frage))) {
|
|
655
|
+
console.log(' Konfiguration bleibt liegen, hmgr pflegt sie nicht mehr.');
|
|
656
|
+
return 0;
|
|
657
|
+
}
|
|
658
|
+
const ergebnis = await anwendungsfaelle.saeubern(name);
|
|
659
|
+
for (const zielErgebnis of ergebnis.ziele) {
|
|
660
|
+
if (zielErgebnis.entfernteLinks.length > 0)
|
|
661
|
+
console.log(` entfernt ${beschreibeOrt(zielErgebnis.ziel.ort)} ${zielErgebnis.entfernteLinks.join(', ')}`);
|
|
662
|
+
if (zielErgebnis.konfigurationZurueckgebaut)
|
|
663
|
+
console.log(` zurückgebaut ${beschreibeOrt(zielErgebnis.ziel.ort)} harness-eigene Konfiguration`);
|
|
664
|
+
}
|
|
665
|
+
return 0;
|
|
666
|
+
}
|
|
667
|
+
throw new Error(`${unterkommando === undefined ? 'harness braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für harness`}: harness list, harness enable <name> oder harness disable <name>. hmgr help harness zeigt Beispiele.`);
|
|
668
|
+
}
|
|
361
669
|
throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
|
|
362
670
|
}
|
|
671
|
+
/** Grund eines Links als Suffix (E37). „manuell" bleibt stumm, damit die Ausgabe ruhig bleibt. */
|
|
672
|
+
function beschreibeGrund(grund) {
|
|
673
|
+
if (grund === undefined || grund.art === 'manuell')
|
|
674
|
+
return '';
|
|
675
|
+
return grund.art === 'plugin' ? ` Plugin „${grund.plugin}"` : ' über Zeiger';
|
|
676
|
+
}
|
|
677
|
+
/** Zahl mit passender Endung, Singular oder Plural, für die Säubern-Frage bei harness disable (E38). */
|
|
678
|
+
function zahlwort(n, ein, viele) {
|
|
679
|
+
return `${n} ${n === 1 ? ein : viele}`;
|
|
680
|
+
}
|
|
681
|
+
/** Text auf `laenge` Zeichen kappen, mit „…" markiert, damit eine Skill-Liste lesbar bleibt (E39). */
|
|
682
|
+
function kappen(text, laenge = 80) {
|
|
683
|
+
return text.length <= laenge ? text : `${text.slice(0, laenge - 1)}…`;
|
|
684
|
+
}
|
|
363
685
|
function beschreibeOrt(ort) {
|
|
364
686
|
if (ort.art === 'home')
|
|
365
687
|
return 'home';
|
|
@@ -373,12 +695,21 @@ function istModellFehler(fehler) {
|
|
|
373
695
|
fehler instanceof SkillUnbekannt ||
|
|
374
696
|
fehler instanceof OrtNichtUnterstuetzt ||
|
|
375
697
|
fehler instanceof ZeigerMehrdeutig ||
|
|
698
|
+
fehler instanceof ZeigerSchreibenGesperrt ||
|
|
376
699
|
fehler instanceof HarnessNichtVorhanden ||
|
|
700
|
+
fehler instanceof HarnessTypUnbekannt ||
|
|
377
701
|
fehler instanceof KeinPassenderRef ||
|
|
378
702
|
fehler instanceof EinschraenkungOhneZeiger ||
|
|
379
703
|
fehler instanceof PluginMehrdeutig ||
|
|
380
704
|
fehler instanceof PluginUnbekannt ||
|
|
381
|
-
fehler instanceof AbhaengigkeitFehlt
|
|
705
|
+
fehler instanceof AbhaengigkeitFehlt ||
|
|
706
|
+
fehler instanceof QuelleUnbekannt ||
|
|
707
|
+
fehler instanceof QuelleNameBelegt ||
|
|
708
|
+
fehler instanceof QuelleAufraeumenLokaleAenderungen ||
|
|
709
|
+
fehler instanceof SkillOrdnerFaehigkeitFehlt ||
|
|
710
|
+
fehler instanceof SkillOrdnerBereitsVerwaltet ||
|
|
711
|
+
fehler instanceof SkillOrdnerNichtVerwaltet ||
|
|
712
|
+
fehler instanceof SkillOrdnerUngueltig);
|
|
382
713
|
}
|
|
383
714
|
/**
|
|
384
715
|
* Der nächste Schritt zu einem Fehler aus dem Kern. Die Fehlertexte selbst bleiben, wie das Modell
|
|
@@ -398,14 +729,29 @@ function naechsterSchritt(fehler) {
|
|
|
398
729
|
if (fehler instanceof AbhaengigkeitFehlt)
|
|
399
730
|
return 'Die Abhängigkeit steht in keinem Plugin desselben Marketplace, das kann nur die Quelle beheben.';
|
|
400
731
|
if (fehler instanceof KeinPassenderRef)
|
|
401
|
-
return '
|
|
732
|
+
return 'Die Tags der Quelle sind frisch geholt, keiner passt. Sonst einen vorhandenen Tag- oder Branchnamen als --version nennen.';
|
|
402
733
|
if (fehler instanceof HarnessNichtVorhanden)
|
|
403
734
|
return 'Der Harness legt sein Verzeichnis beim ersten Start an. hmgr status zeigt, welche Harnesses hier liegen.';
|
|
404
735
|
if (fehler instanceof OrtNichtUnterstuetzt)
|
|
405
736
|
return 'profil:<name> kennt nur hermes, claude-code und agents kennen home und dir:<pfad>.';
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
737
|
+
if (fehler instanceof HarnessTypUnbekannt)
|
|
738
|
+
return 'Bekannt sind claude-code, hermes und agents. hmgr harness list zeigt sie mit ihrem Stand.';
|
|
739
|
+
if (fehler instanceof QuelleUnbekannt)
|
|
740
|
+
return 'hmgr source list zeigt die bekannten Quellen.';
|
|
741
|
+
if (fehler instanceof QuelleNameBelegt)
|
|
742
|
+
return 'hmgr source list zeigt die schon vergebenen Namen.';
|
|
743
|
+
if (fehler instanceof ZeigerSchreibenGesperrt)
|
|
744
|
+
return 'Nutze --local, das schreibt nichts in den Zeiger.';
|
|
745
|
+
if (fehler instanceof SkillOrdnerFaehigkeitFehlt)
|
|
746
|
+
return 'Nur hermes kennt einen verwalteten Skill-Ordner.';
|
|
747
|
+
if (fehler instanceof SkillOrdnerBereitsVerwaltet)
|
|
748
|
+
return `Erst entfernen: hmgr harness skills-dir ${fehler.ziel.harness} remove${fehler.ziel.ort.art === 'profil' ? ` --profil ${fehler.ziel.ort.name}` : ''}.`;
|
|
749
|
+
if (fehler instanceof SkillOrdnerNichtVerwaltet)
|
|
750
|
+
return `Festlegen mit hmgr harness skills-dir ${fehler.ziel.harness} add <pfad>${fehler.ziel.ort.art === 'profil' ? ` --profil ${fehler.ziel.ort.name}` : ''}.`;
|
|
751
|
+
if (fehler instanceof SkillOrdnerUngueltig)
|
|
752
|
+
return fehler.grund === 'ausserhalb'
|
|
753
|
+
? 'Der Pfad muss unterhalb des Hermes-Home liegen.'
|
|
754
|
+
: 'Das ist Hermes\' eigener Skill-Ordner, ein anderer Pfad muss her.';
|
|
409
755
|
return undefined;
|
|
410
756
|
}
|
|
411
757
|
export async function main(argv, umgebung) {
|