skill / tool-publisher
!

Not installable via adompkg

This skill has no published release. adompkg install kyle/tool-publisher will not work until a maintainer publishes a tarball with install.sh and uninstall.sh.

See the publishing docs for the package.json schema and tarball layout required to ship this skill.

Publishing Tools to the Adom Wiki

This skill covers the full lifecycle for making a CLI tool or skill discoverable
and installable by any Adom user — without hardcoding it into gallia/install.mjs,
and without requiring the installing user to have GitHub access to adom-inc/*.

Public vs Private: The Line

Every wiki-published tool has a public side and a private side, and the
demarcation matters for onboarding 3rd-party collaborators:

  • Public (wiki-hosted): the compiled binary, the usage docs, the SKILL.md,
    screenshots, install hint. Anyone on the Adom platform — including 3rd-party
    collaborators with zero GitHub access — can see and install it by hitting
    https://wiki-ufypy5dpx93o.adom.cloud/static/apps/<slug>/<binary>. No auth, no
    GitHub token.
  • Private (GitHub, optional): the source code, build system, issue tracker,
    internal design docs. Stays on adom-inc/<repo> with whatever read permissions
    you want. The wiki page MAY carry a repo link in metadata for team members
    who have GitHub access, but the link is not required and is not used by
    the installer
    .

What this means when you publish:

  • Build the binary yourself (cargo build --release).
  • Upload it to the wiki as a docker_binary asset via adom-wiki asset upload.
    Latest wins — the server auto-deletes any previous docker_binary on the same
    page before inserting the new one.
  • Set metadata.releases.adom_docker.install_hint to a curl command that hits
    /static/apps/<slug>/<asset_name>, not gh release download.
  • Bump the top-level version: field in the publish body (→ pub_version column,
    semver-validated on the wiki side).
  • Do not tell users to clone the private repo. The install flow must work
    end-to-end from the wiki alone, no GitHub credentials.

What lives where — quick-reference table

Artifact Lives in Why
Source code (*.rs, Cargo.toml, tests, CI config) Private GitHub repo (optional) Internal dev, CI secrets, issue tracking. Choose whether to use GitHub at all.
Compiled CLI binary Public wiki at /static/apps/<slug>/<slug> (uploaded as docker_binary asset) Paste-prompt curl must work for any user — no repo access needed.
SKILL.md content Public wiki page (skill_source field) AND embedded in the binary via include_str! Users browse it on the wiki; <tool> install drops it to ~/.claude/skills/<slug>/.
Install prompt (rendered) Public — auto-generated on the wiki page from metadata Users paste it into Claude Code to trigger the install.
Discovery triggers & pitch Public wiki page metadata Aggregated by /discover into the gallia auto-discover snippet.
Screenshots, README, examples, demos Public wiki page content + assets Browsable by anyone; drives adoption.
Internal design notes, WIP features, roadmap Private GitHub repo Not ready for distribution.
Secrets, API keys, signing keys Neither — env vars / 1Password / CI secrets Never committed to either.

Rule of thumb: if a user on a fresh container needs it to install or run your
tool, it goes on the wiki. Everything else stays in the private repo (if you use
one at all).

Architecture: 2-Layer Distribution

Layer 1: Source Code (private GitHub repo, optional link only)
    ↓  cargo build --release   +   adom-wiki asset upload
Layer 2: Adom Wiki (public: binary asset + discovery metadata + install prompt)
    ↓  gallia/hooks/refresh-wiki-catalog.mjs audits installed versions every 30 min
    ↓  stale tool? Claude asks user "Shall I upgrade?"
User Container (binary upgraded from wiki)

Key principle: gallia/install.mjs is for core infrastructure only (adom-cli,
adom-wiki, viewer, MCP servers). New tools use wiki auto-discovery instead —
they get discovered and installed on-demand, and the 30-min refresh hook keeps
users' installed versions in sync with the wiki's pub_version.

Gallia's only role for new tools is carrying the auto-discover snippet. Not
source, not binary, not SKILL.md, not docs — none of it lives in gallia. The
snippet is regenerated from wiki metadata on every container setup and on every
30-min hook tick. Ship updates by re-publishing the wiki page, not by opening a
gallia PR.

When to Use Each Layer

Distribution When Example
gallia/install.mjs Core infra every container needs adom-cli, adom-wiki, viewer
Wiki auto-discovery Tools users install on-demand adom-molecule, shotlog, adom-tsci
gallia/skills/ Cross-cutting reference skills not tied to one app being phased out — prefer wiki

Prerequisites

Before publishing, you need:

  1. A working CLI (see adom-cli-design skill for Rust CLI conventions)
  2. A SKILL.md embedded in the binary (see skill-creator skill)
  3. A built target/release/<tool> binary — where you build it is up to you.
    A private adom-inc/<tool> GitHub repo is the usual home for source, but
    you do NOT need a GitHub release — the binary goes to the wiki.
  4. The adom-wiki CLI installed
  5. A <tool> --version command that prints a semver line (defaults to --version;
    override via metadata.releases.adom_docker.version_command if your CLI uses
    a different flag)

Step 1: Build the CLI

Follow the adom-cli-design skill. Key requirements:

  • Rust with clap, ureq, serde_json
  • OK: / ERROR: output format
  • install subcommand that deploys SKILL.md via include_str!
  • health subcommand if it talks to a server
  • .gitignore with target/
# Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
ureq = { version = "2", features = ["json"] }
clap = { version = "4", features = ["derive", "env"] }

[profile.release]
strip = true
lto = "thin"

Build:

cargo build --release
sudo cp target/release/my-tool /usr/local/bin/
my-tool install  # deploys SKILL.md to ~/.claude/skills/my-tool/

Step 2: Put source on GitHub (optional) and upload binary to the wiki

2a. Private source repo (optional)

Source code stays private. You choose whether to host it on GitHub at all; it's
fine to just keep the source in your local checkout. If you do use GitHub:

gh repo create adom-inc/my-tool --private --description "One-line description"
cd /path/to/my-tool
git init && git remote add origin https://github.com/adom-inc/my-tool.git
echo "target/" > .gitignore
git add -A && git commit -m "Initial release: my-tool v0.1.0"
git branch -M main && git push -u origin main

Do NOT gh release create. The compiled binary does not belong in a GitHub
release anymore — it goes to the wiki, where it's actually installable by people
without GitHub access.

2b. Upload the binary to the wiki

# Publish a minimal page first (Step 3 fills in the full metadata).
# If you're doing this as part of an update (page already exists), skip the
# publish and go straight to the asset upload.
adom-wiki asset upload apps/my-tool \
  --asset-type docker_binary \
  --file target/release/my-tool \
  --caption "v0.1.0 build for adom docker"

The wiki auto-deletes any previous docker_binary on this page before storing
the new file, so this command is idempotent — run it again on every release.

The canonical public download URL is:

https://wiki-ufypy5dpx93o.adom.cloud/static/apps/my-tool/my-tool

Verify it's reachable without any credentials:

curl -I https://wiki-ufypy5dpx93o.adom.cloud/static/apps/my-tool/my-tool
# expect HTTP/1.1 200 OK, content-type: application/octet-stream

Step 3: Publish Wiki Page

Publish as page type app with full metadata including discovery triggers.

3a. Write the wiki content

Create a markdown file (/tmp/my-tool-wiki.md) with:

  • What it does (1-2 paragraphs)
  • What the curl-based install prompt does for them (they paste it, Claude runs it)
  • Quick start examples
  • Link to the repo (optional — informational only)

3b. Publish with metadata

Critical: Include metadata in the initial publish. The v1 publish endpoint
accepts a monotonically-increasing version: field and overwrites content,
brief, and skill_source on re-publish. Metadata updates, however, are best
done via the dedicated endpoint (see 3c below) or by delete+recreate if the
direct metadata edit doesn't stick — this gap is being closed; see the
"Wiki-side work" section at the bottom of this page.

WIKI_URL="https://wiki-ufypy5dpx93o.adom.cloud"

curl -s -X POST "$WIKI_URL/api/v1/pages" \
  -H "Authorization: Bearer adom-wiki-dev-2025" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "app",
    "slug": "my-tool",
    "title": "My Tool Name",
    "brief": "One-line description for search results and page header.",
    "content": "... markdown content ...",
    "skill_source": "... contents of SKILL.md ...",
    "metadata": {
      "repo": "adom-inc/my-tool",
      "repo_visibility": "private",
      "releases": {
        "adom_docker": {
          "asset_name": "my-tool",
          "install_hint": "curl -fsSL https://wiki-ufypy5dpx93o.adom.cloud/static/apps/my-tool/my-tool -o /usr/local/bin/my-tool && chmod +x /usr/local/bin/my-tool && my-tool install"
        }
      },
      "discovery_triggers": [
        "trigger phrase 1",
        "trigger phrase 2",
        "trigger phrase 3"
      ],
      "discovery_pitch": "One sentence explaining what this tool does and why you want it."
    },
    "version": "0.1.0",
    "changelog": "Initial release"
  }'

Notes on the fields:

  • Top-level versionpub_version column on the page. Semver-validated on
    the wiki side. This is the single source of truth for the tool's version.
    Gallia's 30-min refresh hook reads pub_version directly; it ignores
    metadata.version. Don't bother setting metadata.version.
  • install_hint must be self-contained (no GitHub access, no tokens). Use
    curl -fsSL <wiki static URL> — never gh release download.
  • repo is purely informational. Team members with GitHub access may follow
    it to the source; 3rd-party collaborators will just ignore it.
  • If your tool's --version flag isn't the default, also set
    metadata.releases.adom_docker.version_command to the exact shell command
    gallia should run (e.g. "my-tool version --short").

3c. Verify

# Check the page exists and has metadata
adom-wiki page get apps/my-tool | python3 -c "
import sys, json
d = json.load(sys.stdin)
meta = json.loads(d['page']['metadata'])
print('Triggers:', meta.get('discovery_triggers'))
print('Pitch:', meta.get('discovery_pitch'))
print('Install:', meta.get('releases',{}).get('adom_docker',{}).get('install_hint'))
"

# Check the discover page includes your tool
# Visit: https://wiki-ufypy5dpx93o.adom.cloud/discover

Metadata Fields Reference

Required for Auto-Discovery

Field Type Purpose
discovery_triggers string[] Phrases users might say. Claude matches these to suggest the tool.
discovery_pitch string One-sentence hook shown when Claude suggests the tool.

Required for Auto-Install

Field Type Purpose
releases.adom_docker.asset_name string Binary filename on the wiki asset (must match adom-wiki asset upload --file's filename). Also used as the command -v check and default --version bin.
releases.adom_docker.install_hint string Full install command. Must be self-contained (no GitHub auth). Use curl -fsSL https://wiki-.../static/apps/<slug>/<asset>.

Optional

Field Type Purpose
releases.adom_docker.version_command string Shell command to check the installed version (default: <asset_name> --version). Output must contain a semver like 1.2.3.
repo string GitHub repo (owner/name) — informational, not used by installer
repo_visibility string "private" or "public"
releases.windows object Windows installer info (for desktop apps)

NOT used anymore

  • metadata.version — ignored. Use the top-level version: field, which
    writes to the pub_version column.

How Auto-Discovery + Continuous Sync Works

  1. The wiki's /discover page aggregates all pages with discovery_triggers metadata.
  2. gallia/hooks/refresh-wiki-catalog.mjs fetches /discover and writes the
    concatenated SKILL.md snippet to ~/.claude/skills/adom-wiki-discover/SKILL.md.
  3. The same script also fetches /api/v1/pages, walks every page with a
    releases.adom_docker entry, HEAD-checks the binary's public download URL,
    runs the tool's version command, and compares installed to pub_version.
  4. gallia/hooks/check-updates.sh runs the refresh script every ~30 min (via the
    UserPromptSubmit hook). On success, it updates ~/.adom/last-wiki-check.
  5. If any installed tool is older than its wiki pub_version, the hook injects a
    <system-reminder> telling Claude to ASK the user "Shall I upgrade
    from ?". On confirmation, Claude runs the install_hint
    (which curls the wiki asset). On decline, Claude drops it for the session.
  6. install.mjs also calls the refresh script on container setup, so a fresh
    container starts with the current catalog instead of waiting 30 min for the
    first hook tick.

Gallia never downloads tools from GitHub anymore. Every install / upgrade path
is rooted in the wiki — which means any 3rd-party user with access to the Adom
container gets the same install experience as a team member.

How the Install Prompt Works

The wiki renders an "Install this app" block on every app page. The prompt text is
auto-generated by renderInstallPrompt() in wiki/lib/templates.js based on the
page type and metadata.

For app pages, it uses releases.adom_docker.install_hint to build a prompt like:

I want to install the "My Tool" app from the Adom Wiki. Run this:
curl -fsSL https://wiki-.../static/apps/my-tool/my-tool -o /usr/local/bin/my-tool && chmod +x /usr/local/bin/my-tool && my-tool install
Then verify the install works.

Users copy this prompt and paste it into Claude Code, which executes the install.

Writing Good Discovery Triggers

Think about what a user would say when they need your tool:

Good triggers (specific, action-oriented):

["create molecule", "import kicad files", "upload fusion files",
 "molecule from kicad", "optimize molecule 3d"]

Bad triggers (too generic, would match everything):

["help", "tool", "create", "upload"]

Include 8-15 triggers covering:

  • The primary action ("create molecule")
  • Input formats ("import kicad files", "upload fusion files")
  • Alternate phrasings ("molecule from kicad", "kicad to molecule")
  • Specific features ("optimize molecule", "blender optimization")

Writing a Good Discovery Pitch

One sentence that answers: "Why would I want this?"

Good: "Import KiCad, Fusion 360, or EasyEDA design files into Adom molecules
with one command. Handles file upload, 3D optimization, and molecule management."

Bad: "A CLI tool for molecules."

The install subcommand: what it actually needs to do

The paste-prompt curl ... && my-tool install is the entire onboarding surface
for a new user. Don't under-spec this subcommand — users won't forgive a
half-broken install that leaves them to read a --help and figure it out.
Required responsibilities, in order:

  1. Check system prereqs. If your tool needs bun, node, ffmpeg,
    python3.11+, or anything else the base container doesn't already have,
    check for it up front and print a single ERROR: line with the exact
    install command the user should run. Never proceed past a missing prereq —
    the user's first experience must not be a cryptic runtime crash.

    if which::which("bun").is_err() {
        eprintln!("ERROR: `bun` is required but not found.");
        eprintln!("Install with: curl -fsSL https://bun.sh/install | bash");
        std::process::exit(1);
    }
    
  2. Deploy the binary to ~/.local/bin/<tool> (or /usr/local/bin/ if the
    curl in the install hint put it there — your install subcommand should
    handle the case where the binary is re-run from either location).

  3. Deploy every bundled SKILL.md to ~/.claude/skills/<skill-name>/SKILL.md.
    Use include_str! at compile time so each skill lives alongside the source
    in the private repo. Multi-skill apps (next section) drop multiple files
    here.

  4. Install shell completions for the user's current shell. Silent
    best-effort — if the shell's completion dir doesn't exist, skip.
    Completions significantly improve discoverability of subcommands for
    non-programmer users who don't reflexively run --help.

    my-tool completions bash > ~/.local/share/bash-completion/completions/my-tool 2>/dev/null || true
    my-tool completions zsh  > ~/.zsh/completions/_my-tool 2>/dev/null || true
    
  5. Write an OK: line per action taken and a final summary. Mirror the
    adom-cli-design output format. Users and Claude both read install
    output to confirm success.

  6. Be idempotent. Re-running install should update everything to match
    the current binary (newer SKILL.md, newer completions) and print OK: for
    each step, not error out because files exist.

Minimum viable output:

OK: prereqs checked (bun 1.1.30, gh 2.55.0)
OK: binary at /usr/local/bin/my-tool
OK: deployed 2 skill(s) to ~/.claude/skills/
OK: bash completions installed
OK: my-tool ready. Try `my-tool --help`.

Multi-skill apps: bundling more than one SKILL.md

Some apps ship a reference skill (how to drive the app's own commands)
and a recipe skill (higher-level workflow that uses the app). The
archetype is adom-tsci, which ships both:

  • adom-tsci — reference skill for the binary's CLI (start, reload, open, …)
  • adom-tscircuit — recipe skill for designing Adom Molecules in tscircuit
    using the adom-tsci preview workflow

Both belong in the same repo (adom-inc/adom-tsci) and both get deployed by
the same adom-tsci install subcommand. Layout:

adom-inc/adom-tsci/
├── Cargo.toml
├── src/
│   └── main.rs              // include_str! both SKILL.mds
├── skills/
│   ├── adom-tsci/
│   │   └── SKILL.md         // reference skill
│   └── adom-tscircuit/
│       └── SKILL.md         // recipe skill
└── README.md

In main.rs:

const SKILL_REFERENCE: &str = include_str!("../skills/adom-tsci/SKILL.md");
const SKILL_RECIPE:    &str = include_str!("../skills/adom-tscircuit/SKILL.md");

fn install() {
    write_skill("adom-tsci",      SKILL_REFERENCE);
    write_skill("adom-tscircuit", SKILL_RECIPE);
    // ... completions, prereqs, etc
}

Rules:

  • One wiki page per app, not per skill. The wiki page's skill_source
    field carries the primary (reference) skill; secondary skills are
    documented in the page content and shipped in the binary.
  • Each SKILL.md has its own discovery triggers in its frontmatter so
    Claude loads the right one for the right trigger phrase. The wiki page
    metadata's discovery_triggers covers the install trigger ("install
    adom-tsci", "preview tscircuit in webview", etc.).
  • Keep the recipe skill tightly scoped. If a recipe skill grows features
    that aren't specific to this app, split it out into its own wiki page.

Example: shotlog (reference implementation for the wiki-hosted model)

  • Source (private): adom-inc/shotlog — source stays here, no public release.
  • Wiki page: apps/shotlog
  • Wiki asset: docker_binary uploaded via adom-wiki asset upload, public URL
    https://wiki-ufypy5dpx93o.adom.cloud/static/apps/shotlog/shotlog
  • Discovery triggers: 14 phrases (screenshot log, visual debug loop, ...)
  • Install: curl -fsSL <wiki URL>/static/apps/shotlog/shotlog -o /usr/local/bin/shotlog && chmod +x /usr/local/bin/shotlog && shotlog install
  • NOT in install.mjs — discovered and installed on-demand via the wiki
  • Auto-upgrade: gallia's 30-min refresh hook compares installed shotlog --version
    against the wiki pub_version and offers an upgrade when they diverge

Updating a Published Tool

To release a new version:

# 1. Build the new binary
cargo build --release

# 2. Push the source change to the private repo (optional — if you use one)
git commit -am "..." && git push origin main

# 3. Upload the new binary to the wiki. The previous docker_binary asset on
#    this page is auto-deleted before the new one is stored.
adom-wiki asset upload apps/my-tool \
  --asset-type docker_binary \
  --file target/release/my-tool \
  --caption "v0.2.0 build for adom docker"

# 4. Bump the page's pub_version (and install_hint if it changed).
#    The publish endpoint accepts a monotonically-greater version and
#    overwrites content / skill_source / brief / title. If you also need
#    to change metadata and the field-edit path is flaky, fall back to
#    DELETE + recreate. See "Wiki-side work" below.
adom-wiki page publish apps/my-tool \
  --title "My Tool Name" \
  --brief "..." \
  --body-md /tmp/my-tool-wiki.md \
  --skill-source /path/to/SKILL.md \
  --version 0.2.0 \
  --changelog "..."

# 5. Update metadata (discovery triggers, install hint, etc.)
adom-wiki page edit apps/my-tool \
  --field metadata \
  --body-md /tmp/metadata.json

After republishing, the next time any Adom container's 30-min hook ticks, the
refresh script sees the new pub_version and (if the user has the old binary
installed) injects a system-reminder offering an upgrade. Propagation is
automatic from this point — you don't need to do anything else.

Checklist

Before publishing:

  • CLI builds clean with cargo build --release
  • my-tool --version prints a semver (or you set version_command in the wiki metadata)
  • my-tool health works (if applicable)
  • my-tool install deploys SKILL.md correctly, checks prereqs, installs completions
  • my-tool install is idempotent (re-running just re-syncs everything)
  • Binary uploaded to the wiki as docker_binary
  • curl -I https://wiki-.../static/apps/<slug>/<bin> returns 200 with no auth
  • Wiki page has top-level version: "x.y.z" (→ pub_version)
  • Wiki page has discovery_triggers and discovery_pitch in metadata
  • Wiki page has releases.adom_docker.install_hint starting with curl -fsSL https://wiki-not gh release download
  • Install prompt on wiki page renders the new curl-based command
  • Tool appears on /discover page
  • End-to-end install tested in a zero-credential environment — a fresh
    container with no GitHub token must be able to run the install_hint and
    get a working binary
  • NOT added to install.mjs (unless it's core infrastructure)

Wiki-side work (tracked separately)

The wiki service is still closing a couple of gaps. Both affect smoothness of
the publish → update → distribute loop; neither blocks shipping a new tool
today. Workarounds below apply until they land.

  1. Metadata-only updates on an existing page. The adom-wiki page edit --field metadata path is the intended update endpoint, but its propagation
    to /discover isn't fully deterministic yet. Workaround: bump pub_version
    on a full re-publish, or DELETE + re-POST if metadata doesn't stick.

  2. adom-wiki asset upload --asset-type docker_binary is the documented
    path, but if your adom-wiki CLI build doesn't surface that value in its
    --asset-type enum, fall back to the raw curl upload against
    /api/v1/pages/<slug>/assets until the CLI catches up.

These are tracked on the wiki repo. The pattern described in this skill is the
forward-compatible one — don't code around either gap in ways that would break
once they're fixed.

AI Hints: Making CLIs Smart for AI Callers

When AI (Claude) calls your CLI, it needs contextual guidance on what to do next. Add _hint fields to JSON responses that tell the AI:

  • What went wrong and how to fix it
  • What to do after a successful command
  • What might go wrong next

This is the single most important pattern for making a CLI work well with AI. Without hints, the AI guesses. With hints, it acts correctly on the first try.

Pattern 1: Success hints — tell AI the next step

{
  "success": true,
  "output": "Exported gerbers to C:/tmp/gerbers.zip",
  "_hint": "Files saved on Windows. Use pull_file to get them to Docker: pull_file '{\"filePaths\":[\"C:/tmp/gerbers.zip\"],\"saveTo\":\"/tmp/mfg\"}'"
}

The AI reads _hint and knows to call pull_file — without this, it would just report "exported" and stop.

Pattern 2: Warning hints — tell AI what can go wrong

{
  "success": true,
  "output": "Opened BQ25792 from cloud",
  "_hint": "WARNING: May trigger 'Select Electronics Design File' dialog when switching to electronics workspace. If next command fails with 'add-in not responding', check fusion_window_info for blocking dialogs. Try fusion_send_key escape to dismiss."
}

The AI is now primed to handle the dialog if it appears, instead of getting stuck.

Pattern 3: Verification hints — tell AI to check its work

{
  "success": true,
  "output": "Launched schematic editor",
  "_hint": "WARNING: KiCad may show modal dialogs (save prompts, file warnings) that block further commands. ALWAYS check kicad_window_info for hasModalDialogs after this command. If true, use kicad_screenshot_all to capture all windows, pull_file to get screenshots, then read the dialog to decide how to handle it."
}

This turns a simple "opened" response into a full debugging workflow the AI follows automatically.

Pattern 4: Error diagnosis — auto-attach context on failure

// In your CLI code: when a command fails, try to diagnose WHY
let error_str = map.get("error").and_then(|v| v.as_str()).unwrap_or("");
if error_str.contains("not responding") {
    // Auto-run diagnostic command
    let diag = run_diagnostic_command();
    enriched.insert("_hint", json!("Add-in blocked by modal dialog. Run window_info to find it, screenshot it, dismiss with send_key."));
    enriched.insert("_diagnosis", diag);
}

The AI gets the error + diagnosis + recovery steps in one response — no back-and-forth needed.

Pattern 5: Prerequisite hints — tell AI what must be true first

{
  "success": false,
  "error": "Not an Electron document",
  "_hint": "Requires Electronics workspace active (isElectronics: true in get_app_state). The active tab is 3D PCB not PCB Editor. Switch with activate_document or open the .brd file."
}

Implementation: where to add hints

Add hints in the CLI's command routing layer — the single point where responses pass through before being printed to stdout. Don't put them in the backend/bridge; the CLI is the AI's interface.

// In commands.rs — match on command name and success/failure
match command {
    "export_gerbers" if success => add_hint("Files on Windows. Use pull_file..."),
    "open_schematic" if success => add_hint("WARNING: modals may appear..."),
    _ if error.contains("not responding") => add_hint("Blocked by dialog..."),
}

Pattern 6: Modal-first loops — teach AI to poll, not sleep

Desktop apps show modal dialogs that block everything. The AI's natural instinct is sleep 10 — but that wastes time and misses dialogs. Teach it to poll:

{
  "error": "Add-in not responding",
  "_hint": "BLOCKED by modal dialog. DO NOT sleep — check immediately: (1) window_info → find dialogs, (2) screenshot each → read text, (3) click Yes/OK to dismiss. IMPORTANT: Dialogs CASCADE — after dismissing one, check for MORE before proceeding. Loop until app_state returns success."
}

Key phrases that change AI behavior:

  • "DO NOT sleep" — overrides the default instinct
  • "check immediately" — creates urgency
  • "Dialogs CASCADE" — prevents the AI from assuming one dismissal fixes everything
  • "Loop until" — gives a clear termination condition

Rules for good hints

  1. Be specific — "Use pull_file with filePaths array" not "transfer the file"
  2. Include example commands — Show the exact CLI call the AI should make
  3. Warn about traps — "NOTE: Enter does NOT work on this dialog, use Escape"
  4. Chain commands — "After this, run X, then Y, then Z"
  5. Explain prerequisites — "Requires Electronics workspace active"
  6. Override bad instincts — "DO NOT sleep" when the AI should poll instead
  7. Keep it under 200 chars for simple hints, longer for complex recovery flows

Related Skills

  • adom-cli-design — Rust CLI conventions (output format, clap patterns, port registration)
  • skill-creator — How to write SKILL.md files
  • adom-wiki — Wiki CLI for publishing and managing pages