Context
snag's core value is pattern-based policy enforcement across git hook phases. The hook runner (lefthook, husky, pre-commit, raw .git/hooks/) is a separate concern.
Today the policy engine (diff.go, msg.go, push.go, prepare.go, rebase.go, config.go, patterns.go) has zero lefthook awareness. This is good. The lefthook coupling is isolated to two places:
snag install (install_hooks.go) — writes lefthook YAML configs
- Detection/nudge (
checkout.go + shell.go) — checks for lefthook config files or .git/hooks/ scripts
Goal
Preserve this separation so snag can support other hook runners (husky, pre-commit, raw .git/hooks/) in the future without touching the policy engine.
What a multi-runner pivot would look like
snag install becomes runner-aware: snag install --runner=lefthook vs --runner=husky vs --runner=git-hooks
- Nudge detection in
checkout.go and shell hooks in shell.go learn to detect other runners
- Hint messages become dynamic ("run: snag install && lefthook install" vs "run: npx husky install")
- Policy engine stays untouched
Ground rules
- Policy commands must never import or reference a hook runner.
diff.go should never know what lefthook is.
- Runner-specific code stays in
install_hooks.go, checkout.go, and shell.go. If a new file needs runner awareness, that's a design smell.
- Config format (
snag.toml) must remain runner-agnostic. No lefthook, husky, or pre-commit concepts leak into snag.toml.
Context
snag's core value is pattern-based policy enforcement across git hook phases. The hook runner (lefthook, husky, pre-commit, raw
.git/hooks/) is a separate concern.Today the policy engine (
diff.go,msg.go,push.go,prepare.go,rebase.go,config.go,patterns.go) has zero lefthook awareness. This is good. The lefthook coupling is isolated to two places:snag install(install_hooks.go) — writes lefthook YAML configscheckout.go+shell.go) — checks for lefthook config files or.git/hooks/scriptsGoal
Preserve this separation so snag can support other hook runners (husky, pre-commit, raw
.git/hooks/) in the future without touching the policy engine.What a multi-runner pivot would look like
snag installbecomes runner-aware:snag install --runner=lefthookvs--runner=huskyvs--runner=git-hookscheckout.goand shell hooks inshell.golearn to detect other runnersGround rules
diff.goshould never know what lefthook is.install_hooks.go,checkout.go, andshell.go. If a new file needs runner awareness, that's a design smell.snag.toml) must remain runner-agnostic. No lefthook, husky, or pre-commit concepts leak intosnag.toml.