kimal3 0.6.3 → 0.6.5

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,11 +1,12 @@
1
1
  import { cpSync, existsSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { basename, dirname, join } from "node:path";
2
+ import { homedir, platform } from "node:os";
3
+ import { basename, dirname, join, posix, win32 } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { EINRICHTEN_TEXTE } from "./texte/einrichten.js";
6
+ import { eigeneVersion } from "./version.js";
6
7
  /**
7
8
  * Legt den Plugin-Katalog mit dem Logo-Mod in `~/.kimal3/claude-plugins` ab und setzt dort die
8
- * Sprache des Kontos. Claude Code liest den Mod aus genau diesem Ordner (Katalog `kimal3`,
9
+ * Sprache des Kontos und die Ablage (`mitAblage`). Claude Code liest den Mod aus genau diesem Ordner (Katalog `kimal3`,
9
10
  * `plugins-mischen.ts`).
10
11
  *
11
12
  * 🔑 Warum ein fester Ordner im Benutzerordner und nicht der Ordner im installierten npm-Paket:
@@ -24,10 +25,64 @@ export function mitSprache(quelltext, sprache) {
24
25
  return null;
25
26
  return quelltext.replace(SPRACH_ZEILE, `const SPRACHE: 'de' | 'en' = '${sprache}'`); // kein-anzeigetext: Quelltext des Mods
26
27
  }
28
+ /** Die Zeile im Mod, die beim Ablegen die Ablage bekommt; im Paket steht dort die leere. */
29
+ export const ABLAGE_ZEILE = "const ABLAGE: Ablage = KEINE_ABLAGE"; // kein-anzeigetext: Quelltext des Mods
30
+ /**
31
+ * Reine Rechnung: der Mod-Text mit der Ablage, oder `null`, wenn die Zeile nicht genau einmal dasteht. Ohne Ablage
32
+ * (keine eigene Fassung) bleibt die leere Zeile stehen -- der Mod zeigt dann wie bisher nur das Logo (U-9).
33
+ * 🔴 `split`/`join`, nie `replace(zeile, text)`: Im Ersatz-Text eines `replace` sind `$&` und `$'` Befehle, und ein
34
+ * Ordnername darf beides enthalten -- dann stünde fremder Quelltext im Mod (messen-und-pruefen §23, U-16). `JSON.stringify`
35
+ * macht aus jedem Wert ein gültiges Stück Quelltext, auch mit `"` und `\` (Windows-Pfade).
36
+ */
37
+ export function mitAblage(quelltext, ablage) {
38
+ const teile = quelltext.split(ABLAGE_ZEILE);
39
+ if (teile.length !== 2)
40
+ return null;
41
+ return ablage === null ? quelltext : teile.join(`const ABLAGE: Ablage = ${JSON.stringify(ablage)}`); // kein-anzeigetext: Quelltext des Mods
42
+ }
43
+ /**
44
+ * Reine Rechnung hinter `ablageWerte`. Ohne eigene Fassung keine Ablage (`null`).
45
+ * `npm` ist das `npm-cli.js`, das zu DIESEM Node gehört (Windows daneben, sonst unter `lib/`), und nur dann gesetzt, wenn
46
+ * es die Datei gibt und `kimal3` global installiert läuft. Sonst `""`: Der Knopf bittet dann Claude (U-10), statt ein
47
+ * `npm install -g` anzustoßen, nach dem `kimal3 --version` doch die alte Zahl sagte.
48
+ * 🔑 Global heißt, als Erlaubnisliste (messen-und-pruefen §23): `…/node_modules/kimal3/…` mit genau EINEM `node_modules`,
49
+ * ohne `_npx` (der Zwischenspeicher von `npx`), und über dem `node_modules` kein `package.json` (sonst ist es der Ordner
50
+ * eines Projekts). Ein Start aus dem Repo (`cli/dist`) ist damit auch nicht global.
51
+ */
52
+ export function ablageAus(lage) {
53
+ if (lage.fassung === null)
54
+ return null;
55
+ const p = lage.system === "win32" ? win32 : posix;
56
+ const npm = lage.system === "win32"
57
+ ? p.join(p.dirname(lage.node), "node_modules", "npm", "bin", "npm-cli.js") // kein-anzeigetext: Pfad
58
+ : p.join(p.dirname(p.dirname(lage.node)), "lib", "node_modules", "npm", "bin", "npm-cli.js"); // kein-anzeigetext: Pfad
59
+ const teile = lage.kimal3.split(/[\\/]/);
60
+ const i = teile.indexOf("node_modules");
61
+ const global = i > 0 && teile.lastIndexOf("node_modules") === i && teile[i + 1] === "kimal3" && !teile.includes("_npx")
62
+ && !lage.gibtEs(p.join(teile.slice(0, i).join(p.sep), "package.json"));
63
+ return {
64
+ fassung: lage.fassung,
65
+ node: lage.node,
66
+ npm: global && lage.gibtEs(npm) ? npm : "",
67
+ kimal3: lage.kimal3,
68
+ kimal3Ordner: p.join(lage.heim, ".kimal3"), // kein-anzeigetext: Ordnername
69
+ };
70
+ }
71
+ /** Die Ablage dieses Rechners, im laufenden `kimal3` gemessen (Bauplan 2026-10-23, Tabelle in 1.1). */
72
+ export function ablageWerte() {
73
+ return ablageAus({
74
+ fassung: eigeneVersion(),
75
+ node: process.execPath,
76
+ kimal3: fileURLToPath(new URL("./kimal3.js", import.meta.url)), // kein-anzeigetext: Dateiname
77
+ heim: homedir(),
78
+ system: platform(),
79
+ gibtEs: existsSync,
80
+ });
81
+ }
27
82
  /**
28
83
  * 🔴 NIE EIN HALBER ORDNER (Durchsicht-Funde 2 und 9): Claudes Einstellungen zeigen auf `katalogOrdner()`,
29
84
  * und eine laufende Claude-Sitzung hat den Mod daraus geladen. Deshalb:
30
- * 1. Sprache setzen, BEVOR irgendetwas angefasst wird -- fehlt die Zeile, bleibt alles, wie es war.
85
+ * 1. Sprache und Ablage setzen, BEVOR irgendetwas angefasst wird -- fehlt eine der Zeilen, bleibt alles, wie es war.
31
86
  * 2. Steht schon genau das da, was entstehen soll: nichts anfassen (Lage P-8). Das ist auch der Normalfall
32
87
  * eines zweiten Laufs, und so kommt er gar nicht erst in die Nähe der Windows-Sperren unten.
33
88
  * 3. Den neuen Ordner daneben fertig bauen (`….neu`), dann tauschen: alten Ordner wegschieben, neuen an
@@ -46,7 +101,8 @@ export function logoAblegen(sprache) {
46
101
  const neu = `${ziel}.neu`; // kein-anzeigetext: Ordnername
47
102
  const modDatei = (ordner) => join(ordner, "kimal3-logo", "hooks", "register.tsx");
48
103
  try {
49
- const text = mitSprache(readFileSync(modDatei(quelle), "utf8"), sprache);
104
+ const mitSpr = mitSprache(readFileSync(modDatei(quelle), "utf8"), sprache);
105
+ const text = mitSpr === null ? null : mitAblage(mitSpr, ablageWerte());
50
106
  if (text === null)
51
107
  return { ok: false, detail: modDatei(quelle) };
52
108
  if (stehtSchonDa(quelle, ziel, text)) {
@@ -102,7 +158,7 @@ export function frischeLogoAuf(s) {
102
158
  else if (ablage.stand === "alt")
103
159
  console.log(t.logoNotRefreshed);
104
160
  }
105
- /** Gleiche Dateien, gleicher Inhalt -- der Mod mit der Sprache des Kontos, alles andere wie im Paket. */
161
+ /** Gleiche Dateien, gleicher Inhalt -- der Mod mit Sprache und Ablage, alles andere wie im Paket. */
106
162
  function stehtSchonDa(quelle, ziel, modText) {
107
163
  if (!existsSync(ziel))
108
164
  return false;
@@ -2,8 +2,8 @@ const de = {
2
2
  accessExpired: (login) => "🔴🔴🔴 kimal3: Dein Zugang ist abgelaufen — zum Beispiel nach einem neuen Passwort " +
3
3
  `oder einer längeren Pause. Melde dich neu an mit: ${login} 🔴🔴🔴`,
4
4
  messageFromKimal3: (text) => `Nachricht von kimal3: „${text}“`,
5
- newVersion: (neu, eigene) => `🟡🟡🟡 kimal3: Es gibt eine neuere Version: ${neu} (du hast ${eigene}). ` +
6
- `Holen mit: ! npm install -g kimal3@${neu} 🟡🟡🟡`,
5
+ newVersion: (neu, eigene, skill) => `🟡🟡🟡 kimal3: Es gibt eine neuere Version: ${neu} (du hast ${eigene}). ` +
6
+ `Holen mit: ! npm install -g kimal3@${neu} — danach: ${skill} 🟡🟡🟡`,
7
7
  skillsOld: (befehl) => "🟡🟡🟡 kimal3: Deine /k3-Befehle in Claude sind veraltet oder in einer anderen Sprache als dein Konto. " +
8
8
  `Auffrischen: ${befehl} 🟡🟡🟡`,
9
9
  instructionToClaude: "Anweisung an Claude: Gib die Zeilen oben dem Menschen als ERSTE Zeilen deiner nächsten " +
@@ -20,8 +20,8 @@ const en = {
20
20
  accessExpired: (login) => "🔴🔴🔴 kimal3: Your sign-in has expired, for example after a new password " +
21
21
  `or a long break. Sign in again with: ${login} 🔴🔴🔴`,
22
22
  messageFromKimal3: (text) => `Message from kimal3: “${text}”`,
23
- newVersion: (neu, eigene) => `🟡🟡🟡 kimal3: A newer version is available: ${neu} (you have ${eigene}). ` +
24
- `Install it with: ! npm install -g kimal3@${neu} 🟡🟡🟡`,
23
+ newVersion: (neu, eigene, skill) => `🟡🟡🟡 kimal3: A newer version is available: ${neu} (you have ${eigene}). ` +
24
+ `Install it with: ! npm install -g kimal3@${neu} — then: ${skill} 🟡🟡🟡`,
25
25
  skillsOld: (befehl) => "🟡🟡🟡 kimal3: Your /k3 commands in Claude are out of date or in a different language from your account. " +
26
26
  `Update them: ${befehl} 🟡🟡🟡`,
27
27
  instructionToClaude: "Instruction to Claude: Show the lines above to the person verbatim, in bold, as the FIRST lines of " +
@@ -49,7 +49,9 @@ export function wacheBefunde(lage, s) {
49
49
  istAelter(lage.eigeneVersion, lage.neuesteVersion)) {
50
50
  befunde.push({
51
51
  art: "version",
52
- zeile: T.newVersion(lage.neuesteVersion, lage.eigeneVersion),
52
+ // Zwei Befehle GETRENNT, nie mit `&&` verbunden: Die Windows-PowerShell 5.1 kennt `&&` nicht, und
53
+ // ohne `skill install` bleiben Claudes /k3-Befehle auf der alten Fassung (Spec Update in der Leiste, E-4).
54
+ zeile: T.newVersion(lage.neuesteVersion, lage.eigeneVersion, tippen("skill install", s)), // kein-anzeigetext: Befehlsname
53
55
  });
54
56
  }
55
57
  if (lage.skillVeraltet) {
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
- {
2
- "name": "kimal3",
3
- "version": "0.6.3",
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.5",
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,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.