package plugin // errors.go 定义插件系统的错误类型和加载结果类型. // // 设计目标: // - 结构化错误(带错误码),让消费层可以按类型分支处理 // - LoadResult 区分致命/非致命,避免单个插件问题阻断其他插件 // - ValidationResult 用于主动校验,与加载流程解耦 // // 错误层级: // - Error(致命):插件无法加载,如清单不存在,依赖循环 // - Warning(非致命):插件加载但有降级,如技能文件部分损坏 import "fmt" // PluginErrorCode 是插件错误码枚举. // 10 种类型覆盖从清单到依赖的全路径错误场景. // // 精妙之处(CLEVER): 用 iota+1 而非 iota,使零值(0)明确表示"无错误", // 避免 PluginErrorCode 的零值被误判为有效错误码. type PluginErrorCode int const ( // ErrManifestNotFound plugin.json 文件不存在 ErrManifestNotFound PluginErrorCode = iota + 1 // ErrManifestInvalid plugin.json 格式非法(JSON 语法错误) ErrManifestInvalid // ErrManifestValidation 清单内容验证失败(必填字段缺失,非法字符等) ErrManifestValidation // ErrSkillsLoadFailed 技能目录加载失败(读权限,文件损坏等) ErrSkillsLoadFailed // ErrHooksLoadFailed hooks 配置文件加载失败 ErrHooksLoadFailed // ErrMCPConfigInvalid MCP 服务器配置无效(缺少必填字段) ErrMCPConfigInvalid // ErrDependencyMissing 依赖的插件未找到(未安装或未在搜索路径) ErrDependencyMissing // ErrDependencyCycle 依赖关系中存在循环(A→B→A) ErrDependencyCycle // ErrDependencyCrossSource 跨来源依赖不允许(项目级插件依赖用户级插件) // 历史包袱(LEGACY): 当前以插件名前缀推断来源,未来应改为插件注册时携带 // 显式的 Source 字段,以实现精确的来源隔离. ErrDependencyCrossSource // ErrBuiltinConflict 与内置插件名称冲突 ErrBuiltinConflict // ErrConfigValidation 插件配置验证失败(Required 字段缺失 / 类型不匹配). // 12.6 Plugin Config Schema - 用户提供的配置不满足 plugin.json 中 config_schema 的约束. ErrConfigValidation ) // PluginError 是一个结构化的插件错误,包含错误码,上下文和原始原因. // 实现 error 接口,可直接用于 errors.Is/As 链. type PluginError struct { // Code 错误分类码 Code PluginErrorCode // PluginName 触发错误的插件名称 PluginName string // Message 人类可读的错误描述 Message string // Cause 底层原因(可选) Cause error } // Error 实现 error 接口. func (e PluginError) Error() string { if e.Cause != nil { return fmt.Sprintf("plugin %q [%s]: %s: %v", e.PluginName, e.codeName(), e.Message, e.Cause) } return fmt.Sprintf("plugin %q [%s]: %s", e.PluginName, e.codeName(), e.Message) } // Unwrap 实现 errors.Unwrap,支持 errors.Is/As 链式检查. func (e PluginError) Unwrap() error { return e.Cause } // codeName 返回错误码的可读名称,用于日志和错误消息. func (e PluginError) codeName() string { switch e.Code { case ErrManifestNotFound: return "manifest_not_found" case ErrManifestInvalid: return "manifest_invalid" case ErrManifestValidation: return "manifest_validation" case ErrSkillsLoadFailed: return "skills_load_failed" case ErrHooksLoadFailed: return "hooks_load_failed" case ErrMCPConfigInvalid: return "mcp_config_invalid" case ErrDependencyMissing: return "dependency_missing" case ErrDependencyCycle: return "dependency_cycle" case ErrDependencyCrossSource: return "dependency_cross_source" case ErrBuiltinConflict: return "builtin_conflict" case ErrConfigValidation: return "config_validation" default: return fmt.Sprintf("unknown(%d)", int(e.Code)) } } // LoadResult 是 LoadAll 操作的完整结果. // // 升华改进(ELEVATED): 早期方案直接返回 error,第一个失败就停止. // 我们区分 Enabled/Disabled/Errors/Warnings,使单个插件的问题不阻断其他插件加载. // CLI 消费者可以在启动时打印 Warnings 而不中断;SDK 消费者可以编程式处理 Errors. // 原方案 <返回 []error> - 否决原因:丢失了"哪些加载成功了"的信息,消费层无法区分部分成功. type LoadResult struct { // Enabled 成功加载并启用的插件列表 Enabled []*Plugin // Disabled 因依赖降级而被禁用的插件列表 // 精妙之处(CLEVER): 保留禁用插件而非直接丢弃,使消费层可以展示"因 X 禁用了 Y"的诊断信息. Disabled []*Plugin // Errors 致命错误列表(插件完全无法加载) Errors []PluginError // Warnings 非致命警告列表(插件加载但有降级) Warnings []PluginError } // HasErrors 返回是否有致命错误. func (r *LoadResult) HasErrors() bool { return len(r.Errors) > 0 } // Summary 返回加载结果的人类可读摘要. // 适合在启动日志或 CLI 输出中展示. func (r *LoadResult) Summary() string { return fmt.Sprintf( "plugins: %d enabled, %d disabled, %d errors, %d warnings", len(r.Enabled), len(r.Disabled), len(r.Errors), len(r.Warnings), ) } // ValidationResult 是 ValidateManifest 操作的结果. // 与 LoadResult 分离,支持在实际加载前做"预检". type ValidationResult struct { // Valid 清单是否通过所有验证 Valid bool // Errors 阻断加载的验证错误 Errors []PluginError // Warnings 不阻断加载的警告(如建议字段缺失) Warnings []PluginError } // addError 添加一条验证错误(内部辅助). func (vr *ValidationResult) addError(code PluginErrorCode, plugin, msg string, cause error) { vr.Valid = false vr.Errors = append(vr.Errors, PluginError{ Code: code, PluginName: plugin, Message: msg, Cause: cause, }) } // addWarning 添加一条非致命警告(内部辅助). func (vr *ValidationResult) addWarning(code PluginErrorCode, plugin, msg string, cause error) { vr.Warnings = append(vr.Warnings, PluginError{ Code: code, PluginName: plugin, Message: msg, Cause: cause, }) }