diff --git a/pkg/agentic/commands.go b/pkg/agentic/commands.go index fd91418..2cc1245 100644 --- a/pkg/agentic/commands.go +++ b/pkg/agentic/commands.go @@ -71,6 +71,7 @@ func (s *PrepSubsystem) registerCommands(ctx context.Context) { s.registerSessionCommands() s.registerTaskCommands() s.registerLanguageCommands() + s.registerSetupCommands() } // ctx := s.commandContext() diff --git a/pkg/agentic/commands_setup.go b/pkg/agentic/commands_setup.go new file mode 100644 index 0000000..f7b84b1 --- /dev/null +++ b/pkg/agentic/commands_setup.go @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package agentic + +import ( + "dappco.re/go/agent/pkg/setup" + core "dappco.re/go/core" +) + +func (s *PrepSubsystem) registerSetupCommands() { + c := s.Core() + c.Command("setup", core.Command{Description: "Scaffold a workspace with .core config files and optional templates", Action: s.cmdSetup}) + c.Command("agentic:setup", core.Command{Description: "Scaffold a workspace with .core config files and optional templates", Action: s.cmdSetup}) +} + +func (s *PrepSubsystem) cmdSetup(options core.Options) core.Result { + serviceResult := s.Core().Service("setup") + if !serviceResult.OK { + if serviceResult.Value != nil { + return core.Result{Value: serviceResult.Value, OK: false} + } + return core.Result{Value: core.E("agentic.cmdSetup", "setup service is required", nil), OK: false} + } + + service, ok := serviceResult.Value.(*setup.Service) + if !ok || service == nil { + return core.Result{Value: core.E("agentic.cmdSetup", "setup service is required", nil), OK: false} + } + + result := service.Run(setup.Options{ + Path: optionStringValue(options, "path", "_arg"), + DryRun: optionBoolValue(options, "dry_run", "dry-run"), + Force: optionBoolValue(options, "force"), + Template: optionStringValue(options, "template", "template_slug", "template-slug", "slug"), + }) + if !result.OK { + return result + } + + return result +} diff --git a/pkg/agentic/commands_setup_test.go b/pkg/agentic/commands_setup_test.go new file mode 100644 index 0000000..e88ab92 --- /dev/null +++ b/pkg/agentic/commands_setup_test.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package agentic + +import ( + "testing" + "time" + + "dappco.re/go/agent/pkg/setup" + core "dappco.re/go/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCommandsSetup_CmdSetup_Good_WritesCoreConfigs(t *testing.T) { + dir := t.TempDir() + require.True(t, fs.WriteMode(core.JoinPath(dir, "go.mod"), "module example.com/test\n", 0644).OK) + + c := core.New(core.WithService(setup.Register)) + s := &PrepSubsystem{ + ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{}), + backoff: make(map[string]time.Time), + failCount: make(map[string]int), + } + + result := s.cmdSetup(core.NewOptions(core.Option{Key: "path", Value: dir})) + require.True(t, result.OK) + + build := fs.Read(core.JoinPath(dir, ".core", "build.yaml")) + require.True(t, build.OK) + assert.Contains(t, build.Value.(string), "type: go") +} + +func TestCommandsSetup_CmdSetup_Bad_MissingService(t *testing.T) { + s := &PrepSubsystem{ + ServiceRuntime: core.NewServiceRuntime(core.New(), AgentOptions{}), + backoff: make(map[string]time.Time), + failCount: make(map[string]int), + } + + result := s.cmdSetup(core.NewOptions()) + require.False(t, result.OK) + require.Error(t, result.Value.(error)) + assert.Contains(t, result.Value.(error).Error(), "setup service is required") +} + +func TestCommandsSetup_CmdSetup_Ugly_DryRunDoesNotWrite(t *testing.T) { + dir := t.TempDir() + require.True(t, fs.WriteMode(core.JoinPath(dir, "go.mod"), "module example.com/test\n", 0644).OK) + + c := core.New(core.WithService(setup.Register)) + s := &PrepSubsystem{ + ServiceRuntime: core.NewServiceRuntime(c, AgentOptions{}), + backoff: make(map[string]time.Time), + failCount: make(map[string]int), + } + + result := s.cmdSetup(core.NewOptions( + core.Option{Key: "path", Value: dir}, + core.Option{Key: "dry-run", Value: true}, + core.Option{Key: "template", Value: "agent"}, + )) + require.True(t, result.OK) + assert.False(t, fs.Exists(core.JoinPath(dir, ".core"))) + assert.False(t, fs.Exists(core.JoinPath(dir, "PROMPT.md"))) +} diff --git a/pkg/agentic/prep_test.go b/pkg/agentic/prep_test.go index d4c029e..7a39988 100644 --- a/pkg/agentic/prep_test.go +++ b/pkg/agentic/prep_test.go @@ -681,6 +681,8 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) { 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(), "setup") + assert.Contains(t, c.Commands(), "agentic:setup") assert.Contains(t, c.Commands(), "watch") assert.Contains(t, c.Commands(), "workspace/watch") assert.Contains(t, c.Commands(), "agentic:watch") diff --git a/pkg/setup/service_external_test.go b/pkg/setup/service_external_test.go new file mode 100644 index 0000000..9070e95 --- /dev/null +++ b/pkg/setup/service_external_test.go @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package setup_test + +import ( + "context" + "testing" + + "dappco.re/go/agent/pkg/agentic" + "dappco.re/go/agent/pkg/setup" + core "dappco.re/go/core" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestService_DetectGitRemote_Good_GitOrigin(t *testing.T) { + dir := t.TempDir() + c := core.New() + require.True(t, agentic.ProcessRegister(c).OK) + service := &setup.Service{ServiceRuntime: core.NewServiceRuntime(c, setup.RuntimeOptions{})} + + require.True(t, c.Process().RunIn(context.Background(), dir, "git", "init").OK) + require.True(t, c.Process().RunIn(context.Background(), dir, "git", "remote", "add", "origin", "git@forge.lthn.ai:core/agent.git").OK) + + assert.Equal(t, "core/agent", service.DetectGitRemote(dir)) +} diff --git a/pkg/setup/service_test.go b/pkg/setup/service_test.go index a67ea80..50de1eb 100644 --- a/pkg/setup/service_test.go +++ b/pkg/setup/service_test.go @@ -6,10 +6,8 @@ import ( "context" "testing" - "dappco.re/go/agent/pkg/agentic" core "dappco.re/go/core" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestService_Register_Good(t *testing.T) { @@ -44,18 +42,6 @@ func TestService_OnStartup_Ugly_NilRuntime(t *testing.T) { assert.True(t, result.OK) } -func TestService_DetectGitRemote_Good_GitOrigin(t *testing.T) { - dir := t.TempDir() - c := core.New() - require.True(t, agentic.ProcessRegister(c).OK) - service := &Service{ServiceRuntime: core.NewServiceRuntime(c, RuntimeOptions{})} - - require.True(t, c.Process().RunIn(context.Background(), dir, "git", "init").OK) - require.True(t, c.Process().RunIn(context.Background(), dir, "git", "remote", "add", "origin", "git@forge.lthn.ai:core/agent.git").OK) - - assert.Equal(t, "core/agent", service.DetectGitRemote(dir)) -} - func TestService_DetectGitRemote_Bad_NonGitDir(t *testing.T) { c := core.New() service := &Service{ServiceRuntime: core.NewServiceRuntime(c, RuntimeOptions{})}