feat(agentic): expose setup action surface

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 06:39:02 +00:00
parent 613e1fc58e
commit 7a034ccbe8
4 changed files with 83 additions and 4 deletions

View file

@ -3,8 +3,11 @@
package agentic
import (
"context"
"dappco.re/go/agent/pkg/setup"
core "dappco.re/go/core"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func (s *PrepSubsystem) registerSetupCommands() {
@ -14,17 +17,27 @@ func (s *PrepSubsystem) registerSetupCommands() {
}
func (s *PrepSubsystem) cmdSetup(options core.Options) core.Result {
return s.handleSetup(context.Background(), options)
}
// result := c.Action("agentic.setup").Run(ctx, core.NewOptions(
//
// core.Option{Key: "path", Value: "."},
// core.Option{Key: "template", Value: "auto"},
//
// ))
func (s *PrepSubsystem) handleSetup(_ context.Context, 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.setup", "setup service is required", nil), OK: false}
}
return core.Result{Value: core.E("agentic.cmdSetup", "setup service is required", nil), OK: false}
return core.Result{Value: core.E("agentic.setup", "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}
return core.Result{Value: core.E("agentic.setup", "setup service is required", nil), OK: false}
}
result := service.Run(setup.Options{
@ -39,3 +52,44 @@ func (s *PrepSubsystem) cmdSetup(options core.Options) core.Result {
return result
}
func (s *PrepSubsystem) registerSetupTool(server *mcp.Server) {
mcp.AddTool(server, &mcp.Tool{
Name: "agentic_setup",
Description: "Scaffold a workspace with .core config files and optional templates.",
}, s.setupTool)
}
type SetupInput struct {
Path string `json:"path,omitempty"`
DryRun bool `json:"dry_run,omitempty"`
Force bool `json:"force,omitempty"`
Template string `json:"template,omitempty"`
}
type SetupOutput struct {
Success bool `json:"success"`
Path string `json:"path"`
}
func (s *PrepSubsystem) setupTool(ctx context.Context, _ *mcp.CallToolRequest, input SetupInput) (*mcp.CallToolResult, SetupOutput, error) {
result := s.handleSetup(ctx, core.NewOptions(
core.Option{Key: "path", Value: input.Path},
core.Option{Key: "dry_run", Value: input.DryRun},
core.Option{Key: "force", Value: input.Force},
core.Option{Key: "template", Value: input.Template},
))
if !result.OK {
return nil, SetupOutput{}, resultErrorValue("agentic.setup", result)
}
path, ok := result.Value.(string)
if !ok {
return nil, SetupOutput{}, core.E("agentic.setup", "invalid setup output", nil)
}
return nil, SetupOutput{
Success: true,
Path: path,
}, nil
}

View file

@ -3,6 +3,7 @@
package agentic
import (
"context"
"testing"
"time"
@ -64,3 +65,23 @@ func TestCommandsSetup_CmdSetup_Ugly_DryRunDoesNotWrite(t *testing.T) {
assert.False(t, fs.Exists(core.JoinPath(dir, ".core")))
assert.False(t, fs.Exists(core.JoinPath(dir, "PROMPT.md")))
}
func TestCommandsSetup_HandleSetup_Good_ActionAlias(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.handleSetup(context.Background(), core.NewOptions(core.Option{Key: "path", Value: dir}))
require.True(t, result.OK)
createdPath, ok := result.Value.(string)
require.True(t, ok)
assert.Equal(t, dir, createdPath)
assert.True(t, fs.Exists(core.JoinPath(dir, ".core", "build.yaml")))
}

View file

@ -92,7 +92,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
case "agentic.status", "agentic.scan", "agentic.watch",
"agentic.issue.get", "agentic.issue.list", "agentic.issue.assign", "agentic.pr.get", "agentic.pr.list",
"agentic.prompt", "agentic.task", "agentic.flow", "agentic.persona",
"agentic.prompt.version",
"agentic.prompt.version", "agentic.setup",
"agentic.sync.status", "agentic.fleet.nodes", "agentic.fleet.stats", "agentic.fleet.events",
"agentic.credits.balance", "agentic.credits.history",
"agentic.subscription.detect", "agentic.subscription.budget",
@ -184,6 +184,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
c.Action("agentic.ingest", s.handleIngest).Description = "Create issues from agent findings"
c.Action("agentic.poke", s.handlePoke).Description = "Drain next queued task from the queue"
c.Action("agentic.mirror", s.handleMirror).Description = "Mirror agent branches to GitHub"
c.Action("agentic.setup", s.handleSetup).Description = "Scaffold a workspace with .core config files and optional templates"
c.Action("agentic.issue.get", s.handleIssueGet).Description = "Get a Forge issue by number"
c.Action("agentic.issue.list", s.handleIssueList).Description = "List Forge issues for a repo"
@ -454,6 +455,7 @@ func (s *PrepSubsystem) RegisterTools(server *mcp.Server) {
s.registerPRTools(server)
s.registerContentTools(server)
s.registerLanguageTools(server)
s.registerSetupTool(server)
mcp.AddTool(server, &mcp.Tool{
Name: "agentic_scan",

View file

@ -712,6 +712,7 @@ func TestPrep_RegisterTools_Good_RegistersCompletionTool(t *testing.T) {
assert.Contains(t, toolNames, "agentic_complete")
assert.Contains(t, toolNames, "prompt_version")
assert.Contains(t, toolNames, "agentic_prompt_version")
assert.Contains(t, toolNames, "agentic_setup")
assert.Contains(t, toolNames, "session_complete")
assert.Contains(t, toolNames, "agentic_message_send")
assert.Contains(t, toolNames, "agent_send")
@ -738,6 +739,7 @@ func TestPrep_OnStartup_Good_RegistersGenerateCommand(t *testing.T) {
assert.Contains(t, c.Commands(), "prep-workspace")
assert.Contains(t, c.Commands(), "setup")
assert.Contains(t, c.Commands(), "agentic:setup")
assert.True(t, c.Action("agentic.setup").Exists())
assert.Contains(t, c.Commands(), "watch")
assert.Contains(t, c.Commands(), "workspace/watch")
assert.Contains(t, c.Commands(), "agentic:watch")