Run agents
Change-Id: I8c26ad419b1d9a28f39a8334f9f527510ef193cd
diff --git a/server/.env b/server/.env
index 9407122..4d4d76d 100644
--- a/server/.env
+++ b/server/.env
@@ -1,2 +1,3 @@
REMOTE_REPO_URL=https://github.com/iomodo/staff.git
-WORKING_DIR=../
\ No newline at end of file
+WORKING_DIR=../
+OPENAI_API_KEY=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
\ No newline at end of file
diff --git a/server/go.mod b/server/go.mod
index d2079f1..7ab34ee 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -15,4 +15,5 @@
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
+ golang.org/x/text v0.27.0 // indirect
)
diff --git a/server/go.sum b/server/go.sum
index 12bafed..bface14 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -16,6 +16,8 @@
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
+golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/server/server/server.go b/server/server/server.go
index 99c11f1..1453820 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -7,13 +7,22 @@
"os"
"path/filepath"
+ "strings"
+ "time"
+
+ "github.com/iomodo/staff/agent"
"github.com/iomodo/staff/git"
+ "github.com/iomodo/staff/llm"
+ "github.com/iomodo/staff/tm/git_tm"
"github.com/joho/godotenv"
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
)
// Server type defines application global state
type Server struct {
logger *slog.Logger
+ agents []*agent.Agent
}
// NewServer creates new Server
@@ -57,16 +66,97 @@
if isEmpty {
a.logger.Info("Working directory is empty, initializing new repository")
- return a.initializeNewRepository(workingDir, remoteRepoURL)
+ if err := a.initializeNewRepository(workingDir, remoteRepoURL); err != nil {
+ return err
+ }
} else {
a.logger.Info("Working directory is not empty, syncing with remote")
- return a.syncWithRemote(workingDir, remoteRepoURL)
+ if err := a.syncWithRemote(workingDir, remoteRepoURL); err != nil {
+ return err
+ }
}
+
+ // Create shared task manager
+ gitRepo := git.DefaultGit(workingDir)
+ tasksDir := filepath.Join(workingDir, "operations", "tasks")
+ taskManager := git_tm.NewGitTaskManager(gitRepo, tasksDir)
+
+ // Load and start agents
+ agentsDir := filepath.Join(workingDir, "operations", "agents")
+ entries, err := os.ReadDir(agentsDir)
+ if err != nil {
+ return fmt.Errorf("failed to read agents directory: %w", err)
+ }
+
+ for _, entry := range entries {
+ if !entry.IsDir() {
+ continue
+ }
+ agentName := entry.Name()
+ systemPath := filepath.Join(agentsDir, agentName, "system.md")
+ content, err := os.ReadFile(systemPath)
+ if err != nil {
+ a.logger.Error("Failed to read system prompt", slog.String("agent", agentName), slog.String("error", err.Error()))
+ continue
+ }
+ systemPrompt := string(content)
+
+ // LLM configuration
+ baseURL := os.Getenv("OPENAI_BASE_URL")
+ if baseURL == "" {
+ baseURL = "https://api.openai.com/v1"
+ }
+ llmConfig := llm.Config{
+ Provider: llm.ProviderOpenAI,
+ APIKey: os.Getenv("OPENAI_API_KEY"),
+ BaseURL: baseURL,
+ Timeout: 30 * time.Second,
+ }
+
+ config := agent.AgentConfig{
+ Name: agentName,
+ Role: cases.Title(language.English).String(agentName),
+ GitUsername: fmt.Sprintf("Staff %s", cases.Title(language.English).String(agentName)),
+ GitEmail: fmt.Sprintf("%s@staff.com", strings.ToLower(agentName)),
+ WorkingDir: workingDir,
+ LLMProvider: llm.ProviderOpenAI,
+ LLMModel: "gpt-4o",
+ LLMConfig: llmConfig,
+ SystemPrompt: systemPrompt,
+ TaskManager: taskManager,
+ GitRepoPath: workingDir,
+ GitRemote: "origin",
+ GitBranch: "main",
+ }
+
+ ag, err := agent.NewAgent(config)
+ if err != nil {
+ a.logger.Error("Failed to create agent", slog.String("agent", agentName), slog.String("error", err.Error()))
+ continue
+ }
+
+ a.agents = append(a.agents, ag)
+
+ go func(ag *agent.Agent, name string) {
+ a.logger.Info("Starting agent", slog.String("name", name))
+ if err := ag.Run(); err != nil {
+ a.logger.Error("Agent failed", slog.String("name", name), slog.String("error", err.Error()))
+ }
+ }(ag, agentName)
+ }
+
+ return nil
}
// Shutdown method shuts server down
func (a *Server) Shutdown() {
- a.logger.Info("Stoping Server...")
+ a.logger.Info("Stopping Server...")
+
+ for _, ag := range a.agents {
+ ag.Stop()
+ }
+
+ a.logger.Info("All agents stopped")
a.logger.Info("Server stopped")
}