@zenera/cli 1.1.27 → 1.1.28
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 +54 -51
- package/dist/commands/meta.js +103 -35
- package/dist/commands/models.d.ts +12 -0
- package/dist/commands/models.js +23 -14
- package/dist/main.js +0 -0
- package/dist/meta/model.d.ts +1 -1
- package/dist/meta/model.js +1 -1
- package/dist/tui/models.d.ts +3 -1
- package/dist/tui/models.js +4 -4
- package/package.json +3 -3
- package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +5 -0
- package/dist/meta/finetune/guard.d.ts +0 -29
- package/dist/meta/finetune/guard.js +0 -104
- package/dist/meta.d.ts +0 -291
- package/dist/meta.js +0 -981
package/README.md
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/andreyryabov/ZeneraNeo/main/docs/imgs/zenera-logo-with-media-queries.svg"
|
|
4
|
+
alt="Zenera Logo"
|
|
5
|
+
width="20"
|
|
6
|
+
/>
|
|
7
|
+
<div>Powered By Zenera</div>
|
|
8
|
+
</div>
|
|
9
|
+
<br>
|
|
10
|
+
|
|
1
11
|
<div align="center">
|
|
2
|
-
<
|
|
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>
|
|
12
|
+
<img src="https://raw.githubusercontent.com/andreyryabov/ZeneraNeo/main/docs/imgs/banner-cli-with-media-queries.svg" alt="ZENERA CLI" width="720">
|
|
6
13
|
|
|
7
14
|
[](https://www.npmjs.com/package/@zenera/cli)
|
|
8
15
|
[](https://github.com/andreyryabov/ZeneraNeo/blob/main/LICENSE)
|
|
@@ -121,16 +128,15 @@ zen init my-project # scaffolds a project and registers it
|
|
|
121
128
|
zen open my-project # open the specification and project in your editor
|
|
122
129
|
```
|
|
123
130
|
|
|
124
|
-
In the editor, describe the job in `SPECIFICATION.md
|
|
125
|
-
agent
|
|
131
|
+
In the editor, describe the job in `SPECIFICATION.md`. Then, back in the
|
|
132
|
+
terminal, let the meta agent build the system from it:
|
|
126
133
|
|
|
127
|
-
```
|
|
128
|
-
/spec-sync-project
|
|
134
|
+
```sh
|
|
135
|
+
zen meta run my-project /spec-sync-project
|
|
129
136
|
```
|
|
130
137
|
|
|
131
138
|
It creates the specialists, responsibilities, tool access and handoffs the job
|
|
132
|
-
needs.
|
|
133
|
-
real workspace:
|
|
139
|
+
needs. Validate the generated system and run it against a real workspace:
|
|
134
140
|
|
|
135
141
|
```sh
|
|
136
142
|
zen check my-project
|
|
@@ -170,10 +176,11 @@ dashboard rewritten every second: what each item is doing right now, what the
|
|
|
170
176
|
finished ones cost - per model, since agents may each use a different one - and
|
|
171
177
|
what is still queued.
|
|
172
178
|
|
|
173
|
-
To change what the system does, update `SPECIFICATION.md` and
|
|
174
|
-
|
|
179
|
+
To change what the system does, update `SPECIFICATION.md` and run
|
|
180
|
+
`zen meta run /spec-sync-project` again. The next section explains that workflow
|
|
181
|
+
in detail.
|
|
175
182
|
|
|
176
|
-
## You write the specification;
|
|
183
|
+
## You write the specification; the meta agent writes the system
|
|
177
184
|
|
|
178
185
|
`zen init` writes `SPECIFICATION.md` first, and everything else in the folder
|
|
179
186
|
implements it. Where the two disagree the specification wins, so a change to
|
|
@@ -201,9 +208,9 @@ nobody to hand work to, because there is no second job to hand on.
|
|
|
201
208
|
|
|
202
209
|
### How you actually edit it
|
|
203
210
|
|
|
204
|
-
`SPECIFICATION.md` is ordinary Markdown, so any editor will do
|
|
205
|
-
|
|
206
|
-
|
|
211
|
+
`SPECIFICATION.md` is ordinary Markdown, so any editor will do. `zen init`
|
|
212
|
+
installs the prompts that make each step of the loop a single `zen meta run`
|
|
213
|
+
command:
|
|
207
214
|
|
|
208
215
|
```sh
|
|
209
216
|
zen open my-project # or, standing in the folder: zen open
|
|
@@ -213,30 +220,29 @@ That launches the editor you already use: the one this terminal belongs to, or
|
|
|
213
220
|
`$ZENERA_EDITOR`, or the first of VS Code, Cursor, Windsurf, Zed, Sublime Text
|
|
214
221
|
or IntelliJ it can find. `--editor <cmd>` names one outright.
|
|
215
222
|
|
|
216
|
-
In the window that opens:
|
|
217
|
-
|
|
218
223
|
1. **Edit `SPECIFICATION.md`** - what the system is for, which agents exist,
|
|
219
224
|
what each may reach for, and what _done_ means.
|
|
220
|
-
2. **
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
2. **Run `zen meta run /spec-sync-project`.** It reads the specification and
|
|
226
|
+
every file implementing it, works out the difference in both directions, and
|
|
227
|
+
changes the smallest thing that closes each gap.
|
|
223
228
|
3. **Answer `SPECIFICATION-FEEDBACK.md`**, which it writes for anything it could
|
|
224
229
|
not do without guessing. Every question carries two to four written-out
|
|
225
230
|
answers, each with a `[ ]` box: tick one, delete the ones you do not want, or
|
|
226
231
|
write your own in their place.
|
|
227
|
-
4. **
|
|
228
|
-
`SPECIFICATION.md` - never into a prompt - and then
|
|
232
|
+
4. **Run `zen meta run /spec-apply-feedback`.** It folds the answers you chose
|
|
233
|
+
into `SPECIFICATION.md` - never into a prompt - and then
|
|
234
|
+
`/spec-sync-project` again.
|
|
229
235
|
|
|
230
|
-
|
|
236
|
+
Then: `zen check`, then `zen run`.
|
|
231
237
|
|
|
232
238
|
<details>
|
|
233
|
-
<summary>The
|
|
239
|
+
<summary>The prompts <code>zen init</code> installs</summary>
|
|
234
240
|
|
|
235
|
-
They are prompt files under `.github/prompts/`,
|
|
236
|
-
|
|
237
|
-
so they never go stale - and edits to them do not survive.
|
|
241
|
+
They are prompt files under `.github/prompts/`, and `zen meta run /<name>` runs
|
|
242
|
+
one (`zen meta prompts` lists them). Both `zen init` and `zen open` write them
|
|
243
|
+
fresh, so they never go stale - and edits to them do not survive.
|
|
238
244
|
|
|
239
|
-
|
|
|
245
|
+
| Prompt | Does |
|
|
240
246
|
| ---------------------- | ---------------------------------------------------------------- |
|
|
241
247
|
| `/spec-sync-project` | Makes every file match `SPECIFICATION.md`, both directions. |
|
|
242
248
|
| `/spec-apply-feedback` | Folds your answers in `SPECIFICATION-FEEDBACK.md` into the spec. |
|
|
@@ -245,10 +251,8 @@ so they never go stale - and edits to them do not survive.
|
|
|
245
251
|
| `/new-agent` | Adds an agent - prompt, wiring and hand-offs. |
|
|
246
252
|
| `/new-skill` | Adds a skill under `agents/skills/`. |
|
|
247
253
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
prompt. Alongside them, `.github/copilot-instructions.md` is the standing brief
|
|
251
|
-
that explains this runtime to whatever agent is reading, and
|
|
254
|
+
Alongside them, `.github/copilot-instructions.md` is the standing brief that
|
|
255
|
+
explains this runtime to whatever coding agent opens the folder, and
|
|
252
256
|
`.github/skills/zen-cli/` is the CLI's own reference for it.
|
|
253
257
|
|
|
254
258
|
</details>
|
|
@@ -256,9 +260,9 @@ that explains this runtime to whatever agent is reading, and
|
|
|
256
260
|
### The self-improving loop
|
|
257
261
|
|
|
258
262
|
```
|
|
259
|
-
edit SPECIFICATION.md → /spec-sync-project → zen check → zen run
|
|
260
|
-
↑
|
|
261
|
-
|
|
263
|
+
edit SPECIFICATION.md → zen meta run /spec-sync-project → zen check → zen run
|
|
264
|
+
↑ ↓
|
|
265
|
+
└─────────── refine the spec <-──── zen inspect <-─────────────┘
|
|
262
266
|
```
|
|
263
267
|
|
|
264
268
|
`zen inspect` makes the record visible: the run trace, timing and token
|
|
@@ -266,7 +270,7 @@ statistics, agent architecture, and memory used during the run.
|
|
|
266
270
|
|
|
267
271
|

|
|
268
272
|
|
|
269
|
-
The agent
|
|
273
|
+
The meta agent can close the same loop without the page. `zen inspect
|
|
270
274
|
graph` writes the whole run as one Mermaid flowchart - a line per node, short
|
|
271
275
|
sequential ids, the tools counted in a header - which is small enough to read
|
|
272
276
|
whole. Having spotted the loop or the branch that failed, it opens the two or
|
|
@@ -287,7 +291,7 @@ prompt that reads correctly and behaves wrongly explains itself.
|
|
|
287
291
|
`ask` has two modes, decided by whether the question is given:
|
|
288
292
|
|
|
289
293
|
- **One-shot** - a question on the line, in `--question-file`, or `--json`.
|
|
290
|
-
For a script or the agent
|
|
294
|
+
For a script or the meta agent: no banner, no prompt, no picker.
|
|
291
295
|
stdout is the answer and nothing else, and the command exits.
|
|
292
296
|
- **Interactive** - no question. For you, at a terminal: `zen inspect ask`
|
|
293
297
|
walks through the session, run and recorded LLM call (whichever are not
|
|
@@ -298,8 +302,8 @@ prompt that reads correctly and behaves wrongly explains itself.
|
|
|
298
302
|
Every step is a command, and everything each one reads or writes is a plain
|
|
299
303
|
file: the specification, the findings `zen check` prints with a code, a location
|
|
300
304
|
and a fix, the record `zen inspect` renders of what the run actually did. So the
|
|
301
|
-
loop does not need you standing in it. Hand the whole cycle to the agent
|
|
302
|
-
|
|
305
|
+
loop does not need you standing in it. Hand the whole cycle to the meta agent
|
|
306
|
+
and it runs on the system it just built - test it, read the failure,
|
|
303
307
|
change the sentence in the specification that caused it, rebuild, run again.
|
|
304
308
|
"Fix my project" is one instruction. (The first time round, `scripts/_setup.sh`
|
|
305
309
|
prepares the container the tools run in.)
|
|
@@ -344,7 +348,7 @@ Read a codebase and leave a short written note about it in the workspace.
|
|
|
344
348
|
- Every claim in it comes from a file that was actually read.
|
|
345
349
|
```
|
|
346
350
|
|
|
347
|
-
|
|
351
|
+
Run `zen meta run /spec-sync-project`. It writes `agents.yaml` - who
|
|
348
352
|
exists, and what each may reach for:
|
|
349
353
|
|
|
350
354
|
```yaml
|
|
@@ -645,8 +649,8 @@ an accident.
|
|
|
645
649
|
the project: a coding agent rooted at the project directory, which reads
|
|
646
650
|
`SPECIFICATION.md`, changes `agents.yaml`, the prompts and the skills, and runs
|
|
647
651
|
whatever commands it needs to check itself - on the keys already in your
|
|
648
|
-
keyring, and not on anybody's subscription. It is the loop
|
|
649
|
-
|
|
652
|
+
keyring, and not on anybody's subscription. It is the spec-to-system loop as a
|
|
653
|
+
command you can put in a script.
|
|
650
654
|
|
|
651
655
|
```sh
|
|
652
656
|
zen meta run "what does this project do?"
|
|
@@ -663,9 +667,9 @@ Every prompt goes through `run`, whether you typed it or it is a file: the
|
|
|
663
667
|
alternative was a verb that appeared only sometimes, and a bare word after
|
|
664
668
|
`zen meta` that was a subcommand on Tuesdays and a question on Wednesdays.
|
|
665
669
|
|
|
666
|
-
It uses every tool without asking. The prompts it runs
|
|
667
|
-
|
|
668
|
-
|
|
670
|
+
It uses every tool without asking. The prompts it runs rely on reading files,
|
|
671
|
+
writing them and running commands, and a `-p` run has nobody sitting there to
|
|
672
|
+
answer the question - so the
|
|
669
673
|
alternative is not a safer run but a stalled one. `--allow-tool read`,
|
|
670
674
|
`--allow-tool 'shell(git:*)'` narrow it to what a particular run needs, and
|
|
671
675
|
`--ask` puts the question back. It is your own checkout and your own key either
|
|
@@ -683,11 +687,10 @@ to `.tmp/logs/meta.<when>.log`, whose path is printed before the run starts, so
|
|
|
683
687
|
kept beside it as `.tmp/logs/meta.<when>.md`, named again once it has printed -
|
|
684
688
|
a terminal turns that into something you click.
|
|
685
689
|
|
|
686
|
-
`zen meta run /<name>`
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
same prompt runs from the editor, from a terminal and from CI.
|
|
690
|
+
`zen meta run /<name>` runs a prompt file. The prompts under
|
|
691
|
+
`.github/prompts/` - `/spec-sync-project`, `/project-review` - are plain
|
|
692
|
+
Markdown with frontmatter. zen reads one, drops the frontmatter and sends the
|
|
693
|
+
body - so the same prompt runs from a terminal, a script and CI.
|
|
691
694
|
|
|
692
695
|
A terminal has no menu dropping down as you type a slash, so
|
|
693
696
|
`zen meta prompts` lists what this project holds, each with its description.
|
package/dist/commands/meta.js
CHANGED
|
@@ -10,12 +10,14 @@ import { copilotEventLog } from "../eventlog.js";
|
|
|
10
10
|
import { ensureHome } from "../home.js";
|
|
11
11
|
import { assertOwner, KeyStore, PROVIDERS } from "../keys.js";
|
|
12
12
|
import { probeChat, probeModel } from "../liveness.js";
|
|
13
|
-
import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta,
|
|
13
|
+
import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, writeMeta, } from "../meta/index.js";
|
|
14
14
|
import { splitRef } from "../modelref.js";
|
|
15
15
|
import { duration } from "../narrate.js";
|
|
16
16
|
import * as Projects from "../projects.js";
|
|
17
17
|
import { project as resolveProject } from "../resolve.js";
|
|
18
|
-
import {
|
|
18
|
+
import { TEMPLATES } from "../scaffold.js";
|
|
19
|
+
import { bold, choose, CliError, credentialError, cyan, dim, EXIT, green, isInteractive, json, note, pad, progress, readStdin, table, usageError, warn, write, writeAll, } from "../term.js";
|
|
20
|
+
import { wrap } from "../tui/wrap.js";
|
|
19
21
|
import { appendUsage, LEDGER_ENV, ledgerPath, META_PROMPT_ENV, META_SESSION_ENV, } from "../usage.js";
|
|
20
22
|
const USAGE = 'zen meta run [project] [prompt] [options]';
|
|
21
23
|
const VERBS = new Set(['model', 'run', 'prompts', 'resume', 'dataset', 'finetune']);
|
|
@@ -55,15 +57,46 @@ function page(p) {
|
|
|
55
57
|
write(line ? ` ${line}` : '');
|
|
56
58
|
}
|
|
57
59
|
}
|
|
60
|
+
const RUN_FORMS = [
|
|
61
|
+
' zen meta run [project] "<question>" ask it something',
|
|
62
|
+
' zen meta run [project] /<name> [words] run a stored prompt',
|
|
63
|
+
' zen meta run [project] pick one of its stored prompts',
|
|
64
|
+
];
|
|
65
|
+
const RUN_ARGUMENTS = [
|
|
66
|
+
'Arguments:',
|
|
67
|
+
' [project] Name of a project. Default: the one you are in.',
|
|
68
|
+
' [prompt] Your question, in quotes. Also --prompt, or piped in.',
|
|
69
|
+
' /<name> A prompt file in .github/prompts/<name>.prompt.md.',
|
|
70
|
+
' [words] Appended to that prompt as a final line.',
|
|
71
|
+
];
|
|
72
|
+
const RUN_OPTIONS = [
|
|
73
|
+
'Options:',
|
|
74
|
+
' --project <name|dir> Which project to work in. The agent is rooted there.',
|
|
75
|
+
' --model <ref> Model for this run, e.g. vertex/gemini-3.8-flash.',
|
|
76
|
+
' --provider <name> Force the key to use. Default: the model ref says.',
|
|
77
|
+
' --prompt-file <file> Append this file to the prompt, after any words.',
|
|
78
|
+
' --agent <name> A .github/agents/<name>.agent.md.',
|
|
79
|
+
' --effort <level> none, minimal, low, medium, high, xhigh or max.',
|
|
80
|
+
' --allow-tool <list> Only these: read, shell(git:*), write(README.md).',
|
|
81
|
+
' --ask Have it ask before each tool. Default: it does not.',
|
|
82
|
+
' --add-dir <dir> Another directory it may touch. Repeatable.',
|
|
83
|
+
' --resume <id> Continue a copilot session. --continue takes the last.',
|
|
84
|
+
' --session-id <uuid> Start a new session under this id. Default: a random one.',
|
|
85
|
+
` --retries <n> Resume after a rate limit or outage. Default ${DEFAULT_RESUMES}, 0 = off.`,
|
|
86
|
+
' --share <file> Write the transcript to a markdown file.',
|
|
87
|
+
' --events <file> Append what the agent does to this file as it does it:',
|
|
88
|
+
' one JSON line per model call, tool call and message.',
|
|
89
|
+
' --dry-run Print what would run, secrets masked, and stop.',
|
|
90
|
+
' --no-refresh Leave the editor files as they are (parallel runs).',
|
|
91
|
+
' --json Print one object: answer, answerFile, sessionId, exitCode, tokens.',
|
|
92
|
+
];
|
|
58
93
|
export const meta = {
|
|
59
94
|
summary: 'Run the meta agent over this project, on your own keys.',
|
|
60
95
|
usage: USAGE,
|
|
61
96
|
banner: { head: 'Zenera', accent: 'Meta', subtitle: 'Meta Agent', hue: 'cyan' },
|
|
62
97
|
details: [
|
|
63
98
|
'Forms:',
|
|
64
|
-
|
|
65
|
-
' zen meta run [project] /<name> [words] run a stored prompt',
|
|
66
|
-
' zen meta run [project] pick one of its stored prompts',
|
|
99
|
+
...RUN_FORMS,
|
|
67
100
|
' zen meta prompts [project] list the stored prompts',
|
|
68
101
|
' zen meta resume [project] [session] carry on a run that stopped',
|
|
69
102
|
' zen meta model [ref] show or set the model it uses',
|
|
@@ -71,33 +104,12 @@ export const meta = {
|
|
|
71
104
|
' zen meta finetune <verb> tune the project on those cases',
|
|
72
105
|
'',
|
|
73
106
|
'The project may come before the verb instead: `zen meta acme run`.',
|
|
74
|
-
'`zen meta dataset --help
|
|
107
|
+
'`zen meta dataset --help`, `zen meta finetune --help` and `zen meta run --help`',
|
|
108
|
+
'have their own pages; the last lists the stored prompts.',
|
|
75
109
|
'',
|
|
76
|
-
|
|
77
|
-
' [project] Name of a project. Default: the one you are in.',
|
|
78
|
-
' [prompt] Your question, in quotes. Also --prompt, or piped in.',
|
|
79
|
-
' /<name> A prompt file in .github/prompts/<name>.prompt.md.',
|
|
80
|
-
' [words] Appended to that prompt as a final line.',
|
|
110
|
+
...RUN_ARGUMENTS,
|
|
81
111
|
'',
|
|
82
|
-
|
|
83
|
-
' --project <name|dir> Which project to work in. The agent is rooted there.',
|
|
84
|
-
' --model <ref> Model for this run, e.g. vertex/gemini-3.8-flash.',
|
|
85
|
-
' --provider <name> Force the key to use. Default: the model ref says.',
|
|
86
|
-
' --prompt-file <file> Append this file to the prompt, after any words.',
|
|
87
|
-
' --agent <name> A .github/agents/<name>.agent.md.',
|
|
88
|
-
' --effort <level> none, minimal, low, medium, high, xhigh or max.',
|
|
89
|
-
' --allow-tool <list> Only these: read, shell(git:*), write(README.md).',
|
|
90
|
-
' --ask Have it ask before each tool. Default: it does not.',
|
|
91
|
-
' --add-dir <dir> Another directory it may touch. Repeatable.',
|
|
92
|
-
' --resume <id> Continue a copilot session. --continue takes the last.',
|
|
93
|
-
' --session-id <uuid> Start a new session under this id. Default: a random one.',
|
|
94
|
-
` --retries <n> Resume after a rate limit or outage. Default ${DEFAULT_RESUMES}, 0 = off.`,
|
|
95
|
-
' --share <file> Write the transcript to a markdown file.',
|
|
96
|
-
' --events <file> Append what the agent does to this file as it does it:',
|
|
97
|
-
' one JSON line per model call, tool call and message.',
|
|
98
|
-
' --dry-run Print what would run, secrets masked, and stop.',
|
|
99
|
-
' --no-refresh Leave the editor files as they are (parallel runs).',
|
|
100
|
-
' --json Print one object: answer, answerFile, sessionId, exitCode, tokens.',
|
|
112
|
+
...RUN_OPTIONS,
|
|
101
113
|
'',
|
|
102
114
|
'It always uses your own keys — `zen key add` — and never a coding-agent',
|
|
103
115
|
'subscription. The answer goes to stdout and the progress to stderr, so',
|
|
@@ -131,6 +143,9 @@ export const meta = {
|
|
|
131
143
|
'Setting one asks the provider a one-word question first, so a model that',
|
|
132
144
|
'will not answer is never stored. --force skips that.',
|
|
133
145
|
'',
|
|
146
|
+
'`zen meta model --pick` browses every model your keys reach: / filters,',
|
|
147
|
+
't asks one a question, p takes it.',
|
|
148
|
+
'',
|
|
134
149
|
'Examples:',
|
|
135
150
|
' zen meta run "what does this project do?"',
|
|
136
151
|
' zen meta run acme "review the last commit"',
|
|
@@ -144,9 +159,16 @@ export const meta = {
|
|
|
144
159
|
' zen meta model --pick',
|
|
145
160
|
' zen meta run --dry-run "hello"',
|
|
146
161
|
],
|
|
147
|
-
help: (ctx) => {
|
|
162
|
+
help: async (ctx) => {
|
|
148
163
|
const sub = subcommand(ctx.args);
|
|
149
|
-
|
|
164
|
+
if (sub) {
|
|
165
|
+
return page(SUBCOMMANDS[sub.name].page);
|
|
166
|
+
}
|
|
167
|
+
const words = ctx.args.filter((a) => !a.startsWith('-'));
|
|
168
|
+
if (words[0] === 'run' || words[1] === 'run') {
|
|
169
|
+
return await runHelp(ctx, words);
|
|
170
|
+
}
|
|
171
|
+
page({ ...meta, details: meta.details ?? [] });
|
|
150
172
|
},
|
|
151
173
|
run: async (ctx) => {
|
|
152
174
|
// These own their flags, so they are routed before the meta agent's are parsed.
|
|
@@ -216,6 +238,45 @@ export const meta = {
|
|
|
216
238
|
throw usageError(first === undefined ? 'nothing to run' : 'a prompt goes through `run`', `try: zen meta run ${positionals.map(requote).join(' ') || '"what does this project do?"'}`);
|
|
217
239
|
},
|
|
218
240
|
};
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// zen meta run --help
|
|
243
|
+
//
|
|
244
|
+
// The prompts are the reason to type `run`, so its page names them. The
|
|
245
|
+
// shipped ones come from the templates, which `run` refreshes into the project
|
|
246
|
+
// first; a project's own additions are read from the project.
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
async function runHelp(ctx, words) {
|
|
249
|
+
const at = ctx.args.indexOf('--project');
|
|
250
|
+
const flag = ctx.args.find((a) => a.startsWith('--project='))?.slice('--project='.length) ??
|
|
251
|
+
(at >= 0 ? ctx.args[at + 1] : undefined);
|
|
252
|
+
const named = flag ?? (words[1] === 'run' ? words[0] : words[1]);
|
|
253
|
+
const project = (named && !named.startsWith('/') ? await Projects.find(named) : undefined) ??
|
|
254
|
+
(await Projects.current(ctx.cwd));
|
|
255
|
+
const shipped = join(TEMPLATES, 'editor');
|
|
256
|
+
const ours = new Set(await listPrompts(shipped));
|
|
257
|
+
const theirs = project ? await listPrompts(project.dir) : [];
|
|
258
|
+
const names = [...new Set([...ours, ...theirs])].sort();
|
|
259
|
+
const found = await Promise.all(names.map((n) => loadPrompt(ours.has(n) ? shipped : project.dir, n)));
|
|
260
|
+
const lines = [
|
|
261
|
+
'Forms:',
|
|
262
|
+
...RUN_FORMS,
|
|
263
|
+
'',
|
|
264
|
+
project ? `Prompts in ${project.name}:` : 'Prompts:',
|
|
265
|
+
];
|
|
266
|
+
const nameWidth = Math.max(...found.map((p) => p.name.length + 1));
|
|
267
|
+
const room = Math.max(30, Math.min(process.stdout.columns ?? 80, 100) - nameWidth - 6);
|
|
268
|
+
for (const p of found) {
|
|
269
|
+
const [first = '', ...more] = wrap(p.description ?? '', room);
|
|
270
|
+
lines.push(` ${pad(`/${p.name}`, nameWidth)} ${first}`.trimEnd());
|
|
271
|
+
lines.push(...more.map((l) => ` ${' '.repeat(nameWidth)} ${l}`));
|
|
272
|
+
}
|
|
273
|
+
lines.push('', ...RUN_ARGUMENTS, '', ...RUN_OPTIONS, '', 'Examples:', ' zen meta run "what does this project do?"', ' zen meta run acme /project-review', ' zen meta run acme /spec-sync-project agents/triage.md', ' zen meta run acme /analyze --json');
|
|
274
|
+
page({
|
|
275
|
+
usage: 'zen meta run [project] [/<name> [words] | "<question>"] [options]',
|
|
276
|
+
summary: 'Ask the meta agent a question, or run one of the stored prompts.',
|
|
277
|
+
details: lines,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
219
280
|
/** Words the shell would have split are shown back the way they were typed. */
|
|
220
281
|
const requote = (s) => (/\s/.test(s) ? JSON.stringify(s) : s);
|
|
221
282
|
// ---------------------------------------------------------------------------
|
|
@@ -898,8 +959,15 @@ async function pickModel(ctx) {
|
|
|
898
959
|
if (known.length === 0) {
|
|
899
960
|
throw credentialError('no keys to choose from', 'add one: zen key add openai');
|
|
900
961
|
}
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
962
|
+
const held = [...new Set(known)];
|
|
963
|
+
const { browseModels } = await import("./models.js");
|
|
964
|
+
const chosen = await browseModels({
|
|
965
|
+
initial: held.length === 1 ? held[0] : undefined,
|
|
966
|
+
role: 'chat',
|
|
967
|
+
});
|
|
968
|
+
if (!chosen) {
|
|
969
|
+
throw usageError('nothing picked', 'name the model instead: zen meta model <provider>/<id>');
|
|
970
|
+
}
|
|
971
|
+
return `${chosen.provider}/${chosen.id}`;
|
|
904
972
|
}
|
|
905
973
|
//# sourceMappingURL=meta.js.map
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import { type CatalogEntry, type Role } from '../catalog.ts';
|
|
1
2
|
import type { Command } from '../command.ts';
|
|
3
|
+
import { type Provider } from '../keys.ts';
|
|
4
|
+
export interface BrowseRequest {
|
|
5
|
+
/** opens straight into this provider's listing */
|
|
6
|
+
initial?: Provider;
|
|
7
|
+
/** the role filter each listing opens on; tab still cycles */
|
|
8
|
+
role?: Role;
|
|
9
|
+
refresh?: boolean;
|
|
10
|
+
theme?: string;
|
|
11
|
+
}
|
|
12
|
+
/** The browser, for any command that wants a person to pick a model. */
|
|
13
|
+
export declare function browseModels(request?: BrowseRequest): Promise<CatalogEntry | undefined>;
|
|
2
14
|
export declare const models: Command;
|
|
3
15
|
//# sourceMappingURL=models.d.ts.map
|
package/dist/commands/models.js
CHANGED
|
@@ -573,12 +573,29 @@ const browse = async (ctx, args) => {
|
|
|
573
573
|
if (ctx.json || !isInteractive()) {
|
|
574
574
|
throw usageError('browsing needs a person at a terminal', 'from a script: zen models ls --json, or zen models pick --chat');
|
|
575
575
|
}
|
|
576
|
+
const chosen = await browseModels({
|
|
577
|
+
initial,
|
|
578
|
+
theme: values.theme,
|
|
579
|
+
refresh: values.refresh,
|
|
580
|
+
});
|
|
581
|
+
if (!chosen) {
|
|
582
|
+
// Leaving is a fine way to end a browse, but not an answer to a
|
|
583
|
+
// substitution that was waiting for one.
|
|
584
|
+
if (!process.stdout.isTTY) {
|
|
585
|
+
throw new CliError('nothing picked', EXIT.failed);
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
write(chosen.ref);
|
|
590
|
+
};
|
|
591
|
+
/** The browser, for any command that wants a person to pick a model. */
|
|
592
|
+
export async function browseModels(request = {}) {
|
|
576
593
|
const where = await credentials();
|
|
577
594
|
// Offline, like `providers`: the overview should not cost five listings.
|
|
578
595
|
// A provider is fetched when it is opened.
|
|
579
596
|
const cats = await loadCatalogs(PROVIDERS, { offline: true });
|
|
580
597
|
const { browse: draw } = await import("../tui/models.js");
|
|
581
|
-
|
|
598
|
+
return await draw({
|
|
582
599
|
providers: cats.map((c) => ({
|
|
583
600
|
provider: c.provider,
|
|
584
601
|
credential: source(where, c.provider),
|
|
@@ -586,9 +603,10 @@ const browse = async (ctx, args) => {
|
|
|
586
603
|
origin: c.origin,
|
|
587
604
|
fetchedAt: c.fetchedAt,
|
|
588
605
|
})),
|
|
589
|
-
initial,
|
|
590
|
-
|
|
591
|
-
|
|
606
|
+
initial: request.initial,
|
|
607
|
+
role: request.role,
|
|
608
|
+
theme: request.theme,
|
|
609
|
+
load: (provider, refresh) => loadCatalog(provider, { refresh: refresh || request.refresh }),
|
|
592
610
|
balance: async (provider) => provider === 'openrouter' && source(where, provider) ? routerBalance() : undefined,
|
|
593
611
|
test: async (entry) => {
|
|
594
612
|
const role = entry.roles.includes('chat')
|
|
@@ -606,16 +624,7 @@ const browse = async (ctx, args) => {
|
|
|
606
624
|
};
|
|
607
625
|
},
|
|
608
626
|
});
|
|
609
|
-
|
|
610
|
-
// Leaving is a fine way to end a browse, but not an answer to a
|
|
611
|
-
// substitution that was waiting for one.
|
|
612
|
-
if (!process.stdout.isTTY) {
|
|
613
|
-
throw new CliError('nothing picked', EXIT.failed);
|
|
614
|
-
}
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
write(chosen.ref);
|
|
618
|
-
};
|
|
627
|
+
}
|
|
619
628
|
// ---------------------------------------------------------------------------
|
|
620
629
|
// Dispatch
|
|
621
630
|
// ---------------------------------------------------------------------------
|
package/dist/main.js
CHANGED
|
File without changes
|
package/dist/meta/model.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface ModelSources {
|
|
|
23
23
|
export declare const ORDER: readonly ModelSource[];
|
|
24
24
|
export declare function chooseModel(sources: ModelSources): ModelChoice | undefined;
|
|
25
25
|
export declare const SOURCE_LABELS: Record<ModelSource, string>;
|
|
26
|
-
/** First of each row is the default; the rest are
|
|
26
|
+
/** First of each row is the default; the rest are the next best. */
|
|
27
27
|
export declare const RECOMMENDED: Partial<Record<Provider, readonly string[]>>;
|
|
28
28
|
/** The recommended ref for the first provider with a key, or for `only`. */
|
|
29
29
|
export declare function defaultRef(store: KeyStore, only?: Provider): string | undefined;
|
package/dist/meta/model.js
CHANGED
|
@@ -47,7 +47,7 @@ export const SOURCE_LABELS = {
|
|
|
47
47
|
// error. Deep tier deliberately: this agent is pointed at the project itself,
|
|
48
48
|
// and a cheap model reading a specification is a false economy.
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
|
-
/** First of each row is the default; the rest are
|
|
50
|
+
/** First of each row is the default; the rest are the next best. */
|
|
51
51
|
export const RECOMMENDED = {
|
|
52
52
|
openai: ['gpt-5.6-sol', 'gpt-5.6-terra'],
|
|
53
53
|
anthropic: ['claude-opus-5', 'claude-sonnet-5'],
|
package/dist/tui/models.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Balance, type Catalog, type CatalogEntry } from '../catalog.ts';
|
|
1
|
+
import { type Balance, type Catalog, type CatalogEntry, type Role } from '../catalog.ts';
|
|
2
2
|
import type { Provider } from '../keys.ts';
|
|
3
3
|
export interface ProviderRow {
|
|
4
4
|
provider: Provider;
|
|
@@ -21,6 +21,8 @@ export interface BrowseOptions {
|
|
|
21
21
|
providers: readonly ProviderRow[];
|
|
22
22
|
/** opens straight into this provider's listing */
|
|
23
23
|
initial?: Provider;
|
|
24
|
+
/** the role filter a listing opens on, when it has any such model */
|
|
25
|
+
role?: Role;
|
|
24
26
|
theme?: string;
|
|
25
27
|
load(provider: Provider, refresh: boolean): Promise<Catalog>;
|
|
26
28
|
/** what the account has left to spend, for providers that say */
|
package/dist/tui/models.js
CHANGED
|
@@ -103,7 +103,7 @@ function Browser({ options, theme, onPick }) {
|
|
|
103
103
|
const [failure, setFailure] = useState();
|
|
104
104
|
const [query, setQuery] = useState('');
|
|
105
105
|
const [typing, setTyping] = useState(false);
|
|
106
|
-
const [role, setRole] = useState(
|
|
106
|
+
const [role, setRole] = useState(options.role);
|
|
107
107
|
const [selected, setSelected] = useState(0);
|
|
108
108
|
const [verdicts, setVerdicts] = useState(new Map());
|
|
109
109
|
const [balances, setBalances] = useState(new Map());
|
|
@@ -147,7 +147,7 @@ function Browser({ options, theme, onPick }) {
|
|
|
147
147
|
undefined,
|
|
148
148
|
...ROLES.filter((r) => catalog?.entries.some((e) => e.roles.includes(r))),
|
|
149
149
|
], [catalog]);
|
|
150
|
-
const only = present
|
|
150
|
+
const only = present.includes(role) ? role : undefined;
|
|
151
151
|
const rows = useMemo(() => (catalog?.entries ?? [])
|
|
152
152
|
.filter((e) => matches(e, query, { roles: only ? [only] : [] }))
|
|
153
153
|
.sort(byGroup), [catalog, query, only]);
|
|
@@ -159,7 +159,7 @@ function Browser({ options, theme, onPick }) {
|
|
|
159
159
|
setScreen('models');
|
|
160
160
|
setQuery('');
|
|
161
161
|
setTyping(false);
|
|
162
|
-
setRole(
|
|
162
|
+
setRole(options.role);
|
|
163
163
|
setSelected(0);
|
|
164
164
|
top.current = 0;
|
|
165
165
|
if (!catalogs.has(p)) {
|
|
@@ -287,7 +287,7 @@ function Browser({ options, theme, onPick }) {
|
|
|
287
287
|
setSelected(0);
|
|
288
288
|
}
|
|
289
289
|
else if (key.tab) {
|
|
290
|
-
setRole((
|
|
290
|
+
setRole(present[(present.indexOf(only) + 1) % present.length]);
|
|
291
291
|
setSelected(0);
|
|
292
292
|
}
|
|
293
293
|
else if ((key.return || key.rightArrow || input === 'l') && current) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenera/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.28",
|
|
4
4
|
"description": "Command-line front end for @zenera/neo: agentic projects you can run, share and commit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"@google/genai": "^2.18.0",
|
|
53
53
|
"@inkjs/ui": "^2.0.0",
|
|
54
54
|
"@openrouter/sdk": "^1.2.80",
|
|
55
|
-
"@zenera/neo": "^1.1.
|
|
56
|
-
"@zenera/view": "^1.1.
|
|
55
|
+
"@zenera/neo": "^1.1.28",
|
|
56
|
+
"@zenera/view": "^1.1.28",
|
|
57
57
|
"google-auth-library": "^10.0.0",
|
|
58
58
|
"ink": "^7.1.1",
|
|
59
59
|
"openai": "^6.0.0",
|
|
@@ -61,6 +61,11 @@ standard above using its nodes. So, during an analysis:
|
|
|
61
61
|
- The only commands that reach outside the run are `zen memory search`,
|
|
62
62
|
`zen rag docs search` and `zen rag schema search` - to check whether
|
|
63
63
|
something the run needed was findable.
|
|
64
|
+
- You run on the host, in the project root - not in the run's sandbox. A path
|
|
65
|
+
the run used as `/assets/<x>` is `assets/<x>` here (or under the folder
|
|
66
|
+
`assets:` names in `agents.yaml`). So call
|
|
67
|
+
`zen rag docs search -d assets/<x> "<query>"`, never `-d /assets/...`, and
|
|
68
|
+
always pass `-d`: `$ZEN_DOCS_DB` / `$ZEN_SCHEMA_DB` may name the sandbox path.
|
|
64
69
|
- Never run `python3`, `node`, `curl`, `wget` or anything under the run's
|
|
65
70
|
`workspace/`, never call the project's APIs or services, never run
|
|
66
71
|
`zen run`, and never read the project's `resources/` or reference
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { Case } from './dataset/types.ts';
|
|
2
|
-
import { type Tuning } from './tuning.ts';
|
|
3
|
-
export interface GuardTry {
|
|
4
|
-
dir: string;
|
|
5
|
-
verdict: string;
|
|
6
|
-
passed: boolean;
|
|
7
|
-
summary?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface GuardResult {
|
|
10
|
-
case: string;
|
|
11
|
-
tries: GuardTry[];
|
|
12
|
-
broken: boolean;
|
|
13
|
-
}
|
|
14
|
-
/** A guard case as the live view and the status page show it, updated as it goes. */
|
|
15
|
-
export interface GuardSeat {
|
|
16
|
-
case: string;
|
|
17
|
-
try: number;
|
|
18
|
-
step: 'run' | 'analyze' | 'passed' | 'broken';
|
|
19
|
-
/** the try's directory */
|
|
20
|
-
dir: string;
|
|
21
|
-
since: number;
|
|
22
|
-
}
|
|
23
|
-
/** A case is broken only after failing this many tries in a row. */
|
|
24
|
-
export declare const GUARD_TRIES = 2;
|
|
25
|
-
/** Completed cases outside `exclude`, in an order fixed by the seed and the apply. */
|
|
26
|
-
export declare function guardCases(t: Tuning, apply: string, exclude: Set<string>): Case[];
|
|
27
|
-
/** Runs the guard for one attempt of an apply; `patch` is that attempt's diff. */
|
|
28
|
-
export declare function guard(t: Tuning, dir: string, attempt: number, exclude: Set<string>, patch: string): Promise<GuardResult[]>;
|
|
29
|
-
//# sourceMappingURL=guard.d.ts.map
|