@0xgeorgemathew/zcode 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 The zai-code authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,239 @@
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)
18
+
19
+  
20
+
21
+ ## Install
22
+
23
+ ```sh
24
+ npm install -g @0xgeorgemathew/zcode
25
+ ```
26
+
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).
28
+
29
+  
30
+
31
+ ## Start in three commands
32
+
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
+  
40
+
41
+ ## Ask
42
+
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
48
+ ```
49
+
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.
51
+
52
+  
53
+
54
+ ## Watch it work
55
+
56
+ Plain mode keeps two streams apart on purpose:
57
+
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.
60
+
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
68
+ ```
69
+
70
+ `--quiet` silences the stderr feed. `--include-reasoning` puts the full thinking text into the JSON result line.
71
+
72
+  
73
+
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:
77
+
78
+ ```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
82
+ ```
83
+
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
+  
87
+
88
+ ## Dispatch a subagent
89
+
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`.
91
+
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
+ ```
111
+
112
+  
113
+
114
+ ## MCP servers
115
+
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.
117
+
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"
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
127
+ ```
128
+
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.
130
+
131
+  
132
+
133
+ ## Skills
134
+
135
+ Two ways, both real: the model loads skills through its Skill tool.
136
+
137
+ - **Project skills, zero flags.** Put `<name>/SKILL.md` under `<workspace>/.zcode/skills/`. The engine walks your workspace; no copy, no flag.
138
+
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.
140
+
141
+ ```sh
142
+ zcode --skills ./my-skills "Load the word-vault skill, then reply with the magic word it defines."
143
+ ```
144
+
145
+ `--no-skills` runs with an empty skill set. Your desktop user skills (`~/.zcode/skills`) are inherited into the isolation automatically.
146
+
147
+ &nbsp;
148
+
149
+ ## Machine interface
150
+
151
+ `--json` emits NDJSON events on stdout with a stable final line:
152
+
153
+ ```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}
160
+ ```
161
+
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:
165
+
166
+ | event | meaning |
167
+ |---|---|
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 |
174
+
175
+ &nbsp;
176
+
177
+ ## Options
178
+
179
+ | option | takes | default | notes |
180
+ |---|---|---|---|
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 |
197
+
198
+ ## Exit codes
199
+
200
+ | code | meaning |
201
+ |---|---|
202
+ | 0 | success |
203
+ | 1 | unexpected internal error |
204
+ | 2 | usage error, unknown model, unknown effort, bad `--mcp` JSON |
205
+ | 3 | no API key (`zcode login` or `ZAI_API_KEY`) |
206
+ | 4 | engine or builtin provider config not found |
207
+ | 5 | turn failed (engine error, failed terminal, user-input request) |
208
+ | 6 | `agent`: no subagent dispatched or none ended `success` |
209
+ | 124 | `--timeout-ms` exceeded |
210
+ | 130 | interrupted (SIGINT) |
211
+
212
+ &nbsp;
213
+
214
+ ## Under the hood
215
+
216
+ **Key handling.** `ZAI_API_KEY` env var, or `zcode login` which stores it at `~/.zcode/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.
217
+
218
+ **Isolation.** The engine runs with `HOME` pointed at a scratch copy this package owns (`~/.zcode/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.
219
+
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.
221
+
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.
223
+
224
+ &nbsp;
225
+
226
+ ## Development
227
+
228
+ Zero dependencies, plain CommonJS, no build step.
229
+
230
+ ```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
233
+ npm run accept:subagent
234
+ npm run accept:53
235
+ ```
236
+
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`.
238
+
239
+ MIT license.
package/bin/zcode.js ADDED
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // zcode — CLI entry: argv parsing + command dispatch (DESIGN §10).
4
+ const { normalizeModel, unknownModelMessage, normalizeEffort, EFFORT_LEVELS, DEFAULT_MODEL } = require('../lib/models.js');
5
+
6
+ const EXIT = {
7
+ OK: 0,
8
+ INTERNAL: 1,
9
+ USAGE: 2,
10
+ NO_KEY: 3,
11
+ NO_ENGINE: 4,
12
+ TURN_FAILED: 5,
13
+ NO_SUBAGENT: 6,
14
+ TIMEOUT: 124,
15
+ };
16
+
17
+ const USAGE = `usage: zcode [options] [prompt…] one-shot turn (prompt via stdin when piped)
18
+ zcode agent [--profile <name>] <instructions…>
19
+ turn that MUST dispatch a subagent
20
+ zcode login read API key from stdin → credentials
21
+ zcode doctor [--json] [--fresh] engine + isolation + key report
22
+
23
+ options:
24
+ --model <id> glm-5.3 | glm-5.3-flash (+aliases). default: glm-5.3-flash
25
+ --effort <level> low | medium | high | xhigh | max. default: max
26
+ --cwd <dir> workspace for the session (default: process.cwd())
27
+ --profile <name> subagent profile (agent command; default: general-purpose)
28
+ --session <id> continue a previous session (sessionId from a prior result)
29
+ --mcp <json|@f> add MCP servers (repeatable). JSON maps names to defs:
30
+ {"name":{"type":"stdio","command","args","env"}} or
31
+ {"name":{"type":"http","url","headers"}}; @file reads a file.
32
+ Hermetic default: only these run (deterministic, fast)
33
+ --mcp-inherit also inherit the desktop's enabled MCP servers
34
+ --mcp-list print the effective MCP server set as JSON and exit
35
+ --skills <dir> add skills (repeatable): one skill dir (has SKILL.md) or a
36
+ root of <name>/SKILL.md dirs. project <cwd>/.zcode/skills
37
+ is picked up by the engine without any flag
38
+ --no-skills run with an empty skill set (nothing inherited, nothing injected)
39
+ --include-reasoning put the full reasoning text in the final JSON result
40
+ --quiet do not mirror reasoning to stderr while running
41
+ --json NDJSON events on stdout; final line {"type":"result",…}
42
+ (stream events include reasoning_delta = thinking)
43
+ --timeout-ms <n> default 600000
44
+ --fresh rebuild the scratch HOME first
45
+ -p, --prompt <t> prompt text (alias for positional prompt)
46
+ --version, --help`;
47
+
48
+ function fail(code, message) {
49
+ process.stderr.write(`zcode: ${message}\n`);
50
+ process.exit(code);
51
+ }
52
+
53
+ function isDirectory(p) {
54
+ try {
55
+ return require('node:fs').statSync(p).isDirectory();
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+
61
+ // --mcp accepts inline JSON or @file; the JSON must map server names to
62
+ // definition objects ({type:"stdio"|"http"|"sse", ...} per the engine's
63
+ // config schema). Fails (exit 2) before any engine spawn.
64
+ function parseMcpSpec(value) {
65
+ let text = value;
66
+ if (value.startsWith('@')) {
67
+ try {
68
+ text = require('node:fs').readFileSync(value.slice(1), 'utf8');
69
+ } catch {
70
+ fail(EXIT.USAGE, `--mcp file "${value.slice(1)}" could not be read.`);
71
+ }
72
+ }
73
+ let parsed;
74
+ try {
75
+ parsed = JSON.parse(text);
76
+ } catch (err) {
77
+ fail(EXIT.USAGE, `--mcp spec must be valid JSON mapping server names to definitions: ${err.message}`);
78
+ }
79
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
80
+ fail(EXIT.USAGE, `--mcp spec must be valid JSON mapping server names to definitions, got ${Array.isArray(parsed) ? 'an array' : `a ${typeof parsed}`}.`);
81
+ }
82
+ for (const [name, def] of Object.entries(parsed)) {
83
+ if (!def || typeof def !== 'object' || Array.isArray(def)) {
84
+ fail(EXIT.USAGE, `--mcp server "${name}" must be an object (e.g. {"type":"stdio","command":...,"args":[...]}).`);
85
+ }
86
+ }
87
+ return parsed;
88
+ }
89
+
90
+ // Parses argv (without node/script) into {command, prompts, flags}.
91
+ // Exits 2 on usage errors. Rejection happens here, before any engine spawn.
92
+ function parseArgv(argv) {
93
+ const out = {
94
+ command: 'run',
95
+ prompts: [],
96
+ flags: {
97
+ model: DEFAULT_MODEL,
98
+ effort: 'max',
99
+ cwd: process.cwd(),
100
+ json: false,
101
+ fresh: false,
102
+ timeoutMs: 600000,
103
+ profile: 'general-purpose',
104
+ prompt: undefined,
105
+ session: undefined,
106
+ mcpSpecs: [],
107
+ mcpInherit: false,
108
+ mcpList: false,
109
+ skillsDirs: [],
110
+ noSkills: false,
111
+ includeReasoning: false,
112
+ quiet: false,
113
+ },
114
+ };
115
+ const valueFlags = new Map([
116
+ ['--model', 'model'],
117
+ ['--effort', 'effort'],
118
+ ['--cwd', 'cwd'],
119
+ ['--profile', 'profile'],
120
+ ['--timeout-ms', 'timeoutMs'],
121
+ ['-p', 'prompt'],
122
+ ['--prompt', 'prompt'],
123
+ ['--session', 'session'],
124
+ ]);
125
+ const listFlags = new Map([
126
+ ['--mcp', 'mcpSpecs'],
127
+ ['--skills', 'skillsDirs'],
128
+ ]);
129
+ const boolFlags = new Map([
130
+ ['--json', 'json'],
131
+ ['--fresh', 'fresh'],
132
+ ['--mcp-inherit', 'mcpInherit'],
133
+ ['--mcp-list', 'mcpList'],
134
+ ['--no-skills', 'noSkills'],
135
+ ['--include-reasoning', 'includeReasoning'],
136
+ ['--quiet', 'quiet'],
137
+ ]);
138
+ const commands = new Set(['run', 'agent', 'login', 'doctor']);
139
+
140
+ for (let i = 0; i < argv.length; i++) {
141
+ const arg = argv[i];
142
+ if (valueFlags.has(arg) || boolFlags.has(arg) || listFlags.has(arg)) {
143
+ let key;
144
+ if (valueFlags.has(arg)) {
145
+ key = valueFlags.get(arg);
146
+ const value = argv[++i];
147
+ if (value === undefined) fail(EXIT.USAGE, `option ${arg} requires a value.\n${USAGE}`);
148
+ if (key === 'timeoutMs') {
149
+ const n = Number(value);
150
+ if (!Number.isFinite(n) || n <= 0) fail(EXIT.USAGE, `--timeout-ms must be a positive number, got "${value}".`);
151
+ out.flags[key] = n;
152
+ } else if (key === 'session') {
153
+ if (!value.trim()) fail(EXIT.USAGE, '--session requires a session id.');
154
+ out.flags[key] = value.trim();
155
+ } else {
156
+ out.flags[key] = value;
157
+ }
158
+ } else if (listFlags.has(arg)) {
159
+ key = listFlags.get(arg);
160
+ const value = argv[++i];
161
+ if (value === undefined) fail(EXIT.USAGE, `option ${arg} requires a value.\n${USAGE}`);
162
+ if (key === 'mcpSpecs') {
163
+ out.flags.mcpSpecs.push(parseMcpSpec(value, arg));
164
+ } else {
165
+ if (!isDirectory(value)) fail(EXIT.USAGE, `--skills requires an existing directory, got "${value}".`);
166
+ out.flags.skillsDirs.push(value);
167
+ }
168
+ } else {
169
+ key = boolFlags.get(arg);
170
+ out.flags[key] = true;
171
+ }
172
+ continue;
173
+ }
174
+ if (arg === '--help' || arg === '-h') {
175
+ process.stdout.write(USAGE + '\n');
176
+ process.exit(EXIT.OK);
177
+ }
178
+ if (arg === '--version') {
179
+ process.stdout.write(require('../package.json').version + '\n');
180
+ process.exit(EXIT.OK);
181
+ }
182
+ if (arg === '--') {
183
+ out.prompts.push(...argv.slice(i + 1));
184
+ break;
185
+ }
186
+ if (arg.startsWith('--') && arg.length > 2) {
187
+ fail(EXIT.USAGE, `unknown option "${arg}".\n${USAGE}`);
188
+ }
189
+ if (out.prompts.length === 0 && commands.has(arg)) {
190
+ out.command = arg;
191
+ continue;
192
+ }
193
+ out.prompts.push(arg);
194
+ }
195
+
196
+ if (out.command === 'run' && out.flags.prompt !== undefined) {
197
+ out.prompts.unshift(out.flags.prompt);
198
+ }
199
+
200
+ const model = normalizeModel(out.flags.model);
201
+ if (!model) fail(EXIT.USAGE, unknownModelMessage(out.flags.model));
202
+ out.flags.model = model;
203
+
204
+ const effort = normalizeEffort(out.flags.effort);
205
+ if (!effort) {
206
+ fail(
207
+ EXIT.USAGE,
208
+ `unknown effort "${out.flags.effort}". --effort accepts ${EFFORT_LEVELS.join(' | ')}.`
209
+ );
210
+ }
211
+ out.flags.effort = effort;
212
+ return out;
213
+ }
214
+
215
+ async function main() {
216
+ const parsed = parseArgv(process.argv.slice(2));
217
+ const { command, flags } = parsed;
218
+ if (flags.mcpList) {
219
+ return require('../lib/commands/mcp-list.js')(parsed);
220
+ }
221
+ const run = require('../lib/commands/run.js');
222
+ switch (command) {
223
+ case 'run':
224
+ return run(parsed);
225
+ case 'agent':
226
+ return require('../lib/commands/agent.js')(parsed);
227
+ case 'login':
228
+ return require('../lib/commands/login.js')(parsed);
229
+ case 'doctor':
230
+ return require('../lib/commands/doctor.js')(parsed);
231
+ default:
232
+ fail(EXIT.USAGE, `unknown command "${command}".\n${USAGE}`);
233
+ }
234
+ }
235
+
236
+ main().catch((err) => {
237
+ process.stderr.write(`zcode: internal error: ${err && err.stack ? err.stack : err}\n`);
238
+ process.exit(EXIT.INTERNAL);
239
+ });