@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 CHANGED
@@ -1,31 +1,26 @@
1
- <div align="center">
2
-
3
- # ⚡ zcode
4
-
5
- **The execution engine for orchestrator agents.**
6
-
7
- Hand it a scoped task. It implements, verifies with *your* checks, and hands back proof.
8
-
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)
12
- [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
13
-
14
- `GLM-5.3-Flash` · effort `max` by default · `GLM-5.3` on demand
15
-
16
- </div>
17
-
18
- &nbsp;
19
-
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 |
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
  &nbsp;
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 # store your Z.AI key (mode 600, never printed)
37
- zcode doctor # check engine, isolation, key
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
- 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.
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
- Orchestrator skills for Claude Code and Codex ship in [`skills/`](skills/README.md).
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
  &nbsp;
47
51
 
48
52
  ## Models and effort
49
53
 
50
- Every command runs on **GLM-5.3-Flash at effort `max`** unless you say otherwise.
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
- | 🧠 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` |
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 "…" # 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
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 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.
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
  &nbsp;
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
- MIT license.
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 {
@@ -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,
@@ -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
- function effectiveMcpServers({ inherit = false, mcpSpecs = [], live = LIVE_ZCODE } = {}) {
146
- const servers = inherit ? liveMcpServers(live) : {};
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.0",
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
- Run these from a checkout of this repo, or from the installed package (`$(npm root -g)/@0xgeorgemathew/zcode`), which ships `skills/`.
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