From 99ea9aa3e923c1aedd3f7b00393b00d726eee846 Mon Sep 17 00:00:00 2001 From: Marcus Puchalla Date: Sun, 22 Feb 2026 01:39:57 +0100 Subject: [PATCH] fix: tldr-read-enforcer hook ignored when Claude uses a relative file path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hook checks the file size before deciding whether to intercept a Read. It does this with statSync(filePath). But statSync needs an absolute path — if Claude passes a relative path like 'src/api/foo.ts', statSync can't find the file (the hook process has a different working directory than the project). It throws, the catch block silently returns {}, and the hook does nothing — the raw file is read as normal, bypassing the TLDR token savings entirely. Fix: convert relative paths to absolute using input.cwd (the project directory that Claude Code provides in the hook payload): const filePath = isAbsolute(rawFilePath) ? rawFilePath : join(input.cwd, rawFilePath); Also fixes pre-existing TypeScript compile errors: - daemon-client import missing .js extension (required by NodeNext module resolution) - DaemonResponse interface missing imports/importers fields (used in importsDaemon()) - tsconfig excludes src/__tests__ to stop test files breaking the build --- .claude/hooks/src/daemon-client.ts | 3 +++ .claude/hooks/src/tldr-read-enforcer.ts | 11 ++++++++--- .claude/hooks/tsconfig.json | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.claude/hooks/src/daemon-client.ts b/.claude/hooks/src/daemon-client.ts index 479b997a..60ffbf30 100644 --- a/.claude/hooks/src/daemon-client.ts +++ b/.claude/hooks/src/daemon-client.ts @@ -189,6 +189,9 @@ export interface DaemonResponse { // Track response fields hook?: string; total_invocations?: number; + // Imports/importers response fields + imports?: any[]; + importers?: any[]; } /** diff --git a/.claude/hooks/src/tldr-read-enforcer.ts b/.claude/hooks/src/tldr-read-enforcer.ts index 3090da00..f9138c6f 100644 --- a/.claude/hooks/src/tldr-read-enforcer.ts +++ b/.claude/hooks/src/tldr-read-enforcer.ts @@ -10,8 +10,8 @@ */ import { readFileSync, existsSync, statSync } from 'fs'; -import { basename, extname } from 'path'; -import { queryDaemonSync, DaemonResponse, trackHookActivitySync } from './daemon-client'; +import { basename, extname, resolve } from 'path'; +import { queryDaemonSync, DaemonResponse, trackHookActivitySync } from './daemon-client.js'; // Search context from smart-search-router interface SearchContext { @@ -371,7 +371,12 @@ async function main() { return; } - const filePath = input.tool_input.file_path || ''; + const rawFilePath = input.tool_input.file_path || ''; + // resolve() guarantees an absolute path: if rawFilePath is already absolute it returns + // it as-is; if relative, it resolves against projectCwd. Falls back to process.cwd() + // if input.cwd is absent (hook spec guarantees it, but be defensive). + const projectCwd = input.cwd || process.cwd(); + const filePath = resolve(projectCwd, rawFilePath); // Allow non-code files if (!isCodeFile(filePath)) { diff --git a/.claude/hooks/tsconfig.json b/.claude/hooks/tsconfig.json index 713fbd2a..8d3404ec 100644 --- a/.claude/hooks/tsconfig.json +++ b/.claude/hooks/tsconfig.json @@ -15,5 +15,5 @@ "types": ["node"] }, "include": ["src/**/*.ts"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist", "src/__tests__"] }