Merge origin/main into feat/cursor-cli

Merges latest main branch changes including:
- MCP server support and configuration
- Pipeline configuration system
- Prompt customization settings
- GitHub issue comments in validation
- Auth middleware improvements
- Various UI/UX improvements

All Cursor CLI features preserved:
- Multi-provider support (Claude + Cursor)
- Model override capabilities
- Phase model configuration
- Provider tabs in settings

🤖 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
2025-12-31 01:22:18 +01:00
163 changed files with 15300 additions and 1045 deletions

View File

@@ -10,6 +10,11 @@ import type {
CursorModelId,
PhaseModelConfig,
PhaseModelKey,
MCPServerConfig,
FeatureStatusWithPipeline,
PipelineConfig,
PipelineStep,
PromptCustomization,
} from '@automaker/types';
import { getAllCursorModelIds, DEFAULT_PHASE_MODELS } from '@automaker/types';
@@ -267,7 +272,7 @@ export interface Feature extends Omit<
category: string;
description: string;
steps: string[]; // Required in UI (not optional)
status: 'backlog' | 'in_progress' | 'waiting_approval' | 'verified' | 'completed';
status: FeatureStatusWithPipeline;
images?: FeatureImage[]; // UI-specific base64 images
imagePaths?: FeatureImagePath[]; // Stricter type than base (no string | union)
textFilePaths?: FeatureTextFilePath[]; // Text file attachments for context
@@ -348,6 +353,7 @@ export interface TerminalState {
scrollbackLines: number; // Number of lines to keep in scrollback buffer
lineHeight: number; // Line height multiplier for terminal text
maxSessions: number; // Maximum concurrent terminal sessions (server setting)
lastActiveProjectPath: string | null; // Last project path to detect route changes vs project switches
}
// Persisted terminal layout - now includes sessionIds for reconnection
@@ -491,6 +497,15 @@ export interface AppState {
// Claude Agent SDK Settings
autoLoadClaudeMd: boolean; // Auto-load CLAUDE.md files using SDK's settingSources option
enableSandboxMode: boolean; // Enable sandbox mode for bash commands (may cause issues on some systems)
// 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
// Project Analysis
projectAnalysis: ProjectAnalysis | null;
@@ -543,6 +558,9 @@ export interface AppState {
claudeRefreshInterval: number; // Refresh interval in seconds (default: 60)
claudeUsage: ClaudeUsage | null;
claudeUsageLastUpdated: number | null;
// Pipeline Configuration (per-project, keyed by project path)
pipelineConfigByProject: Record<string, PipelineConfig>;
}
// Claude Usage interface matching the server response
@@ -777,6 +795,12 @@ export interface AppActions {
// Claude Agent SDK Settings actions
setAutoLoadClaudeMd: (enabled: boolean) => Promise<void>;
setEnableSandboxMode: (enabled: boolean) => Promise<void>;
setMcpAutoApproveTools: (enabled: boolean) => Promise<void>;
setMcpUnrestrictedTools: (enabled: boolean) => Promise<void>;
// Prompt Customization actions
setPromptCustomization: (customization: PromptCustomization) => Promise<void>;
// AI Profile actions
addAIProfile: (profile: Omit<AIProfile, 'id'>) => void;
@@ -785,6 +809,12 @@ export interface AppActions {
reorderAIProfiles: (oldIndex: number, newIndex: number) => void;
resetAIProfiles: () => void;
// MCP Server actions
addMCPServer: (server: Omit<MCPServerConfig, 'id'>) => void;
updateMCPServer: (id: string, updates: Partial<MCPServerConfig>) => void;
removeMCPServer: (id: string) => void;
reorderMCPServers: (oldIndex: number, newIndex: number) => void;
// Project Analysis actions
setProjectAnalysis: (analysis: ProjectAnalysis | null) => void;
setIsAnalyzing: (analyzing: boolean) => void;
@@ -835,6 +865,7 @@ export interface AppActions {
setTerminalScrollbackLines: (lines: number) => void;
setTerminalLineHeight: (lineHeight: number) => void;
setTerminalMaxSessions: (maxSessions: number) => void;
setTerminalLastActiveProjectPath: (projectPath: string | null) => void;
addTerminalTab: (name?: string) => string;
removeTerminalTab: (tabId: string) => void;
setActiveTerminalTab: (tabId: string) => void;
@@ -874,6 +905,21 @@ export interface AppActions {
} | null
) => void;
// Pipeline actions
setPipelineConfig: (projectPath: string, config: PipelineConfig) => void;
getPipelineConfig: (projectPath: string) => PipelineConfig | null;
addPipelineStep: (
projectPath: string,
step: Omit<PipelineStep, 'id' | 'createdAt' | 'updatedAt'>
) => PipelineStep;
updatePipelineStep: (
projectPath: string,
stepId: string,
updates: Partial<Omit<PipelineStep, 'id' | 'createdAt'>>
) => void;
deletePipelineStep: (projectPath: string, stepId: string) => void;
reorderPipelineSteps: (projectPath: string, stepIds: string[]) => void;
// Reset
reset: () => void;
}
@@ -964,6 +1010,11 @@ const initialState: AppState = {
enabledCursorModels: getAllCursorModelIds(), // All Cursor models enabled by default
cursorDefaultModel: 'auto', // Default to auto selection
autoLoadClaudeMd: false, // Default to disabled (user must opt-in)
enableSandboxMode: true, // Default to enabled for security (can be disabled if issues occur)
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,
isAnalyzing: false,
@@ -983,6 +1034,7 @@ const initialState: AppState = {
scrollbackLines: 5000,
lineHeight: 1.0,
maxSessions: 100,
lastActiveProjectPath: null,
},
terminalLayoutByProject: {},
specCreatingForProject: null,
@@ -990,6 +1042,10 @@ const initialState: AppState = {
defaultRequirePlanApproval: false,
defaultAIProfileId: null,
pendingPlanApproval: null,
claudeRefreshInterval: 60,
claudeUsage: null,
claudeUsageLastUpdated: null,
pipelineConfigByProject: {},
};
export const useAppStore = create<AppState & AppActions>()(
@@ -1636,6 +1692,32 @@ export const useAppStore = create<AppState & AppActions>()(
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
setEnableSandboxMode: async (enabled) => {
set({ enableSandboxMode: enabled });
// Sync to server settings file
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 });
// Sync to server settings file
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
await syncSettingsToServer();
},
// AI Profile actions
addAIProfile: (profile) => {
@@ -1677,6 +1759,29 @@ export const useAppStore = create<AppState & AppActions>()(
set({ aiProfiles: [...DEFAULT_AI_PROFILES, ...userProfiles] });
},
// MCP Server actions
addMCPServer: (server) => {
const id = `mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
set({ mcpServers: [...get().mcpServers, { ...server, id, enabled: true }] });
},
updateMCPServer: (id, updates) => {
set({
mcpServers: get().mcpServers.map((s) => (s.id === id ? { ...s, ...updates } : s)),
});
},
removeMCPServer: (id) => {
set({ mcpServers: get().mcpServers.filter((s) => s.id !== id) });
},
reorderMCPServers: (oldIndex, newIndex) => {
const servers = [...get().mcpServers];
const [movedServer] = servers.splice(oldIndex, 1);
servers.splice(newIndex, 0, movedServer);
set({ mcpServers: servers });
},
// Project Analysis actions
setProjectAnalysis: (analysis) => set({ projectAnalysis: analysis }),
setIsAnalyzing: (analyzing) => set({ isAnalyzing: analyzing }),
@@ -2103,6 +2208,8 @@ export const useAppStore = create<AppState & AppActions>()(
scrollbackLines: current.scrollbackLines,
lineHeight: current.lineHeight,
maxSessions: current.maxSessions,
// Preserve lastActiveProjectPath - it will be updated separately when needed
lastActiveProjectPath: current.lastActiveProjectPath,
},
});
},
@@ -2187,6 +2294,13 @@ export const useAppStore = create<AppState & AppActions>()(
});
},
setTerminalLastActiveProjectPath: (projectPath) => {
const current = get().terminalState;
set({
terminalState: { ...current, lastActiveProjectPath: projectPath },
});
},
addTerminalTab: (name) => {
const current = get().terminalState;
const newTabId = `tab-${Date.now()}`;
@@ -2651,6 +2765,105 @@ export const useAppStore = create<AppState & AppActions>()(
claudeUsageLastUpdated: usage ? Date.now() : null,
}),
// Pipeline actions
setPipelineConfig: (projectPath, config) => {
set({
pipelineConfigByProject: {
...get().pipelineConfigByProject,
[projectPath]: config,
},
});
},
getPipelineConfig: (projectPath) => {
return get().pipelineConfigByProject[projectPath] || null;
},
addPipelineStep: (projectPath, step) => {
const config = get().pipelineConfigByProject[projectPath] || { version: 1, steps: [] };
const now = new Date().toISOString();
const newStep: PipelineStep = {
...step,
id: `step_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 8)}`,
createdAt: now,
updatedAt: now,
};
const newSteps = [...config.steps, newStep].sort((a, b) => a.order - b.order);
newSteps.forEach((s, index) => {
s.order = index;
});
set({
pipelineConfigByProject: {
...get().pipelineConfigByProject,
[projectPath]: { ...config, steps: newSteps },
},
});
return newStep;
},
updatePipelineStep: (projectPath, stepId, updates) => {
const config = get().pipelineConfigByProject[projectPath];
if (!config) return;
const stepIndex = config.steps.findIndex((s) => s.id === stepId);
if (stepIndex === -1) return;
const updatedSteps = [...config.steps];
updatedSteps[stepIndex] = {
...updatedSteps[stepIndex],
...updates,
updatedAt: new Date().toISOString(),
};
set({
pipelineConfigByProject: {
...get().pipelineConfigByProject,
[projectPath]: { ...config, steps: updatedSteps },
},
});
},
deletePipelineStep: (projectPath, stepId) => {
const config = get().pipelineConfigByProject[projectPath];
if (!config) return;
const newSteps = config.steps.filter((s) => s.id !== stepId);
newSteps.forEach((s, index) => {
s.order = index;
});
set({
pipelineConfigByProject: {
...get().pipelineConfigByProject,
[projectPath]: { ...config, steps: newSteps },
},
});
},
reorderPipelineSteps: (projectPath, stepIds) => {
const config = get().pipelineConfigByProject[projectPath];
if (!config) return;
const stepMap = new Map(config.steps.map((s) => [s.id, s]));
const reorderedSteps = stepIds
.map((id, index) => {
const step = stepMap.get(id);
if (!step) return null;
return { ...step, order: index, updatedAt: new Date().toISOString() };
})
.filter((s): s is PipelineStep => s !== null);
set({
pipelineConfigByProject: {
...get().pipelineConfigByProject,
[projectPath]: { ...config, steps: reorderedSteps },
},
});
},
// Reset
reset: () => set(initialState),
}),
@@ -2735,6 +2948,7 @@ export const useAppStore = create<AppState & AppActions>()(
activeTabId: state.terminalState?.activeTabId ?? null,
activeSessionId: state.terminalState?.activeSessionId ?? null,
maximizedSessionId: state.terminalState?.maximizedSessionId ?? null,
lastActiveProjectPath: state.terminalState?.lastActiveProjectPath ?? null,
// Restore persisted settings
defaultFontSize: persistedSettings.defaultFontSize ?? 14,
defaultRunScript: persistedSettings.defaultRunScript ?? '',
@@ -2784,6 +2998,13 @@ export const useAppStore = create<AppState & AppActions>()(
enabledCursorModels: state.enabledCursorModels,
cursorDefaultModel: state.cursorDefaultModel,
autoLoadClaudeMd: state.autoLoadClaudeMd,
enableSandboxMode: state.enableSandboxMode,
// MCP settings
mcpServers: state.mcpServers,
mcpAutoApproveTools: state.mcpAutoApproveTools,
mcpUnrestrictedTools: state.mcpUnrestrictedTools,
// Prompt customization
promptCustomization: state.promptCustomization,
// Profiles and sessions
aiProfiles: state.aiProfiles,
chatSessions: state.chatSessions,

View File

@@ -201,6 +201,7 @@ export const useSetupStore = create<SetupState & SetupActions>()(
isFirstRun: state.isFirstRun,
setupComplete: state.setupComplete,
skipClaudeSetup: state.skipClaudeSetup,
claudeAuthStatus: state.claudeAuthStatus,
}),
}
)