chore: update project management and API integration

- Added new scripts for server development and full application startup in package.json.
- Enhanced project management by checking for existing projects to avoid duplicates.
- Improved API integration with better error handling and connection checks in the Electron API.
- Updated UI components to reflect changes in project and session management.
- Refactored authentication status display to include more detailed information on methods used.
This commit is contained in:
SuperComboGamer
2025-12-12 00:23:43 -05:00
parent 02a1af3314
commit 4b9bd2641f
44 changed files with 8287 additions and 703 deletions

View File

@@ -149,12 +149,12 @@ export function AgentToolsView() {
setTerminalResult(null);
try {
// Simulate agent requesting terminal command execution
console.log(`[Agent Tool] Requesting to run command: ${terminalCommand}`);
// Terminal command simulation for demonstration purposes
console.log(`[Agent Tool] Simulating command: ${terminalCommand}`);
// In mock mode, simulate terminal output
// In real Electron mode, this would use child_process
const mockOutputs: Record<string, string> = {
// Simulated outputs for common commands (preview mode)
// In production, the agent executes commands via Claude SDK
const simulatedOutputs: Record<string, string> = {
ls: "app_spec.txt\nfeatures\nnode_modules\npackage.json\nsrc\ntests\ntsconfig.json",
pwd: currentProject?.path || "/Users/demo/project",
"echo hello": "hello",
@@ -168,8 +168,8 @@ export function AgentToolsView() {
await new Promise((resolve) => setTimeout(resolve, 500));
const output =
mockOutputs[terminalCommand.toLowerCase()] ||
`Command executed: ${terminalCommand}\n(Mock output - real execution requires Electron mode)`;
simulatedOutputs[terminalCommand.toLowerCase()] ||
`[Preview] ${terminalCommand}\n(Terminal commands are executed by the agent during feature implementation)`;
setTerminalResult({
success: true,

View File

@@ -394,22 +394,25 @@ export function BoardView() {
}, []);
// Load features using features API
// IMPORTANT: Do NOT add 'features' to dependency array - it would cause infinite reload loop
const loadFeatures = useCallback(async () => {
if (!currentProject) return;
const currentPath = currentProject.path;
const previousPath = prevProjectPathRef.current;
const isProjectSwitch = previousPath !== null && currentPath !== previousPath;
// If project switched, clear features first to prevent cross-contamination
// Also treat this as an initial load for the new project
if (previousPath !== null && currentPath !== previousPath) {
// Get cached features from store (without adding to dependencies)
const cachedFeatures = useAppStore.getState().features;
// If project switched, mark it but don't clear features yet
// We'll clear after successful API load to prevent data loss
if (isProjectSwitch) {
console.log(
`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}, clearing features`
`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`
);
isSwitchingProjectRef.current = true;
isInitialLoadRef.current = true;
setFeatures([]);
setPersistedCategories([]); // Also clear categories
}
// Update the ref to track current project
@@ -424,6 +427,7 @@ export function BoardView() {
const api = getElectronAPI();
if (!api.features) {
console.error("[BoardView] Features API not available");
// Keep cached features if API is unavailable
return;
}
@@ -441,10 +445,31 @@ export function BoardView() {
thinkingLevel: f.thinkingLevel || "none",
})
);
// Successfully loaded features - now safe to set them
setFeatures(featuresWithIds);
// Only clear categories on project switch AFTER successful load
if (isProjectSwitch) {
setPersistedCategories([]);
}
} else if (!result.success && result.error) {
console.error("[BoardView] API returned error:", result.error);
// If it's a new project or the error indicates no features found,
// that's expected - start with empty array
if (isProjectSwitch) {
setFeatures([]);
setPersistedCategories([]);
}
// Otherwise keep cached features
}
} catch (error) {
console.error("Failed to load features:", error);
// On error, keep existing cached features for the current project
// Only clear on project switch if we have no features from server
if (isProjectSwitch && cachedFeatures.length === 0) {
setFeatures([]);
setPersistedCategories([]);
}
} finally {
setIsLoading(false);
isInitialLoadRef.current = false;
@@ -1475,8 +1500,14 @@ export function BoardView() {
if (isRunning) {
map.in_progress.push(f);
} else {
// Otherwise, use the feature's status
map[f.status].push(f);
// Otherwise, use the feature's status (fallback to backlog for unknown statuses)
const status = f.status as ColumnId;
if (map[status]) {
map[status].push(f);
} else {
// Unknown status, default to backlog
map.backlog.push(f);
}
}
});

View File

@@ -61,12 +61,14 @@ export function AuthenticationStatusDisplay({
{claudeAuthStatus.method === "oauth_token_env"
? "Using CLAUDE_CODE_OAUTH_TOKEN"
: claudeAuthStatus.method === "oauth_token"
? "Using stored OAuth token"
? "Using stored OAuth token (claude login)"
: claudeAuthStatus.method === "api_key_env"
? "Using ANTHROPIC_API_KEY"
: claudeAuthStatus.method === "api_key"
? "Using stored API key"
: "Unknown method"}
: claudeAuthStatus.method === "credentials_file"
? "Using credentials file"
: `Using ${claudeAuthStatus.method || "detected"} authentication`}
</span>
</div>
</>
@@ -107,14 +109,16 @@ export function AuthenticationStatusDisplay({
<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"
{codexAuthStatus.method === "subscription"
? "Using Codex subscription (Plus/Team)"
: 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"}
: `Using ${codexAuthStatus.method || "unknown"} authentication`}
</span>
</div>
</>

View File

@@ -68,19 +68,24 @@ export function useCliStatus() {
try {
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;
// Cast to extended type that includes server-added fields
const auth = result.auth as typeof result.auth & {
oauthTokenValid?: boolean;
apiKeyValid?: boolean;
};
// Map server method names to client method types
// Server returns: oauth_token_env, oauth_token, api_key_env, api_key, credentials_file, none
const validMethods = ["oauth_token_env", "oauth_token", "api_key", "api_key_env", "credentials_file", "none"] as const;
type AuthMethod = typeof validMethods[number];
const method: AuthMethod = validMethods.includes(auth.method as AuthMethod)
? (auth.method as AuthMethod)
: "none";
: auth.authenticated ? "api_key" : "none"; // Default authenticated to api_key, not none
const authStatus = {
authenticated: auth.authenticated,
method,
hasCredentialsFile: auth.hasCredentialsFile ?? false,
oauthTokenValid: auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
apiKeyValid: auth.hasStoredApiKey || auth.hasEnvApiKey,
oauthTokenValid: auth.oauthTokenValid || auth.hasStoredOAuthToken || auth.hasEnvOAuthToken,
apiKeyValid: auth.apiKeyValid || auth.hasStoredApiKey || auth.hasEnvApiKey,
hasEnvOAuthToken: auth.hasEnvOAuthToken,
hasEnvApiKey: auth.hasEnvApiKey,
};
@@ -96,27 +101,30 @@ export function useCliStatus() {
try {
const result = await api.setup.getCodexStatus();
if (result.success && result.auth) {
const auth = result.auth;
// Determine method - prioritize cli_verified and cli_tokens over auth_file
const method =
auth.method === "cli_verified" || auth.method === "cli_tokens"
? auth.method === "cli_verified"
? ("cli_verified" as const)
: ("cli_tokens" as const)
: auth.method === "auth_file"
? ("api_key" as const)
: auth.method === "env_var"
? ("env" as const)
: ("none" as const);
// Cast to extended type that includes server-added fields
const auth = result.auth as typeof result.auth & {
hasSubscription?: boolean;
cliLoggedIn?: boolean;
hasEnvApiKey?: boolean;
};
// Map server method names to client method types
// Server returns: subscription, cli_verified, cli_tokens, api_key, env, none
const validMethods = ["subscription", "cli_verified", "cli_tokens", "api_key", "env", "none"] as const;
type CodexMethod = typeof validMethods[number];
const method: CodexMethod = validMethods.includes(auth.method as CodexMethod)
? (auth.method as CodexMethod)
: auth.authenticated ? "api_key" : "none"; // Default authenticated to api_key
const authStatus = {
authenticated: auth.authenticated,
method,
// Only set apiKeyValid for actual API key methods, not CLI login
// Only set apiKeyValid for actual API key methods, not CLI login or subscription
apiKeyValid:
method === "cli_verified" || method === "cli_tokens"
method === "cli_verified" || method === "cli_tokens" || method === "subscription"
? undefined
: auth.hasAuthFile || auth.hasEnvKey,
: auth.hasAuthFile || auth.hasEnvKey || auth.hasEnvApiKey,
hasSubscription: auth.hasSubscription,
cliLoggedIn: auth.cliLoggedIn,
};
setCodexAuthStatus(authStatus);
}

View File

@@ -21,7 +21,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { useAppStore } from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import { getElectronAPI, type Project } from "@/lib/electron";
import { initializeProject } from "@/lib/project-init";
import {
FolderOpen,
@@ -105,14 +105,34 @@ export function WelcomeView() {
return;
}
const project = {
id: `project-${Date.now()}`,
name,
path,
lastOpened: new Date().toISOString(),
};
// Check if project already exists (by path) to preserve theme and other settings
const existingProject = projects.find((p) => p.path === path);
let project: Project;
if (existingProject) {
// Update existing project, preserving theme and other properties
project = {
...existingProject,
name, // Update name in case it changed
lastOpened: new Date().toISOString(),
};
// Update the project in the store (this will update the existing entry)
const updatedProjects = projects.map((p) =>
p.id === existingProject.id ? project : p
);
// We need to manually update projects since addProject would create a duplicate
useAppStore.setState({ projects: updatedProjects });
} else {
// Create new project
project = {
id: `project-${Date.now()}`,
name,
path,
lastOpened: new Date().toISOString(),
};
addProject(project);
}
addProject(project);
setCurrentProject(project);
// Show initialization dialog if files were created
@@ -148,7 +168,7 @@ export function WelcomeView() {
setIsOpening(false);
}
},
[addProject, setCurrentProject, analyzeProject]
[projects, addProject, setCurrentProject, analyzeProject]
);
const handleOpenProject = useCallback(async () => {