skillctrl 采用模块化、可扩展的架构设计,核心是一个统一的 Canonical Model(规范模型),通过 Adapter(适配器) 模式支持多个 AI 编程助手。
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ (skillctrl-cli) │
│ Commands: source, list, install, uninstall, import, export │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Catalog │ │ State │ │ Git │
│ Parser │ │ Management │ │ Operations │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└────────────────┼────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ Core Traits │
│ Component | Adapter | Importer | Exporter │
└────────────────────────┬────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Claude │ │ Codex │ │ Cursor │
│ Adapter │ │ Adapter │ │ Adapter │
└──────────────┘ └──────────────┘ └──────────────┘
所有可安装内容的抽象表示。
pub trait Component: Any + Send + Sync {
fn kind(&self) -> ComponentKind;
fn id(&self) -> &str;
fn validate(&self) -> ValidationReport;
fn dependencies(&self) -> &[ComponentDependency];
}组件类型:
Skill- AI 能力/技能Rule- 行为规则Command- 命令McpServer- MCP 服务器Hook- 生命周期钩子Resource- 资源文件Agent- AI 代理
将规范模型转换为特定 AI 软件格式的接口。
pub trait Adapter {
fn endpoint(&self) -> Endpoint;
fn capabilities(&self) -> AdapterCapabilities;
fn pre_install(&self, ctx: &InstallContext) -> Result<HookResult>;
fn post_install(&self, result: &InstallResult) -> Result<HookResult>;
}
pub trait InstallAdapter: Adapter {
async fn plan_install(&self, bundle: &BundleManifest, ctx: &InstallContext)
-> Result<InstallPlan>;
async fn apply_install(&self, plan: &InstallPlan) -> Result<InstallResult>;
}适配器职责:
- 解析 bundle manifest
- 规划文件安装位置
- 处理格式转换
- 执行文件操作
- 记录安装状态
从现有配置扫描并转换为规范格式的接口。
pub trait Importer {
fn endpoint(&self) -> Endpoint;
async fn scan(&self, req: &ScanRequest) -> Result<DetectedArtifacts>;
async fn plan_import(&self, req: &ImportRequest, artifacts: &DetectedArtifacts>)
-> Result<ImportPlan>;
async fn apply_import(&self, req: &ApplyImportRequest) -> Result<ImportResult>;
}导入器职责:
- 扫描配置目录
- 识别组件类型
- 提取组件内容
- 生成 bundle manifest
- 写入规范化目录
将 bundle 导出为原生 marketplace 格式。
pub trait Exporter {
fn endpoint(&self) -> Endpoint;
fn supported_formats(&self) -> Vec<ExportFormat>;
async fn plan_export(&self, req: &ExportRequest) -> Result<ExportPlan>;
async fn apply_export(&self, plan: &ExportPlan) -> Result<ExportResult>;
}┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ CLI │───▶│ Catalog │───▶│ Adapter │───▶│ Files │
│ Command │ │ Load │ │ Plan │ │ Write │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │
▼ │
┌─────────┐ │
│ Bundle │ │
│ Manifest│ │
└─────────┘ │
│ │
└───────────────────────────────┘
│
▼
┌─────────┐
│ State │
│ Record │
└─────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ CLI │───▶│Importer │───▶│Scanner │───▶│ Artifacts│
│ Command │ │ Plan │ │ │ │ Detected│
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ Bundle │ │ Components│
│ Generate│ │ Copy │
└─────────┘ └─────────┘
canonical/ .claude/
├── skills/ ├── skills/
│ └── {id}/ │ └── {id}/
│ └── SKILL.md │ └── SKILL.md
├── rules/ ├── rules/
│ └── {id}.md │ └── {id}.md
├── commands/ ├── commands/
│ └── {id}.md │ └── {id}.md
├── agents/ ├── agents/
│ └── {id}/ │ └── {id}/
├── hooks/ ├── settings.json (merged)
└── mcp/ └── .mcp.json (merged)
└── {id}.json
canonical/ .codex/
├── skills/ ├── skills/
│ └── {id}/ │ └── {id}/
│ └── SKILL.md │ └── SKILL.md
├── rules/ ├── AGENTS.md (merged)
│ └── {id}.md
└── mcp/ └── config.toml (merged)
└── {id}.json (mcpServers section)
canonical/ .cursor/
├── rules/ ├── rules/
│ └── {id}.md │ └── {id}.mdc
└── skills/ (converted to .mdc rules)
└── {id}/
使用 SQLite 持久化:
-- 源配置
CREATE TABLE sources (
name TEXT PRIMARY KEY,
repo_url TEXT NOT NULL,
branch TEXT NOT NULL,
cache_path TEXT NOT NULL,
last_commit TEXT,
updated_at TEXT
);
-- 安装记录
CREATE TABLE installations (
id INTEGER PRIMARY KEY,
bundle_id TEXT NOT NULL,
bundle_version TEXT NOT NULL,
source_name TEXT,
endpoint TEXT NOT NULL,
scope TEXT NOT NULL,
project_path TEXT,
installed_at TEXT NOT NULL,
files_created TEXT NOT NULL,
backup_path TEXT,
UNIQUE(bundle_id, endpoint, scope, project_path)
);
-- 文件记录
CREATE TABLE files (
id INTEGER PRIMARY KEY,
installation_id INTEGER NOT NULL,
path TEXT NOT NULL,
original_hash TEXT,
FOREIGN KEY(installation_id) REFERENCES installations(id)
);- 创建
skillctrl-adapter-{name}crate - 实现
Adapter、InstallAdaptertraits - 创建
skillctrl-importer-{name}crate - 实现
Importertrait - 在 CLI 中注册
- 扩展
ComponentKindenum - 更新各 adapter 的
capabilities() - 实现安装/导入逻辑
pub trait Middleware: Send + Sync + 'static {
async fn before_install(
&self,
req: &InstallRequest,
ctx: &mut InstallContext,
) -> Result<()>;
async fn after_install(
&self,
result: &InstallResult,
) -> Result<InstallResult>;
}示例中间件:
ConflictDetectionMiddlewareBackupMiddlewareValidationMiddlewareLoggingMiddleware
所有文件操作使用 tokio::fs 或 spawn_blocking:
let content = tokio::task::spawn_blocking(move || {
std::fs::read_to_string(path)
}).await??;- Git 仓库缓存(按需 fetch)
- Bundle 解析缓存(内存)
- Catalog 索引(未来)
use futures::future::join_all;
let results = join_all(
bundles.iter().map(|b| self.install_bundle(b))
).await;使用统一的 Error 类型:
pub enum Error {
Io(String),
Serialization(String),
Git(String),
Database(String),
Validation(String),
NotFound(String),
Conflict(String),
// ...
}- 输入验证:所有用户输入都经过验证
- 路径安全:使用
camino::Utf8Path防止路径遍历 - 沙箱:支持 sandbox 模式(Codex rules)
- 备份:安装前自动备份
- 签名:未来支持 bundle 签名验证
每个 crate 的 tests/ 模块:
#[cfg(test)]
mod tests {
#[test]
fn test_component_kind() {
assert_eq!(ComponentKind::Skill.to_string(), "skill");
}
}examples/ 中的示例 catalog 和 bundles
未来使用 tempfile 创建临时目录进行测试
- 基础 CLI
- Claude/Codex/Cursor adapters
- 导入/导出框架
- TUI 界面(
ratatui) - 插件系统
- 中间件系统
- 事件系统
- GUI 桌面应用(
tauri) - Web API(
axum) - 云端同步
- 团队协作
欢迎贡献!请阅读 CONTRIBUTING.md 了解详情。
Apache-2.0 OR MIT