feat(agentic): expose plan check action and tool

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 15:28:47 +00:00
parent 534df4278a
commit 00f37096fa
6 changed files with 85 additions and 4 deletions

View file

@ -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)

View file

@ -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)
}

View file

@ -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.",

View file

@ -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 {

View file

@ -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"

View file

@ -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())