diff --git a/pkg/agentic/commands_plan.go b/pkg/agentic/commands_plan.go index cd8a770..1f8858b 100644 --- a/pkg/agentic/commands_plan.go +++ b/pkg/agentic/commands_plan.go @@ -130,6 +130,12 @@ func (s *PrepSubsystem) cmdPlanShow(options core.Options) core.Result { 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.TemplateVersion.Slug != "" { + core.Print(nil, "template: %s v%d", output.Plan.TemplateVersion.Slug, output.Plan.TemplateVersion.Version) + if output.Plan.TemplateVersion.ContentHash != "" { + core.Print(nil, "template id: %s", output.Plan.TemplateVersion.ContentHash) + } + } if output.Plan.Description != "" { core.Print(nil, "description: %s", output.Plan.Description) } diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index e74feb8..d11a821 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -13,21 +13,22 @@ import ( // plan := &Plan{ID: "id-1-a3f2b1", Title: "Migrate Core", Status: "draft", Objective: "Replace raw process calls with Core.Process()"} // r := writePlanResult(PlansRoot(), plan) type Plan struct { - ID string `json:"id"` - Slug string `json:"slug,omitempty"` - Title string `json:"title"` - Status string `json:"status"` - Repo string `json:"repo,omitempty"` - Org string `json:"org,omitempty"` - Objective string `json:"objective"` - Description string `json:"description,omitempty"` - Context map[string]any `json:"context,omitempty"` - Phases []Phase `json:"phases,omitempty"` - Notes string `json:"notes,omitempty"` - Agent string `json:"agent,omitempty"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ArchivedAt time.Time `json:"archived_at,omitempty"` + ID string `json:"id"` + Slug string `json:"slug,omitempty"` + Title string `json:"title"` + Status string `json:"status"` + Repo string `json:"repo,omitempty"` + Org string `json:"org,omitempty"` + Objective string `json:"objective"` + Description string `json:"description,omitempty"` + Context map[string]any `json:"context,omitempty"` + TemplateVersion PlanTemplateVersion `json:"template_version,omitempty"` + Phases []Phase `json:"phases,omitempty"` + Notes string `json:"notes,omitempty"` + Agent string `json:"agent,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ArchivedAt time.Time `json:"archived_at,omitempty"` } // phase := agentic.Phase{Number: 1, Name: "Migrate strings", Status: "in_progress"} @@ -63,15 +64,16 @@ type PhaseCheckpoint struct { } type PlanCreateInput struct { - Title string `json:"title"` - Slug string `json:"slug,omitempty"` - Objective string `json:"objective,omitempty"` - Description string `json:"description,omitempty"` - Context map[string]any `json:"context,omitempty"` - Repo string `json:"repo,omitempty"` - Org string `json:"org,omitempty"` - Phases []Phase `json:"phases,omitempty"` - Notes string `json:"notes,omitempty"` + Title string `json:"title"` + Slug string `json:"slug,omitempty"` + Objective string `json:"objective,omitempty"` + Description string `json:"description,omitempty"` + Context map[string]any `json:"context,omitempty"` + TemplateVersion PlanTemplateVersion `json:"template_version,omitempty"` + Repo string `json:"repo,omitempty"` + Org string `json:"org,omitempty"` + Phases []Phase `json:"phases,omitempty"` + Notes string `json:"notes,omitempty"` } type PlanCreateOutput struct { @@ -305,19 +307,20 @@ func (s *PrepSubsystem) planCreate(_ context.Context, _ *mcp.CallToolRequest, in id := core.ID() plan := Plan{ - ID: id, - Slug: planSlugValue(input.Slug, input.Title, id), - Title: input.Title, - Status: "draft", - Repo: input.Repo, - Org: input.Org, - Objective: objective, - Description: description, - Context: input.Context, - Phases: input.Phases, - Notes: input.Notes, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + ID: id, + Slug: planSlugValue(input.Slug, input.Title, id), + Title: input.Title, + Status: "draft", + Repo: input.Repo, + Org: input.Org, + Objective: objective, + Description: description, + Context: input.Context, + TemplateVersion: input.TemplateVersion, + Phases: input.Phases, + Notes: input.Notes, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } plan = normalisePlan(plan) diff --git a/pkg/agentic/plan_compat.go b/pkg/agentic/plan_compat.go index fb54dee..2d1a066 100644 --- a/pkg/agentic/plan_compat.go +++ b/pkg/agentic/plan_compat.go @@ -46,13 +46,14 @@ type PlanCompatibilityListOutput struct { // view := agentic.PlanCompatibilityView{Slug: "my-plan-abc123", Title: "My Plan", Status: "active"} type PlanCompatibilityView struct { - Slug string `json:"slug"` - Title string `json:"title"` - Description string `json:"description,omitempty"` - Status string `json:"status"` - Progress PlanProgress `json:"progress"` - Phases []Phase `json:"phases,omitempty"` - Context map[string]any `json:"context,omitempty"` + Slug string `json:"slug"` + Title string `json:"title"` + Description string `json:"description,omitempty"` + Status string `json:"status"` + Progress PlanProgress `json:"progress"` + TemplateVersion PlanTemplateVersion `json:"template_version,omitempty"` + Phases []Phase `json:"phases,omitempty"` + Context map[string]any `json:"context,omitempty"` } // input := agentic.PlanStatusUpdateInput{Slug: "my-plan-abc123", Status: "active"} @@ -208,13 +209,14 @@ func planCompatibilitySummary(plan Plan) PlanCompatibilitySummary { func planCompatibilityView(plan Plan) PlanCompatibilityView { return PlanCompatibilityView{ - Slug: plan.Slug, - Title: plan.Title, - Description: plan.Description, - Status: planCompatibilityOutputStatus(plan.Status), - Progress: planProgress(plan), - Phases: plan.Phases, - Context: plan.Context, + Slug: plan.Slug, + Title: plan.Title, + Description: plan.Description, + Status: planCompatibilityOutputStatus(plan.Status), + Progress: planProgress(plan), + TemplateVersion: plan.TemplateVersion, + Phases: plan.Phases, + Context: plan.Context, } } diff --git a/pkg/agentic/template.go b/pkg/agentic/template.go index 922c5bf..d6a7df9 100644 --- a/pkg/agentic/template.go +++ b/pkg/agentic/template.go @@ -235,13 +235,14 @@ func (s *PrepSubsystem) templateCreatePlan(ctx context.Context, _ *mcp.CallToolR } _, created, err := s.planCreate(ctx, nil, PlanCreateInput{ - Title: title, - Slug: input.Slug, - Objective: definition.Description, - Description: definition.Description, - Context: contextData, - Phases: templatePlanPhases(definition), - Notes: templateGuidelinesNote(definition.Guidelines), + Title: title, + Slug: input.Slug, + Objective: definition.Description, + Description: definition.Description, + Context: contextData, + TemplateVersion: version, + Phases: templatePlanPhases(definition), + Notes: templateGuidelinesNote(definition.Guidelines), }) if err != nil { return nil, TemplateCreatePlanOutput{}, err diff --git a/pkg/agentic/template_test.go b/pkg/agentic/template_test.go index 3f14134..df3ab40 100644 --- a/pkg/agentic/template_test.go +++ b/pkg/agentic/template_test.go @@ -111,6 +111,9 @@ func TestTemplate_HandleTemplateCreatePlan_Good(t *testing.T) { require.NoError(t, err) assert.Equal(t, "Authentication Rollout", plan.Title) assert.Equal(t, "in_progress", plan.Status) + assert.Equal(t, 1, plan.TemplateVersion.Version) + assert.Equal(t, "new-feature", plan.TemplateVersion.Slug) + assert.NotEmpty(t, plan.TemplateVersion.ContentHash) require.NotEmpty(t, plan.Phases) require.NotEmpty(t, plan.Phases[0].Tasks) assert.Equal(t, "pending", plan.Phases[0].Tasks[0].Status) @@ -137,6 +140,8 @@ func TestTemplate_HandleTemplateCreatePlan_Good_NoVariables(t *testing.T) { require.NoError(t, err) assert.Equal(t, "api-consistency", stringValue(plan.Context["template"])) assert.Empty(t, plan.Context["variables"]) + assert.Equal(t, 1, plan.TemplateVersion.Version) + assert.Equal(t, "api-consistency", plan.TemplateVersion.Slug) } func TestTemplate_HandleTemplateCreatePlan_Bad(t *testing.T) {