2d7f814a23
Slice 1: internal/service is the one capability layer both the HTTP API and the MCP server call (AGENT.md 1.7); /api/repos and /api/repo route through it. Slice 2: internal/mcp serves an MCP server over Streamable HTTP at /mcp (go-sdk v1.8.0) with read tools list_repos and get_repo as thin adapters over the service, plus a round-trip test. Enabled air polling so hot reload works across the Docker-on-Windows bind mount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.6 KiB
Go
48 lines
1.6 KiB
Go
// Package service is the ONE capability layer behind both the HTTP API and the
|
|
// MCP server (AGENT.md §1.7). HTTP handlers and MCP tool handlers are thin
|
|
// adapters that call these methods; Git/forge logic never lives in a handler.
|
|
// Everything here goes through the internal/git (and later internal/forge)
|
|
// boundaries and obeys the safety rules (§1.4).
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
|
|
"gitmanager/internal/git"
|
|
"gitmanager/internal/repos"
|
|
)
|
|
|
|
// Service holds the shared dependencies the capabilities need.
|
|
type Service struct {
|
|
git *git.CLI
|
|
index *repos.Index
|
|
}
|
|
|
|
// New builds a Service over the git boundary and the scanner's repo index.
|
|
func New(g *git.CLI, index *repos.Index) *Service {
|
|
return &Service{git: g, index: index}
|
|
}
|
|
|
|
// ListRepos returns a snapshot of every discovered repository.
|
|
func (s *Service) ListRepos() []repos.State {
|
|
return s.index.List()
|
|
}
|
|
|
|
// GetRepo returns the cached state for one repo, or false if it is not indexed.
|
|
// The path is cleaned so separator style does not defeat the exact-match lookup.
|
|
func (s *Service) GetRepo(path string) (repos.State, bool) {
|
|
return s.index.Get(filepath.Clean(path))
|
|
}
|
|
|
|
// RepoDetail returns the enriched detail (branches, commits, remotes) for one
|
|
// repo. The second return is false when the path is not an indexed repository —
|
|
// we never run git against an arbitrary caller-supplied path (§1.3).
|
|
func (s *Service) RepoDetail(ctx context.Context, path string) (repos.Detail, bool) {
|
|
base, ok := s.index.Get(filepath.Clean(path))
|
|
if !ok {
|
|
return repos.Detail{}, false
|
|
}
|
|
return repos.BuildDetail(ctx, s.git, base), true
|
|
}
|