claude-gate-1.0.0: src/Claude/Gate/FileContext.hs
-- | Render the current contents of the touched files for a reviewer prompt.
--
-- The dumbify canary and the rule reviewer both get the diffs AND the full
-- current file, so a reference the diff makes to code defined elsewhere in the
-- same file is not a false "I can't see it" confusion, and the reviewer can
-- judge the ABSENCE of required elements (e.g. a missing type signature) that a
-- diff fragment alone cannot show. Each file is capped to keep the prompt bounded.
module Claude.Gate.FileContext
( renderFullFiles
, maxFileContextChars
) where
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Text.Encoding (decodeUtf8Lenient)
import Data.ByteString qualified as ByteString
import System.Directory (doesFileExist)
-- | Each file's contents are truncated to this many bytes, matching the shell
-- gate's @head -c 40000@ per file.
maxFileContextChars :: Int
maxFileContextChars = 40_000
-- | A labelled block per existing file, in the given order. Missing files are
-- skipped (a path may have been deleted this turn).
renderFullFiles :: [FilePath] -> IO Text
renderFullFiles paths = Text.concat <$> mapM renderOne paths
renderOne :: FilePath -> IO Text
renderOne path = do
present <- doesFileExist path
if not present
then pure ""
else do
raw <- ByteString.readFile path
let body = decodeUtf8Lenient (ByteString.take maxFileContextChars raw)
pure (Text.concat ["\n--- ", Text.pack path, " ---\n", body, "\n"])