Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions iii-directory/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion iii-directory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ name = "iii_directory"
path = "src/lib.rs"

[dependencies]
iii-sdk = "=0.11.3"
iii-sdk = "=0.11.6"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "signal", "time", "fs", "process"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
29 changes: 19 additions & 10 deletions iii-directory/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ 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" |
| **Skills** (`directory::skills::*`) | Enriched listing via `directory::skills::list` (`{ id, title, type, description, bytes, modified_at }` per row), a single-skill reader `directory::skills::get { id }` returning `{ id, title, type, description, body, modified_at }`, and `directory::skills::index` which renders a short per-worker overview document (one `## <title>` + first paragraph + `read more` link per `type: index` skill). `title` prefers the YAML frontmatter `title:` over the body H1; `type` is lifted from frontmatter `type:` (e.g. `index`, `how-to`, `reference`) and serialised as `null` when absent. | 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?" |
| **Registry** (`directory::registry::*`) | HTTP proxy over `api.workers.iii.dev` with `workers::{list,info}`. Rows share the core `name` / `description` / `version` fields with `directory::engine::workers::*` and add publication metadata (`type`, `config`, `supported_targets`, `total_downloads`, `dependencies`, optional `image`). `workers::list` is cursor-paginated with a server-authored page size. | "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
Expand All @@ -20,8 +20,11 @@ Skills and prompts are sourced from a single configured folder on disk
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.
share the core `name` / `description` / `version` fields so a parser
that touches only those keys works against either surface; the
registry view also surfaces publication metadata (`type`, `config`,
`supported_targets`, `total_downloads`, `dependencies`, optional
`image`) and the engine view adds runtime / connection state.

## Table of contents

Expand Down Expand Up @@ -126,6 +129,11 @@ A few rules:

- **Skill ids** are the relative path under `skills_folder` with `.md`
stripped. Each segment must satisfy `[a-z0-9_-]{1,64}`.
- **Skill frontmatter is optional.** When present, the reader honours
two keys: `title:` (used by `directory::skills::list` and
`directory::skills::get` in preference to a body `# H1`) and
`type:` (free-form classifier surfaced verbatim on both responses).
Any other YAML keys are ignored.
- **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.
Expand Down Expand Up @@ -163,7 +171,7 @@ tree-shaped picker iterate `list` rows themselves and indent by

## Functions

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

Expand All @@ -172,8 +180,9 @@ other adapter.
| 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::skills::list` | Enriched listing of every fs-backed skill: `{ id, title, type, description, bytes, modified_at }` per row. `title` prefers the YAML frontmatter `title:` over the body H1, `type` is lifted from frontmatter `type:` (`null` when absent), and `description` is the first paragraph of the body — so consumers can render a picker without a follow-up `get` per row. |
| `directory::skills::get` | Fetch one skill by id. Returns `{ id, title, type, description, body, modified_at }` — same shape `directory::skills::list` rows use, plus the raw markdown `body`. Same title-resolution and `type` precedence as `list`. Accepts a bare id or the same id prefixed with `iii://`. |
| `directory::skills::index` | Render one short markdown entry per installed worker (skills with frontmatter `type: index`). Returns `{ body, workers_count }` where `body` is a ready-to-paste page: `# Skills index`, then one `## <worker title>` heading + the worker's first overview paragraph + a `Read iii://<ns>/index` pointer the agent can follow with `directory::skills::get`. Token-light by design; use `directory::skills::list` for per-skill rows. |

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

Expand All @@ -192,15 +201,15 @@ other adapter.
| `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::list` | List workers connected to the engine; shares the core `name` / `description` / `version` fields with `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`. |
| `directory::registry::workers::list` | Browse / search published workers in `api.workers.iii.dev`. Optional free-text `search` (matched fuzzy by `pg_trgm`) and opaque `cursor` for pagination; page size is server-authored. Response is `{ workers: [...], pagination: { next_cursor, has_more, page_size } }`. Shares the core `name` / `description` / `version` fields with `directory::engine::workers::list`. |
| `directory::registry::workers::info` | Full registry detail for one worker. Fans out two parallel registry calls — `GET /w/{slug}` for the worker envelope (publication metadata + readme + functions + triggers) and `GET /w/{slug}/skills` for the skills/prompts tree — and merges them into `{ worker, readme, api_reference, skills_tree }`. The user-facing input still accepts `version:` (semver) or `tag:` (e.g. `latest`); both go on the wire as `?version=…`. |

