Skip to content

Make the plugin forge-agnostic (descriptor + capability flags, no behavior change) - #5

Open
solalbaudoincs wants to merge 7 commits into
mrolafsson:mainfrom
solalbaudoincs:forge-agnostic
Open

solalbaudoincs wants to merge 7 commits into
mrolafsson:mainfrom
solalbaudoincs:forge-agnostic

Conversation

@solalbaudoincs

@solalbaudoincs solalbaudoincs commented Oct 30, 2026 •

Copy link
Copy Markdown

Summary

No behavior change for GitHub. This moves the GitHub-specific knowledge that lives outside github.go behind a forge descriptor, and adds capability flags the TUI consults — groundwork for a GitLab sibling plugin, as discussed in #4. go build/go vet/gofmt clean; full suite 165 passed (159 baseline + 6 new; baseline verified green before starting).

The forge descriptor (forge.go, new)

A small struct-valued var, theForge, one instance per forge. GitHub gets githubForge; the demo runs on it too (it impersonates GitHub today). What it carries:

field replaces the hard-coded
prFromPath pull/N path parsing in agents.go (transcript links)
prHeadRef refs/pull/N/head in worktree.go, demo.go
token the token seam's default (tokenFn still points where it did; ghToken itself is unchanged in github.go)
cloneCmd the gh repo clone invocation in repos.go (Env still starts from os.Environ())
methodNames/methodVerbs merge-method labels in detail.go
name, defaultHost, loginCmd, loginText, cliName the signed-out screen's wording (byte-identical to main for GitHub), toast titles (notify now takes only a body, titled with the forge's name), errSignedOut, the sign-in error prefix

Deliberately left out: knownHost stays on config (user config, already forge-neutral), and the GraphQL endpoint stays in github.go (it's the GitHub client's API detail).

Capability flags (source.go)

caps{reviewRequested, autoMerge} on the source interface. Both current sources report full capability — zero behavior change — but the TUI now consults the flags: the Review tab only appears when reviewRequested (tabShown, cycleTab in tui.go/groups.go), and the merge menu/key only when autoMerge (detail.go). This generalizes what the demo implicitly lacked.

Tests

  • New: forge_test.go (URL path parsing, default host, clone inherits the environment), caps_test.go (Review tab hidden, merge key hidden), screen_test.go (signed-out screen wording matches main exactly).
  • One existing test adapted (TestSignedOutScreen): it asserted GitHub's wording literally; it now asserts via theForge.loginText — same meaning, same string for GitHub.
  • Everything else untouched: 165/165 passing.

Commits

  1. 6388bc0 — the descriptor, and everything threaded through it
  2. 0dbf243 — forge-neutral wording, sign-in and toasts via the forge
  3. bba781f — a doc comment the tabShown insertion had split, fixed
  4. af46c7b — gofmt
  5. accb96f — fix round: cloneCmd's Env now starts from os.Environ() (it had been built from an empty slice, breaking every clone), and the signed-out screen / sign-in errors keep GitHub's exact previous wording through loginText/cliName (a sibling forge can spell its own)

PLAN.md (the implementation plan) is included in the diff — drop it if you'd rather not keep it in-tree.

GitLab itself stays out of scope here; the follow-up would be a sibling plugin standing gitlabForge behind the same seams.

Solal Baudoin added 5 commits October 30, 2026 13:41
The GitHub specifics that lived outside github.go (PR URL parsing,
fork PR head refs, clone and sign-in commands, merge-method
vocabulary, host display) now sit on a forge descriptor (forge.go),
with capability flags on the source interface the TUI consults to
hide a tab or key a forge doesn't support. GitHub and demo behavior
is unchanged; a GitLab sibling can now bring its own descriptor.
The signed-out screen, error wording, login command and toast titles
now come from the forge descriptor, so a sibling forge spells its own.
Tests: caps-gated UI covered (Review tab hidden, merge key hidden);
TestSignedOutScreen asserts via the descriptor.
…orge

cloneCmd built its Env from nothing, so a clone ran gh with no PATH,
HOME or GH_TOKEN (the command's Env now starts from os.Environ, as
before); and the signed-out screen and sign-in errors lost gh's exact
words. Both go through the descriptor now (loginText, cliName), which
renders GitHub byte-identical to main while a sibling forge can spell
its own. Tests cover both.
solalbaudoincs and others added 2 commits October 30, 2026 17:23
…nfig) (#1)

* feat: GitLab merge requests behind the forge descriptor

A gitlabSource over GitLab's REST API (v4) maps merge requests onto the
plugin's pull requests, and gitlabForge spells GitLab's URLs, fork refs,
merge methods and sign-in. "forge": "gitlab" in config.json switches
the plugin to it; GitHub stays the default, and the demo stays on GitHub.

The descriptor now also builds the picker's source and answers the label
hooks and status (newSource, api), so no call site names GitHub's client.
The multi-host cursor and repo paging are shared (fanOut, pageHosts), and
repo paths may nest (group/sub/project) when the forge says so.

* refactor: comments and the truncation notice name the forge in use

The forge's comments still spoke of GitLab as a forge to come, and the
list-cap notice was built at start-up, before the config chose the
forge, so it said GitHub on GitLab.

---------

Co-authored-by: Solal Baudoin <solal.baudoin@macbook-air-solalbaudoin.cheetah-koi.ts.net>
loadAll fired the Review tab's load whatever the forge: with the GitLab
source, which answers nothing for it, a signed-out error from that load
flipped the whole popup to the signed-out screen and hid the lists. Ask
only for the tabs the forge can answer.

Co-authored-by: Solal Baudoin <solal.baudoin@macbook-air-solalbaudoin.cheetah-koi.ts.net>
solalbaudoincs added a commit to solalbaudoincs/herdr-github that referenced this pull request Oct 30, 2026
Forge-agnostic core + GitLab support (from upstream PR mrolafsson#5)
@mrolafsson

Copy link
Copy Markdown
Owner

Thank you so much for all of this! 🙏 It's a huge amount of careful work. The GitHub wording stays exactly the same, there's no new dependency, and the GitLab REST client and its tests are thorough. It's really nice to see someone want to bring the plugin family to GitLab.

I've thought about it a lot, and I don't think I can take it into herdr-github, mainly because I couldn't maintain it well. I don't use GitLab, so I'd have no way to test that side. Every new feature would also need a GitLab version or a capability flag. Today's change is an example: "Update branch" (#1) adds updateBranch to source, and the merge queue added enqueue/dequeue a while back. The branch is now behind both, so merging it would mean either breaking GitLab without knowing or slowing everything down.

What I'd suggest is shipping it as herdr-gitlab, your own fork. The branch is most of the way there already, since it now carries the GitLab source itself (2d0b8dd), not just the seams. You'd be free to shape it around GitLab: MR wording, glab, no Review tab, without having to keep GitHub's exact behaviour. If over time it turns out a lot of code is truly shared, I'd be happy to look at extracting the common parts then, once we can see which they are.

One small thing for anyone reading: the description still says GitLab is out of scope, but the branch now includes it. Could you update the description, or close this in favour of the fork, whichever you prefer?

Thanks again, really, for the thought and care that went into this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants