From 67a448ce9152a9a27a224624ee814fc9fa01a6b0 Mon Sep 17 00:00:00 2001
From: Cody Seibert
Date: Wed, 10 Dec 2025 23:10:04 -0500
Subject: [PATCH] feat: add PR build check workflow and enhance feature
management
- Introduced a new GitHub Actions workflow for PR build checks to ensure code quality and consistency.
- Updated `analysis-view.tsx`, `interview-view.tsx`, and `setup-view.tsx` to incorporate a new `Feature` type for better feature management.
- Refactored various components to improve code readability and maintainability.
- Adjusted type imports in `delete-project-dialog.tsx` and `settings-navigation.tsx` for consistency.
- Enhanced project initialization logic in `project-init.ts` to ensure proper type handling.
- Updated Electron API types in `electron.d.ts` for better clarity and functionality.
---
.github/workflows/pr-check.yml | 33 ++
app/src/components/views/analysis-view.tsx | 19 +-
app/src/components/views/interview-view.tsx | 9 +-
.../components/delete-project-dialog.tsx | 13 +-
.../components/settings-navigation.tsx | 2 +-
app/src/components/views/setup-view.tsx | 348 +++++++++++++-----
app/src/lib/project-init.ts | 2 +-
app/src/types/electron.d.ts | 91 ++++-
8 files changed, 390 insertions(+), 127 deletions(-)
create mode 100644 .github/workflows/pr-check.yml
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
new file mode 100644
index 00000000..7dab6afe
--- /dev/null
+++ b/.github/workflows/pr-check.yml
@@ -0,0 +1,33 @@
+name: PR Build Check
+
+on:
+ pull_request:
+ branches:
+ - "*"
+ push:
+ branches:
+ - main
+ - master
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+ cache: "npm"
+ cache-dependency-path: app/package-lock.json
+
+ - name: Install dependencies
+ working-directory: ./app
+ run: npm ci
+
+ - name: Run build:electron
+ working-directory: ./app
+ run: npm run build:electron
diff --git a/app/src/components/views/analysis-view.tsx b/app/src/components/views/analysis-view.tsx
index 4a6d7237..1fb2852d 100644
--- a/app/src/components/views/analysis-view.tsx
+++ b/app/src/components/views/analysis-view.tsx
@@ -1,7 +1,12 @@
"use client";
import { useCallback, useState } from "react";
-import { useAppStore, FileTreeNode, ProjectAnalysis } from "@/store/app-store";
+import {
+ useAppStore,
+ FileTreeNode,
+ ProjectAnalysis,
+ Feature,
+} from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import {
Card,
@@ -763,7 +768,17 @@ ${Object.entries(projectAnalysis.filesByExtension)
throw new Error("Features API not available");
}
- for (const feature of detectedFeatures) {
+ // Convert DetectedFeature to Feature by adding required id and status
+ for (const detectedFeature of detectedFeatures) {
+ const feature: Feature = {
+ id: `feature-${Date.now()}-${Math.random()
+ .toString(36)
+ .substr(2, 9)}`,
+ category: detectedFeature.category,
+ description: detectedFeature.description,
+ steps: detectedFeature.steps,
+ status: "backlog" as const,
+ };
await api.features.create(currentProject.path, feature);
}
diff --git a/app/src/components/views/interview-view.tsx b/app/src/components/views/interview-view.tsx
index 144be421..354858b8 100644
--- a/app/src/components/views/interview-view.tsx
+++ b/app/src/components/views/interview-view.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState, useCallback, useRef, useEffect } from "react";
-import { useAppStore } from "@/store/app-store";
+import { useAppStore, Feature } from "@/store/app-store";
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -313,11 +313,11 @@ export function InterviewView() {
);
// Create initial feature in the features folder
- const initialFeature = {
+ const initialFeature: Feature = {
id: `feature-${Date.now()}-0`,
category: "Core",
description: "Initial project setup",
- status: "backlog",
+ status: "backlog" as const,
steps: [
"Step 1: Review app_spec.txt",
"Step 2: Set up development environment",
@@ -325,6 +325,9 @@ export function InterviewView() {
],
skipTests: true,
};
+ if (!api.features) {
+ throw new Error("Features API not available");
+ }
await api.features.create(fullProjectPath, initialFeature);
const project = {
diff --git a/app/src/components/views/settings-view/components/delete-project-dialog.tsx b/app/src/components/views/settings-view/components/delete-project-dialog.tsx
index 0ac5870b..c60ce76f 100644
--- a/app/src/components/views/settings-view/components/delete-project-dialog.tsx
+++ b/app/src/components/views/settings-view/components/delete-project-dialog.tsx
@@ -8,7 +8,7 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
-import type { Project } from "@/store/app-store";
+import type { Project } from "@/lib/electron";
interface DeleteProjectDialogProps {
open: boolean;
@@ -49,14 +49,19 @@ export function DeleteProjectDialog({
-
{project.name}
-
{project.path}
+
+ {project.name}
+
+
+ {project.path}
+
)}
- The folder will remain on disk until you permanently delete it from Trash.
+ The folder will remain on disk until you permanently delete it from
+ Trash.
- Let's set up your development environment. We'll check for required
- CLI tools and help you configure them.
+ Let's set up your development environment. We'll check for
+ required CLI tools and help you configure them.
- {getAuthMethodLabel() && `Using ${getAuthMethodLabel()}. `}You can proceed to the next step
+ {getAuthMethodLabel() && `Using ${getAuthMethodLabel()}. `}You
+ can proceed to the next step
@@ -736,15 +848,27 @@ function ClaudeSetupStep({
{/* Navigation */}
-
+
Back
-
+
Skip for now
-
+
Continue
@@ -804,7 +928,10 @@ function CodexSetupStep({
const setupApi = api.setup;
console.log("[Codex Setup] Setup API available:", !!setupApi);
- console.log("[Codex Setup] getCodexStatus available:", !!setupApi?.getCodexStatus);
+ console.log(
+ "[Codex Setup] getCodexStatus available:",
+ !!setupApi?.getCodexStatus
+ );
if (setupApi?.getCodexStatus) {
const result = await setupApi.getCodexStatus();
@@ -822,12 +949,15 @@ function CodexSetupStep({
if (result.auth) {
const method = mapAuthMethod(result.auth.method);
-
+
const authStatus: CodexAuthStatus = {
authenticated: result.auth.authenticated,
method,
// Only set apiKeyValid for actual API key methods, not CLI login
- apiKeyValid: method === "cli_verified" || method === "cli_tokens" ? undefined : result.auth.authenticated,
+ apiKeyValid:
+ method === "cli_verified" || method === "cli_tokens"
+ ? undefined
+ : result.auth.authenticated,
};
console.log("[Codex Setup] Auth Status:", authStatus);
setCodexAuthStatus(authStatus);
@@ -866,16 +996,18 @@ function CodexSetupStep({
const setupApi = api.setup;
if (setupApi?.installCodex) {
- const unsubscribe = setupApi.onInstallProgress?.((progress: { cli?: string; data?: string; type?: string }) => {
- if (progress.cli === "codex") {
- setCodexInstallProgress({
- output: [
- ...codexInstallProgress.output,
- progress.data || progress.type || "",
- ],
- });
+ const unsubscribe = setupApi.onInstallProgress?.(
+ (progress: { cli?: string; data?: string; type?: string }) => {
+ if (progress.cli === "codex") {
+ setCodexInstallProgress({
+ output: [
+ ...codexInstallProgress.output,
+ progress.data || progress.type || "",
+ ],
+ });
+ }
}
- });
+ );
const result = await setupApi.installCodex();
@@ -912,7 +1044,10 @@ function CodexSetupStep({
const api = getElectronAPI();
const setupApi = api.setup;
- console.log("[Codex Setup] storeApiKey available:", !!setupApi?.storeApiKey);
+ console.log(
+ "[Codex Setup] storeApiKey available:",
+ !!setupApi?.storeApiKey
+ );
if (setupApi?.storeApiKey) {
console.log("[Codex Setup] Calling storeApiKey for openai...");
@@ -920,7 +1055,9 @@ function CodexSetupStep({
console.log("[Codex Setup] storeApiKey result:", result);
if (result.success) {
- console.log("[Codex Setup] API key stored successfully, updating state...");
+ console.log(
+ "[Codex Setup] API key stored successfully, updating state..."
+ );
setApiKeys({ ...apiKeys, openai: apiKey });
setCodexAuthStatus({
authenticated: true,
@@ -933,7 +1070,9 @@ function CodexSetupStep({
console.log("[Codex Setup] Failed to store API key:", result.error);
}
} else {
- console.log("[Codex Setup] Web mode - storing API key in app state only");
+ console.log(
+ "[Codex Setup] Web mode - storing API key in app state only"
+ );
setApiKeys({ ...apiKeys, openai: apiKey });
setCodexAuthStatus({
authenticated: true,
@@ -957,13 +1096,14 @@ function CodexSetupStep({
};
const isAuthenticated = codexAuthStatus?.authenticated || apiKeys.openai;
-
+
const getAuthMethodLabel = () => {
if (!isAuthenticated) return null;
if (apiKeys.openai) return "API Key (Manual)";
if (codexAuthStatus?.method === "api_key") return "API Key (Auth File)";
if (codexAuthStatus?.method === "env") return "API Key (Environment)";
- if (codexAuthStatus?.method === "cli_verified") return "CLI Login (ChatGPT)";
+ if (codexAuthStatus?.method === "cli_verified")
+ return "CLI Login (ChatGPT)";
return "Authenticated";
};
@@ -1031,7 +1171,10 @@ function CodexSetupStep({
)}
) : (
-
+
)}
@@ -1114,9 +1257,7 @@ function CodexSetupStep({
Authentication
-
- Codex requires an OpenAI API key
-
+ Codex requires an OpenAI API key
{codexCliStatus?.installed && (
@@ -1236,7 +1377,8 @@ function CodexSetupStep({
Codex is ready to use!
- {getAuthMethodLabel() && `Authenticated via ${getAuthMethodLabel()}. `}
+ {getAuthMethodLabel() &&
+ `Authenticated via ${getAuthMethodLabel()}. `}
You can proceed to complete setup