Skip to content
Mobile
Skill

/ngit-pr

Opening, revising or merging a PR via `gh` (GitHub) or `ngit` (nostr proposals: `pr/feat/*` branches, gitworkshop.dev, `nostr://` remote). Use when a PR task touches either path; finds remotes by URL, covers the three-mains gate and slow pushes.

From plugin
amethyst
1.6k12 skills

Claude Code

$ npx -y skills add vitorpamplona/amethyst --skill ngit-pr --agent claude-code

SKILL.md

ngit-pr.SKILL.md
name: ngit-pr
description: Opening, revising or merging a PR via `gh` (GitHub) or `ngit` (nostr proposals: `pr/feat/*` branches, gitworkshop.dev, `nostr://` remote). Use when a PR task touches either path; finds remotes by URL, covers the three-mains gate and slow pushes.

Pull requests: GitHub **and** git-over-nostr

This repo can be contributed to **two** ways, via **two kinds** of remote. Both end up in GitHub `main`. Opening a PR at all is gated by AGENTS.md "Git Workflow" (only when the human asks).

| Remote kind | URL pattern | Role | |--|--|--| | **GitHub** | `github.com/vitorpamplona/amethyst` | **Canonical** `main`. Moves constantly (bots merge often). Standard `gh` PR flow. | | **git-over-nostr** | `nostr://…/relay.ngit.dev/amethyst` | `ngit`. A push fans out to GitHub **and** the GRASP git servers and publishes nostr events. PRs are **proposals** (the `pr/feat/*` branches), reviewed on **gitworkshop.dev**, *not* GitHub PRs. |

Step 0: identify YOUR remotes (names are not universal)

Remote **names are per-clone**. In the maintainer's checkout the GitHub remote is `upstream` and the nostr remote is `origin`, but yours may differ, and **you may have only one** (e.g. cloned straight from `nostr://…`; pushing it still reaches GitHub via fan-out). Detect by **URL**, never assume a name:

git remote -v
GH_REMOTE=$(git remote -v    | awk '/github\.com/   {print $1; exit}')   # may be empty
NOSTR_REMOTE=$(git remote -v | awk '/nostr:\/\//     {print $1; exit}')  # may be empty

Which path?

| | **GitHub path** (`gh`) | **nostr path** (`ngit`) | |--|--|--| | Needs | a GitHub remote + `gh auth status` | a `nostr://` remote + `ngit` ≥ 3.0 (`ngit --version`; `ngit update --check`) | | PR lives on | GitHub only | nostr + GitHub + GRASP (fans out) | | Use when | Default. Simplest, no alignment gate. | The PR must be reviewable over nostr (gitworkshop), or you're revising/merging an existing **proposal** (`pr/feat/*`). |

**Default to GitHub** unless the task is specifically about a nostr proposal (a gitworkshop link, a `pr/feat/*` branch, "the PRs on the nostr remote"). One remote decides it for you. Revise/merge a PR on **the same path it was created**.

The build gate (both paths, every create, revise and merge)

`main` has no branch protection, and the JVM/Android builds accept APIs that break the shared code: `HashMap.putIfAbsent`, `Dispatchers.IO` without `import kotlinx.coroutines.IO`, the stdlib `use` on skia or okio types. Four PRs merged in one day broke `main` this way (#4529, #4536, #4540, #4541), each merged while its CI was red or still running. So, before **every** push, `ngit send` or merge:

PREPUSH_KMP_METADATA=1 bash .git-hooks/pre-push   # the pre-push hook with its opt-in KMP metadata compiles on: every check CI runs that a plain push skips

That is the script the git pre-push hook runs, but a plain `git push` leaves the KMP metadata compiles out unless `PREPUSH_KMP_METADATA=1` is set or it pushes to `main`, and `.git/hooks/pre-push` is a copy that only `:amethyst:preBuild` or `./gradlew installGitHook` refreshes: a fresh clone or web container may have none. Run `./gradlew installGitHook` once per checkout, and run the script by hand before `ngit send` and `ngit pr merge`, which publish without a plain `git push` of your branch. On macOS, when `iosMain` changed, also run `./gradlew :commons:compileKotlinIosSimulatorArm64 :commonsUI:compileKotlinIosSimulatorArm64`.

**Merge only on green CI.** `gh pr checks <n> --repo vitorpamplona/amethyst --watch` and wait for every job, `lint` and the iOS jobs included, to pass; the jobs run side by side, so a `lint` failure no longer skips the rest. An ngit merge publishes `main` directly, with no PR checks on GitHub, so the gate above is the only check it gets.

---

GitHub path (`gh`)

Branch off `main`, `git push -u "$GH_REMOTE" feat/<slug>`, then `gh pr create --repo vitorpamplona/amethyst --base main --head feat/<slug>`. Revise by pushing more commits to the same branch; review with `gh pr view <n> --repo vitorpamplona/amethyst --comments`; the maintainer merges with `gh pr merge <n> --merge` (or `--squash`) once `gh pr checks <n>` is green (the build gate above). GitHub is the source of truth here — no three-mains gate.

---

nostr path (`ngit`)

**Mental model:** a proposal is a *linear patch series off `main`*, published as nostr events. A "revision" is a new version of that proposal. Merging applies the series to `main` and publishes a merged-status event. There is **no** PR number: a proposal is named by its event id, and a unique hex prefix of it works. `ngit pr merge` makes a plain (no-ff) merge commit.

The three-mains alignment gate (the thing that breaks everything)

Up to **three** `main` heads drift apart:

  • GitHub main (`$GH_REMOTE/main`) — newest, moves every few minutes
  • nostr main (`$NOSTR_REMOTE/main`) — **lags**, often far behind
  • local `main`

**Every create/revise/merge requires the proposal's base to equal the nostr `main`, and pushing `main` to the nostr remote requires GitHub's main to be an ancestor of what you push.** When misaligned, ngit **rejects pre-flight and publishes nothing** (safe — realign and retry). Don't `--force` past it.

git fetch --all
echo "github=$([ -n "$GH_REMOTE" ] && git rev-parse "$GH_REMOTE/main")  \
nostr=$(git ls-remote "$NOSTR_REMOTE" -h refs/heads/main | awk '{print $1}')  \
local=$(git rev-parse main)"
# all present heads equal → proceed.
# local behind GitHub?  git merge --ff-only "$GH_REMOTE/main"  (or "$NOSTR_REMOTE/main" if that's all you have)
# nostr behind GitHub?  ngit sync --trust-server   (moves nostr state up to GitHub; clean fast-forwards only)
#                       or, with local main == GitHub main: git push "$NOSTR_REMOTE" main

With **only** the nostr remote: align local `main` to `$NOSTR_REMOTE/main`; GitHub is handled by fan-out. If GitHub diverged from nostr (`out of sync with nostr` on pu

Read more
@vitorpamplona
Ships withamethyst

Nostr client for Android

Stars on GitHub

Momentum

stars in total
1.6k
Star history of vitorpamplona/amethyst on GitHub, a chart by star-history.com

Other skills on amethyst.

amy-expert

amy-expert

amy CLI in cli/: adding an `amy <verb>` command, Main.kt dispatch, Output.emit/--json contract, exit codes 0/1/2/124, ~/.amy…

android-expert

android-expert

Android shim in `amethyst/`: MainActivity, AndroidManifest/intent filters, :napplet process, play/fdroid flavors, R8 +…

auth-signers

auth-signers

NostrSigner in Amethyst: NostrSignerInternal (local key), NostrSignerRemote (NIP-46 bunker), NostrSignerExternal (NIP-55 Android…

compose-expert

compose-expert

Shared Compose UI in `commonsUI/`: screens, components, the nav shell (`ScreenLayoutSpec`, `AppShellLayout`, Navigation 3 back…

desktop-expert

desktop-expert

Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging.…

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo5d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens3w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about ngit-pr.

Which coding agents can use ngit-pr?
ngit-pr installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.