{"id":48840088,"url":"https://github.com/toshiki670/merge-ready","last_synced_at":"2026-05-31T09:00:51.978Z","repository":{"id":350662284,"uuid":"1207776960","full_name":"toshiki670/merge-ready","owner":"toshiki670","description":"Instantly see whether your PR is ready to merge. A GitHub-aware tool that displays only actionable merge blockers.","archived":false,"fork":false,"pushed_at":"2026-05-30T11:38:35.000Z","size":1132,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T13:13:47.562Z","etag":null,"topics":["pull-request","shell"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toshiki670.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-11T11:42:41.000Z","updated_at":"2026-05-30T11:38:37.000Z","dependencies_parsed_at":"2026-05-23T08:01:44.090Z","dependency_job_id":null,"html_url":"https://github.com/toshiki670/merge-ready","commit_stats":null,"previous_names":["toshiki670/merge-ready"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/toshiki670/merge-ready","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toshiki670%2Fmerge-ready","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toshiki670%2Fmerge-ready/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toshiki670%2Fmerge-ready/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toshiki670%2Fmerge-ready/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toshiki670","download_url":"https://codeload.github.com/toshiki670/merge-ready/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toshiki670%2Fmerge-ready/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33725060,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pull-request","shell"],"created_at":"2026-04-15T01:31:32.663Z","updated_at":"2026-05-31T09:00:51.951Z","avatar_url":"https://github.com/toshiki670.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# merge-ready\n\n[![Crates.io](https://img.shields.io/crates/v/merge-ready)](https://crates.io/crates/merge-ready)\n[![Downloads](https://img.shields.io/crates/d/merge-ready)](https://crates.io/crates/merge-ready)\n[![Docs.rs](https://img.shields.io/docsrs/merge-ready)](https://docs.rs/merge-ready)\n[![CI](https://img.shields.io/github/actions/workflow/status/toshiki670/merge-ready/ci.yml?branch=main\u0026label=ci)](https://github.com/toshiki670/merge-ready/actions/workflows/ci.yml)\n[![License](https://img.shields.io/crates/l/merge-ready)](https://github.com/toshiki670/merge-ready/blob/main/LICENSE)\n\n`merge-ready` is a Rust CLI that reports whether the pull request for your current branch is mergeable. It prints concise status tokens designed for shell prompt integration and automation scripts.\n\n## Install\n\n```bash\ncargo install merge-ready\n```\n\nThis installs two binaries: `merge-ready` (full CLI and daemon) and `merge-ready-prompt` (lightweight prompt binary).\n\nFor development builds:\n\n```bash\ncargo install --path .\n```\n\n## Usage\n\nShow top-level help:\n\n```bash\nmerge-ready --help\n```\n\nShow merge status tokens for prompt integration:\n\n```bash\nmerge-ready-prompt\n```\n\nExample output:\n\n```text\n⚠ Resolve review\n```\n\n`merge-ready-prompt` prints a single status token to stdout and always exits with code `0`. Use the printed token text for conditional logic in shell scripts and prompt hooks.\n\n## Output Tokens\n\n- `✓ Ready for merge` - ready to merge\n- `✎ Ready for review` - pull request is in draft state\n- `+ Create PR` - branch exists but no pull request has been created yet\n- `⚠ Resolve review` - changes were requested in review\n- `@ Assign reviewer` - no reviewer assigned yet\n- `⚠ Run CI action` - CI checks require manual action\n- `⧖ Wait for CI` - CI checks are pending\n- `✗ Fix CI failure` - CI checks failed\n- `✗ Resolve conflict` - merge conflicts exist\n- `✗ Update branch` - branch is behind base branch\n- `? Check branch sync` - branch sync status is unknown\n- `? Check merge blocker` - PR is blocked for an unknown reason\n- `⧖ Wait for status` - GitHub is calculating merge status\n- `? loading` - cache miss; daemon is fetching in the background\n\n### Multiple Pull Requests\n\nWhen a branch has **multiple open pull requests**, statuses are **aggregated by kind**: each distinct status is shown once, followed by the PR numbers that share it (ascending). Status groups appear in the order each kind is first seen when scanning PRs in ascending number order:\n\n```text\n⧖ Wait for status #1734 #2669 #2788 #3275 ✗ Fix CI failure #3512 #3693\n```\n\nIf a PR has more than one status, its number appears in each relevant group.\n\nFor distinct statuses across PRs, each group simply lists its single PR number:\n\n```text\n✓ Ready for merge #200 ✎ Ready for review #201\n```\n\nWhen there is only one open pull request, the `#\u003cnumber\u003e` suffix is omitted:\n\n```text\n✓ Ready for merge\n```\n\n## Background Daemon\n\n`merge-ready` uses a background daemon to cache GitHub API results and serve prompt queries with near-zero latency.\n\nThe daemon starts automatically the first time `merge-ready-prompt` runs. You can also manage it manually:\n\n```bash\nmerge-ready daemon start   # start the background daemon (returns immediately)\nmerge-ready daemon stop    # stop the running daemon\nmerge-ready daemon status  # show pid, cache entries, and uptime\n```\n\nOn the first query the daemon has no cache yet, so `? loading` is printed while it fetches in the background. Subsequent calls return the cached value instantly.\n\nThe daemon exits automatically after 30 minutes of inactivity.\n\n### Rate-Limit-Aware Refresh\n\nThe daemon polls `gh api rate_limit` every 60 seconds and stretches its Hot / Warm / Cold refresh intervals when the bottleneck quota (the smaller of `core` and `graphql` remaining ratios) is depleting. The default refresh intervals are always the floor — scaling only lengthens them.\n\nIf the bottleneck remaining ratio drops to ~5% or hits zero, the daemon pauses all background refreshes globally until the reset timestamp reported by GitHub, then resumes automatically.\n\nSet `MERGE_READY_RATE_LIMIT_AWARE=0` (or `false`) to disable this behaviour entirely; the daemon will then use the fixed Hot / Warm / Cold intervals regardless of quota.\n\n### Watch Mode\n\nTo inspect all cached entries in real time, use the `watch` subcommand:\n\n```bash\nmerge-ready watch   # refresh every second; Ctrl+C to stop\n```\n\nExample output:\n\n```text\nCWD              BRANCH          STATUS             CACHED AT\n/home/user/myapp feat/42-login   ✓ Ready for merge  5s ago\n/home/user/myapp main            ⧖ Wait for CI      23s ago\n```\n\n`watch` polls the daemon once per second and redraws the table in place. If the daemon is not running it prints `daemon is not running` and exits immediately.\n\n## Starship Integration\n\nAdd merge status to your [Starship](https://starship.rs/) prompt by using a custom command module in `~/.config/starship.toml`:\n\n```toml\n[custom.merge_ready]\ncommand = \"merge-ready-prompt\"\nwhen = true\nrequire_repo = true\nshell = [\"/bin/zsh\"]\nformat = \"($output )\"\n```\n\n`require_repo = true` limits the module to git repositories without any shell command overhead. `merge-ready-prompt` itself returns `+ Create PR` when no pull request exists for the branch, so no additional filtering is needed.\n\nIf your environment sets `STARSHIP_SHELL` to a slower shell (for example `fish`), custom modules can be noticeably slower due to shell startup cost. Pinning `shell = [\"/bin/zsh\"]` (or another lightweight shell on your system) keeps prompt latency low.\n\n\u003e **Note:** Do not set `style` in the Starship custom module when using the `[text](style)` syntax in merge-ready's `format` field. Starship's `style` wraps the entire output in its own ANSI codes, and merge-ready's internal reset sequences will break that outer styling, causing subsequent prompt modules (e.g. `cmd_duration`) to lose their color.\n\n## Configuration\n\nThe configuration file is read from `$XDG_CONFIG_HOME/merge-ready.toml` (or `~/.config/merge-ready.toml` if `XDG_CONFIG_HOME` is not set).\n\nOpen the file in your editor:\n\n```bash\nmerge-ready config\n```\n\nAll fields are optional — omitting any field falls back to the default shown below.\n\n```toml\n[merge_ready]\nsymbol = \"✓\"\nlabel = \"Ready for merge\"\nformat = \"[$symbol $label( $pr_ids)](bold green)\"\n\n[no_pull_request]\nsymbol = \"+\"\nlabel = \"Create PR\"\nformat = \"[$symbol $label](cyan)\"\n\n[conflict]\nsymbol = \"✗\"\nlabel = \"Resolve conflict\"\nformat = \"[$symbol $label( $pr_ids)](bold red)\"\n\n[update_branch]\nsymbol = \"✗\"\nlabel = \"Update branch\"\nformat = \"[$symbol $label( $pr_ids)](yellow)\"\n\n# [sync_unknown]\n# symbol = \"?\"\n# label = \"Check branch sync\"\n# format = \"[$symbol $label( $pr_ids)](yellow)\"\n\n[ci_fail]\nsymbol = \"✗\"\nlabel = \"Fix CI failure\"\nformat = \"[$symbol $label( $pr_ids)](bold red)\"\n\n# [ci_action]\n# symbol = \"⚠\"\n# label = \"Run CI action\"\n# format = \"[$symbol $label( $pr_ids)](yellow)\"\n\n[ci_pending]\nsymbol = \"⧖\"\nlabel = \"Wait for CI\"\nformat = \"[$symbol $label( $pr_ids)](cyan)\"\n\n[changes_requested]\nsymbol = \"⚠\"\nlabel = \"Resolve review\"\nformat = \"[$symbol $label( $pr_ids)](yellow)\"\n\n[review_required]\nsymbol = \"@\"\nlabel = \"Assign reviewer\"\nformat = \"[$symbol $label( $pr_ids)](cyan)\"\n\n[draft]\nsymbol = \"✎\"\nlabel = \"Ready for review\"\nformat = \"[$symbol $label( $pr_ids)](dimmed)\"\n\n# [status_calculating]\n# symbol = \"⧖\"\n# label = \"Wait for status\"\n# format = \"[$symbol $label( $pr_ids)](dimmed)\"\n\n# [blocked_unknown]\n# symbol = \"?\"\n# label = \"Check merge blocker\"\n# format = \"[$symbol $label( $pr_ids)](yellow)\"\n\n[error]\nsymbol = \"✗\"\nformat = \"[$symbol $message](bold red)\"\n```\n\nEach token supports three optional fields:\n\n| Field | Description | Default |\n|-------|-------------|---------|\n| `symbol` | Leading symbol | see above |\n| `label` | Status text | see above |\n| `format` | Output template | `\"$symbol $label\"` |\n\nThe `[error]` section uses `$message` instead of `$label`. The message is set automatically from the error that occurred (e.g. `authentication required`, `rate limited`, or the raw API error message).\n\n| Field | Description | Default |\n|-------|-------------|---------|\n| `symbol` | Leading symbol | `\"✗\"` |\n| `format` | Output template | `\"$symbol $message\"` |\n\n### Format Variables\n\nThe following variables are available in `format` templates:\n\n| Variable | Description | Available in |\n|----------|-------------|--------------|\n| `$symbol` | Leading symbol | all tokens |\n| `$label` | Status text | PR state tokens, `no_pull_request` |\n| `$pr_ids` | PR numbers sharing this status, `#`-prefixed and space-separated (e.g. `#1734 #2669`); empty `\"\"` when only one open PR exists on the branch | PR state tokens (12 types) |\n| `$message` | Error message | `[error]` only |\n\n`$pr_ids` is not present in `no_pull_request` or `[error]` tokens. Writing `$pr_ids` in those format strings leaves the literal text `$pr_ids` in the output.\n\n\u003e **Breaking change:** the singular `$pr_id` variable was removed in favour of `$pr_ids`. Configs that still reference `$pr_id` are treated as an unknown variable — a `( #$pr_id)` block becomes always-hidden, so PR numbers simply stop appearing (no crash). Update such configs to `( $pr_ids)`.\n\n### Conditional Format Strings\n\nThe `format` field supports a `(...)` syntax that hides the block when all variables inside are empty. This follows [Starship's conditional format strings](https://starship.rs/config/#conditional-format-strings).\n\n```toml\n# `( $pr_ids)` is shown only when $pr_ids is non-empty (i.e., multiple PRs on the branch)\n[merge_ready]\nformat = \"$symbol $label( $pr_ids)\"\n```\n\n| `$pr_ids` value | Output |\n|-----------------|--------|\n| `\"#200 #201\"` (multiple PRs) | `✓ Ready for merge #200 #201` |\n| `\"\"` (single PR) | `✓ Ready for merge` |\n\nRules:\n\n- All variables in `(...)` are empty → block is hidden\n- At least one variable is non-empty → block is rendered normally\n- No variables in `(...)` → block is always hidden\n\nConditional blocks can also appear inside a `[text](style)` block:\n\n```toml\n[merge_ready]\nformat = \"[$symbol $label( $pr_ids)](bold green)\"\n```\n\nThis applies the style to the whole output while still hiding `( $pr_ids)` when `$pr_ids` is empty.\n\n### Style Strings\n\nThe `format` field supports a [Starship-inspired](https://starship.rs/config/#style-strings) `[text](style)` syntax to apply ANSI colors and attributes:\n\n```toml\n[merge_ready]\nformat = \"[$symbol $label](bold green)\"\n\n[ci_fail]\nformat = \"[$symbol](bold red) $label\"\n\n[changes_requested]\nformat = \"[$symbol](yellow) $label\"\n```\n\nSupported style specifiers:\n\n| Specifier | Examples |\n|-----------|---------|\n| Color names | `red`, `green`, `yellow`, `blue`, `cyan`, `purple`, `white`, `black` |\n| Bright colors | `bright-red`, `bright-green`, … |\n| Attributes | `bold`, `italic`, `underline`, `dimmed`, `inverted`, `blink`, `hidden`, `strikethrough` |\n| 256-color / truecolor | `fg:123`, `bg:255`, `fg:#ff8700` |\n| Disable all styles | `none` |\n\nSpecifiers are case-insensitive and order-independent. Multiple specifiers are separated by spaces (e.g. `bold bright-green`).\n\n## Requirements\n\n- `gh` CLI installed and authenticated\n- Current git branch linked to an existing GitHub pull request\n\n## Features\n\n- Minimal output focused on actionable blockers\n- Prompt-friendly status token output\n- Background daemon caches GitHub API results, eliminating per-prompt API calls\n- Daemon auto-starts on first use; no manual setup required\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoshiki670%2Fmerge-ready","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoshiki670%2Fmerge-ready","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoshiki670%2Fmerge-ready/lists"}