Skip to main content

Modular AI push-hook workflow runner

Project description

ai-push-hooks

ai-push-hooks is a configurable pre-push workflow runner for local Git repositories.

It runs module-based steps (collect, LLM, apply, exec, assert) before push so you can automate checks and optional repo maintenance tasks.

Install

Mise (recommended for repository hooks)

Pin the currently published release in the consuming repository:

mise use npm:ai-push-hooks@0.1.19

This adds the following project-level tool entry to mise.toml and installs it:

[tools]
"npm:ai-push-hooks" = "0.1.19"

After checking in mise.toml, other contributors can install the pinned tool with mise install.

Python

uv tool install ai-push-hooks
# or
pipx install ai-push-hooks

Node (wrapper around Python package)

npm install --save-dev ai-push-hooks
# or
pnpm add -D ai-push-hooks

Requirements:

  • Python 3.10+ (python3 or python) is required. The npm package is a wrapper around the Python CLI, so Python is still required when installed through npm/pnpm.
  • OpenCode is required for llm and apply steps.
  • GitHub CLI (gh) is required only if you use PR creation via gh_pr_create.

Quick start

  1. Add the pinned Mise tool by following Mise installation above.

  2. Generate a starter config:

    mise exec -- ai-push-hooks init --template minimal-docs
    
  3. Add the single repository-owned runner at scripts/hooks/pre-push-runner.sh:

    #!/usr/bin/env bash
    set -euo pipefail
    
    remote_name="${1:-}"
    remote_url="${2:-}"
    push_stdin="$(mktemp)"
    trap 'rm -f "$push_stdin"' EXIT
    cat >"$push_stdin"
    
    # Run deterministic quality checks first. Replace these with the repository's checks.
    npm run lint
    npm test
    
    # Keep ai-push-hooks as the single final phase and replay Git's pre-push input.
    mise exec -- ai-push-hooks hook "$remote_name" "$remote_url" <"$push_stdin"
    

    Make the runner executable:

    chmod +x scripts/hooks/pre-push-runner.sh
    
  4. Configure Lefthook to invoke only that runner in lefthook.yml:

    pre-push:
      commands:
        repository-pre-push:
          run: bash scripts/hooks/pre-push-runner.sh {1} {2}
          use_stdin: true
    

    use_stdin: true forwards Git's ref-update stream to the runner. The runner captures it before quality checks consume or close standard input, then replays it to ai-push-hooks hook. Lefthook's {1} and {2} are the remote name and remote URL. Keep all repository checks in this runner and keep the one ai-push-hooks hook call last so failures propagate and block the push.

  5. Configure modules and steps in Configuration reference.

  6. Push as usual. The workflow runs automatically before push completes.

Commands

If installed as a local npm/pnpm dependency, run commands with npx or pnpm exec.

Command What it does
ai-push-hooks hook <remote-name> <remote-url> Runs the configured pre-push workflow.
ai-push-hooks init --template minimal-docs Writes ai-push-hooks.toml starter config.
ai-push-hooks init --template minimal-docs --force Overwrites an existing config file.

Configuration overview

  • Config file: ai-push-hooks.toml in repo root (required).
  • Prompt resolution precedence for llm and apply steps:
    1. prompt
    2. prompt_file
    3. fallback_prompt_id

Configuration reference

Top-level keys

Key Type Required Default
general table no see section defaults
llm table no see section defaults
logging table no see section defaults
workflow table yes n/a
modules table yes n/a

[general]

Key Type Default Description
enabled bool true Enables or disables the hook globally.
allow_push_on_error bool false If true, push continues even when workflow fails.
require_clean_worktree bool false If true, aborts when local changes exist.
skip_on_sync_branch bool true If true, skips on sync branch/worktree context.
base_branch string "main" Base branch used for new-branch range fallback and default PR base/context.

[llm]

Key Type Default Description
runner string "opencode" LLM runner label (currently OpenCode flow).
model string "openai/gpt-5.5" Model passed to OpenCode.
variant string "" Optional OpenCode variant.
timeout_seconds int 800 Timeout per LLM invocation and related OpenCode calls.
max_parallel int 2 Max concurrent read-only steps (collect, llm).
json_max_retries int 2 Retry count for invalid JSON responses.
invalid_json_feedback_max_chars int 6000 Max invalid output included in retry feedback.
json_retry_new_session bool true Starts a new OpenCode session on JSON retry.
delete_session_after_run bool true Deletes OpenCode sessions after completion.
max_diff_bytes int 180000 Max bytes of git diff sent into workflow artifacts.
session_title_prefix string "ai-push-hooks" Prefix for OpenCode session titles.

[logging]

Key Type Default Description
level string "status" Console verbosity (status, info, debug).
jsonl bool true Enables JSONL event logging.
dir string ".git/ai-push-hooks/logs" Directory for hook.jsonl.
capture_llm_transcript bool true Exports OpenCode session transcripts.
transcript_dir string ".git/ai-push-hooks/transcripts" Transcript export directory.
summary_dir string ".git/ai-push-hooks/summaries" Per-run summary JSON directory.
print_llm_output bool false Mirrors raw OpenCode JSON stream to stdout.

[workflow]

Key Type Required Description
modules array of strings yes Ordered module IDs to run. Must contain at least one module and each ID must exist under [modules].

[modules.<module_id>]

Key Type Required Description
enabled bool no Enables or disables that module. Default true.
steps array of step tables yes Ordered workflow steps for the module. Must be non-empty.

[[modules.<module_id>.steps]]

Key Type Required Applies to Description
id string yes all step types Unique step identifier inside the module.
type string yes all step types One of: collect, llm, apply, exec, assert.
inputs array of strings no non-collect steps Artifact references from earlier steps.
output string yes llm Output artifact filename (often .json).
schema string no llm Validates parsed model output shape.
prompt string conditional llm, apply Highest-priority prompt source.
prompt_file string conditional llm, apply Repo-relative or absolute prompt file path.
fallback_prompt_id string conditional llm, apply Built-in prompt ID used when no higher source resolves.
collector string yes collect Collector handler ID.
allow_paths array of strings yes apply File glob allowlist for edits.
executor string yes exec Exec handler ID.
assertion string yes assert Assertion handler ID.
when_env string no any step Runs step only when env var parses as true.

llm and apply are promptable step types: at least one of prompt, prompt_file, or fallback_prompt_id must be set.

Artifact references in inputs are module-local. Use <step>/<artifact> to reference an artifact produced by an earlier step in the same module (for example, collect/push.diff or analyze/issues.json). Cross-module references such as docs:collect/push.diff are not currently supported.

Supported handler and schema values

Collectors

Value Purpose
docs_context Collects docs-related context and diff artifacts.
beads_status_context Collects branch/beads alignment context.
pr_context Collects PR composition context.

LLM schemas

Value Expected payload
string_array JSON array of strings.
docs_issue_array JSON array of issue objects with at least file and description.
beads_alignment_result JSON object, optionally with commands string array.
pr_create_payload JSON object for PR creation fields.

Exec handlers

Value Purpose
beads_alignment Runs non-interactive Beads commands and writes action report when needed.
gh_pr_create Creates (or reuses) a GitHub PR via gh.

Assertion handlers

Value Purpose
docs_apply_requires_manual_commit Fails when docs were auto-edited and still need user review/commit.
beads_alignment_clean Fails when Beads alignment reports unresolved work.

Built-in fallback prompt IDs

Value Purpose
docs-query-basic Generate doc search queries from diff.
docs-analysis-basic Identify factual documentation drift.
docs-apply-basic Apply minimal doc fixes within allowlist.
beads-plan-basic Build Beads alignment command/report payload.
pr-compose-basic Draft PR title/body/base/head payload.

Environment variable overrides

Boolean env parsing accepts: 1, true, yes, y, on and 0, false, no, n, off.

Env var Effect
AI_PUSH_HOOKS_SKIP If true, sets general.enabled = false.
AI_PUSH_HOOKS_ALLOW_PUSH_ON_ERROR Overrides general.allow_push_on_error.
AI_PUSH_HOOKS_REQUIRE_CLEAN Overrides general.require_clean_worktree.
AI_PUSH_HOOKS_ALLOW_DIRTY If true, forces general.require_clean_worktree = false.
AI_PUSH_HOOKS_BASE_BRANCH Overrides general.base_branch.
AI_PUSH_HOOKS_LOG_LEVEL Overrides logging.level.
AI_PUSH_HOOKS_PRINT_LLM_OUTPUT Overrides logging.print_llm_output.
AI_PUSH_HOOKS_MODEL Overrides llm.model.
AI_PUSH_HOOKS_VARIANT Overrides llm.variant.
AI_PUSH_HOOKS_TIMEOUT_SECONDS Overrides llm.timeout_seconds (integer).

