HookStackGitHub
Back to catalogue
ValidationPostToolUse· Write|EditPostToolUseAfter tool execution · non-blocking· non-blocking

Conflict marker detector

Leftover merge conflict markers caught instantly

After every file write or edit, checks the file on disk for leftover git conflict markers. Requires both an opening and a closing marker line to flag, which avoids false positives on markdown setext underlines. Non-blocking: feeds a warning back to the agent so it resolves the conflict before moving on.

What does the Conflict marker detector hook do?

Conflict marker detector is a Claude Code PostToolUse hook matching Write|Edit. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Leftover merge conflict markers caught instantly.

Use cases

  • Catching half-resolved conflicts after an assisted merge or rebase
  • Preventing conflict markers from reaching CI or production
  • Flagging files pasted from a conflicted state

Tags

#validation#git#merge#quality

settings.json fragment

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/post-edit-conflict-marker-check.mjs"
          }
        ]
      }
    ]
  }
}

Script · .claude/hooks/post-edit-conflict-marker-check.mjs

#!/usr/bin/env node
// Signale les marqueurs de conflit git oubliés après une écriture (PostToolUse Write|Edit)
import { readFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url';

const OPEN_MARKER = /^<{7} /m;
const CLOSE_MARKER = /^>{7} /m;

export function run(input, { readFile = readFileSync, fileExists = existsSync } = {}) {
  const filePath = input.tool_input?.file_path ?? '';
  if (!filePath || !fileExists(filePath)) return null;

  let content;
  try {
    content = readFile(filePath, 'utf8');
  } catch {
    return null;
  }

  // Les deux bornes sont requises : évite les faux positifs (ex. soulignés markdown)
  if (!OPEN_MARKER.test(content) || !CLOSE_MARKER.test(content)) return null;

  return {
    message: `[conflict-marker] ${filePath} contains git conflict markers (<<<<<<< / >>>>>>>). Resolve the conflict and remove the markers before moving on.\n`,
  };
}

/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
  const input = JSON.parse(readFileSync(0, 'utf8'));
  const result = run(input);
  if (result?.message) process.stderr.write(result.message);
}