fix: resolve path format mismatch in folder CLAUDE.md generation (#794) (#813)

The isDirectChild() function failed to match files when the API used
absolute paths (/Users/x/project/app/api) but the database stored
relative paths (app/api/router.py). This caused all folder CLAUDE.md
files to incorrectly show "No recent activity".

Changes:
- Create shared path-utils module with proper path normalization
- Implement suffix matching strategy for mixed path formats
- Update SessionSearch.ts to use shared utilities
- Update regenerate-claude-md.ts to use shared utilities (was using
  outdated broken logic)
- Prevent spurious directory creation from malformed paths
- Add comprehensive test coverage for path matching edge cases

This is the proper fix for #794, replacing PR #809 which only masked
the bug by skipping file creation when "no activity" was shown.

Co-authored-by: bigphoot <bigphoot@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alexander Knigge
2026-01-26 12:48:31 -08:00
committed by GitHub
parent 0b7ecedcd7
commit 182097ef1c
6 changed files with 257 additions and 57 deletions
+24 -6
View File
@@ -147,8 +147,22 @@ describe('formatTimelineForClaudeMd', () => {
});
describe('writeClaudeMdToFolder', () => {
it('should create CLAUDE.md in new folder', () => {
const folderPath = join(tempDir, 'new-folder');
it('should skip non-existent folders (fix for spurious directory creation)', () => {
const folderPath = join(tempDir, 'non-existent-folder');
const content = '# Recent Activity\n\nTest content';
// Should not throw, should silently skip
writeClaudeMdToFolder(folderPath, content);
// Folder and CLAUDE.md should NOT be created
expect(existsSync(folderPath)).toBe(false);
const claudeMdPath = join(folderPath, 'CLAUDE.md');
expect(existsSync(claudeMdPath)).toBe(false);
});
it('should create CLAUDE.md in existing folder', () => {
const folderPath = join(tempDir, 'existing-folder');
mkdirSync(folderPath, { recursive: true });
const content = '# Recent Activity\n\nTest content';
writeClaudeMdToFolder(folderPath, content);
@@ -180,20 +194,22 @@ describe('writeClaudeMdToFolder', () => {
expect(fileContent).not.toContain('Old content');
});
it('should create nested directories', () => {
it('should not create nested directories (fix for spurious directory creation)', () => {
const folderPath = join(tempDir, 'deep', 'nested', 'folder');
const content = 'Nested content';
// Should not throw, should silently skip
writeClaudeMdToFolder(folderPath, content);
// Nested directories should NOT be created
const claudeMdPath = join(folderPath, 'CLAUDE.md');
expect(existsSync(claudeMdPath)).toBe(true);
expect(existsSync(join(tempDir, 'deep'))).toBe(true);
expect(existsSync(join(tempDir, 'deep', 'nested'))).toBe(true);
expect(existsSync(claudeMdPath)).toBe(false);
expect(existsSync(join(tempDir, 'deep'))).toBe(false);
});
it('should not leave .tmp file after write (atomic write)', () => {
const folderPath = join(tempDir, 'atomic-test');
mkdirSync(folderPath, { recursive: true });
const content = 'Atomic write test';
writeClaudeMdToFolder(folderPath, content);
@@ -218,6 +234,7 @@ describe('updateFolderClaudeMdFiles', () => {
it('should fetch timeline and write CLAUDE.md', async () => {
const folderPath = join(tempDir, 'api-test');
mkdirSync(folderPath, { recursive: true }); // Folder must exist - we no longer create directories
const filePath = join(folderPath, 'test.ts');
const apiResponse = {
@@ -412,6 +429,7 @@ describe('updateFolderClaudeMdFiles', () => {
it('should write CLAUDE.md to resolved projectRoot path', async () => {
const subfolderPath = join(tempDir, 'project-root-write-test', 'src', 'utils');
mkdirSync(subfolderPath, { recursive: true }); // Folder must exist - we no longer create directories
const apiResponse = {
content: [{