hmgr 0.0.17 → 0.0.19

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.
@@ -92,4 +92,8 @@ export class ComposerZeiger {
92
92
  daten.scripts = scripts;
93
93
  await writeFile(pfad, `${JSON.stringify(daten, null, 4)}\n`);
94
94
  }
95
+ async verzeichnisExistiert(pfad) {
96
+ const info = await stat(pfad).catch(() => undefined);
97
+ return info !== undefined && info.isDirectory();
98
+ }
95
99
  }
@@ -325,7 +325,10 @@ export class GitQuellen {
325
325
  async refsNachziehen(quelle) {
326
326
  if (quelle.art === 'lokal')
327
327
  return;
328
- await this.git(['fetch', '--prune', '--tags', 'origin'], await this.basis(quelle));
328
+ // --force: mitlaufende Tags (z.B. cmux `nightly`) zeigen bei jedem Build auf einen neuen
329
+ // Commit; ohne --force verweigert git das Überschreiben ("would clobber existing tag") und
330
+ // bricht den ganzen Fetch ab (E87). Betrifft nur lokale Tag-Refs im bare-Basis-Clone.
331
+ await this.git(['fetch', '--prune', '--force', '--tags', 'origin'], await this.basis(quelle));
329
332
  }
330
333
  async standardRef(quelle) {
331
334
  if (quelle.art === 'lokal')
@@ -413,7 +416,8 @@ export class GitQuellen {
413
416
  const basis = await this.basis(checkout.quelle);
414
417
  const von = (await this.git(['rev-parse', 'HEAD'], checkout.pfad)).trim();
415
418
  try {
416
- await this.git(['fetch', '--prune', '--tags', 'origin'], basis);
419
+ // --force: siehe refsNachziehen (E87), mitlaufende Tags dürfen den Fetch nicht abbrechen.
420
+ await this.git(['fetch', '--prune', '--force', '--tags', 'origin'], basis);
417
421
  }
418
422
  catch (fehler) {
419
423
  return { ergebnis: 'fehlgeschlagen', fehler: fehler instanceof Error ? fehler.message : String(fehler) };
package/dist/cli/hilfe.js CHANGED
@@ -34,6 +34,7 @@ hmgr <kommando> [schalter]
34
34
  update Alle Checkouts aktualisieren
35
35
  status Ist-Zustand der Ziele
36
36
  log Verlauf der letzten Läufe (Changelog)
37
+ doctor Fehlende Arbeitsverzeichnisse finden und Vergessen anbieten
37
38
  harness list Harness-Typen mit Stand aktiv/inaktiv und vorhanden
38
39
  harness enable <name> Harness-Typ aktivieren, hmgr richtet ihn künftig ein
39
40
  harness disable <name> Harness-Typ deaktivieren, mit Angebot zum Säubern
@@ -118,7 +119,7 @@ Häufige Fälle:
118
119
  link --ort dir:<pfad> merkt das Verzeichnis von selbst. dir add braucht es vorher nur, wenn die
119
120
  composer.json mehrdeutig ist.
120
121
 
121
- Siehe auch: hmgr help link, hmgr help sync, hmgr help harness`;
122
+ Siehe auch: hmgr help link, hmgr help sync, hmgr help harness, hmgr help doctor`;
122
123
  const LINK = `hmgr link <skill> [schalter]
123
124
 
124
125
  Legt im Ziel einen Symlink auf den Skill an, der aus einer bekannten Quelle stammt, und trägt ihn
@@ -495,7 +496,7 @@ Häufige Fälle:
495
496
  „liegen lassen … ohne Bestätigung nicht gelöscht": die Frage kam ohne Ja zurück, etwa in einer
496
497
  Pipe oder einem Composer-Script. In einem Terminal erneut hmgr update aufrufen.
497
498
 
498
- Siehe auch: hmgr help sync, hmgr help status, hmgr help log`;
499
+ Siehe auch: hmgr help sync, hmgr help status, hmgr help log, hmgr help doctor`;
499
500
  const LOG = `hmgr log
500
501
 
501
502
  Zeigt den Verlauf der letzten Läufe aus ~/.hmgr/verlauf.json (E80), neueste zuerst: dieselbe Datei,
@@ -521,6 +522,25 @@ Häufige Fälle:
521
522
  „Noch keine Ereignisse": entweder frisch installiert, oder jeder bisherige Lauf war ohne Fund.
522
523
 
523
524
  Siehe auch: hmgr help update, hmgr help status`;
525
+ const DOCTOR = `hmgr doctor
526
+
527
+ Prüft, ob jedes gemerkte Arbeitsverzeichnis (hmgr dir add) noch auf der Platte existiert (E88): ein
528
+ umbenanntes oder woanders neu geklontes Projekt hinterlässt sonst für immer einen toten Pfad im
529
+ Zustand, den nur hmgr dir forget von Hand entfernt. Bei einem Fund fragt doctor wie hmgr update
530
+ einmal für alle Funde, ob sie vergessen werden sollen; ohne Bestätigung bleiben sie gemeldet, aber
531
+ gemerkt. Anders als update holt doctor nichts aus dem Netz und schreibt keinen Eintrag in den
532
+ Verlauf — ein reiner Konsolen-Check, kein Lauf im E80-Sinn.
533
+
534
+ Schalter: keine.
535
+
536
+ Beispiel:
537
+ hmgr doctor
538
+
539
+ Häufige Fälle:
540
+ „liegen lassen … ohne Bestätigung nicht vergessen": die Frage kam ohne Ja zurück. In einem
541
+ Terminal erneut hmgr doctor aufrufen, oder hmgr dir forget <pfad> gezielt.
542
+
543
+ Siehe auch: hmgr help update, hmgr help dir`;
524
544
  const STATUS = `hmgr status
525
545
 
526
546
  Liest den Ist-Zustand vom Dateisystem: je Ziel eines vorhandenen, aktiven Harness die Einträge mit Quelle
@@ -648,6 +668,7 @@ const THEMEN = new Map([
648
668
  ['update', UPDATE],
649
669
  ['status', STATUS],
650
670
  ['log', LOG],
671
+ ['doctor', DOCTOR],
651
672
  ['harness', HARNESS],
652
673
  ['help', HILFE],
653
674
  ]);
package/dist/cli/main.js CHANGED
@@ -96,6 +96,7 @@ const SCHALTER = {
96
96
  update: { werte: [], flaggen: [] },
97
97
  status: { werte: [], flaggen: [] },
98
98
  log: { werte: [], flaggen: [] },
99
+ doctor: { werte: [], flaggen: [] },
99
100
  harness: { werte: ['profil'], flaggen: [] },
100
101
  };
101
102
  /**
@@ -715,7 +716,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
715
716
  }
716
717
  if (kommando === 'update') {
717
718
  restPruefen(schalterLesen(argv.slice(1), 'update').rest, 0, 'update');
718
- const { checkouts: liste, bundles, aufraeumen } = await anwendungsfaelle.aktualisieren({ ausloeser: 'cli' });
719
+ const { checkouts: liste, bundles, aufraeumen, doctor, } = await anwendungsfaelle.aktualisieren({ ausloeser: 'cli' });
719
720
  if (liste.length === 0)
720
721
  console.log('Keine Checkouts.');
721
722
  for (const { checkout, aktualisierung, geaenderteSkills } of liste) {
@@ -751,6 +752,13 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
751
752
  const geloescht = aufraeumen.verwaiste.filter((v) => v.geloescht).length;
752
753
  console.log(`${aufraeumen.verwaiste.length} verwaist, ${geloescht} gelöscht, ${aufraeumen.verwaiste.length - geloescht} liegen geblieben.`);
753
754
  }
755
+ // Fehlende Arbeitsverzeichnisse (E88): was es auf der Platte nicht mehr gibt.
756
+ for (const pfad of doctor.fehlendeArbeitsverzeichnisse) {
757
+ if (doctor.vergessen.includes(pfad))
758
+ console.log(` vergessen Arbeitsverzeichnis ${pfad} (nicht mehr auf der Platte)`);
759
+ else
760
+ console.log(` liegen lassen Arbeitsverzeichnis ${pfad} (nicht mehr auf der Platte, ohne Bestätigung nicht vergessen)`);
761
+ }
754
762
  // Zuletzt hmgr selbst (E77): wer schon aktualisiert, will auch wissen, dass sein hmgr alt ist.
755
763
  // Verglichen wird die eigene Version (E72) gegen npm, gemeldet wird nur — installiert wird
756
764
  // nichts. Offline oder Registry unerreichbar heißt keine Zeile, still: der Lauf selbst ist
@@ -809,6 +817,19 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
809
817
  }
810
818
  return 0;
811
819
  }
820
+ if (kommando === 'doctor') {
821
+ restPruefen(schalterLesen(argv.slice(1), 'doctor').rest, 0, 'doctor');
822
+ const { fehlendeArbeitsverzeichnisse, vergessen } = await anwendungsfaelle.doctor();
823
+ if (fehlendeArbeitsverzeichnisse.length === 0)
824
+ console.log('Keine fehlenden Arbeitsverzeichnisse.');
825
+ for (const pfad of fehlendeArbeitsverzeichnisse) {
826
+ if (vergessen.includes(pfad))
827
+ console.log(` vergessen ${pfad}`);
828
+ else
829
+ console.log(` liegen lassen ${pfad} (ohne Bestätigung nicht vergessen)`);
830
+ }
831
+ return 0;
832
+ }
812
833
  if (kommando === 'harness') {
813
834
  const schalter = schalterLesen(argv.slice(1), 'harness');
814
835
  unterkommandoPruefen('harness', schalter);
@@ -1042,6 +1063,8 @@ function verlaufEreignisZeile(ereignis) {
1042
1063
  }
1043
1064
  if (ereignis.art === 'aufgeraeumt')
1044
1065
  return `Checkout ${ereignis.checkout} aufgeräumt`;
1066
+ if (ereignis.art === 'arbeitsverzeichnis-vergessen')
1067
+ return `Arbeitsverzeichnis ${ereignis.pfad} vergessen (nicht mehr auf der Platte)`;
1045
1068
  if (ereignis.art === 'fehler')
1046
1069
  return `Fehler ${ereignis.betrifft}: ${ereignis.fehler}`;
1047
1070
  if (ereignis.art === 'hmgr-installiert')
@@ -2754,6 +2754,9 @@
2754
2754
  } else if (ereignis.art === 'aufgeraeumt') {
2755
2755
  li.appendChild(e('span', 'marke-ok', 'aufgeräumt'));
2756
2756
  li.appendChild(e('span', 'pfad', ereignis.checkout));
2757
+ } else if (ereignis.art === 'arbeitsverzeichnis-vergessen') {
2758
+ li.appendChild(e('span', 'marke-ok', 'vergessen'));
2759
+ li.appendChild(e('span', 'pfad', ereignis.pfad));
2757
2760
  } else if (ereignis.art === 'fehler') {
2758
2761
  li.appendChild(e('span', 'marke-aus', 'fehlgeschlagen'));
2759
2762
  li.appendChild(e('span', 'pfad', ereignis.betrifft + ': ' + ereignis.fehler));
@@ -14,6 +14,17 @@ export function aufraeumFrage(checkouts) {
14
14
  const pfade = checkouts.map((c) => ` ${c.pfad}`).join('\n');
15
15
  return `${kopf}\n${pfade}\nLöschen?`;
16
16
  }
17
+ /**
18
+ * Frage vor dem Vergessen fehlender Arbeitsverzeichnisse (E88), eine für alle Funde — Analogie zu
19
+ * `aufraeumFrage`.
20
+ */
21
+ export function doctorFrage(pfade) {
22
+ const kopf = pfade.length === 1
23
+ ? 'Ein Arbeitsverzeichnis gibt es auf der Platte nicht mehr:'
24
+ : `${pfade.length} Arbeitsverzeichnisse gibt es auf der Platte nicht mehr:`;
25
+ const zeilen = pfade.map((p) => ` ${p}`).join('\n');
26
+ return `${kopf}\n${zeilen}\nVergessen?`;
27
+ }
17
28
  function arbeitsverzeichnisZiel(harness, pfad) {
18
29
  return { harness, ort: { art: 'arbeitsverzeichnis', pfad } };
19
30
  }
@@ -1205,6 +1216,26 @@ export class Anwendungsfaelle {
1205
1216
  zustand.bindungen = zustand.bindungen.filter((b) => !(b.ziel.ort.art === 'arbeitsverzeichnis' && b.ziel.ort.pfad === verzeichnis));
1206
1217
  await this.p.zustand.schreiben(zustand);
1207
1218
  }
1219
+ /**
1220
+ * Arbeitsverzeichnisse erkennen, die es auf der Platte nicht mehr gibt, und ihr Vergessen anbieten
1221
+ * (E88). `rueckfragen: false` (GUI-Automatik, Smoke) meldet nur, fragt nicht und vergisst nichts —
1222
+ * dieselbe Weiche wie bei `verwaisteAufraeumen`.
1223
+ */
1224
+ async doctor(args) {
1225
+ const rueckfragen = args?.rueckfragen ?? true;
1226
+ const zustand = await this.p.zustand.lesen();
1227
+ const fehlend = [];
1228
+ for (const pfad of zustand.arbeitsverzeichnisse)
1229
+ if (!(await this.p.zeiger.verzeichnisExistiert(pfad)))
1230
+ fehlend.push(pfad);
1231
+ if (fehlend.length === 0 || !rueckfragen)
1232
+ return { fehlendeArbeitsverzeichnisse: fehlend, vergessen: [] };
1233
+ if (!(await this.p.rueckfrage.bestaetigen(doctorFrage(fehlend))))
1234
+ return { fehlendeArbeitsverzeichnisse: fehlend, vergessen: [] };
1235
+ for (const pfad of fehlend)
1236
+ await this.arbeitsverzeichnisVergessen(pfad);
1237
+ return { fehlendeArbeitsverzeichnisse: fehlend, vergessen: [...fehlend] };
1238
+ }
1208
1239
  /**
1209
1240
  * Mehrere composer.json unter einem Verzeichnis (E26): hmgr rät nicht, es fragt. Bleibt die Frage
1210
1241
  * ohne Antwort, weil niemand antworten kann oder die Eingabe keine der Kandidaten trifft, gilt
@@ -1785,15 +1816,16 @@ export class Anwendungsfaelle {
1785
1816
  // in seinem Eintrag, er bricht den Lauf nicht ab — wie eine unerreichbare Quelle (E51).
1786
1817
  const bundles = await this.bundleAbgleich();
1787
1818
  const aufraeumen = await this.verwaisteAufraeumen(checkouts, rueckfragen);
1788
- await this.verlaufVermerken(ausloeser, this.aktualisierungsEreignisse(checkouts, bundles, aufraeumen));
1789
- return { checkouts, pluginHarnesses, bundles, aufraeumen };
1819
+ const doctorErgebnis = await this.doctor({ rueckfragen });
1820
+ await this.verlaufVermerken(ausloeser, this.aktualisierungsEreignisse(checkouts, bundles, aufraeumen, doctorErgebnis));
1821
+ return { checkouts, pluginHarnesses, bundles, aufraeumen, doctor: doctorErgebnis };
1790
1822
  }
1791
1823
  /**
1792
1824
  * Ereignisse eines Aktualisieren-Laufs für den Verlauf (E80): Plugins bleiben draußen, weil sich
1793
1825
  * eine Änderung dort nicht messen lässt (`claude plugin marketplace update` liefert keine
1794
1826
  * Auskunft) — die bisherige Zeile „Plugins aktualisiert" war deshalb geschönt.
1795
1827
  */
1796
- aktualisierungsEreignisse(checkouts, bundles, aufraeumen) {
1828
+ aktualisierungsEreignisse(checkouts, bundles, aufraeumen, doctorErgebnis) {
1797
1829
  const ereignisse = [];
1798
1830
  for (const { checkout, aktualisierung, geaenderteSkills } of checkouts) {
1799
1831
  if (aktualisierung.ergebnis === 'aktualisiert')
@@ -1827,6 +1859,8 @@ export class Anwendungsfaelle {
1827
1859
  for (const verwaist of aufraeumen.verwaiste)
1828
1860
  if (verwaist.geloescht)
1829
1861
  ereignisse.push({ art: 'aufgeraeumt', checkout: `${quelleAlsText(verwaist.checkout.quelle)} @ ${verwaist.checkout.ref}` });
1862
+ for (const pfad of doctorErgebnis.vergessen)
1863
+ ereignisse.push({ art: 'arbeitsverzeichnis-vergessen', pfad });
1830
1864
  return ereignisse;
1831
1865
  }
1832
1866
  /** Neuen Eintrag vorn anfügen, nur wenn Ereignisse da sind (E80). Kappen übernimmt der Adapter. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hmgr",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
5
5
  "license": "MIT",
6
6
  "author": "Tim Kaufmann",