@zergai/ztc 0.2.0-canary.1 → 0.2.0-canary.2

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
@@ -10,7 +10,7 @@
10
10
  An autonomous AI agent for the terminal. ZTC combines Claude-style conversational coding with native computer control, multi-agent orchestration, and a Voyager-inspired routine system for autonomous operation in graphical applications.
11
11
 
12
12
  ```
13
- ╭─ ZTC v0.2.0-canary.1 ───────────────────────────────────────────────╮
13
+ ╭─ ZTC v0.2.0-canary.2 ───────────────────────────────────────────────╮
14
14
  │ Provider: Anthropic Model: claude-sonnet-4-20250514 │
15
15
  │ Working directory: ~/projects/my-app Branch: main │
16
16
  ╰───────────────────────────────────────────────────────────────────╯
@@ -30,17 +30,16 @@ An autonomous AI agent for the terminal. ZTC combines Claude-style conversationa
30
30
 
31
31
  ## Quick Start
32
32
 
33
- The ZergAI organization distribution is `@zergai/ztc@next` for the 0.2 beta.
34
- After that release is published, install it with
35
- `npm install -g @zergai/ztc@next`. It provides the same `ztc` and `zerg` commands,
33
+ The ZergAI organization distribution is `@zergai/ztc` for the 0.2 canary.
34
+ Install it with `npm install -g @zergai/ztc`. It provides the same `ztc` and `zerg` commands,
36
35
  license, and package exports; use `@zergai/ztc/<export>` for scoped imports.
37
36
  Install only one distribution globally because the command names overlap.
38
- Existing stable users can keep the legacy package below; beta releases do not
37
+ Existing stable users can keep the legacy package below; canary releases do not
39
38
  move `zerg-ztc@latest` from the 0.1 line.
40
39
 
41
40
  ```bash
42
- # Install the published CLI
43
- npm install -g zerg-ztc@latest
41
+ # Install the 0.2 canary CLI
42
+ npm install -g @zergai/ztc
44
43
 
45
44
  # Start with a read-only inspection of a real project
46
45
  ztc --cwd /path/to/project --plan
@@ -112,7 +111,7 @@ npm start
112
111
  - One authentication route: Zerg Router (`/auth login` or `ztc auth login`), provider API key (`/config key`), or Claude Code CLI OAuth (`claude setup-token` + `CLAUDE_CODE_OAUTH_TOKEN`)
113
112
 
114
113
  The JS and headless surfaces support macOS, Linux, and Windows.
115
- Native payload support for the `0.2.0-canary.1` npm prerelease is `darwin-arm64`
114
+ Native payload support for the `0.2.0-canary.2` npm prerelease is `darwin-arm64`
116
115
  only (`aarch64-apple-darwin`). Interactive TUI, desktop, audio,
117
116
  and computer-control binaries from this prerelease are supported only on Apple
118
117
  Silicon macOS. Other platforms may use the JavaScript/headless APIs, but this
@@ -1038,3 +1037,20 @@ the ZTC 0.1.x releases.
1038
1037
  ---
1039
1038
 
