# Skill

> Skills: Agent Skills folders any agent of their scope can attach.

```python
check = noclick.Skill.from_dir("./skills/invoice-check")              # SKILL.md plus its scripts and files
support = noclick.Agent.from_name("support", create_if_missing=True, instructions="...", skills=[check])
```

A skill is a folder: a `SKILL.md` whose frontmatter names it (`name`) and
says what it does and when to use it (`description`; `always: true` puts it
whole in every turn), plus any other files. Every save is a new version; an
agent follows the latest unless it holds one (`skill.pinned(3)`). A coding
harness finds its skills where it reads skills; an `sdk` agent sees each
one's name and description every turn and reads one with `skill__read`.

## noclick.Skill

```python
class Skill
```

An Agent Skills folder of the account's (every user's agents may use it)
or one user's, versioned. Delete it and it goes to the trash, off every agent
that attaches it; restoring puts it back on them.

| Attribute | Type | Description |
| --- | --- | --- |
| `id` | `str` |  |
| `name` | `str` | Its name, from its latest `SKILL.md`. |
| `description` | `str` |  |
| `always` | `bool` | Whether it's put whole in every turn of the agents that attach it. |
| `version` | `int` | Its latest version's number. |
| `digest` | `str` | Its latest version's content as a digest: what restarts a coding agent's warm sandbox when it changes. |
| `file_count` | `int` |  |
| `size` | `int` | Its latest version's size in bytes. |
| `attached_to` | `list[SkillAttachment]` | The agents that attach it, and the version each holds (`None`: the latest). |
| `user_id` | `str | None` |  |
| `created_at` | `Any` |  |
| `updated_at` | `Any` |  |

### Skill.create

```python
noclick.Skill.create(files, *, allow_existing=False, user=..., client=None) -> Skill
```

A skill from its folder (path → text, or bytes for a file that isn't text), named by its `SKILL.md`;
unique by name to its user, or to the account outside `as_user`. A taken name raises
`noclick.Conflict` with `existing_id`, or with `allow_existing=True` returns the skill already
there as it is (`save_version` to change it). Up to 200 files and 20 MB, `SKILL.md` up to 64 KB.

Parameters:

- `files` (`Files`, required)
- `allow_existing` (`bool`, default `False`)
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `Skill` The new skill at version 1.

Usage:

```python
check = noclick.Skill.create({
    "SKILL.md": "---\nname: invoice-check\ndescription: Check an invoice against its purchase order.\n---\n1. ...",
    "scripts/match.py": open("match.py").read(),
})
```

### Skill.from_dir

```python
noclick.Skill.from_dir(path, *, allow_existing=False, user=..., client=None) -> Skill
```

A skill from a folder on this machine: its `SKILL.md` and every other file under it (`.git`,
`__pycache__` and `node_modules` left out). A name already taken: as `create`.

Parameters:

- `path` (`str`, required)
- `allow_existing` (`bool`, default `False`)
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `Skill` The new skill (or, with `allow_existing=True`, the one already there).

Usage:

```python
check = noclick.Skill.from_dir("./skills/invoice-check")
```

### Skill.from_name

```python
noclick.Skill.from_name(
    name, *, create_if_missing=False, files=None, user=..., client=None
) -> Skill
```

A lazy reference to the skill called `name`. Acting for a user, that
user's own skill is found first, then the account's. With
`create_if_missing=True` it's created from `files` on first use.

Parameters:

- `name` (`str`, required)
- `create_if_missing` (`bool`, default `False`)
- `files` (`Files | None`, default `None`)
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `Skill` A skill that loads (or is created) the first time it's used. Nothing is sent yet.

Usage:

```python
support = noclick.Agent.from_name("support", create_if_missing=True, instructions="...",
                                  skills=[noclick.Skill.from_name("invoice-check")])
```

### Skill.from_id

```python
noclick.Skill.from_id(skill_id, *, client=None, user=...) -> Skill
```

A lazy reference to a skill by id.

Parameters:

- `skill_id` (`str`, required)
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).

Returns: `Skill` A skill that loads the first time you read a field. Nothing is sent yet.

Usage:

```python
skill = noclick.Skill.from_id(support.skills[0]["id"])
```

### Skill.get

```python
noclick.Skill.get(skill_id, *, user=..., client=None) -> Skill
```

Load a skill by id now; an unknown id raises `noclick.NotFound`.

Parameters:

- `skill_id` (`str`, required)
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `Skill` The skill, loaded: its latest version and the agents that attach it.

Usage:

```python
print(noclick.Skill.get(skill_id).attached_to)
```

### Skill.list

```python
noclick.Skill.list(
    *, agent=None, limit=None, user_filter=None, user=..., client=None
) -> list[Skill]
```

The current user's skills and the account's (or those `user_filter` covers), newest first. With
`agent` (an `Agent` or its id), the skills that agent attaches, in its order.

Parameters:

- `agent` (`Any`, default `None`)
- `limit` (`int | None`, default `None`): Return at most this many.
- `user_filter` (`Any`, default `None`): Which users a read covers: `noclick.ALL_USERS`, `noclick.NO_USER`, a `User`, or your id for one. It filters without acting as that user.
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `list[Skill]` Every matching skill, fetched page by page.

Usage:

```python
for skill in noclick.Skill.list():
    print(skill.name, skill.version, [a.name for a in skill.attached_to])
```

### Skill.list_page

```python
noclick.Skill.list_page(
    *, cursor=None, limit=None, user_filter=None, user=..., client=None
) -> Page
```

One page of skills; pass `next_cursor` back as `cursor`.

Parameters:

- `cursor` (`str | None`, default `None`): The `next_cursor` of the page before; omit it for the first page.
- `limit` (`int | None`, default `None`): Return at most this many.
- `user_filter` (`Any`, default `None`): Which users a read covers: `noclick.ALL_USERS`, `noclick.NO_USER`, a `User`, or your id for one. It filters without acting as that user.
- `user` (`Any`, default: the current user): Who the call acts for. Defaults to the user set by `noclick.as_user` (none: the account itself).
- `client` (`Client | None`, default `None`): The client to send with; defaults to the module client built from `NOCLICK_API_KEY`.

Returns: `Page` A page whose `data` holds the skills and whose `next_cursor` is `None` on the last page.

Usage:

```python
page = noclick.Skill.list_page(limit=50)
```

### Skill.pinned

```python
skill.pinned(version) -> SkillLink
```

This skill held at one version, for an agent's `skills=`.

Parameters:

- `version` (`int`, required)

Returns: `SkillLink` What `skills=` takes for a skill the agent holds at `version`.

Usage:

```python
noclick.Agent.from_name("support").update(skills=[noclick.Skill.from_name("invoice-check").pinned(3)])
```

### Skill.save_version

```python
skill.save_version(files, *, removed=(), replace=False, base_version=None) -> Skill
```

Save a new version: `files` written over the latest (the whole folder with `replace=True`),
`removed` taken out. With `base_version` it saves only while the latest is still that version (else
`noclick.Conflict`). Agents following the latest get it in their next conversation; a running coding
sandbox restarts with it.

Parameters:

- `files` (`Files`, required)
- `removed` (`Sequence[str]`, default `()`)
- `replace` (`bool`, default `False`)
- `base_version` (`int | None`, default `None`)

Returns: `Skill` This skill, at its new version.

Usage:

```python
check = noclick.Skill.from_name("invoice-check")
check.save_version({"scripts/match.py": open("match.py").read()}, base_version=check.version)
```

### Skill.update

```python
skill.update(*, name=None, description=None) -> Skill
```

Rename it or change its description: a new version with its `SKILL.md` frontmatter rewritten.

Parameters:

- `name` (`str | None`, default `None`)
- `description` (`str | None`, default `None`)

Returns: `Skill` This skill, as it is now.

Usage:

```python
noclick.Skill.from_name("invoice-check").update(description="Check invoices and flag those over budget.")
```

### Skill.versions

```python
skill.versions() -> list[SkillVersion]
```

Every version, newest first.

Returns: `list[SkillVersion]` Each version's frontmatter, files, size and who saved it.

Usage:

```python
for v in noclick.Skill.from_name("invoice-check").versions():
    print(v.version, v.created_by.type, v.created_at)
```

### Skill.get_version

```python
skill.get_version(version) -> SkillVersion
```

One version, with its files.

Parameters:

- `version` (`int`, required)

Returns: `SkillVersion` That version; an unknown one raises `noclick.NotFound`.

Usage:

```python
first = noclick.Skill.from_name("invoice-check").get_version(1)
```

### Skill.restore

```python
skill.restore(version) -> Skill
```

A new version holding what `version` held.

Parameters:

- `version` (`int`, required)

Returns: `Skill` This skill, at its new version.

Usage:

```python
noclick.Skill.from_name("invoice-check").restore(2)
```

### Skill.files

```python
skill.files(*, version=None) -> list[SkillFileInfo]
```

The files of a version (the latest unless `version`), by path.

Parameters:

- `version` (`int | None`, default `None`)

Returns: `list[SkillFileInfo]` Each file's path, size and hash.

Usage:

```python
print([f.path for f in noclick.Skill.from_name("invoice-check").files()])
```

### Skill.read

```python
skill.read(path, *, version=None) -> SkillFile
```

One file of a version (the latest unless `version`): its text, or base64 when it isn't UTF-8.

Parameters:

- `path` (`str`, required)
- `version` (`int | None`, default `None`)

Returns: `SkillFile` The file; a path the version doesn't hold raises `noclick.NotFound`.

Usage:

```python
print(noclick.Skill.from_name("invoice-check").read("SKILL.md").content)
```

### Skill.delete

```python
skill.delete() -> TrashItem
```

Delete it: to the trash, restorable for 30 days, and off every agent that attaches it until it's
restored.

Returns: `TrashItem` Its `TrashItem`: `restore()` puts it back, on its agents too.

Usage:

```python
noclick.Skill.from_name("old-checks").delete()
```

## noclick.SkillAttachment

```python
class SkillAttachment
```

An agent that attaches a skill, and the version it holds (`None`: the latest).

| Attribute | Type | Description |
| --- | --- | --- |
| `id` | `str` |  |
| `name` | `str` |  |
| `pin` | `int | None` |  |

## noclick.SkillAuthor

```python
class SkillAuthor
```

Who saved a version: a person in the app (`console`), a key (`api`), or an agent's turn.

| Attribute | Type | Description |
| --- | --- | --- |
| `type` | `str | None` |  |
| `agent_id` | `str | None` |  |
| `agent_name` | `str | None` |  |

## noclick.SkillFile

```python
class SkillFile
```

One file's content: its text, or base64 when it isn't UTF-8 (`encoding`).

| Attribute | Type | Description |
| --- | --- | --- |
| `path` | `str` |  |
| `version` | `int` |  |
| `size` | `int` |  |
| `sha256` | `str` |  |
| `content` | `str` |  |
| `encoding` | `str` |  |

## noclick.SkillFileInfo

```python
class SkillFileInfo
```

One file of a version, as its manifest lists it.

| Attribute | Type | Description |
| --- | --- | --- |
| `path` | `str` |  |
| `size` | `int` |  |
| `sha256` | `str` |  |

## noclick.SkillLink

```python
class SkillLink
```

A skill held at one version on an agent (`skill.pinned(n)`).

| Attribute | Type | Description |
| --- | --- | --- |
| `skill` | `Any` |  |
| `pin` | `int` |  |

## noclick.SkillVersion

```python
class SkillVersion
```

One version of a skill: its frontmatter as that version has it, and its files.

| Attribute | Type | Description |
| --- | --- | --- |
| `version` | `int` |  |
| `digest` | `str` |  |
| `name` | `str` |  |
| `description` | `str` |  |
| `always` | `bool` |  |
| `files` | `list[SkillFileInfo]` |  |
| `size` | `int` |  |
| `created_by` | `SkillAuthor` |  |
| `created_at` | `Any` |  |
| `raw` | `dict[str, Any]` | The response JSON exactly as the API sent it. |
