Files
ccs/bin/utils/claude-detector.js
T
kaitranntt 5fae92ac07 feat(glmt): add streaming with real-time thinking blocks
- reorganize bin/ into auth/, glmt/, management/, utils/
- add budget calculator and locale enforcer
- enhance test coverage with unit/integration separation
2025-11-11 15:39:00 -05:00

73 lines
2.4 KiB
JavaScript

'use strict';
const fs = require('fs');
const { execSync } = require('child_process');
const { expandPath } = require('./helpers');
// Detect Claude CLI executable
function detectClaudeCli() {
// Priority 1: CCS_CLAUDE_PATH environment variable (if user wants custom path)
if (process.env.CCS_CLAUDE_PATH) {
const ccsPath = expandPath(process.env.CCS_CLAUDE_PATH);
// Basic validation: file exists
if (fs.existsSync(ccsPath)) {
return ccsPath;
}
// Invalid CCS_CLAUDE_PATH - show warning and fall back to PATH
console.warn('[!] Warning: CCS_CLAUDE_PATH is set but file not found:', ccsPath);
console.warn(' Falling back to system PATH lookup...');
}
// Priority 2: Resolve 'claude' from PATH using which/where.exe
// This fixes Windows npm installation where spawn() can't resolve bare command names
// SECURITY: Commands are hardcoded literals with no user input - safe from injection
const isWindows = process.platform === 'win32';
try {
const cmd = isWindows ? 'where.exe claude' : 'which claude';
const result = execSync(cmd, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
timeout: 5000 // 5 second timeout to prevent hangs
}).trim();
// where.exe may return multiple lines (all matches in PATH order)
const matches = result.split('\n').map(p => p.trim()).filter(p => p);
if (isWindows) {
// On Windows, prefer executables with extensions (.exe, .cmd, .bat)
// where.exe often returns file without extension first, then the actual .cmd wrapper
const withExtension = matches.find(p => /\.(exe|cmd|bat|ps1)$/i.test(p));
const claudePath = withExtension || matches[0];
if (claudePath && fs.existsSync(claudePath)) {
return claudePath;
}
} else {
// On Unix, first match is fine
const claudePath = matches[0];
if (claudePath && fs.existsSync(claudePath)) {
return claudePath;
}
}
} catch (err) {
// Command failed - claude not in PATH
// Fall through to return null
}
// Priority 3: Claude not found
return null;
}
// Show Claude not found error
function showClaudeNotFoundError() {
console.error('ERROR: Claude CLI not found in PATH');
console.error('Install from: https://docs.claude.com/en/docs/claude-code/installation');
process.exit(1);
}
module.exports = {
detectClaudeCli,
showClaudeNotFoundError
};