fix(ax): register named agentic commands
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
7c1aae0402
commit
54581dcbd7
3 changed files with 44 additions and 66 deletions
|
|
@ -13,21 +13,32 @@ import (
|
|||
|
||||
// registerCommands adds agentic CLI commands to Core's command tree.
|
||||
func (s *PrepSubsystem) registerCommands(ctx context.Context) {
|
||||
s.commandCtx = ctx
|
||||
c := s.Core()
|
||||
c.Command("run/task", core.Command{Description: "Run a single task end-to-end", Action: s.cmdRunTaskFactory(ctx)})
|
||||
c.Command("run/orchestrator", core.Command{Description: "Run the queue orchestrator (standalone, no MCP)", Action: s.cmdOrchestratorFactory(ctx)})
|
||||
c.Command("run/task", core.Command{Description: "Run a single task end-to-end", Action: s.cmdRunTask})
|
||||
c.Command("run/orchestrator", core.Command{Description: "Run the queue orchestrator (standalone, no MCP)", Action: s.cmdOrchestrator})
|
||||
c.Command("prep", core.Command{Description: "Prepare a workspace: clone repo, build prompt", Action: s.cmdPrep})
|
||||
c.Command("status", core.Command{Description: "List agent workspace statuses", Action: s.cmdStatus})
|
||||
c.Command("prompt", core.Command{Description: "Build and display an agent prompt for a repo", Action: s.cmdPrompt})
|
||||
c.Command("extract", core.Command{Description: "Extract a workspace template to a directory", Action: s.cmdExtract})
|
||||
}
|
||||
|
||||
// cmdRunTaskFactory returns the run/task action closure (needs ctx for DispatchSync).
|
||||
func (s *PrepSubsystem) cmdRunTaskFactory(ctx context.Context) func(core.Options) core.Result {
|
||||
return func(opts core.Options) core.Result { return s.cmdRunTask(ctx, opts) }
|
||||
// commandContext returns the startup context captured during command registration.
|
||||
//
|
||||
// ctx := s.commandContext()
|
||||
// _ = ctx.Err()
|
||||
func (s *PrepSubsystem) commandContext() context.Context {
|
||||
if s.commandCtx != nil {
|
||||
return s.commandCtx
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdRunTask(ctx context.Context, opts core.Options) core.Result {
|
||||
func (s *PrepSubsystem) cmdRunTask(opts core.Options) core.Result {
|
||||
return s.runTask(s.commandContext(), opts)
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) runTask(ctx context.Context, opts core.Options) core.Result {
|
||||
repo := opts.String("repo")
|
||||
agent := opts.String("agent")
|
||||
task := opts.String("task")
|
||||
|
|
@ -72,12 +83,8 @@ func (s *PrepSubsystem) cmdRunTask(ctx context.Context, opts core.Options) core.
|
|||
return core.Result{OK: true}
|
||||
}
|
||||
|
||||
// cmdOrchestratorFactory returns the orchestrator action closure (needs ctx for blocking).
|
||||
func (s *PrepSubsystem) cmdOrchestratorFactory(ctx context.Context) func(core.Options) core.Result {
|
||||
return func(opts core.Options) core.Result { return s.cmdOrchestrator(ctx, opts) }
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) cmdOrchestrator(ctx context.Context, _ core.Options) core.Result {
|
||||
func (s *PrepSubsystem) cmdOrchestrator(_ core.Options) core.Result {
|
||||
ctx := s.commandContext()
|
||||
core.Print(nil, "core-agent orchestrator running (pid %s)", core.Env("PID"))
|
||||
core.Print(nil, " workspace: %s", WorkspaceRoot())
|
||||
core.Print(nil, " watching queue, draining on 30s tick + completion poke")
|
||||
|
|
|
|||
|
|
@ -640,7 +640,8 @@ func TestCommands_CmdRunTask_Bad_MissingArgs(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
r := s.cmdRunTask(ctx, core.NewOptions())
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdRunTask(core.NewOptions())
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
@ -648,7 +649,8 @@ func TestCommands_CmdRunTask_Bad_MissingTask(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
r := s.cmdRunTask(ctx, core.NewOptions(core.Option{Key: "repo", Value: "go-io"}))
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdRunTask(core.NewOptions(core.Option{Key: "repo", Value: "go-io"}))
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
@ -656,7 +658,8 @@ func TestCommands_CmdOrchestrator_Good_CancelledCtx(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // cancel immediately
|
||||
r := s.cmdOrchestrator(ctx, core.NewOptions())
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdOrchestrator(core.NewOptions())
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
@ -719,7 +722,8 @@ func TestCommands_CmdOrchestrator_Bad_DoneContext(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second))
|
||||
defer cancel()
|
||||
r := s.cmdOrchestrator(ctx, core.NewOptions())
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdOrchestrator(core.NewOptions())
|
||||
assert.True(t, r.OK) // returns OK after ctx.Done()
|
||||
}
|
||||
|
||||
|
|
@ -727,7 +731,8 @@ func TestCommands_CmdOrchestrator_Ugly_CancelledImmediately(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
r := s.cmdOrchestrator(ctx, core.NewOptions())
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdOrchestrator(core.NewOptions())
|
||||
assert.True(t, r.OK) // exits immediately when context is already cancelled
|
||||
}
|
||||
|
||||
|
|
@ -770,8 +775,9 @@ func TestCommands_CmdRunTask_Good_DefaultsApplied(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
s.commandCtx = ctx
|
||||
// Provide repo + task but omit agent + org — tests that defaults (codex, core) are applied
|
||||
r := s.cmdRunTask(ctx, core.NewOptions(
|
||||
r := s.cmdRunTask(core.NewOptions(
|
||||
core.Option{Key: "repo", Value: "go-io"},
|
||||
core.Option{Key: "task", Value: "run all tests"},
|
||||
))
|
||||
|
|
@ -783,7 +789,8 @@ func TestCommands_CmdRunTask_Ugly_MixedIssueString(t *testing.T) {
|
|||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
r := s.cmdRunTask(ctx, core.NewOptions(
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdRunTask(core.NewOptions(
|
||||
core.Option{Key: "repo", Value: "go-io"},
|
||||
core.Option{Key: "task", Value: "fix it"},
|
||||
core.Option{Key: "issue", Value: "issue-42abc"},
|
||||
|
|
@ -792,65 +799,28 @@ func TestCommands_CmdRunTask_Ugly_MixedIssueString(t *testing.T) {
|
|||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
// --- CmdRunTaskFactory Good/Bad/Ugly ---
|
||||
// --- CommandContext Good/Bad/Ugly ---
|
||||
|
||||
func TestCommands_CmdRunTaskFactory_Good(t *testing.T) {
|
||||
func TestCommands_CommandContext_Good_StoredStartupContext(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := s.cmdRunTaskFactory(ctx)
|
||||
assert.NotNil(t, fn, "factory should return a non-nil func")
|
||||
s.registerCommands(ctx)
|
||||
assert.Same(t, ctx, s.commandContext())
|
||||
}
|
||||
|
||||
func TestCommands_CmdRunTaskFactory_Bad(t *testing.T) {
|
||||
func TestCommands_CommandContext_Bad_FallsBackToBackground(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // cancelled ctx
|
||||
|
||||
fn := s.cmdRunTaskFactory(ctx)
|
||||
assert.NotNil(t, fn, "factory should return a func even with cancelled ctx")
|
||||
assert.NotNil(t, s.commandContext())
|
||||
assert.NoError(t, s.commandContext().Err())
|
||||
}
|
||||
|
||||
func TestCommands_CmdRunTaskFactory_Ugly(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := s.cmdRunTaskFactory(ctx)
|
||||
// Call with empty options — should fail gracefully (missing repo+task)
|
||||
r := fn(core.NewOptions())
|
||||
assert.False(t, r.OK)
|
||||
}
|
||||
|
||||
// --- CmdOrchestratorFactory Good/Bad/Ugly ---
|
||||
|
||||
func TestCommands_CmdOrchestratorFactory_Good(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
fn := s.cmdOrchestratorFactory(ctx)
|
||||
assert.NotNil(t, fn, "factory should return a non-nil func")
|
||||
}
|
||||
|
||||
func TestCommands_CmdOrchestratorFactory_Bad(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // cancelled ctx
|
||||
|
||||
fn := s.cmdOrchestratorFactory(ctx)
|
||||
assert.NotNil(t, fn, "factory should return a func even with cancelled ctx")
|
||||
}
|
||||
|
||||
func TestCommands_CmdOrchestratorFactory_Ugly(t *testing.T) {
|
||||
func TestCommands_CommandContext_Ugly_CancelledStartupContext(t *testing.T) {
|
||||
s, _ := testPrepWithCore(t, nil)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel() // pre-cancelled
|
||||
|
||||
fn := s.cmdOrchestratorFactory(ctx)
|
||||
// Calling the factory result with a cancelled ctx should return OK (exits immediately)
|
||||
r := fn(core.NewOptions())
|
||||
s.commandCtx = ctx
|
||||
r := s.cmdOrchestrator(core.NewOptions())
|
||||
assert.True(t, r.OK)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ type PrepSubsystem struct {
|
|||
brainURL string
|
||||
brainKey string
|
||||
codePath string
|
||||
commandCtx context.Context
|
||||
dispatchMu sync.Mutex // serialises concurrency check + spawn
|
||||
drainMu sync.Mutex
|
||||
pokeCh chan struct{}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue