kimal3 0.6.2 → 0.6.4
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/claude-plugins/kimal3-logo/.claude-plugin/plugin.json +7 -7
- package/claude-plugins/kimal3-logo/hooks/app-oeffnen.ts +158 -96
- package/claude-plugins/kimal3-logo/hooks/chat-benennen.ts +135 -0
- package/claude-plugins/kimal3-logo/hooks/register.tsx +87 -84
- package/claude-plugins/kimal3-logo/types/index.d.ts +12 -8
- package/package.json +30 -30
- package/skill/commands/k3kurs.md +41 -41
- package/skill/en/commands/k3course.md +41 -41
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kimal3-logo",
|
|
3
|
-
"version": "0.
|
|
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
|
-
"author": { "name": "KImal3", "url": "https://kimal3.de" },
|
|
6
|
-
"types": "./types/index.d.ts"
|
|
7
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "kimal3-logo",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "KImal3-Logo über dem Eingabefeld, öffnet beim Start das Projekt in der App, benennt den Chat nach seinem Thema · KImal3 logo above the prompt, opens the project in the app on start, names the chat after its topic",
|
|
5
|
+
"author": { "name": "KImal3", "url": "https://kimal3.de" },
|
|
6
|
+
"types": "./types/index.d.ts"
|
|
7
|
+
}
|
|
@@ -1,96 +1,158 @@
|
|
|
1
|
-
import { atom, read, update, type Engine, type Register } from 'claude-code'
|
|
2
|
-
|
|
3
|
-
// Beim Start von Claude Code die Projektseite im Browser der Claude-App öffnen (Dennis, 23.10.: „Automatisch bei jedem
|
|
4
|
-
// Start“, und zwar „in dem Browser in der Claude-Code-App“ — nicht im Standardbrowser).
|
|
5
|
-
// Szenario-Tabelle: docs/superpowers/specs/2026-10-23-mod-app-oeffnen.md
|
|
6
|
-
|
|
7
|
-
const APP = 'https://app.kimal3.de'
|
|
8
|
-
// Positivliste (messen-und-pruefen §23): Nur eine Projektnummer kommt in die Adresse, nie ein anderer Wert aus der Datei (S-4).
|
|
9
|
-
const PROJEKTNUMMER = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
10
|
-
// Der eingebaute Browser der Claude-App, wie ihn das Werkzeug `mcp__Claude_Browser__navigate` nennt.
|
|
11
|
-
const BROWSER = 'Claude_Browser'
|
|
12
|
-
const NAVIGATE = 'mcp__Claude_Browser__navigate'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
1
|
+
import { atom, read, update, type Engine, type Register } from 'claude-code'
|
|
2
|
+
|
|
3
|
+
// Beim Start von Claude Code die Projektseite im Browser der Claude-App öffnen (Dennis, 23.10.: „Automatisch bei jedem
|
|
4
|
+
// Start“, und zwar „in dem Browser in der Claude-Code-App“ — nicht im Standardbrowser).
|
|
5
|
+
// Szenario-Tabelle: docs/superpowers/specs/2026-10-23-mod-app-oeffnen.md
|
|
6
|
+
|
|
7
|
+
const APP = 'https://app.kimal3.de'
|
|
8
|
+
// Positivliste (messen-und-pruefen §23): Nur eine Projektnummer kommt in die Adresse, nie ein anderer Wert aus der Datei (S-4).
|
|
9
|
+
const PROJEKTNUMMER = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
10
|
+
// Der eingebaute Browser der Claude-App, wie ihn das Werkzeug `mcp__Claude_Browser__navigate` nennt.
|
|
11
|
+
const BROWSER = 'Claude_Browser'
|
|
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'
|
|
17
|
+
const geoeffnet = atom({ plugin: 'kimal3-logo', key: 'appGeoeffnet' } as const, false)
|
|
18
|
+
|
|
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). */
|
|
20
|
+
export function adresseFuer(standJson: string | null): string {
|
|
21
|
+
if (standJson !== null) {
|
|
22
|
+
try {
|
|
23
|
+
// Ein BOM am Anfang (Windows-Editoren) lehnt JSON.parse ab, wie in `cli/src/plan-datei.ts`.
|
|
24
|
+
const id: unknown = JSON.parse(standJson.replace(/^\uFEFF/, ''))?.projekt_id
|
|
25
|
+
if (typeof id === 'string' && PROJEKTNUMMER.test(id)) return `${APP}/projekt/${id.toLowerCase()}`
|
|
26
|
+
} catch {
|
|
27
|
+
// kaputtes JSON: wie keine Datei
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return `${APP}/kurs`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Die eigene Erlaubnis (unten): nur der Aufruf dieses Mods, nur eine Adresse der App. Herausgelöst, damit `check:logo` die Grenze prüft. */
|
|
34
|
+
export function istEigenerAufruf(plugin: string, input: unknown): boolean {
|
|
35
|
+
const url = (input as { url?: unknown } | null)?.url
|
|
36
|
+
return plugin === 'kimal3-logo' && typeof url === 'string' && url.startsWith(`${APP}/`)
|
|
37
|
+
}
|
|
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
|
+
|
|
44
|
+
// Gegen zwei gleichzeitige Versuche in diesem Lauf (`session.start` und `session.attach` kurz hintereinander).
|
|
45
|
+
// Das Neuladen des Mods setzt die Variable zurück; dagegen hält der Merker `geoeffnet`.
|
|
46
|
+
let laeuft = false
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Höchstens einmal je Sitzung (S-7, S-8, S-10): Der Merker in `$.state` übersteht das Neuladen des Mods. Er wird erst
|
|
50
|
+
* nach einem gelungenen Aufruf gesetzt — scheitert der erste Versuch, darf ein späteres `session.attach` es noch einmal.
|
|
51
|
+
*/
|
|
52
|
+
async function einmalOeffnen($: Engine): Promise<void> {
|
|
53
|
+
if (laeuft || (await read($, geoeffnet))) return
|
|
54
|
+
laeuft = true
|
|
55
|
+
try {
|
|
56
|
+
if (await oeffne($)) await update($, geoeffnet, () => true)
|
|
57
|
+
} finally {
|
|
58
|
+
laeuft = false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
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> {
|
|
78
|
+
try {
|
|
79
|
+
return await $.fs.read(pfad)
|
|
80
|
+
} catch {
|
|
81
|
+
return null
|
|
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())
|
|
105
|
+
// Scheitern meldet sich als Scheitern, nie im Chat (S-13): eine Zeile nur im Debug-Protokoll (`to: 'debug'` —
|
|
106
|
+
// ohne steht sie im Chatverlauf, Befund der Durchsicht 23.10.).
|
|
107
|
+
try {
|
|
108
|
+
const antwort = await $.mcp.call(BROWSER, 'navigate', { url: adresseFuer(stand) })
|
|
109
|
+
if (!antwort.isError) {
|
|
110
|
+
await einblenden($)
|
|
111
|
+
return true
|
|
112
|
+
}
|
|
113
|
+
$.ui.log(`kimal3-logo: Projektseite nicht geöffnet: ${JSON.stringify(antwort.content).slice(0, 300)}`, { to: 'debug' })
|
|
114
|
+
} catch (fehler) {
|
|
115
|
+
$.ui.log(`kimal3-logo: Projektseite nicht geöffnet: ${String(fehler).slice(0, 300)}`, { to: 'debug' })
|
|
116
|
+
}
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
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
|
+
|
|
130
|
+
export const appOeffnen: Register = on => {
|
|
131
|
+
// Die eigene Erlaubnis, eng: nur der Aufruf dieses Mods, nur eine Adresse der App. Ohne sie lehnt der Auto-Modus
|
|
132
|
+
// ab, weil beim Start niemand um etwas gebeten hat (gemessen 23.10. mit einer Probe). Claudes eigene Aufrufe und
|
|
133
|
+
// jede andere Adresse entscheidet die Kette darunter wie bisher.
|
|
134
|
+
on('tool.check', { tool: NAVIGATE }, ($, e, next) =>
|
|
135
|
+
istEigenerAufruf(next.origin.plugin, e.input)
|
|
136
|
+
? { decision: 'allow', reason: 'kimal3: die eigene Projektseite beim Start' }
|
|
137
|
+
: next(e),
|
|
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
|
+
)
|
|
144
|
+
// Den Browser gibt es nur in der Claude-App, nicht im Terminal oder in VS Code. Beim Start meldet die App
|
|
145
|
+
// `isInteractive: false`, ist aber schon angehängt (gemessen 23.10.: surfaces ["desktop"]). `claude -p`, Skripte,
|
|
146
|
+
// geplante Aufgaben, das Terminal: kein Aufruf (S-6).
|
|
147
|
+
on('session.start', async ($, e, next) => {
|
|
148
|
+
const result = await next(e)
|
|
149
|
+
if ((await $.session.surfaces()).includes('desktop')) await einmalOeffnen($)
|
|
150
|
+
return result
|
|
151
|
+
})
|
|
152
|
+
// Hängt sich die App erst nach dem Start an, kommt das Signal hier.
|
|
153
|
+
on('session.attach', async ($, e, next) => {
|
|
154
|
+
const result = await next(e)
|
|
155
|
+
if (e.surface === 'desktop') await einmalOeffnen($)
|
|
156
|
+
return result
|
|
157
|
+
})
|
|
158
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { atom, read, update, type Engine, type Register, type SessionMessage } from 'claude-code'
|
|
2
|
+
|
|
3
|
+
// Den Chat in der Claude-App umbenennen, sobald sein Thema klar ist (Dennis, 23.10.: „nach jeder Antwort prüft ein
|
|
4
|
+
// kleines Modell, ob das Thema klar ist, benannt wird einmal, nur in der Claude-App, über die Umbenennen-Funktion der App“).
|
|
5
|
+
// Anlass: Die App nimmt den Namen aus der ersten Nachricht — ist das `/k3start`, heißt der Chat für immer „K3start“.
|
|
6
|
+
//
|
|
7
|
+
// Fälle (B-…), geprüft in `npm run check:logo`:
|
|
8
|
+
// B-1 Thema klar → ein Titel aus 2 bis 5 Wörtern, einmal gesetzt · B-2 noch unklar → nichts, nach der nächsten Antwort
|
|
9
|
+
// wieder fragen · B-3 höchstens EIN Versuch je Sitzung, auch wenn die App ablehnt (der Mensch hat den Namen selbst
|
|
10
|
+
// vergeben und Nein gesagt — dann nicht nach jeder Antwort wieder fragen) · B-4 nur in der Claude-App, sonst nicht einmal
|
|
11
|
+
// das Modell fragen · B-5 Antworten von Subagenten und abgebrochene Antworten zählen nicht · B-6 fortgesetzte Chats
|
|
12
|
+
// behalten ihren Namen · B-7 eine Modellantwort, die kein Titel ist, zählt als unklar · B-8 höchstens 8 Fragen je Sitzung
|
|
13
|
+
// · B-9 nicht, wenn alle Erlaubnis-Abfragen aus sind, und nicht, solange der Modus unbekannt ist.
|
|
14
|
+
//
|
|
15
|
+
// Den Schutz für selbst vergebene Namen bringt die App mit: Sie fragt den Menschen, bevor sie einen solchen ersetzt —
|
|
16
|
+
// außer im Modus `bypassPermissions` (Beschreibung von `set_session_title`). Deshalb B-9. Gemessen 23.10. im Modus
|
|
17
|
+
// `auto`: Der Aufruf aus einem Mod benennt den eigenen Chat um.
|
|
18
|
+
//
|
|
19
|
+
// Bekannte Grenzen (Durchsicht 23.10.): Startet die App den Prozess neu, bevor das Thema klar war, kommt der Chat als
|
|
20
|
+
// `resume` zurück und bleibt unbenannt (B-6 geht vor). Was `/clear` mit dem Merker macht, ist nicht gemessen.
|
|
21
|
+
|
|
22
|
+
const SITZUNGEN = 'ccd_session_mgmt'
|
|
23
|
+
const UMBENENNEN = 'mcp__ccd_session_mgmt__set_session_title'
|
|
24
|
+
const MAX_FRAGEN = 8
|
|
25
|
+
const fertig = atom({ plugin: 'kimal3-logo', key: 'chatBenannt' } as const, false)
|
|
26
|
+
const fragen = atom({ plugin: 'kimal3-logo', key: 'benennenFragen' } as const, 0)
|
|
27
|
+
|
|
28
|
+
// Das Modell antwortet mit einem Titel oder genau diesem Wort.
|
|
29
|
+
const UNKLAR = 'UNKLAR'
|
|
30
|
+
const SYSTEM = `Du gibst einem Chat einen Namen. Du bekommst die letzten Nachrichten des Menschen und Claudes letzte Antwort.
|
|
31
|
+
Ist klar, worum es geht, antworte NUR mit einem Titel aus 2 bis 5 Wörtern in der Sprache des Gesprächs, ohne Anführungszeichen und ohne Punkt am Ende.
|
|
32
|
+
Ist es noch nicht klar — nur eine Begrüßung, nur ein Startbefehl, nur die Frage nach dem Stand —, antworte NUR mit: ${UNKLAR}`
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Die Modellantwort als Titel, oder `null` (B-7). Positivliste: eine Zeile, 2 bis 6 Wörter (ein Wort Spielraum über
|
|
36
|
+
* die erbetenen 5), höchstens 60 Zeichen, ohne das Wort „unklar“ (auch nicht „UNKLAR – nur eine Begrüßung“ oder „Es ist
|
|
37
|
+
* noch unklar“, Befund der Durchsicht 23.10.). Ein vorangestelltes „Titel:“ fällt weg.
|
|
38
|
+
*/
|
|
39
|
+
export function titelAus(antwort: string): string | null {
|
|
40
|
+
const zeile = antwort.trim().replace(/^(titel|title)\s*:\s*/i, '').replace(/^["'„“”«»]+|["'„“”«».]+$/g, '').trim()
|
|
41
|
+
if (zeile.includes('\n') || zeile.length > 60 || /(^|[^\p{L}])(unklar|unclear)($|[^\p{L}])/iu.test(zeile)) return null
|
|
42
|
+
const woerter = zeile.split(/\s+/).length
|
|
43
|
+
return woerter >= 2 && woerter <= 6 ? zeile : null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Was das Modell liest: die Nachrichten des Menschen (ohne die Einschübe von Claude Code und ohne die Markierungen eines
|
|
48
|
+
* Startbefehls wie `/k3start`) und die letzte Antwort, gekürzt.
|
|
49
|
+
* ⚠️ Ungemessen: ob der ausgeklappte Text eines Befehls als eigene Nachricht des Menschen ankommt (Durchsicht 23.10.).
|
|
50
|
+
*/
|
|
51
|
+
export function gespraechFuer(nachrichten: readonly SessionMessage[], antwort: string): string {
|
|
52
|
+
const vomMenschen = nachrichten
|
|
53
|
+
.filter(n => n.role === 'user')
|
|
54
|
+
.map(n =>
|
|
55
|
+
n.text
|
|
56
|
+
.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '')
|
|
57
|
+
.replace(/<(command-[a-z-]+)>[\s\S]*?<\/\1>/g, '')
|
|
58
|
+
.trim(),
|
|
59
|
+
)
|
|
60
|
+
.filter(text => text !== '')
|
|
61
|
+
.slice(-6)
|
|
62
|
+
.map(text => `Mensch: ${text.slice(0, 400)}`)
|
|
63
|
+
return [...vomMenschen, `Claude: ${antwort.slice(0, 1500)}`].join('\n\n')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Die eigene Erlaubnis: nur der Aufruf dieses Mods, nur für den eigenen Chat. Herausgelöst, damit `check:logo` die Grenze prüft. */
|
|
67
|
+
export function istEigenesUmbenennen(plugin: string, input: unknown): boolean {
|
|
68
|
+
return plugin === 'kimal3-logo' && (input as { session_id?: unknown } | null)?.session_id === 'self'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Gegen zwei Antworten, die kurz hintereinander enden, während das Modell noch denkt.
|
|
72
|
+
let laeuft = false
|
|
73
|
+
// Der Erlaubnis-Modus aus der letzten Nachricht des Menschen (B-9). Nach dem Neuladen des Mods leer, bis die nächste kommt.
|
|
74
|
+
let modus: string | undefined
|
|
75
|
+
|
|
76
|
+
async function benennen($: Engine, antwort: string): Promise<void> {
|
|
77
|
+
// Erst sperren, dann lesen: Zwischen Prüfen und Sperren darf kein `await` liegen (Durchsicht 23.10.).
|
|
78
|
+
if (laeuft) return
|
|
79
|
+
laeuft = true
|
|
80
|
+
try {
|
|
81
|
+
if (await read($, fertig)) return
|
|
82
|
+
if ((await read($, fragen)) >= MAX_FRAGEN) return
|
|
83
|
+
await update($, fragen, n => n + 1)
|
|
84
|
+
const gespraech = gespraechFuer(await $.session.messages(), antwort)
|
|
85
|
+
const r = await $.model.complete({ model: 'haiku', system: SYSTEM, prompt: gespraech, maxTokens: 40, effort: 'low', timeoutMs: 20_000 })
|
|
86
|
+
if (!r.isAnswered) {
|
|
87
|
+
$.ui.log(`kimal3-logo: kein Titel vom Modell: ${r.reason}`, { to: 'debug' })
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
const titel = titelAus(r.text)
|
|
91
|
+
// Was das Modell statt eines Titels sagte, steht im Debug-Protokoll: Beim Live-Test am 23.10. wurde die erste
|
|
92
|
+
// Antwort verworfen, und ohne diese Zeile ließ sich nicht mehr sagen, warum.
|
|
93
|
+
if (titel === null) {
|
|
94
|
+
$.ui.log(`kimal3-logo: noch kein Titel, Modell sagte: ${JSON.stringify(r.text.slice(0, 200))}`, { to: 'debug' })
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
// B-3: ab hier zählt es als Versuch, wie auch immer die App antwortet.
|
|
98
|
+
await update($, fertig, () => true)
|
|
99
|
+
const ergebnis = await $.mcp.call(SITZUNGEN, 'set_session_title', { session_id: 'self', title: titel })
|
|
100
|
+
if (ergebnis.isError) $.ui.log(`kimal3-logo: Chat nicht umbenannt: ${JSON.stringify(ergebnis.content).slice(0, 300)}`, { to: 'debug' })
|
|
101
|
+
} catch (fehler) {
|
|
102
|
+
$.ui.log(`kimal3-logo: Chat nicht umbenannt: ${String(fehler).slice(0, 300)}`, { to: 'debug' })
|
|
103
|
+
} finally {
|
|
104
|
+
laeuft = false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const chatBenennen: Register = on => {
|
|
109
|
+
// Die eigene Erlaubnis, eng wie beim Öffnen der Projektseite (hooks/app-oeffnen.ts): Der Aufruf kommt nach einer
|
|
110
|
+
// Antwort, um die niemand gebeten hat — ohne sie lehnt der Auto-Modus ab. Claudes eigene Aufrufe entscheidet die Kette darunter.
|
|
111
|
+
on('tool.check', { tool: UMBENENNEN }, ($, e, next) =>
|
|
112
|
+
istEigenesUmbenennen(next.origin.plugin, e.input)
|
|
113
|
+
? { decision: 'allow', reason: 'kimal3: den eigenen Chat nach seinem Thema benennen' }
|
|
114
|
+
: next(e),
|
|
115
|
+
)
|
|
116
|
+
// B-6: Ein fortgesetzter Chat hat seinen Namen schon.
|
|
117
|
+
on('classic.SessionStart', async ($, e, next) => {
|
|
118
|
+
if (e.source === 'resume' || e.source === 'fork') await update($, fertig, () => true)
|
|
119
|
+
return next(e)
|
|
120
|
+
})
|
|
121
|
+
// B-9: Den Modus trägt nur ein klassisches Ereignis; die Nachricht des Menschen kommt vor jeder Antwort.
|
|
122
|
+
on('classic.UserPromptSubmit', ($, e, next) => {
|
|
123
|
+
modus = e.permission_mode
|
|
124
|
+
return next(e)
|
|
125
|
+
})
|
|
126
|
+
on('turn.complete', async ($, e, next) => {
|
|
127
|
+
const ergebnis = await next(e)
|
|
128
|
+
if (e.agentId !== undefined || e.reason !== 'answer') return ergebnis
|
|
129
|
+
if (modus === undefined || modus === 'bypassPermissions') return ergebnis
|
|
130
|
+
if (!(await $.session.surfaces()).includes('desktop')) return ergebnis
|
|
131
|
+
// Nach der Antwort, nicht in ihr: Das Modell braucht ein, zwei Sekunden, die Antwort soll darauf nicht warten.
|
|
132
|
+
$.clock.after(0, () => void benennen($, e.answer))
|
|
133
|
+
return ergebnis
|
|
134
|
+
})
|
|
135
|
+
}
|
|
@@ -1,84 +1,87 @@
|
|
|
1
|
-
import type { Register } from 'claude-code'
|
|
2
|
-
import { appOeffnen } from './app-oeffnen'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<polygon class="tri
|
|
24
|
-
<polygon class="tri
|
|
25
|
-
|
|
26
|
-
</
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
import type { Register } from 'claude-code'
|
|
2
|
+
import { appOeffnen } from './app-oeffnen'
|
|
3
|
+
import { chatBenennen } from './chat-benennen'
|
|
4
|
+
|
|
5
|
+
// Die Sprache des Kontos. `kimal3 einrichten` setzt diese Zeile beim Ablegen in `~/.kimal3/claude-plugins` und prüft
|
|
6
|
+
// danach, dass sie gesetzt ist (`cli/src/logo-ablegen.ts`).
|
|
7
|
+
const SPRACHE: 'de' | 'en' = 'de'
|
|
8
|
+
|
|
9
|
+
// Icon: docs/design/kimal3-icon-animated.svg, unverändert bis auf die viewBox:
|
|
10
|
+
// Sie schneidet den leeren Rand um die drei Dreiecke weg (Original: 0 0 80 80).
|
|
11
|
+
const ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="20 19 40 36" width="24" height="22">
|
|
12
|
+
<style>
|
|
13
|
+
/* color-scheme: sonst malt der Browser hinter die Grafik eine eigene Fläche, wenn hell/dunkel nicht zur App passt */
|
|
14
|
+
:root { background: transparent; color-scheme: light dark; }
|
|
15
|
+
.icon { opacity: 0.8; }
|
|
16
|
+
.tri { fill: none; stroke: hsl(194 100% 51%); stroke-width: 2.8; stroke-linejoin: round; opacity: 0.7; }
|
|
17
|
+
.t1 { animation: trianglePulse 3s ease-in-out infinite; }
|
|
18
|
+
.t2 { animation: trianglePulse 3.5s ease-in-out infinite; animation-delay: .4s; }
|
|
19
|
+
.t3 { animation: trianglePulse 4s ease-in-out infinite; animation-delay: .8s; }
|
|
20
|
+
@keyframes trianglePulse { 0%,100% { opacity: .5; } 50% { opacity: 1; } }
|
|
21
|
+
</style>
|
|
22
|
+
<g class="icon">
|
|
23
|
+
<polygon class="tri t1" points="33.28,21.6 22.08,41.0 44.48,41.0" />
|
|
24
|
+
<polygon class="tri t2" points="46.72,21.6 35.52,41.0 57.92,41.0" />
|
|
25
|
+
<polygon class="tri t3" points="40.00,33.36 28.80,52.76 51.20,52.76" />
|
|
26
|
+
</g>
|
|
27
|
+
</svg>`
|
|
28
|
+
|
|
29
|
+
// Das Cyan der Marke (kimal3.de, --brand in der App)
|
|
30
|
+
const MARKEN_CYAN = '#00C3FF'
|
|
31
|
+
// Der Kurs, für alle (Dennis, 15.10.: „mach alle auf den Kurs“). Abgemeldete schickt die App von dort zum Login.
|
|
32
|
+
// Fest auf `/firma` zeigte der Link Kostenlosen ohne Firma eine leere Seite (Spec L-51). Im Terminal: Strg+Klick; Claude Code hängt dort die Adresse
|
|
33
|
+
// grau dahinter, wenn es dem Terminal keine Links zutraut — FORCE_HYPERLINK hilft nicht.
|
|
34
|
+
const APP = 'https://app.kimal3.de/kurs'
|
|
35
|
+
|
|
36
|
+
// Vor und nach der Wortmarke. Englisch: „AI Workplace“, nie „workspace“ (Spec, Global Constraints).
|
|
37
|
+
const TEXT = {
|
|
38
|
+
de: { vor: 'Dein ', nach: '-Arbeitsplatz', alt: 'KImal3-Logo' },
|
|
39
|
+
en: { vor: 'Your ', nach: ' AI Workplace', alt: 'KImal3 logo' },
|
|
40
|
+
}[SPRACHE]
|
|
41
|
+
|
|
42
|
+
export const register: Register = (on, options) => {
|
|
43
|
+
// Beim Start die Projektseite in der App öffnen (hooks/app-oeffnen.ts).
|
|
44
|
+
appOeffnen(on, options)
|
|
45
|
+
// Den Chat nach seinem Thema benennen, sobald es klar ist (hooks/chat-benennen.ts).
|
|
46
|
+
chatBenennen(on, options)
|
|
47
|
+
|
|
48
|
+
on('ui.render', { component: 'AbovePrompt' }, ($, e, next) => {
|
|
49
|
+
if (e.props.hasSurvey) {
|
|
50
|
+
return next(e)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Im Terminal gibt es kein Svg: drei Dreiecke als Zeichen, die Wortmarke wie auf kimal3.de
|
|
54
|
+
// („KI" in Schriftfarbe, „mal3" im Marken-Cyan). Das Cyan steht als Farbwert, nicht als „cyan" —
|
|
55
|
+
// das Terminal-Cyan ist je nach Farbschema ein anderes, oft ein blasses Türkis.
|
|
56
|
+
if (e.surface === 'terminal') {
|
|
57
|
+
const { Box, Link, Text } = $.ui.resolve(e)
|
|
58
|
+
return (
|
|
59
|
+
<Box flexDirection="row" gap={1}>
|
|
60
|
+
<Text color={MARKEN_CYAN} bold>{'▲▲▲'}</Text>
|
|
61
|
+
<Text bold>
|
|
62
|
+
{TEXT.vor}
|
|
63
|
+
<Link href={APP}>
|
|
64
|
+
{'KI'}
|
|
65
|
+
<Text color={MARKEN_CYAN}>{'mal3'}</Text>
|
|
66
|
+
{TEXT.nach}
|
|
67
|
+
</Link>
|
|
68
|
+
{'.'}
|
|
69
|
+
</Text>
|
|
70
|
+
</Box>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// isInteractive: erst damit spielt die Leiste die CSS-Animation ab, sonst steht das Icon still.
|
|
75
|
+
const { Box, Link, Svg, Text } = $.ui.resolve(e)
|
|
76
|
+
return (
|
|
77
|
+
<Box flexDirection="row" alignItems="center" gap={1}>
|
|
78
|
+
<Svg source={ICON} alt={TEXT.alt} width={24} height={22} isInteractive />
|
|
79
|
+
<Text bold>
|
|
80
|
+
{TEXT.vor}
|
|
81
|
+
<Link href={APP}>{`KImal3${TEXT.nach}`}</Link>
|
|
82
|
+
{'.'}
|
|
83
|
+
</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
declare module 'claude-code' {
|
|
2
|
-
interface PluginState {
|
|
3
|
-
'kimal3-logo': {
|
|
4
|
-
// Die Projektseite wurde in dieser Sitzung schon geöffnet (hooks/app-oeffnen.ts) — übersteht das Neuladen des Mods.
|
|
5
|
-
appGeoeffnet: boolean
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
declare module 'claude-code' {
|
|
2
|
+
interface PluginState {
|
|
3
|
+
'kimal3-logo': {
|
|
4
|
+
// Die Projektseite wurde in dieser Sitzung schon geöffnet (hooks/app-oeffnen.ts) — übersteht das Neuladen des Mods.
|
|
5
|
+
appGeoeffnet: boolean
|
|
6
|
+
// Der Chat wurde benannt oder es gab schon einen Versuch — danach nie wieder (hooks/chat-benennen.ts, B-3, B-6).
|
|
7
|
+
chatBenannt: boolean
|
|
8
|
+
// Wie oft das Modell in dieser Sitzung schon gefragt wurde, ob das Thema klar ist (höchstens 8, B-8).
|
|
9
|
+
benennenFragen: number
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kimal3",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"kimal3": "dist/kimal3.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"skill",
|
|
12
|
-
"claude-plugins",
|
|
13
|
-
"coach",
|
|
14
|
-
"zeigen",
|
|
15
|
-
"README.md"
|
|
16
|
-
],
|
|
17
|
-
"engines": {
|
|
18
|
-
"node": ">=20"
|
|
19
|
-
},
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"kimal3",
|
|
26
|
-
"cli"
|
|
27
|
-
],
|
|
28
|
-
"homepage": "https://app.kimal3.de/pro",
|
|
29
|
-
"license": "SEE LICENSE IN README.md"
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "kimal3",
|
|
3
|
+
"version": "0.6.4",
|
|
4
|
+
"description": "Trag deine KI-Projekte dort ein, wo sie entstehen: im Terminal. · Log your AI projects where they happen: in the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"kimal3": "dist/kimal3.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"skill",
|
|
12
|
+
"claude-plugins",
|
|
13
|
+
"coach",
|
|
14
|
+
"zeigen",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"kimal3",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://app.kimal3.de/pro",
|
|
29
|
+
"license": "SEE LICENSE IN README.md"
|
|
30
|
+
}
|
package/skill/commands/k3kurs.md
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Zurück in den Mini-Kurs — da weiter, wo du aufgehört hast
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
<!-- kimal3:erzeugt — diese Datei wird von `kimal3 skill install` gepflegt und bei
|
|
6
|
-
jedem Update ueberschrieben. Eigene Aenderungen gehen dabei verloren; wer sie
|
|
7
|
-
behalten will, kopiert die Datei unter einem anderen Namen. -->
|
|
8
|
-
# Kurs
|
|
9
|
-
|
|
10
|
-
> Zurück in den Mini-Kurs: „Wo war ich nochmal?" — macht da weiter, wo du aufgehört hast.
|
|
11
|
-
> Den Stand holt `kimal3 kurs` frisch von kimal3 — dieselben Haken, die der Mensch im Browser unter
|
|
12
|
-
> https://app.kimal3.de/kurs sieht.
|
|
13
|
-
|
|
14
|
-
Führe die Schritte der Reihe nach aus. Bleib im Coaching-Ton: Deutsch, freundlich, ein Schritt
|
|
15
|
-
nach dem anderen, das Warum vor dem Wie, null Vorwissen vorausgesetzt.
|
|
16
|
-
|
|
17
|
-
## 1. Den Stand holen
|
|
18
|
-
- Führ `kimal3 kurs` aus (unter Windows so, wie es `SKILL.md` beschreibt).
|
|
19
|
-
- Rückgabewert **2**: Der Mensch ist nicht angemeldet. Zeig ihm `kimal3 login`, lass ihn selbst im Browser bestätigen
|
|
20
|
-
und nenn **keinen** Kursstand.
|
|
21
|
-
- Rückgabewert **3**: Der Stand ist gerade nicht abrufbar. Sag es in einem Satz und **erzähl den Kurs nicht aus dem
|
|
22
|
-
Gedächtnis**.
|
|
23
|
-
- Kennt das Programm den Befehl nicht, ist es zu alt: Nenn `npm install -g kimal3@latest`, danach `kimal3 skill install`.
|
|
24
|
-
|
|
25
|
-
## 2. Stand in einem Satz sagen
|
|
26
|
-
- Sag in **einem** Satz, wo ihr im Kurs steht — aus der Ausgabe (Zeile `Kurs:`), nichts dazuerfinden. Kein Vortrag.
|
|
27
|
-
|
|
28
|
-
## 3. Als Nächstes führen
|
|
29
|
-
- Die Zeile `Als Nächstes:` nennt den nächsten Schritt. Führ **einen** Schritt nach dem anderen: zeigen, machen lassen,
|
|
30
|
-
verstehen. Nie die ganze Liste auf einmal.
|
|
31
|
-
- Steht dahinter **„(du hakst ab)"** und hat der Mensch den Schritt offensichtlich schon getan (zum Beispiel „Claude ganz
|
|
32
|
-
beendet und neu geöffnet" — er sitzt ja gerade in Claude), führ ihn nicht noch einmal: Frag kurz nach und bitte ihn,
|
|
33
|
-
den Schritt unter https://app.kimal3.de/kurs abzuhaken.
|
|
34
|
-
- Steht dahinter **„(setzt Claude)"** und ist er offen, lass die Einrichtung noch einmal laufen — über die Anleitung
|
|
35
|
-
`https://app.kimal3.de/einrichten` (lade sie wie der Kurs-Satz und folge ihr), **nicht** mit einem frei aufgerufenen
|
|
36
|
-
`kimal3 einrichten`. Bleibt der Schritt danach offen, nenn den Grund aus der Ausgabe von `einrichten` und schlag
|
|
37
|
-
keinen dritten Lauf vor.
|
|
38
|
-
|
|
39
|
-
## 4. Abgeschlossen
|
|
40
|
-
- Steht in der Ausgabe `Kurs: abgeschlossen`, gratuliere kurz und frag mit einem Vorschlag, woran er hier arbeiten
|
|
41
|
-
möchte. Kein Kurs-Gerede mehr.
|
|
1
|
+
---
|
|
2
|
+
description: Zurück in den Mini-Kurs — da weiter, wo du aufgehört hast
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- kimal3:erzeugt — diese Datei wird von `kimal3 skill install` gepflegt und bei
|
|
6
|
+
jedem Update ueberschrieben. Eigene Aenderungen gehen dabei verloren; wer sie
|
|
7
|
+
behalten will, kopiert die Datei unter einem anderen Namen. -->
|
|
8
|
+
# Kurs
|
|
9
|
+
|
|
10
|
+
> Zurück in den Mini-Kurs: „Wo war ich nochmal?" — macht da weiter, wo du aufgehört hast.
|
|
11
|
+
> Den Stand holt `kimal3 kurs` frisch von kimal3 — dieselben Haken, die der Mensch im Browser unter
|
|
12
|
+
> https://app.kimal3.de/kurs sieht.
|
|
13
|
+
|
|
14
|
+
Führe die Schritte der Reihe nach aus. Bleib im Coaching-Ton: Deutsch, freundlich, ein Schritt
|
|
15
|
+
nach dem anderen, das Warum vor dem Wie, null Vorwissen vorausgesetzt.
|
|
16
|
+
|
|
17
|
+
## 1. Den Stand holen
|
|
18
|
+
- Führ `kimal3 kurs` aus (unter Windows so, wie es `SKILL.md` beschreibt).
|
|
19
|
+
- Rückgabewert **2**: Der Mensch ist nicht angemeldet. Zeig ihm `kimal3 login`, lass ihn selbst im Browser bestätigen
|
|
20
|
+
und nenn **keinen** Kursstand.
|
|
21
|
+
- Rückgabewert **3**: Der Stand ist gerade nicht abrufbar. Sag es in einem Satz und **erzähl den Kurs nicht aus dem
|
|
22
|
+
Gedächtnis**.
|
|
23
|
+
- Kennt das Programm den Befehl nicht, ist es zu alt: Nenn `npm install -g kimal3@latest`, danach `kimal3 skill install`.
|
|
24
|
+
|
|
25
|
+
## 2. Stand in einem Satz sagen
|
|
26
|
+
- Sag in **einem** Satz, wo ihr im Kurs steht — aus der Ausgabe (Zeile `Kurs:`), nichts dazuerfinden. Kein Vortrag.
|
|
27
|
+
|
|
28
|
+
## 3. Als Nächstes führen
|
|
29
|
+
- Die Zeile `Als Nächstes:` nennt den nächsten Schritt. Führ **einen** Schritt nach dem anderen: zeigen, machen lassen,
|
|
30
|
+
verstehen. Nie die ganze Liste auf einmal.
|
|
31
|
+
- Steht dahinter **„(du hakst ab)"** und hat der Mensch den Schritt offensichtlich schon getan (zum Beispiel „Claude ganz
|
|
32
|
+
beendet und neu geöffnet" — er sitzt ja gerade in Claude), führ ihn nicht noch einmal: Frag kurz nach und bitte ihn,
|
|
33
|
+
den Schritt unter https://app.kimal3.de/kurs abzuhaken.
|
|
34
|
+
- Steht dahinter **„(setzt Claude)"** und ist er offen, lass die Einrichtung noch einmal laufen — über die Anleitung
|
|
35
|
+
`https://app.kimal3.de/einrichten` (lade sie wie der Kurs-Satz und folge ihr), **nicht** mit einem frei aufgerufenen
|
|
36
|
+
`kimal3 einrichten`. Bleibt der Schritt danach offen, nenn den Grund aus der Ausgabe von `einrichten` und schlag
|
|
37
|
+
keinen dritten Lauf vor.
|
|
38
|
+
|
|
39
|
+
## 4. Abgeschlossen
|
|
40
|
+
- Steht in der Ausgabe `Kurs: abgeschlossen`, gratuliere kurz und frag mit einem Vorschlag, woran er hier arbeiten
|
|
41
|
+
möchte. Kein Kurs-Gerede mehr.
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Back into the Mini course — pick up where you left off
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
<!-- kimal3:erzeugt — this file is maintained by `kimal3 skill install` and
|
|
6
|
-
overwritten on every update. Your own changes will be lost; if you want
|
|
7
|
-
to keep them, copy the file under a different name. -->
|
|
8
|
-
# Course
|
|
9
|
-
|
|
10
|
-
> Back into the Mini course: “Where was I again?” — picks up where you left off.
|
|
11
|
-
> `kimal3 course` fetches the status fresh from kimal3 — the same ticks the person sees in the browser at
|
|
12
|
-
> https://app.kimal3.de/kurs.
|
|
13
|
-
|
|
14
|
-
Carry out the steps in order. Stay in the coaching tone: English, friendly, one step at a
|
|
15
|
-
time, the why before the how, no prior knowledge assumed.
|
|
16
|
-
|
|
17
|
-
## 1. Fetch the status
|
|
18
|
-
- Run `kimal3 course` (on Windows the way `SKILL.md` describes).
|
|
19
|
-
- Exit code **2**: the person is not signed in. Show them `kimal3 login`, let them confirm in the browser themselves
|
|
20
|
-
and name **no** course status.
|
|
21
|
-
- Exit code **3**: the status can't be fetched right now. Say so in one sentence and **do not retell the course from
|
|
22
|
-
memory**.
|
|
23
|
-
- If the program doesn't know the command, it is too old: name `npm install -g kimal3@latest`, then `kimal3 skill install`.
|
|
24
|
-
|
|
25
|
-
## 2. Say where you stand in one sentence
|
|
26
|
-
- Say in **one** sentence where you are in the course — from the output (the `Course:` line), invent nothing. No lecture.
|
|
27
|
-
|
|
28
|
-
## 3. Lead on with what comes next
|
|
29
|
-
- The line `Next:` names the next step. Lead on **one** step at a time: show, let them do it, make sure they understand.
|
|
30
|
-
Never the whole list at once.
|
|
31
|
-
- If it says **“(you tick it off)”** after it and the person has obviously already done the step (for example “closed
|
|
32
|
-
Claude completely and reopened it” — they are sitting in Claude right now), don't lead it again: ask briefly and
|
|
33
|
-
ask them to tick it off at https://app.kimal3.de/kurs.
|
|
34
|
-
- If it says **“(Claude sets it)”** and it is open, run the setup once more — through the guide
|
|
35
|
-
`https://app.kimal3.de/setup` (load it the way the course sentence does and follow it), **not** with a freely called
|
|
36
|
-
`kimal3 setup`. If the step is still open afterwards, name the reason from the output of `setup` and don't suggest
|
|
37
|
-
a third run.
|
|
38
|
-
|
|
39
|
-
## 4. Finished
|
|
40
|
-
- If the output says `Course: complete`, congratulate them briefly and ask, with a suggestion, what they would like to
|
|
41
|
-
work on here. No more course talk.
|
|
1
|
+
---
|
|
2
|
+
description: Back into the Mini course — pick up where you left off
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<!-- kimal3:erzeugt — this file is maintained by `kimal3 skill install` and
|
|
6
|
+
overwritten on every update. Your own changes will be lost; if you want
|
|
7
|
+
to keep them, copy the file under a different name. -->
|
|
8
|
+
# Course
|
|
9
|
+
|
|
10
|
+
> Back into the Mini course: “Where was I again?” — picks up where you left off.
|
|
11
|
+
> `kimal3 course` fetches the status fresh from kimal3 — the same ticks the person sees in the browser at
|
|
12
|
+
> https://app.kimal3.de/kurs.
|
|
13
|
+
|
|
14
|
+
Carry out the steps in order. Stay in the coaching tone: English, friendly, one step at a
|
|
15
|
+
time, the why before the how, no prior knowledge assumed.
|
|
16
|
+
|
|
17
|
+
## 1. Fetch the status
|
|
18
|
+
- Run `kimal3 course` (on Windows the way `SKILL.md` describes).
|
|
19
|
+
- Exit code **2**: the person is not signed in. Show them `kimal3 login`, let them confirm in the browser themselves
|
|
20
|
+
and name **no** course status.
|
|
21
|
+
- Exit code **3**: the status can't be fetched right now. Say so in one sentence and **do not retell the course from
|
|
22
|
+
memory**.
|
|
23
|
+
- If the program doesn't know the command, it is too old: name `npm install -g kimal3@latest`, then `kimal3 skill install`.
|
|
24
|
+
|
|
25
|
+
## 2. Say where you stand in one sentence
|
|
26
|
+
- Say in **one** sentence where you are in the course — from the output (the `Course:` line), invent nothing. No lecture.
|
|
27
|
+
|
|
28
|
+
## 3. Lead on with what comes next
|
|
29
|
+
- The line `Next:` names the next step. Lead on **one** step at a time: show, let them do it, make sure they understand.
|
|
30
|
+
Never the whole list at once.
|
|
31
|
+
- If it says **“(you tick it off)”** after it and the person has obviously already done the step (for example “closed
|
|
32
|
+
Claude completely and reopened it” — they are sitting in Claude right now), don't lead it again: ask briefly and
|
|
33
|
+
ask them to tick it off at https://app.kimal3.de/kurs.
|
|
34
|
+
- If it says **“(Claude sets it)”** and it is open, run the setup once more — through the guide
|
|
35
|
+
`https://app.kimal3.de/setup` (load it the way the course sentence does and follow it), **not** with a freely called
|
|
36
|
+
`kimal3 setup`. If the step is still open afterwards, name the reason from the output of `setup` and don't suggest
|
|
37
|
+
a third run.
|
|
38
|
+
|
|
39
|
+
## 4. Finished
|
|
40
|
+
- If the output says `Course: complete`, congratulate them briefly and ask, with a suggestion, what they would like to
|
|
41
|
+
work on here. No more course talk.
|