diff --git a/auto-claude-ui/src/main/agent/agent-queue.ts b/auto-claude-ui/src/main/agent/agent-queue.ts index c864d964..eb5ad7be 100644 --- a/auto-claude-ui/src/main/agent/agent-queue.ts +++ b/auto-claude-ui/src/main/agent/agent-queue.ts @@ -195,14 +195,15 @@ export class AgentQueueManager { }; // Debug: Show OAuth token source - const tokenSource = profileEnv.CLAUDE_CODE_OAUTH_TOKEN + const tokenSource = profileEnv['CLAUDE_CODE_OAUTH_TOKEN'] ? 'Electron app profile' - : (combinedEnv.CLAUDE_CODE_OAUTH_TOKEN ? 'auto-claude/.env' : 'not found'); - const hasToken = !!finalEnv.CLAUDE_CODE_OAUTH_TOKEN; + : (combinedEnv['CLAUDE_CODE_OAUTH_TOKEN'] ? 'auto-claude/.env' : 'not found'); + const oauthToken = (finalEnv as Record)['CLAUDE_CODE_OAUTH_TOKEN']; + const hasToken = !!oauthToken; debugLog('[Agent Queue] OAuth token status:', { source: tokenSource, hasToken, - tokenPreview: hasToken ? finalEnv.CLAUDE_CODE_OAUTH_TOKEN?.substring(0, 20) + '...' : 'none' + tokenPreview: hasToken ? oauthToken?.substring(0, 20) + '...' : 'none' }); const childProcess = spawn(pythonPath, args, { @@ -428,14 +429,15 @@ export class AgentQueueManager { }; // Debug: Show OAuth token source - const tokenSource = profileEnv.CLAUDE_CODE_OAUTH_TOKEN + const tokenSource = profileEnv['CLAUDE_CODE_OAUTH_TOKEN'] ? 'Electron app profile' - : (combinedEnv.CLAUDE_CODE_OAUTH_TOKEN ? 'auto-claude/.env' : 'not found'); - const hasToken = !!finalEnv.CLAUDE_CODE_OAUTH_TOKEN; + : (combinedEnv['CLAUDE_CODE_OAUTH_TOKEN'] ? 'auto-claude/.env' : 'not found'); + const oauthToken = (finalEnv as Record)['CLAUDE_CODE_OAUTH_TOKEN']; + const hasToken = !!oauthToken; debugLog('[Agent Queue] OAuth token status:', { source: tokenSource, hasToken, - tokenPreview: hasToken ? finalEnv.CLAUDE_CODE_OAUTH_TOKEN?.substring(0, 20) + '...' : 'none' + tokenPreview: hasToken ? oauthToken?.substring(0, 20) + '...' : 'none' }); const childProcess = spawn(pythonPath, args, { diff --git a/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts index 200c02dd..2dc363ac 100644 --- a/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/task/worktree-handlers.ts @@ -541,7 +541,7 @@ export function registerWorktreeHandlers( uncommittedFiles = gitStatus .split('\n') .filter(line => line.trim()) - .map(line => line.substring(3)); // Skip 2 status chars + 1 space + .map(line => line.substring(3).trim()); // Skip 2 status chars + 1 space, trim any trailing whitespace hasUncommittedChanges = uncommittedFiles.length > 0; } diff --git a/auto-claude-ui/src/main/updater/http-client.ts b/auto-claude-ui/src/main/updater/http-client.ts index 69b4d469..3ce86850 100644 --- a/auto-claude-ui/src/main/updater/http-client.ts +++ b/auto-claude-ui/src/main/updater/http-client.ts @@ -70,7 +70,7 @@ export function downloadFile( const headers = { 'User-Agent': 'Auto-Claude-UI', - 'Accept': 'application/vnd.github+json' + 'Accept': 'application/octet-stream' }; const request = https.get(url, { headers }, (response) => { diff --git a/auto-claude-ui/src/renderer/components/EnvConfigModal.tsx b/auto-claude-ui/src/renderer/components/EnvConfigModal.tsx index 5ae2eadf..b51952f7 100644 --- a/auto-claude-ui/src/renderer/components/EnvConfigModal.tsx +++ b/auto-claude-ui/src/renderer/components/EnvConfigModal.tsx @@ -29,6 +29,7 @@ import { TooltipContent, TooltipTrigger } from './ui/tooltip'; +import { cn } from '../lib/utils'; interface EnvConfigModalProps { open: boolean; @@ -36,6 +37,7 @@ interface EnvConfigModalProps { onConfigured?: () => void; title?: string; description?: string; + projectId?: string; } export function EnvConfigModal({ @@ -43,7 +45,8 @@ export function EnvConfigModal({ onOpenChange, onConfigured, title = 'Claude Authentication Required', - description = 'A Claude Code OAuth token is required to use AI features like Ideation and Roadmap generation.' + description = 'A Claude Code OAuth token is required to use AI features like Ideation and Roadmap generation.', + projectId }: EnvConfigModalProps) { const [token, setToken] = useState(''); const [showToken, setShowToken] = useState(false); @@ -123,28 +126,18 @@ export function EnvConfigModal({ if (!open) return; const cleanup = window.electronAPI.onTerminalOAuthToken(async (info) => { - if (info.success && info.token) { - // Save the OAuth token - try { - const result = await window.electronAPI.updateSourceEnv({ - claudeOAuthToken: info.token - }); + if (info.success) { + // Token is auto-saved to the profile by the main process + // Just update UI state to reflect authentication success + setSuccess(true); + setHasExistingToken(true); + setIsAuthenticating(false); - if (result.success) { - setSuccess(true); - setHasExistingToken(true); - setIsAuthenticating(false); - - // Notify parent - setTimeout(() => { - onConfigured?.(); - onOpenChange(false); - }, 1500); - } - } catch (err) { - setError('Failed to save authentication token'); - setIsAuthenticating(false); - } + // Notify parent + setTimeout(() => { + onConfigured?.(); + onOpenChange(false); + }, 1500); } }); @@ -191,12 +184,17 @@ export function EnvConfigModal({ }; const handleAuthenticateWithBrowser = async () => { + if (!projectId) { + setError('No project selected. Please select a project first.'); + return; + } + setIsAuthenticating(true); setError(null); try { // Invoke the Claude setup-token flow in terminal - const result = await window.electronAPI.invokeClaudeSetup(); + const result = await window.electronAPI.invokeClaudeSetup(projectId); if (!result.success) { setError(result.error || 'Failed to start authentication'); diff --git a/auto-claude-ui/src/renderer/components/github-issues/components/InvestigationDialog.tsx b/auto-claude-ui/src/renderer/components/github-issues/components/InvestigationDialog.tsx index 7e33a2e1..423c455f 100644 --- a/auto-claude-ui/src/renderer/components/github-issues/components/InvestigationDialog.tsx +++ b/auto-claude-ui/src/renderer/components/github-issues/components/InvestigationDialog.tsx @@ -44,14 +44,14 @@ export function InvestigationDialog({ setSelectedCommentIds([]); window.electronAPI.getIssueComments(projectId, selectedIssue.number) - .then((result) => { + .then((result: { success: boolean; data?: GitHubComment[] }) => { if (result.success && result.data) { setComments(result.data); // By default, select all comments - setSelectedCommentIds(result.data.map(c => c.id)); + setSelectedCommentIds(result.data.map((c: GitHubComment) => c.id)); } }) - .catch((err) => { + .catch((err: unknown) => { console.error('Failed to fetch comments:', err); }) .finally(() => { diff --git a/auto-claude-ui/src/renderer/components/ideation/Ideation.tsx b/auto-claude-ui/src/renderer/components/ideation/Ideation.tsx index 2c1022ba..72ac1061 100644 --- a/auto-claude-ui/src/renderer/components/ideation/Ideation.tsx +++ b/auto-claude-ui/src/renderer/components/ideation/Ideation.tsx @@ -116,6 +116,7 @@ export function Ideation({ projectId, onGoToTask }: IdeationProps) { onConfigured={handleEnvConfigured} title="Claude Authentication Required" description="A Claude Code OAuth token is required to generate AI-powered feature ideas." + projectId={projectId} /> ); @@ -239,6 +240,7 @@ export function Ideation({ projectId, onGoToTask }: IdeationProps) { onConfigured={handleEnvConfigured} title="Claude Authentication Required" description="A Claude Code OAuth token is required to generate AI-powered feature ideas." + projectId={projectId} /> ); diff --git a/auto-claude-ui/src/renderer/components/roadmap/RoadmapHeader.tsx b/auto-claude-ui/src/renderer/components/roadmap/RoadmapHeader.tsx index 1c416260..35294c65 100644 --- a/auto-claude-ui/src/renderer/components/roadmap/RoadmapHeader.tsx +++ b/auto-claude-ui/src/renderer/components/roadmap/RoadmapHeader.tsx @@ -28,7 +28,7 @@ export function RoadmapHeader({ roadmap, competitorAnalysis, onAddFeature, onRef
Analyzed {competitorAnalysis.competitors.length} competitors:
- {competitorAnalysis.competitors.map((comp, idx) => ( + {competitorAnalysis.competitors.map((comp: { name: string; painPoints: unknown[] }, idx: number) => (
• {comp.name}
{comp.painPoints.length} pain points identified
diff --git a/auto-claude-ui/src/renderer/components/roadmap/types.ts b/auto-claude-ui/src/renderer/components/roadmap/types.ts index 797a6f7b..db1d85c7 100644 --- a/auto-claude-ui/src/renderer/components/roadmap/types.ts +++ b/auto-claude-ui/src/renderer/components/roadmap/types.ts @@ -1,4 +1,4 @@ -import type { RoadmapFeature, RoadmapPhase, Roadmap, CompetitorPainPoint } from '../../../shared/types'; +import type { RoadmapFeature, RoadmapPhase, Roadmap, CompetitorPainPoint, CompetitorAnalysis } from '../../../shared/types'; export interface RoadmapProps { projectId: string; diff --git a/auto-claude-ui/src/renderer/lib/browser-mock.ts b/auto-claude-ui/src/renderer/lib/browser-mock.ts index 52e5cdad..3600a6f4 100644 --- a/auto-claude-ui/src/renderer/lib/browser-mock.ts +++ b/auto-claude-ui/src/renderer/lib/browser-mock.ts @@ -83,10 +83,13 @@ const browserMockAPI: ElectronAPI = { } }), + stopRoadmap: async () => ({ success: true }), + // Roadmap Event Listeners onRoadmapProgress: () => () => {}, onRoadmapComplete: () => () => {}, onRoadmapError: () => () => {}, + onRoadmapStopped: () => () => {}, // Context Operations ...contextMock, diff --git a/auto-claude-ui/src/renderer/lib/mocks/integration-mock.ts b/auto-claude-ui/src/renderer/lib/mocks/integration-mock.ts index 657fdd88..6a5a297d 100644 --- a/auto-claude-ui/src/renderer/lib/mocks/integration-mock.ts +++ b/auto-claude-ui/src/renderer/lib/mocks/integration-mock.ts @@ -117,6 +117,11 @@ export const integrationMock = { console.warn('[Browser Mock] investigateGitHubIssue called'); }, + getIssueComments: async () => ({ + success: true, + data: [] + }), + importGitHubIssues: async () => ({ success: false, error: 'Not available in browser mock' diff --git a/auto-claude-ui/src/shared/types/ipc.ts b/auto-claude-ui/src/shared/types/ipc.ts index 47c13e68..c2fe0beb 100644 --- a/auto-claude-ui/src/shared/types/ipc.ts +++ b/auto-claude-ui/src/shared/types/ipc.ts @@ -304,7 +304,8 @@ export interface ElectronAPI { getGitHubIssues: (projectId: string, state?: 'open' | 'closed' | 'all') => Promise>; getGitHubIssue: (projectId: string, issueNumber: number) => Promise>; checkGitHubConnection: (projectId: string) => Promise>; - investigateGitHubIssue: (projectId: string, issueNumber: number) => void; + investigateGitHubIssue: (projectId: string, issueNumber: number, selectedCommentIds?: number[]) => void; + getIssueComments: (projectId: string, issueNumber: number) => Promise>>; importGitHubIssues: (projectId: string, issueNumbers: number[]) => Promise>; createGitHubRelease: ( projectId: string, diff --git a/auto-claude/core/workspace.py b/auto-claude/core/workspace.py index 601f16f0..e4c58969 100644 --- a/auto-claude/core/workspace.py +++ b/auto-claude/core/workspace.py @@ -197,7 +197,9 @@ def merge_existing_build( if had_conflicts: # Git conflicts were resolved (via AI or lock file exclusion) - changes are already staged - _print_merge_success(no_commit, stats, spec_name=spec_name, keep_worktree=True) + _print_merge_success( + no_commit, stats, spec_name=spec_name, keep_worktree=True + ) # Don't auto-delete worktree - let user test and manually cleanup # User can delete with: python auto-claude/run.py --spec --discard @@ -210,7 +212,9 @@ def merge_existing_build( spec_name, delete_after=False, no_commit=no_commit ) if success_result: - _print_merge_success(no_commit, stats, spec_name=spec_name, keep_worktree=True) + _print_merge_success( + no_commit, stats, spec_name=spec_name, keep_worktree=True + ) return True elif smart_result.get("git_conflicts"): # Had git conflicts that AI couldn't fully resolve diff --git a/auto-claude/core/workspace/display.py b/auto-claude/core/workspace/display.py index 24b104ed..c550f7b3 100644 --- a/auto-claude/core/workspace/display.py +++ b/auto-claude/core/workspace/display.py @@ -74,7 +74,7 @@ def print_merge_success( no_commit: bool, stats: dict | None = None, spec_name: str | None = None, - keep_worktree: bool = False + keep_worktree: bool = False, ) -> None: """Print a success message after merge.""" from ui import Icons, box, icon @@ -131,7 +131,9 @@ def print_merge_success( ] ) if spec_name: - lines.append(f" python auto-claude/run.py --spec {spec_name} --discard") + lines.append( + f" python auto-claude/run.py --spec {spec_name} --discard" + ) else: lines.extend( [ diff --git a/auto-claude/ui/menu.py b/auto-claude/ui/menu.py index e3fdb439..3252b4f7 100644 --- a/auto-claude/ui/menu.py +++ b/auto-claude/ui/menu.py @@ -12,12 +12,14 @@ from dataclasses import dataclass try: import termios import tty + _HAS_TERMIOS = True except ImportError: _HAS_TERMIOS = False try: import msvcrt + _HAS_MSVCRT = True except ImportError: _HAS_MSVCRT = False @@ -45,18 +47,18 @@ def _getch() -> str: # Windows implementation ch = msvcrt.getch() # Handle special keys (arrow keys return two bytes) - if ch in (b'\x00', b'\xe0'): + if ch in (b"\x00", b"\xe0"): ch2 = msvcrt.getch() - if ch2 == b'H': + if ch2 == b"H": return "UP" - elif ch2 == b'P': + elif ch2 == b"P": return "DOWN" - elif ch2 == b'M': + elif ch2 == b"M": return "RIGHT" - elif ch2 == b'K': + elif ch2 == b"K": return "LEFT" return "" - return ch.decode('utf-8', errors='replace') + return ch.decode("utf-8", errors="replace") elif _HAS_TERMIOS: # Unix implementation fd = sys.stdin.fileno()