From 179eef668561bc04bc5ed9d7b5a7f1c3c96bce27 Mon Sep 17 00:00:00 2001 From: Kacper Date: Wed, 10 Dec 2025 04:22:59 +0100 Subject: [PATCH] feat(electron): enhance feature execution with async prompt handling and authentication checks - Updated the FeatureExecutor to wrap content blocks in an async generator for multimodal prompt compatibility. - Added authentication checks in the ClaudeProvider to ensure proper API key or token availability, including loading from local CLI config. - Improved error handling for missing authentication, providing clear console messages for user guidance. This update enhances the robustness of feature execution and ensures proper authentication management for the Claude SDK. --- .automaker/feature_list.json | 97 +++-------------------- app/electron/services/feature-executor.js | 38 +++++++-- app/electron/services/model-provider.js | 53 ++++++++++++- 3 files changed, 93 insertions(+), 95 deletions(-) diff --git a/.automaker/feature_list.json b/.automaker/feature_list.json index b9e7ef55..47bb3620 100644 --- a/.automaker/feature_list.json +++ b/.automaker/feature_list.json @@ -142,26 +142,6 @@ "model": "haiku", "thinkingLevel": "none" }, - { - "id": "feature-1765328064583-6zpz7ddil", - "category": "Kanban", - "description": "remove the auto mode activity panel completley.", - "steps": [], - "status": "waiting_approval", - "startedAt": "2025-12-10T00:55:21.540Z", - "imagePaths": [ - { - "id": "img-1765328011980-j8d2r6b78", - "path": "/var/folders/yk/56l0_s6978qfh521xf1dtx3r0000gn/T/automaker-images/1765328011979_Screenshot_2025-12-09_at_7.53.30_PM.png", - "filename": "Screenshot 2025-12-09 at 7.53.30 PM.png", - "mimeType": "image/png" - } - ], - "skipTests": true, - "summary": "Removed auto mode activity panel completely. Deleted: auto-mode-log.tsx. Modified: board-view.tsx - removed AutoModeLog import, showActivityLog state, activity toggle button, and activity panel rendering. Also removed unused cn import and ChevronUp/ChevronDown icons.", - "model": "opus", - "thinkingLevel": "none" - }, { "id": "feature-1765334243840-qmnc0ez5o", "category": "Core", @@ -188,66 +168,6 @@ "model": "gpt-5.1-codex", "thinkingLevel": "none" }, - { - "id": "feature-1765330657132-oapdvbygc", - "category": "Uncategorized", - "description": "these buttons should be refactored to match more with selected theme, make sure they are set to use the button component variant styles", - "steps": [], - "status": "waiting_approval", - "startedAt": "2025-12-10T01:37:40.700Z", - "imagePaths": [ - { - "id": "img-1765330619380-q9tu8blks", - "path": "/var/folders/yk/56l0_s6978qfh521xf1dtx3r0000gn/T/automaker-images/1765330619376_Screenshot_2025-12-09_at_8.36.56_PM.png", - "filename": "Screenshot 2025-12-09 at 8.36.56 PM.png", - "mimeType": "image/png" - } - ], - "skipTests": true, - "summary": "Refactored theme selector and kanban detail level buttons to use Button component variants. Modified: settings-view.tsx. Changed 12 theme buttons and 3 kanban detail buttons from raw <button> to <Button> with dynamic variant (secondary when selected, outline when unselected) and brand-500 ring highlight for selected state.", - "model": "opus", - "thinkingLevel": "none" - }, - { - "id": "feature-1765330774043-35l9kw70q", - "category": "Kanban", - "description": "Increase the width of this modal and reduce font size of log output to make it easier to fit more output in modal", - "steps": [], - "status": "waiting_approval", - "startedAt": "2025-12-10T01:37:40.700Z", - "imagePaths": [ - { - "id": "img-1765330741800-jhmtz9ttc", - "path": "/var/folders/yk/56l0_s6978qfh521xf1dtx3r0000gn/T/automaker-images/1765330741799_Screenshot_2025-12-09_at_8.38.59_PM.png", - "filename": "Screenshot 2025-12-09 at 8.38.59 PM.png", - "mimeType": "image/png" - } - ], - "skipTests": true, - "summary": "Increased modal width from max-w-4xl to max-w-6xl and reduced log output font sizes from text-sm to text-xs. Modified: agent-output-modal.tsx (modal width + container font), log-viewer.tsx (log entry content + preview text fonts).", - "model": "opus", - "thinkingLevel": "none" - }, - { - "id": "feature-1765330800921-uwy5iu3lp", - "category": "Uncategorized", - "description": "what color is the screenshot button? don't change code just answer.", - "steps": [], - "status": "waiting_approval", - "startedAt": "2025-12-10T01:37:40.700Z", - "imagePaths": [ - { - "id": "img-1765330783407-msplpgmwk", - "path": "/var/folders/yk/56l0_s6978qfh521xf1dtx3r0000gn/T/automaker-images/1765330783407_Screenshot_2025-12-09_at_8.39.40_PM.png", - "filename": "Screenshot 2025-12-09 at 8.39.40 PM.png", - "mimeType": "image/png" - } - ], - "skipTests": true, - "summary": "Answered question about screenshot button color. The image attachment button (Paperclip icon) is blue when active (bg-blue-100/text-blue-600 light, bg-blue-900/text-blue-400 dark) and uses standard outline styling when inactive. No code changes made.", - "model": "opus", - "thinkingLevel": "none" - }, { "id": "feature-1765331813319-jzlk7eku2", "category": "Uncategorized", @@ -268,22 +188,23 @@ "thinkingLevel": "none" }, { - "id": "feature-1765333165618-qmik9gy7p", + "id": "feature-1765336835703-4xona4xy9", "category": "Uncategorized", - "description": "what is the text in the attache image say?", + "description": "what is the text in the attache image say? dont change any code just answer\n", "steps": [], - "status": "in_progress", - "startedAt": "2025-12-10T02:19:28.342Z", + "status": "waiting_approval", + "startedAt": "2025-12-10T03:20:37.656Z", "imagePaths": [ { - "id": "img-1765333155109-on4lk435f", - "path": "/Users/webdevcody/Library/Application Support/automaker/images/1765333155106-czd46vc93_Screenshot_2025-12-09_at_9.19.13_PM.png", - "filename": "Screenshot 2025-12-09 at 9.19.13 PM.png", + "id": "img-1765336834302-vue7xm4v0", + "path": "/Users/shirone/Library/Application Support/automaker/images/1765336834301-bldt7d0ay_image-test.png", + "filename": "image-test.png", "mimeType": "image/png" } ], "skipTests": true, - "model": "opus", + "summary": "Analyzed attached image and identified text content: \"Hello World\" in green text on black background. No code changes made as requested.", + "model": "sonnet", "thinkingLevel": "none" } ] \ No newline at end of file diff --git a/app/electron/services/feature-executor.js b/app/electron/services/feature-executor.js index 0acceb59..98f508e1 100644 --- a/app/electron/services/feature-executor.js +++ b/app/electron/services/feature-executor.js @@ -323,8 +323,18 @@ class FeatureExecutor { } } - // Use content blocks instead of plain text - prompt = contentBlocks; + // Wrap content blocks in async generator for SDK (required format for multimodal prompts) + prompt = (async function* () { + yield { + type: "user", + session_id: "", + message: { + role: "user", + content: contentBlocks, + }, + parent_tool_use_id: null, + }; + })(); } // Planning: Analyze the codebase and create implementation plan @@ -356,6 +366,15 @@ class FeatureExecutor { let currentQuery; isCodex = this.isCodexModel(feature); + // Ensure provider auth is available (especially for Claude SDK) + const provider = this.getProvider(feature); + if (provider?.ensureAuthEnv && !provider.ensureAuthEnv()) { + const authMsg = + "Missing Anthropic auth. Set ANTHROPIC_API_KEY or run `claude login` so ~/.claude/config.json contains oauth_token."; + console.error(`[FeatureExecutor] ${authMsg}`); + throw new Error(authMsg); + } + // Validate that model string matches the provider if (isCodex) { // Ensure model string is actually a Codex model, not a Claude model @@ -367,7 +386,6 @@ class FeatureExecutor { // Use Codex provider for OpenAI models console.log(`[FeatureExecutor] Using Codex provider for model: ${modelString}`); - const provider = this.getProvider(feature); currentQuery = provider.executeQuery({ prompt, model: modelString, @@ -712,8 +730,18 @@ class FeatureExecutor { } } - // Use content blocks instead of plain text - prompt = contentBlocks; + // Wrap content blocks in async generator for SDK (required format for multimodal prompts) + prompt = (async function* () { + yield { + type: "user", + session_id: "", + message: { + role: "user", + content: contentBlocks, + }, + parent_tool_use_id: null, + }; + })(); } // Use appropriate provider based on model type diff --git a/app/electron/services/model-provider.js b/app/electron/services/model-provider.js index 92f86139..bbc6f9cd 100644 --- a/app/electron/services/model-provider.js +++ b/app/electron/services/model-provider.js @@ -94,6 +94,45 @@ class ClaudeProvider extends ModelProvider { this.sdk = null; } + /** + * Try to load a Claude OAuth token from the local CLI config (~/.claude/config.json). + * Returns the token string or null if not found. + */ + loadTokenFromCliConfig() { + try { + const fs = require('fs'); + const path = require('path'); + const configPath = path.join(require('os').homedir(), '.claude', 'config.json'); + if (!fs.existsSync(configPath)) { + return null; + } + const raw = fs.readFileSync(configPath, 'utf-8'); + const parsed = JSON.parse(raw); + // CLI config stores token as oauth_token (newer) or token (older) + return parsed.oauth_token || parsed.token || null; + } catch (err) { + console.warn('[ClaudeProvider] Failed to read CLI config token:', err?.message); + return null; + } + } + + ensureAuthEnv() { + // If API key or token already present, keep as-is. + if (process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN) { + console.log('[ClaudeProvider] Auth already present in environment'); + return true; + } + // Try to hydrate from CLI login config + const token = this.loadTokenFromCliConfig(); + if (token) { + process.env.CLAUDE_CODE_OAUTH_TOKEN = token; + console.log('[ClaudeProvider] Loaded CLAUDE_CODE_OAUTH_TOKEN from ~/.claude/config.json'); + return true; + } + console.error('[ClaudeProvider] No Anthropic auth found (env empty, ~/.claude/config.json missing token)'); + return false; + } + /** * Lazily load the Claude SDK */ @@ -105,6 +144,14 @@ class ClaudeProvider extends ModelProvider { } async *executeQuery(options) { + // Ensure we have auth; fall back to CLI login token if available. + if (!this.ensureAuthEnv()) { + const msg = 'Missing Anthropic auth. Set ANTHROPIC_API_KEY or run `claude login` (CLI) so ~/.claude/config.json contains oauth_token.'; + console.error(`[ClaudeProvider] ${msg}`); + yield { type: 'error', error: msg }; + return; + } + const { query } = this.loadSdk(); const sdkOptions = { @@ -168,9 +215,11 @@ class ClaudeProvider extends ModelProvider { validateConfig() { const errors = []; - // Check for OAuth token or API key + // Ensure auth is available (try to auto-load from CLI config) + this.ensureAuthEnv(); + if (!process.env.CLAUDE_CODE_OAUTH_TOKEN && !process.env.ANTHROPIC_API_KEY) { - errors.push('No Claude authentication found. Set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY.'); + errors.push('No Claude authentication found. Set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY, or run `claude login` to populate ~/.claude/config.json.'); } return {