@the-open-engine/zeroshot 6.0.1 → 6.0.3

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/cli/index.js CHANGED
@@ -67,7 +67,7 @@ const {
67
67
  registerDetachedSetupCluster,
68
68
  } = require('../lib/detached-startup');
69
69
  // Setup wizard removed - use: zeroshot settings set <key> <value>
70
- const { checkForUpdates } = require('./lib/update-checker');
70
+ const { checkForUpdates, printLegacyDistroNotice } = require('./lib/update-checker');
71
71
  const { StatusFooter, AGENT_STATE, ACTIVE_STATES } = require('../src/status-footer');
72
72
 
73
73
  // =============================================================================
@@ -5387,13 +5387,15 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
5387
5387
 
5388
5388
  // Main async entry point
5389
5389
  async function main() {
5390
- const isQuiet =
5391
- process.argv.includes('-q') ||
5392
- process.argv.includes('--quiet') ||
5393
- process.env.NODE_ENV === 'test';
5390
+ const isTest = process.env.NODE_ENV === 'test';
5391
+ const isQuiet = process.argv.includes('-q') || process.argv.includes('--quiet') || isTest;
5392
+
5393
+ printLegacyDistroNotice();
5394
5394
 
5395
5395
  // Check for updates (non-blocking if offline)
5396
- await checkForUpdates({ quiet: isQuiet });
5396
+ if (!isTest) {
5397
+ await checkForUpdates({ quiet: isQuiet });
5398
+ }
5397
5399
 
5398
5400
  let args = process.argv.slice(2);
5399
5401
 
@@ -9,10 +9,16 @@
9
9
  */
10
10
 
11
11
  const https = require('https');
12
- const { spawn } = require('child_process');
12
+ const childProcess = require('child_process');
13
+ const fs = require('fs');
14
+ const path = require('path');
13
15
  const readline = require('readline');
14
16
  const { loadSettings, saveSettings } = require('../../lib/settings');
15
17
 
18
+ const NEW_PACKAGE_NAME = '@the-open-engine/zeroshot';
19
+ const LEGACY_PACKAGE_NAME = '@covibes/zeroshot';
20
+ const NEW_PACKAGE_SPEC = `${NEW_PACKAGE_NAME}@latest`;
21
+
16
22
  // 24 hours in milliseconds
17
23
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
18
24
 
@@ -20,15 +26,177 @@ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
20
26
  const FETCH_TIMEOUT_MS = 5000;
21
27
 
22
28
  // npm registry URL
23
- const REGISTRY_URL = 'https://registry.npmjs.org/@the-open-engine/zeroshot/latest';
29
+ const REGISTRY_URL = `https://registry.npmjs.org/${NEW_PACKAGE_NAME}/latest`;
30
+
31
+ function getPackageMetadata() {
32
+ return require('../../package.json');
33
+ }
34
+
35
+ function getCurrentPackageName() {
36
+ return getPackageMetadata().name || NEW_PACKAGE_NAME;
37
+ }
24
38
 
25
39
  /**
26
40
  * Get current package version
27
41
  * @returns {string}
28
42
  */
29
43
  function getCurrentVersion() {
30
- const pkg = require('../../package.json');
31
- return pkg.version;
44
+ return getPackageMetadata().version;
45
+ }
46
+
47
+ function isLegacyDistro(packageName = getCurrentPackageName()) {
48
+ return packageName === LEGACY_PACKAGE_NAME;
49
+ }
50
+
51
+ function printLegacyDistroNotice(packageName = getCurrentPackageName()) {
52
+ if (!isLegacyDistro(packageName)) {
53
+ return false;
54
+ }
55
+
56
+ console.error(
57
+ `\n⚠️ ${LEGACY_PACKAGE_NAME} has moved to ${NEW_PACKAGE_NAME}. ` +
58
+ 'Run `zeroshot update` to switch this installation.\n'
59
+ );
60
+ return true;
61
+ }
62
+
63
+ function getPackageRoot() {
64
+ return path.dirname(require.resolve('../../package.json'));
65
+ }
66
+
67
+ function hasPathSuffix(parts, suffix) {
68
+ if (suffix.length > parts.length) {
69
+ return false;
70
+ }
71
+
72
+ const start = parts.length - suffix.length;
73
+ return suffix.every((part, index) => parts[start + index] === part);
74
+ }
75
+
76
+ function joinPathParts(parts) {
77
+ const joined = parts.join(path.sep);
78
+ return joined === '' ? path.parse(process.cwd()).root : joined;
79
+ }
80
+
81
+ function deriveInstallPrefixFromPackageRoot(packageRoot, packageName) {
82
+ const parts = path.resolve(packageRoot).split(path.sep);
83
+ const packageParts = packageName.split('/');
84
+
85
+ if (!hasPathSuffix(parts, packageParts)) {
86
+ return null;
87
+ }
88
+
89
+ const nodeModulesIndex = parts.length - packageParts.length - 1;
90
+ if (nodeModulesIndex < 0 || parts[nodeModulesIndex] !== 'node_modules') {
91
+ return null;
92
+ }
93
+
94
+ if (parts[nodeModulesIndex - 1] === 'lib') {
95
+ return joinPathParts(parts.slice(0, nodeModulesIndex - 1));
96
+ }
97
+
98
+ return joinPathParts(parts.slice(0, nodeModulesIndex));
99
+ }
100
+
101
+ function resolveNpmCommand(installPrefix = null) {
102
+ const npmName = process.platform === 'win32' ? 'npm.cmd' : 'npm';
103
+ const candidates = [];
104
+
105
+ if (installPrefix) {
106
+ candidates.push(path.join(installPrefix, 'bin', npmName));
107
+ }
108
+
109
+ candidates.push(path.join(path.dirname(process.execPath), npmName));
110
+
111
+ for (const candidate of candidates) {
112
+ if (fs.existsSync(candidate)) {
113
+ return candidate;
114
+ }
115
+ }
116
+
117
+ return npmName;
118
+ }
119
+
120
+ function getNpmConfiguredPrefix(npmCommand = resolveNpmCommand()) {
121
+ return childProcess
122
+ .execFileSync(npmCommand, ['config', 'get', 'prefix'], {
123
+ encoding: 'utf8',
124
+ })
125
+ .trim();
126
+ }
127
+
128
+ function getInstallPrefix(options = {}) {
129
+ if (options.installPrefix) {
130
+ return options.installPrefix;
131
+ }
132
+
133
+ const packageName = options.packageName || getCurrentPackageName();
134
+ const packageRoot = options.packageRoot || getPackageRoot();
135
+ const derivedPrefix = deriveInstallPrefixFromPackageRoot(packageRoot, packageName);
136
+
137
+ if (derivedPrefix) {
138
+ return derivedPrefix;
139
+ }
140
+
141
+ return getNpmConfiguredPrefix(options.npmCommand);
142
+ }
143
+
144
+ function getGlobalModulesDir(installPrefix) {
145
+ const unixGlobalModulesDir = path.join(installPrefix, 'lib', 'node_modules');
146
+ if (fs.existsSync(unixGlobalModulesDir)) {
147
+ return unixGlobalModulesDir;
148
+ }
149
+
150
+ return path.join(installPrefix, 'node_modules');
151
+ }
152
+
153
+ function shellQuote(value) {
154
+ if (/^[A-Za-z0-9_./:@+-]+$/.test(value)) {
155
+ return value;
156
+ }
157
+
158
+ return `'${value.replace(/'/g, "'\\''")}'`;
159
+ }
160
+
161
+ function buildManualInstallCommand(installPrefix = null, useSudo = false) {
162
+ const command = [
163
+ useSudo ? 'sudo' : null,
164
+ 'npm',
165
+ 'install',
166
+ '-g',
167
+ installPrefix ? '--prefix' : null,
168
+ installPrefix ? shellQuote(installPrefix) : null,
169
+ NEW_PACKAGE_SPEC,
170
+ ].filter(Boolean);
171
+
172
+ return command.join(' ');
173
+ }
174
+
175
+ function getUpdateTarget(options = {}) {
176
+ const packageName = options.packageName || getCurrentPackageName();
177
+ const legacy = isLegacyDistro(packageName);
178
+ const installPrefix = getInstallPrefix(options);
179
+ const npmCommand = options.npmCommand || resolveNpmCommand(installPrefix);
180
+
181
+ return {
182
+ packageName,
183
+ legacy,
184
+ installPrefix,
185
+ npmCommand,
186
+ globalModulesDir: getGlobalModulesDir(installPrefix),
187
+ };
188
+ }
189
+
190
+ function buildInstallArgs(updateTarget) {
191
+ const args = ['install', '-g', '--prefix', updateTarget.installPrefix];
192
+
193
+ if (updateTarget.legacy) {
194
+ // npm refuses to replace the legacy package's `zeroshot` bin without this.
195
+ args.push('--force');
196
+ }
197
+
198
+ args.push(NEW_PACKAGE_SPEC);
199
+ return args;
32
200
  }
33
201
 
34
202
  /**
@@ -119,17 +287,10 @@ function promptForUpdate(currentVersion, latestVersion) {
119
287
  * Check if we have write permission to npm global directory
120
288
  * @returns {boolean} True if we can write to npm global prefix
121
289
  */
122
- function canWriteToNpmGlobal() {
123
- const { execSync } = require('child_process');
124
- const fs = require('fs');
125
-
290
+ function canWriteToNpmGlobal(options = {}) {
126
291
  try {
127
- // Get npm global prefix (e.g., /usr/lib or /home/user/.nvm/versions/node/...)
128
- const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
129
- const globalModulesDir = require('path').join(prefix, 'lib', 'node_modules');
130
-
131
- // Check if directory exists and is writable
132
- fs.accessSync(globalModulesDir, fs.constants.W_OK);
292
+ const updateTarget = getUpdateTarget(options);
293
+ fs.accessSync(updateTarget.globalModulesDir, fs.constants.W_OK);
133
294
  return true;
134
295
  } catch {
135
296
  return false;
@@ -140,22 +301,32 @@ function canWriteToNpmGlobal() {
140
301
  * Run npm install to update the package
141
302
  * @returns {Promise<boolean>} True if update succeeded
142
303
  */
143
- function runUpdate() {
304
+ function runUpdate(options = {}) {
144
305
  return new Promise((resolve) => {
306
+ let updateTarget;
307
+ try {
308
+ updateTarget = getUpdateTarget(options);
309
+ } catch {
310
+ console.log('❌ Update failed. Try manually:');
311
+ console.log(` ${buildManualInstallCommand()}\n`);
312
+ resolve(false);
313
+ return;
314
+ }
315
+
145
316
  // Check permissions BEFORE attempting update
146
- if (!canWriteToNpmGlobal()) {
317
+ if (!canWriteToNpmGlobal(options)) {
147
318
  console.log('\n⚠️ Cannot auto-update: no write permission to npm global directory.');
148
319
  console.log(' Run manually with sudo:');
149
- console.log(' sudo npm install -g @the-open-engine/zeroshot@latest\n');
320
+ console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
150
321
  resolve(false);
151
322
  return;
152
323
  }
153
324
 
154
325
  console.log('\n📥 Installing update...');
155
326
 
156
- const proc = spawn('npm', ['install', '-g', '@the-open-engine/zeroshot@latest'], {
327
+ const proc = childProcess.spawn(updateTarget.npmCommand, buildInstallArgs(updateTarget), {
157
328
  stdio: 'inherit',
158
- shell: true,
329
+ shell: false,
159
330
  });
160
331
 
161
332
  proc.on('close', (code) => {
@@ -165,14 +336,14 @@ function runUpdate() {
165
336
  resolve(true);
166
337
  } else {
167
338
  console.log('❌ Update failed. Try manually:');
168
- console.log(' sudo npm install -g @the-open-engine/zeroshot@latest\n');
339
+ console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
169
340
  resolve(false);
170
341
  }
171
342
  });
172
343
 
173
344
  proc.on('error', () => {
174
345
  console.log('❌ Update failed. Try manually:');
175
- console.log(' sudo npm install -g @the-open-engine/zeroshot@latest\n');
346
+ console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
176
347
  resolve(false);
177
348
  });
178
349
  });
@@ -246,9 +417,9 @@ async function checkForUpdates(options = {}) {
246
417
  if (options.quiet) {
247
418
  console.log(`📦 Update available: ${currentVersion} → ${latestVersion}`);
248
419
  if (hasWriteAccess) {
249
- console.log(' Run: npm install -g @the-open-engine/zeroshot@latest\n');
420
+ console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), false)}\n`);
250
421
  } else {
251
- console.log(' Run: sudo npm install -g @the-open-engine/zeroshot@latest\n');
422
+ console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), true)}\n`);
252
423
  }
253
424
  return;
254
425
  }
@@ -257,7 +428,7 @@ async function checkForUpdates(options = {}) {
257
428
  // (they'd say yes then get an error, which is frustrating UX)
258
429
  if (!hasWriteAccess) {
259
430
  console.log(`\n📦 Update available: ${currentVersion} → ${latestVersion}`);
260
- console.log(' Run: sudo npm install -g @the-open-engine/zeroshot@latest\n');
431
+ console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), true)}\n`);
261
432
  return;
262
433
  }
263
434
 
@@ -271,7 +442,17 @@ async function checkForUpdates(options = {}) {
271
442
  module.exports = {
272
443
  checkForUpdates,
273
444
  // Exported for testing and CLI update command
445
+ NEW_PACKAGE_NAME,
446
+ LEGACY_PACKAGE_NAME,
274
447
  getCurrentVersion,
448
+ getCurrentPackageName,
449
+ isLegacyDistro,
450
+ printLegacyDistroNotice,
451
+ deriveInstallPrefixFromPackageRoot,
452
+ getInstallPrefix,
453
+ resolveNpmCommand,
454
+ getUpdateTarget,
455
+ buildInstallArgs,
275
456
  isNewerVersion,
276
457
  fetchLatestVersion,
277
458
  runUpdate,
@@ -7,8 +7,9 @@ function commandExists(command) {
7
7
  if (command.includes(path.sep)) {
8
8
  return fs.existsSync(command);
9
9
  }
10
+ const probe = process.platform === 'win32' ? `where ${command}` : `command -v ${command}`;
10
11
  try {
11
- execSync(`command -v ${command}`, { stdio: 'pipe' });
12
+ execSync(probe, { stdio: 'pipe' });
12
13
  return true;
13
14
  } catch {
14
15
  return false;
@@ -20,9 +21,11 @@ function getCommandPath(command) {
20
21
  if (command.includes(path.sep)) {
21
22
  return fs.existsSync(command) ? command : null;
22
23
  }
24
+ const probe = process.platform === 'win32' ? `where ${command}` : `command -v ${command}`;
23
25
  try {
24
- const output = execSync(`command -v ${command}`, { encoding: 'utf8', stdio: 'pipe' });
25
- return output.trim() || null;
26
+ const output = execSync(probe, { encoding: 'utf8', stdio: 'pipe' });
27
+ // `where` can return multiple matches (one per line); take the first.
28
+ return output.split(/\r?\n/)[0].trim() || null;
26
29
  } catch {
27
30
  return null;
28
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@the-open-engine/zeroshot",
3
- "version": "6.0.1",
3
+ "version": "6.0.3",
4
4
  "description": "Multi-agent orchestration engine for Claude, Codex, and Gemini",
5
5
  "main": "src/orchestrator.js",
6
6
  "bin": {