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.
- package/README.md +10 -0
- package/dist/adapter/bundle-yaml.js +87 -0
- package/dist/adapter/checkout-pfad.js +10 -2
- package/dist/adapter/claude-code.js +231 -7
- package/dist/adapter/composer.js +4 -3
- package/dist/adapter/datei-text.js +18 -0
- package/dist/adapter/dateisystem.js +42 -4
- package/dist/adapter/git.js +182 -8
- package/dist/adapter/hermes.js +73 -1
- package/dist/adapter/marketplace-json.js +2 -1
- package/dist/adapter/mcp-katalog.js +124 -0
- package/dist/adapter/paket-version.js +25 -0
- package/dist/adapter/quelle-text.js +10 -1
- package/dist/adapter/skill-md.js +2 -1
- package/dist/adapter/umgebung.js +43 -0
- package/dist/adapter/vergleich.js +20 -0
- package/dist/adapter/zustand-json.js +11 -3
- package/dist/cli/hilfe.js +197 -0
- package/dist/cli/main.js +255 -7
- package/dist/gui/beobachter.js +108 -0
- package/dist/gui/login-pfad.js +120 -0
- package/dist/gui/main.js +462 -19
- package/dist/gui/mcp-ansicht.js +57 -0
- package/dist/gui/oeffnen.js +1 -1
- package/dist/gui/preload.cjs +20 -1
- package/dist/gui/renderer/index.html +108 -7
- package/dist/gui/renderer/renderer.js +1066 -142
- package/dist/gui/renderer/styles.css +85 -2
- package/dist/kern/anwendungsfaelle.js +624 -12
- package/dist/kern/modell.js +221 -3
- package/dist/kern/quelle-ort.js +14 -1
- package/dist/ports/harness.js +3 -0
- package/dist/ports/mcp-katalog.js +1 -0
- package/dist/ports/umgebung.js +1 -0
- package/dist/ports/zustand.js +15 -1
- package/package.json +59 -2
package/dist/cli/hilfe.js
CHANGED
|
@@ -22,6 +22,14 @@ hmgr <kommando> [schalter]
|
|
|
22
22
|
plugin add <name> Plugin in ein Ziel legen (installieren oder Skills verlinken)
|
|
23
23
|
plugin remove <name> Plugin aus einem Ziel entfernen
|
|
24
24
|
plugin list Plugins der Marketplaces aller Quellen
|
|
25
|
+
bundle add <name> Bundle als Ganzes in ein Ziel installieren
|
|
26
|
+
bundle remove <name> Bundle als Ganzes aus einem Ziel entfernen
|
|
27
|
+
bundle sync [name] Installierte Bundles gegen ihre Definition ziehen
|
|
28
|
+
bundle list Bundles der bekannten Quellen
|
|
29
|
+
mcp add <name> MCP-Server aus dem Katalog in ein Ziel einhängen
|
|
30
|
+
mcp remove <name> MCP-Server aus einem Ziel aushängen
|
|
31
|
+
mcp migrate <name> MCP-Server aus der geteilten .mcp.json in den privaten Scope übernehmen
|
|
32
|
+
mcp list Katalog-Einträge zeigen, mit wo sie eingehängt sind
|
|
25
33
|
sync [pfad] Ziel auf den Stand seines Zeigers bringen
|
|
26
34
|
update Alle Checkouts aktualisieren
|
|
27
35
|
status Ist-Zustand der Ziele
|
|
@@ -29,6 +37,7 @@ hmgr <kommando> [schalter]
|
|
|
29
37
|
harness enable <name> Harness-Typ aktivieren, hmgr richtet ihn künftig ein
|
|
30
38
|
harness disable <name> Harness-Typ deaktivieren, mit Angebot zum Säubern
|
|
31
39
|
harness skills-dir <name> Verwalteten Skill-Ordner zeigen, festlegen oder entfernen
|
|
40
|
+
--version Versionsnummer von hmgr ausgeben
|
|
32
41
|
|
|
33
42
|
hmgr help <kommando> zeigt Schalter, Beispiele und Zusammenhänge.`;
|
|
34
43
|
const SOURCE = `hmgr source add <url|pfad> [--name <name>]
|
|
@@ -226,6 +235,180 @@ Häufige Fälle:
|
|
|
226
235
|
kann nur die Quelle beheben.
|
|
227
236
|
|
|
228
237
|
Siehe auch: hmgr help source, hmgr help sync`;
|
|
238
|
+
const BUNDLE = `hmgr bundle add <name> [schalter]
|
|
239
|
+
hmgr bundle remove <name> [schalter]
|
|
240
|
+
hmgr bundle sync [name] [schalter]
|
|
241
|
+
hmgr bundle list
|
|
242
|
+
|
|
243
|
+
Ein Bundle ist eine benannte Zusammenstellung von Skills über Quellen hinweg: was für ein Vorhaben
|
|
244
|
+
zusammengehört, liegt selten in einem Repo. Anders als ein Plugin (das lebt in einem Marketplace und
|
|
245
|
+
löst Abhängigkeiten nur dort auf) darf ein Bundle Skills aus beliebig vielen Quellen nennen.
|
|
246
|
+
|
|
247
|
+
Definiert wird es von Hand, als YAML-Datei in einer Quelle, Konvention bundles/<name>.yaml. hmgr
|
|
248
|
+
liest und validiert sie, schreibt sie nie — über die Quelle kommt sie versioniert auf alle
|
|
249
|
+
Maschinen. Es gibt keinen eigenen Katalog und keinen Pflegebefehl: bundle list zeigt, was beim
|
|
250
|
+
Quellen-Scan gefunden wurde.
|
|
251
|
+
|
|
252
|
+
Format der Datei:
|
|
253
|
+
|
|
254
|
+
name optional, sonst gilt der Dateiname ohne Endung
|
|
255
|
+
description Pflicht, kurzer Text
|
|
256
|
+
skills Pflicht, nicht leere Liste; je Eintrag source (URL oder Pfad der Quelle)
|
|
257
|
+
und path (Pfad des Skill-Ordners in dieser Quelle)
|
|
258
|
+
|
|
259
|
+
Beispiel bundles/wordpress.yaml:
|
|
260
|
+
|
|
261
|
+
name: wordpress
|
|
262
|
+
description: Alles für die WordPress-Arbeit
|
|
263
|
+
skills:
|
|
264
|
+
- source: git@gitea.example.com:tim/skills.git
|
|
265
|
+
path: skills/wp-core
|
|
266
|
+
- source: https://github.com/taquiri/skills
|
|
267
|
+
path: skills/tq-mailform
|
|
268
|
+
|
|
269
|
+
Ein Ref je Mitglied kennt die Definition nicht: der Link folgt dem Ref, mit dem die Quelle
|
|
270
|
+
eingerichtet ist. Angesprochen wird der Skill über seinen Pfad, nicht über seinen Namen — der Name
|
|
271
|
+
steht im Frontmatter und wird erst beim Lesen der Quelle bekannt.
|
|
272
|
+
|
|
273
|
+
Je Ziel ist das Bundle eine Einheit: add legt alle seine Links, remove nimmt alle wieder weg, sync
|
|
274
|
+
zieht Änderungen an der Definition nach. Nennt die Definition eine Quelle, die hmgr noch nicht
|
|
275
|
+
kennt, legt add sie an und sagt, welche neu sind. Lässt sich auch nur ein Mitglied nicht auflösen
|
|
276
|
+
oder liegt am Ziel ein Namenskonflikt, bricht add ab, bevor irgendetwas verlinkt oder eine Quelle
|
|
277
|
+
angelegt ist — ein halbes Bundle wäre stilles Fehlen von Skills. Wer nur einen Teil braucht,
|
|
278
|
+
verlinkt die erreichbaren Skills einzeln mit hmgr link.
|
|
279
|
+
|
|
280
|
+
Liegt am Ziel schon derselbe Skill aus derselben Quelle als hmgr-Link, wird er als Mitglied
|
|
281
|
+
übernommen: aus Bestand wird ein Bundle, ohne erst Links von Hand zu entfernen. Alles andere unter
|
|
282
|
+
demselben Namen ist ein Konflikt und wird genannt.
|
|
283
|
+
|
|
284
|
+
hmgr unlink <skill> auf ein einzelnes Mitglied zerbricht das Bundle in diesem Ziel: die übrigen
|
|
285
|
+
Links bleiben als einzelne Links liegen, der Bundle-Verweis fällt weg. Das ist kein Fehler, hmgr
|
|
286
|
+
sagt es nur.
|
|
287
|
+
|
|
288
|
+
Den Abgleich macht schon hmgr update für alle installierten Bundles mit, es ist derselbe Vorgang
|
|
289
|
+
und dieselbe Ausgabe (E69): eine geänderte Definition kommt also mit dem gewöhnlichen
|
|
290
|
+
Aktualisieren an, ohne dass man von der Änderung wissen müsste. bundle sync ist der gezielte Weg
|
|
291
|
+
daneben, wenn nur ein Bundle oder nur ein Ziel gemeint ist; ohne Namen und ohne Ziel-Schalter geht
|
|
292
|
+
es denselben Bestand durch wie update. Was sich nicht vollständig nachziehen lässt, bleibt in
|
|
293
|
+
seinem Ziel auf dem alten Stand und wird als fehlgeschlagen gemeldet.
|
|
294
|
+
|
|
295
|
+
Der Zeiger bleibt außen vor: die Zugehörigkeit der Links führt hmgr in seinem Zustand, nicht in der
|
|
296
|
+
composer.json. --local gibt es deshalb nicht, und die Sperre aus E57 trifft bundle nie.
|
|
297
|
+
|
|
298
|
+
Schalter (add, remove und sync):
|
|
299
|
+
--harness <name> claude-code, hermes oder agents, Standard claude-code
|
|
300
|
+
--ort <ort> home, dir:<pfad> oder profil:<name>, Standard home. profil kennt nur hermes
|
|
301
|
+
--composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
|
|
302
|
+
--quelle <teil> nur bei add: Quelle eingrenzen, wenn den Namen mehrere Quellen führen
|
|
303
|
+
|
|
304
|
+
Beispiele:
|
|
305
|
+
hmgr bundle list
|
|
306
|
+
hmgr bundle add wordpress --ort dir:.
|
|
307
|
+
hmgr bundle sync
|
|
308
|
+
hmgr bundle remove wordpress --ort dir:.
|
|
309
|
+
|
|
310
|
+
Häufige Fälle:
|
|
311
|
+
„steht in keiner Quelle": hmgr bundle list zeigt die bekannten Namen, sonst erst hmgr source add.
|
|
312
|
+
„liegt in mehreren Quellen": mit --quelle <teilstring> entscheiden.
|
|
313
|
+
„ist auf dieser Maschine nicht vollständig herstellbar": die Meldung nennt je Mitglied den Grund,
|
|
314
|
+
meist eine Quelle ohne Zugriff oder ein Pfad, an dem kein Skill (mehr) liegt.
|
|
315
|
+
„lässt sich in … nicht anlegen": die Meldung listet alle Namen, die am Ziel schon belegt sind.
|
|
316
|
+
„kein Bundle … installiert": hmgr status zeigt je Ziel seine Bundles.
|
|
317
|
+
|
|
318
|
+
Siehe auch: hmgr help link, hmgr help plugin, hmgr help status`;
|
|
319
|
+
const MCP = `hmgr mcp add <name> [schalter]
|
|
320
|
+
hmgr mcp remove <name> [schalter]
|
|
321
|
+
hmgr mcp migrate <name> [schalter]
|
|
322
|
+
hmgr mcp list
|
|
323
|
+
|
|
324
|
+
Ein MCP-Server ist ein Eintrag im persönlichen Katalog ~/.hmgr/mcp.yaml, nicht in einer Quelle: kein
|
|
325
|
+
Download, keine Installation, kein Update. hmgr liest den Katalog nur, er wird von Hand oder von
|
|
326
|
+
einem LLM gepflegt, das den MCP ohnehin gerade einrichtet. mcp add hängt einen Katalog-Eintrag in
|
|
327
|
+
ein Ziel ein (Projekt oder global), mcp remove hängt ihn wieder aus, mcp list zeigt den Katalog mit
|
|
328
|
+
je Eintrag, wo er eingehängt ist, dazu Einträge, die in Harness-Konfigurationen liegen, aber nicht
|
|
329
|
+
im Katalog stehen (nur genannt, hmgr fasst sie nicht an). mcp list sieht dabei in den Homes und
|
|
330
|
+
Profilen der Harnesses nach und in den Arbeitsverzeichnissen, die hmgr kennt (hmgr dir add); ein
|
|
331
|
+
MCP in einem Verzeichnis, das hmgr nicht kennt, taucht dort nicht auf.
|
|
332
|
+
|
|
333
|
+
Format von ~/.hmgr/mcp.yaml: ein Objekt, Schlüssel ist der MCP-Name. Je Eintrag:
|
|
334
|
+
|
|
335
|
+
beschreibung Pflicht, kurzer Text
|
|
336
|
+
homepage optional, URL
|
|
337
|
+
notizen optional, mehrzeilig
|
|
338
|
+
konfig Pflicht: entweder command (Pflicht), optional args und env,
|
|
339
|
+
oder url (Pflicht), optional headers
|
|
340
|
+
|
|
341
|
+
Andere Schlüssel im konfig-Block sind ein Fehler, mit Eintrag und Feld in der Meldung. Secrets
|
|
342
|
+
stehen als \${VAR}- oder \${env:VAR}-Verweise im Block, nie im Klartext; aufgelöst werden sie vom
|
|
343
|
+
Harness gegen dessen Umgebung, hmgr prüft beim Einhängen nur, ob die Variablen gesetzt sind, und
|
|
344
|
+
warnt sonst. Fehlt die Datei, ist der Katalog leer, kein Fehler; kaputtes YAML ist ein Fehler mit
|
|
345
|
+
dem Dateipfad.
|
|
346
|
+
|
|
347
|
+
Beispiel:
|
|
348
|
+
|
|
349
|
+
teamwork:
|
|
350
|
+
beschreibung: Teamwork.com Projektmanagement
|
|
351
|
+
homepage: https://teamwork.com
|
|
352
|
+
notizen: |
|
|
353
|
+
Erfordert TEAMWORK_API_KEY, siehe Teamwork-Profil.
|
|
354
|
+
konfig:
|
|
355
|
+
command: npx
|
|
356
|
+
args: ["-y", "@teamwork/mcp-server"]
|
|
357
|
+
env:
|
|
358
|
+
TEAMWORK_API_KEY: \${TEAMWORK_API_KEY}
|
|
359
|
+
|
|
360
|
+
context7:
|
|
361
|
+
beschreibung: Context7 Doku-Suche
|
|
362
|
+
konfig:
|
|
363
|
+
url: https://mcp.context7.com/mcp
|
|
364
|
+
headers:
|
|
365
|
+
Authorization: Bearer \${env:CONTEXT7_TOKEN}
|
|
366
|
+
|
|
367
|
+
Die Fähigkeit hängt am Harness-Adapter, nicht am Namen (E16): claude-code kennt Projekt (Scope
|
|
368
|
+
local, nie die geteilte .mcp.json der Projektwurzel, das bleibt Team-Sache wie beim Zeiger, E57) und
|
|
369
|
+
global (Scope user). Hermes kennt nur global, mcp_servers in seiner config.yaml (Home oder Profil
|
|
370
|
+
mit --ort profil:<name>); ein Projekt-Ziel ist bei Hermes ein Fehler, es kennt keine Projektebene für
|
|
371
|
+
MCPs. agents kennt MCP-Server gar nicht.
|
|
372
|
+
|
|
373
|
+
mcp migrate ist der einzige Weg, auf dem hmgr die geteilte .mcp.json anfasst (E64): der Eintrag
|
|
374
|
+
wandert 1:1 aus der Datei in deinen privaten Scope am selben Ort und wird dort gelöscht. Auch ein
|
|
375
|
+
Eintrag, der nicht im Katalog steht, lässt sich so übernehmen — der Konfigblock kommt aus der Datei,
|
|
376
|
+
nicht aus dem Katalog. Ist die Datei mit dem Team geteilt, verlieren die anderen den Server, sobald
|
|
377
|
+
die Änderung committet ist. Nur claude-code hat eine solche Datei und nur an einem
|
|
378
|
+
Arbeitsverzeichnis. Gearbeitet wird in zwei Schritten, erst eintragen, dann entfernen: bricht der
|
|
379
|
+
zweite ab, steht der Eintrag doppelt statt gar nicht mehr, und die Meldung sagt, was zu tun bleibt.
|
|
380
|
+
|
|
381
|
+
Nach dem Einhängen/Aushängen nennt hmgr den Weg zur Wirkung: Claude Code übernimmt die Änderung erst
|
|
382
|
+
mit der nächsten Sitzung, Hermes lädt seine config.yaml selbst nach, dafür gibt es keinen Hinweis.
|
|
383
|
+
|
|
384
|
+
Fehlende \${VAR}-Verweise beim Einhängen sind kein Abbruch, nur eine Warnung (F39): hmgr kann nicht
|
|
385
|
+
wissen, was die Shell beim Start des Harness setzt. Geprüft wird gegen die eigene Prozess-Umgebung
|
|
386
|
+
und, bei einem Hermes-Ziel, zusätzlich gegen <hermes-home>/.env.
|
|
387
|
+
|
|
388
|
+
Schalter (add, remove und migrate):
|
|
389
|
+
--harness <name> claude-code oder hermes, Standard claude-code
|
|
390
|
+
--ort <ort> home, dir:<pfad> oder profil:<name>, Standard home
|
|
391
|
+
|
|
392
|
+
Beispiele:
|
|
393
|
+
hmgr mcp list
|
|
394
|
+
hmgr mcp add teamwork --ort dir:.
|
|
395
|
+
hmgr mcp add context7 --harness hermes --ort profil:researcher
|
|
396
|
+
hmgr mcp remove teamwork --ort dir:.
|
|
397
|
+
hmgr mcp migrate kollegen-tool --ort dir:.
|
|
398
|
+
|
|
399
|
+
Häufige Fälle:
|
|
400
|
+
„steht nicht im Katalog": hmgr mcp list zeigt die bekannten Namen, ~/.hmgr/mcp.yaml pflegt sie.
|
|
401
|
+
„kennt keine MCP-Server": nur claude-code und hermes haben diese Fähigkeit, agents nicht.
|
|
402
|
+
„kennt für MCP-Server den Ort … nicht – Hermes kennt keine Projektebene für MCPs": bei Hermes
|
|
403
|
+
--ort home oder --ort profil:<name> nutzen, kein dir:<pfad>.
|
|
404
|
+
„liegt schon ein MCP-Server …, der nicht von hmgr stammt oder vom Katalogblock abweicht": von Hand
|
|
405
|
+
angleichen oder hmgr mcp remove, dann erneut versuchen.
|
|
406
|
+
„steht in der geteilten …/.mcp.json": mcp remove fasst sie nicht an, hmgr mcp migrate <name> holt
|
|
407
|
+
den Eintrag in den privaten Scope und löscht ihn aus der Datei.
|
|
408
|
+
„gibt es keine geteilte Konfigurationsdatei": mcp migrate geht nur mit --harness claude-code und
|
|
409
|
+
--ort dir:<pfad>.
|
|
410
|
+
|
|
411
|
+
Siehe auch: hmgr help harness, hmgr help status`;
|
|
229
412
|
const SYNC = `hmgr sync [pfad]
|
|
230
413
|
|
|
231
414
|
Bringt ein Arbeitsverzeichnis auf den Stand seines Zeigers: fehlende Quellen übernehmen, fehlende
|
|
@@ -256,6 +439,18 @@ Plugin-Fähigkeit deren Plugins nach (E14). Die Links zeigen weiter auf dieselbe
|
|
|
256
439
|
nicht erneuert werden. Ein Checkout mit lokaler Änderung wird übersprungen und gemeldet (E15),
|
|
257
440
|
damit nichts verloren geht. Lokale Quellen haben keinen Checkout und bleiben unberührt.
|
|
258
441
|
|
|
442
|
+
Unter jeder „aktualisiert"-Zeile steht, welche Skills der neue Stand geändert hat (E70): die Namen
|
|
443
|
+
kommen aus dem Vergleich von altem und neuem Commit, gezählt werden nur Skills, die in diesem
|
|
444
|
+
Checkout auch ausgecheckt sind. Berührt ein Commit nur Dateien daneben (README, CI), bleibt die
|
|
445
|
+
Zeile weg.
|
|
446
|
+
|
|
447
|
+
Im selben Lauf gleicht update die installierten Bundles gegen ihre Definitionen ab (E69): eine
|
|
448
|
+
Definition, die inzwischen ein Mitglied mehr oder weniger nennt, wird in jedem Ziel nachgezogen, in
|
|
449
|
+
dem das Bundle hängt. Das ist der Weg, auf dem eine Bundle-Änderung aus einer Quelle ankommt — ohne
|
|
450
|
+
ihn müsste man von seinen eigenen Änderungen wissen, um sie zu holen. Was sich nicht vollständig
|
|
451
|
+
nachziehen lässt, bleibt in seinem Ziel auf dem alten Stand und wird als fehlgeschlagen gemeldet,
|
|
452
|
+
ohne den übrigen Lauf aufzuhalten. hmgr bundle sync macht dasselbe für ein einzelnes Bundle.
|
|
453
|
+
|
|
259
454
|
Danach räumt update verwaiste Ref-Verzeichnisse auf: Checkouts, in die kein Link mehr zeigt, weil
|
|
260
455
|
ein Link inzwischen auf einen anderen Ref umgelegt wurde. Vor dem Löschen fragt hmgr einmal für
|
|
261
456
|
alle Kandidaten. Ohne Bestätigung, also auch ohne Terminal, bleibt alles liegen und wird nur
|
|
@@ -359,6 +554,8 @@ const THEMEN = new Map([
|
|
|
359
554
|
['unlink', UNLINK],
|
|
360
555
|
['adopt', ADOPT],
|
|
361
556
|
['plugin', PLUGIN],
|
|
557
|
+
['bundle', BUNDLE],
|
|
558
|
+
['mcp', MCP],
|
|
362
559
|
['sync', SYNC],
|
|
363
560
|
['update', UPDATE],
|
|
364
561
|
['status', STATUS],
|
package/dist/cli/main.js
CHANGED
|
@@ -4,16 +4,19 @@ import path from 'node:path';
|
|
|
4
4
|
import { realpathSync } from 'node:fs';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
|
-
import { Anwendungsfaelle } from "../kern/anwendungsfaelle.js";
|
|
8
|
-
import { AbhaengigkeitFehlt, EinschraenkungOhneZeiger, HarnessNichtVorhanden, HarnessTypUnbekannt, KeinPassenderRef, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
7
|
+
import { Anwendungsfaelle, } from "../kern/anwendungsfaelle.js";
|
|
8
|
+
import { AbhaengigkeitFehlt, BundleDateiFehler, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, HarnessNichtVorhanden, HarnessTypUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
9
9
|
import { Agents } from "../adapter/agents.js";
|
|
10
10
|
import { ClaudeCode } from "../adapter/claude-code.js";
|
|
11
11
|
import { Hermes } from "../adapter/hermes.js";
|
|
12
12
|
import { ComposerZeiger } from "../adapter/composer.js";
|
|
13
13
|
import { DateisystemZiele } from "../adapter/dateisystem.js";
|
|
14
14
|
import { GitQuellen } from "../adapter/git.js";
|
|
15
|
+
import { DateiMcpKatalog } from "../adapter/mcp-katalog.js";
|
|
16
|
+
import { paketVersion } from "../adapter/paket-version.js";
|
|
15
17
|
import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
|
|
16
18
|
import { TerminalRueckfrage } from "../adapter/terminal.js";
|
|
19
|
+
import { ProzessUmgebung } from "../adapter/umgebung.js";
|
|
17
20
|
import { ZustandJson } from "../adapter/zustand-json.js";
|
|
18
21
|
import { HILFE, hilfeText } from "./hilfe.js";
|
|
19
22
|
export function umgebungLesen(env = process.env) {
|
|
@@ -38,6 +41,8 @@ export function bauen(umgebung, optionen = {}) {
|
|
|
38
41
|
zeiger: new ComposerZeiger(),
|
|
39
42
|
zustand,
|
|
40
43
|
rueckfrage: optionen.rueckfrage ?? new TerminalRueckfrage(),
|
|
44
|
+
mcpKatalog: new DateiMcpKatalog(path.join(umgebung.hmgrHome, 'mcp.yaml')),
|
|
45
|
+
umgebung: optionen.umgebung ?? new ProzessUmgebung(),
|
|
41
46
|
};
|
|
42
47
|
return { anwendungsfaelle: new Anwendungsfaelle(ports), ports };
|
|
43
48
|
}
|
|
@@ -55,6 +60,8 @@ const SCHALTER = {
|
|
|
55
60
|
unlink: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal'] },
|
|
56
61
|
adopt: { werte: ['harness', 'ort', 'composer'], flaggen: ['local', 'lokal', 'probelauf'] },
|
|
57
62
|
plugin: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: ['local', 'lokal'] },
|
|
63
|
+
bundle: { werte: ['harness', 'ort', 'composer', 'quelle'], flaggen: [] },
|
|
64
|
+
mcp: { werte: ['harness', 'ort'], flaggen: [] },
|
|
58
65
|
sync: { werte: [], flaggen: [] },
|
|
59
66
|
update: { werte: [], flaggen: [] },
|
|
60
67
|
status: { werte: [], flaggen: [] },
|
|
@@ -82,6 +89,20 @@ const UNTERKOMMANDOS = {
|
|
|
82
89
|
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'local', 'lokal'] },
|
|
83
90
|
list: { woerter: 1, schalter: [] },
|
|
84
91
|
},
|
|
92
|
+
bundle: {
|
|
93
|
+
add: { woerter: 2, schalter: ['harness', 'ort', 'composer', 'quelle'] },
|
|
94
|
+
remove: { woerter: 2, schalter: ['harness', 'ort', 'composer'] },
|
|
95
|
+
// sync nimmt einen Bundle-Namen, aber keinen zwingend: ohne Namen gilt der ganze Bestand.
|
|
96
|
+
sync: { woerter: 2, schalter: ['harness', 'ort', 'composer'] },
|
|
97
|
+
list: { woerter: 1, schalter: [] },
|
|
98
|
+
},
|
|
99
|
+
mcp: {
|
|
100
|
+
add: { woerter: 2, schalter: ['harness', 'ort'] },
|
|
101
|
+
remove: { woerter: 2, schalter: ['harness', 'ort'] },
|
|
102
|
+
// Übernehmen (E64): dieselben Ziel-Schalter wie add und remove.
|
|
103
|
+
migrate: { woerter: 2, schalter: ['harness', 'ort'] },
|
|
104
|
+
list: { woerter: 1, schalter: [] },
|
|
105
|
+
},
|
|
85
106
|
harness: {
|
|
86
107
|
list: { woerter: 1, schalter: [] },
|
|
87
108
|
enable: { woerter: 2, schalter: [] },
|
|
@@ -167,6 +188,11 @@ const UEBERNAHME_GRUND = {
|
|
|
167
188
|
export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
168
189
|
const { anwendungsfaelle, ports } = bauen(umgebung, optionen);
|
|
169
190
|
const kommando = argv[0];
|
|
191
|
+
// Version vor allem anderen (E72): nur die Nummer, damit ein Skript sie ohne Nacharbeit liest.
|
|
192
|
+
if (kommando === '--version') {
|
|
193
|
+
console.log(paketVersion());
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
170
196
|
// Hilfe zuerst: --help ist überall eine Flagge, damit kein Kommando sie als Wert liest.
|
|
171
197
|
if (kommando === undefined) {
|
|
172
198
|
console.log(HILFE);
|
|
@@ -207,7 +233,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
207
233
|
console.log(quelleAlsText(quelle) === quelleAlsText(genannt)
|
|
208
234
|
? 'Quelle ist schon bekannt, keine zweite angelegt.'
|
|
209
235
|
: `Quelle ist schon bekannt, geführt als ${quelleAlsText(quelle)}. Keine zweite angelegt.`);
|
|
210
|
-
console.log(`Quelle ${quelleAlsText(quelle)} (${quelle.art}) als „${quelleName(quelle)}", ${skills.length} Skills:`);
|
|
236
|
+
console.log(`Quelle ${quelleAlsText(quelle)} (${quelle.art}) als „${quelleName(quelle)}", ${skills.length} ${skills.length === 1 ? 'Skill' : 'Skills'}:`);
|
|
211
237
|
for (const skill of skills) {
|
|
212
238
|
const beschreibung = skill.beschreibung === undefined ? '' : ` ${kappen(skill.beschreibung)}`;
|
|
213
239
|
console.log(` ${skill.name} ${skill.pfad}${beschreibung}`);
|
|
@@ -365,6 +391,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
365
391
|
if (kommando === 'unlink') {
|
|
366
392
|
const ergebnis = await anwendungsfaelle.linkEntfernen({ skill, ziel, lokal });
|
|
367
393
|
console.log(`Link ${skill} entfernt.`);
|
|
394
|
+
// Zerbrochen (E69): die übrigen Links des Bundles bleiben als Einzel-Installationen liegen.
|
|
395
|
+
for (const bundle of ergebnis.zerbrochen)
|
|
396
|
+
console.log(`Bundle „${bundle}" ist damit zerbrochen, seine übrigen Links bleiben als einzelne Links liegen.`);
|
|
368
397
|
if (ergebnis.hinweis !== undefined)
|
|
369
398
|
console.log(ergebnis.hinweis);
|
|
370
399
|
return 0;
|
|
@@ -478,6 +507,147 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
478
507
|
console.log(` ${zeile}`);
|
|
479
508
|
return 0;
|
|
480
509
|
}
|
|
510
|
+
if (kommando === 'bundle') {
|
|
511
|
+
const schalter = schalterLesen(argv.slice(1), 'bundle');
|
|
512
|
+
unterkommandoPruefen('bundle', schalter);
|
|
513
|
+
const { rest, werte } = schalter;
|
|
514
|
+
const unterkommando = rest[0];
|
|
515
|
+
if (unterkommando === 'list') {
|
|
516
|
+
const katalog = await anwendungsfaelle.bundleKatalog();
|
|
517
|
+
const leer = katalog.every((k) => k.bundles.length === 0 && k.kaputte.length === 0 && k.fehler === undefined);
|
|
518
|
+
if (leer)
|
|
519
|
+
console.log('Keine Quelle mit Bundles. hmgr help bundle zeigt das Format von bundles/<name>.yaml.');
|
|
520
|
+
for (const eintrag of katalog) {
|
|
521
|
+
if (eintrag.fehler !== undefined) {
|
|
522
|
+
console.log(`Quelle ${quelleName(eintrag.quelle)} (${quelleAlsText(eintrag.quelle)}) nicht lesbar: ${eintrag.fehler}`);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (eintrag.bundles.length === 0 && eintrag.kaputte.length === 0)
|
|
526
|
+
continue;
|
|
527
|
+
console.log(`Quelle ${quelleName(eintrag.quelle)} (${quelleAlsText(eintrag.quelle)})`);
|
|
528
|
+
for (const bundle of eintrag.bundles) {
|
|
529
|
+
console.log(` ${bundle.name} ${kappen(bundle.beschreibung)}`);
|
|
530
|
+
for (const mitglied of bundle.mitglieder)
|
|
531
|
+
console.log(` ${mitglied.pfad} aus ${quelleAlsText(mitglied.quelle)}`);
|
|
532
|
+
}
|
|
533
|
+
// Eine kaputte Definition verdeckt die übrigen nicht, sie wird eigens genannt (E69).
|
|
534
|
+
for (const kaputt of eintrag.kaputte)
|
|
535
|
+
console.log(` Fehler ${kaputt.datei}: ${kaputt.fehler}`);
|
|
536
|
+
}
|
|
537
|
+
return 0;
|
|
538
|
+
}
|
|
539
|
+
if (unterkommando !== 'add' && unterkommando !== 'remove' && unterkommando !== 'sync')
|
|
540
|
+
throw new Error(`${unterkommando === undefined ? 'bundle braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für bundle`}: bundle add <name>, bundle remove <name>, bundle sync [name] oder bundle list. hmgr help bundle zeigt Beispiele.`);
|
|
541
|
+
const name = rest[1];
|
|
542
|
+
if (name === undefined && unterkommando !== 'sync')
|
|
543
|
+
throw new Error(`bundle ${unterkommando} braucht einen Bundle-Namen. hmgr bundle list zeigt die Bundles der bekannten Quellen.`);
|
|
544
|
+
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
|
|
545
|
+
if (ziel.ort.art === 'arbeitsverzeichnis') {
|
|
546
|
+
// Wie bei link: Verzeichnis merken und binden, damit status es wiederfindet (E21).
|
|
547
|
+
const composer = werte.get('composer');
|
|
548
|
+
if (composer === undefined)
|
|
549
|
+
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad);
|
|
550
|
+
else
|
|
551
|
+
await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(ziel.ort.pfad, composer);
|
|
552
|
+
}
|
|
553
|
+
if (unterkommando === 'add') {
|
|
554
|
+
const quelle = werte.get('quelle');
|
|
555
|
+
const ergebnis = await anwendungsfaelle.bundleInstallieren({
|
|
556
|
+
bundle: name,
|
|
557
|
+
ziel,
|
|
558
|
+
...(quelle === undefined ? {} : { quelle }),
|
|
559
|
+
});
|
|
560
|
+
console.log(`Bundle ${ergebnis.bundle.name} in ${ziel.harness} ${beschreibeOrt(ziel.ort)} installiert (${quelleAlsText(ergebnis.bundle.quelle)}).`);
|
|
561
|
+
for (const neu of ergebnis.neueQuellen)
|
|
562
|
+
console.log(` neue Quelle ${quelleAlsText(neu)} als „${quelleName(neu)}"`);
|
|
563
|
+
if (ergebnis.gelegt.length > 0)
|
|
564
|
+
console.log(` gelegt ${ergebnis.gelegt.join(', ')}`);
|
|
565
|
+
// Übernommen (F67): schon vorhandene Links derselben Quelle gehören jetzt zum Bundle.
|
|
566
|
+
if (ergebnis.uebernommen.length > 0)
|
|
567
|
+
console.log(` übernommen ${ergebnis.uebernommen.join(', ')}`);
|
|
568
|
+
if (ergebnis.hinweis !== undefined)
|
|
569
|
+
for (const zeile of ergebnis.hinweis.split('\n'))
|
|
570
|
+
console.log(` ${zeile}`);
|
|
571
|
+
return 0;
|
|
572
|
+
}
|
|
573
|
+
if (unterkommando === 'remove') {
|
|
574
|
+
const ergebnis = await anwendungsfaelle.bundleEntfernen({ bundle: name, ziel });
|
|
575
|
+
console.log(`Bundle ${ergebnis.bundle} aus ${ziel.harness} ${beschreibeOrt(ziel.ort)} entfernt.`);
|
|
576
|
+
console.log(ergebnis.entfernt.length === 0
|
|
577
|
+
? ' Kein Link mehr da, nur der Bundle-Verweis ist weg.'
|
|
578
|
+
: ` entfernt ${ergebnis.entfernt.join(', ')}`);
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
// sync: ohne Ziel-Schalter der ganze Bestand, mit --ort/--harness nur dieses Ziel.
|
|
582
|
+
const nurZiel = werte.has('harness') || werte.has('ort');
|
|
583
|
+
const eintraege = await anwendungsfaelle.bundleAbgleich({
|
|
584
|
+
...(name === undefined ? {} : { bundle: name }),
|
|
585
|
+
...(nurZiel ? { ziel } : {}),
|
|
586
|
+
});
|
|
587
|
+
if (eintraege.length === 0)
|
|
588
|
+
console.log('Kein Bundle installiert, nichts abzugleichen.');
|
|
589
|
+
for (const zeile of bundleBericht(eintraege))
|
|
590
|
+
console.log(zeile);
|
|
591
|
+
return 0;
|
|
592
|
+
}
|
|
593
|
+
if (kommando === 'mcp') {
|
|
594
|
+
const schalter = schalterLesen(argv.slice(1), 'mcp');
|
|
595
|
+
unterkommandoPruefen('mcp', schalter);
|
|
596
|
+
const { rest, werte } = schalter;
|
|
597
|
+
const unterkommando = rest[0];
|
|
598
|
+
if (unterkommando === 'list') {
|
|
599
|
+
const uebersicht = await anwendungsfaelle.mcpUebersicht();
|
|
600
|
+
if (uebersicht.katalog.length === 0)
|
|
601
|
+
console.log('Kein MCP im Katalog. hmgr help mcp zeigt das Format von ~/.hmgr/mcp.yaml.');
|
|
602
|
+
for (const { eintrag, eingehaengt, geteilteFundorte } of uebersicht.katalog) {
|
|
603
|
+
console.log(`${eintrag.name} ${eintrag.beschreibung}`);
|
|
604
|
+
if (eingehaengt.length === 0 && geteilteFundorte.length === 0)
|
|
605
|
+
console.log(' nirgends eingehängt');
|
|
606
|
+
for (const ziel of eingehaengt)
|
|
607
|
+
console.log(` ${ziel.harness} ${beschreibeOrt(ziel.ort)}`);
|
|
608
|
+
// Geteilte, nur lesbare Fundorte (E61-Nachtrag): eigens gekennzeichnet, hmgr fasst sie nicht an.
|
|
609
|
+
for (const f of geteilteFundorte)
|
|
610
|
+
console.log(` ${f.ziel.harness} ${beschreibeOrt(f.ziel.ort)} ${f.datei} · Scope ${f.scope} (geteilt, hmgr fasst sie nicht an)`);
|
|
611
|
+
}
|
|
612
|
+
if (uebersicht.unbekannt.length > 0 || uebersicht.unbekannteGeteilteFundorte.length > 0) {
|
|
613
|
+
console.log('Nicht im Katalog, aber eingehängt (hmgr fasst sie nicht an):');
|
|
614
|
+
for (const { ziel, name } of uebersicht.unbekannt)
|
|
615
|
+
console.log(` ${name} ${ziel.harness} ${beschreibeOrt(ziel.ort)}`);
|
|
616
|
+
for (const f of uebersicht.unbekannteGeteilteFundorte)
|
|
617
|
+
console.log(` ${f.name} ${f.ziel.harness} ${beschreibeOrt(f.ziel.ort)} ${f.datei} · Scope ${f.scope}`);
|
|
618
|
+
}
|
|
619
|
+
return 0;
|
|
620
|
+
}
|
|
621
|
+
if (unterkommando !== 'add' && unterkommando !== 'remove' && unterkommando !== 'migrate')
|
|
622
|
+
throw new Error(`${unterkommando === undefined ? 'mcp braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für mcp`}: mcp add <name>, mcp remove <name>, mcp migrate <name> oder mcp list. hmgr help mcp zeigt Beispiele.`);
|
|
623
|
+
const name = rest[1];
|
|
624
|
+
if (name === undefined)
|
|
625
|
+
throw new Error(`mcp ${unterkommando} braucht einen MCP-Namen. hmgr mcp list zeigt die Katalog-Einträge.`);
|
|
626
|
+
const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
|
|
627
|
+
// Übernehmen (E64): der Name muss nicht im Katalog stehen, der Block kommt aus der geteilten Datei.
|
|
628
|
+
if (unterkommando === 'migrate') {
|
|
629
|
+
const ergebnis = await anwendungsfaelle.mcpUebernehmen({ name, ziel });
|
|
630
|
+
console.log(`MCP ${ergebnis.name} in den privaten Scope von ${ziel.harness} ${beschreibeOrt(ziel.ort)} übernommen `
|
|
631
|
+
+ `und aus ${ergebnis.datei} entfernt.`);
|
|
632
|
+
if (ergebnis.hinweis !== undefined)
|
|
633
|
+
console.log(` ${ergebnis.hinweis}`);
|
|
634
|
+
return 0;
|
|
635
|
+
}
|
|
636
|
+
if (unterkommando === 'add') {
|
|
637
|
+
const ergebnis = await anwendungsfaelle.mcpEinhaengen({ name, ziel });
|
|
638
|
+
console.log(`MCP ${ergebnis.eintrag.name} eingehängt in ${ziel.harness} ${beschreibeOrt(ziel.ort)}.`);
|
|
639
|
+
if (ergebnis.hinweis !== undefined)
|
|
640
|
+
console.log(` ${ergebnis.hinweis}`);
|
|
641
|
+
if (ergebnis.warnung !== undefined)
|
|
642
|
+
console.log(` ${ergebnis.warnung}`);
|
|
643
|
+
return 0;
|
|
644
|
+
}
|
|
645
|
+
const ergebnis = await anwendungsfaelle.mcpAushaengen({ name, ziel });
|
|
646
|
+
console.log(`MCP ${ergebnis.eintrag.name} ausgehängt aus ${ziel.harness} ${beschreibeOrt(ziel.ort)}.`);
|
|
647
|
+
if (ergebnis.hinweis !== undefined)
|
|
648
|
+
console.log(` ${ergebnis.hinweis}`);
|
|
649
|
+
return 0;
|
|
650
|
+
}
|
|
481
651
|
if (kommando === 'sync') {
|
|
482
652
|
const { rest } = schalterLesen(argv.slice(1), 'sync');
|
|
483
653
|
restPruefen(rest, 1, 'sync');
|
|
@@ -498,13 +668,18 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
498
668
|
}
|
|
499
669
|
if (kommando === 'update') {
|
|
500
670
|
restPruefen(schalterLesen(argv.slice(1), 'update').rest, 0, 'update');
|
|
501
|
-
const { checkouts: liste, pluginHarnesses, aufraeumen } = await anwendungsfaelle.aktualisieren();
|
|
671
|
+
const { checkouts: liste, pluginHarnesses, bundles, aufraeumen } = await anwendungsfaelle.aktualisieren();
|
|
502
672
|
if (liste.length === 0)
|
|
503
673
|
console.log('Keine Checkouts.');
|
|
504
|
-
for (const { checkout, aktualisierung } of liste) {
|
|
674
|
+
for (const { checkout, aktualisierung, geaenderteSkills } of liste) {
|
|
505
675
|
const kopf = `${quelleAlsText(checkout.quelle)} @ ${checkout.ref}`;
|
|
506
|
-
if (aktualisierung.ergebnis === 'aktualisiert')
|
|
676
|
+
if (aktualisierung.ergebnis === 'aktualisiert') {
|
|
507
677
|
console.log(` aktualisiert ${kopf} ${aktualisierung.von.slice(0, 8)} -> ${aktualisierung.nach.slice(0, 8)}`);
|
|
678
|
+
// Auf Skill-Ebene berichten (E70): sonst steht da nur, dass die Quelle neue Commits hat.
|
|
679
|
+
// Ohne Treffer bleibt die Zeile weg — der Commit hat dann nichts in einem Skill geändert.
|
|
680
|
+
if (geaenderteSkills.length > 0)
|
|
681
|
+
console.log(` ${zahlwort(geaenderteSkills.length, 'Skill', 'Skills')} geändert: ${geaenderteSkills.join(', ')}`);
|
|
682
|
+
}
|
|
508
683
|
else if (aktualisierung.ergebnis === 'unveraendert')
|
|
509
684
|
console.log(` unverändert ${kopf}`);
|
|
510
685
|
else if (aktualisierung.ergebnis === 'uebersprungen')
|
|
@@ -514,6 +689,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
514
689
|
}
|
|
515
690
|
for (const name of pluginHarnesses)
|
|
516
691
|
console.log(` Plugins ${name} aktualisiert (E14)`);
|
|
692
|
+
// Bundles im selben Lauf (E69, F64=O1): eine geänderte Definition wird hier nachgezogen.
|
|
693
|
+
for (const zeile of bundleBericht(bundles))
|
|
694
|
+
console.log(zeile);
|
|
517
695
|
// Zum Schluss die verwaisten Checkouts: was gelöscht wurde und was liegen bleibt.
|
|
518
696
|
for (const verwaist of aufraeumen.verwaiste) {
|
|
519
697
|
const kopf = `${quelleAlsText(verwaist.checkout.quelle)} @ ${verwaist.checkout.ref}`;
|
|
@@ -548,6 +726,9 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
548
726
|
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))}`);
|
|
549
727
|
}
|
|
550
728
|
}
|
|
729
|
+
// Bundles des Ziels (E69), unter den Einträgen: welche Links als Einheit zusammengehören.
|
|
730
|
+
for (const bundle of zielZustand.bundles)
|
|
731
|
+
console.log(` Bundle ${bundle.name} (${quelleAlsText(bundle.quelle)}) ${bundle.skills.join(', ')}`);
|
|
551
732
|
if (zielZustand.plugins !== undefined)
|
|
552
733
|
console.log(` Plugins: ${zielZustand.plugins.length === 0 ? 'keine' : zielZustand.plugins.join(', ')}`);
|
|
553
734
|
}
|
|
@@ -668,6 +849,31 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
|
|
|
668
849
|
}
|
|
669
850
|
throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
|
|
670
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* Bericht über den Bundle-Abgleich (E69), gleich für `hmgr bundle sync` und `hmgr update`: je
|
|
854
|
+
* Bundle eine Zeile mit seinem Ausgang, darunter, was sich geändert hat. „fehlgeschlagen" heißt
|
|
855
|
+
* genau das — der Zusatz sagt, dass das Bundle im Ziel auf dem alten Stand bleibt.
|
|
856
|
+
*/
|
|
857
|
+
function bundleBericht(eintraege) {
|
|
858
|
+
const zeilen = [];
|
|
859
|
+
for (const eintrag of eintraege) {
|
|
860
|
+
const kopf = `Bundle ${eintrag.bundle} ${eintrag.ziel.harness} ${beschreibeOrt(eintrag.ziel.ort)}`;
|
|
861
|
+
if (eintrag.ergebnis === 'fehlgeschlagen') {
|
|
862
|
+
zeilen.push(` fehlgeschlagen ${kopf} bleibt auf dem alten Stand`);
|
|
863
|
+
for (const zeile of (eintrag.fehler ?? '').split('\n'))
|
|
864
|
+
zeilen.push(` ${zeile}`);
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
zeilen.push(` ${eintrag.ergebnis === 'aktualisiert' ? 'aktualisiert ' : 'unverändert '} ${kopf}`);
|
|
868
|
+
for (const neu of eintrag.neueQuellen)
|
|
869
|
+
zeilen.push(` neue Quelle ${quelleAlsText(neu)}`);
|
|
870
|
+
if (eintrag.gelegt.length > 0)
|
|
871
|
+
zeilen.push(` gelegt ${eintrag.gelegt.join(', ')}`);
|
|
872
|
+
if (eintrag.entfernt.length > 0)
|
|
873
|
+
zeilen.push(` entfernt ${eintrag.entfernt.join(', ')}`);
|
|
874
|
+
}
|
|
875
|
+
return zeilen;
|
|
876
|
+
}
|
|
671
877
|
/** Grund eines Links als Suffix (E37). „manuell" bleibt stumm, damit die Ausgabe ruhig bleibt. */
|
|
672
878
|
function beschreibeGrund(grund) {
|
|
673
879
|
if (grund === undefined || grund.art === 'manuell')
|
|
@@ -691,6 +897,7 @@ function beschreibeOrt(ort) {
|
|
|
691
897
|
}
|
|
692
898
|
function istModellFehler(fehler) {
|
|
693
899
|
return (fehler instanceof NameBelegt ||
|
|
900
|
+
fehler instanceof LinkAnlegenFehlgeschlagen ||
|
|
694
901
|
fehler instanceof SkillMehrdeutig ||
|
|
695
902
|
fehler instanceof SkillUnbekannt ||
|
|
696
903
|
fehler instanceof OrtNichtUnterstuetzt ||
|
|
@@ -703,13 +910,28 @@ function istModellFehler(fehler) {
|
|
|
703
910
|
fehler instanceof PluginMehrdeutig ||
|
|
704
911
|
fehler instanceof PluginUnbekannt ||
|
|
705
912
|
fehler instanceof AbhaengigkeitFehlt ||
|
|
913
|
+
fehler instanceof BundleUnbekannt ||
|
|
914
|
+
fehler instanceof BundleMehrdeutig ||
|
|
915
|
+
fehler instanceof BundleMitgliederFehlen ||
|
|
916
|
+
fehler instanceof BundleNamenskonflikte ||
|
|
917
|
+
fehler instanceof BundleNichtInstalliert ||
|
|
918
|
+
fehler instanceof BundleDateiFehler ||
|
|
706
919
|
fehler instanceof QuelleUnbekannt ||
|
|
707
920
|
fehler instanceof QuelleNameBelegt ||
|
|
708
921
|
fehler instanceof QuelleAufraeumenLokaleAenderungen ||
|
|
709
922
|
fehler instanceof SkillOrdnerFaehigkeitFehlt ||
|
|
710
923
|
fehler instanceof SkillOrdnerBereitsVerwaltet ||
|
|
711
924
|
fehler instanceof SkillOrdnerNichtVerwaltet ||
|
|
712
|
-
fehler instanceof SkillOrdnerUngueltig
|
|
925
|
+
fehler instanceof SkillOrdnerUngueltig ||
|
|
926
|
+
fehler instanceof McpUnbekannt ||
|
|
927
|
+
fehler instanceof McpFaehigkeitFehlt ||
|
|
928
|
+
fehler instanceof McpEbeneNichtUnterstuetzt ||
|
|
929
|
+
fehler instanceof McpAbweichenderEintrag ||
|
|
930
|
+
fehler instanceof McpKatalogFehler ||
|
|
931
|
+
fehler instanceof McpNurGeteilterFundort ||
|
|
932
|
+
fehler instanceof McpUebernahmeNichtMoeglich ||
|
|
933
|
+
fehler instanceof McpNichtInGeteilterDatei ||
|
|
934
|
+
fehler instanceof McpUebernahmeUnvollstaendig);
|
|
713
935
|
}
|
|
714
936
|
/**
|
|
715
937
|
* Der nächste Schritt zu einem Fehler aus dem Kern. Die Fehlertexte selbst bleiben, wie das Modell
|
|
@@ -728,6 +950,18 @@ function naechsterSchritt(fehler) {
|
|
|
728
950
|
return 'hmgr plugin list zeigt die Plugins der bekannten Quellen. Fehlt der Marketplace, erst hmgr source add <url>.';
|
|
729
951
|
if (fehler instanceof AbhaengigkeitFehlt)
|
|
730
952
|
return 'Die Abhängigkeit steht in keinem Plugin desselben Marketplace, das kann nur die Quelle beheben.';
|
|
953
|
+
if (fehler instanceof BundleUnbekannt)
|
|
954
|
+
return 'hmgr bundle list zeigt die Bundles der bekannten Quellen. Fehlt die Quelle, erst hmgr source add <url|pfad>.';
|
|
955
|
+
if (fehler instanceof BundleMehrdeutig)
|
|
956
|
+
return 'Quelle eingrenzen: --quelle <teilstring der URL oder des Pfads>. hmgr bundle list zeigt, wo welches Bundle steht.';
|
|
957
|
+
if (fehler instanceof BundleMitgliederFehlen)
|
|
958
|
+
return 'Das Bundle ist nur ganz oder gar nicht zu haben. Wer nur einen Teil braucht, verlinkt die erreichbaren Skills einzeln mit hmgr link.';
|
|
959
|
+
if (fehler instanceof BundleNamenskonflikte)
|
|
960
|
+
return 'Die genannten Namen erst freimachen (hmgr unlink <skill> bzw. hmgr adopt <name>), dann bundle add erneut.';
|
|
961
|
+
if (fehler instanceof BundleNichtInstalliert)
|
|
962
|
+
return 'hmgr status zeigt je Ziel, welche Bundles dort installiert sind.';
|
|
963
|
+
if (fehler instanceof BundleDateiFehler)
|
|
964
|
+
return 'Die Definition liegt in der Quelle und wird dort korrigiert, hmgr schreibt sie nie. hmgr help bundle zeigt das Format.';
|
|
731
965
|
if (fehler instanceof KeinPassenderRef)
|
|
732
966
|
return 'Die Tags der Quelle sind frisch geholt, keiner passt. Sonst einen vorhandenen Tag- oder Branchnamen als --version nennen.';
|
|
733
967
|
if (fehler instanceof HarnessNichtVorhanden)
|
|
@@ -752,6 +986,20 @@ function naechsterSchritt(fehler) {
|
|
|
752
986
|
return fehler.grund === 'ausserhalb'
|
|
753
987
|
? 'Der Pfad muss unterhalb des Hermes-Home liegen.'
|
|
754
988
|
: 'Das ist Hermes\' eigener Skill-Ordner, ein anderer Pfad muss her.';
|
|
989
|
+
if (fehler instanceof McpUnbekannt)
|
|
990
|
+
return 'hmgr mcp list zeigt die Katalog-Einträge. Gepflegt wird der Katalog in ~/.hmgr/mcp.yaml, hmgr help mcp zeigt das Format.';
|
|
991
|
+
if (fehler instanceof McpFaehigkeitFehlt)
|
|
992
|
+
return 'Nur claude-code und hermes kennen MCP-Server.';
|
|
993
|
+
if (fehler instanceof McpEbeneNichtUnterstuetzt)
|
|
994
|
+
return 'claude-code kennt --ort home und --ort dir:<pfad>, hermes --ort home und --ort profil:<name>.';
|
|
995
|
+
if (fehler instanceof McpAbweichenderEintrag)
|
|
996
|
+
return 'Erst von Hand angleichen oder mit hmgr mcp remove entfernen, dann erneut versuchen.';
|
|
997
|
+
if (fehler instanceof McpKatalogFehler)
|
|
998
|
+
return '~/.hmgr/mcp.yaml prüfen und korrigieren, hmgr help mcp zeigt das Format.';
|
|
999
|
+
if (fehler instanceof McpUebernahmeNichtMoeglich)
|
|
1000
|
+
return 'Eine geteilte .mcp.json kennt nur claude-code, und nur an einem Arbeitsverzeichnis: --harness claude-code --ort dir:<pfad>.';
|
|
1001
|
+
if (fehler instanceof McpNichtInGeteilterDatei)
|
|
1002
|
+
return 'hmgr mcp list zeigt, welche Namen dort stehen; mcp migrate übernimmt nur Einträge aus der geteilten Datei.';
|
|
755
1003
|
return undefined;
|
|
756
1004
|
}
|
|
757
1005
|
export async function main(argv, umgebung) {
|