hmgr 0.0.50 → 0.0.52
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 +3 -0
- package/dist/adapter/claude-code.js +3 -0
- package/dist/adapter/hermes.js +3 -0
- package/dist/aenderungen.json +6 -0
- package/dist/cli/hilfe.js +13 -1
- package/dist/cli/main.js +10 -4
- package/dist/gui/main.js +9 -0
- package/dist/gui/renderer/renderer.js +10 -4
- package/dist/kern/anwendungsfaelle.js +93 -15
- package/dist/kern/modell.js +16 -2
- package/package.json +1 -1
package/dist/adapter/agents.js
CHANGED
|
@@ -51,6 +51,9 @@ export class Agents {
|
|
|
51
51
|
if (ort.art === 'arbeitsverzeichnis' && (await istGitRepo(ort.pfad)))
|
|
52
52
|
await gitignoreEintragen(ort.pfad, GITIGNORE_ZEILE);
|
|
53
53
|
}
|
|
54
|
+
quellOrdner() {
|
|
55
|
+
return `${AGENTS_VERZEICHNIS}/${SKILLS_ORDNER}`;
|
|
56
|
+
}
|
|
54
57
|
folgtLinks() {
|
|
55
58
|
return true;
|
|
56
59
|
}
|
|
@@ -681,6 +681,9 @@ export class ClaudeCode {
|
|
|
681
681
|
if (ort.art === 'arbeitsverzeichnis' && (await istGitRepo(ort.pfad)))
|
|
682
682
|
await gitignoreEintragen(ort.pfad, GITIGNORE_ZEILE);
|
|
683
683
|
}
|
|
684
|
+
quellOrdner() {
|
|
685
|
+
return `${CLAUDE_VERZEICHNIS}/${SKILLS_ORDNER}`;
|
|
686
|
+
}
|
|
684
687
|
folgtLinks() {
|
|
685
688
|
return true;
|
|
686
689
|
}
|
package/dist/adapter/hermes.js
CHANGED
package/dist/aenderungen.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"fassung": 1,
|
|
3
3
|
"versionen": {
|
|
4
|
+
"0.0.52": [
|
|
5
|
+
"Liegt ein Skill in einem Repo in mehreren Fassungen, je Werkzeug eine (etwa `.claude/skills` und `.hermes/skills`), verlinkt hmgr jetzt für jeden Harness die passende Fassung. Gibt es keine passende, nennt die Meldung die Pfade, und `hmgr link … --pfad` wählt einen aus."
|
|
6
|
+
],
|
|
7
|
+
"0.0.51": [
|
|
8
|
+
"Ein gelöschter Projektordner, an dem noch ein Bundle hing, wird beim Aktualisieren nicht mehr neu angelegt. `hmgr doctor` findet solche Ordner jetzt und bietet an, sie samt ihren Bundles zu vergessen."
|
|
9
|
+
],
|
|
4
10
|
"0.0.50": [
|
|
5
11
|
"Die Suche in hmgr findet jetzt auch Ordner und Skills mit Umlauten, etwa „döring“. Unter macOS legt hmgr denselben Ordner nicht mehr doppelt an, wenn er einmal aus dem Finder und einmal getippt hinzugefügt wird."
|
|
6
12
|
],
|
package/dist/cli/hilfe.js
CHANGED
|
@@ -147,6 +147,7 @@ Schalter:
|
|
|
147
147
|
--ort <ort> home, wsl:<distro>, dir:<pfad> oder profil:<name>, Standard home. profil
|
|
148
148
|
kennt nur hermes, wsl:<distro> nur claude-code unter Windows (E78)
|
|
149
149
|
--quelle <teil> Teilstring der Quelle, wenn der Skill-Name in mehreren Quellen vorkommt
|
|
150
|
+
--pfad <skill-pfad> Skill-Ordner in der Quelle, wenn der Name dort mehrfach vorkommt (E121)
|
|
150
151
|
--version <spanne> Versionseinschränkung nach Composer-Regeln (etwa ^1.2) oder ein Tag- oder
|
|
151
152
|
Branchname. Nur mit Zeiger, also dir:<pfad> ohne --local (E33)
|
|
152
153
|
--composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
|
|
@@ -155,11 +156,15 @@ Schalter:
|
|
|
155
156
|
|
|
156
157
|
Beispiele:
|
|
157
158
|
hmgr link netzwerk
|
|
159
|
+
hmgr link impeccable --pfad plugin/skills/impeccable
|
|
158
160
|
hmgr link netzwerk --harness hermes --ort profil:researcher
|
|
159
161
|
hmgr link tq-schema --ort dir:. --version ^2.0
|
|
160
162
|
|
|
161
163
|
Häufige Fälle:
|
|
162
164
|
„liegt in mehreren Quellen": mit --quelle <teilstring der URL oder des Pfads> entscheiden.
|
|
165
|
+
„liegt in der Quelle … mehrfach": mit --pfad einen der genannten Pfade wählen. Liegt genau
|
|
166
|
+
eine Fassung im Skill-Ordner des Harness (claude-code .claude/skills, hermes .hermes/skills,
|
|
167
|
+
agents .agents/skills), nimmt hmgr sie von selbst und fragt nicht nach.
|
|
163
168
|
„liegt bereits ein fremder Eintrag": --ersetzen setzen oder vorher hmgr adopt <name>.
|
|
164
169
|
„gibt es keinen Zeiger": --version braucht ein Arbeitsverzeichnis mit composer.json und kein
|
|
165
170
|
--local. Ohne Zeiger den Link ohne --version legen.
|
|
@@ -315,6 +320,11 @@ Liegt am Ziel schon derselbe Skill aus derselben Quelle als hmgr-Link, wird er a
|
|
|
315
320
|
übernommen: aus Bestand wird ein Bundle, ohne erst Links von Hand zu entfernen. Alles andere unter
|
|
316
321
|
demselben Namen ist ein Konflikt und wird genannt.
|
|
317
322
|
|
|
323
|
+
Ein Bundle in einem Arbeitsverzeichnis, das es nicht mehr gibt, legen update und sync nicht wieder
|
|
324
|
+
an: update übergeht es still, sync --ort meldet es als fehlgeschlagen. hmgr doctor bietet an, das
|
|
325
|
+
Arbeitsverzeichnis samt seinen Bundle-Installationen zu vergessen. bundle add legt ein fehlendes
|
|
326
|
+
Verzeichnis weiterhin an, bundle remove trägt kein Arbeitsverzeichnis ein.
|
|
327
|
+
|
|
318
328
|
hmgr unlink <skill> auf ein einzelnes Mitglied nimmt es aus dem Bundle heraus: nur dieser Link
|
|
319
329
|
verschwindet, das Bundle bleibt im Ziel und merkt sich den Skill als herausgenommen. sync und
|
|
320
330
|
update legen ihn nicht wieder an, neue Mitglieder der Definition ergänzen sie weiter. Zurück kommt
|
|
@@ -617,7 +627,9 @@ Prüft, ob jedes gemerkte Arbeitsverzeichnis (hmgr dir add) noch auf der Platte
|
|
|
617
627
|
umbenanntes oder woanders neu geklontes Projekt hinterlässt sonst für immer einen toten Pfad im
|
|
618
628
|
Zustand, den nur hmgr dir forget von Hand entfernt. Bei einem Fund fragt doctor wie hmgr update
|
|
619
629
|
einmal für alle Funde, ob sie vergessen werden sollen; ohne Bestätigung bleiben sie gemeldet, aber
|
|
620
|
-
gemerkt.
|
|
630
|
+
gemerkt. Geprüft werden auch Arbeitsverzeichnisse, an denen nur Bundles oder Bindungen hängen
|
|
631
|
+
(E119); vergessen nimmt dann auch deren Bundle-Installationen aus dem Zustand. Anders als update
|
|
632
|
+
holt doctor nichts aus dem Netz.
|
|
621
633
|
|
|
622
634
|
Dazu prüft doctor Dateien, die eine Quelle mit SOPS verschlüsselt (.gitattributes mit
|
|
623
635
|
filter=sops_dotenv, sops_yaml oder sops_generic) und die noch verschlüsselt im Checkout liegen,
|
package/dist/cli/main.js
CHANGED
|
@@ -121,7 +121,7 @@ const SCHALTER = {
|
|
|
121
121
|
source: { werte: ['name'], flaggen: [] },
|
|
122
122
|
dir: { werte: ['composer'], flaggen: [] },
|
|
123
123
|
link: {
|
|
124
|
-
werte: ['harness', 'ort', 'composer', 'quelle', 'version'],
|
|
124
|
+
werte: ['harness', 'ort', 'composer', 'quelle', 'version', 'pfad'],
|
|
125
125
|
flaggen: ['local', 'lokal', 'ersetzen'],
|
|
126
126
|
},
|
|
127
127
|
unlink: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal'] },
|
|
@@ -483,7 +483,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
483
483
|
'hmgr status zeigt die verlinkten Skills, hmgr source add <url|pfad> die Skills einer Quelle.');
|
|
484
484
|
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort'), ports.umgebung.plattform()) };
|
|
485
485
|
const lokal = flaggen.has('local') || flaggen.has('lokal');
|
|
486
|
-
|
|
486
|
+
// E119: unlink trägt ein Verzeichnis, das es nicht mehr gibt, nicht neu ein.
|
|
487
|
+
if (ziel.ort.art === 'arbeitsverzeichnis' &&
|
|
488
|
+
(kommando === 'link' || (await ports.zeiger.verzeichnisExistiert(ziel.ort.pfad)))) {
|
|
487
489
|
// Verzeichnis merken und binden, damit status es wiederfindet und der Zeiger bekannt ist (E21).
|
|
488
490
|
const composer = werte.get('composer');
|
|
489
491
|
const verz = composer === undefined
|
|
@@ -505,10 +507,12 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
505
507
|
}
|
|
506
508
|
const quelle = werte.get('quelle');
|
|
507
509
|
const version = werte.get('version');
|
|
510
|
+
const pfad = werte.get('pfad');
|
|
508
511
|
const link = await anwendungsfaelle.linkLegen({
|
|
509
512
|
skill,
|
|
510
513
|
ziel,
|
|
511
514
|
...(quelle === undefined ? {} : { quelle }),
|
|
515
|
+
...(pfad === undefined ? {} : { pfad }),
|
|
512
516
|
...(version === undefined ? {} : { version }),
|
|
513
517
|
ersetzen: flaggen.has('ersetzen'),
|
|
514
518
|
lokal,
|
|
@@ -657,7 +661,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
657
661
|
if (name === undefined && unterkommando !== 'sync')
|
|
658
662
|
throw new Error(`bundle ${unterkommando} braucht einen Bundle-Namen. hmgr bundle list zeigt die Bundles der bekannten Quellen.`);
|
|
659
663
|
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort'), ports.umgebung.plattform()) };
|
|
660
|
-
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
664
|
+
if (unterkommando !== 'remove' && ziel.ort.art === 'arbeitsverzeichnis') {
|
|
661
665
|
// Wie bei link: Verzeichnis merken und binden, damit status es wiederfindet (E21).
|
|
662
666
|
const composer = werte.get('composer');
|
|
663
667
|
const verz = composer === undefined
|
|
@@ -1318,7 +1322,9 @@ function istModellFehler(fehler) {
|
|
|
1318
1322
|
*/
|
|
1319
1323
|
function naechsterSchritt(fehler) {
|
|
1320
1324
|
if (fehler instanceof SkillMehrdeutig)
|
|
1321
|
-
return
|
|
1325
|
+
return fehler.nurEineQuelle
|
|
1326
|
+
? `Fassung wählen: hmgr link ${fehler.name} --pfad <einer der genannten Pfade>`
|
|
1327
|
+
: `Quelle eingrenzen: hmgr link ${fehler.name} --quelle <teilstring einer der genannten Quellen>`;
|
|
1322
1328
|
if (fehler instanceof SkillUnbekannt)
|
|
1323
1329
|
return 'Erst die Quelle hinzufügen: hmgr source add <url|pfad>. hmgr source list zeigt die bekannten Quellen.';
|
|
1324
1330
|
if (fehler instanceof NameBelegt)
|
package/dist/gui/main.js
CHANGED
|
@@ -228,6 +228,7 @@ function registriereIpc() {
|
|
|
228
228
|
...(skill.eigenerKontext === undefined ? {} : { eigenerKontext: skill.eigenerKontext }),
|
|
229
229
|
...(skill.argumentHinweis === undefined ? {} : { argumentHinweis: skill.argumentHinweis }),
|
|
230
230
|
...(skill.geaendert === undefined ? {} : { geaendert: skill.geaendert }),
|
|
231
|
+
...(skill.jeHarness === undefined ? {} : { jeHarness: skill.jeHarness }),
|
|
231
232
|
quelleText: quelleAlsText(eintrag.quelle),
|
|
232
233
|
quelleName: quelleName(eintrag.quelle),
|
|
233
234
|
quelleSchluessel: quelleSchluessel(eintrag.quelle),
|
|
@@ -886,6 +887,8 @@ function fensterOeffnen() {
|
|
|
886
887
|
var dskBundleBoxSichtbar = false;
|
|
887
888
|
var dskBundleText = null;
|
|
888
889
|
var dskBundleZuVorher = null, dskBundleKoerperText = null;
|
|
890
|
+
// E121: ein Name mit mehreren Fassungen in der Quelle steht einmal, mit Vermerk statt Pfad.
|
|
891
|
+
var dskMehrfachAnzahl = null, dskMehrfachText = null;
|
|
889
892
|
// Aufklappbereich je Zeile (E92): zu vor dem Klick, danach sichtbar mit der ungekürzten
|
|
890
893
|
// Beschreibung; die Checkbox bleibt unberührt, der Dialog offen.
|
|
891
894
|
var dskKoerperVorher = null;
|
|
@@ -920,6 +923,10 @@ function fensterOeffnen() {
|
|
|
920
923
|
dskAufklapp?.click();
|
|
921
924
|
dskKoerper?.querySelector('p')?.click();
|
|
922
925
|
dskAufklappFreiAngehakt = dskEingabe?.checked ?? null;
|
|
926
|
+
var dskMehrfach = Array.from(document.querySelectorAll('#dsk-liste .wahlzeile'))
|
|
927
|
+
.filter((z) => z.querySelector('b')?.textContent === 'zmehrfach');
|
|
928
|
+
dskMehrfachAnzahl = dskMehrfach.length;
|
|
929
|
+
dskMehrfachText = dskMehrfach[0]?.querySelector('.mono')?.textContent ?? null;
|
|
923
930
|
dskBundleBoxSichtbar = document.getElementById('dsk-bundlebox')?.hidden === false;
|
|
924
931
|
dskBundleText = document.querySelector('#dsk-bundles .wahlzeile')?.textContent
|
|
925
932
|
?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
@@ -1283,6 +1290,8 @@ function fensterOeffnen() {
|
|
|
1283
1290
|
katalogDetails,
|
|
1284
1291
|
katalogMarkenWarnend,
|
|
1285
1292
|
dskMarken,
|
|
1293
|
+
dskMehrfachAnzahl,
|
|
1294
|
+
dskMehrfachText,
|
|
1286
1295
|
dskKoerperVorher,
|
|
1287
1296
|
dskKoerperNachher,
|
|
1288
1297
|
dskKoerperText,
|
|
@@ -813,7 +813,7 @@
|
|
|
813
813
|
/** Skill zu einem Mitglied einer Definition aus dem Skill-Katalog (E114 L1): gleiche Quelle, gleicher
|
|
814
814
|
Pfad. null, wenn die Quelle (noch) nicht eingerichtet oder nicht lesbar ist. */
|
|
815
815
|
function bundleMitgliedSkill(m) {
|
|
816
|
-
return SKILLS.filter(function (s) { return s.quelleSchluessel === m.quelleSchluessel && s.pfad === m.pfad; })[0] || null;
|
|
816
|
+
return SKILLS.filter(function (s) { return s.quelleSchluessel === m.quelleSchluessel && (s.pfad === m.pfad || (s.jeHarness || []).indexOf(m.pfad) > -1); })[0] || null;
|
|
817
817
|
}
|
|
818
818
|
|
|
819
819
|
/** Skill eines nicht verlinkten Mitglieds über seinen Namen, gesucht unter den Mitgliedern der
|
|
@@ -2274,7 +2274,8 @@
|
|
|
2274
2274
|
// Skill-Ordner berührt. Nicht der Pull-Zeitpunkt — der steht als „Zuletzt geprüft" in der
|
|
2275
2275
|
// Update-Ansicht und wäre für alle Skills einer Quelle derselbe.
|
|
2276
2276
|
var geaendert = datumDeutsch(skill.geaendert);
|
|
2277
|
-
var nachRef = ' · Ref ' + skill.ref + (geaendert ? ' · geändert ' + geaendert : '')
|
|
2277
|
+
var nachRef = ' · Ref ' + skill.ref + (geaendert ? ' · geändert ' + geaendert : '')
|
|
2278
|
+
+ (skill.jeHarness ? ' · ' + JE_HARNESS_VERMERK : '');
|
|
2278
2279
|
if (herkunftQuelle) {
|
|
2279
2280
|
herkunft.appendChild(quelleLinkElement(herkunftQuelle));
|
|
2280
2281
|
herkunft.appendChild(e('b', null, nachRef));
|
|
@@ -2341,7 +2342,8 @@
|
|
|
2341
2342
|
var sichtbar = 0;
|
|
2342
2343
|
document.querySelectorAll('#katalog .zeile').forEach(function (li) {
|
|
2343
2344
|
var skill = SKILLS[+li.dataset.index];
|
|
2344
|
-
var passtSuche = !suche || suchform(skill.name).indexOf(suche) > -1 || suchform(skill.pfad).indexOf(suche) > -1
|
|
2345
|
+
var passtSuche = !suche || suchform(skill.name).indexOf(suche) > -1 || suchform(skill.pfad).indexOf(suche) > -1
|
|
2346
|
+
|| (skill.jeHarness || []).some(function (p) { return suchform(p).indexOf(suche) > -1; });
|
|
2345
2347
|
var passtFilter = quellenFilter === 'alle' || skill.quelleSchluessel === quellenFilter;
|
|
2346
2348
|
var an = passtSuche && passtFilter;
|
|
2347
2349
|
li.hidden = !an;
|
|
@@ -2581,9 +2583,13 @@
|
|
|
2581
2583
|
/** Quelle · [Pfad ·] Ref für eine Skill-Karte im „Skills hinzufügen"-Dialog (F106): der Pfad
|
|
2582
2584
|
unterscheidet Skills, die unter demselben Namen mehrfach im selben Repo liegen (z. B. je
|
|
2583
2585
|
Harness ein eigener Ordner). Ein Wurzel-Skill hat pfad === '' und zeigt kein Pfad-Segment. */
|
|
2586
|
+
var JE_HARNESS_VERMERK = 'je Harness eigene Fassung';
|
|
2587
|
+
|
|
2584
2588
|
function skillQuelleBeschriftung(skill) {
|
|
2585
2589
|
var teile = [skill.quelleName];
|
|
2586
|
-
|
|
2590
|
+
// Mehrfach in der Quelle, je Harness eine eigene Fassung (E121): der Kern löst beim Verlinken auf.
|
|
2591
|
+
if (skill.jeHarness) teile.push(JE_HARNESS_VERMERK);
|
|
2592
|
+
else if (skill.pfad) teile.push(skill.pfad);
|
|
2587
2593
|
teile.push('Ref ' + skill.ref);
|
|
2588
2594
|
return teile.join(' · ');
|
|
2589
2595
|
}
|
|
@@ -861,10 +861,10 @@ export class Anwendungsfaelle {
|
|
|
861
861
|
ergebnis.push({
|
|
862
862
|
quelle,
|
|
863
863
|
ref,
|
|
864
|
-
skills: skills.map((skill) => {
|
|
864
|
+
skills: this.fassungenZusammenfassen(skills.map((skill) => {
|
|
865
865
|
const geaendert = daten.get(skill.pfad);
|
|
866
866
|
return geaendert === undefined ? skill : { ...skill, geaendert };
|
|
867
|
-
}),
|
|
867
|
+
})),
|
|
868
868
|
});
|
|
869
869
|
}
|
|
870
870
|
catch (fehler) {
|
|
@@ -873,6 +873,39 @@ export class Anwendungsfaelle {
|
|
|
873
873
|
}
|
|
874
874
|
return ergebnis;
|
|
875
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* Katalog-Zusammenfassung (E121): Ein Name, der in einer Quelle mehrfach vorkommt und mindestens
|
|
878
|
+
* einmal unmittelbar in einem Harness-Skill-Ordner liegt, erscheint einmal, mit `jeHarness`. Als
|
|
879
|
+
* Vertreter dient die Fassung im ersten passenden Harness-Ordner (Reihenfolge der Harnesses),
|
|
880
|
+
* sonst die erste. Alle anderen Namen bleiben unverändert, ebenso die Reihenfolge.
|
|
881
|
+
*/
|
|
882
|
+
fassungenZusammenfassen(skills) {
|
|
883
|
+
const ordner = this.p.harnesses.map((h) => `${h.quellOrdner()}/`);
|
|
884
|
+
const direkt = (s, o) => s.pfad.startsWith(o) && !s.pfad.slice(o.length).includes('/');
|
|
885
|
+
const gruppen = new Map();
|
|
886
|
+
for (const s of skills)
|
|
887
|
+
gruppen.set(s.name, [...(gruppen.get(s.name) ?? []), s]);
|
|
888
|
+
const ergebnis = [];
|
|
889
|
+
for (const s of skills) {
|
|
890
|
+
const gruppe = gruppen.get(s.name);
|
|
891
|
+
if (gruppe.length < 2 || !gruppe.some((g) => ordner.some((o) => direkt(g, o)))) {
|
|
892
|
+
ergebnis.push(s);
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
if (gruppe[0] !== s)
|
|
896
|
+
continue; // Vertreter steht an der Stelle der ersten Fassung
|
|
897
|
+
let vertreter = gruppe[0];
|
|
898
|
+
for (const o of ordner) {
|
|
899
|
+
const treffer = gruppe.find((g) => direkt(g, o));
|
|
900
|
+
if (treffer !== undefined) {
|
|
901
|
+
vertreter = treffer;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
ergebnis.push({ ...vertreter, jeHarness: gruppe.map((g) => g.pfad) });
|
|
906
|
+
}
|
|
907
|
+
return ergebnis;
|
|
908
|
+
}
|
|
876
909
|
/** Alle Marketplaces der bekannten Quellen, für `hmgr plugin list`. */
|
|
877
910
|
async marketplaces() {
|
|
878
911
|
const zustand = await this.p.zustand.lesen();
|
|
@@ -1279,6 +1312,21 @@ export class Anwendungsfaelle {
|
|
|
1279
1312
|
for (const installation of installationen) {
|
|
1280
1313
|
const ziel = Anwendungsfaelle.installationZiel(installation);
|
|
1281
1314
|
const kopf = { ziel, bundle: installation.bundle, quelle: installation.quelle };
|
|
1315
|
+
// E119: Ein Arbeitsverzeichnis, das es nicht mehr gibt, legt der Abgleich nicht neu an. Ohne
|
|
1316
|
+
// genanntes Ziel übergeht er es still (die Meldung übernimmt doctor), mit Ziel ist es ein Fehlschlag.
|
|
1317
|
+
if (ziel.ort.art === 'arbeitsverzeichnis' && !(await this.p.zeiger.verzeichnisExistiert(ziel.ort.pfad))) {
|
|
1318
|
+
if (args?.ziel === undefined)
|
|
1319
|
+
continue;
|
|
1320
|
+
ergebnis.push({
|
|
1321
|
+
...kopf,
|
|
1322
|
+
ergebnis: 'fehlgeschlagen',
|
|
1323
|
+
gelegt: [],
|
|
1324
|
+
entfernt: [],
|
|
1325
|
+
neueQuellen: [],
|
|
1326
|
+
fehler: `Das Arbeitsverzeichnis ${ziel.ort.pfad} gibt es nicht mehr. hmgr doctor bietet an, es zu vergessen.`,
|
|
1327
|
+
});
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1282
1330
|
try {
|
|
1283
1331
|
const harness = await this.harnessGeprueft(ziel.harness);
|
|
1284
1332
|
// Die Definition liegt in der Quelle: ohne Nachziehen bliebe eine Änderung unsichtbar,
|
|
@@ -1383,6 +1431,7 @@ export class Anwendungsfaelle {
|
|
|
1383
1431
|
const zustand = await this.p.zustand.lesen();
|
|
1384
1432
|
zustand.arbeitsverzeichnisse = zustand.arbeitsverzeichnisse.filter((p) => p !== verzeichnis);
|
|
1385
1433
|
zustand.bindungen = zustand.bindungen.filter((b) => !(b.ziel.ort.art === 'arbeitsverzeichnis' && b.ziel.ort.pfad === verzeichnis));
|
|
1434
|
+
zustand.bundleInstallationen = zustand.bundleInstallationen.filter((i) => !(i.ort.art === 'arbeitsverzeichnis' && i.ort.pfad === verzeichnis));
|
|
1386
1435
|
await this.p.zustand.schreiben(zustand);
|
|
1387
1436
|
}
|
|
1388
1437
|
/**
|
|
@@ -1406,7 +1455,15 @@ export class Anwendungsfaelle {
|
|
|
1406
1455
|
async doctorPruefen(rueckfragen) {
|
|
1407
1456
|
const zustand = await this.p.zustand.lesen();
|
|
1408
1457
|
const fehlend = [];
|
|
1409
|
-
|
|
1458
|
+
// E119: auch Pfade, an denen nur Bindungen oder Bundle-Installationen hängen.
|
|
1459
|
+
const pfade = new Set(zustand.arbeitsverzeichnisse);
|
|
1460
|
+
for (const b of zustand.bindungen)
|
|
1461
|
+
if (b.ziel.ort.art === 'arbeitsverzeichnis')
|
|
1462
|
+
pfade.add(b.ziel.ort.pfad);
|
|
1463
|
+
for (const i of zustand.bundleInstallationen)
|
|
1464
|
+
if (i.ort.art === 'arbeitsverzeichnis')
|
|
1465
|
+
pfade.add(i.ort.pfad);
|
|
1466
|
+
for (const pfad of pfade)
|
|
1410
1467
|
if (!(await this.p.zeiger.verzeichnisExistiert(pfad)))
|
|
1411
1468
|
fehlend.push(pfad);
|
|
1412
1469
|
let vergessen = [];
|
|
@@ -1542,7 +1599,7 @@ export class Anwendungsfaelle {
|
|
|
1542
1599
|
throw new Error(`Mehrere Quellen passen auf „${hinweis}", bitte eingrenzen:\n${zeilen(treffer)}`);
|
|
1543
1600
|
}
|
|
1544
1601
|
/** Skill über alle Quellen suchen. Mehrere Treffer sind nur mit Quellenhinweis erlaubt. */
|
|
1545
|
-
async skillFinden(name, quelleHinweis) {
|
|
1602
|
+
async skillFinden(name, quelleHinweis, wahl = {}) {
|
|
1546
1603
|
const zustand = await this.p.zustand.lesen();
|
|
1547
1604
|
const funde = [];
|
|
1548
1605
|
for (const quelle of zustand.quellen) {
|
|
@@ -1550,16 +1607,33 @@ export class Anwendungsfaelle {
|
|
|
1550
1607
|
continue;
|
|
1551
1608
|
const ref = await this.p.quellen.standardRef(quelle);
|
|
1552
1609
|
const skills = await this.p.quellen.skills(quelle, ref);
|
|
1553
|
-
for (const skill of skills)
|
|
1554
|
-
|
|
1555
|
-
funde.push({ quelle, ref, skill });
|
|
1610
|
+
for (const skill of Anwendungsfaelle.skillTreffer(skills, name, wahl))
|
|
1611
|
+
funde.push({ quelle, ref, skill });
|
|
1556
1612
|
}
|
|
1557
1613
|
if (funde.length === 0)
|
|
1558
1614
|
throw new SkillUnbekannt(name);
|
|
1559
1615
|
if (funde.length > 1)
|
|
1560
|
-
throw new SkillMehrdeutig(name, funde.map((f) => f.quelle));
|
|
1616
|
+
throw new SkillMehrdeutig(name, funde.map((f) => ({ quelle: f.quelle, pfad: f.skill.pfad })));
|
|
1561
1617
|
return funde[0];
|
|
1562
1618
|
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Die Skills einer Quelle zu einem Namen (E121). Mit `pfad` zählt nur der Skill mit genau diesem
|
|
1621
|
+
* Pfad. Sonst gewinnt bei mehreren Fassungen die eine, die unmittelbar im Skill-Ordner des
|
|
1622
|
+
* Harness liegt (`<quellOrdner>/<ordner>`, eine Ebene); gibt es keine solche oder mehrere,
|
|
1623
|
+
* bleiben alle, und der Aufrufer meldet die Mehrdeutigkeit.
|
|
1624
|
+
*/
|
|
1625
|
+
static skillTreffer(skills, name, wahl) {
|
|
1626
|
+
const gleichnamig = skills.filter((s) => s.name === name);
|
|
1627
|
+
if (wahl.pfad !== undefined) {
|
|
1628
|
+
const pfad = wahl.pfad.replace(/^\.?\/+|\/+$/g, '');
|
|
1629
|
+
return gleichnamig.filter((s) => s.pfad === pfad);
|
|
1630
|
+
}
|
|
1631
|
+
if (gleichnamig.length < 2 || wahl.quellOrdner === undefined)
|
|
1632
|
+
return gleichnamig;
|
|
1633
|
+
const praefix = `${wahl.quellOrdner}/`;
|
|
1634
|
+
const direkt = gleichnamig.filter((s) => s.pfad.startsWith(praefix) && !s.pfad.slice(praefix.length).includes('/'));
|
|
1635
|
+
return direkt.length === 1 ? direkt : gleichnamig;
|
|
1636
|
+
}
|
|
1563
1637
|
/** Checkout anlegen oder erweitern, Ziel vorbereiten, Link setzen. Ohne Zeiger-Arbeit. */
|
|
1564
1638
|
async linkAnlegen(harness, ziel, fund, ersetzen) {
|
|
1565
1639
|
if (!harness.orte().includes(ziel.ort.art))
|
|
@@ -1603,8 +1677,12 @@ export class Anwendungsfaelle {
|
|
|
1603
1677
|
if (!harness.orte().includes(args.ziel.ort.art))
|
|
1604
1678
|
throw new OrtNichtUnterstuetzt(harness.name, args.ziel.ort.art);
|
|
1605
1679
|
const version = args.version;
|
|
1680
|
+
const wahl = {
|
|
1681
|
+
quellOrdner: harness.quellOrdner(),
|
|
1682
|
+
...(args.pfad === undefined ? {} : { pfad: args.pfad }),
|
|
1683
|
+
};
|
|
1606
1684
|
if (version === undefined) {
|
|
1607
|
-
const fund = await this.skillFinden(args.skill, args.quelle);
|
|
1685
|
+
const fund = await this.skillFinden(args.skill, args.quelle, wahl);
|
|
1608
1686
|
const ergebnis = await this.linkAusFund(harness, args.ziel, fund, args.ersetzen, args.lokal);
|
|
1609
1687
|
return Anwendungsfaelle.hinweisAnhaengen(ergebnis, await this.bundleMitgliedZurueckholen(args.ziel, fund));
|
|
1610
1688
|
}
|
|
@@ -1616,7 +1694,7 @@ export class Anwendungsfaelle {
|
|
|
1616
1694
|
// Eine Versionseinschränkung schreibt immer geteilt (siehe EinschraenkungOhneZeiger oben), das
|
|
1617
1695
|
// trifft die Sperre also ausnahmslos, solange sie steht (E57).
|
|
1618
1696
|
await this.zeigerSchreibenPruefen(args.ziel, args.lokal);
|
|
1619
|
-
const fund = await this.fundAusZeiger(args.skill, version, args.quelle);
|
|
1697
|
+
const fund = await this.fundAusZeiger(args.skill, version, args.quelle, wahl);
|
|
1620
1698
|
const link = await this.linkAnlegen(harness, args.ziel, fund, args.ersetzen);
|
|
1621
1699
|
await this.zeigerErgaenzen(zeigerPfad, fund, version);
|
|
1622
1700
|
const hinweis = await this.harnessHinweis(harness, args.ziel.ort);
|
|
@@ -1933,9 +2011,9 @@ export class Anwendungsfaelle {
|
|
|
1933
2011
|
* gilt der Text als genauer Ref-Name gegen alle Refs, damit Tags und Branches ohne
|
|
1934
2012
|
* Versions-Schema erreichbar bleiben. Nie auf den Standard-Ref ausweichen (B8).
|
|
1935
2013
|
*/
|
|
1936
|
-
async fundAusZeiger(name, einschraenkung, quelleHinweis) {
|
|
2014
|
+
async fundAusZeiger(name, einschraenkung, quelleHinweis, wahl = {}) {
|
|
1937
2015
|
if (einschraenkung === null)
|
|
1938
|
-
return await this.skillFinden(name, quelleHinweis);
|
|
2016
|
+
return await this.skillFinden(name, quelleHinweis, wahl);
|
|
1939
2017
|
const zustand = await this.p.zustand.lesen();
|
|
1940
2018
|
const quellen = zustand.quellen.filter((q) => quellePasst(q, quelleHinweis));
|
|
1941
2019
|
for (const quelle of quellen) {
|
|
@@ -1944,7 +2022,7 @@ export class Anwendungsfaelle {
|
|
|
1944
2022
|
const ref = passenderRef(tags, einschraenkung);
|
|
1945
2023
|
if (ref === null)
|
|
1946
2024
|
continue;
|
|
1947
|
-
const skill = (await this.p.quellen.skills(quelle, ref)
|
|
2025
|
+
const skill = Anwendungsfaelle.skillTreffer(await this.p.quellen.skills(quelle, ref), name, wahl)[0];
|
|
1948
2026
|
if (skill)
|
|
1949
2027
|
return { quelle, ref, skill };
|
|
1950
2028
|
}
|
|
@@ -1952,7 +2030,7 @@ export class Anwendungsfaelle {
|
|
|
1952
2030
|
const refs = await this.p.quellen.refs(quelle).catch(() => []);
|
|
1953
2031
|
if (!refs.includes(einschraenkung))
|
|
1954
2032
|
continue;
|
|
1955
|
-
const skill = (await this.p.quellen.skills(quelle, einschraenkung)
|
|
2033
|
+
const skill = Anwendungsfaelle.skillTreffer(await this.p.quellen.skills(quelle, einschraenkung), name, wahl)[0];
|
|
1956
2034
|
if (skill)
|
|
1957
2035
|
return { quelle, ref: einschraenkung, skill };
|
|
1958
2036
|
}
|
|
@@ -2029,7 +2107,7 @@ export class Anwendungsfaelle {
|
|
|
2029
2107
|
}
|
|
2030
2108
|
for (const [name, einschraenkung] of zeiger.skills) {
|
|
2031
2109
|
try {
|
|
2032
|
-
const fund = await this.fundAusZeiger(name, einschraenkung);
|
|
2110
|
+
const fund = await this.fundAusZeiger(name, einschraenkung, undefined, { quellOrdner: harness.quellOrdner() });
|
|
2033
2111
|
const gefunden = eintraege.find((e) => e.art === 'link' && e.link.name === name);
|
|
2034
2112
|
// Ein eigener Link auf einen anderen Ref weicht ab und wird umgelegt (E33). Das trifft
|
|
2035
2113
|
// die Einschränkung, die inzwischen einen höheren Tag findet.
|
package/dist/kern/modell.js
CHANGED
|
@@ -338,10 +338,24 @@ export class LinkAnlegenFehlgeschlagen extends Error {
|
|
|
338
338
|
export class SkillMehrdeutig extends Error {
|
|
339
339
|
name;
|
|
340
340
|
quellen;
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
funde;
|
|
342
|
+
/** Die Mehrdeutigkeit kommt aus einer einzigen Quelle: `--pfad` hilft, nicht `--quelle` (E121). */
|
|
343
|
+
nurEineQuelle;
|
|
344
|
+
constructor(name, funde) {
|
|
345
|
+
const quellen = [];
|
|
346
|
+
for (const f of funde)
|
|
347
|
+
if (!quellen.some((q) => quelleSchluessel(q) === quelleSchluessel(f.quelle)))
|
|
348
|
+
quellen.push(f.quelle);
|
|
349
|
+
const zeilen = funde
|
|
350
|
+
.map((f) => ` ${quelleSchluessel(f.quelle)}: ${f.pfad === '' ? '(Repo-Wurzel)' : f.pfad}`)
|
|
351
|
+
.join('\n');
|
|
352
|
+
super(quellen.length === 1
|
|
353
|
+
? `Skill „${name}" liegt in der Quelle ${quelleSchluessel(quellen[0])} mehrfach:\n${zeilen}`
|
|
354
|
+
: `Skill „${name}" liegt in mehreren Quellen:\n${zeilen}`);
|
|
343
355
|
this.name = name;
|
|
344
356
|
this.quellen = quellen;
|
|
357
|
+
this.funde = funde;
|
|
358
|
+
this.nurEineQuelle = quellen.length === 1;
|
|
345
359
|
}
|
|
346
360
|
}
|
|
347
361
|
export class SkillUnbekannt extends Error {
|