1040
1039
  Part of the [Zerg](https://github.com/idanbeck/zerg) autonomous AI framework.
1040
+
1041
+ ## Updating the npm CLI
1042
+
1043
+ Run `ztc update` or `ztc --update` to install a newer npm
1044
+ release. `ztc update --check` prints JSON without installing. The
1045
+ installed channel is preserved (`dev`, `next`, or `latest`); select a channel
1046
+ explicitly with `update --channel dev|next|latest`. Updates never downgrade.
1047
+
1048
+ Startup checks only notify on stderr when it is a terminal, never prompt or
1049
+ install, and are cached for one hour. CI, redirected stderr, and help/version
1050
+ requests skip checks. Set `ZERGAI_NO_UPDATE_CHECK=1` to disable startup checks.
1051
+ Self-installation requires the matching macOS/Linux global npm prefix; local,
1052
+ linked, npx, or other package-manager installs must use their original manager.
1053
+ Existing releases need one ordinary npm upgrade before these commands exist.
1054
+
1055
+ See [shared updater policy](https://github.com/Epoch-ML/zerg/blob/development/docs/npm-cli-updates.md)
1056
+ for installation safety, channel switching, and packaging details.
@@ -337,6 +337,9 @@ export class CodexAuthBackend {
337
337
  stream: true,
338
338
  store: false,
339
339
  };
340
+ if (request.outputFormat !== undefined) {
341
+ body.text = { format: request.outputFormat };
342
+ }
340
343
  if (tools.length > 0) {
341
344
  body.tools = tools;
342
345
  body.tool_choice = 'auto';
@@ -89,6 +89,15 @@ export interface BackendRequest {
89
89
  system: string;
90
90
  messages: LlmMessage[];
91
91
  tools: ToolDefinition[];
92
+ /** Explicit Responses text format for Codex subscription inference callers. */
93
+ outputFormat?: {
94
+ type: 'text' | 'json_object';
95
+ } | {
96
+ type: 'json_schema';
97
+ name: string;
98
+ schema: Record<string, unknown>;
99
+ strict?: boolean;
100
+ };
92
101
  /** Router-owned retry plan. Only ZRouter transports put this on the wire. */
93
102
  routePlan?: {
94
103
  fallbacks: string[];
@@ -9,14 +9,16 @@ import { attachMiniZdePane } from '../../zerg/mini_zde_render.js';
9
9
  import { summarizeLoadedManifestYaml, summarizeManifestYaml } from '../../zerg/mini_zde_state.js';
10
10
  /**
11
11
  * Walk upward from `startDir` looking for the repo's bundled headlines
12
- * manifest. Matches `<repo>/zaai/domains/core/headlines/headlines.yaml`.
12
+ * manifest. Matches `<repo>/zerg/zaai/...` and the installed `/app/zaai/...` layout.
13
13
  */
14
14
  function findHeadlinesManifestPath(startDir = process.cwd()) {
15
15
  let current = resolve(startDir);
16
16
  while (true) {
17
- const candidate = join(current, 'zaai', 'domains', 'core', 'headlines', 'headlines.yaml');
18
- if (existsSync(candidate)) {
19
- return candidate;
17
+ for (const suite of [join(current, 'zerg', 'zaai'), join(current, 'zaai')]) {
18
+ const candidate = join(suite, 'domains', 'core', 'headlines', 'headlines.yaml');
19
+ if (existsSync(candidate)) {
20
+ return candidate;
21
+ }
20
22
  }
21
23
  const parent = dirname(current);
22
24
  if (parent === current) {
@@ -171,12 +171,12 @@ export const adaptationRunTool = {
171
171
  ...(args.api_auth_headers ? { api_auth_headers: JSON.parse(args.api_auth_headers) } : {}),
172
172
  };
173
173
  // Derive output_dir (mirrors cli.py) for adaptation.log.
174
- // cli.py sits at zerg/zaai/domains/connector_generation/cli.py; 3 ups from its
175
- // parent reaches zerg/ (the repo root), which has dcaig/ as a direct child
174
+ // cli.py sits at zerg/zaai/domains/connector_generation/cli.py; 4 ups from its
175
+ // parent reaches the monorepo root, which has dcaig/ as a direct child
176
176
  // (matches cli.py's Python-side workspace derivation at cli.py:90-91).
177
177
  const workspace = process.env.ZERG_WORKSPACE_ROOT
178
178
  || (() => {
179
- const derived = resolve(dirname(runnerPath), '..', '..', '..');
179
+ const derived = resolve(dirname(runnerPath), '..', '..', '..', '..');
180
180
  if (!existsSync(resolve(derived, 'dcaig'))) {
181
181
  throw new Error(`Derived workspace ${derived} has no dcaig/ child. Set ZERG_WORKSPACE_ROOT explicitly.`);
182
182
  }
@@ -1829,14 +1829,18 @@ export class MirrorStreamServer {
1829
1829
  const override = process.env.ZERG_CV_STATE_PATH;
1830
1830
  if (override)
1831
1831
  return override;
1832
+ const zaaiRoot = process.env.ZAAI_ROOT;
1833
+ if (zaaiRoot) {
1834
+ return resolve(zaaiRoot, 'domains', 'zcloud', 'modules', 'cv', 'cv_runner', 'state.json');
1835
+ }
1832
1836
  const repoRoot = process.env.ZERG_REPO_ROOT;
1833
1837
  if (repoRoot) {
1834
- return resolve(repoRoot, 'zaai', 'domains', 'zcloud', 'modules', 'cv', 'cv_runner', 'state.json');
1838
+ return resolve(repoRoot, 'zerg', 'zaai', 'domains', 'zcloud', 'modules', 'cv', 'cv_runner', 'state.json');
1835
1839
  }
1836
1840
  // Walk up from this file: ztc/src/web/mirror_stream.ts -> repo root.
1837
1841
  const here = fileURLToPath(import.meta.url);
1838
1842
  const fallbackRoot = resolve(here, '..', '..', '..', '..');
1839
- return resolve(fallbackRoot, 'zaai', 'domains', 'zcloud', 'modules', 'cv', 'cv_runner', 'state.json');
1843
+ return resolve(fallbackRoot, 'zerg', 'zaai', 'domains', 'zcloud', 'modules', 'cv', 'cv_runner', 'state.json');
1840
1844
  }
1841
1845
  /**
1842
1846
  * GET /cv/snapshot — replaces the old Django /api/cv/snapshot endpoint.
@@ -1855,7 +1859,7 @@ export class MirrorStreamServer {
1855
1859
  catch (error) {
1856
1860
  // Missing file with no override env vars set → return empty snapshot.
1857
1861
  // This mirrors the Django endpoint's "soft 200" path for fresh repos.
1858
- const overridden = !!(process.env.ZERG_CV_STATE_PATH || process.env.ZERG_REPO_ROOT);
1862
+ const overridden = !!(process.env.ZERG_CV_STATE_PATH || process.env.ZERG_REPO_ROOT || process.env.ZAAI_ROOT);
1859
1863
  if (!overridden && error.code === 'ENOENT') {
1860
1864
  res.writeHead(200, { 'Content-Type': 'application/json' });
1861
1865
  res.end(JSON.stringify({ schema_version: SCHEMA_VERSION, scenarios: [] }));
@@ -2408,6 +2408,9 @@ var init_codex_auth = __esm({
2408
2408
  stream: true,
2409
2409
  store: false
2410
2410
  };
2411
+ if (request.outputFormat !== void 0) {
2412
+ body.text = { format: request.outputFormat };
2413
+ }
2411
2414
  if (tools.length > 0) {
2412
2415
  body.tools = tools;
2413
2416
  body.tool_choice = "auto";
@@ -12130,7 +12133,7 @@ var invokeSkillTool = makeInvokeSkillTool(resolveAmbientSkills);
12130
12133
  // src/zlm.ts
12131
12134
  var ZLM_EMBED_API_VERSION = "zerglang.ztc-embed/1";
12132
12135
  var ZLM_STDIO_PROTOCOL = "zerglang.ztc-host/1";
12133
- var ZLM_ZTC_VERSION = true ? "0.2.0-canary.1" : "development";
12136
+ var ZLM_ZTC_VERSION = true ? "0.2.0-canary.2" : "development";
12134
12137
  var ZLM_MAX_HOST_TOOLS = 64;
12135
12138
  var ZLM_MAX_SYSTEM_PROMPT_BYTES = 128 * 1024;
12136
12139
  var ZLM_HOST_APPROVAL_CAPABILITIES = Object.freeze([
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+ const { execFile, spawn } = require('node:child_process');
3
+ const { promisify } = require('node:util');
4
+
5
+ // Kept separate so OS process contracts can be exercised independently from the
6
+ // version/cache policy. This is copied alongside the updater in each artifact.
7
+ module.exports = async function runNpm(args, options) {
8
+ if (args[0] === 'prefix') return promisify(execFile)('npm', args, { ...options, encoding: 'utf8', timeout: 5000, maxBuffer: 16384 });
9
+ await new Promise((done, fail) => {
10
+ const child = spawn('npm', args, { ...options, stdio: ['ignore', 'inherit', 'inherit'], shell: false });
11
+ // npm shares the foreground group; a terminal has already signalled it.
12
+ const relay = ![process.stdin, process.stdout, process.stderr].some(stream => stream.isTTY);
13
+ const interrupt = () => { if (relay) child.kill('SIGINT'); };
14
+ const terminate = () => { if (relay) child.kill('SIGTERM'); };
15
+ process.on('SIGINT', interrupt); process.on('SIGTERM', terminate);
16
+ const cleanup = () => { process.off('SIGINT', interrupt); process.off('SIGTERM', terminate); };
17
+ child.on('error', error => { cleanup(); fail(error); });
18
+ child.on('exit', (code, signal) => { cleanup(); if (code === 0) done(); else fail(new Error(`npm exited ${signal || code}`)); });
19
+ });
20
+ return { stdout: '' };
21
+ };
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const { spawn } = require('node:child_process');
4
+ const { join } = require('node:path');
5
+ const { handleUpdate } = require('./update.cjs');
6
+
7
+ async function main() {
8
+ const root = join(__dirname, '..');
9
+ const args = process.argv.slice(2);
10
+ const result = await handleUpdate({ root, args });
11
+ if (result !== null) { process.exitCode = result; return; }
12
+ if (args.length === 1 && ['--help', '-h', 'help'].includes(args[0])) {
13
+ process.stdout.write('Updates: update | --update [--check] [--channel dev|next|latest]\n');
14
+ }
15
+ const metadata = require('../package.json');
16
+ // ZTC's old TUI updaters must not install silently behind this launch policy.
17
+ const env = { ...process.env };
18
+ if (['@zergai/ztc', 'zerg-ztc'].includes(metadata.name)) {
19
+ env.ZTC_NO_AUTO_UPDATE = '1'; env.ZTC_DISABLE_UPDATE = '1';
20
+ }
21
+ const child = spawn(process.execPath, [join(root, metadata.zergCli.entry), ...args], { stdio: 'inherit', env });
22
+ // Attached terminals signal the foreground group, including the child. Relay
23
+ // only for non-TTY supervisors; keep the group intact for terminal resize.
24
+ const relay = ![process.stdin, process.stdout, process.stderr].some(stream => stream.isTTY);
25
+ const interrupt = () => { if (relay) child.kill('SIGINT'); };
26
+ const terminate = () => { if (relay) child.kill('SIGTERM'); };
27
+ process.on('SIGINT', interrupt); process.on('SIGTERM', terminate);
28
+ const cleanup = () => { process.off('SIGINT', interrupt); process.off('SIGTERM', terminate); };
29
+ child.on('error', error => { cleanup(); console.error(`Unable to launch ${metadata.name}: ${error.message}`); process.exitCode = 1; });
30
+ child.on('exit', (code, signal) => {
31
+ cleanup();
32
+ if (signal) process.kill(process.pid, signal);
33
+ else process.exitCode = code ?? 1;
34
+ });
35
+ }
36
+ main().catch(error => { console.error(error.message); process.exitCode = 1; });
@@ -0,0 +1,161 @@
1
+ 'use strict';
2
+
3
+ // Copied into each npm CLI at pack time. No repo paths or runtime dependencies.
4
+ const runNpm = require('./npm-process.cjs');
5
+ const { readFile, realpath, lstat, mkdir, mkdtemp, writeFile, rename, rm } = require('node:fs/promises');
6
+ const { homedir } = require('node:os');
7
+ const { join, resolve, isAbsolute } = require('node:path');
8
+ const registry = 'https://registry.npmjs.org';
9
+ const cacheLifetime = 60 * 60 * 1000;
10
+
11
+ function parseVersion(version) {
12
+ const match = typeof version === 'string' && version.length <= 256 && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(version);
13
+ if (!match || match.slice(1, 4).some(n => !Number.isSafeInteger(Number(n))) || (match[4] || '').split('.').some(n => /^0\d+$/.test(n))) {
14
+ throw new Error('Invalid semantic version');
15
+ }
16
+ return { core: match.slice(1, 4).map(Number), pre: match[4] ? match[4].split('.') : [] };
17
+ }
18
+
19
+ function compareVersions(left, right) {
20
+ const a = parseVersion(left), b = parseVersion(right);
21
+ for (let i = 0; i < 3; i++) if (a.core[i] !== b.core[i]) return a.core[i] > b.core[i] ? 1 : -1;
22
+ if (!a.pre.length && b.pre.length) return 1;
23
+ if (a.pre.length && !b.pre.length) return -1;
24
+ for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
25
+ if (a.pre[i] === b.pre[i]) continue;
26
+ if (a.pre[i] === undefined) return -1;
27
+ if (b.pre[i] === undefined) return 1;
28
+ const an = /^\d+$/.test(a.pre[i]), bn = /^\d+$/.test(b.pre[i]);
29
+ if (an !== bn) return an ? -1 : 1;
30
+ return (an ? BigInt(a.pre[i]) > BigInt(b.pre[i]) : a.pre[i] > b.pre[i]) ? 1 : -1;
31
+ }
32
+ return 0;
33
+ }
34
+
35
+ function releaseChannel(version) {
36
+ const { pre } = parseVersion(version);
37
+ return pre[0] === 'dev' ? 'dev' : pre.length ? 'next' : 'latest';
38
+ }
39
+
40
+ function identity(metadata) {
41
+ if (!/^@zergai\/[a-z0-9][a-z0-9-]*$/.test(metadata.name) && !['zerg-ztc', 'zerg-cyberdeck'].includes(metadata.name)) {
42
+ throw new Error('Unsupported CLI package identity');
43
+ }
44
+ parseVersion(metadata.version);
45
+ }
46
+
47
+ async function checkUpdate(metadata, { fetch = globalThis.fetch, channel = releaseChannel(metadata.version), timeoutMs = 5000 } = {}) {
48
+ identity(metadata);
49
+ if (!['dev', 'next', 'latest'].includes(channel)) throw new Error('Invalid update channel');
50
+ const response = await fetch(`${registry}/${encodeURIComponent(metadata.name)}/${channel}`, {
51
+ headers: { Accept: 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(timeoutMs),
52
+ });
53
+ if (!response.ok) throw new Error(`npm registry check failed (${response.status})`);
54
+ const reader = response.body.getReader();
55
+ const chunks = []; let size = 0;
56
+ try {
57
+ while (true) {
58
+ const { done, value } = await reader.read();
59
+ if (done) break;
60
+ size += value.byteLength;
61
+ if (size > 1024 * 1024) throw new Error('npm registry response too large');
62
+ chunks.push(Buffer.from(value));
63
+ }
64
+ } finally { await reader.cancel(); }
65
+ const data = JSON.parse(Buffer.concat(chunks).toString('utf8'));
66
+ if (data.name !== metadata.name) throw new Error('npm registry package identity mismatch');
67
+ if (releaseChannel(data.version) !== channel) throw new Error('npm registry version does not match the requested channel');
68
+ return { name: metadata.name, current: metadata.version, latest: data.version, channel, hasUpdate: compareVersions(data.version, metadata.version) > 0 };
69
+ }
70
+
71
+ function updateOptions(args) {
72
+ const options = { check: false };
73
+ for (let i = 1; i < args.length; i++) {
74
+ const arg = args[i];
75
+ if (arg === '--check' && !options.check) options.check = true;
76
+ else if (arg === '--channel' && !options.channel && ['dev', 'next', 'latest'].includes(args[i + 1])) options.channel = args[++i];
77
+ else if (['--help', '-h'].includes(arg) && args.length === 2) options.help = true;
78
+ else throw new Error('Usage: update [--check] [--channel dev|next|latest]');
79
+ }
80
+ return options;
81
+ }
82
+
83
+ async function install(root, metadata, info, npm, env) {
84
+ const { stdout } = await npm(['prefix', '--global'], { env });
85
+ const prefix = stdout.trim();
86
+ if (!isAbsolute(prefix)) throw new Error('Cannot identify the global npm installation');
87
+ const expected = join(prefix, 'lib/node_modules', metadata.name);
88
+ // Compare real paths AND reject npm link. Never turn npx/local/checkouts into a
89
+ // global install or update a different Node-version manager's global prefix.
90
+ const matches = await (async () => !(await lstat(expected)).isSymbolicLink() && await realpath(root) === await realpath(expected))().catch(() => false);
91
+ if (!matches) {
92
+ throw new Error('Not this global npm installation. Update using the package manager/prefix that installed this CLI.');
93
+ }
94
+ await npm(['install', '--global', '--prefix', prefix, `--registry=${registry}`, `--@zergai:registry=${registry}`, '--no-audit', '--no-fund', `${metadata.name}@${info.latest}`], { cwd: prefix, env });
95
+ const installed = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
96
+ if (installed.name !== metadata.name || installed.version !== info.latest) throw new Error('npm did not install the requested package version');
97
+ }
98
+
99
+ async function startupCheck(metadata, env, fetch) {
100
+ const channel = releaseChannel(metadata.version);
101
+ const directory = join(env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'zergai-cli-updates');
102
+ const path = join(directory, `${encodeURIComponent(metadata.name)}-${channel}.json`);
103
+ try {
104
+ const cached = JSON.parse(await readFile(path, 'utf8'));
105
+ const age = Date.now() - cached.checkedAt;
106
+ if (Number.isFinite(age) && age >= 0 && age < cacheLifetime && cached.current === metadata.version) {
107
+ if (cached.latest === null) return null;
108
+ if (releaseChannel(cached.latest) === channel) return { latest: cached.latest, hasUpdate: compareVersions(cached.latest, metadata.version) > 0 };
109
+ }
110
+ } catch { /* Missing, stale, or malformed cache: do a bounded public check. */ }
111
+ let info = null;
112
+ try { info = await checkUpdate(metadata, { fetch, timeoutMs: 800 }); }
113
+ catch { /* A registry outage must not prevent use of a CLI. */ }
114
+ let staging;
115
+ try {
116
+ await mkdir(directory, { recursive: true, mode: 0o700 });
117
+ staging = await mkdtemp(join(directory, '.check-'));
118
+ const source = join(staging, 'record');
119
+ await writeFile(source, JSON.stringify({ current: metadata.version, latest: info?.latest ?? null, checkedAt: Date.now() }), { flag: 'wx', mode: 0o600 });
120
+ await rename(source, path);
121
+ } catch { /* Read-only home/cache is supported. */ }
122
+ finally { if (staging) await rm(staging, { recursive: true, force: true }).catch(() => {}); }
123
+ return info;
124
+ }
125
+
126
+ async function handleUpdate({ root, args, env = process.env, stdout = process.stdout, stderr = process.stderr, fetch = globalThis.fetch, npm = runNpm }) {
127
+ const explicit = ['update', '--update'].includes(args[0]);
128
+ if (!explicit && (env.CI || env.ZERGAI_NO_UPDATE_CHECK === '1' || !stderr.isTTY || args.some(arg => ['help', '--help', '-h', 'version', '--version', '-V'].includes(arg)))) return null;
129
+ let options;
130
+ try { options = explicit ? updateOptions(args) : {}; }
131
+ catch (error) { stderr.write(`${error.message}\n`); return 2; }
132
+ if (options.help) {
133
+ stdout.write('Check npm and update this globally installed CLI\nUsage: update | --update [--check] [--channel dev|next|latest]\n--check prints JSON without installing. Channels are preserved by default.\nStartup only notifies; set ZERGAI_NO_UPDATE_CHECK=1 to disable checks.\n');
134
+ return 0;
135
+ }
136
+ try {
137
+ const metadata = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
138
+ identity(metadata);
139
+ if (!explicit) {
140
+ const info = await startupCheck(metadata, env, fetch);
141
+ if (info?.hasUpdate) stderr.write(`Update available for ${metadata.name}: ${metadata.version} → ${info.latest}. Run ${Object.keys(metadata.bin || {})[0] || metadata.name} update.\n`);
142
+ return null;
143
+ }
144
+ // Explicit checks/install always consult the registry; cache is advisory only.
145
+ const info = await checkUpdate(metadata, { fetch, ...options });
146
+ if (options.check) stdout.write(`${JSON.stringify(info)}\n`);
147
+ else if (!info.hasUpdate) stderr.write(`No newer ${info.channel} release of ${metadata.name} (installed ${metadata.version}).\n`);
148
+ else {
149
+ stderr.write(`Updating ${metadata.name}: ${metadata.version} → ${info.latest} (${info.channel})…\n`);
150
+ await install(resolve(root), metadata, info, npm, env);
151
+ stderr.write(`Updated ${metadata.name} to ${info.latest}. The next launch uses the new version.\n`);
152
+ }
153
+ return 0;
154
+ } catch (error) {
155
+ if (!explicit) return null;
156
+ stderr.write(`Update failed: ${error.message}\n`);
157
+ return 1;
158
+ }
159
+ }
160
+
161
+ module.exports = { compareVersions, releaseChannel, checkUpdate, handleUpdate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zergai/ztc",
3
- "version": "0.2.0-canary.1",
3
+ "version": "0.2.0-canary.2",
4
4
  "description": "Zerg Terminal Client - CLI agent for continual AI systems",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -38,9 +38,12 @@
38
38
  "falkordblite"
39
39
  ]
40
40
  },
41
+ "zergCli": {
42
+ "entry": "dist/cli_main.js"
43
+ },
41
44
  "bin": {
42
- "ztc": "dist/cli_main.js",
43
- "zerg": "dist/cli_main.js"
45
+ "ztc": "npm-cli/run.cjs",
46
+ "zerg": "npm-cli/run.cjs"
44
47
  },
45
48
  "exports": {
46
49
  ".": {
@@ -129,7 +132,7 @@
129
132
  "package:prepare": "./scripts/prepare-package.sh",
130
133
  "pack:dry": "npm pack --dry-run --cache ${NPM_CONFIG_CACHE:-/tmp/ztc-npm-cache}",
131
134
  "pack:tarball": "npm pack --cache ${NPM_CONFIG_CACHE:-/tmp/ztc-npm-cache}",
132
- "prepack": "npm run package:prepare",
135
+ "prepack": "npm run package:prepare && node ../scripts/npm/prepare-cli.mjs",
133
136
  "predev": "npm run clean",
134
137
  "dev": "npm run build:rust && tsx src/cli_main.ts",
135
138
  "dev:ink": "tsx src/cli.tsx",
@@ -161,6 +164,7 @@
161
164
  "release:preflight-governance": "node scripts/preflight-release-governance.mjs"
162
165
  },
163
166
  "files": [
167
+ "npm-cli/*.cjs",
164
168
  "LICENSE.md",
165
169
  "COMMERCIAL-LICENSING.md",
166
170
  "NOTICE",
@@ -185,7 +189,6 @@
185
189
  "!packages/sim-swarm/dist/**/*.map"
186
190
  ],
187
191
  "bundleDependencies": [
188
- "@ztc/sim-graph",
189
192
  "@ztc/sim-swarm",
190
193
  "exceljs",
191
194
  "pptxgenjs"
@@ -1 +0,0 @@
1
- export * from './sim_graph.js';
@@ -1 +0,0 @@
1
- export * from './sim_graph.js';