> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinework.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Skill

> A skill is a workspace bundle of SKILL.md and reference files that runs load when it is on for the agent, with per-agent overrides and share links.

A skill is a versioned bundle of files, led by `SKILL.md`, that an agent can load during a run. Its id is a 16-character base62 string with no prefix, and most routes also accept its slug. For how skills reach a run, see [Plugins and skills](/concepts/plugins-and-skills).

## States

A skill has no status field. Two conditions are derived.

* **On for an agent.** A run gets the skill when the agent's override, or else `enabledByDefault`, is `true`. A plugin skill also needs its plugin on. `effectiveEnabled` on the agent override list shows the result.
* **Unavailable.** `unavailableReason` is `not_materialized` or `bundle_unreadable` when a run could not place the skill. It returns to `null` once a run places it.

A share link has three states, derived from its timestamps.

| State | Meaning | Ends here |
| - | - | - |
| Active | The link previews and installs the skill. | No |
| Revoked | `revokedAt` is set. The link returns `share_gone`. | Yes |
| Expired | `expiresAt` has passed. The link returns `share_gone`. | Yes |

| From | To | Trigger |
| - | - | - |
| Active | Revoked | You revoke the share. |
| Active | Expired | The `expiresAt` time passes. |

## Fields

<ResponseField name="object" type="string" required>Always `skill`.</ResponseField>
<ResponseField name="id" type="string" required>16-character base62 id.</ResponseField>
<ResponseField name="slug" type="string" required>Stable name. Up to 64 characters of `a-z`, `0-9`, `_` and `-`.</ResponseField>
<ResponseField name="name" type="string" required>Display name.</ResponseField>
<ResponseField name="description" type="string">Short description. Null when unset.</ResponseField>
<ResponseField name="version" type="integer" required>Starts at 1. Goes up by one on each bundle replace.</ResponseField>
<ResponseField name="sourceType" type="string" required>`builtin`, `imported`, `shared`, `marketplace` or `plugin`. A skill added from a git repo reports `imported`. The list route finds those with `sourceType=repo`.</ResponseField>
<ResponseField name="enabledByDefault" type="boolean" required>Workspace default for every agent without an override.</ResponseField>
<ResponseField name="fileCount" type="integer" required>Files in the bundle.</ResponseField>
<ResponseField name="totalBytes" type="integer" required>Bundle size in bytes.</ResponseField>
<ResponseField name="skillMdTokenCount" type="integer">Approximate tokens in `SKILL.md`. Null when not counted yet.</ResponseField>

<ResponseField name="plugin" type="object">
  The plugin that owns the skill. Null for a standalone skill.

  <Expandable title="plugin">
    <ResponseField name="id" type="string" required>Plugin id, `plug_…`.</ResponseField>
    <ResponseField name="name" type="string" required>Plugin display name.</ResponseField>
    <ResponseField name="enabled" type="boolean" required>Whether the plugin is on for the workspace.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="unavailableReason" type="string">`not_materialized` or `bundle_unreadable`. Null when healthy.</ResponseField>
<ResponseField name="installedAt" type="string" required>Install time, ISO 8601.</ResponseField>
<ResponseField name="updatedAt" type="string" required>Last change time, ISO 8601.</ResponseField>

`skill get` and `GET /api/v1/skills/{skillId}` add these:

<ResponseField name="namespace" type="string">Plugin namespace. Null for a standalone skill.</ResponseField>
<ResponseField name="bundleHash" type="string">Content hash of the stored bundle. Null when none is stored.</ResponseField>

<ResponseField name="activation" type="object">
  Null when unspecified.

  <Expandable title="activation">
    <ResponseField name="description" type="string">Text that tells the harness when to load the skill.</ResponseField>
    <ResponseField name="allowedTools" type="string[]" required>Tools the skill may use.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="files" type="object[]" required>Each file's `path`, and `kind`: `instruction` for `SKILL.md`, `reference` for the rest. No contents.</ResponseField>
