hmgr 0.0.4 → 0.0.5
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/dist/adapter/claude-code.js +21 -17
- package/dist/adapter/dateisystem.js +105 -5
- package/dist/adapter/git.js +96 -10
- package/dist/adapter/hermes.js +163 -10
- package/dist/adapter/skill-md.js +94 -18
- package/dist/adapter/terminal.js +1 -1
- package/dist/adapter/zustand-json.js +4 -0
- package/dist/cli/hilfe.js +129 -26
- package/dist/cli/main.js +268 -20
- package/dist/gui/icons.js +114 -0
- package/dist/gui/main.js +481 -0
- package/dist/gui/oeffnen.js +33 -0
- package/dist/gui/preload.cjs +55 -0
- package/dist/gui/renderer/fonts/Inter-LICENSE.txt +92 -0
- package/dist/gui/renderer/fonts/InterVariable.woff2 +0 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-Medium.woff2 +0 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-OFL.txt +93 -0
- package/dist/gui/renderer/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/dist/gui/renderer/index.html +491 -0
- package/dist/gui/renderer/renderer.js +2611 -0
- package/dist/gui/renderer/styles.css +638 -0
- package/dist/kern/anwendungsfaelle.js +688 -65
- package/dist/kern/modell.js +159 -0
- package/dist/ports/harness.js +13 -0
- package/dist/ports/zustand.js +1 -1
- package/package.json +5 -2
package/dist/kern/modell.js
CHANGED
|
@@ -63,6 +63,64 @@ export function quelleSchluessel(quelle) {
|
|
|
63
63
|
const ort = urlOrt(quelle.url);
|
|
64
64
|
return ort === undefined ? quelle.url : ortSchluessel(ort);
|
|
65
65
|
}
|
|
66
|
+
/** Letztes nicht leeres Segment eines Pfads oder einer URL, ohne abschließende Schrägstriche. */
|
|
67
|
+
function letztesSegment(text) {
|
|
68
|
+
const bereinigt = text.replace(/\/+$/, '');
|
|
69
|
+
const segmente = bereinigt.split('/').filter((s) => s.length > 0);
|
|
70
|
+
return segmente[segmente.length - 1] ?? bereinigt;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Abgeleiteter Anzeigename einer Quelle (E59), ohne Rücksicht auf einen gespeicherten Namen: bei
|
|
74
|
+
* einer Git-Quelle das letzte Pfadsegment der URL ohne `.git`-Suffix (über `urlOrt`, wie E36 den
|
|
75
|
+
* Repo-Namen schon für die Identität liest), bei einer lokalen Quelle ihr Ordnername.
|
|
76
|
+
*/
|
|
77
|
+
export function quelleNameAbleiten(quelle) {
|
|
78
|
+
if (quelle.art === 'lokal')
|
|
79
|
+
return letztesSegment(quelle.pfad);
|
|
80
|
+
const ort = urlOrt(quelle.url);
|
|
81
|
+
return ort !== undefined ? ort.repo : letztesSegment(quelle.url);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Anzeigename einer Quelle (E59): der gespeicherte Name, sonst der abgeleitete. Reine Anzeige,
|
|
85
|
+
* geht nicht in `quelleSchluessel` ein. CLI und GUI nutzen ausschließlich diesen Helfer.
|
|
86
|
+
*/
|
|
87
|
+
export function quelleName(quelle) {
|
|
88
|
+
if (quelle.name !== undefined && quelle.name.trim().length > 0)
|
|
89
|
+
return quelle.name;
|
|
90
|
+
return quelleNameAbleiten(quelle);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Ein Anzeigename, den noch keine der genannten Quellen trägt (E59): der Wunschname, sonst mit
|
|
94
|
+
* angehängter Zahl. Nur für Wege, die eine Quelle nebenbei anlegen (Übernahme, E31): dort gibt es
|
|
95
|
+
* niemanden, der eine Kollision auflösen könnte, und ein Abbruch ließe die Übernahme ohne Ausweg
|
|
96
|
+
* scheitern. Wo der Nutzer den Namen selbst nennt, bleibt eine Kollision ein Fehler.
|
|
97
|
+
*/
|
|
98
|
+
export function quelleNameFrei(wunsch, vorhandene) {
|
|
99
|
+
const belegt = new Set(vorhandene.map(quelleName));
|
|
100
|
+
if (!belegt.has(wunsch))
|
|
101
|
+
return wunsch;
|
|
102
|
+
for (let n = 2;; n += 1) {
|
|
103
|
+
const kandidat = `${wunsch}-${n}`;
|
|
104
|
+
if (!belegt.has(kandidat))
|
|
105
|
+
return kandidat;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Dieselbe Quelle mit gesetztem Anzeigenamen (E59). `undefined` lässt sie unverändert. */
|
|
109
|
+
export function quelleMitNamen(quelle, name) {
|
|
110
|
+
if (name === undefined)
|
|
111
|
+
return quelle;
|
|
112
|
+
return quelle.art === 'git' ? { art: 'git', url: quelle.url, name } : { art: 'lokal', pfad: quelle.pfad, name };
|
|
113
|
+
}
|
|
114
|
+
/** Ein Anzeigename, den schon eine andere geführte Quelle trägt (E59): Namen sind eindeutig. */
|
|
115
|
+
export class QuelleNameBelegt extends Error {
|
|
116
|
+
angefragterName;
|
|
117
|
+
andere;
|
|
118
|
+
constructor(angefragterName, andere) {
|
|
119
|
+
super(`Name „${angefragterName}" ist schon vergeben an ${quelleAlsText(andere)}. Einen anderen Namen wählen.`);
|
|
120
|
+
this.angefragterName = angefragterName;
|
|
121
|
+
this.andere = andere;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
66
124
|
export class NameBelegt extends Error {
|
|
67
125
|
ziel;
|
|
68
126
|
skillName;
|
|
@@ -88,6 +146,14 @@ export class SkillUnbekannt extends Error {
|
|
|
88
146
|
this.name = name;
|
|
89
147
|
}
|
|
90
148
|
}
|
|
149
|
+
/** Eine Quelle, die im Zustand nicht geführt wird (E54): Entfernen kennt nur bekannte Quellen. */
|
|
150
|
+
export class QuelleUnbekannt extends Error {
|
|
151
|
+
quelle;
|
|
152
|
+
constructor(quelle) {
|
|
153
|
+
super(`Quelle „${quelleAlsText(quelle)}" ist nicht bekannt`);
|
|
154
|
+
this.quelle = quelle;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
91
157
|
export class PluginMehrdeutig extends Error {
|
|
92
158
|
name;
|
|
93
159
|
quellen;
|
|
@@ -134,6 +200,36 @@ export class EinschraenkungOhneZeiger extends Error {
|
|
|
134
200
|
this.einschraenkung = einschraenkung;
|
|
135
201
|
}
|
|
136
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Schreiben in einen composer.json-Zeiger ist gesperrt, bis das Composer-Thema (E41, B13) gesondert
|
|
205
|
+
* entschieden ist (E57): der Zeiger ist ein geteiltes Team-Artefakt, ein Schreiben ohne Rücksprache
|
|
206
|
+
* beträfe Kollegen. Lesen bleibt erlaubt, nur link, unlink und plugin add/remove ohne `--local`
|
|
207
|
+
* treffen diese Sperre.
|
|
208
|
+
*/
|
|
209
|
+
export class ZeigerSchreibenGesperrt extends Error {
|
|
210
|
+
ziel;
|
|
211
|
+
constructor(ziel) {
|
|
212
|
+
super(`Schreiben in den Zeiger von ${zielSchluessel(ziel)} ist gesperrt, bis das Composer-Thema entschieden ist – ` +
|
|
213
|
+
'der Zeiger ist ein geteiltes Team-Artefakt. Nutze --local.');
|
|
214
|
+
this.ziel = ziel;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* `quelleAufraeumen` bricht ab, wenn ein Checkout der Quelle lokale Änderungen hat (E15, F31a):
|
|
219
|
+
* geprüft VOR der ersten Löschung, nichts wird gelöscht, kein stilles Teil-Überspringen. Der
|
|
220
|
+
* Aufrufer lässt bei diesem Fehler auch das Vergessen der Quelle unterbleiben, alles bleibt liegen.
|
|
221
|
+
*/
|
|
222
|
+
export class QuelleAufraeumenLokaleAenderungen extends Error {
|
|
223
|
+
checkouts;
|
|
224
|
+
constructor(checkouts) {
|
|
225
|
+
const kopf = checkouts.length === 1
|
|
226
|
+
? 'Ein Checkout hat lokale Änderungen, nichts wurde gelöscht:'
|
|
227
|
+
: `${checkouts.length} Checkouts haben lokale Änderungen, nichts wurde gelöscht:`;
|
|
228
|
+
const pfade = checkouts.map((c) => ` ${c.pfad}`).join('\n');
|
|
229
|
+
super(`${kopf}\n${pfade}\nCommitte/pushe die Änderungen oder verwirf sie, danach lässt sich das Entfernen wiederholen.`);
|
|
230
|
+
this.checkouts = checkouts;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
137
233
|
export class OrtNichtUnterstuetzt extends Error {
|
|
138
234
|
harness;
|
|
139
235
|
ort;
|
|
@@ -159,3 +255,66 @@ export class HarnessNichtVorhanden extends Error {
|
|
|
159
255
|
this.harness = harness;
|
|
160
256
|
}
|
|
161
257
|
}
|
|
258
|
+
/** Ein Name, den kein Harness-Typ trägt: Aktivieren, Deaktivieren und Säubern kennen nur bekannte Typen (E38). */
|
|
259
|
+
export class HarnessTypUnbekannt extends Error {
|
|
260
|
+
harness;
|
|
261
|
+
constructor(harness) {
|
|
262
|
+
super(`„${harness}" ist kein bekannter Harness-Typ`);
|
|
263
|
+
this.harness = harness;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Ein genannter Name, unter dem am Ziel kein fremder Eintrag liegt (E46): weder Übernehmen mit
|
|
268
|
+
* Eintrag-Auswahl noch Löschen kennen einen anderen als einen tatsächlich fremden Eintrag. Ein
|
|
269
|
+
* hmgr-eigener Link zählt hier ebenso wenig wie ein Name, der gar nicht existiert.
|
|
270
|
+
*/
|
|
271
|
+
export class FremderEintragUnbekannt extends Error {
|
|
272
|
+
ziel;
|
|
273
|
+
name;
|
|
274
|
+
constructor(ziel, name) {
|
|
275
|
+
super(`Im Ziel ${zielSchluessel(ziel)} liegt kein fremder Eintrag „${name}"`);
|
|
276
|
+
this.ziel = ziel;
|
|
277
|
+
this.name = name;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/** Ein Harness-Typ ohne die Fähigkeit „verwalteter Skill-Ordner" (E56): heute nur hermes. */
|
|
281
|
+
export class SkillOrdnerFaehigkeitFehlt extends Error {
|
|
282
|
+
harness;
|
|
283
|
+
constructor(harness) {
|
|
284
|
+
super(`Harness ${harness} kennt keinen verwalteten Skill-Ordner`);
|
|
285
|
+
this.harness = harness;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/** Für ein Ziel ist schon ein Skill-Ordner verwaltet (E56): Festlegen erlaubt nur genau einen. */
|
|
289
|
+
export class SkillOrdnerBereitsVerwaltet extends Error {
|
|
290
|
+
ziel;
|
|
291
|
+
pfad;
|
|
292
|
+
constructor(ziel, pfad) {
|
|
293
|
+
super(`Für ${zielSchluessel(ziel)} ist schon ein Skill-Ordner verwaltet: ${pfad}`);
|
|
294
|
+
this.ziel = ziel;
|
|
295
|
+
this.pfad = pfad;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/** Für ein Ziel ist kein Skill-Ordner verwaltet (E56): Entfernen kennt nur einen vorhandenen Eintrag. */
|
|
299
|
+
export class SkillOrdnerNichtVerwaltet extends Error {
|
|
300
|
+
ziel;
|
|
301
|
+
constructor(ziel) {
|
|
302
|
+
super(`Für ${zielSchluessel(ziel)} ist kein Skill-Ordner verwaltet`);
|
|
303
|
+
this.ziel = ziel;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Ein Pfad, der als verwalteter Skill-Ordner nicht in Frage kommt (E56): außerhalb des
|
|
308
|
+
* Hermes-Home oder Hermes' eigener Ordner `skills` (oder darunter).
|
|
309
|
+
*/
|
|
310
|
+
export class SkillOrdnerUngueltig extends Error {
|
|
311
|
+
pfad;
|
|
312
|
+
grund;
|
|
313
|
+
constructor(pfad, grund) {
|
|
314
|
+
super(grund === 'ausserhalb'
|
|
315
|
+
? `${pfad} liegt nicht unterhalb des Hermes-Home`
|
|
316
|
+
: `${pfad} ist Hermes' eigener Skill-Ordner`);
|
|
317
|
+
this.pfad = pfad;
|
|
318
|
+
this.grund = grund;
|
|
319
|
+
}
|
|
320
|
+
}
|
package/dist/ports/harness.js
CHANGED
|
@@ -4,3 +4,16 @@ export function kannProfile(h) {
|
|
|
4
4
|
export function kannPlugins(h) {
|
|
5
5
|
return h.plugins !== undefined;
|
|
6
6
|
}
|
|
7
|
+
export function kannZurueckbauen(h) {
|
|
8
|
+
return typeof h.zurueckbauen === 'function';
|
|
9
|
+
}
|
|
10
|
+
export function kannIstRegistriert(h) {
|
|
11
|
+
return typeof h.istRegistriert === 'function';
|
|
12
|
+
}
|
|
13
|
+
export function kannSkillOrdner(h) {
|
|
14
|
+
return (typeof h.verwalteterSkillOrdner === 'function' &&
|
|
15
|
+
typeof h.skillOrdnerOrte === 'function' &&
|
|
16
|
+
typeof h.skillOrdnerFestlegen === 'function' &&
|
|
17
|
+
typeof h.skillOrdnerEntfernen === 'function' &&
|
|
18
|
+
typeof h.skillOrdnerLoeschen === 'function');
|
|
19
|
+
}
|
package/dist/ports/zustand.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hmgr",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tim Kaufmann",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"dev": "node src/cli/main.ts",
|
|
21
|
-
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"build": "tsc -p tsconfig.build.json && node scripts/copy-gui-assets.mjs",
|
|
22
|
+
"gui": "npm run build && electron dist/gui/main.js",
|
|
23
|
+
"gui:smoke": "npm run build && node scripts/gui-smoke-test.ts",
|
|
22
24
|
"test": "vitest run",
|
|
23
25
|
"test:watch": "vitest",
|
|
24
26
|
"type-check": "tsc --noEmit",
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@types/node": "^24.0.0",
|
|
34
|
+
"electron": "^44.0.0",
|
|
32
35
|
"typescript": "^5.9.0",
|
|
33
36
|
"vitest": "^3.2.0"
|
|
34
37
|
},
|