Merge v0.8.0rc into feat/cursor-cli

Resolved conflicts:
- sdk-options.ts: kept HEAD (MCP & thinking level features)
- auto-mode-service.ts: kept HEAD (MCP features + fallback code)
- agent-output-modal.tsx: used v0.8.0rc (effectiveViewMode + pr-8 spacing)
- feature-suggestions-dialog.tsx: accepted deletion
- electron.ts: used v0.8.0rc (Ideation types)
- package-lock.json: regenerated

Fixed sdk-options.test.ts to expect 'default' permissionMode for read-only operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2026-01-04 13:12:45 +01:00
81 changed files with 6933 additions and 1958 deletions

View File

@@ -34,7 +34,8 @@ export type ViewMode =
| 'profiles'
| 'running-agents'
| 'terminal'
| 'wiki';
| 'wiki'
| 'ideation';
export type ThemeMode =
| 'light'
@@ -159,6 +160,9 @@ export interface KeyboardShortcuts {
settings: string;
profiles: string;
terminal: string;
ideation: string;
githubIssues: string;
githubPrs: string;
// UI shortcuts
toggleSidebar: string;
@@ -191,6 +195,9 @@ export const DEFAULT_KEYBOARD_SHORTCUTS: KeyboardShortcuts = {
settings: 'S',
profiles: 'M',
terminal: 'T',
ideation: 'I',
githubIssues: 'G',
githubPrs: 'R',
// UI
toggleSidebar: '`',
@@ -504,8 +511,6 @@ export interface AppState {
// MCP Servers
mcpServers: MCPServerConfig[]; // List of configured MCP servers for agent use
mcpAutoApproveTools: boolean; // Auto-approve MCP tool calls without permission prompts
mcpUnrestrictedTools: boolean; // Allow unrestricted tools when MCP servers are enabled
// Prompt Customization
promptCustomization: PromptCustomization; // Custom prompts for Auto Mode, Agent, Backlog Plan, Enhancement
@@ -801,8 +806,6 @@ export interface AppActions {
setAutoLoadClaudeMd: (enabled: boolean) => Promise<void>;
setEnableSandboxMode: (enabled: boolean) => Promise<void>;
setSkipSandboxWarning: (skip: boolean) => Promise<void>;
setMcpAutoApproveTools: (enabled: boolean) => Promise<void>;
setMcpUnrestrictedTools: (enabled: boolean) => Promise<void>;
// Prompt Customization actions
setPromptCustomization: (customization: PromptCustomization) => Promise<void>;
@@ -1019,8 +1022,6 @@ const initialState: AppState = {
enableSandboxMode: false, // Default to disabled (can be enabled for additional security)
skipSandboxWarning: false, // Default to disabled (show sandbox warning dialog)
mcpServers: [], // No MCP servers configured by default
mcpAutoApproveTools: true, // Default to enabled - bypass permission prompts for MCP tools
mcpUnrestrictedTools: true, // Default to enabled - don't filter allowedTools when MCP enabled
promptCustomization: {}, // Empty by default - all prompts use built-in defaults
aiProfiles: DEFAULT_AI_PROFILES,
projectAnalysis: null,
@@ -1719,19 +1720,6 @@ export const useAppStore = create<AppState & AppActions>()(
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
setMcpAutoApproveTools: async (enabled) => {
set({ mcpAutoApproveTools: enabled });
// Sync to server settings file
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
setMcpUnrestrictedTools: async (enabled) => {
set({ mcpUnrestrictedTools: enabled });
// Sync to server settings file
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
// Prompt Customization actions
setPromptCustomization: async (customization) => {
set({ promptCustomization: customization });
@@ -3023,8 +3011,6 @@ export const useAppStore = create<AppState & AppActions>()(
skipSandboxWarning: state.skipSandboxWarning,
// MCP settings
mcpServers: state.mcpServers,
mcpAutoApproveTools: state.mcpAutoApproveTools,
mcpUnrestrictedTools: state.mcpUnrestrictedTools,
// Prompt customization
promptCustomization: state.promptCustomization,
// Profiles and sessions

View File

@@ -0,0 +1,324 @@
/**
* Ideation Store - State management for brainstorming and idea management
*/
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type {
Idea,
IdeaCategory,
IdeaStatus,
IdeationPrompt,
AnalysisSuggestion,
ProjectAnalysisResult,
} from '@automaker/types';
// ============================================================================
// Generation Job Types
// ============================================================================
export type GenerationJobStatus = 'generating' | 'ready' | 'error';
export interface GenerationJob {
id: string;
prompt: IdeationPrompt;
status: GenerationJobStatus;
suggestions: AnalysisSuggestion[];
error: string | null;
startedAt: string;
completedAt: string | null;
}
// ============================================================================
// State Interface
// ============================================================================
export type IdeationMode = 'dashboard' | 'prompts';
interface IdeationState {
// Ideas (saved for later)
ideas: Idea[];
selectedIdeaId: string | null;
// Generation jobs (multiple concurrent generations)
generationJobs: GenerationJob[];
selectedJobId: string | null;
// Legacy - keep for backwards compat during transition
suggestions: AnalysisSuggestion[];
selectedPrompt: IdeationPrompt | null;
isGenerating: boolean;
generatingError: string | null;
// Analysis
analysisResult: ProjectAnalysisResult | null;
isAnalyzing: boolean;
analysisProgress: number;
analysisMessage: string;
// UI state
currentMode: IdeationMode;
selectedCategory: IdeaCategory | null;
filterStatus: IdeaStatus | 'all';
}
// ============================================================================
// Actions Interface
// ============================================================================
interface IdeationActions {
// Ideas
setIdeas: (ideas: Idea[]) => void;
addIdea: (idea: Idea) => void;
updateIdea: (id: string, updates: Partial<Idea>) => void;
removeIdea: (id: string) => void;
setSelectedIdea: (id: string | null) => void;
getSelectedIdea: () => Idea | null;
// Generation Jobs
addGenerationJob: (prompt: IdeationPrompt) => string;
updateJobStatus: (
jobId: string,
status: GenerationJobStatus,
suggestions?: AnalysisSuggestion[],
error?: string
) => void;
removeJob: (jobId: string) => void;
clearCompletedJobs: () => void;
setSelectedJob: (jobId: string | null) => void;
getJob: (jobId: string) => GenerationJob | null;
removeSuggestionFromJob: (jobId: string, suggestionId: string) => void;
appendSuggestionsToJob: (jobId: string, suggestions: AnalysisSuggestion[]) => void;
setJobGenerating: (jobId: string, generating: boolean) => void;
// Legacy Suggestions (kept for backwards compat)
setSuggestions: (suggestions: AnalysisSuggestion[]) => void;
clearSuggestions: () => void;
removeSuggestion: (id: string) => void;
setSelectedPrompt: (prompt: IdeationPrompt | null) => void;
setIsGenerating: (isGenerating: boolean) => void;
setGeneratingError: (error: string | null) => void;
// Analysis
setAnalysisResult: (result: ProjectAnalysisResult | null) => void;
setIsAnalyzing: (isAnalyzing: boolean) => void;
setAnalysisProgress: (progress: number, message?: string) => void;
// UI
setMode: (mode: IdeationMode) => void;
setCategory: (category: IdeaCategory | null) => void;
setFilterStatus: (status: IdeaStatus | 'all') => void;
// Reset
reset: () => void;
resetSuggestions: () => void;
}
// ============================================================================
// Initial State
// ============================================================================
const initialState: IdeationState = {
ideas: [],
selectedIdeaId: null,
generationJobs: [],
selectedJobId: null,
suggestions: [],
selectedPrompt: null,
isGenerating: false,
generatingError: null,
analysisResult: null,
isAnalyzing: false,
analysisProgress: 0,
analysisMessage: '',
currentMode: 'dashboard',
selectedCategory: null,
filterStatus: 'all',
};
// ============================================================================
// Store
// ============================================================================
export const useIdeationStore = create<IdeationState & IdeationActions>()(
persist(
(set, get) => ({
...initialState,
// Ideas
setIdeas: (ideas) => set({ ideas }),
addIdea: (idea) =>
set((state) => ({
ideas: [idea, ...state.ideas],
})),
updateIdea: (id, updates) =>
set((state) => ({
ideas: state.ideas.map((idea) => (idea.id === id ? { ...idea, ...updates } : idea)),
})),
removeIdea: (id) =>
set((state) => ({
ideas: state.ideas.filter((idea) => idea.id !== id),
selectedIdeaId: state.selectedIdeaId === id ? null : state.selectedIdeaId,
})),
setSelectedIdea: (id) => set({ selectedIdeaId: id }),
getSelectedIdea: () => {
const state = get();
return state.ideas.find((idea) => idea.id === state.selectedIdeaId) || null;
},
// Generation Jobs
addGenerationJob: (prompt) => {
const jobId = `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const job: GenerationJob = {
id: jobId,
prompt,
status: 'generating',
suggestions: [],
error: null,
startedAt: new Date().toISOString(),
completedAt: null,
};
set((state) => ({
generationJobs: [job, ...state.generationJobs],
}));
return jobId;
},
updateJobStatus: (jobId, status, suggestions, error) =>
set((state) => ({
generationJobs: state.generationJobs.map((job) =>
job.id === jobId
? {
...job,
status,
suggestions: suggestions || job.suggestions,
error: error || null,
completedAt: status !== 'generating' ? new Date().toISOString() : null,
}
: job
),
})),
removeJob: (jobId) =>
set((state) => ({
generationJobs: state.generationJobs.filter((job) => job.id !== jobId),
selectedJobId: state.selectedJobId === jobId ? null : state.selectedJobId,
})),
clearCompletedJobs: () =>
set((state) => ({
generationJobs: state.generationJobs.filter((job) => job.status === 'generating'),
})),
setSelectedJob: (jobId) => set({ selectedJobId: jobId }),
getJob: (jobId) => {
const state = get();
return state.generationJobs.find((job) => job.id === jobId) || null;
},
removeSuggestionFromJob: (jobId, suggestionId) =>
set((state) => ({
generationJobs: state.generationJobs.map((job) =>
job.id === jobId
? {
...job,
suggestions: job.suggestions.filter((s) => s.id !== suggestionId),
}
: job
),
})),
appendSuggestionsToJob: (jobId, suggestions) =>
set((state) => ({
generationJobs: state.generationJobs.map((job) =>
job.id === jobId
? {
...job,
suggestions: [...job.suggestions, ...suggestions],
status: 'ready' as const,
}
: job
),
})),
setJobGenerating: (jobId, generating) =>
set((state) => ({
generationJobs: state.generationJobs.map((job) =>
job.id === jobId
? {
...job,
status: generating ? ('generating' as const) : ('ready' as const),
}
: job
),
})),
// Suggestions (legacy)
setSuggestions: (suggestions) => set({ suggestions }),
clearSuggestions: () => set({ suggestions: [], generatingError: null }),
removeSuggestion: (id) =>
set((state) => ({
suggestions: state.suggestions.filter((s) => s.id !== id),
})),
setSelectedPrompt: (prompt) => set({ selectedPrompt: prompt }),
setIsGenerating: (isGenerating) => set({ isGenerating }),
setGeneratingError: (error) => set({ generatingError: error }),
// Analysis
setAnalysisResult: (result) => set({ analysisResult: result }),
setIsAnalyzing: (isAnalyzing) =>
set({
isAnalyzing,
analysisProgress: isAnalyzing ? 0 : get().analysisProgress,
analysisMessage: isAnalyzing ? 'Starting analysis...' : '',
}),
setAnalysisProgress: (progress, message) =>
set({
analysisProgress: progress,
analysisMessage: message || get().analysisMessage,
}),
// UI
setMode: (mode) => set({ currentMode: mode }),
setCategory: (category) => set({ selectedCategory: category }),
setFilterStatus: (status) => set({ filterStatus: status }),
// Reset
reset: () => set(initialState),
resetSuggestions: () =>
set({
suggestions: [],
selectedPrompt: null,
isGenerating: false,
generatingError: null,
}),
}),
{
name: 'automaker-ideation-store',
version: 3,
partialize: (state) => ({
// Only persist these fields
ideas: state.ideas,
generationJobs: state.generationJobs,
analysisResult: state.analysisResult,
filterStatus: state.filterStatus,
}),
}
)
);