---
title: Skills
description: Packaging a skill bundle and pushing new versions of it.
sidebar:
  order: 2
---

A skill is a bundle of knowledge and procedure that an agent can read. You upload it as an archive, and an agent version grants it by id.

## The archive

A skill is a gzipped tar of the bundle directory, with `SKILL.md` at its root.

```sh
tar -czf skill.tar.gz -C ./refunds .
```

The SDK takes the bytes in whatever form you have them:

```ts
type SkillArchive = Uint8Array | Blob | ReadableStream<Uint8Array>;
```

## Uploading

```ts
import { openAsBlob } from "node:fs";

const skill = await subako.skills.create(await openAsBlob("./skill.tar.gz"));
```

That creates the skill and its first version. Later versions go to the same skill:

```ts
await subako.skills.pushVersion(skill.skill_id, new Uint8Array(archive));
```

Each push is a new numbered version. Nothing is overwritten.

```ts
const versions = await subako.skills.listVersions(skill.skill_id, { limit: 20 });
const all = await subako.skills.list();

await subako.skills.rename(skill.skill_id, { name: "refunds-2026" });
await subako.skills.delete(skill.skill_id);
```

## A stream is not retried

A request whose body is a `ReadableStream` cannot be read twice, so the SDK does not retry it. If you want the retries, pass a `Blob` or a `Uint8Array` instead — the SDK can replay those. See [Timeouts and retries](/reference/timeouts-and-retries).

## Granting it to an agent

The upload does not make the skill reachable. An agent version has to name it:

```ts
await subako.agents.publishVersion(agentId, {
	model: { format: "anthropic", model: "claude-sonnet-5", max_tokens: 8192 },
	skills: [
		{ name: "refunds", skill_id: skill.skill_id, version: { type: "latest" } },
	],
});
```

`name` is the label the model sees the skill under. The two version pins behave differently:

| Prop | Type | Default | Description |
| - | - | - | - |
| `{ type: "latest" }?` | `VersionPinBody` | - | Resolved when each session is created, child sessions included. A session started after you push version 4 sees version 4; one already running stays where it was. |
| `{ type: "pinned", number: 3 }?` | `VersionPinBody` | - | Always that version. Pushing a new one changes nothing until you publish a config that names it. |

`latest` is the one that surprises people: it does not update a live session, and it does not need a new agent version either. Pushing a skill version changes what tomorrow's sessions read.
