feat(agentic): add plan command surface
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
155230fb5b
commit
a009a2827a
3 changed files with 227 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
|||
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})
|
||||
s.registerPlanCommands()
|
||||
}
|
||||
|
||||
// ctx := s.commandContext()
|
||||
|
|
|
|||
165
pkg/agentic/commands_plan.go
Normal file
165
pkg/agentic/commands_plan.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package agentic
|
||||
|
||||
import (
|
||||
core "dappco.re/go/core"
|
||||
)
|
||||
|
||||
func (s *PrepSubsystem) registerPlanCommands() {
|
||||
c := s.Core()
|
||||
c.Command("plan", core.Command{Description: "Manage implementation plans", Action: s.cmdPlan})
|
||||
c.Command("plan/create", core.Command{Description: "Create an implementation plan or create one from a template", Action: s.cmdPlanCreate})
|
||||
c.Command("plan/list", core.Command{Description: "List implementation plans", Action: s.cmdPlanList})
|
||||
c.Command("plan/show", core.Command{Description: "Show an implementation plan", Action: s.cmdPlanShow})
|
||||
c.Command("plan/status", core.Command{Description: "Read or update an implementation plan status", Action: s.cmdPlanStatus})
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPlan(options core.Options) core.Result {
|
||||
return s.cmdPlanList(options)
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPlanCreate(options core.Options) core.Result {
|
||||
ctx := s.commandContext()
|
||||
slug := optionStringValue(options, "slug", "_arg")
|
||||
title := optionStringValue(options, "title")
|
||||
objective := optionStringValue(options, "objective")
|
||||
description := optionStringValue(options, "description")
|
||||
templateName := templateNameValue(optionStringValue(options, "template"), optionStringValue(options, "template_slug", "template-slug"), optionStringValue(options, "import"))
|
||||
|
||||
if templateName != "" {
|
||||
variables := optionStringMapValue(options, "variables")
|
||||
if variables == nil {
|
||||
variables = map[string]string{}
|
||||
}
|
||||
|
||||
_, output, err := s.templateCreatePlan(ctx, nil, TemplateCreatePlanInput{
|
||||
Template: templateName,
|
||||
Variables: variables,
|
||||
Slug: slug,
|
||||
Title: title,
|
||||
Activate: optionBoolValue(options, "activate"),
|
||||
TemplateSlug: templateName,
|
||||
})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "created: %s", output.Plan.Slug)
|
||||
core.Print(nil, "title: %s", output.Plan.Title)
|
||||
core.Print(nil, "status: %s", output.Plan.Status)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
if title == "" {
|
||||
core.Print(nil, "usage: core-agent plan create <slug> --title=\"My Plan\" [--objective=\"...\"] [--description=\"...\"] [--import=bug-fix] [--activate]")
|
||||
return core.Result{Value: core.E("agentic.cmdPlanCreate", "title is required", nil), OK: false}
|
||||
}
|
||||
|
||||
if objective == "" {
|
||||
objective = description
|
||||
}
|
||||
if objective == "" {
|
||||
objective = title
|
||||
}
|
||||
|
||||
_, output, err := s.planCreate(ctx, nil, PlanCreateInput{
|
||||
Title: title,
|
||||
Slug: slug,
|
||||
Objective: objective,
|
||||
Description: description,
|
||||
Context: optionAnyMapValue(options, "context"),
|
||||
Repo: optionStringValue(options, "repo"),
|
||||
Org: optionStringValue(options, "org"),
|
||||
Phases: planPhasesValue(options, "phases"),
|
||||
Notes: optionStringValue(options, "notes"),
|
||||
})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "created: %s", output.ID)
|
||||
core.Print(nil, "path: %s", output.Path)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPlanList(options core.Options) core.Result {
|
||||
ctx := s.commandContext()
|
||||
_, output, err := s.planList(ctx, nil, PlanListInput{
|
||||
Status: optionStringValue(options, "status"),
|
||||
Repo: optionStringValue(options, "repo"),
|
||||
Limit: optionIntValue(options, "limit"),
|
||||
})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
if output.Count == 0 {
|
||||
core.Print(nil, "no plans")
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
for _, plan := range output.Plans {
|
||||
core.Print(nil, " %-10s %-24s %s", plan.Status, plan.Slug, plan.Title)
|
||||
}
|
||||
core.Print(nil, "%d plan(s)", output.Count)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPlanShow(options core.Options) core.Result {
|
||||
ctx := s.commandContext()
|
||||
slug := optionStringValue(options, "slug", "_arg")
|
||||
if slug == "" {
|
||||
core.Print(nil, "usage: core-agent plan show <slug>")
|
||||
return core.Result{Value: core.E("agentic.cmdPlanShow", "slug is required", nil), OK: false}
|
||||
}
|
||||
|
||||
_, output, err := s.planGetCompat(ctx, nil, PlanReadInput{Slug: slug})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "slug: %s", output.Plan.Slug)
|
||||
core.Print(nil, "title: %s", output.Plan.Title)
|
||||
core.Print(nil, "status: %s", output.Plan.Status)
|
||||
core.Print(nil, "progress: %d/%d (%d%%)", output.Plan.Progress.Completed, output.Plan.Progress.Total, output.Plan.Progress.Percentage)
|
||||
if output.Plan.Description != "" {
|
||||
core.Print(nil, "description: %s", output.Plan.Description)
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdPlanStatus(options core.Options) core.Result {
|
||||
ctx := s.commandContext()
|
||||
slug := optionStringValue(options, "slug", "_arg")
|
||||
if slug == "" {
|
||||
core.Print(nil, "usage: core-agent plan status <slug> [--set=ready]")
|
||||
return core.Result{Value: core.E("agentic.cmdPlanStatus", "slug is required", nil), OK: false}
|
||||
}
|
||||
|
||||
set := optionStringValue(options, "set", "status")
|
||||
if set == "" {
|
||||
_, output, err := s.planGetCompat(ctx, nil, PlanReadInput{Slug: slug})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
core.Print(nil, "slug: %s", output.Plan.Slug)
|
||||
core.Print(nil, "status: %s", output.Plan.Status)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
_, output, err := s.planUpdateStatusCompat(ctx, nil, PlanStatusUpdateInput{Slug: slug, Status: set})
|
||||
if err != nil {
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
core.Print(nil, "slug: %s", output.Plan.Slug)
|
||||
core.Print(nil, "status: %s", output.Plan.Status)
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
|
@ -754,6 +754,62 @@ func TestCommands_CmdGenerate_Good_BriefTemplate(t *testing.T) {
|
|||
assert.Contains(t, output, "content: Template draft")
|
||||
}
|
||||
|
||||
func TestCommands_CmdPlanCreate_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
|
||||
r := s.cmdPlanCreate(core.NewOptions(
|
||||
core.Option{Key: "slug", Value: "migrate-core"},
|
||||
core.Option{Key: "title", Value: "Migrate Core"},
|
||||
core.Option{Key: "objective", Value: "Use Core.Process everywhere"},
|
||||
))
|
||||
|
||||
assert.True(t, r.OK)
|
||||
|
||||
output, ok := r.Value.(PlanCreateOutput)
|
||||
require.True(t, ok)
|
||||
require.NotEmpty(t, output.ID)
|
||||
require.NotEmpty(t, output.Path)
|
||||
assert.True(t, fs.Exists(output.Path))
|
||||
|
||||
plan, err := readPlan(PlansRoot(), output.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Migrate Core", plan.Title)
|
||||
assert.Equal(t, "Use Core.Process everywhere", plan.Objective)
|
||||
assert.Equal(t, "draft", plan.Status)
|
||||
assert.Equal(t, "migrate-core", plan.Slug)
|
||||
}
|
||||
|
||||
func TestCommands_CmdPlanStatus_Good_GetAndSet(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
|
||||
_, created, err := s.planCreate(context.Background(), nil, PlanCreateInput{
|
||||
Title: "Status Plan",
|
||||
Objective: "Exercise plan status management",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
getOutput := captureStdout(t, func() {
|
||||
r := s.cmdPlanStatus(core.NewOptions(core.Option{Key: "_arg", Value: created.ID}))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, getOutput, "status:")
|
||||
assert.Contains(t, getOutput, "draft")
|
||||
|
||||
setOutput := captureStdout(t, func() {
|
||||
r := s.cmdPlanStatus(core.NewOptions(
|
||||
core.Option{Key: "_arg", Value: created.ID},
|
||||
core.Option{Key: "set", Value: "ready"},
|
||||
))
|
||||
assert.True(t, r.OK)
|
||||
})
|
||||
assert.Contains(t, setOutput, "status:")
|
||||
assert.Contains(t, setOutput, "ready")
|
||||
|
||||
plan, err := readPlan(PlansRoot(), created.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "ready", plan.Status)
|
||||
}
|
||||
|
||||
func TestCommands_CmdExtract_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
target := core.JoinPath(t.TempDir(), "extract-test")
|
||||
|
|
@ -814,6 +870,11 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) {
|
|||
assert.Contains(t, cmds, "status")
|
||||
assert.Contains(t, cmds, "prompt")
|
||||
assert.Contains(t, cmds, "extract")
|
||||
assert.Contains(t, cmds, "plan")
|
||||
assert.Contains(t, cmds, "plan/create")
|
||||
assert.Contains(t, cmds, "plan/list")
|
||||
assert.Contains(t, cmds, "plan/show")
|
||||
assert.Contains(t, cmds, "plan/status")
|
||||
}
|
||||
|
||||
// --- CmdExtract Bad/Ugly ---
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue