# `CMDC.Skill`
[🔗](https://github.com/tuplehq/cmdc/blob/v0.4.0/lib/cmdc/skill.ex#L1)

Skill（技能）元数据 struct + 发现与加载系统，对标 DeepAgents SkillsMiddleware。

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

## SKILL.md 文件格式

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

    ## 测试规范

    - 使用 ExUnit，describe 按功能分组
    - 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)

# `t`

```elixir
@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
}
```

# `discover`

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

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

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

## 示例

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

# `find`

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

按名称查找 Skill，未找到返回 `nil`。

# `find_by_id`

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

按 skill_id 查找 Skill，未找到返回 `nil`。

# `load`

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

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

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

## 示例

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

..."

# `to_prompt_snippet`

```elixir
@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`

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

写入（或覆盖）Skill 目录下的 `.skill_id` sidecar 文件。

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
