From b20978f8d3acd88f70fae2b5570398cd8049f8b1 Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 11:36:47 +0000 Subject: [PATCH] feat(agentic): add pr-manage command alias Co-Authored-By: Virgil --- pkg/agentic/commands.go | 1 + pkg/agentic/commands_test.go | 7 +++++++ pkg/agentic/review_queue.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index 7196226..470153b 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -24,6 +24,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) { c.Command("brain/ingest", core.Command{Description: "Bulk ingest memories into OpenBrain", Action: s.cmdBrainIngest}) c.Command("brain/seed-memory", core.Command{Description: "Import markdown memories into OpenBrain from a project memory directory", Action: s.cmdBrainSeedMemory}) c.Command("plan-cleanup", core.Command{Description: "Permanently delete archived plans past the retention period", Action: s.cmdPlanCleanup}) + c.Command("pr-manage", core.Command{Description: "Manage open PRs (merge, close, review)", Action: s.cmdPRManage}) c.Command("status", core.Command{Description: "List agent workspace statuses", Action: s.cmdStatus}) c.Command("prompt", core.Command{Description: "Build and display an agent prompt for a repo", Action: s.cmdPrompt}) c.Command("extract", core.Command{Description: "Extract a workspace template to a directory", Action: s.cmdExtract}) diff --git a/pkg/agentic/commands_test.go b/pkg/agentic/commands_test.go index 4d14c2f..fb72999 100644 --- a/pkg/agentic/commands_test.go +++ b/pkg/agentic/commands_test.go @@ -875,11 +875,18 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) { assert.Contains(t, cmds, "plan/list") assert.Contains(t, cmds, "plan/show") assert.Contains(t, cmds, "plan/status") + assert.Contains(t, cmds, "pr-manage") assert.Contains(t, cmds, "task") assert.Contains(t, cmds, "task/update") assert.Contains(t, cmds, "task/toggle") } +func TestCommands_CmdPRManage_Good_NoCandidates(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + r := s.cmdPRManage(core.NewOptions()) + assert.True(t, r.OK) +} + // --- CmdExtract Bad/Ugly --- func TestCommands_CmdExtract_Bad_TargetDirAlreadyHasFiles(t *testing.T) { diff --git a/pkg/agentic/review_queue.go b/pkg/agentic/review_queue.go index 2e5fef5..a2b07e7 100644 --- a/pkg/agentic/review_queue.go +++ b/pkg/agentic/review_queue.go @@ -60,6 +60,39 @@ func (s *PrepSubsystem) registerReviewQueueTool(server *mcp.Server) { }, s.reviewQueue) } +// result := c.Command("pr-manage").Run(ctx, core.NewOptions( +// +// core.Option{Key: "limit", Value: 4}, +// +// )) +func (s *PrepSubsystem) cmdPRManage(options core.Options) core.Result { + ctx := s.commandContext() + input := ReviewQueueInput{ + Limit: optionIntValue(options, "limit"), + Reviewer: optionStringValue(options, "reviewer"), + DryRun: optionBoolValue(options, "dry-run"), + LocalOnly: optionBoolValue(options, "local-only"), + } + + _, output, err := s.reviewQueue(ctx, nil, input) + if err != nil { + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + if output.RateLimit != nil && output.RateLimit.Message != "" { + core.Print(nil, "rate limit: %s", output.RateLimit.Message) + } + for _, item := range output.Processed { + core.Print(nil, "%s: %s (%s)", item.Repo, item.Verdict, item.Action) + } + for _, item := range output.Skipped { + core.Print(nil, "skipped: %s", item) + } + + return core.Result{Value: output, OK: true} +} + func (s *PrepSubsystem) reviewQueue(ctx context.Context, _ *mcp.CallToolRequest, input ReviewQueueInput) (*mcp.CallToolResult, ReviewQueueOutput, error) { limit := input.Limit if limit <= 0 {