feat: close claude.ai-surface gaps — direct-to-main write, PR create/merge, owner repo listing #35

Closed
opened 2026-06-01 19:30:31 +00:00 by mathias · 2 comments
Owner

Context

While using the gitea-mcp tools from a claude.ai web session (brain review + ADR-editing task, 2026-06-01), three friction points surfaced that limit how much an agent can complete end-to-end without dropping to a terminal. None are blockers, but each forces a workaround.

1. No direct-to-main write path

file_write_branch always writes to a feature branch (creating it from base if absent). The project convention (per project_create ADR and the project-wide TBD policy) is commit directly to main, every commit deployable. For single-file doc edits — a DECISIONS.md append, a CLAUDE.md tweak — being forced onto a branch + PR + merge is friction with no compensating safety gain, and it contradicts the trunk-based norm the rest of the platform follows.

Proposal: allow branch: "main" (or the repo default) as a valid target in file_write_branch — i.e. don't force a non-default branch. Keep the branch behaviour as the opt-in path for anything that genuinely warrants review.

2. No PR create/merge tools exposed to the claude.ai surface

Because writes land on a branch (see #1), the natural completion is "open a PR, merge it". pr_create / pr_merge exist in the broader gitea tool family but were not in the toolset exposed to this claude.ai session — only file_write_branch, file_delete, tag_create, release_create, repo_tree, plus the issue tools and read tools. So a branch write becomes a dead end from the web surface: I can create the branch but not land it.

Proposal: either (a) expose pr_create + pr_merge to the claude.ai connector surface so the branch→PR→merge loop can complete, or (b) implement #1 so single edits never need a branch. Doing both is ideal: direct-to-main for trivial edits, PR loop for substantial ones.

3. repo_search does not accept an owner-only listing

repo_search requires a q string; there's no clean "list all repos for owner mathias" call. I had to probe with guessed substrings (brain, ingest, hyperguild, gitea-mcp) to enumerate the repo set, which is lossy — a repo with no guessed-substring match would be invisible.

Proposal: allow repo_search with empty/absent q to return all repos for the owner (paginated), or add a thin repo_list to the exposed surface.

Acceptance criteria

  • An agent on the claude.ai surface can land a single-file edit to main in one tool call (no orphaned branch), or can complete branch → PR → merge without a terminal
  • Repo enumeration for an owner is possible without guessing search terms

Provenance

Surfaced during a brain-review session over the claude.ai gitea connector, 2026-06-01.

## Context While using the gitea-mcp tools from a claude.ai web session (brain review + ADR-editing task, 2026-06-01), three friction points surfaced that limit how much an agent can complete end-to-end without dropping to a terminal. None are blockers, but each forces a workaround. ## 1. No direct-to-main write path `file_write_branch` always writes to a feature branch (creating it from base if absent). The project convention (per `project_create` ADR and the project-wide TBD policy) is **commit directly to `main`, every commit deployable**. For single-file doc edits — a DECISIONS.md append, a CLAUDE.md tweak — being forced onto a branch + PR + merge is friction with no compensating safety gain, and it contradicts the trunk-based norm the rest of the platform follows. **Proposal:** allow `branch: "main"` (or the repo default) as a valid target in `file_write_branch` — i.e. don't force a non-default branch. Keep the branch behaviour as the opt-in path for anything that genuinely warrants review. ## 2. No PR create/merge tools exposed to the claude.ai surface Because writes land on a branch (see #1), the natural completion is "open a PR, merge it". `pr_create` / `pr_merge` exist in the broader gitea tool family but were **not in the toolset exposed to this claude.ai session** — only `file_write_branch`, `file_delete`, `tag_create`, `release_create`, `repo_tree`, plus the issue tools and read tools. So a branch write becomes a dead end from the web surface: I can create the branch but not land it. **Proposal:** either (a) expose `pr_create` + `pr_merge` to the claude.ai connector surface so the branch→PR→merge loop can complete, or (b) implement #1 so single edits never need a branch. Doing both is ideal: direct-to-main for trivial edits, PR loop for substantial ones. ## 3. `repo_search` does not accept an owner-only listing `repo_search` requires a `q` string; there's no clean "list all repos for owner `mathias`" call. I had to probe with guessed substrings (`brain`, `ingest`, `hyperguild`, `gitea-mcp`) to enumerate the repo set, which is lossy — a repo with no guessed-substring match would be invisible. **Proposal:** allow `repo_search` with empty/absent `q` to return all repos for the owner (paginated), or add a thin `repo_list` to the exposed surface. ## Acceptance criteria - [ ] An agent on the claude.ai surface can land a single-file edit to `main` in one tool call (no orphaned branch), **or** can complete branch → PR → merge without a terminal - [ ] Repo enumeration for an owner is possible without guessing search terms ## Provenance Surfaced during a brain-review session over the claude.ai gitea connector, 2026-06-01.
Author
Owner

Item #1 (direct-to-main write) is the priority — fresh repro 2026-06-02

Confirming item #1 from another claude.ai session today. While committing an S5 spike handoff doc to infra (docs/superpowers/handoffs/2026-06-02-video-adapter-placement.md), I called file_write_branch with branch: "main" and it succeeded — landing the commit directly on main at a2e58db.

But that worked only because main is accepted as an arbitrary branch-name string, not because there is a deliberate direct-to-main path. The tool's contract is still "create/update a file on a feature branch (created from base if absent)." Passing main happens to no-op the branch creation since it already exists — it's an accident of the implementation, not a designed behaviour. The risk: nothing guarantees this keeps working if the tool ever starts enforcing non-default branch targets (which its own description implies it should), and there's no validation/intent signalling that direct-to-main is the sanctioned TBD path.

Why this is the highest-value item in this ticket

This is the behaviour the whole GitOps workflow leans on: the platform-wide convention (and gitea-mcp's own #27) is commit directly to main, one logical change per commit, every commit deployable. Forcing single-file edits (handoff docs, DECISIONS.md appends, CLAUDE.md tweaks) onto branch→PR→merge contradicts that and — per item #2 — currently dead-ends, since pr_create/pr_merge aren't on the claude.ai surface. So item #1 is what makes TBD from claude.ai actually function; items #2 (PR loop) and #3 (owner repo listing) are real but secondary.

Suggestion

Split item #1 out as its own focused P0 ticket ("feat: direct-to-main write path for TBD") with a deliberate acceptance criterion:

  • file_write_branch (or a clearer-named sibling) treats the repo default branch as a first-class, sanctioned target — not an incidental branch string
  • direct-to-main is the documented TBD path; feature-branch remains the opt-in for changes that genuinely warrant review
  • behaviour is stable even if branch-target validation is later tightened

Leave #2 and #3 in this ticket. Happy to file the split-out ticket if you'd prefer it tracked separately.

Repro provenance: S5 video-summary service placement spike, claude.ai session 2026-06-02.

## Item #1 (direct-to-main write) is the priority — fresh repro 2026-06-02 Confirming item #1 from another claude.ai session today. While committing an S5 spike handoff doc to `infra` (`docs/superpowers/handoffs/2026-06-02-video-adapter-placement.md`), I called `file_write_branch` with `branch: "main"` and it succeeded — landing the commit directly on `main` at `a2e58db`. But that worked only because `main` is accepted as an arbitrary branch-name string, not because there is a deliberate direct-to-main path. The tool's contract is still "create/update a file on a feature branch (created from base if absent)." Passing `main` happens to no-op the branch creation since it already exists — it's an accident of the implementation, not a designed behaviour. The risk: nothing guarantees this keeps working if the tool ever starts enforcing non-default branch targets (which its own description implies it should), and there's no validation/intent signalling that direct-to-main is the sanctioned TBD path. ### Why this is the highest-value item in this ticket This is the behaviour the whole GitOps workflow leans on: the platform-wide convention (and gitea-mcp's own #27) is **commit directly to `main`, one logical change per commit, every commit deployable**. Forcing single-file edits (handoff docs, DECISIONS.md appends, CLAUDE.md tweaks) onto branch→PR→merge contradicts that and — per item #2 — currently dead-ends, since `pr_create`/`pr_merge` aren't on the claude.ai surface. So item #1 is what makes TBD from claude.ai actually function; items #2 (PR loop) and #3 (owner repo listing) are real but secondary. ### Suggestion Split item #1 out as its own focused P0 ticket ("feat: direct-to-main write path for TBD") with a deliberate acceptance criterion: - `file_write_branch` (or a clearer-named sibling) treats the repo default branch as a first-class, sanctioned target — not an incidental branch string - direct-to-main is the documented TBD path; feature-branch remains the opt-in for changes that genuinely warrant review - behaviour is stable even if branch-target validation is later tightened Leave #2 and #3 in this ticket. Happy to file the split-out ticket if you'd prefer it tracked separately. _Repro provenance: S5 video-summary service placement spike, claude.ai session 2026-06-02._
Author
Owner

Resolved as of v0.4.2 — all three friction points are closed:

  1. Direct-to-main writefile_write_branch already commits directly to any existing branch: BranchExists→true skips branch creation and upserts (proven by TestFileWriteBranchSkipsCreateWhenBranchExists). So branch:"main" is a one-call direct-to-main write. The descriptor said "feature branch", understating it — updated to state both the direct-to-main (trunk) path and the create-from-base (PR) path.
  2. PR create/mergepr_create and pr_merge exist and are exposed. (And per #38/#45, pr_merge now uses canonical repo/number.)
  3. Owner repo listingrepo_list exists (owner-scoped, paginated); code_search also fans out across an owner's repos via ListRepos. No more guessing search substrings.

Only change needed this round was the descriptor clarity fix; the capabilities were already present. Closing.

Resolved as of v0.4.2 — all three friction points are closed: 1. **Direct-to-main write** — `file_write_branch` already commits directly to any existing branch: `BranchExists`→true skips branch creation and upserts (proven by `TestFileWriteBranchSkipsCreateWhenBranchExists`). So `branch:"main"` is a one-call direct-to-main write. The descriptor said "feature branch", understating it — updated to state both the direct-to-main (trunk) path and the create-from-base (PR) path. 2. **PR create/merge** — `pr_create` and `pr_merge` exist and are exposed. (And per #38/#45, `pr_merge` now uses canonical `repo`/`number`.) 3. **Owner repo listing** — `repo_list` exists (owner-scoped, paginated); `code_search` also fans out across an owner's repos via `ListRepos`. No more guessing search substrings. Only change needed this round was the descriptor clarity fix; the capabilities were already present. Closing.
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/gitea-mcp#35