tools: add github_pr_create, lex-code's first approval-gated tool - #152
Merged
Conversation
Every VCS tool in src/tools/vcs/ wraps lex-vcs (lex branch/lex merge), the structural, content-addressed system built into the lex CLI -- none of them touch git or GitHub. Shipping a change from a lex-code run has always meant a human (or Claude, driving `gh` by hand outside this codebase) taking the current branch the rest of the way. Adds github_pr_create: shells to `gh pr create --title --body [--base]` on the current (already-pushed) branch. Deliberately does not push the branch itself -- that stays an ordinary bash/git action any mode with those tools already has. This is also the first tool in the codebase to carry an approval_scope (t.with_approval, lex-llm#41 / lex-lang#737's std.approval effect) -- `grep -rln "approval_scope" src/tools/` returned nothing before this. Opening a PR is visible to everyone with repo access and cannot be un-opened the way a local edit can be un-written, so it is exactly the class of action every other agent gates behind a human; lex-code had the plumbing since #41 and nothing ever used it. Wired into build (all_tools_for_mode, unrestricted) and refactor (refactor_permission's allowlist) -- not into the local-model minimal/ dynamic toolsets, matching the existing convention of keeping local models' visible surface small. Found and fixed a real gap in lex-lang while validating this: `lex run`'s CLI never wired StdinApprovalSink, so every approval request was unconditionally refused regardless of what stdin contained -- fixed upstream (lex-lang#807). Verified end to end against the real dispatch path here after that fix: denying returns "denied by operator"; approving genuinely proceeds to gh pr create, which then fails for the expected unrelated reason (head branch same as base) -- confirming the tool only ever runs after a real approval. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… tools CI's smoke test requires every tool to either invoke a real command it can safely run unconditionally, or be in the skip list with a reason. github_pr_create shells to a real `gh pr create` and is now also approval-gated -- a smoke run in CI has no interactive stdin to answer that prompt, so it would either hang or (once StdinApprovalSink hits EOF) deny every time, proving nothing either way. Same shape as vcs_branch_create/vcs_merge_start/write/edit/bash: covered by its own CLI-shape check, not run for real here. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3 tasks
alpibrupa
added a commit
that referenced
this pull request
Sep 9, 2026
github_pr_create (#152) got a change as far as "a PR exists". Landing it always meant a human (or an agent driving `gh` by hand outside this codebase) polling `gh pr checks`, then running `gh pr merge` once green -- the exact pattern GitHub Copilot's "Agent Merge" and Codex's automatic pre-merge approval reviews both shipped this year, and exactly what a real dogfooding session did by hand for six PRs in a row before this tool existed. Approval-gated for the same reason github_pr_create is: merging is visible to everyone with repo access and cannot be un-merged. The approval prompt covers the whole conditional intent ("merge this once checks pass"), asked before the outcome is known -- the same shape as a human reviewer saying "LGTM, merge when CI is green". Verifies checks itself (`gh pr checks --watch --fail-fast`) rather than trusting the caller's own judgment, and refuses to call `gh pr merge` at all unless that reports success -- a CI failure's own output becomes the tool's error detail, so the failure can actually be diagnosed rather than just reported as "didn't merge". `pr` accepts anything `gh` itself accepts (number, URL, branch name). Wired into refactor_permission's allowlist and all_tools_for_mode, same as github_pr_create; added to smoke.lex's skip list (a smoke run must never trigger a real merge). Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/tools/vcs/wraps lex-vcs (lex branch/lex merge), the structural, content-addressed system built into thelexCLI — none of them touch git or GitHub. Shipping a change from alex-coderun has always meant a human (or Claude, drivingghby hand outside this codebase) taking the current branch the rest of the way.github_pr_create: shells togh pr create --title --body [--base]on the current (already-pushed) branch. Deliberately does NOT push the branch itself — that stays an ordinarybash/gitaction any mode with those tools already has.approval_scope(t.with_approval, lex-llm#41 / lex-lang#737'sstd.approvaleffect) —grep -rln "approval_scope" src/tools/returned nothing before this. Opening a PR is visible to everyone with repo access and cannot be un-opened the way a local edit can be un-written, so it's exactly the class of action every other agent gates behind a human;lex-codehad the plumbing since Expose modes/tools as MCP server (lex-mcp run_http) #41 and nothing ever used it.build(all_tools_for_mode, unrestricted) andrefactor(refactor_permission's allowlist) — not into the local-model minimal/dynamic toolsets, matching the existing convention of keeping local models' visible surface small.lex run's CLI never wiredStdinApprovalSink, so every approval request was unconditionally refused regardless of what stdin contained — fixed upstream in lex-lang#807.Test plan
lex check/lex fmt --check/lex doc-sync --check— cleanlex test tests— 4/4 existing tests still passgh pr create, which then fails for the expected unrelated reason (head branch same as base) — confirming the tool only ever runs after a real approval, not a stubbed one🤖 Generated with Claude Code