@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 +19 -0
- package/lib/settings.js +28 -0
- package/package.json +1 -1
- package/src/claude-task-runner.js +6 -1
- package/src/preflight.js +50 -13
- package/task-lib/attachable-watcher.js +7 -2
- package/task-lib/watcher.js +9 -1
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
|
@@ -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
|
-
|
|
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
|
|
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: '
|
|
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
|
|
299
|
+
'Claude command not available',
|
|
286
300
|
claude.error,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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:
|
|
131
|
-
args:
|
|
135
|
+
command: claudeCommand,
|
|
136
|
+
args: finalArgs,
|
|
132
137
|
cwd,
|
|
133
138
|
env,
|
|
134
139
|
cols: 120,
|
package/task-lib/watcher.js
CHANGED
|
@@ -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(
|
|
47
|
+
const child = spawn(claudeCommand, finalArgs, {
|
|
40
48
|
cwd,
|
|
41
49
|
env,
|
|
42
50
|
stdio: ['ignore', 'pipe', 'pipe'],
|