From 52a431867a0e21d8f10c89b3395d50f298772e58 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 05:39:01 +0000 Subject: [PATCH] feat(agentic): expose content schema generation on CLI Co-Authored-By: Virgil --- pkg/agentic/commands.go | 41 ++++++++++++++++++++++++++++++++++++ pkg/agentic/commands_test.go | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index 17b4edc..a28200f 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -43,6 +43,8 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) { c.Command("agentic:generate", core.Command{Description: "Generate content from a prompt using the platform content pipeline", Action: s.cmdGenerate}) c.Command("content/generate", core.Command{Description: "Generate content from a prompt using the platform content pipeline", Action: s.cmdGenerate}) c.Command("agentic:content/generate", core.Command{Description: "Generate content from a prompt using the platform content pipeline", Action: s.cmdGenerate}) + c.Command("content/schema/generate", core.Command{Description: "Generate SEO schema JSON-LD for article, FAQ, or how-to content", Action: s.cmdContentSchemaGenerate}) + c.Command("agentic:content/schema/generate", core.Command{Description: "Generate SEO schema JSON-LD for article, FAQ, or how-to content", Action: s.cmdContentSchemaGenerate}) c.Command("complete", core.Command{Description: "Run the completion pipeline (QA → PR → Verify → Commit → Ingest → Poke)", Action: s.cmdComplete}) c.Command("agentic:complete", core.Command{Description: "Run the completion pipeline (QA → PR → Verify → Commit → Ingest → Poke)", Action: s.cmdComplete}) c.Command("scan", core.Command{Description: "Scan Forge repos for actionable issues", Action: s.cmdScan}) @@ -417,6 +419,45 @@ func (s *PrepSubsystem) cmdGenerate(options core.Options) core.Result { return core.Result{OK: true} } +func (s *PrepSubsystem) cmdContentSchemaGenerate(options core.Options) core.Result { + schemaType := optionStringValue(options, "schema_type", "schema-type", "type", "kind") + title := optionStringValue(options, "title", "headline") + if schemaType == "" || title == "" { + core.Print(nil, "usage: core-agent content schema generate --type=howto --title=\"Set up the workspace\" [--description=\"...\"] [--url=\"https://example.test/setup\"] [--author=\"Virgil\"] [--questions='[{\"question\":\"...\",\"answer\":\"...\"}]'] [--steps='[{\"name\":\"...\",\"text\":\"...\"}]']") + return core.Result{Value: core.E("agentic.cmdContentSchemaGenerate", "schema type and title are required", nil), OK: false} + } + + result := s.handleContentSchemaGenerate(s.commandContext(), core.NewOptions( + core.Option{Key: "schema_type", Value: schemaType}, + core.Option{Key: "title", Value: title}, + core.Option{Key: "description", Value: optionStringValue(options, "description")}, + core.Option{Key: "url", Value: optionStringValue(options, "url", "link")}, + core.Option{Key: "author", Value: optionStringValue(options, "author")}, + core.Option{Key: "published_at", Value: optionStringValue(options, "published_at", "published-at", "date_published")}, + core.Option{Key: "modified_at", Value: optionStringValue(options, "modified_at", "modified-at", "date_modified")}, + core.Option{Key: "language", Value: optionStringValue(options, "language", "in_language", "in-language")}, + core.Option{Key: "image", Value: optionStringValue(options, "image", "image_url", "image-url")}, + core.Option{Key: "questions", Value: optionAnyValue(options, "questions", "faq")}, + core.Option{Key: "steps", Value: optionAnyValue(options, "steps")}, + )) + if !result.OK { + err := commandResultError("agentic.cmdContentSchemaGenerate", result) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + output, ok := result.Value.(ContentSchemaOutput) + if !ok { + err := core.E("agentic.cmdContentSchemaGenerate", "invalid content schema output", nil) + core.Print(nil, "error: %v", err) + return core.Result{Value: err, OK: false} + } + + core.Print(nil, "schema type: %s", output.SchemaType) + core.Print(nil, "schema json: %s", output.SchemaJSON) + return core.Result{Value: output, OK: true} +} + func (s *PrepSubsystem) cmdComplete(options core.Options) core.Result { result := s.handleComplete(s.commandContext(), options) if !result.OK { diff --git a/pkg/agentic/commands_test.go b/pkg/agentic/commands_test.go index 0ff12e8..9277d91 100644 --- a/pkg/agentic/commands_test.go +++ b/pkg/agentic/commands_test.go @@ -1123,6 +1123,41 @@ func TestCommands_CmdGenerate_Good_BriefTemplate(t *testing.T) { assert.Contains(t, output, "content: Template draft") } +func TestCommands_CmdContentSchemaGenerate_Good(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + output := captureStdout(t, func() { + r := s.cmdContentSchemaGenerate(core.NewOptions( + core.Option{Key: "type", Value: "howto"}, + core.Option{Key: "title", Value: "Set up the workspace"}, + core.Option{Key: "description", Value: "Prepare a fresh workspace for an agent."}, + core.Option{Key: "url", Value: "https://example.test/workspace"}, + core.Option{Key: "steps", Value: `[{"name":"Clone","text":"Clone the repository."},{"name":"Prepare","text":"Build the prompt."}]`}, + )) + assert.True(t, r.OK) + }) + + assert.Contains(t, output, "schema type: HowTo") + assert.Contains(t, output, `"@type":"HowTo"`) + assert.Contains(t, output, `"name":"Set up the workspace"`) +} + +func TestCommands_CmdContentSchemaGenerate_Bad_MissingTitle(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + r := s.cmdContentSchemaGenerate(core.NewOptions( + core.Option{Key: "type", Value: "howto"}, + )) + assert.False(t, r.OK) +} + +func TestCommands_CmdContentSchemaGenerate_Ugly_InvalidSchemaType(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + r := s.cmdContentSchemaGenerate(core.NewOptions( + core.Option{Key: "type", Value: "toast"}, + core.Option{Key: "title", Value: "Set up the workspace"}, + )) + assert.False(t, r.OK) +} + func TestCommands_CmdComplete_Good(t *testing.T) { s, c := testPrepWithCore(t, nil) @@ -1481,6 +1516,8 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) { assert.Contains(t, cmds, "agentic:resume") assert.Contains(t, cmds, "content/generate") assert.Contains(t, cmds, "agentic:content/generate") + assert.Contains(t, cmds, "content/schema/generate") + assert.Contains(t, cmds, "agentic:content/schema/generate") assert.Contains(t, cmds, "complete") assert.Contains(t, cmds, "agentic:complete") assert.Contains(t, cmds, "scan")