From 3154121840ec1a1a5415e4e9c2d4852dad179ba3 Mon Sep 17 00:00:00 2001 From: Kacper Date: Wed, 24 Dec 2025 22:34:22 +0100 Subject: [PATCH] feat: integrate settings service for auto-load CLAUDE.md functionality - Updated API routes to accept an optional settings service for loading the autoLoadClaudeMd setting. - Introduced a new settings helper utility for retrieving project-specific settings. - Enhanced feature generation and spec generation processes to utilize the autoLoadClaudeMd setting. - Refactored relevant route handlers to support the new settings integration across various endpoints. --- apps/server/src/index.ts | 8 ++-- apps/server/src/lib/settings-helpers.ts | 45 +++++++++++++++++++ .../app-spec/generate-features-from-spec.ts | 13 +++++- .../src/routes/app-spec/generate-spec.ts | 21 ++++++++- apps/server/src/routes/app-spec/index.ts | 10 +++-- .../app-spec/routes/generate-features.ts | 8 +++- .../src/routes/app-spec/routes/generate.ts | 6 ++- apps/server/src/routes/context/index.ts | 8 ++-- .../routes/context/routes/describe-file.ts | 15 ++++++- .../routes/context/routes/describe-image.ts | 15 ++++++- apps/server/src/routes/github/index.ts | 8 +++- .../routes/github/routes/validate-issue.ts | 21 +++++++-- .../suggestions/generate-suggestions.ts | 13 +++++- apps/server/src/routes/suggestions/index.ts | 12 ++++- .../src/routes/suggestions/routes/generate.ts | 5 ++- apps/server/src/services/auto-mode-service.ts | 31 +++++++++---- libs/types/src/provider.ts | 12 ++++- 17 files changed, 212 insertions(+), 39 deletions(-) create mode 100644 apps/server/src/lib/settings-helpers.ts diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 37460af9..bd0fd4a0 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -149,17 +149,17 @@ app.use('/api/enhance-prompt', createEnhancePromptRoutes()); app.use('/api/worktree', createWorktreeRoutes()); app.use('/api/git', createGitRoutes()); app.use('/api/setup', createSetupRoutes()); -app.use('/api/suggestions', createSuggestionsRoutes(events)); +app.use('/api/suggestions', createSuggestionsRoutes(events, settingsService)); app.use('/api/models', createModelsRoutes()); -app.use('/api/spec-regeneration', createSpecRegenerationRoutes(events)); +app.use('/api/spec-regeneration', createSpecRegenerationRoutes(events, settingsService)); app.use('/api/running-agents', createRunningAgentsRoutes(autoModeService)); app.use('/api/workspace', createWorkspaceRoutes()); app.use('/api/templates', createTemplatesRoutes()); app.use('/api/terminal', createTerminalRoutes()); app.use('/api/settings', createSettingsRoutes(settingsService)); app.use('/api/claude', createClaudeRoutes(claudeUsageService)); -app.use('/api/github', createGitHubRoutes(events)); -app.use('/api/context', createContextRoutes()); +app.use('/api/github', createGitHubRoutes(events, settingsService)); +app.use('/api/context', createContextRoutes(settingsService)); // Create HTTP server const server = createServer(app); diff --git a/apps/server/src/lib/settings-helpers.ts b/apps/server/src/lib/settings-helpers.ts new file mode 100644 index 00000000..9bf48361 --- /dev/null +++ b/apps/server/src/lib/settings-helpers.ts @@ -0,0 +1,45 @@ +/** + * Helper utilities for loading settings across different parts of the server + */ + +import type { SettingsService } from '../services/settings-service.js'; + +/** + * Get the autoLoadClaudeMd setting, with project settings taking precedence over global. + * Returns false if settings service is not available. + * + * @param projectPath - Path to the project + * @param settingsService - Optional settings service instance + * @param logPrefix - Prefix for log messages (e.g., '[DescribeImage]') + * @returns Promise resolving to the autoLoadClaudeMd setting value + */ +export async function getAutoLoadClaudeMdSetting( + projectPath: string, + settingsService?: SettingsService, + logPrefix = '[SettingsHelper]' +): Promise { + if (!settingsService) { + console.log(`${logPrefix} SettingsService not available, autoLoadClaudeMd disabled`); + return false; + } + + try { + // Check project settings first (takes precedence) + const projectSettings = await settingsService.getProjectSettings(projectPath); + if (projectSettings.autoLoadClaudeMd !== undefined) { + console.log( + `${logPrefix} autoLoadClaudeMd from project settings: ${projectSettings.autoLoadClaudeMd}` + ); + return projectSettings.autoLoadClaudeMd; + } + + // Fall back to global settings + const globalSettings = await settingsService.getGlobalSettings(); + const result = globalSettings.autoLoadClaudeMd ?? false; + console.log(`${logPrefix} autoLoadClaudeMd from global settings: ${result}`); + return result; + } catch (error) { + console.error(`${logPrefix} Failed to load autoLoadClaudeMd setting:`, error); + return false; + } +} diff --git a/apps/server/src/routes/app-spec/generate-features-from-spec.ts b/apps/server/src/routes/app-spec/generate-features-from-spec.ts index 17a83078..e2b7124d 100644 --- a/apps/server/src/routes/app-spec/generate-features-from-spec.ts +++ b/apps/server/src/routes/app-spec/generate-features-from-spec.ts @@ -10,6 +10,8 @@ import { createFeatureGenerationOptions } from '../../lib/sdk-options.js'; import { logAuthStatus } from './common.js'; import { parseAndCreateFeatures } from './parse-and-create-features.js'; import { getAppSpecPath } from '@automaker/platform'; +import type { SettingsService } from '../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js'; const logger = createLogger('SpecRegeneration'); @@ -19,7 +21,8 @@ export async function generateFeaturesFromSpec( projectPath: string, events: EventEmitter, abortController: AbortController, - maxFeatures?: number + maxFeatures?: number, + settingsService?: SettingsService ): Promise { const featureCount = maxFeatures ?? DEFAULT_MAX_FEATURES; logger.debug('========== generateFeaturesFromSpec() started =========='); @@ -91,9 +94,17 @@ IMPORTANT: Do not ask for clarification. The specification is provided above. Ge projectPath: projectPath, }); + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + projectPath, + settingsService, + '[FeatureGeneration]' + ); + const options = createFeatureGenerationOptions({ cwd: projectPath, abortController, + autoLoadClaudeMd, }); logger.debug('SDK Options:', JSON.stringify(options, null, 2)); diff --git a/apps/server/src/routes/app-spec/generate-spec.ts b/apps/server/src/routes/app-spec/generate-spec.ts index 4b6a6426..0762bb90 100644 --- a/apps/server/src/routes/app-spec/generate-spec.ts +++ b/apps/server/src/routes/app-spec/generate-spec.ts @@ -17,6 +17,8 @@ import { createSpecGenerationOptions } from '../../lib/sdk-options.js'; import { logAuthStatus } from './common.js'; import { generateFeaturesFromSpec } from './generate-features-from-spec.js'; import { ensureAutomakerDir, getAppSpecPath } from '@automaker/platform'; +import type { SettingsService } from '../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js'; const logger = createLogger('SpecRegeneration'); @@ -27,7 +29,8 @@ export async function generateSpec( abortController: AbortController, generateFeatures?: boolean, analyzeProject?: boolean, - maxFeatures?: number + maxFeatures?: number, + settingsService?: SettingsService ): Promise { logger.info('========== generateSpec() started =========='); logger.info('projectPath:', projectPath); @@ -83,9 +86,17 @@ ${getStructuredSpecPromptInstruction()}`; content: 'Starting spec generation...\n', }); + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + projectPath, + settingsService, + '[SpecRegeneration]' + ); + const options = createSpecGenerationOptions({ cwd: projectPath, abortController, + autoLoadClaudeMd, outputFormat: { type: 'json_schema', schema: specOutputSchema, @@ -269,7 +280,13 @@ ${getStructuredSpecPromptInstruction()}`; // Create a new abort controller for feature generation const featureAbortController = new AbortController(); try { - await generateFeaturesFromSpec(projectPath, events, featureAbortController, maxFeatures); + await generateFeaturesFromSpec( + projectPath, + events, + featureAbortController, + maxFeatures, + settingsService + ); // Final completion will be emitted by generateFeaturesFromSpec -> parseAndCreateFeatures } catch (featureError) { logger.error('Feature generation failed:', featureError); diff --git a/apps/server/src/routes/app-spec/index.ts b/apps/server/src/routes/app-spec/index.ts index 47950cd3..342aecd7 100644 --- a/apps/server/src/routes/app-spec/index.ts +++ b/apps/server/src/routes/app-spec/index.ts @@ -9,13 +9,17 @@ import { createGenerateHandler } from './routes/generate.js'; import { createGenerateFeaturesHandler } from './routes/generate-features.js'; import { createStopHandler } from './routes/stop.js'; import { createStatusHandler } from './routes/status.js'; +import type { SettingsService } from '../../services/settings-service.js'; -export function createSpecRegenerationRoutes(events: EventEmitter): Router { +export function createSpecRegenerationRoutes( + events: EventEmitter, + settingsService?: SettingsService +): Router { const router = Router(); router.post('/create', createCreateHandler(events)); - router.post('/generate', createGenerateHandler(events)); - router.post('/generate-features', createGenerateFeaturesHandler(events)); + router.post('/generate', createGenerateHandler(events, settingsService)); + router.post('/generate-features', createGenerateFeaturesHandler(events, settingsService)); router.post('/stop', createStopHandler()); router.get('/status', createStatusHandler()); diff --git a/apps/server/src/routes/app-spec/routes/generate-features.ts b/apps/server/src/routes/app-spec/routes/generate-features.ts index a2e6143a..0c80a9b6 100644 --- a/apps/server/src/routes/app-spec/routes/generate-features.ts +++ b/apps/server/src/routes/app-spec/routes/generate-features.ts @@ -13,10 +13,14 @@ import { getErrorMessage, } from '../common.js'; import { generateFeaturesFromSpec } from '../generate-features-from-spec.js'; +import type { SettingsService } from '../../../services/settings-service.js'; const logger = createLogger('SpecRegeneration'); -export function createGenerateFeaturesHandler(events: EventEmitter) { +export function createGenerateFeaturesHandler( + events: EventEmitter, + settingsService?: SettingsService +) { return async (req: Request, res: Response): Promise => { logger.info('========== /generate-features endpoint called =========='); logger.debug('Request body:', JSON.stringify(req.body, null, 2)); @@ -49,7 +53,7 @@ export function createGenerateFeaturesHandler(events: EventEmitter) { setRunningState(true, abortController); logger.info('Starting background feature generation task...'); - generateFeaturesFromSpec(projectPath, events, abortController, maxFeatures) + generateFeaturesFromSpec(projectPath, events, abortController, maxFeatures, settingsService) .catch((error) => { logError(error, 'Feature generation failed with error'); events.emit('spec-regeneration:event', { diff --git a/apps/server/src/routes/app-spec/routes/generate.ts b/apps/server/src/routes/app-spec/routes/generate.ts index 341d634d..a03dacb7 100644 --- a/apps/server/src/routes/app-spec/routes/generate.ts +++ b/apps/server/src/routes/app-spec/routes/generate.ts @@ -13,10 +13,11 @@ import { getErrorMessage, } from '../common.js'; import { generateSpec } from '../generate-spec.js'; +import type { SettingsService } from '../../../services/settings-service.js'; const logger = createLogger('SpecRegeneration'); -export function createGenerateHandler(events: EventEmitter) { +export function createGenerateHandler(events: EventEmitter, settingsService?: SettingsService) { return async (req: Request, res: Response): Promise => { logger.info('========== /generate endpoint called =========='); logger.debug('Request body:', JSON.stringify(req.body, null, 2)); @@ -67,7 +68,8 @@ export function createGenerateHandler(events: EventEmitter) { abortController, generateFeatures, analyzeProject, - maxFeatures + maxFeatures, + settingsService ) .catch((error) => { logError(error, 'Generation failed with error'); diff --git a/apps/server/src/routes/context/index.ts b/apps/server/src/routes/context/index.ts index 37e447bf..3f49f1c1 100644 --- a/apps/server/src/routes/context/index.ts +++ b/apps/server/src/routes/context/index.ts @@ -8,17 +8,19 @@ import { Router } from 'express'; import { createDescribeImageHandler } from './routes/describe-image.js'; import { createDescribeFileHandler } from './routes/describe-file.js'; +import type { SettingsService } from '../../services/settings-service.js'; /** * Create the context router * + * @param settingsService - Optional settings service for loading autoLoadClaudeMd setting * @returns Express router with context endpoints */ -export function createContextRoutes(): Router { +export function createContextRoutes(settingsService?: SettingsService): Router { const router = Router(); - router.post('/describe-image', createDescribeImageHandler()); - router.post('/describe-file', createDescribeFileHandler()); + router.post('/describe-image', createDescribeImageHandler(settingsService)); + router.post('/describe-file', createDescribeFileHandler(settingsService)); return router; } diff --git a/apps/server/src/routes/context/routes/describe-file.ts b/apps/server/src/routes/context/routes/describe-file.ts index 0e680b65..472cbb76 100644 --- a/apps/server/src/routes/context/routes/describe-file.ts +++ b/apps/server/src/routes/context/routes/describe-file.ts @@ -17,6 +17,8 @@ import { PathNotAllowedError } from '@automaker/platform'; import { createCustomOptions } from '../../../lib/sdk-options.js'; import * as secureFs from '../../../lib/secure-fs.js'; import * as path from 'path'; +import type { SettingsService } from '../../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../../lib/settings-helpers.js'; const logger = createLogger('DescribeFile'); @@ -72,9 +74,12 @@ async function extractTextFromStream( /** * Create the describe-file request handler * + * @param settingsService - Optional settings service for loading autoLoadClaudeMd setting * @returns Express request handler for file description */ -export function createDescribeFileHandler(): (req: Request, res: Response) => Promise { +export function createDescribeFileHandler( + settingsService?: SettingsService +): (req: Request, res: Response) => Promise { return async (req: Request, res: Response): Promise => { try { const { filePath } = req.body as DescribeFileRequestBody; @@ -165,6 +170,13 @@ File: ${fileName}${truncated ? ' (truncated)' : ''}`; // Use the file's directory as the working directory const cwd = path.dirname(resolvedPath); + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + cwd, + settingsService, + '[DescribeFile]' + ); + // Use centralized SDK options with proper cwd validation // No tools needed since we're passing file content directly const sdkOptions = createCustomOptions({ @@ -172,6 +184,7 @@ File: ${fileName}${truncated ? ' (truncated)' : ''}`; model: CLAUDE_MODEL_MAP.haiku, maxTurns: 1, allowedTools: [], + autoLoadClaudeMd, sandbox: { enabled: true, autoAllowBashIfSandboxed: true }, }); diff --git a/apps/server/src/routes/context/routes/describe-image.ts b/apps/server/src/routes/context/routes/describe-image.ts index 64ddfa0f..e4821b4a 100644 --- a/apps/server/src/routes/context/routes/describe-image.ts +++ b/apps/server/src/routes/context/routes/describe-image.ts @@ -17,6 +17,8 @@ import { CLAUDE_MODEL_MAP } from '@automaker/types'; import { createCustomOptions } from '../../../lib/sdk-options.js'; import * as fs from 'fs'; import * as path from 'path'; +import type { SettingsService } from '../../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../../lib/settings-helpers.js'; const logger = createLogger('DescribeImage'); @@ -226,9 +228,12 @@ async function extractTextFromStream( * Uses Claude SDK query with multi-part content blocks to include the image (base64), * matching the agent runner behavior. * + * @param settingsService - Optional settings service for loading autoLoadClaudeMd setting * @returns Express request handler for image description */ -export function createDescribeImageHandler(): (req: Request, res: Response) => Promise { +export function createDescribeImageHandler( + settingsService?: SettingsService +): (req: Request, res: Response) => Promise { return async (req: Request, res: Response): Promise => { const requestId = `describe-image-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; const startedAt = Date.now(); @@ -325,12 +330,20 @@ export function createDescribeImageHandler(): (req: Request, res: Response) => P const cwd = path.dirname(actualPath); logger.info(`[${requestId}] Using cwd=${cwd}`); + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + cwd, + settingsService, + '[DescribeImage]' + ); + // Use the same centralized option builder used across the server (validates cwd) const sdkOptions = createCustomOptions({ cwd, model: CLAUDE_MODEL_MAP.haiku, maxTurns: 1, allowedTools: [], + autoLoadClaudeMd, sandbox: { enabled: true, autoAllowBashIfSandboxed: true }, }); diff --git a/apps/server/src/routes/github/index.ts b/apps/server/src/routes/github/index.ts index e8088159..1a2f12ae 100644 --- a/apps/server/src/routes/github/index.ts +++ b/apps/server/src/routes/github/index.ts @@ -16,8 +16,12 @@ import { createDeleteValidationHandler, createMarkViewedHandler, } from './routes/validation-endpoints.js'; +import type { SettingsService } from '../../services/settings-service.js'; -export function createGitHubRoutes(events: EventEmitter): Router { +export function createGitHubRoutes( + events: EventEmitter, + settingsService?: SettingsService +): Router { const router = Router(); router.post('/check-remote', validatePathParams('projectPath'), createCheckGitHubRemoteHandler()); @@ -26,7 +30,7 @@ export function createGitHubRoutes(events: EventEmitter): Router { router.post( '/validate-issue', validatePathParams('projectPath'), - createValidateIssueHandler(events) + createValidateIssueHandler(events, settingsService) ); // Validation management endpoints diff --git a/apps/server/src/routes/github/routes/validate-issue.ts b/apps/server/src/routes/github/routes/validate-issue.ts index 3e75098e..c987453a 100644 --- a/apps/server/src/routes/github/routes/validate-issue.ts +++ b/apps/server/src/routes/github/routes/validate-issue.ts @@ -23,6 +23,8 @@ import { logError, logger, } from './validation-common.js'; +import type { SettingsService } from '../../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../../lib/settings-helpers.js'; /** Valid model values for validation */ const VALID_MODELS: readonly AgentModel[] = ['opus', 'sonnet', 'haiku'] as const; @@ -54,7 +56,8 @@ async function runValidation( issueLabels: string[] | undefined, model: AgentModel, events: EventEmitter, - abortController: AbortController + abortController: AbortController, + settingsService?: SettingsService ): Promise { // Emit start event const startEvent: IssueValidationEvent = { @@ -76,12 +79,20 @@ async function runValidation( // Build the prompt const prompt = buildValidationPrompt(issueNumber, issueTitle, issueBody, issueLabels); + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + projectPath, + settingsService, + '[ValidateIssue]' + ); + // Create SDK options with structured output and abort controller const options = createSuggestionsOptions({ cwd: projectPath, model, systemPrompt: ISSUE_VALIDATION_SYSTEM_PROMPT, abortController, + autoLoadClaudeMd, outputFormat: { type: 'json_schema', schema: issueValidationSchema as Record, @@ -190,7 +201,10 @@ async function runValidation( * - System prompt guiding the validation process * - Async execution with event emission */ -export function createValidateIssueHandler(events: EventEmitter) { +export function createValidateIssueHandler( + events: EventEmitter, + settingsService?: SettingsService +) { return async (req: Request, res: Response): Promise => { try { const { @@ -256,7 +270,8 @@ export function createValidateIssueHandler(events: EventEmitter) { issueLabels, model, events, - abortController + abortController, + settingsService ) .catch((error) => { // Error is already handled inside runValidation (event emitted) diff --git a/apps/server/src/routes/suggestions/generate-suggestions.ts b/apps/server/src/routes/suggestions/generate-suggestions.ts index e4d6aaed..2af01a42 100644 --- a/apps/server/src/routes/suggestions/generate-suggestions.ts +++ b/apps/server/src/routes/suggestions/generate-suggestions.ts @@ -9,6 +9,8 @@ import { createSuggestionsOptions } from '../../lib/sdk-options.js'; import { FeatureLoader } from '../../services/feature-loader.js'; import { getAppSpecPath } from '@automaker/platform'; import * as secureFs from '../../lib/secure-fs.js'; +import type { SettingsService } from '../../services/settings-service.js'; +import { getAutoLoadClaudeMdSetting } from '../../lib/settings-helpers.js'; const logger = createLogger('Suggestions'); @@ -125,7 +127,8 @@ export async function generateSuggestions( projectPath: string, suggestionType: string, events: EventEmitter, - abortController: AbortController + abortController: AbortController, + settingsService?: SettingsService ): Promise { const typePrompts: Record = { features: 'Analyze this project and suggest new features that would add value.', @@ -154,9 +157,17 @@ The response will be automatically formatted as structured JSON.`; // Don't send initial message - let the agent output speak for itself // The first agent message will be captured as an info entry + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await getAutoLoadClaudeMdSetting( + projectPath, + settingsService, + '[Suggestions]' + ); + const options = createSuggestionsOptions({ cwd: projectPath, abortController, + autoLoadClaudeMd, outputFormat: { type: 'json_schema', schema: suggestionsSchema, diff --git a/apps/server/src/routes/suggestions/index.ts b/apps/server/src/routes/suggestions/index.ts index 2ea6f9ae..01e22879 100644 --- a/apps/server/src/routes/suggestions/index.ts +++ b/apps/server/src/routes/suggestions/index.ts @@ -8,11 +8,19 @@ import { validatePathParams } from '../../middleware/validate-paths.js'; import { createGenerateHandler } from './routes/generate.js'; import { createStopHandler } from './routes/stop.js'; import { createStatusHandler } from './routes/status.js'; +import type { SettingsService } from '../../services/settings-service.js'; -export function createSuggestionsRoutes(events: EventEmitter): Router { +export function createSuggestionsRoutes( + events: EventEmitter, + settingsService?: SettingsService +): Router { const router = Router(); - router.post('/generate', validatePathParams('projectPath'), createGenerateHandler(events)); + router.post( + '/generate', + validatePathParams('projectPath'), + createGenerateHandler(events, settingsService) + ); router.post('/stop', createStopHandler()); router.get('/status', createStatusHandler()); diff --git a/apps/server/src/routes/suggestions/routes/generate.ts b/apps/server/src/routes/suggestions/routes/generate.ts index 939e0cde..da57ed76 100644 --- a/apps/server/src/routes/suggestions/routes/generate.ts +++ b/apps/server/src/routes/suggestions/routes/generate.ts @@ -7,10 +7,11 @@ import type { EventEmitter } from '../../../lib/events.js'; import { createLogger } from '@automaker/utils'; import { getSuggestionsStatus, setRunningState, getErrorMessage, logError } from '../common.js'; import { generateSuggestions } from '../generate-suggestions.js'; +import type { SettingsService } from '../../../services/settings-service.js'; const logger = createLogger('Suggestions'); -export function createGenerateHandler(events: EventEmitter) { +export function createGenerateHandler(events: EventEmitter, settingsService?: SettingsService) { return async (req: Request, res: Response): Promise => { try { const { projectPath, suggestionType = 'features' } = req.body as { @@ -37,7 +38,7 @@ export function createGenerateHandler(events: EventEmitter) { setRunningState(true, abortController); // Start generation in background - generateSuggestions(projectPath, suggestionType, events, abortController) + generateSuggestions(projectPath, suggestionType, events, abortController, settingsService) .catch((error) => { logError(error, 'Generate suggestions failed (background)'); events.emit('suggestions:event', { diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index a7b24fef..af9ef746 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -25,7 +25,11 @@ import { promisify } from 'util'; import path from 'path'; import * as secureFs from '../lib/secure-fs.js'; import type { EventEmitter } from '../lib/events.js'; -import { createAutoModeOptions, validateWorkingDirectory } from '../lib/sdk-options.js'; +import { + createAutoModeOptions, + createCustomOptions, + validateWorkingDirectory, +} from '../lib/sdk-options.js'; import { FeatureLoader } from './feature-loader.js'; import type { SettingsService } from './settings-service.js'; @@ -1068,11 +1072,6 @@ Address the follow-up instructions above. Review the previous work and make the * Analyze project to gather context */ async analyzeProject(projectPath: string): Promise { - // Validate project path before proceeding - // This is called here because analyzeProject builds ExecuteOptions directly - // without using a factory function from sdk-options.ts - validateWorkingDirectory(projectPath); - const abortController = new AbortController(); const analysisFeatureId = `analysis-${Date.now()}`; @@ -1100,13 +1099,27 @@ Format your response as a structured markdown document.`; const analysisModel = resolveModelString(undefined, DEFAULT_MODELS.claude); const provider = ProviderFactory.getProviderForModel(analysisModel); - const options: ExecuteOptions = { - prompt, + // Load autoLoadClaudeMd setting + const autoLoadClaudeMd = await this.getAutoLoadClaudeMdSetting(projectPath); + + // Use createCustomOptions for centralized SDK configuration with CLAUDE.md support + const sdkOptions = createCustomOptions({ + cwd: projectPath, model: analysisModel, maxTurns: 5, - cwd: projectPath, allowedTools: ['Read', 'Glob', 'Grep'], abortController, + autoLoadClaudeMd, + }); + + const options: ExecuteOptions = { + prompt, + model: sdkOptions.model ?? analysisModel, + cwd: sdkOptions.cwd ?? projectPath, + maxTurns: sdkOptions.maxTurns, + allowedTools: sdkOptions.allowedTools as string[], + abortController, + settingSources: sdkOptions.settingSources, }; const stream = provider.executeQuery(options); diff --git a/libs/types/src/provider.ts b/libs/types/src/provider.ts index f3aa22d5..53c92717 100644 --- a/libs/types/src/provider.ts +++ b/libs/types/src/provider.ts @@ -19,6 +19,15 @@ export interface ConversationMessage { content: string | Array<{ type: string; text?: string; source?: object }>; } +/** + * System prompt preset configuration for CLAUDE.md auto-loading + */ +export interface SystemPromptPreset { + type: 'preset'; + preset: 'claude_code'; + append?: string; +} + /** * Options for executing a query via a provider */ @@ -26,13 +35,14 @@ export interface ExecuteOptions { prompt: string | Array<{ type: string; text?: string; source?: object }>; model: string; cwd: string; - systemPrompt?: string; + systemPrompt?: string | SystemPromptPreset; 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'>; // Sources for CLAUDE.md loading } /**