yao/registry/manager/robot/deps.go
Max 44af6ba759 Enhance command structure with MCP and Robot functionalities
- Introduce new MCP and Robot command groups in the CLI, allowing for better organization of package management commands.
- Add corresponding command descriptions for MCP and Robot functionalities to improve user guidance.
- Update the agent command group to include additional commands for enhanced agent management.
- Modify .gitignore to exclude specific design markdown files from version control.
- Add environment variable for test application path in GitHub workflows to streamline testing setup.
2026-03-03 08:11:43 +08:00

115 lines
3.2 KiB
Go

package robot
import (
"encoding/json"
"strings"
)
// RobotJSON represents the portable fields exported from a robot member record.
type RobotJSON struct {
DisplayName string `json:"display_name,omitempty"`
Bio *string `json:"bio,omitempty"`
SystemPrompt string `json:"system_prompt,omitempty"`
LanguageModel string `json:"language_model,omitempty"`
RobotConfig json.RawMessage `json:"robot_config,omitempty"`
Agents []string `json:"agents,omitempty"`
MCPServers []string `json:"mcp_servers,omitempty"`
}
// robotConfig is a partial parse of robot_config for dependency extraction.
type robotConfig struct {
Resources struct {
Phases map[string]string `json:"phases,omitempty"`
} `json:"resources,omitempty"`
}
// RobotDep represents a dependency extracted from a robot configuration.
type RobotDep struct {
PackageID string // "@scope/name"
Type string // "assistant" or "mcp"
}
// AnalyzeDeps extracts dependencies from a RobotJSON following DESIGN-ROBOT.md rules:
// - phases values: "yao.robot-host" → @yao/robot-host (assistant)
// - agents values: "yao.keeper.fetch" → @yao/keeper (first-layer assistant, take first 2 segments)
// - mcp_servers values: "ark.image.text2img" → @ark/image.text2img (mcp)
// - Excludes: __yao.* prefixed built-in agents
func AnalyzeDeps(robot *RobotJSON) []RobotDep {
seen := map[string]bool{}
var deps []RobotDep
addDep := func(pkgID, depType string) {
if seen[pkgID] {
return
}
seen[pkgID] = true
deps = append(deps, RobotDep{PackageID: pkgID, Type: depType})
}
// Extract from phases (all are assistants)
if len(robot.RobotConfig) > 0 {
var cfg robotConfig
if err := json.Unmarshal(robot.RobotConfig, &cfg); err == nil {
for _, yaoID := range cfg.Resources.Phases {
if isBuiltIn(yaoID) {
continue
}
pkgID := yaoIDToPackageID(yaoID)
if pkgID != "" {
addDep(pkgID, "assistant")
}
}
}
}
// Extract from agents (first-layer assistant)
for _, yaoID := range robot.Agents {
if isBuiltIn(yaoID) {
continue
}
pkgID := agentYaoIDToPackageID(yaoID)
if pkgID != "" {
addDep(pkgID, "assistant")
}
}
// Extract from mcp_servers
for _, yaoID := range robot.MCPServers {
if isBuiltIn(yaoID) {
continue
}
pkgID := yaoIDToPackageID(yaoID)
if pkgID != "" {
addDep(pkgID, "mcp")
}
}
return deps
}
// isBuiltIn returns true for __yao.* prefixed IDs.
func isBuiltIn(yaoID string) bool {
return strings.HasPrefix(yaoID, "__yao.")
}
// yaoIDToPackageID converts "yao.robot-host" → "@yao/robot-host".
// First "." separates scope from name.
func yaoIDToPackageID(yaoID string) string {
idx := strings.Index(yaoID, ".")
if idx <= 0 || idx >= len(yaoID)-1 {
return ""
}
scope := yaoID[:idx]
name := yaoID[idx+1:]
return "@" + scope + "/" + name
}
// agentYaoIDToPackageID converts "yao.keeper.fetch" → "@yao/keeper".
// Takes the first two segments only (first-layer assistant).
func agentYaoIDToPackageID(yaoID string) string {
parts := strings.SplitN(yaoID, ".", 3)
if len(parts) < 2 || parts[0] == "" || parts[1] == "" {
return ""
}
return "@" + parts[0] + "/" + parts[1]
}