Merge branch 'main' into fix-build

This commit is contained in:
Cody Seibert
2025-12-10 23:37:14 -05:00
14 changed files with 247 additions and 433 deletions

View File

@@ -768,16 +768,14 @@ ${Object.entries(projectAnalysis.filesByExtension)
throw new Error("Features API not available");
}
// Convert DetectedFeature to Feature by adding required id and status
for (const detectedFeature of detectedFeatures) {
const feature: Feature = {
await api.features.create(currentProject.path, {
id: crypto.randomUUID(),
category: detectedFeature.category,
description: detectedFeature.description,
steps: detectedFeature.steps,
status: "backlog" as const,
};
await api.features.create(currentProject.path, feature);
status: "backlog",
});
}
setFeatureListGenerated(true);

View File

@@ -325,6 +325,7 @@ export function InterviewView() {
],
skipTests: true,
};
if (!api.features) {
throw new Error("Features API not available");
}

View File

@@ -53,41 +53,22 @@ export function AuthenticationStatusDisplay({
<>
<div className="flex items-center gap-2">
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
<span className="text-muted-foreground">
Method:{" "}
<span className="font-mono text-foreground">
{claudeAuthStatus.method === "oauth"
? "OAuth Token"
: claudeAuthStatus.method === "api_key"
? "API Key"
: "Unknown"}
</span>
<span className="text-green-400 font-medium">Authenticated</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Info className="w-3 h-3 shrink-0" />
<span>
{claudeAuthStatus.method === "oauth_token_env"
? "Using CLAUDE_CODE_OAUTH_TOKEN"
: claudeAuthStatus.method === "oauth_token"
? "Using stored OAuth token"
: claudeAuthStatus.method === "api_key_env"
? "Using ANTHROPIC_API_KEY"
: claudeAuthStatus.method === "api_key"
? "Using stored API key"
: "Unknown method"}
</span>
</div>
{claudeAuthStatus.oauthTokenValid && (
<div className="flex items-center gap-2 text-green-400">
<CheckCircle2 className="w-3 h-3 shrink-0" />
<span>OAuth token configured</span>
</div>
)}
{claudeAuthStatus.apiKeyValid && (
<div className="flex items-center gap-2 text-green-400">
<CheckCircle2 className="w-3 h-3 shrink-0" />
<span>API key configured</span>
</div>
)}
{apiKeyStatus?.hasAnthropicKey && (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Environment variable detected</span>
</div>
)}
{apiKeys.anthropic && (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Manual API key in settings</span>
</div>
)}
</>
) : apiKeyStatus?.hasAnthropicKey ? (
<div className="flex items-center gap-2 text-blue-400">
@@ -100,9 +81,9 @@ export function AuthenticationStatusDisplay({
<span>Using manual API key from settings</span>
</div>
) : (
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
<span className="text-xs">Not Setup</span>
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
<AlertCircle className="w-3 h-3 shrink-0" />
<span className="text-xs">Not configured</span>
</div>
)}
</div>
@@ -121,44 +102,21 @@ export function AuthenticationStatusDisplay({
<>
<div className="flex items-center gap-2">
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
<span className="text-muted-foreground">
Method:{" "}
<span className="font-mono text-foreground">
{codexAuthStatus.method === "cli_verified" ||
codexAuthStatus.method === "cli_tokens"
? "CLI Login (OpenAI Account)"
: codexAuthStatus.method === "api_key"
? "API Key (Auth File)"
: codexAuthStatus.method === "env"
? "API Key (Environment)"
: "Unknown"}
</span>
<span className="text-green-400 font-medium">Authenticated</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Info className="w-3 h-3 shrink-0" />
<span>
{codexAuthStatus.method === "cli_verified" ||
codexAuthStatus.method === "cli_tokens"
? "Using CLI login (OpenAI account)"
: codexAuthStatus.method === "api_key"
? "Using stored API key"
: codexAuthStatus.method === "env"
? "Using OPENAI_API_KEY"
: "Unknown method"}
</span>
</div>
{codexAuthStatus.method === "cli_verified" ||
codexAuthStatus.method === "cli_tokens" ? (
<div className="flex items-center gap-2 text-green-400">
<CheckCircle2 className="w-3 h-3 shrink-0" />
<span>Account authenticated</span>
</div>
) : codexAuthStatus.apiKeyValid ? (
<div className="flex items-center gap-2 text-green-400">
<CheckCircle2 className="w-3 h-3 shrink-0" />
<span>API key configured</span>
</div>
) : null}
{apiKeyStatus?.hasOpenAIKey && (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Environment variable detected</span>
</div>
)}
{apiKeys.openai && (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Manual API key in settings</span>
</div>
)}
</>
) : apiKeyStatus?.hasOpenAIKey ? (
<div className="flex items-center gap-2 text-blue-400">
@@ -171,9 +129,9 @@ export function AuthenticationStatusDisplay({
<span>Using manual API key from settings</span>
</div>
) : (
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
<span className="text-xs">Not Setup</span>
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
<AlertCircle className="w-3 h-3 shrink-0" />
<span className="text-xs">Not configured</span>
</div>
)}
</div>
@@ -189,19 +147,31 @@ export function AuthenticationStatusDisplay({
</div>
<div className="space-y-1.5 text-xs min-h-12">
{apiKeyStatus?.hasGoogleKey ? (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Using environment variable (GOOGLE_API_KEY)</span>
</div>
<>
<div className="flex items-center gap-2">
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
<span className="text-green-400 font-medium">Authenticated</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Info className="w-3 h-3 shrink-0" />
<span>Using GOOGLE_API_KEY</span>
</div>
</>
) : apiKeys.google ? (
<div className="flex items-center gap-2 text-blue-400">
<Info className="w-3 h-3 shrink-0" />
<span>Using manual API key from settings</span>
</div>
<>
<div className="flex items-center gap-2">
<CheckCircle2 className="w-3 h-3 text-green-500 shrink-0" />
<span className="text-green-400 font-medium">Authenticated</span>
</div>
<div className="flex items-center gap-2 text-muted-foreground">
<Info className="w-3 h-3 shrink-0" />
<span>Using stored API key</span>
</div>
</>
) : (
<div className="flex items-center gap-1.5 text-muted-foreground py-0.5">
<AlertCircle className="w-2.5 h-2.5 shrink-0" />
<span className="text-xs">Not Setup</span>
<div className="flex items-center gap-1.5 text-yellow-500 py-0.5">
<AlertCircle className="w-3 h-3 shrink-0" />
<span className="text-xs">Not configured</span>
</div>
)}
</div>

View File

@@ -69,17 +69,20 @@ export function useCliStatus() {
const result = await api.setup.getClaudeStatus();
if (result.success && result.auth) {
const auth = result.auth;
// Validate method is one of the expected values, default to "none"
const validMethods = ["oauth_token_env", "oauth_token", "api_key", "api_key_env", "none"] as const;
type AuthMethod = typeof validMethods[number];
const method: AuthMethod = validMethods.includes(auth.method as AuthMethod)
? (auth.method as AuthMethod)
: "none";
const authStatus = {
authenticated: auth.authenticated,
method:
auth.method === "oauth_token"
? ("oauth" as const)
: auth.method?.includes("api_key")
? ("api_key" as const)
: ("none" as const),
method,
hasCredentialsFile: auth.hasCredentialsFile ?? false,
oauthTokenValid: auth.hasStoredOAuthToken,
oauthTokenValid: auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
hasEnvOAuthToken: auth.hasEnvOAuthToken,
hasEnvApiKey: auth.hasEnvApiKey,
};
setClaudeAuthStatus(authStatus);
}

View File

@@ -250,21 +250,32 @@ function ClaudeSetupStep({
setClaudeCliStatus(cliStatus);
if (result.auth) {
// Validate method is one of the expected values, default to "none"
const validMethods = [
"oauth_token_env",
"oauth_token",
"api_key",
"api_key_env",
"none",
] as const;
type AuthMethod = (typeof validMethods)[number];
const method: AuthMethod = validMethods.includes(
result.auth.method as AuthMethod
)
? (result.auth.method as AuthMethod)
: "none";
const authStatus = {
authenticated: result.auth.authenticated,
method:
result.auth.method === "oauth_token"
? "oauth"
: result.auth.method?.includes("api_key")
? "api_key"
: "none",
method,
hasCredentialsFile: false,
oauthTokenValid: result.auth.hasStoredOAuthToken,
oauthTokenValid:
result.auth.hasStoredOAuthToken || result.auth.hasEnvOAuthToken,
apiKeyValid:
result.auth.hasStoredApiKey || result.auth.hasEnvApiKey,
hasEnvOAuthToken: result.auth.hasEnvOAuthToken,
hasEnvApiKey: result.auth.hasEnvApiKey,
};
console.log("[Claude Setup] Auth Status:", authStatus);
setClaudeAuthStatus(authStatus as any);
setClaudeAuthStatus(authStatus);
}
}
}
@@ -385,7 +396,7 @@ function ClaudeSetupStep({
if (result.success) {
setClaudeAuthStatus({
authenticated: true,
method: "oauth",
method: "oauth_token",
hasCredentialsFile: false,
oauthTokenValid: true,
});
@@ -463,8 +474,16 @@ function ClaudeSetupStep({
const getAuthMethodLabel = () => {
if (!isAuthenticated) return null;
if (claudeAuthStatus?.method === "oauth") return "Subscription Token";
if (apiKeys.anthropic || claudeAuthStatus?.method === "api_key")
if (
claudeAuthStatus?.method === "oauth_token_env" ||
claudeAuthStatus?.method === "oauth_token"
)
return "Subscription Token";
if (
apiKeys.anthropic ||
claudeAuthStatus?.method === "api_key" ||
claudeAuthStatus?.method === "api_key_env"
)
return "API Key";
return "Authenticated";
};

View File

@@ -41,8 +41,8 @@ export interface StatResult {
error?: string;
}
// Auto Mode types - Import from electron.d.ts to avoid duplication
import type {
// Re-export types from electron.d.ts for external use
export type {
AutoModeEvent,
ModelDefinition,
ProviderStatus,
@@ -55,6 +55,15 @@ import type {
FileStatus,
} from "@/types/electron";
// Import types for internal use in this file
import type {
AutoModeEvent,
WorktreeAPI,
GitAPI,
ModelDefinition,
ProviderStatus,
} from "@/types/electron";
// Feature type - Import from app-store
import type { Feature } from "@/store/app-store";
@@ -101,7 +110,11 @@ export interface SuggestionsEvent {
error?: string;
}
export type SuggestionType = "features" | "refactoring" | "security" | "performance";
export type SuggestionType =
| "features"
| "refactoring"
| "security"
| "performance";
export interface SuggestionsAPI {
generate: (
@@ -176,7 +189,9 @@ export interface AutoModeAPI {
projectPath: string,
maxConcurrency?: number
) => Promise<{ success: boolean; error?: string }>;
stop: (projectPath: string) => Promise<{ success: boolean; error?: string; runningFeatures?: number }>;
stop: (
projectPath: string
) => Promise<{ success: boolean; error?: string; runningFeatures?: number }>;
stopFeature: (
featureId: string
) => Promise<{ success: boolean; error?: string }>;
@@ -231,7 +246,9 @@ export interface SaveImageResult {
export interface ElectronAPI {
ping: () => Promise<string>;
openExternalLink: (url: string) => Promise<{ success: boolean; error?: string }>;
openExternalLink: (
url: string
) => Promise<{ success: boolean; error?: string }>;
openDirectory: () => Promise<DialogResult>;
openFile: (options?: object) => Promise<DialogResult>;
readFile: (filePath: string) => Promise<FileResult>;
@@ -308,17 +325,19 @@ export interface ElectronAPI {
getClaudeStatus: () => Promise<{
success: boolean;
status?: string;
installed?: boolean;
method?: string;
version?: string;
path?: string;
auth?: {
authenticated: boolean;
method: string;
hasCredentialsFile: boolean;
hasToken: boolean;
hasCredentialsFile?: boolean;
hasToken?: boolean;
hasStoredOAuthToken?: boolean;
hasStoredApiKey?: boolean;
hasEnvApiKey?: boolean;
hasEnvOAuthToken?: boolean;
};
error?: string;
}>;
@@ -1481,7 +1500,10 @@ let mockSuggestionsTimeout: NodeJS.Timeout | null = null;
function createMockSuggestionsAPI(): SuggestionsAPI {
return {
generate: async (projectPath: string, suggestionType: SuggestionType = "features") => {
generate: async (
projectPath: string,
suggestionType: SuggestionType = "features"
) => {
if (mockSuggestionsRunning) {
return {
success: false,
@@ -1490,7 +1512,9 @@ function createMockSuggestionsAPI(): SuggestionsAPI {
}
mockSuggestionsRunning = true;
console.log(`[Mock] Generating ${suggestionType} suggestions for: ${projectPath}`);
console.log(
`[Mock] Generating ${suggestionType} suggestions for: ${projectPath}`
);
// Simulate async suggestion generation
simulateSuggestionsGeneration(suggestionType);
@@ -1529,7 +1553,9 @@ function emitSuggestionsEvent(event: SuggestionsEvent) {
mockSuggestionsCallbacks.forEach((cb) => cb(event));
}
async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "features") {
async function simulateSuggestionsGeneration(
suggestionType: SuggestionType = "features"
) {
const typeLabels: Record<SuggestionType, string> = {
features: "feature suggestions",
refactoring: "refactoring opportunities",
@@ -1588,7 +1614,8 @@ async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "f
{
id: `suggestion-${Date.now()}-0`,
category: "Code Smell",
description: "Extract duplicate validation logic into reusable utility",
description:
"Extract duplicate validation logic into reusable utility",
steps: [
"Identify all files with similar validation patterns",
"Create a validation utilities module",
@@ -1601,7 +1628,8 @@ async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "f
{
id: `suggestion-${Date.now()}-1`,
category: "Complexity",
description: "Break down large handleSubmit function into smaller functions",
description:
"Break down large handleSubmit function into smaller functions",
steps: [
"Identify the handleSubmit function in form components",
"Extract validation logic into separate function",
@@ -1609,7 +1637,8 @@ async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "f
"Extract success/error handling into separate functions",
],
priority: 2,
reasoning: "Function is too long and handles multiple responsibilities",
reasoning:
"Function is too long and handles multiple responsibilities",
},
{
id: `suggestion-${Date.now()}-2`,
@@ -1728,7 +1757,8 @@ async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "f
"Add localStorage persistence for user preference",
],
priority: 1,
reasoning: "Dark mode is a standard feature that improves accessibility and user comfort",
reasoning:
"Dark mode is a standard feature that improves accessibility and user comfort",
},
{
id: `suggestion-${Date.now()}-1`,
@@ -1753,7 +1783,8 @@ async function simulateSuggestionsGeneration(suggestionType: SuggestionType = "f
"Add ARIA labels and roles where needed",
],
priority: 3,
reasoning: "Improves accessibility for users who rely on keyboard navigation",
reasoning:
"Improves accessibility for users who rely on keyboard navigation",
},
];
}

View File

@@ -19,7 +19,10 @@ export interface ProjectInitResult {
* Required files and directories in the .automaker directory
* Note: app_spec.txt is NOT created automatically - user must set it up via the spec editor
*/
const REQUIRED_STRUCTURE = {
const REQUIRED_STRUCTURE: {
directories: string[];
files: Record<string, string>;
} = {
directories: [
".automaker",
".automaker/context",

View File

@@ -13,10 +13,12 @@ export interface CliStatus {
// Claude Auth Status
export interface ClaudeAuthStatus {
authenticated: boolean;
method: "oauth" | "api_key" | "none";
hasCredentialsFile: boolean;
method: "oauth_token_env" | "oauth_token" | "api_key" | "api_key_env" | "none";
hasCredentialsFile?: boolean;
oauthTokenValid?: boolean;
apiKeyValid?: boolean;
hasEnvOAuthToken?: boolean;
hasEnvApiKey?: boolean;
error?: string;
}