@covibes/zeroshot 5.0.0 → 5.2.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/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # [5.2.0](https://github.com/covibes/zeroshot/compare/v5.1.0...v5.2.0) (2026-01-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **test:** add retry logic to e2e-claude-command tests ([ed52c86](https://github.com/covibes/zeroshot/commit/ed52c86f86180da02a37616018797acaa58aa764))
7
+
8
+
9
+ ### Features
10
+
11
+ * add claudeCommand setting for custom Claude CLI ([#38](https://github.com/covibes/zeroshot/issues/38)) ([6e1a140](https://github.com/covibes/zeroshot/commit/6e1a140e75b2c37d2a071b8afd853656ba5e12f7)), closes [#37](https://github.com/covibes/zeroshot/issues/37)
12
+
13
+ # [5.1.0](https://github.com/covibes/zeroshot/compare/v5.0.0...v5.1.0) (2026-01-07)
14
+
15
+
16
+ ### Features
17
+
18
+ * add preflight check for root user ([802f0f4](https://github.com/covibes/zeroshot/commit/802f0f41e27c1aa585dec31217fa2aeb3c9ba3db))
19
+
1
20
  # [5.0.0](https://github.com/covibes/zeroshot/compare/v4.2.0...v5.0.0) (2026-01-07)
2
21
 
3
22
 
package/lib/settings.js CHANGED
@@ -66,6 +66,9 @@ const DEFAULT_SETTINGS = {
66
66
  autoCheckUpdates: true, // Check npm registry for newer versions
67
67
  lastUpdateCheckAt: null, // Unix timestamp of last check (null = never checked)
68
68
  lastSeenVersion: null, // Don't re-prompt for same version
69
+ // Claude command - customize how to invoke Claude CLI (default: 'claude')
70
+ // Example: 'ccr code' for claude-code-router integration
71
+ claudeCommand: 'claude',
69
72
  // Docker isolation mounts - preset names or {host, container, readonly?} objects
70
73
  // Valid presets: gh, git, ssh, aws, azure, kube, terraform, gcloud
71
74
  dockerMounts: ['gh', 'git', 'ssh'],
@@ -123,6 +126,15 @@ function validateSetting(key, value) {
123
126
  return `Invalid log level: ${value}. Valid levels: quiet, normal, verbose`;
124
127
  }
125
128
 
129
+ if (key === 'claudeCommand') {
130
+ if (typeof value !== 'string') {
131
+ return 'claudeCommand must be a string';
132
+ }
133
+ if (value.trim().length === 0) {
134
+ return 'claudeCommand cannot be empty';
135
+ }
136
+ }
137
+
126
138
  if (key === 'dockerMounts') {
127
139
  return validateMountConfig(value);
128
140
  }
@@ -174,6 +186,21 @@ function coerceValue(key, value) {
174
186
  return value;
175
187
  }
176
188
 
189
+ /**
190
+ * Get parsed Claude command from settings/env
191
+ * Supports space-separated commands like 'ccr code'
192
+ * @returns {{ command: string, args: string[] }}
193
+ */
194
+ function getClaudeCommand() {
195
+ const settings = loadSettings();
196
+ const raw = process.env.ZEROSHOT_CLAUDE_COMMAND || settings.claudeCommand || 'claude';
197
+ const parts = raw.trim().split(/\s+/);
198
+ return {
199
+ command: parts[0],
200
+ args: parts.slice(1),
201
+ };
202
+ }
203
+
177
204
  module.exports = {
178
205
  loadSettings,
179
206
  saveSettings,
@@ -181,6 +208,7 @@ module.exports = {
181
208
  coerceValue,
182
209
  DEFAULT_SETTINGS,
183
210
  getSettingsFile,
211
+ getClaudeCommand,
184
212
  // Model validation exports
185
213
  MODEL_HIERARCHY,
186
214
  VALID_MODELS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@covibes/zeroshot",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "Multi-agent orchestration engine for Claude - cluster coordinator and CLI",
5
5
  "main": "src/orchestrator.js",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  const { spawn, exec, execSync } = require('child_process');
9
9
  const fs = require('fs');
10
10
  const TaskRunner = require('./task-runner');
11
+ const { getClaudeCommand } = require('../lib/settings');
11
12
 
12
13
  class ClaudeTaskRunner extends TaskRunner {
13
14
  /**
@@ -383,8 +384,12 @@ class ClaudeTaskRunner extends TaskRunner {
383
384
  ? 'stream-json'
384
385
  : desiredOutputFormat;
385
386
 
387
+ // Get configured Claude command (supports custom commands like 'ccr code')
388
+ const { command: claudeCmd, args: claudeExtraArgs } = getClaudeCommand();
389
+
386
390
  const command = [
387
- 'claude',
391
+ claudeCmd,
392
+ ...claudeExtraArgs,
388
393
  '--print',
389
394
  '--dangerously-skip-permissions',
390
395
  '--output-format',
package/src/preflight.js CHANGED
@@ -57,11 +57,19 @@ function commandExists(cmd) {
57
57
 
58
58
  /**
59
59
  * Get Claude CLI version
60
+ * @param {string} claudeCommand - Optional custom Claude command (e.g., 'ccr code')
60
61
  * @returns {{ installed: boolean, version: string | null, error: string | null }}
61
62
  */
62
- function getClaudeVersion() {
63
+ function getClaudeVersion(claudeCommand = 'claude') {
64
+ // Parse command parts
65
+ const parts = claudeCommand.trim().split(/\s+/);
66
+ const command = parts[0];
67
+ const extraArgs = parts.slice(1);
68
+
63
69
  try {
64
- const output = execSync('claude --version', { encoding: 'utf8', stdio: 'pipe' });
70
+ const versionArgs = [...extraArgs, '--version'];
71
+ const versionCmd = [command, ...versionArgs].join(' ');
72
+ const output = execSync(versionCmd, { encoding: 'utf8', stdio: 'pipe' });
65
73
  const match = output.match(/(\d+\.\d+\.\d+)/);
66
74
  return {
67
75
  installed: true,
@@ -73,7 +81,7 @@ function getClaudeVersion() {
73
81
  return {
74
82
  installed: false,
75
83
  version: null,
76
- error: 'Claude CLI not installed',
84
+ error: `Command '${command}' not installed`,
77
85
  };
78
86
  }
79
87
  return {
@@ -271,24 +279,37 @@ function checkDocker() {
271
279
  * @param {boolean} options.requireDocker - Whether Docker is required (true if using --docker)
272
280
  * @param {boolean} options.requireGit - Whether git repo is required (true if using --worktree)
273
281
  * @param {boolean} options.quiet - Suppress success messages
282
+ * @param {string} options.claudeCommand - Custom Claude command (from settings)
274
283
  * @returns {ValidationResult}
275
284
  */
276
285
  function runPreflight(options = {}) {
277
286
  const errors = [];
278
287
  const warnings = [];
279
288
 
289
+ // Get configured Claude command (supports custom commands like 'ccr code')
290
+ const { getClaudeCommand } = require('../lib/settings.js');
291
+ const { command, args } = getClaudeCommand();
292
+ const claudeCommand = options.claudeCommand || [command, ...args].join(' ');
293
+
280
294
  // 1. Check Claude CLI installation
281
- const claude = getClaudeVersion();
295
+ const claude = getClaudeVersion(claudeCommand);
282
296
  if (!claude.installed) {
283
297
  errors.push(
284
298
  formatError(
285
- 'Claude CLI not installed',
299
+ 'Claude command not available',
286
300
  claude.error,
287
- [
288
- 'Install Claude CLI: npm install -g @anthropic-ai/claude-code',
289
- 'Or: brew install claude (macOS)',
290
- 'Then run: claude --version',
291
- ]
301
+ claudeCommand === 'claude'
302
+ ? [
303
+ 'Install Claude CLI: npm install -g @anthropic-ai/claude-code',
304
+ 'Or: brew install claude (macOS)',
305
+ 'Then run: claude --version',
306
+ ]
307
+ : [
308
+ `Command '${claudeCommand}' not found`,
309
+ 'Check settings: zeroshot settings',
310
+ 'Update claudeCommand: zeroshot settings set claudeCommand "your-command"',
311
+ 'Or install the missing command',
312
+ ]
292
313
  )
293
314
  );
294
315
  } else {
@@ -319,7 +340,23 @@ function runPreflight(options = {}) {
319
340
  }
320
341
  }
321
342
 
322
- // 3. Check gh CLI (if required)
343
+ // 3. Check if running as root (blocks --dangerously-skip-permissions)
344
+ if (process.getuid && process.getuid() === 0) {
345
+ errors.push(
346
+ formatError(
347
+ 'Running as root',
348
+ 'Claude CLI refuses --dangerously-skip-permissions flag when running as root (UID 0)',
349
+ [
350
+ 'Run as non-root user in Docker: docker run --user 1000:1000 ...',
351
+ 'Or create non-root user: adduser testuser && su - testuser',
352
+ 'Or use existing node user: docker run --user node ...',
353
+ 'Security: Claude CLI blocks this flag as root to prevent privilege escalation',
354
+ ]
355
+ )
356
+ );
357
+ }
358
+
359
+ // 4. Check gh CLI (if required)
323
360
  if (options.requireGh) {
324
361
  const gh = checkGhAuth();
325
362
  if (!gh.installed) {
@@ -348,7 +385,7 @@ function runPreflight(options = {}) {
348
385
  }
349
386
  }
350
387
 
351
- // 4. Check Docker (if required)
388
+ // 5. Check Docker (if required)
352
389
  if (options.requireDocker) {
353
390
  const docker = checkDocker();
354
391
  if (!docker.available) {
@@ -367,7 +404,7 @@ function runPreflight(options = {}) {
367
404
  }
368
405
  }
369
406
 
370
- // 5. Check git repo (if required for worktree isolation)
407
+ // 6. Check git repo (if required for worktree isolation)
371
408
  if (options.requireGit) {
372
409
  let isGitRepo = false;
373
410
  try {
@@ -84,6 +84,7 @@ process.on('unhandledRejection', (reason) => {
84
84
  import { createRequire } from 'module';
85
85
  const require = createRequire(import.meta.url);
86
86
  const { AttachServer } = require('../src/attach');
87
+ const { getClaudeCommand } = require('../lib/settings.js');
87
88
 
88
89
  // Use the args parsed earlier (during error handler setup)
89
90
  const taskId = taskIdArg;
@@ -115,6 +116,10 @@ if (model && !claudeArgs.includes('--model')) {
115
116
  claudeArgs.unshift('--model', model);
116
117
  }
117
118
 
119
+ // Get configured Claude command (supports custom commands like 'ccr code')
120
+ const { command: claudeCommand, args: claudeExtraArgs } = getClaudeCommand();
121
+ const finalArgs = [...claudeExtraArgs, ...claudeArgs];
122
+
118
123
  // For JSON schema output with silent mode, track final result
119
124
  const silentJsonMode =
120
125
  config.outputFormat === 'json' && config.jsonSchema && config.silentJsonOutput;
@@ -127,8 +132,8 @@ let outputBuffer = '';
127
132
  const server = new AttachServer({
128
133
  id: taskId,
129
134
  socketPath,
130
- command: 'claude',
131
- args: claudeArgs,
135
+ command: claudeCommand,
136
+ args: finalArgs,
132
137
  cwd,
133
138
  env,
134
139
  cols: 120,
@@ -13,6 +13,10 @@ import { appendFileSync } from 'fs';
13
13
  import { dirname } from 'path';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { updateTask } from './store.js';
16
+ import { createRequire } from 'module';
17
+
18
+ const require = createRequire(import.meta.url);
19
+ const { getClaudeCommand } = require('../lib/settings.js');
16
20
 
17
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
18
22
 
@@ -34,9 +38,13 @@ if (model && !claudeArgs.includes('--model')) {
34
38
  claudeArgs.unshift('--model', model);
35
39
  }
36
40
 
41
+ // Get configured Claude command (supports custom commands like 'ccr code')
42
+ const { command: claudeCommand, args: claudeExtraArgs } = getClaudeCommand();
43
+ const finalArgs = [...claudeExtraArgs, ...claudeArgs];
44
+
37
45
  // Spawn claude using regular child_process (not PTY)
38
46
  // --print mode is non-interactive, PTY adds overhead and causes EIO on OOM
39
- const child = spawn('claude', claudeArgs, {
47
+ const child = spawn(claudeCommand, finalArgs, {
40
48
  cwd,
41
49
  env,
42
50
  stdio: ['ignore', 'pipe', 'pipe'],