From 07bcb6b7671715def0c24bc1c51f7359a73a5db8 Mon Sep 17 00:00:00 2001 From: Kacper Date: Wed, 24 Dec 2025 22:05:50 +0100 Subject: [PATCH] feat: add auto-load CLAUDE.md functionality - Introduced a new setting to enable automatic loading of CLAUDE.md files from project-specific directories. - Updated relevant services and components to support the new setting, including the AgentService and AutoModeService. - Added UI controls for managing the auto-load setting in the settings view. - Enhanced SDK options to incorporate settingSources for CLAUDE.md loading. - Updated global and project settings interfaces to include autoLoadClaudeMd property. --- .gitignore | 3 +- apps/server/src/index.ts | 7 +- apps/server/src/lib/sdk-options.ts | 104 +++++++++++++++++- apps/server/src/providers/claude-provider.ts | 2 + apps/server/src/providers/types.ts | 3 +- apps/server/src/services/agent-service.ts | 44 +++++++- apps/server/src/services/auto-mode-service.ts | 43 +++++++- .../ui/src/components/views/settings-view.tsx | 7 ++ .../claude/claude-md-settings.tsx | 82 ++++++++++++++ apps/ui/src/hooks/use-settings-migration.ts | 1 + apps/ui/src/store/app-store.ts | 16 +++ libs/types/src/settings.ts | 9 ++ 12 files changed, 306 insertions(+), 15 deletions(-) create mode 100644 apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx diff --git a/.gitignore b/.gitignore index c752c12e..5efd9e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,5 @@ blob-report/ # Misc *.pem -docker-compose.override.yml \ No newline at end of file +docker-compose.override.yml +.claude/ \ No newline at end of file diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 548f4629..37460af9 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -112,10 +112,11 @@ app.use(express.json({ limit: '50mb' })); const events: EventEmitter = createEventEmitter(); // Create services -const agentService = new AgentService(DATA_DIR, events); -const featureLoader = new FeatureLoader(); -const autoModeService = new AutoModeService(events); +// Note: settingsService is created first so it can be injected into other services const settingsService = new SettingsService(DATA_DIR); +const agentService = new AgentService(DATA_DIR, events, settingsService); +const featureLoader = new FeatureLoader(); +const autoModeService = new AutoModeService(events, settingsService); const claudeUsageService = new ClaudeUsageService(); // Initialize services diff --git a/apps/server/src/lib/sdk-options.ts b/apps/server/src/lib/sdk-options.ts index 7853fbd2..85dc3eb3 100644 --- a/apps/server/src/lib/sdk-options.ts +++ b/apps/server/src/lib/sdk-options.ts @@ -136,6 +136,71 @@ function getBaseOptions(): Partial { }; } +/** + * Build system prompt configuration based on autoLoadClaudeMd setting. + * When autoLoadClaudeMd is true: + * - Uses preset mode with 'claude_code' to enable CLAUDE.md auto-loading + * - If there's a custom systemPrompt, appends it to the preset + * - Sets settingSources to ['project'] for SDK to load CLAUDE.md files + * + * @param config - The SDK options config + * @returns Object with systemPrompt and settingSources for SDK options + */ +function buildClaudeMdOptions(config: CreateSdkOptionsConfig): { + systemPrompt?: string | SystemPromptConfig; + settingSources?: Array<'user' | 'project' | 'local'>; +} { + if (!config.autoLoadClaudeMd) { + // Standard mode - just pass through the system prompt as-is + return config.systemPrompt ? { systemPrompt: config.systemPrompt } : {}; + } + + // Auto-load CLAUDE.md mode - use preset with settingSources + const result: { + systemPrompt: SystemPromptConfig; + settingSources: Array<'user' | 'project' | 'local'>; + } = { + systemPrompt: { + type: 'preset', + preset: 'claude_code', + }, + // Load both user (~/.claude/CLAUDE.md) and project (.claude/CLAUDE.md) settings + settingSources: ['user', 'project'], + }; + + // If there's a custom system prompt, append it to the preset + if (config.systemPrompt) { + result.systemPrompt.append = config.systemPrompt; + } + + console.log( + '[SDK Options] CLAUDE.md auto-loading enabled:', + JSON.stringify( + { + systemPrompt: result.systemPrompt, + settingSources: result.settingSources, + }, + null, + 2 + ) + ); + + return result; +} + +/** + * System prompt configuration for SDK options + * When using preset mode with claude_code, CLAUDE.md files are automatically loaded + */ +export interface SystemPromptConfig { + /** Use preset mode with claude_code to enable CLAUDE.md auto-loading */ + type: 'preset'; + /** The preset to use - 'claude_code' enables CLAUDE.md loading */ + preset: 'claude_code'; + /** Optional additional prompt to append to the preset */ + append?: string; +} + /** * Options configuration for creating SDK options */ @@ -160,6 +225,9 @@ export interface CreateSdkOptionsConfig { type: 'json_schema'; schema: Record; }; + + /** Enable auto-loading of CLAUDE.md files via SDK's settingSources */ + autoLoadClaudeMd?: boolean; } /** @@ -169,11 +237,15 @@ export interface CreateSdkOptionsConfig { * - Uses read-only tools for codebase analysis * - Extended turns for thorough exploration * - Opus model by default (can be overridden) + * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createSpecGenerationOptions(config: CreateSdkOptionsConfig): Options { // Validate working directory before creating options validateWorkingDirectory(config.cwd); + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), // Override permissionMode - spec generation only needs read-only tools @@ -184,7 +256,7 @@ export function createSpecGenerationOptions(config: CreateSdkOptionsConfig): Opt maxTurns: MAX_TURNS.maximum, cwd: config.cwd, allowedTools: [...TOOL_PRESETS.specGeneration], - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), ...(config.outputFormat && { outputFormat: config.outputFormat }), }; @@ -197,11 +269,15 @@ export function createSpecGenerationOptions(config: CreateSdkOptionsConfig): Opt * - Uses read-only tools (just needs to read the spec) * - Quick turns since it's mostly JSON generation * - Sonnet model by default for speed + * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createFeatureGenerationOptions(config: CreateSdkOptionsConfig): Options { // Validate working directory before creating options validateWorkingDirectory(config.cwd); + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), // Override permissionMode - feature generation only needs read-only tools @@ -210,7 +286,7 @@ export function createFeatureGenerationOptions(config: CreateSdkOptionsConfig): maxTurns: MAX_TURNS.quick, cwd: config.cwd, allowedTools: [...TOOL_PRESETS.readOnly], - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; } @@ -222,18 +298,22 @@ export function createFeatureGenerationOptions(config: CreateSdkOptionsConfig): * - Uses read-only tools for analysis * - Standard turns to allow thorough codebase exploration and structured output generation * - Opus model by default for thorough analysis + * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createSuggestionsOptions(config: CreateSdkOptionsConfig): Options { // Validate working directory before creating options validateWorkingDirectory(config.cwd); + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), model: getModelForUseCase('suggestions', config.model), maxTurns: MAX_TURNS.extended, cwd: config.cwd, allowedTools: [...TOOL_PRESETS.readOnly], - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), ...(config.outputFormat && { outputFormat: config.outputFormat }), }; @@ -247,6 +327,7 @@ export function createSuggestionsOptions(config: CreateSdkOptionsConfig): Option * - Standard turns for interactive sessions * - Model priority: explicit model > session model > chat default * - Sandbox enabled for bash safety + * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createChatOptions(config: CreateSdkOptionsConfig): Options { // Validate working directory before creating options @@ -255,6 +336,9 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options { // Model priority: explicit model > session model > chat default const effectiveModel = config.model || config.sessionModel; + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), model: getModelForUseCase('chat', effectiveModel), @@ -265,7 +349,7 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options { enabled: true, autoAllowBashIfSandboxed: true, }, - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; } @@ -278,11 +362,15 @@ export function createChatOptions(config: CreateSdkOptionsConfig): Options { * - Extended turns for thorough feature implementation * - Uses default model (can be overridden) * - Sandbox enabled for bash safety + * - When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options { // Validate working directory before creating options validateWorkingDirectory(config.cwd); + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), model: getModelForUseCase('auto', config.model), @@ -293,7 +381,7 @@ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options { enabled: true, autoAllowBashIfSandboxed: true, }, - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; } @@ -302,6 +390,7 @@ export function createAutoModeOptions(config: CreateSdkOptionsConfig): Options { * Create custom SDK options with explicit configuration * * Use this when the preset options don't fit your use case. + * When autoLoadClaudeMd is true, uses preset mode and settingSources for CLAUDE.md loading */ export function createCustomOptions( config: CreateSdkOptionsConfig & { @@ -313,6 +402,9 @@ export function createCustomOptions( // Validate working directory before creating options validateWorkingDirectory(config.cwd); + // Build CLAUDE.md auto-loading options if enabled + const claudeMdOptions = buildClaudeMdOptions(config); + return { ...getBaseOptions(), model: getModelForUseCase('default', config.model), @@ -320,7 +412,7 @@ export function createCustomOptions( cwd: config.cwd, allowedTools: config.allowedTools ? [...config.allowedTools] : [...TOOL_PRESETS.readOnly], ...(config.sandbox && { sandbox: config.sandbox }), - ...(config.systemPrompt && { systemPrompt: config.systemPrompt }), + ...claudeMdOptions, ...(config.abortController && { abortController: config.abortController }), }; } diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index 2ed2728d..9237cdf6 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -55,6 +55,8 @@ export class ClaudeProvider extends BaseProvider { ...(sdkSessionId && conversationHistory && conversationHistory.length > 0 ? { resume: sdkSessionId } : {}), + // Forward settingSources for CLAUDE.md file loading + ...(options.settingSources && { settingSources: options.settingSources }), }; // Build prompt payload diff --git a/apps/server/src/providers/types.ts b/apps/server/src/providers/types.ts index f3aa22d5..5a594361 100644 --- a/apps/server/src/providers/types.ts +++ b/apps/server/src/providers/types.ts @@ -26,13 +26,14 @@ export interface ExecuteOptions { prompt: string | Array<{ type: string; text?: string; source?: object }>; model: string; cwd: string; - systemPrompt?: string; + systemPrompt?: string | { type: 'preset'; preset: 'claude_code'; append?: string }; maxTurns?: number; allowedTools?: string[]; mcpServers?: Record; abortController?: AbortController; conversationHistory?: ConversationMessage[]; // Previous messages for context sdkSessionId?: string; // Claude SDK session ID for resuming conversations + settingSources?: Array<'user' | 'project' | 'local'>; // Claude filesystem settings to load } /** diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index 93df5566..e25efebe 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -16,6 +16,7 @@ import { import { ProviderFactory } from '../providers/provider-factory.js'; import { createChatOptions, validateWorkingDirectory } from '../lib/sdk-options.js'; import { PathNotAllowedError } from '@automaker/platform'; +import type { SettingsService } from './settings-service.js'; interface Message { id: string; @@ -57,11 +58,13 @@ export class AgentService { private stateDir: string; private metadataFile: string; private events: EventEmitter; + private settingsService: SettingsService | null = null; - constructor(dataDir: string, events: EventEmitter) { + constructor(dataDir: string, events: EventEmitter, settingsService?: SettingsService) { this.stateDir = path.join(dataDir, 'agent-sessions'); this.metadataFile = path.join(dataDir, 'sessions-metadata.json'); this.events = events; + this.settingsService = settingsService ?? null; } async initialize(): Promise { @@ -186,7 +189,11 @@ export class AgentService { // Determine the effective working directory for context loading const effectiveWorkDir = workingDirectory || session.workingDirectory; + // Load autoLoadClaudeMd setting (project setting takes precedence over global) + const autoLoadClaudeMd = await this.getAutoLoadClaudeMdSetting(effectiveWorkDir); + // Load project context files (CLAUDE.md, CODE_QUALITY.md, etc.) + // Note: When autoLoadClaudeMd is enabled, SDK handles CLAUDE.md loading via settingSources const { formattedPrompt: contextFilesPrompt } = await loadContextFiles({ projectPath: effectiveWorkDir, fsModule: secureFs as Parameters[0]['fsModule'], @@ -205,6 +212,7 @@ export class AgentService { sessionModel: session.model, systemPrompt: combinedSystemPrompt, abortController: session.abortController!, + autoLoadClaudeMd, }); // Extract model, maxTurns, and allowedTools from SDK options @@ -224,11 +232,12 @@ export class AgentService { prompt: '', // Will be set below based on images model: effectiveModel, cwd: effectiveWorkDir, - systemPrompt: combinedSystemPrompt, + systemPrompt: sdkOptions.systemPrompt, maxTurns: maxTurns, allowedTools: allowedTools, abortController: session.abortController!, conversationHistory: conversationHistory.length > 0 ? conversationHistory : undefined, + settingSources: sdkOptions.settingSources, sdkSessionId: session.sdkSessionId, // Pass SDK session ID for resuming }; @@ -595,6 +604,37 @@ You have full access to the codebase and can: - Execute tests and builds`; } + /** + * Get the autoLoadClaudeMd setting, with project settings taking precedence over global. + * Returns false if settings service is not available. + */ + private async getAutoLoadClaudeMdSetting(projectPath: string): Promise { + if (!this.settingsService) { + console.log('[AgentService] SettingsService not available, autoLoadClaudeMd disabled'); + return false; + } + + try { + // Check project settings first (takes precedence) + const projectSettings = await this.settingsService.getProjectSettings(projectPath); + if (projectSettings.autoLoadClaudeMd !== undefined) { + console.log( + `[AgentService] autoLoadClaudeMd from project settings: ${projectSettings.autoLoadClaudeMd}` + ); + return projectSettings.autoLoadClaudeMd; + } + + // Fall back to global settings + const globalSettings = await this.settingsService.getGlobalSettings(); + const result = globalSettings.autoLoadClaudeMd ?? false; + console.log(`[AgentService] autoLoadClaudeMd from global settings: ${result}`); + return result; + } catch (error) { + console.error('[AgentService] Failed to load autoLoadClaudeMd setting:', error); + return false; + } + } + private generateId(): string { return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index 1da65e35..a7b24fef 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -27,6 +27,7 @@ import * as secureFs from '../lib/secure-fs.js'; import type { EventEmitter } from '../lib/events.js'; import { createAutoModeOptions, validateWorkingDirectory } from '../lib/sdk-options.js'; import { FeatureLoader } from './feature-loader.js'; +import type { SettingsService } from './settings-service.js'; const execAsync = promisify(exec); @@ -341,9 +342,11 @@ export class AutoModeService { private autoLoopAbortController: AbortController | null = null; private config: AutoModeConfig | null = null; private pendingApprovals = new Map(); + private settingsService: SettingsService | null = null; - constructor(events: EventEmitter) { + constructor(events: EventEmitter, settingsService?: SettingsService) { this.events = events; + this.settingsService = settingsService ?? null; } /** @@ -1780,11 +1783,15 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set. return; } + // Load autoLoadClaudeMd setting (project setting takes precedence over global) + const autoLoadClaudeMd = await this.getAutoLoadClaudeMdSetting(finalProjectPath); + // Build SDK options using centralized configuration for feature implementation const sdkOptions = createAutoModeOptions({ cwd: workDir, model: model, abortController, + autoLoadClaudeMd, }); // Extract model, maxTurns, and allowedTools from SDK options @@ -1823,7 +1830,8 @@ This mock response was generated because AUTOMAKER_MOCK_AGENT=true was set. cwd: workDir, allowedTools: allowedTools, abortController, - systemPrompt: options?.systemPrompt, + systemPrompt: sdkOptions.systemPrompt, + settingSources: sdkOptions.settingSources, }; // Execute via provider @@ -2494,4 +2502,35 @@ Begin implementing task ${task.id} now.`; } }); } + + /** + * Get the autoLoadClaudeMd setting, with project settings taking precedence over global. + * Returns false if settings service is not available. + */ + private async getAutoLoadClaudeMdSetting(projectPath: string): Promise { + if (!this.settingsService) { + console.log('[AutoMode] SettingsService not available, autoLoadClaudeMd disabled'); + return false; + } + + try { + // Check project settings first (takes precedence) + const projectSettings = await this.settingsService.getProjectSettings(projectPath); + if (projectSettings.autoLoadClaudeMd !== undefined) { + console.log( + `[AutoMode] autoLoadClaudeMd from project settings: ${projectSettings.autoLoadClaudeMd}` + ); + return projectSettings.autoLoadClaudeMd; + } + + // Fall back to global settings + const globalSettings = await this.settingsService.getGlobalSettings(); + const result = globalSettings.autoLoadClaudeMd ?? false; + console.log(`[AutoMode] autoLoadClaudeMd from global settings: ${result}`); + return result; + } catch (error) { + console.error('[AutoMode] Failed to load autoLoadClaudeMd setting:', error); + return false; + } + } } diff --git a/apps/ui/src/components/views/settings-view.tsx b/apps/ui/src/components/views/settings-view.tsx index f57735bf..b888c9b6 100644 --- a/apps/ui/src/components/views/settings-view.tsx +++ b/apps/ui/src/components/views/settings-view.tsx @@ -10,6 +10,7 @@ import { SettingsNavigation } from './settings-view/components/settings-navigati import { ApiKeysSection } from './settings-view/api-keys/api-keys-section'; import { ClaudeUsageSection } from './settings-view/api-keys/claude-usage-section'; import { ClaudeCliStatus } from './settings-view/cli-status/claude-cli-status'; +import { ClaudeMdSettings } from './settings-view/claude/claude-md-settings'; import { AIEnhancementSection } from './settings-view/ai-enhancement'; import { AppearanceSection } from './settings-view/appearance/appearance-section'; import { TerminalSection } from './settings-view/terminal/terminal-section'; @@ -47,6 +48,8 @@ export function SettingsView() { apiKeys, validationModel, setValidationModel, + autoLoadClaudeMd, + setAutoLoadClaudeMd, } = useAppStore(); // Hide usage tracking when using API key (only show for Claude Code CLI users) @@ -102,6 +105,10 @@ export function SettingsView() { isChecking={isCheckingClaudeCli} onRefresh={handleRefreshClaudeCli} /> + {showUsageTracking && } ); diff --git a/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx new file mode 100644 index 00000000..920984be --- /dev/null +++ b/apps/ui/src/components/views/settings-view/claude/claude-md-settings.tsx @@ -0,0 +1,82 @@ +import { Label } from '@/components/ui/label'; +import { Checkbox } from '@/components/ui/checkbox'; +import { FileCode } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface ClaudeMdSettingsProps { + autoLoadClaudeMd: boolean; + onAutoLoadClaudeMdChange: (enabled: boolean) => void; +} + +/** + * ClaudeMdSettings Component + * + * UI control for the autoLoadClaudeMd setting which enables automatic loading + * of project instructions from .claude/CLAUDE.md files via the Claude Agent SDK. + * + * Usage: + * ```tsx + * + * ``` + */ +export function ClaudeMdSettings({ + autoLoadClaudeMd, + onAutoLoadClaudeMdChange, +}: ClaudeMdSettingsProps) { + return ( +
+
+
+
+ +
+