when_env is step-level and can point to any env var. A common example is AI_PUSH_HOOKS_CREATE_PR to gate PR creation steps.

Example: docs + PR with opt-in creation

[workflow]
modules = ["docs", "pr"]

[modules.docs]
enabled = true

[[modules.docs.steps]]
id = "collect"
type = "collect"
collector = "docs_context"

[[modules.docs.steps]]
id = "query"
type = "llm"
fallback_prompt_id = "docs-query-basic"
inputs = ["collect/push.diff", "collect/changed-files.txt"]
output = "queries.json"
schema = "string_array"

[[modules.docs.steps]]
id = "analyze"
type = "llm"
fallback_prompt_id = "docs-analysis-basic"
inputs = ["collect/push.diff", "collect/docs-context.txt", "query/queries.json", "collect/recent-commits.txt"]
output = "issues.json"
schema = "docs_issue_array"

[[modules.docs.steps]]
id = "apply"
type = "apply"
fallback_prompt_id = "docs-apply-basic"
inputs = ["collect/push.diff", "collect/docs-context.txt", "analyze/issues.json"]
allow_paths = ["README.md", "docs/**/*.md"]

[[modules.docs.steps]]
id = "assert"
type = "assert"
assertion = "docs_apply_requires_manual_commit"
inputs = ["apply/result.json"]

[modules.pr]
enabled = true

[[modules.pr.steps]]
id = "collect"
type = "collect"
collector = "pr_context"

[[modules.pr.steps]]
id = "compose"
type = "llm"
fallback_prompt_id = "pr-compose-basic"
inputs = ["collect/pr-context.txt", "collect/changed-files.txt", "collect/push.diff", "collect/commits.txt"]
output = "pr-draft.json"
schema = "pr_create_payload"

[[modules.pr.steps]]
id = "create"
type = "exec"
executor = "gh_pr_create"
when_env = "AI_PUSH_HOOKS_CREATE_PR"
inputs = ["compose/pr-draft.json"]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ai_push_hooks-0.1.19.tar.gz (35.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ai_push_hooks-0.1.19-py3-none-any.whl (33.2 kB view details)

Uploaded Python 3

File details

Details for the file ai_push_hooks-0.1.19.tar.gz.

File metadata

  • Download URL: ai_push_hooks-0.1.19.tar.gz
  • Upload date:
  • Size: 35.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ai_push_hooks-0.1.19.tar.gz
Algorithm Hash digest
SHA256 a124fbd7c4246b01ff1f5bd461896b8b8bb094252f5249b4b017d1552b34e491
MD5 b38e131b9e966a4344ef019a84a8404e
BLAKE2b-256 cb7129683e0646e05aa9b20b5145543d1cede4539afe1c97c33feafc01045a89

See more details on using hashes here.

Provenance

The following attestation bundles were made for ai_push_hooks-0.1.19.tar.gz:

Publisher: release.yml on shanebishop1/ai-push-hooks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ai_push_hooks-0.1.19-py3-none-any.whl.

File metadata

  • Download URL: ai_push_hooks-0.1.19-py3-none-any.whl
  • Upload date:
  • Size: 33.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ai_push_hooks-0.1.19-py3-none-any.whl
Algorithm Hash digest
SHA256 5c894754dc62adb087bd677e5277ea403f1a38f755ef7ff89c4a21dd103a54ac
MD5 ad0863007166deb5bc9cc433fd31a566
BLAKE2b-256 3781a378f8141514e80c639e361f5aeabac10d3c66972bd1c0bdcfb8a4275163

See more details on using hashes here.

Provenance

The following attestation bundles were made for ai_push_hooks-0.1.19-py3-none-any.whl:

Publisher: release.yml on shanebishop1/ai-push-hooks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page