Skip to content

feat: --open-issue and --open-pr CLI modes (Phase 2 — T2.1, T2.2, T2.3)#39

Open
canonical-muhammadbassiony wants to merge 4 commits into
feat/phase-1-cli-restorefrom
feat/phase-2-cli-features
Open

feat: --open-issue and --open-pr CLI modes (Phase 2 — T2.1, T2.2, T2.3)#39
canonical-muhammadbassiony wants to merge 4 commits into
feat/phase-1-cli-restorefrom
feat/phase-2-cli-features

Conversation

@canonical-muhammadbassiony

Copy link
Copy Markdown
Collaborator

Summary

Implements the two GitHub-integrated CLI modes and enforces their mutual exclusion.

Tasks Implemented

  • T2.1: --open-issue mode — runs orchestrator in dry-run (extraction only, no Copilot), builds structured markdown issue body categorising suggestions, creates GitHub issue via REST API
  • T2.2: --open-pr mode — runs orchestrator with Copilot, creates branch <prefix>/<runID>, stages, commits, pushes, opens PR via gh CLI
  • T2.3: Early validateFlags check — if both --open-pr and --open-issue are set, exits immediately with error before any API calls

Files Changed

  • cmd/bauer/main.govalidateFlags, runOpenIssue, buildIssueBody, runOpenPR, buildPRBody
  • cmd/bauer/main_test.goTestValidateFlags_* suite
  • internal/orchestrator/orchestrator.goOrchestrationResult.RunID
  • internal/github/issue.go — new: CreateIssue via GitHub REST API
  • internal/github/git.go — new: RunGit helper
  • internal/config/cli.goGitHubRepo field
  • internal/config/manager.goFlagsSource maps GitHubRepo

Part of the Bauer v2 stacked PR series (Branch 4 of 12).

Bauer Agent added 2 commits May 20, 2026 13:02
- T2.1: runOpenIssue: generate plan, create GitHub issue via REST API
- T2.2: runOpenPR: run Copilot, git-branch, git-push, create PR
- T2.3: mutual exclusion validated before any I/O, improved error message

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds two GitHub-integrated CLI execution modes to Bauer—one to open an issue with extracted suggestions and another to apply changes via Copilot and open a PR—plus supporting config and orchestration metadata to carry a run identifier through the workflow.

Changes:

  • Introduces --open-issue (dry-run extraction + GitHub issue creation via REST) and --open-pr (Copilot apply + git branch/commit/push + PR open via gh).
  • Adds RunID to OrchestrationResult and threads it into branch naming / issue body.
  • Adds GitHubRepo to CLI/config plumbing and new GitHub helpers (CreateIssue, RunGit).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
cmd/bauer/main.go Implements --open-issue / --open-pr flows, issue/PR body builders, and flag validation.
internal/orchestrator/orchestrator.go Adds OrchestrationResult.RunID and populates it from artifact run IDs.
internal/github/issue.go New REST-based GitHub issue creation helper.
internal/github/git.go New helper to run git commands and return combined output.
internal/config/cli.go Adds GitHubRepo to CLI flags struct.
internal/config/manager.go Maps CLI GitHubRepo into resolved runtime config.
docs/implementation-log.md Marks Phase 2 branch as done and documents the implemented work.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cmd/bauer/main.go
Comment on lines +351 to +366
// Push the branch.
if _, err := github.RunGit(ctx, repoDir, "push", "origin", branchName); err != nil {
return fmt.Errorf("failed to push branch %q: %w", branchName, err)
}

// Create the pull request.
prBody := buildPRBody(result, branchName)
prURL, err := github.CreatePR(repo.Owner, repo.Name, github.CreatePROptions{
Title: "docs: apply documentation suggestions from Copilot",
Body: prBody,
BaseBranch: "main",
HeadBranch: branchName,
})
if err != nil {
return fmt.Errorf("failed to create PR: %w", err)
}
Comment thread cmd/bauer/main.go
Comment on lines +171 to +193
if cfg.GitHubRepo == "" {
return fmt.Errorf("--github-repo (or BAUER_GITHUB_REPO) is required for --open-issue mode")
}

// Run with dry-run=true: extract + generate prompts, but skip Copilot.
issueCfg := *cfg
issueCfg.DryRun = config.BoolPtr(true)

result, err := orch.Execute(ctx, &issueCfg)
if err != nil {
return fmt.Errorf("orchestration failed: %w", err)
}

if result.ExtractionBundle == nil || result.ExtractionBundle.Document == nil {
return fmt.Errorf("no document data returned by orchestrator")
}
doc := result.ExtractionBundle.Document

title := fmt.Sprintf("docs: %s \u2014 documentation suggestions review", doc.DocumentTitle)
body := buildIssueBody(doc, cfg, result.RunID)

issueURL, err := github.CreateIssue(ctx, token, cfg.GitHubRepo, title, body)
if err != nil {
Comment thread cmd/bauer/main.go
Comment on lines +223 to +233
for _, s := range loc.Suggestions {
brief := s.Change.NewText
if len(brief) > 100 {
brief = brief[:97] + "..."
}
if brief == "" {
brief = s.Change.OriginalText
if len(brief) > 100 {
brief = brief[:97] + "..."
}
}
Comment thread cmd/bauer/main.go Outdated
Comment on lines +137 to +143
// validateFlags checks mutual exclusion and other flag constraints.
// Called immediately after flag parsing, before any I/O or env resolution.
func validateFlags(openPR, openIssue bool) error {
if openPR && openIssue {
return fmt.Errorf("--open-pr and --open-issue are mutually exclusive\n Use --open-pr to apply changes and open a PR.\n Use --open-issue to generate a plan and open an issue without applying changes.")
}
return nil
Comment thread cmd/bauer/main.go
Comment on lines +330 to +332
// Create the new branch.
if _, err := github.RunGit(ctx, repoDir, "checkout", "-b", branchName); err != nil {
return fmt.Errorf("failed to create branch %q: %w", branchName, err)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by design. The workflow assumes the user has the target repository checked out on its default branch (typically main). We branch from the current checkout state, and the PR targets main. This matches the intended usage: run bauer --open-pr from within your repo on main. If we fetched and reset to the remote default branch automatically, we risk discarding local state the user did not expect to lose.

Comment thread internal/github/issue.go
Comment on lines +15 to +24
func CreateIssue(ctx context.Context, token, repo, title, body string) (string, error) {
if token == "" {
return "", fmt.Errorf("GitHub token is required")
}
if repo == "" {
return "", fmt.Errorf("repo is required (owner/name format)")
}
if title == "" {
return "", fmt.Errorf("issue title is required")
}
Comment thread docs/implementation-log.md Outdated
**Files changed:** _(to be filled by agent)_
**Files changed:**
- `cmd/bauer/main.go` — full rewrite: adds `--github-repo` flag; `validateFlags` replaces inline mutual-exclusion guard; implements `runOpenIssue`, `buildIssueBody`, `runOpenPR`, `buildPRBody`, `countAllSuggestions`; `runOpenPR` signature gains `repoDir string`
- `cmd/bauer/main_test.go` — replaces `checkMutualExclusion`/stub tests with `TestValidateFlags_*` suite (T2.3) and `TestRunOpenIssue/PR_ProceedsToWorkflow` (verifies stubs replaced)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Comment thread cmd/bauer/main.go
Comment on lines +171 to +178
if cfg.GitHubRepo == "" {
return fmt.Errorf("--github-repo (or BAUER_GITHUB_REPO) is required for --open-issue mode")
}

parts := strings.SplitN(cfg.GitHubRepo, "/", 3)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid --github-repo %q: expected \"owner/repo\" format", cfg.GitHubRepo)
}
Comment thread cmd/bauer/main.go
Comment on lines +367 to +372
// Create the pull request.
prBody := buildPRBody(result, branchName)
prURL, err := github.CreatePR(repo.Owner, repo.Name, github.CreatePROptions{
Title: "docs: apply documentation suggestions from Copilot",
Body: prBody,
BaseBranch: "main",

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged — added a comment documenting the limitation. A proper fix (reading the default branch from config or via git symbolic-ref refs/remotes/origin/HEAD) will be added when multi-branch support is needed. For now, all target repos in our workflow use main.

Comment thread internal/github/issue.go Outdated
Comment on lines +52 to +55
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", fmt.Errorf("failed to send request to GitHub API: %w", err)
}
**Tasks:** T2.1, T2.2, T2.3

**Summary:** _(to be filled by agent)_
**Summary:** Implemented `--open-issue` and `--open-pr` CLI modes. Added `--github-repo` flag (maps to `cfg.GitHubRepo`). Replaced the mutual-exclusion inline check with a pure `validateFlags(openPR, openIssue bool) error` function called immediately after `fs.Parse`, before any I/O or env-var resolution (T2.3). Implemented `runOpenIssue`: runs the orchestrator in dry-run mode to extract suggestions without invoking Copilot, then builds a structured markdown issue body (categorising suggestions as copy changes vs content additions, with optional Figma link and a next-steps command) and creates the issue via the GitHub REST API using `net/http` (T2.1). Implemented `runOpenPR`: resolves the GitHub token, runs the orchestrator with Copilot enabled, creates a new git branch `<prefix>/<runID>`, stages and commits all changes, pushes the branch, and opens a PR via the `gh` CLI (T2.2). Added `RunID string` field to `OrchestrationResult` so branch naming uses the artifact run ID. Added `GitHubRepo` to `CLIFlags` and `FlagsSource`. Created `internal/github/issue.go` (REST API `CreateIssue`) and `internal/github/git.go` (`RunGit` helper). Updated tests to use `validateFlags` and verify the workflow functions are implemented beyond stub status.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Comment thread cmd/bauer/main.go
Comment on lines +175 to +177
if _, err := github.ParseGitHubRepo(cfg.GitHubRepo); err != nil {
return fmt.Errorf("invalid --github-repo %q: %w", cfg.GitHubRepo, err)
}
Comment thread cmd/bauer/main.go
Comment on lines +297 to +300
token, err := github.GetGitHubToken()
if err != nil {
return fmt.Errorf("GitHub token not found: %w\nSet BAUER_GITHUB_TOKEN, GITHUB_TOKEN, or GH_TOKEN, or run 'gh auth login'", err)
}
Comment thread cmd/bauer/main.go
Comment on lines +344 to +350
// Commit. If there is nothing to commit, report and exit cleanly.
commitMsg := "docs(bauer): apply documentation suggestions"
out, err := github.RunGit(ctx, repoDir, "commit", "-m", commitMsg)
if err != nil {
if strings.Contains(out, "nothing to commit") {
fmt.Println("No changes to commit. Exiting.")
return nil
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