feat(agentic): expose plan check action and tool
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
534df4278a
commit
00f37096fa
6 changed files with 85 additions and 4 deletions
|
|
@ -181,14 +181,23 @@ func (s *PrepSubsystem) cmdPlanCheck(options core.Options) core.Result {
|
|||
return core.Result{Value: core.E("agentic.cmdPlanCheck", "slug is required", nil), OK: false}
|
||||
}
|
||||
|
||||
phaseOrder := optionIntValue(options, "phase", "phase_order")
|
||||
_, output, err := s.planGetCompat(ctx, nil, PlanReadInput{Slug: slug})
|
||||
if err != nil {
|
||||
result := s.handlePlanCheck(ctx, core.NewOptions(
|
||||
core.Option{Key: "slug", Value: slug},
|
||||
core.Option{Key: "phase", Value: optionIntValue(options, "phase", "phase_order")},
|
||||
))
|
||||
if !result.OK {
|
||||
err := commandResultError("agentic.cmdPlanCheck", result)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
check, ok := result.Value.(PlanCheckOutput)
|
||||
if !ok {
|
||||
err := core.E("agentic.cmdPlanCheck", "invalid plan check output", nil)
|
||||
core.Print(nil, "error: %v", err)
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
check := planCheckOutput(output.Plan, phaseOrder)
|
||||
core.Print(nil, "slug: %s", check.Plan.Slug)
|
||||
core.Print(nil, "status: %s", check.Plan.Status)
|
||||
core.Print(nil, "progress: %d/%d (%d%%)", check.Plan.Progress.Completed, check.Plan.Progress.Total, check.Plan.Progress.Percentage)
|
||||
|
|
|
|||
|
|
@ -91,3 +91,37 @@ func TestCommandsPlan_CmdPlanCheck_Ugly_IncompletePhase(t *testing.T) {
|
|||
assert.Equal(t, "Setup", output.PhaseName)
|
||||
assert.Equal(t, []string{"Patch code"}, output.Pending)
|
||||
}
|
||||
|
||||
func TestCommandsPlan_HandlePlanCheck_Good_CompletePlan(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
t.Setenv("CORE_WORKSPACE", dir)
|
||||
|
||||
s := newTestPrep(t)
|
||||
_, created, err := s.planCreate(context.Background(), nil, PlanCreateInput{
|
||||
Title: "Action Check Plan",
|
||||
Description: "Confirm the plan check action reports completion",
|
||||
Phases: []Phase{
|
||||
{
|
||||
Name: "Setup",
|
||||
Tasks: []PlanTask{
|
||||
{ID: "1", Title: "Review RFC", Status: "completed"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
plan, err := readPlan(PlansRoot(), created.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
r := s.handlePlanCheck(context.Background(), core.NewOptions(
|
||||
core.Option{Key: "slug", Value: plan.Slug},
|
||||
))
|
||||
require.True(t, r.OK)
|
||||
|
||||
output, ok := r.Value.(PlanCheckOutput)
|
||||
require.True(t, ok)
|
||||
assert.True(t, output.Success)
|
||||
assert.True(t, output.Complete)
|
||||
assert.Equal(t, plan.Slug, output.Plan.Slug)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,11 @@ func (s *PrepSubsystem) registerPlanTools(server *mcp.Server) {
|
|||
Description: "List plans using the compatibility surface with slug and progress summaries.",
|
||||
}, s.planListCompat)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "plan_check",
|
||||
Description: "Check whether a plan or phase is complete using the compatibility surface.",
|
||||
}, s.planCheck)
|
||||
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "plan_update",
|
||||
Description: "Update a plan using the legacy plain-name MCP alias.",
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ type PlanArchiveOutput struct {
|
|||
Archived string `json:"archived"`
|
||||
}
|
||||
|
||||
// input := agentic.PlanCheckInput{Slug: "my-plan-abc123", Phase: 1}
|
||||
type PlanCheckInput struct {
|
||||
Slug string `json:"slug"`
|
||||
Phase int `json:"phase,omitempty"`
|
||||
}
|
||||
|
||||
// out := agentic.PlanCheckOutput{Success: true, Complete: true}
|
||||
type PlanCheckOutput struct {
|
||||
Success bool `json:"success"`
|
||||
|
|
@ -108,6 +114,18 @@ func (s *PrepSubsystem) handlePlanUpdateStatus(ctx context.Context, options core
|
|||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
// result := c.Action("plan.check").Run(ctx, core.NewOptions(core.Option{Key: "slug", Value: "my-plan-abc123"}))
|
||||
func (s *PrepSubsystem) handlePlanCheck(ctx context.Context, options core.Options) core.Result {
|
||||
_, output, err := s.planCheck(ctx, nil, PlanCheckInput{
|
||||
Slug: optionStringValue(options, "slug", "_arg"),
|
||||
Phase: optionIntValue(options, "phase", "phase_order"),
|
||||
})
|
||||
if err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) planCreateCompat(ctx context.Context, _ *mcp.CallToolRequest, input PlanCreateInput) (*mcp.CallToolResult, PlanCompatibilityCreateOutput, error) {
|
||||
_, created, err := s.planCreate(ctx, nil, input)
|
||||
if err != nil {
|
||||
|
|
@ -186,6 +204,19 @@ func (s *PrepSubsystem) planUpdateStatusCompat(ctx context.Context, _ *mcp.CallT
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) planCheck(ctx context.Context, _ *mcp.CallToolRequest, input PlanCheckInput) (*mcp.CallToolResult, PlanCheckOutput, error) {
|
||||
if input.Slug == "" {
|
||||
return nil, PlanCheckOutput{}, core.E("planCheck", "slug is required", nil)
|
||||
}
|
||||
|
||||
_, output, err := s.planGetCompat(ctx, nil, PlanReadInput{Slug: input.Slug})
|
||||
if err != nil {
|
||||
return nil, PlanCheckOutput{}, err
|
||||
}
|
||||
|
||||
return nil, planCheckOutput(output.Plan, input.Phase), nil
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) planArchiveCompat(ctx context.Context, _ *mcp.CallToolRequest, input PlanDeleteInput) (*mcp.CallToolResult, PlanArchiveOutput, error) {
|
||||
plan, err := archivePlanResult(input, "slug is required", "planArchiveCompat")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
|
|||
c.Action("plan.read", s.handlePlanRead).Description = "Read an implementation plan by ID"
|
||||
c.Action("plan.update", s.handlePlanUpdate).Description = "Update plan status, phases, notes, or agent assignment"
|
||||
c.Action("plan.update_status", s.handlePlanUpdateStatus).Description = "Update an implementation plan lifecycle status by slug"
|
||||
c.Action("plan.check", s.handlePlanCheck).Description = "Check whether a plan or phase is complete"
|
||||
c.Action("plan.archive", s.handlePlanArchive).Description = "Archive an implementation plan by slug"
|
||||
c.Action("plan.delete", s.handlePlanDelete).Description = "Archive an implementation plan by ID"
|
||||
c.Action("plan.list", s.handlePlanList).Description = "List implementation plans with optional filters"
|
||||
|
|
|
|||
|
|
@ -445,6 +445,7 @@ func TestPrep_OnStartup_Good_RegistersPlanActions(t *testing.T) {
|
|||
assert.True(t, c.Action("plan.read").Exists())
|
||||
assert.True(t, c.Action("plan.update").Exists())
|
||||
assert.True(t, c.Action("plan.update_status").Exists())
|
||||
assert.True(t, c.Action("plan.check").Exists())
|
||||
assert.True(t, c.Action("plan.archive").Exists())
|
||||
assert.True(t, c.Action("plan.delete").Exists())
|
||||
assert.True(t, c.Action("plan.list").Exists())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue