- 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.
91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ParsePackageID parses "@scope/name" into (scope, name).
|
|
// The leading "@" on scope is stripped.
|
|
// Examples:
|
|
//
|
|
// "@yao/keeper" → ("yao", "keeper")
|
|
// "@max/tools.search" → ("max", "tools.search")
|
|
func ParsePackageID(id string) (scope, name string, err error) {
|
|
if !strings.HasPrefix(id, "@") {
|
|
return "", "", fmt.Errorf("invalid package ID %q: must start with @", id)
|
|
}
|
|
|
|
parts := strings.SplitN(id[1:], "/", 2)
|
|
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
|
|
return "", "", fmt.Errorf("invalid package ID %q: expected @scope/name", id)
|
|
}
|
|
return parts[0], parts[1], nil
|
|
}
|
|
|
|
// FormatPackageID formats scope and name into "@scope/name".
|
|
func FormatPackageID(scope, name string) string {
|
|
return "@" + scope + "/" + name
|
|
}
|
|
|
|
// PackageDir returns the installation directory for a package relative to appRoot.
|
|
// For assistants: assistants/{scope}/{name}/
|
|
// For mcps: mcps/{scope}/{name}/
|
|
func PackageDir(pkgType, scope, name, appRoot string) string {
|
|
dir := TypeToDir(pkgType)
|
|
namePath := strings.ReplaceAll(name, ".", "/")
|
|
return filepath.Join(appRoot, dir, scope, namePath)
|
|
}
|
|
|
|
// PackageDirRel returns the installation directory relative to appRoot (no leading appRoot prefix).
|
|
func PackageDirRel(pkgType, scope, name string) string {
|
|
dir := TypeToDir(pkgType)
|
|
namePath := strings.ReplaceAll(name, ".", "/")
|
|
return filepath.Join(dir, scope, namePath)
|
|
}
|
|
|
|
// IDFromYaoID converts a Yao dot-separated ID to (scope, name).
|
|
// "yao.keeper" → ("yao", "keeper")
|
|
// "max.tools.search" → ("max", "tools.search")
|
|
func IDFromYaoID(yaoID string) (scope, name string, err error) {
|
|
idx := strings.Index(yaoID, ".")
|
|
if idx <= 0 || idx >= len(yaoID)-1 {
|
|
return "", "", fmt.Errorf("invalid Yao ID %q: expected scope.name", yaoID)
|
|
}
|
|
return yaoID[:idx], yaoID[idx+1:], nil
|
|
}
|
|
|
|
// YaoIDFromPackageID converts "@scope/name" to "scope.name" (Yao dot-separated ID).
|
|
func YaoIDFromPackageID(pkgID string) (string, error) {
|
|
scope, name, err := ParsePackageID(pkgID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return scope + "." + name, nil
|
|
}
|
|
|
|
// PackageIDFromYaoID converts "scope.name" to "@scope/name".
|
|
func PackageIDFromYaoID(yaoID string) (string, error) {
|
|
scope, name, err := IDFromYaoID(yaoID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return FormatPackageID(scope, name), nil
|
|
}
|
|
|
|
// ScopeFromPath extracts the scope from a file path relative to appRoot.
|
|
// "assistants/yao/keeper/" → "yao"
|
|
// "mcps/max/rag-tools/" → "max"
|
|
func ScopeFromPath(relPath string) (string, error) {
|
|
parts := strings.Split(filepath.ToSlash(relPath), "/")
|
|
if len(parts) < 2 {
|
|
return "", fmt.Errorf("cannot extract scope from path %q", relPath)
|
|
}
|
|
return parts[1], nil
|
|
}
|
|
|
|
// IsLocalScope returns true if the scope is "@local" or "local".
|
|
func IsLocalScope(scope string) bool {
|
|
return scope == "local" || scope == "@local"
|
|
}
|