feat(agentic): add phase cli commands
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
a136c04aa1
commit
652548a60a
5 changed files with 244 additions and 0 deletions
|
|
@ -85,6 +85,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
|||
s.registerPlanCommands()
|
||||
s.registerCommitCommands()
|
||||
s.registerSessionCommands()
|
||||
s.registerPhaseCommands()
|
||||
s.registerTaskCommands()
|
||||
s.registerSprintCommands()
|
||||
s.registerStateCommands()
|
||||
|
|
|
|||
133
pkg/agentic/commands_phase.go
Normal file
133
pkg/agentic/commands_phase.go
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
core "dappco.re/go/core"
|
||||
)
|
||||
|
||||
func (s *PrepSubsystem) registerPhaseCommands() {
|
||||
c := s.Core()
|
||||
c.Command("phase", core.Command{Description: "Manage plan phases", Action: s.cmdPhase})
|
||||
c.Command("agentic:phase", core.Command{Description: "Manage plan phases", Action: s.cmdPhase})
|
||||
c.Command("phase/get", core.Command{Description: "Read a plan phase by slug and order", Action: s.cmdPhaseGet})
|
||||
c.Command("agentic:phase/get", core.Command{Description: "Read a plan phase by slug and order", Action: s.cmdPhaseGet})
|
||||
c.Command("phase/update_status", core.Command{Description: "Update a plan phase status by slug and order", Action: s.cmdPhaseUpdateStatus})
|
||||
c.Command("agentic:phase/update_status", core.Command{Description: "Update a plan phase status by slug and order", Action: s.cmdPhaseUpdateStatus})
|
||||
c.Command("phase/update-status", core.Command{Description: "Update a plan phase status by slug and order", Action: s.cmdPhaseUpdateStatus})
|
||||
c.Command("agentic:phase/update-status", core.Command{Description: "Update a plan phase status by slug and order", Action: s.cmdPhaseUpdateStatus})
|
||||
c.Command("phase/add_checkpoint", core.Command{Description: "Append a checkpoint note to a plan phase", Action: s.cmdPhaseAddCheckpoint})
|
||||
c.Command("agentic:phase/add_checkpoint", core.Command{Description: "Append a checkpoint note to a plan phase", Action: s.cmdPhaseAddCheckpoint})
|
||||
c.Command("phase/add-checkpoint", core.Command{Description: "Append a checkpoint note to a plan phase", Action: s.cmdPhaseAddCheckpoint})
|
||||
c.Command("agentic:phase/add-checkpoint", core.Command{Description: "Append a checkpoint note to a plan phase", Action: s.cmdPhaseAddCheckpoint})
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPhase(options core.Options) core.Result {
|
||||
switch action := optionStringValue(options, "action"); action {
|
||||
case "get":
|
||||
return s.cmdPhaseGet(options)
|
||||
case "update_status", "update-status", "update":
|
||||
return s.cmdPhaseUpdateStatus(options)
|
||||
case "add_checkpoint", "add-checkpoint", "checkpoint":
|
||||
return s.cmdPhaseAddCheckpoint(options)
|
||||
case "":
|
||||
core.Print(nil, "usage: core-agent phase get <plan> --phase=1")
|
||||
core.Print(nil, " core-agent phase update-status <plan> --phase=1 --status=completed [--reason=\"...\"]")
|
||||
core.Print(nil, " core-agent phase add-checkpoint <plan> --phase=1 --note=\"Build passes\" [--context='{\"build\":\"ok\"}']")
|
||||
return core.Result{OK: true}
|
||||
default:
|
||||
core.Print(nil, "usage: core-agent phase get <plan> --phase=1")
|
||||
core.Print(nil, " core-agent phase update-status <plan> --phase=1 --status=completed [--reason=\"...\"]")
|
||||
core.Print(nil, " core-agent phase add-checkpoint <plan> --phase=1 --note=\"Build passes\" [--context='{\"build\":\"ok\"}']")
|
||||
return core.Result{Value: core.E("agentic.cmdPhase", core.Concat("unknown phase command: ", action), nil), OK: false}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPhaseGet(options core.Options) core.Result {
|
||||
result := s.handlePhaseGet(s.commandContext(), core.NewOptions(
|
||||
core.Option{Key: "plan_slug", Value: optionStringValue(options, "plan_slug", "plan", "slug", "_arg")},
|
||||
core.Option{Key: "phase_order", Value: optionIntValue(options, "phase_order", "phase")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdPhaseGet", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(PhaseOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdPhaseGet", "invalid phase get output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "phase: %d", output.Phase.Number)
|
||||
core.Print(nil, "name: %s", output.Phase.Name)
|
||||
core.Print(nil, "status: %s", output.Phase.Status)
|
||||
if output.Phase.Description != "" {
|
||||
core.Print(nil, "desc: %s", output.Phase.Description)
|
||||
}
|
||||
if output.Phase.Notes != "" {
|
||||
core.Print(nil, "notes: %s", output.Phase.Notes)
|
||||
}
|
||||
if len(output.Phase.Tasks) > 0 {
|
||||
core.Print(nil, "tasks: %d", len(output.Phase.Tasks))
|
||||
}
|
||||
if len(output.Phase.Checkpoints) > 0 {
|
||||
core.Print(nil, "checkpoints: %d", len(output.Phase.Checkpoints))
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPhaseUpdateStatus(options core.Options) core.Result {
|
||||
result := s.handlePhaseUpdateStatus(s.commandContext(), core.NewOptions(
|
||||
core.Option{Key: "plan_slug", Value: optionStringValue(options, "plan_slug", "plan", "slug", "_arg")},
|
||||
core.Option{Key: "phase_order", Value: optionIntValue(options, "phase_order", "phase")},
|
||||
core.Option{Key: "status", Value: optionStringValue(options, "status")},
|
||||
core.Option{Key: "reason", Value: optionStringValue(options, "reason")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdPhaseUpdateStatus", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(PhaseOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdPhaseUpdateStatus", "invalid phase update output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "phase: %d", output.Phase.Number)
|
||||
core.Print(nil, "name: %s", output.Phase.Name)
|
||||
core.Print(nil, "status: %s", output.Phase.Status)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPhaseAddCheckpoint(options core.Options) core.Result {
|
||||
result := s.handlePhaseAddCheckpoint(s.commandContext(), core.NewOptions(
|
||||
core.Option{Key: "plan_slug", Value: optionStringValue(options, "plan_slug", "plan", "slug", "_arg")},
|
||||
core.Option{Key: "phase_order", Value: optionIntValue(options, "phase_order", "phase")},
|
||||
core.Option{Key: "note", Value: optionStringValue(options, "note")},
|
||||
core.Option{Key: "context", Value: optionAnyMapValue(options, "context")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdPhaseAddCheckpoint", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
output, ok := result.Value.(PhaseOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdPhaseAddCheckpoint", "invalid phase checkpoint output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "phase: %d", output.Phase.Number)
|
||||
core.Print(nil, "name: %s", output.Phase.Name)
|
||||
core.Print(nil, "status: %s", output.Phase.Status)
|
||||
core.Print(nil, "checkpoints: %d", len(output.Phase.Checkpoints))
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
94
pkg/agentic/commands_phase_test.go
Normal file
94
pkg/agentic/commands_phase_test.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommandsPhase_RegisterPhaseCommands_Good_AllRegistered(t *testing.T) {
|
||||
s, c := testPrepWithCore(t, nil)
|
||||
s.registerPhaseCommands()
|
||||
|
||||
cmds := c.Commands()
|
||||
assert.Contains(t, cmds, "phase")
|
||||
assert.Contains(t, cmds, "agentic:phase")
|
||||
assert.Contains(t, cmds, "phase/get")
|
||||
assert.Contains(t, cmds, "agentic:phase/get")
|
||||
assert.Contains(t, cmds, "phase/update_status")
|
||||
assert.Contains(t, cmds, "agentic:phase/update_status")
|
||||
assert.Contains(t, cmds, "phase/update-status")
|
||||
assert.Contains(t, cmds, "agentic:phase/update-status")
|
||||
assert.Contains(t, cmds, "phase/add_checkpoint")
|
||||
assert.Contains(t, cmds, "agentic:phase/add_checkpoint")
|
||||
assert.Contains(t, cmds, "phase/add-checkpoint")
|
||||
assert.Contains(t, cmds, "agentic:phase/add-checkpoint")
|
||||
}
|
||||
|
||||
func TestCommandsPhase_CmdPhase_Good_GetUpdateCheckpoint(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
_, _, err := s.planCreate(context.Background(), nil, PlanCreateInput{
|
||||
Title: "Phase command plan",
|
||||
Slug: "phase-command-plan",
|
||||
Objective: "Exercise phase commands",
|
||||
Phases: []Phase{
|
||||
{Number: 1, Name: "Setup", Status: "pending"},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
output := captureStdout(t, func() {
|
||||
r := s.cmdPhase(core.NewOptions(
|
||||
core.Option{Key: "action", Value: "get"},
|
||||
core.Option{Key: "_arg", Value: "phase-command-plan"},
|
||||
core.Option{Key: "phase", Value: 1},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, output, "phase: 1")
|
||||
assert.Contains(t, output, "name: Setup")
|
||||
assert.Contains(t, output, "status: pending")
|
||||
|
||||
output = captureStdout(t, func() {
|
||||
r := s.cmdPhase(core.NewOptions(
|
||||
core.Option{Key: "action", Value: "update-status"},
|
||||
core.Option{Key: "_arg", Value: "phase-command-plan"},
|
||||
core.Option{Key: "phase", Value: 1},
|
||||
core.Option{Key: "status", Value: "completed"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, output, "status: completed")
|
||||
|
||||
output = captureStdout(t, func() {
|
||||
r := s.cmdPhase(core.NewOptions(
|
||||
core.Option{Key: "action", Value: "add-checkpoint"},
|
||||
core.Option{Key: "_arg", Value: "phase-command-plan"},
|
||||
core.Option{Key: "phase", Value: 1},
|
||||
core.Option{Key: "note", Value: "Build passes"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, output, "checkpoints: 1")
|
||||
}
|
||||
|
||||
func TestCommandsPhase_CmdPhase_Bad_MissingActionStillShowsUsage(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
output := captureStdout(t, func() {
|
||||
r := s.cmdPhase(core.NewOptions())
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, output, "core-agent phase get")
|
||||
}
|
||||
|
||||
func TestCommandsPhase_CmdPhase_Ugly_UnknownAction(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
r := s.cmdPhase(core.NewOptions(core.Option{Key: "action", Value: "explode"}))
|
||||
require.False(t, r.OK)
|
||||
assert.Contains(t, r.Value.(error).Error(), "unknown phase command")
|
||||
}
|
||||
|
|
@ -1614,6 +1614,14 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
|
|||
assert.Contains(t, cmds, "review-queue")
|
||||
assert.Contains(t, cmds, "agentic:review-queue")
|
||||
assert.Contains(t, cmds, "task")
|
||||
assert.Contains(t, cmds, "phase")
|
||||
assert.Contains(t, cmds, "agentic:phase")
|
||||
assert.Contains(t, cmds, "phase/get")
|
||||
assert.Contains(t, cmds, "agentic:phase/get")
|
||||
assert.Contains(t, cmds, "phase/update_status")
|
||||
assert.Contains(t, cmds, "agentic:phase/update_status")
|
||||
assert.Contains(t, cmds, "phase/add_checkpoint")
|
||||
assert.Contains(t, cmds, "agentic:phase/add_checkpoint")
|
||||
assert.Contains(t, cmds, "task/update")
|
||||
assert.Contains(t, cmds, "task/toggle")
|
||||
assert.Contains(t, cmds, "sprint")
|
||||
|
|
|
|||
|
|
@ -754,6 +754,14 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
|
|||
assert.Contains(t, c.Commands(), "task/create")
|
||||
assert.Contains(t, c.Commands(), "task/update")
|
||||
assert.Contains(t, c.Commands(), "task/toggle")
|
||||
assert.Contains(t, c.Commands(), "phase")
|
||||
assert.Contains(t, c.Commands(), "agentic:phase")
|
||||
assert.Contains(t, c.Commands(), "phase/get")
|
||||
assert.Contains(t, c.Commands(), "agentic:phase/get")
|
||||
assert.Contains(t, c.Commands(), "phase/update_status")
|
||||
assert.Contains(t, c.Commands(), "agentic:phase/update_status")
|
||||
assert.Contains(t, c.Commands(), "phase/add_checkpoint")
|
||||
assert.Contains(t, c.Commands(), "agentic:phase/add_checkpoint")
|
||||
assert.Contains(t, c.Commands(), "state")
|
||||
assert.Contains(t, c.Commands(), "state/set")
|
||||
assert.Contains(t, c.Commands(), "state/get")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue