feat(bugseti): add marketplace MCP root
- add MarketplaceMCPRoot config and UI setting\n- prefer config root before env or auto-discovery\n- thread config root into ethics guard usage
This commit is contained in:
parent
9d3fc213ae
commit
c0fd80a2b6
8 changed files with 65 additions and 19 deletions
|
|
@ -9,6 +9,7 @@ interface Config {
|
|||
notificationsEnabled: boolean;
|
||||
notificationSound: boolean;
|
||||
workspaceDir: string;
|
||||
marketplaceMcpRoot: string;
|
||||
theme: string;
|
||||
autoSeedContext: boolean;
|
||||
workHours?: {
|
||||
|
|
@ -161,6 +162,13 @@ interface Config {
|
|||
<input type="text" class="form-input" [(ngModel)]="config.workspaceDir"
|
||||
placeholder="Leave empty for default">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Marketplace MCP Root</label>
|
||||
<input type="text" class="form-input" [(ngModel)]="config.marketplaceMcpRoot"
|
||||
placeholder="Path to core-agent (optional)">
|
||||
<p class="section-description">Override the marketplace MCP root. Leave empty to auto-detect.</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -308,6 +316,7 @@ export class SettingsComponent implements OnInit {
|
|||
notificationsEnabled: true,
|
||||
notificationSound: true,
|
||||
workspaceDir: '',
|
||||
marketplaceMcpRoot: '',
|
||||
theme: 'dark',
|
||||
autoSeedContext: true,
|
||||
workHours: {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Initialize core services
|
||||
notifyService := bugseti.NewNotifyService()
|
||||
notifyService := bugseti.NewNotifyService(configService)
|
||||
statsService := bugseti.NewStatsService(configService)
|
||||
fetcherService := bugseti.NewFetcherService(configService, notifyService)
|
||||
queueService := bugseti.NewQueueService(configService)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ type Config struct {
|
|||
// Workspace
|
||||
WorkspaceDir string `json:"workspaceDir,omitempty"`
|
||||
DataDir string `json:"dataDir,omitempty"`
|
||||
// Marketplace MCP
|
||||
MarketplaceMCPRoot string `json:"marketplaceMcpRoot,omitempty"`
|
||||
|
||||
// Onboarding
|
||||
Onboarded bool `json:"onboarded"`
|
||||
|
|
@ -96,6 +98,7 @@ func NewConfigService() *ConfigService {
|
|||
MaxConcurrentIssues: 1,
|
||||
AutoSeedContext: true,
|
||||
DataDir: bugsetiDir,
|
||||
MarketplaceMCPRoot: "",
|
||||
UpdateChannel: "stable",
|
||||
AutoUpdate: false,
|
||||
UpdateCheckInterval: 6, // Check every 6 hours
|
||||
|
|
@ -181,6 +184,13 @@ func (c *ConfigService) GetConfig() Config {
|
|||
return *c.config
|
||||
}
|
||||
|
||||
// GetMarketplaceMCPRoot returns the configured marketplace MCP root path.
|
||||
func (c *ConfigService) GetMarketplaceMCPRoot() string {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.config.MarketplaceMCPRoot
|
||||
}
|
||||
|
||||
// SetConfig updates the configuration and saves it.
|
||||
func (c *ConfigService) SetConfig(config Config) error {
|
||||
c.mu.Lock()
|
||||
|
|
|
|||
|
|
@ -26,19 +26,32 @@ type EthicsGuard struct {
|
|||
}
|
||||
|
||||
var (
|
||||
ethicsGuardOnce sync.Once
|
||||
ethicsGuardMu sync.Mutex
|
||||
ethicsGuard *EthicsGuard
|
||||
ethicsGuardRoot string
|
||||
)
|
||||
|
||||
func getEthicsGuard(ctx context.Context) *EthicsGuard {
|
||||
ethicsGuardOnce.Do(func() {
|
||||
guard := loadEthicsGuard(ctx)
|
||||
if guard == nil {
|
||||
guard = &EthicsGuard{}
|
||||
}
|
||||
ethicsGuard = guard
|
||||
})
|
||||
return getEthicsGuardWithRoot(ctx, "")
|
||||
}
|
||||
|
||||
func getEthicsGuardWithRoot(ctx context.Context, rootHint string) *EthicsGuard {
|
||||
rootHint = strings.TrimSpace(rootHint)
|
||||
|
||||
ethicsGuardMu.Lock()
|
||||
defer ethicsGuardMu.Unlock()
|
||||
|
||||
if ethicsGuard != nil && ethicsGuardRoot == rootHint {
|
||||
return ethicsGuard
|
||||
}
|
||||
|
||||
guard := loadEthicsGuard(ctx, rootHint)
|
||||
if guard == nil {
|
||||
guard = &EthicsGuard{}
|
||||
}
|
||||
|
||||
ethicsGuard = guard
|
||||
ethicsGuardRoot = rootHint
|
||||
if ethicsGuard == nil {
|
||||
return &EthicsGuard{}
|
||||
}
|
||||
|
|
@ -67,14 +80,14 @@ func guardFromMarketplace(ctx context.Context, client marketplaceClient) *Ethics
|
|||
}
|
||||
}
|
||||
|
||||
func loadEthicsGuard(ctx context.Context) *EthicsGuard {
|
||||
func loadEthicsGuard(ctx context.Context, rootHint string) *EthicsGuard {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||
defer cancel()
|
||||
client, err := newMarketplaceClient(ctx)
|
||||
client, err := newMarketplaceClient(ctx, rootHint)
|
||||
if err != nil {
|
||||
return &EthicsGuard{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,12 +60,12 @@ type mcpMarketplaceClient struct {
|
|||
client *client.Client
|
||||
}
|
||||
|
||||
func newMarketplaceClient(ctx context.Context) (marketplaceClient, error) {
|
||||
func newMarketplaceClient(ctx context.Context, rootHint string) (marketplaceClient, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
command, args, err := resolveMarketplaceCommand()
|
||||
command, args, err := resolveMarketplaceCommand(rootHint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -180,12 +180,17 @@ func toolResultMessage(result *mcp.CallToolResult) string {
|
|||
return "unknown error"
|
||||
}
|
||||
|
||||
func resolveMarketplaceCommand() (string, []string, error) {
|
||||
func resolveMarketplaceCommand(rootHint string) (string, []string, error) {
|
||||
if command := strings.TrimSpace(os.Getenv("BUGSETI_MCP_COMMAND")); command != "" {
|
||||
args := strings.Fields(os.Getenv("BUGSETI_MCP_ARGS"))
|
||||
return command, args, nil
|
||||
}
|
||||
|
||||
if root := strings.TrimSpace(rootHint); root != "" {
|
||||
path := filepath.Join(root, "mcp")
|
||||
return "go", []string{"run", path}, nil
|
||||
}
|
||||
|
||||
if root := strings.TrimSpace(os.Getenv("BUGSETI_MCP_ROOT")); root != "" {
|
||||
path := filepath.Join(root, "mcp")
|
||||
return "go", []string{"run", path}, nil
|
||||
|
|
|
|||
|
|
@ -14,13 +14,15 @@ import (
|
|||
type NotifyService struct {
|
||||
enabled bool
|
||||
sound bool
|
||||
config *ConfigService
|
||||
}
|
||||
|
||||
// NewNotifyService creates a new NotifyService.
|
||||
func NewNotifyService() *NotifyService {
|
||||
func NewNotifyService(config *ConfigService) *NotifyService {
|
||||
return &NotifyService{
|
||||
enabled: true,
|
||||
sound: true,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ func (n *NotifyService) Notify(title, message string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
guard := getEthicsGuard(context.Background())
|
||||
guard := getEthicsGuardWithRoot(context.Background(), n.getMarketplaceRoot())
|
||||
safeTitle := guard.SanitizeNotification(title)
|
||||
safeMessage := guard.SanitizeNotification(message)
|
||||
|
||||
|
|
@ -72,6 +74,13 @@ func (n *NotifyService) Notify(title, message string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (n *NotifyService) getMarketplaceRoot() string {
|
||||
if n == nil || n.config == nil {
|
||||
return ""
|
||||
}
|
||||
return n.config.GetMarketplaceMCPRoot()
|
||||
}
|
||||
|
||||
// NotifyIssue sends a notification about a new issue.
|
||||
func (n *NotifyService) NotifyIssue(issue *Issue) error {
|
||||
title := "New Issue Available"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ func (s *SeederService) SeedIssue(issue *Issue) (*IssueContext, error) {
|
|||
if err != nil {
|
||||
log.Printf("Seed skill failed, using fallback: %v", err)
|
||||
// Fallback to basic context preparation
|
||||
guard := getEthicsGuard(context.Background())
|
||||
guard := getEthicsGuardWithRoot(context.Background(), s.config.GetMarketplaceMCPRoot())
|
||||
ctx = s.prepareBasicContext(issue, guard)
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ func (s *SeederService) runSeedSkill(issue *Issue, workDir string) (*IssueContex
|
|||
mcpCtx, mcpCancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer mcpCancel()
|
||||
|
||||
marketplace, err := newMarketplaceClient(mcpCtx)
|
||||
marketplace, err := newMarketplaceClient(mcpCtx, s.config.GetMarketplaceMCPRoot())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ func (s *SubmitService) Submit(submission *PRSubmission) (*PRResult, error) {
|
|||
return nil, fmt.Errorf("work directory not specified")
|
||||
}
|
||||
|
||||
guard := getEthicsGuard(context.Background())
|
||||
guard := getEthicsGuardWithRoot(context.Background(), s.config.GetMarketplaceMCPRoot())
|
||||
issueTitle := guard.SanitizeTitle(issue.Title)
|
||||
|
||||
// Step 1: Ensure we have a fork
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue