hmgr 0.0.2 → 0.0.3

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 CHANGED
@@ -26,34 +26,5 @@ npm run build
26
26
 
27
27
  ## Kommandos
28
28
 
29
- ```
30
- source add <url|pfad> Quelle hinzufügen (URL wird geklont, Pfad bleibt Arbeitskopie)
31
- source list Quellen zeigen
32
- dir add <pfad> Arbeitsverzeichnis merken und an seinen Zeiger binden
33
- dir forget <pfad> Arbeitsverzeichnis und Bindung vergessen, Links bleiben
34
- dir list Arbeitsverzeichnisse mit ihrer Bindung zeigen
35
- link <skill> Skill in ein Ziel verlinken
36
- unlink <skill> Link entfernen
37
- adopt [name] Fremde Links im Ziel durch eigene hmgr-Links ersetzen
38
- plugin add <name> Plugin in ein Ziel legen (installieren oder Skills verlinken)
39
- plugin remove <name> Plugin aus einem Ziel entfernen
40
- plugin list Plugins der Marketplaces aller Quellen
41
- sync [pfad] Ziel auf den Stand seines Zeigers bringen
42
- update Alle Checkouts aktualisieren
43
- status Ist-Zustand der Ziele
44
- ```
45
-
46
- Optionen:
47
-
48
- ```
49
- --harness <name> claude-code, hermes oder agents, Standard claude-code
50
- --ort home|dir:<pfad>|profil:<name> Standard home
51
- --quelle <teilstring> Quelle bei mehrdeutigem Skill-Namen
52
- --version <spanne> Versionseinschränkung nach Composer-Regeln, nur mit Zeiger
53
- --composer <pfad> composer.json wählen, wenn mehrere in Frage kommen
54
- --local Link nur ins Ziel legen, nicht in den Zeiger
55
- --ersetzen fremden Eintrag am Ziel ersetzen
56
- --probelauf bei adopt nur melden, nichts schreiben
57
- ```
58
-
59
- `hmgr help` zeigt dieselbe Übersicht.
29
+ `hmgr --help` zeigt die Kommandos, `hmgr help <kommando>` Schalter, Beispiele
30
+ und Zusammenhänge.
@@ -0,0 +1,254 @@
1
+ // Hilfetexte der CLI. Nur Zeichenketten, keine Ausgabe: die macht main.ts (Terminal-I/O).
2
+ /** Ebene 1: Orientierung, je Kommando ein Einzeiler, Verweis auf die Kommando-Hilfe. */
3
+ export const HILFE = `hmgr verlinkt Skills aus Git-Quellen in die Verzeichnisse, aus denen ein Harness sie lädt:
4
+ claude-code, hermes oder agents. Welche Skills und Plugins zu einem Arbeitsverzeichnis gehören,
5
+ steht in seinem Zeiger, dem extra-Abschnitt einer composer.json.
6
+
7
+ Arbeitszyklus: Skill in seiner Quelle ändern, committen und pushen, dann hmgr update. Ein frisch
8
+ geklontes Vorhaben holt seinen Stand mit hmgr sync.
9
+
10
+ hmgr <kommando> [schalter]
11
+
12
+ source add <url|pfad> Quelle hinzufügen (URL wird geklont, Pfad bleibt Arbeitskopie)
13
+ source list Quellen zeigen
14
+ dir add <pfad> Arbeitsverzeichnis merken und an seinen Zeiger binden
15
+ dir forget <pfad> Arbeitsverzeichnis und Bindung vergessen, Links bleiben
16
+ dir list Arbeitsverzeichnisse mit ihrer Bindung zeigen
17
+ link <skill> Skill in ein Ziel verlinken
18
+ unlink <skill> Link entfernen
19
+ adopt [name] Fremde Links im Ziel durch eigene hmgr-Links ersetzen
20
+ plugin add <name> Plugin in ein Ziel legen (installieren oder Skills verlinken)
21
+ plugin remove <name> Plugin aus einem Ziel entfernen
22
+ plugin list Plugins der Marketplaces aller Quellen
23
+ sync [pfad] Ziel auf den Stand seines Zeigers bringen
24
+ update Alle Checkouts aktualisieren
25
+ status Ist-Zustand der Ziele
26
+
27
+ hmgr help <kommando> zeigt Schalter, Beispiele und Zusammenhänge.`;
28
+ const SOURCE = `hmgr source add <url|pfad>
29
+ hmgr source list
30
+
31
+ Eine Quelle ist ein Git-Repo mit Skills. source add prüft, ob sie erreichbar ist, liest ihre Skills
32
+ und ihren Marketplace und merkt sie unter ~/.hmgr. Erst danach kennt hmgr die Skill-Namen, mit
33
+ denen link und plugin add arbeiten. Eine URL wird geklont, ein Pfad bleibt die Arbeitskopie, die
34
+ dort schon liegt, und wird von hmgr nicht verändert.
35
+
36
+ Schalter: keine.
37
+
38
+ Beispiele:
39
+ hmgr source add https://github.com/tim/skills.git
40
+ hmgr source add ~/.skills/tim-skills
41
+ hmgr source list
42
+
43
+ Häufige Fälle:
44
+ „Skill … liegt in keiner Quelle": die Quelle fehlt, erst source add, dann link.
45
+ Neue Commits einer bekannten Quelle holt hmgr update, nicht source add.
46
+
47
+ Siehe auch: hmgr help link, hmgr help plugin, hmgr help update`;
48
+ const DIR = `hmgr dir add <pfad> [--composer <pfad>]
49
+ hmgr dir forget <pfad>
50
+ hmgr dir list
51
+
52
+ Ein Arbeitsverzeichnis ist ein Verzeichnis, in dem ein Harness gestartet wird. dir add merkt es und
53
+ bindet es an seinen Zeiger, also an die composer.json, in deren extra-Abschnitt Quellen, Skills und
54
+ Plugins des Vorhabens stehen. Ohne Zeiger bleibt das Verzeichnis gemerkt und jeder Link dort lokal
55
+ (E26). dir forget löst Bindung und Eintrag, die gelegten Links bleiben liegen.
56
+
57
+ Schalter:
58
+ --composer <pfad> composer.json wählen, wenn unter dem Verzeichnis mehrere in Frage kommen
59
+
60
+ Beispiele:
61
+ hmgr dir add ~/Code/site
62
+ hmgr dir add ~/Code/site --composer ~/Code/site/app/composer.json
63
+ hmgr dir list
64
+
65
+ Häufige Fälle:
66
+ „Mehrere composer.json unter …": eine mit --composer nennen, hmgr rät nicht.
67
+ link --ort dir:<pfad> merkt das Verzeichnis von selbst. dir add braucht es vorher nur, wenn die
68
+ composer.json mehrdeutig ist.
69
+
70
+ Siehe auch: hmgr help link, hmgr help sync`;
71
+ const LINK = `hmgr link <skill> [schalter]
72
+
73
+ Legt im Ziel einen Symlink auf den Skill an, der aus einer bekannten Quelle stammt, und trägt ihn
74
+ in den Zeiger des Ziels ein, damit hmgr sync ihn anderswo reproduzieren kann. Einen Zeiger gibt es
75
+ nur für ein Arbeitsverzeichnis mit composer.json, an Home und Profil bleibt jeder Link lokal.
76
+
77
+ Schalter:
78
+ --harness <name> claude-code, hermes oder agents, Standard claude-code
79
+ --ort <ort> home, dir:<pfad> oder profil:<name>, Standard home. profil kennt nur hermes
80
+ --quelle <teil> Teilstring der Quelle, wenn der Skill-Name in mehreren Quellen vorkommt
81
+ --version <spanne> Versionseinschränkung nach Composer-Regeln (etwa ^1.2) oder ein Tag- oder
82
+ Branchname. Nur mit Zeiger, also dir:<pfad> ohne --local (E33)
83
+ --composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
84
+ --local Link nur ins Ziel legen, nicht in den Zeiger
85
+ --ersetzen einen fremden Eintrag gleichen Namens am Ziel ersetzen
86
+
87
+ Beispiele:
88
+ hmgr link netzwerk
89
+ hmgr link netzwerk --harness hermes --ort profil:researcher
90
+ hmgr link tq-schema --ort dir:. --version ^2.0
91
+
92
+ Häufige Fälle:
93
+ „liegt in mehreren Quellen": mit --quelle <teilstring der URL oder des Pfads> entscheiden.
94
+ „liegt bereits ein fremder Eintrag": --ersetzen setzen oder vorher hmgr adopt <name>.
95
+ „gibt es keinen Zeiger": --version braucht ein Arbeitsverzeichnis mit composer.json und kein
96
+ --local. Ohne Zeiger den Link ohne --version legen.
97
+
98
+ Siehe auch: hmgr help unlink, hmgr help sync, hmgr help adopt`;
99
+ const UNLINK = `hmgr unlink <skill> [schalter]
100
+
101
+ Entfernt den Link aus dem Ziel und, wenn das Ziel an einen Zeiger gebunden ist, den Skill aus dem
102
+ Zeiger. Der Checkout unter ~/.hmgr bleibt liegen, Links anderer Ziele bleiben unberührt.
103
+
104
+ Schalter:
105
+ --harness <name> claude-code, hermes oder agents, Standard claude-code
106
+ --ort <ort> home, dir:<pfad> oder profil:<name>, Standard home. profil kennt nur hermes
107
+ --composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
108
+ --local nur den Link entfernen, den Eintrag im Zeiger stehen lassen
109
+
110
+ Beispiele:
111
+ hmgr unlink netzwerk
112
+ hmgr unlink netzwerk --ort dir:. --local
113
+
114
+ Häufige Fälle:
115
+ Bleibt der Skill im Zeiger stehen, legt hmgr sync ihn wieder an. Ohne --local räumt unlink
116
+ beides ab.
117
+
118
+ Siehe auch: hmgr help link, hmgr help sync`;
119
+ const ADOPT = `hmgr adopt [name] [schalter]
120
+
121
+ Ersetzt fremde Symlinks im Ziel durch eigene hmgr-Links: hmgr liest, aus welcher Git-Arbeitskopie
122
+ ein Link stammt, nimmt deren remote.origin.url als Quelle, legt einen Checkout am selben Branch an
123
+ und verlinkt den Skill daraus. Ohne Namen laufen alle fremden Einträge des Ziels durch, mit Namen
124
+ nur dieser eine. Was sich nicht auflösen lässt, bleibt liegen und wird mit Grund gemeldet.
125
+
126
+ Schalter:
127
+ --harness <name> claude-code, hermes oder agents, Standard claude-code
128
+ --ort <ort> home, dir:<pfad> oder profil:<name>, Standard home. profil kennt nur hermes
129
+ --composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
130
+ --local Übernahme nicht in den Zeiger schreiben
131
+ --probelauf nur melden, nichts schreiben
132
+
133
+ Beispiele:
134
+ hmgr adopt --probelauf
135
+ hmgr adopt netzwerk --ort dir:.
136
+
137
+ Häufige Fälle:
138
+ „kein Symlink": der Eintrag ist ein echtes Verzeichnis, hmgr fasst ihn nicht an.
139
+ „liegt in keiner Git-Arbeitskopie" oder „ohne remote.origin.url": die Quelle ist nicht
140
+ bestimmbar. Mit hmgr source add <url> eine Quelle nennen und mit hmgr link --ersetzen verlinken.
141
+ „ohne Branch (detached HEAD)": in der Arbeitskopie einen Branch auschecken, dann erneut adopt.
142
+ „Pfad ist in der Quelle kein Skill": im Repo liegt an der Stelle keine SKILL.md.
143
+
144
+ Siehe auch: hmgr help link, hmgr help status`;
145
+ const PLUGIN = `hmgr plugin add <name> [schalter]
146
+ hmgr plugin remove <name> [schalter]
147
+ hmgr plugin list
148
+
149
+ Ein Plugin ist ein Bündel aus einem Marketplace, also aus einer Quelle mit
150
+ .claude-plugin/marketplace.json. Hat der Harness die Plugin-Fähigkeit (claude-code), lässt hmgr das
151
+ Plugin von dessen CLI installieren, sonst verlinkt es die flache Skill-Liste des Plugins samt
152
+ seinen Abhängigkeiten. In den Zeiger kommt das Plugin, nicht seine Einzelskills. plugin list zeigt
153
+ die Marketplaces aller bekannten Quellen mit ihren Plugins.
154
+
155
+ Schalter (add und remove):
156
+ --harness <name> claude-code, hermes oder agents, Standard claude-code
157
+ --ort <ort> home, dir:<pfad> oder profil:<name>, Standard home. profil kennt nur hermes
158
+ --composer <pfad> composer.json des Arbeitsverzeichnisses, wenn mehrere in Frage kommen
159
+ --local Plugin nicht in den Zeiger schreiben
160
+ --quelle <teil> nur bei add: Quelle eingrenzen, wenn der Name in mehreren Marketplaces steht
161
+
162
+ Beispiele:
163
+ hmgr plugin list
164
+ hmgr plugin add superpowers --ort dir:.
165
+ hmgr plugin remove superpowers
166
+
167
+ Häufige Fälle:
168
+ „Keine Quelle mit Marketplace": erst hmgr source add <url> der Quelle mit der marketplace.json.
169
+ „liegt in keinem Marketplace der bekannten Quellen": Namen mit hmgr plugin list prüfen.
170
+ „liegt in mehreren Marketplaces": mit --quelle <teilstring> entscheiden.
171
+ „braucht …, das im selben Marketplace nicht steht": die Abhängigkeit fehlt im Marketplace, das
172
+ kann nur die Quelle beheben.
173
+
174
+ Siehe auch: hmgr help source, hmgr help sync`;
175
+ const SYNC = `hmgr sync [pfad]
176
+
177
+ Bringt ein Arbeitsverzeichnis auf den Stand seines Zeigers: fehlende Quellen übernehmen, fehlende
178
+ Links legen, Links auf einen anderen Ref umlegen, Plugins herstellen. Ohne Pfad gilt das aktuelle
179
+ Verzeichnis. Das ist der Befehl nach einem frischen Clone eines Vorhabens und nach jeder Änderung
180
+ am Zeiger. Eigene Links, die im Zeiger fehlen, werden als abweichend gemeldet und nicht entfernt,
181
+ fremde Einträge bleiben unberührt.
182
+
183
+ Schalter: keine.
184
+
185
+ Beispiele:
186
+ hmgr sync
187
+ hmgr sync ~/Code/site
188
+
189
+ Häufige Fälle:
190
+ „Kein Zeiger unter …, nichts zu tun": im Verzeichnis liegt keine composer.json. Mit
191
+ hmgr dir add <pfad> --composer <pfad> eine nennen.
192
+ „steht nicht im Zeiger": den Link mit hmgr link <skill> --ort dir:<pfad> eintragen oder mit
193
+ hmgr unlink <skill> --ort dir:<pfad> --local entfernen.
194
+ „Kein Harness auf diesem Rechner": weder ~/.claude noch ~/.hermes noch ~/.agents ist angelegt.
195
+
196
+ Siehe auch: hmgr help dir, hmgr help link, hmgr help update`;
197
+ const UPDATE = `hmgr update
198
+
199
+ Holt für jeden Checkout unter ~/.hmgr den neuesten Stand seines Refs und zieht bei Harnesses mit
200
+ Plugin-Fähigkeit deren Plugins nach (E14). Die Links zeigen weiter auf dieselben Pfade und müssen
201
+ nicht erneuert werden. Ein Checkout mit lokaler Änderung wird übersprungen und gemeldet (E15),
202
+ damit nichts verloren geht. Lokale Quellen haben keinen Checkout und bleiben unberührt.
203
+
204
+ So wird eine Änderung an einem Skill überall wirksam: in der Quelle committen und pushen, dann
205
+ hmgr update.
206
+
207
+ Schalter: keine.
208
+
209
+ Beispiel:
210
+ hmgr update
211
+
212
+ Häufige Fälle:
213
+ „übersprungen … lokale Änderung (E15)": im Checkout liegt eine Änderung, die durch einen Link
214
+ hindurch gemacht wurde. Dort committen und pushen, dann erneut hmgr update.
215
+ „Keine Checkouts": es gibt nur lokale Quellen oder noch keinen Link.
216
+
217
+ Siehe auch: hmgr help sync, hmgr help status`;
218
+ const STATUS = `hmgr status
219
+
220
+ Liest den Ist-Zustand vom Dateisystem: je Ziel eines vorhandenen Harness die Einträge mit Quelle
221
+ und Ref, davor die Versionseinschränkung, falls der Zeiger eine nennt. Fremde Einträge und Links
222
+ ohne bekannte Quelle sind eigens gekennzeichnet, dazu kommen die Plugins des Ziels. Am Ende stehen
223
+ die Harnesses, die auf diesem Rechner nicht liegen.
224
+
225
+ Schalter: keine.
226
+
227
+ Beispiel:
228
+ hmgr status
229
+
230
+ Häufige Fälle:
231
+ „fremd": der Eintrag stammt nicht von hmgr. hmgr adopt <name> übernimmt ihn.
232
+ „Link ohne bekannte Quelle": der Link zeigt in einen hmgr-Checkout, dessen Quelle nicht mehr im
233
+ Zustand steht. Mit hmgr source add <url> die Quelle wieder eintragen.
234
+ „Nicht vorhanden": der Harness hat sein Verzeichnis auf diesem Rechner nicht angelegt, hmgr
235
+ überspringt seine Ziele.
236
+
237
+ Siehe auch: hmgr help adopt, hmgr help sync`;
238
+ /** Ebene 2, je Kommandofamilie ein Text. Unterkommandos teilen sich die Hilfe ihrer Familie. */
239
+ const THEMEN = new Map([
240
+ ['source', SOURCE],
241
+ ['dir', DIR],
242
+ ['link', LINK],
243
+ ['unlink', UNLINK],
244
+ ['adopt', ADOPT],
245
+ ['plugin', PLUGIN],
246
+ ['sync', SYNC],
247
+ ['update', UPDATE],
248
+ ['status', STATUS],
249
+ ['help', HILFE],
250
+ ]);
251
+ /** Hilfetext zu einem Kommando, undefined für ein unbekanntes Thema. */
252
+ export function hilfeText(thema) {
253
+ return THEMEN.get(thema);
254
+ }
package/dist/cli/main.js CHANGED
@@ -15,6 +15,7 @@ import { GitQuellen } from "../adapter/git.js";
15
15
  import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
