hmgr 0.0.7 → 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/claude-code.js +53 -12
- package/dist/adapter/dateisystem.js +85 -43
- 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/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 +36 -15
- package/dist/cli/hilfe.js +100 -13
- package/dist/cli/main.js +219 -18
- package/dist/gui/hmgr-cli.js +44 -0
- package/dist/gui/main.js +344 -4
- package/dist/gui/preload.cjs +26 -2
- package/dist/gui/renderer/index.html +67 -15
- package/dist/gui/renderer/renderer.js +441 -228
- package/dist/gui/renderer/styles.css +69 -16
- package/dist/kern/anwendungsfaelle.js +244 -16
- package/dist/kern/modell.js +112 -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 +4 -3
- package/package.json +7 -1
|
@@ -154,16 +154,34 @@ a{color:var(--signal-tief)}
|
|
|
154
154
|
.stand{margin:22px 4px 0;padding:14px 6px 0;border-top:1px solid rgba(255,255,255,.16);display:flex;flex-wrap:wrap;gap:10px 12px;align-items:center}
|
|
155
155
|
.stand p{flex:1 1 130px;font-size:16px;line-height:1.4;color:var(--emaille-hell)}
|
|
156
156
|
.stand p b{font-weight:600;color:#F1F6EF}
|
|
157
|
-
/* Ruhig, solange nichts ansteht; erst „mit Neuem" (.neu) macht ihn zum Signal. Der Knopf
|
|
158
|
-
|
|
157
|
+
/* Ruhig, solange nichts ansteht; erst „mit Neuem" (.neu) macht ihn zum Signal. Der Knopf öffnet
|
|
158
|
+
nur die Ansicht (E80), deshalb nie disabled. Schmaler als vorher: er trägt jetzt nur noch „›". */
|
|
159
159
|
.standknopf{
|
|
160
160
|
background:transparent;border:1px solid rgba(255,255,255,.3);color:var(--emaille-hell);border-radius:3px;
|
|
161
|
-
padding:6px 13px;cursor:pointer;white-space:nowrap;
|
|
162
|
-
font-size:16px;letter-spacing:-.005em;
|
|
161
|
+
padding:6px 13px;min-width:34px;text-align:center;cursor:pointer;white-space:nowrap;
|
|
162
|
+
font-family:var(--schrift);font-weight:500;font-size:16px;letter-spacing:-.005em;
|
|
163
|
+
transition:background .14s ease,border-color .14s ease,color .14s ease;
|
|
163
164
|
}
|
|
164
165
|
.standknopf:hover{background:var(--emaille-hoch);color:#F1F6EF}
|
|
165
166
|
.standknopf.neu{border-color:var(--emaille-hell);background:var(--emaille-hoch);color:#F1F6EF;font-weight:600}
|
|
166
167
|
|
|
168
|
+
/* Update-Hinweis über hmgr selbst (E77), direkt über dem Einstellungen-Knopf. Dezent wie die
|
|
169
|
+
Stand-Zeile darüber: kleiner, in der hellen Emaille-Farbe, ohne Signalton — es ist eine Ansage,
|
|
170
|
+
keine Aufforderung. Der Befehl darin steht im Monospace und darf umbrechen, er ist lang.
|
|
171
|
+
Das `margin-top:auto` wandert zu ihm, solange er steht: sonst schöbe der Knopf ihn nach oben
|
|
172
|
+
von sich weg, statt dass beide zusammen unten sitzen. */
|
|
173
|
+
.selbstupdate{margin-top:auto;padding:14px 6px 0;display:flex;flex-direction:column;gap:6px}
|
|
174
|
+
/* Neustart-Knopf (E80): unter der GUI-Zeile, nicht über die volle Leistenbreite gestreckt (sonst
|
|
175
|
+
erbte er die Spaltenbreite der Flex-Column). Kein „.neu": er soll nicht wie der Standknopf-Signal
|
|
176
|
+
aussehen, er meldet nur eine Handlung, keinen Zustand. */
|
|
177
|
+
.selbstupdate .standknopf{align-self:flex-start;min-width:0}
|
|
178
|
+
/* Wie bei .zeile und .wahlzeile: die Klasse setzt display, also braucht hidden eine eigene Regel —
|
|
179
|
+
sonst stünde der leere Kasten samt seinem auto-Rand da, obwohl nichts zu melden ist. */
|
|
180
|
+
.selbstupdate[hidden]{display:none}
|
|
181
|
+
.selbstupdate p{font-size:15px;line-height:1.45;color:var(--emaille-hell)}
|
|
182
|
+
.selbstupdate code{font-family:var(--mono);font-size:14px;color:#F1F6EF;word-break:break-all}
|
|
183
|
+
|
|
184
|
+
.selbstupdate:not([hidden]) + .einstellungen{margin-top:0}
|
|
167
185
|
.einstellungen{
|
|
168
186
|
margin-top:auto;padding:14px 6px 0;display:flex;align-items:center;gap:11px;width:100%;
|
|
169
187
|
background:transparent;border:0;border-top:1px solid rgba(255,255,255,.16);cursor:pointer;text-align:left;
|
|
@@ -213,6 +231,9 @@ a{color:var(--signal-tief)}
|
|
|
213
231
|
.chip[aria-pressed="true"]{background:var(--tinte);border-color:var(--tinte);color:var(--blech)}
|
|
214
232
|
/* Harness-Zeichen im Chip (E53 Pkt. 1): kleiner als die Standardgröße, gap übernimmt den Abstand. */
|
|
215
233
|
.chip .hsymbol{width:15px;height:15px;margin-right:0}
|
|
234
|
+
/* Sortierrichtung im Chip (E81): nur der aktive Chip füllt den Pfeil, Farbe erbt über currentColor
|
|
235
|
+
(im gedrückten Chip also --blech), gap übernimmt den Abstand zum Text. */
|
|
236
|
+
.chip .sortpfeil:empty{display:none}
|
|
216
237
|
|
|
217
238
|
.katalogzahl{margin:0 0 12px;font-size:16px;color:var(--tinte-weich)}
|
|
218
239
|
.zeilen{display:flex;flex-direction:column;gap:8px;list-style:none;margin:0;padding:0}
|
|
@@ -298,6 +319,24 @@ a{color:var(--signal-tief)}
|
|
|
298
319
|
.knopf[disabled]:hover{background:var(--tinte);border-color:var(--tinte)}
|
|
299
320
|
.knopf.klein{padding:4px 13px}
|
|
300
321
|
|
|
322
|
+
/* Prüfen-Kringel in der Update-Ansicht (E80): rund statt der früheren großen Signal-Fläche, denn er
|
|
323
|
+
ist ein Anstoß, keine gefährliche Handlung. Dreht sich, solange der Lauf läuft (.laeuft). */
|
|
324
|
+
.kringel{
|
|
325
|
+
width:44px;height:44px;border-radius:50%;flex:none;
|
|
326
|
+
border:1.5px solid var(--tinte);background:var(--tinte);color:var(--blech);
|
|
327
|
+
font-size:22px;line-height:1;display:flex;align-items:center;justify-content:center;
|
|
328
|
+
cursor:pointer;transition:background .14s ease,border-color .14s ease,color .14s ease;
|
|
329
|
+
}
|
|
330
|
+
.kringel:hover{background:var(--emaille);border-color:var(--emaille)}
|
|
331
|
+
.kringel:disabled{opacity:.55;cursor:default}
|
|
332
|
+
.kringel.laeuft{animation:kringel-drehen 1s linear infinite}
|
|
333
|
+
@keyframes kringel-drehen{to{transform:rotate(360deg)}}
|
|
334
|
+
@media (prefers-reduced-motion:reduce){
|
|
335
|
+
/* Die globale Regel weiter unten kappt schon jede Animationsdauer; hier zusätzlich statisch
|
|
336
|
+
gedämpft statt gedreht, damit der laufende Zustand ohne Bewegung sichtbar bleibt. */
|
|
337
|
+
.kringel.laeuft{animation:none;opacity:.6}
|
|
338
|
+
}
|
|
339
|
+
|
|
301
340
|
.ziele{display:flex;flex-direction:column;gap:18px}
|
|
302
341
|
.ziel{background:var(--blech);border:1px solid var(--strich);border-radius:3px;overflow:hidden}
|
|
303
342
|
/* Kopfzeile als Akkordeon-Umschalter (E45): role="button", die ganze Zeile ist klickbar. */
|
|
@@ -309,7 +348,10 @@ a{color:var(--signal-tief)}
|
|
|
309
348
|
.zielkopf .zahl{margin-left:auto;font-family:var(--mono);font-size:16px;color:var(--emaille-zahl)}
|
|
310
349
|
/* Zeichen statt Text-Badge (E45-Nachschärfung): trägt die Farbe selbst als Emoji, nur Größe passend
|
|
311
350
|
zur Kopfzeile. Eigener Randabstand, damit es neben Titel und Pfad Luft behält. */
|
|
312
|
-
.warnung{font-size:19px;line-height:1;margin:0 4px}
|
|
351
|
+
.warnung{font-size:19px;line-height:1;margin:0 4px;display:inline-flex;align-items:center;gap:6px}
|
|
352
|
+
/* Warnzahl neben dem Dreieck (E75): dieselbe Optik wie die Skill-Zahl rechts im selben Kopf, damit
|
|
353
|
+
zwei Zahlen in einer Zeile nicht nach zwei Bauteilen aussehen. Das Dreieck davor trennt sie. */
|
|
354
|
+
.warnzahl{font-family:var(--mono);font-size:16px;color:var(--emaille-zahl)}
|
|
313
355
|
/* Pfeil-Indikator des Akkordeons: zeigt zu, dreht sich beim Aufklappen. */
|
|
314
356
|
.akkordeonpfeil{flex:none;width:15px;height:15px;stroke:var(--emaille-hell);fill:none;stroke-width:1.8;transition:transform .14s ease}
|
|
315
357
|
.zielkopf[aria-expanded="true"] .akkordeonpfeil{transform:rotate(180deg)}
|
|
@@ -336,9 +378,8 @@ a{color:var(--signal-tief)}
|
|
|
336
378
|
.ziel.verzeichnis .zielkoerper > .kartenchips + *{border-top:0;margin-top:0;padding-top:0}
|
|
337
379
|
.kartenchips .hsymbol{margin-right:6px}
|
|
338
380
|
/* Zielordner des gewählten Harness, wie ihn vorher der Abschnittskopf trug (25cddf6): rechts in der
|
|
339
|
-
Chip-Leiste
|
|
381
|
+
Chip-Leiste. */
|
|
340
382
|
.kartenchips .kartenordner{margin-left:auto;align-self:center;color:var(--tinte-weich);word-break:break-all}
|
|
341
|
-
.kartenchips .ruhend{align-self:center}
|
|
342
383
|
|
|
343
384
|
.hsymbol{flex:none;width:17px;height:17px;margin-right:8px;vertical-align:-.19em}
|
|
344
385
|
/* Das Hermes-Zeichen ist ein Bitmap-Favicon mit weißem Grund: leicht gerundet wie eine Tab-Kachel. */
|
|
@@ -533,6 +574,9 @@ dialog::backdrop{background:rgba(9,45,37,.62)}
|
|
|
533
574
|
entfallen ersatzlos, das Icon trägt jetzt die Unterscheidung. */
|
|
534
575
|
.quellenliste{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:15px}
|
|
535
576
|
.quellenliste li{display:grid;grid-template-columns:20px minmax(0,1fr) auto;gap:3px 10px;align-items:center}
|
|
577
|
+
/* Der Leertext ist keine Quellenzeile (E75): ohne diese Ausnahme fiele er als einziges Grid-Kind in
|
|
578
|
+
die 20px-Icon-Spalte und bräche Wort für Wort um. Erbt sonst die Optik der übrigen Leerhinweise. */
|
|
579
|
+
.quellenliste li.leerhinweis{display:block}
|
|
536
580
|
.quellenliste .repo{grid-column:1;grid-row:1}
|
|
537
581
|
/* Umbenennen und Entfernen sitzen zusammen in Spalte 3, sonst lägen sie als eigene Grid-Kinder
|
|
538
582
|
beide in derselben Zelle übereinander. Spalte und Zeile stehen ausdrücklich da, damit sie nicht
|
|
@@ -555,12 +599,16 @@ dialog::backdrop{background:rgba(9,45,37,.62)}
|
|
|
555
599
|
|
|
556
600
|
/* Harness-Schalter im Einstellungen-Dialog (entwurf/gui.html). */
|
|
557
601
|
.harnessliste{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
|
|
558
|
-
|
|
559
|
-
|
|
602
|
+
/* Die Box ist das Listenelement, nicht das Label: so liegen die Homedir-Zeilen (E78) mit in der
|
|
603
|
+
Box, ohne dass ein Klick auf ihre Knöpfe den Schalter umlegt. */
|
|
604
|
+
.harnessliste > li{
|
|
560
605
|
background:var(--blech);border:1px solid var(--strich-ui);border-radius:3px;padding:12px 15px;
|
|
561
606
|
transition:border-color .12s ease;
|
|
562
607
|
}
|
|
563
|
-
.
|
|
608
|
+
.harnessliste > li:hover{border-color:var(--tinte-weich)}
|
|
609
|
+
.harnessschalter{
|
|
610
|
+
display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start;cursor:pointer;
|
|
611
|
+
}
|
|
564
612
|
.harnessschalter input{
|
|
565
613
|
appearance:none;-webkit-appearance:none;
|
|
566
614
|
margin:3px 0 0;flex:none;width:38px;height:22px;
|
|
@@ -576,12 +624,17 @@ dialog::backdrop{background:rgba(9,45,37,.62)}
|
|
|
576
624
|
.harnessschalter b{display:flex;align-items:center;font-family:var(--mono);font-weight:500;font-size:16px;color:var(--tinte);line-height:1.35;word-break:break-word}
|
|
577
625
|
.harnessschalter small{display:block;font-size:16px;color:var(--tinte-weich);line-height:1.4}
|
|
578
626
|
|
|
579
|
-
/*
|
|
580
|
-
.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
|
|
627
|
+
/* Homedirs unter dem Schalter (E78), in derselben Box: je Zeile Art, Pfad und die Knöpfe dazu.
|
|
628
|
+
Eingerückt auf die Textspalte des Schalters (Schalterbreite plus Spaltenabstand). */
|
|
629
|
+
.heime{display:flex;flex-direction:column;gap:6px;margin:8px 0 0 52px}
|
|
630
|
+
.heime:empty{display:none}
|
|
631
|
+
.heimzeile{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
|
|
632
|
+
.heimart{flex:none;font-size:15px;color:var(--tinte-weich)}
|
|
633
|
+
.heimpfad{flex:1 1 200px;min-width:0;font-family:var(--mono);font-size:15px;color:var(--tinte);overflow-wrap:anywhere}
|
|
634
|
+
.heimknoepfe{flex:none;display:flex;flex-wrap:wrap;gap:6px}
|
|
635
|
+
.heime > .knopf{align-self:flex-start}
|
|
636
|
+
|
|
637
|
+
/* Hinweiskasten in ruhiger Messing-Optik, heute nur noch in der MCP-Ansicht (E61). */
|
|
585
638
|
.ruhezeile{
|
|
586
639
|
display:flex;flex-wrap:wrap;gap:10px 14px;align-items:center;
|
|
587
640
|
margin-bottom:14px;padding:11px 14px;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Anwendungsfälle. Pfade werden hier nur als Zeichenketten normalisiert, gelesen wird über die Ports.
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { AbhaengigkeitFehlt, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, FremderEintragUnbekannt, HarnessNichtVorhanden, HarnessTypUnbekannt, KeinPassenderRef, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpUebernahmeNichtMoeglich, McpUnbekannt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, bundleSchluessel, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
3
|
+
import { AbhaengigkeitFehlt, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, FremderEintragUnbekannt, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimUnbekannt, KeinPassenderRef, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpUebernahmeNichtMoeglich, McpUnbekannt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, bundleSchluessel, heimAdresseAlsText, heimAdresseZerlegen, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
4
4
|
import { passenderRef } from "./version.js";
|
|
5
|
-
import { kannIstRegistriert, kannMcp, kannPlugins, kannProfile, kannSkillOrdner, kannZurueckbauen, } from "../ports/harness.js";
|
|
5
|
+
import { kannIstRegistriert, kannMcp, kannPlugins, kannProfile, kannSkillOrdner, kannZurueckbauen, kannZusaetzlicheHeime, } from "../ports/harness.js";
|
|
6
6
|
/**
|
|
7
7
|
* Frage vor dem Löschen, eine für alle Kandidaten (E28). Der Kern formuliert sie fertig, der
|
|
8
8
|
* Adapter stellt sie: Anzahl und Pfade, damit der Nutzer sieht, worüber er entscheidet.
|
|
@@ -83,6 +83,19 @@ export class Anwendungsfaelle {
|
|
|
83
83
|
throw new HarnessNichtVorhanden(name);
|
|
84
84
|
return h;
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Wächter vor jedem neu angelegten Link (E76): in ein Ziel eines deaktivierten Harness legt hmgr
|
|
88
|
+
* keinen. Seit E74 zeigt keine Ansicht solche Ziele, der Link wäre unsichtbarer Zustand. Sitzt in
|
|
89
|
+
* `linkLegen` und `bundleInstallieren`, also vor jeder Wirkung und für CLI wie GUI gleich.
|
|
90
|
+
* Bewusst nicht in `linkAnlegen`: über den laufen auch Abgleich und Aktualisierung, und die
|
|
91
|
+
* pflegen bestehende Links weiter, statt neue anzulegen.
|
|
92
|
+
*/
|
|
93
|
+
async harnessAktivGeprueft(name) {
|
|
94
|
+
const harness = await this.harnessGeprueft(name);
|
|
95
|
+
if (!(await this.aktiveHarnesses()).includes(name))
|
|
96
|
+
throw new HarnessInaktiv(name);
|
|
97
|
+
return harness;
|
|
98
|
+
}
|
|
86
99
|
/** Harnesses, die auf dem Rechner liegen. Alle anderen überspringt hmgr. */
|
|
87
100
|
async vorhandeneHarnesses() {
|
|
88
101
|
const ergebnis = [];
|
|
@@ -103,6 +116,130 @@ export class Anwendungsfaelle {
|
|
|
103
116
|
harnessTypen() {
|
|
104
117
|
return this.p.harnesses.map((h) => h.name);
|
|
105
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Die Home-Ziele eines Harness (E78, F88): das primäre und jedes zusätzliche Homedir, jedes als
|
|
121
|
+
* eigenständiges Ziel. Eine Stelle für alle Aufzählungen — Status, Säubern, GUI —, damit ein
|
|
122
|
+
* zusätzliches Homedir nirgends versehentlich fehlt. Ein `wsl:`-Heim im Zustand einer anderen
|
|
123
|
+
* Plattform lässt der Port weg (Entscheidung 7), hier kommt es dann gar nicht erst an.
|
|
124
|
+
*/
|
|
125
|
+
homeZiele(harness) {
|
|
126
|
+
if (!harness.orte().includes('home'))
|
|
127
|
+
return [];
|
|
128
|
+
const ziele = [{ harness: harness.name, ort: { art: 'home' } }];
|
|
129
|
+
if (kannZusaetzlicheHeime(harness))
|
|
130
|
+
for (const heim of this.p.heime.zusaetzliche(harness.name))
|
|
131
|
+
ziele.push({ harness: harness.name, ort: { art: 'home', heim: heim.adresse } });
|
|
132
|
+
return ziele;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Die Homedirs aller Harness-Typen, für `hmgr harness home` und die GUI (E78). Unabhängig davon,
|
|
136
|
+
* ob der Harness aktiv oder installiert ist: die Einstellung gehört dem Nutzer, nicht dem Ist-Zustand.
|
|
137
|
+
*/
|
|
138
|
+
async harnessHeime() {
|
|
139
|
+
const zustand = await this.p.zustand.lesen();
|
|
140
|
+
return this.p.harnesses.map((harness) => {
|
|
141
|
+
const eintrag = zustand.harnessHeime.find((h) => h.harness === harness.name);
|
|
142
|
+
const primaer = {
|
|
143
|
+
pfad: this.p.heime.primaer(harness.name),
|
|
144
|
+
eingestellt: eintrag?.pfad !== undefined && eintrag.pfad.length > 0,
|
|
145
|
+
};
|
|
146
|
+
const zusaetzliche = this.p.heime
|
|
147
|
+
.zusaetzliche(harness.name)
|
|
148
|
+
.map((h) => ({ adresse: h.adresse, pfad: h.pfad, eingestellt: true }));
|
|
149
|
+
return {
|
|
150
|
+
harness: harness.name,
|
|
151
|
+
heime: [primaer, ...zusaetzliche],
|
|
152
|
+
kannZusaetzliche: kannZusaetzlicheHeime(harness),
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/** Der Eintrag dieses Harness im Zustand, notfalls neu angelegt (E78). */
|
|
157
|
+
static heimEintrag(zustand, harness) {
|
|
158
|
+
let eintrag = zustand.harnessHeime.find((h) => h.harness === harness);
|
|
159
|
+
if (eintrag === undefined) {
|
|
160
|
+
eintrag = { harness, zusaetzliche: [] };
|
|
161
|
+
zustand.harnessHeime.push(eintrag);
|
|
162
|
+
}
|
|
163
|
+
return eintrag;
|
|
164
|
+
}
|
|
165
|
+
/** Einträge ohne Inhalt fallen weg, damit die state.json nicht mit leeren Hüllen zuwächst. */
|
|
166
|
+
static heimeAufraeumen(zustand) {
|
|
167
|
+
zustand.harnessHeime = zustand.harnessHeime.filter((h) => (h.pfad !== undefined && h.pfad.length > 0) || h.zusaetzliche.length > 0);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Primäres Homedir umstellen (E78, F89): hmgr merkt sich den neuen Pfad und zeigt ab dann ihn.
|
|
171
|
+
* Was am alten Ort liegt, bleibt unberührt liegen — dasselbe Verhalten wie beim Deaktivieren
|
|
172
|
+
* (E74). Wer umziehen will, verlinkt am neuen Ort neu; einen Umzugsassistenten gibt es bewusst
|
|
173
|
+
* nicht. Der Pfad wird nicht geprüft: ein Homedir, das erst noch entsteht, ist ein gültiger Wunsch.
|
|
174
|
+
*/
|
|
175
|
+
async harnessHeimSetzen(harnessName, pfad) {
|
|
176
|
+
this.harness(harnessName); // wirft HarnessTypUnbekannt bei unbekanntem Namen
|
|
177
|
+
const aufgeloest = path.resolve(pfad);
|
|
178
|
+
const zustand = await this.p.zustand.lesen();
|
|
179
|
+
Anwendungsfaelle.heimEintrag(zustand, harnessName).pfad = aufgeloest;
|
|
180
|
+
await this.p.zustand.schreiben(zustand);
|
|
181
|
+
return aufgeloest;
|
|
182
|
+
}
|
|
183
|
+
/** Gegenstück zu `harnessHeimSetzen` (E78, F89): wieder das Home des Nutzers, nichts wird verschoben. */
|
|
184
|
+
async harnessHeimZuruecksetzen(harnessName) {
|
|
185
|
+
this.harness(harnessName);
|
|
186
|
+
const zustand = await this.p.zustand.lesen();
|
|
187
|
+
const eintrag = zustand.harnessHeime.find((h) => h.harness === harnessName);
|
|
188
|
+
if (eintrag === undefined || eintrag.pfad === undefined)
|
|
189
|
+
return;
|
|
190
|
+
delete eintrag.pfad;
|
|
191
|
+
Anwendungsfaelle.heimeAufraeumen(zustand);
|
|
192
|
+
await this.p.zustand.schreiben(zustand);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Zusätzliches Homedir hinzufügen (E78, F87): Adresse `wsl:<distro>`, ohne Benutzer-Option —
|
|
196
|
+
* wsl.exe startet als Standardbenutzer der Distro, derselbe, der Claude Code dort nutzt. Das Home
|
|
197
|
+
* wird einmal ermittelt und gespeichert, nicht bei jedem Lauf neu erfragt: sonst hinge jede
|
|
198
|
+
* Ansicht an einem startenden WSL. Der Aufrufer zeigt den ermittelten Pfad an.
|
|
199
|
+
*/
|
|
200
|
+
async harnessHeimHinzufuegen(harnessName, adresse) {
|
|
201
|
+
const harness = this.harness(harnessName);
|
|
202
|
+
if (!kannZusaetzlicheHeime(harness))
|
|
203
|
+
throw new HeimFaehigkeitFehlt(harnessName);
|
|
204
|
+
const zerlegt = heimAdresseZerlegen(adresse);
|
|
205
|
+
if (zerlegt === undefined)
|
|
206
|
+
throw new HeimAdresseUngueltig(adresse);
|
|
207
|
+
const normalisiert = heimAdresseAlsText(zerlegt);
|
|
208
|
+
// Plattformgrenze (Entscheidung 7): ein WSL-Heim lässt sich nur unter Windows anlegen und
|
|
209
|
+
// bedienen. Ohne die Prüfung stünde hier ein Eintrag, den derselbe Rechner nie wieder sieht.
|
|
210
|
+
if (this.p.umgebung.plattform() !== 'win32')
|
|
211
|
+
throw new HeimNurUnterWindows(normalisiert, this.p.umgebung.plattform());
|
|
212
|
+
const zustand = await this.p.zustand.lesen();
|
|
213
|
+
const eintrag = Anwendungsfaelle.heimEintrag(zustand, harnessName);
|
|
214
|
+
if (eintrag.zusaetzliche.some((h) => h.adresse === normalisiert))
|
|
215
|
+
throw new HeimBereitsVorhanden(harnessName, normalisiert);
|
|
216
|
+
const pfad = await this.p.wsl.home(zerlegt.distro);
|
|
217
|
+
eintrag.zusaetzliche.push({ adresse: normalisiert, pfad });
|
|
218
|
+
await this.p.zustand.schreiben(zustand);
|
|
219
|
+
return { adresse: normalisiert, pfad };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Zusätzliches Homedir entfernen (E78): hmgr vergisst es, die Links dort bleiben liegen — wie bei
|
|
223
|
+
* jedem anderen „nicht mehr gemanagt" (E74, F89). Wer sie los werden will, entfernt sie vorher.
|
|
224
|
+
*/
|
|
225
|
+
async harnessHeimEntfernen(harnessName, adresse) {
|
|
226
|
+
this.harness(harnessName);
|
|
227
|
+
const zerlegt = heimAdresseZerlegen(adresse);
|
|
228
|
+
const normalisiert = zerlegt === undefined ? adresse : heimAdresseAlsText(zerlegt);
|
|
229
|
+
const zustand = await this.p.zustand.lesen();
|
|
230
|
+
const eintrag = zustand.harnessHeime.find((h) => h.harness === harnessName);
|
|
231
|
+
if (eintrag === undefined || !eintrag.zusaetzliche.some((h) => h.adresse === normalisiert))
|
|
232
|
+
throw new HeimUnbekannt(harnessName, normalisiert);
|
|
233
|
+
eintrag.zusaetzliche = eintrag.zusaetzliche.filter((h) => h.adresse !== normalisiert);
|
|
234
|
+
Anwendungsfaelle.heimeAufraeumen(zustand);
|
|
235
|
+
await this.p.zustand.schreiben(zustand);
|
|
236
|
+
}
|
|
237
|
+
/** Die installierten WSL-Distros, für die Auswahl beim Hinzufügen (E78, F87). */
|
|
238
|
+
async wslDistros() {
|
|
239
|
+
if (this.p.umgebung.plattform() !== 'win32')
|
|
240
|
+
return [];
|
|
241
|
+
return await this.p.wsl.distros().catch(() => []);
|
|
242
|
+
}
|
|
106
243
|
/**
|
|
107
244
|
* Harness-Typen, die hmgr verwaltet (E38, GRILL.md Runde 2). Nur für sie richtet hmgr beim
|
|
108
245
|
* Arbeitsverzeichnis-Hinzufügen und beim Abgleich etwas ein. Standard: nur claude-code.
|
|
@@ -291,6 +428,8 @@ export class Anwendungsfaelle {
|
|
|
291
428
|
if (!kannMcp(harness))
|
|
292
429
|
continue;
|
|
293
430
|
const ebenen = harness.mcp.ebenen();
|
|
431
|
+
// Nur das primäre Homedir (E78): die claude-CLI dieses Rechners bedient die Konfiguration
|
|
432
|
+
// einer WSL-Distro nicht, dort gibt es keine MCP-Server zu verwalten.
|
|
294
433
|
if (ebenen.includes('home'))
|
|
295
434
|
ziele.push({ harness: harness.name, ort: { art: 'home' } });
|
|
296
435
|
if (ebenen.includes('profil') && kannProfile(harness))
|
|
@@ -354,9 +493,7 @@ export class Anwendungsfaelle {
|
|
|
354
493
|
async saeubern(harnessName) {
|
|
355
494
|
const harness = this.harness(harnessName); // wirft HarnessTypUnbekannt bei unbekanntem Namen
|
|
356
495
|
const zustand = await this.p.zustand.lesen();
|
|
357
|
-
const ziele = [];
|
|
358
|
-
if (harness.orte().includes('home'))
|
|
359
|
-
ziele.push({ harness: harness.name, ort: { art: 'home' } });
|
|
496
|
+
const ziele = [...this.homeZiele(harness)];
|
|
360
497
|
if (harness.orte().includes('profil') && kannProfile(harness))
|
|
361
498
|
for (const name of await harness.profile())
|
|
362
499
|
ziele.push({ harness: harness.name, ort: { art: 'profil', name } });
|
|
@@ -887,7 +1024,7 @@ export class Anwendungsfaelle {
|
|
|
887
1024
|
* legt der Aufruf seine Links wie mit `--local` und trifft die Sperre aus E57 nie.
|
|
888
1025
|
*/
|
|
889
1026
|
async bundleInstallieren(args) {
|
|
890
|
-
const harness = await this.
|
|
1027
|
+
const harness = await this.harnessAktivGeprueft(args.ziel.harness);
|
|
891
1028
|
if (!harness.orte().includes(args.ziel.ort.art))
|
|
892
1029
|
throw new OrtNichtUnterstuetzt(harness.name, args.ziel.ort.art);
|
|
893
1030
|
const bundle = await this.bundleFinden(args.bundle, args.quelle);
|
|
@@ -1160,7 +1297,7 @@ export class Anwendungsfaelle {
|
|
|
1160
1297
|
* hätte sie keinen Platz und der Aufruf schlägt fehl.
|
|
1161
1298
|
*/
|
|
1162
1299
|
async linkLegen(args) {
|
|
1163
|
-
const harness = await this.
|
|
1300
|
+
const harness = await this.harnessAktivGeprueft(args.ziel.harness);
|
|
1164
1301
|
if (!harness.orte().includes(args.ziel.ort.art))
|
|
1165
1302
|
throw new OrtNichtUnterstuetzt(harness.name, args.ziel.ort.art);
|
|
1166
1303
|
const version = args.version;
|
|
@@ -1586,9 +1723,15 @@ export class Anwendungsfaelle {
|
|
|
1586
1723
|
* Checkouts, deren Quelle der Zustand nicht führt, bleiben ganz außen vor (E54): sie werden weder
|
|
1587
1724
|
* geholt noch als verwaist zum Löschen angeboten. Eine vergessene Quelle ist vergessen, ihre
|
|
1588
1725
|
* Checkouts und Links liegen unangetastet, bis die Quelle wieder hinzukommt.
|
|
1726
|
+
*
|
|
1727
|
+
* Schreibt am Ende einen Verlaufseintrag (E80), aber nur, wenn der Lauf mindestens ein Ereignis
|
|
1728
|
+
* ergeben hat: unveränderte oder übersprungene Checkouts und Bundles ohne Änderung erzeugen
|
|
1729
|
+
* nichts, ein leerer Lauf bleibt ganz aus dem Changelog. `ausloeser` steht mit im Eintrag, damit
|
|
1730
|
+
* der Verlauf zeigt, ob ein Fund vom automatischen Lauf, einem Klick oder der CLI kam.
|
|
1589
1731
|
*/
|
|
1590
1732
|
async aktualisieren(args) {
|
|
1591
1733
|
const rueckfragen = args?.rueckfragen ?? true;
|
|
1734
|
+
const ausloeser = args?.ausloeser ?? 'cli';
|
|
1592
1735
|
const bekannt = new Map((await this.p.zustand.lesen()).quellen.map((q) => [quelleSchluessel(q), q]));
|
|
1593
1736
|
const checkouts = [];
|
|
1594
1737
|
for (const checkout of await this.p.quellen.checkouts()) {
|
|
@@ -1623,12 +1766,97 @@ export class Anwendungsfaelle {
|
|
|
1623
1766
|
// danach steht fest, in welchen Checkout noch ein Link zeigt. Ein Fehlschlag je Bundle steckt
|
|
1624
1767
|
// in seinem Eintrag, er bricht den Lauf nicht ab — wie eine unerreichbare Quelle (E51).
|
|
1625
1768
|
const bundles = await this.bundleAbgleich();
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1769
|
+
const aufraeumen = await this.verwaisteAufraeumen(checkouts, rueckfragen);
|
|
1770
|
+
await this.verlaufVermerken(ausloeser, this.aktualisierungsEreignisse(checkouts, bundles, aufraeumen));
|
|
1771
|
+
return { checkouts, pluginHarnesses, bundles, aufraeumen };
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* Ereignisse eines Aktualisieren-Laufs für den Verlauf (E80): Plugins bleiben draußen, weil sich
|
|
1775
|
+
* eine Änderung dort nicht messen lässt (`claude plugin marketplace update` liefert keine
|
|
1776
|
+
* Auskunft) — die bisherige Zeile „Plugins aktualisiert" war deshalb geschönt.
|
|
1777
|
+
*/
|
|
1778
|
+
aktualisierungsEreignisse(checkouts, bundles, aufraeumen) {
|
|
1779
|
+
const ereignisse = [];
|
|
1780
|
+
for (const { checkout, aktualisierung, geaenderteSkills } of checkouts) {
|
|
1781
|
+
if (aktualisierung.ergebnis === 'aktualisiert')
|
|
1782
|
+
ereignisse.push({
|
|
1783
|
+
art: 'checkout',
|
|
1784
|
+
quelle: quelleAlsText(checkout.quelle),
|
|
1785
|
+
ref: checkout.ref,
|
|
1786
|
+
von: aktualisierung.von,
|
|
1787
|
+
nach: aktualisierung.nach,
|
|
1788
|
+
skills: geaenderteSkills,
|
|
1789
|
+
});
|
|
1790
|
+
else if (aktualisierung.ergebnis === 'fehlgeschlagen')
|
|
1791
|
+
ereignisse.push({
|
|
1792
|
+
art: 'fehler',
|
|
1793
|
+
betrifft: `${quelleAlsText(checkout.quelle)} @ ${checkout.ref}`,
|
|
1794
|
+
fehler: aktualisierung.fehler,
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
for (const eintrag of bundles) {
|
|
1798
|
+
if (eintrag.ergebnis === 'fehlgeschlagen')
|
|
1799
|
+
ereignisse.push({ art: 'fehler', betrifft: eintrag.bundle, fehler: eintrag.fehler ?? '' });
|
|
1800
|
+
else if (eintrag.gelegt.length > 0 || eintrag.entfernt.length > 0)
|
|
1801
|
+
ereignisse.push({
|
|
1802
|
+
art: 'bundle',
|
|
1803
|
+
bundle: eintrag.bundle,
|
|
1804
|
+
ziel: eintrag.ziel,
|
|
1805
|
+
gelegt: eintrag.gelegt,
|
|
1806
|
+
entfernt: eintrag.entfernt,
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
for (const verwaist of aufraeumen.verwaiste)
|
|
1810
|
+
if (verwaist.geloescht)
|
|
1811
|
+
ereignisse.push({ art: 'aufgeraeumt', checkout: `${quelleAlsText(verwaist.checkout.quelle)} @ ${verwaist.checkout.ref}` });
|
|
1812
|
+
return ereignisse;
|
|
1813
|
+
}
|
|
1814
|
+
/** Neuen Eintrag vorn anfügen, nur wenn Ereignisse da sind (E80). Kappen übernimmt der Adapter. */
|
|
1815
|
+
async verlaufVermerken(ausloeser, ereignisse) {
|
|
1816
|
+
if (ereignisse.length === 0)
|
|
1817
|
+
return;
|
|
1818
|
+
const daten = await this.p.verlauf.lesen();
|
|
1819
|
+
const eintrag = { zeit: new Date().toISOString(), ausloeser, ereignisse };
|
|
1820
|
+
await this.p.verlauf.schreiben({ gemerkt: daten.gemerkt, eintraege: [eintrag, ...daten.eintraege] });
|
|
1821
|
+
}
|
|
1822
|
+
/** Der Verlauf, neueste zuerst (E80). */
|
|
1823
|
+
async verlauf() {
|
|
1824
|
+
return (await this.p.verlauf.lesen()).eintraege;
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Merkt die installierte Version eines hmgr-Pakets (GUI oder CLI) und schreibt bei einem
|
|
1828
|
+
* Versionssprung einen Verlaufseintrag (E80). Der erste Aufruf ohne gemerkte Version merkt nur,
|
|
1829
|
+
* ohne Ereignis — sonst bekäme jede Erstinstallation ein irreführendes „installiert".
|
|
1830
|
+
*/
|
|
1831
|
+
async versionsSprungVermerken(paket, version, ausloeser) {
|
|
1832
|
+
const daten = await this.p.verlauf.lesen();
|
|
1833
|
+
const bisher = paket === 'gui' ? daten.gemerkt.gui : daten.gemerkt.cli;
|
|
1834
|
+
if (bisher === version)
|
|
1835
|
+
return;
|
|
1836
|
+
const gemerkt = { ...daten.gemerkt, [paket]: version };
|
|
1837
|
+
if (bisher === undefined) {
|
|
1838
|
+
await this.p.verlauf.schreiben({ gemerkt, eintraege: daten.eintraege });
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
const eintrag = {
|
|
1842
|
+
zeit: new Date().toISOString(),
|
|
1843
|
+
ausloeser,
|
|
1844
|
+
ereignisse: [{ art: 'hmgr-installiert', paket, version }],
|
|
1631
1845
|
};
|
|
1846
|
+
await this.p.verlauf.schreiben({ gemerkt, eintraege: [eintrag, ...daten.eintraege] });
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Merkt ein hmgr-eigenes Ereignis (GUI-Update geladen oder CLI-Update auf npm verfügbar) einmal
|
|
1850
|
+
* je neuer Version (E80): dieselbe Version taucht nicht in jedem Lauf erneut im Changelog auf.
|
|
1851
|
+
*/
|
|
1852
|
+
async hmgrEreignisVermerken(ereignis, ausloeser) {
|
|
1853
|
+
const daten = await this.p.verlauf.lesen();
|
|
1854
|
+
const feld = ereignis.art === 'gui-update-geladen' ? 'guiGeladen' : 'cliVerfuegbar';
|
|
1855
|
+
if (daten.gemerkt[feld] === ereignis.version)
|
|
1856
|
+
return;
|
|
1857
|
+
const gemerkt = { ...daten.gemerkt, [feld]: ereignis.version };
|
|
1858
|
+
const eintrag = { zeit: new Date().toISOString(), ausloeser, ereignisse: [ereignis] };
|
|
1859
|
+
await this.p.verlauf.schreiben({ gemerkt, eintraege: [eintrag, ...daten.eintraege] });
|
|
1632
1860
|
}
|
|
1633
1861
|
/**
|
|
1634
1862
|
* Welche Skills ein aktualisierter Checkout mitgebracht hat (E70). Die geänderten Pfade kommen
|
|
@@ -2005,9 +2233,7 @@ export class Anwendungsfaelle {
|
|
|
2005
2233
|
};
|
|
2006
2234
|
const ergebnis = [];
|
|
2007
2235
|
for (const harness of await this.vorhandeneHarnesses()) {
|
|
2008
|
-
const ziele = [];
|
|
2009
|
-
if (harness.orte().includes('home'))
|
|
2010
|
-
ziele.push({ harness: harness.name, ort: { art: 'home' } });
|
|
2236
|
+
const ziele = [...this.homeZiele(harness)];
|
|
2011
2237
|
if (harness.orte().includes('profil') && kannProfile(harness))
|
|
2012
2238
|
for (const name of await harness.profile())
|
|
2013
2239
|
ziele.push({ harness: harness.name, ort: { art: 'profil', name } });
|
|
@@ -2017,7 +2243,9 @@ export class Anwendungsfaelle {
|
|
|
2017
2243
|
for (const ziel of ziele) {
|
|
2018
2244
|
const verzeichnis = harness.zielVerzeichnis(ziel.ort);
|
|
2019
2245
|
const eintraege = await this.p.ziele.eintraege(ziel, verzeichnis).catch(() => []);
|
|
2020
|
-
|
|
2246
|
+
// Plugins nur am primären Homedir (E78): die claude-CLI dieses Rechners kennt die
|
|
2247
|
+
// Plugin-Konfiguration einer WSL-Distro nicht, ihre user-Liste gehörte zum Windows-Home.
|
|
2248
|
+
const plugins = kannPlugins(harness) && !(ziel.ort.art === 'home' && ziel.ort.heim !== undefined)
|
|
2021
2249
|
? await harness.plugins.installierte(ziel).catch(() => [])
|
|
2022
2250
|
: undefined;
|
|
2023
2251
|
const zeiger = await this.zeigerFuerZiel(ziel);
|
package/dist/kern/modell.js
CHANGED
|
@@ -50,9 +50,108 @@ export function marketplaceUrl(quelle) {
|
|
|
50
50
|
}
|
|
51
51
|
export function zielSchluessel(ziel) {
|
|
52
52
|
const o = ziel.ort;
|
|
53
|
-
const ort = o.art === 'home'
|
|
53
|
+
const ort = o.art === 'home'
|
|
54
|
+
? o.heim === undefined
|
|
55
|
+
? 'home'
|
|
56
|
+
: `home:${o.heim}`
|
|
57
|
+
: o.art === 'profil'
|
|
58
|
+
? `profil:${o.name}`
|
|
59
|
+
: `dir:${o.pfad}`;
|
|
54
60
|
return `${ziel.harness}/${ort}`;
|
|
55
61
|
}
|
|
62
|
+
export function heimAdresseZerlegen(text) {
|
|
63
|
+
const roh = text.trim();
|
|
64
|
+
if (!roh.toLowerCase().startsWith('wsl:'))
|
|
65
|
+
return undefined;
|
|
66
|
+
const distro = roh.slice('wsl:'.length).trim();
|
|
67
|
+
if (distro.length === 0)
|
|
68
|
+
return undefined;
|
|
69
|
+
return { art: 'wsl', distro };
|
|
70
|
+
}
|
|
71
|
+
/** Die Adresse eines zusätzlichen Heims in der Schreibweise, die hmgr speichert und anzeigt. */
|
|
72
|
+
export function heimAdresseAlsText(adresse) {
|
|
73
|
+
return `wsl:${adresse.distro}`;
|
|
74
|
+
}
|
|
75
|
+
/** Ein Text, der keine Heim-Adresse ist (E78). */
|
|
76
|
+
export class HeimAdresseUngueltig extends Error {
|
|
77
|
+
adresse;
|
|
78
|
+
constructor(adresse) {
|
|
79
|
+
super(`„${adresse}" ist keine Adresse für ein zusätzliches Homedir. Erwartet wird wsl:<distro>, etwa wsl:Ubuntu.`);
|
|
80
|
+
this.adresse = adresse;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Ein `wsl:`-Heim auf einer Plattform ohne WSL (E78, Entscheidung 7). Steht so eines im Zustand,
|
|
85
|
+
* weil er von einem Windows-Rechner stammt, wird sein Ziel hier nicht angezeigt und nicht bedient;
|
|
86
|
+
* Hinzufügen bricht mit dieser Meldung ab.
|
|
87
|
+
*/
|
|
88
|
+
export class HeimNurUnterWindows extends Error {
|
|
89
|
+
adresse;
|
|
90
|
+
plattform;
|
|
91
|
+
constructor(adresse, plattform) {
|
|
92
|
+
super(`Ein Homedir in WSL („${adresse}") gibt es nur unter Windows, nicht unter ${plattform}.`);
|
|
93
|
+
this.adresse = adresse;
|
|
94
|
+
this.plattform = plattform;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Ein Harness-Typ ohne die Fähigkeit „zusätzliche Homedirs" (E78): heute nur claude-code hat sie. */
|
|
98
|
+
export class HeimFaehigkeitFehlt extends Error {
|
|
99
|
+
harness;
|
|
100
|
+
constructor(harness) {
|
|
101
|
+
super(`Harness ${harness} kennt keine zusätzlichen Homedirs`);
|
|
102
|
+
this.harness = harness;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Dieselbe Adresse ist für diesen Harness schon eingetragen (E78). */
|
|
106
|
+
export class HeimBereitsVorhanden extends Error {
|
|
107
|
+
harness;
|
|
108
|
+
adresse;
|
|
109
|
+
constructor(harness, adresse) {
|
|
110
|
+
super(`${harness} hat schon ein Homedir „${adresse}"`);
|
|
111
|
+
this.harness = harness;
|
|
112
|
+
this.adresse = adresse;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Eine Adresse, die für diesen Harness nicht eingetragen ist (E78): Entfernen kennt nur eingetragene. */
|
|
116
|
+
export class HeimUnbekannt extends Error {
|
|
117
|
+
harness;
|
|
118
|
+
adresse;
|
|
119
|
+
constructor(harness, adresse) {
|
|
120
|
+
super(`${harness} hat kein Homedir „${adresse}"`);
|
|
121
|
+
this.harness = harness;
|
|
122
|
+
this.adresse = adresse;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Etwas, das nur am primären Homedir geht, wurde für ein zusätzliches verlangt (E78): die
|
|
127
|
+
* claude-CLI dieses Rechners bedient das Windows-Home, nicht das einer WSL-Distro — MCP-Server und
|
|
128
|
+
* Plugins lassen sich dort deshalb nicht verwalten. Skill-Links legt hmgr dort selbst, ohne CLI.
|
|
129
|
+
*/
|
|
130
|
+
export class HeimOhneCliZugriff extends Error {
|
|
131
|
+
harness;
|
|
132
|
+
adresse;
|
|
133
|
+
constructor(harness, adresse, was) {
|
|
134
|
+
super(`${harness} kann ${was} im Homedir „${adresse}" nicht verwalten: die claude-CLI dieses Rechners bedient nur ` +
|
|
135
|
+
'das primäre Homedir. Skill-Links legt hmgr dort selbst.');
|
|
136
|
+
this.harness = harness;
|
|
137
|
+
this.adresse = adresse;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Die Bridge in eine WSL-Distro hat nicht geantwortet (E78): die Distro gibt es nicht, sie startet
|
|
142
|
+
* nicht, oder wsl.exe fehlt. Die Ausgabe von wsl.exe steht mit in der Meldung — sie nennt den Grund
|
|
143
|
+
* genauer, als hmgr ihn raten könnte.
|
|
144
|
+
*/
|
|
145
|
+
export class WslBrueckeFehlgeschlagen extends Error {
|
|
146
|
+
distro;
|
|
147
|
+
ausgabe;
|
|
148
|
+
constructor(distro, ausgabe) {
|
|
149
|
+
const zusatz = ausgabe.trim().length === 0 ? '' : `: ${ausgabe.trim()}`;
|
|
150
|
+
super(`wsl.exe -d ${distro} ließ sich nicht ausführen${zusatz}`);
|
|
151
|
+
this.distro = distro;
|
|
152
|
+
this.ausgabe = ausgabe;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
56
155
|
/** Wie eine Quelle geschrieben steht: ihre URL oder ihr Pfad. Für Ausgaben, nicht für Vergleiche. */
|
|
57
156
|
export function quelleAlsText(quelle) {
|
|
58
157
|
return quelle.art === 'git' ? quelle.url : quelle.pfad;
|
|
@@ -286,6 +385,18 @@ export class HarnessNichtVorhanden extends Error {
|
|
|
286
385
|
this.harness = harness;
|
|
287
386
|
}
|
|
288
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Ziel eines deaktivierten Harness beim Anlegen eines Links (E76). Seit E74 zeigt keine Ansicht
|
|
390
|
+
* solche Ziele, ein dort neu gelegter Link wäre unsichtbarer Zustand. Bestehende Links bleiben
|
|
391
|
+
* unberührt, nur das Neuanlegen ist gesperrt.
|
|
392
|
+
*/
|
|
393
|
+
export class HarnessInaktiv extends Error {
|
|
394
|
+
harness;
|
|
395
|
+
constructor(harness) {
|
|
396
|
+
super(`Harness ${harness} ist deaktiviert, hmgr legt dort keine Links an`);
|
|
397
|
+
this.harness = harness;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
289
400
|
/** Ein Name, den kein Harness-Typ trägt: Aktivieren, Deaktivieren und Säubern kennen nur bekannte Typen (E38). */
|
|
290
401
|
export class HarnessTypUnbekannt extends Error {
|
|
291
402
|
harness;
|
package/dist/kern/version.js
CHANGED
|
@@ -78,6 +78,19 @@ export function vergleichen(a, b) {
|
|
|
78
78
|
}
|
|
79
79
|
return 0;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Ist `neu` eine höhere Version als `alt`? Für den Update-Hinweis (E77): die von npm gemeldete
|
|
83
|
+
* Version gegen die installierte. Verglichen wird numerisch je Stelle, nie als Text — sonst stünde
|
|
84
|
+
* „0.0.10" unter „0.0.9". Was sich nicht als Version lesen lässt, gilt als nicht neuer: ein
|
|
85
|
+
* unverständlicher Text darf nie zu einer Meldung führen, die zum Update auffordert.
|
|
86
|
+
*/
|
|
87
|
+
export function versionIstNeuer(neu, alt) {
|
|
88
|
+
const a = versionLesen(neu.trim());
|
|
89
|
+
const b = versionLesen(alt.trim());
|
|
90
|
+
if (a === null || b === null)
|
|
91
|
+
return false;
|
|
92
|
+
return vergleichen(a, b) > 0;
|
|
93
|
+
}
|
|
81
94
|
/**
|
|
82
95
|
* Tilde: „`~1.2` ist gleichbedeutend mit `>=1.2 <2.0.0`, `~1.2.3` mit `>=1.2.3 <1.3.0`", die
|
|
83
96
|
* am weitesten rechts genannte Stelle darf wachsen (getcomposer.org, Writing Version Constraints).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ports/harness.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Ob dieser Harness-Typ zusätzliche Homedirs bedienen kann (E78). Ohne die Fähigkeit: nein. */
|
|
2
|
+
export function kannZusaetzlicheHeime(h) {
|
|
3
|
+
return h.zusaetzlicheHeime?.() === true;
|
|
4
|
+
}
|
|
1
5
|
export function kannProfile(h) {
|
|
2
6
|
return typeof h.profile === 'function';
|
|
3
7
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|