hmgr 0.0.5 → 0.0.7

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.
@@ -0,0 +1,57 @@
1
+ // MCP-Ansicht der GUI (E61-Folgerunde): reine Umformung von Kern-Daten in IPC-taugliche, gewhiteliste
2
+ // Objekte, ohne Electron — testbar wie oeffnen.ts und icons.ts. Der eigentliche IPC-Handler in
3
+ // main.ts ruft diese Funktionen nur noch auf.
4
+ import { variablenVerweise, } from "../kern/anwendungsfaelle.js";
5
+ /** Katalog-Eintrag über IPC: die bekannten Felder, nichts ungefiltert durchgereicht (E40 R3). */
6
+ export function mcpEintragSerialisieren(eintrag) {
7
+ return {
8
+ name: eintrag.name,
9
+ beschreibung: eintrag.beschreibung,
10
+ ...(eintrag.homepage === undefined ? {} : { homepage: eintrag.homepage }),
11
+ ...(eintrag.notizen === undefined ? {} : { notizen: eintrag.notizen }),
12
+ konfig: eintrag.konfig,
13
+ };
14
+ }
15
+ /**
16
+ * F39 an der Katalog-Karte: dieselbe Prüf-Funktion wie beim tatsächlichen Einhängen
17
+ * (`variablenVerweise`), hier aber nur gegen die übergebene Umgebung geprüft — ohne den
18
+ * ortsbezogenen `.env`-Abgleich, der ist ortsspezifisch und bleibt dem eigentlichen Einhängen
19
+ * vorbehalten (kommt von dort als `warnung` zurück).
20
+ */
21
+ export function mcpFehlendeVariablen(eintrag, umgebung) {
22
+ return variablenVerweise(eintrag.konfig).filter((name) => !(name in umgebung) || umgebung[name] === undefined);
23
+ }
24
+ /** Übersicht über IPC (E61-Folgerunde): Katalog-Einträge whitelisted, dazu der F39-Vermerk je Karte. */
25
+ export function mcpUebersichtSerialisieren(uebersicht, umgebung) {
26
+ return {
27
+ katalog: uebersicht.katalog.map((k) => ({
28
+ eintrag: mcpEintragSerialisieren(k.eintrag),
29
+ eingehaengt: k.eingehaengt,
30
+ geteilteFundorte: k.geteilteFundorte,
31
+ fehlendeVariablen: mcpFehlendeVariablen(k.eintrag, umgebung),
32
+ })),
33
+ unbekannt: uebersicht.unbekannt,
34
+ unbekannteGeteilteFundorte: uebersicht.unbekannteGeteilteFundorte,
35
+ };
36
+ }
37
+ /** Ergebnis von `mcpEinhaengen`/`mcpAushaengen` über IPC, whitelisted wie der Katalog-Eintrag. */
38
+ export function mcpWirkungSerialisieren(ergebnis) {
39
+ return {
40
+ eintrag: mcpEintragSerialisieren(ergebnis.eintrag),
41
+ ziel: ergebnis.ziel,
42
+ ...(ergebnis.hinweis === undefined ? {} : { hinweis: ergebnis.hinweis }),
43
+ ...(ergebnis.warnung === undefined ? {} : { warnung: ergebnis.warnung }),
44
+ };
45
+ }
46
+ /**
47
+ * Ergebnis von `mcpUebernehmen` über IPC (E64). Kein Katalog-Eintrag darin: übernommen wird auch,
48
+ * was der Katalog nicht kennt, deshalb reicht der Name.
49
+ */
50
+ export function mcpUebernahmeSerialisieren(ergebnis) {
51
+ return {
52
+ name: ergebnis.name,
53
+ ziel: ergebnis.ziel,
54
+ datei: ergebnis.datei,
55
+ ...(ergebnis.hinweis === undefined ? {} : { hinweis: ergebnis.hinweis }),
56
+ };
57
+ }
@@ -9,7 +9,7 @@ import { urlOrt } from "../kern/quelle-ort.js";
9
9
  * (`git@host:besitzer/repo.git`) hat keine Webseite als Adresse, aber Host, Besitzer und Repo sind
10
10
  * bekannt (E36), also wird daraus die https-Repo-Seite gebaut — `shell.openExternal` bekommt so nie
11
11
  * die ssh-Adresse selbst zu sehen. Lokale Quellen und `file://`-Quellen öffnen ihren Pfad im
12
- * Finder. Alles, woraus sich weder eine http(s)-Adresse noch ein Pfad gewinnen lässt, wird
12
+ * Dateimanager. Alles, woraus sich weder eine http(s)-Adresse noch ein Pfad gewinnen lässt, wird
13
13
  * abgelehnt: kein anderes Protokoll geht an eine Electron-Shell-Funktion.
14
14
  */
15
15
  export function entscheideOeffnen(quelle) {
@@ -5,13 +5,19 @@ contextBridge.exposeInMainWorld('hmgr', {
5
5
  version: () => ipcRenderer.invoke('hmgr:version'),
6
6
  /** Home-Verzeichnis, nur für pfadKurz im Renderer (Anzeige-Kürzung auf ~). */
7
7
  home: () => ipcRenderer.invoke('hmgr:home'),
8
+ /** Plattform (`process.platform`), nur für den Pfadtrenner der Anzeige im Renderer (E68). */
9
+ plattform: () => ipcRenderer.invoke('hmgr:plattform'),
8
10
  istZustand: () => ipcRenderer.invoke('hmgr:istZustand'),
9
11
  marketplaces: () => ipcRenderer.invoke('hmgr:marketplaces'),
10
12
  skills: () => ipcRenderer.invoke('hmgr:skills'),
11
13
  quellen: () => ipcRenderer.invoke('hmgr:quellen'),
14
+ /** Bundles aller bekannten Quellen (E69), entdeckt beim Quellen-Scan: kein eigener Katalog. */
15
+ bundles: () => ipcRenderer.invoke('hmgr:bundles'),
16
+ bundleInstallieren: (args) => ipcRenderer.invoke('hmgr:bundleInstallieren', args),
17
+ bundleEntfernen: (args) => ipcRenderer.invoke('hmgr:bundleEntfernen', args),
12
18
  /** Icon einer Quelle als data-URL, oder null (E60). */
13
19
  icon: (text) => ipcRenderer.invoke('hmgr:icon', text),
14
- /** Öffnet eine Quelle: Repo-Seite im Browser oder Ordner im Finder (E60). */
20
+ /** Öffnet eine Quelle: Repo-Seite im Browser oder Ordner im Dateimanager (E60). */
15
21
  quelleOeffnen: (text) => ipcRenderer.invoke('hmgr:quelleOeffnen', text),
16
22
  quelleHinzufuegen: (text, name) => ipcRenderer.invoke('hmgr:quelleHinzufuegen', text, name),
17
23
  quelleEntfernen: (text) => ipcRenderer.invoke('hmgr:quelleEntfernen', text),
@@ -52,4 +58,17 @@ contextBridge.exposeInMainWorld('hmgr', {
52
58
  onAktualisierenAnstossen: (hoerer) => {
53
59
  ipcRenderer.on('hmgr:aktualisierenAnstossen', () => hoerer());
54
60
  },
61
+ /** Anstoß zum stillen Neuladen der Anzeige (E62): Main ruft bei Fokus-Wechsel und beobachteten
62
+ Dateiänderungen, nie mit Netzzugriff (im Unterschied zu onAktualisierenAnstossen). */
63
+ onNeuLadenAnstossen: (hoerer) => {
64
+ ipcRenderer.on('hmgr:neuLadenAnstossen', () => hoerer());
65
+ },
66
+ /** MCP-Ansicht (E61-Folgerunde): Katalog gelesen, nie geschrieben. */
67
+ mcpUebersicht: () => ipcRenderer.invoke('hmgr:mcpUebersicht'),
68
+ /** Plätze für den Einhängen-Dialog (F42): kommt aus dem Kern, hängt an der Fähigkeit (E16). */
69
+ mcpPlaetze: () => ipcRenderer.invoke('hmgr:mcpPlaetze'),
70
+ mcpEinhaengen: (args) => ipcRenderer.invoke('hmgr:mcpEinhaengen', args),
71
+ mcpAushaengen: (args) => ipcRenderer.invoke('hmgr:mcpAushaengen', args),
72
+ /** Übernehmen aus der geteilten .mcp.json in den privaten Scope (E64), nach Bestätigung im Renderer. */
73
+ mcpUebernehmen: (args) => ipcRenderer.invoke('hmgr:mcpUebernehmen', args),
55
74
  });
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>hmgr — Skills und Plugins auf diesem Rechner</title>
6
+ <title>hmgr — The Harness Manager</title>
7
7
  <!-- Nichts aus dem Netz: der Entwurf holt Inter und JetBrains Mono von Google, eine Desktop-App
8
8
  lädt keine fremden Ressourcen. Beide Schriften liegen deshalb als woff2 unter renderer/fonts/
9
9
  und werden in styles.css per @font-face eingebunden, 'self' deckt sie ab. -->
@@ -25,14 +25,20 @@
25
25
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1 5h14M1 11h14"/><path d="M4 3v4M9 3v4M6 9v4M12 9v4"/></svg>
26
26
  Ziele <span class="zahl" id="navzahlziele"></span>
27
27
  </button>
28
- <button class="plakette" data-ansicht="katalog">
29
- <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M2 3h12v10H2z"/><path d="M2 6h12M6 6v7"/></svg>
30
- Skills <span class="zahl" id="navzahlskills"></span>
28
+ <!-- Stecker mit zwei Stiften: der MCP-Katalog haengt Server in Harnesses ein, mehr sagt das
29
+ Zeichen nicht (entwurf/gui.html, keine Marken-Logos wie bei den Harnessen). -->
30
+ <button class="plakette" data-ansicht="mcp">
31
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1.4 8h3.2"/><path d="M4.6 3.9h4v8.2h-4z"/><path d="M8.6 6.2h5.8M8.6 9.8h5.8"/></svg>
32
+ MCP <span class="zahl" id="navzahlmcp"></span>
31
33
  </button>
32
34
  <button class="plakette" data-ansicht="plugins">
33
35
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M3 3h10v10H3z"/><path d="M6 1v2M10 1v2M6 13v2M10 13v2"/></svg>
34
36
  Plugins <span class="zahl" id="navzahlplugins"></span>
35
37
  </button>
38
+ <button class="plakette" data-ansicht="katalog">
39
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M2 3h12v10H2z"/><path d="M2 6h12M6 6v7"/></svg>
40
+ Skills <span class="zahl" id="navzahlskills"></span>
41
+ </button>
36
42
  </nav>
37
43
 
38
44
  <div class="stand">
@@ -93,14 +99,35 @@
93
99
  <button class="chip" data-zielfilter="alle" aria-pressed="true">Alle</button>
94
100
  <button class="chip" data-zielfilter="achtung" aria-pressed="false">Benötigt Aufmerksamkeit (0)</button>
95
101
  </div>
102
+ </div>
103
+
104
+ <p class="katalogzahl" id="zielzahl"></p>
105
+ <div class="ziele" id="zielliste"></div>
106
+ <!-- Unter der letzten Verzeichnis-Karte, nicht oben bei den Agents: dort gehört er
107
+ thematisch hin (2026-09-05). Fester Fuß statt Teil der neu gebauten Liste, so bleibt
108
+ der Klick-Listener aus der Startphase gebunden. -->
109
+ <div class="zielfussleiste">
96
110
  <button class="knopf leise mitsymbol" type="button" id="verzneu">
97
111
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M1.2 13.2V4.1h3.9l1.3 1.8h5.2v7.3z"/><path d="M13.2 1.9v4.6M10.9 4.2h4.6"/></svg>
98
112
  Verzeichnis hinzufügen
99
113
  </button>
100
114
  </div>
115
+ </section>
101
116
 
102
- <p class="katalogzahl" id="zielzahl"></p>
103
- <div class="ziele" id="zielliste"></div>
117
+ <!-- ============ MCP ============ -->
118
+ <!-- E61, abgespeckt zum reinen Katalog (F46, Korrektur an E61): der Katalog wird hier nicht
119
+ bearbeitet, hmgr liest ~/.hmgr/mcp.yaml und schreibt sie nie, also gibt es in dieser
120
+ Ansicht kein Anlegen und kein Ändern eines Eintrags, nur Einhängen. Wo ein Server hängt
121
+ und das Aushängen zeigen seither nur noch die Ziel-Karten. -->
122
+ <section id="ansicht-mcp" hidden>
123
+ <div class="ansichtskopf">
124
+ <h1>MCP-Server</h1>
125
+ <!-- Der Pfad steht hier in POSIX-Schreibweise und wird beim Start in die Schreibweise
126
+ der Plattform gesetzt (E68, ladeUmgebung im Renderer). -->
127
+ <p>Dein Katalog aus <span class="mono" id="mcpkatalogpfad">~/.hmgr/mcp.yaml</span>. Häng einen Server dorthin ein, wo du ihn brauchst: in ein Arbeitsverzeichnis, damit er nur dort Kontext kostet, oder global. Den Katalog selbst pflegst du in der Datei, hmgr liest sie nur.</p>
128
+ </div>
129
+
130
+ <div class="mcpliste" id="mcpliste"></div>
104
131
  </section>
105
132
 
106
133
  <!-- ============ AKTUALISIEREN ============ -->
@@ -189,7 +216,18 @@
189
216
  <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>
190
217
  <input type="search" id="dsk-suche" placeholder="Skill suchen" aria-label="Skills durchsuchen">
191
218
  </div>
192
- <div class="wahl" id="dsk-liste"></div>
219
+ <!-- Bundle-Sektion (E69, F68): kein eigener Navigationspunkt, Bundles stehen hier, wo die
220
+ Entscheidung fällt, was ins Ziel kommt. Leer bleibt der Kasten weg. -->
221
+ <div class="dskgruppe" id="dsk-bundlebox" hidden>
222
+ <span class="etikett">Bundles</span>
223
+ <p class="ohne dskgruppentext">Ein Bundle bringt eine ganze Zusammenstellung auf einmal, auch über Quellen hinweg. Fehlende Quellen legt hmgr dabei an.</p>
224
+ <div class="wahl" id="dsk-bundles"></div>
225
+ <p class="ohne dskgruppenfehler" id="dsk-bundlefehler" hidden></p>
226
+ </div>
227
+ <div class="dskgruppe">
228
+ <span class="etikett" id="dsk-skilletikett">Skills</span>
229
+ <div class="wahl" id="dsk-liste"></div>
230
+ </div>
193
231
  </div>
194
232
  <div class="dfuss">
195
233
  <span class="links mono" id="dsk-bilanz">0 ausgewählt</span>
@@ -457,6 +495,69 @@
457
495
  </form>
458
496
  </dialog>
459
497
 
498
+ <dialog id="d-mcp">
499
+ <form method="dialog">
500
+ <div class="dkopf">
501
+ <span class="etikett">Einhängen</span>
502
+ <h3>Wohin soll <span id="dm-name">der Server</span>?</h3>
503
+ <p id="dm-text">Ein Ort je Einhängen. agents kennt keine MCP-Server und steht deshalb nicht zur Wahl.</p>
504
+ </div>
505
+ <div class="dkoerper"><div class="wahl" id="dm-orte"></div></div>
506
+ <div class="dfuss">
507
+ <button class="knopf leise" value="abbruch">Abbrechen</button>
508
+ <button class="knopf signal" value="los" id="dm-los">Einhängen</button>
509
+ </div>
510
+ </form>
511
+ </dialog>
512
+
513
+ <dialog id="d-mcp-hinzufuegen">
514
+ <form method="dialog">
515
+ <div class="dkopf">
516
+ <span class="etikett">MCP hinzufügen</span>
517
+ <h3>Welche MCP-Server sollen in <span id="dmh-ziel">diesem Ziel</span> hängen?</h3>
518
+ <p>Kreuz jeden Server an, der hier eingehängt werden soll. Schon Eingehängtes ist ausgegraut.</p>
519
+ </div>
520
+ <div class="dkoerper">
521
+ <div class="wahl" id="dmh-liste"></div>
522
+ </div>
523
+ <div class="dfuss">
524
+ <span class="links mono" id="dmh-bilanz">0 ausgewählt</span>
525
+ <button class="knopf leise" value="abbruch">Abbrechen</button>
526
+ <button class="knopf signal" value="los" id="dmh-los">Einhängen</button>
527
+ </div>
528
+ </form>
529
+ </dialog>
530
+
531
+ <dialog id="d-mcpaus">
532
+ <form method="dialog">
533
+ <div class="dkopf">
534
+ <span class="etikett">Rückfrage</span>
535
+ <h3 id="dma-titel">Aushängen?</h3>
536
+ <p id="dma-text"></p>
537
+ </div>
538
+ <div class="dfuss">
539
+ <button class="knopf leise" value="nein">Nein, behalten</button>
540
+ <button class="knopf signal" value="ja">Ja, aushängen</button>
541
+ </div>
542
+ </form>
543
+ </dialog>
544
+
545
+ <!-- Uebernehmen aus der geteilten .mcp.json (E64): die einzige Aktion, mit der hmgr diese Datei
546
+ schreibt, deshalb eine eigene Rueckfrage vor dem Ausfuehren. -->
547
+ <dialog id="d-mcpueb">
548
+ <form method="dialog">
549
+ <div class="dkopf">
550
+ <span class="etikett">Rückfrage</span>
551
+ <h3 id="dmu-titel">Übernehmen?</h3>
552
+ <p id="dmu-text"></p>
553
+ </div>
554
+ <div class="dfuss">
555
+ <button class="knopf leise" value="nein">Nein, so lassen</button>
556
+ <button class="knopf signal" value="ja">Ja, übernehmen</button>
557
+ </div>
558
+ </form>
559
+ </dialog>
560
+
460
561
  <dialog id="d-einstellungen">
461
562
  <form method="dialog">
462
563
  <div class="dkopf">