releasy <command> --help is authoritative. This page is the quick map.
For the model behind these commands, see concepts.md. For config they read, see configuration.md.
- Global options
- At a glance: which command does what
- Pipeline:
run·refresh·graph discover·graph update·analyze-fails·continue· Sequential mode ·skip·abort·clear - One-off porting:
cherry-pick·project-backport·rebase - Inspection:
status - Multi-project:
new·list·where·adopt - Project board:
setup-project·project push·project pull - Release:
release·draft-release - Features:
feature * - PR membership:
pr *
| Option | Description | Default |
|---|---|---|
--config <path> |
Path to config.yaml |
./config.yaml |
--session-file <path> |
Path to session file. Overrides session_file: in config. |
<config-dir>/<target_branch>.session.yaml (or <name> when unset) |
--version |
Print version and exit | — |
run |
continue |
refresh |
refresh --merge-target |
refresh --analyze-fails |
refresh --address-review |
analyze-fails |
|
|---|---|---|---|---|---|---|---|
| Discovers new PRs | ✅ | — | — | — | — | — | — |
| Creates new port branches | ✅ | — | — | — | — | — | — |
| Opens new rebase PRs | ✅ new | ✅ missed | — | — | — | — | — |
| AI-resolves cherry-pick conflicts | ✅ | — | — | — | — | — | — |
| AI-resolves merge conflicts (target moved on) | — | — | — | ✅ | — | — | — |
| AI-investigates failing CI | — | — | — | — | ✅ | — | ✅ |
| AI-addresses reviewer comments | — | — | — | — | — | ✅ | — |
| Refreshes merged/superseded state | — | — | ✅ | ✅ | ✅ | ✅ | — |
| Iterates state entries | only skip / ensure-PR | ✅ all | ✅ all tracked | ✅ all tracked | ✅ all tracked | ✅ all tracked | ✅ all tracked |
| Mutates work-dir | ✅ cherry-picks | ✅ push only | — | ✅ merges | ✅ commits | ✅ commits | ✅ commits |
| Pushes to origin | ✅ | ✅ | — | ✅ (merges only) | ✅ (plain) | ✅ (plain) | ✅ (plain) |
One-liners:
run— do new work. Discover, cherry-pick, push, open PRs.continue— I fixed something by hand; reconcile state. Push/open what's pending. No git ops beyond push + status checks.refresh— re-sync status across tracked PRs (merged-from-upstream sweep, supersede detection, label reconciliation). With--merge-targetit also merges target in and AI-resolves conflicts; with--analyze-failsit triages failing CI; with--address-reviewit lets the AI act on reviewer feedback. The three flags compose; inside one invocation they run in the fixed order merge-target → analyze-fails → address-review.analyze-fails— CI is red; let AI triage. Iterative per-shard fix loop. Also available asrefresh --analyze-failswhen you want to bundle it with the other refresh passes under one lock and one status-sync.
Why both
runandcontinue?runonly acts on PRs it's cherry-picking right now. If you fix a conflict by hand on a branch with no rebase PR yet,runeither skips it (if_exists: skip) or rebuilds from base (recreate).continuepreserves your manual fix and just pushes + opens the PR.
graph discover is a read-only diagnostic
sibling of run — see its section. graph update
refines that graph from issue comments (no git).
The rest (skip, abort,
status, board-sync, release, feature) never touch git
history.
Port PRs onto the base branch.
Discovers PRs from pr_sources, creates port branches from
origin/<base>, cherry-picks, opens PRs. AI-resolves cherry-pick
conflicts when ai_resolve.enabled is on. Unresolved → singleton dropped
or partial-group draft PR with ai-needs-attention. See
Conflict resolution.
A partial-group draft PR is auto-resumed on the next run (with
retry_failed on): the not-yet-applied PRs are appended and re-resolved,
up to pr_policy.max_partial_continue_attempts (default 2) before it's
left for manual help. No need to set if_exists: append by hand.
For PRs with an existing rebase PR, run doesn't rebuild — it routes
through the same merge-target flow refresh uses:
clean merge → leave alone; conflict → AI-resolve and plain push (never
force). if_exists: append is the only setting that cherry-picks new
commits on top of an existing PR.
releasy run [--onto <ver>] [--work-dir <path>]
[--resolve-conflicts | --no-resolve-conflicts]
[--retry-failed | --no-retry-failed]
[--merge-target | --no-merge-target]
[--only <url-or-id> | --pr <URL>]
[--dry-run]| Option | Description | Default |
|---|---|---|
--onto <ver> |
Version label for derived base name. Naming-only — never resolved as a git ref. | from target_branch |
--work-dir <path> |
Working dir for git ops. | config / cwd |
--resolve-conflicts / --no-resolve-conflicts |
Kill-switch for AI resolver. AI runs only if both this and ai_resolve.enabled are true. |
on |
--retry-failed / --no-retry-failed |
Re-attempt entries in conflict status. No-PR-yet: rebuild from base (only when if_exists: recreate). PR open: merge-target flow (PR always preserved). |
pr_policy.retry_failed |
--merge-target / --no-merge-target |
Push a merge commit on PRs even without conflicts. Never force-pushes. | off |
--only <url-or-id> |
Single PR URL or group/singleton id. Drops everything else. Non-zero if nothing matches. Mutex with --pr. |
— |
--pr <URL> |
Single PR by URL. Exits cleanly (0) when the PR isn't in session scope. Use from webhook/cron callers. Mutex with --only. |
— |
--dry-run |
No writes anywhere (state / git / GitHub). Read-only fetches still happen; cannot predict cherry-pick conflicts. | off |
Exit: 1 on any conflict (in scope), else 0.
Maintenance pass over tracked PRs.
Never opens PRs, never discovers, never cherry-picks. Status sync
always runs (catch merges/closes upstream, supersede sweep,
merged-label apply, session-label reconcile). The three branch-mutating
passes are opt-in via flags — bare refresh only re-syncs state.
--merge-target — for each tracked PR, merge origin/<base>
into the PR branch via git merge --no-ff:
- clean → push the merge commit (you opted in)
- conflict + AI resolves → push, restore status, set
ai_resolved - conflict + AI gives up → reset local, mark
conflict
--analyze-fails — for each tracked PR, walk failed praktika
status entries on the PR's head SHA, bundle the failing tests per
shard, and let Claude run the iterative fix-build-rerun loop. Same
machinery as the standalone analyze-fails
command — see that section for outcome classifications, flaky-elsewhere
heuristic, and config. Per-PR sub-flags: --no-flaky-check,
--post-comment / --no-post-comment.
--address-review — for each tracked PR, fetch comments and let
the AI append fix commits. Filters compose: trust gate
(trusted_associations by default, plus any trusted_reviewers)
--since+ dropped if hidden (minimized/outdated) + kept only when the inline thread is unresolved or the top-level comment has no later reply by the PR author. Linear history only — append commits, never amend/rebase/force-push. Statefullast_review_addressed_atstamp drives implicit re-run--sinceon tracked PRs.
All three flags compose. Inside one invocation the phase order is fixed:
status sync → merge-target → analyze-fails → address-review
PRs left in conflict by the merge phase skip both subsequent passes.
The ordering exists because analyze-fails reads commit statuses
tied to the current head SHA — any push that lands first
(merge-target, address-review) would invalidate the CI report it
consumes.
Uses ai_resolve.merge_prompt_file for conflicts,
analyze_fails.prompt_file for CI triage, and
review_response.prompt_file for review feedback. Suitable for cron.
Note that run also applies the merge flow to PRs
with its own --merge-target — explicit refresh is mainly for
cron cadence, CI triage, and the review pass.
releasy refresh [--pr <URL>]
[--work-dir <path>]
[--resolve-conflicts | --no-resolve-conflicts]
[--merge-target | --no-merge-target]
[--analyze-fails | --no-analyze-fails]
[--no-flaky-check]
[--post-comment | --no-post-comment]
[--address-review | --no-address-review]
[--only <url-or-id>]
[--dry-run]
[--stateless ...]| Option | Description | Default |
|---|---|---|
--pr <URL> |
Operate on one PR by URL. Stateful mode silently exits (0) when the URL isn't tracked in this session. Stateless mode (--stateless) acts on any PR. Required with --stateless. |
walk every tracked PR |
--work-dir <path> |
Working dir. | config / cwd |
--resolve-conflicts / --no-resolve-conflicts |
Toggle AI resolver (only meaningful with --merge-target). |
on |
--merge-target / --no-merge-target |
Merge origin/<base> into PR branches + push. |
off |
--analyze-fails / --no-analyze-fails |
Run the AI CI-triage pass on each in-scope PR. | off |
--no-flaky-check |
(with --analyze-fails) skip flaky-elsewhere cross-check. |
off |
--post-comment / --no-post-comment |
(with --analyze-fails) post per-PR summary comment. |
analyze_fails.post_comment_to_pr |
--address-review / --no-address-review |
Run the AI review-feedback pass. Needs at least one trust source — review_response.trusted_associations (defaults to OWNER/MEMBER/COLLABORATOR/CONTRIBUTOR) or review_response.trusted_reviewers. Refuses only if both are empty. |
off |
--only <url-or-id> |
Single tracked PR (URL — source or rebase) or feature/group id. | — |
--dry-run |
No writes anywhere; print intended actions. | off |
--stateless |
Skip session/state. Requires --pr. |
off |
Stateless-only overrides: --origin, --build-command,
--claude-command, --prompt-file, --timeout, --max-iterations.
Rejected without --stateless.
Exit: 1 if any PR ended up in conflict, any address-review run
failed, or any analyze-fails per-PR run errored — else 0.
Auto-discover PR groups (and optionally post them as an issue).
Walks candidates oldest-merged first, trial-cherry-picking each onto the
target in a scratch worktree. A real (non-cosmetic) conflict means the PR
continues an earlier one, so the two are grouped: every connected set of
such PRs collapses into one combined unit, cherry-picked together in
apply order (prerequisite first). Cosmetic conflicts (whitespace, comment
drift, independent regions) are not grouped — they port standalone and are
resolved trivially at run time. Writes a deps overlay at
<session-stem>.deps.yaml (multi-PR auto_discovered groups, sort: listed)
that run picks up. Main session is never modified.
With --open-issue it posts the result as a GitHub issue on origin — each
group shown as a numbered apply sequence — so the team can review and steer it
via graph update. Re-running --open-issue updates
the same issue, not a duplicate.
Declared pr_sources.groups[] are treated as single super-nodes —
discovery never subdivides or auto-merges them (it only warns if one shares a
dependency with other PRs).
releasy graph discover [--onto <ver>] [--work-dir <path>]
[-o <path>] [--deps-file <path> | --no-write]
[--no-ai] [--max-depth <N>] [--limit <N>]
[--include-already-merged] [--redo]
[--open-issue] [--issue-title <text>]| Option | Description | Default |
|---|---|---|
--redo |
Re-discover from scratch, ignoring the prior graph. By default a re-run reuses every already-discovered unit and only trial-picks PRs new since the last run (see Incremental re-runs). | off |
| Output | Where | Override |
|---|---|---|
| Diagnostic report (always written) | <config-dir>/graph.<base>.yaml |
-o <path> |
Deps overlay (consumed by run) |
<session-stem>.deps.yaml |
pr_sources.deps_file: in session, or --deps-file <path>, or --no-write to skip |
Graph issue (with --open-issue) |
a new/updated issue on origin | --issue-title <text>; labels = graph.issue_labels + target-branch name |
--no-write and --deps-file are mutually exclusive. So are -o and
--open-issue — the tracked report must stay at the default path so
graph update can find it.
Hybrid AI flow per conflict:
- Deterministic —
git log target..source -- <file>→Source-PR:trailers + merge-containment → candidate unit IDs. - Candidates found → ask Claude (text-only, no tools) to
confirm/refine.
discovery_method: git-graph+claude. - No candidates → invoke full AI resolver (tools, builds). Outcomes:
MISSING_PREREQS:→ those become deps (ai-resolve); resolver succeeds → no deps needed (ai-resolve-clean); resolver fails → empty deps + warning (git-graph). - Always reset the scratch worktree.
--no-ai skips both AI steps. Trade-off: fast/free but the deterministic
mapping misses semantic dependencies.
Port-branch caching: trial-pick results are preserved as
feature/<base>/<unit_id> and reused by run via
if_exists: skip — no re-cherry-pick. This covers standalone PRs and
auto-discovered groups: a group's combined branch feature/<base>/<group-id>
is built (members cherry-picked in apply order, cross-repo commits fetched as
needed) and cached when it applies cleanly. A group that conflicts combined — or
whose commits can't be fetched — isn't cached (run rebuilds and resolves it).
User-declared pr_sources.groups[] are not combined-cached. --no-write
disables caching (true dry-run).
Upstream-backport recursion (opt-in): when a cross-repo PR
(repo_slug ≠ origin) conflicts on a prerequisite that isn't among the
candidates, and ai_resolve.auto_add_prerequisite_prs.enabled is set with an
upstream remote configured, the prerequisite PR is fetched from upstream,
added to the candidate set, trial-picked, and folded into the same group
(prerequisite first). Recurses on the prereq's own prereqs, bounded by
--max-depth (default max_prereq_depth). Without those settings — or if the
upstream commit can't be fetched — it's flagged missing-prerequisites.
Round-trip notes:
- Groups are emitted as multi-PR
auto_discoveredentries (sort: listed, prerequisite first). Move an entry into the main session (dropauto_discovered:) to make it permanent. - Re-running rewrites the deps file from scratch — hand-edits are lost; use
--no-write/--deps-file <path>to redirect.
Re-scanning for new PRs: just re-run graph discover — there is no
graph refresh. It re-queries pr_sources (labels etc.) fresh each run, so
PRs that newly match are picked up and PRs that landed in target drop out; the
summary prints a refresh-diff (refresh: N removed [...] · M added [...]).
Incremental re-runs (default): a re-run reuses every unit already in the
prior graph.<base>.yaml — standalone PRs and groups — and only trial-picks
PRs that are new in pr_sources since the last run (skipping their re-pick,
prereq-tracing, and AI). A reused group is rebuilt from its recorded members; a
brand-new PR that conflicts into a member attaches to that group. A PR that
was re-merged (same URL, new SHA) or that dropped out of the candidate set is
re-discovered, not reused.
Pass --redo to ignore the prior graph and trial-pick everything from
scratch.
When origin/<base> has moved since the last run, discovery still reuses
the prior groupings but treats cached branches as stale: reused units are
emitted cached: false (so run re-picks them onto the new tip) and a warning
nudges you toward --redo. The refresh-diff (refresh: N removed · M added)
still reports what changed either way.
Exit: 0 regardless of conflicts found — read-only diagnostic.
Refine the discovered graph from trusted member comments on its issue.
Feeds new trusted comments on the graph issue (from
graph discover --open-issue) to Claude with the
prior graph and rebuilds the graph from the reply. No git, no trial-picks.
A member can ask for any change in prose; Claude decides:
- add a PR ("also port #2000");
- veto a PR ("don't port #1010") → recorded, and (unless
graph.apply_exclusions: false) added toexclude_prssorunskips it; - regroup into an atomic unit, or reorder via
depends_on.
releasy graph update [--onto <ver>] [--since <iso>] [--work-dir <path>]
[--post-comment | --no-post-comment] [--dry-run]| Option | Description | Default |
|---|---|---|
--onto <ver> |
Base branch; must match the graph discover value. |
target_branch |
--since <iso> |
Only ingest comments after this ISO-8601 timestamp. | stored last-ingest watermark |
--work-dir <path> |
Locate config / report; no git ops run. | config / cwd |
--post-comment / --no-post-comment |
Summary comment on the issue after applying. | graph.post_comment |
--dry-run |
Show the rebuilt graph + intended session edits; write nothing. | off |
Trust gate. Only comments whose author_association is in
graph.trusted_associations (default OWNER, MEMBER, COLLABORATOR) or whose
login is in graph.trusted_reviewers reach Claude. RelEasy's own comments
are skipped.
Addressed comments are collapsed as Outdated (unless
graph.minimize_addressed_comments: false); comments Claude ignored or
couldn't action (questions, 👍) stay visible so a human sees what's pending.
Rewrites the report, deps overlay, and session (include_prs /
exclude_prs), and refreshes the issue. Deps here are AI/human-asserted, not
trial-pick-verified — re-run graph discover for verified deps.
Exit: 0 on success or clean no-op; 1 on fetch error, malformed reply,
dependency cycle, or a session edit that couldn't be applied.
Investigate red CI on a PR (or every tracked PR).
Also available as
refresh --analyze-fails— same per-shard fix loop, runs alongside--merge-target/--address-reviewunder a single project lock. Prefer the refresh form when you want a bundled cron pass; reach for standaloneanalyze-failswhen CI triage is the only thing you're doing.
Walks failed commit-status entries on the PR's head SHA whose target_url
points at the praktika JSON viewer (GitHub-Actions job logs are
deliberately ignored). Per failed shard, bundles all failures into a
single Claude invocation that runs iteratively: triage → pick highest-
leverage root cause → fix → build → re-run still-failing tests in one
batch → repeat (up to max_iterations).
A flaky-elsewhere map cross-references failures across other tracked
PRs (flaky_elsewhere_threshold default 2) so master-side flakes get
classified UNRELATED instead of fix attempts.
Per-shard outcomes:
| Outcome | Meaning |
|---|---|
DONE |
Every test now passes (or confirmed flake). |
PARTIAL |
Some fixed; some still failing or unexplored. Common. |
UNRELATED |
Whole shard is master-side flake. No code changes. |
UNRESOLVED |
Couldn't make progress. |
The failed-test list lands at .releasy/failed-tests.txt for the AI to
read. Anthropic spend rolls into ai_cost_usd (same field as run /
refresh) and surfaces on the board's
AI Cost column.
releasy analyze-fails [--pr <URL>] [--work-dir <path>]
[--dry-run]
[--push | --no-push]
[--no-flaky-check]
[--post-comment | --no-post-comment]
[--only <url-or-id>]
[--stateless ...]| Option | Description | Default |
|---|---|---|
--pr <URL> |
PR on origin. Omit to iterate every tracked PR with a rebase_pr_url. |
— |
--work-dir <path> |
Working dir. | config / cwd |
--dry-run |
List failed tests + flake counts, exit. No Claude, no push. | off |
--push / --no-push |
Push AI commits. Plain push, no force; race aborts. | on |
--no-flaky-check |
Skip flaky-elsewhere assessment. | off |
--post-comment / --no-post-comment |
Per-PR summary comment with outcomes + commit list. | analyze_fails.post_comment_to_pr |
--only <url-or-id> |
Single tracked PR / feature / group. Mutex with --pr and --stateless. |
— |
--stateless |
Skip session/state/lock; act on --pr alone. config.yaml still loaded if present. |
off |
Stateless-only overrides: --origin, --build-command, --claude-command,
--prompt-file, --timeout, --max-iterations, --max-prs.
Custom Claude allowlists for test runners go in config.yaml. Use
{work_dir} (alias {repo_dir}, {cwd}) so paths aren't hard-coded:
analyze_fails:
allowed_tools:
- Read
- Bash(git:*)
- Bash(tests/clickhouse-test:*)
- Bash({work_dir}/build/programs/clickhouse:*)Exit: 1 on any per-PR failure (fetch / push race / non-linear history);
0 otherwise even if everything is UNRELATED.
Linear history only — same as
refresh --address-review. Append commits only. To retract:git revert <sha>.
Reconcile state after a manual fix.
Walks every port in state. Doesn't discover, doesn't cherry-pick, doesn't merge. Per entry:
| State | Action |
|---|---|
skipped |
leave |
conflict, AI gave up (failed_step_index set) |
highlight; user must act |
conflict, branch clean (manually resolved) |
push, open PR (if auto_pr), flip to needs_review |
conflict, still unresolved |
highlight with conflict files + git status hint |
branch_created (branch on origin, no PR) |
push (if needed) + open PR |
needs_review |
leave |
Always finishes with a project-board reconcile.
releasy continue [--branch <branch-or-feature-id>] [--work-dir <path>]
[--dry-run]| Option | Description | Default |
|---|---|---|
--branch <name> |
Operate on one entry — flip from conflict to needs_review. |
full pass |
--work-dir <path> |
Working dir. | config / cwd |
--dry-run |
Show what would happen — no state writes, pushes, PR opens, or project sync. Read-only GitHub fetches still happen. | off |
Exit: 1 if any conflict remains (full pass) or the branch couldn't be
marked resolved.
When sequential: true is in config.yaml, both run
and continue (without --branch) process one
PR per invocation. Queue is sorted by merged_at.
- First invocation → port earliest PR, push, open rebase PR, exit.
- You review, approve, merge that PR on GitHub.
- Next invocation → checks GitHub:
- Previous PR merged → mark
merged, re-fetch, port the next. - Previous PR not merged → exit
1, change nothing.
- Previous PR merged → mark
- Repeat. AI-unresolvable conflict → stops; resolve manually + run
releasy continue --branch <id>.
Constraints:
- Incompatible with
pr_sources.groups(session load fails). - Requires
target_branch:in config. - Re-run
setup-projectonce to provision the newmergedStatus option.
releasy run
# (review, approve, merge on GitHub, then:)
releasy continueDrop a conflicted port from this run.
Marks skipped so subsequent passes ignore it. Doesn't touch git.
releasy skip --branch <branch-or-feature-id>Stop tracking this run as in-progress.
Persists state. No undo for ports already pushed — branches and PRs stay exactly as they are.
releasy abortWipe local-only port artifacts that never reached a PR.
With an identifier, clears that one feature. Without it, scans state for
every feature stuck in a damaged local-only state (conflict or
branch_created with no rebase PR), lists them, and clears after a
confirmation prompt. For each: aborts any in-progress
cherry-pick / merge / rebase, force-deletes the local port branch, and
drops the state entry so the next run starts fresh. Refuses any
feature whose rebase PR is already open — those live on GitHub and are
out of scope.
releasy clear [<identifier>] [--work-dir <path>] [--dry-run] [--yes]| Option | Description | Default |
|---|---|---|
<identifier> |
Feature ID / branch / source-PR number / source-PR URL. Omit to sweep all damaged local-only entries. | sweep all |
--work-dir <path> |
Working dir for git ops. | config / cwd |
--dry-run |
Show what would be cleaned; change nothing. | off |
--yes / -y |
Skip the confirmation prompt in sweep mode. | off |
One-off cross-repo cherry-pick — no config, no state.
Cherry-picks a PR (.../pull/N, merge commit with -m 1), commit
(.../commit/<sha>), or tag from any public GitHub repo onto a fresh
branch off --target in --origin; optionally AI-resolves conflicts,
pushes, and opens a PR back to --target. Persists nothing — no
config / state / lock / board. Re-running makes a brand-new branch each
time (pin it with --branch-name).
releasy cherry-pick --origin <url> --target <branch> --commit <github-url>
[--branch-name <name>] [--push | --no-push] [--with-pr]
[--resolve-conflicts --build-command <cmd>]
[--mode backport|forward_port]
[--claude-command <exe>] [--prompt-file <path>]
[--timeout <s>] [--max-iterations <n>]
[--formatting-example <pr-url>] [--work-dir <path>]| Option | Description | Default |
|---|---|---|
--origin <url> (required) |
Origin remote (ssh/https) to clone, push to, and open the PR against. | — |
--target <branch> (required) |
Existing origin branch to base the port on / open the PR against. | — |
--commit <url> (required) |
GitHub URL to cherry-pick: PR, commit, or tag; any public repo (incl. forks). | — |
--branch-name <name> |
Port branch name. | releasy/port/<id>-<6hex> |
--push / --no-push |
Push the branch to origin. | on |
--with-pr |
Open a PR from the branch back to --target (implies --push; needs RELEASY_GITHUB_TOKEN). |
off |
--resolve-conflicts |
On conflict, invoke Claude. Requires --build-command. |
off |
--mode backport|forward_port |
Port direction for the resolver. backport adapts code (adjust signatures, drop non-crucial upstream functionality) and declares a prerequisite only when the PR truly can't stand without it; forward_port is strict (reports MISSING_PREREQS). |
backport |
--build-command <cmd> |
Shell command Claude runs to verify the resolution compiles. Required with --resolve-conflicts. |
— |
--claude-command <exe> |
Claude executable. | claude |
--prompt-file <path> |
AI-resolve prompt template. | bundled |
--timeout <s> |
Per-attempt Claude timeout (seconds). | 7200 |
--max-iterations <n> |
Max build attempts per resolve. | 5 |
--formatting-example <pr-url> |
Append that PR's "CI/CD Options" section to the new PR body (needs --with-pr). |
— |
--work-dir <path> |
Working dir for git ops. | cwd |
Batch backport upstream PRs queued in a GitHub Project — no config, no state.
For "Stable" releases. Walks a GitHub Project (one view per version), and
for every item whose content is an upstream ClickHouse/ClickHouse PR
whose Port Versions field includes --version, opens a Backport PR
into --target on origin (Altinity/ClickHouse): cherry-picks the upstream
merge commit (-m 1), optionally AI-resolves conflicts in backport mode,
pushes, and opens the PR. The PR title is <version> Backport of #<n> - <upstream title>; the body carries the upstream PR's changelog
category + entry (entry text with (<upstream url> by @<author>)
appended) followed by the CI/CD Options section taken verbatim from the
target branch's PULL_REQUEST_TEMPLATE.md; a <version> label is added.
After creating the PR it is added back to the same project with its
Port Versions set to <version> (nothing is ever deleted).
Persists nothing — the GitHub Project + open origin PRs are the only
source of truth. Re-running is idempotent: any item that already has a
backport PR into --target (matched by branch name, then by an open PR
titled Backport of #<n> / referencing the upstream URL) is skipped. Only
ever opens PRs into origin — never upstream. Needs RELEASY_GITHUB_TOKEN
with the project scope.
releasy project-backport --project <project-url> --version <ver> --target <branch>
[--origin <url>] [--work-dir <path>]
[--resolve-conflicts --build-command <cmd>]
[--claude-command <exe>] [--prompt-file <path>]
[--timeout <s>] [--max-iterations <n>]
[--limit <n>] [--dry-run]| Option | Description | Default |
|---|---|---|
--project <url> (required) |
GitHub ProjectV2 URL, e.g. https://github.com/orgs/Altinity/projects/26. |
— |
--version <ver> (required) |
Target version (e.g. 24.8). Filters items by Port Versions; also the PR label, title prefix, and Port Versions value set on the new card. |
— |
--target <branch> (required) |
Existing origin branch to cherry-pick onto and open PRs against (e.g. customizations/24.8.14). |
— |
--origin <url> |
Origin remote to clone / push / open PRs against. | git@github.com:Altinity/ClickHouse.git |
--work-dir <path> |
Working dir for git ops. If omitted, a stable cache clone is created/reused. | $XDG_CACHE_HOME/releasy/Altinity-ClickHouse |
--resolve-conflicts |
On conflict, invoke the AI resolver (backport mode). Requires --build-command. |
off |
--build-command <cmd> |
Shell command Claude runs to verify the resolution compiles. Required with --resolve-conflicts. |
— |
--claude-command <exe> |
Claude executable. | claude |
--prompt-file <path> |
AI-resolve prompt template. | bundled |
--timeout <s> |
Per-attempt Claude timeout (seconds). | 7200 |
--max-iterations <n> |
Max build attempts per resolve. | 5 |
--limit <n> |
Process at most n items (newest upstream PR first). |
all |
--dry-run |
Plan only: list qualifying items and what would be created / skipped. No clone, cherry-pick, push, or GitHub writes. | off |
Re-port an existing rebase PR onto a different target branch.
For each PR in scope: skips if it already targets --target; otherwise
branches off origin/<target>, cherry-picks its commits one at a time
(AI-resolving conflicts; falls back to a single squashed
git merge --squash if the cherry-pick path won't apply), pushes a fresh
branch, opens a new PR (referencing Port of <old PR> onto <target>),
and closes the original with a superseded by <new PR> comment. With
--pr only that PR; without it, every tracked rebase PR in state.
Never mutates the state file — it's a one-way porter, not a migration.
releasy rebase --target <branch> [--pr <url>] [--only <url-or-id>]
[--resolve-conflicts | --no-resolve-conflicts]
[--work-dir <path>] [--dry-run]| Option | Description | Default |
|---|---|---|
--target <branch> (required) |
Existing origin branch to rebase onto. | — |
--pr <url> |
The rebase PR to port. Omit to walk every tracked rebase PR. | all tracked |
--only <url-or-id> |
Restrict the walk to one tracked PR (source or rebase URL) or feature / group ID. Mutex with --pr; non-zero if no match. |
— |
--resolve-conflicts / --no-resolve-conflicts |
AI resolver on conflicts (needs ai_resolve.enabled). |
on |
--work-dir <path> |
Working dir for git ops. | config / cwd |
--dry-run |
No branches / cherry-picks / pushes / PR changes. Read-only fetches still happen. | off |
Print current pipeline state.
Rich-text per-status sub-tables, ordered with conflicts first (see
STATUS_DISPLAY_ORDER in src/releasy/state.py).
Reads state only — no git, no network.
releasy statusSee concepts.md → Multiple projects.
Scaffold a fresh project.
Writes config.yaml (at --out) + sibling <target_branch>.session.yaml
(falls back to <name> when --target-branch is omitted). Refuses
to overwrite. Prints config's absolute path on stdout (everything else on
stderr) so it composes:
cd $(dirname "$(releasy new --target-branch antalya-25.8 --project antalya)")releasy new [--name <slug>] [--target-branch <branch>] [--project <id>] [--out <path>]| Option | Description | Default |
|---|---|---|
--name <slug> |
[A-Za-z0-9._-]{1,64}. |
auto: <target-branch>-<6hex> |
--target-branch <branch> |
Seeds target_branch: + auto-name. |
empty |
--project <id> |
Seeds project:. |
empty |
--out <path> |
Config path. Refuses to overwrite. | ./config.yaml |
Auto-generated names get a 6-hex CSPRNG suffix so back-to-back calls don't collide.
Every project on this machine. Alias: releasy ls.
One row per project: name, phase, feature counts, last-run timestamp, owning config path.
releasy listPrint the state-file path for the current config.
releasy where
# /home/<you>/.local/state/releasy/antalya-26.3.state.yamlRebind state to the current config.
After moving/renaming a config.yaml, the next mutating command trips an
ownership-collision check. Run adopt from the new location to rebind;
the old path is appended to a history list for audit.
If no state exists yet, creates an empty one — doubles as "register this config without doing anything else".
releasy adoptNo-ops unless notifications.github_project is set and
RELEASY_GITHUB_TOKEN has project scope. UI setup:
configuration.md → GitHub Project board.
Create / verify the GitHub Project.
If configured: verifies project, reconciles Status options to the
canonical set, provisions AI Cost. If unset: creates a new project,
prints the URL, runs an initial sync.
releasy setup-projectDestructive: drops non-canonical Status options. Cards on dropped options are re-synced based on local state immediately after.
Push local state to the project board.
Reconciles every known feature: attaches missing PR cards, refreshes existing, updates Status, and deletes cards no longer backed by local state. No git, no PRs — only the board. Use after hand-editing state, rotating tokens, or wiring up a new project URL.
releasy project pushExit: 1 if sync was skipped (no project / no token / bad URL) or any
item failed, 0 otherwise.
Rebuild local state from GitHub + the project board.
Use when local state is missing or stale (fresh machine, teammate
takeover, throwaway CI runner) but the world outside is intact. Read-only
on git — only the GitHub APIs are hit. Merges into any existing state
file; the board wins for Skipped and AI Cost, GitHub wins for PR
status, local-only fields (ai_iterations, failed_step_index,
partial_pr_count) are preserved.
releasy project pullRequires notifications.github_project in config and
RELEASY_GITHUB_TOKEN with project scope.
Build a release branch from a tag.
Creates a release base branch from --base-tag and merges every finished
port (needs_review, optionally skipped) onto it.
releasy release --base-tag <tag> --name <branch> [--strict] [--include-skipped] [--work-dir <path>]| Option | Description | Default |
|---|---|---|
--base-tag <tag> (required) |
Tag/ref to base on. Must be local or fetchable from origin. | — |
--name <branch> (required) |
Release branch name. | — |
--strict |
Abort if any enabled feature isn't needs_review. |
off |
--include-skipped |
Include skipped features. |
off |
--work-dir <path> |
Working dir. | config / cwd |
Generate a release changelog from the PRs merged into the target branch.
Queries origin in one Search call for PRs whose base is --base (the
target branch) and that merged in the --from..--to window — no
per-PR fetch, no merge-commit walk. Forward-ports (forwardport /
forward-port label or title) are dropped, each PR is classified by its
Changelog category, and the result is rendered as Altinity's release-notes
markdown. --prs / --prs-file supply an explicit PR set instead,
bypassing discovery. With -o writes to disk and publishes nothing;
without it, creates a draft GitHub release on origin (tag = --name,
commitish = --to) and prints its URL.
--from / --to are resolved against a local clone only to derive the
window dates and the comparison SHAs.
releasy draft-release --from <ref> --to <ref> [--base <branch>]
[--prs <url> ...] [--prs-file <path>]
[--name <tag>] [--title <text>] [-o <file>]
[--compared-to-url <url>] [--docker-image-url <url>]
[--work-dir <path>]| Option | Description | Default |
|---|---|---|
--from <ref> (required) |
Window lower bound, non-inclusive by date (usually the previous release / upstream fork tag). | — |
--to <ref> (required) |
Window upper bound (inclusive) and draft commitish (release-branch tip or the tag being cut). | — |
--base <branch> |
Branch whose merged PRs are collected. | target branch → --to |
--prs <url> |
Explicit PR URL to include (repeatable); bypasses discovery. | — |
--prs-file <path> |
File of PR URLs (one per line, # comments ok); merged with --prs. |
— |
--name <tag> |
Release tag. Defaults to --to when it's a tag; left blank if --to is a commit / branch. |
— |
--title <text> |
Changelog heading / draft display name. | prettified --name |
-o / --output <file> |
Write markdown to file instead of creating a draft release. | — |
--compared-to-url <url> |
Override the "as compared to" header link. | auto |
--docker-image-url <url> |
Docker image URL; placeholder sha256-TBD if omitted. |
— |
--work-dir <path> |
Local clone for resolving --from / --to. |
config / cwd |
Manages the static features: list in the session file (the dynamic
counterpart is pr_sources.*). Schema:
configuration.md.
releasy feature add --id <id> --source-branch <branch> --description <desc>
releasy feature enable --id <id>
releasy feature disable --id <id>
releasy feature remove --id <id>
releasy feature list| Subcommand | Description |
|---|---|
add |
Append entry. Requires --id, --source-branch, --description. |
enable |
Set enabled: true. Requires --id. |
disable |
Set enabled: false. Requires --id. |
remove |
Delete from session. Doesn't touch branches. Requires --id. |
list |
Print features grouped by enabled/disabled. |
Add, remove, and list individual PR URLs in the session so you never
hand-edit pr_sources.include_prs, pr_sources.exclude_prs, or
pr_sources.groups[].prs. Schema:
configuration.md.
releasy pr add <PR-URL> [--group <id>] [--context <text>]
releasy pr remove <PR-URL> [--keep-discovery]
releasy pr list| Subcommand | Description |
|---|---|
add |
Append URL to pr_sources.include_prs (or groups[<id>].prs with --group). Validates the URL via the GitHub API, idempotent on re-add, and clears the URL from exclude_prs if it was previously excluded. Optional --context sets the per-PR ai_context note. |
remove |
Drop the URL from every session list (include_prs, every group's prs, both ai_context dicts) and purge the matching FeatureState. By default also appends the URL to exclude_prs so label-driven discovery doesn't re-add it on the next refresh; pass --keep-discovery to skip that step. Refuses if the URL is part of a multi-PR group still in state (groups are atomic — use releasy clear <identifier> to wipe the whole group). |
list |
Print every URL the session references — top-level include_prs, each group's prs, and exclude_prs — with their ai_context notes. |
Exit: 1 on a malformed URL, an unreachable PR, a group id that doesn't
exist, or any cross-list collision (URL already in include_prs when
adding with --group, etc.); 0 otherwise. All mutating subcommands
take the project lock; list is read-only.