@0xgeorgemathew/zcode 0.4.0 → 0.4.4
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 +50 -43
- package/bin/zcode.js +9 -3
- package/lib/appserver.js +16 -1
- package/lib/commands/exec.js +1 -0
- package/lib/commands/mcp-list.js +1 -1
- package/lib/commands/skills.js +39 -0
- package/lib/executor.js +2 -1
- package/lib/isolation.js +32 -3
- package/lib/skills.js +64 -0
- package/package.json +13 -3
- package/skills/README.md +9 -1
package/README.md
CHANGED
|
@@ -1,31 +1,26 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 |
|
|
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>
|
|
29
24
|
|
|
30
25
|
|
|
31
26
|
|
|
@@ -33,35 +28,44 @@ Opus, Sonnet, GPT, or any agent with a shell delegates implementation to zcode.
|
|
|
33
28
|
|
|
34
29
|
```sh
|
|
35
30
|
npm install -g @0xgeorgemathew/zcode
|
|
36
|
-
echo "$ZAI_API_KEY" | zcode login #
|
|
37
|
-
zcode doctor #
|
|
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
|
+
zcode skills install # optional: teach Claude Code and Codex to delegate to zcode
|
|
38
34
|
|
|
39
35
|
zcode task --check "npm test" "Fix the off-by-one in src/paginate.ts"
|
|
40
36
|
```
|
|
41
37
|
|
|
42
|
-
|
|
38
|
+
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.
|
|
39
|
+
|
|
40
|
+
The package ships skills that teach Claude Code and Codex how to delegate to zcode. Install them with one command:
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
```sh
|
|
43
|
+
zcode skills install # both; or: zcode skills install claude | codex
|
|
44
|
+
zcode skills # show what is installed and where
|
|
45
|
+
zcode skills uninstall # remove them
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The Claude Code skill goes to `~/.claude/skills/zcode` and the Codex skill to `${CODEX_HOME:-~/.codex}/skills/zcode`. The package runs no install scripts. Details are in [`skills/`](skills/README.md).
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
## Models and effort
|
|
49
53
|
|
|
50
|
-
Every command runs on
|
|
54
|
+
Every command runs on GLM-5.3-Flash at effort `max` unless you pass a flag.
|
|
51
55
|
|
|
52
|
-
| | flag | values | default |
|
|
56
|
+
| setting | flag | values | default |
|
|
53
57
|
|---|---|---|---|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
58
|
+
| model | `--model` | `glm-5.3-flash` (aliases `flash`, `5.3-flash`, `53f`), `glm-5.3` (aliases `5.3`, `53`) | `glm-5.3-flash` |
|
|
59
|
+
| effort | `--effort` | `low`, `medium`, `high`, `xhigh`, `max` | `max` |
|
|
56
60
|
|
|
57
61
|
```sh
|
|
58
|
-
zcode task "
|
|
59
|
-
zcode task --effort high "
|
|
60
|
-
zcode task --model glm-5.3 "
|
|
61
|
-
zcode task --model glm-5.3 --effort medium "
|
|
62
|
+
zcode task "..." # GLM-5.3-Flash, effort max
|
|
63
|
+
zcode task --effort high "..." # Flash with less reasoning
|
|
64
|
+
zcode task --model glm-5.3 "..." # GLM-5.3, effort max
|
|
65
|
+
zcode task --model glm-5.3 --effort medium "..." # GLM-5.3, effort medium
|
|
62
66
|
```
|
|
63
67
|
|
|
64
|
-
A task contract can
|
|
68
|
+
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.
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
|
|
@@ -231,7 +235,8 @@ Inside `zcode task`, subagents are optional. The executor decides when to use th
|
|
|
231
235
|
| `--timeout-ms` | all turns; wait | run 600000, task 1800000 | the whole invocation incl. checks and repairs |
|
|
232
236
|
| `--session` | run | | 0.3 compat: continue by zcode id or engine `sess_` id |
|
|
233
237
|
| `--profile` | agent | `general-purpose` | subagent profile |
|
|
234
|
-
| `--mcp` / `--mcp-inherit` / `--mcp-list` | run, task, agent | hermetic | MCP servers (inline JSON or `@file`) |
|
|
238
|
+
| `--mcp` / `--mcp-inherit` / `--mcp-list` | run, task, agent | hermetic, plus the desktop's `playwright` | MCP servers (inline JSON or `@file`) |
|
|
239
|
+
| `--no-browser` | run, task, agent | off | Drop the desktop's `playwright` MCP server |
|
|
235
240
|
| `--skills` / `--no-skills` | run, task, agent | desktop skills | add skills; or run with none |
|
|
236
241
|
| `--json`, `--quiet` | all | | see Machine interface |
|
|
237
242
|
| `--thinking` / `--no-thinking` | turns | run on, task off | mirror reasoning to stderr |
|
|
@@ -285,4 +290,6 @@ npm run accept:53
|
|
|
285
290
|
|
|
286
291
|
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`.
|
|
287
292
|
|
|
288
|
-
|
|
293
|
+
## License
|
|
294
|
+
|
|
295
|
+
MIT. Source and issues: [github.com/0xgeorgemathew/zcode](https://github.com/0xgeorgemathew/zcode).
|
package/bin/zcode.js
CHANGED
|
@@ -14,6 +14,8 @@ const USAGE = `usage:
|
|
|
14
14
|
zcode cancel <id> [--json] stop a running session
|
|
15
15
|
zcode login read API key from stdin → credentials
|
|
16
16
|
zcode doctor [--json] engine + isolation + key report
|
|
17
|
+
zcode skills [install|uninstall] [claude|codex]
|
|
18
|
+
install the orchestrator skills (default: status)
|
|
17
19
|
|
|
18
20
|
session:
|
|
19
21
|
--id <id> name the session (letters, digits, . _ -); default zc-<random>.
|
|
@@ -43,6 +45,7 @@ execution:
|
|
|
43
45
|
--profile <name> subagent profile (agent command; default general-purpose)
|
|
44
46
|
--mcp <json|@f> add MCP servers (repeatable); hermetic by default
|
|
45
47
|
--mcp-inherit also inherit the desktop's enabled MCP servers
|
|
48
|
+
--no-browser drop the desktop's playwright MCP server (on by default)
|
|
46
49
|
--mcp-list print the effective MCP server set as JSON and exit
|
|
47
50
|
--skills <dir> add skills (repeatable): one skill dir or a root of them
|
|
48
51
|
--no-skills empty skill set
|
|
@@ -94,9 +97,9 @@ function parseMcpSpec(value) {
|
|
|
94
97
|
return parsed;
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
const COMMANDS = new Set(['run', 'task', 'agent', 'resume', 'status', 'list', 'wait', 'cancel', 'login', 'doctor', '__exec']);
|
|
100
|
+
const COMMANDS = new Set(['run', 'task', 'agent', 'resume', 'status', 'list', 'wait', 'cancel', 'login', 'doctor', 'skills', '__exec']);
|
|
98
101
|
const TASK_ONLY = new Set(['task', 'accept', 'check', 'file', 'context', 'maxRepairs', 'checkTimeoutMs']);
|
|
99
|
-
const START_ONLY = new Set(['id', 'label', 'owns', 'allowOverlap', 'mcpSpecs', 'mcpInherit', 'skillsDirs', 'noSkills', 'profile', 'cwd']);
|
|
102
|
+
const START_ONLY = new Set(['id', 'label', 'owns', 'allowOverlap', 'mcpSpecs', 'mcpInherit', 'noBrowser', 'skillsDirs', 'noSkills', 'profile', 'cwd']);
|
|
100
103
|
|
|
101
104
|
function parseArgv(argv) {
|
|
102
105
|
const out = {
|
|
@@ -127,6 +130,7 @@ function parseArgv(argv) {
|
|
|
127
130
|
checkTimeoutMs: undefined,
|
|
128
131
|
mcpSpecs: [],
|
|
129
132
|
mcpInherit: false,
|
|
133
|
+
noBrowser: false,
|
|
130
134
|
mcpList: false,
|
|
131
135
|
skillsDirs: [],
|
|
132
136
|
noSkills: false,
|
|
@@ -148,7 +152,7 @@ function parseArgv(argv) {
|
|
|
148
152
|
]);
|
|
149
153
|
const boolFlags = new Map([
|
|
150
154
|
['--json', ['json', true]], ['--fresh', ['fresh', true]], ['--mcp-inherit', ['mcpInherit', true]],
|
|
151
|
-
['--mcp-list', ['mcpList', true]], ['--no-skills', ['noSkills', true]],
|
|
155
|
+
['--mcp-list', ['mcpList', true]], ['--no-skills', ['noSkills', true]], ['--no-browser', ['noBrowser', true]],
|
|
152
156
|
['--include-reasoning', ['includeReasoning', true]], ['--quiet', ['quiet', true]],
|
|
153
157
|
['--detach', ['detach', true]], ['--allow-overlap', ['allowOverlap', true]],
|
|
154
158
|
['--thinking', ['thinking', true]], ['--no-thinking', ['thinking', false]], ['--stream', ['stream', true]],
|
|
@@ -265,6 +269,8 @@ async function main() {
|
|
|
265
269
|
return require('../lib/commands/login.js')(parsed);
|
|
266
270
|
case 'doctor':
|
|
267
271
|
return require('../lib/commands/doctor.js')(parsed);
|
|
272
|
+
case 'skills':
|
|
273
|
+
return require('../lib/commands/skills.js')(parsed);
|
|
268
274
|
default:
|
|
269
275
|
return fail(2, `unknown command "${parsed.command}".\n${USAGE}`);
|
|
270
276
|
}
|
package/lib/appserver.js
CHANGED
|
@@ -150,6 +150,17 @@ class AppServerClient {
|
|
|
150
150
|
if (this.onUserInputRequest) this.onUserInputRequest(frame);
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
|
+
if (method === 'interaction/browserList') {
|
|
154
|
+
// The desktop app answers with its built-in browser panes; the CLI has
|
|
155
|
+
// none. An empty list lets the agent fall back to the user's own tools
|
|
156
|
+
// (Playwright, MCP browsers) instead of failing on a policy error.
|
|
157
|
+
reply({ browsers: [] });
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (method === 'interaction/browserExecute') {
|
|
161
|
+
replyError(-32000, 'zcode: no built-in browser in the CLI; use Playwright or a browser MCP server');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
153
164
|
// Everything else — incl. interaction/requestOfficialMcpAuthHeaders,
|
|
154
165
|
// proven non-fatal with -32601 — gets method-not-found and the run
|
|
155
166
|
// continues.
|
|
@@ -220,8 +231,12 @@ function workspaceKeyFor(workspacePath) {
|
|
|
220
231
|
// turn always sees the caller's --mcp servers. Inherited servers are
|
|
221
232
|
// best-effort and never block the send.
|
|
222
233
|
async function waitForMcp(client, { workspacePath, required, timeoutMs = 20000, onEvent = () => {} }) {
|
|
223
|
-
if (!required || required.length === 0) return;
|
|
224
234
|
const ws = { workspace: { workspacePath, workspaceKey: workspaceKeyFor(workspacePath) } };
|
|
235
|
+
if (!required || required.length === 0) {
|
|
236
|
+
// Still start inherited servers (the browser server); never block on them.
|
|
237
|
+
client.request('mcp/list', { ...ws, mode: 'connect' }, { timeoutMs: 15000 }).catch(() => {});
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
225
240
|
try {
|
|
226
241
|
await client.request('mcp/list', { ...ws, mode: 'connect' }, { timeoutMs: 15000 });
|
|
227
242
|
} catch {
|
package/lib/commands/exec.js
CHANGED
|
@@ -77,6 +77,7 @@ function buildInvocation(parsed) {
|
|
|
77
77
|
acceptanceDeclared: false,
|
|
78
78
|
mcpSpecs: flags.mcpSpecs,
|
|
79
79
|
mcpInherit: flags.mcpInherit,
|
|
80
|
+
noBrowser: flags.noBrowser,
|
|
80
81
|
skillsDirs: flags.skillsDirs.map((d) => path.resolve(d)),
|
|
81
82
|
noSkills: flags.noSkills,
|
|
82
83
|
profile: flags.profile,
|
package/lib/commands/mcp-list.js
CHANGED
|
@@ -6,7 +6,7 @@ const { effectiveMcpServers } = require('../isolation.js');
|
|
|
6
6
|
|
|
7
7
|
module.exports = async function mcpList(parsed) {
|
|
8
8
|
const { flags } = parsed;
|
|
9
|
-
const servers = effectiveMcpServers({ inherit: flags.mcpInherit, mcpSpecs: flags.mcpSpecs });
|
|
9
|
+
const servers = effectiveMcpServers({ inherit: flags.mcpInherit, browser: !flags.noBrowser, mcpSpecs: flags.mcpSpecs });
|
|
10
10
|
process.stdout.write(JSON.stringify({ type: 'mcp', servers }) + '\n');
|
|
11
11
|
process.exit(0);
|
|
12
12
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// zcode skills [status|install|uninstall] [claude|codex]
|
|
3
|
+
// Copies the orchestrator skills shipped in this package into Claude Code
|
|
4
|
+
// and Codex. Nothing runs at npm install time; this command is the installer.
|
|
5
|
+
const { installSkills, uninstallSkills, skillStatus, PLATFORMS } = require('../skills.js');
|
|
6
|
+
|
|
7
|
+
const ACTIONS = new Set(['status', 'install', 'uninstall']);
|
|
8
|
+
|
|
9
|
+
module.exports = function skills({ prompts, flags }) {
|
|
10
|
+
const [action = 'status', ...platforms] = prompts;
|
|
11
|
+
const fail = (msg) => {
|
|
12
|
+
process.stderr.write(`zcode: ${msg}\nusage: zcode skills [status|install|uninstall] [claude|codex]\n`);
|
|
13
|
+
process.exit(2);
|
|
14
|
+
};
|
|
15
|
+
if (!ACTIONS.has(action)) fail(`unknown skills action "${action}".`);
|
|
16
|
+
for (const p of platforms) if (!PLATFORMS.includes(p)) fail(`unknown platform "${p}" (claude | codex).`);
|
|
17
|
+
|
|
18
|
+
const view = (t) => ({ platform: t.platform, name: t.name, path: t.to, ...(t.installed !== undefined && { installed: t.installed }) });
|
|
19
|
+
let rows;
|
|
20
|
+
if (action === 'install') rows = installSkills({ platforms });
|
|
21
|
+
else if (action === 'uninstall') rows = uninstallSkills({ platforms });
|
|
22
|
+
else rows = skillStatus().filter((t) => !platforms.length || platforms.includes(t.platform));
|
|
23
|
+
|
|
24
|
+
if (flags.json) {
|
|
25
|
+
process.stdout.write(JSON.stringify({ action, skills: rows.map(view) }, null, 2) + '\n');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (rows.length === 0) {
|
|
29
|
+
process.stdout.write('zcode: nothing to uninstall\n');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const t of rows) {
|
|
33
|
+
const what = action === 'install' ? 'installed' : action === 'uninstall' ? 'removed' : t.installed ? 'installed' : 'not installed';
|
|
34
|
+
process.stdout.write(`${t.name.padEnd(12)} ${what.padEnd(14)} ${t.to}\n`);
|
|
35
|
+
}
|
|
36
|
+
if (action === 'status' && rows.some((t) => !t.installed)) {
|
|
37
|
+
process.stdout.write('\nrun `zcode skills install` to install them\n');
|
|
38
|
+
}
|
|
39
|
+
};
|
package/lib/executor.js
CHANGED
|
@@ -52,7 +52,7 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
52
52
|
// Settings a session keeps across invocations (stored as invocation.json).
|
|
53
53
|
const SETTING_KEYS = [
|
|
54
54
|
'kind', 'label', 'cwd', 'model', 'effort', 'timeoutMs', 'maxRepairs', 'checkTimeoutMs',
|
|
55
|
-
'checks', 'owns', 'ownsDisplay', 'acceptanceDeclared', 'mcpSpecs', 'mcpInherit',
|
|
55
|
+
'checks', 'owns', 'ownsDisplay', 'acceptanceDeclared', 'mcpSpecs', 'mcpInherit', 'noBrowser',
|
|
56
56
|
'skillsDirs', 'noSkills', 'profile', 'includeReasoning', 'allowOverlap', 'stream',
|
|
57
57
|
];
|
|
58
58
|
|
|
@@ -338,6 +338,7 @@ async function execute(inv, opts = {}) {
|
|
|
338
338
|
live: liveDir(env),
|
|
339
339
|
mcpSpecs: inv.mcpSpecs || [],
|
|
340
340
|
mcpInherit: inv.mcpInherit,
|
|
341
|
+
browser: !inv.noBrowser,
|
|
341
342
|
skillsDirs: inv.skillsDirs || [],
|
|
342
343
|
noSkills: inv.noSkills,
|
|
343
344
|
});
|
package/lib/isolation.js
CHANGED
|
@@ -142,8 +142,18 @@ function liveMcpServers(live = LIVE_ZCODE) {
|
|
|
142
142
|
// base (specs still win by name). Hermetic default is deliberate: the
|
|
143
143
|
// desktop set includes OAuth servers that can never complete headless and
|
|
144
144
|
// stall the engine's connect pool.
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
// Exception: the desktop's local browser server (stdio, no OAuth) is always
|
|
146
|
+
// inherited, so browser prompts open the same Playwright window the desktop
|
|
147
|
+
// opens. --no-browser drops it.
|
|
148
|
+
const BROWSER_SERVERS = ['playwright'];
|
|
149
|
+
|
|
150
|
+
function effectiveMcpServers({ inherit = false, browser = true, mcpSpecs = [], live = LIVE_ZCODE } = {}) {
|
|
151
|
+
const liveSet = liveMcpServers(live);
|
|
152
|
+
const servers = inherit ? liveSet : {};
|
|
153
|
+
for (const name of BROWSER_SERVERS) {
|
|
154
|
+
if (!browser) delete servers[name];
|
|
155
|
+
else if (liveSet[name]) servers[name] = liveSet[name];
|
|
156
|
+
}
|
|
147
157
|
for (const spec of mcpSpecs) {
|
|
148
158
|
for (const [name, def] of Object.entries(spec)) servers[name] = def;
|
|
149
159
|
}
|
|
@@ -200,6 +210,11 @@ function isDanglingLink(p) {
|
|
|
200
210
|
// sources + this invocation's flags; engine state under the HOME (cli/db,
|
|
201
211
|
// cli/agents, cli/rollout, logs, memories) is never touched, so a session
|
|
202
212
|
// stays resumable across invocations. Returns facts for reporting.
|
|
213
|
+
// Desktop skills that drive the app's own UI (the built-in browser pane).
|
|
214
|
+
// The CLI cannot serve them, and loading one steers the agent away from
|
|
215
|
+
// Playwright into a dead end.
|
|
216
|
+
const HOST_ONLY_SKILLS = new Set(['control-browser']);
|
|
217
|
+
|
|
203
218
|
function buildEngineHome({
|
|
204
219
|
home,
|
|
205
220
|
root,
|
|
@@ -207,6 +222,7 @@ function buildEngineHome({
|
|
|
207
222
|
live = LIVE_ZCODE,
|
|
208
223
|
mcpSpecs = [],
|
|
209
224
|
mcpInherit = false,
|
|
225
|
+
browser = true,
|
|
210
226
|
skillsDirs = [],
|
|
211
227
|
noSkills = false,
|
|
212
228
|
}) {
|
|
@@ -237,7 +253,7 @@ function buildEngineHome({
|
|
|
237
253
|
zai.options = zai.options || {};
|
|
238
254
|
zai.options.apiKey = key;
|
|
239
255
|
}
|
|
240
|
-
const servers = effectiveMcpServers({ inherit: mcpInherit, mcpSpecs, live });
|
|
256
|
+
const servers = effectiveMcpServers({ inherit: mcpInherit, browser, mcpSpecs, live });
|
|
241
257
|
cfg.mcp = { ...(cfg.mcp || {}), servers };
|
|
242
258
|
writeJson600(path.join(cliDir, 'config.json'), cfg);
|
|
243
259
|
|
|
@@ -268,6 +284,7 @@ function buildEngineHome({
|
|
|
268
284
|
for (const entry of listDir(liveSkills)) {
|
|
269
285
|
if (!entry.isDirectory()) continue;
|
|
270
286
|
if (!fs.existsSync(path.join(liveSkills, entry.name, 'SKILL.md'))) continue;
|
|
287
|
+
if (HOST_ONLY_SKILLS.has(entry.name)) continue;
|
|
271
288
|
fs.cpSync(path.join(liveSkills, entry.name), path.join(skillsDir, entry.name), {
|
|
272
289
|
recursive: true,
|
|
273
290
|
});
|
|
@@ -345,6 +362,18 @@ function migrateRoot(env = process.env) {
|
|
|
345
362
|
// config, and the uncontrolled overrides deleted. cwd is set by the caller.
|
|
346
363
|
function engineEnv({ home, builtinProviderConfig, parentEnv = process.env }) {
|
|
347
364
|
const childEnv = { ...parentEnv };
|
|
365
|
+
const realHome = parentEnv.HOME || os.homedir();
|
|
366
|
+
// The scratch HOME hides the user's tool caches. Point Playwright and npm
|
|
367
|
+
// back at the real ones so installed browsers and packages are found.
|
|
368
|
+
if (!childEnv.PLAYWRIGHT_BROWSERS_PATH) {
|
|
369
|
+
const cache = process.platform === 'darwin'
|
|
370
|
+
? path.join(realHome, 'Library', 'Caches', 'ms-playwright')
|
|
371
|
+
: path.join(realHome, '.cache', 'ms-playwright');
|
|
372
|
+
if (fs.existsSync(cache)) childEnv.PLAYWRIGHT_BROWSERS_PATH = cache;
|
|
373
|
+
}
|
|
374
|
+
if (!childEnv.npm_config_cache && !childEnv.NPM_CONFIG_CACHE) {
|
|
375
|
+
childEnv.npm_config_cache = path.join(realHome, '.npm');
|
|
376
|
+
}
|
|
348
377
|
childEnv.HOME = home;
|
|
349
378
|
childEnv.ZCODE_BUILTIN_PROVIDER_CONFIG_FILE = builtinProviderConfig;
|
|
350
379
|
for (const name of [
|
package/lib/skills.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The orchestrator skills shipped in the package, and where `zcode skills`
|
|
3
|
+
// installs them:
|
|
4
|
+
//
|
|
5
|
+
// skills/claude-code/zcode -> ~/.claude/skills/zcode
|
|
6
|
+
// skills/codex/zcode -> ${CODEX_HOME:-~/.codex}/skills/zcode
|
|
7
|
+
//
|
|
8
|
+
// Only the zcode skill directory is ever replaced or removed.
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const os = require('node:os');
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
|
|
13
|
+
const pkgRoot = path.join(__dirname, '..');
|
|
14
|
+
const PLATFORMS = ['claude', 'codex'];
|
|
15
|
+
|
|
16
|
+
function targets(env = process.env) {
|
|
17
|
+
const home = env.HOME || os.homedir();
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
platform: 'claude',
|
|
21
|
+
name: 'Claude Code',
|
|
22
|
+
from: path.join(pkgRoot, 'skills', 'claude-code', 'zcode'),
|
|
23
|
+
to: path.join(home, '.claude', 'skills', 'zcode'),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
platform: 'codex',
|
|
27
|
+
name: 'Codex',
|
|
28
|
+
from: path.join(pkgRoot, 'skills', 'codex', 'zcode'),
|
|
29
|
+
to: path.join(env.CODEX_HOME || path.join(home, '.codex'), 'skills', 'zcode'),
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function select(platforms, env) {
|
|
35
|
+
const want = platforms && platforms.length ? new Set(platforms) : new Set(PLATFORMS);
|
|
36
|
+
return targets(env).filter((t) => want.has(t.platform));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function installSkills({ platforms, env = process.env } = {}) {
|
|
40
|
+
const done = [];
|
|
41
|
+
for (const t of select(platforms, env)) {
|
|
42
|
+
fs.mkdirSync(path.dirname(t.to), { recursive: true });
|
|
43
|
+
fs.rmSync(t.to, { recursive: true, force: true });
|
|
44
|
+
fs.cpSync(t.from, t.to, { recursive: true });
|
|
45
|
+
done.push(t);
|
|
46
|
+
}
|
|
47
|
+
return done;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function uninstallSkills({ platforms, env = process.env } = {}) {
|
|
51
|
+
const done = [];
|
|
52
|
+
for (const t of select(platforms, env)) {
|
|
53
|
+
if (!fs.existsSync(t.to)) continue;
|
|
54
|
+
fs.rmSync(t.to, { recursive: true, force: true });
|
|
55
|
+
done.push(t);
|
|
56
|
+
}
|
|
57
|
+
return done;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function skillStatus({ env = process.env } = {}) {
|
|
61
|
+
return targets(env).map((t) => ({ ...t, installed: fs.existsSync(path.join(t.to, 'SKILL.md')) }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { installSkills, uninstallSkills, skillStatus, targets, PLATFORMS };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xgeorgemathew/zcode",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Lean GLM-5.3 execution engine for orchestrator agents: task contracts, independent sessions, zcode-run verification, structured handbacks, subagents, MCP, skills. Wraps the ZCode engine. Zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=20"
|
|
12
12
|
},
|
|
13
|
-
"dependencies": {
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@0xgeorgemathew/zcode": "^0.4.0"
|
|
15
|
+
},
|
|
14
16
|
"files": [
|
|
15
17
|
"bin",
|
|
16
18
|
"lib",
|
|
@@ -41,5 +43,13 @@
|
|
|
41
43
|
"ndjson",
|
|
42
44
|
"orchestrator",
|
|
43
45
|
"executor"
|
|
44
|
-
]
|
|
46
|
+
],
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/0xgeorgemathew/zcode.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/0xgeorgemathew/zcode#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/0xgeorgemathew/zcode/issues"
|
|
54
|
+
}
|
|
45
55
|
}
|
package/skills/README.md
CHANGED
|
@@ -9,7 +9,15 @@ These skills teach an orchestrating agent how to delegate work to zcode, run ses
|
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
After `npm install -g @0xgeorgemathew/zcode`, install both skills with:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
zcode skills install # or: zcode skills install claude | codex
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This copies the Claude Code skill to `~/.claude/skills/zcode` and the Codex skill to `${CODEX_HOME:-~/.codex}/skills/zcode`, replacing only an earlier copy of the zcode skill. Run it again after upgrading zcode to refresh them. `zcode skills` shows what is installed, and `zcode skills uninstall` removes them. The package runs no install scripts, so npm never asks to allow one.
|
|
19
|
+
|
|
20
|
+
To copy them by hand instead, run these from a checkout of this repo, or from the installed package (`$(npm root -g)/@0xgeorgemathew/zcode`), which ships `skills/`.
|
|
13
21
|
|
|
14
22
|
**Claude Code.** Personal skills, available in every project:
|
|
15
23
|
|