hmgr 0.0.14 → 0.0.16
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/dateisystem.js +14 -9
- package/dist/cli/hilfe.js +3 -1
- package/dist/cli/main.js +1 -1
- package/dist/gui/main.js +88 -0
- package/dist/gui/renderer/index.html +4 -0
- package/dist/gui/renderer/renderer.js +157 -6
- package/dist/gui/renderer/styles.css +11 -0
- package/dist/kern/anwendungsfaelle.js +18 -0
- package/package.json +1 -1
|
@@ -153,25 +153,30 @@ export class DateisystemZiele {
|
|
|
153
153
|
}
|
|
154
154
|
return undefined;
|
|
155
155
|
}
|
|
156
|
-
/**
|
|
157
|
-
* Ein Skill ist ein Verzeichnis. Dateien wie .DS_Store zählen nicht als fremder Eintrag,
|
|
158
|
-
* ebenso wenig ein Symlink, der auf kein Verzeichnis zeigt.
|
|
159
|
-
*/
|
|
160
|
-
istVerzeichnis(info) {
|
|
161
|
-
return info !== undefined && info.istVerzeichnis;
|
|
162
|
-
}
|
|
163
156
|
async eintraege(ziel, verzeichnis) {
|
|
164
157
|
const zugriff = this.zugriffFuer(ziel);
|
|
165
158
|
const namen = await zugriff.namen(verzeichnis);
|
|
166
159
|
const ergebnis = [];
|
|
167
160
|
for (const name of [...namen].sort()) {
|
|
168
161
|
const pfad = zugriff.verbinden(verzeichnis, name);
|
|
169
|
-
|
|
162
|
+
const info = await zugriff.info(pfad);
|
|
163
|
+
// Kein Eintrag überhaupt: nichts zu melden.
|
|
164
|
+
if (info === undefined)
|
|
165
|
+
continue;
|
|
166
|
+
// Eine normale Datei (kein Verzeichnis, kein Link), etwa .DS_Store: zählt nicht (E12).
|
|
167
|
+
if (!info.istLink && !info.istVerzeichnis)
|
|
170
168
|
continue;
|
|
171
169
|
const zielPfad = await zugriff.linkZiel(pfad);
|
|
172
170
|
const gedeutet = zielPfad ? await this.deuten(zielPfad) : undefined;
|
|
171
|
+
// Ein Symlink, der ins Leere zeigt (E85): weder überspringen noch verschlucken — die Deutung
|
|
172
|
+
// funktioniert rein pfadstrukturell auch ohne dass das Ziel existiert, und ein eigener Link
|
|
173
|
+
// wird als kaputt gemeldet statt unsichtbar zu bleiben.
|
|
174
|
+
const kaputt = info.istLink && !info.istVerzeichnis ? true : undefined;
|
|
173
175
|
if (gedeutet?.art === 'link')
|
|
174
|
-
ergebnis.push({
|
|
176
|
+
ergebnis.push({
|
|
177
|
+
art: 'link',
|
|
178
|
+
link: { ziel, name, checkout: gedeutet.checkout, skill: gedeutet.skill, ...(kaputt ? { kaputt } : {}) },
|
|
179
|
+
});
|
|
175
180
|
else if (gedeutet)
|
|
176
181
|
ergebnis.push({ art: 'link-ohne-quelle', name, checkoutPfad: gedeutet.checkoutPfad });
|
|
177
182
|
// Ist der fremde Eintrag ein Symlink, wandert sein Ziel mit: darauf setzt die Übernahme auf (B12).
|
package/dist/cli/hilfe.js
CHANGED
|
@@ -449,7 +449,9 @@ const UPDATE = `hmgr update
|
|
|
449
449
|
Holt für jeden Checkout unter ~/.hmgr den neuesten Stand seines Refs und zieht bei Harnesses mit
|
|
450
450
|
Plugin-Fähigkeit deren Plugins nach (E14). Die Links zeigen weiter auf dieselben Pfade und müssen
|
|
451
451
|
nicht erneuert werden. Ein Checkout mit lokaler Änderung wird übersprungen und gemeldet (E15),
|
|
452
|
-
damit nichts verloren geht. Lokale Quellen haben keinen Checkout und bleiben unberührt.
|
|
452
|
+
damit nichts verloren geht. Lokale Quellen haben keinen Checkout und bleiben unberührt. Auch eine
|
|
453
|
+
Quelle ohne eigenen Checkout wird dabei aufgefrischt (E85): ihr Katalog zeigt danach, was neu im
|
|
454
|
+
Remote liegt, auch ohne dass irgendwo ein Checkout von ihr existiert.
|
|
453
455
|
|
|
454
456
|
Unter jeder „aktualisiert"-Zeile steht, welche Skills der neue Stand geändert hat (E70): die Namen
|
|
455
457
|
kommen aus dem Vergleich von altem und neuem Commit, gezählt werden nur Skills, die in diesem
|
package/dist/cli/main.js
CHANGED
|
@@ -783,7 +783,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
783
783
|
// Steht im Zeiger eine Versionseinschränkung, kommt sie vor den aufgelösten Ref (E33).
|
|
784
784
|
const einschraenkung = zielZustand.einschraenkungen.get(eintrag.link.name);
|
|
785
785
|
const spanne = einschraenkung === undefined ? '' : `${einschraenkung} → `;
|
|
786
|
-
console.log(` ${eintrag.link.name} ${spanne}${quelleAlsText(eintrag.link.checkout.quelle)} @ ${eintrag.link.checkout.ref} (${eintrag.link.skill.pfad})${beschreibeGrund(zielZustand.gruende.get(eintrag.link.name))}`);
|
|
786
|
+
console.log(` ${eintrag.link.name} ${spanne}${quelleAlsText(eintrag.link.checkout.quelle)} @ ${eintrag.link.checkout.ref} (${eintrag.link.skill.pfad})${beschreibeGrund(zielZustand.gruende.get(eintrag.link.name))}${eintrag.link.kaputt ? ` — Ziel fehlt, Quelle wurde vermutlich geändert. \`hmgr link ${eintrag.link.name}\` erneut ausführen` : ''}`);
|
|
787
787
|
}
|
|
788
788
|
}
|
|
789
789
|
// Bundles des Ziels (E69), unter den Einträgen: welche Links als Einheit zusammengehören.
|
package/dist/gui/main.js
CHANGED
|
@@ -24,6 +24,10 @@ import { hmgrCliVersion } from "./hmgr-cli.js";
|
|
|
24
24
|
import { cliUpdateHinweis } from "../adapter/npm-registry.js";
|
|
25
25
|
const hierVerzeichnis = path.dirname(fileURLToPath(import.meta.url));
|
|
26
26
|
const smoketest = process.env.HMGR_GUI_SMOKETEST === '1';
|
|
27
|
+
// E84 Teil 1: Pfad eines im Fixture noch unbekannten Verzeichnisses, das der Smoke-Lauf per Dialog
|
|
28
|
+
// hinzufügt, um den Sprung zur neuen Karte zu belegen. Nur zur Konstruktionszeit des DOM-Auszug-
|
|
29
|
+
// Skripts gelesen (Node-Seite), nicht zur Laufzeit im Renderer.
|
|
30
|
+
const smoketestNeuesVerzeichnis = process.env.HMGR_SMOKETEST_NEUES_VERZEICHNIS ?? '';
|
|
27
31
|
/**
|
|
28
32
|
* Nur eine hmgr-Instanz je Nutzer (E67). Grund: zwei laufende Fenster schrieben nacheinander auf
|
|
29
33
|
* dieselbe `state.json` und überschrieben sich gegenseitig — der Zustand ist einer, nicht einer je
|
|
@@ -930,6 +934,14 @@ function fensterOeffnen() {
|
|
|
930
934
|
var kundeXWarnzahl = warnzahlVon(kundeXKarte);
|
|
931
935
|
var kundeXWarnlabel = warnungVon(kundeXKarte)?.getAttribute('aria-label') ?? null;
|
|
932
936
|
var agentsWarnungVorhanden = warnungVon(agentsKarte) !== null;
|
|
937
|
+
// Kaputter Link (E85): eigene Quelle „kaputt-repo", Skill „bruechig" im Claude-Code-
|
|
938
|
+
// Home-Ziel, dessen Checkout-Ordner die Fixture vor dem Start gelöscht hat. Die Karte
|
|
939
|
+
// muss dafür ein Dreieck zeigen (Auslöser 1) und die Zeile eine Warnmarke „Ziel fehlt".
|
|
940
|
+
var claudeKarte = karteMitKopftext('Claude Code · Home');
|
|
941
|
+
var claudeWarnzahl = warnzahlVon(claudeKarte);
|
|
942
|
+
var kaputtZeile = Array.from(claudeKarte ? claudeKarte.querySelectorAll('.linkliste li') : [])
|
|
943
|
+
.find((li) => (li.querySelector('.name')?.textContent || '') === 'bruechig');
|
|
944
|
+
var kaputtMarkeVorhanden = (kaputtZeile?.querySelector('.merker.warnend')?.textContent || '') === 'Ziel fehlt';
|
|
933
945
|
// Dialog „MCP hinzufügen" (E47-Muster): öffnen an der Hermes-Karte und über Abbrechen
|
|
934
946
|
// wieder schließen, ohne IPC-Schreibaktion (mcpEinhaengen liefe erst nach „Einhängen").
|
|
935
947
|
var dmhFehler = null;
|
|
@@ -945,6 +957,67 @@ function fensterOeffnen() {
|
|
|
945
957
|
} catch (fehler) {
|
|
946
958
|
dmhFehler = String(fehler);
|
|
947
959
|
}
|
|
960
|
+
// E84 Teil 2: Suchfeld über der Zielliste filtert Agent- und Verzeichnis-Karten. Erst
|
|
961
|
+
// zurück auf die Ziele-Ansicht (der Lauf steht seit der MCP-Prüfung oben auf „mcp").
|
|
962
|
+
document.querySelector('[data-ansicht="ziele"]')?.click();
|
|
963
|
+
var zielsucheVorhanden = document.getElementById('zielsuche') !== null;
|
|
964
|
+
var setzeZielsuche = function (wert) {
|
|
965
|
+
var feld = document.getElementById('zielsuche');
|
|
966
|
+
if (!feld) return;
|
|
967
|
+
feld.value = wert;
|
|
968
|
+
feld.dispatchEvent(new Event('input', { bubbles: true }));
|
|
969
|
+
};
|
|
970
|
+
// „kunde-x" trifft im Fixture nur die Verzeichnis-Karte, kein Agent-Titel heißt so.
|
|
971
|
+
setzeZielsuche('kunde-x');
|
|
972
|
+
var zielzahlNachSuche = document.getElementById('zielzahl')?.textContent ?? null;
|
|
973
|
+
var zielKartenNachSucheZahl = document.querySelectorAll('#zielliste .ziel').length;
|
|
974
|
+
var agentsKopfNachSucheVorhanden = Array.from(document.querySelectorAll('#zielliste .gruppenkopf'))
|
|
975
|
+
.some(function (h) { return h.textContent === 'Agents'; });
|
|
976
|
+
setzeZielsuche('');
|
|
977
|
+
var zielzahlNachLeeren = document.getElementById('zielzahl')?.textContent ?? null;
|
|
978
|
+
var zielKartenNachLeerenZahl = document.querySelectorAll('#zielliste .ziel').length;
|
|
979
|
+
// Nachbesserung E84 Teil 2: die Suche muss auch die gekürzte Schreibweise (pfadKurz)
|
|
980
|
+
// einer Agent-Karte treffen, nicht nur ihren rohen Pfad — genau das steht im
|
|
981
|
+
// Kartenkopf. „~/.hermes" trifft im Fixture „Hermes · Home" und „Hermes · Profil
|
|
982
|
+
// „researcher““ (beide unter ~/.hermes/…), aber weder Claude Code noch Agents noch
|
|
983
|
+
// die Verzeichnis-Karte „kunde-x".
|
|
984
|
+
setzeZielsuche('~/.hermes');
|
|
985
|
+
var zielzahlNachSucheKurz = document.getElementById('zielzahl')?.textContent ?? null;
|
|
986
|
+
var zielKartenNachSucheKurzTitel = Array.from(document.querySelectorAll('#zielliste .ziel h3'))
|
|
987
|
+
.map(function (h) { return h.textContent; }).join('|');
|
|
988
|
+
var kundeXNachSucheKurzVorhanden = zielKartenNachSucheKurzTitel.indexOf('kunde-x') > -1;
|
|
989
|
+
setzeZielsuche('');
|
|
990
|
+
// E84 Teil 1: ein neues Arbeitsverzeichnis über den Dialog anlegen, danach muss seine
|
|
991
|
+
// Karte anspringen (aufgeklappt, kurz hervorgehoben). Der Pfad kommt aus dem Fixture
|
|
992
|
+
// (leer, wenn keiner mitgegeben wurde, dann bleibt dieser Block ein No-Op).
|
|
993
|
+
var sprungNeuesVerzeichnis = ${JSON.stringify(smoketestNeuesVerzeichnis)};
|
|
994
|
+
var sprungFehler = null;
|
|
995
|
+
var sprungKarteVorhanden = false;
|
|
996
|
+
var sprungKarteExpanded = null;
|
|
997
|
+
var sprungKarteHervorgehoben = null;
|
|
998
|
+
try {
|
|
999
|
+
if (sprungNeuesVerzeichnis) {
|
|
1000
|
+
document.getElementById('verzneu')?.click();
|
|
1001
|
+
var dvPfadFeld = document.getElementById('dv-pfad');
|
|
1002
|
+
dvPfadFeld.value = sprungNeuesVerzeichnis;
|
|
1003
|
+
document.querySelector('#d-verzeichnis button[value="los"]')?.click();
|
|
1004
|
+
// arbeitsverzeichnisHinzufuegen läuft über IPC, dann ladeAlles: kurz nachfassen.
|
|
1005
|
+
var sprungKarte = null;
|
|
1006
|
+
for (var wartezeitSprung = 0; wartezeitSprung < 3000; wartezeitSprung += 100) {
|
|
1007
|
+
await new Promise(function (r) { setTimeout(r, 100); });
|
|
1008
|
+
sprungKarte = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
1009
|
+
.find(function (k) { return (k.querySelector('.zielkopf h3')?.textContent || '').indexOf('neu-verzeichnis') > -1; });
|
|
1010
|
+
if (sprungKarte) break;
|
|
1011
|
+
}
|
|
1012
|
+
if (sprungKarte) {
|
|
1013
|
+
sprungKarteVorhanden = true;
|
|
1014
|
+
sprungKarteExpanded = sprungKarte.querySelector('.zielkopf')?.getAttribute('aria-expanded') ?? null;
|
|
1015
|
+
sprungKarteHervorgehoben = sprungKarte.classList.contains('ziel-neu');
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
} catch (fehler) {
|
|
1019
|
+
sprungFehler = String(fehler);
|
|
1020
|
+
}
|
|
948
1021
|
// Schriften aus fonts/ (Zusatzpunkt 6): erzwingt das Laden und belegt es danach.
|
|
949
1022
|
await Promise.all([
|
|
950
1023
|
document.fonts.load('400 16px Inter'),
|
|
@@ -1053,6 +1126,8 @@ function fensterOeffnen() {
|
|
|
1053
1126
|
kundeXWarnzahl,
|
|
1054
1127
|
kundeXWarnlabel,
|
|
1055
1128
|
agentsWarnungVorhanden,
|
|
1129
|
+
claudeWarnzahl,
|
|
1130
|
+
kaputtMarkeVorhanden,
|
|
1056
1131
|
quellenLeerDisplay,
|
|
1057
1132
|
agentsKarteVorhanden,
|
|
1058
1133
|
agentsMcpAbschnittVorhanden,
|
|
@@ -1074,6 +1149,19 @@ function fensterOeffnen() {
|
|
|
1074
1149
|
verlaufVorhanden,
|
|
1075
1150
|
verlaufText,
|
|
1076
1151
|
verlaufBloecke,
|
|
1152
|
+
zielsucheVorhanden,
|
|
1153
|
+
zielzahlNachSuche,
|
|
1154
|
+
zielKartenNachSucheZahl,
|
|
1155
|
+
agentsKopfNachSucheVorhanden,
|
|
1156
|
+
zielzahlNachLeeren,
|
|
1157
|
+
zielKartenNachLeerenZahl,
|
|
1158
|
+
zielzahlNachSucheKurz,
|
|
1159
|
+
zielKartenNachSucheKurzTitel,
|
|
1160
|
+
kundeXNachSucheKurzVorhanden,
|
|
1161
|
+
sprungFehler,
|
|
1162
|
+
sprungKarteVorhanden,
|
|
1163
|
+
sprungKarteExpanded,
|
|
1164
|
+
sprungKarteHervorgehoben,
|
|
1077
1165
|
});
|
|
1078
1166
|
})()`)
|
|
1079
1167
|
.then((auszug) => console.log(`[smoke] DOM-Auszug: ${auszug}`))
|
|
@@ -109,6 +109,10 @@
|
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
111
|
<div class="werkzeuge">
|
|
112
|
+
<div class="suche">
|
|
113
|
+
<svg viewBox="0 0 16 16" stroke-width="1.6" aria-hidden="true"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5 14.5 14.5"/></svg>
|
|
114
|
+
<input type="search" id="zielsuche" placeholder="Ziel oder Pfad suchen" aria-label="Ziele durchsuchen" spellcheck="false" autocomplete="off">
|
|
115
|
+
</div>
|
|
112
116
|
<div class="filter" role="group" aria-label="Ziele filtern">
|
|
113
117
|
<button class="chip" data-zielfilter="alle" aria-pressed="true">Alle</button>
|
|
114
118
|
<button class="chip" data-zielfilter="achtung" aria-pressed="false">Benötigt Aufmerksamkeit (0)</button>
|
|
@@ -358,6 +358,7 @@
|
|
|
358
358
|
var ignoriert = z.ignoriert || [];
|
|
359
359
|
return z.eintraege.filter(function (ei) {
|
|
360
360
|
if (ei.art === 'fremd') return ignoriert.indexOf(ei.name) === -1;
|
|
361
|
+
if (ei.art === 'link') return !!ei.link.kaputt;
|
|
361
362
|
return ei.art === 'link-ohne-quelle';
|
|
362
363
|
}).length;
|
|
363
364
|
}
|
|
@@ -404,6 +405,20 @@
|
|
|
404
405
|
// gilt „alle". Nicht persistiert, nur für die Dauer der Sitzung.
|
|
405
406
|
var VERZ_CHIP = {};
|
|
406
407
|
|
|
408
|
+
// Suchbegriff über die ganze Zielliste (E84 Teil 2, F100): Agent- wie Verzeichnis-Karten, in
|
|
409
|
+
// Kleinschreibung verglichen. Nicht persistiert, nur für die Dauer der Sitzung, und anders als
|
|
410
|
+
// der Chip-Wechsel leert die Suche weder ZIEL_OFFEN noch VERZ_CHIP.
|
|
411
|
+
var zielsuche = '';
|
|
412
|
+
|
|
413
|
+
// Ob schon einmal geladen wurde (E84 Teil 1, F99): der allererste Aufbau kennt noch keinen alten
|
|
414
|
+
// Stand, gegen den sich ein „neu hinzugekommenes" Verzeichnis abgrenzen ließe.
|
|
415
|
+
var ersteLadungAlles = true;
|
|
416
|
+
// Pfad eines Arbeitsverzeichnisses, dessen Karte anspringen soll, sobald die Ziele-Ansicht
|
|
417
|
+
// sichtbar wird (E84 Teil 1): gesetzt, wenn beim Laden eine andere Ansicht offen war. Ein
|
|
418
|
+
// weiteres neues Verzeichnis vor dem Wechsel ersetzt die Vormerkung, zeigeAnsicht('ziele')
|
|
419
|
+
// verbraucht sie.
|
|
420
|
+
var zielSprungVorgemerkt = null;
|
|
421
|
+
|
|
407
422
|
// Aufgeklappte Skill-Details im Katalog (F10), Schlüssel Quelle + Pfad: der Index in SKILLS
|
|
408
423
|
// verschiebt sich, wenn ladeAlles Quellen oder Skills anders sortiert oder eine wegfällt.
|
|
409
424
|
// Nicht persistiert, nur für die Dauer der Sitzung.
|
|
@@ -456,6 +471,10 @@
|
|
|
456
471
|
rufe(function () { return window.hmgr.bundles(); }),
|
|
457
472
|
rufe(function () { return window.hmgr.harnessHeime(); }),
|
|
458
473
|
]);
|
|
474
|
+
// Alter Stand der Verzeichnis-Pfade, vor dem Überschreiben unten (E84 Teil 1): zielKarten()
|
|
475
|
+
// liest hier noch ZIELE/AKTIVE_HARNESSE von vor diesem Lauf. null heißt „kein Vergleich", der
|
|
476
|
+
// allererste Aufbau hat noch keinen alten Stand.
|
|
477
|
+
var vorherVerzeichnisPfade = ersteLadungAlles ? null : zielKarten().verzeichnisse.map(function (v) { return v.pfad; });
|
|
459
478
|
ZIELE = ergebnisse[0];
|
|
460
479
|
SKILLS = ergebnisse[1];
|
|
461
480
|
// Eine alphabetische Liste über alle Quellen; wer nach Quelle sucht, hat die Filter-Chips.
|
|
@@ -476,6 +495,8 @@
|
|
|
476
495
|
STANDARD_REF = {};
|
|
477
496
|
SKILLS.forEach(function (s) { if (STANDARD_REF[s.quelleText] === undefined) STANDARD_REF[s.quelleText] = s.ref; });
|
|
478
497
|
|
|
498
|
+
ersteLadungAlles = false;
|
|
499
|
+
|
|
479
500
|
baueZiele();
|
|
480
501
|
baueSkills();
|
|
481
502
|
baueQuellenfilter();
|
|
@@ -483,6 +504,18 @@
|
|
|
483
504
|
baueEinstellungen();
|
|
484
505
|
baueMcp();
|
|
485
506
|
aktualisiereKopf();
|
|
507
|
+
|
|
508
|
+
// Genau ein neuer Verzeichnis-Pfad ⇒ Sprungziel (E84 Teil 1, F99): mehrere neue Pfade oder der
|
|
509
|
+
// allererste Aufbau lösen keinen Sprung aus. Gilt für beide Wege, GUI-Dialog wie CLI-Watcher
|
|
510
|
+
// (E62): beide laufen über ladeAlles. Erst NACH baueZiele() oben: springeZuVerzeichnis baut die
|
|
511
|
+
// Liste selbst noch einmal (Filter/Suche zurücksetzen kann nötig sein) und hebt die Karte
|
|
512
|
+
// hervor — ein baueZiele() danach würde diese frische Karte sonst wieder verwerfen.
|
|
513
|
+
if (vorherVerzeichnisPfade !== null) {
|
|
514
|
+
var neueVerzeichnisPfade = zielKarten().verzeichnisse
|
|
515
|
+
.map(function (v) { return v.pfad; })
|
|
516
|
+
.filter(function (p) { return vorherVerzeichnisPfade.indexOf(p) === -1; });
|
|
517
|
+
if (neueVerzeichnisPfade.length === 1) zielSprungVormerken(neueVerzeichnisPfade[0]);
|
|
518
|
+
}
|
|
486
519
|
}
|
|
487
520
|
|
|
488
521
|
/** Karten, wie die Zielliste sie zeigt: je Agent-Ziel eine, je Arbeitsverzeichnis eine gebündelte
|
|
@@ -545,6 +578,15 @@
|
|
|
545
578
|
// Der Verlauf zeigt immer den aktuellen Stand der Datei, nicht nur den des letzten Laufs hier.
|
|
546
579
|
void ladeVerlauf();
|
|
547
580
|
}
|
|
581
|
+
// Vorgemerkter Sprung zu einem neu hinzugekommenen Arbeitsverzeichnis (E84 Teil 1): kam er
|
|
582
|
+
// rein, während eine andere Ansicht offen war, holt der Wechsel hierher ihn jetzt nach. Die
|
|
583
|
+
// Ansicht steht schon sichtbar (siehe ANSICHTEN.forEach oben), springeZuVerzeichnis findet die
|
|
584
|
+
// Karte also.
|
|
585
|
+
if (name === 'ziele' && zielSprungVorgemerkt !== null) {
|
|
586
|
+
var vorgemerkterPfad = zielSprungVorgemerkt;
|
|
587
|
+
zielSprungVorgemerkt = null;
|
|
588
|
+
springeZuVerzeichnis(vorgemerkterPfad);
|
|
589
|
+
}
|
|
548
590
|
window.scrollTo(0, 0);
|
|
549
591
|
}
|
|
550
592
|
document.querySelectorAll('[data-ansicht]').forEach(function (k) {
|
|
@@ -713,6 +755,15 @@
|
|
|
713
755
|
|
|
714
756
|
li.appendChild(e('span', 'name', name));
|
|
715
757
|
|
|
758
|
+
// Kaputter Link (E85): das Zielverzeichnis existiert nicht mehr, meist weil ein `update` den
|
|
759
|
+
// Skill aus seiner Quelle nicht mehr fand. Derselbe Marken-Stil wie „registriert Hooks".
|
|
760
|
+
if (ei.art === 'link' && ei.link.kaputt) {
|
|
761
|
+
var kaputtMarke = e('span', 'merker leise warnend', 'Ziel fehlt');
|
|
762
|
+
kaputtMarke.setAttribute('aria-label', 'Ziel fehlt: Quelle wurde vermutlich geändert, hmgr link ' + name + ' erneut ausführen');
|
|
763
|
+
kaputtMarke.title = 'Quelle wurde vermutlich geändert. hmgr link ' + name + ' erneut ausführen.';
|
|
764
|
+
li.appendChild(kaputtMarke);
|
|
765
|
+
}
|
|
766
|
+
|
|
716
767
|
// Die Beschreibung aus der SKILL.md (E39): die Frage, wegen der die Zeile gelesen wird,
|
|
717
768
|
// deshalb die breiteste Spalte. Ein Link ohne bekannte Quelle kennt seinen Skill nicht.
|
|
718
769
|
li.appendChild(wasZelle(ei.art === 'link' ? ei.link.skill : null));
|
|
@@ -1258,6 +1309,9 @@
|
|
|
1258
1309
|
kopf.setAttribute('role', 'button');
|
|
1259
1310
|
kopf.setAttribute('tabindex', '0');
|
|
1260
1311
|
var schluessel = verzKey(pfad);
|
|
1312
|
+
// Wiederfindbar für den Sprung nach dem Hinzufügen (E84 Teil 1): kein CSS-Selektor auf den
|
|
1313
|
+
// Pfad, der könnte Zeichen tragen, die eine Attribut-Selektion nicht unverändert übersteht.
|
|
1314
|
+
karte.dataset.verzKey = schluessel;
|
|
1261
1315
|
var bodyId = 'zielkoerper-' + zielKoerperNr++;
|
|
1262
1316
|
var offen = zielFilter === 'achtung' || !!ZIEL_OFFEN[schluessel];
|
|
1263
1317
|
kopf.setAttribute('aria-controls', bodyId);
|
|
@@ -1377,16 +1431,35 @@
|
|
|
1377
1431
|
return karte;
|
|
1378
1432
|
}
|
|
1379
1433
|
|
|
1434
|
+
/** Ob eine Agent-Karte zum aktuellen Suchbegriff passt (E84 Teil 2): ihr Titel, ihr voller
|
|
1435
|
+
Verzeichnis-Pfad oder dessen gekürzte Schreibweise (pfadKurz) — genau das, was im Kartenkopf
|
|
1436
|
+
steht (`e('span', 'mono', pfadKurz(z.verzeichnis))` in zielKarte). Ohne die Kürzung fand die
|
|
1437
|
+
Suche nach „~/.claude" nichts, weil im rohen Pfad der volle Home-Pfad steht. In
|
|
1438
|
+
Kleinschreibung, leerer Suchbegriff heißt „alles". */
|
|
1439
|
+
function zielKartePasstZuSuche(z) {
|
|
1440
|
+
var s = zielsuche.trim().toLowerCase();
|
|
1441
|
+
if (!s) return true;
|
|
1442
|
+
var text = (zielTitel(z) + ' ' + (z.verzeichnis || '') + ' ' + pfadKurz(z.verzeichnis || '')).toLowerCase();
|
|
1443
|
+
return text.indexOf(s) > -1;
|
|
1444
|
+
}
|
|
1445
|
+
/** Ob eine Verzeichnis-Karte zum aktuellen Suchbegriff passt (E84 Teil 2): voller Pfad oder
|
|
1446
|
+
gekürzte Schreibweise (pfadKurz). */
|
|
1447
|
+
function verzKartePasstZuSuche(pfad) {
|
|
1448
|
+
var s = zielsuche.trim().toLowerCase();
|
|
1449
|
+
if (!s) return true;
|
|
1450
|
+
return pfad.toLowerCase().indexOf(s) > -1 || pfadKurz(pfad).toLowerCase().indexOf(s) > -1;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1380
1453
|
function baueZiele() {
|
|
1381
1454
|
var liste = $('zielliste');
|
|
1382
1455
|
liste.textContent = '';
|
|
1383
1456
|
var karten = zielKarten();
|
|
1384
1457
|
|
|
1385
1458
|
var agentsSichtbar = karten.agents
|
|
1386
|
-
.filter(function (z) { return zielFilter === 'alle' || zielBraucht(z); })
|
|
1459
|
+
.filter(function (z) { return (zielFilter === 'alle' || zielBraucht(z)) && zielKartePasstZuSuche(z); })
|
|
1387
1460
|
.sort(function (a, b) { return zielTitel(a).localeCompare(zielTitel(b), 'de'); });
|
|
1388
1461
|
var verzeichnisseSichtbar = karten.verzeichnisse
|
|
1389
|
-
.filter(function (g) { return zielFilter === 'alle' || g.teile.some(zielBraucht); })
|
|
1462
|
+
.filter(function (g) { return (zielFilter === 'alle' || g.teile.some(zielBraucht)) && verzKartePasstZuSuche(g.pfad); })
|
|
1390
1463
|
.sort(function (a, b) { return pfadKurz(a.pfad).localeCompare(pfadKurz(b.pfad), 'de'); });
|
|
1391
1464
|
|
|
1392
1465
|
if (agentsSichtbar.length > 0) {
|
|
@@ -1399,10 +1472,16 @@
|
|
|
1399
1472
|
}
|
|
1400
1473
|
var gezeigt = agentsSichtbar.length + verzeichnisseSichtbar.length;
|
|
1401
1474
|
var gesamt = karten.agents.length + karten.verzeichnisse.length;
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1475
|
+
var suchbegriffGetrimmt = zielsuche.trim();
|
|
1476
|
+
if (gezeigt === 0) {
|
|
1477
|
+
if (suchbegriffGetrimmt) {
|
|
1478
|
+
liste.appendChild(e('p', 'leerhinweis', 'Kein Ziel passt zu „' + suchbegriffGetrimmt + '".'));
|
|
1479
|
+
} else {
|
|
1480
|
+
liste.appendChild(e('p', 'leerhinweis', zielFilter === 'achtung'
|
|
1481
|
+
? 'Kein Ziel braucht gerade Aufmerksamkeit.'
|
|
1482
|
+
: 'Noch keine Ziele. Füge unten ein Arbeitsverzeichnis hinzu oder verlinke einen Skill.'));
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1406
1485
|
|
|
1407
1486
|
$('zielzahl').textContent = gezeigt + ' von ' + gesamt + ' Karten gezeigt';
|
|
1408
1487
|
|
|
@@ -1430,6 +1509,73 @@
|
|
|
1430
1509
|
});
|
|
1431
1510
|
});
|
|
1432
1511
|
|
|
1512
|
+
// Suchfeld über die ganze Zielliste (E84 Teil 2, F100): tippen filtert live, anders als der
|
|
1513
|
+
// Chip-Wechsel bleiben ZIEL_OFFEN und VERZ_CHIP dabei unangetastet.
|
|
1514
|
+
var zielsucheFeld = $('zielsuche');
|
|
1515
|
+
if (zielsucheFeld) {
|
|
1516
|
+
zielsucheFeld.addEventListener('input', function () {
|
|
1517
|
+
zielsuche = zielsucheFeld.value;
|
|
1518
|
+
baueZiele();
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/** Ob die Ziele-Ansicht gerade die sichtbare Ansicht ist (E84 Teil 1). */
|
|
1523
|
+
function ansichtZieleSichtbar() {
|
|
1524
|
+
var ansicht = $('ansicht-ziele');
|
|
1525
|
+
return !!ansicht && !ansicht.hidden;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/** Die im DOM stehende Karte eines Arbeitsverzeichnisses, über data-verz-key gefunden (E84
|
|
1529
|
+
Teil 1): kein CSS-Attribut-Selektor auf den Pfad selbst, der könnte Sonderzeichen tragen. */
|
|
1530
|
+
function findeVerzeichnisKarte(schluessel) {
|
|
1531
|
+
var treffer = Array.prototype.filter.call(
|
|
1532
|
+
document.querySelectorAll('#zielliste .ziel.verzeichnis'),
|
|
1533
|
+
function (k) { return k.dataset.verzKey === schluessel; },
|
|
1534
|
+
);
|
|
1535
|
+
return treffer[0];
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/** Ob die Verzeichnis-Karte unter dem *aktuellen* Filter und Suchbegriff überhaupt sichtbar
|
|
1539
|
+
wäre, ohne beides zurückzusetzen (E84 Teil 1) — dieselbe Regel wie in baueZiele. */
|
|
1540
|
+
function verzKarteWaereSichtbar(pfad) {
|
|
1541
|
+
var gruppe = zielKarten().verzeichnisse.filter(function (g) { return g.pfad === pfad; })[0];
|
|
1542
|
+
if (!gruppe) return false;
|
|
1543
|
+
return (zielFilter === 'alle' || gruppe.teile.some(zielBraucht)) && verzKartePasstZuSuche(pfad);
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
/** Klappt die Karte eines Arbeitsverzeichnisses auf, scrollt sie ins Bild und hebt sie kurz
|
|
1547
|
+
hervor (E84 Teil 1, F99). Setzt Suchfeld und Filter nur zurück, wenn die Karte sonst gar
|
|
1548
|
+
nicht sichtbar wäre — die Reihenfolge ist wichtig: der Filterwechsel leert ZIEL_OFFEN, erst
|
|
1549
|
+
danach wird der Schlüssel der Karte wieder aufgeklappt gemerkt. */
|
|
1550
|
+
function springeZuVerzeichnis(pfad) {
|
|
1551
|
+
var schluessel = verzKey(pfad);
|
|
1552
|
+
if (!verzKarteWaereSichtbar(pfad)) {
|
|
1553
|
+
zielFilter = 'alle';
|
|
1554
|
+
zielsuche = '';
|
|
1555
|
+
if (zielsucheFeld) zielsucheFeld.value = '';
|
|
1556
|
+
document.querySelectorAll('#ansicht-ziele .chip').forEach(function (a) {
|
|
1557
|
+
a.setAttribute('aria-pressed', a.dataset.zielfilter === 'alle' ? 'true' : 'false');
|
|
1558
|
+
});
|
|
1559
|
+
ZIEL_OFFEN = {};
|
|
1560
|
+
VERZ_CHIP = {};
|
|
1561
|
+
}
|
|
1562
|
+
ZIEL_OFFEN[schluessel] = true;
|
|
1563
|
+
baueZiele();
|
|
1564
|
+
var karte = findeVerzeichnisKarte(schluessel);
|
|
1565
|
+
if (!karte) return;
|
|
1566
|
+
karte.scrollIntoView({ block: 'nearest' });
|
|
1567
|
+
karte.classList.add('ziel-neu');
|
|
1568
|
+
setTimeout(function () { karte.classList.remove('ziel-neu'); }, 1500);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
/** Springt sofort, wenn die Ziele-Ansicht gerade sichtbar ist, sonst merkt sich den Pfad für
|
|
1572
|
+
den nächsten Wechsel dorthin (E84 Teil 1): zeigeAnsicht('ziele') holt die Vormerkung nach
|
|
1573
|
+
und verbraucht sie dabei. Ein späterer Aufruf vor dem Wechsel ersetzt die vorherige. */
|
|
1574
|
+
function zielSprungVormerken(pfad) {
|
|
1575
|
+
if (ansichtZieleSichtbar()) springeZuVerzeichnis(pfad);
|
|
1576
|
+
else zielSprungVorgemerkt = pfad;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1433
1579
|
// entfernenKontext.eintraege: [{harness, ort}], immer eines bei frageEntfernen (Einzel-Ziel),
|
|
1434
1580
|
// mehrere bei frageEntfernenAlle (E53 Pkt. 2, „Alle"-Zeile über mehrere Harnesses).
|
|
1435
1581
|
var entfernenKontext = null;
|
|
@@ -1672,8 +1818,13 @@
|
|
|
1672
1818
|
if (wert !== 'los') return;
|
|
1673
1819
|
var pfad = $('dv-pfad').value.trim();
|
|
1674
1820
|
if (!pfad) return;
|
|
1821
|
+
// Stand vor dem Anlegen (E84 Teil 1): war das Verzeichnis schon eine Karte, findet ladeAlles
|
|
1822
|
+
// unten keine Differenz (kein neuer Pfad) — der Dialog kennt den Pfad trotzdem, diese Karte
|
|
1823
|
+
// ist dann das Sprungziel.
|
|
1824
|
+
var karteVorhanden = zielKarten().verzeichnisse.some(function (g) { return g.pfad === pfad; });
|
|
1675
1825
|
var ergebnis = await rufe(function () { return window.hmgr.arbeitsverzeichnisHinzufuegen(pfad); });
|
|
1676
1826
|
await ladeAlles();
|
|
1827
|
+
if (karteVorhanden) zielSprungVormerken(pfad);
|
|
1677
1828
|
$('de-titel').textContent = 'Arbeitsverzeichnis hinzugefügt';
|
|
1678
1829
|
$('de-text').textContent = ergebnis.hinweis || ('Zeiger: ' + (ergebnis.zeigerPfad || 'keiner'));
|
|
1679
1830
|
setzeErfolgHinweise([]);
|
|
@@ -367,6 +367,17 @@ a{color:var(--signal-tief)}
|
|
|
367
367
|
.zielkoerper{padding:16px 18px 18px}
|
|
368
368
|
.zielkoerper[hidden]{display:none}
|
|
369
369
|
|
|
370
|
+
/* Hervorhebung der Karte eines frisch hinzugefügten Arbeitsverzeichnisses (E84 Teil 1): kurzer
|
|
371
|
+
Farbschimmer am Kartenrand, die Klasse fällt nach ~1,5s selbst wieder ab (setTimeout in
|
|
372
|
+
renderer.js, damit ein Neuaufbau der Liste sie nicht wieder einschaltet). Die globale
|
|
373
|
+
prefers-reduced-motion-Regel weiter unten kappt die Animationsdauer schon auf .01ms, der
|
|
374
|
+
Ruhezustand (kein Schimmer) reicht dort als Hinweis. */
|
|
375
|
+
.ziel.ziel-neu{animation:ziel-aufblitzen 1.5s ease-out}
|
|
376
|
+
@keyframes ziel-aufblitzen{
|
|
377
|
+
from{box-shadow:0 0 0 3px var(--signal)}
|
|
378
|
+
to{box-shadow:0 0 0 3px transparent}
|
|
379
|
+
}
|
|
380
|
+
|
|
370
381
|
.gruppenkopf{display:block;margin:16px 0 0;padding:0 0 9px;font-size:19px;font-weight:800;letter-spacing:.2em;color:var(--tinte);border-bottom:2px solid var(--tinte)}
|
|
371
382
|
.gruppenkopf:first-child{margin-top:0}
|
|
372
383
|
|
|
@@ -1754,6 +1754,24 @@ export class Anwendungsfaelle {
|
|
|
1754
1754
|
if (aktualisierung.ergebnis === 'aktualisiert' || aktualisierung.ergebnis === 'unveraendert')
|
|
1755
1755
|
this.nachgezogen.add(quelleSchluessel(checkout.quelle));
|
|
1756
1756
|
}
|
|
1757
|
+
// Quellen ohne eigenen Checkout fetchen den Checkout-Loop oben nie an (E85): ihr Basis-Clone
|
|
1758
|
+
// würde sonst für immer auf dem Stand von `source add` stehen bleiben, und ein neuer Skill in
|
|
1759
|
+
// so einer Quelle wäre für `link`/`status`/`source list` unsichtbar. Jede git-Quelle, die im
|
|
1760
|
+
// Loop noch nicht nachgezogen wurde, holt hier ihre Refs nach — ohne Checkout-Update, reine
|
|
1761
|
+
// Katalogpflege. Ein Fehlschlag (kein Netz, Quelle nicht erreichbar) bricht den Lauf nicht ab.
|
|
1762
|
+
for (const quelle of bekannt.values()) {
|
|
1763
|
+
if (quelle.art !== 'git')
|
|
1764
|
+
continue;
|
|
1765
|
+
if (this.nachgezogen.has(quelleSchluessel(quelle)))
|
|
1766
|
+
continue;
|
|
1767
|
+
try {
|
|
1768
|
+
await this.p.quellen.refsNachziehen(quelle);
|
|
1769
|
+
}
|
|
1770
|
+
catch {
|
|
1771
|
+
// wie ein unerreichbarer Checkout (E51): übrige Quellen laufen weiter.
|
|
1772
|
+
}
|
|
1773
|
+
this.nachgezogen.add(quelleSchluessel(quelle));
|
|
1774
|
+
}
|
|
1757
1775
|
// Nach den Checkouts die Plugins nachziehen (E14).
|
|
1758
1776
|
const pluginHarnesses = [];
|
|
1759
1777
|
for (const harness of await this.vorhandeneHarnesses()) {
|