diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index bb42017..edddf2e 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -18,6 +18,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) { s.startupContext = ctx c := s.Core() c.Command("run/task", core.Command{Description: "Run a single task end-to-end", Action: s.cmdRunTask}) + c.Command("dispatch/sync", core.Command{Description: "Dispatch a single task synchronously and block until it completes", Action: s.cmdDispatchSync}) c.Command("run/orchestrator", core.Command{Description: "Run the queue orchestrator (standalone, no MCP)", Action: s.cmdOrchestrator}) c.Command("dispatch", core.Command{Description: "Dispatch queued agents", Action: s.cmdDispatch}) c.Command("agentic:dispatch", core.Command{Description: "Dispatch queued agents", Action: s.cmdDispatch}) @@ -68,10 +69,14 @@ func (s *PrepSubsystem) commandContext() context.Context { } func (s *PrepSubsystem) cmdRunTask(options core.Options) core.Result { - return s.runTask(s.commandContext(), options) + return s.runDispatchSync(s.commandContext(), options, "run task", "agentic.runTask") } -func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core.Result { +func (s *PrepSubsystem) cmdDispatchSync(options core.Options) core.Result { + return s.runDispatchSync(s.commandContext(), options, "dispatch sync", "agentic.runDispatchSync") +} + +func (s *PrepSubsystem) runDispatchSync(ctx context.Context, options core.Options, commandLabel, errorName string) core.Result { repo := options.String("repo") agent := options.String("agent") task := options.String("task") @@ -79,8 +84,8 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core. org := options.String("org") if repo == "" || task == "" { - core.Print(nil, "usage: core-agent run task --repo= --task=\"...\" --agent=codex [--issue=N] [--org=core]") - return core.Result{Value: core.E("agentic.runTask", "repo and task are required", nil), OK: false} + core.Print(nil, "usage: core-agent %s --repo= --task=\"...\" --agent=codex [--issue=N] [--org=core]", commandLabel) + return core.Result{Value: core.E(errorName, "repo and task are required", nil), OK: false} } if agent == "" { agent = "codex" @@ -91,7 +96,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core. issue := parseIntString(issueValue) - core.Print(nil, "core-agent run task") + core.Print(nil, "core-agent %s", commandLabel) core.Print(nil, " repo: %s/%s", org, repo) core.Print(nil, " agent: %s", agent) if issue > 0 { @@ -107,7 +112,7 @@ func (s *PrepSubsystem) runTask(ctx context.Context, options core.Options) core. if !result.OK { failureError := result.Error if failureError == nil { - failureError = core.E("agentic.runTask", "dispatch failed", nil) + failureError = core.E(errorName, "dispatch failed", nil) } core.Print(nil, "FAILED: %v", failureError) return core.Result{Value: failureError, OK: false} diff --git a/pkg/agentic/commands_test.go b/pkg/agentic/commands_test.go index afc2be5..3a4225d 100644 --- a/pkg/agentic/commands_test.go +++ b/pkg/agentic/commands_test.go @@ -1168,6 +1168,15 @@ func TestCommands_CmdRunTask_Bad_MissingArgs(t *testing.T) { assert.False(t, r.OK) } +func TestCommands_CmdDispatchSync_Bad_MissingArgs(t *testing.T) { + s, _ := testPrepWithCore(t, nil) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + s.startupContext = ctx + r := s.cmdDispatchSync(core.NewOptions()) + assert.False(t, r.OK) +} + func TestCommands_CmdRunTask_Bad_MissingTask(t *testing.T) { s, _ := testPrepWithCore(t, nil) ctx, cancel := context.WithCancel(context.Background()) @@ -1264,6 +1273,7 @@ func TestCommands_RegisterCommands_Good_AllRegistered(t *testing.T) { cmds := c.Commands() assert.Contains(t, cmds, "run/task") + assert.Contains(t, cmds, "dispatch/sync") assert.Contains(t, cmds, "run/orchestrator") assert.Contains(t, cmds, "dispatch") assert.Contains(t, cmds, "agentic:dispatch") diff --git a/pkg/agentic/dispatch_sync.go b/pkg/agentic/dispatch_sync.go index bade12e..23eb702 100644 --- a/pkg/agentic/dispatch_sync.go +++ b/pkg/agentic/dispatch_sync.go @@ -91,3 +91,28 @@ func (s *PrepSubsystem) DispatchSync(ctx context.Context, input DispatchSyncInpu } } } + +// result := c.Action("agentic.dispatch.sync").Run(ctx, core.NewOptions( +// +// core.Option{Key: "repo", Value: "go-io"}, +// core.Option{Key: "task", Value: "Fix the failing tests"}, +// +// )) +func (s *PrepSubsystem) handleDispatchSync(ctx context.Context, options core.Options) core.Result { + input := dispatchSyncInputFromOptions(options) + result := s.DispatchSync(ctx, input) + if result.Error != nil { + return core.Result{Value: result.Error, OK: false} + } + return core.Result{Value: result, OK: result.OK} +} + +func dispatchSyncInputFromOptions(options core.Options) DispatchSyncInput { + return DispatchSyncInput{ + Org: optionStringValue(options, "org"), + Repo: optionStringValue(options, "repo", "_arg"), + Agent: optionStringValue(options, "agent"), + Task: optionStringValue(options, "task"), + Issue: optionIntValue(options, "issue"), + } +} diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index 7b10045..05255ac 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -159,6 +159,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { c.Action("agent.subscription.budget.update", s.handleSubscriptionBudgetUpdate).Description = "Update the compute budget for a fleet node" c.Action("agentic.dispatch", s.handleDispatch).Description = "Prep workspace and spawn a subagent" + c.Action("agentic.dispatch.sync", s.handleDispatchSync).Description = "Dispatch a single task synchronously and block until it completes" c.Action("agentic.prep", s.handlePrep).Description = "Clone repo and build agent prompt" c.Action("agentic.status", s.handleStatus).Description = "List workspace states (running/completed/blocked)" c.Action("agentic.resume", s.handleResume).Description = "Resume a blocked or completed workspace" diff --git a/pkg/agentic/prep_test.go b/pkg/agentic/prep_test.go index d363e4e..6c0c753 100644 --- a/pkg/agentic/prep_test.go +++ b/pkg/agentic/prep_test.go @@ -468,6 +468,7 @@ func TestPrep_OnStartup_Good_RegistersPlanActions(t *testing.T) { s.ServiceRuntime = core.NewServiceRuntime(c, AgentOptions{}) require.True(t, s.OnStartup(context.Background()).OK) + assert.True(t, c.Action("agentic.dispatch.sync").Exists()) assert.True(t, c.Action("plan.create").Exists()) assert.True(t, c.Action("plan.get").Exists()) assert.True(t, c.Action("plan.read").Exists()) @@ -654,6 +655,7 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) { assert.Contains(t, c.Commands(), "generate") assert.Contains(t, c.Commands(), "agentic:generate") assert.Contains(t, c.Commands(), "complete") + assert.Contains(t, c.Commands(), "dispatch/sync") assert.Contains(t, c.Commands(), "agentic:plan") assert.Contains(t, c.Commands(), "prep-workspace") assert.Contains(t, c.Commands(), "brain/ingest")