# Skills and SkillATI

> Skills as methodology docs vs tools as data access, progressive disclosure (metadata → SKILL.md → resources), scope-driven resolution cascade, and remote GCS registry via `/skillati/*`.

- Repository: Parcha-ai/ati
- GitHub: https://github.com/Parcha-ai/ati
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa/llms-full.txt

## Source Files

- `src/core/skill.rs`
- `src/core/skillati.rs`
- `src/cli/skills.rs`
- `src/cli/skillati.rs`
- `skills/google-workspace/SKILL.md`
- `skills/google-workspace/skill.toml`

---

---
title: "Skills and SkillATI"
description: "Skills as methodology docs vs tools as data access, progressive disclosure (metadata → SKILL.md → resources), scope-driven resolution cascade, and remote GCS registry via `/skillati/*`."
---

ATI separates **tools** (manifest-backed API/MCP/CLI calls that return data) from **skills** (methodology documents under `~/.ati/skills/` or a remote SkillATI bucket). Skills teach agents *how* to use tools; they never replace manifests. Local skills load from `SkillRegistry` in `src/core/skill.rs`; remote skills flow through `SkillAtiClient` in `src/core/skillati.rs` and proxy routes under `/skillati/*`. Progressive disclosure keeps token use bounded: catalog metadata first, `SKILL.md` body on activation, `scripts/` / `references/` / `assets/` only when the body or agent requests them.

## Skills vs tools

| Surface | Role | Defined in | Agent access |
|---------|------|------------|--------------|
| Tool | Data/API call with schema | `manifests/*.toml` (`[[tools]]` or OpenAPI/MCP discovery) | `ati run <tool> --arg value` |
| Skill | Workflow, examples, guardrails | `SKILL.md` + optional `skill.toml` per skill directory | `ati skill read`, `ati skill fetch read`, or proxy `/skillati/:name` |

Skills reference tools, providers, and categories through bindings in metadata — not the reverse. Installing a skill does not edit manifests.

<Note>
The bundled `google-workspace` skill binds `providers = ["google_workspace"]` in `skill.toml` and documents `ati run google_workspace -- …` patterns in `SKILL.md`. A JWT with `tool:google_workspace:*` or `skill:google-workspace` can surface that skill without changing the provider manifest.
</Note>

## Skill directory layout

Each skill is one subdirectory under `~/.ati/skills/<name>/` (or a GCS prefix `<skill-name>/` in a bucket):

:::files
~/.ati/skills/<name>/
├── SKILL.md          # Methodology body; optional YAML frontmatter (Anthropic spec)
├── skill.toml        # Optional ATI bindings ([skill] table)
├── references/       # Level-3 docs (on demand)
├── scripts/          # Level-3 helpers (on demand)
└── assets/           # Level-3 templates/data (on demand)
:::

Metadata priority when loading locally: YAML frontmatter in `SKILL.md` → `skill.toml` → inferred from `SKILL.md` body. Skill names must satisfy Anthropic rules (1–64 chars, lowercase letters, digits, hyphens; no consecutive hyphens).

## Progressive disclosure

ATI mirrors the Agent Skills three-level model used by Claude Code:

| Level | Payload | When loaded | ATI surface |
|-------|---------|-------------|-------------|
| 1 — Metadata | `name`, `description`, optional `when_to_use` | Agent provisioning / catalog listing | `GET /skillati/catalog`, `ati skill fetch catalog`, orchestrator `build_skill_listing` → `<system-reminder>` block (250 chars/entry, 8000 chars/body budget on the Python client) |
| 2 — Instructions | `SKILL.md` body (frontmatter stripped), `skill_directory`, `description` | Agent activates a skill | `GET /skillati/:name`, `ati skill fetch read <name>` → `SkillAtiActivation` |
| 3 — Resources | Files under `references/`, `scripts/`, `assets/` | On demand per SKILL.md or explicit fetch | `ati skill fetch resources\|cat\|refs\|ref`, `/skillati/:name/resources`, `/file`, `/refs`, `/ref/:reference` |

`SkillAtiActivation` intentionally omits a resource manifest at Level 2. Agents list or read Level 3 explicitly.

```mermaid
sequenceDiagram
    participant Agent
    participant Proxy as ati proxy
    participant SkillATI as SkillAtiClient
    participant GCS as GCS bucket

    Agent->>Proxy: GET /skillati/catalog (JWT)
    Proxy->>SkillATI: catalog()
    SkillATI->>GCS: _skillati/catalog.v1.json or list prefixes
    GCS-->>SkillATI: RemoteSkillMeta[]
    Proxy-->>Agent: skills[] (scope-filtered)

    Agent->>Proxy: GET /skillati/:name
    Proxy->>SkillATI: read_skill(name)
    SkillATI->>GCS: GET :name/SKILL.md
    SkillATI-->>Proxy: SkillAtiActivation (substituted body)
    Proxy-->>Agent: name, description, skill_directory, content

    Agent->>Proxy: GET /skillati/:name/file?path=scripts/foo.sh
    Proxy->>SkillATI: read_path(name, path)
    SkillATI->>GCS: GET object
    Proxy-->>Agent: SkillAtiFile (text or base64)
```

## Local skill registry

`SkillRegistry::load` scans `~/.ati/skills/*/`, builds indexes by name, tool, provider, and category, and backs CLI commands in `src/cli/skills.rs`.

| Command | Purpose |
|---------|---------|
| `ati skill list` | List installed skills (filters: `--category`, `--provider`, `--tool`) |
| `ati skill show <name>` | Show `SKILL.md` (`--meta`, `--refs`) |
| `ati skill info <name>` | Structured metadata |
| `ati skill read <name>` | Raw body for agents (`--tool`, `--with-refs`) |
| `ati skill resolve` | Skills auto-loaded for scopes (`--scopes` path) |
| `ati skill install\|remove\|init\|validate\|verify\|diff\|update` | Lifecycle (always local filesystem) |

When `ATI_PROXY_URL` is set, read-only skill commands (`list`, `show`, `search`, `info`, `read`, `resolve`) forward to proxy `/skills` endpoints; install/remove/init/validate stay local.

`build_skill_context` caps injected skill metadata at 32 KiB for `ati assist` system prompts.

## Scope-driven resolution cascade

`skill::resolve_skills` and `skill::visible_skills` determine which local skills appear for a JWT `scope` claim:

1. **Explicit** — `skill:X` loads skill `X` directly.
2. **Tool binding** — `tool:Y` loads skills whose `tools[]` includes `Y`.
3. **Provider binding** — tool `Y`’s provider `P` loads skills with `providers[]` containing `P`.
4. **Category binding** — provider category `C` loads skills with `categories[]` containing `C`.
5. **Dependencies** — each loaded skill’s `depends_on[]` is transitively loaded.

Wildcard scope `*` sees the full local registry but does not auto-load every skill into assist context. A second pass via `filter_tools_by_scope` maps legacy underscore tool scopes to colon-namespaced manifest tools.

Remote catalog visibility uses the same cascade in `visible_remote_skill_names` (`src/proxy/server.rs`): explicit `skill:X`, then tool/provider/category bindings against `RemoteSkillMeta` entries. `visible_skill_names_with_remote` unions local and remote names before `/skillati/*` handlers run.

<Warning>
A token with only `skill:foo` can list and read remote `foo` but cannot read remote `bar`, even if `bar` appears in a cross-skill URI inside `foo`’s body — each `fetch read` / `/skillati/:name` call re-checks visibility.
</Warning>

Proxy `POST /skills/resolve` accepts `{ "scopes": [...], "include_content": bool }`, resolves via `resolve_skills`, intersects with `visible_skill_names` (local only on that route), and optionally embeds full `SKILL.md` content.

