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.
This commit is contained in:
Kacper
2025-12-10 04:22:59 +01:00
parent 3934ba65da
commit 179eef6685
3 changed files with 93 additions and 95 deletions

View File

@@ -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"
}
]

View File

@@ -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

View File

@@ -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 {