mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
- Updated references to `app_spec.txt` and `feature_list.json` in app_spec.txt to include the correct path. - Enhanced coding_prompt.md by incorporating testing utilities for better test management and readability. - Added new utility functions in tests/utils.ts to streamline test interactions. This commit aims to improve documentation accuracy and maintainability of testing practices.
437 lines
12 KiB
TypeScript
437 lines
12 KiB
TypeScript
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
import type { Project } from "@/lib/electron";
|
|
|
|
export type ViewMode = "welcome" | "spec" | "board" | "code" | "agent" | "settings" | "analysis" | "tools" | "interview";
|
|
export type ThemeMode = "light" | "dark" | "system";
|
|
|
|
export interface ApiKeys {
|
|
anthropic: string;
|
|
google: string;
|
|
}
|
|
|
|
export interface ImageAttachment {
|
|
id: string;
|
|
data: string; // base64 encoded image data
|
|
mimeType: string; // e.g., "image/png", "image/jpeg"
|
|
filename: string;
|
|
size: number; // file size in bytes
|
|
}
|
|
|
|
export interface ChatMessage {
|
|
id: string;
|
|
role: "user" | "assistant";
|
|
content: string;
|
|
timestamp: Date;
|
|
images?: ImageAttachment[];
|
|
}
|
|
|
|
export interface ChatSession {
|
|
id: string;
|
|
title: string;
|
|
projectId: string;
|
|
messages: ChatMessage[];
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
archived: boolean;
|
|
}
|
|
|
|
export interface FeatureImage {
|
|
id: string;
|
|
data: string; // base64 encoded
|
|
mimeType: string;
|
|
filename: string;
|
|
size: number;
|
|
}
|
|
|
|
export interface Feature {
|
|
id: string;
|
|
category: string;
|
|
description: string;
|
|
steps: string[];
|
|
status: "backlog" | "in_progress" | "verified";
|
|
images?: FeatureImage[];
|
|
}
|
|
|
|
export interface FileTreeNode {
|
|
name: string;
|
|
path: string;
|
|
isDirectory: boolean;
|
|
children?: FileTreeNode[];
|
|
size?: number;
|
|
extension?: string;
|
|
}
|
|
|
|
export interface ProjectAnalysis {
|
|
fileTree: FileTreeNode[];
|
|
totalFiles: number;
|
|
totalDirectories: number;
|
|
filesByExtension: Record<string, number>;
|
|
analyzedAt: string;
|
|
}
|
|
|
|
export interface AppState {
|
|
// Project state
|
|
projects: Project[];
|
|
currentProject: Project | null;
|
|
|
|
// View state
|
|
currentView: ViewMode;
|
|
sidebarOpen: boolean;
|
|
|
|
// Theme
|
|
theme: ThemeMode;
|
|
|
|
// Features/Kanban
|
|
features: Feature[];
|
|
|
|
// App spec
|
|
appSpec: string;
|
|
|
|
// IPC status
|
|
ipcConnected: boolean;
|
|
|
|
// API Keys
|
|
apiKeys: ApiKeys;
|
|
|
|
// Project Analysis
|
|
projectAnalysis: ProjectAnalysis | null;
|
|
isAnalyzing: boolean;
|
|
|
|
// Chat Sessions
|
|
chatSessions: ChatSession[];
|
|
currentChatSession: ChatSession | null;
|
|
chatHistoryOpen: boolean;
|
|
|
|
// Auto Mode
|
|
isAutoModeRunning: boolean;
|
|
runningAutoTasks: string[]; // Feature IDs being worked on (supports concurrent tasks)
|
|
autoModeActivityLog: AutoModeActivity[];
|
|
}
|
|
|
|
export interface AutoModeActivity {
|
|
id: string;
|
|
featureId: string;
|
|
timestamp: Date;
|
|
type: "start" | "progress" | "tool" | "complete" | "error" | "planning" | "action" | "verification";
|
|
message: string;
|
|
tool?: string;
|
|
passes?: boolean;
|
|
phase?: "planning" | "action" | "verification";
|
|
}
|
|
|
|
export interface AppActions {
|
|
// Project actions
|
|
setProjects: (projects: Project[]) => void;
|
|
addProject: (project: Project) => void;
|
|
removeProject: (projectId: string) => void;
|
|
setCurrentProject: (project: Project | null) => void;
|
|
|
|
// View actions
|
|
setCurrentView: (view: ViewMode) => void;
|
|
toggleSidebar: () => void;
|
|
setSidebarOpen: (open: boolean) => void;
|
|
|
|
// Theme actions
|
|
setTheme: (theme: ThemeMode) => void;
|
|
|
|
// Feature actions
|
|
setFeatures: (features: Feature[]) => void;
|
|
updateFeature: (id: string, updates: Partial<Feature>) => void;
|
|
addFeature: (feature: Omit<Feature, "id">) => void;
|
|
removeFeature: (id: string) => void;
|
|
moveFeature: (id: string, newStatus: Feature["status"]) => void;
|
|
|
|
// App spec actions
|
|
setAppSpec: (spec: string) => void;
|
|
|
|
// IPC actions
|
|
setIpcConnected: (connected: boolean) => void;
|
|
|
|
// API Keys actions
|
|
setApiKeys: (keys: Partial<ApiKeys>) => void;
|
|
|
|
// Analysis actions
|
|
setProjectAnalysis: (analysis: ProjectAnalysis | null) => void;
|
|
setIsAnalyzing: (isAnalyzing: boolean) => void;
|
|
clearAnalysis: () => void;
|
|
|
|
// Chat Session actions
|
|
createChatSession: (title?: string) => ChatSession;
|
|
updateChatSession: (sessionId: string, updates: Partial<ChatSession>) => void;
|
|
addMessageToSession: (sessionId: string, message: ChatMessage) => void;
|
|
setCurrentChatSession: (session: ChatSession | null) => void;
|
|
archiveChatSession: (sessionId: string) => void;
|
|
unarchiveChatSession: (sessionId: string) => void;
|
|
deleteChatSession: (sessionId: string) => void;
|
|
setChatHistoryOpen: (open: boolean) => void;
|
|
toggleChatHistory: () => void;
|
|
|
|
// Auto Mode actions
|
|
setAutoModeRunning: (running: boolean) => void;
|
|
addRunningTask: (taskId: string) => void;
|
|
removeRunningTask: (taskId: string) => void;
|
|
clearRunningTasks: () => void;
|
|
addAutoModeActivity: (activity: Omit<AutoModeActivity, "id" | "timestamp">) => void;
|
|
clearAutoModeActivity: () => void;
|
|
|
|
// Reset
|
|
reset: () => void;
|
|
}
|
|
|
|
const initialState: AppState = {
|
|
projects: [],
|
|
currentProject: null,
|
|
currentView: "welcome",
|
|
sidebarOpen: true,
|
|
theme: "dark",
|
|
features: [],
|
|
appSpec: "",
|
|
ipcConnected: false,
|
|
apiKeys: {
|
|
anthropic: "",
|
|
google: "",
|
|
},
|
|
projectAnalysis: null,
|
|
isAnalyzing: false,
|
|
chatSessions: [],
|
|
currentChatSession: null,
|
|
chatHistoryOpen: false,
|
|
isAutoModeRunning: false,
|
|
runningAutoTasks: [],
|
|
autoModeActivityLog: [],
|
|
};
|
|
|
|
export const useAppStore = create<AppState & AppActions>()(
|
|
persist(
|
|
(set, get) => ({
|
|
...initialState,
|
|
|
|
// Project actions
|
|
setProjects: (projects) => set({ projects }),
|
|
|
|
addProject: (project) => {
|
|
const projects = get().projects;
|
|
const existing = projects.findIndex((p) => p.path === project.path);
|
|
if (existing >= 0) {
|
|
const updated = [...projects];
|
|
updated[existing] = { ...project, lastOpened: new Date().toISOString() };
|
|
set({ projects: updated });
|
|
} else {
|
|
set({
|
|
projects: [...projects, { ...project, lastOpened: new Date().toISOString() }],
|
|
});
|
|
}
|
|
},
|
|
|
|
removeProject: (projectId) => {
|
|
set({ projects: get().projects.filter((p) => p.id !== projectId) });
|
|
},
|
|
|
|
setCurrentProject: (project) => {
|
|
set({ currentProject: project });
|
|
if (project) {
|
|
set({ currentView: "board" });
|
|
} else {
|
|
set({ currentView: "welcome" });
|
|
}
|
|
},
|
|
|
|
// View actions
|
|
setCurrentView: (view) => set({ currentView: view }),
|
|
toggleSidebar: () => set({ sidebarOpen: !get().sidebarOpen }),
|
|
setSidebarOpen: (open) => set({ sidebarOpen: open }),
|
|
|
|
// Theme actions
|
|
setTheme: (theme) => set({ theme }),
|
|
|
|
// Feature actions
|
|
setFeatures: (features) => set({ features }),
|
|
|
|
updateFeature: (id, updates) => {
|
|
set({
|
|
features: get().features.map((f) =>
|
|
f.id === id ? { ...f, ...updates } : f
|
|
),
|
|
});
|
|
},
|
|
|
|
addFeature: (feature) => {
|
|
const id = `feature-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
set({ features: [...get().features, { ...feature, id }] });
|
|
},
|
|
|
|
removeFeature: (id) => {
|
|
set({ features: get().features.filter((f) => f.id !== id) });
|
|
},
|
|
|
|
moveFeature: (id, newStatus) => {
|
|
set({
|
|
features: get().features.map((f) =>
|
|
f.id === id ? { ...f, status: newStatus } : f
|
|
),
|
|
});
|
|
},
|
|
|
|
// App spec actions
|
|
setAppSpec: (spec) => set({ appSpec: spec }),
|
|
|
|
// IPC actions
|
|
setIpcConnected: (connected) => set({ ipcConnected: connected }),
|
|
|
|
// API Keys actions
|
|
setApiKeys: (keys) => set({ apiKeys: { ...get().apiKeys, ...keys } }),
|
|
|
|
// Analysis actions
|
|
setProjectAnalysis: (analysis) => set({ projectAnalysis: analysis }),
|
|
setIsAnalyzing: (isAnalyzing) => set({ isAnalyzing }),
|
|
clearAnalysis: () => set({ projectAnalysis: null, isAnalyzing: false }),
|
|
|
|
// Chat Session actions
|
|
createChatSession: (title) => {
|
|
const currentProject = get().currentProject;
|
|
if (!currentProject) {
|
|
throw new Error("No project selected");
|
|
}
|
|
|
|
const now = new Date();
|
|
const session: ChatSession = {
|
|
id: `chat-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
title: title || `Chat ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`,
|
|
projectId: currentProject.id,
|
|
messages: [{
|
|
id: "welcome",
|
|
role: "assistant",
|
|
content: "Hello! I'm the Automaker Agent. I can help you build software autonomously. What would you like to create today?",
|
|
timestamp: now,
|
|
}],
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
archived: false,
|
|
};
|
|
|
|
set({
|
|
chatSessions: [...get().chatSessions, session],
|
|
currentChatSession: session,
|
|
});
|
|
|
|
return session;
|
|
},
|
|
|
|
updateChatSession: (sessionId, updates) => {
|
|
set({
|
|
chatSessions: get().chatSessions.map((session) =>
|
|
session.id === sessionId
|
|
? { ...session, ...updates, updatedAt: new Date() }
|
|
: session
|
|
),
|
|
});
|
|
|
|
// Update current session if it's the one being updated
|
|
const currentSession = get().currentChatSession;
|
|
if (currentSession && currentSession.id === sessionId) {
|
|
set({
|
|
currentChatSession: { ...currentSession, ...updates, updatedAt: new Date() }
|
|
});
|
|
}
|
|
},
|
|
|
|
addMessageToSession: (sessionId, message) => {
|
|
const sessions = get().chatSessions;
|
|
const sessionIndex = sessions.findIndex(s => s.id === sessionId);
|
|
|
|
if (sessionIndex >= 0) {
|
|
const updatedSessions = [...sessions];
|
|
updatedSessions[sessionIndex] = {
|
|
...updatedSessions[sessionIndex],
|
|
messages: [...updatedSessions[sessionIndex].messages, message],
|
|
updatedAt: new Date(),
|
|
};
|
|
|
|
set({ chatSessions: updatedSessions });
|
|
|
|
// Update current session if it's the one being updated
|
|
const currentSession = get().currentChatSession;
|
|
if (currentSession && currentSession.id === sessionId) {
|
|
set({
|
|
currentChatSession: updatedSessions[sessionIndex]
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
setCurrentChatSession: (session) => {
|
|
set({ currentChatSession: session });
|
|
},
|
|
|
|
archiveChatSession: (sessionId) => {
|
|
get().updateChatSession(sessionId, { archived: true });
|
|
},
|
|
|
|
unarchiveChatSession: (sessionId) => {
|
|
get().updateChatSession(sessionId, { archived: false });
|
|
},
|
|
|
|
deleteChatSession: (sessionId) => {
|
|
const currentSession = get().currentChatSession;
|
|
set({
|
|
chatSessions: get().chatSessions.filter((s) => s.id !== sessionId),
|
|
currentChatSession: currentSession?.id === sessionId ? null : currentSession,
|
|
});
|
|
},
|
|
|
|
setChatHistoryOpen: (open) => set({ chatHistoryOpen: open }),
|
|
|
|
toggleChatHistory: () => set({ chatHistoryOpen: !get().chatHistoryOpen }),
|
|
|
|
// Auto Mode actions
|
|
setAutoModeRunning: (running) => set({ isAutoModeRunning: running }),
|
|
|
|
addRunningTask: (taskId) => {
|
|
const current = get().runningAutoTasks;
|
|
if (!current.includes(taskId)) {
|
|
set({ runningAutoTasks: [...current, taskId] });
|
|
}
|
|
},
|
|
|
|
removeRunningTask: (taskId) => {
|
|
set({ runningAutoTasks: get().runningAutoTasks.filter(id => id !== taskId) });
|
|
},
|
|
|
|
clearRunningTasks: () => set({ runningAutoTasks: [] }),
|
|
|
|
addAutoModeActivity: (activity) => {
|
|
const id = `activity-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
const newActivity: AutoModeActivity = {
|
|
...activity,
|
|
id,
|
|
timestamp: new Date(),
|
|
};
|
|
|
|
// Keep only the last 100 activities to avoid memory issues
|
|
const currentLog = get().autoModeActivityLog;
|
|
const updatedLog = [...currentLog, newActivity].slice(-100);
|
|
|
|
set({ autoModeActivityLog: updatedLog });
|
|
},
|
|
|
|
clearAutoModeActivity: () => set({ autoModeActivityLog: [] }),
|
|
|
|
// Reset
|
|
reset: () => set(initialState),
|
|
}),
|
|
{
|
|
name: "automaker-storage",
|
|
partialize: (state) => ({
|
|
projects: state.projects,
|
|
currentProject: state.currentProject,
|
|
theme: state.theme,
|
|
sidebarOpen: state.sidebarOpen,
|
|
apiKeys: state.apiKeys,
|
|
chatSessions: state.chatSessions,
|
|
chatHistoryOpen: state.chatHistoryOpen,
|
|
}),
|
|
}
|
|
)
|
|
);
|