It's not automatic detection. The IDE extensions tell git-ai what kind of change is happening by calling git-ai checkpoint <preset> --hook-input <json>.
Each supported tool (Claude Code, Cursor, Copilot, etc.) has a preset that interprets a hook_event_name field in the JSON payload. The pattern is the same across all presets: two checkpoints bracket each AI edit.
Before the AI edits — the extension fires a "before" event (PreToolUse for Claude, beforeSubmitPrompt for Cursor, before_edit for tab completions). The preset returns CheckpointKind::Human. This captures the baseline state of files so git-ai knows what the human wrote before the AI touched anything.
After the AI edits — the extension fires an "after" event. The preset returns either CheckpointKind::AiAgent (for conversational tools like Claude Code, Cursor composer) or CheckpointKind::AiTab (for inline tab completions like Copilot tab).
The difference between AiAgent and AiTab is just which preset handles it. The ai_tab preset always returns AiTab and carries no transcript (tab completions aren't conversations). Agent presets return AiAgent and include the full conversation transcript, model name, and session ID.
So the before/after pair is what lets git-ai diff the two snapshots and attribute the lines in between to the right author. Lines that changed between a Human checkpoint and the subsequent AiAgent checkpoint were written by the AI. Lines changed outside that window are human-authored.
Each preset also extracts tool-specific metadata differently — Claude's reads the JSONL transcript file, Cursor's queries its SQLite database, tab completions just pass through the completion ID. But the kind determination is always just a string match on hook_event_name.