feat(agentic): add issue update and archive CLI commands
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
524810cbda
commit
3883466cc4
2 changed files with 147 additions and 0 deletions
|
|
@ -100,6 +100,8 @@ func (s *PrepSubsystem) registerForgeCommands() {
|
|||
c.Command("issue/list", core.Command{Description: "List Forge issues for a repo", Action: s.cmdIssueList})
|
||||
c.Command("issue/comment", core.Command{Description: "Comment on a Forge issue", Action: s.cmdIssueComment})
|
||||
c.Command("issue/create", core.Command{Description: "Create a Forge issue", Action: s.cmdIssueCreate})
|
||||
c.Command("issue/update", core.Command{Description: "Update a tracked platform issue", Action: s.cmdIssueUpdate})
|
||||
c.Command("issue/archive", core.Command{Description: "Archive a tracked platform issue", Action: s.cmdIssueArchive})
|
||||
c.Command("pr/get", core.Command{Description: "Get a Forge PR", Action: s.cmdPRGet})
|
||||
c.Command("pr/list", core.Command{Description: "List Forge PRs for a repo", Action: s.cmdPRList})
|
||||
c.Command("pr/merge", core.Command{Description: "Merge a Forge PR", Action: s.cmdPRMerge})
|
||||
|
|
@ -228,6 +230,72 @@ func (s *PrepSubsystem) cmdIssueCreate(options core.Options) core.Result {
|
|||
return core.Result{Value: issue.Index, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdIssueUpdate(options core.Options) core.Result {
|
||||
ctx := context.Background()
|
||||
id := optionStringValue(options, "id", "slug", "_arg")
|
||||
if id == "" {
|
||||
core.Print(nil, "usage: core-agent issue update <slug> [--title=\"...\"] [--description=\"...\"] [--type=bug] [--status=open] [--priority=high] [--labels=a,b] [--sprint-id=7|--sprint-slug=phase-1]")
|
||||
return core.Result{Value: core.E("agentic.cmdIssueUpdate", "slug or id is required", nil), OK: false}
|
||||
}
|
||||
|
||||
result := s.handleIssueRecordUpdate(ctx, core.NewOptions(
|
||||
core.Option{Key: "slug", Value: id},
|
||||
core.Option{Key: "title", Value: options.String("title")},
|
||||
core.Option{Key: "description", Value: options.String("description")},
|
||||
core.Option{Key: "type", Value: options.String("type")},
|
||||
core.Option{Key: "status", Value: options.String("status")},
|
||||
core.Option{Key: "priority", Value: options.String("priority")},
|
||||
core.Option{Key: "labels", Value: options.String("labels")},
|
||||
core.Option{Key: "sprint_id", Value: options.String("sprint-id")},
|
||||
core.Option{Key: "sprint_slug", Value: options.String("sprint-slug")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdIssueUpdate", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(IssueOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdIssueUpdate", "invalid issue update output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "%s", output.Issue.Slug)
|
||||
core.Print(nil, " status: %s", output.Issue.Status)
|
||||
core.Print(nil, " title: %s", output.Issue.Title)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdIssueArchive(options core.Options) core.Result {
|
||||
ctx := context.Background()
|
||||
id := optionStringValue(options, "id", "slug", "_arg")
|
||||
if id == "" {
|
||||
core.Print(nil, "usage: core-agent issue archive <slug>")
|
||||
return core.Result{Value: core.E("agentic.cmdIssueArchive", "slug or id is required", nil), OK: false}
|
||||
}
|
||||
|
||||
result := s.handleIssueRecordArchive(ctx, core.NewOptions(
|
||||
core.Option{Key: "slug", Value: id},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdIssueArchive", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(IssueArchiveOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdIssueArchive", "invalid issue archive output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "archived: %s", output.Archived)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPRGet(options core.Options) core.Result {
|
||||
ctx := context.Background()
|
||||
org, repo, num := parseForgeArgs(options)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
core "dappco.re/go/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// --- parseForgeArgs ---
|
||||
|
|
@ -145,6 +146,82 @@ func TestCommandsforge_CmdIssueCreate_Ugly(t *testing.T) {
|
|||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
func TestCommandsforge_CmdIssueUpdate_Good(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/v1/issues/fix-auth", r.URL.Path)
|
||||
require.Equal(t, http.MethodPatch, r.Method)
|
||||
|
||||
bodyResult := core.ReadAll(r.Body)
|
||||
require.True(t, bodyResult.OK)
|
||||
|
||||
var payload map[string]any
|
||||
parseResult := core.JSONUnmarshalString(bodyResult.Value.(string), &payload)
|
||||
require.True(t, parseResult.OK)
|
||||
require.Equal(t, "Fix auth middleware", payload["title"])
|
||||
require.Equal(t, "in_progress", payload["status"])
|
||||
|
||||
_, _ = w.Write([]byte(`{"data":{"issue":{"slug":"fix-auth","title":"Fix auth middleware","status":"in_progress","priority":"high","labels":["auth","backend"]}}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
subsystem := testPrepWithPlatformServer(t, server, "secret-token")
|
||||
result := subsystem.cmdIssueUpdate(core.NewOptions(
|
||||
core.Option{Key: "_arg", Value: "fix-auth"},
|
||||
core.Option{Key: "title", Value: "Fix auth middleware"},
|
||||
core.Option{Key: "status", Value: "in_progress"},
|
||||
core.Option{Key: "priority", Value: "high"},
|
||||
core.Option{Key: "labels", Value: "auth,backend"},
|
||||
))
|
||||
require.True(t, result.OK)
|
||||
|
||||
output, ok := result.Value.(IssueOutput)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "fix-auth", output.Issue.Slug)
|
||||
assert.Equal(t, "in_progress", output.Issue.Status)
|
||||
assert.Equal(t, []string{"auth", "backend"}, output.Issue.Labels)
|
||||
}
|
||||
|
||||
func TestCommandsforge_CmdIssueUpdate_Bad_MissingSlug(t *testing.T) {
|
||||
subsystem := testPrepWithPlatformServer(t, nil, "secret-token")
|
||||
result := subsystem.cmdIssueUpdate(core.NewOptions())
|
||||
assert.False(t, result.OK)
|
||||
assert.EqualError(t, result.Value.(error), "agentic.cmdIssueUpdate: slug or id is required")
|
||||
}
|
||||
|
||||
func TestCommandsforge_CmdIssueArchive_Good(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/v1/issues/fix-auth", r.URL.Path)
|
||||
require.Equal(t, http.MethodDelete, r.Method)
|
||||
|
||||
_, _ = w.Write([]byte(`{"data":{"result":{"slug":"fix-auth","success":true}}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
subsystem := testPrepWithPlatformServer(t, server, "secret-token")
|
||||
result := subsystem.cmdIssueArchive(core.NewOptions(
|
||||
core.Option{Key: "slug", Value: "fix-auth"},
|
||||
))
|
||||
require.True(t, result.OK)
|
||||
|
||||
output, ok := result.Value.(IssueArchiveOutput)
|
||||
require.True(t, ok)
|
||||
assert.True(t, output.Success)
|
||||
assert.Equal(t, "fix-auth", output.Archived)
|
||||
}
|
||||
|
||||
func TestCommandsforge_CmdIssueArchive_Ugly_ServerError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
subsystem := testPrepWithPlatformServer(t, server, "secret-token")
|
||||
result := subsystem.cmdIssueArchive(core.NewOptions(
|
||||
core.Option{Key: "_arg", Value: "fix-auth"},
|
||||
))
|
||||
assert.False(t, result.OK)
|
||||
}
|
||||
|
||||
func TestCommandsforge_CmdPRGet_Ugly(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) }))
|
||||
t.Cleanup(srv.Close)
|
||||
|
|
@ -244,4 +321,6 @@ func TestCommandsforge_RegisterForgeCommands_Good_RepoSyncRegistered(t *testing.
|
|||
s, c := testPrepWithCore(t, nil)
|
||||
s.registerForgeCommands()
|
||||
assert.Contains(t, c.Commands(), "repo/sync")
|
||||
assert.Contains(t, c.Commands(), "issue/update")
|
||||
assert.Contains(t, c.Commands(), "issue/archive")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue