@zenera/cli 1.1.14 → 1.1.16

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 (32) hide show
  1. package/README.md +563 -206
  2. package/dist/banner.d.ts +5 -1
  3. package/dist/banner.js +2 -2
  4. package/dist/command.d.ts +5 -0
  5. package/dist/commands/init.js +46 -2
  6. package/dist/main.js +10 -6
  7. package/dist/validate.js +1 -1
  8. package/package.json +2 -2
  9. package/templates/editor/.github/copilot-instructions.md +253 -218
  10. package/templates/editor/.github/prompts/new-agent.prompt.md +6 -6
  11. package/templates/editor/.github/prompts/new-skill.prompt.md +7 -7
  12. package/templates/editor/.github/prompts/review-project.prompt.md +1 -1
  13. package/templates/editor/.github/prompts/sync-with-spec.prompt.md +26 -26
  14. package/templates/editor/.github/skills/zen-cli/SKILL.md +62 -6
  15. package/templates/editor/.github/skills/zen-cli/references/check.md +3 -3
  16. package/templates/editor/.github/skills/zen-cli/references/faker.md +14 -14
  17. package/templates/editor/.github/skills/zen-cli/references/frame.md +6 -6
  18. package/templates/editor/.github/skills/zen-cli/references/inspect.md +3 -3
  19. package/templates/editor/.github/skills/zen-cli/references/keys.md +9 -9
  20. package/templates/editor/.github/skills/zen-cli/references/memory.md +11 -11
  21. package/templates/editor/.github/skills/zen-cli/references/models.md +8 -8
  22. package/templates/editor/.github/skills/zen-cli/references/projects.md +3 -3
  23. package/templates/editor/.github/skills/zen-cli/references/rag.md +191 -56
  24. package/templates/editor/.github/skills/zen-cli/references/run.md +3 -3
  25. package/templates/editor/.github/skills/zen-cli/references/sandbox.md +8 -8
  26. package/templates/editor/.github/skills/zen-memory/SKILL.md +50 -50
  27. package/templates/editor/.github/skills/zen-rag-docs/SKILL.md +71 -48
  28. package/templates/editor/.github/skills/zen-rag-schema/SKILL.md +67 -67
  29. package/templates/editor/.vscode/settings.json +5 -2
  30. package/templates/project/INSTRUCTIONS.md +1 -1
  31. package/templates/project/assets/README.md +1 -1
  32. package/templates/project/sandbox/Dockerfile.tmpl +3 -0
package/README.md CHANGED
@@ -1,28 +1,103 @@
1
+ <div align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/andreyryabov/ZeneraNeo/main/docs/imgs/banner-cli-dark.svg">
4
+ <img src="https://raw.githubusercontent.com/andreyryabov/ZeneraNeo/main/docs/imgs/banner-cli-light.svg" alt="ZENERA CLI" width="720">
5
+ </picture>
6
+
7
+ [![npm](https://img.shields.io/npm/v/@zenera/cli.svg)](https://www.npmjs.com/package/@zenera/cli)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/andreyryabov/ZeneraNeo/blob/main/LICENSE)
9
+ [![Node](https://img.shields.io/badge/node-%E2%89%A524-brightgreen.svg)](https://nodejs.org)
10
+
11
+ </div>
12
+
1
13
  # @zenera/cli
2
14
 
3
- **`zen` — put a team of AI agents to work on your problem, in one command.**
15
+ **`zen` - build specialist agents. Share them. Run them from the command line.**
4
16
 
5
- Describe the job in plain language and you get back a working multi-agent
6
- system: specialists that reason, read and write files, run real commands, look
7
- things up when they need to, and hand work to each other until the job is done.
8
- No framework to learn, no application to build, no glue code to maintain.
17
+ `@zenera/cli` is the command-line package in the
18
+ [ZeneraNeo](https://github.com/andreyryabov/ZeneraNeo) toolkit. It creates,
19
+ runs, tests and improves the specialized agent systems defined by a project.
9
20
 
10
- The system is a folder — so it is yours. Commit it, review it, improve it, send
11
- it to a colleague, or start from one someone else already built and make it your
12
- own. It runs the same everywhere, on whichever models you prefer, and it never
13
- carries your keys with it.
21
+ Not one general assistant that is passable at everything - a team built for the
22
+ work you actually keep doing, under a name you can type:
14
23
 
15
- [![npm](https://img.shields.io/npm/v/@zenera/cli.svg)](https://www.npmjs.com/package/@zenera/cli)
16
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
17
- [![Node](https://img.shields.io/badge/node-%E2%89%A524-brightgreen.svg)](https://nodejs.org)
24
+ ```sh
25
+ zen run accountant "Q3 - match the receipts drawer to the ledger, then update the return"
26
+ zen run accountant "this MSA against our playbook - redlines, and what I must not sign"
27
+ zen run analyst "why churn doubled in the EU accounts - the exports are in this folder"
28
+ zen run analyst "what changed in EU battery regulation this year, with sources"
29
+ zen run devops "this week's advisories - which of them actually reach our code"
30
+ zen run devops "the 03:12 outage - timeline, contributing factors, owners"
31
+ ```
32
+
33
+ Three agents there, six things asked of them: an agent you have built is not a
34
+ script with one job, it is a specialist you keep going back to. And it is work
35
+ that comes back - every quarter, every sprint, every deal - which is exactly the
36
+ work nobody has shipped you a product for.
37
+
38
+ Once the agent exists it is a command: it works on the files you are standing
39
+ in, runs real commands in a container, searches the web when it has to, and
40
+ records every token it spent doing it.
41
+
42
+ Getting there is three beats:
43
+
44
+ 1. **Describe it.** You write `SPECIFICATION.md` - what the system is for,
45
+ which specialists it needs, what each may reach for, what _done_ means. The
46
+ rest of the folder is built to implement it.
47
+ 2. **Test it.** `zen check` validates the project and every file it names,
48
+ `zen run` exercises it for real, `zen inspect` opens the record of what it
49
+ actually did. When it comes out wrong you fix the specification, not the
50
+ prompts, and go round again.
51
+ 3. **Share it.** It is a folder - commit it, review it in a pull request, send
52
+ it. One `zen init <dir>` on a project that arrived by clone registers it
53
+ without touching a thing, and it is their command now too.
18
54
 
19
55
  > Part of [ZeneraNeo](https://github.com/andreyryabov/ZeneraNeo). This is an
20
- > open-source side project for experimentation and chore work — **not** the
56
+ > open-source side project for experimentation and chore work - **not** the
21
57
  > official Zenera AI Platform. It carries no support or stability promises.
22
58
 
59
+ ## What you build
60
+
61
+ Half a page of specification turns into a system like one of these. Nothing here
62
+ ships with the CLI - each is a folder somebody wrote, and could send you.
63
+
64
+ | Project | The team inside it |
65
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | **accountant** | A bookkeeper that reads and classifies every receipt, statement, invoice and agreement in the folder; a reconciler that holds each against a ledger line or a playbook position and lists what has no pair; a preparer that fills the return or drafts the redlines; a checker that refuses anything no document substantiates. |
67
+ | **analyst** | A planner that splits the question into branches; researchers that run in parallel over your files and the web; a statistician that writes and runs Python in the sandbox; an editor that joins it into one answer where every claim carries its source. |
68
+ | **devops** | A gatherer that pulls advisories, logs, alerts and deploys into one picture; a tracer that works out whether a vulnerable path is reachable at all, or which change actually broke it; an engineer that plans the fix, applies it a step at a time, and stops the moment a check fails. |
69
+
70
+ Build one, commit it, and it runs the same on anyone else's machine - on
71
+ whichever models they prefer, and without carrying your keys.
72
+
73
+ ## What makes it different
74
+
75
+ Most tools give you one agent, or a framework and an empty file. This is a
76
+ factory: it designs the system for you, then keeps it honest.
77
+
78
+ - **You state the problem; a meta-agent designs the system.** Specialists,
79
+ prompts, tool grants, hand-offs, where it fans out - all of it drawn from what
80
+ you wrote, and checked before it ever runs.
81
+ - **A self-improving loop, not a one-off build.** Build, run, read the record,
82
+ change what was wrong, run again - and the system can drive that loop on
83
+ itself. Failures come back as findings with a cause and a fix, so the next
84
+ version is written rather than debugged.
85
+ - **It watches itself work.** Every run is recorded against the architecture it
86
+ declared - the agent nobody called, the skill that never fired, where the
87
+ tokens went. Nothing to instrument, nothing to sign up for.
88
+ - **Customisable all the way down.** Live in the specification and never look
89
+ lower. Or set the model, tools and memory of a single agent. Or take the
90
+ kernel itself and manage the trajectory turn by turn. No layer is sealed.
91
+ - **Your documents, searchable.** Hybrid retrieval over your files and your API
92
+ descriptions, plus a memory that is a graph rather than a bucket of rows - so
93
+ what an agent learned in March is still there in September.
94
+ - **Integrations it writes itself.** No MCP server to find, no connector to wait
95
+ for. If it has to reach your ERP, your database or a twenty-year-old SOAP
96
+ endpoint, it writes the call and runs it in a sandbox.
97
+
23
98
  ## Install
24
99
 
25
- Node.js 24+. One command — the OpenAI, Anthropic, Google and OpenRouter SDKs
100
+ Node.js 24+. One command - the OpenAI, Anthropic, Google and OpenRouter SDKs
26
101
  all ship with the CLI, so any provider works out of the box.
27
102
 
28
103
  ```sh
@@ -37,50 +112,191 @@ npx @zenera/cli --help
37
112
 
38
113
  ## Quickstart
39
114
 
40
- Four commands, from nothing to an answer:
115
+ From nothing to a specialized agent system:
41
116
 
42
117
  ```sh
43
118
  npm i -g @zenera/cli # every vendor SDK comes with it
44
119
  zen key add openai # asks for the key without showing it; stored in ~/.zenera
45
120
  zen init my-project # scaffolds a project and registers it
46
- cd my-project && zen run "introduce yourself"
121
+ zen open my-project # open the specification and project in your editor
122
+ ```
123
+
124
+ In the editor, describe the job in `SPECIFICATION.md`, then send this in the
125
+ agent chat:
126
+
127
+ ```
128
+ /sync-with-spec
47
129
  ```
48
130
 
49
- Then the rest of the loop:
131
+ It creates the specialists, responsibilities, tool access and handoffs the job
132
+ needs. Back in the terminal, validate the generated system and run it against a
133
+ real workspace:
50
134
 
51
135
  ```sh
52
- zen run # nothing to say yet — a full-screen terminal app (a TUI)
53
- zen check # validate the project and every file it names
136
+ zen check my-project
137
+ cd ~/code/some-repo
138
+ zen run my-project "summarise this repo and write NOTES.md"
139
+ ```
140
+
141
+ Then, day to day:
142
+
143
+ ```sh
144
+ zen run my-project # no prompt given: opens a full-screen terminal app (a TUI)
145
+ zen check my-project # validate the project and every file it names
54
146
  zen inspect # open the last run's report.html
55
147
  zen list --sessions # every project, its sessions and last run
56
- echo "triage this" | zen run --quiet | jq
148
+ echo "triage this" | zen run my-project --quiet | jq
149
+ ```
150
+
151
+ Standing inside the project, the name is optional: a bare `zen run`, `zen check`
152
+ or `zen inspect` means the one you are in.
153
+
154
+ Giving a prompt on the command line skips every question: it starts a fresh
155
+ session and uses the current directory as the workspace, with write access.
156
+ `--session`, `--workspace` and `--read-only` override that.
157
+
158
+ To change what the system does, update `SPECIFICATION.md` and send
159
+ `/sync-with-spec` again. The next section explains that workflow in detail.
160
+
161
+ ## You write the specification; a coding agent writes the system
162
+
163
+ `zen init` writes `SPECIFICATION.md` first, and everything else in the folder
164
+ implements it. Where the two disagree the specification wins, so a change to
165
+ what the system does starts there and not in `agents.yaml`.
166
+
167
+ The one `zen init` writes is not a heading list - it is a true specification of
168
+ the project just scaffolded, so you can read it against the files beside it
169
+ before changing a word:
170
+
171
+ ```markdown
172
+ ## Agents
173
+
174
+ One agent, `default`, which is the entry point and the whole system. It has
175
+ nobody to hand work to, because there is no second job to hand on.
176
+
177
+ ## Done means
178
+
179
+ - The change asked for is in the workspace, or the question is answered from
180
+ what is actually in it.
181
+ - Everything the agent claims a command did, that command actually did - it was
182
+ run, and its output was read.
183
+ - The reply names what changed, file by file, and says what it deliberately did
184
+ not do.
57
185
  ```
58
186
 
59
- Or ask a question from wherever you are and get an answer back:
187
+ ### How you actually edit it
188
+
189
+ `SPECIFICATION.md` is ordinary Markdown, so any editor will do - but the loop is
190
+ meant to be driven from a coding agent's chat, and `zen init` installs the
191
+ prompts that make each step a single command:
60
192
 
61
193
  ```sh
62
- cd ~/code/some-repo
63
- zen run my-project "summarise this repo and write NOTES.md"
194
+ zen open my-project # or, standing in the folder: zen open
64
195
  ```
65
196
 
66
- A prompt on the command line asks nothing: a fresh session, the directory you
67
- are standing in as the workspace, writable. `--session`, `--workspace` and
68
- `--read-only` override that.
197
+ That launches the editor you already use: the one this terminal belongs to, or
198
+ `$ZENERA_EDITOR`, or the first of VS Code, Cursor, Windsurf, Zed, Sublime Text
199
+ or IntelliJ it can find. `--editor <cmd>` names one outright.
200
+
201
+ In the window that opens:
202
+
203
+ 1. **Edit `SPECIFICATION.md`** - what the system is for, which agents exist,
204
+ what each may reach for, and what _done_ means.
205
+ 2. **Open the chat panel and send `/sync-with-spec`.** It reads the
206
+ specification and every file implementing it, works out the difference in
207
+ both directions, and changes the smallest thing that closes each gap.
208
+ 3. **Read `SPECIFICATION-FEEDBACK.md`**, which it writes for anything it could
209
+ not do without guessing. Answer its questions by editing `SPECIFICATION.md`
210
+ - not by editing prompts - and send `/sync-with-spec` again.
69
211
 
70
- Then open the folder in your editor and tell your coding agent what the system
71
- should do. It writes the agents; `zen run` runs them; `zen inspect` shows you
72
- every request, tool call and token it spent.
212
+ Back in the terminal: `zen check`, then `zen run`.
213
+
214
+ <details>
215
+ <summary>The chat commands <code>zen init</code> installs</summary>
216
+
217
+ They are prompt files under `.github/prompts/`, which VS Code and its forks
218
+ offer as chat slash-commands. Both `zen init` and `zen open` write them fresh,
219
+ so they never go stale - and edits to them do not survive.
220
+
221
+ | In chat | Does |
222
+ | ----------------- | ----------------------------------------------------------- |
223
+ | `/sync-with-spec` | Makes every file match `SPECIFICATION.md`, both directions. |
224
+ | `/review-project` | Reads the project as a reviewer would, and reports. |
225
+ | `/new-agent` | Adds an agent - prompt, wiring and hand-offs. |
226
+ | `/new-skill` | Adds a skill under `agents/skills/`. |
227
+
228
+ In an editor that does not support prompt files, paste the contents of
229
+ `.github/prompts/sync-with-spec.prompt.md` into its chat instead - it is only a
230
+ prompt. Alongside them, `.github/copilot-instructions.md` is the standing brief
231
+ that explains this runtime to whatever agent is reading, and
232
+ `.github/skills/zen-cli/` is the CLI's own reference for it.
233
+
234
+ </details>
235
+
236
+ ### The self-improving loop
237
+
238
+ ```
239
+ edit SPECIFICATION.md → /sync-with-spec → zen check → zen run
240
+ ↑ ↓
241
+ └───── refine the spec <-──── zen inspect <-───────┘
242
+ ```
243
+
244
+ `zen inspect` makes the record visible: the run trace, timing and token
245
+ statistics, agent architecture, and memory used during the run.
246
+
247
+ ![Run inspection: trace, agent architecture, and memory](https://raw.githubusercontent.com/andreyryabov/ZeneraNeo/main/docs/imgs/0910_480.gif)
248
+
249
+ Every step is a command, and everything each one reads or writes is a plain
250
+ file: the specification, the findings `zen check` prints with a code, a location
251
+ and a fix, the record `zen inspect` renders of what the run actually did. So the
252
+ loop does not need you standing in it. Hand the whole cycle to the agent in your
253
+ editor and it runs on the system it just built - test it, read the failure,
254
+ change the sentence in the specification that caused it, rebuild, run again.
255
+ "Fix my project" is one instruction. (The first time round, `scripts/_setup.sh`
256
+ prepares the container the tools run in.)
257
+
258
+ The last turn is the one that matters. When a run comes out wrong the fix is the
259
+ sentence that was missing from the specification, and the prompt edit follows
260
+ from that. Prompts patched directly drift away from the document meant to
261
+ describe them, and a project whose specification is no longer true is a project
262
+ with no specification.
263
+
264
+ Full guide:
265
+ [docs/specification.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/specification.md).
73
266
 
74
267
  ## A worked example
75
268
 
76
- A two-agent system that reads a repository and writes a note about it — the
77
- whole thing, in three files.
269
+ From a sentence to a working two-agent system, without hand-writing any wiring.
78
270
 
79
271
  ```sh
80
272
  zen init repo-notes && cd repo-notes
273
+ zen open # opens this folder in your editor
274
+ ```
275
+
276
+ Replace `SPECIFICATION.md` with what you want. This is the whole input - no
277
+ YAML, no prompt files:
278
+
279
+ ```markdown
280
+ # Specification
281
+
282
+ ## Purpose
283
+
284
+ Read a codebase and leave a short written note about it in the workspace.
285
+
286
+ ## Agents
287
+
288
+ - `reader` - explores the workspace and forms a picture of it. May read files
289
+ and list directories, and nothing else. Hands off to `writer`.
290
+ - `writer` - turns that picture into `NOTES.md`. May write to the workspace.
291
+
292
+ ## Done means
293
+
294
+ - `NOTES.md` exists, is Markdown, and is under 40 lines.
295
+ - Every claim in it comes from a file that was actually read.
81
296
  ```
82
297
 
83
- `agents.yaml` — who exists, and what each may reach for:
298
+ Send `/sync-with-spec` in the editor's chat. It writes `agents.yaml` - who
299
+ exists, and what each may reach for:
84
300
 
85
301
  ```yaml
86
302
  default: reader
@@ -100,6 +316,11 @@ agents:
100
316
  handoffs: [reader] # a hand-off does not return by itself; give it a way back
101
317
  ```
102
318
 
319
+ - and one prompt file per agent.
320
+
321
+ <details>
322
+ <summary>The prompt files it writes</summary>
323
+
103
324
  `agents/prompts/reader.md`:
104
325
 
105
326
  ```markdown
@@ -115,6 +336,28 @@ You write the summary you were handed to `NOTES.md`, in Markdown, under 40
115
336
  lines. Then say where you put it and stop.
116
337
  ```
117
338
 
339
+ </details>
340
+
341
+ <details>
342
+ <summary>What each <code>agents.yaml</code> key means</summary>
343
+
344
+ | Key | Is |
345
+ | ------------- | --------------------------------------------------------------------- |
346
+ | `name` | how everything else refers to this agent |
347
+ | `description` | what it is for - read by the _other_ agents when deciding to hand off |
348
+ | `system` | its own brief, in prose, at `agents/prompts/<name>.md` |
349
+ | `model` | this agent's model; the top-level one otherwise |
350
+ | `tools` | what it may reach for - `workspace:*`, `sandbox:*`, a named tool |
351
+ | `handoffs` | who it may pass the work to |
352
+ | `skills` | knowledge pulled in mid-run instead of carried in every prompt |
353
+
354
+ The file is validated strictly at load: an unknown tool, a handoff to nobody, a
355
+ missing prompt file - each fails immediately, naming the offending key, rather
356
+ than surfacing three turns into a run as a confused model. Full reference:
357
+ [docs/agents-yaml.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/agents-yaml.md).
358
+
359
+ </details>
360
+
118
361
  Check it, then point it at a real directory:
119
362
 
120
363
  ```sh
@@ -124,17 +367,23 @@ zen run repo-notes "summarise this repo" # this directory is the workspace
124
367
  zen inspect --project repo-notes --open # what it actually did
125
368
  ```
126
369
 
127
- ## The idea
370
+ That is the end of it: `repo-notes` is now a command like any other, in any
371
+ directory, for as long as the folder exists. Commit the folder and whoever
372
+ clones it types `zen init repo-notes` once - a directory that is already a
373
+ project is registered, not rebuilt, and nothing in it is touched - and the name
374
+ is a command on their machine too.
375
+
376
+ ## A project is a folder
128
377
 
129
- An **agentic project is a folder**. Prompts, agent wiring, skills and tool
130
- selections are files — Markdown and YAML — not code buried inside an
131
- application. That folder can be committed, copied to another machine, reviewed
132
- in a pull request, and handed to someone else who runs it with one command.
133
- Credentials live in `$HOME`, so the project never contains a secret.
378
+ Prompts, agent wiring, skills and tool selections are all files - Markdown and
379
+ YAML - rather than code buried inside an application. So the folder can be
380
+ committed, copied to another machine, reviewed in a pull request, and handed to
381
+ someone else who runs it with one command. Credentials live in `$HOME`, never in
382
+ the project, so there is no secret to strip before sharing it.
134
383
 
135
384
  ```
136
385
  my-project/
137
- SPECIFICATION.md what this is for — the intent
386
+ SPECIFICATION.md what this is for - the intent
138
387
  INSTRUCTIONS.md house rules, prepended to every agent
139
388
  agents.yaml who exists, what they may reach for
140
389
  agents/
@@ -149,15 +398,54 @@ my-project/
149
398
  runs/<id>/ input, output, state, report.html, meta
150
399
  ```
151
400
 
152
- `SPECIFICATION.md` comes first because the rest implements it. The one `zen init`
153
- writes is a true specification of the project it just scaffolded; replace it with
154
- what you are building, then run `/sync-with-spec` in your editor to make the
155
- files match. See
156
- [docs/specification.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/specification.md).
401
+ Everything above the `sessions/` line is the system; everything below it is what
402
+ happened when it ran. Nothing here is tied to the machine it was written on,
403
+ which is the point of the CLI: it makes agentic systems shareable the way
404
+ repositories are shareable.
405
+
406
+ `zen init` also writes `.vscode/` and a `.github/` tree - a standing brief that
407
+ explains this runtime to whatever coding agent you open the folder with, plus
408
+ the prompts and skills it needs to keep the project matching its specification.
157
409
 
158
- `zen init` also writes `.github/copilot-instructions.md` — a standing brief that
159
- explains this runtime to whatever coding agent you have open in that folder, so
160
- you are not expected to hand-author `agents.yaml`.
410
+ ## Concepts
411
+
412
+ - **Project** - a named directory holding a complete agent definition and the
413
+ sessions that have run against it. It is self-describing: `agents.yaml` is
414
+ what makes a directory a project, so moving or cloning it loses nothing.
415
+ - **Session** - a context that persists across runs: one workspace, one memory,
416
+ one store for large files, and a record of everything that happened, added to
417
+ as it goes. Resumable.
418
+ - **Run** - one prompt in, one answer out, inside a session. Recorded in full,
419
+ whether or not you were watching.
420
+ - **Workspace** - the directory the agents may read and write. A prompt given on
421
+ the command line uses the current directory; the TUI offers the session's own
422
+ empty folder instead, and asks before using anything outside it.
423
+ - **Keyring** - `~/.zenera/neo`, readable only by you. See
424
+ [Credentials](#credentials).
425
+ - **Cache** - `~/.zenera/neo/cache`, one place for work already done: vectors,
426
+ parses, model listings, generated mocks. Shared by every project on the
427
+ machine, and never emptied by anything but you. See [Cache](#cache--work-you-have-already-paid-for).
428
+
429
+ ## Sandbox commands
430
+
431
+ Agents use an isolated container when a project grants them command-line tools.
432
+ `zen run` prepares it automatically before the first command, but these commands
433
+ make setup, diagnostics and cleanup explicit - useful on a new machine or when
434
+ you want to validate a project's execution environment before a run.
435
+
436
+ ```sh
437
+ zen sandbox status # engine, project image and active containers
438
+ zen sandbox up # prepare the execution environment for this project
439
+ zen sandbox pull # pull or build this project's image only
440
+ zen sandbox status --project my-app # inspect a named project from anywhere
441
+ zen sandbox disk # storage used by the engine and known projects
442
+ zen sandbox clean # remove containers created by zen
443
+ ```
444
+
445
+ `status`, `up` and `pull` use the project in the current directory, or accept
446
+ `--project <name|dir>`. `--image <ref>` selects an image explicitly. `clean` and
447
+ `disk` are machine-wide operations, so use them when you mean to inspect or
448
+ remove resources beyond the current project.
161
449
 
162
450
  ## Commands
163
451
 
@@ -166,52 +454,83 @@ you are not expected to hand-author `agents.yaml`.
166
454
  | `init` | Creates a project here, or in `<dir>`, and registers it. |
167
455
  | `list` | Every known project: sessions, last run, whether one is live. |
168
456
  | `open` | Opens a project in your editor. |
169
- | `key` | The credential keyring — add, check, switch, remove. |
170
- | `models` | What this machine can use — list, search, test, pick. |
171
- | `run` | Runs the project — the TUI on a terminal, a single answer otherwise. |
457
+ | `key` | The credential keyring - add, check, switch, remove. |
458
+ | `models` | What this machine can use - list, search, test, pick. |
459
+ | `run` | Runs the project - the TUI on a terminal, a single answer otherwise. |
172
460
  | `inspect` | Opens or rebuilds a run's `report.html`. |
173
- | `memory` | What the agents remember — size, listing, one node, or a whole page. |
461
+ | `memory` | What the agents remember - size, listing, one node, or a whole page. |
174
462
  | `check` | Validates the project and every file it names, and asks the models. |
175
463
  | `sandbox` | Checks and prepares the container that command-line tools run in. |
176
464
  | `cache` | What work has been kept, and getting rid of it. |
177
465
  | `version` | CLI, library and Node versions. |
178
466
 
179
- Commands can also come from a package installed alongside this one, so a new
180
- capability is a subcommand rather than a new binary to remember — one thing on
181
- your path, one keyring, one name. `zen --help` lists them whether or not they
182
- are installed and says what to run if not; nothing is imported until you type
183
- the command, so an uninstalled one costs nothing and an installed one costs
184
- nothing until it is used.
467
+ ### Commands from other packages
468
+
469
+ A command can also come from a package installed alongside this one, so a new
470
+ capability arrives as a subcommand instead of another binary to remember: one
471
+ thing on your path, one keyring, one name. `zen --help` lists these whether or
472
+ not they are installed, and tells you what to install if not. Nothing is
473
+ imported until you type the command, so an uninstalled one costs you nothing and
474
+ an installed one costs nothing until it is used.
185
475
 
186
476
  | Command | Package | Does |
187
477
  | ------- | --------------- | ---------------------------------------------- |
188
478
  | `faker` | `@zenera/faker` | A mock API from an openapi/swagger document. |
189
479
  | `rag` | `@zenera/rag` | Search an openapi/swagger document as a graph. |
190
480
 
481
+ **`zen faker`** turns an OpenAPI or Swagger description into a working mock
482
+ server. It generates behavior for each route, checks the result against the
483
+ response contract, and reuses the generated implementation on later requests.
484
+
191
485
  ```sh
192
486
  npm i -g @zenera/faker
193
487
  zen faker serve api/openapi.yaml --port 8787 # a working mock, bodies written by a model
194
488
 
489
+ curl -s localhost:8787/users/12324
490
+ # { "user_id": 12324, "email": "brooke.hoffman@example.org", … }
491
+ ```
492
+
493
+ **`zen rag`** adds advanced retrieval for the knowledge an agent needs to work
494
+ from: documentation and API schemas. Document indexes support semantic,
495
+ full-text and hybrid search with passages returned alongside their source lines.
496
+ Schema indexes add graph-aware retrieval across operations, types, fields,
497
+ requests and responses, so an answer can include the connected API context
498
+ rather than an isolated match.
499
+
500
+ ```sh
195
501
  npm i -g @zenera/rag
196
502
  zen rag schema index --embedding openai:text-embedding-3-small ./specs/*.yaml
197
503
  zen rag schema search --output-property "user billing history" --format ts
504
+
505
+ zen rag docs index ./handbook --embedding openai:text-embedding-3-small
506
+ zen rag docs search "how does failover work when the primary is unreachable"
198
507
  ```
199
508
 
200
- They use this keyring and these credentials, so there is nothing new to
201
- configure. Details:
509
+ Use [@zenera/rag](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/rag/README.md)
510
+ for its complete retrieval capabilities and examples. Details:
202
511
  [@zenera/faker](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/faker/README.md)
203
512
  ·
204
513
  [@zenera/rag](https://github.com/andreyryabov/ZeneraNeo/blob/main/packages/rag/README.md).
205
514
 
206
- Global flags: `-h/--help`, `-v/--version`, `--json`, `-C <dir>`. Exit codes: `0`
207
- ok, `1` the run failed, `2` bad invocation, `3` invalid project, `4` no usable
208
- credential, `5` sandbox unavailable.
209
-
210
- `stdout` is the answer, `stderr` is the narration, and `--json` is on every
211
- command — so `zen run … | jq` is a supported way to use it, not an accident.
515
+ ### Conventions
212
516
 
213
517
  The binary is installed under three names: `zen`, `zn` and `zenera`.
214
518
 
519
+ Global flags: `-h/--help`, `-v/--version`, `--json`, `-C <dir>`.
520
+
521
+ `stdout` carries the answer and `stderr` carries the narration, and `--json`
522
+ works on every command - so `zen run … | jq` is a supported way to use this, not
523
+ an accident.
524
+
525
+ | Exit code | Meaning |
526
+ | --------- | -------------------- |
527
+ | `0` | ok |
528
+ | `1` | the run failed |
529
+ | `2` | bad invocation |
530
+ | `3` | invalid project |
531
+ | `4` | no usable credential |
532
+ | `5` | sandbox unavailable |
533
+
215
534
  ## Credentials
216
535
 
217
536
  One keyring serves every provider, and a key goes in the same way whatever it
@@ -222,36 +541,40 @@ zen key add <provider> # asks for the key without showing it
222
541
  zen key add <provider> < key.txt # or pipe it in
223
542
  ```
224
543
 
225
- The value is never given as an argument — a command line is visible to anyone
544
+ The value is never passed as an argument. A command line is visible to anyone
226
545
  listing running processes, is saved in your shell history and is captured in CI
227
- logs — so piping it in and the hidden prompt are the only two ways. Entries live
228
- in `~/.zenera/neo/keys.json`, in a file only you can read, and are copied into
229
- the environment just before a run, so an environment variable you set yourself
230
- always wins and a project checked out on a machine without `zen` still runs.
546
+ logs - so the hidden prompt and piping it in are the only two ways.
547
+
548
+ Entries live in `~/.zenera/neo/keys.json`, in a file only you can read. They are
549
+ copied into the environment just before a run, which has two consequences worth
550
+ knowing: an environment variable you set yourself always wins, and a project
551
+ checked out on a machine without `zen` still runs.
231
552
 
232
553
  | Provider | The value is | Exported as |
233
554
  | ------------ | ------------------------------------- | -------------------------------- |
234
555
  | `openai` | a secret | `OPENAI_API_KEY` |
235
556
  | `anthropic` | a secret | `ANTHROPIC_API_KEY` |
236
- | `google` | a secret — AI Studio | `GEMINI_API_KEY` |
557
+ | `google` | a secret - AI Studio | `GEMINI_API_KEY` |
237
558
  | `vertex` | a path to a service-account JSON file | `GOOGLE_APPLICATION_CREDENTIALS` |
238
559
  | `openrouter` | a secret | `OPENROUTER_API_KEY` |
239
- | `exa` | a secret — for the search tool | `EXA_API_KEY` |
560
+ | `exa` | a secret - for the search tool | `EXA_API_KEY` |
240
561
 
241
562
  `zen key add` verifies the credential against the provider before it finishes,
242
- but stores it either way: a key that cannot be checked right now — offline,
243
- behind a proxy — is not a key that is wrong. `--no-check` skips the call.
563
+ but stores it either way: a key that cannot be checked right now - offline,
564
+ behind a proxy - is not a key that is wrong. `--no-check` skips the call.
244
565
 
245
566
  ### Which key a model uses
246
567
 
247
568
  A model reference is `[provider[/api]:]model`, and the first segment names a
248
569
  **provider, not a vendor**. So `vertex:gemini-3.5-flash` and
249
570
  `google:gemini-3.5-flash` are the same model reached through two different
250
- services, needing two different credentials — and a bare `gpt-5.4-mini` goes to
251
- the default provider, `openai`. `zen check` resolves every reference in a
252
- project against what is stored, says which credential each one needs, and spends
253
- a few tokens asking each of them to answer — the only way to catch a model id
254
- this account is not served. `--no-models` stops before the asking.
571
+ services, needing two different credentials; a bare `gpt-5.4-mini` goes to the
572
+ default provider, `openai`.
573
+
574
+ `zen check` resolves every reference in a project against what is stored and
575
+ says which credential each one needs. It then spends a few tokens asking each
576
+ model to answer, which is the only way to catch a model id this account is not
577
+ served. `--no-models` stops before that.
255
578
 
256
579
  ### Which models you can use
257
580
 
@@ -267,15 +590,20 @@ zen models test vertex:gemini-embedding-001 # one real call, one verdict
267
590
  zen models pick --embedding # the first ref that answers, on stdout
268
591
  ```
269
592
 
270
- Listings are cached for a day in `~/.zenera/neo/cache`. When a provider cannot
271
- be asked the last listing is used and said to be stale; only if there was never
272
- one does a short built-in list stand in.
593
+ Listings are cached for a day in `~/.zenera/neo/cache`. If a provider cannot be
594
+ reached, the last listing is used and marked stale; only when there has never
595
+ been one does a short built-in list stand in.
596
+
597
+ `zen models test` separates three kinds of failure, because each calls for a
598
+ different action:
273
599
 
274
- `test` distinguishes three failures, because they want three different actions.
275
- A **refused** model means the credential was rejected. A **blocked** one means
276
- the credential was accepted and the account then said no — an API switched off,
277
- an empty balance, a model this key was never granted — and the fix comes with
278
- it:
600
+ - **refused** - the provider rejected the credential. Fix or replace the key.
601
+ - **blocked** - the credential was accepted and the account then said no: an API
602
+ switched off, an empty balance, a model this key was never granted. The key is
603
+ fine; the account needs changing, and the command that changes it is printed
604
+ with the error.
605
+ - **unknown** - the question never arrived (offline, proxy, timeout). Nothing is
606
+ wrong with the key; try again.
279
607
 
280
608
  ```
281
609
  $ zen models test vertex:gemini-embedding-001
@@ -285,164 +613,192 @@ error 1 of 1 did not answer
285
613
  find one that does: zen models pick --embedding
286
614
  ```
287
615
 
288
- `pick` tries a short list of candidates one at a time and stops at the first
289
- that works, printing the bare ref on stdout — so recovering from the above is
290
- one substitution, whether a person or an agent is doing it:
616
+ `zen models pick` tries a short list of candidates one at a time, stops at the
617
+ first that works, and prints the bare reference on stdout. So recovering from
618
+ the above is a single substitution, whether a person or an agent is doing it:
291
619
 
292
620
  ```sh
293
621
  zen rag schema index --embedding "$(zen models pick --embedding)" ./specs/*.yaml
294
622
  ```
295
623
 
296
- ### Vertex AI
624
+ ### Setting up each provider
297
625
 
298
- Vertex takes two kinds of credential, and you never say which you are giving:
299
- if the value is a path to a file that exists it is a service-account key, and
300
- otherwise it is treated as an API key. The name you choose for the entry has no
301
- say in it — `vertex/express` is simply an entry called `express`, exactly as
302
- `vertex/prod` is one called `prod`, and either name can hold either kind.
626
+ The shape is the same everywhere - `zen key add <provider>`, then answer the
627
+ prompt. Open the one you need for its specifics.
303
628
 
304
- The usual one is a **service-account JSON file** — give its path, not its
305
- contents. Run the command with nothing piped and it asks:
629
+ <details>
630
+ <summary><strong>OpenAI</strong> - one secret, exported as <code>OPENAI_API_KEY</code></summary>
306
631
 
307
632
  ```sh
308
- zen key add vertex --gcp-location us-central1
309
- # Paste the key, or a path to the file: /Users/you/keys/vertex-sa.json
633
+ zen key add openai
310
634
  ```
311
635
 
312
- The prompt is read by `zen`, not by your shell, so give a full path there — `~`
313
- is not expanded. In a script, pipe the path in instead:
636
+ `openai` is the default provider, so a reference with no prefix comes here:
637
+ `gpt-5.4-mini` and `openai:gpt-5.4-mini` are the same model.
638
+
639
+ </details>
640
+
641
+ <details>
642
+ <summary><strong>Anthropic</strong> - one secret, exported as <code>ANTHROPIC_API_KEY</code></summary>
314
643
 
315
644
  ```sh
316
- echo ~/keys/vertex-sa.json | zen key add vertex --gcp-location us-central1
645
+ zen key add anthropic
646
+ zen run --model anthropic:claude-sonnet-4-5 "summarise this repo"
317
647
  ```
318
648
 
319
- The file is copied into `~/.zenera/neo/keys/`, where only you can read it, so
320
- moving or cleaning up the original later cannot break it.
649
+ </details>
321
650
 
322
- - `--gcp-location <region>` is worth setting. It takes a concrete region, or one
323
- of the endpoints that route across regions: `us` and `eu` pool capacity while
324
- keeping processing inside that territory, `global` takes whatever is free and
325
- promises no residency. `global` pays about ten seconds of cold start on the
326
- first request each process makes — a region answers in about two.
327
- - Which models a location serves is per model, and not guessable. In one
328
- project, `gemini-embedding-2` answered at `us` but 404'd at `us-central1`,
329
- while `gemini-2.5-flash` did the opposite. New models often reach `global`,
330
- `us` and `eu` first. `zen models test vertex:<model>` is what settles it —
331
- `zen key add` only establishes that the credential itself works.
332
- - `--gcp-project <id>` is only needed when the `project_id` inside the file is not
333
- the project you want.
651
+ <details>
652
+ <summary><strong>OpenRouter</strong> - one secret, exported as <code>OPENROUTER_API_KEY</code></summary>
334
653
 
335
- The alternative is an **express-mode API key** — a single secret, stored under
336
- `VERTEX_API_KEY`. It is the Vertex console's way of handing out access without a
337
- service account, and it needs neither a project nor a region, so `--gcp-project`
338
- and `--gcp-location` mean nothing there and are not stored.
654
+ One credential for many vendors' models. The model id keeps its own vendor
655
+ prefix, after the `openrouter:` one:
656
+
657
+ ```sh
658
+ zen key add openrouter
659
+ zen models search haiku --tools --free
660
+ zen run --model openrouter:anthropic/claude-haiku-4.5 "summarise this repo"
661
+ ```
339
662
 
340
- ### Gemini, three ways
663
+ </details>
341
664
 
342
- The same Gemini models are reachable through three different credentials, and
343
- which one you hold decides the prefix a model reference needs.
665
+ <details>
666
+ <summary><strong>Google AI Studio</strong> - one secret, exported as <code>GEMINI_API_KEY</code></summary>
344
667
 
345
- **AI Studio** — one key and nothing else to configure, the shortest way to a
346
- working `gemini-3.5-flash`:
668
+ One key and nothing else to configure: the shortest way to a working Gemini
669
+ model.
347
670
 
348
671
  ```sh
349
- zen key add google # asks for the key without showing it
672
+ zen key add google
350
673
  zen check # google:gemini-3.5-flash now resolves
351
674
  ```
352
675
 
353
- **Vertex, service account** — what production usually runs on. Give the path
354
- and a region, because the file says which project it belongs to but never which
355
- region to call:
676
+ The same Gemini models are also served by Vertex AI, under a different
677
+ credential. Both can be configured at once - they are separate entries for
678
+ separate services, and the prefix on the model reference decides which is used:
356
679
 
357
680
  ```sh
358
- echo ~/keys/vertex-sa.json | zen key add vertex --gcp-location us-central1
681
+ zen run --model google:gemini-3.5-flash "summarise this repo"
682
+ zen run --model vertex:gemini-3.5-flash "summarise this repo"
359
683
  ```
360
684
 
361
- Add `--gcp-project` only when the `project_id` inside the file is not the one you
362
- want to bill:
685
+ </details>
686
+
687
+ <details>
688
+ <summary><strong>Vertex AI</strong> - a service-account file, or an express-mode key</summary>
689
+
690
+ Vertex accepts two kinds of credential, and you never declare which one you are
691
+ giving: if the value is a path to a file that exists it is treated as a
692
+ service-account key, otherwise as an API key. The name you give the entry has no
693
+ bearing on this - `vertex/express` is just an entry called `express`, exactly as
694
+ `vertex/prod` is one called `prod`, and either name can hold either kind.
695
+
696
+ **A service-account JSON file** is the usual one, and what production normally
697
+ runs on. Give its path, not its contents. Run the command with nothing piped and
698
+ it asks:
363
699
 
364
700
  ```sh
365
- echo ~/keys/vertex-sa.json \
366
- | zen key add vertex --gcp-project other-project --gcp-location europe-west4
701
+ zen key add vertex --gcp-location us-central1
702
+ # Paste the key, or a path to the file: /Users/you/keys/vertex-sa.json
367
703
  ```
368
704
 
369
- **Vertex, express mode** — paste the key at the prompt; no flags apply:
705
+ The prompt is read by `zen`, not by your shell, so give a full path there - `~`
706
+ is not expanded. In a script, pipe the path in instead:
370
707
 
371
708
  ```sh
372
- zen key add vertex
709
+ echo ~/keys/vertex-sa.json | zen key add vertex --gcp-location us-central1
373
710
  ```
374
711
 
375
- Holding several at once is the ordinary case. Name them and switch:
712
+ The file is copied into `~/.zenera/neo/keys/`, where only you can read it, so
713
+ moving or cleaning up the original later cannot break it.
714
+
715
+ - `--gcp-location <region>` is worth setting, because the file says which project
716
+ it belongs to but never which region to call. It takes a concrete region, or
717
+ one of the endpoints that route across regions: `us` and `eu` pool capacity
718
+ while keeping processing inside that territory, and `global` takes whatever is
719
+ free and promises no residency. `global` costs about ten seconds of cold start
720
+ on the first request each process makes; a named region answers in about two.
721
+ - Which models a location serves varies per model and is not guessable. In one
722
+ project, `gemini-embedding-2` answered at `us` but 404'd at `us-central1`,
723
+ while `gemini-2.5-flash` did the opposite. New models often reach `global`,
724
+ `us` and `eu` first. `zen models test vertex:<model>` is what settles it -
725
+ `zen key add` only establishes that the credential itself works.
726
+ - `--gcp-project <id>` is only needed when the `project_id` inside the file is
727
+ not the project you want to bill:
376
728
 
377
- ```sh
378
- echo ~/keys/prod-sa.json | zen key add vertex/prod --gcp-location us-central1
379
- echo ~/keys/dev-sa.json | zen key add vertex/dev --gcp-location global
380
- zen key add vertex/express # the express key, same provider
729
+ ```sh
730
+ echo ~/keys/vertex-sa.json \
731
+ | zen key add vertex --gcp-project other-project --gcp-location europe-west4
732
+ ```
733
+
734
+ **An express-mode API key** is the alternative: a single secret, stored under
735
+ `VERTEX_API_KEY`. It is the Vertex console's way of granting access without a
736
+ service account, and it needs neither a project nor a region, so `--gcp-project`
737
+ and `--gcp-location` mean nothing there and are not stored.
381
738
 
382
- zen key use vertex/dev # which one the next run uses
383
- zen key ls --check # all three, and whether they still work
384
- zen key show vertex/prod # masked; --reveal prints the path
739
+ ```sh
740
+ zen key add vertex # paste the key at the prompt; no flags apply
385
741
  ```
386
742
 
387
- `google` and `vertex` can both be configured — they are separate entries for
388
- separate services, and the reference picks:
743
+ **An existing `gcloud` login counts too.** If you have run
744
+ `gcloud auth application-default login`, Vertex works with nothing stored at
745
+ all: `zen key ls` shows that login as `adc`, marked `~` because it came from
746
+ outside the keyring. Anything already set in `GOOGLE_APPLICATION_CREDENTIALS`,
747
+ `VERTEX_API_KEY` or `GEMINI_API_KEY` is listed the same way and wins over the
748
+ keyring, so it is always visible which credential a run will actually use.
749
+
750
+ </details>
751
+
752
+ <details>
753
+ <summary><strong>Exa</strong> - one secret, exported as <code>EXA_API_KEY</code></summary>
754
+
755
+ Not a model provider: this is what backs the search tool an agent reaches for
756
+ when it has to look something up on the web.
389
757
 
390
758
  ```sh
391
- zen run --model google:gemini-3.5-flash "summarise this repo"
392
- zen run --model vertex:gemini-3.5-flash "summarise this repo"
759
+ zen key add exa
393
760
  ```
394
761
 
395
- If you have already run `gcloud auth application-default login`, that login is
396
- itself a usable credential: `zen key ls` shows it as `adc`, marked `~` because
397
- it came from outside the keyring, and Vertex works with nothing stored at all.
398
- Anything already set in `GOOGLE_APPLICATION_CREDENTIALS`, `VERTEX_API_KEY` or
399
- `GEMINI_API_KEY` is listed the same way and wins over the keyring, so it is
400
- always visible which credential a run will actually use.
762
+ </details>
401
763
 
402
- ### More than one key per provider
764
+ <details>
765
+ <summary><strong>More than one key for the same provider</strong></summary>
403
766
 
404
- Entries are named, so a provider can hold several and one of them is active:
767
+ Entries are named, so a provider can hold several, one of which is active:
405
768
 
406
769
  ```sh
407
770
  zen key add openai/work # a second entry
408
- zen key use openai/work # which one runs use
771
+ zen key use openai/work # the one that runs will use
409
772
  zen key ls --check # everything stored, and whether it still works
410
- zen key show vertex/default # masked — --reveal prints the secret
773
+ zen key show vertex/default # masked - --reveal prints the secret
411
774
  zen key env openai # shell exports, for other tools
412
775
  zen key rm openai/work
413
776
  ```
414
777
 
778
+ It is the same for Vertex, where holding several at once is the ordinary case:
779
+
780
+ ```sh
781
+ echo ~/keys/prod-sa.json | zen key add vertex/prod --gcp-location us-central1
782
+ echo ~/keys/dev-sa.json | zen key add vertex/dev --gcp-location global
783
+ zen key add vertex/express # the express key, same provider
784
+
785
+ zen key use vertex/dev # which one the next run uses
786
+ ```
787
+
415
788
  `zen key ls` marks the active entry with `*`, and anything it found outside the
416
- keyring — in your environment, or in a `gcloud` login — with `~`, so it is always
789
+ keyring - in your environment, or in a `gcloud` login - with `~`, so it is always
417
790
  clear where a working provider actually comes from.
418
791
 
419
- ## Concepts
420
-
421
- - **Project** — a named directory holding a complete agent definition and the
422
- sessions that ran against it. Self-describing: `agents.yaml` is what makes it
423
- one, so moving or cloning the directory loses nothing.
424
- - **Session** — a context that persists: one workspace, one memory, one store
425
- for large files, and a record of everything that happened, added to as it
426
- goes. Resumable.
427
- - **Run** — one prompt in, one answer out, inside a session. Recorded in full,
428
- whether or not you were watching.
429
- - **Workspace** — the directory the agents may read and write. A prompt given on
430
- the command line uses the current directory; the TUI offers the session's own
431
- empty folder and confirms anything outside it.
432
- - **Keyring** — `~/.zenera/neo`, readable only by you. Keys are copied into the
433
- environment just before a run, so an environment variable you set yourself
434
- always wins and a project checked out on a machine without `zen` still runs.
435
- - **Cache** — `~/.zenera/neo/cache`, one place for work already done: vectors,
436
- parses, model listings, generated mocks. Shared by every project on the
437
- machine, and never evicted by anything but you.
792
+ </details>
438
793
 
439
- ## What has already been paid for
794
+ ## Cache - work you have already paid for
440
795
 
441
- Embedding a paragraph, parsing a document, asking a provider what it serves —
442
- all expensive, all perfectly repeatable. They are kept in one store,
443
- `~/.zenera/neo/cache/<kind>/`, keyed by every input that produced them. Change
444
- an input and you are asking a different question, which is why nothing in there
445
- is ever invalidated: it is simply never asked for again.
796
+ Embedding a paragraph, parsing a document, asking a provider what it serves: all
797
+ of these are slow or costly, and all of them give the same answer every time. So
798
+ the answers are kept in `~/.zenera/neo/cache/<kind>/`, under a key built from
799
+ every input that produced them. Change any input and the key changes, so you get
800
+ a fresh answer rather than a stale one. That is why entries are never
801
+ invalidated: one that no longer matches anything is simply never read again.
446
802
 
447
803
  ```
448
804
  zen cache ls # what is kept, by kind
@@ -452,17 +808,19 @@ zen cache prune --max-size 2GB # or keep it under a ceiling
452
808
  zen cache clear --kind docs-parse # throw one kind away
453
809
  ```
454
810
 
455
- Age is when an entry was last _used_, not when it was written, so a vector a
456
- weekly rebuild reads is never old. Nothing evicts on its own — a store that
457
- quietly deletes things is only ever noticed when it has deleted the wrong one —
458
- so retention is a decision made out loud, here. Nothing in it is precious
459
- either: every entry is work that can be done again, and the only cost of
460
- removing one is paying for it a second time.
811
+ Age means when an entry was last _read_, not when it was written, so a vector
812
+ that a weekly rebuild keeps using never counts as old.
813
+
814
+ Nothing is ever evicted automatically. A store that quietly deletes things is
815
+ only noticed once it has deleted the wrong one, so every removal is something
816
+ you ask for. Nothing in the cache is precious either: every entry is work that
817
+ can be done again, and the only cost of deleting one is paying for it a second
818
+ time.
461
819
 
462
820
  ## The library underneath
463
821
 
464
822
  This is a shell over
465
- [`@zenera/neo`](https://www.npmjs.com/package/@zenera/neo) — agents, models,
823
+ [`@zenera/neo`](https://www.npmjs.com/package/@zenera/neo) - agents, models,
466
824
  tools, skills, memory and a running record of everything that happened. Use it
467
825
  directly when you want the runtime inside your own application rather than on a
468
826
  terminal:
@@ -470,21 +828,20 @@ terminal:
470
828
 
471
829
  ## Documentation
472
830
 
473
- [docs/specification.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/specification.md)
474
- — writing the specification the project implements ·
475
- [docs/projects.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/projects.md)
476
- — the folder a project is ·
477
- [docs/agents-yaml.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/agents-yaml.md)
478
- — every key in the configuration file.
479
-
480
- [docs/knowledge.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/knowledge.md)
481
- — a searchable document index ·
482
- [docs/integrations.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/integrations.md)
483
- — an API description, searchable and mockable ·
484
- [docs/memory.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/memory.md)
485
- — what agents keep between sessions.
831
+ - [docs/specification.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/specification.md)
832
+ - writing the specification the project implements.
833
+ - [docs/projects.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/projects.md)
834
+ - what the project folder contains, file by file.
835
+ - [docs/agents-yaml.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/agents-yaml.md)
836
+ - every key in the configuration file.
837
+ - [docs/knowledge.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/knowledge.md)
838
+ - building a searchable document index.
839
+ - [docs/integrations.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/integrations.md)
840
+ - making an API description searchable and mockable.
841
+ - [docs/memory.md](https://github.com/andreyryabov/ZeneraNeo/blob/main/docs/memory.md)
842
+ - what agents keep between sessions.
486
843
 
487
844
  ## License
488
845
 
489
- Early days and moving fast — issues, questions and pull requests are welcome.
846
+ Early days and moving fast - issues, questions and pull requests are welcome.
490
847
  [MIT](LICENSE).