diff --git a/pkg/agentic/commands_plan.go b/pkg/agentic/commands_plan.go index b6f552f..9047d0e 100644 --- a/pkg/agentic/commands_plan.go +++ b/pkg/agentic/commands_plan.go @@ -10,6 +10,8 @@ func (s *PrepSubsystem) registerPlanCommands() { c := s.Core() c.Command("plan", core.Command{Description: "Manage implementation plans", Action: s.cmdPlan}) c.Command("agentic:plan", core.Command{Description: "Manage implementation plans", Action: s.cmdPlan}) + c.Command("plan/templates", core.Command{Description: "List available plan templates", Action: s.cmdPlanTemplates}) + c.Command("agentic:plan/templates", core.Command{Description: "List available plan templates", Action: s.cmdPlanTemplates}) c.Command("plan/create", core.Command{Description: "Create an implementation plan or create one from a template", Action: s.cmdPlanCreate}) c.Command("agentic:plan/create", core.Command{Description: "Create an implementation plan or create one from a template", Action: s.cmdPlanCreate}) c.Command("plan/from-issue", core.Command{Description: "Create an implementation plan from a tracked issue", Action: s.cmdPlanFromIssue}) @@ -37,9 +39,49 @@ func (s *PrepSubsystem) registerPlanCommands() { } func (s *PrepSubsystem) cmdPlan(options core.Options) core.Result { + if action := optionStringValue(options, "action", "_arg"); action == "templates" { + return s.cmdPlanTemplates(options) + } + return s.cmdPlanList(options) } +func (s *PrepSubsystem) cmdPlanTemplates(options core.Options) core.Result { + ctx := s.commandContext() + result := s.handleTemplateList(ctx, core.NewOptions( + core.Option{Key: "category", Value: optionStringValue(options, "category")}, + )) + if !result.OK { + err := commandResultError("agentic.cmdPlanTemplates", result) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + output, ok := result.Value.(TemplateListOutput) + if !ok { + err := core.E("agentic.cmdPlanTemplates", "invalid template list output", nil) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + if output.Total == 0 { + core.Print(nil, "no templates") + return core.Result{Value: output, OK: true} + } + + for _, template := range output.Templates { + core.Print(nil, " %-24s %-24s %d phase(s)", template.Slug, template.Name, template.PhasesCount) + if template.Category != "" { + core.Print(nil, " category: %s", template.Category) + } + if len(template.Variables) > 0 { + core.Print(nil, " variables: %d", len(template.Variables)) + } + } + core.Print(nil, "%d template(s)", output.Total) + return core.Result{Value: output, OK: true} +} + func (s *PrepSubsystem) cmdPlanCreate(options core.Options) core.Result { ctx := s.commandContext() slug := optionStringValue(options, "slug", "_arg") diff --git a/pkg/agentic/commands_plan_test.go b/pkg/agentic/commands_plan_test.go index b25e2db..38a11fb 100644 --- a/pkg/agentic/commands_plan_test.go +++ b/pkg/agentic/commands_plan_test.go @@ -164,6 +164,37 @@ func TestCommandsPlan_HandlePlanCheck_Good_CompletePlan(t *testing.T) { assert.Equal(t, plan.Slug, output.Plan.Slug) } +func TestCommandsPlan_CmdPlanTemplates_Good(t *testing.T) { + s := testPrepWithPlatformServer(t, nil, "") + + r := s.cmdPlanTemplates(core.NewOptions( + core.Option{Key: "category", Value: "development"}, + )) + + require.True(t, r.OK) + + output, ok := r.Value.(TemplateListOutput) + require.True(t, ok) + assert.True(t, output.Success) + assert.NotZero(t, output.Total) +} + +func TestCommandsPlan_CmdPlanTemplates_Ugly_NoMatchingCategory(t *testing.T) { + s := testPrepWithPlatformServer(t, nil, "") + + r := s.cmdPlanTemplates(core.NewOptions( + core.Option{Key: "category", Value: "does-not-exist"}, + )) + + require.True(t, r.OK) + + output, ok := r.Value.(TemplateListOutput) + require.True(t, ok) + assert.True(t, output.Success) + assert.Zero(t, output.Total) + assert.Empty(t, output.Templates) +} + func TestCommandsPlan_RegisterPlanCommands_Good_SpecAliasRegistered(t *testing.T) { c := core.New(core.WithOption("name", "test")) s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{})} @@ -172,6 +203,8 @@ func TestCommandsPlan_RegisterPlanCommands_Good_SpecAliasRegistered(t *testing.T assert.Contains(t, c.Commands(), "agentic:plan") assert.Contains(t, c.Commands(), "plan") + assert.Contains(t, c.Commands(), "agentic:plan/templates") + assert.Contains(t, c.Commands(), "plan/templates") assert.Contains(t, c.Commands(), "agentic:plan/create") assert.Contains(t, c.Commands(), "agentic:plan/get") assert.Contains(t, c.Commands(), "plan/get")