# SkillSource CRD

The `SkillSource` custom resource fetches [AgentSkills.io](https://agentskills.io/specification)-formatted skill content from an upstream source (Git, OCI, or ConfigMap) into the workspace content PVC, where `PromptPack` resources can reference it via `spec.skills`.

A skill is a directory containing a `SKILL.md` file with YAML frontmatter (`name`, `description`, optional `allowed-tools`, `metadata`) and a Markdown body that PromptKit loads on demand.

**Short name:** `skl` (e.g. `kubectl get skl`).

## Example

```yaml
apiVersion: omnia.altairalabs.ai/v1alpha1
kind: SkillSource
metadata:
  name: anthropic-skills
  namespace: dev-agents
spec:
  type: git
  git:
    url: https://github.com/anthropic/skills
    ref:
      tag: v1.4.0
  interval: 1h
  timeout: 5m
  targetPath: skills/anthropic
  filter:
    include:
      - "ai-safety/*"
      - "pdf-processing/*"
    exclude:
      - "**/draft-*"
```

## Spec

| Field | Type | Description |
|---|---|---|
| `type` | `git` \| `oci` \| `configmap` | Required. Selects the source variant; the matching `git`/`oci`/`configMap` block must be set (CEL-validated). |
| `git` | object | Git source: `url` (`http://`, `https://` or `ssh://`), optional `ref` (`branch`/`tag`/`commit`; branch wins over tag, tag over commit; default branch when unset), `path` (subdirectory), `secretRef` (`username`/`password` for HTTPS, `identity`/`known_hosts` for SSH), or instead `connectionRef`, a [Connection](/reference/core/connection/#git-sources) whose hosts include the repository's. |
| `oci` | object | OCI source: `url` (`oci://registry/repo:tag` or `@digest`), `insecure` (default `false`), `secretRef` (`username`/`password` or `.dockerconfigjson`). |
| `configMap` | object | ConfigMap source: `name`. Every key in the ConfigMap's `data` and `binaryData` becomes a file, with `__` in a key decoded to `/` (e.g. `pdf__SKILL.md` ⇒ `pdf/SKILL.md`). Editing the ConfigMap triggers a re-sync. |
| `interval` | duration | Required. How often a `git` or `oci` source fetches (e.g. `1h`); pattern `^([0-9]+(\.[0-9]+)?(ms\|s\|m\|h))+$`. A source fetches when `status.nextFetchTime` passes, and sooner when its spec changes, its last fetch failed, a Connection it fetches with changes, the operator restarts, or its synced content is missing from the volume. A `configmap` source applies an edit to its ConfigMap at once. |
| `timeout` | duration | Per-fetch timeout (default `60s`). |
| `suspend` | bool | Stop reconciling when `true` (default `false`). |
| `targetPath` | string | Path under the workspace content PVC where synced content lands. Defaults to `skills/{source-name}`. Every sync creates a content-addressable version. |
| `filter` | object | Narrows which synced skills are exposed. It decides `skillCount` and the duplicate-name check, and a PromptPack referencing the source sees only the skills it keeps; `spec.skills[].include` on the PromptPack narrows further. The synced tree on the PVC still holds every skill. See below. |

### Filter

```yaml
filter:
  include:        # globs matched against each skill's directory path; empty = include all
    - "safety-*"
  exclude:        # path globs applied after include
    - "**/draft-*"
  names:          # exact frontmatter `name:` matches; empty = no name filter
    - "ai-safety"
```

Globs use Go `filepath.Match` syntax: `*` does not cross a `/`, and `**` is not
recursive (it behaves like `*`), so `**/draft-*` matches `x/draft-y` but not
`draft-y` or `x/y/draft-z`.

## Status

| Field | Description |
|---|---|
| `phase` | `Ready` after a successful fetch and sync, `Error` when the fetch or sync fails (retried after one minute). |
| `observedGeneration` | Last spec generation the controller has processed. |
| `artifact` | Synced `revision`, `contentPath` (`<targetPath>/.arena/versions/<version>`), `version` (first 12 hex characters of the content's SHA-256), `checksum`, `size`, `lastUpdateTime`. |
| `skillCount` | Number of skills retained after the filter. |
| `conditions[type=SourceAvailable]` | True when the upstream is reachable and the latest fetch succeeded. |
| `conditions[type=ContentValid]` | True when every `SKILL.md` parses cleanly and there are no duplicate names within the source. |
| `conditions[type=Ready]` | Roll-up: True when the fetch succeeded and the content is valid. |
| `lastFetchTime` / `nextFetchTime` | Timestamps of the most recent and next scheduled fetch. |

See [Workspace content layout § Skills](/explanation/platform/workspace-content-layout/#skills) for how a synced `SkillSource` reaches a running agent.

## Cross-namespace policy

SkillSource resolution is scoped to the PromptPack's own NAMESPACE, not to a 'workspace' — a namespace's workspace membership is only a label an operator can set, and multiple namespaces can share one workspace name. To share the same upstream content across namespaces you need a SkillSource per NAMESPACE, even when those namespaces belong to the same workspace.

## Related

- [`PromptPack` CRD reference](/reference/core/promptpack/)
- [Use Skills how-to](/how-to/agents/use-skills/)
- [PromptKit Skills concepts](https://promptkit.altairalabs.ai/concepts/skills/)