Skip to content

Contextual Detection

How Lore's post-commit hook decides what to do with each commit.

Overview

When the hook fires after a commit, Lore evaluates a chain of rules before asking any questions. The first matching rule wins.

Detection Chain

flowchart TD
    A["Post-commit hook fires"] --> A1["Reconnect stdin (< /dev/tty)"]
    A1 --> B{"doc-skip in message?"}
    B -->|Yes| C["Skip silently"]
    B -->|No| D{"/dev/tty available?<br/>TERM != dumb?"}
    D -->|"No (CI, pipe, dumb term)"| E["Defer to pending"]
    D -->|Yes| F{"Rebase in progress?"}
    F -->|Yes| E
    F -->|No| G{"Merge commit?"}
    G -->|Yes| H["Skip — 1-line message"]
    G -->|No| I1{"Cherry-pick?"}
    I1 -->|Yes| I2{"Doc exists for<br/>source commit?"}
    I2 -->|Yes| C
    I2 -->|No| J1{"Amend?"}
    I1 -->|No| J1
    J1 -->|Yes| J2{"Doc exists for<br/>pre-amend commit?"}
    J2 -->|No| R["Proceed — create new doc"]
    J2 -->|Yes| K0{"Question 0:<br/>Document this? Y/n"}
    K0 -->|No| C
    K0 -->|Yes| K["[U]pdate / [C]reate / [S]kip"]
    J1 -->|No| L["Decision Engine scoring"]
    L --> M{"Score?"}
    M -->|">=60"| N["Ask full questions"]
    M -->|"35-59"| O["Ask reduced questions"]
    M -->|"15-34"| P["Suggest skip — confirm"]
    M -->|"<15"| Q["Auto-skip silently"]
    E --> E2["VS Code IPC<br/>(if GIT_ASKPASS)"]
    E2 -.->|fallback| E3["OS dialog<br/>(osascript / zenity / PowerShell)"]
    E3 -.->|fallback| E4["Lock file<br/>(~/.lore/notify.lock)"]

Detection Rules (Priority Order)

# Rule Action Reason
1 [doc-skip] in commit message Skip (silent) Explicit developer intent
2 Non-TTY or TERM=dumb Defer to pending CI/pipes must never block
3 Rebase in progress Defer to pending Avoid prompts during replay
4 Merge commit (2+ parents) Skip (1-line msg) Infrastructure commits
5a Cherry-pick + source doc exists Skip silently Already documented
5b Cherry-pick + no source doc Continue evaluation Source not in corpus — let the next rule decide
6a Amend + existing doc Question 0 → [U]pdate / [C]reate / [S]kip Editing prior work (configurable: hooks.amend_prompt)
6b Amend + no existing doc Proceed — create a new doc First-time documentation of an amended commit
7 Decision Engine score Score-based action (full / reduced / suggest skip / auto-skip) Multi-signal analysis on every other commit

Amend Workflow

When git commit --amend is detected and a document exists for the pre-amend commit:

  1. Question 0: "Amend detected. Document this change? [Y/n]" — skip for trivial typo fixes
  2. Choice: "[U]pdate existing / [C]reate new / [S]kip?"
  3. Update: Pre-fills Type, What, and Why from the existing document, then overwrites it
  4. Create: Creates a new document (the original remains)
  5. Skip: Does nothing

Configure via .lorerc:

hooks:
  amend_prompt: true  # Set to false to skip Question 0

How stdin Works in Git Hooks

Git redirects stdin to /dev/null for hooks — even when you commit from an interactive terminal. This means isatty(stdin) always returns false inside a hook.

Lore's hook solves this by reconnecting stdin from the terminal:

exec lore _hook-post-commit < /dev/tty

This is why interactive questions work in terminal emulators (iTerm2, Terminal.app, VS Code integrated terminal) but not in environments where /dev/tty is unavailable (CI, Docker, pipes).

Windows: Git uses Git Bash (MSYS2) for hooks, which provides /dev/tty. Interactive questions work in Git Bash, Windows Terminal, and VS Code integrated terminal. PowerShell and CMD without Git Bash defer to pending.

Non-TTY Detection

After reconnecting stdin via /dev/tty, Lore checks whether stdin is a real TTY:

Environment /dev/tty isatty(stdin) Behavior
Terminal (iTerm, Terminal.app) Available true Interactive questions
VS Code integrated terminal Available true Interactive questions
CI/CD (GitHub Actions, Docker) Not available false Silent defer to pending
Pipe (git commit \| ...) Not available false Silent defer to pending
Cron/scripts Not available false Silent defer to pending

When stdin is not a TTY, the commit is deferred to pending. If an IDE is detected (via GIT_ASKPASS), Lore also sends a notification.

IDE Detection for Notifications

After deferring, Lore detects the IDE environment to send a notification. VS Code and its forks are identified via the GIT_ASKPASS environment variable (containing "code", "cursor", "windsurf", or "codium" in the path). A secondary signal is VSCODE_GIT_ASKPASS_NODE.

IDE Notifications

When a commit is deferred and an IDE is detected, Lore sends a notification:

  1. VS Code IPC — Native extension notification (multi-instance aware)
  2. OS Dialogosascript (macOS), zenity/kdialog (Linux), PowerShell (Windows)
  3. Fallback — Lock file notification (~/.lore/notify.lock)

Skip Patterns

Explicit Skip

Add [doc-skip] anywhere in your commit message:

git commit -m "chore: update deps [doc-skip]"
# → Lore skips silently, counts as "covered" in metrics

Decision Engine Auto-Skip

Certain commit types are auto-skipped by default. Configure via .lorerc:

# .lorerc
decision:
  always_skip: [docs, style, ci, build]

Commits matching these conventional types receive a score of 0 and are skipped silently.

Troubleshooting

"lore shows a dialog instead of interactive questions"

Your hook is likely outdated — it is missing the < /dev/tty redirect that reconnects stdin from the terminal. Reinstall:

lore hook uninstall
lore hook install

Verify:

grep "dev/tty" .git/hooks/post-commit
# Should show: exec lore _hook-post-commit < /dev/tty

"lore doesn't trigger after my commit"

Check in this order:

  1. Hook installed? grep "LORE" .git/hooks/post-commit
  2. Hook executable? ls -la .git/hooks/post-commit (should show -rwx)
  3. lore in PATH? which lore
  4. Score too low? lore decision --explain HEAD — might be auto-skipped
  5. Non-TTY? Check lore pending — the commit may have been deferred

"lore asks too many questions for trivial commits"

Add overrides in .lorerc:

decision:
  always_skip: [docs, style, ci, build, chore]
  threshold_full: 70    # Default: 60. Raise to reduce how often full questions are asked.

Or use [doc-skip] in your commit messages for one-off cases.

Tips & Tricks

  • Use [doc-skip] for trivial commits (typo fixes, CI config, dependency bumps).
  • Check what would happen: lore decision --explain HEAD shows the full scoring breakdown.
  • Customize always_ask and always_skip in .lorerc to match your team's conventions.
  • Rebased commits go to pending — run lore pending resolve after a rebase to clear them.
  • If you press Ctrl+C during any question (type selector, What, Why, amend prompts), partial answers are saved immediately to .lore/pending/. Resume with lore pending resolve.

See Also