# Skills registry and fetch

> `ati skill install|resolve`, manifest generation from SKILL.md, GCS bucket layout, `ati skill fetch` / `skillati` commands, and proxy `/skillati/*` endpoints.

- 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/cli/skills.rs`
- `src/core/skillati.rs`
- `src/core/skill.rs`
- `src/proxy/server.rs`
- `scripts/test_skills_e2e.sh`
- `scripts/test_skill_fetch_e2e.sh`

---

---
title: "Skills registry and fetch"
description: "`ati skill install|resolve`, manifest generation from SKILL.md, GCS bucket layout, `ati skill fetch` / `skillati` commands, and proxy `/skillati/*` endpoints."
---

ATI maintains two skill surfaces: **local** skills under `~/.ati/skills/` (installed, resolved, and served by `/skills*`) and **remote** skills in a GCS bucket or upstream proxy (listed and fetched lazily via `ati skill fetch` / `ati skillati` and `/skillati/*`). Install and resolve operate on disk; fetch and SkillATI endpoints implement progressive disclosure (catalog metadata → SKILL.md activation → on-demand resources) without copying remote content into `~/.ati/skills/` unless you explicitly install.

## Local registry layout

Each installed skill is a directory:

```text
~/.ati/skills/<skill-name>/
├── SKILL.md          # Methodology body (+ optional YAML frontmatter)
├── skill.toml        # Optional ATI bindings (tools, providers, categories, depends_on)
├── references/       # Optional on-demand docs
├── scripts/          # Optional helpers
├── assets/           # Optional templates/data
└── provider.toml     # Optional bundled provider manifest (install fallback)
```

`SkillRegistry::load` indexes skills by name, tool, provider, and category. Metadata priority is YAML frontmatter in `SKILL.md`, then `skill.toml`, then inferred fields.

## Install skills

`ati skill install` copies skill trees into `~/.ati/skills/`. Writes always happen locally; install does not route through the proxy when `ATI_PROXY_URL` is set.

<ParamField body="source" type="string" required>
Path, HTTPS/git URL, or URL with `#subdir` fragment. Git sources may pin with `@<sha>` when the suffix is 7+ hex digits.
</ParamField>

<ParamField body="--from-git" type="string">
Explicit git URL (deprecated; URLs are auto-detected).
</ParamField>

<ParamField body="--name" type="string">
Override destination directory name.
</ParamField>

<ParamField body="--all" type="flag">
Install every child directory that contains `SKILL.md` or `skill.toml`.
</ParamField>

<ParamField body="--local" type="flag">
Use Ollama only for optional manifest generation (no network LLM fallback).
</ParamField>

<Steps>
<Step title="Install from a local directory">

```bash
ati skill install ./my-skill/
```

Copies into `~/.ati/skills/my-skill/`, records `[ati.integrity]` in `skill.toml` when `SKILL.md` exists, and may generate a provider manifest (see below).

</Step>

<Step title="Install from git">

```bash
ati skill install https://github.com/org/repo#skill-name
ati skill install https://github.com/org/repo@abc1234#skill-name
```

Shallow clone by default; full clone when a SHA pin is present, then checkout.

</Step>

<Step title="Verify installation">

```bash
ati skill list
ati skill info my-skill
ati skill validate my-skill --check-tools
```

</Step>
</Steps>

### Manifest generation from SKILL.md

After copy, `generate_manifest_from_skill` may create `~/.ati/manifests/<provider>.toml` from `SKILL.md` using an LLM:

| Condition | Backend |
|-----------|---------|
| `--local` or `ATI_MANIFEST_PROVIDER=local` | Ollama only (errors if unavailable; no network fallback) |
| `ATI_MANIFEST_PROVIDER=cerebras` | `CEREBRAS_API_KEY` |
| `ATI_MANIFEST_PROVIDER=anthropic` | `ANTHROPIC_API_KEY` |
| Default | Cerebras, then Anthropic |
| LLM failure or invalid TOML | `provider.toml` bundled in the skill directory |

Generation is skipped when the target manifest already exists or `SKILL.md` is missing. Provider name comes from `skill.toml` `providers = [...]` or the skill directory name.

<Note>
Installing a skill never edits existing provider manifests in place; a collision prints `Provider '<name>' already has a manifest, skipping generation.` and leaves the file unchanged.
</Note>

## Resolve skills for scopes

`ati skill resolve` answers which **local** skills auto-load for JWT scopes. It loads `~/.ati/skills/`, manifests under `~/.ati/manifests/`, and scopes from the environment or `--scopes <path>` (JSON with `scopes` array and optional `agent_id`).

Resolution cascade (local `skill::resolve_skills`):

1. `skill:X` → skill X
2. `tool:Y` → skills whose `tools` include Y
3. Tool Y’s provider → skills bound to that provider
4. Provider category → skills bound to that category
5. `depends_on` → transitive dependencies
6. Legacy underscore tool scopes via `filter_tools_by_scope`

Wildcard scope `*` lists the registry but does **not** auto-load every skill.

<ResponseExample>

```json
[
  {
    "name": "compliance-screening",
    "version": "1.0.0",
    "description": "Screen entities against sanctions lists",
    "tools": ["ca_person_sanctions_search"],
    "providers": ["complyadvantage"],
    "categories": ["compliance"]
  }
]
```

</ResponseExample>

<Warning>
`POST /skills/resolve` on the proxy resolves **local** `SkillRegistry` entries only, filtered by the caller’s visible local skill names. Remote GCS skills are not included in that response; use `GET /skillati/catalog` plus per-skill `GET /skillati/:name` for remote methodology.
</Warning>

When `ATI_PROXY_URL` is set, read-only skill commands (`list`, `show`, `search`, `info`, `read`, `resolve`) forward to `/skills` on the proxy; `install`, `remove`, `init`, and `validate` still run locally.

## Remote GCS bucket layout

Remote skills use one GCS prefix per skill name. Object keys are `{skill-name}/{relative-path}` (forward slashes). `SKILL.md` is required for discovery; `skill.toml` is optional metadata at install/build time.

```text
gs://<bucket>/
├── _skillati/catalog.v1.json     # Recommended catalog index (fast path)
├── compliance-screening/
│   ├── SKILL.md
│   └── skill.toml
├── fal-generate/
│   ├── SKILL.md
│   ├── scripts/generate.sh
│   └── references/usage.md
└── ...
```

### Catalog index manifest

Build the index offline from a local skills tree:

```bash
ati skill fetch build-index ./skills/ --output-file catalog.json
# Upload to gs://<bucket>/_skillati/catalog.v1.json
```

`build_catalog_manifest` walks either a single skill directory (if it contains `SKILL.md`) or immediate child directories with `SKILL.md`. Each entry includes `RemoteSkillMeta` (name, description, `when_to_use`, bindings, keywords) plus a `resources` list (visible files only; excludes `SKILL.md`, `skill.toml`, dotfiles).

| Field | Meaning |
|-------|---------|
| `version` | Catalog schema version (default `1`) |
| `generated_at` | RFC3339 timestamp |
| `skills[]` | Entries with `meta`, `resources`, `resources_complete` |

Index lookup order (override with comma-separated `ATI_SKILL_REGISTRY_INDEX_OBJECT`):

1. `_skillati/catalog.v1.json` (default)
2. `_skillati/catalog.json`
3. `skillati-catalog.json`

If no index loads, the client lists bucket skill prefixes and reads each `SKILL.md` concurrently (`FALLBACK_CATALOG_CONCURRENCY = 24`).

## Registry configuration

| Variable | Values | Role |
|----------|--------|------|
| `ATI_SKILL_REGISTRY` | `gcs://<bucket>` | Direct GCS via keyring `gcp_credentials` |
| `ATI_SKILL_REGISTRY` | `proxy` | Delegate to `ATI_PROXY_URL` `/skillati/*` (requires session token when enforced) |
| `ATI_SKILL_REGISTRY_INDEX_OBJECT` | Comma-separated object paths | Override catalog index candidates |
| `ATI_SKILL_FETCH_DISABLED` | `1` / `true` | Block fetch commands (except `build-index`); for pre-installed sandboxes |
| `ATI_PROXY_URL` | HTTP base | Routes `ati skill fetch` / `ati skillati` to proxy |
| `ATI_SESSION_TOKEN` | JWT | Bearer auth on proxy fetch |

Proxy startup with GCS:

```bash
ati key set gcp_credentials < /path/to/service-account.json
ATI_SKILL_REGISTRY=gcs://my-skills-bucket ati proxy --port 8090
```

## Progressive disclosure and fetch commands

`ati skill fetch <subcommand>` and `ati skillati <subcommand>` share the same implementation (`SkillAtiCommands`).

```mermaid
sequenceDiagram
  participant Agent
  participant CLI as ati skill fetch
  participant Proxy as ati proxy
  participant GCS as GCS bucket

  Agent->>CLI: catalog
  alt ATI_PROXY_URL set
    CLI->>Proxy: GET /skillati/catalog
    Proxy->>GCS: load index or list prefixes
  else gcs:// registry
    CLI->>GCS: read _skillati/catalog.v1.json
  end
  Agent->>CLI: read my-skill
  CLI->>Proxy: GET /skillati/my-skill
  Proxy->>GCS: GET my-skill/SKILL.md
  Proxy-->>Agent: SkillAtiActivation JSON
  Agent->>CLI: cat my-skill scripts/run.sh
  CLI->>Proxy: GET /skillati/my-skill/file?path=...
  Proxy->>GCS: GET my-skill/scripts/run.sh
```

| Level | Command | Payload |
|-------|---------|---------|
| 1 — Metadata | `catalog` [--search Q] | `RemoteSkillMeta` list (250-char listing budgets apply in orchestrators) |
| 2 — Instructions | `read <name>` | `SkillAtiActivation`: `name`, `description`, `skill_directory`, `content` (no `resources` field) |
| 3 — Resources | `resources`, `cat`, `refs`, `ref` | Paths under `references/`, `scripts/`, `assets/` |

Level-2 text output mirrors Claude Code’s activation shape:

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

<description>

<SKILL.md body>
```

Substitutions applied in `read_skill`:

- `${ATI_SKILL_DIR}` and `${CLAUDE_SKILL_DIR}` → `skillati://<name>`
- `.claude/skills/<other>/…` → `skillati://<other>/…` when `<other>` is a valid catalog name
- Bare `<catalog-skill>/(references|scripts|assets)/…` cross-links when the prefix matches a catalog entry

`cat` supports sibling skills via `../other-skill/path` (validated names only). Binary files require `--output json` (base64 in `SkillAtiFile`).

<ParamField body="ATI_SKILL_FETCH_DISABLED" type="env">
When set, fetch subcommands exit with guidance to use the host’s native Skill tool; `build-index` remains available for publishers.
</ParamField>

## Proxy `/skillati/*` endpoints

All handlers require `SkillAtiClient::from_env` (503 if `ATI_SKILL_REGISTRY` unset). JWT (or virtual key) scopes gate visibility via `visible_skill_names_with_remote` (local union + remote cascade mirroring `resolve_skills`). Denied skills return **404** (`SkillNotFound`), not 403.

| Method | Path | Query / body | Success body |
|--------|------|--------------|--------------|
| GET | `/skillati/catalog` | `?search=` (optional, max 25 matches) | `{ "skills": [ RemoteSkillMeta, ... ] }` |
| GET | `/skillati/{name}` | — | `SkillAtiActivation` JSON |
| GET | `/skillati/{name}/resources` | `?prefix=` | `{ name, prefix, resources: [] }` |
| GET | `/skillati/{name}/file` | `?path=` (required) | `SkillAtiFile` (`kind: text` or `binary`) |
| GET | `/skillati/{name}/refs` | — | `{ name, references: [] }` |
| GET | `/skillati/{name}/ref/{reference}` | — | `{ name, reference, content }` |

:::endpoint GET /skillati/catalog
List remote skills visible under the caller’s scopes. Optional fuzzy `search` filters name, description, keywords, tools, providers, and categories.
:::

:::endpoint GET /skillati/{name}
Return Level-2 activation: frontmatter stripped, directory variables and cross-skill paths rewritten to `skillati://` URIs. Does not embed the resource manifest.
:::

:::endpoint GET /skillati/{name}/file
Read any skill-relative path. `path=SKILL.md` is handled via the activation path on the proxy transport. Invalid or escaping paths → 400; missing object → 404.
:::

Error mapping (`skillati_error_response`):

| Condition | HTTP |
|-----------|------|
| Registry not configured / missing GCS creds | 503 |
| Skill or path not found | 404 |
| Invalid path | 400 |
| GCS or upstream proxy failure | 502 |

Remote visibility uses the same binding model as local resolve: explicit `skill:X`, then tool/provider/category matches from scoped tools. Wildcard scopes see the full remote catalog.

## Local vs remote API split

```text
                    ┌─────────────────────┐
                    │   Agent / CLI       │
                    └─────────┬───────────┘
                              │
          ┌───────────────────┼───────────────────┐
          ▼                   ▼                   ▼
   ~/.ati/skills/      ATI_SKILL_REGISTRY    JWT scopes
   (install/resolve)   gcs:// or proxy
          │                   │
          ▼                   ▼
   GET /skills*          GET /skillati/*
   POST /skills/resolve   (lazy GCS + cache)
```

| Concern | Local `/skills*` | Remote `/skillati/*` |
|---------|------------------|----------------------|
| Storage | `~/.ati/skills/` | GCS bucket (or proxy cache) |
| Install | `ati skill install` | Publish objects + catalog index |
| List | `ati skill list` | `ati skill fetch catalog` |
| Read body | `ati skill show/read` | `ati skill fetch read` |
| Scope resolve | `ati skill resolve`, `POST /skills/resolve` | Catalog + per-handler visibility |

## Verification

<Check>
`scripts/test_skills_e2e.sh` exercises install, resolve, bindings across HTTP/OpenAPI/MCP manifests, and proxy-mode list/read.
</Check>

<Check>
`scripts/test_skill_fetch_e2e.sh` boots a proxy against `gcs://parcha-ati-skills` (override with `ATI_E2E_BUCKET`) and asserts Level-2 shape: non-empty `description`, no `resources` field, `${ATI_SKILL_DIR}` substitution, and `.claude/skills/` rewrites.
</Check>

Integration tests in `tests/proxy_server_test.rs` cover scope-gated `/skillati/*` handlers, including remote-only skills with `ATI_SKILL_REGISTRY=proxy`.

Troubleshooting:

- **503 on `/skillati/*`**: set `ATI_SKILL_REGISTRY` and ensure `gcp_credentials` is in the proxy keyring for `gcs://` mode.
- **404 for a known remote skill**: token lacks `skill:<name>` or tool/provider/category cascade; wildcard scope is required for catalog-wide access.
- **Empty catalog**: upload `_skillati/catalog.v1.json` or ensure bucket prefixes contain `SKILL.md`.
- **Fetch blocked in sandbox**: `ATI_SKILL_FETCH_DISABLED` — use the host Skill tool or pre-install under `~/.ati/skills/`.

## Related pages

<CardGroup>
<Card title="Skills and SkillATI" href="/skills-and-skillati">
Progressive disclosure model, scope cascade overview, and how skills differ from tools.
</Card>
<Card title="Proxy API reference" href="/proxy-api-reference">
Full proxy route list including `/skills` bundle and auth requirements.
</Card>
<Card title="JWT and scopes reference" href="/jwt-scopes-reference">
`skill:`, `tool:`, and wildcard scope grammar used by SkillATI visibility.
</Card>
<Card title="Environment variables" href="/environment-variables">
`ATI_SKILL_REGISTRY`, index override, and fetch-disable flags.
</Card>
<Card title="CLI reference" href="/cli-reference">
Top-level `ati skill` and `ati skillati` command tree.
</Card>
</CardGroup>