## SkillATI remote registry

SkillATI is the lazy GCS (or proxy-mediated) registry for skills not installed under `~/.ati/skills/`.

### Configuration

| Variable | Values | Behavior |
|----------|--------|----------|
| `ATI_SKILL_REGISTRY` | unset | SkillATI disabled; `/skillati/*` returns 503 on proxy |
| | `gcs://<bucket>` | Direct GCS via `gcp_credentials` keyring key |
| | `proxy` | Client calls proxy `/skillati/*`; requires `ATI_PROXY_URL` |
| `ATI_SKILL_REGISTRY_INDEX_OBJECT` | object path | Override catalog index location |
| `ATI_SKILL_FETCH_DISABLED` | `1` / `true` | Blocks `ati skill fetch` (not `build-index`); for sandboxes with pre-installed skills |
| `ATI_PROXY_URL` | URL | Routes `ati skill fetch` and read-only `ati skill` to proxy |

GCS layout: one prefix per skill (`<skill-name>/SKILL.md`, …). Catalog index candidates (first hit wins): `_skillati/catalog.v1.json`, `_skillati/catalog.json`, `skillati-catalog.json`. Without an index, ATI lists bucket prefixes and parses each `SKILL.md` (up to 24 concurrent).

Publish a catalog with:

```bash
ati skill fetch build-index --source-dir ./skills --output-file catalog.v1.json
# Upload to GCS as _skillati/catalog.v1.json (recommended)
```

### Level-2 activation shape

`ati skill fetch read <name>` (alias: `ati skillati read`) returns JSON:

<ResponseField name="name" type="string">
Skill identifier.
</ResponseField>

<ResponseField name="description" type="string">
One-line description from catalog entry when available.
</ResponseField>

<ResponseField name="skill_directory" type="string">
Virtual base URI, always `skillati://<name>`.
</ResponseField>

<ResponseField name="content" type="string">
`SKILL.md` body after frontmatter strip and `substitute_skill_refs`.
</ResponseField>

Text output uses the Claude Code preamble:

```text
Base directory for this skill: skillati://<name>

<description>

<SKILL.md body>
```

### Reference substitution

`substitute_skill_refs` rewrites paths so Claude-authored skills work in sandboxes without local `.claude/skills/`:

1. `${ATI_SKILL_DIR}` and `${CLAUDE_SKILL_DIR}` → `skillati://<current-skill>/`
2. `.claude/skills/<other>/…` (valid skill name) → `skillati://<other>/…`
3. Bare `<catalog-skill>/(references|scripts|assets)/…` → `skillati://<catalog-skill>/…` when the first segment is in the remote catalog

Prose mentions like "the `.claude/skills/` directory" are left unchanged (`is_anthropic_valid_name` guard).

### CLI: `ati skill fetch`

Subcommands (also under `ati skillati` if exposed):

| Subcommand | Remote action |
|------------|---------------|
| `catalog [--search Q]` | List/filter `RemoteSkillMeta` |
| `read <name>` | Level-2 activation |
| `resources <name> [--prefix P]` | List paths without content |
| `cat <name> <path>` | Read arbitrary skill-relative file |
| `refs <name>` | List `references/*` basenames |
| `ref <name> <reference>` | Read `references/<reference>` |
| `build-index` | Offline manifest generation (local only) |

With `ATI_PROXY_URL`, fetch uses the same paths on the proxy with `ATI_SESSION_TOKEN` Bearer auth.

## Proxy HTTP routes

### Local skills (`~/.ati/skills/`)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/skills` | List skills (`?category`, `?provider`, `?tool`, `?search`) |
| GET | `/skills/:name` | Detail (`?meta`, `?refs`) |
| GET | `/skills/:name/bundle` | All files in skill directory |
| POST | `/skills/bundle` | Multi-skill bundle |
| POST | `/skills/resolve` | Scope → resolved skill metadata (+ optional content) |

