hmgr 0.0.1
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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/adapter/checkout-pfad.js +66 -0
- package/dist/adapter/claude-code.js +184 -0
- package/dist/adapter/composer.js +94 -0
- package/dist/adapter/dateisystem.js +126 -0
- package/dist/adapter/git.js +339 -0
- package/dist/adapter/gitignore.js +16 -0
- package/dist/adapter/hermes.js +142 -0
- package/dist/adapter/marketplace-json.js +98 -0
- package/dist/adapter/quelle-text.js +11 -0
- package/dist/adapter/skill-md.js +49 -0
- package/dist/adapter/zustand-json.js +25 -0
- package/dist/cli/main.js +407 -0
- package/dist/kern/anwendungsfaelle.js +683 -0
- package/dist/kern/modell.js +146 -0
- package/dist/kern/version.js +244 -0
- package/dist/ports/harness.js +6 -0
- package/dist/ports/quellen.js +1 -0
- package/dist/ports/zeiger.js +1 -0
- package/dist/ports/ziele.js +1 -0
- package/dist/ports/zustand.js +3 -0
- package/package.json +35 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Begriffe aus CONTEXT.md. Nur Werte und Regeln, keine Pfade zu Dateien außerhalb des Modells.
|
|
2
|
+
/** Ansprache eines Plugins im Zeiger und gegenüber der claude-CLI: `<plugin>@<marketplace>`. */
|
|
3
|
+
export function pluginSchluessel(plugin) {
|
|
4
|
+
return `${plugin.name}@${plugin.marketplace}`;
|
|
5
|
+
}
|
|
6
|
+
export function pluginSchluesselZerlegen(text) {
|
|
7
|
+
const trenner = text.lastIndexOf('@');
|
|
8
|
+
if (trenner <= 0)
|
|
9
|
+
return { name: text };
|
|
10
|
+
return { name: text.slice(0, trenner), marketplace: text.slice(trenner + 1) };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* URL, unter der Claude Code den Marketplace einer Quelle registriert (E24). Git-Hosts außer
|
|
14
|
+
* GitHub und GitLab brauchen das `.git`-Suffix, sonst liest Claude Code die URL als Direktlink
|
|
15
|
+
* auf eine marketplace.json.
|
|
16
|
+
*/
|
|
17
|
+
export function marketplaceUrl(quelle) {
|
|
18
|
+
if (quelle.art === 'lokal')
|
|
19
|
+
return quelle.pfad;
|
|
20
|
+
const url = quelle.url.replace(/\/+$/, '');
|
|
21
|
+
if (url.endsWith('.git'))
|
|
22
|
+
return url;
|
|
23
|
+
let host;
|
|
24
|
+
if (url.includes('://')) {
|
|
25
|
+
try {
|
|
26
|
+
host = new URL(url).hostname;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// SSH in scp-Schreibweise: git@host:besitzer/repo
|
|
34
|
+
host = /^(?:[^@/]+@)?([^@/:]+):/.exec(url)?.[1] ?? '';
|
|
35
|
+
}
|
|
36
|
+
if (host.length === 0)
|
|
37
|
+
return url;
|
|
38
|
+
const klein = host.toLowerCase();
|
|
39
|
+
if (klein === 'github.com' || klein === 'gitlab.com')
|
|
40
|
+
return url;
|
|
41
|
+
return `${url}.git`;
|
|
42
|
+
}
|
|
43
|
+
export function zielSchluessel(ziel) {
|
|
44
|
+
const o = ziel.ort;
|
|
45
|
+
const ort = o.art === 'home' ? 'home' : o.art === 'profil' ? `profil:${o.name}` : `dir:${o.pfad}`;
|
|
46
|
+
return `${ziel.harness}/${ort}`;
|
|
47
|
+
}
|
|
48
|
+
export function quelleSchluessel(quelle) {
|
|
49
|
+
return quelle.art === 'git' ? quelle.url : `lokal:${quelle.pfad}`;
|
|
50
|
+
}
|
|
51
|
+
export class NameBelegt extends Error {
|
|
52
|
+
ziel;
|
|
53
|
+
skillName;
|
|
54
|
+
constructor(ziel, skillName) {
|
|
55
|
+
super(`Im Ziel ${zielSchluessel(ziel)} liegt bereits ein fremder Eintrag „${skillName}"`);
|
|
56
|
+
this.ziel = ziel;
|
|
57
|
+
this.skillName = skillName;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class SkillMehrdeutig extends Error {
|
|
61
|
+
name;
|
|
62
|
+
quellen;
|
|
63
|
+
constructor(name, quellen) {
|
|
64
|
+
super(`Skill „${name}" liegt in mehreren Quellen: ${quellen.map(quelleSchluessel).join(', ')}`);
|
|
65
|
+
this.name = name;
|
|
66
|
+
this.quellen = quellen;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class SkillUnbekannt extends Error {
|
|
70
|
+
name;
|
|
71
|
+
constructor(name) {
|
|
72
|
+
super(`Skill „${name}" liegt in keiner Quelle`);
|
|
73
|
+
this.name = name;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export class PluginMehrdeutig extends Error {
|
|
77
|
+
name;
|
|
78
|
+
quellen;
|
|
79
|
+
constructor(name, quellen) {
|
|
80
|
+
super(`Plugin „${name}" liegt in mehreren Marketplaces: ${quellen.map(quelleSchluessel).join(', ')}`);
|
|
81
|
+
this.name = name;
|
|
82
|
+
this.quellen = quellen;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class PluginUnbekannt extends Error {
|
|
86
|
+
name;
|
|
87
|
+
constructor(name) {
|
|
88
|
+
super(`Plugin „${name}" liegt in keinem Marketplace der bekannten Quellen`);
|
|
89
|
+
this.name = name;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Eine Abhängigkeit löst sich nur im selben Marketplace auf (Q7, kein Cross-Marketplace). */
|
|
93
|
+
export class AbhaengigkeitFehlt extends Error {
|
|
94
|
+
plugin;
|
|
95
|
+
abhaengigkeit;
|
|
96
|
+
constructor(plugin, abhaengigkeit) {
|
|
97
|
+
super(`Plugin „${plugin}" braucht „${abhaengigkeit}", das im selben Marketplace nicht steht`);
|
|
98
|
+
this.plugin = plugin;
|
|
99
|
+
this.abhaengigkeit = abhaengigkeit;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class KeinPassenderRef extends Error {
|
|
103
|
+
skill;
|
|
104
|
+
einschraenkung;
|
|
105
|
+
constructor(skill, einschraenkung) {
|
|
106
|
+
super(`Kein Ref für „${skill}" passt zu ${einschraenkung}`);
|
|
107
|
+
this.skill = skill;
|
|
108
|
+
this.einschraenkung = einschraenkung;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Eine Versionseinschränkung braucht einen Zeiger, in dem sie stehen kann (E33). */
|
|
112
|
+
export class EinschraenkungOhneZeiger extends Error {
|
|
113
|
+
ziel;
|
|
114
|
+
einschraenkung;
|
|
115
|
+
constructor(ziel, einschraenkung) {
|
|
116
|
+
super(`Für ${zielSchluessel(ziel)} gibt es keinen Zeiger, in den „${einschraenkung}" gehört. ` +
|
|
117
|
+
'Eine Versionseinschränkung gibt es nur für ein Arbeitsverzeichnis mit composer.json, ohne --local.');
|
|
118
|
+
this.ziel = ziel;
|
|
119
|
+
this.einschraenkung = einschraenkung;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export class OrtNichtUnterstuetzt extends Error {
|
|
123
|
+
harness;
|
|
124
|
+
ort;
|
|
125
|
+
constructor(harness, ort) {
|
|
126
|
+
super(`Harness ${harness} kennt den Ort „${ort}" nicht`);
|
|
127
|
+
this.harness = harness;
|
|
128
|
+
this.ort = ort;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export class ZeigerMehrdeutig extends Error {
|
|
132
|
+
verzeichnis;
|
|
133
|
+
kandidaten;
|
|
134
|
+
constructor(verzeichnis, kandidaten) {
|
|
135
|
+
super(`Mehrere composer.json unter ${verzeichnis}: ${kandidaten.join(', ')}. Bitte eine mit --composer nennen`);
|
|
136
|
+
this.verzeichnis = verzeichnis;
|
|
137
|
+
this.kandidaten = kandidaten;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export class HarnessNichtVorhanden extends Error {
|
|
141
|
+
harness;
|
|
142
|
+
constructor(harness) {
|
|
143
|
+
super(`Harness ${harness} liegt nicht auf diesem Rechner`);
|
|
144
|
+
this.harness = harness;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Versionen und Einschränkungen nach Composer-Regeln (E33). Reines Modul: keine Ports, kein Git.
|
|
2
|
+
/**
|
|
3
|
+
* Eine Version mit optionalem Präfix `v`, ein bis drei Stellen, dazu eine Vorabversion, die mit
|
|
4
|
+
* einem Buchstaben beginnt. So bleibt `1.2.3.4` unlesbar und wird nicht als Vorabversion von
|
|
5
|
+
* `1.2.3` gedeutet.
|
|
6
|
+
*/
|
|
7
|
+
const MUSTER = /^[vV]?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:[-_.]?([A-Za-z][0-9A-Za-z.\-_]*))?$/;
|
|
8
|
+
/** `1.0 - 2.0`: Composer verlangt Leerraum um den Bindestrich. */
|
|
9
|
+
const SPANNE = /^(\S+)\s+-\s+(\S+)$/;
|
|
10
|
+
const VERGLEICH = /^(>=|<=|!=|<>|==|>|<|=)\s*(.+)$/;
|
|
11
|
+
/** `2.1.*` und `2.*`, mit optionalem Präfix `v`. */
|
|
12
|
+
const WILDCARD = /^[vV]?(\d+)(?:\.(\d+))?\.\*$/;
|
|
13
|
+
function fassung(haupt, neben, patch) {
|
|
14
|
+
return { haupt, neben, patch, vorab: [] };
|
|
15
|
+
}
|
|
16
|
+
function kennungen(text) {
|
|
17
|
+
const teile = [];
|
|
18
|
+
for (const stueck of text.split(/[.\-_]/)) {
|
|
19
|
+
for (const treffer of stueck.match(/\d+|\D+/g) ?? [])
|
|
20
|
+
teile.push(/^\d+$/.test(treffer) ? Number(treffer) : treffer.toLowerCase());
|
|
21
|
+
}
|
|
22
|
+
return teile;
|
|
23
|
+
}
|
|
24
|
+
/** Version samt der Zahl genannter Stellen. Tilde, Caret und Wildcard brauchen diese Zahl. */
|
|
25
|
+
function stellen(text) {
|
|
26
|
+
const treffer = MUSTER.exec(text.trim());
|
|
27
|
+
if (treffer === null)
|
|
28
|
+
return null;
|
|
29
|
+
const roh = treffer[4];
|
|
30
|
+
return {
|
|
31
|
+
version: {
|
|
32
|
+
haupt: Number(treffer[1] ?? 0),
|
|
33
|
+
neben: Number(treffer[2] ?? 0),
|
|
34
|
+
patch: Number(treffer[3] ?? 0),
|
|
35
|
+
vorab: roh === undefined ? [] : kennungen(roh),
|
|
36
|
+
},
|
|
37
|
+
anzahl: treffer[3] !== undefined ? 3 : treffer[2] !== undefined ? 2 : 1,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Fehlende Stellen gelten als 0. Null heißt: der Text ist keine Version. */
|
|
41
|
+
export function versionLesen(text) {
|
|
42
|
+
return stellen(text)?.version ?? null;
|
|
43
|
+
}
|
|
44
|
+
/** Kleiner, gleich, größer als -1, 0, 1. Eine Endfassung steht über ihren Vorabversionen. */
|
|
45
|
+
export function vergleichen(a, b) {
|
|
46
|
+
if (a.haupt !== b.haupt)
|
|
47
|
+
return a.haupt < b.haupt ? -1 : 1;
|
|
48
|
+
if (a.neben !== b.neben)
|
|
49
|
+
return a.neben < b.neben ? -1 : 1;
|
|
50
|
+
if (a.patch !== b.patch)
|
|
51
|
+
return a.patch < b.patch ? -1 : 1;
|
|
52
|
+
if (a.vorab.length === 0 && b.vorab.length === 0)
|
|
53
|
+
return 0;
|
|
54
|
+
if (a.vorab.length === 0)
|
|
55
|
+
return 1;
|
|
56
|
+
if (b.vorab.length === 0)
|
|
57
|
+
return -1;
|
|
58
|
+
const laenge = Math.max(a.vorab.length, b.vorab.length);
|
|
59
|
+
for (let i = 0; i < laenge; i++) {
|
|
60
|
+
const x = a.vorab[i];
|
|
61
|
+
const y = b.vorab[i];
|
|
62
|
+
if (x === undefined)
|
|
63
|
+
return -1;
|
|
64
|
+
if (y === undefined)
|
|
65
|
+
return 1;
|
|
66
|
+
if (typeof x === 'number' && typeof y === 'number') {
|
|
67
|
+
if (x !== y)
|
|
68
|
+
return x < y ? -1 : 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// Eine Zahl steht unter einem Text, wie in `1.0.0-1` gegen `1.0.0-alpha`.
|
|
72
|
+
if (typeof x === 'number')
|
|
73
|
+
return -1;
|
|
74
|
+
if (typeof y === 'number')
|
|
75
|
+
return 1;
|
|
76
|
+
if (x !== y)
|
|
77
|
+
return x < y ? -1 : 1;
|
|
78
|
+
}
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Tilde: „`~1.2` ist gleichbedeutend mit `>=1.2 <2.0.0`, `~1.2.3` mit `>=1.2.3 <1.3.0`", die
|
|
83
|
+
* am weitesten rechts genannte Stelle darf wachsen (getcomposer.org, Writing Version Constraints).
|
|
84
|
+
* Bei nur einer Stelle wächst die Hauptversion.
|
|
85
|
+
*/
|
|
86
|
+
function tilde(gelesen) {
|
|
87
|
+
const { version, anzahl } = gelesen;
|
|
88
|
+
const obere = anzahl >= 3 ? fassung(version.haupt, version.neben + 1, 0) : fassung(version.haupt + 1, 0, 0);
|
|
89
|
+
return [
|
|
90
|
+
{ operator: '>=', version },
|
|
91
|
+
{ operator: '<', version: obere },
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Caret: „`^1.2.3` ist gleichbedeutend mit `>=1.2.3 <2.0.0`", vor 1.0 gilt „`^0.3` als
|
|
96
|
+
* `>=0.3.0 <0.4.0`" (getcomposer.org, Writing Version Constraints). Bei Hauptversion 0 wächst
|
|
97
|
+
* also die Nebenversion, sonst die Hauptversion.
|
|
98
|
+
*/
|
|
99
|
+
function caret(gelesen) {
|
|
100
|
+
const { version, anzahl } = gelesen;
|
|
101
|
+
const obere = version.haupt === 0 && anzahl >= 2 ? fassung(0, version.neben + 1, 0) : fassung(version.haupt + 1, 0, 0);
|
|
102
|
+
return [
|
|
103
|
+
{ operator: '>=', version },
|
|
104
|
+
{ operator: '<', version: obere },
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
/** Eine Angabe ohne Operator: Tilde, Caret, Wildcard oder eine genaue Version. */
|
|
108
|
+
function angabeLesen(text) {
|
|
109
|
+
if (text === '*')
|
|
110
|
+
return [];
|
|
111
|
+
if (text.startsWith('~') || text.startsWith('^')) {
|
|
112
|
+
const gelesen = stellen(text.slice(1));
|
|
113
|
+
if (gelesen === null)
|
|
114
|
+
return null;
|
|
115
|
+
return text.startsWith('~') ? tilde(gelesen) : caret(gelesen);
|
|
116
|
+
}
|
|
117
|
+
const wildcard = WILDCARD.exec(text);
|
|
118
|
+
if (wildcard !== null) {
|
|
119
|
+
const haupt = Number(wildcard[1] ?? 0);
|
|
120
|
+
const neben = wildcard[2];
|
|
121
|
+
// `1.0.*` heißt `>=1.0 <1.1`, `2.*` heißt `>=2.0 <3.0`.
|
|
122
|
+
return [
|
|
123
|
+
{ operator: '>=', version: fassung(haupt, neben === undefined ? 0 : Number(neben), 0) },
|
|
124
|
+
{
|
|
125
|
+
operator: '<',
|
|
126
|
+
version: neben === undefined ? fassung(haupt + 1, 0, 0) : fassung(haupt, Number(neben) + 1, 0),
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
const version = versionLesen(text);
|
|
131
|
+
if (version === null)
|
|
132
|
+
return null;
|
|
133
|
+
return [{ operator: '=', version }];
|
|
134
|
+
}
|
|
135
|
+
function tokenLesen(text) {
|
|
136
|
+
const vergleich = VERGLEICH.exec(text);
|
|
137
|
+
if (vergleich === null)
|
|
138
|
+
return angabeLesen(text);
|
|
139
|
+
const version = versionLesen(vergleich[2] ?? '');
|
|
140
|
+
if (version === null)
|
|
141
|
+
return null;
|
|
142
|
+
const roh = vergleich[1] ?? '=';
|
|
143
|
+
const operator = roh === '<>' ? '!=' : roh === '==' ? '=' : roh;
|
|
144
|
+
return [{ operator, version }];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Eine Bindestrich-Spanne: „`1.0 - 2.0` ist gleichbedeutend mit `>=1.0.0 <2.1`, weil aus der
|
|
148
|
+
* `2.0` eine `2.0.*` wird. `1.0.0 - 2.1.0` dagegen ist `>=1.0.0 <=2.1.0`" (getcomposer.org).
|
|
149
|
+
*/
|
|
150
|
+
function spanneLesen(von, bis) {
|
|
151
|
+
const unten = stellen(von);
|
|
152
|
+
const oben = stellen(bis);
|
|
153
|
+
if (unten === null || oben === null)
|
|
154
|
+
return null;
|
|
155
|
+
if (oben.anzahl >= 3)
|
|
156
|
+
return [
|
|
157
|
+
{ operator: '>=', version: unten.version },
|
|
158
|
+
{ operator: '<=', version: oben.version },
|
|
159
|
+
];
|
|
160
|
+
const grenze = oben.anzahl === 2 ? fassung(oben.version.haupt, oben.version.neben + 1, 0) : fassung(oben.version.haupt + 1, 0, 0);
|
|
161
|
+
return [
|
|
162
|
+
{ operator: '>=', version: unten.version },
|
|
163
|
+
{ operator: '<', version: grenze },
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
/** UND-verknüpfte Bedingungen, getrennt durch Leerzeichen oder Komma. */
|
|
167
|
+
function gruppeLesen(text) {
|
|
168
|
+
if (text.length === 0)
|
|
169
|
+
return null;
|
|
170
|
+
const spanne = SPANNE.exec(text);
|
|
171
|
+
if (spanne !== null)
|
|
172
|
+
return spanneLesen(spanne[1] ?? '', spanne[2] ?? '');
|
|
173
|
+
const bedingungen = [];
|
|
174
|
+
for (const token of text.split(/[\s,]+/)) {
|
|
175
|
+
if (token.length === 0)
|
|
176
|
+
continue;
|
|
177
|
+
const gelesen = tokenLesen(token);
|
|
178
|
+
if (gelesen === null)
|
|
179
|
+
return null;
|
|
180
|
+
bedingungen.push(...gelesen);
|
|
181
|
+
}
|
|
182
|
+
return bedingungen;
|
|
183
|
+
}
|
|
184
|
+
/** Null heißt: der Text ist keine Einschränkung, er gilt dann als Ref-Name (E33). */
|
|
185
|
+
export function einschraenkungLesen(text) {
|
|
186
|
+
const roh = text.trim();
|
|
187
|
+
if (roh.length === 0)
|
|
188
|
+
return null;
|
|
189
|
+
const gruppen = [];
|
|
190
|
+
for (const teil of roh.split('||')) {
|
|
191
|
+
const gruppe = gruppeLesen(teil.trim());
|
|
192
|
+
if (gruppe === null)
|
|
193
|
+
return null;
|
|
194
|
+
gruppen.push(gruppe);
|
|
195
|
+
}
|
|
196
|
+
return { gruppen };
|
|
197
|
+
}
|
|
198
|
+
function passtBedingung(version, bedingung) {
|
|
199
|
+
const c = vergleichen(version, bedingung.version);
|
|
200
|
+
switch (bedingung.operator) {
|
|
201
|
+
case '=':
|
|
202
|
+
return c === 0;
|
|
203
|
+
case '!=':
|
|
204
|
+
return c !== 0;
|
|
205
|
+
case '>':
|
|
206
|
+
return c > 0;
|
|
207
|
+
case '>=':
|
|
208
|
+
return c >= 0;
|
|
209
|
+
case '<':
|
|
210
|
+
return c < 0;
|
|
211
|
+
case '<=':
|
|
212
|
+
return c <= 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export function passt(version, einschraenkung) {
|
|
216
|
+
return einschraenkung.gruppen.some((gruppe) => gruppe.every((bedingung) => passtBedingung(version, bedingung)));
|
|
217
|
+
}
|
|
218
|
+
/** Nennt die Einschränkung selbst eine Vorabversion, dann sind Vorabversionen wählbar (E33). */
|
|
219
|
+
function nenntVorab(einschraenkung) {
|
|
220
|
+
return einschraenkung.gruppen.some((gruppe) => gruppe.some((b) => b.version.vorab.length > 0));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Der höchste Tag, der zur Einschränkung passt, oder null. `refs` sind die Tags der Quelle,
|
|
224
|
+
* Branches gehören nicht hinein (E33). Tags ohne Versions-Schema fallen heraus.
|
|
225
|
+
*/
|
|
226
|
+
export function passenderRef(refs, einschraenkung) {
|
|
227
|
+
const gelesen = einschraenkungLesen(einschraenkung);
|
|
228
|
+
if (gelesen === null)
|
|
229
|
+
return null;
|
|
230
|
+
const vorabErlaubt = nenntVorab(gelesen);
|
|
231
|
+
let bester;
|
|
232
|
+
for (const ref of refs) {
|
|
233
|
+
const version = versionLesen(ref);
|
|
234
|
+
if (version === null)
|
|
235
|
+
continue;
|
|
236
|
+
if (version.vorab.length > 0 && !vorabErlaubt)
|
|
237
|
+
continue;
|
|
238
|
+
if (!passt(version, gelesen))
|
|
239
|
+
continue;
|
|
240
|
+
if (bester === undefined || vergleichen(version, bester.version) > 0)
|
|
241
|
+
bester = { ref, version };
|
|
242
|
+
}
|
|
243
|
+
return bester?.ref ?? null;
|
|
244
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hmgr",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Harness Manager: Skills und Plugins für AI-Harnesses verwalten",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Tim Kaufmann",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"hmgr": "dist/cli/main.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.6"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "node src/cli/main.ts",
|
|
21
|
+
"build": "tsc -p tsconfig.build.json",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"type-check": "tsc --noEmit",
|
|
25
|
+
"prepack": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"yaml": "^2.8.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^24.0.0",
|
|
32
|
+
"typescript": "^5.9.0",
|
|
33
|
+
"vitest": "^3.2.0"
|
|
34
|
+
}
|
|
35
|
+
}
|