docs(optimize): remove references to non-existent .claudeignore

Claude Code does not document or implement a .claudeignore feature.
The junk-reads detector's fix is now a CLAUDE.md instruction asking
Claude to avoid generated/dependency directories. The separate
detectMissingClaudeignore finding and its tests are removed; checking
for the presence of a non-existent file has no signal.

Closes #61.
This commit is contained in:
Ninym 2026-04-17 08:26:03 +02:00
parent f2d1753d3a
commit bd71377fdd
3 changed files with 10 additions and 106 deletions

View file

@ -32,7 +32,6 @@ const TOKENS_PER_SKILL_DEF = 80
const TOKENS_PER_COMMAND_DEF = 60
const CLAUDEMD_TOKENS_PER_LINE = 13
const BASH_TOKENS_PER_CHAR = 0.25
const ESTIMATED_READS_PER_MISSING_IGNORE = 10
// ============================================================================
// Detector thresholds
@ -52,7 +51,6 @@ const LOW_RATIO_HIGH_THRESHOLD = 2
const LOW_RATIO_MEDIUM_THRESHOLD = 3
const MIN_API_CALLS_FOR_CACHE = 10
const CACHE_EXCESS_HIGH_THRESHOLD = 15000
const MISSING_IGNORE_HIGH_THRESHOLD = 3
const UNUSED_MCP_HIGH_THRESHOLD = 3
const GHOST_AGENTS_HIGH_THRESHOLD = 5
const GHOST_AGENTS_MEDIUM_THRESHOLD = 2
@ -97,8 +95,6 @@ const JUNK_PATTERN = new RegExp(`/(?:${JUNK_DIRS.join('|')})/`)
const SHELL_PROFILES = ['.zshrc', '.bashrc', '.bash_profile', '.profile']
const TOP_ITEMS_PREVIEW = 3
const MISSING_IGNORE_PATHS_PREVIEW = 2
const JUNK_DIRS_IGNORE_PREVIEW = 8
const GHOST_NAMES_PREVIEW = 5
const GHOST_CLEANUP_COMMANDS_LIMIT = 10
@ -408,18 +404,17 @@ export function detectJunkReads(calls: ToolCall[], dateRange?: DateRange): Waste
const detected = sorted.map(([d]) => d)
const commonDefaults = ['node_modules', '.git', 'dist', '__pycache__']
const extras = commonDefaults.filter(d => !dirCounts.has(d)).slice(0, Math.max(0, 6 - detected.length))
const ignoreContent = [...detected, ...extras].join('\n')
const dirsToAvoid = [...detected, ...extras].join(', ')
return {
title: 'Claude is reading build/dependency folders',
explanation: `Claude read into ${dirList} (${totalJunkReads} reads). These are generated or dependency directories, not your code. A .claudeignore tells Claude to skip them.`,
explanation: `Claude read into ${dirList} (${totalJunkReads} reads). These are generated or dependency directories, not your code. Tell Claude in CLAUDE.md to avoid them.`,
impact: totalJunkReads > JUNK_READS_HIGH_THRESHOLD ? 'high' : totalJunkReads > JUNK_READS_MEDIUM_THRESHOLD ? 'medium' : 'low',
tokensSaved,
fix: {
type: 'file-content',
label: 'Create .claudeignore in your project root:',
path: '.claudeignore',
content: ignoreContent,
type: 'paste',
label: 'Append to your project CLAUDE.md:',
text: `Do not read or search files under these directories unless I explicitly ask: ${dirsToAvoid}.`,
},
trend,
}
@ -531,43 +526,6 @@ export function detectUnusedMcp(
}
}
export function detectMissingClaudeignore(projectCwds: Set<string>): WasteFinding | null {
const missing: string[] = []
for (const cwd of projectCwds) {
if (!existsSync(cwd)) continue
if (existsSync(join(cwd, '.claudeignore'))) continue
for (const dir of JUNK_DIRS) {
if (existsSync(join(cwd, dir))) {
missing.push(cwd)
break
}
}
}
if (missing.length === 0) return null
const shortPaths = missing.map(shortHomePath)
const display = shortPaths.length <= MISSING_IGNORE_PATHS_PREVIEW + 1
? shortPaths.join(', ')
: `${shortPaths.slice(0, MISSING_IGNORE_PATHS_PREVIEW).join(', ')} + ${shortPaths.length - MISSING_IGNORE_PATHS_PREVIEW} more`
const tokensSaved = missing.length * ESTIMATED_READS_PER_MISSING_IGNORE * AVG_TOKENS_PER_READ
return {
title: `Add .claudeignore to ${missing.length} project${missing.length > 1 ? 's' : ''}`,
explanation: `${missing.length} project${missing.length > 1 ? 's have' : ' has'} build/dependency folders (node_modules, .git, etc.) but no .claudeignore: ${display}. Without it, Claude can wander into them.`,
impact: missing.length >= MISSING_IGNORE_HIGH_THRESHOLD ? 'high' : 'medium',
tokensSaved,
fix: {
type: 'file-content',
label: 'Create .claudeignore in each project root:',
path: '.claudeignore',
content: JUNK_DIRS.slice(0, JUNK_DIRS_IGNORE_PREVIEW).join('\n'),
},
}
}
function expandImports(filePath: string, seen: Set<string>, depth: number): { totalLines: number; importedFiles: number } {
if (depth > MAX_IMPORT_DEPTH || seen.has(filePath)) return { totalLines: 0, importedFiles: 0 }
seen.add(filePath)
@ -1018,7 +976,6 @@ export async function scanAndDetect(
() => detectJunkReads(toolCalls, dateRange),
() => detectDuplicateReads(toolCalls, dateRange),
() => detectUnusedMcp(toolCalls, projects, projectCwds),
() => detectMissingClaudeignore(projectCwds),
() => detectBloatedClaudeMd(projectCwds),
() => detectBashBloat(),
]

View file

@ -15,7 +15,6 @@ vi.mock('os', async () => {
const FAKE_HOME_FOR_MOCK = process.env['CODEBURN_TEST_FAKE_HOME']!
import {
detectMissingClaudeignore,
detectBloatedClaudeMd,
detectUnusedMcp,
detectBashBloat,
@ -60,48 +59,6 @@ afterAll(() => {
}
})
// ============================================================================
// detectMissingClaudeignore
// ============================================================================
describe('detectMissingClaudeignore', () => {
it('flags a project with node_modules but no .claudeignore', () => {
const root = makeFixtureRoot()
const projectDir = join(root, 'myapp')
mkdirSync(join(projectDir, 'node_modules'), { recursive: true })
const finding = detectMissingClaudeignore(new Set([projectDir]))
expect(finding).not.toBeNull()
expect(finding!.impact).toBe('medium')
})
it('does not flag when .claudeignore exists', () => {
const root = makeFixtureRoot()
const projectDir = join(root, 'myapp')
mkdirSync(join(projectDir, 'node_modules'), { recursive: true })
writeFile(join(projectDir, '.claudeignore'), 'node_modules\n')
expect(detectMissingClaudeignore(new Set([projectDir]))).toBeNull()
})
it('does not flag project without junk dirs', () => {
const root = makeFixtureRoot()
const projectDir = join(root, 'myapp')
mkdirSync(join(projectDir, 'src'), { recursive: true })
expect(detectMissingClaudeignore(new Set([projectDir]))).toBeNull()
})
it('escalates to high when three or more projects need it', () => {
const root = makeFixtureRoot()
const cwds = new Set<string>()
for (let i = 0; i < 3; i++) {
const p = join(root, `proj-${i}`)
mkdirSync(join(p, 'node_modules'), { recursive: true })
cwds.add(p)
}
const finding = detectMissingClaudeignore(cwds)
expect(finding!.impact).toBe('high')
})
})
// ============================================================================
// detectBloatedClaudeMd (including @-import expansion)
// ============================================================================

View file

@ -6,7 +6,6 @@ import {
detectLowReadEditRatio,
detectCacheBloat,
detectBloatedClaudeMd,
detectMissingClaudeignore,
computeHealth,
computeTrend,
type ToolCall,
@ -77,13 +76,14 @@ describe('detectJunkReads', () => {
expect(detectJunkReads(calls)).toBeNull()
})
it('builds .claudeignore content from detected + common extras', () => {
it('suggests CLAUDE.md advice listing detected and common junk dirs', () => {
const calls = Array.from({ length: 5 }, () => call('Read', { file_path: '/x/node_modules/a.js' }))
const finding = detectJunkReads(calls)!
expect(finding.fix.type).toBe('file-content')
if (finding.fix.type === 'file-content') {
expect(finding.fix.content).toContain('node_modules')
expect(finding.fix.type).toBe('paste')
if (finding.fix.type === 'paste') {
expect(finding.fix.text).toContain('node_modules')
}
expect(finding.fix.label).toContain('CLAUDE.md')
})
})
@ -207,16 +207,6 @@ describe('detectBloatedClaudeMd', () => {
})
})
describe('detectMissingClaudeignore', () => {
it('returns null for empty set', () => {
expect(detectMissingClaudeignore(new Set())).toBeNull()
})
it('returns null for non-existent cwds', () => {
expect(detectMissingClaudeignore(new Set(['/does/not/exist']))).toBeNull()
})
})
describe('computeHealth', () => {
it('returns A with 100 for no findings', () => {
const { score, grade } = computeHealth([])