@zenera/cli 1.1.41 → 1.1.43
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 +6 -4
- package/dist/commands/check.js +7 -5
- package/dist/commands/run.js +18 -6
- package/dist/resolve.d.ts +2 -0
- package/dist/resolve.js +1 -1
- package/dist/validate.d.ts +11 -0
- package/dist/validate.js +46 -3
- package/package.json +4 -4
- package/templates/editor/.github/copilot-instructions.md +7 -3
- package/templates/editor/.github/prompts/spec-sync-project.prompt.md +19 -4
- package/templates/editor/.github/skills/zen-cli/SKILL.md +2 -1
- package/templates/editor/.github/skills/zen-cli/references/check.md +2 -1
- package/templates/editor/.github/skills/zen-cli/references/run.md +12 -4
- package/templates/editor/.github/skills/zen-review/scripts/check-paths.sh +5 -1
- package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +79 -2
- package/templates/project/agents/memory-instructions.md +14 -0
- package/templates/project/agents/prompts/default.md.tmpl +60 -0
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ in line with it, validate the setup, and run it:
|
|
|
115
115
|
```sh
|
|
116
116
|
zen meta run my-project /spec-sync-project
|
|
117
117
|
zen check my-project
|
|
118
|
-
zen run my-project "Summarize this repository and write NOTES.md"
|
|
118
|
+
zen run my-project --here "Summarize this repository and write NOTES.md"
|
|
119
119
|
zen inspect
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -127,13 +127,15 @@ than silently guessing. After you answer, apply that feedback with
|
|
|
127
127
|
## Run it your way
|
|
128
128
|
|
|
129
129
|
With no prompt, `zen run` opens the terminal UI. Provide a prompt for a
|
|
130
|
-
one-shot run, or use JSON and batch mode to integrate with other tools
|
|
130
|
+
one-shot run, or use JSON and batch mode to integrate with other tools. A
|
|
131
|
+
one-shot run works in a fresh directory inside its session; `--here` points it
|
|
132
|
+
at the directory you are in instead:
|
|
131
133
|
|
|
132
134
|
```sh
|
|
133
135
|
zen run my-project # interactive terminal UI
|
|
134
|
-
zen run my-project "Review the latest changes"
|
|
136
|
+
zen run my-project --here "Review the latest changes"
|
|
135
137
|
git diff | zen run my-project
|
|
136
|
-
zen run my-project "Summarize the changes" --read-only
|
|
138
|
+
zen run my-project --here "Summarize the changes" --read-only
|
|
137
139
|
zen run my-project "Triage these alerts" --json | jq
|
|
138
140
|
zen run batch --input cases.json # run a set of requests
|
|
139
141
|
```
|
package/dist/commands/check.js
CHANGED
|
@@ -6,7 +6,7 @@ import { duration } from "../narrate.js";
|
|
|
6
6
|
import { project as resolveProject } from "../resolve.js";
|
|
7
7
|
import { refreshShared } from "../scaffold.js";
|
|
8
8
|
import { bold, count, cyan, dim, green, invalidError, json, progress, red, table, usageError, write, writeAll, yellow, } from "../term.js";
|
|
9
|
-
import { validateProject, } from "../validate.js";
|
|
9
|
+
import { repinDockerfile, validateProject, } from "../validate.js";
|
|
10
10
|
const USAGE = 'zen check [name|dir] [--project <name|dir>] [--fix] [--no-sandbox] [--no-models] [--strict] [--quiet]';
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
// zen check
|
|
@@ -57,8 +57,10 @@ export const check = {
|
|
|
57
57
|
'(.vscode/ and .github/). They restate how this version of zen behaves,',
|
|
58
58
|
'so a copy left behind by an upgrade is worse than none — they are',
|
|
59
59
|
'replaced whether or not they were edited, and the report that follows is',
|
|
60
|
-
'of the project as repaired.
|
|
61
|
-
'
|
|
60
|
+
'of the project as repaired. It also pins the @zenera/* installs in the',
|
|
61
|
+
'sandbox Dockerfile to this zen\u2019s version, changing nothing else in it.',
|
|
62
|
+
'Nothing else is touched: agents.yaml, the prompts, the specification and',
|
|
63
|
+
'agents/instructions.md are yours.',
|
|
62
64
|
'',
|
|
63
65
|
'The argument is a directory if one is there and a registered project',
|
|
64
66
|
'name otherwise; with neither, the project you are standing in.',
|
|
@@ -87,7 +89,7 @@ export const check = {
|
|
|
87
89
|
// the files that are there now and the exit code answers the repaired
|
|
88
90
|
// project. A fix that left an error behind in the output would be read
|
|
89
91
|
// as a fix that failed.
|
|
90
|
-
const fixed = values.fix ? refreshShared(dir) : [];
|
|
92
|
+
const fixed = values.fix ? [...refreshShared(dir), ...repinDockerfile(dir)] : [];
|
|
91
93
|
// Materialised first, so the credential verdicts are the ones a run
|
|
92
94
|
// would reach: a key in the environment and a key in the keyring are
|
|
93
95
|
// the same key by the time the library asks. The project's own `.env`
|
|
@@ -329,7 +331,7 @@ function fixedLines(fixed) {
|
|
|
329
331
|
return [];
|
|
330
332
|
}
|
|
331
333
|
return [
|
|
332
|
-
`${bold('
|
|
334
|
+
`${bold('Fixed')} ${dim(count(fixed.length, 'file'))}`,
|
|
333
335
|
...fixed.map((f) => ` ${green('wrote')} ${f}`),
|
|
334
336
|
'',
|
|
335
337
|
];
|
package/dist/commands/run.js
CHANGED
|
@@ -33,6 +33,8 @@ export const run = {
|
|
|
33
33
|
' --new Start a new session without asking which one.',
|
|
34
34
|
' --input <file> Read the whole request from JSON. `-` is stdin.',
|
|
35
35
|
' --workspace <dir> Directory the agent can read and write.',
|
|
36
|
+
' Default: a fresh one inside the new session.',
|
|
37
|
+
' --here Same as --workspace with the directory you are in.',
|
|
36
38
|
' --memory <dir> Directory the agents remember into.',
|
|
37
39
|
' --memory-read-only Recall from it, write nothing back.',
|
|
38
40
|
' --model <ref> Use this model instead of the default.',
|
|
@@ -59,7 +61,9 @@ export const run = {
|
|
|
59
61
|
'the answer alone and `2>/dev/null` hides the progress.',
|
|
60
62
|
'',
|
|
61
63
|
'A prompt always starts a new session, so --new is for the TUI, where the',
|
|
62
|
-
'alternative is being asked which session to continue.',
|
|
64
|
+
'alternative is being asked which session to continue. Its workspace is a',
|
|
65
|
+
'fresh, empty directory inside that session; to ask about the files where',
|
|
66
|
+
'you are, say --here.',
|
|
63
67
|
'',
|
|
64
68
|
'--input names a file holding the whole request - project, input, workspace,',
|
|
65
69
|
'memory - and what it says wins over the same flag on the line. Its paths are',
|
|
@@ -98,6 +102,7 @@ export const run = {
|
|
|
98
102
|
' zen run open the TUI in this project',
|
|
99
103
|
' zen run acme open the TUI in the acme project',
|
|
100
104
|
' zen run "what changed?" ask that, print the answer, exit',
|
|
105
|
+
' zen run --here "what changed?" ... about the files in this directory',
|
|
101
106
|
' git diff | zen run take the prompt from stdin',
|
|
102
107
|
' zen run "what changed?" > out.md redirect the answer into a file',
|
|
103
108
|
' zen run --out out.md "what changed?" write the answer to out.md only',
|
|
@@ -118,6 +123,7 @@ export const run = {
|
|
|
118
123
|
new: { type: 'boolean' },
|
|
119
124
|
input: { type: 'string' },
|
|
120
125
|
workspace: { type: 'string' },
|
|
126
|
+
here: { type: 'boolean' },
|
|
121
127
|
memory: { type: 'string' },
|
|
122
128
|
'memory-read-only': { type: 'boolean' },
|
|
123
129
|
model: { type: 'string' },
|
|
@@ -155,16 +161,20 @@ export const run = {
|
|
|
155
161
|
if (values.theme !== undefined && !parseChoice(values.theme)) {
|
|
156
162
|
throw usageError(`unknown theme: ${values.theme}`, 'dark, light or auto');
|
|
157
163
|
}
|
|
164
|
+
if (values.here && values.workspace !== undefined) {
|
|
165
|
+
throw usageError('--here and --workspace both name the workspace', 'keep one');
|
|
166
|
+
}
|
|
158
167
|
// With `--input -` the pipe carries the request, not the prompt, so it
|
|
159
168
|
// is read here and never looked at again below.
|
|
160
169
|
const request = values.input ? await readRequest(values.input, ctx.cwd, piped) : undefined;
|
|
161
170
|
const prompt = typed || (request ? '' : (piped ?? ''));
|
|
162
171
|
// A prompt on the command line is a request for an answer, not a
|
|
163
172
|
// conversation to pick up. So it answers the three questions itself:
|
|
164
|
-
// a fresh session,
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
173
|
+
// a fresh session, a fresh directory inside it as the workspace, and
|
|
174
|
+
// no confirmation for any of it. Reading the files where it was typed
|
|
175
|
+
// takes --here: an agent that can write should not land in whatever
|
|
176
|
+
// directory the shell happened to be in. Every flag still wins, and
|
|
177
|
+
// the TUI, where there is someone to ask, still asks.
|
|
168
178
|
const input = request ? request.input : prompt;
|
|
169
179
|
const shot = Boolean(prompt) || Boolean(request);
|
|
170
180
|
if (values.events && !shot) {
|
|
@@ -175,7 +185,8 @@ export const run = {
|
|
|
175
185
|
project: request?.project ?? values.project ?? (named ? head : undefined),
|
|
176
186
|
session: values.session,
|
|
177
187
|
fresh: values.new || (shot && !values.session),
|
|
178
|
-
workspace: request?.workspace ?? values.workspace ?? (
|
|
188
|
+
workspace: request?.workspace ?? values.workspace ?? (values.here ? ctx.cwd : undefined),
|
|
189
|
+
ask: !shot,
|
|
179
190
|
yes: values.yes || shot,
|
|
180
191
|
});
|
|
181
192
|
const engine = await Engine.open({
|
|
@@ -234,6 +245,7 @@ const NOT_IN_A_BATCH = [
|
|
|
234
245
|
['session', 'every item is a new session of its own'],
|
|
235
246
|
['new', 'every item is new already'],
|
|
236
247
|
['workspace', 'they cannot share one; each item gets its own, or names one in the file'],
|
|
248
|
+
['here', 'they cannot share one; each item gets its own, or names one in the file'],
|
|
237
249
|
['plain', 'a batch never draws the TUI'],
|
|
238
250
|
['theme', 'a batch never draws the TUI'],
|
|
239
251
|
['events', 'each item already has its own output.json'],
|
package/dist/resolve.d.ts
CHANGED
package/dist/resolve.js
CHANGED
|
@@ -137,7 +137,7 @@ export async function chooseWorkspace(session, want) {
|
|
|
137
137
|
await approve(at, session, want);
|
|
138
138
|
return at;
|
|
139
139
|
}
|
|
140
|
-
if (!term.isInteractive()) {
|
|
140
|
+
if (want.ask === false || !term.isInteractive()) {
|
|
141
141
|
return own;
|
|
142
142
|
}
|
|
143
143
|
const chosen = await term.choose('Workspace — what the agent can read and write', [
|
package/dist/validate.d.ts
CHANGED
|
@@ -176,4 +176,15 @@ export interface ValidateOptions {
|
|
|
176
176
|
}
|
|
177
177
|
export declare function validateProject(opts: ValidateOptions): Promise<Report>;
|
|
178
178
|
export declare function availableTools(root: string, config: ProjectConfig): AnyTool<unknown>[];
|
|
179
|
+
/**
|
|
180
|
+
* Pins every `@zenera/*` install in the project's Dockerfile to this `zen`,
|
|
181
|
+
* which is what `zen check --fix` does about `sandbox.pin.stale` and
|
|
182
|
+
* `sandbox.pin.absent`. Only the version after each name is touched, and only
|
|
183
|
+
* on lines `checkPin` reads, so whatever else the file holds is left alone.
|
|
184
|
+
*
|
|
185
|
+
* Returns the project-relative path when the file was rewritten, and nothing
|
|
186
|
+
* when there is no Dockerfile, it resolves outside the project, or it already
|
|
187
|
+
* pins this version — a project that does not load is the report's to explain.
|
|
188
|
+
*/
|
|
189
|
+
export declare function repinDockerfile(root: string): string[];
|
|
179
190
|
//# sourceMappingURL=validate.d.ts.map
|
package/dist/validate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SHAPES, auditModels, credentialFor, envNames, form, resolveBuild, } from '@zenera/engine';
|
|
2
2
|
import { EXA_GROUP, FileSkillProvider, SANDBOX_MOUNT, Sandbox, exaTools, fileTools, frontmatter, projectRegistry, readProjectConfig, sandboxTools, selectTools, toList, } from '@zenera/neo';
|
|
3
|
-
import { existsSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, } from 'node:fs';
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
|
-
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
6
6
|
import { probeModels } from "./liveness.js";
|
|
7
7
|
import { BuildError, ensurePodmanReady } from "./podman.js";
|
|
8
8
|
import { ownVersion, staleShared } from "./scaffold.js";
|
|
@@ -1315,7 +1315,7 @@ function checkPin(path, rel, add) {
|
|
|
1315
1315
|
}
|
|
1316
1316
|
}
|
|
1317
1317
|
}
|
|
1318
|
-
const fix = `pin them to ${mine}, the version \`zen --version\` reports`;
|
|
1318
|
+
const fix = `pin them to ${mine}, the version \`zen --version\` reports — \`zen check --fix\` does it`;
|
|
1319
1319
|
if (stale.size > 0) {
|
|
1320
1320
|
add({
|
|
1321
1321
|
severity: 'warning',
|
|
@@ -1337,6 +1337,49 @@ function checkPin(path, rel, add) {
|
|
|
1337
1337
|
});
|
|
1338
1338
|
}
|
|
1339
1339
|
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Pins every `@zenera/*` install in the project's Dockerfile to this `zen`,
|
|
1342
|
+
* which is what `zen check --fix` does about `sandbox.pin.stale` and
|
|
1343
|
+
* `sandbox.pin.absent`. Only the version after each name is touched, and only
|
|
1344
|
+
* on lines `checkPin` reads, so whatever else the file holds is left alone.
|
|
1345
|
+
*
|
|
1346
|
+
* Returns the project-relative path when the file was rewritten, and nothing
|
|
1347
|
+
* when there is no Dockerfile, it resolves outside the project, or it already
|
|
1348
|
+
* pins this version — a project that does not load is the report's to explain.
|
|
1349
|
+
*/
|
|
1350
|
+
export function repinDockerfile(root) {
|
|
1351
|
+
let rel;
|
|
1352
|
+
try {
|
|
1353
|
+
const ref = readProjectConfig(root).config.sandbox?.build?.dockerfile;
|
|
1354
|
+
rel = ref === undefined ? undefined : normalise(root, ref);
|
|
1355
|
+
}
|
|
1356
|
+
catch {
|
|
1357
|
+
return [];
|
|
1358
|
+
}
|
|
1359
|
+
if (rel === undefined) {
|
|
1360
|
+
return [];
|
|
1361
|
+
}
|
|
1362
|
+
const path = join(root, rel);
|
|
1363
|
+
let text;
|
|
1364
|
+
try {
|
|
1365
|
+
text = readFileSync(path, 'utf8');
|
|
1366
|
+
}
|
|
1367
|
+
catch {
|
|
1368
|
+
return [];
|
|
1369
|
+
}
|
|
1370
|
+
const mine = ownVersion();
|
|
1371
|
+
const next = text
|
|
1372
|
+
.split('\n')
|
|
1373
|
+
.map((line) => /^\s*#/.test(line)
|
|
1374
|
+
? line
|
|
1375
|
+
: line.replace(ZENERA_INSTALL, (_, name) => `@zenera/${name}@${mine}`))
|
|
1376
|
+
.join('\n');
|
|
1377
|
+
if (next === text) {
|
|
1378
|
+
return [];
|
|
1379
|
+
}
|
|
1380
|
+
writeFileSync(path, next);
|
|
1381
|
+
return [rel.split(sep).join('/')];
|
|
1382
|
+
}
|
|
1340
1383
|
/** Written by the smoke test and read back, to prove the mount is real. */
|
|
1341
1384
|
const SMOKE = 'zen-check';
|
|
1342
1385
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenera/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.43",
|
|
4
4
|
"description": "Command-line front end for @zenera/neo: agentic projects you can run, share and commit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"@google/genai": "^2.30.0",
|
|
55
55
|
"@inkjs/ui": "^2.0.0",
|
|
56
56
|
"@openrouter/sdk": "^1.4.25",
|
|
57
|
-
"@zenera/engine": "^1.1.
|
|
58
|
-
"@zenera/neo": "^1.1.
|
|
59
|
-
"@zenera/view": "^1.1.
|
|
57
|
+
"@zenera/engine": "^1.1.43",
|
|
58
|
+
"@zenera/neo": "^1.1.43",
|
|
59
|
+
"@zenera/view": "^1.1.43",
|
|
60
60
|
"google-auth-library": "^10.0.0",
|
|
61
61
|
"ink": "^7.1.1",
|
|
62
62
|
"openai": "^6.0.0",
|
|
@@ -55,7 +55,10 @@ code, `zen-topology` for `handoffs:` or `fork:` on any agent,
|
|
|
55
55
|
configured and already passing `zen check` is exactly the case that looks
|
|
56
56
|
finished and is not: the obligation lives in the skill, so a review that never
|
|
57
57
|
opens the skill cannot find what is missing. This applies whatever prompted the
|
|
58
|
-
change - a task scoped to `SPECIFICATION.md` does not narrow it.
|
|
58
|
+
change - a task scoped to `SPECIFICATION.md` does not narrow it. There is one
|
|
59
|
+
exception: a `/spec-sync-project` pass that the `zen-spec-sync` skill classes as
|
|
60
|
+
**spec-only**. It touches no agent, prompt, skill or wiring, so there is no
|
|
61
|
+
obligation for it to meet.
|
|
59
62
|
|
|
60
63
|
**Load `zen-instructions` before writing, editing, reviewing or deleting any
|
|
61
64
|
`agents/*instructions.md`.** They are the house rules, prepended to every
|
|
@@ -1990,8 +1993,9 @@ candidates; these are the judgements to make about each one)**
|
|
|
1990
1993
|
and says so honestly - an unchanged specification line is not evidence
|
|
1991
1994
|
that anything implements it
|
|
1992
1995
|
- [ ] No question in `SPECIFICATION-FEEDBACK.md` has a box marked `[x]`, or
|
|
1993
|
-
exactly one answer left under it, without a matching `✅ Answered` entry
|
|
1994
|
-
|
|
1996
|
+
exactly one answer left under it, without a matching `✅ Answered` entry
|
|
1997
|
+
or a strike naming the specification line that superseded it - that is
|
|
1998
|
+
an answer given and never folded into the specification
|
|
1995
1999
|
- [ ] `.spec-sync/` is committed and `.tmp/` is not
|
|
1996
2000
|
|
|
1997
2001
|
---
|
|
@@ -32,7 +32,14 @@ accounted for against the specification.
|
|
|
32
32
|
|
|
33
33
|
The scope decides what you **read and edit**. It never decides what you
|
|
34
34
|
**verify**: §6, §7 and the five checks in the skill run at full size on every
|
|
35
|
-
pass, however small the diff.
|
|
35
|
+
pass, however small the diff. The one exception is the skill's **spec-only
|
|
36
|
+
pass**, and only under the conditions the skill sets.
|
|
37
|
+
|
|
38
|
+
When inputs disagree - a ticked answer against a later specification edit, the
|
|
39
|
+
specification against `zen --version` - use the order of precedence in the
|
|
40
|
+
skill. The user's own message comes first. Do not use session history, file
|
|
41
|
+
timestamps or `git log` to work out why something changed. Whatever the order
|
|
42
|
+
does not settle becomes a question in `SPECIFICATION-FEEDBACK.md`.
|
|
36
43
|
|
|
37
44
|
## 1. Read before changing anything
|
|
38
45
|
|
|
@@ -120,8 +127,10 @@ the model to compute or remember. Specifically:
|
|
|
120
127
|
the container is the same one that built it. An unpinned name drifts away from
|
|
121
128
|
the host on the next image build; a stale pin leaves the container holding an
|
|
122
129
|
older `zen` than the session outside it. `zen check` reports both, as
|
|
123
|
-
`sandbox.pin.absent` and `sandbox.pin.stale`,
|
|
124
|
-
|
|
130
|
+
`sandbox.pin.absent` and `sandbox.pin.stale`, and `zen check --fix` repairs
|
|
131
|
+
both by rewriting only the version after each `@zenera/*` name. The pin
|
|
132
|
+
follows `zen --version` even when `SPECIFICATION.md` names another number.
|
|
133
|
+
The `zen-spec-sync` skill gives the one question to raise in that case.
|
|
125
134
|
|
|
126
135
|
Never delete an **extra** without saying so. Report it, say why the
|
|
127
136
|
specification does not cover it, and leave it unless I tell you to remove it.
|
|
@@ -379,11 +388,17 @@ since the last pass is work to close, not a question to ask again. If a question
|
|
|
379
388
|
has a box marked `[x]`, or one answer left under it, and no `✅` entry, the
|
|
380
389
|
reader answered it and never ran `/spec-apply-feedback` - implement that
|
|
381
390
|
answer, fold its text into `SPECIFICATION.md` yourself, and say so in the chat.
|
|
391
|
+
Do this unless the specification has since been edited on the same point. In
|
|
392
|
+
that case the edit supersedes the answer: strike the question, naming the line
|
|
393
|
+
that replaced it, and leave the newer text alone.
|
|
382
394
|
|
|
383
395
|
## 6. Run `scripts/_setup.sh` and watch it finish
|
|
384
396
|
|
|
385
397
|
After `zen check` passes, run `scripts/_setup.sh` yourself. A pass is not done
|
|
386
|
-
until it has run to completion in this session.
|
|
398
|
+
until it has run to completion in this session. The exception is a spec-only
|
|
399
|
+
pass, as the `zen-spec-sync` skill defines it: there `scripts/_setup.sh --check`
|
|
400
|
+
reporting every step `skipped` is enough, and anything else sends you back to
|
|
401
|
+
the full run below.
|
|
387
402
|
|
|
388
403
|
It is slow - embedding a large corpus is hours, and the progress line says how
|
|
389
404
|
many - so start it in the background and follow the logs instead of waiting
|
|
@@ -70,7 +70,8 @@ guess a flag, and do not read them all.
|
|
|
70
70
|
zen init scaffold a project here, pick a model
|
|
71
71
|
zen check validate everything before spending a turn
|
|
72
72
|
zen run the TUI
|
|
73
|
-
zen run "what changed?" one answer,
|
|
73
|
+
zen run "what changed?" one answer, in a fresh session directory
|
|
74
|
+
zen run --here "what changed?" ... about the files in this directory
|
|
74
75
|
zen run batch --input cases.json many questions at once, each isolated
|
|
75
76
|
zen inspect --open what the model was actually given
|
|
76
77
|
zen inspect graph --dir <run dir> the same run as a graph you can read
|
|
@@ -55,7 +55,8 @@ checked too. A word that is neither is a usage error (exit 2), not a report.
|
|
|
55
55
|
is written once, by `zen init`, and nothing rewrites it afterwards, so it falls
|
|
56
56
|
behind the host on the next upgrade and the container ends up holding a
|
|
57
57
|
different `zen` than the session outside it. Read rather than run, so this one
|
|
58
|
-
is reported with `--no-sandbox` too.
|
|
58
|
+
is reported with `--no-sandbox` too. `--fix` rewrites the pins to this `zen`'s
|
|
59
|
+
version and touches nothing else in the Dockerfile.
|
|
59
60
|
|
|
60
61
|
### Findings
|
|
61
62
|
|
|
@@ -16,6 +16,7 @@ answers once on stdout and exits.
|
|
|
16
16
|
| `--new` | Start a new session rather than continuing one |
|
|
17
17
|
| `--input <file>` | Read the whole request from JSON; `-` is stdin |
|
|
18
18
|
| `--workspace <dir>` | What the agent may read and write |
|
|
19
|
+
| `--here` | `--workspace` with the directory you are in |
|
|
19
20
|
| `--memory <dir>` | Where the agents remember into, instead of the project's |
|
|
20
21
|
| `--memory-read-only` | Recall from it; write nothing back |
|
|
21
22
|
| `--model <ref>` | Override the default model for this run |
|
|
@@ -270,7 +271,7 @@ question of what the agents know without changing what they know.
|
|
|
270
271
|
|
|
271
272
|
### What means nothing in a batch
|
|
272
273
|
|
|
273
|
-
`--session`, `--new`, `--workspace`, `--plain` and `--theme` are refused by
|
|
274
|
+
`--session`, `--new`, `--workspace`, `--here`, `--plain` and `--theme` are refused by
|
|
274
275
|
name rather than ignored. Every item is a new session of its own, they cannot
|
|
275
276
|
share a workspace, and a batch never draws the TUI.
|
|
276
277
|
|
|
@@ -297,11 +298,18 @@ git diff | zen run "summarise this diff"
|
|
|
297
298
|
## What a prompt on the command line implies
|
|
298
299
|
|
|
299
300
|
A prompt is a request for an answer, not a conversation to pick up, so it
|
|
300
|
-
answers the three questions itself: **a fresh session**, **
|
|
301
|
-
|
|
302
|
-
|
|
301
|
+
answers the three questions itself: **a fresh session**, **a fresh, empty
|
|
302
|
+
directory inside it** (`<session>/workspace/`) as the workspace, and **no
|
|
303
|
+
confirmation**. To have it read and write the directory you are in, say
|
|
304
|
+
`--here` (the same as `--workspace .`). Every flag still wins - `--session`,
|
|
305
|
+
`--workspace`, `--here` and `--read-only` override it - and the TUI, where
|
|
303
306
|
there is someone to ask, still asks.
|
|
304
307
|
|
|
308
|
+
```
|
|
309
|
+
zen run "draft a release note" works in sessions/<id>/workspace/
|
|
310
|
+
zen run --here "what changed?" reads and writes the directory you are in
|
|
311
|
+
```
|
|
312
|
+
|
|
305
313
|
There is no `resume`. A session continues itself, because its state is what it
|
|
306
314
|
is; `--session <id>` picks which one.
|
|
307
315
|
|
|
@@ -36,8 +36,12 @@ NOISE='^(and/or|he/she|his/her|input/output|read/write|w/o|n/a|N/A|24/7|km/h)$|^
|
|
|
36
36
|
# A url, stripped before tokenising rather than filtered after it — the token
|
|
37
37
|
# regex reads `https://acme.dev/docs` as `//acme.dev` *and* `/docs`, and the
|
|
38
38
|
# second half is indistinguishable from a real absolute path once it is alone.
|
|
39
|
+
#
|
|
40
|
+
# A meta agent command, `zen meta run /spec-edit`, goes the same way: its
|
|
41
|
+
# `/<name>` is a stored prompt, not a file the running agent is told to open.
|
|
39
42
|
strip_urls() {
|
|
40
|
-
sed -E 's#[a-zA-Z][a-zA-Z0-9+.-]*://[^][ )"'"'"'`>]*##g'
|
|
43
|
+
sed -E -e 's#[a-zA-Z][a-zA-Z0-9+.-]*://[^][ )"'"'"'`>]*##g' \
|
|
44
|
+
-e 's#(zen meta run( [A-Za-z0-9_.-]+)?) /[A-Za-z0-9_-]+#\1#g' "$1"
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
# The three trees §2.5 names, prose only: a skill's own script is code the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: zen-spec-sync
|
|
3
|
-
description: Running `/spec-sync-project` as an incremental pass - the committed `.spec-sync` baseline of what the last pass applied, how to scope a pass from the specification and feedback diffs and from what moved in the project itself, the five checks a narrow pass may never skip, which skills a pass must load before editing (`zen-instructions` for any house rule it touches), the iteration history format, and why the baseline is written last. Load before every `/spec-sync-project`, including the first one on a project that has no baseline yet.
|
|
3
|
+
description: Running `/spec-sync-project` as an incremental pass - the committed `.spec-sync` baseline of what the last pass applied, how to scope a pass from the specification and feedback diffs and from what moved in the project itself, which input wins when they disagree, the five checks a narrow pass may never skip, which skills a pass must load before editing (`zen-instructions` for any house rule it touches), the iteration history format, and why the baseline is written last. Load before every `/spec-sync-project`, including the first one on a project that has no baseline yet.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Syncing a project with its specification, one change at a time
|
|
@@ -103,9 +103,64 @@ editing anything, and report it in the chat as the pass's scope.
|
|
|
103
103
|
covers it, and you say which line, or it does not, and it is an **extra**
|
|
104
104
|
reported under `➕`.
|
|
105
105
|
|
|
106
|
+
## When the inputs disagree
|
|
107
|
+
|
|
108
|
+
Two inputs can ask for different things - a ticked answer and a later edit to
|
|
109
|
+
the same specification line, say. Decide by this order, highest first, and do
|
|
110
|
+
not weigh them again:
|
|
111
|
+
|
|
112
|
+
1. **The user's message that started this pass**, where it says more than the
|
|
113
|
+
command name. It is the newest and most direct statement of intent there is.
|
|
114
|
+
2. **`SPECIFICATION.md` as it stands now.** An edit to the specification is
|
|
115
|
+
made on purpose and after the questions were written, so it outranks them.
|
|
116
|
+
3. **An answer ticked in `SPECIFICATION-FEEDBACK.md`** and not yet folded in.
|
|
117
|
+
4. **What the last pass left open.**
|
|
118
|
+
|
|
119
|
+
A ticked answer that the current specification text contradicts was
|
|
120
|
+
**superseded**, not ignored: do not fold its text back in over the newer line.
|
|
121
|
+
Strike the question with one line naming the specification line that replaced
|
|
122
|
+
it, and say in the chat that you did.
|
|
123
|
+
|
|
124
|
+
Facts outrank all four. `zen --version` decides the sandbox pin (below); a
|
|
125
|
+
specification line asking for something impossible is a question, not an order.
|
|
126
|
+
Where the order above does not decide, build what the higher-ranked input asks
|
|
127
|
+
for, write **one** question in `SPECIFICATION-FEEDBACK.md`, and carry on.
|
|
128
|
+
|
|
129
|
+
### Do not reconstruct intent
|
|
130
|
+
|
|
131
|
+
The diffs say what changed. The order above says what wins. A feedback question
|
|
132
|
+
covers whatever is left. Do not use anything else to work out _why_ somebody
|
|
133
|
+
changed something: no session history or chat transcripts, no file timestamps,
|
|
134
|
+
no `git log` or `git blame`, no older history entries beyond the one `applied:`
|
|
135
|
+
names. If a pass catches itself guessing at what the human meant, that guess is
|
|
136
|
+
the text of a question, and the pass moves on.
|
|
137
|
+
|
|
138
|
+
### The `zen` version in the specification
|
|
139
|
+
|
|
140
|
+
The `@zenera/cli` and `@zenera/rag` pin in `sandbox/Dockerfile` always follows
|
|
141
|
+
`zen --version` - `zen check` warns on any other number, older or newer. Nothing
|
|
142
|
+
outranks that, not a specification line and not the user's message: the image
|
|
143
|
+
cannot hold a `zen` other than the one building it. If a version was asked for
|
|
144
|
+
that is not the one in hand, say so in the chat - `zen` has to be upgraded on the
|
|
145
|
+
host first.
|
|
146
|
+
|
|
147
|
+
When `SPECIFICATION.md` names an exact version other than `zen --version`, pin
|
|
148
|
+
the Dockerfile to `zen --version` and keep **one** `✏️` question open for that
|
|
149
|
+
line. If one is already open, update it instead of adding another. Offer these
|
|
150
|
+
answers:
|
|
151
|
+
|
|
152
|
+
- State the pin without a number - "pinned to the `zen` version that builds the
|
|
153
|
+
image" - so the next upgrade does not raise the question again
|
|
154
|
+
_(recommended)_.
|
|
155
|
+
- Replace the number with the one `zen --version` reports.
|
|
156
|
+
- Keep the number, upgrade `zen` on the host to it, and run the pass again.
|
|
157
|
+
|
|
158
|
+
That is the whole of the work. A version mismatch is not a reason to look for
|
|
159
|
+
anything else.
|
|
160
|
+
|
|
106
161
|
## The five checks a narrow pass may never skip
|
|
107
162
|
|
|
108
|
-
However small the diff:
|
|
163
|
+
However small the diff - with the one narrowing for a spec-only pass below:
|
|
109
164
|
|
|
110
165
|
1. **`zen check` passes.** Run it before the first edit and again after the
|
|
111
166
|
last.
|
|
@@ -131,6 +186,28 @@ However small the diff:
|
|
|
131
186
|
it, the history entry claims it, and the baseline records it as applied.
|
|
132
187
|
Specification-driven rules go in a topic file of the project's own beside it.
|
|
133
188
|
|
|
189
|
+
### The spec-only pass
|
|
190
|
+
|
|
191
|
+
A pass qualifies when **all** of the following hold:
|
|
192
|
+
|
|
193
|
+
- the implementation diff (input 4) is empty
|
|
194
|
+
- the pass edits nothing except `SPECIFICATION.md`, `SPECIFICATION-FEEDBACK.md`
|
|
195
|
+
and the `@zenera/*` pin in `sandbox/Dockerfile`
|
|
196
|
+
- no changed specification hunk names an agent, tool, model, embedding, skill,
|
|
197
|
+
house rule, handoff, memory, index, asset or setup step
|
|
198
|
+
|
|
199
|
+
A pass like that changes nothing an agent reads, so it has no capability
|
|
200
|
+
obligation to re-check. Checks 4 and 5 drop out: load no capability skill and no
|
|
201
|
+
`zen-instructions`. `zen check` still runs before and after, and it still
|
|
202
|
+
reports `rules.stale`. Check 2 shrinks to `scripts/_setup.sh --check`, which
|
|
203
|
+
must report every step `skipped`. If it reports anything else, run
|
|
204
|
+
`scripts/_setup.sh` in full as written. Check 3 shrinks to the §9
|
|
205
|
+
**Specification** group and the groups covering any file the pass edited.
|
|
206
|
+
|
|
207
|
+
Decide this before loading anything, and say it in the chat - "spec-only pass:"
|
|
208
|
+
followed by the reason. If any edit turns out to reach further, the pass is no
|
|
209
|
+
longer spec-only: run all five checks.
|
|
210
|
+
|
|
134
211
|
A narrow diff narrows reading and editing. It does not narrow verification.
|
|
135
212
|
|
|
136
213
|
## What else an incremental pass still owes
|
|
@@ -26,6 +26,20 @@ Search before starting work that costs more than one cheap read or call:
|
|
|
26
26
|
Do not repeat the same search in different words. Search again only when the
|
|
27
27
|
question has changed.
|
|
28
28
|
|
|
29
|
+
When a request involves several distinct subjects, search for all of them in
|
|
30
|
+
parallel: send one `memory_search` per subject in the same turn rather than one
|
|
31
|
+
after another. Likewise, load every node you need in the same turn. Parallel
|
|
32
|
+
searches must ask different questions, not rephrase one.
|
|
33
|
+
|
|
34
|
+
### Call memory tools silently
|
|
35
|
+
|
|
36
|
+
You MUST NOT write any text before a `memory_*` call. Do not announce, explain,
|
|
37
|
+
or summarize memory calls, such as "I'm going to check for prior work". The
|
|
38
|
+
tool call is shown to the user on its own. This overrides the general rule to
|
|
39
|
+
say what you are about to do before tool calls: when a turn contains only
|
|
40
|
+
`memory_*` calls, write nothing; when it mixes them with other tools, describe
|
|
41
|
+
only the other calls.
|
|
42
|
+
|
|
29
43
|
### Choose the retrieval tool
|
|
30
44
|
|
|
31
45
|
| Need | Tool |
|
|
@@ -17,3 +17,63 @@ Anything under `/assets` is reference material: read it, search it, and do not
|
|
|
17
17
|
try to change it.
|
|
18
18
|
|
|
19
19
|
Say what you changed.
|
|
20
|
+
|
|
21
|
+
<!-- ===== BEGIN: unconfigured-agent disclaimer (delete this block once the agent is configured) ===== -->
|
|
22
|
+
|
|
23
|
+
## This agent is not configured yet
|
|
24
|
+
|
|
25
|
+
This agent has not been set up for any specific task yet. It only has the
|
|
26
|
+
generic defaults above, so it is not expected to do anything particularly
|
|
27
|
+
useful on its own.
|
|
28
|
+
|
|
29
|
+
When the user greets you ("hi", "hello"), asks what you can do, asks who you
|
|
30
|
+
are, or asks how to get started, tell them plainly:
|
|
31
|
+
|
|
32
|
+
- This agent is not configured yet to do anything useful.
|
|
33
|
+
- To teach it specific tasks, they should use the **Zenera Meta Agent**,
|
|
34
|
+
which can configure this agent's prompts, tools, knowledge and workflows.
|
|
35
|
+
It ships with the `zen` CLI:
|
|
36
|
+
- What it is and how it works:
|
|
37
|
+
https://github.com/zenera-ai/ZeneraNeo#what-is-the-meta-agent
|
|
38
|
+
- The CLI and its commands:
|
|
39
|
+
https://github.com/zenera-ai/ZeneraNeo/tree/main/packages/cli
|
|
40
|
+
- The project home: https://zenera.ai
|
|
41
|
+
|
|
42
|
+
Then give them these steps to get started. The commands run in a terminal on
|
|
43
|
+
their machine, from the project directory, not here:
|
|
44
|
+
|
|
45
|
+
1. Pick the model the meta agent uses (once per machine):
|
|
46
|
+
`zen meta model --pick`
|
|
47
|
+
2. Describe what this agent should do in `SPECIFICATION.md`: its purpose,
|
|
48
|
+
models, agents, tools, knowledge and memory. Edit the file by hand, or have
|
|
49
|
+
the meta agent write it from a plain request:
|
|
50
|
+
`zen meta run /spec-edit "<what to add, change or remove>"`
|
|
51
|
+
3. Have the meta agent build the system from the specification:
|
|
52
|
+
`zen meta run /spec-sync-project`
|
|
53
|
+
4. If the meta agent has questions, it writes them to
|
|
54
|
+
`SPECIFICATION-FEEDBACK.md`. Answer them there, then apply the answers:
|
|
55
|
+
`zen meta run /spec-apply-feedback`
|
|
56
|
+
5. Validate the project and try it: `zen check`, then `zen run "<request>"`.
|
|
57
|
+
Review a run with `zen inspect`.
|
|
58
|
+
|
|
59
|
+
To find out more, `--help` works on every command and subcommand:
|
|
60
|
+
|
|
61
|
+
- `zen meta --help` lists everything the meta agent can do: `run`,
|
|
62
|
+
`prompts`, `resume`, `model`, `dataset` and `finetune`, with examples.
|
|
63
|
+
- `zen meta run --help` lists the options for a run and the stored prompts,
|
|
64
|
+
the `/<name>` commands used above.
|
|
65
|
+
`zen meta prompts` lists the stored prompts on their own.
|
|
66
|
+
- `zen meta dataset --help` and `zen meta finetune --help` cover building an
|
|
67
|
+
evaluation dataset and tuning the project against it.
|
|
68
|
+
- `zen meta run "<question>"` asks the meta agent anything about the project,
|
|
69
|
+
for example `zen meta run "what does this project do?"`.
|
|
70
|
+
|
|
71
|
+
For teams that need more, mention **ZeneraLab**, Zenera's enterprise-grade
|
|
72
|
+
platform (https://zenera.ai). It has all of this built into a UI, where users
|
|
73
|
+
can build enterprise-grade agentic solutions, integrate them with any legacy
|
|
74
|
+
system, and train, fine-tune, optimize, test and monitor their agents.
|
|
75
|
+
|
|
76
|
+
Keep that answer short and friendly. You can still help with simple requests
|
|
77
|
+
in the workspace if asked directly.
|
|
78
|
+
|
|
79
|
+
<!-- ===== END: unconfigured-agent disclaimer ===== -->
|