16
16
  import { TerminalRueckfrage } from "../adapter/terminal.js";
17
17
  import { ZustandJson } from "../adapter/zustand-json.js";
18
+ import { HILFE, hilfeText } from "./hilfe.js";
18
19
  export function umgebungLesen(env = process.env) {
19
20
  const home = env.HOME ?? homedir();
20
21
  return { home, hmgrHome: env.HMGR_HOME ?? path.join(home, '.hmgr') };
@@ -35,9 +36,9 @@ export function bauen(umgebung, optionen = {}) {
35
36
  };
36
37
  return { anwendungsfaelle: new Anwendungsfaelle(ports), ports };
37
38
  }
38
- /** Schalter ohne Wert. Alles andere braucht einen. */
39
- const FLAGGEN = new Set(['ersetzen', 'local', 'lokal', 'probelauf']);
40
- function schalterLesen(args) {
39
+ /** Schalter ohne Wert. Alles andere braucht einen. `help` steht hier, damit --help nie greift. */
40
+ const FLAGGEN = new Set(['ersetzen', 'local', 'lokal', 'probelauf', 'help']);
41
+ function schalterLesen(args, kommando) {
41
42
  const rest = [];
42
43
  const werte = new Map();
43
44
  const flaggen = new Set();
@@ -54,7 +55,8 @@ function schalterLesen(args) {
54
55
  }
55
56
  const wert = args[++i];
56
57
  if (wert === undefined)
57
- throw new Error(`Schalter --${name} braucht einen Wert`);
58
+ throw new Error(`Schalter --${name} braucht einen Wert, etwa --${name} <wert>. ` +
59
+ `${kommando === undefined ? 'hmgr --help' : `hmgr help ${kommando}`} zeigt die Schalter.`);
58
60
  werte.set(name, wert);
59
61
  }
60
62
  return { rest, werte, flaggen };
@@ -69,31 +71,6 @@ function ortLesen(text) {
69
71
  return { art: 'profil', name: roh.slice(7) };
70
72
  throw new Error(`Unbekannter Ort „${roh}", erwartet home, dir:<pfad> oder profil:<name>`);
71
73
  }
72
- const HILFE = `hmgr <kommando>
73
-
74
- source add <url|pfad> Quelle hinzufügen (URL wird geklont, Pfad bleibt Arbeitskopie)
75
- source list Quellen zeigen
76
- dir add <pfad> Arbeitsverzeichnis merken und an seinen Zeiger binden
77
- dir forget <pfad> Arbeitsverzeichnis und Bindung vergessen, Links bleiben
78
- dir list Arbeitsverzeichnisse mit ihrer Bindung zeigen
79
- link <skill> Skill in ein Ziel verlinken
80
- unlink <skill> Link entfernen
81
- adopt [name] Fremde Links im Ziel durch eigene hmgr-Links ersetzen
82
- plugin add <name> Plugin in ein Ziel legen (installieren oder Skills verlinken)
83
- plugin remove <name> Plugin aus einem Ziel entfernen
84
- plugin list Plugins der Marketplaces aller Quellen
85
- sync [pfad] Ziel auf den Stand seines Zeigers bringen
86
- update Alle Checkouts aktualisieren
87
- status Ist-Zustand der Ziele
88
-
89
- --harness <name> claude-code, hermes oder agents, Standard claude-code
90
- --ort home|dir:<pfad>|profil:<name> Standard home
91
- --quelle <teilstring> Quelle bei mehrdeutigem Skill-Namen
92
- --version <spanne> Versionseinschränkung nach Composer-Regeln, nur mit Zeiger (E33)
93
- --composer <pfad> composer.json wählen, wenn mehrere in Frage kommen
94
- --local Link nur ins Ziel legen, nicht in den Zeiger
95
- --ersetzen fremden Eintrag am Ziel ersetzen
96
- --probelauf bei adopt nur melden, nichts schreiben`;
97
74
  /** Klartext für den Grund, aus dem ein fremder Eintrag liegen bleibt (B12). */
98
75
  const UEBERNAHME_GRUND = {
99
76
  'kein-link': 'kein Symlink',
@@ -105,16 +82,36 @@ const UEBERNAHME_GRUND = {
105
82
  export async function ausfuehren(argv, umgebung, optionen = {}) {
106
83
  const { anwendungsfaelle, ports } = bauen(umgebung, optionen);
107
84
  const kommando = argv[0];
108
- if (kommando === undefined || kommando === 'help' || kommando === '--help') {
85
+ // Hilfe zuerst: --help ist überall eine Flagge, damit kein Kommando sie als Wert liest.
86
+ if (kommando === undefined) {
109
87
  console.log(HILFE);
110
- return kommando === undefined ? 1 : 0;
88
+ return 1;
89
+ }
90
+ if (kommando === 'help' || kommando === '--help' || kommando === '-h') {
91
+ const thema = argv[1];
92
+ if (thema === undefined || thema.startsWith('-')) {
93
+ console.log(HILFE);
94
+ return 0;
95
+ }
96
+ const text = hilfeText(thema);
97
+ if (text === undefined)
98
+ throw new Error(`Unbekanntes Hilfe-Thema „${thema}". hmgr --help zeigt alle Kommandos.`);
99
+ console.log(text);
100
+ return 0;
101
+ }
102
+ if (argv.slice(1).includes('--help')) {
103
+ const text = hilfeText(kommando);
104
+ if (text === undefined)
105
+ throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
106
+ console.log(text);
107
+ return 0;
111
108
  }
112
109
  if (kommando === 'source') {
113
110
  const unterkommando = argv[1];
114
111
  if (unterkommando === 'add') {
115
112
  const text = argv[2];
116
113
  if (text === undefined)
117
- throw new Error('source add braucht eine URL oder einen Pfad');
114
+ throw new Error('source add braucht eine URL oder einen Pfad, etwa hmgr source add https://example.com/skills.git');
118
115
  const quelle = quelleAusText(text);
119
116
  const { skills, marketplace } = await anwendungsfaelle.quelleHinzufuegen(quelle);
120
117
  console.log(`Quelle ${quelleAlsText(quelle)} (${quelle.art}), ${skills.length} Skills:`);
@@ -137,15 +134,15 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
137
134
  console.log(`${quelle.art} ${quelleAlsText(quelle)}`);
138
135
  return 0;
139
136
  }
140
- throw new Error('source add <url|pfad> oder source list');
137
+ throw new Error(`${unterkommando === undefined ? 'source braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für source`}: source add <url|pfad> oder source list. hmgr help source zeigt Beispiele.`);
141
138
  }
142
139
  if (kommando === 'dir') {
143
- const { rest, werte } = schalterLesen(argv.slice(1));
140
+ const { rest, werte } = schalterLesen(argv.slice(1), 'dir');
144
141
  const unterkommando = rest[0];
145
142
  if (unterkommando === 'add') {
146
143
  const pfad = rest[1];
147
144
  if (pfad === undefined)
148
- throw new Error('dir add braucht einen Pfad');
145
+ throw new Error('dir add braucht einen Pfad, etwa hmgr dir add .');
149
146
  const composer = werte.get('composer');
150
147
  const ergebnis = composer === undefined
151
148
  ? await anwendungsfaelle.arbeitsverzeichnisHinzufuegen(pfad)
@@ -160,7 +157,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
160
157
  if (unterkommando === 'forget') {
161
158
  const pfad = rest[1];
162
159
  if (pfad === undefined)
163
- throw new Error('dir forget braucht einen Pfad');
160
+ throw new Error('dir forget braucht einen Pfad. hmgr dir list zeigt die gemerkten Arbeitsverzeichnisse.');
164
161
  await anwendungsfaelle.arbeitsverzeichnisVergessen(pfad);
165
162
  console.log(`Arbeitsverzeichnis ${path.resolve(pfad)} vergessen, die Links bleiben liegen.`);
166
163
  return 0;
@@ -175,13 +172,14 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
175
172
  }
176
173
  return 0;
177
174
  }
178
- throw new Error('dir add <pfad>, dir forget <pfad> oder dir list');
175
+ throw new Error(`${unterkommando === undefined ? 'dir braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für dir`}: dir add <pfad>, dir forget <pfad> oder dir list. hmgr help dir zeigt Beispiele.`);
179
176
  }
180
177
  if (kommando === 'link' || kommando === 'unlink') {
181
- const { rest, werte, flaggen } = schalterLesen(argv.slice(1));
178
+ const { rest, werte, flaggen } = schalterLesen(argv.slice(1), kommando);
182
179
  const skill = rest[0];
183
180
  if (skill === undefined)
184
- throw new Error(`${kommando} braucht einen Skill-Namen`);
181
+ throw new Error(`${kommando} braucht einen Skill-Namen, etwa hmgr ${kommando} <skill>. ` +
182
+ 'hmgr status zeigt die verlinkten Skills, hmgr source add <url|pfad> die Skills einer Quelle.');
185
183
  const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
186
184
  const lokal = flaggen.has('local') || flaggen.has('lokal');
187
185
  if (ziel.ort.art === 'arbeitsverzeichnis') {
@@ -214,7 +212,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
214
212
  return 0;
215
213
  }
216
214
  if (kommando === 'adopt') {
217
- const { rest, werte, flaggen } = schalterLesen(argv.slice(1));
215
+ const { rest, werte, flaggen } = schalterLesen(argv.slice(1), 'adopt');
218
216
  const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
219
217
  const probelauf = flaggen.has('probelauf');
220
218
  const name = rest[0];
@@ -253,7 +251,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
253
251
  return 0;
254
252
  }
255
253
  if (kommando === 'plugin') {
256
- const { rest, werte, flaggen } = schalterLesen(argv.slice(1));
254
+ const { rest, werte, flaggen } = schalterLesen(argv.slice(1), 'plugin');
257
255
  const unterkommando = rest[0];
258
256
  if (unterkommando === 'list') {
259
257
  const marketplaces = await anwendungsfaelle.marketplaces();
@@ -272,10 +270,10 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
272
270
  return 0;
273
271
  }
274
272
  if (unterkommando !== 'add' && unterkommando !== 'remove')
275
- throw new Error('plugin add <name>, plugin remove <name> oder plugin list');
273
+ throw new Error(`${unterkommando === undefined ? 'plugin braucht ein Unterkommando' : `Unbekanntes Unterkommando „${unterkommando}" für plugin`}: plugin add <name>, plugin remove <name> oder plugin list. hmgr help plugin zeigt Beispiele.`);
276
274
  const name = rest[1];
277
275
  if (name === undefined)
278
- throw new Error(`plugin ${unterkommando} braucht einen Plugin-Namen`);
276
+ throw new Error(`plugin ${unterkommando} braucht einen Plugin-Namen. hmgr plugin list zeigt die Plugins der bekannten Quellen.`);
279
277
  const ziel = { harness: werte.get('harness') ?? 'claude-code', ort: ortLesen(werte.get('ort')) };
280
278
  const lokal = flaggen.has('local') || flaggen.has('lokal');
281
279
  if (ziel.ort.art === 'arbeitsverzeichnis') {
@@ -302,7 +300,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
302
300
  return 0;
303
301
  }
304
302
  if (kommando === 'sync') {
305
- const { rest } = schalterLesen(argv.slice(1));
303
+ const { rest } = schalterLesen(argv.slice(1), 'sync');
306
304
  const ergebnis = await anwendungsfaelle.abgleich(rest[0]);
307
305
  if (ergebnis.zeigerPfad !== undefined)
308
306
  console.log(`Zeiger ${ergebnis.zeigerPfad}`);
@@ -360,7 +358,7 @@ export async function ausfuehren(argv, umgebung, optionen = {}) {
360
358
  console.log(`Nicht vorhanden: ${fehlend.join(', ')}`);
361
359
  return 0;
362
360
  }
363
- throw new Error(`Unbekanntes Kommando „${kommando}"`);
361
+ throw new Error(`Unbekanntes Kommando „${kommando}". hmgr --help zeigt alle Kommandos.`);
364
362
  }
365
363
  function beschreibeOrt(ort) {
366
364
  if (ort.art === 'home')
@@ -382,13 +380,43 @@ function istModellFehler(fehler) {
382
380
  fehler instanceof PluginUnbekannt ||
383
381
  fehler instanceof AbhaengigkeitFehlt);
384
382
  }
383
+ /**
384
+ * Der nächste Schritt zu einem Fehler aus dem Kern. Die Fehlertexte selbst bleiben, wie das Modell
385
+ * sie schreibt, die Handlungsanweisung gehört an die CLI, weil sie Kommandos nennt.
386
+ */
387
+ function naechsterSchritt(fehler) {
388
+ if (fehler instanceof SkillMehrdeutig)
389
+ return `Quelle eingrenzen: hmgr link ${fehler.name} --quelle <teilstring einer der genannten Quellen>`;
390
+ if (fehler instanceof SkillUnbekannt)
391
+ return 'Erst die Quelle hinzufügen: hmgr source add <url|pfad>. hmgr source list zeigt die bekannten Quellen.';
392
+ if (fehler instanceof NameBelegt)
393
+ return `Entweder mit --ersetzen überschreiben oder den fremden Eintrag mit hmgr adopt ${fehler.skillName} übernehmen.`;
394
+ if (fehler instanceof PluginMehrdeutig)
395
+ return 'Quelle eingrenzen: --quelle <teilstring der URL oder des Pfads>. hmgr plugin list zeigt die Marketplaces.';
396
+ if (fehler instanceof PluginUnbekannt)
397
+ return 'hmgr plugin list zeigt die Plugins der bekannten Quellen. Fehlt der Marketplace, erst hmgr source add <url>.';
398
+ if (fehler instanceof AbhaengigkeitFehlt)
399
+ return 'Die Abhängigkeit steht in keinem Plugin desselben Marketplace, das kann nur die Quelle beheben.';
400
+ if (fehler instanceof KeinPassenderRef)
401
+ return 'hmgr update holt neue Tags der Quelle. Sonst einen vorhandenen Tag- oder Branchnamen als --version nennen.';
402
+ if (fehler instanceof HarnessNichtVorhanden)
403
+ return 'Der Harness legt sein Verzeichnis beim ersten Start an. hmgr status zeigt, welche Harnesses hier liegen.';
404
+ if (fehler instanceof OrtNichtUnterstuetzt)
405
+ return 'profil:<name> kennt nur hermes, claude-code und agents kennen home und dir:<pfad>.';
406
+ const text = fehler instanceof Error ? fehler.message : '';
407
+ if (text.startsWith('Unbekannter Harness'))
408
+ return 'Bekannt sind claude-code, hermes und agents.';
409
+ return undefined;
410
+ }
385
411
  export async function main(argv, umgebung) {
386
412
  try {
387
413
  return await ausfuehren(argv, umgebung);
388
414
  }
389
415
  catch (fehler) {
390
416
  const text = fehler instanceof Error ? fehler.message : String(fehler);
391
- console.error(istModellFehler(fehler) ? text : `Fehler: ${text}`);
417
+ const kopf = istModellFehler(fehler) ? text : `Fehler: ${text}`;
418
+ const schritt = naechsterSchritt(fehler);
419
+ console.error(schritt === undefined ? kopf : `${kopf}\n${schritt}`);
392
420
  return 1;
393
421
  }
394
422
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hmgr",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
5
5
  "license": "MIT",
6
6
  "author": "Tim Kaufmann",