+ CLAUDE.md Integration +

+
+

+ Configure automatic loading of project-specific instructions. +

+
+
+
+ onAutoLoadClaudeMdChange(checked === true)} + className="mt-1" + data-testid="auto-load-claude-md-checkbox" + /> +
+ +

+ Automatically load project instructions from{' '} + + .claude/CLAUDE.md + {' '} + files. When enabled, Claude will read and follow conventions specified in your + project's CLAUDE.md file. Project settings override global settings. +

+
+
+
+
+ ); +} diff --git a/apps/ui/src/hooks/use-settings-migration.ts b/apps/ui/src/hooks/use-settings-migration.ts index df5d85a5..2bca750b 100644 --- a/apps/ui/src/hooks/use-settings-migration.ts +++ b/apps/ui/src/hooks/use-settings-migration.ts @@ -223,6 +223,7 @@ export async function syncSettingsToServer(): Promise { muteDoneSound: state.muteDoneSound, enhancementModel: state.enhancementModel, validationModel: state.validationModel, + autoLoadClaudeMd: state.autoLoadClaudeMd, keyboardShortcuts: state.keyboardShortcuts, aiProfiles: state.aiProfiles, projects: state.projects, diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts index 978d67cc..874e1a6d 100644 --- a/apps/ui/src/store/app-store.ts +++ b/apps/ui/src/store/app-store.ts @@ -478,6 +478,9 @@ export interface AppState { // Validation Model Settings validationModel: AgentModel; // Model used for GitHub issue validation (default: opus) + // Claude Agent SDK Settings + autoLoadClaudeMd: boolean; // Auto-load CLAUDE.md files using SDK's settingSources option + // Project Analysis projectAnalysis: ProjectAnalysis | null; isAnalyzing: boolean; @@ -751,6 +754,9 @@ export interface AppActions { // Validation Model actions setValidationModel: (model: AgentModel) => void; + // Claude Agent SDK Settings actions + setAutoLoadClaudeMd: (enabled: boolean) => Promise; + // AI Profile actions addAIProfile: (profile: Omit) => void; updateAIProfile: (id: string, updates: Partial) => void; @@ -922,6 +928,7 @@ const initialState: AppState = { muteDoneSound: false, // Default to sound enabled (not muted) enhancementModel: 'sonnet', // Default to sonnet for feature enhancement validationModel: 'opus', // Default to opus for GitHub issue validation + autoLoadClaudeMd: false, // Default to disabled (user must opt-in) aiProfiles: DEFAULT_AI_PROFILES, projectAnalysis: null, isAnalyzing: false, @@ -1547,6 +1554,14 @@ export const useAppStore = create()( // Validation Model actions setValidationModel: (model) => set({ validationModel: model }), + // Claude Agent SDK Settings actions + setAutoLoadClaudeMd: async (enabled) => { + set({ autoLoadClaudeMd: enabled }); + // Sync to server settings file + const { syncSettingsToServer } = await import('@/hooks/use-settings-migration'); + await syncSettingsToServer(); + }, + // AI Profile actions addAIProfile: (profile) => { const id = `profile-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; @@ -2690,6 +2705,7 @@ export const useAppStore = create()( muteDoneSound: state.muteDoneSound, enhancementModel: state.enhancementModel, validationModel: state.validationModel, + autoLoadClaudeMd: state.autoLoadClaudeMd, // Profiles and sessions aiProfiles: state.aiProfiles, chatSessions: state.chatSessions, diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts index e18c2987..e73e7269 100644 --- a/libs/types/src/settings.ts +++ b/libs/types/src/settings.ts @@ -297,6 +297,10 @@ export interface GlobalSettings { // Window State (Electron only) /** Persisted window bounds for restoring position/size across sessions */ windowBounds?: WindowBounds; + + // Claude Agent SDK Settings + /** Auto-load CLAUDE.md files using SDK's settingSources option */ + autoLoadClaudeMd?: boolean; } /** @@ -392,6 +396,10 @@ export interface ProjectSettings { // Session Tracking /** Last chat session selected in this project */ lastSelectedSessionId?: string; + + // Claude Agent SDK Settings + /** Auto-load CLAUDE.md files using SDK's settingSources option (project override) */ + autoLoadClaudeMd?: boolean; } /** @@ -450,6 +458,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = { recentFolders: [], worktreePanelCollapsed: false, lastSelectedSessionByProject: {}, + autoLoadClaudeMd: false, }; /** Default credentials (empty strings - user must provide API keys) */