@netnodeag/kraftwerk 0.33.0 → 0.34.1

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.
Files changed (29) hide show
  1. package/README.md +317 -242
  2. package/dist/cli/projects.js +30 -8
  3. package/dist/inspector/instances.d.ts +40 -0
  4. package/dist/inspector/instances.js +120 -1
  5. package/dist/inspector/server.js +18 -1
  6. package/inspector/dist/assets/{editor-BlAeAqt0.js → editor-CXrDJOv_.js} +3 -3
  7. package/inspector/dist/assets/{index-Dnqjpy0h.js → index-7J4zAS6J.js} +1 -1
  8. package/inspector/dist/assets/index-B7rwNdVj.css +1 -0
  9. package/inspector/dist/assets/index-BBGjdXk1.js +213 -0
  10. package/inspector/dist/assets/{index-e-11MUIo.js → index-BIDsfw8C.js} +1 -1
  11. package/inspector/dist/assets/{index--wwp-KD0.js → index-BNcXQYbv.js} +1 -1
  12. package/inspector/dist/assets/{index-BvCAG0mQ.js → index-BQMgADlc.js} +1 -1
  13. package/inspector/dist/assets/{index-AR7uGZPa.js → index-BULB2rC_.js} +1 -1
  14. package/inspector/dist/assets/{index-BKnMNAOc.js → index-Bumwg9SM.js} +1 -1
  15. package/inspector/dist/assets/{index-DcXZX6dj.js → index-BxEG-IdY.js} +1 -1
  16. package/inspector/dist/assets/{index--9LQl_DL.js → index-C3hpUVfp.js} +1 -1
  17. package/inspector/dist/assets/{index-LBoNKP4N.js → index-CMMYSoL3.js} +1 -1
  18. package/inspector/dist/assets/{index-D3O4auYk.js → index-CNJyAniG.js} +1 -1
  19. package/inspector/dist/assets/{index-9rElziKS.js → index-CUCCMUMY.js} +1 -1
  20. package/inspector/dist/assets/{index-C3ugp_Wf.js → index-ChuqpBnv.js} +1 -1
  21. package/inspector/dist/assets/{index-CdkhUV5I.js → index-CszaAR9T.js} +1 -1
  22. package/inspector/dist/assets/{index-CV4Aiss-.js → index-DAi1VtJ8.js} +1 -1
  23. package/inspector/dist/assets/{index-B0qGMqfM.js → index-DrSWAGft.js} +1 -1
  24. package/inspector/dist/assets/{index-BvGhLr64.js → index-Upf-BEsx.js} +1 -1
  25. package/inspector/dist/assets/{index-BxPjb1aG.js → index-x5G6Np7T.js} +1 -1
  26. package/inspector/dist/index.html +2 -2
  27. package/package.json +1 -1
  28. package/inspector/dist/assets/index-B7W4QbA2.css +0 -1
  29. package/inspector/dist/assets/index-C5IBvIVa.js +0 -213
package/README.md CHANGED
@@ -6,23 +6,44 @@ knowledge, skills and workflows belong to the team, not to the person who
6
6
  happened to set them up, and every result is checked before anyone relies on
7
7
  it.
8
8
 
