{new Date(message.timestamp).toLocaleTimeString([], {
diff --git a/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx b/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
index 1fbe6ee1..aff8704d 100644
--- a/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
+++ b/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
@@ -8,7 +8,6 @@ import {
} from '@/lib/agent-context-parser';
import { cn } from '@/lib/utils';
import {
- Cpu,
Brain,
ListTodo,
Sparkles,
@@ -20,6 +19,7 @@ import {
} from 'lucide-react';
import { getElectronAPI } from '@/lib/electron';
import { SummaryDialog } from './summary-dialog';
+import { getProviderIconForModel } from '@/components/ui/provider-icon';
/**
* Formats thinking level for compact display
@@ -109,7 +109,10 @@ export function AgentInfoPanel({
-
+ {(() => {
+ const ProviderIcon = getProviderIconForModel(feature.model);
+ return
;
+ })()}
{formatModelName(feature.model ?? DEFAULT_MODEL)}
{feature.thinkingLevel && feature.thinkingLevel !== 'none' ? (
@@ -133,7 +136,10 @@ export function AgentInfoPanel({
{/* Model & Phase */}
-
+ {(() => {
+ const ProviderIcon = getProviderIconForModel(feature.model);
+ return
;
+ })()}
{formatModelName(feature.model ?? DEFAULT_MODEL)}
{agentInfo.currentPhase && (
diff --git a/apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx b/apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
index b48f78a3..b4474e72 100644
--- a/apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
+++ b/apps/ui/src/components/views/board-view/components/kanban-card/card-header.tsx
@@ -18,12 +18,12 @@ import {
MoreVertical,
ChevronDown,
ChevronUp,
- Cpu,
GitFork,
} from 'lucide-react';
import { CountUpTimer } from '@/components/ui/count-up-timer';
import { formatModelName, DEFAULT_MODEL } from '@/lib/agent-context-parser';
import { DeleteConfirmDialog } from '@/components/ui/delete-confirm-dialog';
+import { getProviderIconForModel } from '@/components/ui/provider-icon';
interface CardHeaderProps {
feature: Feature;
@@ -109,12 +109,17 @@ export function CardHeaderSection({
Spawn Sub-Task
{/* Model info in dropdown */}
-
-
-
- {formatModelName(feature.model ?? DEFAULT_MODEL)}
-
-
+ {(() => {
+ const ProviderIcon = getProviderIconForModel(feature.model);
+ return (
+
+
+
+
{formatModelName(feature.model ?? DEFAULT_MODEL)}
+
+
+ );
+ })()}
@@ -288,12 +293,17 @@ export function CardHeaderSection({
Spawn Sub-Task
{/* Model info in dropdown */}
-
-
-
- {formatModelName(feature.model ?? DEFAULT_MODEL)}
-
-
+ {(() => {
+ const ProviderIcon = getProviderIconForModel(feature.model);
+ return (
+
+
+
+
{formatModelName(feature.model ?? DEFAULT_MODEL)}
+
+
+ );
+ })()}
diff --git a/apps/ui/src/components/views/setup-view/hooks/use-cli-status.ts b/apps/ui/src/components/views/setup-view/hooks/use-cli-status.ts
index afae1645..176efc2a 100644
--- a/apps/ui/src/components/views/setup-view/hooks/use-cli-status.ts
+++ b/apps/ui/src/components/views/setup-view/hooks/use-cli-status.ts
@@ -40,8 +40,13 @@ export function useCliStatus({
logger.info(`Raw status result for ${cliType}:`, result);
if (result.success) {
+ // Handle both response formats:
+ // - Claude API returns {status: 'installed' | 'not_installed'}
+ // - Codex API returns {installed: boolean}
+ const isInstalled =
+ typeof result.installed === 'boolean' ? result.installed : result.status === 'installed';
const cliStatus = {
- installed: result.status === 'installed',
+ installed: isInstalled,
path: result.path || null,
version: result.version || null,
method: result.method || 'none',
diff --git a/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
index 529cfc02..9637a081 100644
--- a/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
@@ -15,7 +15,6 @@ import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
Loader2,
- Terminal,
Key,
ArrowRight,
ArrowLeft,
@@ -31,6 +30,7 @@ import {
import { toast } from 'sonner';
import { StatusBadge, TerminalOutput } from '../components';
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
+import { AnthropicIcon } from '@/components/ui/provider-icon';
interface ClaudeSetupStepProps {
onNext: () => void;
@@ -310,7 +310,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
Claude Code Setup
Configure for code generation
@@ -339,7 +339,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
-
-
+
Cursor CLI Setup
Optional - Use Cursor as an AI provider
@@ -195,7 +195,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
-
+
Cursor CLI Status
Optional
diff --git a/apps/ui/src/hooks/use-electron-agent.ts b/apps/ui/src/hooks/use-electron-agent.ts
index a1037fe8..83ab5477 100644
--- a/apps/ui/src/hooks/use-electron-agent.ts
+++ b/apps/ui/src/hooks/use-electron-agent.ts
@@ -329,6 +329,17 @@ export function useElectronAgent({
if (event.message) {
const errorMessage = event.message;
setMessages((prev) => [...prev, errorMessage]);
+ } else {
+ // Some providers stream an error without a message payload. Ensure the
+ // user still sees a clear error bubble in the chat.
+ const fallbackMessage: Message = {
+ id: `err_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
+ role: 'assistant',
+ content: `Error: ${event.error}`,
+ timestamp: new Date().toISOString(),
+ isError: true,
+ };
+ setMessages((prev) => [...prev, fallbackMessage]);
}
break;
diff --git a/libs/platform/src/subprocess.ts b/libs/platform/src/subprocess.ts
index 7d079863..7634dc5c 100644
--- a/libs/platform/src/subprocess.ts
+++ b/libs/platform/src/subprocess.ts
@@ -44,11 +44,15 @@ export async function* spawnJSONLProcess(options: SubprocessOptions): AsyncGener
console.log(`[SubprocessManager] Passing ${stdinData.length} bytes via stdin`);
}
+ // On Windows, .cmd files must be run through shell (cmd.exe)
+ const needsShell = process.platform === 'win32' && command.toLowerCase().endsWith('.cmd');
+
const childProcess: ChildProcess = spawn(command, args, {
cwd,
env: processEnv,
// Use 'pipe' for stdin when we need to write data, otherwise 'ignore'
stdio: [stdinData ? 'pipe' : 'ignore', 'pipe', 'pipe'],
+ shell: needsShell,
});
// Write stdin data if provided
@@ -194,10 +198,14 @@ export async function spawnProcess(options: SubprocessOptions): Promise {
+ // On Windows, .cmd files must be run through shell (cmd.exe)
+ const needsShell = process.platform === 'win32' && command.toLowerCase().endsWith('.cmd');
+
const childProcess = spawn(command, args, {
cwd,
env: processEnv,
stdio: ['ignore', 'pipe', 'pipe'],
+ shell: needsShell,
});
let stdout = '';