# iii-directory

> Engine introspection (functions / triggers / workers), workers registry proxy, and filesystem-backed skill + prompt reader.

| field | value |
|-------|-------|
| version | 0.5.1 |
| type | binary |
| repo | https://github.com/iii-hq/workers |
| supported_targets | x86_64-apple-darwin, aarch64-apple-darwin, i686-pc-windows-msvc, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, armv7-unknown-linux-gnueabihf |
| author | iii |

## installation

```sh
iii worker add iii-directory@0.5.1
```

## configuration

```yaml
- download_timeout_ms: 60000
  registry_url: https://api.workers.iii.dev
  skills_folder: ./skills
  system_prompt_skills:
    - skills/iii/*.md
    - skills/directory/*.md
```

## readme

# iii-directory

Engine introspection, workers registry proxy, and filesystem-backed
skill + prompt reader for the [iii engine](https://github.com/iii-hq/iii).
Every public function sits under a single `directory::*` namespace,
split into four sub-namespaces (all MCP-agnostic):

| Surface | What clients see | When to use it |
|---|---|---|
| **Skills** (`directory::skills::*`) | Enriched listing via `directory::skills::list` (`{ id, title, description, bytes, modified_at }` per row) and a single-skill reader `directory::skills::get { id }` returning `{ id, title, description, body, modified_at }` | Orientation: "when and why to use my worker's tools" |
| **Prompts** (`directory::prompts::*`) | Static prompt templates listed by `directory::prompts::list` and read by `directory::prompts::get` | Parametric command templates the *user* invokes |
| **Engine** (`directory::engine::*`) | Read-side enrichment over `engine::functions::list`, `engine::workers::list`, `engine::trigger-types::list`, `engine::triggers::list` | "What's connected to the engine right now?" |
| **Registry** (`directory::registry::*`) | HTTP proxy over `api.workers.iii.dev` with the same `workers::{list,info}` shape as `directory::engine::workers::*` | "What's published in the public registry?" |

Skills and prompts are sourced from a single configured folder on disk
(`skills_folder`). The only write path is the
**`directory::skills::download`** function, which pulls markdown into
`skills_folder` from either the
[workers registry](https://workers.iii.dev) or a GitHub repo. Once
downloaded, files belong to the developer — edit them however you want.

`directory::engine::workers::*` and `directory::registry::workers::*`
share the same envelope shape so callers can switch between the local
engine view and the published-registry view without re-learning the API.

## Table of contents

1. [Install](#install)
2. [Configuration](#configuration)
3. [Quickstart: download some skills](#quickstart-download-some-skills)
4. [On-disk layout](#on-disk-layout)
5. [Skill ids](#skill-ids)
6. [Functions](#functions)
7. [Custom trigger types](#custom-trigger-types)
8. [Local development & testing](#local-development--testing)
9. [Migration from skills v0.2.x](#migration-from-skills-v02x)

---

## Install

```bash
iii worker add iii-directory
```

`iii worker add` fetches the binary, writes a config block into
`~/.iii/config.yaml`, and the engine starts the worker on the next
`iii start`.

---

## Configuration

```yaml
# Folder that backs every read (`directory::skills::list`,
# `directory::skills::get`, `directory::prompts::*`) and every write
# from `directory::skills::download`. Relative paths are resolved
# against the process current working directory; absolute paths are
# used as-is.
skills_folder: ./skills

# Workers registry base URL — used by `directory::skills::download`
# and the `directory::registry::*` proxies when a `worker=` source is
# specified. Override for self-hosted deployments.
registry_url: https://api.workers.iii.dev

# Timeout for a single download (`git clone` or HTTP request) in ms.
download_timeout_ms: 60000
```

The folder is created on first download if it doesn't exist.

---

## Quickstart: download some skills

```bash
# Pull a specific worker's skills + prompts at a fixed semver from
# the registry. Files land under `<skills_folder>/agent-memory/`.
iii trigger --function-id=directory::skills::download \
  --payload='{"worker": "agent-memory", "version": "1.2.3"}'

# Same, but always fetch whatever's tagged `latest` (also the default
# when neither version nor tag is given).
iii trigger --function-id=directory::skills::download \
  --payload='{"worker": "agent-memory"}'

# Pull a single subfolder out of a public GitHub repo via
# `git clone --depth 1 --branch main`. Files land under
# `<skills_folder>/frontend-design/`. The `branch` field defaults to
# `main`; pass `"master"` for older repos that haven't migrated.
iii trigger --function-id=directory::skills::download \
  --payload='{
    "repo": "https://github.com/anthropics/skills",
    "skill": "frontend-design"
  }'
```

The response is `{ namespace, skills_written, prompts_written, source }`
where `skills_written` and `prompts_written` are arrays of relative
paths / prompt names that were materialised in this run.

After every successful download the worker fires the
`directory::skills::on-change` and/or `directory::prompts::on-change`
trigger types so that subscribers like the [`mcp`](../mcp/) worker can
forward MCP `notifications/list_changed` to their clients.

---

## On-disk layout

The worker assumes a fixed layout under `skills_folder`:

```text
skills_folder/
  <namespace>/                 # one folder per `directory::skills::download` namespace
    index.md                   # → iii://<namespace>/index
    contacts.md                # → iii://<namespace>/contacts
    emails/send-email.md       # → iii://<namespace>/emails/send-email
    prompts/                   # ← magic marker for prompts
      send-email.md            # ← MCP slash-command (needs YAML frontmatter)
      triage.md
```

A few rules:

- **Skill ids** are the relative path under `skills_folder` with `.md`
  stripped. Each segment must satisfy `[a-z0-9_-]{1,64}`.
- **Prompts** live under any `*/prompts/*.md` path. They must start with
  a YAML frontmatter block declaring at least `description`; `name`
  is optional and overrides the file-stem default.
- Files anywhere else (i.e. *not* in a `prompts/` segment) are skills.

The download function namespaces by source:

| Source | Destination |
|---|---|
| `repo=URL skill=NAME branch?=main` | `<skills_folder>/<NAME>/...` |
| `worker=NAME version=…` | `<skills_folder>/<NAME>/...` |
| `worker=NAME tag=…` (default `tag=latest`) | `<skills_folder>/<NAME>/...` |

Re-pulling the same source overwrites files **file-by-file** —
existing siblings outside the response set are preserved (so
hand-edited additions survive a re-pull).

---

## Skill ids

Skills are addressed by their relative path under `skills_folder` with
`.md` stripped — e.g. `<skills_folder>/agent-memory/observe.md` →
id `"agent-memory/observe"`. The same string is what
`directory::skills::list` returns and what `directory::skills::get`
expects in `{ "id": ... }`. The legacy `iii://{id}` link form is still
accepted on `get` (the prefix is auto-stripped), but the worker no
longer parses any other `iii://` URI shape — bodies are read solely by
id, and the auto-rendered tree-shaped index that previous releases
served at `iii://directory/skills` is gone. Consumers that want a
tree-shaped picker iterate `list` rows themselves and indent by
`id.matches('/').count()`.

---

## Functions

Fifteen functions, all under `directory::*`. All registrations are
namespace-clean; this worker is intentionally agnostic to MCP and any
other adapter.

### `directory::skills::*` (filesystem reader)

| Function ID | Description |
|---|---|
| `directory::skills::download` | Pull markdown into `skills_folder`. Either `{repo, skill, branch?}` (defaults `branch=main`) or `{worker, version?|tag?}` (defaults `tag=latest`). |
| `directory::skills::list` | Enriched listing of every fs-backed skill: `{ id, title, description, bytes, modified_at }` per row. Title and description are extracted from each body's H1 + first paragraph so consumers can render a picker without a follow-up `get` per row. |
| `directory::skills::get` | Fetch one skill by id. Returns `{ id, title, description, body, modified_at }` — same flat shape as `directory::prompts::get`. Accepts a bare id or the same id prefixed with `iii://`. |

### `directory::prompts::*` (filesystem reader)

| Function ID | Description |
|---|---|
| `directory::prompts::list` | Metadata-only listing of every fs-backed prompt. |
| `directory::prompts::get` | Fetch one prompt's body + `{name, description, modified_at}`. Plain shape, no envelope. |

### `directory::engine::*` (engine introspection)

| Function ID | Description |
|---|---|
| `directory::engine::functions::list` | List functions registered with the engine; filter by search/prefix/worker. |
| `directory::engine::functions::info` | Single-function detail: schemas, owning worker, registered triggers, bundled how-to. |
| `directory::engine::triggers::list` | List trigger TYPES registered with the engine; filter by search/prefix/worker. |
| `directory::engine::triggers::info` | Single trigger-type detail: configuration schema, return schema, instance count. |
| `directory::engine::registered-triggers::list` | List registered trigger INSTANCES (subscriber rows). |
| `directory::engine::registered-triggers::info` | Composite: instance + trigger-type detail + function detail. |
| `directory::engine::workers::list` | List workers connected to the engine; same row shape as `directory::registry::workers::list`. |
| `directory::engine::workers::info` | One worker's `worker` envelope + functions + trigger types + registered triggers. |

### `directory::registry::*` (workers registry HTTP proxy)

| Function ID | Description |
|---|---|
| `directory::registry::workers::list` | Search published workers in `api.workers.iii.dev`. Same row shape as `directory::engine::workers::list`. |
| `directory::registry::workers::info` | Full registry detail for one worker: `worker` envelope (matching `directory::engine::workers::info.worker`) plus `readme`, `api_reference`, `skills_tree`. |

Both `directory::registry::*` responses are cached in-process for
`registry_cache_ttl_ms` (default 60s).

There is **no** `directory::skills::register` /
`directory::prompts::register` — see
[Migration](#migration-from-skills-v02x) below.

---

## Custom trigger types

| Trigger type | Fires when | Payload to subscribers |
|---|---|---|
| `directory::skills::on-change` | After a `directory::skills::download` that wrote at least one skill markdown file | `{ "op": "download", "namespace": "<ns>", "source": "repo" \| "registry" }` |
| `directory::prompts::on-change` | After a `directory::skills::download` that wrote at least one prompt markdown file | `{ "op": "download", "namespace": "<ns>", "source": "repo" \| "registry" }` |

Dispatches are fire-and-forget (Void), so the download path doesn't
block on downstream latency.

---

## Local development & testing

### Run from source

```bash
cargo run --release -- --url ws://127.0.0.1:49134 --config ./config.yaml
```

### Tests

```bash
# Fast, offline — exercises the pure helpers (markdown / id validators
# / fs source) without needing an iii engine.
cargo test --lib

# Full BDD suite — requires an iii engine on ws://127.0.0.1:49134
# (or III_ENGINE_WS_URL). The git-backed download scenarios spin up
# a local fixture repo via `git init`; the registry-backed scenarios
# point a wiremock server at the worker's `registry_url` config.
cargo test

# One feature group at a time. Available tags:
#   @engine  @read  @prompts  @download  @download_repo  @download_registry
cargo test --test bdd -- --tags @download
```

The BDD harness lives under [tests/](tests/). Feature files mirror the
modules in [src/functions/](src/functions/). Step definitions under
[tests/steps/](tests/steps/) drive each feature through the same
`iii.trigger` path the production binary uses.

## api reference

```json
{
  "functions": [
    {
      "description": "Fetch one filesystem-backed skill by id. Returns the raw markdown body plus id, title, description, and modified_at — same flat shape as directory::prompts::get. Accepts a bare id (e.g. \"directory/skills/list\") or the same id prefixed with iii://.",
      "metadata": {
        "tool": {
          "label": "Get skill"
        }
      },
      "name": "directory::skills::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Skill id (the same string returned by `directory::skills::list`, e.g. `\"directory/skills/list\"`). The legacy `iii://{id}` form is also accepted for ergonomics; the prefix is stripped before validation. Other URI schemes are rejected.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "SkillGetInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "body": {
            "description": "Raw markdown body (post-frontmatter) from disk.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "modified_at": {
            "description": "File mtime as RFC 3339.",
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        },
        "required": [
          "body",
          "description",
          "id",
          "modified_at",
          "title"
        ],
        "title": "SkillGetOutput",
        "type": "object"
      }
    },
    {
      "description": "List registered trigger instances (the link rows between trigger types and target functions). Filter by trigger_type, function_id, worker, or free-text search.",
      "metadata": {},
      "name": "directory::engine::registered-triggers::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "function_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "trigger_type": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "RegisteredTriggerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "RegisteredTriggerListEntry": {
            "properties": {
              "config_summary": {
                "description": "Truncated (~80 chars) JSON preview of `config` so listings stay scannable. Use `directory::registered-trigger-info` for the full payload.",
                "type": "string"
              },
              "function_id": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              },
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "config_summary",
              "function_id",
              "id",
              "trigger_type"
            ],
            "type": "object"
          }
        },
        "properties": {
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/RegisteredTriggerListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "registered_triggers"
        ],
        "title": "RegisteredTriggerListOutput",
        "type": "object"
      }
    },
    {
      "description": "List every worker currently connected to the engine. Filter by name substring, runtime, or status. Same row shape as directory::registry::workers::list so callers learn one envelope.",
      "metadata": {},
      "name": "directory::engine::workers::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "runtime": {
            "default": null,
            "description": "Exact runtime match (e.g. `\"rust\"`, `\"node\"`).",
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Case-insensitive substring match against `name`.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "default": null,
            "description": "Exact status match (e.g. `\"connected\"`).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "WorkerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Worker": {
            "description": "Shared worker envelope used by both `directory::worker-list` rows and the `worker` field of `directory::worker-info`. Field names line up with `registry::Worker` (see [`crate::functions::registry::Worker`]) so callers learn one shape across local + registry surfaces.",
            "properties": {
              "active_invocations": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "connected_at_ms": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "description": {
                "description": "Engine-side workers carry no description; field present for shape parity with `registry::Worker.description`. Always `None`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "description": "Engine-assigned connection id (directory-specific).",
                "type": "string"
              },
              "ip_address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "isolation": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Worker name as registered with the engine.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "runtime": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Connection state (e.g. `\"connected\"`, `\"disconnected\"`).",
                "type": "string"
              },
              "version": {
                "description": "Worker version string from the worker's published manifest.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "active_invocations",
              "connected_at_ms",
              "function_count",
              "id",
              "status"
            ],
            "type": "object"
          }
        },
        "properties": {
          "workers": {
            "items": {
              "$ref": "#/definitions/Worker"
            },
            "type": "array"
          }
        },
        "required": [
          "workers"
        ],
        "title": "WorkerListOutput",
        "type": "object"
      }
    },
    {
      "description": "List every trigger TYPE registered with the engine. Filter by search, prefix, worker. (For registered trigger instances, use directory::engine::registered-triggers::list.)",
      "metadata": {},
      "name": "directory::engine::triggers::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "prefix": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "TriggerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TriggerListEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "description",
              "id"
            ],
            "type": "object"
          }
        },
        "properties": {
          "triggers": {
            "items": {
              "$ref": "#/definitions/TriggerListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "triggers"
        ],
        "title": "TriggerListOutput",
        "type": "object"
      }
    },
    {
      "description": "List filesystem-backed skills (id, title, description, bytes, modified_at) from skills_folder. Each row carries the H1 title and first-paragraph description so consumers can render a picker or indented index without one get per row.",
      "metadata": {},
      "name": "directory::skills::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "ListSkillsInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "SkillEntry": {
            "properties": {
              "bytes": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "description": {
                "description": "First paragraph of the body, empty when the file has only headings.",
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "modified_at": {
                "description": "File mtime as RFC 3339 (best effort; empty if unavailable).",
                "type": "string"
              },
              "title": {
                "description": "First `# H1` line in the body, falling back to `id` when absent.",
                "type": "string"
              }
            },
            "required": [
              "bytes",
              "description",
              "id",
              "modified_at",
              "title"
            ],
            "type": "object"
          }
        },
        "properties": {
          "skills": {
            "items": {
              "$ref": "#/definitions/SkillEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "skills"
        ],
        "title": "ListSkillsOutput",
        "type": "object"
      }
    },
    {
      "description": "List workers from the public registry (api.workers.iii.dev) matching the free-text term `search`. Same row shape as directory::engine::workers::list so callers learn one envelope. Results are cached for `registry_cache_ttl_ms` (default 60s).",
      "metadata": {},
      "name": "directory::registry::workers::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`directory::registry::workers::list` input. Mirrors [`crate::functions::directory::WorkerListInput.search`] so callers can switch between local and registry surfaces without re-learning the API. Adds `limit` for paging because the registry is paged.",
        "properties": {
          "limit": {
            "default": null,
            "description": "Max results to return. Defaults to 20; capped at 100.",
            "format": "uint32",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Free-text query forwarded to the registry as `?q=…`. Required — the public registry doesn't support an unscoped browse endpoint.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "WorkerListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "RegistryAuthor": {
            "properties": {
              "is_verified": {
                "default": false,
                "type": "boolean"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profile_picture": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "Worker": {
            "description": "Shared worker envelope used by both `directory::registry::workers::list` rows and the `worker` field of `directory::registry::workers::info`. Same field names as [`crate::functions::directory::Worker`] so callers learn one shape across local + registry surfaces.",
            "properties": {
              "author": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RegistryAuthor"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "repo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "version": {
                "description": "Latest published version (worker-list) or the resolved version (worker-info, when called with `version` / `tag`).",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "workers": {
            "items": {
              "$ref": "#/definitions/Worker"
            },
            "type": "array"
          }
        },
        "required": [
          "workers"
        ],
        "title": "WorkerListOutput",
        "type": "object"
      }
    },
    {
      "description": "Fetch one filesystem-backed prompt by name. Returns the raw markdown body plus name, description, and modified_at — no envelope, no templating.",
      "metadata": {},
      "name": "directory::prompts::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "PromptGetInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "body": {
            "description": "Raw markdown body (post-frontmatter) from disk.",
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "modified_at": {
            "description": "File mtime as RFC 3339.",
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "body",
          "description",
          "modified_at",
          "name"
        ],
        "title": "PromptGetOutput",
        "type": "object"
      }
    },
    {
      "description": "Full detail for one trigger type: configuration schema, return schema, owning worker, and current instance count.",
      "metadata": {},
      "name": "directory::engine::triggers::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TriggerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "configuration_schema": {
            "description": "SDK 0.11.3 surfaces a single `trigger_request_format` that doubles as the per-instance configuration shape; expose it explicitly so callers don't have to know the alias."
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "instance_count": {
            "format": "uint",
            "minimum": 0,
            "type": "integer"
          },
          "return_schema": true,
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "description",
          "id",
          "instance_count"
        ],
        "title": "TriggerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Download skills + prompts into skills_folder. Pass {repo, skill, branch?} to clone a single skill folder from a GitHub repo (git clone --depth 1 --branch <branch>; branch defaults to \"main\"), or {worker, version?|tag?} to pull from the workers registry (defaults to tag=\"latest\" when neither version nor tag is given). Files in the destination namespace are overwritten file-by-file.",
      "metadata": {
        "tool": {
          "label": "Download skills"
        }
      },
      "name": "directory::skills::download",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "branch": {
            "default": null,
            "description": "Source A: branch to clone. Defaults to `\"main\"`. Pass `\"master\"` (or any other branch name) for repos whose default branch is not `main`.",
            "type": [
              "string",
              "null"
            ]
          },
          "repo": {
            "default": null,
            "description": "Source A: GitHub repo URL. Pair with `skill`.",
            "type": [
              "string",
              "null"
            ]
          },
          "skill": {
            "default": null,
            "description": "Source A: subfolder under `skills/` inside the repo. Doubles as the destination namespace inside `skills_folder`.",
            "type": [
              "string",
              "null"
            ]
          },
          "tag": {
            "default": null,
            "description": "Source B: registry tag to pull (e.g. `latest`). Mutually exclusive with `version`. Defaults to `\"latest\"` when neither `version` nor `tag` is provided.",
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "default": null,
            "description": "Source B: explicit semver to pull. Mutually exclusive with `tag`.",
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "description": "Source B: workers registry name. Pair with exactly one of `version` / `tag`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "DownloadInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "namespace": {
            "type": "string"
          },
          "prompts_written": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "skills_written": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "source": true
        },
        "required": [
          "namespace",
          "prompts_written",
          "skills_written",
          "source"
        ],
        "title": "DownloadOutput",
        "type": "object"
      }
    },
    {
      "description": "List every function registered with the engine. Filter by free-text search, namespace prefix, and/or worker name.",
      "metadata": {},
      "name": "directory::engine::functions::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "prefix": {
            "default": null,
            "description": "Exact prefix match on `function_id` (e.g. `\"mem::\"`).",
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "default": null,
            "description": "Case-insensitive substring match against `function_id` and `description`.",
            "type": [
              "string",
              "null"
            ]
          },
          "worker": {
            "default": null,
            "description": "Exact worker-name match (the worker that registered the function).",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "FunctionListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "FunctionListEntry": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              },
              "worker_name": {
                "description": "Worker that registered it (resolved via `WorkerInfo.functions[]`), or the first `::` segment of `function_id` as fallback.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "function_id"
            ],
            "type": "object"
          }
        },
        "properties": {
          "functions": {
            "items": {
              "$ref": "#/definitions/FunctionListEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "functions"
        ],
        "title": "FunctionListOutput",
        "type": "object"
      }
    },
    {
      "description": "List filesystem-backed prompts (name, description, modified_at) from skills_folder.",
      "metadata": {},
      "name": "directory::prompts::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "ListPromptsInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "PromptEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "modified_at": {
                "description": "File mtime as RFC 3339.",
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "modified_at",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "prompts": {
            "items": {
              "$ref": "#/definitions/PromptEntry"
            },
            "type": "array"
          }
        },
        "required": [
          "prompts"
        ],
        "title": "ListPromptsOutput",
        "type": "object"
      }
    },
    {
      "description": "Full detail for one function: schemas, owning worker, registered triggers that target it, and any matching how-to skill from skills_folder.",
      "metadata": {},
      "name": "directory::engine::functions::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "function_id": {
            "type": "string"
          }
        },
        "required": [
          "function_id"
        ],
        "title": "FunctionInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "HowGuide": {
            "description": "Primary how-to skill that documents this function. Kept tiny so `function-info` stays cheap to render; deeper related skills come back via [`FunctionInfoOutput::related_skills`] as title-only refs that callers can pull on demand through `directory::skills::get`.",
            "properties": {
              "body": {
                "type": "string"
              },
              "skill_id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "body",
              "skill_id",
              "title"
            ],
            "type": "object"
          },
          "RegisteredTriggerSummary": {
            "properties": {
              "config": true,
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "config",
              "id",
              "trigger_type"
            ],
            "type": "object"
          },
          "RelatedSkillRef": {
            "description": "Title-only reference to another skill that mentions a function. Bodies are intentionally omitted; callers fetch on demand via `directory::skills::get { id: \"<skill_id>\" }`.",
            "properties": {
              "skill_id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "skill_id",
              "title"
            ],
            "type": "object"
          }
        },
        "properties": {
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "function_id": {
            "type": "string"
          },
          "how_guide": {
            "anyOf": [
              {
                "$ref": "#/definitions/HowGuide"
              },
              {
                "type": "null"
              }
            ]
          },
          "metadata": true,
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/RegisteredTriggerSummary"
            },
            "type": "array"
          },
          "related_skills": {
            "description": "Other skills (any `type`) that mention this function via either the literal `function_id` or the `iii://fn/<dotted/path>` URI. Body content is omitted; fetch on demand via `directory::skills::get`.",
            "items": {
              "$ref": "#/definitions/RelatedSkillRef"
            },
            "type": "array"
          },
          "request_schema": true,
          "response_schema": true,
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "function_id",
          "registered_triggers",
          "related_skills"
        ],
        "title": "FunctionInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Worker envelope plus the lists of functions, trigger types, and registered triggers it owns. The `worker` field has the same shape as directory::registry::workers::info so callers can switch between local + registry surfaces with the same parser.",
      "metadata": {},
      "name": "directory::engine::workers::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Worker": {
            "description": "Shared worker envelope used by both `directory::worker-list` rows and the `worker` field of `directory::worker-info`. Field names line up with `registry::Worker` (see [`crate::functions::registry::Worker`]) so callers learn one shape across local + registry surfaces.",
            "properties": {
              "active_invocations": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "connected_at_ms": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "description": {
                "description": "Engine-side workers carry no description; field present for shape parity with `registry::Worker.description`. Always `None`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "description": "Engine-assigned connection id (directory-specific).",
                "type": "string"
              },
              "ip_address": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "isolation": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Worker name as registered with the engine.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "os": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "runtime": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Connection state (e.g. `\"connected\"`, `\"disconnected\"`).",
                "type": "string"
              },
              "version": {
                "description": "Worker version string from the worker's published manifest.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "active_invocations",
              "connected_at_ms",
              "function_count",
              "id",
              "status"
            ],
            "type": "object"
          },
          "WorkerFunctionEntry": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              }
            },
            "required": [
              "function_id"
            ],
            "type": "object"
          },
          "WorkerRegisteredTriggerEntry": {
            "properties": {
              "function_id": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "function_id",
              "id",
              "trigger_type"
            ],
            "type": "object"
          },
          "WorkerTriggerTypeEntry": {
            "properties": {
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "id"
            ],
            "type": "object"
          }
        },
        "properties": {
          "functions": {
            "items": {
              "$ref": "#/definitions/WorkerFunctionEntry"
            },
            "type": "array"
          },
          "registered_triggers": {
            "items": {
              "$ref": "#/definitions/WorkerRegisteredTriggerEntry"
            },
            "type": "array"
          },
          "trigger_types": {
            "items": {
              "$ref": "#/definitions/WorkerTriggerTypeEntry"
            },
            "type": "array"
          },
          "worker": {
            "allOf": [
              {
                "$ref": "#/definitions/Worker"
              }
            ],
            "description": "Same shape as `worker-list` rows (and `registry::worker-info.worker`)."
          }
        },
        "required": [
          "functions",
          "registered_triggers",
          "trigger_types",
          "worker"
        ],
        "title": "WorkerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Full denormalized detail for one registered trigger: instance config + trigger-type detail + function detail.",
      "metadata": {},
      "name": "directory::engine::registered-triggers::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "RegisteredTriggerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "FunctionInfoOutput": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "function_id": {
                "type": "string"
              },
              "how_guide": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/HowGuide"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "metadata": true,
              "registered_triggers": {
                "items": {
                  "$ref": "#/definitions/RegisteredTriggerSummary"
                },
                "type": "array"
              },
              "related_skills": {
                "description": "Other skills (any `type`) that mention this function via either the literal `function_id` or the `iii://fn/<dotted/path>` URI. Body content is omitted; fetch on demand via `directory::skills::get`.",
                "items": {
                  "$ref": "#/definitions/RelatedSkillRef"
                },
                "type": "array"
              },
              "request_schema": true,
              "response_schema": true,
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "function_id",
              "registered_triggers",
              "related_skills"
            ],
            "type": "object"
          },
          "HowGuide": {
            "description": "Primary how-to skill that documents this function. Kept tiny so `function-info` stays cheap to render; deeper related skills come back via [`FunctionInfoOutput::related_skills`] as title-only refs that callers can pull on demand through `directory::skills::get`.",
            "properties": {
              "body": {
                "type": "string"
              },
              "skill_id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "body",
              "skill_id",
              "title"
            ],
            "type": "object"
          },
          "RegisteredTriggerSummary": {
            "properties": {
              "config": true,
              "id": {
                "type": "string"
              },
              "trigger_type": {
                "type": "string"
              }
            },
            "required": [
              "config",
              "id",
              "trigger_type"
            ],
            "type": "object"
          },
          "RelatedSkillRef": {
            "description": "Title-only reference to another skill that mentions a function. Bodies are intentionally omitted; callers fetch on demand via `directory::skills::get { id: \"<skill_id>\" }`.",
            "properties": {
              "skill_id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "skill_id",
              "title"
            ],
            "type": "object"
          },
          "TriggerInfoOutput": {
            "properties": {
              "configuration_schema": {
                "description": "SDK 0.11.3 surfaces a single `trigger_request_format` that doubles as the per-instance configuration shape; expose it explicitly so callers don't have to know the alias."
              },
              "description": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "instance_count": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "return_schema": true,
              "worker_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "description",
              "id",
              "instance_count"
            ],
            "type": "object"
          }
        },
        "properties": {
          "config": true,
          "function": {
            "anyOf": [
              {
                "$ref": "#/definitions/FunctionInfoOutput"
              },
              {
                "type": "null"
              }
            ],
            "description": "Full function detail for `function_id`. `None` if the function has been unregistered between calls."
          },
          "function_id": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "metadata": true,
          "trigger": {
            "anyOf": [
              {
                "$ref": "#/definitions/TriggerInfoOutput"
              },
              {
                "type": "null"
              }
            ],
            "description": "Full trigger-type detail for `trigger_type`. `None` if the type has been unregistered between calls."
          },
          "trigger_type": {
            "type": "string"
          },
          "worker_name": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "config",
          "function_id",
          "id",
          "trigger_type"
        ],
        "title": "RegisteredTriggerInfoOutput",
        "type": "object"
      }
    },
    {
      "description": "Fetch full registry metadata for one worker: worker envelope (same shape as directory::registry::workers::list rows and directory::engine::workers::info), readme, full API reference (functions + triggers schemas), and tree of skill/prompt file paths. Pass either `version` or `tag` (defaults to tag=\"latest\"). Results are cached for `registry_cache_ttl_ms`.",
      "metadata": {},
      "name": "directory::registry::workers::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "`directory::registry::workers::info` input. Pass either `version` or `tag`; if neither is provided we fall back to `tag: \"latest\"`.",
        "properties": {
          "name": {
            "description": "Worker name in the registry (e.g. `\"resend\"`).",
            "type": "string"
          },
          "tag": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "version": {
            "default": null,
            "description": "Mutually exclusive with `tag`. If neither is provided we fall back to `tag: \"latest\"`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkerInfoInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ApiReference": {
            "properties": {
              "functions": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/ApiReferenceFunction"
                },
                "type": "array"
              },
              "triggers": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/ApiReferenceTrigger"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "ApiReferenceFunction": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "metadata": true,
              "name": {
                "type": "string"
              },
              "request_schema": true,
              "response_schema": true
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "ApiReferenceTrigger": {
            "properties": {
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "invocation_schema": true,
              "metadata": true,
              "name": {
                "type": "string"
              },
              "return_schema": true
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "RegistryAuthor": {
            "properties": {
              "is_verified": {
                "default": false,
                "type": "boolean"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "profile_picture": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "SkillsTree": {
            "properties": {
              "prompts": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/SkillsTreePrompt"
                },
                "type": "array"
              },
              "skills": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/SkillsTreeSkill"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "SkillsTreePrompt": {
            "properties": {
              "description": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          },
          "SkillsTreeSkill": {
            "properties": {
              "path": {
                "type": "string"
              }
            },
            "required": [
              "path"
            ],
            "type": "object"
          },
          "Worker": {
            "description": "Shared worker envelope used by both `directory::registry::workers::list` rows and the `worker` field of `directory::registry::workers::info`. Same field names as [`crate::functions::directory::Worker`] so callers learn one shape across local + registry surfaces.",
            "properties": {
              "author": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/RegistryAuthor"
                  },
                  {
                    "type": "null"
                  }
                ],
                "default": null
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              },
              "repo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "version": {
                "description": "Latest published version (worker-list) or the resolved version (worker-info, when called with `version` / `tag`).",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "api_reference": {
            "$ref": "#/definitions/ApiReference"
          },
          "readme": {
            "type": [
              "string",
              "null"
            ]
          },
          "skills_tree": {
            "$ref": "#/definitions/SkillsTree"
          },
          "worker": {
            "allOf": [
              {
                "$ref": "#/definitions/Worker"
              }
            ],
            "description": "Same shape as `directory::registry::workers::list` rows (and `directory::engine::workers::info.worker`)."
          }
        },
        "required": [
          "api_reference",
          "skills_tree",
          "worker"
        ],
        "title": "WorkerInfoOutput",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "Fires after every successful directory::skills::download that wrote at least one prompt markdown file.",
      "invocation_schema": {},
      "metadata": {},
      "name": "directory::prompts::on-change",
      "return_schema": {}
    },
    {
      "description": "Fires after every successful directory::skills::download that wrote at least one skill markdown file.",
      "invocation_schema": {},
      "metadata": {},
      "name": "directory::skills::on-change",
      "return_schema": {}
    }
  ]
}
```
