hmgr 0.0.34 → 0.0.35

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.
@@ -22,6 +22,8 @@ function distroOrt(distro) {
22
22
  return { art: 'home', heim: `wsl:${distro}` };
23
23
  }
24
24
  const GITIGNORE_ZEILE = '.claude/';
25
+ /** Wie lange „keine claude-CLI erreichbar" gilt, bevor erneut gefragt wird (E103). */
26
+ const ERREICHBARKEIT_NEGATIV_MS = 60_000;
25
27
  /**
26
28
  * Standard-Ausführer: `claude` vom PATH. Ein Fehlschlag wird zum Code, nicht zur Ausnahme.
27
29
  * stdin ist zu, sonst hängt eine CLI, die auf Eingabe wartet, den ganzen Ablauf; der Timeout
@@ -336,6 +338,10 @@ class ClaudeMcp {
336
338
  ebenen() {
337
339
  return ['home', 'arbeitsverzeichnis'];
338
340
  }
341
+ /** Ob am Ort eine claude-CLI antwortet (E103): ohne sie bringt Einhängen oder Aushängen dort nichts. */
342
+ async ortBedienbar(ort) {
343
+ return await this.harness.cliErreichbar(ort);
344
+ }
339
345
  hinweisNachAenderung() {
340
346
  return 'Claude Code übernimmt die Änderung mit der nächsten Sitzung.';
341
347
  }
