Add api package

Change-Id: Ifcdd1f45c01e98b91c1edef3371332cb4a098e82
diff --git a/server/api/api.go b/server/api/api.go
new file mode 100644
index 0000000..fd78ee4
--- /dev/null
+++ b/server/api/api.go
@@ -0,0 +1,67 @@
+package api
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/iomodo/staff/app"
+)
+
+const APIURLSuffix = "/api/v1"
+const HealthCheckPath = "/healthcheck"
+
+type API struct {
+	Logger  *log.Logger
+	Root    *gin.Engine
+	APIRoot *gin.RouterGroup // 'api/v1'
+	Auth    *gin.RouterGroup // 'api/v1/auth'
+}
+
+// Init initializes api
+func Init(router *gin.Engine, application *app.App) error {
+	apiObj := API{}
+	apiObj.Root = router
+	apiObj.Root.Use(func(c *gin.Context) {
+		c.Set("app", application)
+		c.Next()
+	})
+	apiObj.APIRoot = router.Group(APIURLSuffix)
+	apiObj.initHealthCheck()
+
+	apiObj.Root.NoRoute(func(c *gin.Context) {
+		c.JSON(http.StatusNotFound, "Page not found")
+	})
+
+	return nil
+}
+
+func (apiObj *API) initHealthCheck() {
+	apiObj.Root.GET(HealthCheckPath, healthCheck)
+}
+
+func healthCheck(c *gin.Context) {
+	_, err := getApp(c)
+	if err != nil {
+		responseFormat(c, http.StatusInternalServerError, err.Error())
+		return
+	}
+	responseFormat(c, http.StatusOK, "OK")
+}
+
+func responseFormat(c *gin.Context, respStatus int, data interface{}) {
+	c.JSON(respStatus, data)
+}
+
+func getApp(c *gin.Context) (*app.App, error) {
+	appInt, ok := c.Get("app")
+	if !ok {
+		return nil, fmt.Errorf("missing application in the context")
+	}
+	a, ok := appInt.(*app.App)
+	if !ok {
+		return nil, fmt.Errorf("wrong data type of the application in the context")
+	}
+	return a, nil
+}
diff --git a/server/app/app.go b/server/app/app.go
new file mode 100644
index 0000000..a016c4b
--- /dev/null
+++ b/server/app/app.go
@@ -0,0 +1,48 @@
+package app
+
+import (
+	"fmt"
+	"log/slog"
+
+	"github.com/iomodo/staff/agent"
+	"github.com/iomodo/staff/config"
+	"github.com/iomodo/staff/git"
+	"github.com/iomodo/staff/tm/git_tm"
+)
+
+// App type defines application global state
+type App struct {
+	logger  *slog.Logger
+	config  *config.Config
+	manager *agent.Manager
+}
+
+// NewApp creates new App
+func NewApp(config *config.Config, logger *slog.Logger) (*App, error) {
+	// Create task manager using config
+	gitInterface := git.New(config, logger)
+	taskManager := git_tm.NewGitTaskManager(gitInterface, config, logger)
+
+	manager, err := agent.NewManager(config, taskManager, logger)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create agent manager: %w", err)
+	}
+
+	return &App{
+		logger:  logger,
+		config:  config,
+		manager: manager,
+	}, nil
+}
+
+func (a *App) Start() {
+	a.manager.StartAllAgents()
+}
+
+func (a *App) Stop() {
+	if a.manager != nil {
+		if err := a.manager.Close(); err != nil {
+			a.logger.Error("Error closing manager", slog.String("error", err.Error()))
+		}
+	}
+}
diff --git a/server/config.yaml b/server/config.yaml
index 804ea2c..0842f0e 100644
--- a/server/config.yaml
+++ b/server/config.yaml
@@ -1,6 +1,9 @@
 # Staff MVP Configuration
 # This is a minimal config for testing the MVP
 
+server:
+  listen_address: ":9325"
+
 openai:
   api_key: "${OPENAI_API_KEY}"
   base_url: ""
diff --git a/server/config/config.go b/server/config/config.go
index baceb04..1a39c6f 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -10,6 +10,7 @@
 
 // Config represents the Staff MVP configuration
 type Config struct {
+	Server ServerConfig  `yaml:"server"`
 	OpenAI OpenAIConfig  `yaml:"openai"`
 	GitHub GitHubConfig  `yaml:"github"`
 	Gerrit GerritConfig  `yaml:"gerrit"`
@@ -18,6 +19,10 @@
 	Git    GitConfig     `yaml:"git"`
 }
 
+type ServerConfig struct {
+	ListenAddress string `yaml:"listen_address"`
+}
+
 // OpenAIConfig represents OpenAI provider configuration
 type OpenAIConfig struct {
 	APIKey     string        `yaml:"api_key"`
@@ -126,6 +131,10 @@
 		config.Gerrit.Project = gerritProject
 	}
 
+	if listenAddr := os.Getenv("LISTEN_ADDRESS"); listenAddr != "" {
+		config.Server.ListenAddress = listenAddr
+	}
+
 	// Re-validate after env overrides
 	if err := validateConfig(*config); err != nil {
 		return nil, fmt.Errorf("invalid configuration after env overrides: %w", err)
diff --git a/server/go.mod b/server/go.mod
index 01bba4c..c4de622 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -11,6 +11,31 @@
 )
 
 require (
+	github.com/bytedance/sonic v1.11.6 // indirect
+	github.com/bytedance/sonic/loader v0.1.1 // indirect
+	github.com/cloudwego/base64x v0.1.4 // indirect
+	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/gin-gonic/gin v1.10.1 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.20.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 	github.com/spf13/pflag v1.0.6 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	golang.org/x/arch v0.8.0 // indirect
+	golang.org/x/crypto v0.23.0 // indirect
+	golang.org/x/net v0.25.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
+	google.golang.org/protobuf v1.34.1 // indirect
 )
