# `Nous.Skill`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/skill.ex#L1)

Reusable instruction/capability packages for agents.

Skills inject domain knowledge, tools, and prompt fragments into an agent's
context. They can be defined as Elixir modules, markdown files, or inline structs.

## Skill Groups

Built-in categories for organizing skills:

| Group | Purpose |
|-------|---------|
| `:coding` | Code generation, implementation, refactoring |
| `:review` | Code quality, security scanning |
| `:testing` | Test creation and validation |
| `:debug` | Bug finding and systematic debugging |
| `:git` | Version control operations |
| `:docs` | Documentation generation |
| `:planning` | Architecture and task decomposition |

## Activation Modes

- `:manual` — only activated explicitly by name
- `:auto` — activated at agent init, always on
- `{:on_match, fn}` — activated when user input matches predicate
- `{:on_tag, tags}` — activated when agent has matching tags
- `{:on_glob, patterns}` — activated when working with matching file patterns

## Module-Based Skills

    defmodule MyApp.Skills.CodeReview do
      use Nous.Skill, tags: [:code, :quality], group: :review

      @impl true
      def name, do: "code_review"

      @impl true
      def description, do: "Reviews code for bugs, style, and quality"

      @impl true
      def instructions(_agent, _ctx) do
        "You are a code review specialist..."
      end

      @impl true
      def match?(input), do: String.contains?(input, "review")
    end

## File-Based Skills

Markdown files with YAML frontmatter in a skills directory:

    ---
    name: code_review
    description: Reviews code for quality and bugs
    tags: [code, review]
    group: review
    activation: auto
    ---

    You are a code review specialist...

## Usage

    agent = Agent.new("openai:gpt-4",
      skills: [
        MyApp.Skills.CodeReview,        # module
        "priv/skills/",                 # directory
        {:group, :testing}              # built-in group
      ]
    )

# `activation`

```elixir
@type activation() ::
  :manual
  | :auto
  | {:on_match, (String.t() -&gt; boolean())}
  | {:on_tag, [atom()]}
  | {:on_glob, [String.t()]}
```

# `scope`

```elixir
@type scope() :: :builtin | :project | :personal | :plugin
```

# `status`

```elixir
@type status() :: :discovered | :loaded | :active | :inactive
```

# `t`

```elixir
@type t() :: %Nous.Skill{
  activation: activation(),
  allowed_tools: [String.t()] | nil,
  description: String.t(),
  group: atom() | nil,
  instructions: String.t() | nil,
  model_override: String.t() | nil,
  name: String.t(),
  priority: integer(),
  scope: scope(),
  source: :module | :file | :inline,
  source_ref: module() | String.t() | nil,
  status: status(),
  tags: [atom()],
  tools: [Nous.Tool.t()]
}
```

# `description`

```elixir
@callback description() :: String.t()
```

Called to return the skill description (used for matching).

# `group`
*optional* 

```elixir
@callback group() :: atom() | nil
```

Return the skill group (`:coding`, `:review`, `:testing`, etc.).

# `instructions`

```elixir
@callback instructions(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) ::
  String.t()
```

Called to return the skill instructions injected into the system prompt.

# `match?`
*optional* 

```elixir
@callback match?(input :: String.t()) :: boolean()
```

Check if this skill should activate for the given user input.

# `name`

```elixir
@callback name() :: String.t()
```

Called to return the skill name.

# `tags`
*optional* 

```elixir
@callback tags() :: [atom()]
```

Return tags for categorization and filtering.

# `tools`
*optional* 

```elixir
@callback tools(agent :: Nous.Agent.t(), ctx :: Nous.Agent.Context.t()) :: [Nous.Tool.t()]
```

Called to return tools provided by this skill.

# `__using__`
*macro* 

Set up a module-based skill.

## Options

  * `:tags` - tags for categorization and filtering (default: `[]`)
  * `:group` - skill group (`:coding`, `:review`, `:testing`, ...)
  * `:keywords` - list of lowercase strings; when given, generates a
    `match?/1` that activates the skill if the downcased input contains any
    of them. Define `match?/1` yourself for anything fancier.

# `from_module`

```elixir
@spec from_module(module()) :: t()
```

Build a `Skill.t()` struct from a module implementing the `Nous.Skill` behaviour.

---

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