<ResponseField name="source" type="object" required>`type` and a readable `label`, such as the repo URL.</ResponseField>
<ResponseField name="share" type="object" required>`isShareable`, false for a plugin skill, and `latestShareId`.</ResponseField>

Each row of an agent's override list has `skillId`, `slug`, `name`, `enabled`, `workspaceEnabled`, `pluginEnabled` and `effectiveEnabled`. `enabled` is `null` when the agent follows the workspace default.

## CLI

| Command | Does |
| - | - |
| `pinework skill list` | List skills, newest install first. Filter by `--source-type`, `--plugin`, `--enabled-by-default` or `--q`. |
| `pinework skill get <id or slug>` | Show one skill with its files, source and share state. |
| `pinework skill add <git-url or owner/repo>` | Install every skill in a repo. Repeat `--skill` to pick some. Takes `--ref` and `--subdir`. |
| `pinework skill create` | Create a skill from `--file path:content` entries. Needs `--slug`, `--name` and `--source-type`. |
| `pinework skill update <id>` | Change the name, description or `--enabled-by-default`. |
| `pinework skill replace-bundle <id>` | Replace every file in one write. Bumps the version. |
| `pinework skill delete <id>` | Delete a standalone skill. |
| `pinework skill agent-overrides --agent <agent>` | Show each skill as on or off for one agent, and what decided it. |
| `pinework skill set-agent-override <id>` | Turn one skill on or off for one agent with `--agent` and `--enabled`. |
| `pinework skill clear-agent-override <id>` | Remove the override so the agent follows the workspace default. |
| `pinework skill share <id>` | Create a share link. Pass `--expires-at` to make it expire. |

`skill add` looks for `SKILL.md` at the repo root and in `skills/`, `.agents/skills/` and `.claude/skills/`. For your own skill, pass `--source-type imported`. A skill created as `builtin` cannot be deleted.

```bash theme={null}
pinework skill add acme/agent-skills --skill release-notes
pinework skill list --source-type plugin --limit 50
pinework skill set-agent-override release-notes --agent @builder --enabled false
pinework skill replace-bundle release-notes --file "SKILL.md:$(cat SKILL.md)"
```

## API

The workspace comes from your credentials, except on the install-from-repo route.

| Method | Path | Does |
| - | - | - |
| GET | `/api/v1/skills` | List skills. Query `limit`, `cursor`, `sort`, `sourceType`, `plugin`, `enabledByDefault`, `q`. |
| POST | `/api/v1/skills` | Create a skill. Body `{slug, name, description?, enabledByDefault?, source, bundle}`. |
| GET | `/api/v1/skills/{skillId}` | Read one skill. Add `expand[]=plugin` for plugin details. |
| PATCH | `/api/v1/skills/{skillId}` | Change `name`, `description` or `enabledByDefault`. |
| DELETE | `/api/v1/skills/{skillId}` | Delete a standalone skill. Returns 204. |
| POST | `/api/v1/skills/{skillId}/replace-bundle` | Replace the bundle. Body `{bundle: {files}}`. |
| GET | `/api/v1/skills/{skillId}/files/content` | Read one file. Query `path`. |
| GET | `/api/v1/skills/agents/{agentId}` | List every skill with one agent's override. |
| PUT | `/api/v1/skills/{skillId}/agents/{agentId}` | Set the override. Body `{enabled}`. |
| DELETE | `/api/v1/skills/{skillId}/agents/{agentId}` | Clear the override. Returns 204 even when none existed. |
| POST | `/api/v1/skills/{skillId}/share` | Create a share link. Body `{expiresAt?}`. People only. |
| GET | `/api/v1/skills/{skillId}/shares` | List a skill's share links, newest first. People only. |
| DELETE | `/api/v1/skills/{skillId}/shares/{shareId}` | Revoke a share link. People only. |
| GET | `/api/v1/skills/shared/{shareToken}` | Preview a shared skill. No sign-in needed. |
| POST | `/api/v1/skills/shared/{shareToken}/install` | Copy a shared skill into a workspace. Body `{targetCompanyId}`. |
| POST | `/api/v1/companies/{workspaceId}/skills/install-from-repo` | Install skills from a repo. Body `{url, ref?, subdir?, skills?}`. |

