From b7a102cc3a2f16da972eb86a6582e69dee7138a1 Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 12:15:56 +0000 Subject: [PATCH] feat(agentic): add plan archive CLI command Co-Authored-By: Virgil --- pkg/agentic/commands_plan.go | 30 ++++++++++++++++++++++++++++++ pkg/agentic/commands_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/pkg/agentic/commands_plan.go b/pkg/agentic/commands_plan.go index dd89475..d926ffc 100644 --- a/pkg/agentic/commands_plan.go +++ b/pkg/agentic/commands_plan.go @@ -13,6 +13,7 @@ func (s *PrepSubsystem) registerPlanCommands() { 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}) + c.Command("plan/archive", core.Command{Description: "Archive an implementation plan by slug or ID", Action: s.cmdPlanArchive}) } func (s *PrepSubsystem) cmdPlan(options core.Options) core.Result { @@ -163,3 +164,32 @@ func (s *PrepSubsystem) cmdPlanStatus(options core.Options) core.Result { core.Print(nil, "status: %s", output.Plan.Status) return core.Result{Value: output, OK: true} } + +func (s *PrepSubsystem) cmdPlanArchive(options core.Options) core.Result { + ctx := s.commandContext() + id := optionStringValue(options, "id", "slug", "_arg") + if id == "" { + core.Print(nil, "usage: core-agent plan archive [--reason=\"...\"]") + return core.Result{Value: core.E("agentic.cmdPlanArchive", "slug or id is required", nil), OK: false} + } + + result := s.handlePlanArchive(ctx, core.NewOptions( + core.Option{Key: "slug", Value: id}, + core.Option{Key: "reason", Value: optionStringValue(options, "reason")}, + )) + if !result.OK { + err := commandResultError("agentic.cmdPlanArchive", result) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + output, ok := result.Value.(PlanArchiveOutput) + if !ok { + err := core.E("agentic.cmdPlanArchive", "invalid plan archive output", nil) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + core.Print(nil, "archived: %s", output.Archived) + return core.Result{Value: output, OK: true} +} diff --git a/pkg/agentic/commands_test.go b/pkg/agentic/commands_test.go index d27f77b..4b8ca9c 100644 --- a/pkg/agentic/commands_test.go +++ b/pkg/agentic/commands_test.go @@ -937,6 +937,30 @@ func TestCommands_CmdPlanStatus_Good_GetAndSet(t *testing.T) { assert.Equal(t, "ready", plan.Status) } +func TestCommands_CmdPlanArchive_Good(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + + _, created, err := s.planCreate(context.Background(), nil, PlanCreateInput{ + Title: "Archive Plan", + Objective: "Exercise archive command", + }) + require.NoError(t, err) + + output := captureStdout(t, func() { + r := s.cmdPlanArchive(core.NewOptions( + core.Option{Key: "_arg", Value: created.ID}, + )) + assert.True(t, r.OK) + }) + + assert.Contains(t, output, "archived:") + + plan, err := readPlan(PlansRoot(), created.ID) + require.NoError(t, err) + assert.Equal(t, "archived", plan.Status) + assert.False(t, plan.ArchivedAt.IsZero()) +} + func TestCommands_CmdExtract_Good(t *testing.T) { s, _ := testPrepWithCore(t, nil) target := core.JoinPath(t.TempDir(), "extract-test") @@ -1003,6 +1027,7 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) { assert.Contains(t, cmds, "plan/list") assert.Contains(t, cmds, "plan/show") assert.Contains(t, cmds, "plan/status") + assert.Contains(t, cmds, "plan/archive") assert.Contains(t, cmds, "pr-manage") assert.Contains(t, cmds, "task") assert.Contains(t, cmds, "task/update")