@zenera/cli 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -11
- package/dist/audit.d.ts +8 -6
- package/dist/audit.js +14 -22
- package/dist/commands/check.js +34 -7
- package/dist/commands/init.js +71 -11
- package/dist/commands/key.js +126 -36
- package/dist/commands/models.js +3 -3
- package/dist/commands/open.js +2 -2
- package/dist/commands/run.js +3 -0
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +1 -0
- package/dist/keys.d.ts +95 -12
- package/dist/keys.js +175 -34
- package/dist/lib.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/liveness.d.ts +16 -6
- package/dist/liveness.js +74 -23
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.js +58 -7
- package/dist/scaffold.d.ts +21 -21
- package/dist/scaffold.js +132 -204
- package/dist/validate.js +3 -3
- package/package.json +2 -18
- package/templates/{.github → editor/.github}/copilot-instructions.md +7 -6
- package/templates/editor/.github/skills/api-schema-index/SKILL.md +292 -0
- package/templates/editor/.github/skills/zen-cli/SKILL.md +74 -0
- package/templates/editor/.github/skills/zen-cli/references/check.md +92 -0
- package/templates/editor/.github/skills/zen-cli/references/faker.md +111 -0
- package/templates/editor/.github/skills/zen-cli/references/frame.md +119 -0
- package/templates/editor/.github/skills/zen-cli/references/inspect.md +61 -0
- package/templates/editor/.github/skills/zen-cli/references/keys.md +114 -0
- package/templates/editor/.github/skills/zen-cli/references/projects.md +99 -0
- package/templates/editor/.github/skills/zen-cli/references/rag.md +159 -0
- package/templates/editor/.github/skills/zen-cli/references/run.md +104 -0
- package/templates/editor/.github/skills/zen-cli/references/sandbox.md +91 -0
- package/templates/editor/.vscode/settings.json +6 -0
- package/templates/parts/exa.yaml.tmpl +5 -0
- package/templates/parts/model.yaml.tmpl +4 -0
- package/templates/parts/models.yaml.tmpl +10 -0
- package/templates/project/INSTRUCTIONS.md +7 -0
- package/templates/project/SPECIFICATION.md +6 -0
- package/templates/project/agents/prompts/default.md +15 -0
- package/templates/project/agents.yaml.tmpl +44 -0
- package/templates/project/assets/README.md +12 -0
- package/templates/project/gitignore +9 -0
- package/templates/{sandbox → project/sandbox}/Dockerfile +2 -0
- package/templates/.github/skills/zen-cli/SKILL.md +0 -110
- /package/templates/{.github → editor/.github}/prompts/new-agent.prompt.md +0 -0
- /package/templates/{.github → editor/.github}/prompts/new-skill.prompt.md +0 -0
- /package/templates/{.github → editor/.github}/prompts/review-project.prompt.md +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# The frame
|
|
2
|
+
|
|
3
|
+
What is true of every command, whichever one is being run.
|
|
4
|
+
|
|
5
|
+
## Invocation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
zen <command> [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Three names run the same program: `zen`, `zn` and `zenera`. Help and error
|
|
12
|
+
hints name the binary that was actually invoked, so `zn run` is spelled `zn`
|
|
13
|
+
back at you.
|
|
14
|
+
|
|
15
|
+
## Global options
|
|
16
|
+
|
|
17
|
+
| Flag | Meaning |
|
|
18
|
+
| ----------------------- | -------------------------------------------------------- |
|
|
19
|
+
| `-h`, `--help` | The command list, or a command's own help |
|
|
20
|
+
| `-v`, `--version` | The version, and nothing else |
|
|
21
|
+
| `--json` | Machine-readable output on stdout instead of a rendering |
|
|
22
|
+
| `-C`, `--directory <d>` | Act as if run in `<d>` |
|
|
23
|
+
|
|
24
|
+
These are lifted out of the argument list before a command parses its own
|
|
25
|
+
flags, so they work in any position and a command never declares them itself.
|
|
26
|
+
Everything after the command name belongs to that command.
|
|
27
|
+
|
|
28
|
+
## Aliases
|
|
29
|
+
|
|
30
|
+
Not listed in help, but they work.
|
|
31
|
+
|
|
32
|
+
| Alias | Command |
|
|
33
|
+
| -------------------- | --------- |
|
|
34
|
+
| `ls` | `list` |
|
|
35
|
+
| `new` | `init` |
|
|
36
|
+
| `keys` | `key` |
|
|
37
|
+
| `validate`, `doctor` | `check` |
|
|
38
|
+
| `report` | `inspect` |
|
|
39
|
+
| `edit`, `code` | `open` |
|
|
40
|
+
| `mock` | `faker` |
|
|
41
|
+
|
|
42
|
+
## stdout is the answer, stderr is the narration
|
|
43
|
+
|
|
44
|
+
The answer to what was asked goes to stdout: a model's reply, a report, a list.
|
|
45
|
+
Progress, banners, warnings and the spinner go to stderr. So
|
|
46
|
+
`zen run acme "…" > answer.md` captures the answer and nothing else, and
|
|
47
|
+
`2>/dev/null` silences everything but it.
|
|
48
|
+
|
|
49
|
+
`--json` applies to every command and prints one machine-readable document on
|
|
50
|
+
stdout in place of the rendered form. Under `--json` nothing is asked
|
|
51
|
+
interactively — the questions a run would ask are taken as answered.
|
|
52
|
+
|
|
53
|
+
The banner is drawn only when stderr is a terminal, so a piped invocation never
|
|
54
|
+
sees it.
|
|
55
|
+
|
|
56
|
+
## Exit codes
|
|
57
|
+
|
|
58
|
+
| Code | Name | Means |
|
|
59
|
+
| ---- | ----------- | ---------------------------------------------------------- |
|
|
60
|
+
| 0 | ok | It worked |
|
|
61
|
+
| 1 | failed | It ran and did not succeed |
|
|
62
|
+
| 2 | usage | The command line was wrong |
|
|
63
|
+
| 3 | invalid | The project is not valid — bad `agents.yaml`, missing file |
|
|
64
|
+
| 4 | credentials | No usable credential for what was asked |
|
|
65
|
+
| 5 | sandbox | No container engine, or the image could not be prepared |
|
|
66
|
+
|
|
67
|
+
A wrong invocation and a wrong answer are deliberately not the same code, which
|
|
68
|
+
is what a script needs. Failure messages name the offending key or file; a load
|
|
69
|
+
error names the exact path, as in `agents.yaml: agents[1].skills.discovery — …`.
|
|
70
|
+
Read it rather than guessing: the loader is strict on purpose and an unknown key
|
|
71
|
+
is an error, not a value quietly ignored.
|
|
72
|
+
|
|
73
|
+
## Which project
|
|
74
|
+
|
|
75
|
+
Most commands operate on a project directory, resolved in this order:
|
|
76
|
+
|
|
77
|
+
1. `--project <name|dir>` — a registry name or a path.
|
|
78
|
+
2. The working directory, if it is a project (or is inside one).
|
|
79
|
+
3. For `zen run`, a bare first word that names a registered project.
|
|
80
|
+
|
|
81
|
+
`zen check` is the exception: its bare argument is a directory if one is there
|
|
82
|
+
and a registered name otherwise, and it needs no `zenera.json`, so an
|
|
83
|
+
unregistered directory can still be validated.
|
|
84
|
+
|
|
85
|
+
## Environment
|
|
86
|
+
|
|
87
|
+
| Variable | Effect |
|
|
88
|
+
| ------------------ | ----------------------------------------------------- |
|
|
89
|
+
| `ZENERA_HOME` | Moves the whole home tree. Default `~/.zenera/neo` |
|
|
90
|
+
| `ZENERA_THEME` | `dark`, `light` or `auto` for the TUI palette |
|
|
91
|
+
| `ZENERA_EDITOR` | The editor `zen open` launches |
|
|
92
|
+
| `ZENERA_DEBUG` | Print a stack trace when something unexpected escapes |
|
|
93
|
+
| `VISUAL`, `EDITOR` | Consulted by `zen open` after `ZENERA_EDITOR` |
|
|
94
|
+
| `COLORFGBG` | Read as a hint when the palette is being detected |
|
|
95
|
+
| Provider keys | `OPENAI_API_KEY` and friends — see [keys.md](keys.md) |
|
|
96
|
+
|
|
97
|
+
A real environment variable always beats the keyring.
|
|
98
|
+
|
|
99
|
+
## Where things live
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
~/.zenera/neo/ the home tree (0700)
|
|
103
|
+
projects.json the registry: name -> directory
|
|
104
|
+
keys.json credentials (0600)
|
|
105
|
+
keys/ key files, for providers that hold a file
|
|
106
|
+
faker/ generator cache for `zen faker`
|
|
107
|
+
|
|
108
|
+
<project>/
|
|
109
|
+
zenera.json { version, name }
|
|
110
|
+
INSTRUCTIONS.md house rules, prepended to every agent's prompt
|
|
111
|
+
agents.yaml the configuration
|
|
112
|
+
agents/prompts/ one .md per agent
|
|
113
|
+
agents/skills/ one directory per skill, each with SKILL.md
|
|
114
|
+
sandbox/Dockerfile the image commands run in, when the project builds one
|
|
115
|
+
sessions/ run state, memory, blobs, reports — never committed
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Credential files are refused if anyone but the owner can read them, the way
|
|
119
|
+
`ssh` refuses a loose private key: `chmod 600` and try again.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Reports — `zen inspect`
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
zen inspect [run] [--session <id>] [--open] [--rebuild] [--serve [port]]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Alias: `report`.
|
|
8
|
+
|
|
9
|
+
Renders the trajectory of a run: every message, tool call, skill activation and
|
|
10
|
+
hand-off, in order, with what each one cost. With no arguments it takes the
|
|
11
|
+
newest run of the newest session.
|
|
12
|
+
|
|
13
|
+
| Flag | Meaning |
|
|
14
|
+
| ---------------- | ------------------------------------------------------- |
|
|
15
|
+
| `[run]` | A run id. The newest otherwise |
|
|
16
|
+
| `--session <id>` | Which session the run belongs to |
|
|
17
|
+
| `--open` | Open the report in a browser |
|
|
18
|
+
| `--rebuild` | Rebuild `report.html` from the recorded state |
|
|
19
|
+
| `--serve [port]` | Serve it locally, which the report needs for its assets |
|
|
20
|
+
|
|
21
|
+
Use `--serve` rather than opening the file directly when the report has media in
|
|
22
|
+
it; the page fetches its assets and `file://` will not give them to it.
|
|
23
|
+
|
|
24
|
+
## Why it is the first thing to look at
|
|
25
|
+
|
|
26
|
+
It shows what the model was actually given, which is rarely what you assumed. A
|
|
27
|
+
prompt that reads correctly and behaves wrongly is nearly always a prompt that
|
|
28
|
+
was assembled differently from how it looks in the repository: a skill that did
|
|
29
|
+
not activate, a hand-off that fired early, a tool that was withheld, an asset
|
|
30
|
+
that was not attached.
|
|
31
|
+
|
|
32
|
+
The report also draws the architecture — agents, their tools, their hand-offs —
|
|
33
|
+
and falls back to reconstructing the wiring from the trajectory when the run
|
|
34
|
+
did not record it.
|
|
35
|
+
|
|
36
|
+
## Where it comes from
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<project>/sessions/<session-id>/runs/<run-id>/
|
|
40
|
+
input.md what was asked
|
|
41
|
+
output.md what came back
|
|
42
|
+
state.json the whole trajectory
|
|
43
|
+
report.html the rendering, rebuilt from state.json on demand
|
|
44
|
+
meta.json when it ran, how long it took
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Ids are timestamps: `20260825-143012-a7f3`. Listing them is `zen list --sessions`.
|
|
48
|
+
|
|
49
|
+
Large images are lifted out of the recorded state and stored alongside it, so a
|
|
50
|
+
photograph re-sent on every turn does not bloat every message. The report
|
|
51
|
+
resolves them back.
|
|
52
|
+
|
|
53
|
+
## What it prints
|
|
54
|
+
|
|
55
|
+
The path to `report.html` on stdout — it is the answer, so it pipes. `--json`
|
|
56
|
+
gives `{ session, run, report }` instead. A report that is missing is built
|
|
57
|
+
before either; `--rebuild` builds one that already exists again, which is always
|
|
58
|
+
safe because the report is derived and `state.json` is the truth. That is also
|
|
59
|
+
what makes an old run readable by a newer renderer.
|
|
60
|
+
|
|
61
|
+
To assert on a run in a script, read `state.json` rather than the report.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Credentials — `zen key`
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
zen key <ls|add|use|check|rm|show|env> [ref] [options]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Alias: `keys`.
|
|
8
|
+
|
|
9
|
+
Keys live in `~/.zenera/neo/keys.json`, mode `0600`, and are materialised into
|
|
10
|
+
the environment just before a run. **A real environment variable always wins**,
|
|
11
|
+
so a `.env` or an exported key still decides.
|
|
12
|
+
|
|
13
|
+
A reference is `provider` or `provider/name`. A provider may hold several named
|
|
14
|
+
keys; one of them is active.
|
|
15
|
+
|
|
16
|
+
## Subcommands
|
|
17
|
+
|
|
18
|
+
| Command | What it does |
|
|
19
|
+
| --------------------------------- | --------------------------------------- |
|
|
20
|
+
| `zen key ls [--check]` | Everything stored, and its state |
|
|
21
|
+
| `zen key add <provider>[/name]` | Read a key from stdin, or ask for it |
|
|
22
|
+
| `zen key use <provider>/<name>` | Choose which one a run uses |
|
|
23
|
+
| `zen key check [provider[/name]]` | Ask the provider whether it still works |
|
|
24
|
+
| `zen key rm <provider>/<name>` | Forget one |
|
|
25
|
+
| `zen key show <ref> [--reveal]` | Masked by default |
|
|
26
|
+
| `zen key env [provider …]` | Shell exports, for other tools |
|
|
27
|
+
|
|
28
|
+
`zen key add` also takes `--project` and `--location`, for a Vertex service
|
|
29
|
+
account.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
zen key add openai # prompts, echo off
|
|
33
|
+
pbpaste | zen key add openai/work # or from stdin
|
|
34
|
+
eval "$(zen key env)" # hand them to something else
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**The secret never comes from argv.** A command line lands in `ps`, in shell
|
|
38
|
+
history and in CI logs, so `zen key add` takes the value from piped stdin or an
|
|
39
|
+
echo-off prompt and from nowhere else.
|
|
40
|
+
|
|
41
|
+
## Providers
|
|
42
|
+
|
|
43
|
+
| Name | Environment variable | Holds | Where a key comes from |
|
|
44
|
+
| ------------ | -------------------------------- | ------ | ----------------------------------- |
|
|
45
|
+
| `openai` | `OPENAI_API_KEY` | secret | platform.openai.com/api-keys |
|
|
46
|
+
| `anthropic` | `ANTHROPIC_API_KEY` | secret | console.anthropic.com/settings/keys |
|
|
47
|
+
| `google` | `GEMINI_API_KEY` | secret | aistudio.google.com/apikey |
|
|
48
|
+
| `vertex` | `GOOGLE_APPLICATION_CREDENTIALS` | file | a service-account JSON key from GCP |
|
|
49
|
+
| `vertex` | `VERTEX_API_KEY` | secret | an express-mode key from GCP |
|
|
50
|
+
| `openrouter` | `OPENROUTER_API_KEY` | secret | openrouter.ai/settings/keys |
|
|
51
|
+
| `exa` | `EXA_API_KEY` | secret | dashboard.exa.ai/api-keys |
|
|
52
|
+
|
|
53
|
+
`exa` is a service the tools call, not a model provider.
|
|
54
|
+
|
|
55
|
+
### Vertex takes either shape
|
|
56
|
+
|
|
57
|
+
A **service-account file** is absorbed into `~/.zenera/neo/keys/` and
|
|
58
|
+
`GOOGLE_APPLICATION_CREDENTIALS` points at it. It wants a project too, from
|
|
59
|
+
`--project`, or `GOOGLE_CLOUD_PROJECT`, or the `project_id` inside the file, and
|
|
60
|
+
a region from `--location` (`global` otherwise). Application Default
|
|
61
|
+
Credentials from `gcloud auth application-default login` work with no entry at
|
|
62
|
+
all.
|
|
63
|
+
|
|
64
|
+
An **express-mode key** is an ordinary secret under `VERTEX_API_KEY`, and
|
|
65
|
+
addresses no project: a key and a project are alternatives, and sending both
|
|
66
|
+
gets a `403` that mentions neither. `--project` and `--location` are therefore
|
|
67
|
+
refused alongside a key.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
zen key add vertex --project acme-prod --location europe-west4
|
|
71
|
+
# paste a path → the file shape
|
|
72
|
+
# paste a key → the express shape
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Which one you gave is read off the value: a path that exists is the file, and
|
|
76
|
+
anything else is the key.
|
|
77
|
+
|
|
78
|
+
## Credentials the keyring does not hold
|
|
79
|
+
|
|
80
|
+
`zen key ls` also lists what the environment brought and what `gcloud` left
|
|
81
|
+
behind, marked `~` and named for the variable rather than for a key:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
~ vertex/$VERTEX_API_KEY vx-1…9f0z live from the environment
|
|
85
|
+
~ vertex/adc …/application_default_credentials.json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
These can be listed and checked, not chosen or forgotten — there is nothing to
|
|
89
|
+
choose between, and nothing of ours to remove. Unset the variable, or
|
|
90
|
+
`gcloud auth application-default revoke`.
|
|
91
|
+
|
|
92
|
+
## Liveness
|
|
93
|
+
|
|
94
|
+
`zen key check` and `zen key ls --check` do one round trip per key and record
|
|
95
|
+
the verdict:
|
|
96
|
+
|
|
97
|
+
- **live** — authenticated. A rate-limited answer counts as live, because it
|
|
98
|
+
proves the credential.
|
|
99
|
+
- **dead** — the provider rejected it. A verdict.
|
|
100
|
+
- **unknown** — the provider could not be asked. Says nothing about the key;
|
|
101
|
+
usually the network.
|
|
102
|
+
|
|
103
|
+
`zen init` uses the same probe to pick which provider to scaffold with, so a
|
|
104
|
+
project is not built around a revoked key.
|
|
105
|
+
|
|
106
|
+
## When a run says there is no credential
|
|
107
|
+
|
|
108
|
+
The frame does **not** materialise the keyring for you — each command that needs
|
|
109
|
+
a credential opens the store itself. If `zen key ls` shows a key as live and a
|
|
110
|
+
command still says `provider "openai": no api key — set OPENAI_API_KEY`, the
|
|
111
|
+
credential is fine and the command is at fault. Everything that runs a model
|
|
112
|
+
(`run`, `check`, `models`, `faker`, `rag`) already does this.
|
|
113
|
+
|
|
114
|
+
Exit code `4` means no usable credential for what was asked.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Projects — `init`, `list`, `open`, `version`
|
|
2
|
+
|
|
3
|
+
## `zen init`
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
zen init [dir] [--name <name>] [--model <ref>] [--force]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Creates a project here, or in `<dir>`, and records it in the registry so `zen
|
|
10
|
+
list` and `zen open` can find it by name.
|
|
11
|
+
|
|
12
|
+
| Flag | Meaning |
|
|
13
|
+
| --------------- | ------------------------------------------------------- |
|
|
14
|
+
| `--name <name>` | The registry name. Defaults to the directory's basename |
|
|
15
|
+
| `--model <ref>` | The model to scaffold with, taken as written |
|
|
16
|
+
| `--force` | Write into a directory that already holds a project |
|
|
17
|
+
|
|
18
|
+
What it writes:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
zenera.json { version, name }
|
|
22
|
+
INSTRUCTIONS.md house rules
|
|
23
|
+
agents.yaml the configuration
|
|
24
|
+
agents/prompts/default.md the default agent's prompt
|
|
25
|
+
agents/skills/ empty, for skills
|
|
26
|
+
assets/README.md
|
|
27
|
+
sandbox/Dockerfile the image the sandbox builds
|
|
28
|
+
sessions/ empty
|
|
29
|
+
.gitignore
|
|
30
|
+
.vscode/settings.json editor files
|
|
31
|
+
.github/ copilot instructions, prompts, this skill
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The project's own files are never overwritten — `--force` is what allows
|
|
35
|
+
writing into an occupied directory, and the files already there stay. The
|
|
36
|
+
editor files are the exception: `.vscode/settings.json` and the `.github/` tree
|
|
37
|
+
are ours and are replaced on every `init` and every `zen open`, so edits to them
|
|
38
|
+
do not survive.
|
|
39
|
+
|
|
40
|
+
The default agent gets the file tools and a sandboxed shell, plus `exa:*` when
|
|
41
|
+
the keyring holds an Exa key.
|
|
42
|
+
|
|
43
|
+
**Choosing the model.** Without `--model`, the keyring is asked — not counted.
|
|
44
|
+
Stored credentials are probed, because holding a key is not the same as holding
|
|
45
|
+
a working one, and the model is picked from a provider that answers. A key from
|
|
46
|
+
the environment is taken at its word. Each provider has its own scaffolded
|
|
47
|
+
default, and every scaffolded ref names its provider: the model shorthand reads
|
|
48
|
+
the first segment as a **provider name**, not a vendor, so a bare
|
|
49
|
+
`gemini-3.5-flash` would be asked of OpenAI.
|
|
50
|
+
|
|
51
|
+
## `zen list`
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
zen list [--sessions] [--prune]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every known project: its sessions, the last run, and whether one is live right
|
|
58
|
+
now.
|
|
59
|
+
|
|
60
|
+
| Flag | Meaning |
|
|
61
|
+
| ------------ | -------------------------------------------- |
|
|
62
|
+
| `--sessions` | Expand each project into its sessions |
|
|
63
|
+
| `--prune` | Forget entries whose directory has gone away |
|
|
64
|
+
|
|
65
|
+
The registry is an index, not the truth. An entry pointing at a directory that
|
|
66
|
+
no longer exists is shown dimmed rather than hidden, because a moved project is
|
|
67
|
+
a thing to fix, not a thing to silently lose.
|
|
68
|
+
|
|
69
|
+
## `zen open`
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
zen open [project] [--editor <cmd>] [--wait]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Opens the project directory in an editor. This is the only command that locates
|
|
76
|
+
a project for a human rather than for itself.
|
|
77
|
+
|
|
78
|
+
| Flag | Meaning |
|
|
79
|
+
| ---------------- | ------------------------------------- |
|
|
80
|
+
| `--editor <cmd>` | The command to launch |
|
|
81
|
+
| `--wait` | Do not return until the editor closes |
|
|
82
|
+
|
|
83
|
+
The editor is chosen in this order: `--editor`, `$ZENERA_EDITOR`, the editor
|
|
84
|
+
this terminal belongs to, `$VISUAL` or `$EDITOR`, a known editor on `PATH` or
|
|
85
|
+
installed, then the platform opener.
|
|
86
|
+
|
|
87
|
+
Opening refreshes the editor files (`.vscode/settings.json` and the `.github/`
|
|
88
|
+
tree) in the directory being opened. VS Code and its forks are launched with
|
|
89
|
+
`--disable-workspace-trust`, so the settings written there apply to the new
|
|
90
|
+
window immediately rather than after a prompt.
|
|
91
|
+
|
|
92
|
+
## `zen version`
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
zen version
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The CLI version, the library version and the Node version. `zen --version`
|
|
99
|
+
prints the CLI version alone and returns before anything else loads.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# API search — `zen rag`
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
zen rag schema <index|search|show|stats> [spec...]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Provided by `@zenera/rag` — `npm i -g @zenera/rag` if `zen rag` says it is not
|
|
8
|
+
installed.
|
|
9
|
+
|
|
10
|
+
Reads an OpenAPI/Swagger document as a **graph** — operations, schemas and the
|
|
11
|
+
fields inside them, joined by the `$ref`s between them — and makes it
|
|
12
|
+
searchable. The answer to a search is not a list of matches but the connected
|
|
13
|
+
piece of the API that matched: the operations, the schemas they carry and the
|
|
14
|
+
fields inside them, printed as text, a diagram, or TypeScript that compiles.
|
|
15
|
+
|
|
16
|
+
It exists because a large specification does not fit in a context window and
|
|
17
|
+
grepping it returns fragments that name types nobody printed.
|
|
18
|
+
|
|
19
|
+
## `index`
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
zen rag schema index <spec...> [--embedding <ref>] [-o <dir>] [--batch <n>]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Flag | Default | Meaning |
|
|
26
|
+
| ------------------- | ------------- | ------------------------------------------------------------ |
|
|
27
|
+
| `--embedding <ref>` | — | Which embedder makes the vectors. Omit it to see the choices |
|
|
28
|
+
| `-o`, `--out <dir>` | `./schema-db` | Where the index goes |
|
|
29
|
+
| `--batch <n>` | `96` | Texts per embedding request, and how often progress prints |
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
zen rag schema index openapi.yaml --embedding openai:text-embedding-3-small
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Unlike the faker, `$ref`s are **bundled, not dereferenced**: component names are
|
|
36
|
+
the node ids and the cycles between them are the edges. `discriminator` is kept —
|
|
37
|
+
it is what turns a `oneOf` into a tagged union a compiler can narrow.
|
|
38
|
+
|
|
39
|
+
What lands in `<out>`:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
manifest.json written last; its presence means the index is complete
|
|
43
|
+
graph.json the nodes and edges
|
|
44
|
+
schemas.json the schemas, read lazily
|
|
45
|
+
operations.json the operations, read lazily
|
|
46
|
+
lance/ the vector and full-text indexes
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The manifest records the embedding ref **and** the embedder's own id, so a
|
|
50
|
+
search with a different model is refused rather than quietly returning nonsense.
|
|
51
|
+
|
|
52
|
+
## `search`
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
zen rag schema search [terms…] [filters…]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Every argument is validated before an embedder is constructed, so a typo is a
|
|
59
|
+
usage error rather than a credential error.
|
|
60
|
+
|
|
61
|
+
### Terms — repeatable, and the field is the point
|
|
62
|
+
|
|
63
|
+
| Term | Searches |
|
|
64
|
+
| ----------------------- | ---------------------------------------- |
|
|
65
|
+
| `<text>` | Everything, the same as `--all` |
|
|
66
|
+
| `--all <q>` | Everything, unfiltered |
|
|
67
|
+
| `--method <q>` | Operations |
|
|
68
|
+
| `--type <q>` | Schemas, on the side `--direction` names |
|
|
69
|
+
| `--input-type <q>` | Schemas a call accepts |
|
|
70
|
+
| `--output-type <q>` | Schemas a call returns |
|
|
71
|
+
| `--property <q>` | Fields and parameters, per `--direction` |
|
|
72
|
+
| `--input-property <q>` | Fields and parameters a call accepts |
|
|
73
|
+
| `--output-property <q>` | Fields a call returns |
|
|
74
|
+
| `--query <json\|->` | A whole query object; `-` reads stdin |
|
|
75
|
+
|
|
76
|
+
Putting the intent in the field that matches what is wanted is what makes the
|
|
77
|
+
search good. A request field belongs in `--input-property`, a response field in
|
|
78
|
+
`--output-property`; `--all` cannot filter and is the weakest of them.
|
|
79
|
+
|
|
80
|
+
### Filters and shape
|
|
81
|
+
|
|
82
|
+
| Flag | Default | Meaning |
|
|
83
|
+
| --------------------------- | ------------- | ------------------------------------------------------- |
|
|
84
|
+
| `-d`, `--dir <dir>` | `./schema-db` | Which index |
|
|
85
|
+
| `--embedding <ref>` | the index's | Must be the one the index was built with |
|
|
86
|
+
| `--direction <d>` | `any` | `input`, `output` or `any` |
|
|
87
|
+
| `--method-type <t>` | `any` | `read_only`, `read_write` or `any` |
|
|
88
|
+
| `--exclude-id <id>` | — | Drop a node. Repeatable |
|
|
89
|
+
| `--exclude-method <name>` | — | Drop an operation by name. Repeatable |
|
|
90
|
+
| `--exclude-type <name>` | — | Drop a schema by name. Repeatable |
|
|
91
|
+
| `--exclude-property <name>` | — | Drop a field by name. Repeatable |
|
|
92
|
+
| `--limit <n>` | `5` | Seeds kept per term |
|
|
93
|
+
| `--max-hops <n>` | `3` | How far apart two hits may be |
|
|
94
|
+
| `--max-nodes <n>` | `200` | Nodes per result |
|
|
95
|
+
| `--format <f>` | `text` | `text`, `mermaid`, `mermaid-flowchart`, `ts`, `openapi` |
|
|
96
|
+
| `--no-docs` | — | Leave the descriptions out |
|
|
97
|
+
| `--interactive` | — | Prompt, search, refine. Needs a terminal |
|
|
98
|
+
| `--quiet` | — | No narration |
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
zen rag schema search --method "reset a user password" --format ts
|
|
102
|
+
zen rag schema search --output-property "invoice total" --direction output
|
|
103
|
+
echo '{"methods":["cancel a subscription"]}' | zen rag schema search --query -
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The exclusions are what turn one search into a session: pass back the ids of
|
|
107
|
+
what you have already been shown to be shown something else instead of the same
|
|
108
|
+
thing again.
|
|
109
|
+
|
|
110
|
+
`--format ts` emits TypeScript closed over its own `$ref`s — everything named is
|
|
111
|
+
also declared, so the output compiles on its own.
|
|
112
|
+
|
|
113
|
+
### `--interactive`
|
|
114
|
+
|
|
115
|
+
A prompt that keeps the query between searches:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
<text> search everything
|
|
119
|
+
all|method|type <text> search one field
|
|
120
|
+
input-property <text> also: output-property, property, input-type, output-type
|
|
121
|
+
direction <d> input | output | any
|
|
122
|
+
method-type <t> read_only | read_write | any
|
|
123
|
+
format <f> text | mermaid | mermaid-flowchart | ts | openapi
|
|
124
|
+
show the query as it stands
|
|
125
|
+
reset forget it, exclusions included
|
|
126
|
+
quit
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## `show`
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
zen rag schema show <id...> [-d <dir>] [--format <f>]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Prints named nodes with no search in between. Needs no embedder and no
|
|
136
|
+
credential — it is a read of the graph.
|
|
137
|
+
|
|
138
|
+
## `stats`
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
zen rag schema stats [-d <dir>]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
What is in an index and what built it: counts by kind, the embedding model, the
|
|
145
|
+
documents it came from. Also needs no embedder.
|
|
146
|
+
|
|
147
|
+
## Giving it to an agent
|
|
148
|
+
|
|
149
|
+
`@zenera/rag/tools` exports the same search as tools an agent can call:
|
|
150
|
+
|
|
151
|
+
| Tool | For |
|
|
152
|
+
| -------------------------- | ------------------------------------------------------------------- |
|
|
153
|
+
| `search_api` | The search above, with the same fields |
|
|
154
|
+
| `describe_types` | Named schemas as TypeScript, closed over what they refer to |
|
|
155
|
+
| `find_types_with_property` | Every schema with a field of this name — exact lookup, no searching |
|
|
156
|
+
| `list_methods` | Operations by path, to see the shape of the API before asking |
|
|
157
|
+
|
|
158
|
+
They share the group `schema`, so an agent takes them with `schema:*` in its
|
|
159
|
+
`tools:`.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Running — `zen run`
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
zen run [project] [prompt] [options]
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
On a terminal with no prompt it opens the TUI. With a prompt, or with no tty, it
|
|
8
|
+
answers once on stdout and exits.
|
|
9
|
+
|
|
10
|
+
## Options
|
|
11
|
+
|
|
12
|
+
| Flag | What it does |
|
|
13
|
+
| ----------------------- | ------------------------------------------------------ |
|
|
14
|
+
| `--project <name\|dir>` | Which project. Inferred from the directory otherwise |
|
|
15
|
+
| `--session <id>` | Continue a particular session |
|
|
16
|
+
| `--new` | Start a fresh one |
|
|
17
|
+
| `--workspace <dir>` | What the agent may read and write |
|
|
18
|
+
| `--model <ref>` | Override the default model for this run |
|
|
19
|
+
| `--image <ref>` | Override the container image commands run in |
|
|
20
|
+
| `--read-only` | Withhold every tool that can write |
|
|
21
|
+
| `--quiet` | The answer only; no narration |
|
|
22
|
+
| `--plain` | One shot, even on a terminal |
|
|
23
|
+
| `--theme <dark\|light>` | Force the palette. Detected otherwise; `$ZENERA_THEME` |
|
|
24
|
+
| `--out <file>` | Write the answer to a file as well as to stdout |
|
|
25
|
+
| `--yes` | Accept the questions it would otherwise ask |
|
|
26
|
+
|
|
27
|
+
Flags always beat the file: the repository states intent, the invocation
|
|
28
|
+
overrides it.
|
|
29
|
+
|
|
30
|
+
## Which word is the project
|
|
31
|
+
|
|
32
|
+
The first positional is read as a project when it names one, and as the first
|
|
33
|
+
word of the prompt when it does not:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
zen run acme the acme project, TUI
|
|
37
|
+
zen run acme "what changed?" the acme project, one answer
|
|
38
|
+
zen run "what changed?" this directory's project, one answer
|
|
39
|
+
zen run --project why "why?" when the project is called "why"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The prompt comes from the argument, or from stdin, or from the TUI:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
git diff | zen run --quiet "summarise this diff"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What a prompt on the command line implies
|
|
49
|
+
|
|
50
|
+
A prompt is a request for an answer, not a conversation to pick up, so it
|
|
51
|
+
answers the three questions itself: **a fresh session**, **the directory you are
|
|
52
|
+
in** as the workspace, and **no confirmation**. Every flag still wins —
|
|
53
|
+
`--session`, `--workspace` and `--read-only` override it — and the TUI, where
|
|
54
|
+
there is someone to ask, still asks.
|
|
55
|
+
|
|
56
|
+
There is no `resume`. A session continues itself, because its state is what it
|
|
57
|
+
is; `--session <id>` picks which one.
|
|
58
|
+
|
|
59
|
+
## The TUI
|
|
60
|
+
|
|
61
|
+
Drawn only when there is a terminal on both stdin and stdout, no prompt,
|
|
62
|
+
no `--plain`, no `--quiet` and no `--json`. It streams the answer, shows
|
|
63
|
+
reasoning as it arrives, and reports per-turn and per-session token usage in the
|
|
64
|
+
footer.
|
|
65
|
+
|
|
66
|
+
Reasoning only _arrives_ if the model was asked for it: OpenAI needs
|
|
67
|
+
`reasoningSummary` on the responses API, Anthropic needs `thinkingBudgetTokens`,
|
|
68
|
+
Gemini has `includeThoughts` on by default. An OpenAI project shows nothing
|
|
69
|
+
until `agents.yaml` asks.
|
|
70
|
+
|
|
71
|
+
Palette selection: `--theme` > `$ZENERA_THEME` > a query to the terminal >
|
|
72
|
+
`COLORFGBG` > dark.
|
|
73
|
+
|
|
74
|
+
## Sessions
|
|
75
|
+
|
|
76
|
+
One session is one continuing conversation, with its own workspace, memory and
|
|
77
|
+
run history. Ids look like `20260825-143012-a7f3`.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
<project>/sessions/<id>/
|
|
81
|
+
workspace/ what the agent sees, unless --workspace said otherwise
|
|
82
|
+
runs/<run-id>/ input.md, output.md, state.json, report.html, meta.json
|
|
83
|
+
.data/
|
|
84
|
+
state.json the live, resumable state, rewritten after every run
|
|
85
|
+
session.json when it was made, and the workspace it is rooted at
|
|
86
|
+
memory/
|
|
87
|
+
blobs/
|
|
88
|
+
sandbox/home/ /home/agent inside the container
|
|
89
|
+
.lock held while a run is in flight
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The recorded workspace is what makes resuming safe: a session that quietly
|
|
93
|
+
changed what "the workspace" meant between turns would be unexplainable, so it
|
|
94
|
+
is written once and reused.
|
|
95
|
+
|
|
96
|
+
None of `sessions/` is source and none of it is committed.
|
|
97
|
+
|
|
98
|
+
A session is locked while it runs, and a lock whose process is gone is stale by
|
|
99
|
+
definition and is taken over.
|
|
100
|
+
|
|
101
|
+
## Afterwards
|
|
102
|
+
|
|
103
|
+
Each run writes a `report.html` next to its state; the run prints a
|
|
104
|
+
`file://` link to it. See [inspect.md](inspect.md).
|