Cherry-picked both PRs to main (both had merge conflicts with current main). PR #920 (@Spunky84): CLAUDE_MEM_EXCLUDED_PROJECTS setting with glob patterns to exclude entire projects from memory tracking (privacy/confidentiality). Early-exit in session-init and observation handlers. 11 unit tests. PR #699 (@leepokai): CLAUDE_MEM_FOLDER_MD_EXCLUDE setting with JSON array of paths to exclude from CLAUDE.md file generation (fixes SwiftUI/Xcode build conflicts and drizzle kit migration failures). Closes #620. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -287,6 +287,26 @@ function isProjectRoot(folderPath: string): boolean {
|
||||
return existsSync(gitPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a folder path is excluded from CLAUDE.md generation.
|
||||
* A folder is excluded if it matches or is within any path in the exclude list.
|
||||
*
|
||||
* @param folderPath - Absolute path to check
|
||||
* @param excludePaths - Array of paths to exclude
|
||||
* @returns true if folder should be excluded
|
||||
*/
|
||||
function isExcludedFolder(folderPath: string, excludePaths: string[]): boolean {
|
||||
const normalizedFolder = path.resolve(folderPath);
|
||||
for (const excludePath of excludePaths) {
|
||||
const normalizedExclude = path.resolve(excludePath);
|
||||
if (normalizedFolder === normalizedExclude ||
|
||||
normalizedFolder.startsWith(normalizedExclude + path.sep)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update CLAUDE.md files for folders containing the given files.
|
||||
* Fetches timeline from worker API and writes formatted content.
|
||||
@@ -304,10 +324,21 @@ export async function updateFolderClaudeMdFiles(
|
||||
port: number,
|
||||
projectRoot?: string
|
||||
): Promise<void> {
|
||||
// Load settings to get configurable observation limit
|
||||
// Load settings to get configurable observation limit and exclude list
|
||||
const settings = SettingsDefaultsManager.loadFromFile(SETTINGS_PATH);
|
||||
const limit = parseInt(settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS, 10) || 50;
|
||||
|
||||
// Parse exclude paths from settings
|
||||
let folderMdExcludePaths: string[] = [];
|
||||
try {
|
||||
const parsed = JSON.parse(settings.CLAUDE_MEM_FOLDER_MD_EXCLUDE || '[]');
|
||||
if (Array.isArray(parsed)) {
|
||||
folderMdExcludePaths = parsed.filter((p): p is string => typeof p === 'string');
|
||||
}
|
||||
} catch {
|
||||
logger.warn('FOLDER_INDEX', 'Failed to parse CLAUDE_MEM_FOLDER_MD_EXCLUDE setting');
|
||||
}
|
||||
|
||||
// Track folders containing CLAUDE.md files that were read/modified in this observation.
|
||||
// We must NOT update these - it would cause "file modified since read" errors in Claude Code.
|
||||
// See: https://github.com/thedotmack/claude-mem/issues/859
|
||||
@@ -362,6 +393,11 @@ export async function updateFolderClaudeMdFiles(
|
||||
logger.debug('FOLDER_INDEX', 'Skipping folder with active CLAUDE.md to avoid race condition', { folderPath });
|
||||
continue;
|
||||
}
|
||||
// Skip folders in user-configured exclude list
|
||||
if (folderMdExcludePaths.length > 0 && isExcludedFolder(folderPath, folderMdExcludePaths)) {
|
||||
logger.debug('FOLDER_INDEX', 'Skipping excluded folder', { folderPath });
|
||||
continue;
|
||||
}
|
||||
folderPaths.add(folderPath);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user