@0xgeorgemathew/zcode 0.3.2 → 0.4.1

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,268 @@
1
- # zcode
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.
4
-
5
- Built for agent callers, not humans: stable machine contract on stdout, everything a watcher needs on stderr, explicit exit codes, no surprises.
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.
15
-
16
- [![npm](https://img.shields.io/npm/v/@0xgeorgemathew/zcode.svg)](https://www.npmjs.com/package/@0xgeorgemathew/zcode)
17
- [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/0xgeorgemathew/zcode/main/docs/assets/hero.svg" alt="A zcode work order: the contract on the left, the handback with changed files, a passing check and a COMPLETED stamp on the right" width="960">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@0xgeorgemathew/zcode"><img src="https://img.shields.io/npm/v/@0xgeorgemathew/zcode.svg?color=1C3F95&label=npm" alt="npm version"></a>
7
+ <img src="https://img.shields.io/badge/node-%E2%89%A520-1C3F95" alt="node 20 or later">
8
+ <img src="https://img.shields.io/badge/dependencies-0-1C3F95" alt="zero dependencies">
9
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-1C3F95" alt="MIT license"></a>
10
+ </p>
11
+
12
+ zcode is a command-line executor for orchestrator agents. Claude Code, Codex, or any agent with a shell hands it a task contract. zcode implements the task with GLM-5.3-Flash, runs your checks itself, and returns a handback that lists the changed files, the check results, and anything it could not verify.
13
+
14
+ The orchestrator keeps product decisions, design direction, and coordination. zcode works only inside the scope it is given.
15
+
16
+ <table>
17
+ <tr><td><b>Contract in</b></td><td>Objective, scope, constraints, binding design decisions, acceptance criteria, owned paths and checks. Only the objective is required.</td></tr>
18
+ <tr><td><b>Checks run by zcode</b></td><td>zcode runs each <code>--check</code> after the work. A failing check sends its output back to the model for up to 2 repair turns.</td></tr>
19
+ <tr><td><b>Status from evidence</b></td><td><code>completed</code> requires passing checks and no edits outside <code>--owns</code>. The model's own report is kept as a claim.</td></tr>
20
+ <tr><td><b>Sessions</b></td><td>Stable ids, <code>--detach</code>, <code>status</code>, <code>wait</code>, <code>resume</code>, <code>cancel</code>. A session whose process died shows as interrupted and can be resumed.</td></tr>
21
+ <tr><td><b>Parallel work</b></td><td>Two sessions with overlapping <code>--owns</code> in one repo are refused with exit 7.</td></tr>
22
+ <tr><td><b>Output</b></td><td>One <code>zcode.result/1</code> JSON line with <code>--json</code>. No colour codes when stdout is not a terminal.</td></tr>
23
+ </table>
18
24
 
19
25
  &nbsp;
20
26
 
21
- ## Install
27
+ ## Quick start
22
28
 
23
29
  ```sh
24
30
  npm install -g @0xgeorgemathew/zcode
31
+ echo "$ZAI_API_KEY" | zcode login # stores your Z.AI key with mode 600; zcode never prints it
32
+ zcode doctor # checks the engine, isolation and key
33
+
34
+ zcode task --check "npm test" "Fix the off-by-one in src/paginate.ts"
25
35
  ```
26
36
 
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).
37
+ The CLI needs node 20 or later. The engine child runs best on node 26. zcode also needs the ZCode engine, which today ships inside the ZCode desktop app; zcode finds it automatically.
38
+
39
+ Installing the package also installs the Claude Code skill to `~/.claude/skills/zcode` and the Codex skill to `${CODEX_HOME:-~/.codex}/skills/zcode`. These skills teach the orchestrator how to delegate to zcode. Set `ZCODE_SKIP_SKILLS=1` to skip this step. Details are in [`skills/`](skills/README.md).
28
40
 
29
41
  &nbsp;
30
42
 
31
- ## Start in three commands
43
+ ## Models and effort
32
44
 
33
- ```sh
34
- echo "$ZAI_API_KEY" | zcode login # store your Z.AI key (mode 600, never printed)
35
- zcode doctor # check engine, isolation, key
36
- zcode "Explain this repo in two sentences"
37
- ```
38
-
39
- &nbsp;
45
+ Every command runs on GLM-5.3-Flash at effort `max` unless you pass a flag.
40
46
 
41
- ## Ask
47
+ | setting | flag | values | default |
48
+ |---|---|---|---|
49
+ | model | `--model` | `glm-5.3-flash` (aliases `flash`, `5.3-flash`, `53f`), `glm-5.3` (aliases `5.3`, `53`) | `glm-5.3-flash` |
50
+ | effort | `--effort` | `low`, `medium`, `high`, `xhigh`, `max` | `max` |
42
51
 
43
52
  ```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
53
+ zcode task "..." # GLM-5.3-Flash, effort max
54
+ zcode task --effort high "..." # Flash with less reasoning
55
+ zcode task --model glm-5.3 "..." # GLM-5.3, effort max
56
+ zcode task --model glm-5.3 --effort medium "..." # GLM-5.3, effort medium
48
57
  ```
49
58
 
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.
59
+ A task contract can set `"model"` and `"effort"`. Flags override the contract. A resumed session keeps its original model and effort unless you pass the flags again.
51
60
 
52
61
  &nbsp;
53
62
 
54
- ## Watch it work
63
+ ## Hand off a task
55
64
 
56
- Plain mode keeps two streams apart on purpose:
65
+ ```sh
66
+ zcode task --cwd ./site --id page-1 \
67
+ --owns src/pages/pricing/ \
68
+ --check "npm run build" --check "npm test -- pricing" \
69
+ --accept "Three plan cards, the middle one highlighted" \
70
+ --context @design/pricing.md \
71
+ "Implement the pricing page from the supplied design notes"
72
+ ```
57
73
 
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.
74
+ 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:
60
75
 
61
76
  ```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
77
+ ✓ completed page-1 142.3s
78
+ Pricing page with three plan cards; middle card highlighted per design notes.
79
+ changed 3 files
80
+ A src/pages/pricing/index.tsx
81
+ A src/pages/pricing/PlanCard.tsx
82
+ M src/pages/pricing/styles.css
83
+ checks 2/2 passed · 1 repair turn
84
+ ✓ npm run build
85
+ ✓ npm test -- pricing
86
+ not verified
87
+ - prose acceptance criteria are confirmed only by the executor's own report
68
88
  ```
69
89
 
70
- `--quiet` silences the stderr feed. `--include-reasoning` puts the full thinking text into the JSON result line.
71
-
72
- &nbsp;
90
+ 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.
73
91
 
74
- ## Continue a conversation
92
+ For small work, the objective alone is enough: `zcode task "Fix the typo in the README heading"`.
75
93
 
76
- Every result carries a `sessionId`. Sessions persist on disk, so a later invocation, in a new process, picks up the same thread:
94
+ ### The contract
77
95
 
78
- ```sh
79
- SID=$(zcode --json "Remember the secret word BANANA. Reply with exactly: OK" | tail -1 | jq -r .sessionId)
96
+ 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.
80
97
 
81
- zcode --session "$SID" "Reply with only the secret word I told you." # -> BANANA
98
+ ```json
99
+ {
100
+ "objective": "Implement page 2 (Team) per the design spec",
101
+ "scope": ["Team page route and its components"],
102
+ "constraints": ["No new dependencies", "Keep existing routing untouched"],
103
+ "design": ["Grid of 3 columns ≥1024px, 1 column below", "Use tokens from src/theme.ts only"],
104
+ "acceptance": ["Every member card shows name, role, avatar"],
105
+ "checks": ["npm run build", "npm test -- team"],
106
+ "files": ["src/theme.ts", "src/pages/home/index.tsx"],
107
+ "references": ["design/team.png described in design/team.md"],
108
+ "context": ["Copy lives in content/team.json"],
109
+ "owns": ["src/pages/team/**", "content/team.json"],
110
+ "limits": { "timeoutMs": 1800000, "maxRepairs": 2, "checkTimeoutMs": 300000 },
111
+ "model": "glm-5.3",
112
+ "effort": "high"
113
+ }
82
114
  ```
83
115
 
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.
85
-
86
- &nbsp;
116
+ `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.
87
117
 
88
- ## Dispatch a subagent
118
+ ### Status: what a result means
89
119
 
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`.
120
+ | status | meaning | exit |
121
+ |---|---|---|
122
+ | `completed` | zcode's own checks ran and passed, the executor reports done, no unmet criteria, nothing changed outside `--owns` | 0 |
123
+ | `unverified` | executor reports done, but no checks were declared (or no usable handback), so completion is self-reported | 9 |
124
+ | `partial` | work remains: executor says partial, a criterion is unmet, or files changed outside the owned scope | 8 |
125
+ | `blocked` | needs the orchestrator: `questions` lists what is missing | 10 |
126
+ | `failed` | checks still failing after the repair budget (`error.kind: checks_failed`, exit 8), engine/turn error (5), or timeout (124) | 5 / 8 / 124 |
127
+ | `cancelled` | stopped by `zcode cancel`, Ctrl-C, or SIGTERM | 130 |
91
128
 
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
- ```
98
-
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:
100
-
101
- ```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
- }]
110
- ```
129
+ 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.
111
130
 
112
131
  &nbsp;
113
132
 
114
- ## MCP servers
133
+ ## Sessions
115
134
 
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.
135
+ 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.
117
136
 
118
137
  ```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"
121
-
122
- zcode --mcp '{"docs":{"type":"http","url":"https://mcp.example.com/mcp","headers":{"Authorization":"Bearer …"}}}' \
123
- "Look up the MCP spec section on tools"
124
-
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
+ zcode task --detach --id page-1 --owns src/pages/home/ --check "npm run build" "Build page 1"
139
+ zcode task --detach --id page-2 --owns src/pages/team/ --check "npm run build" "Build page 2"
140
+ zcode status # all sessions: id, state/status, kind, updated, label
141
+ zcode status page-1 --json # one session: state, phase, activity, recent events, result
142
+ zcode wait page-1 page-2 # block until both finish; print each handback
143
+ zcode resume page-2 "Use the 2-column variant below 1024px." # answer / follow up / retry
144
+ zcode cancel page-1 # stop the turn and any running check; recorded as cancelled
127
145
  ```
128
146
 
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.
147
+ - **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.
148
+ - **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".
149
+ - **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`.
150
+ - **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
151
 
131
- &nbsp;
152
+ ### Concurrency and ownership
132
153
 
133
- ## Skills
154
+ `--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
155
 
135
- Two ways, both real: the model loads skills through its Skill tool.
156
+ - 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.
157
+ - 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.
158
+ - For overlapping or risky parallel work, give each session its own git worktree via `--cwd`.
159
+ - `ZAI_CODE_MAX_ACTIVE` (default 8) is a soft machine-wide cap. Sessions beyond it wait in `queued`.
160
+
161
+ &nbsp;
136
162
 
137
- - **Project skills, zero flags.** Put `<name>/SKILL.md` under `<workspace>/.zcode/skills/`. The engine walks your workspace; no copy, no flag.
163
+ ## One-shot turns and subagent dispatch
138
164
 
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.
165
+ `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
166
 
141
167
  ```sh
142
- zcode --skills ./my-skills "Load the word-vault skill, then reply with the magic word it defines."
168
+ zcode "What is 17*23? Reply with only the number."
169
+ zcode --model glm-5.3 --effort high "Draft a commit message for the staged diff"
170
+ echo "$PROMPT" | zcode
143
171
  ```
144
172
 
145
- `--no-skills` runs with an empty skill set. Your desktop user skills (`~/.zcode/skills`) are inherited into the isolation automatically.
173
+ `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`).
174
+
175
+ 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
176
 
147
177
  &nbsp;
148
178
 
149
179
  ## Machine interface
150
180
 
151
- `--json` emits NDJSON events on stdout with a stable final line:
181
+ `--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
182
 
153
183
  ```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}
184
+ {"type":"result","schema":"zcode.result/1","ok":true,"status":"completed","id":"page-1","kind":"task",
185
+ "summary":"…","response":"…executor report…","issues":[],"questions":[],"unresolved":[],"notVerified":[],
186
+ "changes":{"tracking":"git","files":[{"path":"src/pages/home/index.tsx","change":"added","owned":true}]},
187
+ "scopeViolations":[],"checks":[{"command":"npm run build","ok":true,"exitCode":0,"timedOut":false,"durationMs":8123,"output":"…tail…"}],
188
+ "checksPassed":true,"repairs":0,"handback":{"status":"done","summary":"…","changes":[],"checks":[],"criteria":[],"unresolved":[],"questions":[],"notes":""},
189
+ "subagents":[{"agentId":"agent_…","profileId":"general-purpose","status":"success","summary":"…","durationMs":59765}],
190
+ "error":null,"cwd":"/abs/site","workspaceRoot":"/abs/site","owns":["src/pages/home/"],"sessionId":"sess_…","resumed":false,"turns":1,
191
+ "model":"GLM-5.3-Flash","effort":"max","mode":"yolo","usage":{"inputTokens":19608,"outputTokens":3120},
192
+ "toolCalls":14,"durationMs":142301,"files":{"session":"…/sessions/page-1","result":"…/result.json","events":"…/events.ndjson"}}
160
193
  ```
161
194
 
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:
195
+ `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
196
 
166
197
  | event | meaning |
167
198
  |---|---|
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 |
199
+ | `{"type":"session","id","kind","cwd","owns","checks","model","effort","resumed"}` | which session is active, and its scope |
200
+ | `{"type":"phase","phase":"starting\|executing\|reporting\|checking\|repairing\|queued"}` | what is running |
201
+ | `{"type":"tool","name":"Edit","phase":"started"}` | tool lifecycle (`scheduled`, `started`, `result`, `error`) |
202
+ | `{"type":"subagent","phase":"started\|returned\|error"}` | subagent activity |
203
+ | `{"type":"check","phase":"finished","command","ok","exitCode","durationMs"}` | zcode-run verification |
204
+ | `{"type":"mcp","name","status"}` · `{"type":"retry",…}` · `{"type":"model_request_started",…}` | MCP settling, start-up retry, engine model evidence |
205
+ | `{"type":"stream","kind":"text_delta\|reasoning_delta","delta"}` | live text (run/agent; task with `--stream`) |
206
+
207
+ `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
208
 
175
209
  &nbsp;
176
210
 
177
211
  ## Options
178
212
 
179
- | option | takes | default | notes |
213
+ | option | applies to | default | notes |
180
214
  |---|---|---|---|
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 |
215
+ | `--id`, `--label` | run, task, agent | `zc-<random>` | session identity |
216
+ | `--detach` | run, task, agent, resume | off | background worker; prints the id |
217
+ | `--cwd` | run, task, agent | current dir | the workspace and blast radius |
218
+ | `--owns` | run, task, agent | whole workspace | repeatable path or glob |
219
+ | `--allow-overlap` | run, task, agent | off | start despite overlapping ownership |
220
+ | `--task` | task | | JSON contract, markdown brief, or `-` |
221
+ | `--accept`, `--check`, `--file`, `--context` | task | | repeatable; `--context @file` reads a file |
222
+ | `--max-repairs` | task, resume | `2` | repair turns after failing checks |
223
+ | `--check-timeout-ms` | task, resume | `300000` | per check; checks get `CI=1` when unset |
224
+ | `--model` | all turns | `glm-5.3-flash` | `glm-5.3` plus aliases `5.3`, `53`, `flash`, `5.3-flash`, `53f` |
225
+ | `--effort` | all turns | `max` | `low`, `medium`, `high`, `xhigh`, `max` |
226
+ | `--timeout-ms` | all turns; wait | run 600000, task 1800000 | the whole invocation incl. checks and repairs |
227
+ | `--session` | run | | 0.3 compat: continue by zcode id or engine `sess_` id |
228
+ | `--profile` | agent | `general-purpose` | subagent profile |
229
+ | `--mcp` / `--mcp-inherit` / `--mcp-list` | run, task, agent | hermetic | MCP servers (inline JSON or `@file`) |
230
+ | `--skills` / `--no-skills` | run, task, agent | desktop skills | add skills; or run with none |
231
+ | `--json`, `--quiet` | all | | see Machine interface |
232
+ | `--thinking` / `--no-thinking` | turns | run on, task off | mirror reasoning to stderr |
233
+ | `--stream`, `--include-reasoning` | turns | off | deltas in task events; full reasoning in the result |
234
+
235
+ 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
236
 
198
237
  ## Exit codes
199
238
 
200
239
  | code | meaning |
201
240
  |---|---|
202
- | 0 | success |
241
+ | 0 | completed |
203
242
  | 1 | unexpected internal error |
204
- | 2 | usage error, unknown model, unknown effort, bad `--mcp` JSON |
243
+ | 2 | usage error (bad flag, model, effort, contract, unknown or duplicate session id) |
205
244
  | 3 | no API key (`zcode login` or `ZAI_API_KEY`) |
206
245
  | 4 | engine or builtin provider config not found |
207
- | 5 | turn failed (engine error, failed terminal, user-input request) |
246
+ | 5 | failed: engine or turn error |
208
247
  | 6 | `agent`: no subagent dispatched or none ended `success` |
248
+ | 7 | workspace conflict, or the session is already running |
249
+ | 8 | partial, or checks still failing after repairs |
250
+ | 9 | unverified |
251
+ | 10 | blocked |
209
252
  | 124 | `--timeout-ms` exceeded |
210
- | 130 | interrupted (SIGINT) |
253
+ | 130 | cancelled |
211
254
 
212
255
  &nbsp;
213
256
 
214
257
  ## Under the hood
215
258
 
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.
259
+ **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
260
 
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.
261
+ **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
262
 
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.
263
+ **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
264
 
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.
265
+ **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
266
 
224
267
  &nbsp;
225
268
 
@@ -228,12 +271,15 @@ Event types on the way in:
228
271
  Zero dependencies, plain CommonJS, no build step.
229
272
 
230
273
  ```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
274
+ npm test # unit suite: in-process, scripted fake engine, no CLI spawn, no API calls
275
+ npm run test:live # functional suite: spawns the CLI against the real engine and GLM API
276
+ npm run accept:flash # black-box acceptance runs (real API)
233
277
  npm run accept:subagent
234
278
  npm run accept:53
235
279
  ```
236
280
 
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`.
281
+ 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`.
282
+
283
+ ## License
238
284
 
239
- MIT license.
285
+ MIT. Source and issues: [github.com/0xgeorgemathew/zcode](https://github.com/0xgeorgemathew/zcode).