@0xgeorgemathew/zcode 0.3.2 → 0.4.0

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 CHANGED
@@ -1,225 +1,273 @@
1
- # zcode
1
+ <div align="center">
2
2
 
3
- Headless GLM-5.3 / GLM-5.3-Flash turns for agents, with the full agentic runtime underneath: subagents that do real work, MCP servers, skills, session memory. Zero dependencies. One binary, NDJSON out.
3
+ # ⚡ zcode
4
4
 
5
- Built for agent callers, not humans: stable machine contract on stdout, everything a watcher needs on stderr, explicit exit codes, no surprises.
5
+ **The execution engine for orchestrator agents.**
6
6
 
7
- ```text
8
- $ zcode "What is 17*23? Reason carefully, then reply with only the number."
9
- │ The user wants 17 * 23. Simple math. 17*23 = 17*20 + 17*3 = 340 + 51 = 391. Reply with only the number.
10
- 391
11
- GLM-5.3-Flash · effort max · 0 tool calls · 9.1s · sess_03bf3114-44
12
- ```
13
-
14
- That is a real transcript. Thinking streams as a dimmed quote block on stderr, the answer lands alone on stdout, and a status line closes the turn. What you pipe is exactly what the model answered.
7
+ Hand it a scoped task. It implements, verifies with *your* checks, and hands back proof.
15
8
 
16
- [![npm](https://img.shields.io/npm/v/@0xgeorgemathew/zcode.svg)](https://www.npmjs.com/package/@0xgeorgemathew/zcode)
9
+ [![npm](https://img.shields.io/npm/v/@0xgeorgemathew/zcode.svg?color=cb3837&logo=npm)](https://www.npmjs.com/package/@0xgeorgemathew/zcode)
10
+ [![node](https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white)](package.json)
11
+ [![deps](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
17
12
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
18
13
 
19
- &nbsp;
14
+ `GLM-5.3-Flash` · effort `max` by default · `GLM-5.3` on demand
20
15
 
21
- ## Install
16
+ </div>
22
17
 
23
- ```sh
24
- npm install -g @0xgeorgemathew/zcode
25
- ```
18
+ &nbsp;
26
19
 
27
- Needs node 20 or newer for the CLI, 26 recommended for the engine child, plus the ZCode engine on the machine (today: the copy bundled with the ZCode desktop app, resolved automatically).
20
+ Opus, Sonnet, GPT, or any agent with a shell delegates implementation to zcode. The orchestrator keeps product decisions, design direction, and coordination. zcode owns execution inside the scope it is given, and reports back what changed, what was checked, what failed, and what is still unverified.
21
+
22
+ | | |
23
+ |---|---|
24
+ | 🧾 **Contracts, not prompts** | objective, scope, constraints, binding design decisions, acceptance criteria, checks |
25
+ | ✅ **Evidence over claims** | zcode runs your checks itself and repairs failures in bounded turns; the model's report is only a claim |
26
+ | 🧵 **Real sessions** | stable ids, detach, `status`, `wait`, `resume`, `cancel`; interrupted sessions are resumable |
27
+ | 🧱 **Safe parallelism** | `--owns` leases per workspace; overlapping scopes are refused, out-of-scope edits are flagged |
28
+ | 🤖 **Machine-first output** | one stable `zcode.result/1` JSON line; clean, colour-free text when not on a TTY |
28
29
 
29
30
  &nbsp;
30
31
 
31
- ## Start in three commands
32
+ ## Quick start
32
33
 
33
34
  ```sh
35
+ npm install -g @0xgeorgemathew/zcode
34
36
  echo "$ZAI_API_KEY" | zcode login # store your Z.AI key (mode 600, never printed)
35
37
  zcode doctor # check engine, isolation, key
36
- zcode "Explain this repo in two sentences"
37
- ```
38
-
39
- &nbsp;
40
-
41
- ## Ask
42
38
 
43
- ```sh
44
- zcode "Reply with exactly: OK" # GLM-5.3-Flash, effort max
45
- zcode --model glm-5.3 "Reply with exactly: OK" # GLM-5.3
46
- zcode --effort high "Draft a commit message" # low | medium | high | xhigh | max
47
- echo "$PROMPT" | zcode # prompt via stdin
39
+ zcode task --check "npm test" "Fix the off-by-one in src/paginate.ts"
48
40
  ```
49
41
 
50
- Only `glm-5.3` and `glm-5.3-flash` (plus short aliases) are accepted. Anything else exits 2 before any engine spawn or API call. Default effort is max.
42
+ Needs node 20+ for the CLI (26 recommended for the engine child) and the ZCode engine on the machine. Today that is the copy bundled with the ZCode desktop app, which is resolved automatically.
43
+
44
+ Orchestrator skills for Claude Code and Codex ship in [`skills/`](skills/README.md).
51
45
 
52
46
  &nbsp;
53
47
 
54
- ## Watch it work
48
+ ## Models and effort
55
49
 
56
- Plain mode keeps two streams apart on purpose:
50
+ Every command runs on **GLM-5.3-Flash at effort `max`** unless you say otherwise.
57
51
 
58
- - **stdout** carries the answer, nothing else. Pipe it, capture it, trust it.
59
- - **stderr** is the live feed, all dimmed so it visually recedes: thinking as `│ ` quote lines, tool calls as `→ started` / `✓ ok` / `✗ error`, then one status line with model, effort, tool count, duration, and the session id.
52
+ | | flag | values | default |
53
+ |---|---|---|---|
54
+ | 🧠 model | `--model` | `glm-5.3-flash` (aliases `flash`, `5.3-flash`, `53f`) · `glm-5.3` (aliases `5.3`, `53`) | `glm-5.3-flash` |
55
+ | 🎚️ effort | `--effort` | `low` · `medium` · `high` · `xhigh` · `max` | `max` |
60
56
 
61
- ```text
62
- $ zcode "Create a file named note.txt containing exactly the word ready, then reply with just: DONE"
63
- │ This is a trivial task: create note.txt containing exactly the word "ready", then reply with just "DONE".
64
- │ …
65
- → Write started
66
- DONE
67
- GLM-5.3-Flash · effort max · 1 tool call · 11.1s · sess_e9fc3633-3b
57
+ ```sh
58
+ zcode task "…" # GLM-5.3-Flash, effort max
59
+ zcode task --effort high "…" # Flash, lighter reasoning
60
+ zcode task --model glm-5.3 "…" # full GLM-5.3, effort max
61
+ zcode task --model glm-5.3 --effort medium "…" # full GLM-5.3, medium effort
68
62
  ```
69
63
 
70
- `--quiet` silences the stderr feed. `--include-reasoning` puts the full thinking text into the JSON result line.
64
+ A task contract can also set `"model"` and `"effort"`; flags win over the contract. A resumed session keeps the model and effort it started with unless you pass the flags again.
71
65
 
72
66
  &nbsp;
73
67
 
74
- ## Continue a conversation
75
-
76
- Every result carries a `sessionId`. Sessions persist on disk, so a later invocation, in a new process, picks up the same thread:
68
+ ## Hand off a task
77
69
 
78
70
  ```sh
79
- SID=$(zcode --json "Remember the secret word BANANA. Reply with exactly: OK" | tail -1 | jq -r .sessionId)
80
-
81
- zcode --session "$SID" "Reply with only the secret word I told you." # -> BANANA
71
+ zcode task --cwd ./site --id page-1 \
72
+ --owns src/pages/pricing/ \
73
+ --check "npm run build" --check "npm test -- pricing" \
74
+ --accept "Three plan cards, the middle one highlighted" \
75
+ --context @design/pricing.md \
76
+ "Implement the pricing page from the supplied design notes"
82
77
  ```
83
78
 
84
- The continued result line reports `"resumed": true` with the same `sessionId`. The status footer shows the short id so plain-mode callers can continue without a JSON round-trip.
79
+ zcode turns this into an execution contract and runs a fresh engine session in yolo mode, with `--cwd` as the blast radius. The model may split the work across subagents. When it finishes, **zcode runs the checks itself**. If a check fails, zcode feeds the failure output back for a bounded number of repair turns (`--max-repairs`, default 2), then reports:
85
80
 
86
- &nbsp;
81
+ ```text
82
+ ✓ completed page-1 142.3s
83
+ Pricing page with three plan cards; middle card highlighted per design notes.
84
+ changed 3 files
85
+ A src/pages/pricing/index.tsx
86
+ A src/pages/pricing/PlanCard.tsx
87
+ M src/pages/pricing/styles.css
88
+ checks 2/2 passed · 1 repair turn
89
+ ✓ npm run build
90
+ ✓ npm test -- pricing
91
+ not verified
92
+ - prose acceptance criteria are confirmed only by the executor's own report
93
+ ```
87
94
 
88
- ## Dispatch a subagent
95
+ That block is the format of the summary printed to stdout. The live feed (session header, phases, tools, subagents, check results) goes to stderr. With `--json` you get the same data as one machine-readable result line.
89
96
 
90
- `zcode agent` forces a dispatch through the Agent tool and verifies the outcome programmatically. It never trusts the model's prose: exit 0 only if the parent turn succeeded and at least one subagent ended with status `success`.
97
+ For small work, the objective alone is enough: `zcode task "Fix the typo in the README heading"`.
91
98
 
92
- ```sh
93
- WORK=$(mktemp -d)
94
- zcode agent --cwd "$WORK" \
95
- "Use the Agent tool to create hello.txt containing exactly HELLO-SUBAGENT, then report the agent id."
96
- cat "$WORK/hello.txt" # HELLO-SUBAGENT
97
- ```
99
+ ### The contract
98
100
 
99
- Use `--cwd` to declare where the subagent may work. `--profile` picks the subagent profile (default `general-purpose`; your own `~/.zcode/agents/*.md` profiles resolve too). With `--json`, the final line carries the engine-reported evidence:
101
+ Flags cover the common fields. For richer handoffs, pass `--task contract.json`, `--task brief.md`, or `--task -` to read stdin. Only `objective` is required; unknown keys are rejected so a typo never silently drops a requirement.
100
102
 
101
103
  ```json
102
- "subagents": [{
103
- "agentId": "agent_8f5144b0-…",
104
- "childSessionId": "sess_subagent_agent_8f5144b0-…",
105
- "profileId": "general-purpose",
106
- "status": "success",
107
- "summary": "…the subagent's own report…",
108
- "durationMs": 59765
109
- }]
104
+ {
105
+ "objective": "Implement page 2 (Team) per the design spec",
106
+ "scope": ["Team page route and its components"],
107
+ "constraints": ["No new dependencies", "Keep existing routing untouched"],
108
+ "design": ["Grid of 3 columns ≥1024px, 1 column below", "Use tokens from src/theme.ts only"],
109
+ "acceptance": ["Every member card shows name, role, avatar"],
110
+ "checks": ["npm run build", "npm test -- team"],
111
+ "files": ["src/theme.ts", "src/pages/home/index.tsx"],
112
+ "references": ["design/team.png described in design/team.md"],
113
+ "context": ["Copy lives in content/team.json"],
114
+ "owns": ["src/pages/team/**", "content/team.json"],
115
+ "limits": { "timeoutMs": 1800000, "maxRepairs": 2, "checkTimeoutMs": 300000 },
116
+ "model": "glm-5.3",
117
+ "effort": "high"
118
+ }
110
119
  ```
111
120
 
112
- &nbsp;
121
+ `design` entries are presented to the model as binding decisions it must implement, not revisit. If the model hits a consequential product or design question the contract does not answer, it reports `blocked` with its questions instead of guessing.
113
122
 
114
- ## MCP servers
123
+ ### Status: what a result means
115
124
 
116
- Hermetic by default: a run registers only the servers you pass. `--mcp` takes inline JSON or `@file`, repeatable; stdio, http, and sse are supported.
125
+ | status | meaning | exit |
126
+ |---|---|---|
127
+ | `completed` | zcode's own checks ran and passed, the executor reports done, no unmet criteria, nothing changed outside `--owns` | 0 |
128
+ | `unverified` | executor reports done, but no checks were declared (or no usable handback), so completion is self-reported | 9 |
129
+ | `partial` | work remains: executor says partial, a criterion is unmet, or files changed outside the owned scope | 8 |
130
+ | `blocked` | needs the orchestrator: `questions` lists what is missing | 10 |
131
+ | `failed` | checks still failing after the repair budget (`error.kind: checks_failed`, exit 8), engine/turn error (5), or timeout (124) | 5 / 8 / 124 |
132
+ | `cancelled` | stopped by `zcode cancel`, Ctrl-C, or SIGTERM | 130 |
117
133
 
118
- ```sh
119
- zcode --mcp '{"word-server":{"type":"stdio","command":"node","args":["srv.js"],"enabled":true}}' \
120
- "Call get_magic_word and reply with exactly what it returns"
134
+ A model claiming success is never enough on its own. A subagent ending `success` only means it returned. The handback block the model writes is reported as `handback` (a claim), and the status is derived from what zcode observed. `notVerified` lists anything zcode could not check itself.
121
135
 
122
- zcode --mcp '{"docs":{"type":"http","url":"https://mcp.example.com/mcp","headers":{"Authorization":"Bearer …"}}}' \
123
- "Look up the MCP spec section on tools"
136
+ &nbsp;
124
137
 
125
- zcode --mcp @servers.json --mcp-list # print the effective set as JSON
126
- zcode --mcp-inherit … # also load the desktop's enabled servers
138
+ ## Sessions
139
+
140
+ Every invocation gets a session: `--id page-1`, or a generated `zc-xxxxxxxx`. Each session has its own engine HOME (`~/.zcode-cli/sessions/<id>/home`), so conversation state, engine database, MCP servers, and skills never leak between sessions. An existing id is never re-run, so retrying a start you lost track of fails fast instead of doing the work twice. Use `status` to check on it, or `resume` to continue it.
141
+
142
+ ```sh
143
+ zcode task --detach --id page-1 --owns src/pages/home/ --check "npm run build" "Build page 1"
144
+ zcode task --detach --id page-2 --owns src/pages/team/ --check "npm run build" "Build page 2"
145
+ zcode status # all sessions: id, state/status, kind, updated, label
146
+ zcode status page-1 --json # one session: state, phase, activity, recent events, result
147
+ zcode wait page-1 page-2 # block until both finish; print each handback
148
+ zcode resume page-2 "Use the 2-column variant below 1024px." # answer / follow up / retry
149
+ zcode cancel page-1 # stop the turn and any running check; recorded as cancelled
127
150
  ```
128
151
 
129
- The CLI forces the connection and waits until your servers have settled before sending the prompt, so the first turn never misses a slow stdio spawn. Connection state streams to stderr (`◇ mcp word-server connected`) and into the JSON events. Inheritance is opt-in because desktop sets often include browser-OAuth servers that can never finish headless, and they stall the engine's connect pool.
152
+ - **Lifecycle.** A session is `queued`, then `running`, then `finished` with a status. If the process that owned a running session dies, the session shows as `interrupted`, which can be resumed.
153
+ - **Resume** sends your message into the same engine conversation. For task sessions, the model is told that earlier work is already applied to the workspace, and zcode re-runs the checks and repairs and produces a fresh handback. A resume with no message (task sessions) means "continue from where you stopped".
154
+ - **Never automatically re-sent.** zcode retries only engine start-up, which happens before anything reaches the model. A prompt that was delivered is never re-sent automatically. Continuing is always an explicit `resume`.
155
+ - **Detached sessions** write their NDJSON to `~/.zcode-cli/sessions/<id>/stdout.ndjson` and diagnostics to `stderr.log`. The handback lands in `result.json`. You never need to read these files: `status` and `wait` print everything.
130
156
 
131
- &nbsp;
157
+ ### Concurrency and ownership
132
158
 
133
- ## Skills
159
+ `--owns` declares what a session may change: a path owns its subtree, and globs are supported. Patterns are relative to `--cwd`. A session without `--owns` owns the whole workspace, which is right for a one-off task in its own checkout.
134
160
 
135
- Two ways, both real: the model loads skills through its Skill tool.
161
+ - Starting a session whose ownership **overlaps a running session in the same workspace** is refused with exit 7, unless `--allow-overlap` is passed. The workspace is the git toplevel, so sibling directories of one repo count as one workspace. Disjoint owners (`src/pages/home/` vs `src/pages/team/`) run side by side.
162
+ - After the work, zcode diffs the workspace and reports each changed file. Changes outside the session's ownership are listed in `scopeViolations`, and for task sessions they make the result `partial`. Files that fall in a concurrent session's ownership are attributed to that session, not counted against this one.
163
+ - For overlapping or risky parallel work, give each session its own git worktree via `--cwd`.
164
+ - `ZAI_CODE_MAX_ACTIVE` (default 8) is a soft machine-wide cap. Sessions beyond it wait in `queued`.
136
165
 
137
- - **Project skills, zero flags.** Put `<name>/SKILL.md` under `<workspace>/.zcode/skills/`. The engine walks your workspace; no copy, no flag.
166
+ &nbsp;
138
167
 
139
- - **Injected per run.** `--skills <dir>` where dir is one skill (has `SKILL.md`) or a root of `<name>/SKILL.md` dirs. Copied into the isolated scratch for this run, removed after.
168
+ ## One-shot turns and subagent dispatch
169
+
170
+ `zcode` with a prompt (or `zcode run`) is a plain turn. The answer streams to stdout, and thinking is mirrored to stderr as `│ ` lines.
140
171
 
141
172
  ```sh
142
- zcode --skills ./my-skills "Load the word-vault skill, then reply with the magic word it defines."
173
+ zcode "What is 17*23? Reply with only the number."
174
+ zcode --model glm-5.3 --effort high "Draft a commit message for the staged diff"
175
+ echo "$PROMPT" | zcode
143
176
  ```
144
177
 
145
- `--no-skills` runs with an empty skill set. Your desktop user skills (`~/.zcode/skills`) are inherited into the isolation automatically.
178
+ `zcode agent "<instructions>"` forces a dispatch through the engine's Agent tool. It is verified from `session/subagents`: exit 6 if no subagent ran or any ended in a status other than `success`. `--profile` picks the subagent profile (default `general-purpose`).
179
+
180
+ Inside `zcode task`, subagents are optional. The executor decides when to use them, gives each a bounded scope and file ownership, and must verify the integrated result.
146
181
 
147
182
  &nbsp;
148
183
 
149
184
  ## Machine interface
150
185
 
151
- `--json` emits NDJSON events on stdout with a stable final line:
186
+ `--json`: stdout carries NDJSON events, then exactly one final `{"type":"result",…}` line. With `--quiet`, the result line is the only output. For task sessions, text/reasoning deltas are left out of the events unless you pass `--stream`, so an orchestrator reading the output pays only for lifecycle events.
152
187
 
153
188
  ```json
154
- {"type":"result","ok":true,"response":"…","model":"GLM-5.3-Flash","mode":"yolo",
155
- "effort":"max","sessionId":"sess_…","resumed":false,
156
- "usage":{"inputTokens":19608,"outputTokens":3},
157
- "reasoningChars":2531,"toolCalls":2,"toolEvents":9,
158
- "mcpServers":["word-server"],"skillsInjected":["word-vault"],
159
- "durationMs":21972}
189
+ {"type":"result","schema":"zcode.result/1","ok":true,"status":"completed","id":"page-1","kind":"task",
190
+ "summary":"…","response":"…executor report…","issues":[],"questions":[],"unresolved":[],"notVerified":[],
191
+ "changes":{"tracking":"git","files":[{"path":"src/pages/home/index.tsx","change":"added","owned":true}]},
192
+ "scopeViolations":[],"checks":[{"command":"npm run build","ok":true,"exitCode":0,"timedOut":false,"durationMs":8123,"output":"…tail…"}],
193
+ "checksPassed":true,"repairs":0,"handback":{"status":"done","summary":"…","changes":[],"checks":[],"criteria":[],"unresolved":[],"questions":[],"notes":""},
194
+ "subagents":[{"agentId":"agent_…","profileId":"general-purpose","status":"success","summary":"…","durationMs":59765}],
195
+ "error":null,"cwd":"/abs/site","workspaceRoot":"/abs/site","owns":["src/pages/home/"],"sessionId":"sess_…","resumed":false,"turns":1,
196
+ "model":"GLM-5.3-Flash","effort":"max","mode":"yolo","usage":{"inputTokens":19608,"outputTokens":3120},
197
+ "toolCalls":14,"durationMs":142301,"files":{"session":"…/sessions/page-1","result":"…/result.json","events":"…/events.ndjson"}}
160
198
  ```
161
199
 
162
- `model` and `effort` are engine-reported values cross-checked from the engine's own `model_request_started` event, not the wrapper's echo. A failed run still prints a final line with `"ok":false` plus the error kind, so a caller always gets one parseable frame.
163
-
164
- Event types on the way in:
200
+ `model` and `effort` are engine-reported (from the engine's `model_request_started` event), not echoes. `sessionId` is the engine's session id; `id` is the zcode session id you pass to `status`, `wait`, `resume`, and `cancel`. A run that fails still ends with a parseable result line.
165
201
 
166
202
  | event | meaning |
167
203
  |---|---|
168
- | `{"type":"stream","kind":"text_delta","delta":"…"}` | answer text, live |
169
- | `{"type":"stream","kind":"reasoning_delta","delta":"…"}` | thinking, live |
170
- | `{"type":"tool","name":"Bash","phase":"started"}` | tool lifecycle: scheduled, started, result, error |
171
- | `{"type":"mcp","name":"word-server","status":"connected","toolCount":1}` | your MCP servers settling |
172
- | `{"type":"model_request_started",…}` | engine-reported model and reasoning level |
173
- | `{"type":"state","mode":"yolo"}` | session mode |
204
+ | `{"type":"session","id","kind","cwd","owns","checks","model","effort","resumed"}` | which session is active, and its scope |
205
+ | `{"type":"phase","phase":"starting\|executing\|reporting\|checking\|repairing\|queued"}` | what is running |
206
+ | `{"type":"tool","name":"Edit","phase":"started"}` | tool lifecycle (`scheduled`, `started`, `result`, `error`) |
207
+ | `{"type":"subagent","phase":"started\|returned\|error"}` | subagent activity |
208
+ | `{"type":"check","phase":"finished","command","ok","exitCode","durationMs"}` | zcode-run verification |
209
+ | `{"type":"mcp","name","status"}` · `{"type":"retry",…}` · `{"type":"model_request_started",…}` | MCP settling, start-up retry, engine model evidence |
210
+ | `{"type":"stream","kind":"text_delta\|reasoning_delta","delta"}` | live text (run/agent; task with `--stream`) |
211
+
212
+ `zcode status <id> --json` returns `{"type":"status", state, status, phase, pid, live, activity, recent:[…], result?}`. `zcode wait --json` prints one result line per session as each finishes.
174
213
 
175
214
  &nbsp;
176
215
 
177
216
  ## Options
178
217
 
179
- | option | takes | default | notes |
218
+ | option | applies to | default | notes |
180
219
  |---|---|---|---|
181
- | `--model` | id | `glm-5.3-flash` | `glm-5.3` plus aliases: `5.3`, `53`, `flash`, `5.3-flash`, `53f` |
182
- | `--effort` | level | `max` | `low`, `medium`, `high`, `xhigh`, `max` |
183
- | `--cwd` | dir | current dir | the session workspace, and the blast radius |
184
- | `--session` | id | new session | continue a previous conversation |
185
- | `--mcp` | JSON or `@file` | none | repeatable; stdio, http, sse |
186
- | `--mcp-inherit` | flag | off | also load desktop-enabled MCP servers |
187
- | `--mcp-list` | flag | | print effective servers as JSON, exit |
188
- | `--skills` | dir | none | repeatable; one skill or a root of skills |
189
- | `--no-skills` | flag | off | empty skill set for this run |
190
- | `--profile` | name | `general-purpose` | subagent profile for `agent` |
191
- | `--include-reasoning` | flag | off | full thinking text in the result line |
192
- | `--quiet` | flag | off | silence the stderr live feed |
193
- | `--json` | flag | off | NDJSON events plus result line on stdout |
194
- | `--timeout-ms` | number | `600000` | |
195
- | `--fresh` | flag | off | rebuild the isolated scratch HOME first |
196
- | `-p`, `--prompt` | text | | alias for the positional prompt |
220
+ | `--id`, `--label` | run, task, agent | `zc-<random>` | session identity |
221
+ | `--detach` | run, task, agent, resume | off | background worker; prints the id |
222
+ | `--cwd` | run, task, agent | current dir | the workspace and blast radius |
223
+ | `--owns` | run, task, agent | whole workspace | repeatable path or glob |
224
+ | `--allow-overlap` | run, task, agent | off | start despite overlapping ownership |
225
+ | `--task` | task | | JSON contract, markdown brief, or `-` |
226
+ | `--accept`, `--check`, `--file`, `--context` | task | | repeatable; `--context @file` reads a file |
227
+ | `--max-repairs` | task, resume | `2` | repair turns after failing checks |
228
+ | `--check-timeout-ms` | task, resume | `300000` | per check; checks get `CI=1` when unset |
229
+ | `--model` | all turns | `glm-5.3-flash` | `glm-5.3` plus aliases `5.3`, `53`, `flash`, `5.3-flash`, `53f` |
230
+ | `--effort` | all turns | `max` | `low`, `medium`, `high`, `xhigh`, `max` |
231
+ | `--timeout-ms` | all turns; wait | run 600000, task 1800000 | the whole invocation incl. checks and repairs |
232
+ | `--session` | run | | 0.3 compat: continue by zcode id or engine `sess_` id |
233
+ | `--profile` | agent | `general-purpose` | subagent profile |
234
+ | `--mcp` / `--mcp-inherit` / `--mcp-list` | run, task, agent | hermetic | MCP servers (inline JSON or `@file`) |
235
+ | `--skills` / `--no-skills` | run, task, agent | desktop skills | add skills; or run with none |
236
+ | `--json`, `--quiet` | all | | see Machine interface |
237
+ | `--thinking` / `--no-thinking` | turns | run on, task off | mirror reasoning to stderr |
238
+ | `--stream`, `--include-reasoning` | turns | off | deltas in task events; full reasoning in the result |
239
+
240
+ Only `glm-5.3` and `glm-5.3-flash` are accepted; anything else exits 2 before any spawn. Effort levels `low`, `high`, and `max` have been observed live on Flash. `medium` and `xhigh` may be rejected server-side.
197
241
 
198
242
  ## Exit codes
199
243
 
200
244
  | code | meaning |
201
245
  |---|---|
202
- | 0 | success |
246
+ | 0 | completed |
203
247
  | 1 | unexpected internal error |
204
- | 2 | usage error, unknown model, unknown effort, bad `--mcp` JSON |
248
+ | 2 | usage error (bad flag, model, effort, contract, unknown or duplicate session id) |
205
249
  | 3 | no API key (`zcode login` or `ZAI_API_KEY`) |
206
250
  | 4 | engine or builtin provider config not found |
207
- | 5 | turn failed (engine error, failed terminal, user-input request) |
251
+ | 5 | failed: engine or turn error |
208
252
  | 6 | `agent`: no subagent dispatched or none ended `success` |
253
+ | 7 | workspace conflict, or the session is already running |
254
+ | 8 | partial, or checks still failing after repairs |
255
+ | 9 | unverified |
256
+ | 10 | blocked |
209
257
  | 124 | `--timeout-ms` exceeded |
210
- | 130 | interrupted (SIGINT) |
258
+ | 130 | cancelled |
211
259
 
212
260
  &nbsp;
213
261
 
214
262
  ## Under the hood
215
263
 
216
- **Key handling.** `ZAI_API_KEY` env var, or `zcode login` which stores it at `~/.zcode-cli/credentials.json` (mode 600). The key is never printed or logged. The only file it ever lands in is the scratch provider config, which the engine requires to register the provider.
264
+ **Key handling.** The key comes from the `ZAI_API_KEY` env var, or from `zcode login`, which stores it at `~/.zcode-cli/credentials.json` (mode 600). It is never printed or logged. Engine messages and check output are scrubbed before they are stored or shown. The only files the key ever lands in are each session's scratch provider config, which the engine requires to register the provider.
217
265
 
218
- **Isolation.** This package's own state lives under `~/.zcode-cli` (never under `~/.zcode`, which belongs to the desktop app; if an older 0.3.x left files there, they are migrated out automatically on first run). The engine runs with `HOME` pointed at a scratch copy we own (`~/.zcode-cli/home`, mode 700, mtime-cached). Your live `~/.zcode`, which the desktop app is using right now, is never written to. Your OAuth `credentials.json` is deliberately never copied. The scratch keeps exactly the `zai-api` provider rule with your key, your agents, and your skills.
266
+ **Isolation.** This package's state lives under `~/.zcode-cli`, never under `~/.zcode`, which belongs to the desktop app. Each session's engine runs with `HOME` pointed at its own scratch directory. On every invocation zcode rewrites the scratch config from your live desktop config: provider rule with your key, agent profiles, skills, MCP servers. It never touches the engine's database there, so sessions stay resumable. The desktop's OAuth `credentials.json` is never copied, and the live `~/.zcode` is never written. The engine's ~30 MB plugin cache is shared between sessions through a symlink.
219
267
 
220
- **Yolo mode.** Sessions run with auto-approved tool side effects, matching the engine's own headless default; `--cwd` is the declared blast radius. A headless run that requests interactive user input fails fast with exit 5 rather than hanging.
268
+ **Yolo mode.** Tool side effects are auto-approved, matching the engine's own headless default; `--cwd` is the blast radius. If a headless turn requests interactive input, the result is reported as `blocked` instead of hanging. The executor contract forbids commits, pushes, deploys, and other external side effects unless the task explicitly asks for them.
221
269
 
222
- **Engine.** Resolved from the ZCode desktop app bundle (`zcode doctor` prints path, version, sha256). `ZAI_CODE_ENGINE` points elsewhere if you need to pin one. macOS today; nothing else in the wrapper is macOS-specific.
270
+ **Environment.** `ZAI_CODE_HOME` (state root), `ZAI_CODE_ENGINE` (engine bundle), `ZAI_CODE_NODE` (engine interpreter), `ZAI_CODE_BUILTIN_PROVIDER_CONFIG`, `ZAI_CODE_LIVE_DIR` (desktop config dir, default `~/.zcode`), `ZAI_CODE_MAX_ACTIVE`, `NO_COLOR`.
223
271
 
224
272
  &nbsp;
225
273
 
@@ -228,12 +276,13 @@ Event types on the way in:
228
276
  Zero dependencies, plain CommonJS, no build step.
229
277
 
230
278
  ```sh
231
- npm test # functional only, no mocks; live tests hit the real GLM API
232
- npm run accept:flash # one-shot through GLM-5.3-Flash
279
+ npm test # unit suite: in-process, scripted fake engine, no CLI spawn, no API calls
280
+ npm run test:live # functional suite: spawns the CLI against the real engine and GLM API
281
+ npm run accept:flash # black-box acceptance runs (real API)
233
282
  npm run accept:subagent
234
283
  npm run accept:53
235
284
  ```
236
285
 
237
- Tests load the key from the repo `.env` (never committed, never shipped). The design record and pinned wire facts live in `DESIGN.md` and `docs/BUILD-REPORT.md`.
286
+ Live tests load the key from the repo `.env`, which is never committed or shipped. The design record and the wire-protocol facts pinned live are in `DESIGN.md` (§17 covers the 0.4 executor redesign) and `docs/BUILD-REPORT.md`.
238
287
 
239
288
  MIT license.