From 072ad72f14dba497c86cde9ad37db97fd56c749b Mon Sep 17 00:00:00 2001 From: Kacper Date: Wed, 24 Dec 2025 23:17:20 +0100 Subject: [PATCH] refactor: implement filterClaudeMdFromContext utility for context file handling - Introduced a new utility function to filter out CLAUDE.md from context files when autoLoadClaudeMd is enabled, enhancing clarity and preventing duplication. - Updated AgentService and AutoModeService to utilize the new filtering function, streamlining context file management. - Improved documentation for the new utility, detailing its purpose and usage in context file handling. --- apps/server/src/lib/settings-helpers.ts | 67 ++++++++++++++- apps/server/src/services/agent-service.ts | 42 +--------- apps/server/src/services/auto-mode-service.ts | 82 +------------------ 3 files changed, 71 insertions(+), 120 deletions(-) diff --git a/apps/server/src/lib/settings-helpers.ts b/apps/server/src/lib/settings-helpers.ts index 477c81ec..9c4456ff 100644 --- a/apps/server/src/lib/settings-helpers.ts +++ b/apps/server/src/lib/settings-helpers.ts @@ -1,8 +1,9 @@ /** - * Helper utilities for loading settings across different parts of the server + * Helper utilities for loading settings and context file handling across different parts of the server */ import type { SettingsService } from '../services/settings-service.js'; +import type { ContextFilesResult, ContextFileInfo } from '@automaker/utils'; /** * Get the autoLoadClaudeMd setting, with project settings taking precedence over global. @@ -43,3 +44,67 @@ export async function getAutoLoadClaudeMdSetting( throw error; } } + +/** + * Filters out CLAUDE.md from context files when autoLoadClaudeMd is enabled + * and rebuilds the formatted prompt without it. + * + * When autoLoadClaudeMd is true, the SDK handles CLAUDE.md loading via settingSources, + * so we need to exclude it from the manual context loading to avoid duplication. + * Other context files (CODE_QUALITY.md, CONVENTIONS.md, etc.) are preserved. + * + * @param contextResult - Result from loadContextFiles + * @param autoLoadClaudeMd - Whether SDK auto-loading is enabled + * @returns Filtered context prompt (empty string if no non-CLAUDE.md files) + */ +export function filterClaudeMdFromContext( + contextResult: ContextFilesResult, + autoLoadClaudeMd: boolean +): string { + // If autoLoadClaudeMd is disabled, return the original prompt unchanged + if (!autoLoadClaudeMd || contextResult.files.length === 0) { + return contextResult.formattedPrompt; + } + + // Filter out CLAUDE.md (case-insensitive) + const nonClaudeFiles = contextResult.files.filter((f) => f.name.toLowerCase() !== 'claude.md'); + + // If all files were CLAUDE.md, return empty string + if (nonClaudeFiles.length === 0) { + return ''; + } + + // Rebuild prompt without CLAUDE.md using the same format as loadContextFiles + const formattedFiles = nonClaudeFiles.map((file) => formatContextFileEntry(file)); + + return `# Project Context Files + +The following context files provide project-specific rules, conventions, and guidelines. +Each file serves a specific purpose - use the description to understand when to reference it. +If you need more details about a context file, you can read the full file at the path provided. + +**IMPORTANT**: You MUST follow the rules and conventions specified in these files. +- Follow ALL commands exactly as shown (e.g., if the project uses \`pnpm\`, NEVER use \`npm\` or \`npx\`) +- Follow ALL coding conventions, commit message formats, and architectural patterns specified +- Reference these rules before running ANY shell commands or making commits + +--- + +${formattedFiles.join('\n\n---\n\n')} + +--- + +**REMINDER**: Before taking any action, verify you are following the conventions specified above. +`; +} + +/** + * Format a single context file entry for the prompt + * (Matches the format used in @automaker/utils/context-loader.ts) + */ +function formatContextFileEntry(file: ContextFileInfo): string { + const header = `## ${file.name}`; + const pathInfo = `**Path:** \`${file.path}\``; + const descriptionInfo = file.description ? `\n**Purpose:** ${file.description}` : ''; + return `${header}\n${pathInfo}${descriptionInfo}\n\n${file.content}`; +} diff --git a/apps/server/src/services/agent-service.ts b/apps/server/src/services/agent-service.ts index 62811ba3..323c23c8 100644 --- a/apps/server/src/services/agent-service.ts +++ b/apps/server/src/services/agent-service.ts @@ -17,7 +17,7 @@ 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'; -import { getAutoLoadClaudeMdSetting } from '../lib/settings-helpers.js'; +import { getAutoLoadClaudeMdSetting, filterClaudeMdFromContext } from '../lib/settings-helpers.js'; interface Message { id: string; @@ -205,45 +205,7 @@ export class AgentService { // When autoLoadClaudeMd is enabled, filter out CLAUDE.md to avoid duplication // (SDK handles CLAUDE.md via settingSources), but keep other context files like CODE_QUALITY.md - let contextFilesPrompt = contextResult.formattedPrompt; - if (autoLoadClaudeMd && contextResult.files.length > 0) { - const nonClaudeFiles = contextResult.files.filter( - (f) => f.name.toLowerCase() !== 'claude.md' - ); - - // Rebuild prompt without CLAUDE.md - if (nonClaudeFiles.length > 0) { - const formattedFiles = nonClaudeFiles.map((file) => { - const header = `## ${file.name}`; - const pathInfo = `**Path:** \`${file.path}\``; - const descriptionInfo = file.description ? `\n**Purpose:** ${file.description}` : ''; - return `${header}\n${pathInfo}${descriptionInfo}\n\n${file.content}`; - }); - - contextFilesPrompt = `# Project Context Files - -The following context files provide project-specific rules, conventions, and guidelines. -Each file serves a specific purpose - use the description to understand when to reference it. -If you need more details about a context file, you can read the full file at the path provided. - -**IMPORTANT**: You MUST follow the rules and conventions specified in these files. -- Follow ALL commands exactly as shown (e.g., if the project uses \`pnpm\`, NEVER use \`npm\` or \`npx\`) -- Follow ALL coding conventions, commit message formats, and architectural patterns specified -- Reference these rules before running ANY shell commands or making commits - ---- - -${formattedFiles.join('\n\n---\n\n')} - ---- - -**REMINDER**: Before taking any action, verify you are following the conventions specified above. -`; - } else { - // All files were CLAUDE.md, so no context to add - contextFilesPrompt = ''; - } - } + const contextFilesPrompt = filterClaudeMdFromContext(contextResult, autoLoadClaudeMd); // Build combined system prompt with base prompt and context files const baseSystemPrompt = this.getSystemPrompt(); diff --git a/apps/server/src/services/auto-mode-service.ts b/apps/server/src/services/auto-mode-service.ts index 38da279e..bcdb92a8 100644 --- a/apps/server/src/services/auto-mode-service.ts +++ b/apps/server/src/services/auto-mode-service.ts @@ -32,7 +32,7 @@ import { } from '../lib/sdk-options.js'; import { FeatureLoader } from './feature-loader.js'; import type { SettingsService } from './settings-service.js'; -import { getAutoLoadClaudeMdSetting } from '../lib/settings-helpers.js'; +import { getAutoLoadClaudeMdSetting, filterClaudeMdFromContext } from '../lib/settings-helpers.js'; const execAsync = promisify(exec); @@ -576,45 +576,7 @@ export class AutoModeService { // When autoLoadClaudeMd is enabled, filter out CLAUDE.md to avoid duplication // (SDK handles CLAUDE.md via settingSources), but keep other context files like CODE_QUALITY.md - let contextFilesPrompt = contextResult.formattedPrompt; - if (autoLoadClaudeMd && contextResult.files.length > 0) { - const nonClaudeFiles = contextResult.files.filter( - (f) => f.name.toLowerCase() !== 'claude.md' - ); - - // Rebuild prompt without CLAUDE.md - if (nonClaudeFiles.length > 0) { - const formattedFiles = nonClaudeFiles.map((file) => { - const header = `## ${file.name}`; - const pathInfo = `**Path:** \`${file.path}\``; - const descriptionInfo = file.description ? `\n**Purpose:** ${file.description}` : ''; - return `${header}\n${pathInfo}${descriptionInfo}\n\n${file.content}`; - }); - - contextFilesPrompt = `# Project Context Files - -The following context files provide project-specific rules, conventions, and guidelines. -Each file serves a specific purpose - use the description to understand when to reference it. -If you need more details about a context file, you can read the full file at the path provided. - -**IMPORTANT**: You MUST follow the rules and conventions specified in these files. -- Follow ALL commands exactly as shown (e.g., if the project uses \`pnpm\`, NEVER use \`npm\` or \`npx\`) -- Follow ALL coding conventions, commit message formats, and architectural patterns specified -- Reference these rules before running ANY shell commands or making commits - ---- - -${formattedFiles.join('\n\n---\n\n')} - ---- - -**REMINDER**: Before taking any action, verify you are following the conventions specified above. -`; - } else { - // All files were CLAUDE.md, so no context to add - contextFilesPrompt = ''; - } - } + const contextFilesPrompt = filterClaudeMdFromContext(contextResult, autoLoadClaudeMd); if (options?.continuationPrompt) { // Continuation prompt is used when recovering from a plan approval @@ -819,45 +781,7 @@ ${formattedFiles.join('\n\n---\n\n')} // When autoLoadClaudeMd is enabled, filter out CLAUDE.md to avoid duplication // (SDK handles CLAUDE.md via settingSources), but keep other context files like CODE_QUALITY.md - let contextFilesPrompt = contextResult.formattedPrompt; - if (autoLoadClaudeMd && contextResult.files.length > 0) { - const nonClaudeFiles = contextResult.files.filter( - (f) => f.name.toLowerCase() !== 'claude.md' - ); - - // Rebuild prompt without CLAUDE.md - if (nonClaudeFiles.length > 0) { - const formattedFiles = nonClaudeFiles.map((file) => { - const header = `## ${file.name}`; - const pathInfo = `**Path:** \`${file.path}\``; - const descriptionInfo = file.description ? `\n**Purpose:** ${file.description}` : ''; - return `${header}\n${pathInfo}${descriptionInfo}\n\n${file.content}`; - }); - - contextFilesPrompt = `# Project Context Files - -The following context files provide project-specific rules, conventions, and guidelines. -Each file serves a specific purpose - use the description to understand when to reference it. -If you need more details about a context file, you can read the full file at the path provided. - -**IMPORTANT**: You MUST follow the rules and conventions specified in these files. -- Follow ALL commands exactly as shown (e.g., if the project uses \`pnpm\`, NEVER use \`npm\` or \`npx\`) -- Follow ALL coding conventions, commit message formats, and architectural patterns specified -- Reference these rules before running ANY shell commands or making commits - ---- - -${formattedFiles.join('\n\n---\n\n')} - ---- - -**REMINDER**: Before taking any action, verify you are following the conventions specified above. -`; - } else { - // All files were CLAUDE.md, so no context to add - contextFilesPrompt = ''; - } - } + const contextFilesPrompt = filterClaudeMdFromContext(contextResult, autoLoadClaudeMd); // Build complete prompt with feature info, previous context, and follow-up instructions let fullPrompt = `## Follow-up on Feature Implementation