@@ -508,7 +514,12 @@ export class ClaudeCode {
508
514
  */
509
515
  plugins;
510
516
  mcp;
511
- /** Ob die claude-CLI antwortet, je Welt einmal je Lauf: Schlüssel '' für Windows, sonst die Distro. */
517
+ /**
518
+ * Ob die claude-CLI antwortet, je Welt: Schlüssel '' für Windows, sonst die Distro. Ein positives
519
+ * Ergebnis gilt für die Lebenszeit des Adapters, ein negatives verfällt nach 60 Sekunden (E103), damit
520
+ * eine laufende GUI eine inzwischen installierte claude findet. Solange `bis` fehlt (die Antwort steht
521
+ * aus) oder noch nicht erreicht ist, teilen sich parallele Anfragen das Versprechen.
522
+ */
512
523
  erreichbarkeit = new Map();
513
524
  /**
514
525
  * `heime` sagt, wo die Homedirs liegen (E78): das primäre und, seit F87, zusätzliche in einer
@@ -536,6 +547,7 @@ export class ClaudeCode {
536
547
  bruecken.set(distro, (b = new WslBruecke(distro)));
537
548
  return b;
538
549
  }),
550
+ jetzt: wsl?.jetzt ?? (() => Date.now()),
539
551
  distros: wsl?.distros ??
540
552
  (async () => this.heime
541
553
  .zusaetzliche(this.name)
@@ -551,18 +563,32 @@ export class ClaudeCode {
551
563
  bruecke(distro) {
552
564
  return this.wsl.bruecke(distro);
553
565
  }
554
- /** Ob an diesem Ort eine claude-CLI antwortet (`claude --version`), je Welt einmal je Lauf. */
566
+ /**
567
+ * Ob an diesem Ort eine claude-CLI antwortet (`claude --version`), je Welt: ein „ja" bleibt, ein
568
+ * „nein" gilt 60 Sekunden (E103).
569
+ */
555
570
  async erreichbar(ort) {
556
571
  const schluessel = wslDistroVonOrt(ort) ?? '';
557
- let versprechen = this.erreichbarkeit.get(schluessel);
558
- if (versprechen === undefined) {
559
- const cli = schluessel === '' ? this.ausfuehrer : this.ausfuehrerFuer(ort);
560
- versprechen = cli(['--version'])
572
+ const bekannt = this.erreichbarkeit.get(schluessel);
573
+ if (bekannt !== undefined && (bekannt.bis === undefined || this.wsl.jetzt() < bekannt.bis))
574
+ return await bekannt.versprechen;
575
+ const cli = schluessel === '' ? this.ausfuehrer : this.ausfuehrerFuer(ort);
576
+ const eintrag = {
577
+ versprechen: cli(['--version'])
561
578
  .then(({ code }) => code === 0)
562
- .catch(() => false);
563
- this.erreichbarkeit.set(schluessel, versprechen);
564
- }
565
- return await versprechen;
579
+ .catch(() => false)
580
+ .then((ok) => {
581
+ if (!ok)
582
+ eintrag.bis = this.wsl.jetzt() + ERREICHBARKEIT_NEGATIV_MS;
583
+ return ok;
584
+ }),
585
+ };
586
+ this.erreichbarkeit.set(schluessel, eintrag);
587
+ return await eintrag.versprechen;
588
+ }
589
+ /** Ob an diesem Ort eine claude-CLI antwortet, ohne zu werfen (E103): für Plätze, die ohne CLI nichts bringen. */
590
+ async cliErreichbar(ort) {
591
+ return await this.erreichbar(ort);
566
592
  }
567
593
  /** Wirft `ClaudeCliNichtErreichbar`, wenn an diesem Ort keine claude-CLI antwortet (E100). */
568
594
  async cliPruefen(ort) {
@@ -578,8 +604,10 @@ export class ClaudeCode {
578
604
  * Windows durchläuft — oder, wenn dort keine CLI liegt, sobald sie in der Distro eines
579
605
  * eingetragenen WSL-Orts antwortet. Die Distros werden erst dann befragt, nacheinander bis zur
580
606
  * ersten Antwort, damit ein Rechner mit Windows-claude keinen wsl.exe-Start zahlt. Jede Welt wird
581
- * einmal je Lauf geprüft; in der GUI kommt ein später eingetragener WSL-Ort beim nächsten Aufruf
582
- * dazu, ohne die schon geprüften erneut zu fragen.
607
+ * einmal geprüft (ein „ja" bleibt); in der GUI kommt ein später eingetragener WSL-Ort beim nächsten Aufruf
608
+ * dazu, ohne die schon geprüften erneut zu fragen. Ein negatives Ergebnis verfällt nach 60 Sekunden
609
+ * (E103): ein erneuter Aufruf danach fragt noch einmal und setzt die Fähigkeiten, sobald claude
610
+ * inzwischen da ist.
583
611
  */
584
612
  async vorhanden() {
585
613
  const primaer = this.heime.primaer(this.name);
@@ -1202,6 +1202,23 @@
1202
1202
  var verzMcpZiele = MCP_PLAETZE.filter(function (p) {
1203
1203
  return p.ort.art === 'arbeitsverzeichnis' && p.ort.pfad === pfad;
1204
1204
  });
1205
+ // E103: mcpPlaetze() lässt Orte weg, an denen der Harness nicht bedienbar ist (keine CLI). Hängt
1206
+ // dort schon etwas, bleibt der Abschnitt samt Entfernen-Knopf trotzdem stehen; nur das
1207
+ // „hinzufügen" entfällt (mcpKannAn).
1208
+ var gesehen = {};
1209
+ verzMcpZiele.forEach(function (p) { gesehen[zielKey(p)] = true; });
1210
+ var vorhandene = [];
1211
+ MCP_KATALOG.forEach(function (k) {
1212
+ k.eingehaengt.forEach(function (z) { vorhandene.push(z); });
1213
+ k.geteilteFundorte.forEach(function (g) { vorhandene.push(g.ziel); });
1214
+ });
1215
+ MCP_FREMD.forEach(function (x) { vorhandene.push(x.ziel); });
1216
+ MCP_FREMD_GETEILT.forEach(function (x) { vorhandene.push(x.ziel); });
1217
+ vorhandene.forEach(function (z) {
1218
+ if (z.ort.art !== 'arbeitsverzeichnis' || z.ort.pfad !== pfad || gesehen[zielKey(z)]) return;
1219
+ gesehen[zielKey(z)] = true;
1220
+ verzMcpZiele.push(z);
1221
+ });
1205
1222
 
1206
1223
  if (reihenfolge.length === 0) {
1207
1224
  var leer = e('div', 'leerzeile');
@@ -499,10 +499,15 @@ export class Anwendungsfaelle {
499
499
  * Die Plätze für den Einhängen-Dialog der GUI (E61-Folgerunde, F42): dieselbe Liste wie in
500
500
  * `mcpUebersicht`, aber roh, ohne den Abgleich mit Katalog-Einträgen. Die Fähigkeit hängt am
501
501
  * Adapter, nicht am Harness-Namen (E16): agents kommt nie vor, weil es `mcp` nicht implementiert.
502
+ * Orte, an denen der Harness nicht bedienbar ist (E103, etwa keine claude-CLI), fehlen; die
503
+ * Übersicht lässt sie stehen, damit vorhandene Einträge sichtbar bleiben.
502
504
  */
503
505
  async mcpPlaetze() {
504
506
  const plaetze = [];
505
507
  for (const ziel of await this.mcpZiele()) {
508
+ const mcp = this.harnessMitMcp(ziel.harness).mcp;
509
+ if (mcp.ortBedienbar !== undefined && !(await mcp.ortBedienbar(ziel.ort)))
510
+ continue;
506
511
  const wurzel = await this.mcpGitWurzelFuer(this.harnessMitMcp(ziel.harness).mcp, ziel);
507
512
  plaetze.push(wurzel === null ? ziel : { ...ziel, gitWurzel: wurzel });
508
513
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hmgr",
3
- "version": "0.0.34",
3
+ "version": "0.0.35",
4
4
  "description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
5
5
  "license": "MIT",
6
6
  "author": "Tim Kaufmann",