Skip to content

Repository files navigation

gh-delta

npm version CI Node.js: >=18 License: MIT

gh-delta is a small deterministic GitHub watcher for agent or automation loops. It runs one detection pass, compares current GitHub issue and pull request state with a local snapshot, prints JSON or operator text, and exits with a machine-readable code. Scheduling belongs to cron, an automation system, or the caller.

The tool does not decide what to do. It only detects changes such as new PRs, merged PRs, CI status changes, review decision changes, requested-reviewer and assignee changes, unresolved review threads, new or deleted comments, relabeling, base-branch retargets, draft transitions, mergeability flips, missing objects, and catch-all updates. Your orchestrator, script, or agent owns the action.

gh-delta is not a dashboard, inbox, or PR bot. It is a deterministic GitHub delta detector for schedulers, scripts, and agent loops.

See Alternatives and adjacent tools for how gh-delta compares to related projects.

gh-delta seeding a zero-config baseline, then reporting two GitHub deltas on the next tick

Table of Contents

Requirements

  • Node.js 18 or newer.
  • GitHub CLI (gh) installed and authenticated.
  • Read access to the repository being watched.
  • Any OS: Linux and macOS get the POSIX guarantees (and Linux is what CI exercises); Windows works with documented caveats — see Platform Notes.

To validate gh auth locally:

gh auth status

Quick Start

gh-delta zero-config command-line invocation seeding a baseline

Minimal zero-config invocation:

gh-delta --repo owner/repo

--repo is optional inside a GitHub checkout: gh-delta derives owner/name from the current directory's git remote (origin, then upstream) when the flag is omitted, so gh-delta with no flags works from a clone. See Zero-config repo for details.

Install from npm:

npm install --global gh-delta
gh-delta --help
gh-delta --help-json
gh-delta --version

No install required:

npx gh-delta --repo owner/repo --monitor-id prs --state-dir ./state --entities pr --format text

Or run from a source checkout:

git clone https://github.com/diegomarino/gh-delta.git
cd gh-delta
npm install
npm run check

node ./gh-delta.mjs --repo owner/repo --monitor-id prs --state-dir ./state --entities pr --format json

The first successful run establishes the baseline and exits 0. Later runs compare against that baseline. Use an explicit --state-dir for durable monitors; the zero-config state location is intentionally convenient but can be cleared by tmp cleanup.

For installation modes, repeated runs, snapshots, outposts, and examples, read the Usage Guide. For the exact CLI contract, use gh-delta --help-json or docs/contract.md.

Core Concepts

  • gh-delta runs one detector pass and exits. Cron, CI, systemd, an agent, or an external automation system owns the schedule.
  • The snapshot is local state. A baseline run seeds it; later runs compare GitHub state against it.
  • --monitor-id names a recurring monitor. Reuse it for the same watcher and use different ids for independent watchers.
  • Exit code 10 means deltas were found. Exit code 0 means baseline/no deltas. Error behavior is specified in Exit Codes.
  • --format json is the machine contract. --format text is an operator log format.
  • gh-delta list is a read-only inventory of the monitors that have run on this machine — which repo, monitor id, and entities, when each last ran, and whether its snapshot is healthy. Every successful run leaves a best-effort registry breadcrumb (opt out with --no-registry) so list sees monitors in any state location. It never contacts GitHub and never touches snapshots.

Exact CLI flags, report fields, delta classes, snapshot semantics, and outpost payloads live in docs/contract.md.

Output

Text output is designed for scheduled logs:

gh-delta --format text report for a changed PR and a relabeled issue

JSON output is designed for programs and agents:

gh-delta --format json --detail report for a PR with CI, review, and requested-reviewer changes

Use --summary-line when an agent only needs a display sentence. Use --detail when it also needs structured class-level explanations. The full report shape is specified in Report Shape, and practical output examples are in docs/usage.md.

Semantic summaries (--summaries)

The per-delta from/to fingerprints are opaque digests tuned for change detection — correct for "did CI change?" but useless for "is CI green?". Pass --summaries to add a normalized, typed summary object to every PR delta that has an observed to state. It is derived from the same single observation that produced the fingerprints (no second GitHub fetch), and is a sibling of to, so the content-addressed delta.id and every existing field stay byte-identical whether or not the flag is set. Consumers may treat it as a hint and still re-derive authoritative facts themselves.

"summary": {
  // 'none' means ZERO checks ran — never conflated with 'green'. Fail-closed
  // consumers decide what "no CI" means. Precedence is failed > pending > green.
  "ciRollup": "green" | "failed" | "pending" | "none",
  // 'none' also covers "no review-required rule" and "required but none submitted
  // yet"; GitHub does not distinguish these without a branch-protection fetch.
  "reviewDecision": "approved" | "changes_requested" | "review_required" | "none",
  // 'unknown' = GitHub has not finished recomputing mergeability (kept honest,
  // never collapsed to a boolean).
  "mergeable": "mergeable" | "conflicting" | "unknown",
  // GitHub's mergeStateStatus from the same observation. 'unknown' = not reported;
  // fail-closed, treated like mergeable: unknown. A PR can be mergeable yet 'behind'
  // its base or 'blocked' by a protection rule, so this is NOT folded into mergeable.
  "mergeStateStatus":
    "behind" | "blocked" | "clean" | "dirty" | "draft" | "has_hooks" | "unstable" | "unknown",
  "state": "open" | "closed" | "merged",
  "isDraft": true,                    // boolean
  "unresolvedReviewThreads": 0,       // non-negative integer
  "headSha": "<head commit SHA, or '' if unobserved>"
}

Issue deltas and the missing lifecycle (to is null) carry no summary. The field set and enum domains are also emitted machine-readably under output.deltaSummaryFields / output.deltaSummaryEnums in --help-json, and the authoritative schema lives in Delta Summary schema.

Baseline state emission (--baseline-emit-state)

By default the first run seeds a baseline silently (deltas: []), so a PR that is already stuck — in merge conflict or blocked on CI at seed time — stays invisible until it changes again. Pass --baseline-emit-state to emit one synthetic baseline-state delta per tracked open item on that first run instead; the run then exits 10 with baseline: true and a non-empty deltas array. The delta ids are content-addressed and stable across re-baselining, so idempotent consumers dedupe them for free. Off by default — existing behavior is byte-identical. Do not treat a baseline-state delta as newly created.

Watch Loops and Outposts

See RUNBOOK.md for timer-driven loop patterns. The recommended setup is cron-native: seed the baseline once, then create a recurring scheduler whose prompt runs one detector pass and stops.

See docs/watch-loop-prompt.md for a prompt template for cron-owned watcher ticks.

--outpost-url sends one best-effort HTTP notification per delta. gh-delta does not provide retries, an outbox, acknowledgement, replay, or action routing; the receiving endpoint owns filtering, dedupe, and downstream action. Read the Usage Guide for a worked command and the Outpost Payload contract for the exact envelope.

Worked schedulers and receivers live in examples/ in the source repository. Examples are GitHub documentation and are not shipped in the npm package.

Programmatic Use

gh-delta exposes a small subpath-only ESM surface for embedding in orchestrators:

import { detectDeltas } from 'gh-delta/detect';
import { REPORT_SCHEMA_VERSION } from 'gh-delta/contract';

The package root is intentionally not exported. Use the gh-delta binary for CLI execution, and import explicit subpaths such as gh-delta/detect or gh-delta/outpost for programmatic use. See Programmatic API Surface for the complete import contract and docs/usage.md for practical notes.

Troubleshooting

Common failure symptoms include: monitor re-baselined after a reboot, gh auth failures, page-cap errors, and corrupt snapshots.

See Troubleshooting / FAQ for symptoms, causes, and fixes, including how to locate your snapshot file on Linux and macOS.

Development

npm test
npm run test:coverage
npm run lint
npm run format:check
npm run check
npm run release:check

npm run check is the normal local gate: ESLint, Prettier check, then the Node test suite. npm run release:check adds the coverage report and npm pack --dry-run package-content verification.

npm test imports helper files only; it does not run the live GitHub mutation cycle. npm run e2e:playground is the explicit live acceptance test. It creates and deletes a real private GitHub repository via gh, requires an authenticated gh and network access, and should not run in CI or sandboxes. See test/e2e/README.md.

The project intentionally has no runtime dependencies. Development tooling is limited to ESLint and Prettier.

See docs/release-checklist.md before publishing a new npm release.

Documentation changes should follow behavior changes: if you change CLI flags, delta classes, snapshot behavior, or any outpost path, update:

  • docs/contract.md first — it is the canonical source for all CLI options, delta classes, exit codes, report shape, and outpost schema;
  • docs/usage.md, for user-facing recipes and operating notes;
  • this README, for the top-level route into the project;
  • docs/architecture.md, for internals and rationale only.

Documentation

Doc Read it when
docs/usage.md Installing, running, reading output, and wiring practical integrations
RUNBOOK.md Setting up a scheduled watch loop
docs/contract.md You need the exact classes, exit codes, CLI flags, and payload schema
docs/architecture.md Understanding internals, boundaries, and design rationale
docs/watch-loop-prompt.md You want a cron-tick prompt template
examples/README.md You want source-repo worked integrations — cron/CI/systemd/push/library
docs/troubleshooting.md Something misbehaves
docs/alternatives.md Comparing gh-delta to other tools
docs/entities-research/README.md Researching future watch entities
CONTRIBUTING.md Contributing changes
CHANGELOG.md Checking what changed between versions

License

MIT © diegomarino

About

Deterministic GitHub issue and pull request delta detector for agent watch loops

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages