hmgr 0.0.45 → 0.0.47
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/git.js +215 -10
- package/dist/adapter/sops.js +107 -0
- package/dist/adapter/zustand-json.js +2 -1
- package/dist/aenderungen.json +6 -0
- package/dist/cli/hilfe.js +44 -10
- package/dist/cli/main.js +67 -13
- package/dist/cli/verlauf-anzeige.js +17 -0
- package/dist/gui/main.js +80 -15
- package/dist/gui/preload.cjs +2 -0
- package/dist/gui/renderer/renderer.js +325 -88
- package/dist/gui/renderer/styles.css +8 -0
- package/dist/kern/anwendungsfaelle.js +250 -62
- package/package.json +1 -1
package/dist/cli/main.js
CHANGED
|
@@ -4,7 +4,7 @@ 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 { checkoutGruppen, quellenAnzeigeName, sammelZeile, verlaufZeitpunkt } from "./verlauf-anzeige.js";
|
|
7
|
+
import { checkoutGruppen, quellenAnzeigeName, sammelZeile, sopsGrundText, sopsZeile, verlaufZeitpunkt, } from "./verlauf-anzeige.js";
|
|
8
8
|
import { Anwendungsfaelle, pluginWeltText, } from "../kern/anwendungsfaelle.js";
|
|
9
9
|
import { AbhaengigkeitFehlt, BundleDateiFehler, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, ClaudeCliNichtErreichbar, HeimUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, WslBrueckeFehlgeschlagen, ZeigerSchreibenGesperrt, heimAdresseAlsText, heimAdresseZerlegen, wslDistroVonOrt, wslUncZerlegen, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
10
10
|
import { Agents } from "../adapter/agents.js";
|
|
@@ -19,6 +19,7 @@ import { DateiMcpKatalog } from "../adapter/mcp-katalog.js";
|
|
|
19
19
|
import { cliUpdateHinweis } from "../adapter/npm-registry.js";
|
|
20
20
|
import { paketVersion } from "../adapter/paket-version.js";
|
|
21
21
|
import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
|
|
22
|
+
import { standardSops } from "../adapter/sops.js";
|
|
22
23
|
import { TerminalRueckfrage } from "../adapter/terminal.js";
|
|
23
24
|
import { ProzessUmgebung } from "../adapter/umgebung.js";
|
|
24
25
|
import { signalSchreiben } from "../adapter/gui-signal.js";
|
|
@@ -92,7 +93,7 @@ export function bauen(umgebung, optionen = {}) {
|
|
|
92
93
|
});
|
|
93
94
|
const ports = {
|
|
94
95
|
harnesses: [claude, new Hermes(heime, zugriffFuerOrt), new Agents(heime, zugriffFuerOrt)],
|
|
95
|
-
quellen: new GitQuellen(wurzel),
|
|
96
|
+
quellen: new GitQuellen(wurzel, { sops: optionen.sops ?? standardSops(umgebung.home) }),
|
|
96
97
|
ziele: new DateisystemZiele(wurzel, zustand, umgebung.xdgStateHome, (ziel) => zugriffFuerOrt(ziel.ort)),
|
|
97
98
|
zeiger: new ComposerZeiger(),
|
|
98
99
|
zustand,
|
|
@@ -125,7 +126,7 @@ const SCHALTER = {
|
|
|
125
126
|
unlink: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal'] },
|
|
126
127
|
adopt: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal', 'probelauf'] },
|
|
127
128
|
plugin: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: ['local', 'lokal'] },
|
|
128
|
-
bundle: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: [] },
|
|
129
|
+
bundle: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: ['complete', 'vervollstaendigen'] },
|
|
129
130
|
mcp: { werte: ['harness', 'ort'], flaggen: [] },
|
|
130
131
|
sync: { werte: [], flaggen: [] },
|
|
131
132
|
update: { werte: ['quelle'], flaggen: [] },
|
|
@@ -160,7 +161,8 @@ const UNTERKOMMANDOS = {
|
|
|
160
161
|
add: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'quelle'] },
|
|
161
162
|
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer'] },
|
|
162
163
|
// sync nimmt einen Bundle-Namen, aber keinen zwingend: ohne Namen gilt der ganze Bestand.
|
|
163
|
-
|
|
164
|
+
// --complete/--vervollstaendigen holt herausgenommene Mitglieder zurück (E114 L4).
|
|
165
|
+
sync: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'complete', 'vervollstaendigen'] },
|
|
164
166
|
list: { woerter: 1, schalter: [] },
|
|
165
167
|
},
|
|
166
168
|
mcp: {
|
|
@@ -491,9 +493,10 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
491
493
|
if (kommando === 'unlink') {
|
|
492
494
|
const ergebnis = await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
493
495
|
console.log(`Link ${skill} entfernt.`);
|
|
494
|
-
//
|
|
495
|
-
for (const bundle of ergebnis.
|
|
496
|
-
console.log(
|
|
496
|
+
// Herausgenommen (E114 L2): das Bundle bleibt, der Abgleich legt den Skill nicht wieder an.
|
|
497
|
+
for (const bundle of ergebnis.herausgenommenAus)
|
|
498
|
+
console.log(`„${skill}" ist aus dem Bundle „${bundle}" herausgenommen, das Bundle bleibt. ` +
|
|
499
|
+
`Zurück mit hmgr link ${skill} oder hmgr bundle sync ${bundle} --complete.`);
|
|
497
500
|
if (ergebnis.hinweis !== undefined)
|
|
498
501
|
console.log(ergebnis.hinweis);
|
|
499
502
|
return 0;
|
|
@@ -612,10 +615,15 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
612
615
|
if (kommando === 'bundle') {
|
|
613
616
|
const schalter = schalterLesen(argv.slice(1), 'bundle');
|
|
614
617
|
unterkommandoPruefen('bundle', schalter);
|
|
615
|
-
const { rest, werte } = schalter;
|
|
618
|
+
const { rest, werte, flaggen } = schalter;
|
|
616
619
|
const unterkommando = rest[0];
|
|
617
620
|
if (unterkommando === 'list') {
|
|
618
621
|
const katalog = await anwendungsfaelle.bundleKatalog();
|
|
622
|
+
// Je Bundle die Ziele, in denen es liegt, mit seiner Lage (E114 L6). Nur nötig, wenn
|
|
623
|
+
// überhaupt ein Bundle installiert ist; der Ist-Zustand kostet sonst einen Scan aller Ziele.
|
|
624
|
+
const installiert = (await ports.zustand.lesen()).bundleInstallationen.length > 0
|
|
625
|
+
? (await anwendungsfaelle.istZustand({ plugins: false })).flatMap((z) => z.bundles.map((b) => ({ ziel: z.ziel, bundle: b })))
|
|
626
|
+
: [];
|
|
619
627
|
const leer = katalog.every((k) => k.bundles.length === 0 && k.kaputte.length === 0 && k.fehler === undefined);
|
|
620
628
|
if (leer)
|
|
621
629
|
console.log('Keine Quelle mit Bundles. hmgr help bundle zeigt das Format von bundles/<name>.yaml.');
|
|
@@ -631,6 +639,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
631
639
|
console.log(` ${bundle.name} ${kappen(bundle.beschreibung)}`);
|
|
632
640
|
for (const mitglied of bundle.mitglieder)
|
|
633
641
|
console.log(` ${mitglied.pfad} aus ${quelleAlsText(mitglied.quelle)}`);
|
|
642
|
+
for (const { ziel, bundle: lage } of installiert)
|
|
643
|
+
if (lage.name === bundle.name && quelleSchluessel(lage.quelle) === quelleSchluessel(bundle.quelle))
|
|
644
|
+
console.log(` installiert ${ziel.harness} ${beschreibeOrt(ziel.ort)} ${bundleLage(lage)}`);
|
|
634
645
|
}
|
|
635
646
|
// Eine kaputte Definition verdeckt die übrigen nicht, sie wird eigens genannt (E69).
|
|
636
647
|
for (const kaputt of eintrag.kaputte)
|
|
@@ -683,9 +694,11 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
683
694
|
}
|
|
684
695
|
// sync: ohne Ziel-Schalter der ganze Bestand, mit --ort/--harness nur dieses Ziel.
|
|
685
696
|
const nurZiel = werte.has('harness') || werte.has('ort');
|
|
697
|
+
const vervollstaendigen = flaggen.has('complete') || flaggen.has('vervollstaendigen');
|
|
686
698
|
const eintraege = await anwendungsfaelle.bundleAbgleich({
|
|
687
699
|
...(name === undefined ? {} : { bundle: name }),
|
|
688
700
|
...(nurZiel ? { ziel } : {}),
|
|
701
|
+
...(vervollstaendigen ? { aufnehmen: 'alle' } : {}),
|
|
689
702
|
});
|
|
690
703
|
if (eintraege.length === 0)
|
|
691
704
|
console.log('Kein Bundle installiert, nichts abzugleichen.');
|
|
@@ -843,12 +856,17 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
843
856
|
else
|
|
844
857
|
console.log(` liegen lassen Arbeitsverzeichnis ${pfad} (nicht mehr auf der Platte, ohne Bestätigung nicht vergessen)`);
|
|
845
858
|
}
|
|
859
|
+
// SOPS-Dateien, die verschlüsselt im Checkout lagen (E115).
|
|
860
|
+
const sopsQuellen = doctor.sops.length === 0 ? [] : await anwendungsfaelle.quellen();
|
|
861
|
+
for (const zeile of sopsBerichtZeilen(doctor.sops, sopsQuellen))
|
|
862
|
+
console.log(zeile);
|
|
846
863
|
// Die Sammelzeile steht ganz am Ende des Berichts (E109); „Außerdem" nur, wenn sonst etwas im Lauf stand.
|
|
847
864
|
const updateSammel = sammelZeile(updateOhne, !updateAndere &&
|
|
848
865
|
pluginFehler.length === 0 &&
|
|
849
866
|
bundleBericht(bundles).length === 0 &&
|
|
850
867
|
aufraeumen.verwaiste.length === 0 &&
|
|
851
|
-
doctor.fehlendeArbeitsverzeichnisse.length === 0
|
|
868
|
+
doctor.fehlendeArbeitsverzeichnisse.length === 0 &&
|
|
869
|
+
doctor.sops.length === 0);
|
|
852
870
|
if (updateSammel !== undefined)
|
|
853
871
|
console.log(updateSammel);
|
|
854
872
|
// Zuletzt hmgr selbst (E77): wer schon aktualisiert, will auch wissen, dass sein hmgr alt ist.
|
|
@@ -888,13 +906,22 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
888
906
|
}
|
|
889
907
|
// Bundles des Ziels (E69), unter den Einträgen: welche Links als Einheit zusammengehören.
|
|
890
908
|
for (const bundle of zielZustand.bundles)
|
|
891
|
-
console.log(` Bundle ${bundle.name} (${quelleAlsText(bundle.quelle)}) ${bundle
|
|
909
|
+
console.log(` Bundle ${bundle.name} (${quelleAlsText(bundle.quelle)}) ${bundleLage(bundle)}`);
|
|
892
910
|
if (zielZustand.plugins !== undefined)
|
|
893
911
|
console.log(` Plugins: ${zielZustand.plugins.length === 0 ? 'keine' : zielZustand.plugins.join(', ')}`);
|
|
894
912
|
}
|
|
895
913
|
const fehlend = await anwendungsfaelle.nichtVorhandeneHarnesses();
|
|
896
914
|
if (fehlend.length > 0)
|
|
897
915
|
console.log(`Nicht vorhanden: ${fehlend.join(', ')}`);
|
|
916
|
+
// SOPS-Dateien, die noch verschlüsselt im Checkout liegen (E115); ohne Fund keine Zeile.
|
|
917
|
+
const verschluesselt = await anwendungsfaelle.sopsVerschluesselteDateien();
|
|
918
|
+
if (verschluesselt.length > 0) {
|
|
919
|
+
const quellen = await anwendungsfaelle.quellen();
|
|
920
|
+
console.log('Noch verschlüsselt im Checkout:');
|
|
921
|
+
for (const { checkout, datei } of verschluesselt)
|
|
922
|
+
console.log(` ${datei} aus ${quellenAnzeigeName({ quelle: quelleAlsText(checkout.quelle), ref: checkout.ref }, quellen)} (${path.join(checkout.pfad, datei)})`);
|
|
923
|
+
console.log('hmgr doctor versucht, sie zu entschlüsseln, und nennt den Grund, wenn es nicht klappt.');
|
|
924
|
+
}
|
|
898
925
|
return 0;
|
|
899
926
|
}
|
|
900
927
|
if (kommando === 'log') {
|
|
@@ -924,7 +951,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
924
951
|
if (ereignis.art === 'checkout')
|
|
925
952
|
continue;
|
|
926
953
|
andere = true;
|
|
927
|
-
console.log(` ${verlaufEreignisZeile(ereignis)}`);
|
|
954
|
+
console.log(` ${verlaufEreignisZeile(ereignis, quellenListe)}`);
|
|
928
955
|
// Unter einem Versionssprung die Commits der neuen Version(en), eingerückt (E108).
|
|
929
956
|
if (ereignis.art === 'hmgr-installiert') {
|
|
930
957
|
const aenderungen = await anwendungsfaelle.hmgrAenderungen(ereignis.vorher, ereignis.version);
|
|
@@ -940,7 +967,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
940
967
|
}
|
|
941
968
|
if (kommando === 'doctor') {
|
|
942
969
|
restPruefen(schalterLesen(argv.slice(1), 'doctor').rest, 0, 'doctor');
|
|
943
|
-
const { fehlendeArbeitsverzeichnisse, vergessen } = await anwendungsfaelle.doctor();
|
|
970
|
+
const { fehlendeArbeitsverzeichnisse, vergessen, sops } = await anwendungsfaelle.doctor();
|
|
944
971
|
if (fehlendeArbeitsverzeichnisse.length === 0)
|
|
945
972
|
console.log('Keine fehlenden Arbeitsverzeichnisse.');
|
|
946
973
|
for (const pfad of fehlendeArbeitsverzeichnisse) {
|
|
@@ -949,6 +976,10 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
949
976
|
else
|
|
950
977
|
console.log(` liegen lassen ${pfad} (ohne Bestätigung nicht vergessen)`);
|
|
951
978
|
}
|
|
979
|
+
// SOPS-Dateien (E115): nur bei einem Fund eine Zeile, wer kein SOPS nutzt, sieht nichts davon.
|
|
980
|
+
const sopsQuellen = sops.length === 0 ? [] : await anwendungsfaelle.quellen();
|
|
981
|
+
for (const zeile of sopsBerichtZeilen(sops, sopsQuellen))
|
|
982
|
+
console.log(zeile);
|
|
952
983
|
return 0;
|
|
953
984
|
}
|
|
954
985
|
if (kommando === 'harness') {
|
|
@@ -1134,6 +1165,16 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
1134
1165
|
}
|
|
1135
1166
|
throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
|
|
1136
1167
|
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Lage eines installierten Bundles in einem Ziel (E114 L6), für `status` und `bundle list`:
|
|
1170
|
+
* verlinkt von gesamt, die verlinkten Namen und, wenn etwas fehlt, was.
|
|
1171
|
+
*/
|
|
1172
|
+
function bundleLage(bundle) {
|
|
1173
|
+
const gesamt = bundle.skills.length + bundle.nichtVerlinkt.length;
|
|
1174
|
+
const verlinkt = bundle.skills.length === 0 ? '' : ` ${bundle.skills.join(', ')}`;
|
|
1175
|
+
const fehlt = bundle.nichtVerlinkt.length === 0 ? '' : ` fehlt: ${bundle.nichtVerlinkt.join(', ')}`;
|
|
1176
|
+
return `${bundle.skills.length}/${gesamt}${verlinkt}${fehlt}`;
|
|
1177
|
+
}
|
|
1137
1178
|
/**
|
|
1138
1179
|
* Bericht über den Bundle-Abgleich (E69), gleich für `hmgr bundle sync` und `hmgr update`: je
|
|
1139
1180
|
* Bundle eine Zeile mit seinem Ausgang, darunter, was sich geändert hat. „fehlgeschlagen" heißt
|
|
@@ -1163,8 +1204,21 @@ function bundleBericht(eintraege) {
|
|
|
1163
1204
|
function aenderungsZeilen(aenderungen) {
|
|
1164
1205
|
return aenderungen.flatMap((a) => [` ${a.version}`, ...a.commits.map((c) => ` ${c}`)]);
|
|
1165
1206
|
}
|
|
1207
|
+
/** Bericht zu SOPS-Dateien aus doctor und update (E115), Spalten wie die übrigen Berichtszeilen. */
|
|
1208
|
+
function sopsBerichtZeilen(befunde, quellen) {
|
|
1209
|
+
return befunde.map((befund) => {
|
|
1210
|
+
const ort = `${befund.datei} aus ${quellenAnzeigeName({ quelle: quelleAlsText(befund.checkout.quelle), ref: befund.checkout.ref }, quellen)}`;
|
|
1211
|
+
if (befund.ergebnis === 'repariert')
|
|
1212
|
+
return ` entschlüsselt ${ort}`;
|
|
1213
|
+
if (befund.ergebnis === 'verschluesselt')
|
|
1214
|
+
return ` verschlüsselt ${ort}: ${sopsGrundText(befund.grund)}`;
|
|
1215
|
+
return ` fehlgeschlagen ${ort}: ${befund.fehler}`;
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1166
1218
|
/** Eine Ereigniszeile im Verlauf (E80), eingerückt unter der Kopfzeile ihres Laufs. Checkouts stellt `checkoutGruppen` dar (E109). */
|
|
1167
|
-
function verlaufEreignisZeile(ereignis) {
|
|
1219
|
+
function verlaufEreignisZeile(ereignis, quellen) {
|
|
1220
|
+
if (ereignis.art === 'sops-verschluesselt' || ereignis.art === 'sops-repariert')
|
|
1221
|
+
return sopsZeile(ereignis, quellen);
|
|
1168
1222
|
if (ereignis.art === 'bundle') {
|
|
1169
1223
|
const teile = [
|
|
1170
1224
|
ereignis.gelegt.length > 0 ? `gelegt ${ereignis.gelegt.join(', ')}` : undefined,
|
|
@@ -59,3 +59,20 @@ export function verlaufZeitpunkt(iso, jetzt = new Date()) {
|
|
|
59
59
|
return `Gestern, ${uhr}`;
|
|
60
60
|
return `${zwei(datum.getDate())}.${zwei(datum.getMonth() + 1)}.${datum.getFullYear()}, ${uhr}`;
|
|
61
61
|
}
|
|
62
|
+
/** Warum eine SOPS-Datei verschlüsselt blieb (E115), in Alltagssprache. GUI: renderer.js, sopsGrundText. */
|
|
63
|
+
export function sopsGrundText(grund) {
|
|
64
|
+
if (grund === 'sops-fehlt')
|
|
65
|
+
return 'sops ist auf diesem Rechner nicht installiert';
|
|
66
|
+
if (grund === 'lokale-aenderung')
|
|
67
|
+
return 'die Datei ist im Checkout von Hand geändert';
|
|
68
|
+
if (grund === 'wrapper-fehlt')
|
|
69
|
+
return 'sops-clean.sh fehlt (weder in /usr/local/bin noch in ~/bin)';
|
|
70
|
+
return 'die Entschlüsselung klappt nicht, vermutlich fehlt der passende Schlüssel';
|
|
71
|
+
}
|
|
72
|
+
/** Verlaufszeile zu einer SOPS-Datei (E115): verschlüsselt geblieben oder nachträglich entschlüsselt. */
|
|
73
|
+
export function sopsZeile(ereignis, quellen) {
|
|
74
|
+
const ort = `${ereignis.datei} aus ${quellenAnzeigeName(ereignis, quellen)}`;
|
|
75
|
+
return ereignis.grund === undefined
|
|
76
|
+
? `Verschlüsselte Datei ${ort} entschlüsselt`
|
|
77
|
+
: `Datei ${ort} bleibt verschlüsselt: ${sopsGrundText(ereignis.grund)}`;
|
|
78
|
+
}
|
package/dist/gui/main.js
CHANGED
|
@@ -174,6 +174,9 @@ function zielZustandSerialisieren(z) {
|
|
|
174
174
|
bundles: z.bundles.map((b) => ({
|
|
175
175
|
name: b.name,
|
|
176
176
|
skills: b.skills,
|
|
177
|
+
// E114 L3: nicht verlinkte Mitglieder (herausgenommen oder von Hand gelöscht).
|
|
178
|
+
nichtVerlinkt: b.nichtVerlinkt,
|
|
179
|
+
herausgenommen: b.herausgenommen,
|
|
177
180
|
quelleText: quelleAlsText(b.quelle),
|
|
178
181
|
quelleName: quelleName(b.quelle),
|
|
179
182
|
})),
|
|
@@ -234,8 +237,10 @@ function registriereIpc() {
|
|
|
234
237
|
return ergebnis;
|
|
235
238
|
}));
|
|
236
239
|
// Bundles aller bekannten Quellen (E69): flach wie der Skill-Katalog, je Bundle seine Quelle und
|
|
237
|
-
//
|
|
238
|
-
//
|
|
240
|
+
// seine Mitglieder als Quelle plus Pfad (E114 L1). Name und Beschreibung eines Mitglieds sucht der
|
|
241
|
+
// Renderer im Skill-Katalog nach (`quelleSchluessel` + `pfad`): eine noch nicht eingerichtete
|
|
242
|
+
// Quelle wird hier nicht gelesen, sonst klonte jedes Öffnen des Dialogs. Kaputte Definitionen
|
|
243
|
+
// kommen als eigene Liste mit, damit die GUI sie nennen kann, statt sie zu verschlucken.
|
|
239
244
|
ipcMain.handle('hmgr:bundles', async () => auffangen(async () => {
|
|
240
245
|
const bundles = [];
|
|
241
246
|
const kaputte = [];
|
|
@@ -244,7 +249,11 @@ function registriereIpc() {
|
|
|
244
249
|
bundles.push({
|
|
245
250
|
name: bundle.name,
|
|
246
251
|
beschreibung: bundle.beschreibung,
|
|
247
|
-
mitglieder: bundle.mitglieder.
|
|
252
|
+
mitglieder: bundle.mitglieder.map((m) => ({
|
|
253
|
+
quelleSchluessel: quelleSchluessel(m.quelle),
|
|
254
|
+
quelleText: quelleAlsText(m.quelle),
|
|
255
|
+
pfad: m.pfad,
|
|
256
|
+
})),
|
|
248
257
|
// Anzeige im Dialog: aus welchen Quellen das Bundle Skills holt, ohne Dubletten.
|
|
249
258
|
quellen: [...new Set(bundle.mitglieder.map((m) => quelleAlsText(m.quelle)))],
|
|
250
259
|
quelleText: quelleAlsText(bundle.quelle),
|
|
@@ -272,6 +281,24 @@ function registriereIpc() {
|
|
|
272
281
|
};
|
|
273
282
|
}));
|
|
274
283
|
ipcMain.handle('hmgr:bundleEntfernen', async (_event, args) => auffangen(async () => anwendungsfaelle.bundleEntfernen({ bundle: args.bundle, ziel: zielAusIpc(args.harness, args.ort) })));
|
|
284
|
+
// Vervollständigen (E114 L4, `aufnehmen: 'alle'`) und „Wieder verlinken" einzelner Mitglieder (L3,
|
|
285
|
+
// Namensliste): der Abgleich dieses einen Bundles in diesem Ziel. Wie bundleEntfernen ohne Zeiger.
|
|
286
|
+
// Der Abgleich meldet einen Fehlschlag im Ergebnis statt zu werfen; hier wird er zum Fehler, damit
|
|
287
|
+
// `rufe` im Renderer ihn zeigt.
|
|
288
|
+
ipcMain.handle('hmgr:bundleAbgleichen', async (_event, args) => auffangen(async () => {
|
|
289
|
+
const eintraege = await anwendungsfaelle.bundleAbgleich({
|
|
290
|
+
bundle: args.bundle,
|
|
291
|
+
quelle: quelleAusText(args.quelleText),
|
|
292
|
+
ziel: zielAusIpc(args.harness, args.ort),
|
|
293
|
+
aufnehmen: args.aufnehmen,
|
|
294
|
+
});
|
|
295
|
+
const eintrag = eintraege[0];
|
|
296
|
+
if (eintrag === undefined)
|
|
297
|
+
throw new Error(`Bundle „${args.bundle}" ist dort nicht installiert.`);
|
|
298
|
+
if (eintrag.ergebnis === 'fehlgeschlagen')
|
|
299
|
+
throw new Error(`Bundle „${args.bundle}" ließ sich nicht vervollständigen: ${eintrag.fehler ?? ''}`);
|
|
300
|
+
return { gelegt: eintrag.gelegt };
|
|
301
|
+
}));
|
|
275
302
|
// Nur die Herkunft, ohne die Quellen zu lesen: der Katalog daneben tut das schon.
|
|
276
303
|
ipcMain.handle('hmgr:quellen', async () => auffangen(async () => (await anwendungsfaelle.quellen()).map((quelle) => ({
|
|
277
304
|
art: quelle.art,
|
|
@@ -858,6 +885,7 @@ function fensterOeffnen() {
|
|
|
858
885
|
// fällt, was ins Ziel kommt, und zeigt je Bundle Beschreibung und Herkunft.
|
|
859
886
|
var dskBundleBoxSichtbar = false;
|
|
860
887
|
var dskBundleText = null;
|
|
888
|
+
var dskBundleZuVorher = null, dskBundleKoerperText = null;
|
|
861
889
|
// Aufklappbereich je Zeile (E92): zu vor dem Klick, danach sichtbar mit der ungekürzten
|
|
862
890
|
// Beschreibung; die Checkbox bleibt unberührt, der Dialog offen.
|
|
863
891
|
var dskKoerperVorher = null;
|
|
@@ -895,6 +923,13 @@ function fensterOeffnen() {
|
|
|
895
923
|
dskBundleBoxSichtbar = document.getElementById('dsk-bundlebox')?.hidden === false;
|
|
896
924
|
dskBundleText = document.querySelector('#dsk-bundles .wahlzeile')?.textContent
|
|
897
925
|
?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
926
|
+
// E114 L1: die Bundle-Wahlzeile klappt auf und zeigt ihre Skills samt Beschreibung.
|
|
927
|
+
var dskBundleKoerper = document.querySelector('#dsk-bundles .wahlzeile .wahlkoerper');
|
|
928
|
+
dskBundleZuVorher = dskBundleKoerper?.hidden ?? null;
|
|
929
|
+
document.querySelector('#dsk-bundles .wahlzeile .aufklappknopf')?.click();
|
|
930
|
+
dskBundleKoerperText = dskBundleKoerper && !dskBundleKoerper.hidden
|
|
931
|
+
? dskBundleKoerper.textContent?.replace(/\\s+/g, ' ').trim() ?? null
|
|
932
|
+
: null;
|
|
898
933
|
document.getElementById('d-skills')?.close();
|
|
899
934
|
}
|
|
900
935
|
// Verlinken-Dialog „Wohin?" (E76): er bietet nur Ziele aktiver Harnesses an. Der Text
|
|
@@ -908,20 +943,33 @@ function fensterOeffnen() {
|
|
|
908
943
|
?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
909
944
|
document.getElementById('d-ziel')?.close();
|
|
910
945
|
}
|
|
911
|
-
// Bundle-Gruppe an der
|
|
912
|
-
//
|
|
913
|
-
|
|
946
|
+
// Bundle-Gruppe an der Claude-Code-Home-Karte (E69, E114): Kopf mit Name, Quelle, Zählung
|
|
947
|
+
// „1 von 2 Skills", „Vervollständigen" und „Entfernen"; darunter zugeklappt die Zeilen
|
|
948
|
+
// aller Mitglieder. „skill-creator" hat die Fixture herausgenommen, es steht als blasse
|
|
949
|
+
// Zeile „nicht verlinkt" mit „Wieder verlinken" in der Gruppe.
|
|
950
|
+
var homeGruppe = Array.from(document.querySelectorAll('#zielliste .ziel'))
|
|
951
|
+
.find((k) => (k.querySelector('.zielkopf')?.textContent || '').indexOf('Claude Code · Home') > -1)
|
|
952
|
+
?.querySelector('.bundlegruppe') ?? null;
|
|
953
|
+
var bundleGruppeText = homeGruppe?.querySelector('.bundlekopf')?.textContent
|
|
914
954
|
?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
915
|
-
var bundleGruppeZeilen = Array.from(
|
|
955
|
+
var bundleGruppeZeilen = Array.from(homeGruppe?.querySelectorAll('.linkliste li:not(.nichtverlinkt) .name') ?? [])
|
|
916
956
|
.map((n) => n.textContent.trim()).join(',');
|
|
917
|
-
|
|
918
|
-
|
|
957
|
+
var bundleNichtVerlinktText = homeGruppe?.querySelector('.linkliste li.nichtverlinkt')?.textContent
|
|
958
|
+
?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
959
|
+
var bundleVervollstaendigenKnopf = Array.from(homeGruppe?.querySelectorAll('.bundlekopf button') ?? [])
|
|
960
|
+
.some((k) => k.textContent === 'Vervollständigen');
|
|
961
|
+
var bundleKoerperZuVorher = homeGruppe?.querySelector('.linkliste')?.hidden ?? null;
|
|
962
|
+
homeGruppe?.querySelector('.bundlekopf .aufklappknopf')?.click();
|
|
963
|
+
var bundleKoerperAufNachKlick = homeGruppe?.querySelector('.linkliste')?.hidden === false;
|
|
964
|
+
// Entfernen an der Mitgliedszeile sagt, dass der Skill herausgenommen wird und das
|
|
965
|
+
// Bundle bleibt (E114 L2, früher: zerbricht).
|
|
966
|
+
var bundleHerausnehmenText = null;
|
|
919
967
|
try {
|
|
920
|
-
|
|
921
|
-
|
|
968
|
+
homeGruppe?.querySelector('.linkliste li:not(.nichtverlinkt) .knopf')?.click();
|
|
969
|
+
bundleHerausnehmenText = document.getElementById('dx-text')?.textContent ?? null;
|
|
922
970
|
document.querySelector('#d-entfernen button[value="nein"]')?.click();
|
|
923
971
|
} catch (fehler) {
|
|
924
|
-
|
|
972
|
+
bundleHerausnehmenText = String(fehler);
|
|
925
973
|
}
|
|
926
974
|
// Sammelstreifen unter „Alle" (E95): das Bundle „vorhaben" liegt an kunde-x in Claude Code
|
|
927
975
|
// und Hermes — ein Streifen mit beiden Harness-Zeichen, Entfernen nennt beide Harnesses
|
|
@@ -932,12 +980,21 @@ function fensterOeffnen() {
|
|
|
932
980
|
var streifenZahl = null, streifenHarnesse = null, streifenText = null;
|
|
933
981
|
var streifenDialogTitel = null, streifenDialogText = null;
|
|
934
982
|
var chipGruppeZahl = null, chipGruppeZeilenZahl = null, chipStreifenZahl = null;
|
|
983
|
+
// E114 L1: unter „Alle" stehen die Mitglieder in der Gruppe und nicht zusätzlich in der
|
|
984
|
+
// Tabelle. Name ohne Harness-Zeichen: nur die Textknoten der Namenszelle.
|
|
985
|
+
var nurName = (n) => Array.from(n.childNodes).filter((t) => t.nodeType === 3).map((t) => t.textContent).join('').trim();
|
|
986
|
+
var alleGruppeZeilen = null, alleTabelleZeilen = null;
|
|
935
987
|
try {
|
|
936
988
|
var streifen = Array.from(streifenKarte?.querySelectorAll('.bundlegruppe') ?? []);
|
|
937
989
|
streifenZahl = streifen.length;
|
|
938
990
|
streifenHarnesse = streifen[0]?.querySelector('.zeilenzeichen')?.getAttribute('aria-label') ?? null;
|
|
939
|
-
streifenText = streifen[0]?.textContent?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
940
|
-
streifen[0]?.
|
|
991
|
+
streifenText = streifen[0]?.querySelector('.bundlekopf')?.textContent?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
992
|
+
alleGruppeZeilen = Array.from(streifen[0]?.querySelectorAll('.linkliste li .name') ?? []).map(nurName).join(',');
|
|
993
|
+
alleTabelleZeilen = Array.from(streifenKarte?.querySelectorAll('.linkliste li') ?? [])
|
|
994
|
+
.filter((li) => li.closest('.bundlegruppe') === null && li.querySelector('.name'))
|
|
995
|
+
.map((li) => nurName(li.querySelector('.name'))).join(',');
|
|
996
|
+
Array.from(streifen[0]?.querySelectorAll('.bundlekopf button') ?? [])
|
|
997
|
+
.find((k) => k.textContent === 'Entfernen')?.click();
|
|
941
998
|
streifenDialogTitel = document.getElementById('dx-titel')?.textContent ?? null;
|
|
942
999
|
streifenDialogText = document.getElementById('dx-text')?.textContent ?? null;
|
|
943
1000
|
document.querySelector('#d-entfernen button[value="nein"]')?.click();
|
|
@@ -1236,13 +1293,21 @@ function fensterOeffnen() {
|
|
|
1236
1293
|
dskAufklappFreiAngehakt,
|
|
1237
1294
|
dskBundleBoxSichtbar,
|
|
1238
1295
|
dskBundleText,
|
|
1296
|
+
dskBundleZuVorher,
|
|
1297
|
+
dskBundleKoerperText,
|
|
1239
1298
|
dzZieleText,
|
|
1240
1299
|
bundleGruppeText,
|
|
1241
1300
|
bundleGruppeZeilen,
|
|
1242
|
-
|
|
1301
|
+
bundleNichtVerlinktText,
|
|
1302
|
+
bundleVervollstaendigenKnopf,
|
|
1303
|
+
bundleKoerperZuVorher,
|
|
1304
|
+
bundleKoerperAufNachKlick,
|
|
1305
|
+
bundleHerausnehmenText,
|
|
1243
1306
|
streifenZahl,
|
|
1244
1307
|
streifenHarnesse,
|
|
1245
1308
|
streifenText,
|
|
1309
|
+
alleGruppeZeilen,
|
|
1310
|
+
alleTabelleZeilen,
|
|
1246
1311
|
streifenDialogTitel,
|
|
1247
1312
|
streifenDialogText,
|
|
1248
1313
|
chipGruppeZahl,
|
package/dist/gui/preload.cjs
CHANGED
|
@@ -15,6 +15,8 @@ contextBridge.exposeInMainWorld('hmgr', {
|
|
|
15
15
|
bundles: () => ipcRenderer.invoke('hmgr:bundles'),
|
|
16
16
|
bundleInstallieren: (args) => ipcRenderer.invoke('hmgr:bundleInstallieren', args),
|
|
17
17
|
bundleEntfernen: (args) => ipcRenderer.invoke('hmgr:bundleEntfernen', args),
|
|
18
|
+
/** Vervollständigen ('alle') oder einzelne Mitglieder wieder verlinken (E114). */
|
|
19
|
+
bundleAbgleichen: (args) => ipcRenderer.invoke('hmgr:bundleAbgleichen', args),
|
|
18
20
|
/** Icon einer Quelle als data-URL, oder null (E60). */
|
|
19
21
|
icon: (text) => ipcRenderer.invoke('hmgr:icon', text),
|
|
20
22
|
/** Öffnet eine Quelle: Repo-Seite im Browser oder Ordner im Dateimanager (E60). */
|