fix: resolve CI lint and TypeScript errors
- Fix Python formatting (ruff) in workspace.py, display.py, menu.py - Fix TypeScript errors: - Add missing CompetitorAnalysis import in types.ts - Add type annotations to RoadmapHeader.tsx map callback - Add cn import and fix EnvConfigModal OAuth token handling - Add type annotations to InvestigationDialog.tsx - Add missing stopRoadmap and onRoadmapStopped to browser-mock - Add getIssueComments to ElectronAPI type and mocks - Update investigateGitHubIssue to accept optional selectedCommentIds - Fix CLAUDE_CODE_OAUTH_TOKEN access in agent-queue.ts - Include pending bug fixes: - Add .trim() to git status parsing in worktree-handlers.ts - Change Accept header to application/octet-stream in http-client.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, string | undefined>)['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<string, string | undefined>)['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, {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
+3
-3
@@ -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(() => {
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ export function RoadmapHeader({ roadmap, competitorAnalysis, onAddFeature, onRef
|
||||
<TooltipContent className="max-w-md">
|
||||
<div className="space-y-2">
|
||||
<div className="font-semibold">Analyzed {competitorAnalysis.competitors.length} competitors:</div>
|
||||
{competitorAnalysis.competitors.map((comp, idx) => (
|
||||
{competitorAnalysis.competitors.map((comp: { name: string; painPoints: unknown[] }, idx: number) => (
|
||||
<div key={idx} className="text-sm">
|
||||
<div className="font-medium">• {comp.name}</div>
|
||||
<div className="text-muted-foreground ml-3">{comp.painPoints.length} pain points identified</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -83,10 +83,13 @@ const browserMockAPI: ElectronAPI = {
|
||||
}
|
||||
}),
|
||||
|
||||
stopRoadmap: async () => ({ success: true }),
|
||||
|
||||
// Roadmap Event Listeners
|
||||
onRoadmapProgress: () => () => {},
|
||||
onRoadmapComplete: () => () => {},
|
||||
onRoadmapError: () => () => {},
|
||||
onRoadmapStopped: () => () => {},
|
||||
// Context Operations
|
||||
...contextMock,
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -304,7 +304,8 @@ export interface ElectronAPI {
|
||||
getGitHubIssues: (projectId: string, state?: 'open' | 'closed' | 'all') => Promise<IPCResult<GitHubIssue[]>>;
|
||||
getGitHubIssue: (projectId: string, issueNumber: number) => Promise<IPCResult<GitHubIssue>>;
|
||||
checkGitHubConnection: (projectId: string) => Promise<IPCResult<GitHubSyncStatus>>;
|
||||
investigateGitHubIssue: (projectId: string, issueNumber: number) => void;
|
||||
investigateGitHubIssue: (projectId: string, issueNumber: number, selectedCommentIds?: number[]) => void;
|
||||
getIssueComments: (projectId: string, issueNumber: number) => Promise<IPCResult<Array<{ id: number; body: string; user: { login: string; avatar_url?: string }; created_at: string; updated_at: string }>>>;
|
||||
importGitHubIssues: (projectId: string, issueNumbers: number[]) => Promise<IPCResult<GitHubImportResult>>;
|
||||
createGitHubRelease: (
|
||||
projectId: string,
|
||||
|
||||
@@ -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 <name> --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
|
||||
|
||||
@@ -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(
|
||||
[
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user