@covibes/zeroshot 5.3.0 → 5.4.1
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/LICENSE +1 -1
- package/README.md +8 -385
- package/cli/index.js +108 -4615
- package/package.json +16 -125
- package/CHANGELOG.md +0 -537
- package/cli/commands/providers.js +0 -150
- package/cli/lib/first-run.js +0 -211
- package/cli/lib/update-checker.js +0 -281
- package/cli/message-formatter-utils.js +0 -75
- package/cli/message-formatters-normal.js +0 -253
- package/cli/message-formatters-watch.js +0 -181
- package/cluster-templates/base-templates/debug-workflow.json +0 -375
- package/cluster-templates/base-templates/full-workflow.json +0 -697
- package/cluster-templates/base-templates/single-worker.json +0 -63
- package/cluster-templates/base-templates/worker-validator.json +0 -152
- package/cluster-templates/conductor-bootstrap.json +0 -124
- package/docker/zeroshot-cluster/Dockerfile +0 -152
- package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
- package/lib/completion.js +0 -174
- package/lib/docker-config.js +0 -212
- package/lib/id-detector.js +0 -53
- package/lib/provider-detection.js +0 -59
- package/lib/provider-names.js +0 -56
- package/lib/settings.js +0 -405
- package/lib/stream-json-parser.js +0 -10
- package/scripts/fix-node-pty-permissions.js +0 -75
- package/scripts/record-demo.sh +0 -279
- package/scripts/test-install.sh +0 -40
- package/scripts/validate-templates.js +0 -100
- package/src/agent/agent-config.js +0 -193
- package/src/agent/agent-context-builder.js +0 -335
- package/src/agent/agent-hook-executor.js +0 -402
- package/src/agent/agent-lifecycle.js +0 -683
- package/src/agent/agent-stuck-detector.js +0 -256
- package/src/agent/agent-task-executor.js +0 -1431
- package/src/agent/agent-trigger-evaluator.js +0 -67
- package/src/agent/output-extraction.js +0 -200
- package/src/agent/output-reformatter.js +0 -175
- package/src/agent/schema-utils.js +0 -111
- package/src/agent-wrapper.js +0 -570
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/attach/attach-client.js +0 -438
- package/src/attach/attach-server.js +0 -543
- package/src/attach/index.js +0 -35
- package/src/attach/protocol.js +0 -220
- package/src/attach/ring-buffer.js +0 -121
- package/src/attach/socket-discovery.js +0 -267
- package/src/claude-task-runner.js +0 -523
- package/src/config-router.js +0 -80
- package/src/config-validator.js +0 -1660
- package/src/copy-worker.js +0 -43
- package/src/github.js +0 -139
- package/src/isolation-manager.js +0 -1381
- package/src/ledger.js +0 -614
- package/src/logic-engine.js +0 -223
- package/src/message-bus-bridge.js +0 -139
- package/src/message-bus.js +0 -249
- package/src/name-generator.js +0 -232
- package/src/orchestrator.js +0 -2519
- package/src/preflight.js +0 -485
- package/src/process-metrics.js +0 -566
- package/src/providers/anthropic/cli-builder.js +0 -45
- package/src/providers/anthropic/index.js +0 -134
- package/src/providers/anthropic/models.js +0 -23
- package/src/providers/anthropic/output-parser.js +0 -159
- package/src/providers/base-provider.js +0 -181
- package/src/providers/capabilities.js +0 -51
- package/src/providers/google/cli-builder.js +0 -55
- package/src/providers/google/index.js +0 -116
- package/src/providers/google/models.js +0 -24
- package/src/providers/google/output-parser.js +0 -92
- package/src/providers/index.js +0 -75
- package/src/providers/openai/cli-builder.js +0 -122
- package/src/providers/openai/index.js +0 -135
- package/src/providers/openai/models.js +0 -21
- package/src/providers/openai/output-parser.js +0 -129
- package/src/schemas/sub-cluster.js +0 -156
- package/src/status-footer.js +0 -980
- package/src/sub-cluster-wrapper.js +0 -560
- package/src/task-runner.js +0 -30
- package/src/template-resolver.js +0 -369
- package/src/tui/CHANGES.txt +0 -133
- package/src/tui/LAYOUT.md +0 -261
- package/src/tui/README.txt +0 -192
- package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
- package/src/tui/data-poller.js +0 -325
- package/src/tui/demo.js +0 -208
- package/src/tui/formatters.js +0 -128
- package/src/tui/index.js +0 -193
- package/src/tui/keybindings.js +0 -383
- package/src/tui/layout.js +0 -334
- package/src/tui/renderer.js +0 -194
- package/task-lib/attachable-watcher.js +0 -272
- package/task-lib/claude-recovery.js +0 -119
- package/task-lib/commands/clean.js +0 -50
- package/task-lib/commands/get-log-path.js +0 -23
- package/task-lib/commands/kill.js +0 -32
- package/task-lib/commands/list.js +0 -105
- package/task-lib/commands/logs.js +0 -411
- package/task-lib/commands/resume.js +0 -42
- package/task-lib/commands/run.js +0 -57
- package/task-lib/commands/schedule.js +0 -105
- package/task-lib/commands/scheduler-cmd.js +0 -96
- package/task-lib/commands/schedules.js +0 -98
- package/task-lib/commands/status.js +0 -44
- package/task-lib/commands/unschedule.js +0 -16
- package/task-lib/completion.js +0 -9
- package/task-lib/config.js +0 -10
- package/task-lib/name-generator.js +0 -230
- package/task-lib/package.json +0 -3
- package/task-lib/runner.js +0 -144
- package/task-lib/scheduler.js +0 -252
- package/task-lib/store.js +0 -230
- package/task-lib/tui/formatters.js +0 -166
- package/task-lib/tui/index.js +0 -197
- package/task-lib/tui/layout.js +0 -111
- package/task-lib/tui/renderer.js +0 -119
- package/task-lib/tui.js +0 -384
- package/task-lib/watcher.js +0 -188
package/src/preflight.js
DELETED
|
@@ -1,485 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Preflight Validation - Check all dependencies before starting
|
|
3
|
-
*
|
|
4
|
-
* Validates:
|
|
5
|
-
* - Selected provider CLI installed
|
|
6
|
-
* - gh CLI installed and authenticated (if using issue numbers)
|
|
7
|
-
* - Docker available (if using --docker)
|
|
8
|
-
*
|
|
9
|
-
* Provides CLEAR, ACTIONABLE error messages with recovery instructions.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { execSync } = require('child_process');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const fs = require('fs');
|
|
15
|
-
const os = require('os');
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Validation result
|
|
19
|
-
* @typedef {Object} ValidationResult
|
|
20
|
-
* @property {boolean} valid - Whether validation passed
|
|
21
|
-
* @property {string[]} errors - Fatal errors that block execution
|
|
22
|
-
* @property {string[]} warnings - Non-fatal warnings
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Format error with recovery instructions
|
|
27
|
-
* @param {string} title - Error title
|
|
28
|
-
* @param {string} detail - Error details
|
|
29
|
-
* @param {string[]} recovery - Recovery steps
|
|
30
|
-
* @returns {string}
|
|
31
|
-
*/
|
|
32
|
-
function formatError(title, detail, recovery) {
|
|
33
|
-
let msg = `\n❌ ${title}\n`;
|
|
34
|
-
msg += ` ${detail}\n`;
|
|
35
|
-
if (recovery.length > 0) {
|
|
36
|
-
msg += `\n To fix:\n`;
|
|
37
|
-
recovery.forEach((step, i) => {
|
|
38
|
-
msg += ` ${i + 1}. ${step}\n`;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return msg;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Check if a command exists (cross-platform)
|
|
46
|
-
* @param {string} cmd - Command to check
|
|
47
|
-
* @returns {boolean}
|
|
48
|
-
*/
|
|
49
|
-
function commandExists(cmd) {
|
|
50
|
-
try {
|
|
51
|
-
// Windows uses 'where', Unix uses 'which'
|
|
52
|
-
const checkCmd = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
|
|
53
|
-
execSync(checkCmd, { encoding: 'utf8', stdio: 'pipe' });
|
|
54
|
-
return true;
|
|
55
|
-
} catch {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Get Claude CLI version
|
|
62
|
-
* @param {string} claudeCommand - Optional custom Claude command (e.g., 'ccr code')
|
|
63
|
-
* @returns {{ installed: boolean, version: string | null, error: string | null }}
|
|
64
|
-
*/
|
|
65
|
-
function getClaudeVersion(claudeCommand = 'claude') {
|
|
66
|
-
// Parse command parts
|
|
67
|
-
const parts = claudeCommand.trim().split(/\s+/);
|
|
68
|
-
const command = parts[0];
|
|
69
|
-
const extraArgs = parts.slice(1);
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const versionArgs = [...extraArgs, '--version'];
|
|
73
|
-
const versionCmd = [command, ...versionArgs].join(' ');
|
|
74
|
-
const output = execSync(versionCmd, { encoding: 'utf8', stdio: 'pipe' });
|
|
75
|
-
const match = output.match(/(\d+\.\d+\.\d+)/);
|
|
76
|
-
return {
|
|
77
|
-
installed: true,
|
|
78
|
-
version: match ? match[1] : 'unknown',
|
|
79
|
-
error: null,
|
|
80
|
-
};
|
|
81
|
-
} catch (err) {
|
|
82
|
-
if (err.message.includes('command not found') || err.message.includes('not found')) {
|
|
83
|
-
return {
|
|
84
|
-
installed: false,
|
|
85
|
-
version: null,
|
|
86
|
-
error: `Command '${command}' not installed`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
installed: false,
|
|
91
|
-
version: null,
|
|
92
|
-
error: err.message,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Check macOS Keychain for Claude Code credentials
|
|
99
|
-
* @returns {{ authenticated: boolean, error: string | null }}
|
|
100
|
-
*/
|
|
101
|
-
function checkMacOsKeychain() {
|
|
102
|
-
if (os.platform() !== 'darwin') {
|
|
103
|
-
return { authenticated: false, error: 'Not macOS' };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
// Check if Claude Code credentials exist in Keychain
|
|
108
|
-
execSync('security find-generic-password -s "Claude Code-credentials"', {
|
|
109
|
-
encoding: 'utf8',
|
|
110
|
-
stdio: 'pipe',
|
|
111
|
-
timeout: 2000,
|
|
112
|
-
});
|
|
113
|
-
return { authenticated: true, error: null };
|
|
114
|
-
} catch {
|
|
115
|
-
return { authenticated: false, error: 'No credentials in Keychain' };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Check Claude CLI authentication status
|
|
121
|
-
* @returns {{ authenticated: boolean, error: string | null, configDir: string, method?: string }}
|
|
122
|
-
*/
|
|
123
|
-
function checkClaudeAuth() {
|
|
124
|
-
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
125
|
-
const credentialsPath = path.join(configDir, '.credentials.json');
|
|
126
|
-
|
|
127
|
-
// Check if credentials file exists
|
|
128
|
-
if (!fs.existsSync(credentialsPath)) {
|
|
129
|
-
// No credentials file - check macOS Keychain as fallback
|
|
130
|
-
// Only use Keychain when using default config dir (not custom CLAUDE_CONFIG_DIR)
|
|
131
|
-
const isDefaultConfigDir = !process.env.CLAUDE_CONFIG_DIR;
|
|
132
|
-
if (isDefaultConfigDir) {
|
|
133
|
-
const keychainResult = checkMacOsKeychain();
|
|
134
|
-
if (keychainResult.authenticated) {
|
|
135
|
-
return {
|
|
136
|
-
authenticated: true,
|
|
137
|
-
error: null,
|
|
138
|
-
configDir,
|
|
139
|
-
method: 'keychain',
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
authenticated: false,
|
|
145
|
-
error: 'No credentials file found',
|
|
146
|
-
configDir,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Check if credentials file has content
|
|
151
|
-
try {
|
|
152
|
-
const content = fs.readFileSync(credentialsPath, 'utf8');
|
|
153
|
-
const creds = JSON.parse(content);
|
|
154
|
-
|
|
155
|
-
// Check for OAuth token (primary auth method)
|
|
156
|
-
if (creds.claudeAiOauth?.accessToken) {
|
|
157
|
-
// Check if token is expired
|
|
158
|
-
const expiresAt = creds.claudeAiOauth.expiresAt;
|
|
159
|
-
if (expiresAt && new Date(expiresAt) < new Date()) {
|
|
160
|
-
return {
|
|
161
|
-
authenticated: false,
|
|
162
|
-
error: 'OAuth token expired',
|
|
163
|
-
configDir,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
return {
|
|
167
|
-
authenticated: true,
|
|
168
|
-
error: null,
|
|
169
|
-
configDir,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Check for API key auth
|
|
174
|
-
if (creds.apiKey) {
|
|
175
|
-
return {
|
|
176
|
-
authenticated: true,
|
|
177
|
-
error: null,
|
|
178
|
-
configDir,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
authenticated: false,
|
|
184
|
-
error: 'No valid authentication found in credentials',
|
|
185
|
-
configDir,
|
|
186
|
-
};
|
|
187
|
-
} catch (err) {
|
|
188
|
-
return {
|
|
189
|
-
authenticated: false,
|
|
190
|
-
error: `Failed to parse credentials: ${err.message}`,
|
|
191
|
-
configDir,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Check gh CLI authentication status
|
|
198
|
-
* @returns {{ installed: boolean, authenticated: boolean, error: string | null }}
|
|
199
|
-
*/
|
|
200
|
-
function checkGhAuth() {
|
|
201
|
-
// Check if gh is installed
|
|
202
|
-
if (!commandExists('gh')) {
|
|
203
|
-
return {
|
|
204
|
-
installed: false,
|
|
205
|
-
authenticated: false,
|
|
206
|
-
error: 'gh CLI not installed',
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Check auth status
|
|
211
|
-
try {
|
|
212
|
-
execSync('gh auth status', { encoding: 'utf8', stdio: 'pipe' });
|
|
213
|
-
return {
|
|
214
|
-
installed: true,
|
|
215
|
-
authenticated: true,
|
|
216
|
-
error: null,
|
|
217
|
-
};
|
|
218
|
-
} catch (err) {
|
|
219
|
-
// gh auth status returns non-zero if not authenticated
|
|
220
|
-
const stderr = err.stderr || err.message || '';
|
|
221
|
-
|
|
222
|
-
if (stderr.includes('not logged in')) {
|
|
223
|
-
return {
|
|
224
|
-
installed: true,
|
|
225
|
-
authenticated: false,
|
|
226
|
-
error: 'gh CLI not authenticated',
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return {
|
|
231
|
-
installed: true,
|
|
232
|
-
authenticated: false,
|
|
233
|
-
error: stderr.trim() || 'Unknown gh auth error',
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Check Docker availability
|
|
240
|
-
* @returns {{ available: boolean, error: string | null }}
|
|
241
|
-
*/
|
|
242
|
-
function checkDocker() {
|
|
243
|
-
try {
|
|
244
|
-
execSync('docker --version', { encoding: 'utf8', stdio: 'pipe' });
|
|
245
|
-
|
|
246
|
-
// Also check if Docker daemon is running
|
|
247
|
-
execSync('docker info', { encoding: 'utf8', stdio: 'pipe' });
|
|
248
|
-
|
|
249
|
-
return {
|
|
250
|
-
available: true,
|
|
251
|
-
error: null,
|
|
252
|
-
};
|
|
253
|
-
} catch (err) {
|
|
254
|
-
const stderr = err.stderr || err.message || '';
|
|
255
|
-
|
|
256
|
-
if (stderr.includes('command not found') || stderr.includes('not found')) {
|
|
257
|
-
return {
|
|
258
|
-
available: false,
|
|
259
|
-
error: 'Docker not installed',
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (stderr.includes('Cannot connect') || stderr.includes('Is the docker daemon running')) {
|
|
264
|
-
return {
|
|
265
|
-
available: false,
|
|
266
|
-
error: 'Docker daemon not running',
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return {
|
|
271
|
-
available: false,
|
|
272
|
-
error: stderr.trim() || 'Unknown Docker error',
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Run all preflight checks
|
|
279
|
-
* @param {Object} options - Preflight options
|
|
280
|
-
* @param {boolean} options.requireGh - Whether gh CLI is required (true if using issue number)
|
|
281
|
-
* @param {boolean} options.requireDocker - Whether Docker is required (true if using --docker)
|
|
282
|
-
* @param {boolean} options.requireGit - Whether git repo is required (true if using --worktree)
|
|
283
|
-
* @param {boolean} options.quiet - Suppress success messages
|
|
284
|
-
* @param {string} options.claudeCommand - Custom Claude command (from settings)
|
|
285
|
-
* @param {string} options.provider - Provider override
|
|
286
|
-
* @returns {ValidationResult}
|
|
287
|
-
*/
|
|
288
|
-
function runPreflight(options = {}) {
|
|
289
|
-
const errors = [];
|
|
290
|
-
const warnings = [];
|
|
291
|
-
|
|
292
|
-
const { loadSettings, getClaudeCommand } = require('../lib/settings.js');
|
|
293
|
-
const { normalizeProviderName } = require('../lib/provider-names');
|
|
294
|
-
const settings = loadSettings();
|
|
295
|
-
const providerName = normalizeProviderName(
|
|
296
|
-
options.provider || settings.defaultProvider || 'claude'
|
|
297
|
-
);
|
|
298
|
-
|
|
299
|
-
if (providerName === 'claude') {
|
|
300
|
-
const { command, args } = getClaudeCommand();
|
|
301
|
-
const claudeCommand = options.claudeCommand || [command, ...args].join(' ');
|
|
302
|
-
|
|
303
|
-
const claude = getClaudeVersion(claudeCommand);
|
|
304
|
-
if (!claude.installed) {
|
|
305
|
-
errors.push(
|
|
306
|
-
formatError(
|
|
307
|
-
'Claude command not available',
|
|
308
|
-
claude.error,
|
|
309
|
-
claudeCommand === 'claude'
|
|
310
|
-
? [
|
|
311
|
-
'Install Claude CLI: npm install -g @anthropic-ai/claude-code',
|
|
312
|
-
'Or: brew install claude (macOS)',
|
|
313
|
-
'Then run: claude --version',
|
|
314
|
-
]
|
|
315
|
-
: [
|
|
316
|
-
`Command '${claudeCommand}' not found`,
|
|
317
|
-
'Check settings: zeroshot settings',
|
|
318
|
-
'Update claudeCommand: zeroshot settings set claudeCommand "your-command"',
|
|
319
|
-
'Or install the missing command',
|
|
320
|
-
]
|
|
321
|
-
)
|
|
322
|
-
);
|
|
323
|
-
} else if (claude.version) {
|
|
324
|
-
const [major, minor] = claude.version.split('.').map(Number);
|
|
325
|
-
if (major < 1 || (major === 1 && minor < 0)) {
|
|
326
|
-
warnings.push(
|
|
327
|
-
`⚠️ Claude CLI version ${claude.version} may be outdated. Consider upgrading.`
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Claude CLI refuses --dangerously-skip-permissions when running as root.
|
|
333
|
-
if (process.getuid && process.getuid() === 0) {
|
|
334
|
-
errors.push(
|
|
335
|
-
formatError(
|
|
336
|
-
'Running as root',
|
|
337
|
-
'Claude CLI refuses --dangerously-skip-permissions flag when running as root (UID 0)',
|
|
338
|
-
[
|
|
339
|
-
'Run as non-root user in Docker: docker run --user 1000:1000 ...',
|
|
340
|
-
'Or create non-root user: adduser testuser && su - testuser',
|
|
341
|
-
'Or use existing node user: docker run --user node ...',
|
|
342
|
-
'Security: Claude CLI blocks this flag as root to prevent privilege escalation',
|
|
343
|
-
]
|
|
344
|
-
)
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
} else if (providerName === 'codex') {
|
|
348
|
-
if (!commandExists('codex')) {
|
|
349
|
-
errors.push(
|
|
350
|
-
formatError('Codex CLI not available', 'Command "codex" not installed', [
|
|
351
|
-
'Install Codex CLI: npm install -g @openai/codex',
|
|
352
|
-
'Then run: codex --version',
|
|
353
|
-
])
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
|
-
} else if (providerName === 'gemini') {
|
|
357
|
-
if (!commandExists('gemini')) {
|
|
358
|
-
errors.push(
|
|
359
|
-
formatError('Gemini CLI not available', 'Command "gemini" not installed', [
|
|
360
|
-
'Install Gemini CLI: npm install -g @google/gemini-cli',
|
|
361
|
-
'Then run: gemini --version',
|
|
362
|
-
])
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
errors.push(
|
|
367
|
-
formatError('Unknown provider', `Provider "${providerName}" is not supported`, [
|
|
368
|
-
'Use claude, codex, or gemini',
|
|
369
|
-
])
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// 4. Check gh CLI (if required)
|
|
374
|
-
if (options.requireGh) {
|
|
375
|
-
const gh = checkGhAuth();
|
|
376
|
-
if (!gh.installed) {
|
|
377
|
-
errors.push(
|
|
378
|
-
formatError('GitHub CLI (gh) not installed', 'Required for fetching issues by number', [
|
|
379
|
-
'Install: brew install gh (macOS) or apt install gh (Linux)',
|
|
380
|
-
'Or download from: https://cli.github.com/',
|
|
381
|
-
])
|
|
382
|
-
);
|
|
383
|
-
} else if (!gh.authenticated) {
|
|
384
|
-
errors.push(
|
|
385
|
-
formatError('GitHub CLI (gh) not authenticated', gh.error, [
|
|
386
|
-
'Run: gh auth login',
|
|
387
|
-
'Select GitHub.com, HTTPS, and authenticate via browser',
|
|
388
|
-
'Then verify: gh auth status',
|
|
389
|
-
])
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// 5. Check Docker (if required)
|
|
395
|
-
if (options.requireDocker) {
|
|
396
|
-
const docker = checkDocker();
|
|
397
|
-
if (!docker.available) {
|
|
398
|
-
errors.push(
|
|
399
|
-
formatError(
|
|
400
|
-
'Docker not available',
|
|
401
|
-
docker.error,
|
|
402
|
-
docker.error.includes('daemon')
|
|
403
|
-
? ['Start Docker Desktop', 'Or run: sudo systemctl start docker (Linux)']
|
|
404
|
-
: [
|
|
405
|
-
'Install Docker Desktop from: https://docker.com/products/docker-desktop',
|
|
406
|
-
'Then start Docker and verify: docker info',
|
|
407
|
-
]
|
|
408
|
-
)
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// 6. Check git repo (if required for worktree isolation)
|
|
414
|
-
if (options.requireGit) {
|
|
415
|
-
let isGitRepo = false;
|
|
416
|
-
try {
|
|
417
|
-
execSync('git rev-parse --git-dir', { stdio: 'pipe' });
|
|
418
|
-
isGitRepo = true;
|
|
419
|
-
} catch {
|
|
420
|
-
// Not a git repo
|
|
421
|
-
}
|
|
422
|
-
if (!isGitRepo) {
|
|
423
|
-
errors.push(
|
|
424
|
-
formatError('Not in a git repository', 'Worktree isolation requires a git repository', [
|
|
425
|
-
'Run from within a git repository',
|
|
426
|
-
'Or use --docker instead of --worktree for non-git directories',
|
|
427
|
-
'Initialize a repo with: git init',
|
|
428
|
-
])
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return {
|
|
434
|
-
valid: errors.length === 0,
|
|
435
|
-
errors,
|
|
436
|
-
warnings,
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Run preflight checks and exit if failed
|
|
442
|
-
* @param {Object} options - Preflight options
|
|
443
|
-
* @param {boolean} options.requireGh - Whether gh CLI is required
|
|
444
|
-
* @param {boolean} options.requireDocker - Whether Docker is required
|
|
445
|
-
* @param {boolean} options.requireGit - Whether git repo is required
|
|
446
|
-
* @param {boolean} options.quiet - Suppress success messages
|
|
447
|
-
* @param {string} options.provider - Provider override
|
|
448
|
-
*/
|
|
449
|
-
function requirePreflight(options = {}) {
|
|
450
|
-
const result = runPreflight(options);
|
|
451
|
-
|
|
452
|
-
// Print warnings regardless of success
|
|
453
|
-
if (result.warnings.length > 0) {
|
|
454
|
-
for (const warning of result.warnings) {
|
|
455
|
-
console.warn(warning);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (!result.valid) {
|
|
460
|
-
console.error('\n' + '='.repeat(60));
|
|
461
|
-
console.error('PREFLIGHT CHECK FAILED');
|
|
462
|
-
console.error('='.repeat(60));
|
|
463
|
-
|
|
464
|
-
for (const error of result.errors) {
|
|
465
|
-
console.error(error);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
console.error('='.repeat(60) + '\n');
|
|
469
|
-
process.exit(1);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
if (!options.quiet) {
|
|
473
|
-
console.log('✓ Preflight checks passed');
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
module.exports = {
|
|
478
|
-
runPreflight,
|
|
479
|
-
requirePreflight,
|
|
480
|
-
getClaudeVersion,
|
|
481
|
-
checkClaudeAuth,
|
|
482
|
-
checkGhAuth,
|
|
483
|
-
checkDocker,
|
|
484
|
-
formatError,
|
|
485
|
-
};
|