hmgr 0.0.6 → 0.0.12
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/agents.js +14 -5
- package/dist/adapter/bundle-yaml.js +87 -0
- package/dist/adapter/claude-code.js +53 -12
- package/dist/adapter/dateisystem.js +85 -43
- package/dist/adapter/git.js +148 -0
- package/dist/adapter/heime.js +76 -0
- package/dist/adapter/hermes.js +16 -7
- package/dist/adapter/npm-registry.js +61 -0
- package/dist/adapter/paket-version.js +25 -0
- package/dist/adapter/umgebung.js +6 -1
- package/dist/adapter/verlauf-json.js +39 -0
- package/dist/adapter/wsl.js +212 -0
- package/dist/adapter/zustand-json.js +40 -12
- package/dist/cli/hilfe.js +197 -11
- package/dist/cli/main.js +376 -21
- package/dist/gui/hmgr-cli.js +44 -0
- package/dist/gui/main.js +445 -4
- package/dist/gui/preload.cjs +30 -2
- package/dist/gui/renderer/index.html +79 -16
- package/dist/gui/renderer/renderer.js +807 -286
- package/dist/gui/renderer/styles.css +89 -16
- package/dist/kern/anwendungsfaelle.js +687 -18
- package/dist/kern/modell.js +192 -1
- package/dist/kern/version.js +13 -0
- package/dist/ports/dateizugriff.js +1 -0
- package/dist/ports/harness.js +4 -0
- package/dist/ports/heime.js +1 -0
- package/dist/ports/verlauf.js +4 -0
- package/dist/ports/zustand.js +16 -1
- package/package.json +26 -5
package/dist/cli/main.js
CHANGED
|
@@ -4,19 +4,24 @@ import path from 'node:path';
|
|
|
4
4
|
import { realpathSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
|
-
import { Anwendungsfaelle } from "../kern/anwendungsfaelle.js";
|
|
8
|
-
import { AbhaengigkeitFehlt, EinschraenkungOhneZeiger, HarnessNichtVorhanden, HarnessTypUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
7
|
+
import { Anwendungsfaelle, } from "../kern/anwendungsfaelle.js";
|
|
8
|
+
import { AbhaengigkeitFehlt, BundleDateiFehler, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimOhneCliZugriff, HeimUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, WslBrueckeFehlgeschlagen, ZeigerSchreibenGesperrt, heimAdresseAlsText, heimAdresseZerlegen, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
9
9
|
import { Agents } from "../adapter/agents.js";
|
|
10
|
-
import { ClaudeCode } from "../adapter/claude-code.js";
|
|
10
|
+
import { ClaudeCode, claudeAusfuehrer } from "../adapter/claude-code.js";
|
|
11
11
|
import { Hermes } from "../adapter/hermes.js";
|
|
12
12
|
import { ComposerZeiger } from "../adapter/composer.js";
|
|
13
|
-
import { DateisystemZiele } from "../adapter/dateisystem.js";
|
|
13
|
+
import { DateisystemZiele, LokalerZugriff } from "../adapter/dateisystem.js";
|
|
14
|
+
import { ZustandHeime } from "../adapter/heime.js";
|
|
15
|
+
import { WslBruecke, WslZugriff, wslAusfuehrer, wslDistros } from "../adapter/wsl.js";
|
|
14
16
|
import { GitQuellen } from "../adapter/git.js";
|
|
15
17
|
import { DateiMcpKatalog } from "../adapter/mcp-katalog.js";
|
|
18
|
+
import { cliUpdateHinweis } from "../adapter/npm-registry.js";
|
|
19
|
+
import { paketVersion } from "../adapter/paket-version.js";
|
|
16
20
|
import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
|
|
17
21
|
import { TerminalRueckfrage } from "../adapter/terminal.js";
|
|
18
22
|
import { ProzessUmgebung } from "../adapter/umgebung.js";
|
|
19
23
|
import { ZustandJson } from "../adapter/zustand-json.js";
|
|
24
|
+
import { VerlaufJson } from "../adapter/verlauf-json.js";
|
|
20
25
|
import { HILFE, hilfeText } from "./hilfe.js";
|
|
21
26
|
export function umgebungLesen(env = process.env) {
|
|
22
27
|
const home = env.HOME ?? homedir();
|
|
@@ -29,19 +34,45 @@ export function umgebungLesen(env = process.env) {
|
|
|
29
34
|
}
|
|
30
35
|
export function bauen(umgebung, optionen = {}) {
|
|
31
36
|
const zustand = new ZustandJson(path.join(umgebung.hmgrHome, 'state.json'));
|
|
37
|
+
const verlauf = new VerlaufJson(umgebung.hmgrHome);
|
|
32
38
|
const wurzel = path.join(umgebung.hmgrHome, 'checkouts');
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
const umgebungPort = optionen.umgebung ?? new ProzessUmgebung();
|
|
40
|
+
// Homedirs aus dem Zustand (E78): das primäre lässt sich umstellen (F89), dazu kommen
|
|
41
|
+
// zusätzliche in einer WSL-Distro (F87). `wsl:`-Heime fallen außerhalb von Windows heraus.
|
|
42
|
+
const heime = new ZustandHeime(zustand, umgebung.home, umgebungPort.plattform());
|
|
43
|
+
const wslAusfuehren = optionen.wslAusfuehrer ?? wslAusfuehrer;
|
|
44
|
+
// Je Distro eine Bridge, damit die einmal ermittelte Automount-Wurzel im Lauf erhalten bleibt.
|
|
45
|
+
const bruecken = new Map();
|
|
46
|
+
const bruecke = (distro) => {
|
|
47
|
+
let vorhanden = bruecken.get(distro);
|
|
48
|
+
if (vorhanden === undefined) {
|
|
49
|
+
vorhanden = new WslBruecke(distro, wslAusfuehren);
|
|
50
|
+
bruecken.set(distro, vorhanden);
|
|
51
|
+
}
|
|
52
|
+
return vorhanden;
|
|
53
|
+
};
|
|
54
|
+
const lokal = new LokalerZugriff();
|
|
55
|
+
const zugriffFuerOrt = (ort) => {
|
|
56
|
+
const adresse = ort.art === 'home' ? ort.heim : undefined;
|
|
57
|
+
const zerlegt = adresse === undefined ? undefined : heimAdresseZerlegen(adresse);
|
|
58
|
+
return zerlegt === undefined ? lokal : new WslZugriff(bruecke(zerlegt.distro));
|
|
59
|
+
};
|
|
60
|
+
const claude = new ClaudeCode(heime, optionen.claudeAusfuehrer ?? claudeAusfuehrer, zugriffFuerOrt);
|
|
36
61
|
const ports = {
|
|
37
|
-
harnesses: [claude, new Hermes(
|
|
62
|
+
harnesses: [claude, new Hermes(heime), new Agents(heime)],
|
|
38
63
|
quellen: new GitQuellen(wurzel),
|
|
39
|
-
ziele: new DateisystemZiele(wurzel, zustand, umgebung.xdgStateHome),
|
|
64
|
+
ziele: new DateisystemZiele(wurzel, zustand, umgebung.xdgStateHome, (ziel) => zugriffFuerOrt(ziel.ort)),
|
|
40
65
|
zeiger: new ComposerZeiger(),
|
|
41
66
|
zustand,
|
|
42
67
|
rueckfrage: optionen.rueckfrage ?? new TerminalRueckfrage(),
|
|
43
68
|
mcpKatalog: new DateiMcpKatalog(path.join(umgebung.hmgrHome, 'mcp.yaml')),
|
|
44
|
-
umgebung:
|
|
69
|
+
umgebung: umgebungPort,
|
|
70
|
+
heime,
|
|
71
|
+
wsl: {
|
|
72
|
+
distros: async () => await wslDistros(wslAusfuehren),
|
|
73
|
+
home: async (distro) => await bruecke(distro).home(),
|
|
74
|
+
},
|
|
75
|
+
verlauf,
|
|
45
76
|
};
|
|
46
77
|
return { anwendungsfaelle: new Anwendungsfaelle(ports), ports };
|
|
47
78
|
}
|
|
@@ -59,10 +90,12 @@ const SCHALTER = {
|
|
|
59
90
|
unlink: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal'] },
|
|
60
91
|
adopt: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal', 'probelauf'] },
|
|
61
92
|
plugin: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: ['local', 'lokal'] },
|
|
93
|
+
bundle: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: [] },
|
|
62
94
|
mcp: { werte: ['harness', 'ort'], flaggen: [] },
|
|
63
95
|
sync: { werte: [], flaggen: [] },
|
|
64
96
|
update: { werte: [], flaggen: [] },
|
|
65
97
|
status: { werte: [], flaggen: [] },
|
|
98
|
+
log: { werte: [], flaggen: [] },
|
|
66
99
|
harness: { werte: ['profil'], flaggen: [] },
|
|
67
100
|
};
|
|
68
101
|
/**
|
|
@@ -87,6 +120,13 @@ const UNTERKOMMANDOS = {
|
|
|
87
120
|
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'local', 'lokal'] },
|
|
88
121
|
list: { woerter: 1, schalter: [] },
|
|
89
122
|
},
|
|
123
|
+
bundle: {
|
|
124
|
+
add: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'quelle'] },
|
|
125
|
+
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer'] },
|
|
126
|
+
// sync nimmt einen Bundle-Namen, aber keinen zwingend: ohne Namen gilt der ganze Bestand.
|
|
127
|
+
sync: { woerter: 2, schalter: ['harness', 'ort', 'composer'] },
|
|
128
|
+
list: { woerter: 1, schalter: [] },
|
|
129
|
+
},
|
|
90
130
|
mcp: {
|
|
91
131
|
add: { woerter: 2, schalter: ['harness', 'ort'] },
|
|
92
132
|
remove: { woerter: 2, schalter: ['harness', 'ort'] },
|
|
@@ -156,6 +196,11 @@ function unterkommandoPruefen(kommando, schalter) {
|
|
|
156
196
|
`hmgr help ${kommando} zeigt die Schalter.`);
|
|
157
197
|
restPruefen(schalter.rest, menge.woerter, kommando, unterkommando);
|
|
158
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* `--ort` deuten. Ein zusätzliches Homedir (E78, F87) heißt nach seiner Adresse: `--ort wsl:Ubuntu`,
|
|
201
|
+
* auch in der ausgeschriebenen Form `--ort home:wsl:Ubuntu`. Beides ergibt denselben Ort, damit
|
|
202
|
+
* niemand raten muss, ob das `home:` davor gehört.
|
|
203
|
+
*/
|
|
159
204
|
function ortLesen(text) {
|
|
160
205
|
const roh = text ?? 'home';
|
|
161
206
|
if (roh === 'home')
|
|
@@ -164,7 +209,10 @@ function ortLesen(text) {
|
|
|
164
209
|
return { art: 'arbeitsverzeichnis', pfad: path.resolve(roh.slice(4)) };
|
|
165
210
|
if (roh.startsWith('profil:'))
|
|
166
211
|
return { art: 'profil', name: roh.slice(7) };
|
|
167
|
-
|
|
212
|
+
const heim = heimAdresseZerlegen(roh.startsWith('home:') ? roh.slice(5) : roh);
|
|
213
|
+
if (heim !== undefined)
|
|
214
|
+
return { art: 'home', heim: heimAdresseAlsText(heim) };
|
|
215
|
+
throw new Error(`Unbekannter Ort „${roh}", erwartet home, wsl:<distro>, dir:<pfad> oder profil:<name>`);
|
|
168
216
|
}
|
|
169
217
|
/** Klartext für den Grund, aus dem ein fremder Eintrag liegen bleibt (B12). */
|
|
170
218
|
const UEBERNAHME_GRUND = {
|
|
@@ -179,6 +227,11 @@ const UEBERNAHME_GRUND = {
|
|
|
179
227
|
export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
180
228
|
const { anwendungsfaelle, ports } = bauen(umgebung, optionen);
|
|
181
229
|
const kommando = argv[0];
|
|
230
|
+
// Version vor allem anderen (E72): nur die Nummer, damit ein Skript sie ohne Nacharbeit liest.
|
|
231
|
+
if (kommando === '--version') {
|
|
232
|
+
console.log(paketVersion());
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
182
235
|
// Hilfe zuerst: --help ist überall eine Flagge, damit kein Kommando sie als Wert liest.
|
|
183
236
|
if (kommando === undefined) {
|
|
184
237
|
console.log(HILFE);
|
|
@@ -203,6 +256,14 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
203
256
|
console.log(text);
|
|
204
257
|
return 0;
|
|
205
258
|
}
|
|
259
|
+
// Versionssprung vermerken (E80): bei jedem echten Kommando, Hilfe und Version sind oben schon
|
|
260
|
+
// zurückgekehrt. Ein Fehler hier (etwa ein kaputtes verlauf.json) darf kein Kommando kippen.
|
|
261
|
+
try {
|
|
262
|
+
await anwendungsfaelle.versionsSprungVermerken('cli', paketVersion(), 'cli');
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
// still, siehe oben.
|
|
266
|
+
}
|
|
206
267
|
if (kommando === 'source') {
|
|
207
268
|
const schalter = schalterLesen(argv.slice(1), 'source');
|
|
208
269
|
unterkommandoPruefen('source', schalter);
|
|
@@ -377,6 +438,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
377
438
|
if (kommando === 'unlink') {
|
|
378
439
|
const ergebnis = await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
379
440
|
console.log(`Link ${skill} entfernt.`);
|
|
441
|
+
// Zerbrochen (E69): die übrigen Links des Bundles bleiben als Einzel-Installationen liegen.
|
|
442
|
+
for (const bundle of ergebnis.zerbrochen)
|
|
443
|
+
console.log(`Bundle „${bundle}" ist damit zerbrochen, seine übrigen Links bleiben als einzelne Links liegen.`);
|
|
380
444
|
if (ergebnis.hinweis !== undefined)
|
|
381
445
|
console.log(ergebnis.hinweis);
|
|
382
446
|
return 0;
|
|
@@ -490,6 +554,89 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
490
554
|
console.log(` ${zeile}`);
|
|
491
555
|
return 0;
|
|
492
556
|
}
|
|
557
|
+
if (kommando === 'bundle') {
|
|
558
|
+
const schalter = schalterLesen(argv.slice(1), 'bundle');
|
|
559
|
+
unterkommandoPruefen('bundle', schalter);
|
|
560
|
+
const { rest, werte } = schalter;
|
|
561
|
+
const unterkommando = rest[0];
|
|
562
|
+
if (unterkommando === 'list') {
|
|
563
|
+
const katalog = await anwendungsfaelle.bundleKatalog();
|
|
564
|
+
const leer = katalog.every((k) => k.bundles.length === 0 && k.kaputte.length === 0 && k.fehler === undefined);
|
|
565
|
+
if (leer)
|
|
566
|
+
console.log('Keine Quelle mit Bundles. hmgr help bundle zeigt das Format von bundles/<name>.yaml.');
|
|
567
|
+
for (const eintrag of katalog) {
|
|
568
|
+
if (eintrag.fehler !== undefined) {
|
|
569
|
+
console.log(`Quelle ${quelleName(eintrag.quelle)} (${quelleAlsText(eintrag.quelle)}) nicht lesbar: ${eintrag.fehler}`);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (eintrag.bundles.length === 0 && eintrag.kaputte.length === 0)
|
|
573
|
+
continue;
|
|
574
|
+
console.log(`Quelle ${quelleName(eintrag.quelle)} (${quelleAlsText(eintrag.quelle)})`);
|
|
575
|
+
for (const bundle of eintrag.bundles) {
|
|
576
|
+
console.log(` ${bundle.name} ${kappen(bundle.beschreibung)}`);
|
|
577
|
+
for (const mitglied of bundle.mitglieder)
|
|
578
|
+
console.log(` ${mitglied.pfad} aus ${quelleAlsText(mitglied.quelle)}`);
|
|
579
|
+
}
|
|
580
|
+
// Eine kaputte Definition verdeckt die übrigen nicht, sie wird eigens genannt (E69).
|
|
581
|
+
for (const kaputt of eintrag.kaputte)
|
|
582
|
+
console.log(` Fehler ${kaputt.datei}: ${kaputt.fehler}`);
|
|
583
|
+
}
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
586
|
+
if (unterkommando !== 'add' && unterkommando !== 'remove' && unterkommando !== 'sync')
|
|
587
|
+
throw new Error(`${unterkommando === undefined ? 'bundle braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für bundle`}: bundle add <name>, bundle remove <name>, bundle sync [name] oder bundle list. hmgr help bundle zeigt Beispiele.`);
|
|
588
|
+
const name = rest[1];
|
|
589
|
+
if (name === undefined && unterkommando !== 'sync')
|
|
590
|
+
throw new Error(`bundle ${unterkommando} braucht einen Bundle-Namen. hmgr bundle list zeigt die Bundles der bekannten Quellen.`);
|
|
591
|
+
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
|
|
592
|
+
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
593
|
+
// Wie bei link: Verzeichnis merken und binden, damit status es wiederfindet (E21).
|
|
594
|
+
const composer = werte.get('composer');
|
|
595
|
+
if (composer === undefined)
|
|
596
|
+
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad);
|
|
597
|
+
else
|
|
598
|
+
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
599
|
+
}
|
|
600
|
+
if (unterkommando === 'add') {
|
|
601
|
+
const quelle = werte.get('quelle');
|
|
602
|
+
const ergebnis = await anwendungsfaelle.bundleInstallieren({
|
|
603
|
+
bundle: name,
|
|
604
|
+
ziel,
|
|
605
|
+
...(quelle === undefined ? {} : { quelle }),
|
|
606
|
+
});
|
|
607
|
+
console.log(`Bundle ${ergebnis.bundle.name} in ${ziel.harness} ${beschreibeOrt(ziel.ort)} installiert (${quelleAlsText(ergebnis.bundle.quelle)}).`);
|
|
608
|
+
for (const neu of ergebnis.neueQuellen)
|
|
609
|
+
console.log(` neue Quelle ${quelleAlsText(neu)} als „${quelleName(neu)}"`);
|
|
610
|
+
if (ergebnis.gelegt.length > 0)
|
|
611
|
+
console.log(` gelegt ${ergebnis.gelegt.join(', ')}`);
|
|
612
|
+
// Übernommen (F67): schon vorhandene Links derselben Quelle gehören jetzt zum Bundle.
|
|
613
|
+
if (ergebnis.uebernommen.length > 0)
|
|
614
|
+
console.log(` übernommen ${ergebnis.uebernommen.join(', ')}`);
|
|
615
|
+
if (ergebnis.hinweis !== undefined)
|
|
616
|
+
for (const zeile of ergebnis.hinweis.split('\n'))
|
|
617
|
+
console.log(` ${zeile}`);
|
|
618
|
+
return 0;
|
|
619
|
+
}
|
|
620
|
+
if (unterkommando === 'remove') {
|
|
621
|
+
const ergebnis = await anwendungsfaelle.bundleEntfernen({ bundle: name, ziel });
|
|
622
|
+
console.log(`Bundle ${ergebnis.bundle} aus ${ziel.harness} ${beschreibeOrt(ziel.ort)} entfernt.`);
|
|
623
|
+
console.log(ergebnis.entfernt.length === 0
|
|
624
|
+
? ' Kein Link mehr da, nur der Bundle-Verweis ist weg.'
|
|
625
|
+
: ` entfernt ${ergebnis.entfernt.join(', ')}`);
|
|
626
|
+
return 0;
|
|
627
|
+
}
|
|
628
|
+
// sync: ohne Ziel-Schalter der ganze Bestand, mit --ort/--harness nur dieses Ziel.
|
|
629
|
+
const nurZiel = werte.has('harness') || werte.has('ort');
|
|
630
|
+
const eintraege = await anwendungsfaelle.bundleAbgleich({
|
|
631
|
+
...(name === undefined ? {} : { bundle: name }),
|
|
632
|
+
...(nurZiel ? { ziel } : {}),
|
|
633
|
+
});
|
|
634
|
+
if (eintraege.length === 0)
|
|
635
|
+
console.log('Kein Bundle installiert, nichts abzugleichen.');
|
|
636
|
+
for (const zeile of bundleBericht(eintraege))
|
|
637
|
+
console.log(zeile);
|
|
638
|
+
return 0;
|
|
639
|
+
}
|
|
493
640
|
if (kommando === 'mcp') {
|
|
494
641
|
const schalter = schalterLesen(argv.slice(1), 'mcp');
|
|
495
642
|
unterkommandoPruefen('mcp', schalter);
|
|
@@ -568,13 +715,18 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
568
715
|
}
|
|
569
716
|
if (kommando === 'update') {
|
|
570
717
|
restPruefen(schalterLesen(argv.slice(1), 'update').rest, 0, 'update');
|
|
571
|
-
const { checkouts: liste,
|
|
718
|
+
const { checkouts: liste, bundles, aufraeumen } = await anwendungsfaelle.aktualisieren({ ausloeser: 'cli' });
|
|
572
719
|
if (liste.length === 0)
|
|
573
720
|
console.log('Keine Checkouts.');
|
|
574
|
-
for (const { checkout, aktualisierung } of liste) {
|
|
721
|
+
for (const { checkout, aktualisierung, geaenderteSkills } of liste) {
|
|
575
722
|
const kopf = `${quelleAlsText(checkout.quelle)} @ ${checkout.ref}`;
|
|
576
|
-
if (aktualisierung.ergebnis === 'aktualisiert')
|
|
723
|
+
if (aktualisierung.ergebnis === 'aktualisiert') {
|
|
577
724
|
console.log(` aktualisiert ${kopf} ${aktualisierung.von.slice(0, 8)} -> ${aktualisierung.nach.slice(0, 8)}`);
|
|
725
|
+
// Auf Skill-Ebene berichten (E70): sonst steht da nur, dass die Quelle neue Commits hat.
|
|
726
|
+
// Ohne Treffer bleibt die Zeile weg — der Commit hat dann nichts in einem Skill geändert.
|
|
727
|
+
if (geaenderteSkills.length > 0)
|
|
728
|
+
console.log(` ${zahlwort(geaenderteSkills.length, 'Skill', 'Skills')} geändert: ${geaenderteSkills.join(', ')}`);
|
|
729
|
+
}
|
|
578
730
|
else if (aktualisierung.ergebnis === 'unveraendert')
|
|
579
731
|
console.log(` unverändert ${kopf}`);
|
|
580
732
|
else if (aktualisierung.ergebnis === 'uebersprungen')
|
|
@@ -582,8 +734,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
582
734
|
else
|
|
583
735
|
console.log(` fehlgeschlagen ${kopf} ${aktualisierung.fehler}`);
|
|
584
736
|
}
|
|
585
|
-
|
|
586
|
-
|
|
737
|
+
// Bundles im selben Lauf (E69, F64=O1): eine geänderte Definition wird hier nachgezogen.
|
|
738
|
+
for (const zeile of bundleBericht(bundles))
|
|
739
|
+
console.log(zeile);
|
|
587
740
|
// Zum Schluss die verwaisten Checkouts: was gelöscht wurde und was liegen bleibt.
|
|
588
741
|
for (const verwaist of aufraeumen.verwaiste) {
|
|
589
742
|
const kopf = `${quelleAlsText(verwaist.checkout.quelle)} @ ${verwaist.checkout.ref}`;
|
|
@@ -598,11 +751,26 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
598
751
|
const geloescht = aufraeumen.verwaiste.filter((v) => v.geloescht).length;
|
|
599
752
|
console.log(`${aufraeumen.verwaiste.length} verwaist, ${geloescht} gelöscht, ${aufraeumen.verwaiste.length - geloescht} liegen geblieben.`);
|
|
600
753
|
}
|
|
754
|
+
// Zuletzt hmgr selbst (E77): wer schon aktualisiert, will auch wissen, dass sein hmgr alt ist.
|
|
755
|
+
// Verglichen wird die eigene Version (E72) gegen npm, gemeldet wird nur — installiert wird
|
|
756
|
+
// nichts. Offline oder Registry unerreichbar heißt keine Zeile, still: der Lauf selbst ist
|
|
757
|
+
// fertig und gelungen, ein fehlgeschlagener Nebenblick darf ihn nicht kommentieren.
|
|
758
|
+
const selbst = await cliUpdateHinweis(paketVersion());
|
|
759
|
+
if (selbst !== null) {
|
|
760
|
+
console.log(selbst.text);
|
|
761
|
+
await anwendungsfaelle.hmgrEreignisVermerken({ art: 'cli-verfuegbar', version: selbst.neu }, 'cli');
|
|
762
|
+
}
|
|
601
763
|
return 0;
|
|
602
764
|
}
|
|
603
765
|
if (kommando === 'status') {
|
|
604
766
|
restPruefen(schalterLesen(argv.slice(1), 'status').rest, 0, 'status');
|
|
605
|
-
|
|
767
|
+
// Ziele inaktiver Harnesses bleiben aus der Liste (E74), auch wenn dort noch Links oder fremde
|
|
768
|
+
// Einträge liegen: was hmgr nicht managed, zeigt es nicht. Gefiltert wird hier, nicht in
|
|
769
|
+
// istZustand — der Kern bleibt ungefiltert (E19), sonst hielte kein Link in einem inaktiven
|
|
770
|
+
// Harness seinen Checkout mehr vor dem Aufräumen.
|
|
771
|
+
const aktiveStatus = new Set(await anwendungsfaelle.aktiveHarnesses());
|
|
772
|
+
const sichtbareZiele = (await anwendungsfaelle.istZustand()).filter((z) => aktiveStatus.has(z.ziel.harness));
|
|
773
|
+
for (const zielZustand of sichtbareZiele) {
|
|
606
774
|
console.log(`Ziel ${zielZustand.ziel.harness} ${beschreibeOrt(zielZustand.ziel.ort)} (${zielZustand.verzeichnis})`);
|
|
607
775
|
if (zielZustand.eintraege.length === 0)
|
|
608
776
|
console.log(' (leer)');
|
|
@@ -618,6 +786,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
618
786
|
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))}`);
|
|
619
787
|
}
|
|
620
788
|
}
|
|
789
|
+
// Bundles des Ziels (E69), unter den Einträgen: welche Links als Einheit zusammengehören.
|
|
790
|
+
for (const bundle of zielZustand.bundles)
|
|
791
|
+
console.log(` Bundle ${bundle.name} (${quelleAlsText(bundle.quelle)}) ${bundle.skills.join(', ')}`);
|
|
621
792
|
if (zielZustand.plugins !== undefined)
|
|
622
793
|
console.log(` Plugins: ${zielZustand.plugins.length === 0 ? 'keine' : zielZustand.plugins.join(', ')}`);
|
|
623
794
|
}
|
|
@@ -626,6 +797,18 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
626
797
|
console.log(`Nicht vorhanden: ${fehlend.join(', ')}`);
|
|
627
798
|
return 0;
|
|
628
799
|
}
|
|
800
|
+
if (kommando === 'log') {
|
|
801
|
+
restPruefen(schalterLesen(argv.slice(1), 'log').rest, 0, 'log');
|
|
802
|
+
const eintraege = await anwendungsfaelle.verlauf();
|
|
803
|
+
if (eintraege.length === 0)
|
|
804
|
+
console.log('Noch keine Ereignisse.');
|
|
805
|
+
for (const eintrag of eintraege) {
|
|
806
|
+
console.log(verlaufZeitpunkt(eintrag.zeit));
|
|
807
|
+
for (const ereignis of eintrag.ereignisse)
|
|
808
|
+
console.log(` ${verlaufEreignisZeile(ereignis)}`);
|
|
809
|
+
}
|
|
810
|
+
return 0;
|
|
811
|
+
}
|
|
629
812
|
if (kommando === 'harness') {
|
|
630
813
|
const schalter = schalterLesen(argv.slice(1), 'harness');
|
|
631
814
|
unterkommandoPruefen('harness', schalter);
|
|
@@ -679,13 +862,83 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
679
862
|
'harness skills-dir <name> add <pfad> oder harness skills-dir <name> remove. ' +
|
|
680
863
|
'hmgr help harness zeigt Beispiele.');
|
|
681
864
|
}
|
|
865
|
+
// Homedirs eines Harness (E78): zeigen, das primäre umstellen (F89), zusätzliche in einer
|
|
866
|
+
// WSL-Distro hinzufügen und entfernen (F87).
|
|
867
|
+
if (unterkommando === 'home') {
|
|
868
|
+
const harnessName = rest[1];
|
|
869
|
+
if (harnessName === undefined)
|
|
870
|
+
throw new Error('harness home braucht einen Harness-Namen, etwa hmgr harness home claude-code. ' +
|
|
871
|
+
'hmgr harness list zeigt die bekannten Namen.');
|
|
872
|
+
const unter = rest[2];
|
|
873
|
+
if (unter === undefined) {
|
|
874
|
+
restPruefen(rest, 2, 'harness', 'home');
|
|
875
|
+
const ansicht = (await anwendungsfaelle.harnessHeime()).find((h) => h.harness === harnessName);
|
|
876
|
+
if (ansicht === undefined)
|
|
877
|
+
throw new HarnessTypUnbekannt(harnessName);
|
|
878
|
+
for (const heim of ansicht.heime) {
|
|
879
|
+
const wie = heim.adresse ?? (heim.eingestellt ? 'home (eingestellt)' : 'home');
|
|
880
|
+
console.log(`${harnessName} ${wie} ${heim.pfad}`);
|
|
881
|
+
}
|
|
882
|
+
if (ansicht.kannZusaetzliche)
|
|
883
|
+
console.log(` Weiteres Homedir: hmgr harness home ${harnessName} add wsl:<distro>.`);
|
|
884
|
+
return 0;
|
|
885
|
+
}
|
|
886
|
+
if (unter === 'set') {
|
|
887
|
+
restPruefen(rest, 4, 'harness', 'home');
|
|
888
|
+
const pfad = rest[3];
|
|
889
|
+
if (pfad === undefined)
|
|
890
|
+
throw new Error('harness home <name> set braucht einen Pfad, etwa hmgr harness home claude-code set D:\\Profile\\tim.');
|
|
891
|
+
const gesetzt = await anwendungsfaelle.harnessHeimSetzen(harnessName, pfad);
|
|
892
|
+
console.log(`${harnessName} liest sein Homedir jetzt unter ${gesetzt}.`);
|
|
893
|
+
console.log(' Was am alten Ort liegt, bleibt dort liegen. hmgr zeigt es nicht mehr.');
|
|
894
|
+
return 0;
|
|
895
|
+
}
|
|
896
|
+
if (unter === 'reset') {
|
|
897
|
+
restPruefen(rest, 3, 'harness', 'home');
|
|
898
|
+
await anwendungsfaelle.harnessHeimZuruecksetzen(harnessName);
|
|
899
|
+
const ansicht = (await anwendungsfaelle.harnessHeime()).find((h) => h.harness === harnessName);
|
|
900
|
+
console.log(`${harnessName} liest sein Homedir wieder unter ${ansicht?.heime[0]?.pfad ?? '?'}.`);
|
|
901
|
+
return 0;
|
|
902
|
+
}
|
|
903
|
+
if (unter === 'add') {
|
|
904
|
+
restPruefen(rest, 4, 'harness', 'home');
|
|
905
|
+
const adresse = rest[3];
|
|
906
|
+
if (adresse === undefined) {
|
|
907
|
+
const distros = await anwendungsfaelle.wslDistros();
|
|
908
|
+
const beispiel = distros[0] ?? 'Ubuntu';
|
|
909
|
+
throw new Error(`harness home <name> add braucht eine Adresse, etwa hmgr harness home ${harnessName} add wsl:${beispiel}.` +
|
|
910
|
+
(distros.length > 0 ? ` Installierte Distros: ${distros.join(', ')}.` : ''));
|
|
911
|
+
}
|
|
912
|
+
const heim = await anwendungsfaelle.harnessHeimHinzufuegen(harnessName, adresse);
|
|
913
|
+
console.log(`${harnessName} ${heim.adresse}: Homedir ${heim.pfad}.`);
|
|
914
|
+
console.log(` Verlinken dorthin mit --harness ${harnessName} --ort ${heim.adresse}.`);
|
|
915
|
+
return 0;
|
|
916
|
+
}
|
|
917
|
+
if (unter === 'remove') {
|
|
918
|
+
restPruefen(rest, 4, 'harness', 'home');
|
|
919
|
+
const adresse = rest[3];
|
|
920
|
+
if (adresse === undefined)
|
|
921
|
+
throw new Error(`harness home <name> remove braucht eine Adresse, etwa hmgr harness home ${harnessName} remove wsl:Ubuntu.`);
|
|
922
|
+
await anwendungsfaelle.harnessHeimEntfernen(harnessName, adresse);
|
|
923
|
+
console.log(`${harnessName} ${adresse}: Homedir vergessen. Die Links dort bleiben liegen.`);
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
throw new Error(`Unbekanntes Unterkommando „${unter}" für harness home: harness home <name>, ` +
|
|
927
|
+
'harness home <name> set <pfad>, harness home <name> reset, harness home <name> add wsl:<distro> ' +
|
|
928
|
+
'oder harness home <name> remove wsl:<distro>. hmgr help harness zeigt Beispiele.');
|
|
929
|
+
}
|
|
682
930
|
if (unterkommando === 'list') {
|
|
683
931
|
const aktive = new Set(await anwendungsfaelle.aktiveHarnesses());
|
|
684
932
|
const fehlend = new Set(await anwendungsfaelle.nichtVorhandeneHarnesses());
|
|
933
|
+
const heime = await anwendungsfaelle.harnessHeime();
|
|
685
934
|
for (const name of anwendungsfaelle.harnessTypen()) {
|
|
686
935
|
const aktivText = aktive.has(name) ? 'aktiv' : 'inaktiv';
|
|
687
936
|
const vorhandenText = fehlend.has(name) ? 'nicht vorhanden' : 'vorhanden';
|
|
688
|
-
|
|
937
|
+
// Zusätzliche Homedirs (E78) gehören in die Übersicht: sonst sähe niemand, dass ein
|
|
938
|
+
// zweites Ziel je Harness existiert, ohne extra danach zu fragen.
|
|
939
|
+
const zusatz = (heime.find((h) => h.harness === name)?.heime ?? []).filter((h) => h.adresse !== undefined);
|
|
940
|
+
const heimText = zusatz.length === 0 ? '' : `, ${zusatz.map((h) => h.adresse).join(', ')}`;
|
|
941
|
+
console.log(`${name} ${aktivText}, ${vorhandenText}${heimText}`);
|
|
689
942
|
}
|
|
690
943
|
return 0;
|
|
691
944
|
}
|
|
@@ -734,10 +987,69 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
734
987
|
}
|
|
735
988
|
return 0;
|
|
736
989
|
}
|
|
737
|
-
throw new Error(`${unterkommando === undefined ? 'harness braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für harness`}: harness list, harness enable <name> oder harness
|
|
990
|
+
throw new Error(`${unterkommando === undefined ? 'harness braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für harness`}: harness list, harness enable <name>, harness disable <name> oder harness home <name>. hmgr help harness zeigt Beispiele.`);
|
|
738
991
|
}
|
|
739
992
|
throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
|
|
740
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* Bericht über den Bundle-Abgleich (E69), gleich für `hmgr bundle sync` und `hmgr update`: je
|
|
996
|
+
* Bundle eine Zeile mit seinem Ausgang, darunter, was sich geändert hat. „fehlgeschlagen" heißt
|
|
997
|
+
* genau das — der Zusatz sagt, dass das Bundle im Ziel auf dem alten Stand bleibt.
|
|
998
|
+
*/
|
|
999
|
+
function bundleBericht(eintraege) {
|
|
1000
|
+
const zeilen = [];
|
|
1001
|
+
for (const eintrag of eintraege) {
|
|
1002
|
+
const kopf = `Bundle ${eintrag.bundle} ${eintrag.ziel.harness} ${beschreibeOrt(eintrag.ziel.ort)}`;
|
|
1003
|
+
if (eintrag.ergebnis === 'fehlgeschlagen') {
|
|
1004
|
+
zeilen.push(` fehlgeschlagen ${kopf} bleibt auf dem alten Stand`);
|
|
1005
|
+
for (const zeile of (eintrag.fehler ?? '').split('\n'))
|
|
1006
|
+
zeilen.push(` ${zeile}`);
|
|
1007
|
+
continue;
|
|
1008
|
+
}
|
|
1009
|
+
zeilen.push(` ${eintrag.ergebnis === 'aktualisiert' ? 'aktualisiert ' : 'unverändert '} ${kopf}`);
|
|
1010
|
+
for (const neu of eintrag.neueQuellen)
|
|
1011
|
+
zeilen.push(` neue Quelle ${quelleAlsText(neu)}`);
|
|
1012
|
+
if (eintrag.gelegt.length > 0)
|
|
1013
|
+
zeilen.push(` gelegt ${eintrag.gelegt.join(', ')}`);
|
|
1014
|
+
if (eintrag.entfernt.length > 0)
|
|
1015
|
+
zeilen.push(` entfernt ${eintrag.entfernt.join(', ')}`);
|
|
1016
|
+
}
|
|
1017
|
+
return zeilen;
|
|
1018
|
+
}
|
|
1019
|
+
/** Kopfzeile eines Verlaufseintrags (E80): lokale Zeit, damit sie zum Rechner des Lesers passt. */
|
|
1020
|
+
function verlaufZeitpunkt(iso) {
|
|
1021
|
+
const datum = new Date(iso);
|
|
1022
|
+
const zwei = (n) => String(n).padStart(2, '0');
|
|
1023
|
+
return (`${zwei(datum.getDate())}.${zwei(datum.getMonth() + 1)}.${datum.getFullYear()} ` +
|
|
1024
|
+
`${zwei(datum.getHours())}:${zwei(datum.getMinutes())}`);
|
|
1025
|
+
}
|
|
1026
|
+
/** Eine Ereigniszeile im Verlauf (E80), eingerückt unter der Kopfzeile ihres Laufs. */
|
|
1027
|
+
function verlaufEreignisZeile(ereignis) {
|
|
1028
|
+
if (ereignis.art === 'checkout') {
|
|
1029
|
+
const stand = `${ereignis.von.slice(0, 7)} → ${ereignis.nach.slice(0, 7)}`;
|
|
1030
|
+
const skills = ereignis.skills.length > 0 ? `, Skills: ${ereignis.skills.join(', ')}` : '';
|
|
1031
|
+
return `${ereignis.quelle} @ ${ereignis.ref}: ${stand}${skills}`;
|
|
1032
|
+
}
|
|
1033
|
+
if (ereignis.art === 'bundle') {
|
|
1034
|
+
const teile = [
|
|
1035
|
+
ereignis.gelegt.length > 0 ? `gelegt ${ereignis.gelegt.join(', ')}` : undefined,
|
|
1036
|
+
ereignis.entfernt.length > 0 ? `entfernt ${ereignis.entfernt.join(', ')}` : undefined,
|
|
1037
|
+
].filter((t) => t !== undefined);
|
|
1038
|
+
// Formatierung wie in bundleBericht (Harness + beschreibeOrt); ein String-Ziel (vor E80-Nachtrag
|
|
1039
|
+
// geschrieben) bleibt unverändert stehen.
|
|
1040
|
+
const ziel = typeof ereignis.ziel === 'string' ? ereignis.ziel : `${ereignis.ziel.harness} ${beschreibeOrt(ereignis.ziel.ort)}`;
|
|
1041
|
+
return `Bundle ${ereignis.bundle} → ${ziel}: ${teile.join('; ')}`;
|
|
1042
|
+
}
|
|
1043
|
+
if (ereignis.art === 'aufgeraeumt')
|
|
1044
|
+
return `Checkout ${ereignis.checkout} aufgeräumt`;
|
|
1045
|
+
if (ereignis.art === 'fehler')
|
|
1046
|
+
return `Fehler ${ereignis.betrifft}: ${ereignis.fehler}`;
|
|
1047
|
+
if (ereignis.art === 'hmgr-installiert')
|
|
1048
|
+
return ereignis.paket === 'gui' ? `hmgr-GUI ${ereignis.version} installiert` : `hmgr ${ereignis.version} installiert`;
|
|
1049
|
+
if (ereignis.art === 'gui-update-geladen')
|
|
1050
|
+
return `GUI-Update ${ereignis.version} geladen`;
|
|
1051
|
+
return `CLI ${ereignis.version} verfügbar`;
|
|
1052
|
+
}
|
|
741
1053
|
/** Grund eines Links als Suffix (E37). „manuell" bleibt stumm, damit die Ausgabe ruhig bleibt. */
|
|
742
1054
|
function beschreibeGrund(grund) {
|
|
743
1055
|
if (grund === undefined || grund.art === 'manuell')
|
|
@@ -752,9 +1064,10 @@ function zahlwort(n, ein, viele) {
|
|
|
752
1064
|
function kappen(text, laenge = 80) {
|
|
753
1065
|
return text.length <= laenge ? text : `${text.slice(0, laenge - 1)}…`;
|
|
754
1066
|
}
|
|
1067
|
+
/** Ort als Text, so geschrieben, wie `--ort` ihn wieder annimmt. */
|
|
755
1068
|
function beschreibeOrt(ort) {
|
|
756
1069
|
if (ort.art === 'home')
|
|
757
|
-
return 'home';
|
|
1070
|
+
return ort.heim === undefined ? 'home' : ort.heim;
|
|
758
1071
|
if (ort.art === 'profil')
|
|
759
1072
|
return `profil:${ort.name}`;
|
|
760
1073
|
return `dir:${ort.pfad}`;
|
|
@@ -768,12 +1081,19 @@ function istModellFehler(fehler) {
|
|
|
768
1081
|
fehler instanceof ZeigerMehrdeutig ||
|
|
769
1082
|
fehler instanceof ZeigerSchreibenGesperrt ||
|
|
770
1083
|
fehler instanceof HarnessNichtVorhanden ||
|
|
1084
|
+
fehler instanceof HarnessInaktiv ||
|
|
771
1085
|
fehler instanceof HarnessTypUnbekannt ||
|
|
772
1086
|
fehler instanceof KeinPassenderRef ||
|
|
773
1087
|
fehler instanceof EinschraenkungOhneZeiger ||
|
|
774
1088
|
fehler instanceof PluginMehrdeutig ||
|
|
775
1089
|
fehler instanceof PluginUnbekannt ||
|
|
776
1090
|
fehler instanceof AbhaengigkeitFehlt ||
|
|
1091
|
+
fehler instanceof BundleUnbekannt ||
|
|
1092
|
+
fehler instanceof BundleMehrdeutig ||
|
|
1093
|
+
fehler instanceof BundleMitgliederFehlen ||
|
|
1094
|
+
fehler instanceof BundleNamenskonflikte ||
|
|
1095
|
+
fehler instanceof BundleNichtInstalliert ||
|
|
1096
|
+
fehler instanceof BundleDateiFehler ||
|
|
777
1097
|
fehler instanceof QuelleUnbekannt ||
|
|
778
1098
|
fehler instanceof QuelleNameBelegt ||
|
|
779
1099
|
fehler instanceof QuelleAufraeumenLokaleAenderungen ||
|
|
@@ -789,7 +1109,14 @@ function istModellFehler(fehler) {
|
|
|
789
1109
|
fehler instanceof McpNurGeteilterFundort ||
|
|
790
1110
|
fehler instanceof McpUebernahmeNichtMoeglich ||
|
|
791
1111
|
fehler instanceof McpNichtInGeteilterDatei ||
|
|
792
|
-
fehler instanceof McpUebernahmeUnvollstaendig
|
|
1112
|
+
fehler instanceof McpUebernahmeUnvollstaendig ||
|
|
1113
|
+
fehler instanceof HeimAdresseUngueltig ||
|
|
1114
|
+
fehler instanceof HeimBereitsVorhanden ||
|
|
1115
|
+
fehler instanceof HeimFaehigkeitFehlt ||
|
|
1116
|
+
fehler instanceof HeimNurUnterWindows ||
|
|
1117
|
+
fehler instanceof HeimOhneCliZugriff ||
|
|
1118
|
+
fehler instanceof HeimUnbekannt ||
|
|
1119
|
+
fehler instanceof WslBrueckeFehlgeschlagen);
|
|
793
1120
|
}
|
|
794
1121
|
/**
|
|
795
1122
|
* Der nächste Schritt zu einem Fehler aus dem Kern. Die Fehlertexte selbst bleiben, wie das Modell
|
|
@@ -808,14 +1135,42 @@ function naechsterSchritt(fehler) {
|
|
|
808
1135
|
return 'hmgr plugin list zeigt die Plugins der bekannten Quellen. Fehlt der Marketplace, erst hmgr source add <url>.';
|
|
809
1136
|
if (fehler instanceof AbhaengigkeitFehlt)
|
|
810
1137
|
return 'Die Abhängigkeit steht in keinem Plugin desselben Marketplace, das kann nur die Quelle beheben.';
|
|
1138
|
+
if (fehler instanceof BundleUnbekannt)
|
|
1139
|
+
return 'hmgr bundle list zeigt die Bundles der bekannten Quellen. Fehlt die Quelle, erst hmgr source add <url|pfad>.';
|
|
1140
|
+
if (fehler instanceof BundleMehrdeutig)
|
|
1141
|
+
return 'Quelle eingrenzen: --quelle <teilstring der URL oder des Pfads>. hmgr bundle list zeigt, wo welches Bundle steht.';
|
|
1142
|
+
if (fehler instanceof BundleMitgliederFehlen)
|
|
1143
|
+
return 'Das Bundle ist nur ganz oder gar nicht zu haben. Wer nur einen Teil braucht, verlinkt die erreichbaren Skills einzeln mit hmgr link.';
|
|
1144
|
+
if (fehler instanceof BundleNamenskonflikte)
|
|
1145
|
+
return 'Die genannten Namen erst freimachen (hmgr unlink <skill> bzw. hmgr adopt <name>), dann bundle add erneut.';
|
|
1146
|
+
if (fehler instanceof BundleNichtInstalliert)
|
|
1147
|
+
return 'hmgr status zeigt je Ziel, welche Bundles dort installiert sind.';
|
|
1148
|
+
if (fehler instanceof BundleDateiFehler)
|
|
1149
|
+
return 'Die Definition liegt in der Quelle und wird dort korrigiert, hmgr schreibt sie nie. hmgr help bundle zeigt das Format.';
|
|
811
1150
|
if (fehler instanceof KeinPassenderRef)
|
|
812
1151
|
return 'Die Tags der Quelle sind frisch geholt, keiner passt. Sonst einen vorhandenen Tag- oder Branchnamen als --version nennen.';
|
|
813
1152
|
if (fehler instanceof HarnessNichtVorhanden)
|
|
814
1153
|
return 'Der Harness legt sein Verzeichnis beim ersten Start an. hmgr status zeigt, welche Harnesses hier liegen.';
|
|
1154
|
+
if (fehler instanceof HarnessInaktiv)
|
|
1155
|
+
return `Erst aktivieren: hmgr harness enable ${fehler.harness}. Bestehende Links dort bleiben unberührt.`;
|
|
815
1156
|
if (fehler instanceof OrtNichtUnterstuetzt)
|
|
816
1157
|
return 'profil:<name> kennt nur hermes, claude-code und agents kennen home und dir:<pfad>.';
|
|
817
1158
|
if (fehler instanceof HarnessTypUnbekannt)
|
|
818
1159
|
return 'Bekannt sind claude-code, hermes und agents. hmgr harness list zeigt sie mit ihrem Stand.';
|
|
1160
|
+
if (fehler instanceof HeimAdresseUngueltig)
|
|
1161
|
+
return 'Ein zusätzliches Homedir wird als wsl:<distro> angegeben. Ein abweichender normaler Pfad gehört an hmgr harness home <name> set <pfad>.';
|
|
1162
|
+
if (fehler instanceof HeimFaehigkeitFehlt)
|
|
1163
|
+
return 'Zusätzliche Homedirs kennt bisher nur claude-code. Für einen abweichenden Pfad: hmgr harness home <name> set <pfad>.';
|
|
1164
|
+
if (fehler instanceof HeimNurUnterWindows)
|
|
1165
|
+
return 'Auf diesem Rechner gibt es kein WSL. Das Homedir wird auf dem Windows-Rechner eingetragen, auf dem die Distro läuft.';
|
|
1166
|
+
if (fehler instanceof HeimBereitsVorhanden)
|
|
1167
|
+
return `hmgr harness home ${fehler.harness} zeigt die eingetragenen Homedirs.`;
|
|
1168
|
+
if (fehler instanceof HeimUnbekannt)
|
|
1169
|
+
return `hmgr harness home ${fehler.harness} zeigt die eingetragenen Homedirs, add trägt eines ein.`;
|
|
1170
|
+
if (fehler instanceof HeimOhneCliZugriff)
|
|
1171
|
+
return 'Skill-Links dort legt hmgr selbst: hmgr link <skill> --harness claude-code --ort <adresse>.';
|
|
1172
|
+
if (fehler instanceof WslBrueckeFehlgeschlagen)
|
|
1173
|
+
return 'wsl -l -q zeigt die installierten Distros. Startet die Distro nicht, hilft wsl --shutdown und ein erneuter Versuch.';
|
|
819
1174
|
if (fehler instanceof QuelleUnbekannt)
|
|
820
1175
|
return 'hmgr source list zeigt die bekannten Quellen.';
|
|
821
1176
|
if (fehler instanceof QuelleNameBelegt)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Die installierte hmgr-CLI aus Sicht der GUI (E77). Die GUI trägt ihre eigene Version in sich
|
|
2
|
+
// (`paketVersion()`, E72) — die CLI daneben ist ein zweites, unabhängig installiertes Paket, ihre
|
|
3
|
+
// Version steht nur über ihren eigenen Aufruf fest.
|
|
4
|
+
import spawn from 'cross-spawn';
|
|
5
|
+
import { versionLesen } from "../kern/version.js";
|
|
6
|
+
/**
|
|
7
|
+
* `hmgr --version` (E72: nur die Nummer, Exit 0). Null heißt „nichts zu melden": keine CLI im PATH,
|
|
8
|
+
* Fehlercode, Timeout, oder eine Ausgabe, die keine Version ist. Kein Fehler dringt nach außen —
|
|
9
|
+
* eine fehlende CLI ist der Normalfall bei einer reinen GUI-Installation, keine Störung.
|
|
10
|
+
*
|
|
11
|
+
* `spawn` von cross-spawn wie im claude-Adapter (E65/B7): unter Windows ist die npm-installierte
|
|
12
|
+
* `hmgr` ein `.cmd`-Shim, das Node seit dem Fix zu CVE-2024-27980 ohne Umweg über cmd.exe nicht
|
|
13
|
+
* mehr startet. stdin zu und ein Timeout, damit nichts hängen bleibt.
|
|
14
|
+
*/
|
|
15
|
+
export function hmgrCliVersion() {
|
|
16
|
+
return new Promise((aufloesen) => {
|
|
17
|
+
let fertig = false;
|
|
18
|
+
const melden = (wert) => {
|
|
19
|
+
if (fertig)
|
|
20
|
+
return;
|
|
21
|
+
fertig = true;
|
|
22
|
+
aufloesen(wert);
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
const kind = spawn('hmgr', ['--version'], {
|
|
26
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
27
|
+
timeout: 10_000,
|
|
28
|
+
killSignal: 'SIGKILL',
|
|
29
|
+
});
|
|
30
|
+
let stdout = '';
|
|
31
|
+
kind.stdout.on('data', (stueck) => (stdout += stueck));
|
|
32
|
+
kind.on('error', () => melden(null));
|
|
33
|
+
kind.on('close', (code) => {
|
|
34
|
+
if (code !== 0)
|
|
35
|
+
return melden(null);
|
|
36
|
+
const text = stdout.trim().split('\n')[0]?.trim() ?? '';
|
|
37
|
+
melden(versionLesen(text) === null ? null : text);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
melden(null);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|