kimal3 0.4.0 → 0.5.0
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/README.md +4 -0
- package/coach/de/kimal3-coach.md +2 -2
- package/coach/en/kimal3-coach.md +2 -2
- package/dist/befehle/einrichten.js +1 -1
- package/dist/befehle/login.js +6 -2
- package/dist/befehle/skill-install.js +4 -1
- package/dist/befehle/zeigen-einrichten.js +104 -0
- package/dist/befehle/zeigen-start.js +143 -0
- package/dist/befehle/zeigen.js +288 -0
- package/dist/befehle-katalog-en.js +27 -0
- package/dist/befehle-katalog.js +27 -0
- package/dist/befehlswoerter.js +11 -0
- package/dist/kimal3.js +10 -0
- package/dist/logo-ablegen.js +26 -6
- package/dist/slash-befehle.js +1 -0
- package/dist/texte/einrichten.js +10 -3
- package/dist/texte/zeigen.js +189 -0
- package/dist/zeigen-rechnung.js +226 -0
- package/dist/zeigen-umgebung.js +190 -0
- package/package.json +2 -1
- package/skill/SKILL.md +3 -2
- package/skill/commands/k3.md +4 -3
- package/skill/commands/k3kurs.md +41 -41
- package/skill/commands/k3start.md +1 -1
- package/skill/commands/k3zeigen.md +303 -0
- package/skill/en/SKILL.md +3 -2
- package/skill/en/commands/k3.md +4 -3
- package/skill/en/commands/k3course.md +41 -41
- package/skill/en/commands/k3show.md +302 -0
- package/skill/en/commands/k3start.md +1 -1
- package/zeigen/gemeinsam.py +82 -0
- package/zeigen/starter.py +163 -0
- package/zeigen/werkzeug.py +250 -0
- package/zeigen/zeitleiste.py +393 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show a task instead of describing it — I record it and turn it into a plan
|
|
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
|
+
# Show
|
|
9
|
+
|
|
10
|
+
> You do a task once on screen and say out loud what you are doing and why.
|
|
11
|
+
> I record it, evaluate it on your computer and write a description of it into the
|
|
12
|
+
> project — so that we can automate the task afterwards without you first having to write
|
|
13
|
+
> it down as a requirement.
|
|
14
|
+
|
|
15
|
+
Carry out the steps in order. Stay in the coaching tone: English, friendly, **one question at a
|
|
16
|
+
time and always with a suggestion**, the why before the how, no prior knowledge assumed. The
|
|
17
|
+
mechanics are done by the program (`kimal3 show …`); you lead the conversation with the person. How
|
|
18
|
+
to deal with `kimal3`'s exit codes in general is in `~/.claude/skills/kimal3/SKILL.md`; here are only
|
|
19
|
+
the cases of this command.
|
|
20
|
+
|
|
21
|
+
## 🔴 Three rules for the whole conversation
|
|
22
|
+
|
|
23
|
+
1. **Before the yes in step 10 you read nothing from the recording.** No `zeitleiste.md`, no
|
|
24
|
+
picture, no log, no other file from `material/aufnahmen/` — and you don't **search** there
|
|
25
|
+
either: no grep, no glob, no file search across the project that includes this folder. What
|
|
26
|
+
lies there only leaves the computer with the person's yes. You may read the output of
|
|
27
|
+
`kimal3 show …` — it contains only numbers, times and folder names.
|
|
28
|
+
2. **Install nothing, record nothing, delete nothing without an explicit yes to exactly that
|
|
29
|
+
step.** You open a file from the recording for the person only in Notepad, and in a way that
|
|
30
|
+
your call returns at once: in Bash `cmd //c start "" notepad "<path>"`, in PowerShell
|
|
31
|
+
`Start-Process notepad -ArgumentList '"<path>"'`. A bare `notepad "<path>"` blocks until the
|
|
32
|
+
window is closed again. Never in an IDE or an editor that Claude Code is attached to (selected
|
|
33
|
+
lines end up in the conversation there by themselves).
|
|
34
|
+
3. **A failure never looks like a success.** If a `kimal3 show …` doesn't end with `0`, say
|
|
35
|
+
honestly what its message says, and don't carry on as if it had worked.
|
|
36
|
+
|
|
37
|
+
Every `kimal3 show …` runs **in the project folder** (step 3) — the program finds the recording
|
|
38
|
+
through the folder it is started in. If a recording lies in another project, change into it first:
|
|
39
|
+
the project folder is the part of the path before `material/aufnahmen/`.
|
|
40
|
+
|
|
41
|
+
## 1. Arrive and look
|
|
42
|
+
|
|
43
|
+
First run `kimal3 show status`. It only reads. **Don't** read the “Set up:” block out to the
|
|
44
|
+
person — just note whether anything in it is missing (a line with “missing”, “can't be checked”,
|
|
45
|
+
“I can't tell” or “(… is needed)” → step 4).
|
|
46
|
+
|
|
47
|
+
| The output says | What you do |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `kimal3 show` is Windows-only for now; the Mac follows. | Say exactly that, kindly: showing a task doesn't work on this computer yet, the Mac is coming. Offer that the person describes the task in words instead. The command ends here. |
|
|
50
|
+
| “I can't tell whether a recording is running.” (exit code 1) | Say honestly that you can't tell right now. **Start nothing.** Offer to look again in a minute. |
|
|
51
|
+
| “Running since 09:12 (3 minutes), folder `…`, stops at 09:27 at the latest.” | A recording from an earlier session is still running. If the folder is a full path outside this project, first change into its project folder. Then ask: “There's still a recording running from 09:12 — are you through with showing the task? Then I'll stop it now.” Yes → step 8. |
|
|
52
|
+
| “Running (started in another session).” | A recording is running that this project can't match. Say so, **don't start a second one**, and suggest checking with `kimal3 show status` once it has ended. |
|
|
53
|
+
| “No recording is running.” followed by “The last one (`…`) stopped by itself after 15 minutes.” | A recording is finished but probably not evaluated yet. **Don't silently start a new one over it.** Ask with a suggestion: “The recording from 09:12 stopped by itself after 15 minutes — shall I evaluate it, or would you rather show the task again? I'd evaluate it; whatever you did after the 15 minutes is then missing.” Evaluate → step 9 (the time is in the folder name, `…-0912`). Again → carry on here. |
|
|
54
|
+
| “No recording is running.” followed by “The last one (`…`) broke off.” | Kindly: the last recording broke off in the middle — the person did nothing wrong. Offer to show the task again. |
|
|
55
|
+
| “No recording is running.” | Carry on. |
|
|
56
|
+
|
|
57
|
+
The last line says whether the folder is right — remember it for step 3:
|
|
58
|
+
“Recordings go here: …” (right) · “Switch into a project first …” (the session is in the
|
|
59
|
+
workbench) · “The project folder is nested too deeply for the recorder …” (the path is too long).
|
|
60
|
+
|
|
61
|
+
If the session is in the workbench, say **that first**, in one sentence: the recording belongs in
|
|
62
|
+
a project, and you'll suggest one in a moment. Then explain in at most five short sentences what
|
|
63
|
+
happens, and ask **one** question:
|
|
64
|
+
|
|
65
|
+
> “You show me the task once and talk while you do it. I record the screen, sound, clicks and
|
|
66
|
+
> keys. It stays on your computer. Before I read any of it, I'll ask you. Which task do you want
|
|
67
|
+
> to show me — in one sentence?”
|
|
68
|
+
|
|
69
|
+
Until the person has answered, nothing is installed and nothing is recorded.
|
|
70
|
+
|
|
71
|
+
## 2. The task in one sentence
|
|
72
|
+
|
|
73
|
+
Wait for the answer. You take the person's sentence **unchanged** — it will stand at the top of
|
|
74
|
+
the timeline, and exactly where it and the demonstration differ lies the most important follow-up
|
|
75
|
+
question (step 11). A rough sentence is enough; don't dig.
|
|
76
|
+
|
|
77
|
+
## 3. The right folder
|
|
78
|
+
|
|
79
|
+
- **Recordings already go to the right place here?** Carry on with step 4.
|
|
80
|
+
- **The session is in the workbench** (`workbench-…`): suggest a project with a short name from the
|
|
81
|
+
task — lower case, hyphens: “I'd create the project `projects/quote-list` for this — does that
|
|
82
|
+
work?” (if the folder in the workbench is called `projekte/`, then there). Only after the yes do
|
|
83
|
+
you create **only** `plan/` and `material/` in it, nothing in advance. From now on every
|
|
84
|
+
`kimal3 show …` runs in this project folder (e.g. `cd "<project folder>" && kimal3 show status`).
|
|
85
|
+
Check with `kimal3 show status` that the last line now says “Recordings go here: …”.
|
|
86
|
+
- **The path is too deep:** name the number from the message and why (the recorder fails on long
|
|
87
|
+
paths). Suggest creating the project closer to the drive, and ask before you create anything.
|
|
88
|
+
|
|
89
|
+
## 4. Setting up — only if something is missing
|
|
90
|
+
|
|
91
|
+
If nothing is missing, skip this step without a word about it. If something is missing, say
|
|
92
|
+
**exactly this first** and wait for the answer:
|
|
93
|
+
|
|
94
|
+
> “For this I'll set up the recorder once — a free program that records screen, sound, clicks
|
|
95
|
+
> and keys. About 2.1 GB, 10 to 20 minutes depending on your connection, only in your user
|
|
96
|
+
> folder, no admin rights. Because it records keystrokes, your company's antivirus may flag it;
|
|
97
|
+
> on a work computer, check with IT first if in doubt. Shall I?”
|
|
98
|
+
|
|
99
|
+
- **Yes:** start `kimal3 show setup` **in the background** — it can take longer than the ten
|
|
100
|
+
minutes a command may run in the foreground. Say that it is running and the person can do
|
|
101
|
+
something else meanwhile. **Wait for the notice that it has finished** — don't check in between,
|
|
102
|
+
and don't sleep in a loop. After that:
|
|
103
|
+
- exit code `0` → `kimal3 show status`; if all four parts are there (uv, recorder, FFmpeg,
|
|
104
|
+
speech model), carry on with step 5.
|
|
105
|
+
- exit code `1` (“Step … didn't work.”) → name that step, and offer a ready-made
|
|
106
|
+
message to IT: **what** (uv from Astral · the recorder openadapt-capture 1.3.0, MIT licence,
|
|
107
|
+
with Python 3.12 · FFmpeg · the speech model large-v3-turbo from Hugging Face), **what for**
|
|
108
|
+
(showing a work task once on screen and evaluating it on the computer), **where from**
|
|
109
|
+
(astral.sh or winget, pypi.org, huggingface.co), **which folders** (only the user folder, no
|
|
110
|
+
admin rights).
|
|
111
|
+
- **No:** “No problem — you can do it any time later with `/k3show`.” The command ends here.
|
|
112
|
+
|
|
113
|
+
## 5. The briefing before the start
|
|
114
|
+
|
|
115
|
+
Say these four points before anything records:
|
|
116
|
+
|
|
117
|
+
1. **All screens** are recorded. Close anything that shows other people's data, switch on “Do
|
|
118
|
+
not disturb”, and not during a call. If you can, use a **test customer** instead of a real
|
|
119
|
+
one.
|
|
120
|
+
2. **Don't type a password** — every key is recorded.
|
|
121
|
+
3. Say out loud **why** you do something.
|
|
122
|
+
4. When you're done, come back here and type **“done”**; the recording stops by itself after 15
|
|
123
|
+
minutes at the latest.
|
|
124
|
+
|
|
125
|
+
Then: “Type ‘go’ when you're ready.” — and wait.
|
|
126
|
+
|
|
127
|
+
- **A clear go** — “go”, “ready”, “start” — → step 6.
|
|
128
|
+
- **A bare “ok” or “yes”** → ask once: “Shall I start now?”
|
|
129
|
+
- **Anything else** → answer and say that it starts with “go”.
|
|
130
|
+
|
|
131
|
+
**Nothing starts before the go.**
|
|
132
|
+
|
|
133
|
+
## 6. Starting
|
|
134
|
+
|
|
135
|
+
After the go: `kimal3 show start '<the person's sentence>'`. Pass the sentence as **one** argument,
|
|
136
|
+
so that the shell changes nothing in it — in Bash in single quotes (a `'` inside as `'\''`), in
|
|
137
|
+
PowerShell in single quotes (a `'` inside doubled). `--at-most` (1 to 15 minutes) only if the person
|
|
138
|
+
explicitly wants it shorter.
|
|
139
|
+
|
|
140
|
+
| The output says | What you do |
|
|
141
|
+
|---|---|
|
|
142
|
+
| “The recording is running (folder …), stops at … at the latest.” (exit code 0) | Say exactly: “Running. Show me — and type ‘done’ here when you're through.” **Then end your turn** — no further command, no checking. |
|
|
143
|
+
| “A recording is already running (since 09:12, folder `…`).” · “A recording is already starting …” | No second one is created. Ask: “There's still a recording running from 09:12 — shall I stop it so that we can start again?” Yes → `kimal3 show stop` (step 8), then back to step 5. |
|
|
144
|
+
| “A recording is already running (started in another session).” | Ask: “There's still a recording running from another session — shall I stop it so that we can start again?” Yes → `kimal3 show stop`; if it says it can't match it here, say so honestly and suggest waiting until it stops by itself (`kimal3 show status`). |
|
|
145
|
+
| “I can't tell whether a recording is running — so I won't start a second one.” | Say it honestly, force nothing, look again later. |
|
|
146
|
+
| “The recorder isn't set up yet: `kimal3 show setup`.” | Back to step 4. |
|
|
147
|
+
| “The recording didn't start: <reason>. Log: …” | Name the reason. **Don't open the log** — it contains window titles. If “Whether the recorder is still running: `kimal3 show status`.” follows, run `kimal3 show status` and say what it reports. Offer another attempt. |
|
|
148
|
+
| “Switch into a project first …” · “The project folder is nested too deeply …” | Back to step 3. |
|
|
149
|
+
|
|
150
|
+
## 7. While recording
|
|
151
|
+
|
|
152
|
+
If the person writes anything other than “done” (a question, a thought), answer briefly and end
|
|
153
|
+
with: “The recording is still running — type ‘done’ when you're through.” Open nothing on the
|
|
154
|
+
screen meanwhile (it would end up in the video) and don't stop the recording on your own.
|
|
155
|
+
“done”, “finished”, “I'm through” and the like → step 8.
|
|
156
|
+
|
|
157
|
+
## 8. Stopping
|
|
158
|
+
|
|
159
|
+
First say: “I'm finishing the recording — that can take a minute, with a long recording several.”
|
|
160
|
+
Otherwise the person sees nothing happen while it finishes.
|
|
161
|
+
|
|
162
|
+
Then `kimal3 show stop` — with the **longest wait the tool allows (10 minutes)**: the recorder finishes
|
|
163
|
+
the video, and with a long recording that takes a while; with the default of 2 minutes the call
|
|
164
|
+
would break off in the middle. The program answers after a little over 8 minutes at the latest, so
|
|
165
|
+
before the 10 minutes are up.
|
|
166
|
+
|
|
167
|
+
| The output says | What you do |
|
|
168
|
+
|---|---|
|
|
169
|
+
| “Recording stopped: 1:33 minutes, 30 clicks, 49 keys, audio present.” | Repeat the numbers in one sentence, carry on with step 9. |
|
|
170
|
+
| “The recording is still being finished — run `kimal3 show stop` again in a moment.” (exit code 1) | Say: “The recorder is still finishing the recording — I'll check again in a moment.” Then run `kimal3 show stop` again, again with the 10-minute wait. **Never** say “not closed cleanly”, and don't offer a new recording. |
|
|
171
|
+
| “The recording stopped by itself after 15 minutes: …” (exit code 0) | Say: “The recording stopped by itself after 15 minutes — whatever you did after that isn't in it.” Carry on with step 9. |
|
|
172
|
+
| “No recording is running.” | Check with `kimal3 show status`. If it says “The last one (`…`) stopped by itself after … minutes.”, carry on with step 9; otherwise say honestly that there is no recording. |
|
|
173
|
+
| “I can't tell whether a recording is running.” | Say it honestly — **never** “stopped”. Offer to look again in a minute with `kimal3 show status`. |
|
|
174
|
+
| “The recording wasn't closed cleanly: <reason>.” (exit code 1) | Name the reason and offer a new recording. |
|
|
175
|
+
| “The recording broke off: <reason>. …” (exit code 1) | Kindly: the person did nothing wrong. Name the reason and offer a new recording. |
|
|
176
|
+
| “The recording couldn't be stopped …” · “A recording is running that I can't match here …” | Run `kimal3 show status` and say honestly what it reports — **never** “stopped”. If the session is in the wrong folder, change into the recording's project. |
|
|
177
|
+
|
|
178
|
+
## 9. Evaluating — on the person's computer
|
|
179
|
+
|
|
180
|
+
`kimal3 show evaluate` (without a folder it takes the project's latest recording; the language of
|
|
181
|
+
the speech recognition follows the account — `--language de` or `--language en` only if the person
|
|
182
|
+
spoke the other language). This takes about as long as the recording:
|
|
183
|
+
|
|
184
|
+
- **Recording up to about 3 minutes:** in the foreground, with the longest wait (10 minutes).
|
|
185
|
+
- **Longer — and always when it stopped by itself after 15 minutes:** **in the background**, as
|
|
186
|
+
with the setup. Say that it takes about as long as the recording, and **wait for the notice that
|
|
187
|
+
it has finished** — no checking in between, no loop.
|
|
188
|
+
|
|
189
|
+
| The output says | What you do |
|
|
190
|
+
|---|---|
|
|
191
|
+
| “Timeline ready: … File: `…`.” | Say the numbers in one sentence. Remember the path — **don't open the file**. Carry on with step 10. |
|
|
192
|
+
| “I didn't recognise any speech — was the microphone muted or the wrong one selected?” | Ask exactly that and offer a new recording. |
|
|
193
|
+
| “The recording has no audio track — without sound there's no timeline. A new recording helps.” | Offer a new recording. |
|
|
194
|
+
| “I can't find a recording in this project (`material/aufnahmen/`).” | The session isn't in the recording's project: change into it (the part of the path before `material/aufnahmen/`, from `kimal3 show status` or the stop message) and evaluate again. |
|
|
195
|
+
| “The recording folder doesn't exist: …” | Say honestly that the recording isn't (or is no longer) there. Check with `kimal3 show status`; otherwise offer a new recording. |
|
|
196
|
+
| “The recorder isn't set up yet: `kimal3 show setup`.” | Back to step 4, then evaluate again. |
|
|
197
|
+
| “The timeline wasn't created: <reason>.” | Name the reason; if the speech model is missing, back to step 4. |
|
|
198
|
+
| “The recording is still running — first `kimal3 show stop`.” | Back to step 8. |
|
|
199
|
+
|
|
200
|
+
## 10. Data protection — before you read
|
|
201
|
+
|
|
202
|
+
First one question, and wait for the answer:
|
|
203
|
+
|
|
204
|
+
> “Did the demonstration show real data about people — customer names, email addresses?”
|
|
205
|
+
|
|
206
|
+
**Yes:** then the four questions from `context/security.md` (in the workplace above the workbench;
|
|
207
|
+
in a German workplace `kontext/sicherheit.md`) come **now**, before reading — one at a time, each
|
|
208
|
+
with a suggestion: which fields · where to (to Anthropic, the company behind Claude, data centres
|
|
209
|
+
in the USA) · on what legal basis · can it be done more sparingly, e.g. once more with test
|
|
210
|
+
customers. You write the answers to `results/data-protection-demonstration.md` (in a German
|
|
211
|
+
workbench `ergebnisse/`). If the answer is “once more with test customers”, read nothing, suggest
|
|
212
|
+
deleting this recording (step 13, the sentence **without** the plan), and start again at step 5.
|
|
213
|
+
|
|
214
|
+
Then this question, word for word:
|
|
215
|
+
|
|
216
|
+
> “For me to read the timeline, it goes to Anthropic: what you said, where you clicked (with the
|
|
217
|
+
> names of the buttons and fields — these can contain names) and what you typed. Screenshots show
|
|
218
|
+
> your whole screen — I'll only look at those where the text isn't enough, and ask you before
|
|
219
|
+
> each one. Shall I open the file for you so you can look first? Agreed — or would you rather
|
|
220
|
+
> leave out the typed parts?”
|
|
221
|
+
|
|
222
|
+
- **“Open it”:** in Notepad, as in rule 2 (`cmd //c start "" notepad "<full path>"` or
|
|
223
|
+
`Start-Process`), with the full path to `zeitleiste.md` (the project folder plus the path from
|
|
224
|
+
step 9). The person reads it themselves; you don't read along. Then ask the question about
|
|
225
|
+
agreeing once more.
|
|
226
|
+
- **“Without the typed parts”:** `kimal3 show evaluate --without-typed` — with the same
|
|
227
|
+
`--language` as the first time, if you set one, and with the same waiting rule as in step 9. The
|
|
228
|
+
timeline is rewritten, and typed text then stands as “[typed, left out]”. The person has agreed
|
|
229
|
+
to this version → step 11.
|
|
230
|
+
- **Agreed:** → step 11.
|
|
231
|
+
- **No:** read nothing. Say: “You can read the file yourself and describe the task to me — or I
|
|
232
|
+
delete the recording.”
|
|
233
|
+
- **The person describes it:** take the description, ask follow-up questions as in step 11,
|
|
234
|
+
point 4 (one at a time), and carry on with step 12 — plan and way to build it **from the
|
|
235
|
+
description**, without reading anything from the recording. Then step 13.
|
|
236
|
+
- **Delete:** step 13, the sentence **without** the plan.
|
|
237
|
+
|
|
238
|
+
## 11. Read, retell, ask
|
|
239
|
+
|
|
240
|
+
Now — and only now — you read `zeitleiste.md`, and only this file.
|
|
241
|
+
|
|
242
|
+
1. **Retell the task:** in numbered steps, in the **person's words** (quote what was said), each
|
|
243
|
+
step with the time from the timeline (`0:26`).
|
|
244
|
+
2. **Look for the contradictions, explicitly.** Three sources say something about the task: the
|
|
245
|
+
task sentence at the top of the timeline, what the person **says**, and what they **click and
|
|
246
|
+
type**. Go through the task sentence part by part (what, where to, to whom, when) and ask for
|
|
247
|
+
each part: does the demonstration show exactly that? Then check every spoken sentence against
|
|
248
|
+
the click next to it. Every place where two sources differ, you name in the retelling with ⚠️ —
|
|
249
|
+
for example: “You clicked ‘tomorrow’, but said ‘for the current day’ — which one applies?” or
|
|
250
|
+
“The task sentence says ‘into a channel’, but you showed a direct message — which one applies?”
|
|
251
|
+
*These places are exactly why showing reveals more than describing.*
|
|
252
|
+
3. **Screenshots only one by one and only after asking.** If the text isn't enough at a point (a
|
|
253
|
+
click without a name, a sentence pointing at something visible), ask first: “At 0:26 the text
|
|
254
|
+
only says ‘…’. May I look at the screen at that point?” Yes → `kimal3 show picture 0:26`; it
|
|
255
|
+
reports “Picture: <path>”, and you look at **only this one** picture. No → ask what was to be
|
|
256
|
+
seen there.
|
|
257
|
+
4. **Then ask — one question at a time, always with a suggestion:** first the contradictions, one
|
|
258
|
+
after the other. After that, what a single demonstration can never show: **how often and when**
|
|
259
|
+
(“I'd guess every Monday morning — is that right?”) · **what happens in an exception** (a
|
|
260
|
+
public holiday, an empty list, the system can't be reached) · **who the result goes to**.
|
|
261
|
+
|
|
262
|
+
## 12. Write it into the plan and suggest a way to build it
|
|
263
|
+
|
|
264
|
+
Write `plan/task-shown.md` with these parts:
|
|
265
|
+
|
|
266
|
+
- **Goal** in one sentence
|
|
267
|
+
- **The steps**, numbered
|
|
268
|
+
- **Rules and exceptions** from the answers
|
|
269
|
+
- **What is still open**
|
|
270
|
+
- **Where it comes from:** demonstration of <date> (or: description of <date>, if nothing was
|
|
271
|
+
read)
|
|
272
|
+
|
|
273
|
+
The plan carries the content **itself** and doesn't point to files in the recording — that is
|
|
274
|
+
about to go. Typed text that looks like access data doesn't go into it.
|
|
275
|
+
|
|
276
|
+
Then say how you **would build it**: through the programs' interfaces (fetching the data
|
|
277
|
+
directly, sending the message directly), **not** by re-clicking the clicks — that way it doesn't
|
|
278
|
+
break when a screen changes. The building doesn't happen here; it is the next step in the
|
|
279
|
+
project, right away or in the next session with `/k3start`.
|
|
280
|
+
|
|
281
|
+
## 13. Deleting the recording — only after a yes
|
|
282
|
+
|
|
283
|
+
Measure the size of the recording folder and suggest, with the real values. If the task is in
|
|
284
|
+
the plan (step 12):
|
|
285
|
+
|
|
286
|
+
> “The recording is 12.9 MB and lies in `material/aufnahmen/2026-10-19-0912`. I'd delete it —
|
|
287
|
+
> the description is in the plan, and the recording contains everything you typed. What I read
|
|
288
|
+
> from it stays in the conversation history on this computer. Is that alright?”
|
|
289
|
+
|
|
290
|
+
If the suggestion comes without a plan (no in step 10, or “once more with test customers”), leave
|
|
291
|
+
out the part about the plan:
|
|
292
|
+
|
|
293
|
+
> “The recording is 12.9 MB and lies in `material/aufnahmen/2026-10-19-0912`. I'd delete it —
|
|
294
|
+
> the recording contains everything you typed. What I read from it stays in the conversation
|
|
295
|
+
> history on this computer. Is that alright?”
|
|
296
|
+
|
|
297
|
+
- **Yes:** delete **exactly this one** folder — not `material/aufnahmen/` as a whole, not the
|
|
298
|
+
`.gitignore` and `.ignore` next to it — and check that it is gone.
|
|
299
|
+
- **No:** it stays. Say where, and that it doesn't end up in Git.
|
|
300
|
+
|
|
301
|
+
Finally, in two sentences without jargon, what is now in the project, and **one** concrete next
|
|
302
|
+
step.
|
|
@@ -24,7 +24,7 @@ say so briefly and skip it — no filler.
|
|
|
24
24
|
- Is there a status file (`NEXT-STEPS.md` or `ROADMAP.md`) — a small file in which the
|
|
25
25
|
latest status and the next step are written down? Then read it.
|
|
26
26
|
- **Where the status file lives and what it is called.** The project rule applies **only inside a workplace**: if a folder `projects/` or `projekte/` sits directly in a folder `workbench-…` or `werkbank-…` (both spellings occur, also mixed) and the session is in or under a folder in it, that **project folder** is the place. Name there: under `projects/` it is `NEXT-STEPS.md`, under `projekte/` `WEITER-HIER.md`. **Everywhere else** (starter package, your own repos, even a folder `~/projects/my-repo`) it stays as before: the file lives in the folder you started in; if one exists, use it; if one has to be created, it is called `NEXT-STEPS.md` — or `WEITER-HIER.md` if the folder was created in German. If both exist, ask which one applies and write to neither until you have the answer. **Which one applies (in a workplace):** if the project has one, the project's applies. Otherwise an existing workbench note (`workbench-…/NEXT-STEPS.md` or `werkbank-…/WEITER-HIER.md` from the old template) applies — you keep reading and maintaining it. If neither exists, it is created **in the project**. **Never create a new status file in the workbench.**
|
|
27
|
-
- **Are you in the workbench itself (`workbench-…` or `werkbank-…`), not inside a project?** If its `CLAUDE.md` has the section “First session” (German: “Erste Sitzung”), follow **it** — it takes priority over everything below. If it isn't there (any more), show the projects in `projects/` or `projekte/` (without `example-capture-invoices` and `beispiel-rechnungen-erfassen`) with the **first status line** of each one's status file, or “no status file yet” if there is none; an existing workbench note (`NEXT-STEPS.md` or `WEITER-HIER.md`) goes above them. Recommend **one** of them and ask **one** question about it.
|
|
27
|
+
- **Are you in the workbench itself (`workbench-…` or `werkbank-…`), not inside a project?** If its `CLAUDE.md` has the section “First session” (German: “Erste Sitzung”), follow **it** — it takes priority over everything below. If it isn't there (any more), show the projects in `projects/` or `projekte/` (without `example-capture-invoices` and `beispiel-rechnungen-erfassen`) with the **first status line** of each one's status file, or “no status file yet” if there is none; an existing workbench note (`NEXT-STEPS.md` or `WEITER-HIER.md`) goes above them. Recommend **one** of them and ask **one** question about it. If the person picks a project from the list or names a new task (then you first create the folder and status file in the project, never in the workbench), bring the session into the project folder: with `change_directory` (if it only appears as a name in your tool list, load it via the tool search) — and say first: “The Claude app will ask whether I may change the folder, and then whether you trust it — both are fine, click Allow each time. If you land in an empty conversation, type `/k3start` there; you will find this conversation again in the list on the left.” Without the tool, or if the person declines: give the exact project folder (not the workbench above it) — in the app “Choose folder” and pick that folder, in the terminal `/exit`, `cd '‹path›'`, `claude` — and then `/k3start`. For a new project, read `projects/CLAUDE.md` (German: `projekte/CLAUDE.md`) first: from now on it applies.
|
|
28
28
|
- No status file? **First check whether there is a `.git` folder where the status file would live** (in a workplace the project folder, otherwise the folder you started in). If there is, this
|
|
29
29
|
project keeps a version history, and `git log --oneline -10` plus `git status` show you the
|
|
30
30
|
latest state.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Gemeinsame Teile von werkzeug.py und starter.py: Dateinamen, Umgebung des Aufnehmers, Musterliste der Fehlergruende,
|
|
2
|
+
# JSON lesen/schreiben, Stopp einer Sitzung. Kein eigener Einstieg; laeuft mit dem Python des Aufnehmers.
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
# Der Aufnehmer bekommt einen eigenen Unterordner, in den sonst niemand schreibt: Beim Abschliessen versiegelt er JEDE
|
|
9
|
+
# Datei seines Ordners und prueft das Siegel danach nach. Lag unser Herzschlag (starter.json) mit darin, brach es
|
|
10
|
+
# (`capture_seal_failed`, Lauf 1 am 21.10.); dasselbe droht jeder Datei, die wir dort schreiben. Bericht, Protokoll,
|
|
11
|
+
# Abbruch-Datei, Zeitleiste und Bilder liegen deshalb eine Ebene hoeher.
|
|
12
|
+
ROH = "roh"
|
|
13
|
+
STARTER = "starter.json"
|
|
14
|
+
PROTOKOLL = "aufnahme.log"
|
|
15
|
+
ABBRECHEN = "abbrechen"
|
|
16
|
+
ZUSTAND_AUFNEHMER = "capture-state.json"
|
|
17
|
+
UMGEBUNG = {
|
|
18
|
+
"STOP_SEQUENCES": '[["o","a",".","s","t","o","p"]]', # die Strg-Folge des Aufnehmers beendet sonst bei Strg+C (B1)
|
|
19
|
+
"RECORD_AUDIO_RETAIN_WAVEFORM": "true",
|
|
20
|
+
"PYTHONUNBUFFERED": "1",
|
|
21
|
+
"PYTHONUTF8": "1",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Zwilling von GRUENDE in cli/src/zeigen-rechnung.ts -- gleiche Reihenfolge, gleiche Schalter (der Test vergleicht beide).
|
|
25
|
+
GRUENDE = [
|
|
26
|
+
(re.compile(r"FFmpegUnavailableError|ffmpeg.*not found", re.IGNORECASE), "ffmpeg-fehlt"),
|
|
27
|
+
(re.compile(r"returned success without a non-empty output"), "pfad-zu-lang"),
|
|
28
|
+
(re.compile(r"PortAudio|No Default Input Device|sounddevice"), "kein-mikrofon"),
|
|
29
|
+
(re.compile(r"DesktopCaptureError|screen layout"), "bildschirm-gesperrt"),
|
|
30
|
+
(re.compile(r"did not become ready|did not start"), "startet-nicht"),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def fehler_grund(protokoll: str) -> str:
|
|
35
|
+
return next((code for muster, code in GRUENDE if muster.search(protokoll)), "unbekannt")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def schreibe_json_atomar(pfad: str, daten: dict) -> None:
|
|
39
|
+
tmp = pfad + ".tmp"
|
|
40
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
41
|
+
json.dump(daten, f)
|
|
42
|
+
for versuch in range(20):
|
|
43
|
+
try:
|
|
44
|
+
os.replace(tmp, pfad)
|
|
45
|
+
return
|
|
46
|
+
except PermissionError: # Windows: ein Leser haelt die Datei gerade offen
|
|
47
|
+
if versuch == 19:
|
|
48
|
+
raise
|
|
49
|
+
time.sleep(0.05)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def lies_json(pfad: str) -> dict | None:
|
|
53
|
+
try:
|
|
54
|
+
with open(pfad, encoding="utf-8") as f:
|
|
55
|
+
daten = json.load(f)
|
|
56
|
+
except (OSError, ValueError):
|
|
57
|
+
return None
|
|
58
|
+
return daten if isinstance(daten, dict) else None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def lies_protokoll(ordner: str) -> str:
|
|
62
|
+
try:
|
|
63
|
+
with open(os.path.join(ordner, PROTOKOLL), encoding="utf-8", errors="replace") as f:
|
|
64
|
+
return f.read()
|
|
65
|
+
except OSError:
|
|
66
|
+
return ""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def sauber(ordner: str) -> bool:
|
|
70
|
+
zustand = lies_json(os.path.join(ordner, ROH, ZUSTAND_AUFNEHMER)) or {}
|
|
71
|
+
return zustand.get("integrity_verified") is True and zustand.get("phase") == "complete"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _stoppe_sitzung(sitzung: str, timeout: float) -> None:
|
|
75
|
+
"""Stoppt genau diese Sitzung und kehrt erst nach dem geprueften Abschluss zurueck; sonst eine Ausnahme. Laut
|
|
76
|
+
Aufnehmer (control.stop_recording) ist ein Stopp idempotent: ein zweiter, gleichzeitiger wartet auf denselben
|
|
77
|
+
Abschluss. Nach Ablauf von `timeout` schliesst der Aufnehmer trotzdem weiter ab -- nur die Antwort fehlt."""
|
|
78
|
+
if not sitzung: # ohne Sitzung stoppt stop_recording die EINE laufende Aufnahme -- womoeglich eine fremde
|
|
79
|
+
raise ValueError("keine Sitzung")
|
|
80
|
+
from openadapt_capture.control import stop_recording # erst nach chdir: der Aufnehmer liest beim Import eine .env
|
|
81
|
+
|
|
82
|
+
stop_recording(sitzung, timeout=timeout)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Starter hinter `werkzeug.py starten <konfig.json>`: haelt den Aufnehmer als eigenes Kind (versteckte Konsole), zieht die
|
|
2
|
+
# Bremse nach der Frist, beendet ihn bei `abbrechen` und schreibt laufend starter.json mit Herzschlag `zuletzt`.
|
|
3
|
+
# Von Node mit pythonw abgehaengt gestartet: keine Ausgabe. Einstieg bleibt werkzeug.py (Ruling R-12: eigene Datei).
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from gemeinsam import (ABBRECHEN, PROTOKOLL, ROH, STARTER, UMGEBUNG, _stoppe_sitzung, fehler_grund, lies_protokoll,
|
|
12
|
+
sauber, schreibe_json_atomar)
|
|
13
|
+
|
|
14
|
+
TAKT_SEK = 2.0 # so oft schaut der Starter nach Sitzung, Abbruch und Frist (Wanduhr, nicht ein Zeitgeber ueber 15 Minuten)
|
|
15
|
+
NACHTOETEN_SEK = 0.5 # Pause zwischen den ersten Versuchen, einen Prozessbaum zu beenden
|
|
16
|
+
NACHTOETEN_VERSUCHE = 3 # danach meldet der Starter "nicht-beendet" und versucht es jeden Takt weiter
|
|
17
|
+
# Video-Abschluss und eingebaute Spracherkennung nach 15 Minuten brauchen mehr als die Vorgabe 60 s. Der Starter
|
|
18
|
+
# laeuft ohne Claudes 10-Minuten-Grenze; `kimal3 zeigen stopp` wartet kuerzer (werkzeug.STOPP_SEK) und meldet dann
|
|
19
|
+
# "wird noch abgeschlossen".
|
|
20
|
+
BREMSE_STOPP_SEK = 600
|
|
21
|
+
AUFNEHMEN = "import sys; from openadapt_capture.cli import record; record(sys.argv[1], audio=True)"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _starte_kind(befehl: list, ordner: str, protokoll) -> subprocess.Popen:
|
|
25
|
+
# Eigene, VERSTECKTE Konsole: Sonst oeffnen python.exe, sein multiprocessing-Kind und FFmpeg je ein Fenster
|
|
26
|
+
# (gemessen 18.10.: detached + windowsHide allein reicht nicht; starter-probe.py).
|
|
27
|
+
si = subprocess.STARTUPINFO()
|
|
28
|
+
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
29
|
+
si.wShowWindow = 0 # SW_HIDE
|
|
30
|
+
return subprocess.Popen(befehl, cwd=ordner, env=dict(os.environ, **UMGEBUNG), stdout=protokoll,
|
|
31
|
+
stderr=subprocess.STDOUT, startupinfo=si, creationflags=subprocess.CREATE_NEW_CONSOLE)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _beende_baum(kind: subprocess.Popen) -> bool:
|
|
35
|
+
"""Beendet Kind samt Enkeln (multiprocessing, FFmpeg) und prueft, dass keiner mehr lebt."""
|
|
36
|
+
import psutil
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
baum = [psutil.Process(kind.pid)]
|
|
40
|
+
baum += baum[0].children(recursive=True)
|
|
41
|
+
except psutil.NoSuchProcess:
|
|
42
|
+
baum = []
|
|
43
|
+
subprocess.run(["taskkill", "/PID", str(kind.pid), "/T", "/F"], capture_output=True,
|
|
44
|
+
creationflags=subprocess.CREATE_NO_WINDOW)
|
|
45
|
+
_, lebend = psutil.wait_procs(baum, timeout=10)
|
|
46
|
+
for p in lebend:
|
|
47
|
+
try:
|
|
48
|
+
p.kill()
|
|
49
|
+
except psutil.NoSuchProcess:
|
|
50
|
+
pass
|
|
51
|
+
_, lebend = psutil.wait_procs(lebend, timeout=5)
|
|
52
|
+
try:
|
|
53
|
+
kind.wait(timeout=5)
|
|
54
|
+
except subprocess.TimeoutExpired:
|
|
55
|
+
return False
|
|
56
|
+
return not lebend
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _mit_herzschlag(arbeit, melde):
|
|
60
|
+
"""Fuehrt eine blockierende Arbeit (Stopp, Prozessbaum beenden) in einem Faden aus und schreibt waehrenddessen jeden
|
|
61
|
+
Takt `zuletzt` -- sonst haelt Node einen gesunden Starter, der gerade abschliesst, nach 10 s fuer tot."""
|
|
62
|
+
ergebnis = {}
|
|
63
|
+
|
|
64
|
+
def lauf():
|
|
65
|
+
try:
|
|
66
|
+
ergebnis["wert"] = arbeit()
|
|
67
|
+
except BaseException as fehler:
|
|
68
|
+
ergebnis["fehler"] = fehler
|
|
69
|
+
|
|
70
|
+
faden = threading.Thread(target=lauf, daemon=True)
|
|
71
|
+
faden.start()
|
|
72
|
+
while True:
|
|
73
|
+
faden.join(TAKT_SEK)
|
|
74
|
+
if not faden.is_alive():
|
|
75
|
+
break
|
|
76
|
+
melde()
|
|
77
|
+
if "fehler" in ergebnis:
|
|
78
|
+
raise ergebnis["fehler"]
|
|
79
|
+
return ergebnis.get("wert")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _warte_auf_kind(kind: subprocess.Popen, sekunden: float, melde) -> bool:
|
|
83
|
+
"""Wartet in Takt-Scheiben mit Herzschlag; True, wenn das Kind in der Zeit geendet hat."""
|
|
84
|
+
ende = time.time() + sekunden
|
|
85
|
+
while kind.poll() is None and time.time() < ende:
|
|
86
|
+
try:
|
|
87
|
+
kind.wait(timeout=TAKT_SEK)
|
|
88
|
+
except subprocess.TimeoutExpired:
|
|
89
|
+
melde()
|
|
90
|
+
return kind.poll() is not None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _raeume_weg(kind: subprocess.Popen, melde) -> None:
|
|
94
|
+
"""Kehrt erst zurueck, wenn der Baum nachweislich weg ist. Solange er lebt, bleibt der Starter da (Bremse bleibt
|
|
95
|
+
gezogen) und meldet `grund: nicht-beendet` -- nie einen Endzustand, waehrend noch etwas aufnimmt."""
|
|
96
|
+
versuch = 0
|
|
97
|
+
while not _mit_herzschlag(lambda: _beende_baum(kind), melde):
|
|
98
|
+
versuch += 1
|
|
99
|
+
if versuch < NACHTOETEN_VERSUCHE:
|
|
100
|
+
time.sleep(NACHTOETEN_SEK)
|
|
101
|
+
continue
|
|
102
|
+
melde(grund="nicht-beendet")
|
|
103
|
+
time.sleep(TAKT_SEK)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def starte(konfig: dict, befehl: list | None = None) -> None:
|
|
107
|
+
ordner = konfig["ordner"]
|
|
108
|
+
gestartet = time.time()
|
|
109
|
+
bericht = {"zustand": "startet", "sitzung": None, "gestartet": gestartet,
|
|
110
|
+
"frist": gestartet + float(konfig["hoechstens_sek"]), "grund": None, "zuletzt": gestartet}
|
|
111
|
+
pfad = os.path.join(ordner, STARTER)
|
|
112
|
+
|
|
113
|
+
def melde(**felder):
|
|
114
|
+
# `zuletzt` ist der Herzschlag: Node haelt einen Starter, der vor <= 10 s schrieb, fuer lebendig (Ruling R-3).
|
|
115
|
+
bericht.update(felder, zuletzt=time.time())
|
|
116
|
+
schreibe_json_atomar(pfad, bericht)
|
|
117
|
+
|
|
118
|
+
melde()
|
|
119
|
+
kind = None
|
|
120
|
+
try:
|
|
121
|
+
python = os.path.join(os.path.dirname(sys.executable), "python.exe")
|
|
122
|
+
roh = os.path.join(ordner, ROH) # nur der Aufnehmer schreibt dort (gemeinsam.ROH)
|
|
123
|
+
os.makedirs(roh, exist_ok=True)
|
|
124
|
+
with open(os.path.join(ordner, PROTOKOLL), "w", encoding="utf-8") as protokoll:
|
|
125
|
+
kind = _starte_kind(befehl or [python, "-c", AUFNEHMEN, roh], roh, protokoll)
|
|
126
|
+
while True:
|
|
127
|
+
try:
|
|
128
|
+
kind.wait(timeout=TAKT_SEK)
|
|
129
|
+
except subprocess.TimeoutExpired:
|
|
130
|
+
pass
|
|
131
|
+
treffer = None if bericht["sitzung"] else re.search(r"Capture session: ([0-9a-f-]{36})", lies_protokoll(ordner))
|
|
132
|
+
melde(**({"zustand": "laeuft", "sitzung": treffer.group(1)} if treffer else {}))
|
|
133
|
+
if kind.poll() is not None:
|
|
134
|
+
return melde(**_ende(ordner, bericht["sitzung"], "gestoppt"))
|
|
135
|
+
if os.path.exists(os.path.join(ordner, ABBRECHEN)):
|
|
136
|
+
_raeume_weg(kind, melde)
|
|
137
|
+
return melde(zustand="abgebrochen", grund=None)
|
|
138
|
+
if time.time() >= bericht["frist"]:
|
|
139
|
+
if bericht["sitzung"] is None:
|
|
140
|
+
_raeume_weg(kind, melde)
|
|
141
|
+
return melde(zustand="fehlgeschlagen", grund=fehler_grund(lies_protokoll(ordner)))
|
|
142
|
+
try:
|
|
143
|
+
_mit_herzschlag(lambda: _stoppe_sitzung(bericht.get("sitzung"), BREMSE_STOPP_SEK), melde)
|
|
144
|
+
except Exception:
|
|
145
|
+
pass # ob die Aufnahme sauber zu ist, sagt danach capture-state.json (_ende)
|
|
146
|
+
if not _warte_auf_kind(kind, 120, melde):
|
|
147
|
+
_raeume_weg(kind, melde)
|
|
148
|
+
return melde(**_ende(ordner, bericht["sitzung"], "selbst-gestoppt"))
|
|
149
|
+
except Exception:
|
|
150
|
+
grund = "unbekannt"
|
|
151
|
+
if kind is not None and kind.poll() is None:
|
|
152
|
+
try:
|
|
153
|
+
_raeume_weg(kind, melde)
|
|
154
|
+
except Exception:
|
|
155
|
+
grund = "nicht-beendet" # nicht nachgewiesen weg: Node sagt "kann ich nicht feststellen", nie "beendet"
|
|
156
|
+
melde(zustand="fehlgeschlagen", grund=grund)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _ende(ordner: str, sitzung: str | None, art: str) -> dict:
|
|
160
|
+
"""Das Kind ist weg: sauber abgeschlossen -> `art`; mit Sitzung, aber nicht sauber -> abgebrochen; ohne -> fehlgeschlagen."""
|
|
161
|
+
if sitzung is not None and sauber(ordner):
|
|
162
|
+
return {"zustand": art, "grund": None}
|
|
163
|
+
return {"zustand": "abgebrochen" if sitzung else "fehlgeschlagen", "grund": fehler_grund(lies_protokoll(ordner))}
|