hmgr 0.0.26 → 0.0.28
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/agents.js +10 -5
- package/dist/adapter/claude-code.js +23 -12
- package/dist/adapter/dateisystem.js +4 -1
- package/dist/adapter/heime.js +9 -1
- package/dist/adapter/hermes.js +11 -4
- package/dist/adapter/wsl.js +3 -0
- package/dist/cli/hilfe.js +7 -0
- package/dist/cli/main.js +9 -5
- package/dist/gui/hmgr-cli.js +41 -5
- package/dist/gui/main.js +37 -0
- package/dist/gui/renderer/index.html +2 -2
- package/dist/gui/renderer/renderer.js +67 -20
- package/dist/kern/anwendungsfaelle.js +8 -6
- package/dist/kern/modell.js +39 -0
- package/package.json +1 -1
package/dist/adapter/agents.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
// Das universelle Skill-Verzeichnis `.agents/skills`: ein Harness-Typ ohne Programm dahinter (E35).
|
|
2
2
|
// Home ist `~/.agents/skills`, das Arbeitsverzeichnis `<pfad>/.agents/skills`.
|
|
3
|
-
import {
|
|
3
|
+
import { stat } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { OrtNichtUnterstuetzt } from "../kern/modell.js";
|
|
6
6
|
import { gitignoreEintragen, istGitRepo } from "./gitignore.js";
|
|
7
|
-
import {
|
|
7
|
+
import { LokalerZugriff } from "./dateisystem.js";
|
|
8
|
+
import { arbeitsverzeichnisVerbinden, nurHome } from "./heime.js";
|
|
8
9
|
const AGENTS_VERZEICHNIS = '.agents';
|
|
9
10
|
const SKILLS_ORDNER = 'skills';
|
|
10
11
|
const GITIGNORE_ZEILE = '.agents/';
|
|
11
12
|
export class Agents {
|
|
12
13
|
name = 'agents';
|
|
13
14
|
heime;
|
|
15
|
+
/** Zugriff je Ort (E96): lokal, für ein Arbeitsverzeichnis in WSL die Bridge der Distro. */
|
|
16
|
+
zugriffFuer;
|
|
14
17
|
/**
|
|
15
18
|
* `heime` sagt, wo das Homedir liegt (E78, F89): umstellbar, aber ohne zusätzliche Homedirs —
|
|
16
19
|
* die gibt es bisher nur bei claude-code. Ein blanker Pfad steht für „genau dieses Home".
|
|
17
20
|
*/
|
|
18
|
-
constructor(heime) {
|
|
21
|
+
constructor(heime, zugriffFuer) {
|
|
19
22
|
this.heime = typeof heime === 'string' ? nurHome(heime) : heime;
|
|
23
|
+
const lokal = new LokalerZugriff();
|
|
24
|
+
this.zugriffFuer = zugriffFuer ?? (() => lokal);
|
|
20
25
|
}
|
|
21
26
|
get home() {
|
|
22
27
|
return this.heime.primaer(this.name);
|
|
@@ -37,11 +42,11 @@ export class Agents {
|
|
|
37
42
|
if (ort.art === 'home')
|
|
38
43
|
return path.join(this.heime.pfad(this.name, ort), AGENTS_VERZEICHNIS, SKILLS_ORDNER);
|
|
39
44
|
if (ort.art === 'arbeitsverzeichnis')
|
|
40
|
-
return
|
|
45
|
+
return arbeitsverzeichnisVerbinden(ort.pfad, AGENTS_VERZEICHNIS, SKILLS_ORDNER);
|
|
41
46
|
throw new OrtNichtUnterstuetzt(this.name, ort.art);
|
|
42
47
|
}
|
|
43
48
|
async vorbereiten(ort) {
|
|
44
|
-
await
|
|
49
|
+
await this.zugriffFuer(ort).verzeichnisAnlegen(this.zielVerzeichnis(ort));
|
|
45
50
|
// .agents/ darf nicht ins Repo (B7).
|
|
46
51
|
if (ort.art === 'arbeitsverzeichnis' && (await istGitRepo(ort.pfad)))
|
|
47
52
|
await gitignoreEintragen(ort.pfad, GITIGNORE_ZEILE);
|
|
@@ -3,11 +3,11 @@ import spawn from 'cross-spawn';
|
|
|
3
3
|
import { realpathSync } from 'node:fs';
|
|
4
4
|
import { readFile, stat } from 'node:fs/promises';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { HeimOhneCliZugriff, McpAbweichenderEintrag, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, OrtNichtUnterstuetzt, pluginSchluessel, } from "../kern/modell.js";
|
|
6
|
+
import { ArbeitsverzeichnisOhneCliZugriff, HeimOhneCliZugriff, McpAbweichenderEintrag, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, OrtNichtUnterstuetzt, pluginSchluessel, wslUncZerlegen, } from "../kern/modell.js";
|
|
7
7
|
import { ohneBom } from "./datei-text.js";
|
|
8
8
|
import { LokalerZugriff } from "./dateisystem.js";
|
|
9
9
|
import { gitignoreEintragen, istGitRepo } from "./gitignore.js";
|
|
10
|
-
import { heimVerbinden, nurHome } from "./heime.js";
|
|
10
|
+
import { arbeitsverzeichnisVerbinden, heimVerbinden, nurHome } from "./heime.js";
|
|
11
11
|
import { tiefGleich } from "./vergleich.js";
|
|
12
12
|
const CLAUDE_VERZEICHNIS = '.claude';
|
|
13
13
|
const SKILLS_ORDNER = 'skills';
|
|
@@ -19,6 +19,17 @@ const SKILLS_ORDNER = 'skills';
|
|
|
19
19
|
function zusaetzlichesHeim(ort) {
|
|
20
20
|
return ort.art === 'home' ? ort.heim : undefined;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* MCP und Plugins gehen nur über die claude-CLI dieses Rechners (E61, E78, E96): ein zusätzliches
|
|
24
|
+
* Homedir und ein Arbeitsverzeichnis in WSL liegen außerhalb ihrer Konfiguration.
|
|
25
|
+
*/
|
|
26
|
+
function ohneCliZugriffPruefen(harness, ort, was) {
|
|
27
|
+
const heim = zusaetzlichesHeim(ort);
|
|
28
|
+
if (heim !== undefined)
|
|
29
|
+
throw new HeimOhneCliZugriff(harness, heim, was);
|
|
30
|
+
if (ort.art === 'arbeitsverzeichnis' && wslUncZerlegen(ort.pfad) !== undefined)
|
|
31
|
+
throw new ArbeitsverzeichnisOhneCliZugriff(harness, ort.pfad, was);
|
|
32
|
+
}
|
|
22
33
|
const GITIGNORE_ZEILE = '.claude/';
|
|
23
34
|
/**
|
|
24
35
|
* Standard-Ausführer: `claude` vom PATH. Ein Fehlschlag wird zum Code, nicht zur Ausnahme.
|
|
@@ -125,9 +136,7 @@ class ClaudePlugins {
|
|
|
125
136
|
* Startverzeichnis ab, mit dem Zielverzeichnis als cwd landete sie unter `.claude/skills/`.
|
|
126
137
|
*/
|
|
127
138
|
scope(ziel) {
|
|
128
|
-
|
|
129
|
-
if (heim !== undefined)
|
|
130
|
-
throw new HeimOhneCliZugriff(this.harness.name, heim, 'Plugins');
|
|
139
|
+
ohneCliZugriffPruefen(this.harness.name, ziel.ort, 'Plugins');
|
|
131
140
|
if (ziel.ort.art === 'home')
|
|
132
141
|
return { scope: 'user' };
|
|
133
142
|
if (ziel.ort.art === 'arbeitsverzeichnis')
|
|
@@ -249,9 +258,7 @@ class ClaudeMcp {
|
|
|
249
258
|
return 'Claude Code übernimmt die Änderung mit der nächsten Sitzung.';
|
|
250
259
|
}
|
|
251
260
|
scope(ort) {
|
|
252
|
-
|
|
253
|
-
if (heim !== undefined)
|
|
254
|
-
throw new HeimOhneCliZugriff(this.harness.name, heim, 'MCP-Server');
|
|
261
|
+
ohneCliZugriffPruefen(this.harness.name, ort, 'MCP-Server');
|
|
255
262
|
if (ort.art === 'home')
|
|
256
263
|
return { scope: 'user' };
|
|
257
264
|
if (ort.art === 'arbeitsverzeichnis')
|
|
@@ -308,6 +315,9 @@ class ClaudeMcp {
|
|
|
308
315
|
async geteilteFundorte(ort) {
|
|
309
316
|
if (ort.art !== 'arbeitsverzeichnis')
|
|
310
317
|
return [];
|
|
318
|
+
// In WSL gibt es hier nichts zu übernehmen (E96): die MCP-Verwaltung ist dort gesperrt.
|
|
319
|
+
if (wslUncZerlegen(ort.pfad) !== undefined)
|
|
320
|
+
return [];
|
|
311
321
|
const datei = path.join(ort.pfad, '.mcp.json');
|
|
312
322
|
const server = await projektMcpJsonLesen(ort.pfad);
|
|
313
323
|
return Object.keys(server).map((name) => ({ name, datei, scope: 'project' }));
|
|
@@ -331,6 +341,7 @@ class ClaudeMcp {
|
|
|
331
341
|
const ziel = { harness: this.harness.name, ort };
|
|
332
342
|
if (ort.art !== 'arbeitsverzeichnis')
|
|
333
343
|
throw new McpUebernahmeNichtMoeglich(ziel);
|
|
344
|
+
ohneCliZugriffPruefen(this.harness.name, ort, 'MCP-Server');
|
|
334
345
|
const datei = path.join(ort.pfad, '.mcp.json');
|
|
335
346
|
const block = (await projektMcpJsonLesen(ort.pfad))[name];
|
|
336
347
|
if (block === undefined)
|
|
@@ -366,9 +377,7 @@ class ClaudeMcp {
|
|
|
366
377
|
* die MCP-Operationen, bevor sie den Adapter erreichen.
|
|
367
378
|
*/
|
|
368
379
|
async mcpServersAm(ort) {
|
|
369
|
-
|
|
370
|
-
if (heim !== undefined)
|
|
371
|
-
throw new HeimOhneCliZugriff(this.harness.name, heim, 'MCP-Server');
|
|
380
|
+
ohneCliZugriffPruefen(this.harness.name, ort, 'MCP-Server');
|
|
372
381
|
const daten = await this.claudeJsonLesen();
|
|
373
382
|
if (ort.art === 'home')
|
|
374
383
|
return mcpServersVon(daten);
|
|
@@ -442,13 +451,15 @@ export class ClaudeCode {
|
|
|
442
451
|
if (ort.art === 'home')
|
|
443
452
|
return heimVerbinden(ort, this.heime.pfad(this.name, ort), CLAUDE_VERZEICHNIS, SKILLS_ORDNER);
|
|
444
453
|
if (ort.art === 'arbeitsverzeichnis')
|
|
445
|
-
return
|
|
454
|
+
return arbeitsverzeichnisVerbinden(ort.pfad, CLAUDE_VERZEICHNIS, SKILLS_ORDNER);
|
|
446
455
|
throw new OrtNichtUnterstuetzt(this.name, ort.art);
|
|
447
456
|
}
|
|
448
457
|
async vorbereiten(ort) {
|
|
449
458
|
// Über den Zugriff, nicht über `mkdir`: in einem WSL-Heim liegt das Verzeichnis im
|
|
450
459
|
// Linux-Dateisystem, wo Windows-Node nicht anlegen kann (E78).
|
|
451
460
|
await this.zugriffFuer(ort).verzeichnisAnlegen(this.zielVerzeichnis(ort));
|
|
461
|
+
// `.git` und `.gitignore` sind gewöhnliche Dateien im Arbeitsverzeichnis und bleiben auch bei
|
|
462
|
+
// einem WSL-Pfad über den Share unter `ort.pfad` erreichbar, nicht über den Linux-Pfad (E96).
|
|
452
463
|
if (ort.art === 'arbeitsverzeichnis' && (await istGitRepo(ort.pfad)))
|
|
453
464
|
await gitignoreEintragen(ort.pfad, GITIGNORE_ZEILE);
|
|
454
465
|
}
|
|
@@ -51,6 +51,9 @@ export function linkFehlerHinweis(plattform = process.platform) {
|
|
|
51
51
|
*/
|
|
52
52
|
export class LokalerZugriff {
|
|
53
53
|
trenner = path.sep;
|
|
54
|
+
linkFehlerHinweis() {
|
|
55
|
+
return linkFehlerHinweis();
|
|
56
|
+
}
|
|
54
57
|
verbinden(verzeichnis, name) {
|
|
55
58
|
return path.join(verzeichnis, name);
|
|
56
59
|
}
|
|
@@ -213,7 +216,7 @@ export class DateisystemZiele {
|
|
|
213
216
|
await zugriff.linkAnlegen(pfad, checkoutZiel);
|
|
214
217
|
}
|
|
215
218
|
catch (ursache) {
|
|
216
|
-
throw new LinkAnlegenFehlgeschlagen(pfad, checkoutZiel, ursache instanceof Error ? ursache.message : String(ursache), linkFehlerHinweis());
|
|
219
|
+
throw new LinkAnlegenFehlgeschlagen(pfad, checkoutZiel, ursache instanceof Error ? ursache.message : String(ursache), zugriff.linkFehlerHinweis());
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
async linkEntfernen(ziel, verzeichnis, name) {
|
package/dist/adapter/heime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Homedirs eines Harness aus dem Zustand (E78). Synchron, siehe HeimePort.
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { HeimUnbekannt, OrtNichtUnterstuetzt, heimAdresseZerlegen } from "../kern/modell.js";
|
|
3
|
+
import { HeimUnbekannt, OrtNichtUnterstuetzt, heimAdresseZerlegen, wslUncZerlegen } from "../kern/modell.js";
|
|
4
4
|
/**
|
|
5
5
|
* Pfade in der Welt eines Homedirs zusammensetzen (E78): POSIX in einer WSL-Distro, sonst die
|
|
6
6
|
* Schreibweise des Betriebssystems. Ohne das ergäbe `path.join` unter Windows `\`-Pfade für ein
|
|
@@ -12,6 +12,14 @@ export function heimVerbinden(ort, ...teile) {
|
|
|
12
12
|
? path.posix.join(...teile)
|
|
13
13
|
: path.join(...teile);
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Pfad unterhalb eines Arbeitsverzeichnisses (E96). Liegt es in WSL (`\\wsl$\<distro>\…`), kommt der
|
|
17
|
+
* POSIX-Pfad in der Distro heraus, den die Bridge bedient; sonst `path.join` am gespeicherten Pfad.
|
|
18
|
+
*/
|
|
19
|
+
export function arbeitsverzeichnisVerbinden(pfad, ...teile) {
|
|
20
|
+
const wsl = wslUncZerlegen(pfad);
|
|
21
|
+
return wsl !== undefined ? path.posix.join(wsl.linuxPfad, ...teile) : path.join(pfad, ...teile);
|
|
22
|
+
}
|
|
15
23
|
/**
|
|
16
24
|
* Ob eine Adresse auf dieser Plattform bedienbar ist (E78, Entscheidung 7). `wsl:`-Heime gibt es
|
|
17
25
|
* nur unter Windows; steht eines im Zustand, weil er von einem Windows-Rechner stammt, wird sein
|
package/dist/adapter/hermes.js
CHANGED
|
@@ -6,7 +6,8 @@ import path from 'node:path';
|
|
|
6
6
|
import { Document, isMap, isScalar, isSeq, parseDocument } from 'yaml';
|
|
7
7
|
import { McpAbweichenderEintrag, OrtNichtUnterstuetzt, SkillOrdnerUngueltig, } from "../kern/modell.js";
|
|
8
8
|
import { gitignoreEintragen, istGitRepo } from "./gitignore.js";
|
|
9
|
-
import {
|
|
9
|
+
import { LokalerZugriff } from "./dateisystem.js";
|
|
10
|
+
import { arbeitsverzeichnisVerbinden, nurHome } from "./heime.js";
|
|
10
11
|
import { tiefGleich } from "./vergleich.js";
|
|
11
12
|
const HERMES_VERZEICHNIS = '.hermes';
|
|
12
13
|
const PROFILE_VERZEICHNIS = 'profiles';
|
|
@@ -70,6 +71,8 @@ function mcpKonfigAlsObjekt(konfig) {
|
|
|
70
71
|
export class Hermes {
|
|
71
72
|
name = 'hermes';
|
|
72
73
|
heime;
|
|
74
|
+
/** Zugriff je Ort (E96): lokal, für ein Arbeitsverzeichnis in WSL die Bridge der Distro. */
|
|
75
|
+
zugriffFuer;
|
|
73
76
|
/**
|
|
74
77
|
* MCP-Server (E61): nur Home und Profil, denn Hermes kennt keine Projektebene für MCPs. Bindung
|
|
75
78
|
* über Arrow-Funktionen auf `this`, damit die Fähigkeit ohne eigene Klasse auskommt.
|
|
@@ -86,8 +89,10 @@ export class Hermes {
|
|
|
86
89
|
* Hermes' Zielverzeichnis steht in einer config.yaml; die über die Bridge zu lesen und zu
|
|
87
90
|
* schreiben wäre ein eigener Schnitt. Ein blanker Pfad steht für „genau dieses Home".
|
|
88
91
|
*/
|
|
89
|
-
constructor(heime) {
|
|
92
|
+
constructor(heime, zugriffFuer) {
|
|
90
93
|
this.heime = typeof heime === 'string' ? nurHome(heime) : heime;
|
|
94
|
+
const lokal = new LokalerZugriff();
|
|
95
|
+
this.zugriffFuer = zugriffFuer ?? (() => lokal);
|
|
91
96
|
}
|
|
92
97
|
get home() {
|
|
93
98
|
return this.heime.primaer(this.name);
|
|
@@ -155,7 +160,7 @@ export class Hermes {
|
|
|
155
160
|
}
|
|
156
161
|
zielVerzeichnis(ort) {
|
|
157
162
|
if (ort.art === 'arbeitsverzeichnis')
|
|
158
|
-
return
|
|
163
|
+
return arbeitsverzeichnisVerbinden(ort.pfad, HERMES_VERZEICHNIS, EIGENER_ORDNER);
|
|
159
164
|
const hermesHome = this.hermesHome(ort);
|
|
160
165
|
return this.ersterEintrag(hermesHome) ?? path.join(hermesHome, STANDARD_ORDNER);
|
|
161
166
|
}
|
|
@@ -246,13 +251,15 @@ export class Hermes {
|
|
|
246
251
|
}
|
|
247
252
|
async vorbereiten(ort) {
|
|
248
253
|
const verzeichnis = this.zielVerzeichnis(ort);
|
|
249
|
-
await mkdir(verzeichnis, { recursive: true });
|
|
250
254
|
if (ort.art === 'arbeitsverzeichnis') {
|
|
255
|
+
// Über den Zugriff: in einem WSL-Arbeitsverzeichnis legt die Bridge der Distro es an (E96).
|
|
256
|
+
await this.zugriffFuer(ort).verzeichnisAnlegen(verzeichnis);
|
|
251
257
|
// .hermes/ darf nicht ins Repo (B7).
|
|
252
258
|
if (await istGitRepo(ort.pfad))
|
|
253
259
|
await gitignoreEintragen(ort.pfad, GITIGNORE_ZEILE);
|
|
254
260
|
return;
|
|
255
261
|
}
|
|
262
|
+
await mkdir(verzeichnis, { recursive: true });
|
|
256
263
|
await this.registrieren(this.hermesHome(ort), verzeichnis);
|
|
257
264
|
}
|
|
258
265
|
/** Trägt das Verzeichnis in skills.external_dirs ein, falls es fehlt. Kommentare bleiben erhalten. */
|
package/dist/adapter/wsl.js
CHANGED
|
@@ -151,6 +151,9 @@ export class WslZugriff {
|
|
|
151
151
|
constructor(bruecke) {
|
|
152
152
|
this.bruecke = bruecke;
|
|
153
153
|
}
|
|
154
|
+
linkFehlerHinweis() {
|
|
155
|
+
return `Prüfe, ob die Distro ${this.bruecke.distro} läuft und das Linkziel unter /mnt/c erreichbar ist.`;
|
|
156
|
+
}
|
|
154
157
|
verbinden(verzeichnis, name) {
|
|
155
158
|
return path.posix.join(verzeichnis, name);
|
|
156
159
|
}
|
package/dist/cli/hilfe.js
CHANGED
|
@@ -104,6 +104,11 @@ Plugins des Vorhabens stehen. Ohne Zeiger bleibt das Verzeichnis gemerkt und jed
|
|
|
104
104
|
(E26). dir forget löst Bindung und Eintrag, die gelegten Links bleiben liegen. Gebunden wird nur für
|
|
105
105
|
Harnesses, die hmgr verwaltet: standardmäßig nur claude-code (E38).
|
|
106
106
|
|
|
107
|
+
Ein Arbeitsverzeichnis unter Windows, das in einer WSL-Distro liegt (\\\\wsl$\\<distro>\\… oder
|
|
108
|
+
\\\\wsl.localhost\\<distro>\\…, E96), bedient hmgr über die Bridge dieser Distro, wie ein Homedir in
|
|
109
|
+
WSL: der Link liegt im Linux-Dateisystem und zeigt auf den Windows-Checkout (/mnt/c). MCP-Server und
|
|
110
|
+
Plugins gibt es dort nicht, die claude-CLI dieses Rechners schreibt nur in die Windows-Konfiguration.
|
|
111
|
+
|
|
107
112
|
Schalter:
|
|
108
113
|
--composer <pfad> nur bei add: composer.json wählen, wenn unter dem Verzeichnis mehrere in
|
|
109
114
|
Frage kommen
|
|
@@ -623,6 +628,8 @@ Links dort legt hmgr über wsl.exe an; das Linkziel bleibt der vorhandene Window
|
|
|
623
628
|
nach /mnt/c. MCP-Server und Plugins gibt es dort nicht: die claude-CLI dieses Rechners bedient nur das
|
|
624
629
|
primäre Homedir. remove lässt hmgr das Homedir vergessen, die Links dort bleiben liegen.
|
|
625
630
|
|
|
631
|
+
Ein Arbeitsverzeichnis unter \\\\wsl$\\<distro>\\… bedient hmgr ebenso über die Distro (hmgr help dir, E96).
|
|
632
|
+
|
|
626
633
|
Zusätzliche Homedirs kennt bisher nur claude-code, und nur unter Windows. Steht eines im Zustand,
|
|
627
634
|
etwa weil er von einem Windows-Rechner kommt, zeigt hmgr sein Ziel auf anderen Plattformen nicht.
|
|
628
635
|
|
package/dist/cli/main.js
CHANGED
|
@@ -5,7 +5,7 @@ import { realpathSync } from 'node:fs';
|
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
import { Anwendungsfaelle, } from "../kern/anwendungsfaelle.js";
|
|
8
|
-
import { AbhaengigkeitFehlt, BundleDateiFehler, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimOhneCliZugriff, HeimUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, WslBrueckeFehlgeschlagen, ZeigerSchreibenGesperrt, heimAdresseAlsText, heimAdresseZerlegen, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
8
|
+
import { AbhaengigkeitFehlt, BundleDateiFehler, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, ArbeitsverzeichnisOhneCliZugriff, HeimOhneCliZugriff, HeimUnbekannt, KeinPassenderRef, LinkAnlegenFehlgeschlagen, McpAbweichenderEintrag, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpKatalogFehler, McpNichtInGeteilterDatei, McpNurGeteilterFundort, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUebernahmeUnvollstaendig, McpUnbekannt, NameBelegt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillOrdnerUngueltig, SkillUnbekannt, ZeigerMehrdeutig, WslBrueckeFehlgeschlagen, ZeigerSchreibenGesperrt, heimAdresseAlsText, heimAdresseZerlegen, wslDistroVonOrt, pluginSchluessel, quelleName, quelleSchluessel, } from "../kern/modell.js";
|
|
9
9
|
import { Agents } from "../adapter/agents.js";
|
|
10
10
|
import { ClaudeCode, claudeAusfuehrer } from "../adapter/claude-code.js";
|
|
11
11
|
import { Hermes } from "../adapter/hermes.js";
|
|
@@ -53,13 +53,14 @@ export function bauen(umgebung, optionen = {}) {
|
|
|
53
53
|
};
|
|
54
54
|
const lokal = new LokalerZugriff();
|
|
55
55
|
const zugriffFuerOrt = (ort) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
// Ein WSL-Heim (E78) und ein Arbeitsverzeichnis unter \\wsl$\<distro> (E96) gehen über die
|
|
57
|
+
// Bridge ihrer Distro; alles andere über das lokale Dateisystem.
|
|
58
|
+
const distro = wslDistroVonOrt(ort);
|
|
59
|
+
return distro === undefined ? lokal : new WslZugriff(bruecke(distro));
|
|
59
60
|
};
|
|
60
61
|
const claude = new ClaudeCode(heime, optionen.claudeAusfuehrer ?? claudeAusfuehrer, zugriffFuerOrt);
|
|
61
62
|
const ports = {
|
|
62
|
-
harnesses: [claude, new Hermes(heime), new Agents(heime)],
|
|
63
|
+
harnesses: [claude, new Hermes(heime, zugriffFuerOrt), new Agents(heime, zugriffFuerOrt)],
|
|
63
64
|
quellen: new GitQuellen(wurzel),
|
|
64
65
|
ziele: new DateisystemZiele(wurzel, zustand, umgebung.xdgStateHome, (ziel) => zugriffFuerOrt(ziel.ort)),
|
|
65
66
|
zeiger: new ComposerZeiger(),
|
|
@@ -1152,6 +1153,7 @@ function istModellFehler(fehler) {
|
|
|
1152
1153
|
fehler instanceof HeimFaehigkeitFehlt ||
|
|
1153
1154
|
fehler instanceof HeimNurUnterWindows ||
|
|
1154
1155
|
fehler instanceof HeimOhneCliZugriff ||
|
|
1156
|
+
fehler instanceof ArbeitsverzeichnisOhneCliZugriff ||
|
|
1155
1157
|
fehler instanceof HeimUnbekannt ||
|
|
1156
1158
|
fehler instanceof WslBrueckeFehlgeschlagen);
|
|
1157
1159
|
}
|
|
@@ -1204,6 +1206,8 @@ function naechsterSchritt(fehler) {
|
|
|
1204
1206
|
return `hmgr harness home ${fehler.harness} zeigt die eingetragenen Homedirs.`;
|
|
1205
1207
|
if (fehler instanceof HeimUnbekannt)
|
|
1206
1208
|
return `hmgr harness home ${fehler.harness} zeigt die eingetragenen Homedirs, add trägt eines ein.`;
|
|
1209
|
+
if (fehler instanceof ArbeitsverzeichnisOhneCliZugriff)
|
|
1210
|
+
return 'Skill-Links dort legt hmgr selbst: hmgr link <skill> --ort dir:<pfad>.';
|
|
1207
1211
|
if (fehler instanceof HeimOhneCliZugriff)
|
|
1208
1212
|
return 'Skill-Links dort legt hmgr selbst: hmgr link <skill> --harness claude-code --ort <adresse>.';
|
|
1209
1213
|
if (fehler instanceof WslBrueckeFehlgeschlagen)
|
package/dist/gui/hmgr-cli.js
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
1
|
// Die installierte hmgr-CLI aus Sicht der GUI (E77). Die GUI trägt ihre eigene Version in sich
|
|
2
2
|
// (`paketVersion()`, E72) — die CLI daneben ist ein zweites, unabhängig installiertes Paket, ihre
|
|
3
3
|
// Version steht nur über ihren eigenen Aufruf fest.
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
4
6
|
import spawn from 'cross-spawn';
|
|
5
7
|
import { versionLesen } from "../kern/version.js";
|
|
8
|
+
/**
|
|
9
|
+
* Zeigt der aufgelöste Treffer auf die laufende GUI selbst (E97)? Deren Electron-exe heißt ebenfalls
|
|
10
|
+
* `hmgr.exe`; eine zweite Instanz endet an der Einzelinstanz-Sperre ohne Ausgabe und würde als
|
|
11
|
+
* „CLI vor 0.0.7" gelten. Unter win32 ohne Rücksicht auf Groß-/Kleinschreibung.
|
|
12
|
+
*/
|
|
13
|
+
export function istEigeneGui(aufgeloest, execPath, plattform) {
|
|
14
|
+
if (!aufgeloest)
|
|
15
|
+
return false;
|
|
16
|
+
const a = path.resolve(aufgeloest);
|
|
17
|
+
const b = path.resolve(execPath);
|
|
18
|
+
return plattform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Spawn-Optionen der CLI-Abfrage. Festes `cwd` (E97): `which` sucht unter Windows zuerst im
|
|
22
|
+
* Arbeitsverzeichnis, und cross-spawn wechselt für die Suche in genau dieses `cwd` — läge es im
|
|
23
|
+
* Installationsordner der GUI, würde die eigene `hmgr.exe` statt der CLI gefunden.
|
|
24
|
+
*/
|
|
25
|
+
export function cliSpawnOptionen(heim = homedir()) {
|
|
26
|
+
return {
|
|
27
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
28
|
+
timeout: 10_000,
|
|
29
|
+
killSignal: 'SIGKILL',
|
|
30
|
+
cwd: heim,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Der Pfad, den cross-spawn für `hmgr` starten würde. `_parse` setzt `file` nur unter win32 (in
|
|
35
|
+
* parse.js `parseNonShell`, nur dort wird aufgelöst); sonst bleibt es `undefined`. `file` ist der
|
|
36
|
+
* aufgelöste Pfad vor einer Umsetzung auf cmd.exe oder Shebang, also der Treffer von `which`.
|
|
37
|
+
*/
|
|
38
|
+
function aufgeloesterPfad(optionen) {
|
|
39
|
+
const geparst = spawn._parse('hmgr', ['--version'], optionen);
|
|
40
|
+
return geparst.file;
|
|
41
|
+
}
|
|
6
42
|
/**
|
|
7
43
|
* `hmgr --version` (E72: nur die Nummer, Exit 0). Kein Fehler dringt nach außen — der Aufrufer
|
|
8
44
|
* entscheidet anhand des Befunds, was zu melden ist.
|
|
@@ -22,11 +58,11 @@ export function hmgrCliVersion() {
|
|
|
22
58
|
aufloesen(wert);
|
|
23
59
|
};
|
|
24
60
|
try {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
61
|
+
const optionen = cliSpawnOptionen();
|
|
62
|
+
if (istEigeneGui(aufgeloesterPfad(optionen), process.execPath, process.platform)) {
|
|
63
|
+
return melden({ art: 'fehlt' });
|
|
64
|
+
}
|
|
65
|
+
const kind = spawn('hmgr', ['--version'], optionen);
|
|
30
66
|
let stdout = '';
|
|
31
67
|
kind.stdout.on('data', (stueck) => (stdout += stueck));
|
|
32
68
|
kind.on('error', () => melden({ art: 'fehlt' }));
|
package/dist/gui/main.js
CHANGED
|
@@ -886,6 +886,35 @@ function fensterOeffnen() {
|
|
|
886
886
|
} catch (fehler) {
|
|
887
887
|
bundleZerbrichtText = String(fehler);
|
|
888
888
|
}
|
|
889
|
+
// Sammelstreifen unter „Alle" (E95): das Bundle „vorhaben" liegt an kunde-x in Claude Code
|
|
890
|
+
// und Hermes — ein Streifen mit beiden Harness-Zeichen, Entfernen nennt beide Harnesses
|
|
891
|
+
// (nur der Dialogtext, kein Klick auf „Ja"). Unter dem Harness-Chip steht weiter die
|
|
892
|
+
// volle Gruppe mit Zeilen; danach zurück auf „Alle".
|
|
893
|
+
var streifenKarte = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
894
|
+
.find((k) => (k.querySelector('.zielkopf')?.textContent || '').indexOf('kunde-x') > -1);
|
|
895
|
+
var streifenZahl = null, streifenHarnesse = null, streifenText = null;
|
|
896
|
+
var streifenDialogTitel = null, streifenDialogText = null;
|
|
897
|
+
var chipGruppeZahl = null, chipGruppeZeilenZahl = null, chipStreifenZahl = null;
|
|
898
|
+
try {
|
|
899
|
+
var streifen = Array.from(streifenKarte?.querySelectorAll('.bundlegruppe') ?? []);
|
|
900
|
+
streifenZahl = streifen.length;
|
|
901
|
+
streifenHarnesse = streifen[0]?.querySelector('.zeilenzeichen')?.getAttribute('aria-label') ?? null;
|
|
902
|
+
streifenText = streifen[0]?.textContent?.replace(/\\s+/g, ' ').trim() ?? null;
|
|
903
|
+
streifen[0]?.querySelector('button')?.click();
|
|
904
|
+
streifenDialogTitel = document.getElementById('dx-titel')?.textContent ?? null;
|
|
905
|
+
streifenDialogText = document.getElementById('dx-text')?.textContent ?? null;
|
|
906
|
+
document.querySelector('#d-entfernen button[value="nein"]')?.click();
|
|
907
|
+
var chipClaude = streifenKarte?.querySelector('.kartenchips [data-chipwahl="claude-code"]');
|
|
908
|
+
chipClaude?.click();
|
|
909
|
+
var kundeXNachChip = Array.from(document.querySelectorAll('#zielliste .ziel.verzeichnis'))
|
|
910
|
+
.find((k) => (k.querySelector('.zielkopf')?.textContent || '').indexOf('kunde-x') > -1);
|
|
911
|
+
chipGruppeZahl = kundeXNachChip?.querySelectorAll('.bundlegruppe').length ?? null;
|
|
912
|
+
chipGruppeZeilenZahl = kundeXNachChip?.querySelectorAll('.bundlegruppe .linkliste li').length ?? null;
|
|
913
|
+
chipStreifenZahl = kundeXNachChip?.querySelectorAll('.bundlegruppe .zeilenzeichen').length ?? null;
|
|
914
|
+
kundeXNachChip?.querySelector('.kartenchips [data-chipwahl="alle"]')?.click();
|
|
915
|
+
} catch (fehler) {
|
|
916
|
+
streifenDialogText = String(fehler);
|
|
917
|
+
}
|
|
889
918
|
// MCP-Ansicht (E61-Folgerunde, F46: abgespeckt zum reinen Katalog): Navigationsreihenfolge
|
|
890
919
|
// Ziele, MCP, Plugins, Skills, Karten mit Name/Beschreibung/Homepage-Link, F39-Warnzeile,
|
|
891
920
|
// Einhängen-Dialog öffnen/abbrechen ohne IPC-Schreibaktion. Orte-Liste und Fremdkasten
|
|
@@ -1172,6 +1201,14 @@ function fensterOeffnen() {
|
|
|
1172
1201
|
bundleGruppeText,
|
|
1173
1202
|
bundleGruppeZeilen,
|
|
1174
1203
|
bundleZerbrichtText,
|
|
1204
|
+
streifenZahl,
|
|
1205
|
+
streifenHarnesse,
|
|
1206
|
+
streifenText,
|
|
1207
|
+
streifenDialogTitel,
|
|
1208
|
+
streifenDialogText,
|
|
1209
|
+
chipGruppeZahl,
|
|
1210
|
+
chipGruppeZeilenZahl,
|
|
1211
|
+
chipStreifenZahl,
|
|
1175
1212
|
interGeladen: document.fonts.check('400 16px Inter'),
|
|
1176
1213
|
monoGeladen: document.fonts.check('500 16px "JetBrains Mono"'),
|
|
1177
1214
|
quelleneuVorhanden,
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
<h1>MCP-Server</h1>
|
|
143
143
|
<!-- Der Pfad steht hier in POSIX-Schreibweise und wird beim Start in die Schreibweise
|
|
144
144
|
der Plattform gesetzt (E68, ladeUmgebung im Renderer). -->
|
|
145
|
-
<p>Dein Katalog aus <span class="mono" id="mcpkatalogpfad">~/.hmgr/mcp.yaml</span>. Häng einen Server dorthin ein, wo du ihn brauchst: in ein Arbeitsverzeichnis, damit er nur dort Kontext kostet, oder global. Den Katalog selbst pflegst du in der Datei, hmgr liest sie nur.</p>
|
|
145
|
+
<p>Dein Katalog aus <span class="mono" id="mcpkatalogpfad">~/.hmgr/mcp.yaml</span>. Häng einen Server dorthin ein, wo du ihn brauchst: in ein Arbeitsverzeichnis, damit er nur dort Kontext kostet, oder global. Den Katalog selbst pflegst du in der Datei, hmgr liest sie nur. Arbeitsverzeichnisse in WSL (\\wsl$\…) gehen dort nicht: die claude-CLI dieses Rechners kennt ihre Konfiguration nicht.</p>
|
|
146
146
|
</div>
|
|
147
147
|
|
|
148
148
|
<div class="mcpliste" id="mcpliste"></div>
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
<div class="dkopf">
|
|
346
346
|
<span class="etikett">Homedir in WSL</span>
|
|
347
347
|
<h3>Welche Distro?</h3>
|
|
348
|
-
<p>Claude Code in einer WSL-Distro hat ein eigenes Homedir. hmgr ermittelt es einmal, zeigt es an und legt Links dort über wsl.exe an; die Checkouts bleiben, wo sie sind. Das Ziel steht danach als eigene Karte neben dem Windows-Homedir, gespiegelt wird nichts.</p>
|
|
348
|
+
<p>Claude Code in einer WSL-Distro hat ein eigenes Homedir. hmgr ermittelt es einmal, zeigt es an und legt Links dort über wsl.exe an; die Checkouts bleiben, wo sie sind. Das Ziel steht danach als eigene Karte neben dem Windows-Homedir, gespiegelt wird nichts. Ein Arbeitsverzeichnis unter \\wsl$\… bedient hmgr ebenfalls über die Distro, ohne dass du hier etwas eintragen musst.</p>
|
|
349
349
|
</div>
|
|
350
350
|
<div class="dkoerper">
|
|
351
351
|
<div class="feld">
|
|
@@ -826,21 +826,35 @@
|
|
|
826
826
|
return gruppe;
|
|
827
827
|
}
|
|
828
828
|
|
|
829
|
-
/** Ein Bundle als einzelner Streifen (Chip „Alle" einer Verzeichnis-Karte, E69/E53):
|
|
830
|
-
Zeichen
|
|
831
|
-
|
|
829
|
+
/** Ein Bundle als einzelner Streifen (Chip „Alle" einer Verzeichnis-Karte, E69/E53/E95): die
|
|
830
|
+
Harness-Zeichen aller Harnesses, in denen es liegt (wie in den Skill-Zeilen), Name, Mitglieder,
|
|
831
|
+
Entfernen für alle diese Harnesses. `treffer`: [{b, z}], je Harness einer, gleicher Name und
|
|
832
|
+
gleiche Quelle. Mitglieder: die des ersten Treffers, abweichende anderer Harnesses folgen. */
|
|
833
|
+
function bundleStreifen(treffer) {
|
|
834
|
+
var b = treffer[0].b;
|
|
832
835
|
var streifen = e('div', 'bundlegruppe');
|
|
833
836
|
var kopf = e('div', 'bundlekopf');
|
|
834
837
|
var titel = e('span', 'bundletitel');
|
|
835
|
-
|
|
838
|
+
var harnesse = [];
|
|
839
|
+
treffer.forEach(function (t) { if (harnesse.indexOf(t.z.ziel.harness) === -1) harnesse.push(t.z.ziel.harness); });
|
|
840
|
+
harnesse.sort(function (x, y) { return harnessName(x).localeCompare(harnessName(y), 'de'); });
|
|
841
|
+
titel.appendChild(harnesseZeichenSammel(harnesse));
|
|
836
842
|
titel.appendChild(e('span', 'etikett', 'Bundle'));
|
|
837
843
|
titel.appendChild(e('b', 'bundlename', b.name));
|
|
838
844
|
kopf.appendChild(titel);
|
|
839
|
-
|
|
845
|
+
var mitglieder = [];
|
|
846
|
+
treffer.forEach(function (t) {
|
|
847
|
+
t.b.skills.forEach(function (n) { if (mitglieder.indexOf(n) === -1) mitglieder.push(n); });
|
|
848
|
+
});
|
|
849
|
+
kopf.appendChild(e('span', 'bundlemitglieder', mitglieder.join(', ')));
|
|
840
850
|
var weg = e('button', 'knopf leise klein', 'Entfernen');
|
|
841
851
|
weg.type = 'button';
|
|
842
|
-
weg.setAttribute('aria-label', 'Bundle ' + b.name + ' aus '
|
|
843
|
-
|
|
852
|
+
weg.setAttribute('aria-label', 'Bundle ' + b.name + ' aus '
|
|
853
|
+
+ treffer.map(function (t) { return zielTitel(t.z); }).join(', ') + ' entfernen');
|
|
854
|
+
weg.addEventListener('click', function () {
|
|
855
|
+
if (treffer.length === 1) frageBundleEntfernen(b, treffer[0].z.ziel);
|
|
856
|
+
else frageBundleEntfernenAlle(b.name, mitglieder, treffer);
|
|
857
|
+
});
|
|
844
858
|
kopf.appendChild(weg);
|
|
845
859
|
streifen.appendChild(kopf);
|
|
846
860
|
return streifen;
|
|
@@ -1209,13 +1223,22 @@
|
|
|
1209
1223
|
// Bundle gehört aber zu genau einem Harness — deshalb hier ein Streifen je Bundle mit seinem
|
|
1210
1224
|
// Harness-Zeichen statt einer Umgruppierung der Tabelle. Unter dem Harness-Chip zeigt linkTeil
|
|
1211
1225
|
// die volle Gruppe über den Zeilen.
|
|
1226
|
+
// Ein Bundle, das in mehreren Harnesses am selben Ort liegt, ist hier ein Streifen (E95), wie
|
|
1227
|
+
// die Skill-Zeilen: Schlüssel ist Name plus Quelle, gleichnamige Bundles aus zwei Quellen
|
|
1228
|
+
// bleiben getrennt.
|
|
1229
|
+
var bundleGruppen = {}, bundleReihenfolge = [];
|
|
1212
1230
|
teile.forEach(function (z) {
|
|
1213
1231
|
(z.bundles || [])
|
|
1214
1232
|
.filter(function (b) { return b.skills.length > 0; })
|
|
1215
|
-
.
|
|
1216
|
-
|
|
1217
|
-
|
|
1233
|
+
.forEach(function (b) {
|
|
1234
|
+
var schluessel = b.name + '\u0000' + b.quelleText;
|
|
1235
|
+
if (!bundleGruppen[schluessel]) { bundleGruppen[schluessel] = []; bundleReihenfolge.push(schluessel); }
|
|
1236
|
+
bundleGruppen[schluessel].push({ b: b, z: z });
|
|
1237
|
+
});
|
|
1218
1238
|
});
|
|
1239
|
+
bundleReihenfolge
|
|
1240
|
+
.sort(function (a, b) { return bundleGruppen[a][0].b.name.localeCompare(bundleGruppen[b][0].b.name, 'de'); })
|
|
1241
|
+
.forEach(function (k) { teil.appendChild(bundleStreifen(bundleGruppen[k])); });
|
|
1219
1242
|
|
|
1220
1243
|
var kopf = e('div', 'linkkopf');
|
|
1221
1244
|
kopf.appendChild(e('span'));
|
|
@@ -1612,12 +1635,24 @@
|
|
|
1612
1635
|
|
|
1613
1636
|
/** Entfernen des ganzen Bundles an einem Ziel (E69): alle seine Links auf einmal. */
|
|
1614
1637
|
function frageBundleEntfernen(b, ziel) {
|
|
1615
|
-
entfernenKontext = { art: 'bundle', bundle: b.name,
|
|
1638
|
+
entfernenKontext = { art: 'bundle', bundle: b.name, ziele: [ziel] };
|
|
1616
1639
|
$('dx-titel').textContent = 'Bundle ' + b.name + ' entfernen?';
|
|
1617
1640
|
$('dx-text').textContent = zahlwort(b.skills.length, 'Link', 'Links') + ' aus ' + zielTitel({ ziel: ziel })
|
|
1618
1641
|
+ ' verschwinden: ' + b.skills.join(', ') + '. Die Checkouts bleiben liegen, bis kein Link mehr darauf zeigt.';
|
|
1619
1642
|
zeige($('d-entfernen'));
|
|
1620
1643
|
}
|
|
1644
|
+
/** Entfernen eines Bundles in allen Harnesses seines Streifens (E95): `treffer` wie bei
|
|
1645
|
+
bundleStreifen, der Dialog nennt die Harnesses mit ihrer Linkzahl und die Mitglieder. */
|
|
1646
|
+
function frageBundleEntfernenAlle(name, mitglieder, treffer) {
|
|
1647
|
+
entfernenKontext = { art: 'bundle', bundle: name, ziele: treffer.map(function (t) { return t.z.ziel; }) };
|
|
1648
|
+
var proHarness = treffer.map(function (t) {
|
|
1649
|
+
return harnessName(t.z.ziel.harness) + ' ' + zahlwort(t.b.skills.length, 'Link', 'Links');
|
|
1650
|
+
});
|
|
1651
|
+
$('dx-titel').textContent = 'Bundle ' + name + ' entfernen?';
|
|
1652
|
+
$('dx-text').textContent = 'Das Bundle verschwindet in ' + treffer.length + ' Harnesses (' + proHarness.join(', ')
|
|
1653
|
+
+ '): ' + mitglieder.join(', ') + '. Die Checkouts bleiben liegen, bis kein Link mehr darauf zeigt.';
|
|
1654
|
+
zeige($('d-entfernen'));
|
|
1655
|
+
}
|
|
1621
1656
|
/** Entfernen einer „Alle"-Zeile (E53 Pkt. 2): entfernbareZiele sind die Ziele (voller z.ziel), in
|
|
1622
1657
|
denen der Link weg soll, nichtEntfernbareHarnesse die, in denen er nur über ein Plugin hängt
|
|
1623
1658
|
und liegen bleibt. Der Dialog nennt beides. */
|
|
@@ -1640,17 +1675,29 @@
|
|
|
1640
1675
|
nachDialog($('d-entfernen'), async function (wert) {
|
|
1641
1676
|
if (wert !== 'ja' || entfernenKontext === null) return;
|
|
1642
1677
|
var kontext = entfernenKontext;
|
|
1643
|
-
// Bundle als Ganzes (E69): ein Aufruf, der Kern nimmt alle Links des Bundles weg.
|
|
1678
|
+
// Bundle als Ganzes (E69): je Ziel ein Aufruf, der Kern nimmt alle Links des Bundles weg. Bei
|
|
1679
|
+
// mehreren Zielen (E95) hält ein Fehlschlag die übrigen nicht auf, rufe meldet ihn selbst.
|
|
1644
1680
|
if (kontext.art === 'bundle') {
|
|
1645
|
-
var
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1681
|
+
var entferntB = 0;
|
|
1682
|
+
var gescheitertB = [];
|
|
1683
|
+
for (var j = 0; j < kontext.ziele.length; j++) {
|
|
1684
|
+
var zielB = kontext.ziele[j];
|
|
1685
|
+
try {
|
|
1686
|
+
var wegB = await rufe(function () {
|
|
1687
|
+
return window.hmgr.bundleEntfernen({ bundle: kontext.bundle, harness: zielB.harness, ort: zielB.ort });
|
|
1688
|
+
});
|
|
1689
|
+
entferntB += wegB.entfernt.length;
|
|
1690
|
+
} catch (fehler) {
|
|
1691
|
+
gescheitertB.push(harnessName(zielB.harness));
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1652
1694
|
await ladeAlles();
|
|
1653
|
-
|
|
1695
|
+
if (gescheitertB.length === 0) {
|
|
1696
|
+
melde('Bundle ' + kontext.bundle + ' entfernt, ' + zahlwort(entferntB, 'Link', 'Links') + ' weniger.');
|
|
1697
|
+
} else if (kontext.ziele.length > 1) {
|
|
1698
|
+
melde('Bundle ' + kontext.bundle + ' blieb in ' + gescheitertB.join(', ') + ' liegen, '
|
|
1699
|
+
+ zahlwort(entferntB, 'Link', 'Links') + ' weniger.');
|
|
1700
|
+
}
|
|
1654
1701
|
return;
|
|
1655
1702
|
}
|
|
1656
1703
|
// Ein Fehlschlag in einem Harness hält die übrigen nicht auf: rufe hat ihn schon gemeldet, die
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Anwendungsfälle. Pfade werden hier nur als Zeichenketten normalisiert, gelesen wird über die Ports.
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { AbhaengigkeitFehlt, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, FremderEintragUnbekannt, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimUnbekannt, KeinPassenderRef, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUnbekannt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, bundleSchluessel, heimAdresseAlsText, heimAdresseZerlegen, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
3
|
+
import { AbhaengigkeitFehlt, BundleMehrdeutig, BundleMitgliederFehlen, BundleNamenskonflikte, BundleNichtInstalliert, BundleUnbekannt, EinschraenkungOhneZeiger, FremderEintragUnbekannt, HarnessInaktiv, HarnessNichtVorhanden, HarnessTypUnbekannt, HeimAdresseUngueltig, HeimBereitsVorhanden, HeimFaehigkeitFehlt, HeimNurUnterWindows, HeimUnbekannt, KeinPassenderRef, McpEbeneNichtUnterstuetzt, McpFaehigkeitFehlt, McpOrtUnterGitWurzel, McpUebernahmeNichtMoeglich, McpUnbekannt, OrtNichtUnterstuetzt, PluginMehrdeutig, PluginUnbekannt, QuelleAufraeumenLokaleAenderungen, QuelleNameBelegt, QuelleUnbekannt, SkillMehrdeutig, SkillOrdnerBereitsVerwaltet, SkillOrdnerFaehigkeitFehlt, SkillOrdnerNichtVerwaltet, SkillUnbekannt, ZeigerMehrdeutig, ZeigerSchreibenGesperrt, bundleSchluessel, heimAdresseAlsText, heimAdresseZerlegen, wslDistroVonOrt, wslUncZerlegen, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
4
4
|
import { mitBegrenzung } from "./begrenzung.js";
|
|
5
5
|
import { passenderRef } from "./version.js";
|
|
6
6
|
import { kannIstRegistriert, kannMcp, kannPlugins, kannProfile, kannSkillOrdner, kannZurueckbauen, kannZusaetzlicheHeime, } from "../ports/harness.js";
|
|
@@ -471,9 +471,11 @@ export class Anwendungsfaelle {
|
|
|
471
471
|
if (ebenen.includes('profil') && kannProfile(harness))
|
|
472
472
|
for (const name of await harness.profile())
|
|
473
473
|
ziele.push({ harness: harness.name, ort: { art: 'profil', name } });
|
|
474
|
+
// Arbeitsverzeichnisse in WSL (E96) ebenso nicht: ihre Konfiguration liegt in der Distro.
|
|
474
475
|
if (ebenen.includes('arbeitsverzeichnis'))
|
|
475
476
|
for (const pfad of zustand.arbeitsverzeichnisse)
|
|
476
|
-
|
|
477
|
+
if (wslUncZerlegen(pfad) === undefined)
|
|
478
|
+
ziele.push(arbeitsverzeichnisZiel(harness.name, pfad));
|
|
477
479
|
}
|
|
478
480
|
return ziele;
|
|
479
481
|
}
|
|
@@ -2363,16 +2365,16 @@ export class Anwendungsfaelle {
|
|
|
2363
2365
|
}
|
|
2364
2366
|
// Installierte Plugins je Ziel vorab, mit begrenzter Parallelität (E93): `claude plugin list`
|
|
2365
2367
|
// ist ein Prozess je Ziel (cwd-gebunden, kein Sammelaufruf), nacheinander dauert das bei vielen
|
|
2366
|
-
// Arbeitsverzeichnissen Sekunden. Plugins nur am primären Homedir (E78)
|
|
2367
|
-
//
|
|
2368
|
-
// Windows-Home.
|
|
2368
|
+
// Arbeitsverzeichnissen Sekunden. Plugins nur am primären Homedir (E78) und in Arbeitsverzeichnissen
|
|
2369
|
+
// außerhalb von WSL (E96): die claude-CLI dieses Rechners kennt die Plugin-Konfiguration einer
|
|
2370
|
+
// WSL-Distro nicht, ihre user-Liste gehörte zum Windows-Home.
|
|
2369
2371
|
const pluginListen = new Map();
|
|
2370
2372
|
if (mitPlugins) {
|
|
2371
2373
|
const abfragen = [];
|
|
2372
2374
|
for (const { harness, ziele } of plan)
|
|
2373
2375
|
if (kannPlugins(harness))
|
|
2374
2376
|
for (const ziel of ziele)
|
|
2375
|
-
if (
|
|
2377
|
+
if (wslDistroVonOrt(ziel.ort) === undefined)
|
|
2376
2378
|
abfragen.push({
|
|
2377
2379
|
schluessel: zielSchluessel(ziel),
|
|
2378
2380
|
abruf: () => harness.plugins.installierte(ziel).catch(() => []),
|
package/dist/kern/modell.js
CHANGED
|
@@ -68,6 +68,29 @@ export function heimAdresseZerlegen(text) {
|
|
|
68
68
|
return undefined;
|
|
69
69
|
return { art: 'wsl', distro };
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Zerlegt einen WSL-UNC-Pfad (E96): `\\wsl$\<distro>\…` oder `\\wsl.localhost\<distro>\…`, auch mit
|
|
73
|
+
* `\\?\UNC\`-Präfix und mit Schrägstrichen in beiden Richtungen. Der Hostteil gilt ohne Rücksicht
|
|
74
|
+
* auf Groß-/Kleinschreibung, der Distro-Name bleibt unverändert. `linuxPfad` ist der Pfad in der
|
|
75
|
+
* Distro, absolut und ohne abschließenden Trenner. Alles andere ergibt undefined. Rein und
|
|
76
|
+
* plattformunabhängig, damit auch Tests außerhalb von Windows ihn abdecken.
|
|
77
|
+
*/
|
|
78
|
+
export function wslUncZerlegen(pfad) {
|
|
79
|
+
const treffer = /^\\\\(?:\?\\UNC\\)?(?:wsl\$|wsl\.localhost)\\([^\\]+)(?:\\(.*))?$/i.exec(pfad.trim().replaceAll('/', '\\'));
|
|
80
|
+
if (treffer === null)
|
|
81
|
+
return undefined;
|
|
82
|
+
const distro = treffer[1];
|
|
83
|
+
const rest = (treffer[2] ?? '').split('\\').filter((teil) => teil.length > 0);
|
|
84
|
+
return { distro, linuxPfad: `/${rest.join('/')}` };
|
|
85
|
+
}
|
|
86
|
+
/** Die WSL-Distro, über deren Bridge ein Ort bedient wird: ein `wsl:`-Heim (E78) oder ein WSL-UNC-Arbeitsverzeichnis (E96). */
|
|
87
|
+
export function wslDistroVonOrt(ort) {
|
|
88
|
+
if (ort.art === 'home')
|
|
89
|
+
return ort.heim === undefined ? undefined : heimAdresseZerlegen(ort.heim)?.distro;
|
|
90
|
+
if (ort.art === 'arbeitsverzeichnis')
|
|
91
|
+
return wslUncZerlegen(ort.pfad)?.distro;
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
71
94
|
/** Die Adresse eines zusätzlichen Heims in der Schreibweise, die hmgr speichert und anzeigt. */
|
|
72
95
|
export function heimAdresseAlsText(adresse) {
|
|
73
96
|
return `wsl:${adresse.distro}`;
|
|
@@ -137,6 +160,22 @@ export class HeimOhneCliZugriff extends Error {
|
|
|
137
160
|
this.adresse = adresse;
|
|
138
161
|
}
|
|
139
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* MCP-Server oder Plugins für ein Arbeitsverzeichnis in WSL verlangt (E96): die claude-CLI dieses
|
|
165
|
+
* Rechners schreibt in die Windows-Konfiguration, die das Claude in der Distro nie liest. Skill-Links
|
|
166
|
+
* legt hmgr dort selbst, über die Bridge der Distro.
|
|
167
|
+
*/
|
|
168
|
+
export class ArbeitsverzeichnisOhneCliZugriff extends Error {
|
|
169
|
+
harness;
|
|
170
|
+
pfad;
|
|
171
|
+
constructor(harness, pfad, was) {
|
|
172
|
+
super(`${harness === 'claude-code' ? 'Claude Code' : harness} kann ${was} im Arbeitsverzeichnis „${pfad}" nicht verwalten: ` +
|
|
173
|
+
'es liegt in WSL, die claude-CLI dieses Rechners schreibt nur in die Windows-Konfiguration. ' +
|
|
174
|
+
'Skill-Links legt hmgr dort selbst.');
|
|
175
|
+
this.harness = harness;
|
|
176
|
+
this.pfad = pfad;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
140
179
|
/**
|
|
141
180
|
* Die Bridge in eine WSL-Distro hat nicht geantwortet (E78): die Distro gibt es nicht, sie startet
|
|
142
181
|
* nicht, oder wsl.exe fehlt. Die Ausgabe von wsl.exe steht mit in der Meldung — sie nennt den Grund
|