kimal3 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kimal3-logo",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "KImal3-Logo über dem Eingabefeld, öffnet beim Start das Projekt in der App · KImal3 logo above the prompt, opens the project in the app on start",
|
|
5
5
|
"author": { "name": "KImal3", "url": "https://kimal3.de" },
|
|
6
6
|
"types": "./types/index.d.ts"
|
|
@@ -10,6 +10,10 @@ const PROJEKTNUMMER = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]
|
|
|
10
10
|
// Der eingebaute Browser der Claude-App, wie ihn das Werkzeug `mcp__Claude_Browser__navigate` nennt.
|
|
11
11
|
const BROWSER = 'Claude_Browser'
|
|
12
12
|
const NAVIGATE = 'mcp__Claude_Browser__navigate'
|
|
13
|
+
// Den Browser-Bereich neben dem Chat einblenden: `navigate` allein öffnet die Seite in einem VERSTECKTEN Bereich
|
|
14
|
+
// (gemessen 23.10. in einer frischen Sitzung: `navOk: true`, „Browser pane opened“, Dennis sah nichts).
|
|
15
|
+
const ANSICHT = 'ccd_view'
|
|
16
|
+
const EINBLENDEN = 'mcp__ccd_view__show_pane'
|
|
13
17
|
const geoeffnet = atom({ plugin: 'kimal3-logo', key: 'appGeoeffnet' } as const, false)
|
|
14
18
|
|
|
15
19
|
/** Die Adresse für den Inhalt von `plan/stand.json` (`null`: keine Datei). Ohne gültige Nummer der Kurs, wie der Link in der Leiste (S-3, S-4). */
|
|
@@ -32,6 +36,11 @@ export function istEigenerAufruf(plugin: string, input: unknown): boolean {
|
|
|
32
36
|
return plugin === 'kimal3-logo' && typeof url === 'string' && url.startsWith(`${APP}/`)
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
/** Die eigene Erlaubnis zum Einblenden: nur der Aufruf dieses Mods, nur der Browser-Bereich (`preview`), nie ein anderer. */
|
|
40
|
+
export function istEigenesEinblenden(plugin: string, input: unknown): boolean {
|
|
41
|
+
return plugin === 'kimal3-logo' && (input as { pane?: unknown } | null)?.pane === 'preview'
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
// Gegen zwei gleichzeitige Versuche in diesem Lauf (`session.start` und `session.attach` kurz hintereinander).
|
|
36
45
|
// Das Neuladen des Mods setzt die Variable zurück; dagegen hält der Merker `geoeffnet`.
|
|
37
46
|
let laeuft = false
|
|
@@ -50,19 +59,57 @@ async function einmalOeffnen($: Engine): Promise<void> {
|
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
/** Der Startordner und alle Ordner darüber, nächster zuerst; Trenner immer `/` (Windows nimmt beide). */
|
|
63
|
+
export function ordnerNachOben(cwd: string): string[] {
|
|
64
|
+
const teile = cwd.replace(/\\/g, '/').replace(/\/+$/, '').split('/')
|
|
65
|
+
return teile.map((_, i) => teile.slice(0, teile.length - i).join('/'))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Das Hauptprojekt einer Git-Arbeitskopie aus ihrer `.git`-Datei (`gitdir: C:/…/kimal3-app/.git/worktrees/s8`).
|
|
70
|
+
* `null` für alles andere, etwa ein Untermodul (`gitdir: ../.git/modules/…`).
|
|
71
|
+
*/
|
|
72
|
+
export function hauptordnerAusGitdatei(text: string): string | null {
|
|
73
|
+
const treffer = /^gitdir:\s*(.+?)[\\/]\.git[\\/]worktrees[\\/][^\\/]+\s*$/.exec(text.trim())
|
|
74
|
+
return treffer === null ? null : treffer[1].replace(/\\/g, '/')
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function liesDatei($: Engine, pfad: string): Promise<string | null> {
|
|
56
78
|
try {
|
|
57
|
-
|
|
79
|
+
return await $.fs.read(pfad)
|
|
58
80
|
} catch {
|
|
59
|
-
|
|
81
|
+
return null
|
|
60
82
|
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `plan/stand.json` des Projekts, in dem Claude läuft (S-5, S-24, S-25): vom Startordner nach oben, bis zur Wurzel des
|
|
87
|
+
* eigenen Git-Projekts und nie darüber hinaus — sonst bekäme ein Projekt ohne Plan die Seite eines anderen. Ist die
|
|
88
|
+
* Wurzel eine Arbeitskopie (`.git` ist dort eine Datei), zählt danach die Projektdatei ihres Hauptprojekts.
|
|
89
|
+
*/
|
|
90
|
+
async function findeStand($: Engine, cwd: string): Promise<string | null> {
|
|
91
|
+
for (const ordner of ordnerNachOben(cwd)) {
|
|
92
|
+
const stand = await liesDatei($, `${ordner}/plan/stand.json`)
|
|
93
|
+
if (stand !== null) return stand
|
|
94
|
+
if (!(await $.fs.exists(`${ordner}/.git`))) continue
|
|
95
|
+
// Ein Ordner `.git` lässt sich nicht lesen: echte Projektwurzel, hier ist Schluss.
|
|
96
|
+
const gitdatei = await liesDatei($, `${ordner}/.git`)
|
|
97
|
+
const haupt = gitdatei === null ? null : hauptordnerAusGitdatei(gitdatei)
|
|
98
|
+
return haupt === null ? null : liesDatei($, `${haupt}/plan/stand.json`)
|
|
99
|
+
}
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function oeffne($: Engine): Promise<boolean> {
|
|
104
|
+
const stand = await findeStand($, await $.session.cwd())
|
|
61
105
|
// Scheitern meldet sich als Scheitern, nie im Chat (S-13): eine Zeile nur im Debug-Protokoll (`to: 'debug'` —
|
|
62
106
|
// ohne steht sie im Chatverlauf, Befund der Durchsicht 23.10.).
|
|
63
107
|
try {
|
|
64
108
|
const antwort = await $.mcp.call(BROWSER, 'navigate', { url: adresseFuer(stand) })
|
|
65
|
-
if (!antwort.isError)
|
|
109
|
+
if (!antwort.isError) {
|
|
110
|
+
await einblenden($)
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
66
113
|
$.ui.log(`kimal3-logo: Projektseite nicht geöffnet: ${JSON.stringify(antwort.content).slice(0, 300)}`, { to: 'debug' })
|
|
67
114
|
} catch (fehler) {
|
|
68
115
|
$.ui.log(`kimal3-logo: Projektseite nicht geöffnet: ${String(fehler).slice(0, 300)}`, { to: 'debug' })
|
|
@@ -70,6 +117,16 @@ async function oeffne($: Engine): Promise<boolean> {
|
|
|
70
117
|
return false
|
|
71
118
|
}
|
|
72
119
|
|
|
120
|
+
/** Die geöffnete Seite auch zeigen. Scheitert das, ist die Seite trotzdem offen — eine Zeile im Debug-Protokoll, kein zweiter Versuch. */
|
|
121
|
+
async function einblenden($: Engine): Promise<void> {
|
|
122
|
+
try {
|
|
123
|
+
const antwort = await $.mcp.call(ANSICHT, 'show_pane', { pane: 'preview' })
|
|
124
|
+
if (antwort.isError) $.ui.log(`kimal3-logo: Browser nicht eingeblendet: ${JSON.stringify(antwort.content).slice(0, 300)}`, { to: 'debug' })
|
|
125
|
+
} catch (fehler) {
|
|
126
|
+
$.ui.log(`kimal3-logo: Browser nicht eingeblendet: ${String(fehler).slice(0, 300)}`, { to: 'debug' })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
73
130
|
export const appOeffnen: Register = on => {
|
|
74
131
|
// Die eigene Erlaubnis, eng: nur der Aufruf dieses Mods, nur eine Adresse der App. Ohne sie lehnt der Auto-Modus
|
|
75
132
|
// ab, weil beim Start niemand um etwas gebeten hat (gemessen 23.10. mit einer Probe). Claudes eigene Aufrufe und
|
|
@@ -79,6 +136,11 @@ export const appOeffnen: Register = on => {
|
|
|
79
136
|
? { decision: 'allow', reason: 'kimal3: die eigene Projektseite beim Start' }
|
|
80
137
|
: next(e),
|
|
81
138
|
)
|
|
139
|
+
on('tool.check', { tool: EINBLENDEN }, ($, e, next) =>
|
|
140
|
+
istEigenesEinblenden(next.origin.plugin, e.input)
|
|
141
|
+
? { decision: 'allow', reason: 'kimal3: den Browser mit der Projektseite einblenden' }
|
|
142
|
+
: next(e),
|
|
143
|
+
)
|
|
82
144
|
// Den Browser gibt es nur in der Claude-App, nicht im Terminal oder in VS Code. Beim Start meldet die App
|
|
83
145
|
// `isInteractive: false`, ist aber schon angehängt (gemessen 23.10.: surfaces ["desktop"]). `claude -p`, Skripte,
|
|
84
146
|
// geplante Aufgaben, das Terminal: kein Aufruf (S-6).
|
package/package.json
CHANGED