Files
claude-mem/.claude/plans/fix-empty-claude-md-files.md
T
Alex Newman 4df9f61347 refactor: implement in-process worker architecture for hooks (#722)
* fix: stop generating empty CLAUDE.md files

- Return empty string instead of "No recent activity" when no observations exist
- Skip writing CLAUDE.md files when formatted content is empty
- Remove redundant "auto-generated by claude-mem" HTML comment
- Clean up 98 existing empty CLAUDE.md files across the codebase
- Update tests to expect empty string for empty input

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* build assets

* refactor: implement in-process worker architecture for hooks

Replaces spawn-based worker startup with in-process architecture:
- Hook processes now become the worker when port 37777 is free
- Eliminates Windows spawn issues (NO SPAWN rule)
- SessionStart chains: smart-install && stop && context

Key changes:
- worker-service.ts: hook case starts WorkerService in-process
- hook-command.ts: skipExit option prevents process.exit() when hosting worker
- hooks.json: single chained command replaces separate start/hook commands
- worker-utils.ts: ensureWorkerRunning() returns boolean, doesn't block
- handlers: graceful fallback when worker unavailable

All 761 tests pass. Manual verification confirms hook stays alive as worker.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* context

* a

* MAESTRO: Mark PR #722 test verification task complete

All 797 tests passed (3 skipped, 0 failed) after merge conflict resolution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* MAESTRO: Mark PR #722 build verification task complete

* MAESTRO: Mark PR #722 code review task complete

Code review verified:
- worker-service.ts hook case starts WorkerService in-process
- hook-command.ts has skipExit option
- hooks.json uses single chained command
- worker-utils.ts ensureWorkerRunning() returns boolean

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* MAESTRO: Mark PR #722 conflict resolution push task complete

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 19:49:15 -05:00

8.2 KiB

Plan: Fix Empty CLAUDE.md File Generation

Problem Statement

Currently the CLAUDE.md generator creates files with wasteful content:

  1. Empty files with "No recent activity" - Files are created even when there are zero observations for a folder
  2. Redundant HTML comment - "" is unnecessary since the <claude-mem-context> tag already conveys this information

These issues create noisy, wasteful context that loads automatically and provides no value.

Phase 0: Documentation Discovery

Allowed APIs (from code analysis)

  • formatTimelineForClaudeMd(timelineText: string): string - src/utils/claude-md-utils.ts:139
  • formatObservationsForClaudeMd(observations, folderPath): string - scripts/regenerate-claude-md.ts:238
  • writeClaudeMdToFolder(folderPath, newContent): void - src/utils/claude-md-utils.ts:94
  • updateFolderClaudeMdFiles(filePaths, project, port, projectRoot): Promise<void> - src/utils/claude-md-utils.ts:257
  • replaceTaggedContent(existingContent, newContent): string - src/utils/claude-md-utils.ts:64

Key Locations

File Lines Purpose
src/utils/claude-md-utils.ts 139-235 Main formatting function
src/utils/claude-md-utils.ts 143 HTML comment generation
src/utils/claude-md-utils.ts 209-211 "No recent activity" handling
src/utils/claude-md-utils.ts 322-323 Write decision point
scripts/regenerate-claude-md.ts 238-286 Regeneration script formatting
scripts/regenerate-claude-md.ts 242 HTML comment generation (duplicate)
scripts/regenerate-claude-md.ts 245-247 "No recent activity" handling
scripts/regenerate-claude-md.ts 452-453 Write decision point
tests/utils/claude-md-utils.test.ts 96-109 Tests for "No recent activity" behavior

Anti-patterns to avoid

  • Do NOT add new configuration options for this behavior - just fix it
  • Do NOT add logging for skipped files (unnecessary noise)

Phase 1: Modify formatTimelineForClaudeMd to Return Empty on No Observations

Task 1.1: Update formatTimelineForClaudeMd return behavior

File: src/utils/claude-md-utils.ts Lines: 139-235

Changes:

  1. Remove HTML comment line at line 143
  2. Change the empty observations case (lines 209-211) to return an empty string instead of "No recent activity"

Before (lines 141-144):

lines.push('# Recent Activity');
lines.push('');
lines.push('<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->');
lines.push('');

After:

lines.push('# Recent Activity');
lines.push('');

Before (lines 209-212):

if (observations.length === 0) {
  lines.push('*No recent activity*');
  return lines.join('\n');
}

After:

if (observations.length === 0) {
  return '';
}

Verification

  • Run bun test tests/utils/claude-md-utils.test.ts
  • Tests at lines 96-109 will FAIL (expected - they test for "No recent activity")
  • Update tests to expect empty string for empty input

Phase 2: Update updateFolderClaudeMdFiles to Skip Empty Content

Task 2.1: Add empty content check before writing

File: src/utils/claude-md-utils.ts Lines: 322-323

Changes: After formatting, check if result is empty and skip writing if so.

Before (lines 321-325):

const formatted = formatTimelineForClaudeMd(result.content[0].text);
writeClaudeMdToFolder(folderPath, formatted);

logger.debug('FOLDER_INDEX', 'Updated CLAUDE.md', { folderPath });

After:

const formatted = formatTimelineForClaudeMd(result.content[0].text);
if (!formatted) {
  logger.debug('FOLDER_INDEX', 'No observations for folder, skipping', { folderPath });
  continue;
}
writeClaudeMdToFolder(folderPath, formatted);

logger.debug('FOLDER_INDEX', 'Updated CLAUDE.md', { folderPath });

Verification

  • Grep for files containing "No recent activity": should find none after running

Phase 3: Update Regeneration Script

Task 3.1: Remove HTML comment from formatObservationsForClaudeMd

File: scripts/regenerate-claude-md.ts Lines: 238-286

Changes:

  1. Remove HTML comment line at line 242
  2. Change empty observations case (lines 245-247) to return empty string

Before (lines 240-244):

lines.push('# Recent Activity');
lines.push('');
lines.push('<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->');
lines.push('');

After:

lines.push('# Recent Activity');
lines.push('');

Before (lines 245-248):

if (observations.length === 0) {
  lines.push('*No recent activity*');
  return lines.join('\n');
}

After:

if (observations.length === 0) {
  return '';
}

Task 3.2: Update regenerateFolder to handle empty formatted content

File: scripts/regenerate-claude-md.ts Lines: 432-459

The script already skips folders with no observations (lines 443-444), so this change is already compatible. The formatObservationsForClaudeMd returning empty string doesn't change behavior since observations are checked before calling it.

Verification

  • Run bun scripts/regenerate-claude-md.ts --dry-run in the project
  • Should NOT show any folders with 0 observations

Phase 4: Update Tests

Task 4.1: Update tests for new empty behavior

File: tests/utils/claude-md-utils.test.ts Lines: 96-109

Changes: Update the two tests that expect "No recent activity" to expect empty string instead.

Before (lines 96-101):

it('should return "No recent activity" for empty input', () => {
  const result = formatTimelineForClaudeMd('');

  expect(result).toContain('# Recent Activity');
  expect(result).toContain('*No recent activity*');
});

After:

it('should return empty string for empty input', () => {
  const result = formatTimelineForClaudeMd('');

  expect(result).toBe('');
});

Before (lines 103-109):

it('should return "No recent activity" when no table rows exist', () => {
  const input = 'Just some plain text without table rows';

  const result = formatTimelineForClaudeMd(input);

  expect(result).toContain('*No recent activity*');
});

After:

it('should return empty string when no table rows exist', () => {
  const input = 'Just some plain text without table rows';

  const result = formatTimelineForClaudeMd(input);

  expect(result).toBe('');
});

Task 4.2: Remove HTML comment assertions from any other tests

Search for tests that assert on "auto-generated" comment and update accordingly.

Verification

  • Run full test suite: bun test
  • All tests should pass

Phase 5: Cleanup Existing Empty Files

Task 5.1: Run cleanup to remove existing empty CLAUDE.md files

Command:

bun scripts/regenerate-claude-md.ts --clean

This will:

  • Find all CLAUDE.md files with <claude-mem-context> tags
  • Strip the tagged section
  • Delete files that become empty after stripping
  • Preserve files that have user content outside the tags

Verification

  • grep -r "No recent activity" . --include="CLAUDE.md" should return no results
  • grep -r "auto-generated by claude-mem" . --include="CLAUDE.md" should return no results

Summary of Changes

File Change
src/utils/claude-md-utils.ts:143 Remove HTML comment line
src/utils/claude-md-utils.ts:209-211 Return empty string instead of "No recent activity"
src/utils/claude-md-utils.ts:322 Skip writing if formatted content is empty
scripts/regenerate-claude-md.ts:242 Remove HTML comment line
scripts/regenerate-claude-md.ts:245-247 Return empty string instead of "No recent activity"
tests/utils/claude-md-utils.test.ts:96-109 Update tests to expect empty string

Final Verification Checklist

  • bun test passes
  • No "No recent activity" CLAUDE.md files exist
  • No "auto-generated" comments in CLAUDE.md files
  • Build succeeds: npm run build-and-sync
  • New observations correctly generate CLAUDE.md files with content
  • Folders without observations get no CLAUDE.md file created