hmgr 0.0.21 → 0.0.23
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 +5 -1
- package/dist/cli/hilfe.js +6 -0
- package/dist/cli/main.js +2 -1
- package/dist/gui/main.js +62 -22
- package/dist/gui/renderer/renderer.js +25 -3
- package/dist/gui/selbst-update.js +31 -0
- package/dist/kern/anwendungsfaelle.js +29 -2
- package/dist/kern/modell.js +17 -0
- package/package.json +1 -1
|
@@ -241,6 +241,7 @@ class ClaudeMcp {
|
|
|
241
241
|
this.ausfuehrer = ausfuehrer;
|
|
242
242
|
this.home = home;
|
|
243
243
|
}
|
|
244
|
+
anGitWurzelGebunden = true;
|
|
244
245
|
ebenen() {
|
|
245
246
|
return ['home', 'arbeitsverzeichnis'];
|
|
246
247
|
}
|
|
@@ -359,7 +360,10 @@ class ClaudeMcp {
|
|
|
359
360
|
/**
|
|
360
361
|
* Die MCP-Server, die `~/.claude.json` für diesen Ort führt. Ein Arbeitsverzeichnis steht dort
|
|
361
362
|
* unter dem aufgelösten Pfad, weil Claude Code den cwd auflöst (belegt an 2.1.261); hmgr merkt
|
|
362
|
-
* sich Arbeitsverzeichnisse aber so, wie der Nutzer sie genannt hat, also zählen beide.
|
|
363
|
+
* sich Arbeitsverzeichnisse aber so, wie der Nutzer sie genannt hat, also zählen beide. Liegt der
|
|
364
|
+
* Ordner in einem Git-Repo, ist der Schlüssel die Git-Wurzel, nicht der Ordner (E91, belegt an
|
|
365
|
+
* 2.1.285): für einen Unterordner liest diese Methode deshalb nichts, und der Kern sperrt dort
|
|
366
|
+
* die MCP-Operationen, bevor sie den Adapter erreichen.
|
|
363
367
|
*/
|
|
364
368
|
async mcpServersAm(ort) {
|
|
365
369
|
const heim = zusaetzlichesHeim(ort);
|
package/dist/cli/hilfe.js
CHANGED
|
@@ -383,6 +383,12 @@ global (Scope user). Hermes kennt nur global, mcp_servers in seiner config.yaml
|
|
|
383
383
|
mit --ort profil:<name>); ein Projekt-Ziel ist bei Hermes ein Fehler, es kennt keine Projektebene für
|
|
384
384
|
MCPs. agents kennt MCP-Server gar nicht.
|
|
385
385
|
|
|
386
|
+
Bei claude-code führt der Scope local die Server je Git-Repo, nicht je Unterordner (E91). Ein
|
|
387
|
+
Arbeitsverzeichnis, das in einem Repo liegt, ohne dessen Wurzel zu sein, bekommt deshalb kein mcp add,
|
|
388
|
+
mcp remove und mcp migrate: die Meldung nennt den Wurzelordner, dort gehören die Server hin (ist er
|
|
389
|
+
noch kein Arbeitsverzeichnis: hmgr dir add <wurzel>). Die Wurzel selbst und Ordner ohne Repo gehen
|
|
390
|
+
wie gewohnt.
|
|
391
|
+
|
|
386
392
|
mcp migrate ist der einzige Weg, auf dem hmgr die geteilte .mcp.json anfasst (E64): der Eintrag
|
|
387
393
|
wandert 1:1 aus der Datei in deinen privaten Scope am selben Ort und wird dort gelöscht. Auch ein
|
|
388
394
|
Eintrag, der nicht im Katalog steht, lässt sich so übernehmen — der Konfigblock kommt aus der Datei,
|
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, 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, 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";
|
|
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";
|
|
@@ -1142,6 +1142,7 @@ function istModellFehler(fehler) {
|
|
|
1142
1142
|
fehler instanceof McpAbweichenderEintrag ||
|
|
1143
1143
|
fehler instanceof McpKatalogFehler ||
|
|
1144
1144
|
fehler instanceof McpNurGeteilterFundort ||
|
|
1145
|
+
fehler instanceof McpOrtUnterGitWurzel ||
|
|
1145
1146
|
fehler instanceof McpUebernahmeNichtMoeglich ||
|
|
1146
1147
|
fehler instanceof McpNichtInGeteilterDatei ||
|
|
1147
1148
|
fehler instanceof McpUebernahmeUnvollstaendig ||
|
package/dist/gui/main.js
CHANGED
|
@@ -13,7 +13,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
13
13
|
import { bauen, umgebungLesen } from "../cli/main.js";
|
|
14
14
|
import { quelleAlsText, quelleAusText } from "../adapter/quelle-text.js";
|
|
15
15
|
import { paketVersion } from "../adapter/paket-version.js";
|
|
16
|
-
import { infoPlistPfad, neuereVersionAufPlatte } from "./selbst-update.js";
|
|
16
|
+
import { infoPlistPfad, MeldungsPuffer, neuereVersionAufPlatte } from "./selbst-update.js";
|
|
17
17
|
import { quelleName, quelleSchluessel } from "../kern/modell.js";
|
|
18
18
|
import { kannProfile } from "../ports/harness.js";
|
|
19
19
|
import { iconFuerQuelle } from "./icons.js";
|
|
@@ -28,6 +28,8 @@ const smoketest = process.env.HMGR_GUI_SMOKETEST === '1';
|
|
|
28
28
|
// E84 Teil 1: Pfad eines im Fixture noch unbekannten Verzeichnisses, das der Smoke-Lauf per Dialog
|
|
29
29
|
// hinzufügt, um den Sprung zur neuen Karte zu belegen. Nur zur Konstruktionszeit des DOM-Auszug-
|
|
30
30
|
// Skripts gelesen (Node-Seite), nicht zur Laufzeit im Renderer.
|
|
31
|
+
/** E90-Nachtrag: Smoke-Lauf, in dem die Platten-Erkennung schon vor dem Laden der Seite feuert. */
|
|
32
|
+
const smoketestFrueh = process.env.HMGR_GUI_SMOKE_FRUEH === '1';
|
|
31
33
|
const smoketestNeuesVerzeichnis = process.env.HMGR_SMOKETEST_NEUES_VERZEICHNIS ?? '';
|
|
32
34
|
/**
|
|
33
35
|
* Nur eine hmgr-Instanz je Nutzer (E67). Grund: zwei laufende Fenster schrieben nacheinander auf
|
|
@@ -507,6 +509,9 @@ function fensterOeffnen() {
|
|
|
507
509
|
sandbox: true,
|
|
508
510
|
},
|
|
509
511
|
});
|
|
512
|
+
meldungsPuffer.bereitAbmelden();
|
|
513
|
+
hauptfenster.webContents.on('did-start-loading', () => meldungsPuffer.bereitAbmelden());
|
|
514
|
+
hauptfenster.webContents.on('did-finish-load', () => meldungsPuffer.bereitSetzen());
|
|
510
515
|
hauptfenster.on('closed', () => {
|
|
511
516
|
hauptfenster = null;
|
|
512
517
|
zuletztGemeldeteGuiVersion = null;
|
|
@@ -519,7 +524,21 @@ function fensterOeffnen() {
|
|
|
519
524
|
console.log(`[renderer] ${event.message}`);
|
|
520
525
|
});
|
|
521
526
|
// Nur für den Smoke-Test (scripts/gui-smoke-test.ts): ein DOM-Auszug im Log, danach Ende.
|
|
522
|
-
if (smoketest) {
|
|
527
|
+
if (smoketest && smoketestFrueh) {
|
|
528
|
+
// E90-Nachtrag: die Platten-Erkennung feuert schon vor dem Laden der Seite (direkt nach der
|
|
529
|
+
// Fenstererzeugung); die Meldung muss trotzdem ankommen. Kein großer DOM-Auszug, nur das Modal.
|
|
530
|
+
const fenster = hauptfenster;
|
|
531
|
+
plattenVersionPruefen(true);
|
|
532
|
+
fenster.webContents.once('did-finish-load', () => {
|
|
533
|
+
setTimeout(() => {
|
|
534
|
+
void fenster.webContents
|
|
535
|
+
.executeJavaScript(NEUSTART_AUSZUG_SKRIPT)
|
|
536
|
+
.then((auszug) => console.log(`[smoke] Neustart-Modal früh: ${String(auszug)}`))
|
|
537
|
+
.finally(() => app.quit());
|
|
538
|
+
}, 1500);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (smoketest && !smoketestFrueh) {
|
|
523
542
|
const fenster = hauptfenster;
|
|
524
543
|
fenster.webContents.once('did-finish-load', () => {
|
|
525
544
|
setTimeout(() => {
|
|
@@ -860,6 +879,8 @@ function fensterOeffnen() {
|
|
|
860
879
|
// schon (gesperrte Zeile), „teamwork" nirgends (die Notizen je Ort).
|
|
861
880
|
var mcpWahlText = null;
|
|
862
881
|
var mcpWahlTextFrei = null;
|
|
882
|
+
// E91: die Wahlzeile des Unterordners im Repo ist deaktiviert und trägt die Notiz.
|
|
883
|
+
var mcpUnterordnerGesperrt = null;
|
|
863
884
|
try {
|
|
864
885
|
var mcpEinKnoepfe = Array.from(document.querySelectorAll('#mcpliste .mcpkopf button'))
|
|
865
886
|
.filter((b) => (b.textContent || '').indexOf('Einhängen') === 0);
|
|
@@ -874,6 +895,9 @@ function fensterOeffnen() {
|
|
|
874
895
|
document.querySelector('#d-mcp button[value="abbruch"]')?.click();
|
|
875
896
|
dMcpOffenNachAbbrechen = document.getElementById('d-mcp')?.open === true;
|
|
876
897
|
mcpWahlTextFrei = await mcpWahlOeffnen(mcpEinKnoepfe[1]);
|
|
898
|
+
mcpUnterordnerGesperrt = Array.from(document.querySelectorAll('#dm-orte .wahlzeile'))
|
|
899
|
+
.filter((z) => (z.textContent || '').indexOf('unterordner') > -1)
|
|
900
|
+
.map((z) => z.querySelector('input')?.disabled === true);
|
|
877
901
|
document.querySelector('#d-mcp button[value="abbruch"]')?.click();
|
|
878
902
|
} catch (fehler) {
|
|
879
903
|
mcpFehler = String(fehler);
|
|
@@ -1143,6 +1167,7 @@ function fensterOeffnen() {
|
|
|
1143
1167
|
dMcpOffenNachAbbrechen,
|
|
1144
1168
|
mcpWahlText,
|
|
1145
1169
|
mcpWahlTextFrei,
|
|
1170
|
+
mcpUnterordnerGesperrt,
|
|
1146
1171
|
mcpFehler,
|
|
1147
1172
|
dMcpAusOffenNachOeffnen,
|
|
1148
1173
|
dMcpAusOffenNachAbbrechen,
|
|
@@ -1218,26 +1243,12 @@ function fensterOeffnen() {
|
|
|
1218
1243
|
.then(async () => {
|
|
1219
1244
|
if (process.env.HMGR_GUI_SMOKE_PLATTENVERSION === undefined)
|
|
1220
1245
|
return;
|
|
1246
|
+
// Der Rest-Dialog (d-erfolg) öffnet über einen 90-ms-Timer nach der letzten Aktion des
|
|
1247
|
+
// Auszugs; ohne dieses Abwarten löste die Erkennung mal vor, mal nach ihm aus.
|
|
1248
|
+
await fenster.webContents.executeJavaScript(`new Promise(function (r) { var n = 0; (function w() { if (document.querySelector('dialog[open]') || ++n > 30) r(); else setTimeout(w, 50); })(); })`);
|
|
1221
1249
|
plattenVersionPruefen(true);
|
|
1222
1250
|
await new Promise((r) => setTimeout(r, 800));
|
|
1223
|
-
const auszug = await fenster.webContents.executeJavaScript(
|
|
1224
|
-
var dlg = document.getElementById('d-neustart');
|
|
1225
|
-
// Ein anderer Dialog steht noch offen (Rest des ersten Auszugs): das Modal wartet
|
|
1226
|
-
// solange, und öffnet erst, wenn der andere zu ist.
|
|
1227
|
-
var anderePlatzhalter = Array.from(document.querySelectorAll('dialog[open]')).filter(function (d) { return d !== dlg; });
|
|
1228
|
-
var wartetSolangeAndererOffen = anderePlatzhalter.length > 0 && dlg?.open !== true;
|
|
1229
|
-
anderePlatzhalter.forEach(function (d) { d.close(); });
|
|
1230
|
-
await new Promise(function (r) { setTimeout(r, 300); });
|
|
1231
|
-
var offen = dlg?.open === true;
|
|
1232
|
-
var titel = document.getElementById('dn-titel')?.textContent ?? null;
|
|
1233
|
-
var zeile = document.getElementById('selbstupdate-gui');
|
|
1234
|
-
var zeileText = zeile && !zeile.hidden ? zeile.textContent : null;
|
|
1235
|
-
var neustartKnopfSichtbar = document.getElementById('selbstupdate-neustart')?.hidden === false;
|
|
1236
|
-
var knopfTexte = Array.from(dlg?.querySelectorAll('.dfuss button') ?? []).map(function (b) { return b.textContent; }).join('|');
|
|
1237
|
-
dlg?.querySelector('button[value="spaeter"]')?.click();
|
|
1238
|
-
var nachSpaeterOffen = dlg?.open === true;
|
|
1239
|
-
return JSON.stringify({ wartetSolangeAndererOffen, offen, titel, zeileText, neustartKnopfSichtbar, knopfTexte, nachSpaeterOffen });
|
|
1240
|
-
})()`);
|
|
1251
|
+
const auszug = await fenster.webContents.executeJavaScript(NEUSTART_AUSZUG_SKRIPT);
|
|
1241
1252
|
console.log(`[smoke] Neustart-Modal: ${String(auszug)}`);
|
|
1242
1253
|
})
|
|
1243
1254
|
.finally(() => app.quit());
|
|
@@ -1337,11 +1348,40 @@ function updatePruefen() {
|
|
|
1337
1348
|
console.log(`[updater] ${fehler instanceof Error ? fehler.message : String(fehler)}`);
|
|
1338
1349
|
}
|
|
1339
1350
|
}
|
|
1340
|
-
/**
|
|
1341
|
-
|
|
1351
|
+
/** Das Skript für den Modal-Auszug im Smoke-Test (Neustart-Modal, Sidebar-Zeile, „Später"). */
|
|
1352
|
+
const NEUSTART_AUSZUG_SKRIPT = `(async () => {
|
|
1353
|
+
var dlg = document.getElementById('d-neustart');
|
|
1354
|
+
// Ein anderer Dialog steht noch offen (Rest des ersten Auszugs): das Modal wartet
|
|
1355
|
+
// solange, und öffnet erst, wenn der andere zu ist.
|
|
1356
|
+
var anderePlatzhalter = Array.from(document.querySelectorAll('dialog[open]')).filter(function (d) { return d !== dlg; });
|
|
1357
|
+
var wartetSolangeAndererOffen = anderePlatzhalter.length > 0 && dlg?.open !== true;
|
|
1358
|
+
anderePlatzhalter.forEach(function (d) { d.close(); });
|
|
1359
|
+
await new Promise(function (r) { setTimeout(r, 300); });
|
|
1360
|
+
var offen = dlg?.open === true;
|
|
1361
|
+
var titel = document.getElementById('dn-titel')?.textContent ?? null;
|
|
1362
|
+
var zeile = document.getElementById('selbstupdate-gui');
|
|
1363
|
+
var zeileText = zeile && !zeile.hidden ? zeile.textContent : null;
|
|
1364
|
+
var neustartKnopfSichtbar = document.getElementById('selbstupdate-neustart')?.hidden === false;
|
|
1365
|
+
var knopfTexte = Array.from(dlg?.querySelectorAll('.dfuss button') ?? []).map(function (b) { return b.textContent; }).join('|');
|
|
1366
|
+
dlg?.querySelector('button[value="spaeter"]')?.click();
|
|
1367
|
+
var nachSpaeterOffen = dlg?.open === true;
|
|
1368
|
+
return JSON.stringify({ wartetSolangeAndererOffen, offen, titel, zeileText, neustartKnopfSichtbar, knopfTexte, nachSpaeterOffen });
|
|
1369
|
+
})()`;
|
|
1370
|
+
/**
|
|
1371
|
+
* Ein Hinweis für die Sidebar (E77). Ist das Fenster schon zu, verfällt er stillschweigend. Solange
|
|
1372
|
+
* die Seite nicht geladen ist, hat der Renderer seinen Listener noch nicht (E90-Nachtrag): der
|
|
1373
|
+
* Puffer hält je Art die letzte Nachricht und sendet sie nach `did-finish-load` — das Skript am
|
|
1374
|
+
* Ende des Bodys registriert `onSelbstUpdate` synchron, `did-finish-load` kommt erst danach.
|
|
1375
|
+
*/
|
|
1376
|
+
const meldungsPuffer = new MeldungsPuffer((nachricht) => {
|
|
1342
1377
|
if (hauptfenster === null || hauptfenster.isDestroyed())
|
|
1343
1378
|
return;
|
|
1344
1379
|
hauptfenster.webContents.send('hmgr:selbstUpdate', nachricht);
|
|
1380
|
+
});
|
|
1381
|
+
function selbstUpdateMelden(nachricht) {
|
|
1382
|
+
if (hauptfenster === null || hauptfenster.isDestroyed())
|
|
1383
|
+
return;
|
|
1384
|
+
meldungsPuffer.melden(nachricht);
|
|
1345
1385
|
}
|
|
1346
1386
|
/** Windows: ein Paket ist geladen und wartet auf `quitAndInstall` (E90, Merker für `hmgr:neuStarten`). */
|
|
1347
1387
|
let geladenesPaketVorhanden = false;
|
|
@@ -960,6 +960,18 @@
|
|
|
960
960
|
return MCP_PLAETZE.some(function (p) { return zielKey(p) === zk; });
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
+
/* E91: Claude Code führt MCP-Server je Git-Wurzel. Liegt ein Arbeitsverzeichnis in einem Repo,
|
|
964
|
+
ohne dessen Wurzel zu sein, liefert der Kern den Platz mit gitWurzel, und hier gibt es dort
|
|
965
|
+
kein Hinzufügen, Entfernen oder Übernehmen. Ergebnis: die Wurzel oder null. */
|
|
966
|
+
function mcpGitWurzel(ziel) {
|
|
967
|
+
var zk = zielKey(ziel);
|
|
968
|
+
var platz = MCP_PLAETZE.find(function (p) { return zielKey(p) === zk; });
|
|
969
|
+
return platz && platz.gitWurzel ? platz.gitWurzel : null;
|
|
970
|
+
}
|
|
971
|
+
function mcpWurzelNotiz(wurzel) {
|
|
972
|
+
return 'Liegt im Git-Repo ' + wurzel + '. Claude Code führt MCP-Server je Repo — dort einhängen.';
|
|
973
|
+
}
|
|
974
|
+
|
|
963
975
|
/** Die MCP-Zeilen eines Ziels, aus den drei Quellen der MCP-Ansicht zusammengesucht: eingehängte
|
|
964
976
|
Katalog-Einträge (mit Entfernen-Knopf), ihre Fundorte in der geteilten .mcp.json des Projekts
|
|
965
977
|
(E61-Nachtrag, seit E64 mit Übernehmen-Knopf) und Einträge, die dort hängen, aber nicht im
|
|
@@ -991,6 +1003,13 @@
|
|
|
991
1003
|
/** Der MCP-Abschnitt unter der Skill-Link-Tabelle einer Ziel-Karte (E61-GUI-Nachtrag): nur, wenn
|
|
992
1004
|
dort etwas eingehängt ist oder der „hinzufügen"-Knopf ohnehin sichtbar wäre (mcpKannAn). */
|
|
993
1005
|
function mcpAbschnitt(ziel) {
|
|
1006
|
+
var wurzel = mcpGitWurzel(ziel);
|
|
1007
|
+
if (wurzel !== null) {
|
|
1008
|
+
var gesperrt = e('div', 'mcpkarteabschnitt');
|
|
1009
|
+
gesperrt.appendChild(e('span', 'etikett', 'MCP'));
|
|
1010
|
+
gesperrt.appendChild(e('p', 'ohne mcpkarteleer', mcpWurzelNotiz(wurzel)));
|
|
1011
|
+
return gesperrt;
|
|
1012
|
+
}
|
|
994
1013
|
var zeilen = mcpZeilenFuerZiel(ziel);
|
|
995
1014
|
if (zeilen.length === 0 && !mcpKannAn(ziel)) return null;
|
|
996
1015
|
var abschnitt = e('div', 'mcpkarteabschnitt');
|
|
@@ -1035,7 +1054,7 @@
|
|
|
1035
1054
|
/** Knopf „MCP hinzufügen …" im Kartenfuß neben „Skills hinzufügen" (E61-GUI-Nachtrag, Gegenrichtung
|
|
1036
1055
|
zur MCP-Ansicht): nur, wo der Harness an diesem Ort MCP kann (mcpKannAn, E16). */
|
|
1037
1056
|
function mcpHinzufuegenKnopf(ziel) {
|
|
1038
|
-
if (!mcpKannAn(ziel)) return null;
|
|
1057
|
+
if (!mcpKannAn(ziel) || mcpGitWurzel(ziel) !== null) return null;
|
|
1039
1058
|
var knopf = e('button', 'knopf leise klein mitsymbol', 'MCP hinzufügen …');
|
|
1040
1059
|
knopf.type = 'button';
|
|
1041
1060
|
knopf.insertBefore(harnessZeichen(ziel.harness), knopf.firstChild);
|
|
@@ -3071,14 +3090,17 @@
|
|
|
3071
3090
|
input.type = 'radio';
|
|
3072
3091
|
input.name = 'mcport';
|
|
3073
3092
|
input.value = String(i);
|
|
3074
|
-
input.disabled = schon || !aktiv;
|
|
3093
|
+
input.disabled = schon || !aktiv || !!ziel.gitWurzel;
|
|
3075
3094
|
label.appendChild(input);
|
|
3076
3095
|
|
|
3077
3096
|
var txt = e('span');
|
|
3078
3097
|
txt.appendChild(beschriftet(e('b', null, zielTitel({ ziel: ziel })), ziel.harness));
|
|
3079
3098
|
txt.appendChild(e('span', 'mono', mcpOrtSpur(ziel)));
|
|
3080
3099
|
|
|
3081
|
-
if (
|
|
3100
|
+
if (ziel.gitWurzel) {
|
|
3101
|
+
// E91: der Kern sperrt diesen Platz, Einhängen ginge an die Wurzel.
|
|
3102
|
+
txt.appendChild(e('span', 'notiz', mcpWurzelNotiz(ziel.gitWurzel)));
|
|
3103
|
+
} else if (schon) {
|
|
3082
3104
|
// F46: die Orte-Liste am Katalog-Eintrag gibt es nicht mehr, der Weg zum Aushängen führt
|
|
3083
3105
|
// über den Entfernen-Knopf im MCP-Abschnitt der Ziel-Karte.
|
|
3084
3106
|
txt.appendChild(e('span', 'notiz', 'Hängt hier schon. Zum Aushängen den Entfernen-Knopf am MCP-Abschnitt der Ziel-Karte benutzen.'));
|
|
@@ -72,3 +72,34 @@ export function neuereVersionAufPlatte(plistPfad, laufend) {
|
|
|
72
72
|
const platte = plistVersion(plistPfad);
|
|
73
73
|
return platte !== null && istNeuer(platte, laufend) ? platte : null;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Puffer für Meldungen an den Renderer (E90-Nachtrag). Solange die Seite nicht geladen ist, hat der
|
|
77
|
+
* Renderer seinen Listener noch nicht registriert, ein `webContents.send` ginge ins Leere. Bis
|
|
78
|
+
* `bereitSetzen()` wird deshalb je `art` nur die letzte Nachricht behalten und dann einmal
|
|
79
|
+
* gesendet; danach geht alles sofort durch. `bereitAbmelden()` (neues Laden der Seite) schaltet
|
|
80
|
+
* wieder auf Puffern. Ohne Electron-Import, damit ohne App testbar.
|
|
81
|
+
*/
|
|
82
|
+
export class MeldungsPuffer {
|
|
83
|
+
bereit = false;
|
|
84
|
+
wartend = new Map();
|
|
85
|
+
senden;
|
|
86
|
+
constructor(senden) {
|
|
87
|
+
this.senden = senden;
|
|
88
|
+
}
|
|
89
|
+
melden(nachricht) {
|
|
90
|
+
if (this.bereit)
|
|
91
|
+
this.senden(nachricht);
|
|
92
|
+
else
|
|
93
|
+
this.wartend.set(nachricht.art, nachricht);
|
|
94
|
+
}
|
|
95
|
+
bereitSetzen() {
|
|
96
|
+
this.bereit = true;
|
|
97
|
+
const gepuffert = [...this.wartend.values()];
|
|
98
|
+
this.wartend.clear();
|
|
99
|
+
for (const nachricht of gepuffert)
|
|
100
|
+
this.senden(nachricht);
|
|
101
|
+
}
|
|
102
|
+
bereitAbmelden() {
|
|
103
|
+
this.bereit = false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -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, 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, marketplaceUrl, pluginSchluessel, pluginSchluesselZerlegen, quelleAlsText, quelleMitNamen, quelleName, quelleNameAbleiten, quelleNameFrei, quelleSchluessel, zielSchluessel, } from "./modell.js";
|
|
4
4
|
import { passenderRef } from "./version.js";
|
|
5
5
|
import { kannIstRegistriert, kannMcp, kannPlugins, kannProfile, kannSkillOrdner, kannZurueckbauen, kannZusaetzlicheHeime, } from "../ports/harness.js";
|
|
6
6
|
/**
|
|
@@ -345,6 +345,25 @@ export class Anwendungsfaelle {
|
|
|
345
345
|
throw new McpFaehigkeitFehlt(name);
|
|
346
346
|
return harness;
|
|
347
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Die Git-Wurzel, an der ein Arbeitsverzeichnis in Wahrheit hängt (E91), wenn der Harness seinen
|
|
350
|
+
* local-Scope je Git-Wurzel führt und der Ordner in einem Repo liegt, aber nicht dessen Wurzel
|
|
351
|
+
* ist. Sonst null: kein Repo, Ordner fehlt, der Ordner ist die Wurzel, oder die Fähigkeit hat das
|
|
352
|
+
* Merkmal nicht.
|
|
353
|
+
*/
|
|
354
|
+
async mcpGitWurzelFuer(faehig, ziel) {
|
|
355
|
+
if (faehig.anGitWurzelGebunden !== true || ziel.ort.art !== 'arbeitsverzeichnis')
|
|
356
|
+
return null;
|
|
357
|
+
const kopie = await this.p.quellen.arbeitskopie(ziel.ort.pfad).catch(() => null);
|
|
358
|
+
if (kopie === null || kopie.relativerPfad === '')
|
|
359
|
+
return null;
|
|
360
|
+
return kopie.wurzel;
|
|
361
|
+
}
|
|
362
|
+
async mcpGitWurzelPruefen(faehig, ziel) {
|
|
363
|
+
const wurzel = await this.mcpGitWurzelFuer(faehig, ziel);
|
|
364
|
+
if (wurzel !== null)
|
|
365
|
+
throw new McpOrtUnterGitWurzel(ziel, wurzel);
|
|
366
|
+
}
|
|
348
367
|
/** MCP-Katalog, unverändert wie in ~/.hmgr/mcp.yaml gepflegt (E61). hmgr schreibt ihn nie. */
|
|
349
368
|
async mcpKatalog() {
|
|
350
369
|
return await this.p.mcpKatalog.lesen();
|
|
@@ -385,6 +404,7 @@ export class Anwendungsfaelle {
|
|
|
385
404
|
const faehig = this.harnessMitMcp(harness.name);
|
|
386
405
|
if (!faehig.mcp.ebenen().includes(args.ziel.ort.art))
|
|
387
406
|
throw new McpEbeneNichtUnterstuetzt(harness.name, args.ziel.ort.art);
|
|
407
|
+
await this.mcpGitWurzelPruefen(faehig.mcp, args.ziel);
|
|
388
408
|
await faehig.mcp.einhaengen(eintrag.name, eintrag.konfig, args.ziel.ort);
|
|
389
409
|
const warnung = await this.mcpVariablenWarnung(faehig, eintrag, args.ziel.ort);
|
|
390
410
|
const hinweis = faehig.mcp.hinweisNachAenderung?.();
|
|
@@ -402,6 +422,7 @@ export class Anwendungsfaelle {
|
|
|
402
422
|
const faehig = this.harnessMitMcp(harness.name);
|
|
403
423
|
if (!faehig.mcp.ebenen().includes(args.ziel.ort.art))
|
|
404
424
|
throw new McpEbeneNichtUnterstuetzt(harness.name, args.ziel.ort.art);
|
|
425
|
+
await this.mcpGitWurzelPruefen(faehig.mcp, args.ziel);
|
|
405
426
|
await faehig.mcp.aushaengen(eintrag.name, args.ziel.ort);
|
|
406
427
|
const hinweis = faehig.mcp.hinweisNachAenderung?.();
|
|
407
428
|
return { eintrag, ziel: args.ziel, ...(hinweis === undefined ? {} : { hinweis }) };
|
|
@@ -420,6 +441,7 @@ export class Anwendungsfaelle {
|
|
|
420
441
|
const uebernehmen = faehig.mcp.geteiltenUebernehmen;
|
|
421
442
|
if (uebernehmen === undefined)
|
|
422
443
|
throw new McpUebernahmeNichtMoeglich(args.ziel);
|
|
444
|
+
await this.mcpGitWurzelPruefen(faehig.mcp, args.ziel);
|
|
423
445
|
const { datei } = await uebernehmen.call(faehig.mcp, args.name, args.ziel.ort);
|
|
424
446
|
const hinweis = faehig.mcp.hinweisNachAenderung?.();
|
|
425
447
|
return { name: args.name, ziel: args.ziel, datei, ...(hinweis === undefined ? {} : { hinweis }) };
|
|
@@ -458,7 +480,12 @@ export class Anwendungsfaelle {
|
|
|
458
480
|
* Adapter, nicht am Harness-Namen (E16): agents kommt nie vor, weil es `mcp` nicht implementiert.
|
|
459
481
|
*/
|
|
460
482
|
async mcpPlaetze() {
|
|
461
|
-
|
|
483
|
+
const plaetze = [];
|
|
484
|
+
for (const ziel of await this.mcpZiele()) {
|
|
485
|
+
const wurzel = await this.mcpGitWurzelFuer(this.harnessMitMcp(ziel.harness).mcp, ziel);
|
|
486
|
+
plaetze.push(wurzel === null ? ziel : { ...ziel, gitWurzel: wurzel });
|
|
487
|
+
}
|
|
488
|
+
return plaetze;
|
|
462
489
|
}
|
|
463
490
|
/**
|
|
464
491
|
* Übersicht für `hmgr mcp list` (E61): je Katalog-Eintrag, wo er eingehängt ist, dazu Einträge in
|
package/dist/kern/modell.js
CHANGED
|
@@ -524,6 +524,23 @@ export class McpNurGeteilterFundort extends Error {
|
|
|
524
524
|
this.datei = datei;
|
|
525
525
|
}
|
|
526
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* MCP-Operation an einem Arbeitsverzeichnis, das in einem Git-Repo liegt, aber nicht dessen Wurzel
|
|
529
|
+
* ist (E91): Claude Code führt den local-Scope je Repo, nicht je Unterordner. Einhängen, Aushängen
|
|
530
|
+
* und Übernehmen sind dort gesperrt; ein Aushängen würde sonst den Eintrag der Wurzel entfernen.
|
|
531
|
+
*/
|
|
532
|
+
export class McpOrtUnterGitWurzel extends Error {
|
|
533
|
+
ziel;
|
|
534
|
+
wurzel;
|
|
535
|
+
constructor(ziel, wurzel) {
|
|
536
|
+
const pfad = ziel.ort.art === 'arbeitsverzeichnis' ? ziel.ort.pfad : '';
|
|
537
|
+
super(`${pfad} liegt im Git-Repo ${wurzel}. Claude Code führt MCP-Server je Repo, nicht je Unterordner: `
|
|
538
|
+
+ `was dort hängt, gilt auch hier. Häng MCP-Server an ${wurzel} ein; ist das noch kein Arbeitsverzeichnis: `
|
|
539
|
+
+ `hmgr dir add ${wurzel}.`);
|
|
540
|
+
this.ziel = ziel;
|
|
541
|
+
this.wurzel = wurzel;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
527
544
|
/**
|
|
528
545
|
* Übernehmen (E64) an einem Ort, der keine geteilte Konfigurationsdatei kennt: ein Harness ohne
|
|
529
546
|
* diese Fähigkeit (Hermes kennt keine geteilte Datei) oder eine Ebene ohne sie (bei Claude Code hat
|