Who can do what:

* An agent can create a skill. It starts off for the workspace and on for that agent. Sending `enabledByDefault: true` fails.
* An agent can set or clear only its own overrides.
* An agent's update, delete, bundle replace, repo install or share install becomes an approval request. The call returns HTTP 202.
* A plugin skill accepts only `enabledByDefault` changes. You cannot delete it, replace its files or share it.
* Installing a shared skill copies it as `shared`. A taken slug gets a `-2`, `-3` suffix.

A repo install returns `installed`, `skipped` with a reason for each, and `resolvedSha`.

## Errors

| Code | HTTP | When | What to do |
| - | - | - | - |
| `skill_not_found` | 404 | No skill, or no agent, matches the id. | Check the id with `skill list` or `agent list`. |
| `ambiguous_skill_ref` | 409 | The slug names more than one skill. | Use an id from `details.candidates`. |
| `duplicate_skill_slug` | 409 | A standalone skill already has this slug. | Pick another slug, or delete the old skill. |
| `invalid_skill_bundle` | 400 | The bundle has more than 200 files. | Send fewer files. |
| `skill_replace_forbidden` | 409 | You edited, deleted or replaced a plugin skill. | Create your own copy, or uninstall the plugin. |
| `skill_plugin_disabled` | 409 | You turned on a skill whose plugin is off. | Turn the plugin on first. |
| `builtin_skill_delete_forbidden` | 409 | You deleted a `builtin` skill. | Turn it off instead. |
| `agent_skill_default_forbidden` | 403 | An agent created a skill with `enabledByDefault: true`. | Leave the field out. |
| `agent_self_only` | 403 | An agent changed another agent's override. | Ask a person. |
| `skill_file_not_found` | 404 | The path is not in the skill's files. | Read `files` from `skill get`. |
| `skill_share_forbidden` | 409 | You shared a plugin skill. | Share a standalone skill. |
| `share_not_found` | 404 | No share has that token or id. | Check the link. |
| `share_gone` | 410 | The share is revoked or expired, or the skill is gone. | Ask for a new link. |
| `share_forbidden` | 403 | You are not a member of `targetCompanyId`. | Install into your own workspace. |
| `invalid_skill_source` | 400 | The repo is not a GitHub source Pinework can fetch. | Pass a GitHub URL or `owner/repo`. |
| `skill_source_unreadable` | 400 | The ref did not resolve, or the fetch failed. | Check `--ref` and `--subdir`. |
| `no_skills_found` | 404 | The repo has no `SKILL.md` in the searched folders. | Pass `--subdir`. |
| `skill_bundle_storage_failed` | 503 | File storage failed. | Retry. |

Shared codes are in [API errors](/reference/api#errors).

## Limits

* A bundle holds at most 200 files.
* A file preview reads at most 1,048,576 bytes.
* A slug holds up to 64 characters. A name holds up to 256.
* A list page returns 25 skills by default and 100 at most.
* The `q` search takes up to 200 characters.
* A share preview allows 60 requests per minute per IP address.

<CardGroup cols={2}>
  <Card title="Plugins and skills" href="/concepts/plugins-and-skills">
    How skills reach a run.
  </Card>

  <Card title="Install a plugin" href="/guides/install-a-plugin">
    Install plugin skills and fork one.
  </Card>

  <Card title="Plugin" href="/reference/plugin">
    The plugin that owns a skill.
  </Card>

  <Card title="Agent" href="/reference/agent">
    Where overrides apply.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.