diff --git a/server/go.sum b/server/go.sum
index 56f2486..e104fd7 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -1,18 +1,92 @@
+github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
+github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
+github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
+github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
+github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
+github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
+github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
 golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/server/server/server.go b/server/server/server.go
index 6ff91ef..eae7a9f 100644
--- a/server/server/server.go
+++ b/server/server/server.go
@@ -1,27 +1,33 @@
 package server
 
 import (
+	"context"
 	"fmt"
 	"log/slog"
+	"net/http"
 	"os"
+	"time"
 
-	"github.com/iomodo/staff/agent"
+	"github.com/gin-gonic/gin"
+	"github.com/iomodo/staff/api"
+	"github.com/iomodo/staff/app"
 	"github.com/iomodo/staff/config"
-	"github.com/iomodo/staff/git"
-	"github.com/iomodo/staff/tm/git_tm"
 	"github.com/joho/godotenv"
 )
 
 // Server type defines application global state
 type Server struct {
-	logger  *slog.Logger
-	manager *agent.Manager
-	config  *config.Config
+	router *gin.Engine
+	logger *slog.Logger
+	app    *app.App
+	config *config.Config
+	srv    *http.Server
 }
 
 // NewServer creates new Server
 func NewServer(logger *slog.Logger, configPath string) (*Server, error) {
 	_ = godotenv.Load()
+	router := gin.New()
 
 	// Load configuration
 	cfg, err := config.LoadConfigWithEnvOverrides(configPath)
@@ -30,6 +36,7 @@
 	}
 
 	s := &Server{
+		router: router,
 		logger: logger,
 		config: cfg,
 	}
@@ -43,42 +50,94 @@
 func (s *Server) Start() error {
 	s.logger.Info("Server is starting...")
 
-	// Create task manager using config
-	gitInterface := git.New(s.config, s.logger)
-	taskManager := git_tm.NewGitTaskManager(gitInterface, s.config, s.logger)
+	// Use custom slog-based logger middleware
+	s.router.Use(s.slogLogger())
+	s.router.Use(s.slogRecovery())
 
-	// Create agent manager with config
-	var err error
-	s.manager, err = agent.NewManager(s.config, taskManager, s.logger)
+	application, err := app.NewApp(s.config, s.logger)
 	if err != nil {
-		return fmt.Errorf("failed to create agent manager: %w", err)
+		s.logger.Error("Can't create app", slog.String("error", err.Error()))
+		return fmt.Errorf("can't create new app: %w", err)
+	}
+	s.app = application
+
+	err = api.Init(s.router, application)
+	if err != nil {
+		s.logger.Error("Can't init api", slog.String("error", err.Error()))
+		return fmt.Errorf("can't init api: %w", err)
 	}
 
-	s.manager.StartAllAgents()
+	s.logger.Info("Server is listening on", slog.String("address", s.config.Server.ListenAddress))
+	s.srv = &http.Server{
+		Addr:    ":" + s.config.Server.ListenAddress,
+		Handler: s.router,
+	}
+
+	// Initializing the server in a goroutine so that
+	// it won't block the graceful shutdown handling below
+	go func() {
+		if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+			s.logger.Error("listen: ", slog.String("error", err.Error()))
+		}
+	}()
+
+	s.app.Start()
 	s.logger.Info("Server started successfully", slog.Int("agents", len(s.config.Agents)))
 	return nil
 }
 
+// slogLogger returns a Gin middleware that logs requests using slog
+func (s *Server) slogLogger() gin.HandlerFunc {
+	return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
+		s.logger.Info("HTTP Request",
+			slog.String("method", param.Method),
+			slog.String("path", param.Path),
+			slog.String("ip", param.ClientIP),
+			slog.Int("status", param.StatusCode),
+			slog.String("latency", param.Latency.String()),
+			slog.String("user_agent", param.Request.UserAgent()),
+			slog.String("error", param.ErrorMessage),
+		)
+		return ""
+	})
+}
+
+// slogRecovery returns a Gin middleware that recovers from panics and logs using slog
+func (s *Server) slogRecovery() gin.HandlerFunc {
+	return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
+		if err, ok := recovered.(string); ok {
+			s.logger.Error("Panic recovered",
+				slog.String("error", err),
+				slog.String("method", c.Request.Method),
+				slog.String("path", c.Request.URL.Path),
+				slog.String("ip", c.ClientIP()),
+			)
+		} else {
+			s.logger.Error("Panic recovered",
+				slog.Any("error", recovered),
+				slog.String("method", c.Request.Method),
+				slog.String("path", c.Request.URL.Path),
+				slog.String("ip", c.ClientIP()),
+			)
+		}
+		c.AbortWithStatus(http.StatusInternalServerError)
+	})
+}
+
 // Shutdown method shuts server down
 func (s *Server) Shutdown() {
 	s.logger.Info("Stopping Server...")
 
-	if s.manager != nil {
-		if err := s.manager.Close(); err != nil {
-			s.logger.Error("Error closing manager", slog.String("error", err.Error()))
-		}
+	// The context is used to inform the server it has 5 seconds to finish
+	// the request it is currently handling
+
+	s.app.Stop()
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	if err := s.srv.Shutdown(ctx); err != nil {
+		s.logger.Error("Server forced to shutdown", slog.String("error", err.Error()))
 	}
 
 	s.logger.Info("All agents stopped")
 	s.logger.Info("Server stopped")
 }
-
-// GetManager returns the agent manager instance
-func (s *Server) GetManager() *agent.Manager {
-	return s.manager
-}
-
-// GetConfig returns the server configuration
-func (s *Server) GetConfig() *config.Config {
-	return s.config
-}