### SkillATI (remote)

| Method | Path | Description |
|--------|------|-------------|
| GET | `/skillati/catalog` | Scope-filtered catalog (`?search`) |
| GET | `/skillati/:name` | Level-2 `SkillAtiActivation` |
| GET | `/skillati/:name/resources` | Resource paths (`?prefix`) |
| GET | `/skillati/:name/file` | Single file (`?path=`) → `SkillAtiFile` |
| GET | `/skillati/:name/refs` | Reference basenames |
| GET | `/skillati/:name/ref/:reference` | Reference file content |

All SkillATI routes require `ATI_SKILL_REGISTRY` on the proxy and JWT scope gating. Unconfigured registry → 503; out-of-scope skill → 404 (`SkillNotFound`).

:::endpoint GET /skillati/catalog
Scope-filtered remote skill catalog. Response: `{ "skills": [ RemoteSkillMeta, ... ] }`. Optional `search` query applies fuzzy filter (limit 25).
:::

:::endpoint GET /skillati/{name}
Level-2 activation for one remote skill. Response fields: `name`, `description`, `skill_directory`, `content`. No `resources` array.
:::

## Orchestrator integration (Level 1)

`AtiOrchestrator.build_skill_listing` (Python `ati-client`) calls `GET {proxy}/skillati/catalog` with the sandbox JWT and formats entries into a `<system-reminder>` block — matching Claude Code’s skill listing shape for agent provisioning without loading full `SKILL.md` bodies.

## Security notes

- Remote skill bodies can reach LLM context via assist or explicit read; audit registry content for prompt injection.
- GCS mode: `gcp_credentials` in the keyring; proxy mode keeps GCP keys off sandboxes.
- `read_skill` content is bounded by object size; local `build_skill_context` enforces 32 KiB for metadata injection.

<Info>
Provider-neutral: skills are plain directories or bucket objects. Any agent runtime that can call ATI CLI or proxy HTTP can use the same SkillATI URIs and progressive-disclosure contract without binding to a single model vendor.
</Info>

## Example binding

`skills/google-workspace/skill.toml`:

```toml
[skill]
name = "google-workspace"
providers = ["google_workspace"]
categories = ["productivity"]
```

`SKILL.md` documents `ati run google_workspace -- drive files list` and related flows. With `tool:google_workspace:*` in the JWT, resolution step 3 loads this skill; with `skill:google-workspace`, step 1 loads it directly.

## Troubleshooting

| Symptom | Likely cause |
|---------|----------------|
| `/skillati/*` 503 | `ATI_SKILL_REGISTRY` unset on proxy |
| Remote skill 404 with valid scope | Empty local `skills/` and missing remote binding; check `skill:X` or tool/provider/category cascade |
| `ati skill fetch` exits 1 | `ATI_SKILL_FETCH_DISABLED` set in sandbox |
| Empty catalog | Wrong bucket, missing index, or invalid `gcp_credentials` |
| Substitution not applied | Skill name not in catalog (rule 3); invalid `.claude/skills/<name>` segment |

## Related pages

<CardGroup>
<Card title="Scopes and tool discovery" href="/scopes-and-tool-discovery">
JWT scopes, wildcards, and how tool scopes drive skill visibility.
</Card>
<Card title="Skills registry and fetch" href="/skills-registry-and-fetch">
Install, resolve, GCS publishing, and fetch command details.
</Card>
<Card title="Proxy API reference" href="/proxy-api-reference">
Full `/skills` and `/skillati/*` request shapes and auth.
</Card>
<Card title="JWT and scopes reference" href="/jwt-scopes-reference">
`skill:` and `tool:` scope grammar.
</Card>
<Card title="Environment variables" href="/environment-variables">
`ATI_SKILL_REGISTRY`, `ATI_SKILL_FETCH_DISABLED`, and related env contract.
</Card>
</CardGroup>