9
- **Why teams need it.** AI work today lives in individual silos: personal
10
- prompts, Claude Code sessions, scripts, and one-off automations. Kraftwerk
11
- gives a team or organisation one place where AI use becomes shared,
12
- repeatable practice: agents that remember and are accountable, knowledge that
13
- is curated and verified, workflows whose results are checked before the work
14
- moves on.
9
+ ```bash
10
+ npm install -g @netnodeag/kraftwerk # or run any command below via npx @netnodeag/kraftwerk
11
+
12
+ cd your-project
13
+ kraftwerk init # scaffold the workspace
14
+ kraftwerk doctor # check harnesses, docker, workflows, env vars
15
+ kraftwerk ui # open it at http://localhost:1981
16
+ ```
17
+
18
+ Needs Node 20+ and at least one agent harness (Claude Code, Codex, or Pi).
19
+ Details in [Install](#install).
20
+
21
+ **Why teams need it.** AI work today lives in individual silos. Personal
22
+ prompts, Claude Code sessions, one-off scripts. What one person figures out
23
+ stays with that person. Kraftwerk gives a team one place where that work
24
+ becomes shared practice.
25
+
26
+ - **Shared Agentic Workspace.** Prompts, sessions and scripts turn into
27
+ workflows, skills and knowledge that everyone can see, use and improve.
28
+ - **Persistent AI Coworkers.** Agents have a name, a role, a memory and
29
+ standing orders. They keep the knowledge they work with current, and every
30
+ conversation continues where the last one ended.
31
+ - **Combine agentic workflows with deterministic workflows.** Every agent step
32
+ has to pass a check that reads the actual files before the work moves on.
33
+ Each run leaves a trace of what happened, how long it took and what it cost.
34
+ - **RSI (Recursive Self-Improvement).** By default, all system components are
35
+ built around self-improving.
15
36
 
16
37
  **What's in the workspace.**
17
38
 
18
- - **Agents** — persistent AI coworkers, each with its own identity, memory
19
- and standing orders, running on Claude Code, Codex, Pi, and others
20
- - **Knowledge** — shared organisational and project context, browsed like a
21
- wiki, edited with autosave, verified by humans, exportable as PDF
22
- - **Skills** — reusable capabilities and ways of working
23
- - **Workflows** — repeatable multi-step processes
24
- - **Verification** — deterministic gates that check the work
25
- - **Inspector** — the web UI that brings it all together
39
+ - **Agents.** Persistent AI coworkers, each with its own identity, memory
40
+ and standing orders, running on Claude Code, Codex, Pi, and others.
41
+ - **Knowledge.** Shared organisational and project context, browsed like a
42
+ wiki, edited with autosave, verified by humans, exportable as PDF.
43
+ - **Skills.** Reusable capabilities and ways of working.
44
+ - **Workflows.** Repeatable multi-step processes.
45
+ - **Verification.** Gates that check the work against the files on disk.
46
+ - **Inspector.** The web UI that brings it all together.
26
47
 
27
48
  This README is the reference for the framework and CLI. For the overview,
28
49
  start at the [project README](https://github.com/NETNODEAG/kraftwerk#readme).
@@ -30,36 +51,77 @@ start at the [project README](https://github.com/NETNODEAG/kraftwerk#readme).
30
51
  ## Under the hood
31
52
 
32
53
  Deterministic workflow-as-code over headless agent harnesses, in the spirit of
33
- [super-simple-software-factory](https://github.com/disler/super-simple-software-factory):
34
- **code owns the control flow, agents work inside bounded phases.**
35
- "Agent proposes, code disposes."
54
+ [super-simple-software-factory](https://github.com/disler/super-simple-software-factory).
55
+ Code owns the control flow, agents work inside bounded phases. "Agent
56
+ proposes, code disposes."
57
+
58
+ Nothing binds to an SDK. Every agent phase spawns one short-lived CLI process
59
+ on the agent's harness, then judges the result afterwards against a typed JSON
60
+ envelope and file gates. Failed checks are corrected in the same session,
61
+ never by a cold restart. Every run leaves a `trace.jsonl` event log and ends
62
+ with a time, token and cost summary table.
63
+
64
+ ## Install
65
+
66
+ You need Node 20 or newer and at least one agent harness on your PATH: Claude
67
+ Code (`claude`), Codex (`codex`), or Pi (`pi`). See [Harnesses](#harnesses)
68
+ for how to get them. `kraftwerk doctor` checks for all of it and names
69
+ whatever is missing.
36
70
 
37
- No SDK dependency: every agent phase spawns one short-lived CLI process on the
38
- agent's **harness** and judges the result afterwards (typed JSON envelope +
39
- file gates). Failed checks are corrected in the same session, never by a cold
40
- restart. Every run leaves a `trace.jsonl` event log and ends with a
41
- time/token/cost summary table.
71
+ Install it globally to get the `kraftwerk` command in every project:
72
+
73
+ ```bash
74
+ npm install -g @netnodeag/kraftwerk
75
+ kraftwerk --version
76
+ ```
77
+
78
+ Without installing, every command works prefixed with `npx`, at the cost of a
79
+ download per call:
80
+
81
+ ```bash
82
+ npx @netnodeag/kraftwerk --version
83
+ ```
84
+
85
+ Upgrading is the same command with `@latest`. A running inspector serves the
86
+ version it started with, so restart each UI afterwards. It offers a relaunch
87
+ by itself once a newer version is on disk.
88
+
89
+ ```bash
90
+ npm install -g @netnodeag/kraftwerk@latest
91
+ kraftwerk projects # every workspace on this machine, running or not
92
+ ```
42
93
 
43
94
  ## Consume
44
95
 
45
- Zero-setup consumer (YAML workflows only): a repo containing workflow
46
- folders under `workflows/` (or `src/workflows/`) IS a complete consumer —
47
- no package.json, no install:
96
+ Any repo with workflow folders under `workflows/` (or `src/workflows/`) is
97
+ already a complete consumer. No package.json, no local dependency, YAML
98
+ workflows only:
48
99
 
49
100
  ```bash
50
- npx @netnodeag/kraftwerk init # scaffold kraftwerk.yml + workflows/ + example
101
+ cd your-project
102
+ kraftwerk init # scaffold kraftwerk.yml + kraftwerk-data/ (workflow, agent, knowledge)
103
+ kraftwerk doctor # preflight: harness CLIs, docker, workflows, declared env vars
104
+ kraftwerk run hello "Was ist kraftwerk?"
105
+ kraftwerk ui # inspector on http://localhost:1981
106
+ ```
107
+
108
+ The same without a global install:
109
+
110
+ ```bash
111
+ npx @netnodeag/kraftwerk init
51
112
  npx @netnodeag/kraftwerk run hello "Was ist kraftwerk?"
113
+ npx @netnodeag/kraftwerk ui
52
114
  ```
53
115
 
54
- Your own workflows are just more folders under `workflows/` — a
116
+ Your own workflows are just more folders under `workflows/`, each a
55
117
  `workflow.yml` plus prompt files, discovered automatically (see
56
- [YAML workflows](#yaml-workflows)). Or let a coding agent build one:
118
+ [YAML workflows](#yaml-workflows)). Or let a coding agent build one.
57
119
  `npx @netnodeag/kraftwerk create "<what it should do>"` prints a
58
- self-contained brief that Claude Code / Codex follows end to end.
120
+ self-contained brief that Claude Code or Codex follows end to end.
59
121
 
60
- For a local checkout / programmatic consumer (TS workflows, custom gates,
61
- approval loops), add the dependency (the `kraftwerk` alias keeps imports
62
- short):
122
+ For a local checkout or a programmatic consumer with TS workflows, custom
123
+ gates and approval loops, add the dependency. The `kraftwerk` alias keeps
124
+ imports short:
63
125
 
64
126
  ```jsonc
65
127
  // package.json of your workflow project
@@ -70,14 +132,15 @@ short):
70
132
  import { defineAgent, Run, runCli, fileNonEmpty, envelopeContract } from "kraftwerk";
71
133
  ```
72
134
 
73
- ## CLI — kraftwerk
135
+ ## The kraftwerk CLI
74
136
 
75
- Ships with the package (`npx @netnodeag/kraftwerk …` anywhere, `npm link` in
76
- the checkout for a global `kraftwerk`). Workflows are auto-discovered under
77
- `src/workflows/` (or `workflows/`): every folder with a `workflow.yml` and
78
- every top-level `.yml` file. Every command works from any subdirectory —
79
- the CLI walks up to the project root (marked by `kraftwerk.yml`, a
80
- workflows root, or `.git`).
137
+ The CLI ships with the package. Use `kraftwerk …` after a global install, or
138
+ `npx @netnodeag/kraftwerk …` anywhere without one. `npm link` in a dev
139
+ checkout also gives you the bare command. Workflows are auto-discovered under
140
+ `src/workflows/` (or `workflows/`), meaning every folder with a `workflow.yml`
141
+ and every top-level `.yml` file. Every command works from any subdirectory,
142
+ because the CLI walks up to the project root, marked by `kraftwerk.yml`, a
143
+ workflows root, or `.git`.
81
144
 
82
145
  ```bash
83
146
  kraftwerk init # make this repo a consumer: kraftwerk.yml, workflows/, example
@@ -87,8 +150,9 @@ kraftwerk run # interactive: pick workflow, type the r
87
150
  kraftwerk runs # past runs from output/*/trace.jsonl; runs show <id> for detail
88
151
  kraftwerk knowledge # Context & Knowledge: OKF bundles (list/get/put/verify/search/...)
89
152
  kraftwerk ui # inspector web UI on http://localhost:1981; --port, --output
153
+ kraftwerk projects # every workspace on this machine; projects start|stop|forget <ref>
90
154
  kraftwerk doctor # preflight: harness CLIs, docker, workflows, declared env vars
91
- kraftwerk validate # all discovered — schema + semantics + files, exit 1 on failure
155
+ kraftwerk validate # all discovered: schema + semantics + files, exit 1 on failure
92
156
  kraftwerk validate src/workflows/pitch # specific paths
93
157
  kraftwerk create "was der Workflow tun soll" # for LLM agents: prints a build brief
94
158
  kraftwerk runner build # build the Docker sandbox image (once)
@@ -96,16 +160,16 @@ kraftwerk run --sandbox website-check "https://..." # isolated container per r
96
160
  kraftwerk runner ps / stop <run-id> # see / stop running sandbox containers
97
161
  ```
98
162
 
99
- ### Project config — kraftwerk.yml
163
+ ### The kraftwerk.yml project config
100
164
 
101
- Optional, at the project root (also the root marker for the walk-up); all
102
- fields optional: `workflows:` (workflows root), `output:` (run-artifact
103
- directory, default `output/`), `knowledge:` (OKF knowledge-bundle root,
104
- default `knowledge/`), and `agents:` (team agent-definition root, default
165
+ Optional, at the project root, and also the root marker for the walk-up. All
166
+ fields are optional. `workflows:` sets the workflows root, `output:` the
167
+ run-artifact directory (default `output/`), `knowledge:` the OKF bundle root
168
+ (default `knowledge/`), and `agents:` the team agent-definition root (default
105
169
  `agents/`).
106
170
 
107
- `switcher:` links other kraftwerk workspaces from the inspector header —
108
- the workspace name becomes a dropdown listing them:
171
+ `switcher:` links other kraftwerk workspaces from the inspector header. The
172
+ workspace name becomes a dropdown listing them:
109
173
 
110
174
  ```yaml
111
175
  switcher:
@@ -114,67 +178,67 @@ switcher:
114
178
  icon: "🛰" # optional
115
179
  ```
116
180
 
117
- ### Triggering from CI / cron / webhooks
181
+ ### Triggering from CI, cron, or webhooks
118
182
 
119
- `run --json` is the machine mode: non-interactive, one JSON result object
120
- on stdout (`ok`, `runDir`, per-phase stats, totals), all narration on
121
- stderr. `KRAFTWERK_YES=1` equals `--yes`, `--quiet` silences narration.
122
- Exit codes: 0 ok, 2 usage/config error (unknown workflow, missing env),
123
- 3 run failed (gate/blocked/harness), 1 unexpected.
183
+ `run --json` is the machine mode. It runs non-interactively, prints one JSON
184
+ result object on stdout (`ok`, `runDir`, per-phase stats, totals), and sends
185
+ all narration to stderr. `KRAFTWERK_YES=1` equals `--yes`, and `--quiet`
186
+ silences narration. Exit codes: 0 ok, 2 usage or config error (unknown
187
+ workflow, missing env), 3 run failed (gate, blocked, harness), 1 unexpected.
124
188
 
125
189
  ```bash
126
190
  KRAFTWERK_YES=1 npx @netnodeag/kraftwerk run tagline "https://..." --json > result.json
127
191
  ```
128
192
 
129
193
  Workflows declare the env vars they need via top-level `requires:
130
- [MATOMO_TOKEN, ...]` — checked before anything spawns, listed by
131
- `kraftwerk list` and `kraftwerk doctor`.
194
+ [MATOMO_TOKEN, ...]`. The engine checks them before anything spawns, and both
195
+ `kraftwerk list` and `kraftwerk doctor` list them.
132
196
 
133
- ### Remote workflows — --from
197
+ ### Remote workflows with --from
134
198
 
135
- `list` and `run` accept `--from github:org/repo[@ref]` (or any git URL):
136
- the repo is shallow-cloned to `~/.cache/kraftwerk/remotes/` (refreshed per
137
- call, cached offline) and its workflows run locally — artifacts land in
138
- YOUR `output/`, not the cache. Share one workflow library across projects
139
- without vendoring:
199
+ `list` and `run` accept `--from github:org/repo[@ref]`, or any git URL. The
200
+ repo is shallow-cloned to `~/.cache/kraftwerk/remotes/`, refreshed per call
201
+ and cached for offline use, and its workflows run locally. Artifacts land in
202
+ YOUR `output/`, never in the cache. That way one workflow library serves many
203
+ projects without vendoring:
140
204
 
141
205
  ```bash
142
206
  npx @netnodeag/kraftwerk run --from github:NETNODEAG/workflows tagline "https://..."
143
207
  ```
144
208
 
145
209
  Sandbox mode (`--sandbox`) runs the workflow in a `kraftwerk-runner`
146
- container (see `runner/Dockerfile`): workflow folder mounted read-only,
147
- the run directory bind-mounted straight into the host `output/` — trace
148
- and artifacts appear live, no copy-back. Env vars come from
149
- `<project>/runner.env` (plus `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`
150
- pass-through); `--run-id` pins the run folder name for external triggers
151
- (the inspector uses this). `runner.json` in the run dir records
152
- container, exit code, and timing.
153
-
154
- `run` prompts for whatever is missing (workflow picker, request input);
155
- invalid workflows show up red in `list` with their validation error
156
- instead of breaking the listing. `create` is meant to be run BY an LLM
157
- agent (Claude Code, Codex): it prints a self-contained brief — schema
158
- example, gates, harness rules, verify ladder — that the agent follows to
159
- author the workflow folder and validate/smoke it with this CLI.
160
-
161
- ### Inspector on a server — deploy starter
162
-
163
- The repo root ships [`deploy-starter/`](../deploy-starter/): copy it
164
- into your consumer repo as `deploy/` and it builds a small image (this
165
- package from npm + the claude/codex/pi CLIs) that serves `kraftwerk ui`
166
- for that repo, bind-mounted at `/work`. `compose.yml` is localhost-only
167
- (SSH tunnel); `compose.traefik.yml` layers traefik routing + mandatory
168
- basic-auth on top — the UI has no authentication of its own and its chat
169
- runs coding agents against the mounted repo. Agent logins made inside
170
- the container persist in the `agent-home` volume. This is a different
171
- image from the `kraftwerk-runner` sandbox (`runner/Dockerfile`) used by
172
- `run --sandbox`.
173
-
174
- ## Team — persistent agents
175
-
176
- The inspector's "team" screen turns chat agents into persistent teammates.
177
- A team member is one folder under the project's `agents/` root:
210
+ container, built from `runner/Dockerfile`. The workflow folder mounts
211
+ read-only. The run directory bind-mounts straight into the host `output/`, so
212
+ trace and artifacts appear live with no copy-back. Env vars come from
213
+ `<project>/runner.env`, plus `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`
214
+ pass-through. `--run-id` pins the run folder name for external triggers, which
215
+ is what the inspector uses. `runner.json` in the run dir records container,
216
+ exit code, and timing.
217
+
218
+ `run` prompts for whatever is missing, both the workflow picker and the
219
+ request input. Invalid workflows show up red in `list` with their validation
220
+ error instead of breaking the listing. `create` is meant to be run BY an LLM
221
+ agent such as Claude Code or Codex. It prints a self-contained brief covering
222
+ the schema example, gates, harness rules and verify ladder, which the agent
223
+ follows to author the workflow folder and validate and smoke it with this CLI.
224
+
225
+ ### Inspector on a server
226
+
227
+ The repo root ships [`deploy-starter/`](../deploy-starter/). Copy it into your
228
+ consumer repo as `deploy/` and it builds a small image, this package from npm
229
+ plus the claude, codex and pi CLIs, that serves `kraftwerk ui` for that repo
230
+ bind-mounted at `/work`. `compose.yml` is localhost-only, reachable over an
231
+ SSH tunnel. `compose.traefik.yml` layers traefik routing and mandatory
232
+ basic-auth on top. Treat that auth as load-bearing. The UI has no
233
+ authentication of its own and its chat runs coding agents against the mounted
234
+ repo. Agent logins made inside the container persist in the `agent-home`
235
+ volume. This is a different image from the `kraftwerk-runner` sandbox
236
+ (`runner/Dockerfile`) used by `run --sandbox`.
237
+
238
+ ## Persistent team agents
239
+
240
+ The inspector's "team" screen turns chat agents into persistent teammates. A
241
+ team member is one folder under the project's `agents/` root:
178
242
 
179
243
  ```
180
244
  agents/max/
@@ -187,7 +251,7 @@ agents/max/
187
251
  name: Max
188
252
  emoji: 🛠️
189
253
  description: Runs and explains this project's workflows
190
- harness: claude # claude | codex | pi — which chat backend runs it
254
+ harness: claude # claude | codex | pi (which chat backend runs it)
191
255
  model: sonnet # optional; harness default when omitted
192
256
  effort: medium # optional: low | medium | high | xhigh | max
193
257
  workflows: [tagline, website-check]
@@ -198,23 +262,26 @@ skills: [report-html] # optional allowlist; omit = all skills, [] = no
198
262
  Sessions with a member are ordinary chats scoped `{ kind: "team", member }`,
199
263
  listed per member in a second sidebar. On the first message the member gets
200
264
  its role plus its connected workflows and knowledge bundles injected as
201
- context — including how to run workflows
265
+ context. That context includes how to run workflows
202
266
  (`KRAFTWERK_YES=1 npx kraftwerk run <workflow> "<request>"`) and how to read
203
- and write knowledge through `kraftwerk knowledge` (writes stamped with the
204
- member's own actor, `<slug>/<harness>`), so it triggers its own workflows
205
- when a request matches and keeps its bundles current. Model/effort ride on
206
- backend-specific channels: the claude adapter takes the model via ACP session
207
- options and the thinking budget via `MAX_THINKING_TOKENS`; codex gets a
208
- `CODEX_CONFIG` env override (`model`, `model_reasoning_effort`); pi gets
209
- `--model`/`--thinking` flags. Members are created and edited in the UI (or by
210
- editing the files — the definition is read fresh for each new session).
267
+ and write knowledge through `kraftwerk knowledge`, with writes stamped with
268
+ the member's own actor, `<slug>/<harness>`. So the member triggers its own
269
+ workflows when a request matches, and keeps its bundles current.
270
+
271
+ Model and effort ride on backend-specific channels. The claude adapter takes
272
+ the model via ACP session options and the thinking budget via
273
+ `MAX_THINKING_TOKENS`. Codex gets a `CODEX_CONFIG` env override (`model`,
274
+ `model_reasoning_effort`). Pi gets `--model` and `--thinking` flags. Members
275
+ are created and edited in the UI, or by editing the files, since the
276
+ definition is read fresh for each new session.
211
277
 
212
278
  ### Skills in chat
213
279
 
214
- Chats — general ones and team sessions — can use skills: Claude-style
215
- instruction packages, one folder per skill with a `SKILL.md` (YAML
216
- frontmatter `name` + `description`, then the instructions). Discovered
217
- roots, each shadowing same-named skills in the roots below it:
280
+ Chats, both general ones and team sessions, can use skills. These are
281
+ Claude-style instruction packages, one folder per skill with a `SKILL.md`
282
+ holding YAML frontmatter (`name`, `description`) and then the instructions.
283
+ Four roots are discovered, each shadowing same-named skills in the roots below
284
+ it:
218
285
 
219
286
  ```
220
287
  <project>/agents/<slug>/skills/<name>/SKILL.md # private to that one team agent
@@ -223,26 +290,26 @@ roots, each shadowing same-named skills in the roots below it:
223
290
  ~/.claude/skills/<name>/SKILL.md # personal, per user
224
291
  ```
225
292
 
226
- Agent skills are visible only to that agent's sessions and always apply —
227
- a member's `skills:` allowlist narrows the shared roots only. They're
228
- managed in the agent profile ("own skills") or by editing the files.
293
+ Agent skills are visible only to that agent's sessions and always apply. A
294
+ member's `skills:` allowlist narrows the shared roots only. Manage them in the
295
+ agent profile under "own skills", or by editing the files.
229
296
 
230
- Every chat lists its visible skills as context ("## Your skills"), so the
297
+ Every chat lists its visible skills as context under "## Your skills", so the
231
298
  agent reaches for one when the request matches. Typing `/` in the composer
232
- opens an autocomplete over them; sending `/<name> <args>` expands the
233
- skill's SKILL.md into the prompt — which is why this works identically on
234
- claude, codex, and pi. Natively on top of that, claude discovers
235
- `.claude/skills` itself (a team member's `skills:` allowlist narrows that
236
- via ACP session options) and pi loads each visible skill folder via
237
- `--skill`. Team members take an optional `skills:` list in `agent.yml`:
238
- omitted means all discovered skills, an empty list means none, otherwise
239
- it's the allowlist. `GET /api/skills` returns what's discovered.
240
-
241
- ### Routines — scheduled prompts
242
-
243
- A member can have routines: cron-scheduled prompts, like standing orders for
244
- an employee. Definitions live next to the member in
245
- `agents/<slug>/routines.yml` (git-tracked); run state (last run, last
299
+ opens an autocomplete over them. Sending `/<name> <args>` expands the skill's
300
+ SKILL.md into the prompt, which is what makes this work identically on claude,
301
+ codex, and pi. On top of that, claude discovers `.claude/skills` natively (a
302
+ team member's `skills:` allowlist narrows that via ACP session options) and pi
303
+ loads each visible skill folder via `--skill`. Team members take an optional
304
+ `skills:` list in `agent.yml`. Omitted means all discovered skills, an empty
305
+ list means none, otherwise it is the allowlist. `GET /api/skills` returns
306
+ what's discovered.
307
+
308
+ ### Routines, or scheduled prompts
309
+
310
+ A member can have routines, cron-scheduled prompts that work like standing
311
+ orders for an employee. Definitions live next to the member in
312
+ `agents/<slug>/routines.yml` and are git-tracked. Run state (last run, last
246
313
  session, errors) lives in `<output>/routines-state.json`.
247
314
 
248
315
  ```yaml
@@ -256,25 +323,25 @@ session, errors) lives in `<output>/routines-state.json`.
256
323
  enabled: true
257
324
  ```
258
325
 
259
- The inspector server runs the scheduler in-process (no external cron): every
260
- due routine opens a fresh session for the member, posts the prompt, and the
261
- run shows up in the sessions sidebar titled "⏰ <name>". Routine sessions run
262
- unattended, so tool-permission requests are auto-approved (the
263
- request/approval pair stays in the thread as an audit trail) — treat routine
264
- prompts with the same trust as `KRAFTWERK_YES=1` workflow runs. Manage
265
- routines on the member page: create/edit/delete, toggle enabled, "run now",
266
- and jump to the last run's session. Missed schedules while the server is
267
- down are skipped, not replayed.
326
+ The inspector server runs the scheduler in-process, so there is no external
327
+ cron to set up. Every due routine opens a fresh session for the member, posts
328
+ the prompt, and shows up in the sessions sidebar titled "⏰ <name>". Routine
329
+ sessions run unattended, so tool-permission requests are auto-approved, with
330
+ the request and approval pair left in the thread as an audit trail. Give
331
+ routine prompts the same trust you would give a `KRAFTWERK_YES=1` workflow
332
+ run. Manage routines on the member page, where you can create, edit and
333
+ delete them, toggle enabled, hit "run now", and jump to the last run's
334
+ session. Schedules missed while the server is down are skipped, not replayed.
268
335
 
269
- ## Context & Knowledge — OKF bundles
336
+ ## Context & Knowledge
270
337
 
271
338
  Alongside runs and chats, a project can keep curated knowledge as
272
339
  [OKF v0.2](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)
273
- bundles (Open Knowledge Format): directories of markdown files with YAML
274
- frontmatter under `knowledge/`, one concept per file. Human-readable,
275
- agent-parseable, diffable in git — and self-describing: frontmatter
276
- carries provenance (`sources`, `generated`), trust (`verified`), and
277
- lifecycle (`status`, `stale_after`).
340
+ bundles, the Open Knowledge Format. A bundle is a directory of markdown files
341
+ with YAML frontmatter under `knowledge/`, one concept per file. Human-readable,
342
+ agent-parseable, diffable in git. The frontmatter describes itself: it carries
343
+ provenance (`sources`, `generated`), trust (`verified`), and lifecycle
344
+ (`status`, `stale_after`).
278
345
 
279
346
  ```
280
347
  knowledge/
@@ -284,10 +351,10 @@ knowledge/
284
351
  playbooks/refunds.md # a concept: YAML frontmatter + markdown body
285
352
  ```
286
353
 
287
- The `kraftwerk knowledge` CLI is the enforced write path — `put` stamps
288
- `generated: { by, at }` with the writing actor, appends the bundle log,
289
- and regenerates the derived `index.md`, so an agent-maintained corpus
290
- stays trustable:
354
+ The `kraftwerk knowledge` CLI is the enforced write path. `put` stamps
355
+ `generated: { by, at }` with the writing actor, appends the bundle log, and
356
+ regenerates the derived `index.md`, which is what keeps an agent-maintained
357
+ corpus trustable:
291
358
 
292
359
  ```bash
293
360
  kraftwerk knowledge init customer-support # new bundle
@@ -302,18 +369,22 @@ kraftwerk knowledge fsck --fix # heal out-of-band edits
302
369
  ```
303
370
 
304
371
  Actors follow the OKF convention: `<producer>/<version>` for agents,
305
- `human:<id>` for people, `process:<id>` for automation. Consumers derive
306
- a **trust tier** per concept: no `verified` ⇒ unverified, verified by
307
- non-human actors ⇒ machine-confirmed, verified by a `human:` actor ⇒
308
- human-reviewed. The inspector's **Context & Knowledge** screen shows
309
- bundles, concepts with trust/status/staleness badges, sources, and the
310
- bundle log — the ✓ verify button records a `human:user` verification, and
311
- "curate in chat" opens a knowledge-scoped chat whose agent knows the OKF
312
- essentials and writes through the CLI.
372
+ `human:<id>` for people, `process:<id>` for automation. Consumers derive a
373
+ trust tier per concept from that:
374
+
375
+ - No `verified` field means unverified.
376
+ - Verified by a non-human actor means machine-confirmed.
377
+ - Verified by a `human:` actor means human-reviewed.
378
+
379
+ The inspector's Context & Knowledge screen shows bundles, concepts with
380
+ trust, status and staleness badges, sources, and the bundle log. The verify
381
+ button records a `human:user` verification. "Curate in chat" opens a
382
+ knowledge-scoped chat whose agent knows the OKF essentials and writes through
383
+ the CLI.
313
384
 
314
385
  Workflows read and write knowledge through the same CLI via a
315
- [CLI grant](#cli-grants) — the trust model stays intact because every
316
- agent write is stamped with its actor:
386
+ [CLI grant](#cli-grants). The trust model stays intact because every agent
387
+ write is stamped with its actor:
317
388
 
318
389
  ```yaml
319
390
  clis:
@@ -323,7 +394,7 @@ agents:
323
394
  clis: [npx kraftwerk knowledge]
324
395
  ```
325
396
 
326
- ## The agent — four axes
397
+ ## The agent's four axes
327
398
 
328
399
  ```ts
329
400
  export const desloper = defineAgent({
@@ -334,7 +405,7 @@ export const desloper = defineAgent({
334
405
  effort: "high", // optional: low | medium | high | xhigh | max
335
406
  tools: ["Read", "Write", "Edit"], // governance: capability boundary
336
407
  persona: `Du bist Lektor:in ...`, // WHO: the system prompt
337
- clis: { // optional: CLI grants — the hint is injected
408
+ clis: { // optional: CLI grants, the hint is injected
338
409
  git: "Versionierung; nach jedem Schritt committen", // into the persona ONCE
339
410
  },
340
411
  mcp: { // optional: MCP servers (governance, like tools)
@@ -344,18 +415,18 @@ export const desloper = defineAgent({
344
415
  ```
345
416
 
346
417
  The task arrives per phase, so one agent can serve several phases. Phases on
347
- the same harness share one resumed session (an agent sees the conversation so
348
- far but always speaks with its own persona); phases on different harnesses
349
- share state through the run files only.
418
+ the same harness share one resumed session, where an agent sees the
419
+ conversation so far but always speaks with its own persona. Phases on
420
+ different harnesses share state through the run files only.
350
421
 
351
- ## Primitives
422
+ ## Building blocks
352
423
 
353
- | Primitive | What it does |
424
+ | Building block | What it does |
354
425
  | --------- | ------------ |
355
426
  | `defineAgent` ([src/agent.ts](src/agent.ts)) | persona + model/effort + tools + harness |
356
- | `Run.agentPhase({name, agent, prompt, gates})` ([src/run.ts](src/run.ts)) | spawn → parse envelope → run gates → correct in-session (bounded by `maxGateRetries`) |
427
+ | `Run.agentPhase({name, agent, prompt, gates})` ([src/run.ts](src/run.ts)) | spawn, parse envelope, run gates, correct in-session (bounded by `maxGateRetries`) |
357
428
  | `Run.codePhase(name, fn)` | deterministic step, timed and traced |
358
- | Gates ([src/gates.ts](src/gates.ts)) | post-execution file checks: `fileNonEmpty`, `slotsFilled`, `containsText` — or your own `Gate` |
429
+ | Gates ([src/gates.ts](src/gates.ts)) | post-execution file checks: `fileNonEmpty`, `slotsFilled`, `containsText`, or your own `Gate` |
359
430
  | Envelope ([src/envelope.ts](src/envelope.ts)) | every phase prompt ends with `envelopeContract(phase)`; `parseEnvelope` enforces it |
360
431
  | Stats ([src/stats.ts](src/stats.ts)) | per-phase attempts/time/tokens/cost, `run.printSummary()` renders the table |
361
432
  | `runCli(workflows)` ([src/cli.ts](src/cli.ts)) | registry CLI: `npm start -- <name> [--yes] [--verbose] "<request>"` |
@@ -375,20 +446,22 @@ same interface ([src/harness.ts](src/harness.ts)):
375
446
  | Hermetic | `--setting-sources ""` | `--ignore-user-config` | `--no-context-files` |
376
447
  | MCP | `--mcp-config` + `--strict-mcp-config`, allowlist `mcp__<name>` | `-c mcp_servers.*` + `--approve-for-me` (headless approvals) | not supported (own extension system) |
377
448
  | CLIs | scoped allowlist `Bash(<name>:*)` | sandbox runs them anyway (hint only) | plain `bash` tool (no scoping) |
378
- | Quirks | — | no system-prompt flag (persona prepended to prompt); governance = workspace-write sandbox, not per-tool | `effort` maps 1:1 to `--thinking`; tool names lowercased |
449
+ | Quirks | none | no system-prompt flag (persona prepended to prompt); governance = workspace-write sandbox, not per-tool | `effort` maps 1:1 to `--thinking`; tool names lowercased |
450
+
451
+ Prerequisites per harness:
379
452
 
380
- Prerequisites: **claude** — Claude Code installed + logged in. **codex** —
381
- `brew install --cask codex` + `codex login`. **pi** — `npm install -g
382
- @earendil-works/pi-coding-agent`; Anthropic models reuse the Claude
383
- subscription OAuth, other vendors need their key in the env (check with
384
- `pi auth check --provider deepseek`).
453
+ - **claude** needs Claude Code installed and logged in.
454
+ - **codex** needs `brew install --cask codex` and `codex login`.
455
+ - **pi** needs `npm install -g @earendil-works/pi-coding-agent`. Anthropic
456
+ models reuse the Claude subscription OAuth, other vendors need their key in
457
+ the env (check with `pi auth check --provider deepseek`).
385
458
 
386
459
  ## YAML workflows
387
460
 
388
- Linear workflows can be pure config — GitHub-Actions-flavored (`steps`,
389
- `runs-on`, `${{ request }}` / `${{ agent }}`). The canonical form is a
390
- **folder**: `workflow.yml` holds agents + steps, long prompts live as files
391
- next to it. Loaded with `loadWorkflow(path)` and registered like any other
461
+ Linear workflows can be pure config, GitHub-Actions-flavored with `steps`,
462
+ `runs-on`, and `${{ request }}` / `${{ agent }}`. The canonical form is a
463
+ folder. `workflow.yml` holds agents and steps, long prompts live as files next
464
+ to it. Load it with `loadWorkflow(path)` and register it like any other
392
465
  workflow:
393
466
 
394
467
  ```
@@ -421,25 +494,26 @@ steps:
421
494
  - contains: { file: brand.md, text: "## Tonalitaet", label: Tonalitaet }
422
495
  ```
423
496
 
424
- Running it needs no code at all — `kraftwerk run tagline "..."` discovers
425
- the folder. Programmatic registration works too:
497
+ Running it needs no code at all, because `kraftwerk run tagline "..."`
498
+ discovers the folder. Programmatic registration works too:
426
499
 
427
500
  ```ts
428
501
  const tagline = await loadWorkflow(path.join(import.meta.dirname, "workflows/tagline"));
429
502
  runCli({ [tagline.name]: tagline });
430
503
  ```
431
504
 
432
- Single-line `prompt:`/`persona:`/`workspace:` values are file references
433
- inside the folder; multiline values stay inline (a plain single `.yml` file
434
- with everything inline works too). `${{ agent }}` interpolates the step's
435
- agent id — three jury steps can share one `prompts/assess.md` that writes
436
- `verdict-${{ agent }}.md` (see the pitch workflow). The engine appends the
505
+ Single-line `prompt:`, `persona:` and `workspace:` values are file references
506
+ inside the folder. Multiline values stay inline, and a plain single `.yml`
507
+ file with everything inline works too. `${{ agent }}` interpolates the step's
508
+ agent id, so three jury steps can share one `prompts/assess.md` that writes
509
+ `verdict-${{ agent }}.md`, as the pitch workflow does. The engine appends the
437
510
  envelope contract to every step prompt itself.
438
511
 
439
- **Validation**: [`schema/workflow.schema.json`](schema/workflow.schema.json)
440
- (strict — unknown keys are errors, editors autocomplete via the
441
- `# yaml-language-server: $schema=…` line) plus semantic checks (agent
442
- references, duplicate steps, variables, referenced files):
512
+ Validation runs against
513
+ [`schema/workflow.schema.json`](schema/workflow.schema.json), which is strict:
514
+ unknown keys are errors, and editors autocomplete via the
515
+ `# yaml-language-server: $schema=…` line. Semantic checks cover agent
516
+ references, duplicate steps, variables, and referenced files.
443
517
 
444
518
  ```bash
445
519
  kraftwerk validate # all discovered workflows
@@ -447,17 +521,17 @@ kraftwerk validate src/workflows/tagline # specific paths
447
521
  npm start -- validate <path> # runCli consumers (TS registry)
448
522
  ```
449
523
 
450
- Gates: `file_non_empty: <file>`, `slots_filled: <file>`,
524
+ Gates come in four forms: `file_non_empty: <file>`, `slots_filled: <file>`,
451
525
  `contains: {file, text, label?}`, and `check: <script>` (or
452
- `check: {run, label?}`) — a bash validation script executed in the run
453
- directory; exit 0 passes, non-zero fails and everything the script printed
454
- becomes the failure message, verbatim in the correction prompt. Single-line
455
- values reference a file inside the workflow folder, like `run:`.
456
-
457
- Steps take an optional `if:` — deterministic preconditions in the same
458
- forms as gates, evaluated against the run directory just before the step.
459
- Any unmet precondition SKIPS the step (traced as `phase_skipped`, run
460
- continues) — the way to avoid spawning an agent when a previous script
526
+ `check: {run, label?}`). The last one is a bash validation script executed in
527
+ the run directory. Exit 0 passes. Non-zero fails, and everything the script
528
+ printed becomes the failure message, verbatim in the correction prompt.
529
+ Single-line values reference a file inside the workflow folder, like `run:`.
530
+
531
+ Steps take an optional `if:`, holding deterministic preconditions in the same
532
+ forms as gates, evaluated against the run directory just before the step. Any
533
+ unmet precondition skips the step, traced as `phase_skipped` while the run
534
+ continues. This is how you avoid spawning an agent when a previous script
461
535
  found nothing to do:
462
536
 
463
537
  ```yaml
@@ -473,16 +547,16 @@ found nothing to do:
473
547
  Living examples:
474
548
  [`../agent-playground/src/workflows/tagline/`](../agent-playground/src/workflows/tagline/)
475
549
  and [`../agent-playground/src/workflows/pitch/`](../agent-playground/src/workflows/pitch/);
476
- `check`/`if` in [`../agent-playground/src/workflows/helpdesk-check/`](../agent-playground/src/workflows/helpdesk-check/).
477
- v1 is deliberately linear — approval loops and AGENTS.md-style context files
478
- stay on the roadmap (skills exist in chat, not in workflow runs); anything
479
- non-linear is a TS workflow.
550
+ `check` and `if` in [`../agent-playground/src/workflows/helpdesk-check/`](../agent-playground/src/workflows/helpdesk-check/).
551
+ v1 is deliberately linear. Approval loops and AGENTS.md-style context files
552
+ stay on the roadmap, and skills exist in chat rather than in workflow runs.
553
+ Anything non-linear is a TS workflow.
480
554
 
481
555
  ### MCP servers alongside the workflow
482
556
 
483
- A workflow folder can carry its own MCP servers; agents opt in by name
484
- (governance, like `tools`). Relative files resolve inside the folder,
485
- absolute paths and `url:` entries hook up external/remote servers:
557
+ A workflow folder can carry its own MCP servers, and agents opt in by name,
558
+ the same governance model as `tools`. Relative files resolve inside the
559
+ folder. Absolute paths and `url:` entries hook up external or remote servers:
486
560
 
487
561
  ```yaml
488
562
  mcp:
@@ -498,20 +572,20 @@ agents:
498
572
  mcp: [calculator] # this agent may use these servers
499
573
  ```
500
574
 
501
- The stdio server is any MCP server (e.g. `@modelcontextprotocol/sdk` +
502
- `server.tool(...)` + `StdioServerTransport`, its deps in the consumer's
503
- `package.json`). On claude the servers are passed hermetically
504
- (`--strict-mcp-config`) and the allowlist gains `mcp__<name>`; on codex
505
- they become `-c mcp_servers.*` overrides and the phase runs with
506
- `--approve-for-me` so headless MCP calls get approved; `runs-on: pi` +
507
- `mcp` is rejected at validation time. Living example:
575
+ The stdio server is any MCP server, for instance `@modelcontextprotocol/sdk`
576
+ with `server.tool(...)` and `StdioServerTransport`, its deps declared in the
577
+ consumer's `package.json`. On claude the servers are passed hermetically with
578
+ `--strict-mcp-config` and the allowlist gains `mcp__<name>`. On codex they
579
+ become `-c mcp_servers.*` overrides and the phase runs with `--approve-for-me`
580
+ so headless MCP calls get approved. Combining `runs-on: pi` with `mcp` is
581
+ rejected at validation time. Living example:
508
582
  [`../agent-playground/src/workflows/rechner/`](../agent-playground/src/workflows/rechner/).
509
583
 
510
584
  ### CLI grants
511
585
 
512
- For existing command-line tools an MCP server is overkill — declare them
513
- once and grant per agent, so no step prompt has to repeat which CLIs
514
- exist or how to call them:
586
+ For command-line tools that already exist, an MCP server is more than the job
587
+ needs. Declare them once and grant them per agent, so no step prompt has to
588
+ repeat which CLIs exist or how to call them:
515
589
 
516
590
  ```yaml
517
591
  clis: # command prefix -> one-line usage hint
@@ -523,37 +597,38 @@ agents:
523
597
  clis: [my, git] # this agent may call these via Bash
524
598
  ```
525
599
 
526
- The hint is injected into the agent's persona ONCE (that's the point —
527
- step prompts stay clean). Per harness: **claude** additionally scopes the
528
- Bash allowlist to `Bash(<name>:*)` — the granted prefixes run headless
529
- without approval, everything else keeps claude's default judgment
530
- (read-only commands auto-approve, mutating ones are denied). **codex**
531
- needs nothing (the workspace-write sandbox runs commands anyway).
532
- **pi** has no per-command scoping — a grant enables the plain `bash`
533
- tool.
600
+ The hint is injected into the agent's persona ONCE. That is the whole point,
601
+ since it keeps step prompts clean.
602
+
603
+ Each harness handles the grant differently. Claude also scopes the Bash
604
+ allowlist to `Bash(<name>:*)`, so the granted prefixes run headless without
605
+ approval while everything else keeps claude's default judgment, auto-approving
606
+ read-only commands and denying mutating ones. Codex needs nothing, because the
607
+ workspace-write sandbox runs commands anyway. Pi has no per-command scoping, so
608
+ a grant enables the plain `bash` tool.
534
609
 
535
610
  ## Used by
536
611
 
537
- - [`../agent-playground/`](../agent-playground/) — the in-repo consumer with
538
- the YAML example workflows (`tagline`, `pitch`, `rechner` with its own
539
- MCP server, `website-check` with script steps).
540
- - `nn-content-workflow-2` (in the local `langgraph/` experiments folder,
541
- outside this repo) — the netnode.ch content board and Matomo report
542
- generator; the full ADW pattern including the engineer approval gate and
543
- revision loop.
612
+ - [`../agent-playground/`](../agent-playground/) is the in-repo consumer with
613
+ the YAML example workflows: `tagline`, `pitch`, `rechner` with its own
614
+ MCP server, and `website-check` with script steps.
615
+ - `nn-content-workflow-2` lives in the local `langgraph/` experiments folder
616
+ outside this repo. It generates the netnode.ch content board and Matomo
617
+ report, and shows the full ADW pattern including the engineer approval gate
618
+ and revision loop.
544
619
 
545
620
  To scaffold a new workflow, use the repo-root skill `/new-workflow`.
546
621
 
547
622
  ## Developer
548
623
 
549
- Source is TypeScript under `src/`; the published package ships compiled
550
- JavaScript + type declarations under `dist/` (built by `tsc -p
551
- tsconfig.build.json`). The bin shim `bin/kraftwerk.js` runs the TS source
552
- via `tsx` whenever `src/` is present (dev checkout, `npm link`) — edits
553
- are always live, a stale `dist/` can never shadow them. Published
554
- installs contain no `src/`, so they take the compiled `dist/` path.
555
- `KRAFTWERK_DIST=1 kraftwerk …` forces `dist/` from the checkout, e.g. to
556
- verify a fresh build.
624
+ Source is TypeScript under `src/`. The published package ships compiled
625
+ JavaScript and type declarations under `dist/`, built by
626
+ `tsc -p tsconfig.build.json`. The bin shim `bin/kraftwerk.js` runs the TS
627
+ source via `tsx` whenever `src/` is present, which covers a dev checkout and
628
+ `npm link`. Edits are always live, and a stale `dist/` can never shadow them.
629
+ Published installs contain no `src/`, so they take the compiled `dist/` path.
630
+ `KRAFTWERK_DIST=1 kraftwerk …` forces `dist/` from the checkout, which is
631
+ handy for verifying a fresh build.
557
632
 
558
633
  ```bash
559
634
  npm run typecheck # tsc --noEmit over src/
@@ -570,11 +645,11 @@ npm link # global `kraftwerk` command from this checkout (no build ne
570
645
  npm publish # runs npm run build first via prepublishOnly
571
646
  ```
572
647
 
573
- The tarball is whitelisted via `files`: `bin/`, `dist/`, `runner/`
574
- (Dockerfile for sandboxed runs), `schema/` (workflow JSON schema), and
575
- `inspector/dist/` (the prebuilt web UI for `kraftwerk ui` — built by
576
- `prepublishOnly`, served by the dependency-free server compiled into
577
- `dist/inspector/`) — no `src/` or examples. Check with `npm pack
578
- --dry-run` before a release. Runtime deps stay regular `dependencies`;
579
- `tsx`, `typescript`, and the inspector's Vite/React toolchain are
580
- dev-only, so consumers install none of them.
648
+ The `files` field whitelists the tarball: `bin/`, `dist/`, `runner/` (the
649
+ Dockerfile for sandboxed runs), `schema/` (the workflow JSON schema), and
650
+ `inspector/dist/` (the prebuilt web UI for `kraftwerk ui`, built by
651
+ `prepublishOnly` and served by the dependency-free server compiled into
652
+ `dist/inspector/`). No `src/`, no examples. Check with `npm pack --dry-run`
653
+ before a release. Runtime deps stay regular `dependencies`, while `tsx`,
654
+ `typescript`, and the inspector's Vite and React toolchain are dev-only, so
655
+ consumers install none of them.