目标:打通 skill 的安装 / 发布 / 更新闭环,以及跨项目 skill 快速构建(以 profile 为唯一快速构建入口)
- Skillman 只管理 skill 目录生命周期,不解析 skill 内部格式。
- market 类型只支持
fs与git。 - 安装记录不存
agent,用安装路径作为稳定事实。 - profile 以“路径分组 + skill 列表”表达,不反推 agent。
source_type必须保留:market/worktree。config.json.agents主要用于安装时声明相对路径;配置变更只影响新安装与运行时归类。
~/.skillman/
├── settings.json
├── skillman.lock
└── data/
├── config.json
├── market_subscriptions.json
├── markets/
│ └── <market-name>/
│ └── skills/
├── registry/
│ ├── projects.txt
│ └── installed_skills.json
├── profiles/
│ └── <profile-name>.json
├── pending_prs.json
└── worktrees/
└── <market>-<skill>-<timestamp>/
interface Settings {
schema: 1;
paths: {
data_dir: string;
log_dir: string;
};
defaults: {
default_market?: string;
default_install_mode: "copy" | "link"; // 默认 link
default_agents?: string[]; // 默认 ["codex"]
};
}type MarketType = "fs" | "git";
type SourceType = "market" | "worktree";
type InstallMode = "copy" | "link";
interface GitAuth {
mode: "inherit" | "env" | "ssh" | "token";
env?: Record<string, string>;
}
interface GitConfig {
branch: string; // 默认 main
auth?: GitAuth;
}
interface Market {
name: string;
type: MarketType;
uri: string; // 用户输入源
install_path: string; // 本地 clone / 挂载根目录
path: string; // skills 根目录
git?: GitConfig;
}
interface AgentConfig {
name: string; // codex / claude / gemini / 其他
skill_path: string; // 项目内 skills 根目录;支持相对路径
uninstall_cmd?: string;
}
interface ConfigFile {
schema: 1;
markets: Market[];
agents: AgentConfig[];
}支持变量:
{{project_root}}{{install_root}}{{market}}{{skill_name}}
最小约定:
- 这是一个完整命令模板。
- 若为空:内置
codex/claude/gemini用默认命令;其他 agent 记 warning 并跳过调用。
用于记录“某 market 在某项目哪个 skill 根路径生效过”。
interface MarketSubscription {
market: string;
project_root: string;
install_root: string;
installed_at: string;
updated_at: string;
}
interface MarketSubscriptionsFile {
schema: 1;
subscriptions: MarketSubscription[];
}主键:(market, project_root, install_root)
说明:
- 不存
agent,因为 remove 阶段按“最新 agents 配置路径”动态归类。 - 归类失败的路径进入“未匹配 agent”分组。
interface ProjectInstalledSkill {
market: string;
skill_name: string;
project_root: string;
install_root: string;
install_path: string; // 绝对路径 = install_root + "/" + skill_name
relative_path: string; // 相对 project_root 的路径
mode: InstallMode;
source_type: SourceType;
source_path: string;
version: string; // git commit hash 或 mtime
version_type: "git_commit" | "mtime";
content_hash: string;
installed_at: string;
updated_at: string;
}主键:(market, skill_name, install_path)
说明:
- 同一 skill 安装到多个路径会产生多条记录。
- 不依赖 agent 作为主键或必需字段。
interface ProfileSkillRef {
market: string;
skill_name: string;
mode: InstallMode;
source_type: SourceType;
}
interface ProfilePathGroup {
relative_path: string; // 例如 .claude/skills
skills: ProfileSkillRef[];
}
interface Profile {
schema: 1;
name: string;
description?: string;
items: ProfilePathGroup[];
created_at: string;
updated_at: string;
source_project?: string;
}- 以
relative_path聚合已安装记录。 - 每个路径下输出
skills[],元素最小字段为{ market, skill_name, mode, source_type }。
- 通过
projects.txt解析当前project_root(最长前缀匹配)。 - 对每个
item.relative_path计算install_root = project_root + relative_path。 - 逐 skill 安装到该
install_root。 source_type=worktree:- 优先 worktree;
- worktree 不存在则回退到 market;
- market 也不存在则跳过该项并返回 warning。
interface PRConsumer {
project_root: string;
install_root: string;
install_path: string;
mode: InstallMode;
}
interface PendingPR {
market: string;
skill_name: string;
branch: string;
worktree_path: string;
pr_number?: number;
pr_url?: string;
origin_project: string;
consumers: PRConsumer[];
created_at: string;
updated_at: string;
}
interface PendingPRsFile {
schema: 1;
records: PendingPR[];
}主键:(market, skill_name)
规则:
- UTF-8 文本。
- 一行一个
project_root绝对路径。 - 空行忽略。
- 规范化后去重(Windows 忽略大小写)。
- 保留首次写入顺序。
interface AgentBucket {
agent: string; // codex / claude / gemini / 其他配置名 / unmatched
install_roots: string[];
}
function classifyInstallRoot(
projectRoot: string,
installRoot: string,
agents: AgentConfig[]
): string {
for (const agent of agents) {
const expected = path.isAbsolute(agent.skill_path)
? path.normalize(agent.skill_path)
: path.normalize(path.join(projectRoot, agent.skill_path));
if (expected === path.normalize(installRoot)) return agent.name;
}
return "unmatched";
}说明:
- 保留
codex/claude/gemini/其他 agents归类。 - 任意安装路径匹配不上时进入
unmatched。
config.json(markets, agents)
│
├── install(按选定 agent 解析 install_root)
▼
ProjectInstalledSkill(key: market+skill+install_path)
│
├── snapshot/apply(按 relative_path 分组)
├── archive/worktree(source_type 生命周期)
└── 聚合
▼
MarketSubscription(key: market+project_root+install_root)
market remove:
1) 按 market 拉取记录
2) 处理 link/copy 清理
3) 按最新 agents 路径归类(含 unmatched)并执行卸载调用
4) 清理 subscription/pending/config