Both `directory::registry::*` responses are cached in-process for
`registry_cache_ttl_ms` (default 60s).
Expand Down
51 changes: 33 additions & 18 deletions iii-directory/skills/directory/registry/workers/info.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ title: Inspect one worker's full registry metadata

Call `directory::registry::workers::info` to pull the FULL published
metadata for one worker from the public registry: worker envelope
(name, description, version, repo, author), readme markdown, the API
(name, description, version, repo, author, plus the publication
metadata `type` / `config` / `supported_targets` / `total_downloads` /
`dependencies` / optional `image`), readme markdown, the API
reference (functions + triggers with schemas), and the list of skill /
prompt files the bundle ships.

This is the REMOTE counterpart to `directory::engine::workers::info`.
Both responses wrap the worker payload in a top-level `worker` field,
Both responses wrap the worker payload in a top-level `worker` field
and the core fields (`name`, `description`, `version`) are guaranteed
on both surfaces so a parser that touches only those keys works against
either; everything else is surface-specific (registry adds `repo` /
`author` plus the top-level `readme`, `api_reference`, `skills_tree`,
directory adds runtime/connection state).
on both surfaces, so a parser that only touches those keys works
against either; everything else is surface-specific (registry adds
publication metadata plus the top-level `readme`, `api_reference`,
`skills_tree`; the engine view adds runtime / connection state).

| Question | Use this |
|-----------------------------------------------------------|---------------------------------------|
Expand All @@ -36,18 +38,25 @@ directory adds runtime/connection state).
```

You may pass either `version` or `tag`, not both. With neither, the
worker info defaults to `tag: "latest"`.
worker info defaults to `tag: "latest"`. The worker rewrites both
inputs to `?version=…` on the wire (per the OpenAPI contract — the
registry's `?version` query param accepts both tags and exact semvers).

# Outputs

```json
{
"worker": { // same shape as directory::registry::workers::list rows
"name": "agent-memory", // shared core field with directory::engine::workers::info.worker
"description": "Persistent memory tier for agents.", // shared core field
"version": "1.2.3", // shared core field (the resolved version)
"repo": "https://github.com/iii-hq/workers",
"author": { "name": "iii", "is_verified": true }
"worker": {
"name": "agent-memory", // shared core field
"description": "Persistent memory tier for agents.", // shared core field
"type": "binary", // binary | image | engine
"version": "1.2.3", // shared core field (resolved)
"repo": "https://github.com/iii-hq/workers",
"config": {},
"supported_targets": ["x86_64-unknown-linux-gnu"],
"total_downloads": 4242,
"dependencies": [],
"author": { "name": "iii", "pfp": null, "verified": true }
},
"readme": "# agent-memory\n\nDocs here.", // optional; null if registry omits it
"api_reference": {
Expand Down Expand Up @@ -77,13 +86,19 @@ worker info defaults to `tag: "latest"`.
}
```

`worker` / `readme` / `api_reference` come from `GET /w/{slug}?version=…`.
`skills_tree` comes from a parallel `GET /w/{slug}/skills?version=…`
call — the worker fans both out concurrently and merges them, dropping
the markdown `content` and prompt `args_schema` from the skills payload
(call `directory::skills::download` to materialise bodies on disk).

# Caching

Each unique `(name, version|tag)` pair is cached for
`registry_cache_ttl_ms` (default 60s). Repeat calls within the TTL
window don't hit the registry — they return the same response from
in-process memory. To bust the cache, wait out the TTL or call with a
different version/tag.
window don't hit the registry — they return the same merged response
from in-process memory. To bust the cache, wait out the TTL or call
with a different version/tag.

# Worked example

Expand All @@ -102,7 +117,7 @@ Pin to an exact version:
# Related

- `directory::registry::workers::list` — discover the worker name first.
- `directory::engine::workers::info` — same `worker` envelope against
the connected engine.
- `directory::engine::workers::info` — same core `worker` fields
(`name` / `description` / `version`) against the connected engine.
- `directory::skills::download` — install the worker's skill bundle
locally (uses the same registry under the hood).
71 changes: 46 additions & 25 deletions iii-directory/skills/directory/registry/workers/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ title: List workers from the public registry

# When to use

Use `directory::registry::workers::list` to search the public workers
registry (`api.workers.iii.dev`) by free-text term and get back a list
of PUBLISHED workers — the workers a user could install, regardless of
Use `directory::registry::workers::list` to browse or search the public
workers registry (`api.workers.iii.dev`) and get back a page of
PUBLISHED workers — the workers a user could install, regardless of
whether any of them are currently connected to this engine.

This is the REMOTE counterpart to `directory::engine::workers::list`.
Rows on both surfaces share a fixed set of core fields (`name`,
`description`, `version`) so a parser that touches only those keys
works against either; everything else is surface-specific (registry
adds `repo` / `author`, directory adds runtime/connection state).
Rows on both surfaces share the core fields `name` / `description` /
`version` (so a parser that only touches those keys works against
either), but the registry row also surfaces publication metadata
(`type`, `config`, `supported_targets`, `total_downloads`,
`dependencies`, optional `image`) that the engine view doesn't have.

| Question | Use this |
|---------------------------------------------------|---------------------------------------|
Expand All @@ -26,60 +27,80 @@ adds `repo` / `author`, directory adds runtime/connection state).

```json
{
"search": "memory", // required, non-empty; forwarded to GET /search?q=...
"limit": 20 // optional, default 20, capped at 100
"search": "memory", // optional free-text query (matched fuzzy by pg_trgm against name + description)
"cursor": "..." // optional opaque cursor returned by a previous call's pagination.next_cursor
}
```

The registry doesn't currently expose an unscoped browse endpoint, so
`search` MUST be non-empty. (Local `directory::engine::workers::list`
allows empty `search` since it has access to all connected workers
locally.)
Both fields are optional. With no `search`, the registry orders by
`total_downloads DESC`. With `search`, it ranks by similarity. Page
size is server-authored — the client cannot override it.

# Outputs

```json
{
"workers": [
{
"name": "agent-memory", // shared core field
"description": "Persistent memory tier for agents.", // shared core field
"version": "0.4.0", // shared core field (latest published)
"repo": "https://github.com/iii-hq/workers",
"author": { "name": "iii", "profile_picture": null, "is_verified": true }
"name": "agent-memory", // shared core field
"description": "Persistent memory tier for agents.", // shared core field
"type": "binary", // binary | image | engine
"version": "0.4.0", // shared core field (latest published)
"repo": "https://github.com/iii-hq/workers",
"config": {},
"supported_targets": ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin"],
"total_downloads": 4242,
"dependencies": [],
"author": { "name": "iii", "pfp": null, "verified": true }
}
]
],
"pagination": {
"next_cursor": "eyJzIjo0Mi4wLCJpZCI6IjBkNTRhMWZmLTJjMjMtNGY4MC05ZTRkLTRmNmVkM2EwYTgxMiJ9",
"has_more": true,
"page_size": 20
}
}
```

The first three fields (`name`, `description`, `version`) are shared
with `directory::engine::workers::list` rows.
with `directory::engine::workers::list` rows; everything else is
registry-only metadata.

`pagination.next_cursor` is opaque — pass it back as `cursor:` to fetch
the next page. `null` on the last page (with `has_more: false`).
`page_size` is the server's choice; clients can't override it.

# Caching

Each unique `(search, limit)` pair is cached for `registry_cache_ttl_ms`
Each unique `(search, cursor)` pair is cached for `registry_cache_ttl_ms`
(default 60s). Repeat calls within the TTL window don't hit the
registry — they return the same response from in-process memory.

# Worked example

Browse the most-downloaded workers (no search):

```json
{}
```

Find every published worker mentioning "memory":

```json
{ "search": "memory" }
```

Top 5 results for "router":
Fetch the next page (using a cursor from a previous call):

```json
{ "search": "router", "limit": 5 }
{ "search": "memory", "cursor": "eyJzIjo0Mi4wLCJpZCI6IjBkNTRhMWZmLTJjMjMtNGY4MC05ZTRkLTRmNmVkM2EwYTgxMiJ9" }
```

# Related

- `directory::registry::workers::info` — full registry detail for one
worker.
- `directory::engine::workers::list` — same row shape against connected
workers.
- `directory::engine::workers::list` — same shared core fields against
connected workers.
- `directory::skills::download` — install a worker's skill bundle by
name.
Loading
Loading