CMDC.Skill (cmdc v0.5.1)

Copy Markdown View Source

Skill(技能)元数据 struct + 发现与加载系统。

Skill 是通过 SKILL.md 文件定义的领域知识片段,Agent 在启动时自动发现并按需注入到系统提示词。

SKILL.md 文件格式

---
name: elixir-testing
description: Elixir 项目测试最佳实践
allowed_tools:           # 可选,限制 Skill 激活时 Agent 可用工具
  - read_file
  - grep
  - shell
---

## 测试规范

- 使用 ExUnitdescribe 按功能分组
- Mock 外部服务 Mock 内部模块
...

两级目录

  • 项目级.cmdc/skills/ — 项目特定,优先级高于全局
  • 全局级~/.cmdc/skills/ — 所有项目通用

同名 Skill 以项目级为准(覆盖全局)。

Skill Identity(.skill_id sidecar)

每个 Skill 目录可包含 .skill_id 文件,存储该 Skill 的持久唯一标识。 discover/1 首次发现时自动生成(格式 {name}__imp_{uuid8}),后续读取已有值。 此 ID 可供外部系统(如 cmdc_skill_engine)追踪版本、进化谱系等。

使用示例

# 发现所有 Skills
skills = CMDC.Skill.discover(["./skills", "~/.cmdc/skills"])

# 加载完整内容
{:ok, skill_with_content} = CMDC.Skill.load(skill)

# 生成系统提示词注入片段(仅 name + description,不含完整内容)
snippet = CMDC.Skill.to_prompt_snippet(skills)

Summary

Functions

在给定目录列表中发现所有 SKILL.md 文件,返回解析后的 %Skill{} 列表。

按名称查找 Skill,未找到返回 nil

按 skill_id 查找 Skill,未找到返回 nil

加载单个 Skill 的完整内容(包含 SKILL.md 正文部分)。

生成系统提示词注入片段(仅 name + description 列表,不含完整内容)。

写入(或覆盖)Skill 目录下的 .skill_id sidecar 文件。

Types

t()

@type t() :: %CMDC.Skill{
  allowed_tools: [String.t()] | nil,
  compatibility: String.t() | nil,
  content: String.t() | nil,
  description: String.t(),
  license: String.t() | nil,
  metadata: map(),
  name: String.t(),
  path: String.t(),
  skill_id: String.t() | nil
}

Functions

discover(dirs)

@spec discover([String.t()]) :: [t()]

在给定目录列表中发现所有 SKILL.md 文件,返回解析后的 %Skill{} 列表。

目录列表按优先级从低到高排列(后面的覆盖前面的同名 Skill)。

示例

skills = CMDC.Skill.discover(["~/.cmdc/skills", "./.cmdc/skills"])
# => [%CMDC.Skill{name: "elixir-testing", ...}, ...]

find(skills, name)

@spec find([t()], String.t()) :: t() | nil

按名称查找 Skill,未找到返回 nil

find_by_id(skills, skill_id)

@spec find_by_id([t()], String.t()) :: t() | nil

按 skill_id 查找 Skill,未找到返回 nil

load(skill)

@spec load(t()) :: {:ok, t()} | {:error, String.t()}

加载单个 Skill 的完整内容(包含 SKILL.md 正文部分)。

如果 Skill 已有 :content,直接返回;否则从文件重新读取。

示例

{:ok, loaded} = CMDC.Skill.load(skill)
loaded.content # => "## 测试规范

..."

to_prompt_snippet(skills)

@spec to_prompt_snippet([t()]) :: String.t()

生成系统提示词注入片段(仅 name + description 列表,不含完整内容)。

供 Agent 启动时注入到系统提示词,引导 LLM 了解可用的 Skills。 LLM 可在需要时通过工具加载完整内容。

示例

snippet = CMDC.Skill.to_prompt_snippet(skills)
# =>
# ## 可用 Skills
#
# - **elixir-testing**: Elixir 项目测试最佳实践
# - **git-workflow**: Git 工作流约定

write_skill_id(skill_dir, skill_id)

@spec write_skill_id(String.t(), String.t()) :: :ok | {:error, term()}

写入(或覆盖)Skill 目录下的 .skill_id sidecar 文件。

供外部系统(如 cmdc_skill_engine)在 FIX/DERIVED/CAPTURED 进化后 更新 Skill 身份标识。