feat(framework): add service layer to git and agentic packages
- Add pkg/framework/framework.go for cleaner imports
- Add pkg/git/service.go with Core service wrapper
- Add pkg/agentic/service.go with AI/Claude service wrapper
- Services use IPC pattern with ACTION() dispatch
Usage:
import "github.com/host-uk/core/pkg/framework"
app, _ := framework.New(
framework.WithService(git.NewService(git.ServiceOptions{})),
framework.WithServiceLock(),
)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
a0f4baafad
commit
c21a271dcf
3 changed files with 276 additions and 0 deletions
107
pkg/agentic/service.go
Normal file
107
pkg/agentic/service.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/host-uk/core/pkg/framework"
|
||||
)
|
||||
|
||||
// Actions for AI service IPC
|
||||
|
||||
// ActionCommit requests Claude to create a commit.
|
||||
type ActionCommit struct {
|
||||
Path string
|
||||
Name string
|
||||
CanEdit bool // allow Write/Edit tools
|
||||
}
|
||||
|
||||
// ActionPrompt sends a custom prompt to Claude.
|
||||
type ActionPrompt struct {
|
||||
Prompt string
|
||||
WorkDir string
|
||||
AllowedTools []string
|
||||
}
|
||||
|
||||
// ServiceOptions for configuring the AI service.
|
||||
type ServiceOptions struct {
|
||||
DefaultTools []string
|
||||
}
|
||||
|
||||
// DefaultServiceOptions returns sensible defaults.
|
||||
func DefaultServiceOptions() ServiceOptions {
|
||||
return ServiceOptions{
|
||||
DefaultTools: []string{"Bash", "Read", "Glob", "Grep"},
|
||||
}
|
||||
}
|
||||
|
||||
// Service provides AI/Claude operations as a Core service.
|
||||
type Service struct {
|
||||
*framework.ServiceRuntime[ServiceOptions]
|
||||
}
|
||||
|
||||
// NewService creates an AI service factory.
|
||||
func NewService(opts ServiceOptions) func(*framework.Core) (any, error) {
|
||||
return func(c *framework.Core) (any, error) {
|
||||
return &Service{
|
||||
ServiceRuntime: framework.NewServiceRuntime(c, opts),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnStartup registers action handlers.
|
||||
func (s *Service) OnStartup(ctx context.Context) error {
|
||||
s.Core().RegisterAction(s.handle)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handle(c *framework.Core, msg framework.Message) error {
|
||||
switch m := msg.(type) {
|
||||
case ActionCommit:
|
||||
return s.handleCommit(m)
|
||||
case ActionPrompt:
|
||||
return s.handlePrompt(m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handleCommit(action ActionCommit) error {
|
||||
prompt := Prompt("commit")
|
||||
|
||||
tools := "Bash,Read,Glob,Grep"
|
||||
if action.CanEdit {
|
||||
tools = "Bash,Read,Write,Edit,Glob,Grep"
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(context.Background(), "claude", "-p", prompt, "--allowedTools", tools)
|
||||
cmd.Dir = action.Path
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (s *Service) handlePrompt(action ActionPrompt) error {
|
||||
tools := "Bash,Read,Glob,Grep"
|
||||
if len(action.AllowedTools) > 0 {
|
||||
tools = ""
|
||||
for i, t := range action.AllowedTools {
|
||||
if i > 0 {
|
||||
tools += ","
|
||||
}
|
||||
tools += t
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(context.Background(), "claude", "-p", action.Prompt, "--allowedTools", tools)
|
||||
if action.WorkDir != "" {
|
||||
cmd.Dir = action.WorkDir
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
67
pkg/framework/framework.go
Normal file
67
pkg/framework/framework.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// Package framework provides the Core DI/service framework.
|
||||
// Import this package for cleaner access to the framework types.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// import "github.com/host-uk/core/pkg/framework"
|
||||
//
|
||||
// app, _ := framework.New(
|
||||
// framework.WithServiceLock(),
|
||||
// )
|
||||
package framework
|
||||
|
||||
import (
|
||||
core "github.com/host-uk/core/pkg/framework/core"
|
||||
)
|
||||
|
||||
// Re-export core types for cleaner imports
|
||||
type (
|
||||
Core = core.Core
|
||||
Option = core.Option
|
||||
Message = core.Message
|
||||
Startable = core.Startable
|
||||
Stoppable = core.Stoppable
|
||||
Config = core.Config
|
||||
Display = core.Display
|
||||
WindowOption = core.WindowOption
|
||||
Features = core.Features
|
||||
Contract = core.Contract
|
||||
Error = core.Error
|
||||
ServiceRuntime[T any] = core.ServiceRuntime[T]
|
||||
Runtime = core.Runtime
|
||||
ServiceFactory = core.ServiceFactory
|
||||
)
|
||||
|
||||
// Re-export core functions
|
||||
var (
|
||||
New = core.New
|
||||
WithService = core.WithService
|
||||
WithName = core.WithName
|
||||
WithApp = core.WithApp
|
||||
WithAssets = core.WithAssets
|
||||
WithServiceLock = core.WithServiceLock
|
||||
App = core.App
|
||||
E = core.E
|
||||
NewRuntime = core.NewRuntime
|
||||
NewWithFactories = core.NewWithFactories
|
||||
)
|
||||
|
||||
// NewServiceRuntime creates a new ServiceRuntime for a service.
|
||||
func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] {
|
||||
return core.NewServiceRuntime(c, opts)
|
||||
}
|
||||
|
||||
// Re-export generic functions
|
||||
func ServiceFor[T any](c *Core, name string) (T, error) {
|
||||
return core.ServiceFor[T](c, name)
|
||||
}
|
||||
|
||||
func MustServiceFor[T any](c *Core, name string) T {
|
||||
return core.MustServiceFor[T](c, name)
|
||||
}
|
||||
|
||||
// Action types
|
||||
type (
|
||||
ActionServiceStartup = core.ActionServiceStartup
|
||||
ActionServiceShutdown = core.ActionServiceShutdown
|
||||
)
|
||||
102
pkg/git/service.go
Normal file
102
pkg/git/service.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/host-uk/core/pkg/framework"
|
||||
)
|
||||
|
||||
// Actions for git service IPC
|
||||
|
||||
// ActionStatus requests git status for paths.
|
||||
type ActionStatus struct {
|
||||
Paths []string
|
||||
Names map[string]string
|
||||
}
|
||||
|
||||
// ActionPush requests git push for a path.
|
||||
type ActionPush struct{ Path, Name string }
|
||||
|
||||
// ActionPull requests git pull for a path.
|
||||
type ActionPull struct{ Path, Name string }
|
||||
|
||||
// ServiceOptions for configuring the git service.
|
||||
type ServiceOptions struct {
|
||||
WorkDir string
|
||||
}
|
||||
|
||||
// Service provides git operations as a Core service.
|
||||
type Service struct {
|
||||
*framework.ServiceRuntime[ServiceOptions]
|
||||
lastStatus []RepoStatus
|
||||
}
|
||||
|
||||
// NewService creates a git service factory.
|
||||
func NewService(opts ServiceOptions) func(*framework.Core) (any, error) {
|
||||
return func(c *framework.Core) (any, error) {
|
||||
return &Service{
|
||||
ServiceRuntime: framework.NewServiceRuntime(c, opts),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnStartup registers action handlers.
|
||||
func (s *Service) OnStartup(ctx context.Context) error {
|
||||
s.Core().RegisterAction(s.handle)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handle(c *framework.Core, msg framework.Message) error {
|
||||
switch m := msg.(type) {
|
||||
case ActionStatus:
|
||||
return s.handleStatus(m)
|
||||
case ActionPush:
|
||||
return s.handlePush(m)
|
||||
case ActionPull:
|
||||
return s.handlePull(m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handleStatus(action ActionStatus) error {
|
||||
ctx := context.Background()
|
||||
statuses := Status(ctx, StatusOptions{
|
||||
Paths: action.Paths,
|
||||
Names: action.Names,
|
||||
})
|
||||
s.lastStatus = statuses
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) handlePush(action ActionPush) error {
|
||||
return Push(context.Background(), action.Path)
|
||||
}
|
||||
|
||||
func (s *Service) handlePull(action ActionPull) error {
|
||||
return Pull(context.Background(), action.Path)
|
||||
}
|
||||
|
||||
// Status returns last status result.
|
||||
func (s *Service) Status() []RepoStatus { return s.lastStatus }
|
||||
|
||||
// DirtyRepos returns repos with uncommitted changes.
|
||||
func (s *Service) DirtyRepos() []RepoStatus {
|
||||
var dirty []RepoStatus
|
||||
for _, st := range s.lastStatus {
|
||||
if st.Error == nil && st.IsDirty() {
|
||||
dirty = append(dirty, st)
|
||||
}
|
||||
}
|
||||
return dirty
|
||||
}
|
||||
|
||||
// AheadRepos returns repos with unpushed commits.
|
||||
func (s *Service) AheadRepos() []RepoStatus {
|
||||
var ahead []RepoStatus
|
||||
for _, st := range s.lastStatus {
|
||||
if st.Error == nil && st.HasUnpushed() {
|
||||
ahead = append(ahead, st)
|
||||
}
|
||||
}
|
||||
return ahead
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue