mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
feat(cli): implement Claude CLI detection and model selection for features
- Added a new service to detect the installation status of Claude Code CLI, providing users with installation recommendations and commands. - Integrated CLI detection into the SettingsView to inform users about the CLI status and its benefits for ultrathink tasks. - Enhanced feature creation and editing dialogs to allow users to select from multiple models (Haiku, Sonnet, Opus) and specify thinking levels (None, Low, Medium, High, Ultrathink). - Updated the feature executor to utilize the selected model and thinking configuration during task execution, improving flexibility and performance. This update enhances user experience by providing clearer options for model selection and ensuring optimal performance with the Claude CLI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
This commit is contained in:
@@ -551,3 +551,21 @@ ipcMain.handle("auto-mode:commit-feature", async (_, { projectPath, featureId })
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Claude CLI Detection IPC Handlers
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Check Claude Code CLI installation status
|
||||
*/
|
||||
ipcMain.handle("claude:check-cli", async () => {
|
||||
try {
|
||||
const claudeCliDetector = require("./services/claude-cli-detector");
|
||||
const info = claudeCliDetector.getInstallationInfo();
|
||||
return { success: true, ...info };
|
||||
} catch (error) {
|
||||
console.error("[IPC] claude:check-cli error:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -138,6 +138,9 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
// Claude CLI Detection API
|
||||
checkClaudeCli: () => ipcRenderer.invoke("claude:check-cli"),
|
||||
});
|
||||
|
||||
// Also expose a flag to detect if we're in Electron
|
||||
|
||||
119
app/electron/services/claude-cli-detector.js
Normal file
119
app/electron/services/claude-cli-detector.js
Normal file
@@ -0,0 +1,119 @@
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
|
||||
class ClaudeCliDetector {
|
||||
/**
|
||||
* Check if Claude Code CLI is installed and accessible
|
||||
* @returns {Object} { installed: boolean, path: string|null, version: string|null, method: 'cli'|'sdk'|'none' }
|
||||
*/
|
||||
static detectClaudeInstallation() {
|
||||
try {
|
||||
// Method 1: Check if 'claude' command is in PATH
|
||||
try {
|
||||
const claudePath = execSync('which claude', { encoding: 'utf-8' }).trim();
|
||||
const version = execSync('claude --version', { encoding: 'utf-8' }).trim();
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
version: version,
|
||||
method: 'cli'
|
||||
};
|
||||
} catch (error) {
|
||||
// CLI not in PATH, check local installation
|
||||
}
|
||||
|
||||
// Method 2: Check for local installation
|
||||
const localClaudePath = path.join(os.homedir(), '.claude', 'local', 'claude');
|
||||
if (fs.existsSync(localClaudePath)) {
|
||||
try {
|
||||
const version = execSync(`${localClaudePath} --version`, { encoding: 'utf-8' }).trim();
|
||||
return {
|
||||
installed: true,
|
||||
path: localClaudePath,
|
||||
version: version,
|
||||
method: 'cli-local'
|
||||
};
|
||||
} catch (error) {
|
||||
// Local CLI exists but may not be executable
|
||||
}
|
||||
}
|
||||
|
||||
// Method 3: Check Windows path
|
||||
if (process.platform === 'win32') {
|
||||
try {
|
||||
const claudePath = execSync('where claude', { encoding: 'utf-8' }).trim();
|
||||
const version = execSync('claude --version', { encoding: 'utf-8' }).trim();
|
||||
return {
|
||||
installed: true,
|
||||
path: claudePath,
|
||||
version: version,
|
||||
method: 'cli'
|
||||
};
|
||||
} catch (error) {
|
||||
// Not found
|
||||
}
|
||||
}
|
||||
|
||||
// Method 4: SDK mode (using OAuth token)
|
||||
if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
||||
return {
|
||||
installed: true,
|
||||
path: null,
|
||||
version: 'SDK Mode',
|
||||
method: 'sdk'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
installed: false,
|
||||
path: null,
|
||||
version: null,
|
||||
method: 'none'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[ClaudeCliDetector] Error detecting Claude installation:', error);
|
||||
return {
|
||||
installed: false,
|
||||
path: null,
|
||||
version: null,
|
||||
method: 'none',
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get installation recommendations
|
||||
*/
|
||||
static getInstallationInfo() {
|
||||
const detection = this.detectClaudeInstallation();
|
||||
|
||||
if (detection.installed) {
|
||||
return {
|
||||
status: 'installed',
|
||||
method: detection.method,
|
||||
version: detection.version,
|
||||
path: detection.path,
|
||||
recommendation: detection.method === 'cli'
|
||||
? 'Using Claude Code CLI - optimal for long-running tasks'
|
||||
: 'Using SDK mode - works well but CLI may provide better performance'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'not_installed',
|
||||
recommendation: 'Consider installing Claude Code CLI for better performance with ultrathink',
|
||||
installCommands: {
|
||||
macos: 'curl -fsSL claude.ai/install.sh | bash',
|
||||
windows: 'irm https://claude.ai/install.ps1 | iex',
|
||||
linux: 'curl -fsSL claude.ai/install.sh | bash',
|
||||
npm: 'npm install -g @anthropic-ai/claude-code'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClaudeCliDetector;
|
||||
|
||||
@@ -4,10 +4,97 @@ const contextManager = require("./context-manager");
|
||||
const featureLoader = require("./feature-loader");
|
||||
const mcpServerFactory = require("./mcp-server-factory");
|
||||
|
||||
// Model name mappings
|
||||
const MODEL_MAP = {
|
||||
haiku: "claude-haiku-4-20250514",
|
||||
sonnet: "claude-sonnet-4-20250514",
|
||||
opus: "claude-opus-4-5-20251101",
|
||||
};
|
||||
|
||||
// Thinking level to budget_tokens mapping
|
||||
// These values control how much "thinking time" the model gets for extended thinking
|
||||
const THINKING_BUDGET_MAP = {
|
||||
none: null, // No extended thinking
|
||||
low: 4096, // Light thinking
|
||||
medium: 16384, // Moderate thinking
|
||||
high: 65536, // Deep thinking
|
||||
ultrathink: 262144, // Ultra-deep thinking (maximum reasoning)
|
||||
};
|
||||
|
||||
/**
|
||||
* Feature Executor - Handles feature implementation using Claude Agent SDK
|
||||
*/
|
||||
class FeatureExecutor {
|
||||
/**
|
||||
* Get the model string based on feature's model setting
|
||||
*/
|
||||
getModelString(feature) {
|
||||
const modelKey = feature.model || "opus"; // Default to opus
|
||||
return MODEL_MAP[modelKey] || MODEL_MAP.opus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get thinking configuration based on feature's thinkingLevel
|
||||
*/
|
||||
getThinkingConfig(feature) {
|
||||
const level = feature.thinkingLevel || "none";
|
||||
const budgetTokens = THINKING_BUDGET_MAP[level];
|
||||
|
||||
if (budgetTokens === null) {
|
||||
return null; // No extended thinking
|
||||
}
|
||||
|
||||
return {
|
||||
type: "enabled",
|
||||
budget_tokens: budgetTokens,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for ultrathink execution - validate and warn
|
||||
*/
|
||||
prepareForUltrathink(feature, thinkingConfig) {
|
||||
if (feature.thinkingLevel !== 'ultrathink') {
|
||||
return { ready: true };
|
||||
}
|
||||
|
||||
const warnings = [];
|
||||
const recommendations = [];
|
||||
|
||||
// Check CLI installation
|
||||
const claudeCliDetector = require('./claude-cli-detector');
|
||||
const cliInfo = claudeCliDetector.getInstallationInfo();
|
||||
|
||||
if (cliInfo.status === 'not_installed') {
|
||||
warnings.push('Claude Code CLI not detected - ultrathink may have timeout issues');
|
||||
recommendations.push('Install Claude Code CLI for optimal ultrathink performance');
|
||||
}
|
||||
|
||||
// Validate budget tokens
|
||||
if (thinkingConfig && thinkingConfig.budget_tokens > 32000) {
|
||||
warnings.push(`Ultrathink budget (${thinkingConfig.budget_tokens} tokens) exceeds recommended 32K - may cause long-running requests`);
|
||||
recommendations.push('Consider using batch processing for budgets above 32K');
|
||||
}
|
||||
|
||||
// Cost estimate (rough)
|
||||
const estimatedCost = (thinkingConfig?.budget_tokens || 0) / 1000 * 0.015; // Rough estimate
|
||||
if (estimatedCost > 1.0) {
|
||||
warnings.push(`Estimated cost: ~$${estimatedCost.toFixed(2)} per execution`);
|
||||
}
|
||||
|
||||
// Time estimate
|
||||
warnings.push('Ultrathink tasks typically take 45-180 seconds');
|
||||
|
||||
return {
|
||||
ready: true,
|
||||
warnings,
|
||||
recommendations,
|
||||
estimatedCost,
|
||||
estimatedTime: '45-180 seconds',
|
||||
cliInfo
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep helper
|
||||
*/
|
||||
@@ -46,9 +133,39 @@ class FeatureExecutor {
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Get model and thinking configuration from feature settings
|
||||
const modelString = this.getModelString(feature);
|
||||
const thinkingConfig = this.getThinkingConfig(feature);
|
||||
|
||||
// Prepare for ultrathink if needed
|
||||
if (feature.thinkingLevel === 'ultrathink') {
|
||||
const preparation = this.prepareForUltrathink(feature, thinkingConfig);
|
||||
|
||||
console.log(`[FeatureExecutor] Ultrathink preparation:`, preparation);
|
||||
|
||||
// Log warnings
|
||||
if (preparation.warnings && preparation.warnings.length > 0) {
|
||||
preparation.warnings.forEach(warning => {
|
||||
console.warn(`[FeatureExecutor] ⚠️ ${warning}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Send preparation info to renderer
|
||||
sendToRenderer({
|
||||
type: 'auto_mode_ultrathink_preparation',
|
||||
featureId: feature.id,
|
||||
warnings: preparation.warnings || [],
|
||||
recommendations: preparation.recommendations || [],
|
||||
estimatedCost: preparation.estimatedCost,
|
||||
estimatedTime: preparation.estimatedTime
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[FeatureExecutor] Using model: ${modelString}, thinking: ${feature.thinkingLevel || 'none'}`);
|
||||
|
||||
// Configure options for the SDK query
|
||||
const options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
model: modelString,
|
||||
systemPrompt: promptBuilder.getCodingPrompt(),
|
||||
maxTurns: 1000,
|
||||
cwd: projectPath,
|
||||
@@ -74,6 +191,11 @@ class FeatureExecutor {
|
||||
abortController: abortController,
|
||||
};
|
||||
|
||||
// Add thinking configuration if enabled
|
||||
if (thinkingConfig) {
|
||||
options.thinking = thinkingConfig;
|
||||
}
|
||||
|
||||
// Build the prompt for this specific feature
|
||||
const prompt = promptBuilder.buildFeaturePrompt(feature);
|
||||
|
||||
@@ -256,8 +378,38 @@ class FeatureExecutor {
|
||||
projectPath
|
||||
);
|
||||
|
||||
// Get model and thinking configuration from feature settings
|
||||
const modelString = this.getModelString(feature);
|
||||
const thinkingConfig = this.getThinkingConfig(feature);
|
||||
|
||||
// Prepare for ultrathink if needed
|
||||
if (feature.thinkingLevel === 'ultrathink') {
|
||||
const preparation = this.prepareForUltrathink(feature, thinkingConfig);
|
||||
|
||||
console.log(`[FeatureExecutor] Ultrathink preparation:`, preparation);
|
||||
|
||||
// Log warnings
|
||||
if (preparation.warnings && preparation.warnings.length > 0) {
|
||||
preparation.warnings.forEach(warning => {
|
||||
console.warn(`[FeatureExecutor] ⚠️ ${warning}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Send preparation info to renderer
|
||||
sendToRenderer({
|
||||
type: 'auto_mode_ultrathink_preparation',
|
||||
featureId: feature.id,
|
||||
warnings: preparation.warnings || [],
|
||||
recommendations: preparation.recommendations || [],
|
||||
estimatedCost: preparation.estimatedCost,
|
||||
estimatedTime: preparation.estimatedTime
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[FeatureExecutor] Resuming with model: ${modelString}, thinking: ${feature.thinkingLevel || 'none'}`);
|
||||
|
||||
const options = {
|
||||
model: "claude-opus-4-5-20251101",
|
||||
model: modelString,
|
||||
systemPrompt: promptBuilder.getVerificationPrompt(),
|
||||
maxTurns: 1000,
|
||||
cwd: projectPath,
|
||||
@@ -273,6 +425,11 @@ class FeatureExecutor {
|
||||
abortController: abortController,
|
||||
};
|
||||
|
||||
// Add thinking configuration if enabled
|
||||
if (thinkingConfig) {
|
||||
options.thinking = thinkingConfig;
|
||||
}
|
||||
|
||||
// Build prompt with previous context
|
||||
const prompt = promptBuilder.buildResumePrompt(feature, previousContext);
|
||||
|
||||
|
||||
@@ -84,6 +84,12 @@ class FeatureLoader {
|
||||
if (f.summary !== undefined) {
|
||||
featureData.summary = f.summary;
|
||||
}
|
||||
if (f.model !== undefined) {
|
||||
featureData.model = f.model;
|
||||
}
|
||||
if (f.thinkingLevel !== undefined) {
|
||||
featureData.thinkingLevel = f.thinkingLevel;
|
||||
}
|
||||
return featureData;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user