$worker

iii-directory

v0.9.0

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

  • macOS: arm64 · x64
  • Linux: arm64 · armv7 · x64
  • Windows: arm64 · x64 · x86

install

install
$iii worker add iii-directory
README.md

iii-directory

Workers registry HTTP proxy and filesystem-backed skill + prompt reader for the iii engine. Every public function sits under a single directory::* namespace, split into three sub-namespaces (all MCP-agnostic):

Surface What clients see When to use it
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 ## </code> + first paragraph + <code class="readme-inline-code">read more</code> link per <code class="readme-inline-code">type: index</code> skill). <code class="readme-inline-code">title</code> prefers the YAML frontmatter <code class="readme-inline-code">title:</code> over the body H1; <code class="readme-inline-code">type</code> is lifted from frontmatter <code class="readme-inline-code">type:</code> (e.g. <code class="readme-inline-code">index</code>, <code class="readme-inline-code">how-to</code>, <code class="readme-inline-code">reference</code>) and serialised as <code class="readme-inline-code">null</code> when absent.</td> <td>Orientation: "when and why to use my worker's tools"</td> </tr> <tr> <td><strong>Prompts</strong> (<code class="readme-inline-code">directory::prompts::*</code>)</td> <td>Static prompt templates listed by <code class="readme-inline-code">directory::prompts::list</code> and read by <code class="readme-inline-code">directory::prompts::get</code></td> <td>Parametric command templates the <em>user</em> invokes</td> </tr> <tr> <td><strong>Registry</strong> (<code class="readme-inline-code">directory::registry::*</code>)</td> <td>HTTP proxy over <code class="readme-inline-code">api.workers.iii.dev</code> with <code class="readme-inline-code">workers::{list,info}</code>. Rows share the core <code class="readme-inline-code">name</code> / <code class="readme-inline-code">description</code> / <code class="readme-inline-code">version</code> fields with the engine's <code class="readme-inline-code">engine::workers::list</code> and add publication metadata (<code class="readme-inline-code">type</code>, <code class="readme-inline-code">config</code>, <code class="readme-inline-code">supported_targets</code>, <code class="readme-inline-code">total_downloads</code>, <code class="readme-inline-code">dependencies</code>, optional <code class="readme-inline-code">image</code>). <code class="readme-inline-code">workers::list</code> is cursor-paginated with a server-authored page size.</td> <td>"What's published in the public registry?"</td> </tr> </tbody></table> <p>Engine introspection (functions / triggers / registered triggers / workers) is served by the engine natively at <code class="readme-inline-code">engine::functions::*</code>, <code class="readme-inline-code">engine::triggers::*</code>, <code class="readme-inline-code">engine::registered-triggers::*</code>, and <code class="readme-inline-code">engine::workers::*</code>. Earlier versions of this crate wrapped those calls under <code class="readme-inline-code">directory::engine::*</code> helpers; the wrappers have been removed — call the engine ids directly.</p> <p>Skills and prompts are sourced from a single configured folder on disk (<code class="readme-inline-code">skills_folder</code>). The only write path is the <strong><code class="readme-inline-code">directory::skills::download</code></strong> function, which pulls markdown into <code class="readme-inline-code">skills_folder</code> from either the <a href="https://workers.iii.dev">workers registry</a> or a GitHub repo. Once downloaded, files belong to the developer — edit them however you want.</p> <p><code class="readme-inline-code">directory::registry::workers::*</code> and the engine's <code class="readme-inline-code">engine::workers::*</code> share the core <code class="readme-inline-code">name</code> / <code class="readme-inline-code">description</code> / <code class="readme-inline-code">version</code> fields so a parser that touches only those keys works against either surface; the registry view also surfaces publication metadata (<code class="readme-inline-code">type</code>, <code class="readme-inline-code">config</code>, <code class="readme-inline-code">supported_targets</code>, <code class="readme-inline-code">total_downloads</code>, <code class="readme-inline-code">dependencies</code>, optional <code class="readme-inline-code">image</code>) and the engine view adds runtime / connection state.</p> <h2 id="table-of-contents" class="scroll-mt-20">Table of contents</h2> <ol> <li><a href="#install">Install</a></li> <li><a href="#configuration">Configuration</a></li> <li><a href="#quickstart-download-some-skills">Quickstart: download some skills</a></li> <li><a href="#on-disk-layout">On-disk layout</a></li> <li><a href="#skill-ids">Skill ids</a></li> <li><a href="#functions">Functions</a></li> <li><a href="#custom-trigger-types">Custom trigger types</a></li> <li><a href="#local-development--testing">Local development & testing</a></li> <li><a href="#migration-from-skills-v02x">Migration from skills v0.2.x</a></li> </ol> <hr> <h2 id="install" class="scroll-mt-20">Install</h2> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#59873A">iii</span><span style="color:#B56959"> worker</span><span style="color:#B56959"> add</span><span style="color:#B56959"> iii-directory</span></span></code></pre><p><code class="readme-inline-code">iii worker add</code> fetches the binary, writes a config block into <code class="readme-inline-code">~/.iii/config.yaml</code>, and the engine starts the worker on the next <code class="readme-inline-code">iii start</code>.</p> <hr> <h2 id="skills" class="scroll-mt-20">Skills</h2> <p>Install the <code class="readme-inline-code">iii-directory</code> agent skill for Claude Code, Cursor, and 30+ other agents:</p> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#59873A">npx</span><span style="color:#B56959"> skills</span><span style="color:#B56959"> add</span><span style="color:#B56959"> iii-hq/workers</span><span style="color:#A65E2B"> --skill</span><span style="color:#B56959"> iii-directory</span></span></code></pre><p>Browse or install every worker skill at once:</p> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#59873A">npx</span><span style="color:#B56959"> skills</span><span style="color:#B56959"> add</span><span style="color:#B56959"> iii-hq/workers</span><span style="color:#A65E2B"> --list</span></span> <span class="line"><span style="color:#59873A">npx</span><span style="color:#B56959"> skills</span><span style="color:#B56959"> add</span><span style="color:#B56959"> iii-hq/workers</span><span style="color:#A65E2B"> --all</span></span></code></pre><hr> <h2 id="configuration" class="scroll-mt-20">Configuration</h2> <p>Runtime settings live in the <strong><code class="readme-inline-code">configuration</code> worker</strong> under id <strong><code class="readme-inline-code">iii-directory</code></strong> (the same pattern <code class="readme-inline-code">database</code> and <code class="readme-inline-code">storage</code> use). At boot the worker registers its JSON Schema, reads the live value via <code class="readme-inline-code">configuration::get</code> (the configuration worker env-expands <code class="readme-inline-code">${VAR}</code>), and binds a <code class="readme-inline-code">configuration</code> trigger so it re-fetches on change.</p> <p>Persisted values default to <code class="readme-inline-code">./data/configuration/iii-directory.yaml</code> (fs adapter). Edit that file directly, call <code class="readme-inline-code">configuration::set id=iii-directory</code>, or use the console Workers tab — all three propagate without a redeploy.</p> <h3 id="fields" class="scroll-mt-20">Fields</h3> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#A0ADA0"># TOPOLOGY — changing any of these requires a worker restart.</span></span> <span class="line"><span style="color:#998418">skills_folder</span><span style="color:#999999">:</span><span style="color:#B56959"> ~/.iii/skills</span><span style="color:#A0ADA0"> # read/write root for skills + prompts</span></span> <span class="line"><span style="color:#998418">local_skills_folder</span><span style="color:#999999">:</span><span style="color:#B56959"> ./.iii/skills</span><span style="color:#A0ADA0"> # project-scoped overrides (whole-namespace local-wins)</span></span> <span class="line"><span style="color:#998418">auto_download</span><span style="color:#999999">:</span><span style="color:#1E754F"> true</span><span style="color:#A0ADA0"> # subscribe to worker-add + run the boot reconcile</span></span> <span class="line"></span> <span class="line"><span style="color:#A0ADA0"># TUNABLE — hot-reload live on `configuration:updated`.</span></span> <span class="line"><span style="color:#998418">registry_url</span><span style="color:#999999">:</span><span style="color:#B56959"> https://api.workers.iii.dev</span><span style="color:#A0ADA0"> # workers registry base URL</span></span> <span class="line"><span style="color:#998418">download_timeout_ms</span><span style="color:#999999">:</span><span style="color:#2F798A"> 60000</span><span style="color:#A0ADA0"> # per git-clone / HTTP request timeout (ms)</span></span> <span class="line"><span style="color:#998418">registry_cache_ttl_ms</span><span style="color:#999999">:</span><span style="color:#2F798A"> 60000</span><span style="color:#A0ADA0"> # in-process TTL for registry::workers::* responses</span></span> <span class="line"><span style="color:#998418">filter_unregistered</span><span style="color:#999999">:</span><span style="color:#1E754F"> true</span><span style="color:#A0ADA0"> # hide skills whose namespace isn't an installed worker</span></span></code></pre><p>The <code class="readme-inline-code">skills_folder</code> is created on first download if it doesn't exist.</p> <h3 id="zero-config-default-seed" class="scroll-mt-20">Zero-config default + seed</h3> <p>With no seed and no stored value the worker uses built-in defaults (<code class="readme-inline-code">skills_folder: ~/.iii/skills</code>, <code class="readme-inline-code">registry_url: https://api.workers.iii.dev</code>). Pass <code class="readme-inline-code">--config <path></code> to supply a YAML seed: when present and no value is stored yet, its contents become <code class="readme-inline-code">initial_value</code> on <code class="readme-inline-code">configuration::register</code> (see <a href="config.yaml.example"><code class="readme-inline-code">config.yaml.example</code></a>). Engine-managed deployments inline the config under the worker entry; the engine delivers it via <code class="readme-inline-code">--config</code>.</p> <h3 id="hot-reload" class="scroll-mt-20">Hot reload</h3> <p>On <code class="readme-inline-code">configuration::set</code> (or an external edit to the persisted file), the worker re-fetches the authoritative value. Tunable changes apply in place and the registry caches are cleared so a repointed <code class="readme-inline-code">registry_url</code> takes effect immediately. Topology changes (<code class="readme-inline-code">skills_folder</code> / <code class="readme-inline-code">local_skills_folder</code> / <code class="readme-inline-code">auto_download</code>) are refused with a "restart required" log; the previous configuration is kept until the worker restarts.</p> <hr> <h2 id="quickstart-download-some-skills" class="scroll-mt-20">Quickstart: download some skills</h2> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#A0ADA0"># Pull a specific worker's skills + prompts at a fixed semver from</span></span> <span class="line"><span style="color:#A0ADA0"># the registry. Files land under `<skills_folder>/agent-memory/`.</span></span> <span class="line"><span style="color:#59873A">iii</span><span style="color:#B56959"> trigger</span><span style="color:#A65E2B"> --function-id=directory::skills::download</span><span style="color:#A65E2B"> \</span></span> <span class="line"><span style="color:#A65E2B"> --payload=</span><span style="color:#B5695977">'</span><span style="color:#B56959">{"worker": "agent-memory", "version": "1.2.3"}</span><span style="color:#B5695977">'</span></span> <span class="line"></span> <span class="line"><span style="color:#A0ADA0"># Same, but always fetch whatever's tagged `latest` (also the default</span></span> <span class="line"><span style="color:#A0ADA0"># when neither version nor tag is given).</span></span> <span class="line"><span style="color:#59873A">iii</span><span style="color:#B56959"> trigger</span><span style="color:#A65E2B"> --function-id=directory::skills::download</span><span style="color:#A65E2B"> \</span></span> <span class="line"><span style="color:#A65E2B"> --payload=</span><span style="color:#B5695977">'</span><span style="color:#B56959">{"worker": "agent-memory"}</span><span style="color:#B5695977">'</span></span> <span class="line"></span> <span class="line"><span style="color:#A0ADA0"># Pull a single subfolder out of a public GitHub repo via</span></span> <span class="line"><span style="color:#A0ADA0"># `git clone --depth 1 --branch main`. Files land under</span></span> <span class="line"><span style="color:#A0ADA0"># `<skills_folder>/frontend-design/`. The `branch` field defaults to</span></span> <span class="line"><span style="color:#A0ADA0"># `main`; pass `"master"` for older repos that haven't migrated.</span></span> <span class="line"><span style="color:#59873A">iii</span><span style="color:#B56959"> trigger</span><span style="color:#A65E2B"> --function-id=directory::skills::download</span><span style="color:#A65E2B"> \</span></span> <span class="line"><span style="color:#A65E2B"> --payload=</span><span style="color:#B5695977">'</span><span style="color:#B56959">{</span></span> <span class="line"><span style="color:#B56959"> "repo": "https://github.com/anthropics/skills",</span></span> <span class="line"><span style="color:#B56959"> "skill": "frontend-design"</span></span> <span class="line"><span style="color:#B56959"> }</span><span style="color:#B5695977">'</span></span></code></pre><p>The response is <code class="readme-inline-code">{ namespace, skills_written, prompts_written, source }</code> where <code class="readme-inline-code">skills_written</code> and <code class="readme-inline-code">prompts_written</code> are arrays of relative paths / prompt names that were materialised in this run.</p> <p>After every successful download the worker fires the <code class="readme-inline-code">directory::skills::on-change</code> and/or <code class="readme-inline-code">directory::prompts::on-change</code> trigger types so that subscribers like the <a href="../mcp/"><code class="readme-inline-code">mcp</code></a> worker can forward MCP <code class="readme-inline-code">notifications/list_changed</code> to their clients.</p> <hr> <h2 id="on-disk-layout" class="scroll-mt-20">On-disk layout</h2> <p>The worker assumes a fixed layout under <code class="readme-inline-code">skills_folder</code>:</p> <pre><code>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</code></pre><p>A few rules:</p> <ul> <li><strong>Skill ids</strong> are the relative path under <code class="readme-inline-code">skills_folder</code> with <code class="readme-inline-code">.md</code> stripped. Each segment must satisfy <code class="readme-inline-code">[a-z0-9_-]{1,64}</code>.</li> <li><strong>Skill frontmatter is optional.</strong> When present, the reader honours two keys: <code class="readme-inline-code">title:</code> (used by <code class="readme-inline-code">directory::skills::list</code> and <code class="readme-inline-code">directory::skills::get</code> in preference to a body <code class="readme-inline-code"># H1</code>) and <code class="readme-inline-code">type:</code> (free-form classifier surfaced verbatim on both responses). Any other YAML keys are ignored.</li> <li><strong>Prompts</strong> live under any <code class="readme-inline-code">*/prompts/*.md</code> path. They must start with a YAML frontmatter block declaring at least <code class="readme-inline-code">description</code>; <code class="readme-inline-code">name</code> is optional and overrides the file-stem default.</li> <li>Files anywhere else (i.e. <em>not</em> in a <code class="readme-inline-code">prompts/</code> segment) are skills.</li> </ul> <p>The download function namespaces by source:</p> <table> <thead> <tr> <th>Source</th> <th>Destination</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">repo=URL skill=NAME branch?=main</code></td> <td><code class="readme-inline-code"><skills_folder>/<NAME>/...</code></td> </tr> <tr> <td><code class="readme-inline-code">worker=NAME version=…</code></td> <td><code class="readme-inline-code"><skills_folder>/<NAME>/...</code></td> </tr> <tr> <td><code class="readme-inline-code">worker=NAME tag=…</code> (default <code class="readme-inline-code">tag=latest</code>)</td> <td><code class="readme-inline-code"><skills_folder>/<NAME>/...</code></td> </tr> </tbody></table> <p>Re-pulling the same source overwrites files <strong>file-by-file</strong> — existing siblings outside the response set are preserved (so hand-edited additions survive a re-pull).</p> <hr> <h2 id="skill-ids" class="scroll-mt-20">Skill ids</h2> <p>Skills are addressed by their relative path under <code class="readme-inline-code">skills_folder</code> with <code class="readme-inline-code">.md</code> stripped — e.g. <code class="readme-inline-code"><skills_folder>/agent-memory/observe.md</code> → id <code class="readme-inline-code">"agent-memory/observe"</code>. The same string is what <code class="readme-inline-code">directory::skills::list</code> returns and what <code class="readme-inline-code">directory::skills::get</code> expects in <code class="readme-inline-code">{ "id": ... }</code>. The legacy <code class="readme-inline-code">iii://{id}</code> link form is still accepted on <code class="readme-inline-code">get</code> (the prefix is auto-stripped), but the worker no longer parses any other <code class="readme-inline-code">iii://</code> URI shape — bodies are read solely by id, and the auto-rendered tree-shaped index that previous releases served at <code class="readme-inline-code">iii://directory/skills</code> is gone. Consumers that want a tree-shaped picker iterate <code class="readme-inline-code">list</code> rows themselves and indent by <code class="readme-inline-code">id.matches('/').count()</code>.</p> <hr> <h2 id="functions" class="scroll-mt-20">Functions</h2> <p>Sixteen functions, all under <code class="readme-inline-code">directory::*</code>. All registrations are namespace-clean; this worker is intentionally agnostic to MCP and any other adapter.</p> <h3 id="directoryskills-filesystem-reader" class="scroll-mt-20"><code class="readme-inline-code">directory::skills::*</code> (filesystem reader)</h3> <table> <thead> <tr> <th>Function ID</th> <th>Description</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">directory::skills::download</code></td> <td>Pull markdown into <code class="readme-inline-code">skills_folder</code>. Either <code class="readme-inline-code">{repo, skill, branch?}</code> (defaults <code class="readme-inline-code">branch=main</code>) or `{worker, version?</td> </tr> <tr> <td><code class="readme-inline-code">directory::skills::list</code></td> <td>Enriched listing of every fs-backed skill: <code class="readme-inline-code">{ id, title, type, description, bytes, modified_at }</code> per row. <code class="readme-inline-code">title</code> prefers the YAML frontmatter <code class="readme-inline-code">title:</code> over the body H1, <code class="readme-inline-code">type</code> is lifted from frontmatter <code class="readme-inline-code">type:</code> (<code class="readme-inline-code">null</code> when absent), and <code class="readme-inline-code">description</code> is the first paragraph of the body — so consumers can render a picker without a follow-up <code class="readme-inline-code">get</code> per row.</td> </tr> <tr> <td><code class="readme-inline-code">directory::skills::get</code></td> <td>Fetch one skill by id. Returns <code class="readme-inline-code">{ id, title, type, description, body, modified_at }</code> — same shape <code class="readme-inline-code">directory::skills::list</code> rows use, plus the raw markdown <code class="readme-inline-code">body</code>. Same title-resolution and <code class="readme-inline-code">type</code> precedence as <code class="readme-inline-code">list</code>. Accepts a bare id or the same id prefixed with <code class="readme-inline-code">iii://</code>.</td> </tr> <tr> <td><code class="readme-inline-code">directory::skills::index</code></td> <td>Render one short markdown entry per installed worker (skills with frontmatter <code class="readme-inline-code">type: index</code>). Returns <code class="readme-inline-code">{ body, workers_count }</code> where <code class="readme-inline-code">body</code> is a ready-to-paste page: <code class="readme-inline-code"># Skills index</code>, then one <code class="readme-inline-code">## <worker title></code> heading + the worker's first overview paragraph + a <code class="readme-inline-code">Read iii://<ns>/index</code> pointer the agent can follow with <code class="readme-inline-code">directory::skills::get</code>. Token-light by design; use <code class="readme-inline-code">directory::skills::list</code> for per-skill rows.</td> </tr> </tbody></table> <h3 id="directoryprompts-filesystem-reader" class="scroll-mt-20"><code class="readme-inline-code">directory::prompts::*</code> (filesystem reader)</h3> <table> <thead> <tr> <th>Function ID</th> <th>Description</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">directory::prompts::list</code></td> <td>Metadata-only listing of every fs-backed prompt.</td> </tr> <tr> <td><code class="readme-inline-code">directory::prompts::get</code></td> <td>Fetch one prompt's body + <code class="readme-inline-code">{name, description, modified_at}</code>. Plain shape, no envelope.</td> </tr> </tbody></table> <h3 id="engine-introspection-native" class="scroll-mt-20">Engine introspection (native)</h3> <p>Engine introspection is no longer wrapped here. Call the engine's native ids directly — every one takes the same filters (<code class="readme-inline-code">prefix</code>, <code class="readme-inline-code">search</code>, <code class="readme-inline-code">worker</code>, <code class="readme-inline-code">include_internal</code> where applicable):</p> <table> <thead> <tr> <th>Function ID</th> <th>Description</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">engine::functions::list</code></td> <td>List functions registered with the engine.</td> </tr> <tr> <td><code class="readme-inline-code">engine::functions::info</code></td> <td>Single-function detail: schemas, owning worker.</td> </tr> <tr> <td><code class="readme-inline-code">engine::triggers::list</code></td> <td>List trigger TYPES (the providers, e.g. <code class="readme-inline-code">http</code>, <code class="readme-inline-code">cron</code>).</td> </tr> <tr> <td><code class="readme-inline-code">engine::triggers::info</code></td> <td>Single trigger-type detail: configuration schema, return schema.</td> </tr> <tr> <td><code class="readme-inline-code">engine::registered-triggers::list</code></td> <td>List trigger INSTANCES (subscriber rows).</td> </tr> <tr> <td><code class="readme-inline-code">engine::registered-triggers::info</code></td> <td>Single registered-trigger detail.</td> </tr> <tr> <td><code class="readme-inline-code">engine::workers::list</code></td> <td>List workers with an open engine WS connection. Daemon-managed providers (<code class="readme-inline-code">iii-http</code>, <code class="readme-inline-code">iii-cron</code>, <code class="readme-inline-code">iii-state</code>) won't appear — call <code class="readme-inline-code">worker::list</code> from the supervisor to see those.</td> </tr> <tr> <td><code class="readme-inline-code">engine::workers::info</code></td> <td>One worker's detail by <code class="readme-inline-code">name</code>.</td> </tr> </tbody></table> <h3 id="directoryregistry-workers-registry-http-proxy" class="scroll-mt-20"><code class="readme-inline-code">directory::registry::*</code> (workers registry HTTP proxy)</h3> <table> <thead> <tr> <th>Function ID</th> <th>Description</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">directory::registry::workers::list</code></td> <td>Browse / search published workers in <code class="readme-inline-code">api.workers.iii.dev</code>. Optional free-text <code class="readme-inline-code">search</code> (matched fuzzy by <code class="readme-inline-code">pg_trgm</code>) and opaque <code class="readme-inline-code">cursor</code> for pagination; page size is server-authored. Response is <code class="readme-inline-code">{ workers: [...], pagination: { next_cursor, has_more, page_size } }</code>. Shares the core <code class="readme-inline-code">name</code> / <code class="readme-inline-code">description</code> / <code class="readme-inline-code">version</code> fields with the engine's <code class="readme-inline-code">engine::workers::list</code>.</td> </tr> <tr> <td><code class="readme-inline-code">directory::registry::workers::info</code></td> <td>Full registry detail for one worker. Fans out two parallel registry calls — <code class="readme-inline-code">GET /w/{slug}</code> for the worker envelope (publication metadata + readme + functions + triggers) and <code class="readme-inline-code">GET /w/{slug}/skills</code> for the skills/prompts tree — and merges them into <code class="readme-inline-code">{ worker, readme, api_reference, skills_tree }</code>. The user-facing input still accepts <code class="readme-inline-code">version:</code> (semver) or <code class="readme-inline-code">tag:</code> (e.g. <code class="readme-inline-code">latest</code>); both go on the wire as <code class="readme-inline-code">?version=…</code>.</td> </tr> </tbody></table> <p>Both <code class="readme-inline-code">directory::registry::*</code> responses are cached in-process for <code class="readme-inline-code">registry_cache_ttl_ms</code> (default 60s).</p> <p>There is <strong>no</strong> <code class="readme-inline-code">directory::skills::register</code> / <code class="readme-inline-code">directory::prompts::register</code> — see <a href="#migration-from-skills-v02x">Migration</a> below.</p> <hr> <h2 id="custom-trigger-types" class="scroll-mt-20">Custom trigger types</h2> <table> <thead> <tr> <th>Trigger type</th> <th>Fires when</th> <th>Payload to subscribers</th> </tr> </thead> <tbody><tr> <td><code class="readme-inline-code">directory::skills::on-change</code></td> <td>After a <code class="readme-inline-code">directory::skills::download</code> that wrote at least one skill markdown file</td> <td><code class="readme-inline-code">{ "op": "download", "namespace": "<ns>", "source": "repo" | "registry" }</code></td> </tr> <tr> <td><code class="readme-inline-code">directory::prompts::on-change</code></td> <td>After a <code class="readme-inline-code">directory::skills::download</code> that wrote at least one prompt markdown file</td> <td><code class="readme-inline-code">{ "op": "download", "namespace": "<ns>", "source": "repo" | "registry" }</code></td> </tr> </tbody></table> <p>Dispatches are fire-and-forget (Void), so the download path doesn't block on downstream latency.</p> <hr> <h2 id="local-development-testing" class="scroll-mt-20">Local development & testing</h2> <h3 id="run-from-source" class="scroll-mt-20">Run from source</h3> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#A0ADA0"># --config is an optional YAML seed (see config.yaml.example); omit it to</span></span> <span class="line"><span style="color:#A0ADA0"># rely on the value stored in the `configuration` worker (or built-in defaults).</span></span> <span class="line"><span style="color:#59873A">cargo</span><span style="color:#B56959"> run</span><span style="color:#A65E2B"> --release</span><span style="color:#A65E2B"> --</span><span style="color:#A65E2B"> --url</span><span style="color:#B56959"> ws://127.0.0.1:49134</span><span style="color:#A65E2B"> --config</span><span style="color:#B56959"> ./config.yaml.example</span></span></code></pre><h3 id="tests" class="scroll-mt-20">Tests</h3> <pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"><span style="color:#A0ADA0"># Fast, offline — exercises the pure helpers (markdown / id validators</span></span> <span class="line"><span style="color:#A0ADA0"># / fs source) without needing an iii engine.</span></span> <span class="line"><span style="color:#59873A">cargo</span><span style="color:#B56959"> test</span><span style="color:#A65E2B"> --lib</span></span> <span class="line"></span> <span class="line"><span style="color:#A0ADA0"># Full BDD suite — requires an iii engine on ws://127.0.0.1:49134</span></span> <span class="line"><span style="color:#A0ADA0"># (or III_ENGINE_WS_URL). The git-backed download scenarios spin up</span></span> <span class="line"><span style="color:#A0ADA0"># a local fixture repo via `git init`; the registry-backed scenarios</span></span> <span class="line"><span style="color:#A0ADA0"># point a wiremock server at the worker's `registry_url` config.</span></span> <span class="line"><span style="color:#59873A">cargo</span><span style="color:#B56959"> test</span></span> <span class="line"></span> <span class="line"><span style="color:#A0ADA0"># One feature group at a time. Available tags:</span></span> <span class="line"><span style="color:#A0ADA0"># @engine @read @prompts @download @download_repo @download_registry</span></span> <span class="line"><span style="color:#59873A">cargo</span><span style="color:#B56959"> test</span><span style="color:#A65E2B"> --test</span><span style="color:#B56959"> bdd</span><span style="color:#A65E2B"> --</span><span style="color:#A65E2B"> --tags</span><span style="color:#B56959"> @download</span></span></code></pre><p>The BDD harness lives under <a href="tests/">tests/</a>. Feature files mirror the modules in <a href="src/functions/">src/functions/</a>. Step definitions under <a href="tests/steps/">tests/steps/</a> drive each feature through the same <code class="readme-inline-code">iii.trigger</code> path the production binary uses.</p> </div></div></section></div><aside class="flex flex-col gap-y-4 @4xl:sticky @4xl:top-4 @4xl:self-start @4xl:max-h-[calc(100dvh-2rem)] @4xl:overflow-y-auto"><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">details</span></div><div class="p-4 flex flex-col gap-y-2.5"><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">version</span><span class="text-ink truncate text-right lowercase tabular-nums">v0.9.0</span></div><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">type</span><span class="text-ink truncate text-right lowercase ">binary</span></div><div class="flex items-baseline justify-between font-mono text-[13px] gap-x-3 min-w-0"><span class="text-ink-faint shrink-0 uppercase tracking-[0.06em] text-[11px]">repo</span><a href="https://github.com/iii-hq/workers" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink lowercase hover:text-accent transition-colors min-w-0 text-right justify-end"><span class="truncate">iii-hq/workers</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a></div></div></div><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">api</span></div><div class="p-4 flex flex-col gap-y-4"><div class="flex flex-col gap-y-1.5"><div class="flex items-baseline justify-between"><span class="font-mono text-[10px] uppercase tracking-[0.18em] text-ink-ghost">functions</span><span class="font-mono text-[10px] text-ink-ghost tabular-nums">13</span></div><ul class="flex flex-col"><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::__on_worker_added">directory::__on_worker_added</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::engine::functions::info">directory::engine::functions::info</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::on-config-change">directory::on-config-change</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::prompts::get">directory::prompts::get</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::prompts::list">directory::prompts::list</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::registry::workers::info">directory::registry::workers::info</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::registry::workers::list">directory::registry::workers::list</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::download">directory::skills::download</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::download_from_registry">directory::skills::download_from_registry</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::download_from_repo">directory::skills::download_from_repo</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::get">directory::skills::get</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::index">directory::skills::index</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#fn-directory::skills::list">directory::skills::list</a></li></ul></div><div class="flex flex-col gap-y-1.5"><div class="flex items-baseline justify-between"><span class="font-mono text-[10px] uppercase tracking-[0.18em] text-ink-ghost">triggers</span><span class="font-mono text-[10px] text-ink-ghost tabular-nums">2</span></div><ul class="flex flex-col"><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#trigger-directory::prompts::on-change">directory::prompts::on-change</a></li><li><a class="block font-mono text-[12.5px] truncate py-0.5 transition-colors text-ink hover:text-accent" href="/workers/iii-directory?tab=api#trigger-directory::skills::on-change">directory::skills::on-change</a></li></ul></div></div></div><div class="border border-rule bg-bg"><div class="bg-panel px-3.5 py-2.5 border-b border-rule"><span class="font-mono text-[11px] font-medium uppercase tracking-[0.18em] text-ink-faint">author</span></div><div class="p-4 flex flex-col gap-y-2.5"><div class="flex items-center gap-x-2 font-mono text-[13px]"><img src="https://iii.dev/docs/_mintlify/favicons/motiadev/AFkVbz_UcSL_5jsm/_generated/favicon/apple-touch-icon.png" alt="" class="size-5 object-cover"/><span class="text-ink lowercase">iii</span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-badge-check size-3.5 text-accent" aria-hidden="true"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"></path><path d="m9 12 2 2 4-4"></path></svg></div></div></div></aside></div></div></div><!--$--><!--/$--></main><footer class="border-t border-rule bg-bg"><div class="flex items-center justify-between flex-wrap gap-x-6 gap-y-3 px-9 py-4.5"><a aria-label="iii worker — registry home" class="inline-flex items-center gap-x-2 text-ink" href="/"><svg viewBox="0 0 1075.74 1075.69" aria-hidden="true" focusable="false" class="size-[14px]" fill="currentColor"><rect x="0" y="0.05" width="268.94" height="268.94"></rect><rect x="403.4" y="0.05" width="268.94" height="268.94"></rect><rect x="806.81" y="0.05" width="268.94" height="268.94"></rect><rect x="0" y="403.45" width="268.94" height="672.24"></rect><rect x="403.4" y="403.45" width="268.94" height="672.24"></rect><rect x="806.81" y="403.45" width="268.94" height="672.24"></rect></svg><span class="font-mono text-[13px] font-medium tracking-[-0.02em] lowercase">iii / worker</span></a><nav aria-label="footer" class="flex items-center flex-wrap gap-x-6 gap-y-2"><a href="https://iii.dev" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">iii.dev<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a href="https://iii.dev/docs" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">docs<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a class="font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors" href="/badge-preview">badge preview</a><a href="https://discord.gg/iiidev" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">discord<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a><a href="https://github.com/iii-hq/iii" target="_blank" rel="noreferrer" class="inline-flex items-center gap-x-1 font-mono text-[13px] text-ink-faint hover:text-ink lowercase transition-colors">github<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-3 shrink-0" aria-hidden="true"><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path></svg></a></nav></div></footer></div></div></body></html>