{"id":"63e07631-1ea9-4c97-86ca-d20367a846f4","entityType":"agent","slug":"clawhub-ceciliaz030-aomi-build","name":"Build","canonicalUrl":"https://www.xpersona.co/agent/clawhub-ceciliaz030-aomi-build","canonicalPath":"/agent/clawhub-ceciliaz030-aomi-build","generatedAt":"2026-10-11T10:51:54.561Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":null},"description":"Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl... Skill: Build Owner: ceciliaz030 Summary: Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl... Tags: ai-agents:0.1.0, development:0.1.0, latest:0.1.1, scaffolding:0.1.0 Version history: v0.1.1 | 2026-07-10T19:31:13.740Z | auto aomi-build v0.1.1 - Revamped documentation and manifest for clarity, precise permissio","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s174406t7fv0e6ry94swqb3jc186877h:aomi-build","sourceUrl":"https://clawhub.ai/ceciliaz030/aomi-build","homepage":"https://clawhub.ai/ceciliaz030/skills/aomi-build","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/ceciliaz030/aomi-build","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/ceciliaz030/skills/aomi-build","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl..."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":null},"stars":null,"forks":null,"downloads":1116,"packageName":null,"latestVersion":"0.1.1","tractionLabel":"1.1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T08:11:28.504Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T08:11:28.517Z","lastCrawledAt":"2026-10-11T08:11:28.504Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T08:11:28.504Z","lastVerifiedAt":null,"highlights":[{"version":"0.1.1","createdAt":"2026-07-10T19:31:13.740Z","changelog":"aomi-build v0.1.1 - Revamped documentation and manifest for clarity, precise permissions, and OWASP compliance. - Added SECURITY.md and new reference guides (examples, host routes, troubleshooting) to improve onboarding and troubleshooting. - Expanded SKILL.md with focused usage, prerequisites, error handling, and step-by-step instructions for greenfield and OpenAPI scaffolding flows. - Removed legacy/unused docs (skill-card.md), updated references for better discoverability. - Introduced a quick scaffold Bash template and granular permission manifest for safer scoped codegen. - Synced with aomi-sdk v3.0.1 runtime and agent skill conventions.","fileCount":11,"zipByteSize":48099},{"version":"0.0.1","createdAt":"2026-05-16T11:09:29.186Z","changelog":"aomi-build 0.0.1 – Initial release. - Scaffolds production-ready Rust SDK crates for Aomi apps and plugins from OpenAPI/Swagger specs or product requirements. - Generates lib.rs, client.rs, tool.rs with tool schemas, host-interop flows, and validation steps. - Supports sync HTTP, async tools, proxy-unwrap, and host-interop transaction flows. - Operates with strict least-privilege: only touches source/app files and runs cargo & git. - Requires Rust 2024 edition, aomi-sdk v0.1.15+, and optionally a local `aomi-apps` checkout. - No network access; reads from local files only.","fileCount":10,"zipByteSize":43711},{"version":"0.1.0","createdAt":"2026-04-20T10:38:43.491Z","changelog":"Build Aomi apps and plugins from APIs, specs, and SDK docs","fileCount":6,"zipByteSize":11618}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s174406t7fv0e6ry94swqb3jc186877h:aomi-build","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T10:51:54.558Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ceciliaz030-aomi-build/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":null},"readme":"Skill: Build\n\nOwner: ceciliaz030\n\nSummary: Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl...\n\nTags: ai-agents:0.1.0, development:0.1.0, latest:0.1.1, scaffolding:0.1.0\n\nVersion history:\n\nv0.1.1 | 2026-07-10T19:31:13.740Z | auto\n\naomi-build v0.1.1\n\n- Revamped documentation and manifest for clarity, precise permissions, and OWASP compliance.\n- Added SECURITY.md and new reference guides (examples, host routes, troubleshooting) to improve onboarding and troubleshooting.\n- Expanded SKILL.md with focused usage, prerequisites, error handling, and step-by-step instructions for greenfield and OpenAPI scaffolding flows.\n- Removed legacy/unused docs (skill-card.md), updated references for better discoverability.\n- Introduced a quick scaffold Bash template and granular permission manifest for safer scoped codegen.\n- Synced with aomi-sdk v3.0.1 runtime and agent skill conventions.\n\nv0.0.1 | 2026-05-16T11:09:29.186Z | auto\n\naomi-build 0.0.1 – Initial release.\n\n- Scaffolds production-ready Rust SDK crates for Aomi apps and plugins from OpenAPI/Swagger specs or product requirements.\n- Generates lib.rs, client.rs, tool.rs with tool schemas, host-interop flows, and validation steps.\n- Supports sync HTTP, async tools, proxy-unwrap, and host-interop transaction flows.\n- Operates with strict least-privilege: only touches source/app files and runs cargo & git.\n- Requires Rust 2024 edition, aomi-sdk v0.1.15+, and optionally a local `aomi-apps` checkout.\n- No network access; reads from local files only.\n\nv0.1.0 | 2026-04-20T10:38:43.491Z | user\n\nBuild Aomi apps and plugins from APIs, specs, and SDK docs\n\nArchive index:\n\nArchive v0.1.1: 11 files, 48099 bytes\n\nFiles: agents/openai.yaml (270b), references/aomi-sdk-patterns.md (9529b), references/examples.md (24490b), references/host-routes.md (9729b), references/spec-to-tools.md (8872b), references/troubleshooting.md (12661b), SECURITY.md (14693b), skill-card.md (2760b), SKILL.md (25565b), templates/quick-scaffold.sh (5907b), _meta.json (129b)\n\nFile v0.1.1:SKILL.md\n\n---\nname: aomi-build\ndescription: >\n  Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK\n  references. aomi-build generates production-ready Rust SDK crates (lib.rs,\n  client.rs, tool.rs) with tool schemas, preambles, host-interop flows, and\n  validation — turning a vendor's API surface into AI-agent-callable tools. It\n  covers the current `aomi-build` OpenAPI pipeline (`gen-specs` → `gen-client`\n  → `gen-tool` → curate → compile/test) as well as greenfield apps. Use when\n  the user wants to scaffold a new Aomi app from a spec, wrap a REST API as\n  agent-callable tools, port an existing SDK, or extend an Aomi runtime with new\n  integrations. Trigger with prompts about wrapping APIs, scaffolding Rust crates\n  from specs, or adding protocol integrations that aomi-transact can drive. Output\n  crates support progenitor-generated OpenAPI clients, curated tool layers, sync\n  HTTP, async tools (DynAsyncSink), typed secrets, route plans\n  (ToolReturn/RouteStep), EVM and SVM host handoffs, and multi-step\n  quote→approval→swap flows. Same runtime that aomi-transact drives.\ntags: [crypto, web3, evm, rust, sdk-scaffolding, openapi, swagger, agent-tools, defi, builder-tools]\ncompatibility: 'Best when a local aomi-sdk checkout is available, often at ../aomi-sdk. Falls back to bundled references when the SDK repo is not present. Verified against aomi-sdk v3.0.1 (Rust 2024 edition) and the current aomi-build Rust CLI. Install the current CLI with cargo install --git https://github.com/aomi-labs/aomi-sdk --features cli aomi-sdk, or run from source with cargo run -p aomi-sdk --features cli --bin aomi-build -- <command>. Designed for claude-code; also works with Cursor, Codex CLI, Gemini, and any agent runtime that supports the Anthropic skill spec.'\nlicense: MIT\nversion: \"0.1.1\"\nauthor: 'aomi-labs <hello@aomi.dev>'\n# Claude Code allowed-tools. The skill scaffolds Rust source files (Write/Edit),\n# inspects existing apps and SDK examples (Read/Grep), and runs cargo + git\n# (Bash). Operational scope is locked down by OWASP permissions.shell below\n# to `cargo` and `git` only — defense in depth.\nallowed-tools: 'Bash(cargo:*), Bash(git:*), Read, Write, Edit, Grep'\nmetadata:\n  author: 'aomi-labs <hello@aomi.dev>'\n  version: \"0.1.1\"\n  # Provenance — author-declared upstream coordinates.\n  # `gh skill install` will add/overwrite `ref`, `tree_sha`, `installed_via`,\n  # and `installed_at` at install time. Do not pre-populate those fields.\n  repository: aomi-labs/skills\n  homepage: https://github.com/aomi-labs/skills/tree/main/aomi-build\n\n# OWASP AST03 (Over-Privileged Skills) permission manifest.\n# Spec: https://owasp.org/www-project-agentic-skills-top-10/ast03\n# Universal Skill Format v1.0 (March 2026).\npermissions:\n  files:\n    # The skill reads source files in the user's project (the aomi-sdk\n    # checkout or wherever the user runs from) and the SDK's bundled\n    # docs/examples for pattern reference.\n    read:\n      - ./\n      - ../aomi-sdk/\n    # The skill writes new Rust source files within the project's apps/ tree\n    # and may amend the workspace manifest to add the new crate to `exclude`.\n    # cargo writes to target/ as a compile artifact; git writes index entries\n    # when staging the new manifest for source control.\n    write:\n      - ./apps/\n      - ./Cargo.toml\n      - ./Cargo.lock\n      - ./target/\n      - ../aomi-sdk/apps/\n      - ../aomi-sdk/Cargo.toml\n      - ../aomi-sdk/Cargo.lock\n      - ../aomi-sdk/target/\n    # Identity files must never be modified (AST03 mitigation #3).\n    # build.rs is denied because Rust build scripts run user-supplied\n    # code at compile time; the standard Aomi app shape (lib.rs,\n    # client.rs, tool.rs) does not need one. If the user genuinely\n    # needs a build script they must opt in explicitly outside the\n    # skill's default flow.\n    deny_write:\n      - SOUL.md\n      - MEMORY.md\n      - AGENTS.md\n      - build.rs\n\n  network:\n    # The skill makes no network calls of its own. Any docs / specs / repo\n    # links the user references are fetched out-of-band by the user (or by\n    # the agent's own WebFetch capability operating outside the skill's\n    # operational scope), then pasted into the conversation.\n    allow: []\n    deny: \"*\"\n\n  # Shell access restricted to `cargo` and `git` argv prefixes (least-privilege\n  # extension of the spec's boolean form, consistent with AST03 intent).\n  # `cargo` runs aomi-build, build, and test; `git` runs\n  # ls-files (used by app discovery) and add/status/diff for normal source work.\n  shell:\n    - cargo\n    - git\n\n  # No MCP/tool surface beyond local cargo + git + filesystem.\n  tools: []\n\n# Risk tier per spec: L0 safe, L1 low, L2 elevated, L3 destructive.\n# L1 = the skill writes source files and runs the Rust toolchain.\n# It does not move funds, sign transactions, custody secrets, or make\n# network calls of its own.\nrisk_tier: L1\n\nrequires:\n  binaries: [cargo, git]\n---\n\n# Aomi Build\n\n## Overview\n\nAomi Build scaffolds production-ready Rust SDK crates for Aomi apps and plugins from\nOpenAPI/Swagger specs, SDK docs, or product requirements. Generates `lib.rs`, `client.rs`,\n`tool.rs` with typed tool schemas, host-interop flows, and validation steps.\n\n## When to Use\n\n- Scaffold a new Aomi app from an OpenAPI spec or REST API\n- Wrap an existing SDK as agent-callable Aomi tools\n- Extend an Aomi runtime with new protocol integrations\n\nDo **not** use this skill for executing transactions — use **aomi-transact** for that.\n\n## Prerequisites\n\n- Rust toolchain (2024 edition) and `cargo` on PATH\n- `git` on PATH\n- Aomi SDK v3.0.1 or newer\n- Local `aomi-sdk` checkout at `../aomi-sdk` (recommended)\n- Current `aomi-build` binary, or run it from source with the `cli` feature\n\n## Quick Start\n\n```bash\ncd ../aomi-sdk\ncargo run -p aomi-sdk --features cli --bin aomi-build -- init my-integration\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app my-integration\ncargo run -p aomi-sdk --features cli --bin aomi-build -- new-app geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json\n```\n\n## Instructions\n\n1. Identify the integration target and its callable surface.\n2. State the proposed toolset (3–8 intent-shaped tools) before coding.\n3. Scaffold greenfield apps with `aomi-build init <name>`; scaffold OpenAPI-driven apps with `aomi-build new-app <name>` or the staged `gen-specs` / `gen-client` / `gen-tool` pipeline.\n4. Implement `client.rs` (HTTP, auth, models), `tool.rs` (`DynAomiTool` impls), `lib.rs` (manifest + preamble).\n5. For execution apps, return `ToolReturn::with_routes(...)` instead of bare JSON.\n6. Build and validate: `aomi-build compile --app <name>` or `cargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>`. For generated OpenAPI apps, also run `aomi-build test-schema <name>` when the live API is safe to fuzz.\n\n## Examples\n\n```bash\ngrep -r \"dyn_aomi_app!\" ../aomi-sdk/apps/\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app binance\ncargo test --manifest-path apps/my-integration/Cargo.toml\n```\n\n## Output\n\n- Rust crate at `apps/<name>/` with `lib.rs`, `client.rs`, `tool.rs`, `Cargo.toml`\n- Compiled `.so`/`.dylib` plugin artifact under `target/`\n- Typed tool schema embedded in the plugin manifest\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `compile` reports zero plugins | No tracked or discoverable `apps/*/Cargo.toml` | Check the app path and package name; current compile scans tracked manifests and the apps directory |\n| `SDK version mismatch` | Plugin built against old SDK | Bump version in `Cargo.toml`, rebuild all |\n| `JsonSchema derive failed` | Missing derive on Args | Add `schemars` dep, `#[derive(JsonSchema)]` on Args |\n| Async tool hangs | `is_canceled()` not polled | Add cancellation check in `run_async` loop |\n| Installed `aomi-build` lacks `deploy` | Old binary on PATH | Run from source with `cargo run -p aomi-sdk --features cli --bin aomi-build -- ...` or reinstall from the current repo |\n| Generated tool names are endpoint-shaped | `gen-tool` stubs one tool per operationId | Curate `tool.rs` into user-intent tools before shipping |\n| `progenitor` fails | Spec violates generator assumptions | Inspect `openapi.preprocessed.yaml`, patch `openapi.yaml`, or add a generic preprocessing pass |\n\n## Safety Justification\n\n`Bash(cargo:*, git:*)` — restricted to two argv prefixes. `cargo` runs `aomi-build`, builds, and tests\ncrates; `git` runs `ls-files` and `add` only. No other shell commands permitted;\n`permissions.shell` enforces this at the OWASP AST03 level.\n\n`Read` — reads within `./` and `../aomi-sdk/` only. `Write` — writes to `./apps/`,\n`../aomi-sdk/apps/`, `Cargo.toml`, `Cargo.lock`, `target/` only; identity files\n(`SOUL.md`, `MEMORY.md`, `AGENTS.md`, `build.rs`) are `deny_write`-listed.\n`Edit` — same paths as Write. `Grep` — read-only search, no writes.\n\nRisk tier: L1 (source files + Rust toolchain only; no fund movement, no network calls).\n\n---\n\nUse this skill for tasks like:\n\n- \"Build an Aomi app from this OpenAPI spec.\"\n- \"Turn these REST endpoints into an Aomi plugin.\"\n- \"Scaffold a new Aomi SDK app for this product/API.\"\n- \"Update an existing Aomi app to support these new endpoints.\"\n- \"Turn these builder docs or SDK repos into an Aomi assistant.\"\n\n## First Read\n\nIf a local `aomi-sdk` checkout exists (often at `../aomi-sdk`), inspect these first. The current SDK is **v3.0.1**, Rust 2024 edition. The `aomi-build` binary is part of the `aomi-sdk` crate behind the `cli` feature.\n\n- `sdk/examples/app-template-http/src/lib.rs` — canonical HTTP-API template (sync read-only)\n- `sdk/examples/app-template-http/src/client.rs`\n- `sdk/examples/app-template-http/src/tool.rs`\n- `sdk/examples/app-template-http/Cargo.toml` — note `edition = \"2024\"` and `crate-type = [\"cdylib\"]`\n- `sdk/src/types.rs` and `sdk/src/route.rs` — `DynToolCallCtx`, `DynAomiTool`, async tool contracts, and `ToolReturn` / `RouteStep`\n- `docs/repo-structure.md` — file roles and authoring guidelines\n- `docs/host-interop.md` — public host tools (`encode_and_call`, `stage_tx`, `simulate_batch`, `commit_txs`, `evm_commit_message`) plus SVM route targets\n- `docs/aomi-build.md` and `sdk/bin/build/CONTRIBUTING.md` — current `aomi-build` commands (`gen-specs`, `gen-client`, `gen-tool`, `new-app`, `test-schema`, `tighten-spec`, `init`, `compile`, `deploy`, `connect`, `sdk check|fix`)\n- `docs/sdk-version-compatibility.md` — exact-match SDK version gate enforced via `aomi_sdk_version` symbol\n- 2 or 3 relevant apps under `apps/*/src/{lib,client,tool}.rs`. Recommended:\n  - `apps/binance` — execution-oriented with required secrets and normalized models\n  - `apps/oneinch` — execution planner with routed quote → approval → swap flow\n  - `apps/geckoterminal` — current OpenAPI/progenitor app-local pipeline (`openapi.yaml`, generated `src/client/`, curated `tool.rs`)\n  - `apps/svm-transfer` — current SVM lane-1/lane-2 route patterns (`svm_stage_ix`, `svm_stage_tx`, `svm_commit_ix`, `svm_commit_tx`)\n  - `apps/khalani`, `apps/polymarket`, or `apps/polymarket-rewards` — host handoff via `ToolReturn` routes\n\nIf the supplied docs mostly point to GitHub repositories, SDKs, or examples instead of listing public endpoints:\n\n- treat those linked repositories as the real source of truth\n- inspect their README, config examples, example commands, and RPC/API surfaces\n- check whether they expose or produce a runnable service interface such as REST, GraphQL, JSON-RPC, gRPC, webhooks, or another stable client contract\n- prefer building against that executable surface instead of wrapping the docs themselves\n- avoid inventing a public transactional API that the docs do not actually publish\n\nIf the current repo is `aomi-widget`, also inspect:\n\n- `apps/landing/content/examples/*.mdx`\n- `apps/landing/content/guides/build/**/*.mdx`\n\nIf the `aomi-sdk` checkout is not available, read:\n\n- [references/aomi-sdk-patterns.md](references/aomi-sdk-patterns.md) — manifest shape, file roles, real-app conventions\n- [references/spec-to-tools.md](references/spec-to-tools.md) — converting OpenAPI / SDK docs / endpoint lists into intent-shaped tools\n- [references/host-routes.md](references/host-routes.md) — `ToolReturn` envelope and `RouteStep` builders for execution apps that hand off to the host wallet\n- [references/examples.md](references/examples.md) — five end-to-end walkthroughs anchored to real apps (`binance`, builder fallback, `polymarket` routes upgrade, async tool with cancellation, SDK version bump)\n- [references/troubleshooting.md](references/troubleshooting.md) — common build/runtime failures with concrete fixes (untracked `Cargo.toml`, SDK version mismatch, async tool hangs, route resolution issues, JsonSchema derive failures)\n\n## Default Workflow\n\n1. Identify the product surface:\n   - What external API, SDK, repo, or spec is the source of truth?\n   - What concrete callable surface exists: REST, GraphQL, JSON-RPC, gRPC, webhook, CLI contract, or something else?\n   - Is there a real target we can point the app at: hosted service, self-hosted node, local example stack, or customer-provided endpoint?\n   - Is this read-only, execution-oriented, or mixed?\n   - What auth/env vars are required?\n   - What user state must come from the host or caller?\n   - Is this actually a public end-user API, a standard client interface exposed by a runtime/example app, or only builder-facing documentation?\n2. Describe the intended user-facing toolset before implementation:\n   - list the proposed tools by name\n   - say what user intent each tool serves\n   - call out which tools are read-only, which prepare actions, and which write or submit\n   - mention any expected target URL, runtime, or host dependency\n   - if the toolset is uncertain, surface the uncertainty before coding\n   - identify the primary user workflow the app should make easy first\n   - keep the first pass to the smallest sufficient toolset for that workflow unless the user asked for broader API coverage\n3. Reduce the spec into semantically meaningful tools.\n4. Scaffold or update the Aomi app using the standard file split:\n   - `lib.rs` for manifest and preamble. Register with `dyn_aomi_app!` including the `namespaces = [...]` field — `[\"evm-core\"]` for most EVM apps and generated OpenAPI apps, SVM namespaces such as `[\"svm-reads\", \"svm-ix-broadcast\", \"svm-tx-broadcast\"]` for Solana apps, or `[]` only for tools that need no host namespace at all. The old `\"common\"` namespace is stale and should not be copied into new apps.\n   - `client.rs` for HTTP client, auth, models, and normalization\n   - `tool.rs` for `DynAomiTool` implementations. Sync tools implement `run`; async tools set `const IS_ASYNC: bool = true` and implement `run_async` with `DynAsyncSink::emit`/`complete`/`is_canceled` (see `sdk/examples/hello-app/src/lib.rs`).\n5. Write the preamble around actual tool behavior, confirmation rules, and any host handoff. Execution apps that drive multi-step wallet flows return `ToolReturn::with_routes(...)` instead of bare JSON — see [references/host-routes.md](references/host-routes.md).\n6. Validate with the SDK build flow and add focused tests when logic is non-trivial.\n\n## Tool Design Rules\n\n- First decide what kind of app this should be:\n  - product client\n  - execution assistant\n  - builder / SDK / runtime assistant\n- Before implementing, state the proposed toolset in concrete user-facing terms. This is part of the design, not optional polish.\n- Prefer the smallest sufficient toolset that makes the primary user workflow work end to end.\n- If there are multiple plausible integration targets, briefly state which one you are choosing and why before coding.\n- Prefer tools that interact with an actual product surface over tools that merely restate documentation.\n- A hosted API is not required. A self-hosted service, local example stack, standard RPC server, or other runnable interface still counts as a real integration target.\n- If the source material is SDK- or architecture-heavy, first ask whether it produces a service that clients call. If yes, build the client for that service.\n- Only fall back to a builder-oriented or docs-oriented tool surface when no stable executable target is available.\n- Do not mirror every endpoint 1:1 unless that is actually the cleanest model-facing API or the user explicitly asked for broad coverage.\n- Prefer 3 to 8 tools with clear user intent boundaries such as `search_*`, `get_*`, `build_*`, `submit_*`, `list_*`, or `resolve_*`.\n- Prefer intent-shaped tool names over raw protocol or transport names when practical.\n- Aggregate noisy upstream endpoints behind a smaller tool surface when the model does not need the raw distinction.\n- Prefer typed arguments over raw JSON string blobs when the primary workflow can be modeled cleanly that way.\n- Separate core tools from escape hatches. A generic fallback tool such as `*_rpc` or `*_raw` is fine, but it should not replace a clean core workflow.\n- Keep args typed and documented with `JsonSchema`. Field doc comments are model-facing and matter.\n- Return stable JSON with predictable keys. Normalize upstream naming, paging, and inconsistent shapes inside `client.rs` or helper functions.\n- Convert upstream errors into short actionable messages. Do not leak raw HTML, secrets, or giant payload dumps.\n\n## File Responsibilities\n\n### `lib.rs`\n\n- Keep it easy to scan.\n- Define `PREAMBLE` or a small `build_preamble()` hook.\n- Register tools with `dyn_aomi_app!`. Always include the `namespaces` field explicitly — `namespaces = [\"evm-core\"]` for most EVM or generated API apps, SVM namespaces for Solana apps, and `namespaces = []` only when no host namespace should be injected. The macro generates the C ABI exports (`aomi_create`, `aomi_manifest`, `aomi_async_tool_start`, etc.) and embeds the SDK version stamp the host uses for the exact-match compatibility check.\n- Only keep manifest-level wiring here.\n\n### `client.rs`\n\n- Own the app struct, HTTP client, auth headers, env vars, typed models, and response normalization.\n- Prefer `reqwest::blocking::Client` with explicit timeouts for sync tools, matching the current SDK examples.\n- Keep third-party API quirks here instead of spreading them across tool implementations.\n\n### `tool.rs`\n\n- Implement `DynAomiTool`. Required associated types: `App` (the app struct from `client.rs`) and `Args` (a `JsonSchema + Deserialize` struct). Required consts: `NAME`, `DESCRIPTION`. Optional const: `IS_ASYNC` (defaults to `false`).\n- Use descriptions that tell the model when to call the tool, not just what endpoint it wraps.\n- Map normalized client results into concise JSON results. Sync tools return `Result<Value, String>` from `run()`; async tools return `Result<(), String>` from `run_async()` and emit progress through the `DynAsyncSink`. Cancellation: poll `sink.is_canceled()` and return `Ok(())` early.\n- Use `DynToolCallCtx` when host state such as connected wallet, session state, or caller attributes is needed. `ctx.session_id` and `ctx.call_id` are stable identifiers for logging or routing.\n- For execution apps that hand off to the wallet, return `ToolReturn::with_routes(value, [RouteStep::on_return(...).bind_as(...).prompt(...)])` instead of a bare `Value`. The `run_with_routes()` method on `DynAomiTool` has a default impl that wraps `run()` — only override it when you need routes. See [references/host-routes.md](references/host-routes.md).\n\n## Preamble Rules\n\nWrite the preamble from the app's real contract:\n\n- Define role, capabilities, workflow, and guardrails.\n- Mention tool order for multi-step flows.\n- State explicit confirmation requirements before write actions.\n- If dates matter, include the current date or instruct the app to use exact dates.\n- If the app relies on host wallet/signing tools, say that clearly and do not imply hidden infrastructure.\n\nFor deeper patterns and examples, read [references/aomi-sdk-patterns.md](references/aomi-sdk-patterns.md).\n\n## Host Interop And Execution\n\nFor execution-oriented apps:\n\n- Follow the public host conventions from `docs/host-interop.md`. The available host tools include `encode_and_call`, `stage_tx`, `simulate_batch`, `commit_txs`, `evm_commit_message`, and SVM targets such as `svm_stage_ix`, `svm_stage_tx`, `svm_commit_ix`, and `svm_commit_tx`. Apps reference these by name or typed route markers in route hints — they are public contract, not private infrastructure.\n- Do not invent private namespaces (`CommonNamespace` etc.) or internal fallback behavior. Do not use the old `\"common\"` namespace in new app manifests; current host namespace names are canonical strings such as `\"evm-core\"`, `\"svm-reads\"`, `\"svm-ix-broadcast\"`, and `\"svm-tx-broadcast\"`.\n- When the next step belongs to the host wallet or signer, return a `ToolReturn` envelope with explicit `RouteStep` builders instead of any prose-based `SYSTEM_NEXT_ACTION` convention. The runtime's `RoutedEventBridge` resolves `OnSyncReturn` and `OnBoundEvent` triggers, splices wallet-callback artifacts (`signature`, `transaction_hash`) into hinted args, and injects the continuation prompt. The runtime never parses prose — structured fields are the contract.\n- Preserve exact transaction or signature args when a downstream host tool must execute them. For raw external tx payloads, use `stage_tx` with `data: { raw: \"0x...\" }`; for ABI-driven calls, use `data: { encode: { signature, args } }`.\n- Do not claim a write succeeded until the upstream API submit step has actually completed.\n\nFor deeper coverage of the routes pattern, including `OnSyncReturn` vs `OnBoundEvent`, `bind_as` aliases, and worked examples from `apps/khalani` and `apps/polymarket`, read [references/host-routes.md](references/host-routes.md).\n\n## Validation\n\nWhen working inside `aomi-sdk`:\n\n- Scaffold greenfield apps with `cargo run -p aomi-sdk --features cli --bin aomi-build -- init <name>`, or scaffold API-driven apps with `cargo run -p aomi-sdk --features cli --bin aomi-build -- new-app <platform> --from-url <url>`.\n- Build the plugin with `cargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>`. Optional flags: `--release`, `--target <triple>`. The build validates the manifest, codesigns on macOS, and validates the produced plugin.\n- For OpenAPI apps, the current pipeline is `gen-specs` (writes `apps/<p>/openapi.yaml` by default, or `ext/specs/<p>.yaml` with `--shared`) → `gen-client` (progenitor into `apps/<p>/src/client/` or `ext/src/<p>/`) → `gen-tool` (mechanical stubs) → curate → `compile`. `openapi.preprocessed.yaml` is a debug artifact overwritten by `gen-client`; edit `openapi.yaml`, not the preprocessed copy.\n- `gen-tool` intentionally produces endpoint-shaped stubs. Before shipping, collapse them into user-story tools, write a real preamble, and add `apps/<p>/test.json` when the app needs real LLM/runtime validation.\n- If `compile` reports zero built plugins for a brand new app, check the app path, package metadata, and whether `[package.metadata.aomi.skip]` is set. Current compile scans tracked manifests and the apps directory.\n- For a direct compile signal on an untracked app, use `cargo build --manifest-path apps/<name>/Cargo.toml`.\n- If the app has meaningful branching or normalization logic, add unit tests with `aomi_sdk::testing::{TestCtxBuilder, run_tool, run_async_tool}`. `TestCtxBuilder::new(tool_name).build()` produces a `DynToolCallCtx`; `run_tool` returns a full `ToolReturn` with routes; `run_async_tool` returns `(updates, terminal)`.\n- The host-plugin compatibility gate is **exact-match SDK version**. After bumping `sdk/Cargo.toml` `package.version`, all apps must be rebuilt — the host rejects plugins whose `aomi_sdk_version` symbol does not match its compiled `AOMI_SDK_VERSION`. See `docs/sdk-version-compatibility.md`.\n- If a real target is available, validate the app with a short ladder:\n  - compile/build\n  - connectivity check\n  - one representative read flow\n  - one representative write or submit flow when applicable\n  - post-write verification such as status, receipt, or refreshed state\n- Prefer proving one end-to-end user scenario over checking many disconnected endpoints.\n\nWhen the task also touches docs or demos in `aomi-widget`, update the relevant examples or guides to match the new app behavior.\n\n## Output Expectations\n\nAim to leave behind:\n\n- a coherent Aomi app crate or patch\n- typed tool args and strong descriptions\n- a preamble that explains the tool contract and rules\n- stable JSON outputs for the host/model\n- an app that can point at a real product surface when one exists\n- a short validation pass or a clear note about what could not be verified\n\n## Resources\n\n- Source repository: https://github.com/aomi-labs/skills/tree/main/aomi-build\n- Companion runtime skill: [aomi-transact](https://github.com/aomi-labs/skills/tree/main/aomi-transact)\n- npm runtime client: https://www.npmjs.com/package/@aomi-labs/client\n- Aomi SDK patterns: [references/aomi-sdk-patterns.md](references/aomi-sdk-patterns.md)\n- Spec-to-tools mapping: [references/spec-to-tools.md](references/spec-to-tools.md)\n- Host route conventions: [references/host-routes.md](references/host-routes.md)\n- End-to-end build examples: [references/examples.md](references/examples.md)\n- Troubleshooting playbook: [references/troubleshooting.md](references/troubleshooting.md)\n- Anthropic skill spec: https://docs.claude.com/en/docs/claude-code/skills\n\nFile v0.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn7axfgphsdj10cpkqw6n840nh86837c\",\n  \"slug\": \"aomi-build\",\n  \"version\": \"0.1.1\",\n  \"publishedAt\": 1783711873740\n}\n\nFile v0.1.1:references/aomi-sdk-patterns.md\n\n# Aomi SDK Patterns\n\nThese patterns come from the SDK examples (`sdk/examples/app-template-http`), current SDK source, and inspected public apps in `aomi-sdk/apps`. Current SDK is **v3.0.1**, Rust **2024 edition**.\n\n## Canonical Layout\n\nUse this split unless there is a strong reason not to:\n\n```text\napps/my-app/\n├─ Cargo.toml\n└─ src/\n   ├─ lib.rs\n   ├─ client.rs\n   └─ tool.rs\n```\n\n- `lib.rs`: manifest, preamble, `dyn_aomi_app!`\n- `client.rs`: app struct, HTTP client, auth, models, helpers\n- `tool.rs`: `DynAomiTool` impls and user-facing tool surface\n\n`Cargo.toml` must declare `edition = \"2024\"`, `crate-type = [\"cdylib\"]`, and depend on `aomi-sdk = { workspace = true }`. The host enforces an exact-match SDK version gate: after bumping `sdk/Cargo.toml`, all apps must be rebuilt — see `docs/sdk-version-compatibility.md`.\n\n## Minimal Manifest Shape\n\n```rust\nuse aomi_sdk::*;\n\nmod client;\nmod tool;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are ...\n\"#;\n\ndyn_aomi_app!(\n    app = client::MyApp,\n    name = \"my-app\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [\n        client::SearchThing,\n        client::GetThing,\n    ],\n    namespaces = [\"evm-core\"]\n);\n```\n\nKeep `lib.rs` small. The manifest should be easy to audit at a glance.\n\nThe `namespaces` field is required. Current canonical host namespaces are explicit strings:\n\n- `namespaces = [\"evm-core\"]` for most EVM apps and generated OpenAPI apps. This injects the current EVM host tools such as `encode_and_call`, `stage_tx`, `simulate_batch`, `commit_txs`, and `evm_commit_message`.\n- `namespaces = [\"svm-reads\", \"svm-ix-broadcast\", \"svm-tx-broadcast\"]` for Solana apps that need SVM read/stage/commit host tools.\n- `namespaces = []` only for apps that should receive no host namespace at all.\n\nDo not copy the old `\"common\"` namespace into new apps. Current SDK docs note that legacy namespace was removed in host iter-39; the loader skips unknown namespaces.\n\nThe macro generates the C ABI exports (`aomi_create`, `aomi_manifest`, `aomi_async_tool_start`, `aomi_dyn_exec_poll`, etc.) and embeds the SDK version stamp the host uses for compatibility checks.\n\n## What The Real Apps Show\n\n### `sdk/examples/app-template-http`\n\nUse as the default baseline for read-only HTTP APIs.\n\n- Simple `reqwest::blocking` client\n- Clean typed args\n- Small tool surface\n- Straightforward JSON normalization\n\n### `apps/x`\n\nUse this pattern when the upstream API:\n\n- needs an env-backed API key\n- has a wrapper response envelope\n- benefits from normalized data models and formatting helpers\n\nNotable conventions:\n\n- auth env vars live in `client.rs`\n- logical API failures are normalized before reaching tools\n- tools return concise, model-friendly JSON\n\n### `apps/polymarket`\n\nUse this pattern when the app needs:\n\n- multiple upstream API surfaces\n- dynamic preamble context such as exact current date\n- intent resolution before execution\n- multi-step flows with explicit user confirmation\n\nNotable conventions:\n\n- preamble explains exact flow order\n- tool surface separates search, details, intent resolution, preview, and submit\n- results include next-step hints without hiding uncertainty\n\n### `apps/khalani` and `apps/polymarket`\n\nUse this pattern when execution must hand off to host wallet tools.\n\nNotable conventions:\n\n- app tools never send the wallet request directly\n- build/submit tools return `ToolReturn::with_routes(value, [...])` envelopes — never prose-based hints\n- routes use `RouteStep::on_return(\"evm_commit_message\", typed_data).bind_as(\"signature\").prompt(...)` for typed-message signing, or `RouteStep::on_return(\"commit_txs\", args)` for EVM transaction commit, to declare what tool the host should call next, what args to pass, and what alias to bind the result under\n- subsequent routes use `RouteStep::on_bound_event(\"submit_*\", template, \"signature\")` to wait for the bound alias before continuing\n- preamble tells the model to preserve exact host args and let the runtime resolve the route — the runtime never parses prose\n\n### `apps/geckoterminal`\n\nUse this pattern for the current OpenAPI/progenitor app-local pipeline.\n\nNotable conventions:\n\n- `openapi.yaml` is the editable source spec.\n- `openapi.preprocessed.yaml` is a regenerated debug artifact from `gen-client`; inspect it when progenitor fails, but do not edit it.\n- `src/client/` is generated by progenitor and wrapped by a hand-curated `tool.rs`.\n- `Cargo.toml` includes `progenitor-client`, matching `reqwest` 0.13, and any dependency markers detected from the generated client.\n- `gen-tool` stubs one tool per `operationId`; the shipped app should curate those stubs into user-intent tools and a real preamble.\n\n### Executable product integrations\n\nWhen the source material is mostly SDK docs, example repos, runtime notes, or architecture docs, first check whether it exposes or produces a client-facing interface such as:\n\n- REST or GraphQL\n- JSON-RPC\n- gRPC\n- webhooks\n- a stable CLI request/response contract\n- a local example service or reference node\n\nIf such a surface exists:\n\n- build the app against that executable interface\n- treat the SDK, example repo, and docs as implementation references\n- expose user-useful operations against the real service, not just summaries of the docs\n- validate with at least one real call when possible\n\n### Builder-oriented fallbacks\n\nUse a builder-oriented shape only when the source material is mostly:\n\n- SDK documentation\n- example repositories\n- architecture notes\n- runtime / RPC references\n- config files and quickstarts\n\nIn that case:\n\n- do not pretend there is a public swap, quote, or portfolio API unless the source really documents one\n- do not hide the absence of a real integration target\n- prefer tools such as `list_*_resources`, `get_*_overview`, `get_*_quickstart`, `get_*_rpc_surface`, or `get_*_network_defaults`\n- make the preamble explicit that the app is a builder assistant, not an end-user trading agent\n- say clearly what would be needed to upgrade the app into a real client later, such as a base URL, running example service, or customer endpoint\n\n## Tool Authoring Checklist\n\nEvery tool should answer these:\n\n- What user intent does it serve?\n- What exact name should the model call?\n- What fields does the model need to provide?\n- What result shape will be easiest for the model to reason over?\n- If the upstream API is inconsistent, where will normalization happen?\n\nPrefer names like:\n\n- `search_*`\n- `get_*`\n- `list_*`\n- `resolve_*`\n- `build_*`\n- `submit_*`\n\n## Client Conventions\n\nKeep these in `client.rs` whenever possible:\n\n- base URLs\n- auth headers\n- shared request helpers\n- response envelopes\n- normalization helpers\n- typed upstream models\n\nPrefer short actionable errors such as:\n\n- `X_API_KEY environment variable not set`\n- `Gamma API error 404: ...`\n- `Failed to parse markets: ...`\n\n## Validation Commands\n\nInside `aomi-sdk`, the standard loop is:\n\n```bash\ncargo run -p aomi-sdk --features cli --bin aomi-build -- init my-app\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app my-app\n```\n\nFor OpenAPI- or URL-driven scaffolds, use the staged pipeline or one-shot orchestrator:\n\n```bash\ncargo run -p aomi-sdk --features cli --bin aomi-build -- gen-specs geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json\ncargo run -p aomi-sdk --features cli --bin aomi-build -- gen-client geckoterminal --force\ncargo run -p aomi-sdk --features cli --bin aomi-build -- gen-tool geckoterminal --all --force\ncargo run -p aomi-sdk --features cli --bin aomi-build -- new-app geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json\n```\n\nDefault OpenAPI mode is app-local: `apps/<platform>/openapi.yaml`, `apps/<platform>/src/client/`, and `apps/<platform>/src/tool.rs`. Use `--shared` only for large reusable providers; then specs live under `ext/specs/` and clients under `ext/src/`.\n\n`compile` accepts `--release` and `--target <triple>`. It validates the manifest, codesigns the cdylib on macOS, and validates the produced plugin file.\n\nOne caveat from practice:\n\n- Current `aomi-build compile` scans tracked app manifests and falls back to the apps directory, so a brand-new app is less likely to be skipped than in older xtask builds.\n- Use `cargo build --manifest-path apps/my-app/Cargo.toml` for an immediate crate-level compile check when debugging package errors.\n- Apps with `[package.metadata.aomi.skip]` set are excluded intentionally — useful for in-progress crates.\n\nFor generated APIs:\n\n- `aomi-build test-schema <platform>` runs a Schemathesis smoke against the live API. It is GET-only by default; pass `--write` only for sandbox/testnet APIs where fuzzing writes is acceptable.\n- `aomi-build tighten-spec <platform> --in-place` can replace loose `additionalProperties: true` response bodies using captured JSON samples, then rerun `gen-client --force`.\n\nFor focused logic tests, use the SDK test helpers:\n\n```rust\nuse aomi_sdk::testing::{TestCtxBuilder, run_tool, run_async_tool};\n\nlet ctx = TestCtxBuilder::new(\"search_thing\").build();\nlet result = run_tool::<MyTool>(&MyApp, json!({\"query\": \"eth\"}), ctx)?;\n// result is a ToolReturn — bare-value tools have empty routes; route-returning\n// tools include the structured RouteStep list under result.routes\n```\n\n`run_tool` returns the full `ToolReturn` (handy for asserting routes alongside the JSON payload). `run_async_tool` returns `(updates, terminal)` so you can assert intermediate `emit` payloads as well as the final `complete` payload.\n\nFile v0.1.1:references/examples.md\n\n# Build Examples\n\nRead this when:\n\n- You need to translate a concrete spec or doc set into a working app.\n- You want to see the SKILL.md guidance applied end-to-end.\n- You're deciding what kind of app to build and want to pattern-match against a real one in `apps/`.\n\nEach example is anchored to a real app crate in `aomi-sdk/apps`. Code excerpts come directly from those crates; the **\"What you'd type\"** blocks show how you'd brief the skill to reproduce them.\n\nThe build lifecycle is consistent across every example:\n\n> **identify surface** → **propose toolset** → **scaffold** → **wire client + tools** → **build + test** → **handoff hooks**\n\nIf you only remember one thing: **don't mirror endpoints; map user intents.** A spec with 20 endpoints is rarely 20 tools. It's usually 4-8.\n\n---\n\n## 1. CEX read + signed orders — `apps/binance` shape\n\n**Anchored to** `apps/binance/src/{lib.rs, client.rs, tool.rs, types.rs}`. The canonical \"exchange API\" shape: HMAC auth, public reads, signed writes, normalized response models.\n\n### Source material\n\nA REST API doc (Binance Spot v3) with ~30 endpoints across:\n\n- public: tickers, depth, klines, 24h stats\n- signed (HMAC-SHA256): place order, cancel order, account balances, trade history\n\n### What you'd type\n\n> \"Build an Aomi app for Binance Spot. Cover the main public reads (price, depth, klines, 24h stats) plus signed order placement and account queries. Auth is HMAC-SHA256 with `BINANCE_API_KEY` + `BINANCE_SECRET_KEY`. Trading pairs use uppercase no-separator format (BTCUSDT).\"\n\n### Tool decisions\n\nResist 1:1 mapping. The spec has 30+ endpoints; the user intent reduces to 8 tools:\n\n| Tool name | Intent | Endpoint(s) |\n|-----------|--------|-------------|\n| `binance_get_price` | \"what's the price of X?\" | `GET /ticker/price` |\n| `binance_get_depth` | \"what's the order book for X?\" | `GET /depth` |\n| `binance_get_klines` | \"give me OHLC for technical analysis\" | `GET /klines` |\n| `binance_get_24hr_stats` | \"rolling 24h stats\" | `GET /ticker/24hr` |\n| `binance_place_order` | \"submit a buy/sell order\" | `POST /order` (signed) |\n| `binance_cancel_order` | \"cancel my order\" | `DELETE /order` (signed) |\n| `binance_get_account` | \"what's my balance?\" | `GET /account` (signed) |\n| `binance_get_trades` | \"my fill history\" | `GET /myTrades` (signed) |\n\nSkip: server time, exchange info, system status, sub-account endpoints, futures (a separate app), savings, staking, mining. They'd bloat the model's tool surface without serving a clear primary user intent.\n\n### Manifest (`lib.rs`)\n\n```rust\nuse aomi_sdk::*;\n\nmod client;\nmod tool;\nmod types;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are an AI assistant specialized in interacting with the Binance cryptocurrency exchange...\n\n## Authentication\n- Public market data endpoints do not require authentication\n- Signed endpoints (orders, account, trades) require both api_key and secret_key\n- The signature is computed as HMAC-SHA256(secret_key, query_string_with_timestamp)\n- The timestamp parameter is appended automatically before signing\n\n## Execution Guidelines\n- Use price tickers for quick spot checks; use klines for technical analysis\n- Check account balance before placing orders\n- Order quantities and prices must respect lot size and tick size filters\n- Always verify the trading pair exists before placing orders\"#;\n\ndyn_aomi_app!(\n    app = client::BinanceApp,\n    name = \"binance\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [\n        client::GetPrice,\n        client::GetDepth,\n        client::GetKlines,\n        client::Get24hrStats,\n        client::PlaceOrder,\n        client::CancelOrder,\n        client::GetAccount,\n        client::GetTrades,\n    ],\n    namespaces = [\"evm-core\"]\n);\n```\n\n`namespaces = [\"evm-core\"]` because the app is execution-oriented and may need current wallet/EVM host context even when some signing is venue-side HMAC. Use `namespaces = []` only for apps that should receive no host namespace at all.\n\n### Client (`client.rs`) — auth and helpers\n\nKeep all third-party API quirks in `client.rs`. Tools should never see them.\n\n```rust\nuse hmac::{Hmac, Mac};\nuse sha2::Sha256;\n\ntype HmacSha256 = Hmac<Sha256>;\n\npub(crate) fn sign(secret_key: &str, query_string: &str) -> Result<String, String> {\n    let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes())\n        .map_err(|e| format!(\"[binance] failed to create HMAC key: {e}\"))?;\n    mac.update(query_string.as_bytes());\n    Ok(hex_encode(&mac.finalize().into_bytes()))\n}\n\npub(crate) const SPOT_BASE_URL: &str = \"https://api.binance.com/api/v3\";\n\npub(crate) struct BinanceClient {\n    pub(crate) http: reqwest::blocking::Client,\n}\n\nimpl BinanceClient {\n    pub(crate) fn new() -> Result<Self, String> {\n        let http = reqwest::blocking::Client::builder()\n            .timeout(Duration::from_secs(30))\n            .build()\n            .map_err(|e| format!(\"[binance] failed to build HTTP client: {e}\"))?;\n        Ok(Self { http })\n    }\n    // public_get, signed_get, signed_post helpers...\n}\n```\n\n### Tool args (`client.rs`) — typed with `JsonSchema`\n\n```rust\n#[derive(Debug, Deserialize, JsonSchema)]\npub(crate) struct GetPriceArgs {\n    /// Trading pair symbol (e.g., \"BTCUSDT\", \"ETHUSDT\"). If omitted, returns prices for all symbols.\n    pub(crate) symbol: Option<String>,\n}\n\npub(crate) struct GetPrice;\n```\n\nThe doc comment on `symbol` becomes the model-facing schema description. It matters — write it for the model, not for the developer.\n\n### Tool impl (`tool.rs`)\n\n```rust\nimpl DynAomiTool for GetPrice {\n    type App = BinanceApp;\n    type Args = GetPriceArgs;\n    const NAME: &'static str = \"binance_get_price\";\n    const DESCRIPTION: &'static str =\n        \"Get the latest price for a trading pair, or all trading pairs if no symbol is specified.\";\n\n    fn run(_app: &BinanceApp, args: Self::Args, _ctx: DynToolCallCtx) -> Result<Value, String> {\n        let client = BinanceClient::new()?;\n        let query = match &args.symbol {\n            Some(s) => format!(\"symbol={s}\"),\n            None => String::new(),\n        };\n        ok(client.public_get::<BinancePriceResponse>(SPOT_BASE_URL, \"/ticker/price\", &query)?)\n    }\n}\n```\n\nThe `ok(...)` helper at the top of `tool.rs` adds a stable `\"source\": \"binance\"` field to every response — this prevents accidental name collisions when results from multiple apps appear in the same conversation history.\n\n### Validation\n\n```bash\ncargo run -p aomi-sdk --features cli --bin aomi-build -- init binance      # if scaffolding from scratch\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app binance\n```\n\nAdd a unit test for the args-encoding logic (the HMAC query-string canonicalization is the kind of thing that breaks silently):\n\n```rust\n#[test]\nfn signed_query_string_includes_timestamp() {\n    let qs = build_signed_query(\"symbol=BTCUSDT\", 1700000000000);\n    assert!(qs.contains(\"timestamp=1700000000000\"));\n    assert!(qs.contains(\"signature=\"));\n}\n```\n\n### Pattern notes\n\n- **Auth resolution lives in tool.rs**, not in `lib.rs`. `resolve_secret_value(arg, ENV_VAR, error_msg)` reads from the explicit tool arg first, then falls back to environment. This pattern repeats across every credentialed app — see `apps/dune`, `apps/neynar`, `apps/x` for the same shape.\n- **Errors are short and prefixed.** `[binance] missing api_key argument and BINANCE_API_KEY environment variable` — never raw upstream HTML or stack traces.\n- **One tool per user intent.** `binance_get_price` covers the symbol-or-all-symbols variation through an optional arg, not two tools.\n\n---\n\n## 2. SDK-only / builder-oriented — when no public API exists\n\nAnchored to the **fallback case** — the source material is mostly SDK docs and example repos with no public hosted API. The right move is a builder assistant, not a fake transactional client.\n\n### Source material\n\nA protocol's docs link to:\n\n- a Rust/TS SDK on GitHub\n- an example client repo\n- a self-hosted node config + RPC reference\n- architecture diagrams\n\nThere is no `https://api.<protocol>.com` to call.\n\n### What you'd type\n\n> \"Build an Aomi app for `<protocol>`. The docs mostly describe how to integrate via their SDK and run a self-hosted node. There's no public REST API. Make a builder assistant that helps users understand the SDK surface, find example commands, and look up RPC/network defaults.\"\n\n### What NOT to do\n\n- Don't invent endpoints like `https://api.protocol.com/swap/quote` if the docs don't publish one.\n- Don't claim the app can submit transactions when it can't.\n- Don't generate tools named `place_order` or `submit_swap` against a service that doesn't exist.\n\n### Tool decisions\n\n| Tool name | Intent | Source |\n|-----------|--------|--------|\n| `list_<protocol>_resources` | \"what's available in this ecosystem?\" | curated index of SDK packages, example repos, docs |\n| `get_<protocol>_overview` | \"high-level architecture\" | architecture doc summary |\n| `get_<protocol>_quickstart` | \"how do I run this locally?\" | quickstart commands from README |\n| `get_<protocol>_rpc_surface` | \"what RPC methods are available?\" | RPC reference |\n| `get_<protocol>_defaults` | \"default ports, chain IDs, addresses\" | config reference |\n\nThese are deliberately read-only, doc-shaped tools. The preamble must surface this:\n\n```rust\nconst PREAMBLE: &str = r#\"## Role\nYou are a builder assistant for `<protocol>`. You help developers understand\nthe SDK surface and run a local example stack. **You are NOT an end-user\ntrading agent.** This protocol does not expose a hosted public API for swaps\nor transactions; users who want to execute on-chain operations must run their\nown node or service.\n\n## Workflow\n1. Use `list_*_resources` to enumerate the available SDK packages and examples.\n2. Use `get_*_quickstart` for concrete commands to run a local example.\n3. Use `get_*_rpc_surface` and `get_*_defaults` for integration details.\n\n## Guardrails\n- Do not pretend a hosted public API exists.\n- Do not generate calldata for protocols that require user-side signing.\n- If a user asks for a swap or trade, explain that they need to run their own\n  service against the SDK and provide the relevant quickstart.\n\"#;\n```\n\n### Pattern notes\n\n- **Builder apps are valid outcomes**, but they're the fallback, not the default. Always check first whether the SDK produces a runnable service the app could call.\n- **The \"upgrade path\" matters.** If the user later spins up a hosted instance, the builder app should be replaceable with a real client app. Keep tool names suffixed with `_resources` / `_overview` / `_quickstart` / `_defaults` so it's clear at a glance which is which.\n- See `references/spec-to-tools.md` \"Builder-oriented fallbacks\" for more detail.\n\n---\n\n## 3. Adding wallet handoff to an existing app — `run` → `run_with_routes`\n\n**Anchored to** `apps/polymarket/src/tool.rs`. Demonstrates upgrading a `Value`-returning tool to a `ToolReturn`-with-routes tool when the app needs to chain into host wallet tools.\n\n### Before — bare `run` returning `Value`\n\n```rust\nimpl DynAomiTool for BuildPolymarketOrder {\n    type App = PolymarketApp;\n    type Args = BuildPolymarketOrderArgs;\n    const NAME: &'static str = \"build_polymarket_order\";\n    const DESCRIPTION: &'static str = \"Build a Polymarket order preview.\";\n\n    fn run(_app: &PolymarketApp, args: Self::Args, ctx: DynToolCallCtx) -> Result<Value, String> {\n        let client = PolymarketClient::new()?;\n        let preview = client.build_preview(&args)?;\n        Ok(json!({ \"preview\": preview, \"next_step\": \"User must sign typed data manually\" }))\n    }\n}\n```\n\nThis works for direct-SDK mode where the app holds the private key and submits internally. But for wallet-mode (the user's wallet signs), the prose `\"next_step\"` field is brittle — the runtime can't act on it.\n\n### After — `run_with_routes` returning `ToolReturn`\n\n```rust\nuse aomi_sdk::{RouteStep, ToolReturn, builder::host};\n\nimpl DynAomiTool for BuildPolymarketOrder {\n    type App = PolymarketApp;\n    type Args = BuildPolymarketOrderArgs;\n    const NAME: &'static str = \"build_polymarket_order\";\n    const DESCRIPTION: &'static str =\n        \"Build a canonical Polymarket order preview and continuation template. \\\n         This tool never places the order itself. In wallet mode it also returns \\\n         the explicit post-confirmation signing sequence.\";\n\n    fn run_with_routes(\n        _app: &PolymarketApp,\n        args: Self::Args,\n        ctx: DynToolCallCtx,\n    ) -> Result<ToolReturn, String> {\n        let connected_wallet = args\n            .wallet_address\n            .clone()\n            .or_else(|| ctx.attribute_string(&[\"domain\", \"evm\", \"address\"]));\n        let (mode, wallet_address) = determine_polymarket_execution(connected_wallet.as_deref())?;\n\n        let client = PolymarketClient::new()?;\n        let preview = client.build_preview(&args)?;\n\n        match mode {\n            ExecutionMode::DirectSdk => {\n                // SDK handles signing; no host handoff needed\n                Ok(ToolReturn::value(json!({\n                    \"preview\": preview,\n                    \"execution_mode\": \"DIRECT_SDK\",\n                })))\n            }\n            ExecutionMode::Wallet => {\n                let typed_data = build_clob_l1_typed_data(&preview, &wallet_address)?;\n                let submit_template = build_submit_template(&preview);\n\n                Ok(ToolReturn::with_routes(\n                    json!({\n                        \"preview\": preview,\n                        \"execution_mode\": \"WALLET\",\n                        \"wallet_request\": typed_data.clone(),\n                    }),\n                    [\n                        RouteStep::on_return_to::<host::EvmCommitMessage>(typed_data)\n                            .bind_as(\"clob_l1_signature\")\n                            .prompt(\"Sign the CLOB L1 authorization.\"),\n                        RouteStep::on_bound_to::<SubmitPolymarketOrder>(\n                            submit_template,\n                            \"clob_l1_signature\",\n                        )\n                        .prompt(\"Wallet signed — submit the order.\"),\n                    ],\n                ))\n            }\n        }\n    }\n}\n```\n\n### What changed\n\n| Before | After |\n|--------|-------|\n| `fn run(...) -> Result<Value, String>` | `fn run_with_routes(...) -> Result<ToolReturn, String>` |\n| Returned `json!({...})` | Returns `ToolReturn::value(...)` or `ToolReturn::with_routes(...)` |\n| Prose `\"next_step\"` field | Structured `RouteStep` with typed `host::EvmCommitMessage` target |\n| Runtime couldn't act on next-step hint | Runtime mechanically chains: sign → bind alias → submit |\n\nTools that don't need routes need **no changes** — the default `run_with_routes()` impl wraps `run()` into `ToolReturn::value(...)` automatically.\n\n### Manifest update\n\nAdd `\"evm-core\"` to `namespaces` if it wasn't already there:\n\n```rust\ndyn_aomi_app!(\n    app = client::PolymarketApp,\n    name = \"polymarket\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [...],\n    namespaces = [\"evm-core\"]   // required because routes reference host wallet targets\n);\n```\n\n### Pattern notes\n\n- **Don't convert tools that don't need it.** `get_polymarket_details`, `search_polymarket` are pure reads — leave them as `run`. Only the build/submit tools get the routes treatment.\n- **Match the `bind_as` alias to the wallet artifact.** `evm_commit_message` callbacks publish a `signature`; bind it under a domain-specific name like `\"clob_l1_signature\"` so multi-sign flows don't collide.\n- **The prompt field is a hint.** The runtime renders it into the next system prompt for the LLM, but doesn't force the call. Keep prompts short and action-oriented (*\"Sign the typed data\"*, *\"Submit the order\"*).\n- See `references/host-routes.md` for the full route contract.\n\n---\n\n## 4. Async tool with cancellation — `apps/sdk/examples/hello-app` shape\n\n**Anchored to** `sdk/examples/hello-app/src/lib.rs`. Use this when the tool emits progress over time (polling a long-running job, streaming events, scanning a slow upstream) and must respect cancellation.\n\n### Source material\n\nA REST API with a long-poll endpoint, a websocket stream, or any operation that doesn't return synchronously within a few seconds.\n\n### What you'd type\n\n> \"Wrap this poll endpoint as an Aomi async tool. It returns updates over a few minutes and the user might cancel mid-stream.\"\n\n### Tool impl\n\n```rust\nimpl DynAomiTool for PollJobStatus {\n    type App = MyApp;\n    type Args = PollJobStatusArgs;\n    const NAME: &'static str = \"poll_job_status\";\n    const DESCRIPTION: &'static str = \"Poll a long-running job until terminal status.\";\n    const IS_ASYNC: bool = true;     // ← the key flag\n\n    fn run_async(\n        app: &Self::App,\n        args: Self::Args,\n        ctx: DynToolCallCtx,\n        sink: DynAsyncSink,           // ← async sink, not Result<Value, String>\n    ) -> Result<(), String> {\n        let client = MyClient::new()?;\n\n        loop {\n            // Cooperative cancellation check\n            if sink.is_canceled() {\n                return Ok(());\n            }\n\n            let status = client.get_job_status(&args.job_id)?;\n\n            if status.is_terminal() {\n                // Final result via complete()\n                sink.complete(json!({\n                    \"job_id\": args.job_id,\n                    \"status\": status.kind,\n                    \"result\": status.result,\n                }))?;\n                return Ok(());\n            } else {\n                // Progress update via emit()\n                sink.emit(json!({\n                    \"job_id\": args.job_id,\n                    \"status\": status.kind,\n                    \"progress\": status.progress,\n                }))?;\n                std::thread::sleep(Duration::from_millis(args.poll_interval_ms.unwrap_or(1000)));\n            }\n        }\n    }\n}\n```\n\n### Hard rules for async tools\n\n| Rule | Reason |\n|------|--------|\n| Set `const IS_ASYNC: bool = true` | The macro generates different FFI exports for async; without this flag the runtime calls `run` and never reads the sink |\n| Implement `run_async`, NOT `run` | Mixing both is a confusing footgun — pick one |\n| Poll `sink.is_canceled()` regularly | The runtime sets this when the user aborts; without polling, the tool hangs |\n| Use `emit(...)` for progress, `complete(...)` for the final value | `complete` signals terminal status to the runtime; `emit` adds an intermediate update |\n| Don't return a `Value` from `run_async` | Return `Result<(), String>` — the data path is the sink, not the return |\n| Don't return route envelopes from `emit` | Only `complete()` accepts `ToolReturn` envelopes; intermediate `emit` calls take bare `Value`s |\n\n### Testing async tools\n\n```rust\nuse aomi_sdk::testing::{TestCtxBuilder, run_async_tool};\nuse serde_json::json;\n\n#[test]\nfn poll_emits_progress_then_completes() {\n    let ctx = TestCtxBuilder::new(\"poll_job_status\").build();\n    let (updates, terminal) = run_async_tool::<PollJobStatus>(\n        &MyApp,\n        json!({ \"job_id\": \"test-job\", \"poll_interval_ms\": 10 }),\n        ctx,\n    ).unwrap();\n\n    assert!(updates.len() >= 1);\n    assert_eq!(terminal.value[\"status\"], \"completed\");\n}\n```\n\n`run_async_tool` returns `(updates, terminal)` — assert both shapes. `updates` is a `Vec<Value>` of intermediate `emit` payloads; `terminal` is a `ToolReturn` (with optional routes) from the final `complete` or from a returned error.\n\n### Pattern notes\n\n- **Don't spawn Tokio inside `run_async`**. The runtime drives the call on its own thread. Use `std::thread::sleep` for delays, blocking HTTP for I/O.\n- **Cancellation is cooperative.** If your inner loop calls a blocking HTTP request, the cancellation can only fire between requests — not mid-request. For very long requests, prefer chunked or streaming HTTP that yields control.\n- **Panic containment is automatic.** If `run_async` panics, the runtime catches it and surfaces an error to the LLM rather than crashing the host. See `apps/sdk/examples/hello-app/src/lib.rs` `PanicSyncTool` for a deliberate test of this path.\n\n---\n\n## 5. Updating an app for a new SDK version\n\n**Anchored to** the exact-match SDK version gate documented in `docs/sdk-version-compatibility.md`. The host rejects plugins whose `aomi_sdk_version` symbol does not match its compiled `AOMI_SDK_VERSION`. After bumping `sdk/Cargo.toml`, all apps must be rebuilt — there's no per-app version negotiation.\n\n### When this comes up\n\n- Routine SDK bumps (`0.1.14` → `0.1.15`).\n- Adding a new `host::*` tool marker that an existing app would benefit from.\n- Adopting new SDK APIs (e.g. `RouteBuilder` fluent style replacing manual `with_routes`).\n\n### What you'd type\n\n> \"Update `apps/<name>` for the new SDK version. The SDK introduced `<feature>`; refactor the relevant tools.\"\n\n### Workflow\n\n1. **Check `sdk/Cargo.toml`** — confirm the new SDK version. The workspace `Cargo.toml` uses `{ workspace = true }` for `aomi-sdk`, so individual apps don't pin a version.\n2. **Search for deprecated patterns**:\n   ```bash\n   # Hunt for prose-based hand-off (legacy)\n   git grep 'SYSTEM_NEXT_ACTION' apps/<name>/\n\n   # Hunt for tools that should now use route hints\n   git grep 'next_step\\|\"requires_signature\"' apps/<name>/\n   ```\n3. **Convert tools that should benefit** — see Example 3 above for `run` → `run_with_routes`.\n4. **Add `namespaces`** if you've grown into using host tools:\n   ```rust\n   dyn_aomi_app!(\n       ...\n       namespaces = [\"evm-core\"]   // was []\n   );\n   ```\n5. **Rebuild + test:**\n   ```bash\n   cargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>\n   cargo test -p <name>\n   ```\n6. **Confirm the host accepts the new plugin.** Load it in your local runtime; the host logs the SDK version match check on plugin load.\n\n### Pattern notes\n\n- **The version gate is exact-match, not semver-compatible.** A `0.1.14` plugin will NOT load against a `0.1.15` host even though `0.1.14` and `0.1.15` are semver-compatible. This is by design — the FFI surface and manifest format live inside `aomi-sdk`, and the hosted runtime treats SDK drift as a coordinated rebuild.\n- **If you only changed app code, no rebuild is needed.** App-only release tags (`apps-v0.1.X`) don't change `AOMI_SDK_VERSION` and don't trigger a forced rebuild.\n- **The version stamp is automatic.** The plugin exports `aomi_sdk_version` as a symbol via the macro — you never write the version in app code.\n\n---\n\n## What All Five Examples Have in Common\n\n- **Decide the app type before naming tools.** Product client (real API), execution assistant (build/submit/sign), or builder assistant (SDK + docs only). The wrong call here cascades into wrong tool names and wrong preamble.\n- **Tool surface is shaped by user intent, not by endpoint count.** 30 endpoints rarely need 30 tools. 3-8 intent-shaped tools (`search_*`, `get_*`, `build_*`, `submit_*`) usually beat raw endpoint mirroring.\n- **Auth resolution lives in tool boundary code**, not the app struct. Read explicit args first, fall back to env vars, never embed credentials in preambles or tool descriptions.\n- **`namespaces = [\"evm-core\"]`** for most EVM or generated API apps, and for any app that uses EVM host tools (`stage_tx`, `commit_txs`, `evm_commit_message`, etc.) or returns `ToolReturn::with_routes` envelopes. Use SVM namespaces for Solana tools. Use `namespaces = []` only when no host namespace should be injected.\n- **Prefer typed JSON shapes.** Args are `JsonSchema + Deserialize` structs; client responses are typed deserializations from `client.rs`; tool outputs add a stable `\"source\"` field for cross-app deduplication.\n- **The build loop is `cargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>`.** For crate-level debugging, use `cargo build --manifest-path apps/<name>/Cargo.toml`.\n- **Validate against a real target when one is available.** A passing compile is necessary but not sufficient — call the real API for at least one read flow before declaring the app done.\n\nFor deeper coverage of specific patterns:\n\n- File roles, real-app conventions, and the validation loop → [aomi-sdk-patterns.md](aomi-sdk-patterns.md)\n- Spec-to-tool reduction with mapping rubrics → [spec-to-tools.md](spec-to-tools.md)\n- The full `ToolReturn` / `RouteStep` contract → [host-routes.md](host-routes.md)\n- Common build errors and recovery → [troubleshooting.md](troubleshooting.md)\n\nFile v0.1.1:references/host-routes.md\n\n# Host Routes\n\nRead this when:\n\n- The app you're building must hand off to the host wallet (sign, submit, broadcast) at some point.\n- The app needs to chain multiple tool calls where a later step depends on an artifact produced by an earlier wallet callback (`signature`, `transaction_hash`).\n- You see `ToolReturn` or `RouteStep` in an existing app and want to know what the runtime does with them.\n\n## What this replaces\n\nOlder Aomi apps returned a `SYSTEM_NEXT_ACTION` field embedded inside a JSON payload, and the runtime parsed prose hints to figure out what to call next. **That convention is gone.** The current contract is structured: tools return `ToolReturn::with_routes(value, [...])` envelopes, the runtime resolves the routes mechanically, and prose is never parsed.\n\nIf you see `SYSTEM_NEXT_ACTION` in older code or docs, treat it as outdated. Replace it with a `RouteStep` that names the next tool by its `host::*` marker.\n\n## The envelope\n\nA tool returns either a bare `Value` (read-only) or a `ToolReturn` (with routes):\n\n```rust\npub struct ToolReturn {\n    pub value: Value,                 // the tool's structured payload\n    pub routes: Vec<RouteStep>,       // ordered continuations\n}\n```\n\nTools opt into routes by overriding `run_with_routes()` instead of (or in addition to) `run()`. The default `run_with_routes()` impl wraps `run()` into `ToolReturn::value(...)` with empty routes — so non-routing tools need no changes.\n\n```rust\nimpl DynAomiTool for BuildMyOrder {\n    type App = MyApp;\n    type Args = BuildMyOrderArgs;\n    const NAME: &'static str = \"build_my_order\";\n    const DESCRIPTION: &'static str = \"Build an order and return the next signing step.\";\n\n    fn run_with_routes(\n        _app: &Self::App,\n        args: Self::Args,\n        ctx: DynToolCallCtx,\n    ) -> Result<ToolReturn, String> {\n        // ... build typed_data, prepare submit_template ...\n        Ok(ToolReturn::with_routes(\n            json!({ \"preview\": preview, \"wallet_request\": typed_data.clone() }),\n            [\n                RouteStep::on_return(\"evm_commit_message\", typed_data)\n                    .bind_as(\"clob_l1_signature\")\n                    .prompt(\"Sign the typed data to authorize the order.\"),\n                RouteStep::on_bound_event(\n                    \"submit_my_order\",\n                    submit_template,\n                    \"clob_l1_signature\",\n                )\n                .prompt(\"Wallet signed — submit the order now.\"),\n            ],\n        ))\n    }\n}\n```\n\n## RouteStep anatomy\n\n```rust\npub struct RouteStep {\n    pub tool: String,          // the next tool to call (host or app-local)\n    pub args: Value,           // hinted args; aliases get spliced in\n    pub trigger: RouteTrigger, // OnSyncReturn or OnBoundEvent { alias }\n    pub bind_as: Option<String>, // publish this step's result under an alias\n    pub prompt: Option<String>,  // override prompt text for this step\n}\n```\n\n### Triggers\n\n- **`RouteTrigger::OnSyncReturn`** (built via `RouteStep::on_return(...)`) — the route fires immediately when the current tool returns. Use this for \"do the next thing right away\" continuations.\n- **`RouteTrigger::OnBoundEvent { alias }`** (built via `RouteStep::on_bound_event(..., alias)`) — the route fires when the named alias resolves in the session's artifact store. Wallet callbacks publish artifacts (`signature`, `transaction_hash`) under aliases, so a step bound to `\"signature\"` fires only after the wallet signs.\n\n### Aliases (`bind_as`)\n\n`bind_as` publishes the step's terminal result under the given alias in the session's artifact store. A later step with `OnBoundEvent { alias: \"<same-name>\" }` consumes that artifact. The runtime splices the artifact into hinted args automatically — you don't have to know the wallet callback shape, just bind the alias and reference it.\n\nWallet tools publish their callbacks under predictable aliases:\n\n| Host tool | Callback artifact | Typical alias |\n|-----------|-------------------|---------------|\n| `commit_txs` | `transaction_hash` / receipts | `\"transaction_hash\"` (or domain-specific, e.g. `\"approve_tx_hash\"`) |\n| `evm_commit_message` | `signature` | `\"signature\"` (or domain-specific, e.g. `\"clob_l1_signature\"`) |\n| `stage_tx` | `pending_tx_id` | `\"pending_tx_id\"` |\n\nPick descriptive aliases when you have multiple of the same kind in flight (e.g. `\"approve_signature\"` vs `\"swap_signature\"`).\n\n### Typed targets\n\nFor host tools, prefer the typed `RouteTarget` markers in `aomi_sdk::builder::host` over raw string names:\n\n```rust\nuse aomi_sdk::builder::host;\n\nRouteStep::on_return_to::<host::EvmCommitMessage>(typed_data)\n    .bind_as(\"signature\")\n    .prompt(\"Sign the typed data.\");\n\nRouteStep::on_bound_to::<host::CommitTxs>(submit_args, \"pending_tx_id\")\n    .prompt(\"Broadcast the staged transaction.\");\n```\n\nAvailable `host::*` markers (non-exhaustive — confirm against `sdk/src/builder.rs` and `docs/host-interop.md`): `EncodeAndCall`, `StageTx`, `SimulateBatch`, `CommitTxs`, `EvmCommitMessage`, plus SVM targets such as `SvmStageIx`, `SvmStageTx`, `SvmCommitIx`, and `SvmCommitTx`. Using the marker types means renames in the host contract show up as compile errors instead of silent string drift.\n\n## Fluent builder style\n\nFor multi-step routes with shared state, the `RouteBuilder` API is more readable than nested `with_routes([...])` calls:\n\n```rust\nuse aomi_sdk::{RouteBuilder, ToolReturn};\nuse aomi_sdk::builder::host;\n\nlet mut route = RouteBuilder::new(value);\n\nroute.next(|next| {\n    next.add::<host::EncodeAndCall>(allowance_args)\n        .note(\"preflight allowance check; surface failures before continuing\");\n    next.add::<host::EvmCommitMessage>(typed_data)\n        .note(\"sign the typed data to authorize the order\");\n});\n\nroute.after(|after| {\n    after.awaits(\"clob_l1_signature\");\n    after.next(|next| {\n        next.add_named(\"submit_my_order\", submit_template)\n            .note(\"wallet signed — submit the order now\");\n    });\n});\n\nOk(route.build())\n```\n\n`add::<T>()` resolves the tool name from a typed marker; `add_named(name, args)` is the escape hatch for app-local tools or markers you don't have a `RouteTarget` for. `.note(...)` sets the per-step `prompt` field.\n\n## What the runtime does with routes\n\nThe runtime treats each route as advisory routing, not blind execution:\n\n- **`OnSyncReturn` steps** render into the next system prompt the LLM sees. The model still chooses whether to call the suggested tool — routes are hints, not forced calls.\n- **`OnBoundEvent` steps** wait in a queue until the named alias resolves. Wallet callbacks, staged-transaction completions, and other out-of-band events flow through the runtime's `RoutedEventBridge`, which splices the callback artifact into hinted args (the `signature` field in your hinted submit template gets filled with the actual signature when the wallet signs) before the continuation prompt is injected.\n- **`bind_as` aliases** persist for the session's lifetime once published. A later step in a different tool call can still bind to them.\n\nThe runtime never parses prose. The route's structured fields — `tool`, `args`, `trigger`, `bind_as` — are the contract.\n\n## When NOT to use routes\n\n- **Pure read-only tools** (search, get, list). Return a bare `Value` from `run()`. Adding empty routes is noise.\n- **Single-call writes that complete in-tool.** If your tool calls an HTTP submit endpoint and gets back a confirmation, return the confirmation as a `Value`. No host handoff needed.\n- **Direct-mode flows where the SDK handles signing internally.** E.g. when the app is configured with a private key and submits through the upstream SDK's own sign-and-broadcast path. Use `ToolReturn::value(...)` with no routes and let the result speak for itself.\n\nThe rule of thumb: use routes when (a) the host wallet must take an action and (b) a follow-up tool needs the wallet's callback artifact. If neither applies, a bare `Value` is the right shape.\n\n## Worked examples in the repo\n\n- **`apps/khalani`** — quote → build → wallet sign → submit. Uses `RouteBuilder` with preflight checks injected via typed host targets. Demonstrates `add_named` for app-local continuations and the `.after(...).awaits(...)` pattern for wallet callbacks.\n- **`apps/polymarket`** — order preview → CLOB L1 signature → CLOB L2 signature → submit. Shows `bind_as(\"clob_l1_signature\")` chaining and the wallet-mode vs direct-SDK-mode split.\n- **`apps/polymarket-rewards`** — LP-position mutation flows with multiple bound aliases.\n- **`apps/svm-transfer`** — Solana lane examples for `svm_stage_ix`, `svm_stage_tx`, `svm_commit_ix`, and `svm_commit_tx`.\n\nFor a tool that doesn't need routes at all, `apps/binance` and `apps/oneinch` (read paths) are the cleanest references — they return bare `Value`s and let the model decide what to call next.\n\n## Testing routes\n\n`aomi_sdk::testing::run_tool` returns the full `ToolReturn`, so route assertions are straightforward:\n\n```rust\nuse aomi_sdk::testing::{TestCtxBuilder, run_tool};\nuse serde_json::json;\n\n#[test]\nfn build_order_emits_signature_route() {\n    let ctx = TestCtxBuilder::new(\"build_my_order\").build();\n    let result = run_tool::<BuildMyOrder>(&MyApp, json!({\"market_id\": \"...\"}), ctx).unwrap();\n\n    assert_eq!(result.routes.len(), 2);\n    assert_eq!(result.routes[0].tool, \"evm_commit_message\");\n    assert_eq!(result.routes[0].bind_as.as_deref(), Some(\"clob_l1_signature\"));\n    assert!(matches!(\n        result.routes[1].trigger,\n        RouteTrigger::OnBoundEvent { ref alias } if alias == \"clob_l1_signature\"\n    ));\n}\n```\n\nFor async tools, `run_async_tool` returns `(updates, terminal)` — `terminal` is a `ToolReturn` with the same shape.\n\nFile v0.1.1:references/spec-to-tools.md\n\n# Spec To Tools\n\nUse this when the input is an OpenAPI document, Swagger spec, Postman collection, endpoint list, or product brief.\n\nIt also applies when the \"spec\" is really one of these:\n\n- SDK docs that link out to source repos\n- runtime / RPC documentation\n- example applications\n- architecture notes with concrete commands, configs, and method names\n\n## Extract First\n\nBefore writing code, pull out:\n\n- base URL and authentication scheme\n- the actual integration target the finished app should call\n- main entities and identifiers\n- read operations vs write operations\n- pagination, filters, and search parameters\n- user-specific inputs the host must provide\n- confirmation or safety requirements\n- rate limits, async jobs, and polling behavior\n- common error shapes\n- whether the docs actually publish an end-user API or mainly document a builder workflow\n\nIf a detail is missing, do not invent it. Leave a TODO or ask for the smallest missing piece.\n\n## Find The Real Integration Target\n\nPrefer the nearest executable product surface over explanatory documentation.\n\nAsk these questions early:\n\n- What will the finished app actually call?\n- Is there a concrete service contract such as REST, GraphQL, JSON-RPC, gRPC, webhook delivery, or a stable CLI protocol?\n- Is the target hosted, self-hosted, customer-provided, or only available through a local example stack?\n- If the source is an SDK or example project, does it expose a service that clients use after the builder sets it up?\n\nUseful rule of thumb:\n\n- If users can point the app at a running thing, build the client for that thing.\n- If no running thing exists yet, only then consider a builder or reference assistant.\n\n## Decide The App Type Early\n\nChoose one of these before naming tools:\n\n- **Product client**: the source exposes a real callable product surface, even if it is self-hosted or example-backed.\n- **Execution assistant**: the docs expose quote/build/submit flows and host or wallet handoff matters.\n- **Builder assistant**: the docs mostly explain how to build on top of a network, SDK, or runtime.\n\nBuilder assistants are valid outcomes, but they are the fallback, not the default. If the docs mostly point to SDK repos and example stacks, first check whether those repos produce a runnable interface that the app can call.\n\n## Map Endpoints To Model-Facing Tools\n\nDo not default to one tool per endpoint. Instead, group endpoints by user intent.\n\nBefore implementation, write down the proposed toolset explicitly:\n\n- tool name\n- what the user would ask for that should trigger it\n- key inputs\n- key outputs\n- whether it reads, prepares, or writes\n- whether it depends on a live target, wallet, signer, or host callback\n\nTreat this as a required checkpoint. If the proposed toolset is weak, too docs-oriented, too endpoint-shaped, or not clearly tied to user intent, revise it before writing code.\n\nWhen choosing the first implementation:\n\n- optimize for the primary user workflow first\n- keep the toolset as small as possible while still making that workflow work end to end\n- prefer app-specific high-value actions over broad protocol coverage when the source makes the important workflow obvious\n- avoid schema or discovery tools unless they are needed to support the chosen workflow\n- only mirror the wider API surface if the user asked for broad coverage\n\nGood mappings:\n\n- several lookup endpoints -> `search_*` or `get_*`\n- multiple list and detail calls -> `resolve_*` then `get_*`\n- quote + build + submit endpoints -> `get_*_quote`, `build_*`, `submit_*`\n- create side effects -> preview/build first, then explicit submit after confirmation\n- standard runtime interfaces -> wrap the standard methods first, then add extension hooks or custom methods\n- SDK + example repo + config + RPC docs -> `list_*_resources`, `get_*_overview`, `get_*_quickstart`, `get_*_rpc_surface`, `get_*_defaults`\n- example-backed transactional app -> one health/connectivity tool, a few read tools for key state, one write tool for the main action, and one verification tool for the outcome\n\nLess useful mappings:\n\n- raw REST verbs as tool names\n- exposing every transport detail directly to the model\n- returning unnormalized upstream payloads when only 20 percent of the fields matter\n- choosing a docs-summary tool surface when a real client could be built instead\n- inventing public user actions that are not actually documented by the source\n- building a large first-pass toolset before proving the primary user workflow\n\n## Preamble Rubric\n\nA strong preamble usually includes:\n\n- `## Role`\n- `## Capabilities`\n- `## Workflow`\n- `## Rules`\n\nFor execution apps, spell out:\n\n- when confirmation is required\n- which tool comes first\n- which tool hands off to the wallet or host\n- what must be preserved exactly between steps\n\n## Output Shape Rubric\n\nReturn the minimum stable JSON the model needs for the next step.\n\nGood result shapes often include:\n\n- a top-level echo of the key input\n- normalized identifiers\n- concise summaries\n- arrays of candidate objects\n- `requires_selection`, `selection_reason`, or `next_step_hint` when ambiguity remains\n\nWhen the host must take over (sign a transaction, sign typed data, simulate a batch), return a structured `ToolReturn` envelope rather than a prose `SYSTEM_NEXT_ACTION` field. The runtime resolves the route, splices wallet-callback artifacts into the next tool's args, and injects the continuation prompt — see [host-routes.md](host-routes.md) for the full shape.\n\n```rust\nuse aomi_sdk::{RouteStep, ToolReturn};\n\nToolReturn::with_routes(value, [\n    RouteStep::on_return(\"evm_commit_message\", typed_data)\n        .bind_as(\"signature\")\n        .prompt(\"Suggested next step: sign the typed data.\"),\n    RouteStep::on_bound_event(\"submit_my_order\", submit_template, \"signature\")\n        .prompt(\"Wallet signed — submit the order now.\"),\n])\n```\n\nThe route's structured fields are the contract; the runtime never parses prose.\n\nAvoid:\n\n- leaking auth credentials\n- giant raw payloads\n- mixed naming conventions from multiple upstream APIs\n- claiming success before the final submit step succeeds\n\n## Suggested Build Loop\n\n1. Read the spec and summarize the app contract.\n2. Identify the concrete service or runtime target.\n3. Propose the tool surface in concrete user-facing terms.\n4. Scaffold or patch `lib.rs`, `client.rs`, and `tool.rs`.\n5. Normalize auth, models, and errors in `client.rs`.\n6. Implement small, strongly typed tools.\n7. Build and test.\n8. If the target is available, verify a short end-to-end scenario:\n   - connectivity\n   - key read\n   - key write when applicable\n   - post-write verification\n9. Update docs or examples if the repo includes them.\n\n## Current OpenAPI Pipeline\n\nThe current `aomi-build` Rust CLI has a staged OpenAPI path. Use it when the input is a public OpenAPI/Swagger spec:\n\n```bash\naomi-build gen-specs <platform> --from-url <url>     # or --source all|well-known|apis-guru|github\naomi-build gen-client <platform> --force             # progenitor client\naomi-build gen-tool <platform> --all --force         # mechanical tool stubs\naomi-build compile --app <platform>\n```\n\n`aomi-build new-app <platform> --from-url <url>` is the one-shot orchestrator for the same stages plus a cargo build.\n\nDefault mode is **app-local**:\n\n- editable source spec: `apps/<platform>/openapi.yaml`\n- generated debug spec: `apps/<platform>/openapi.preprocessed.yaml`\n- generated Rust client: `apps/<platform>/src/client/`\n- curated app layer: `apps/<platform>/src/lib.rs` and `apps/<platform>/src/tool.rs`\n\nUse `--shared` only when the same generated client will be reused by multiple apps. Shared mode moves specs to `ext/specs/<platform>.yaml` and clients to `ext/src/<platform>/`.\n\nImportant rules:\n\n- Edit `openapi.yaml`, not `openapi.preprocessed.yaml`; the preprocessed file is overwritten by `gen-client` and exists to debug progenitor failures.\n- `gen-tool` names tools from `operationId` and can produce endpoint-shaped stubs. Treat those stubs as scaffolding, not final UX.\n- `progenitor-client` and the generated client's dependency markers belong in `Cargo.toml`; match the generated client's `reqwest` version.\n- Use `aomi-build test-schema <platform>` for live schema conformance. It is GET-only by default; pass `--write` only against safe sandbox/testnet APIs.\n- Use `aomi-build tighten-spec <platform> --in-place` when loose `additionalProperties: true` schemas make generated responses too untyped.\n\nIf the app is brand new in `aomi-sdk`, validate it with `cargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>`. A direct `cargo build --manifest-path apps/<name>/Cargo.toml` is still the fastest crate-level compile check when debugging package errors. After bumping `sdk/Cargo.toml` `package.version`, all apps must be rebuilt because the host enforces an exact-match SDK version gate.\n\nFile v0.1.1:references/troubleshooting.md\n\n# Troubleshooting\n\nRead this when a build fails or behaves differently than the workflow predicts. Each section lists symptoms, likely cause, and a concrete fix.\n\n## Build / aomi-build\n\n### `aomi-build compile --app <name>` reports \"0 plugins built\"\n\n**Symptoms:**\n- The build completes with no errors but produces no plugin file.\n- The expected `target/release/lib<name>.dylib` (or `.so` / `.dll`) is missing.\n\n**Cause:**\n- The app path, package metadata, or skip marker prevented discovery. Current `aomi-build compile` scans tracked manifests and the apps directory, but apps with `[package.metadata.aomi.skip]` are still skipped intentionally.\n\n**Fix:**\n\n```bash\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app <name>\n```\n\nFor an immediate compile signal without staging anything:\n\n```bash\ncargo build --manifest-path apps/<name>/Cargo.toml\n```\n\nIf the app is intentionally hidden behind package metadata, remove the skip only when it is ready.\n\n### App is intentionally skipped\n\n**Symptoms:**\n- App exists but `compile` skips it.\n- The build logs mention `package metadata aomi.skip = true`.\n\n**Cause:**\n- The app's `Cargo.toml` has `[package.metadata.aomi.skip]` set, usually for in-progress crates that don't yet build.\n\n**Fix:**\n\nIf the app is ready, remove the skip block from `apps/<name>/Cargo.toml`. If it's still in-progress, leave it alone — the skip is intentional.\n\n### `cargo build` succeeds but `aomi-build compile` errors with manifest validation\n\n**Symptoms:**\n- The crate compiles fine.\n- `aomi-build compile` rejects with errors like \"manifest does not export `aomi_create`\" or \"missing `aomi_sdk_version` symbol\".\n\n**Cause:**\n- `lib.rs` is missing the `dyn_aomi_app!` macro invocation, OR the macro arguments are malformed (e.g. typo in `tools = [...]`, missing `namespaces` field).\n\n**Fix:**\n\nCompare against `sdk/examples/app-template-http/src/lib.rs`:\n\n```rust\ndyn_aomi_app!(\n    app = client::MyApp,         // type from client.rs\n    name = \"my-app\",             // string identifier\n    version = \"0.1.0\",           // app version, not SDK version\n    preamble = PREAMBLE,         // const &str\n    tools = [client::Tool1, client::Tool2],\n    namespaces = []              // required field; [], [\"evm-core\"], or SVM namespace sets\n);\n```\n\nCommon mistakes:\n- Trailing comma after `namespaces = []` — fine, but make sure it's there if you copy-paste.\n- `preamble = \"...\"` inline string instead of `preamble = PREAMBLE` const reference. Both work; const is preferred for long preambles.\n- Forgetting the `namespaces` field entirely — required by the current SDK.\n\n### macOS codesigning failure\n\n**Symptoms:**\n- Build error like `codesign failed: errSecInternalComponent` or `unable to sign cdylib`.\n\n**Cause:**\n- macOS requires cdylibs to be signed before they can be loaded. `aomi-build compile` runs `codesign` automatically; this can fail if Xcode CLI tools are missing or the keychain is locked.\n\n**Fix:**\n\n```bash\n# Confirm Xcode CLI tools are installed\nxcode-select -p\n\n# If missing:\nxcode-select --install\n\n# If keychain is locked:\nsecurity unlock-keychain login.keychain\n```\n\nThen retry the build. For CI, ensure the runner has codesigning capability or use `--target` to build for a non-Apple platform.\n\n## Runtime / Host\n\n### Plugin rejected at host load: SDK version mismatch\n\n**Symptoms:**\n- Host log: `plugin <name> rejected: aomi_sdk_version 0.1.14 does not match host 0.1.15`.\n\n**Cause:**\n- The plugin was built against an older (or newer) `aomi-sdk` than the host. The host enforces an **exact-match** version gate.\n\n**Fix:**\n\n```bash\n# Pull the latest aomi-sdk\ngit -C ../aomi-sdk pull\n\n# Rebuild ALL apps — the version gate is repo-wide\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --release\n```\n\nA single app rebuild is not enough if other apps in the runtime also need to load. The hosted runtime treats SDK drift as a coordinated rebuild event — see `docs/sdk-version-compatibility.md`.\n\n### Plugin loads but tool calls return empty/null\n\n**Symptoms:**\n- Host loads the plugin successfully.\n- `aomi app current` shows the right tools.\n- Calling a tool returns nothing or `{}`.\n\n**Cause:**\nSeveral:\n- The tool's `run` returned `Ok(Value::Null)` or `Ok(json!({}))` — check the implementation.\n- The HTTP client succeeded but response deserialization silently produced an empty struct (loose `serde` defaults).\n- For async tools: `complete()` was never called, only `emit()`.\n\n**Fix:**\n\n1. Add a temporary `eprintln!(\"response: {:?}\", raw_response)` before the JSON conversion in `client.rs`.\n2. Confirm the upstream response shape matches your typed model. Run with `RUST_LOG=debug` if the SDK exposes it.\n3. For async tools, audit `run_async` for a path that emits without ever completing. Every async tool must either `complete()` once or return `Err(...)`.\n\n## Tool implementation\n\n### `JsonSchema` derive failure\n\n**Symptoms:**\n```\nerror[E0277]: the trait bound `MyType: JsonSchema` is not satisfied\n```\n\n**Cause:**\n- A field in `MyArgs` is a custom type that doesn't implement `JsonSchema`. The derive needs every field to be deriveable.\n\n**Fix:**\n\nAdd the derive on the inner type:\n\n```rust\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct MyArgs {\n    pub config: NestedConfig,    // NestedConfig must also derive JsonSchema\n}\n\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct NestedConfig {\n    pub field: String,\n}\n```\n\nFor std/external types that don't have it (`bigdecimal::BigDecimal`, `chrono::DateTime`, etc.) — represent them as `String` in the args struct and parse inside the tool. Schemars-compatible features for some crates (`schemars` with `chrono` feature) work but tie you to specific versions.\n\n### `Args` deserialization fails at runtime\n\n**Symptoms:**\n- Tool called by the LLM returns `Err(\"missing field 'symbol'\")` or `Err(\"invalid type: string, expected u32\")`.\n\n**Cause:**\n- The LLM produced args that don't match your `Args` struct. Common reasons: required field marked as required in schema but the LLM omitted it; type mismatch from prose-style numeric strings.\n\n**Fix:**\n\nMake optional fields explicitly optional and add doc comments that guide the LLM:\n\n```rust\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct GetDepthArgs {\n    /// Trading pair symbol in uppercase, no separator (e.g., \"BTCUSDT\", \"ETHUSDT\").\n    pub symbol: String,\n\n    /// Optional. Number of price levels to return. Valid values: 5, 10, 20, 50, 100, 500, 1000, 5000. Default 100.\n    #[serde(default)]\n    pub limit: Option<u32>,\n}\n```\n\nDoc comments become part of the model-facing schema. Spell out valid values, units, and formats — they are the only signal the model has.\n\n### Async tool hangs / never completes\n\n**Symptoms:**\n- The host shows the tool as \"in progress\" indefinitely.\n- No `emit` or `complete` events ever fire.\n\n**Cause:**\nSeveral:\n- The tool calls a blocking HTTP request that itself hangs (no timeout).\n- Inner loop never reaches `sink.complete(...)` because of an unhandled condition.\n- `IS_ASYNC` set to `true` but the tool implements `run` instead of `run_async`.\n\n**Fix:**\n\n1. Confirm `const IS_ASYNC: bool = true;` is set, AND `run_async` is implemented (not `run`).\n2. Add explicit timeouts to all HTTP calls:\n\n   ```rust\n   reqwest::blocking::Client::builder()\n       .timeout(Duration::from_secs(30))\n       .build()\n   ```\n\n3. Audit every code path in `run_async` — does each branch call either `sink.complete(...)` or return `Err(...)`? A loop with no exit condition will hang silently.\n4. Add `is_canceled()` checks at loop boundaries:\n\n   ```rust\n   loop {\n       if sink.is_canceled() { return Ok(()); }\n       // ... work ...\n   }\n   ```\n\n### Tool returns successfully but routes don't fire\n\n**Symptoms:**\n- Tool returns a `ToolReturn::with_routes(value, [...])` envelope.\n- The runtime acknowledges the tool result but never invokes the routed-to tool.\n\n**Cause:**\nSeveral:\n- The route's `tool` field references a tool name that doesn't exist (typo, or the tool isn't registered).\n- For `OnBoundEvent` routes: the alias never resolves because no upstream step published it via `bind_as`.\n- The app's `namespaces = []` doesn't include `\"evm-core\"`, so host tool references in routes are not authorized.\n\n**Fix:**\n\n1. Confirm `namespaces = [\"evm-core\"]` in the manifest.\n2. For `OnSyncReturn` routes: confirm the `tool` field matches an exported tool name exactly. For host tools, prefer typed markers such as `RouteStep::on_return_to::<host::EvmCommitMessage>(args)` or `host::CommitTxs` to catch typos at compile time.\n3. For `OnBoundEvent` routes: trace the alias chain. The earlier step that publishes via `bind_as(\"foo\")` must execute and complete before a later step bound to `\"foo\"` can fire. Check the order of routes in the `with_routes([...])` array.\n4. Inspect the host's event log: it logs every `OnBoundEvent` resolution and any unresolved aliases at session end.\n\n## Testing\n\n### `run_tool` fails with \"args type mismatch\"\n\n**Symptoms:**\n- Test code: `run_tool::<MyTool>(&MyApp, json!({\"foo\": \"bar\"}), ctx)?`\n- Error: `expected MyArgs, got Value::Object`.\n\n**Cause:**\n- The args you passed don't deserialize into `MyTool::Args`. Same as runtime — but easier to catch in tests.\n\n**Fix:**\n\nConstruct the args with the typed struct first, then serialize:\n\n```rust\nlet args = json!(MyArgs {\n    foo: \"bar\".to_string(),\n    optional_field: None,\n});\nlet result = run_tool::<MyTool>(&MyApp, args, ctx)?;\n```\n\nThis catches missing fields and type mismatches at compile time rather than runtime.\n\n### `run_async_tool` returns no updates\n\n**Symptoms:**\n- `let (updates, terminal) = run_async_tool::<...>(...)`.\n- `updates` is empty.\n\n**Cause:**\n- The async tool went straight to `complete()` without any `emit()` calls. Or the test runs faster than the tool's polling loop.\n\n**Fix:**\n\nCheck whether `updates.is_empty()` is actually a bug or expected for this tool. Many async tools have legitimate fast paths — e.g. job already complete on first poll. Adjust the assertion to allow `>= 0` updates and only require `terminal.value` to be correct.\n\nFor tools that should always emit at least one progress update, audit the implementation — they may have a synchronous fast path that should be split into a separate sync tool.\n\n## Manifest / Workspace\n\n### \"package not found in workspace\" when compiling an app\n\n**Symptoms:**\n- `cargo build --manifest-path apps/<name>/Cargo.toml` or `aomi-build compile --app <name>` errors with `package <name> not found in workspace`.\n\n**Cause:**\n- The app is not in the workspace's expected app layout, or it was copied by hand without matching the current app manifest conventions.\n\n**Fix:**\n\nThe `aomi-sdk` workspace excludes app crates by default because they build as cdylibs separately. Add the new app to `exclude` if your current checkout still requires it:\n\n```toml\n# Cargo.toml (workspace root)\n[workspace]\nexclude = [\n    \"apps/<existing>\",\n    \"apps/<your-new-app>\",   # add this\n]\n```\n\n`aomi-build init <name>` handles the current scaffold shape. If you scaffolded by copying instead, compare against a freshly generated app.\n\n### Two apps with the same `name = \"...\"` in `dyn_aomi_app!`\n\n**Symptoms:**\n- Both apps build successfully.\n- Host loads only one of them; the other is silently shadowed.\n\n**Cause:**\n- The `name` field in `dyn_aomi_app!` is the runtime identifier and must be unique across all loaded plugins.\n\n**Fix:**\n\nChange one of the names:\n\n```rust\ndyn_aomi_app!(\n    app = client::MyApp,\n    name = \"my-app\",       // must be unique\n    ...\n);\n```\n\nThe crate name (`Cargo.toml` `[package].name`) and the app name don't have to match, but it's a good convention to keep them aligned.\n\n## Diagnostic Checklist\n\nWhen something doesn't work, run through these in order:\n\n- [ ] `aomi-sdk` repo is on the latest commit (`git -C ../aomi-sdk log -1`)?\n- [ ] SDK version in `sdk/Cargo.toml` matches the host's `AOMI_SDK_VERSION`?\n- [ ] `aomi-build compile --app <name>` sees the app?\n- [ ] App package metadata does not set `[package.metadata.aomi.skip]` unless intentionally skipped?\n- [ ] `dyn_aomi_app!` has all required fields including `namespaces`?\n- [ ] Every `Args` struct derives `Deserialize + JsonSchema`?\n- [ ] Async tools set `const IS_ASYNC: bool = true` AND implement `run_async` (not `run`)?\n- [ ] Every async tool path either calls `complete()` or returns `Err(...)`?\n- [ ] HTTP clients have explicit timeouts?\n- [ ] For wallet-handoff tools: `namespaces = [\"evm-core\"]` and routes use typed `host::*` markers?\n- [ ] For deprecated patterns: no remaining `SYSTEM_NEXT_ACTION` strings, no prose-only \"next_step\" hints?\n\nFile v0.1.1:SECURITY.md\n\n# aomi-build Security Posture\n\nThis document maps the `aomi-build` skill against [OWASP Agentic Skills Top 10 (v1.0, March 2026)](https://owasp.org/www-project-agentic-skills-top-10/) and records the controls in place for each risk. Reviewers can audit the per-control claims against the live SKILL.md frontmatter, the references, and the captured scanner reports under [`.scanner-reports/aomi-build/`](../.scanner-reports/aomi-build/).\n\n**Last reviewed:** 2026-07-05 against `aomi-sdk` v3.0.1 and current `aomi-build` CLI source.\n\n## Threat model\n\n`aomi-build` is a procedure for an AI agent to scaffold new Aomi app crates from API docs, OpenAPI/Swagger specs, SDK docs, repository examples, endpoint notes, runtime interfaces, or product requirements. The skill writes Rust source files (`lib.rs`, `client.rs`, `tool.rs`, `Cargo.toml`) inside an `aomi-sdk`-shaped workspace and runs `cargo` + `git` commands to compile and track the new crate. It does not move funds, sign transactions, custody secrets, or make network calls of its own. It is correctly classified as **risk_tier: L1** (low) under the OWASP universal manifest schema.\n\nThe principal harm path the skill must guard against is **scaffolding code that, when later compiled and run by the user, exfiltrates data or executes attacker-controlled logic**. The OWASP `permissions:` manifest, the explicit `build.rs` deny-write entry, the no-network policy, and the documented \"do not embed credentials in scaffolded source\" rule all target this path.\n\n## Controls by AST risk\n\n### AST01 — Malicious Skills\n\n**Risk**: A skill is published that hides an exfiltration / drain payload behind benign-looking documentation.\n\n**Controls in place:**\n\n- The skill is published from [`aomi-labs/skills`](https://github.com/aomi-labs/skills) under MIT license; provenance is verifiable via `git log` and the GitHub repo signing keys.\n- The skill body (`SKILL.md` plus `references/`, `templates/`, `agents/`) contains no executable code beyond the `templates/quick-scaffold.sh` shell wrapper and shell snippets in documentation. The wrapper is human-auditable (~157 lines, no minification, no eval/exec, no curl-pipe-bash).\n- All shell snippets in `references/*.md` are documentation, not executed by the skill itself. The skill's actual operational scope is constrained to `cargo <subcommand>` and `git <subcommand>` per the `permissions.shell` declaration.\n- No network calls outside the declared `permissions.network.allow` list (which is empty — the skill is fully offline).\n- **Open**: signed releases (sigstore / `gh attestation`) are not yet wired up. Tracked separately at the repo level.\n\n### AST02 — Skill Injection / Tampering\n\n**Risk**: A modified skill is loaded from an untrusted source; tampering goes undetected.\n\n**Controls in place:**\n\n- Canonical source is the `aomi-labs/skills` GitHub repo. Tags are not yet signed; users who care about integrity should pin to a commit SHA and verify against the upstream repo.\n- The `permissions.files.deny_write` list (`SOUL.md`, `MEMORY.md`, `AGENTS.md`) blocks the skill from rewriting agent identity files, which is the canonical injection target.\n- The same `deny_write` list also includes `build.rs` — Rust build scripts run user-supplied code at compile time, and a tampered skill could otherwise scaffold a malicious build script that runs the first time the user types `cargo build`.\n- **Open**: `gh skill` repo / ref / tree-SHA frontmatter pre-population is on the release checklist (see `docs/todo` item #8) but not yet landed.\n\n### AST03 — Over-Privileged Skills\n\n**Risk**: A skill declares broad permissions (`shell: true`, `network: true`) it doesn't actually need; an injection prompt later abuses the privilege.\n\n**Controls in place:**\n\n- A complete OWASP-format `permissions:` manifest is declared in `SKILL.md` frontmatter:\n  - `files.read`: `./` and `../aomi-sdk/` only — the project the user is working in plus the upstream SDK checkout for pattern reference. No reads under `~/.ssh/`, `~/.aws/`, `~/.config/`, or other credential paths.\n  - `files.write`: scoped to `apps/`, workspace `Cargo.toml`, `Cargo.lock`, and `target/` within the project root. The skill never writes outside the workspace.\n  - `files.deny_write`: identity files (`SOUL.md`, `MEMORY.md`, `AGENTS.md`) plus `build.rs` (Rust build scripts run code at compile time and are not part of the canonical Aomi app shape).\n  - `network.allow: []` and `network.deny: \"*\"` — the skill makes no network calls. Spec / docs URLs that the user references are fetched out-of-band by the user (or via the agent's `WebFetch` operating outside the skill's operational scope) and pasted into the conversation.\n  - `shell`: array form with two argv prefixes (`cargo`, `git`). Spec example uses boolean; the array form is a least-privilege extension consistent with AST03 intent.\n  - `tools: []` — no MCP / external tool surface.\n- Claude Code's `allowed-tools` field is set to `Bash, Read, Write, Edit, Grep` (sufficient for scaffolding) and the OWASP manifest provides the actual operational lockdown as defense-in-depth.\n\n**Verification**:\n\n- [`Cisco AI Defense skill-scanner`](https://github.com/cisco-ai-defense/skill-scanner) v0.x — **0 findings**, `Status: SAFE`. Report: `.scanner-reports/aomi-build/cisco-ai-defense.md`.\n- [`NMitchem/SkillScan`](https://github.com/NMitchem/SkillScan) — **Risk 0.0/10**, 0 findings, `PASS`. Report: `.scanner-reports/aomi-build/skillscan.txt`.\n\n### AST04 — Skill Confused Deputy\n\n**Risk**: A skill's identity is reused for a privileged action the user didn't authorize.\n\n**Controls in place:**\n\n- The skill is **scaffold-only by default**. It does not run scaffolded code automatically. The standard validation loop (`cargo build`, `aomi-build compile`, or `cargo run -p aomi-sdk --features cli --bin aomi-build -- compile`) is invoked **only** when the user has asked for it and reviewed the scaffolded files.\n- The skill explicitly forbids fabricating endpoints, auth flows, or contract addresses the source material does not document — surfaced in the SKILL.md description and in `references/spec-to-tools.md` (\"Find The Real Integration Target\", \"Builder-oriented fallbacks\").\n- For execution-oriented apps that hand off to the host wallet, the skill teaches `ToolReturn::with_routes` over prose-based `SYSTEM_NEXT_ACTION` hints. The runtime resolves routes mechanically; the skill cannot smuggle non-self recipients past simulation by reformulating prose. See [`references/host-routes.md`](references/host-routes.md).\n\n### AST05 — Skill Side-Effects / Hidden Actions\n\n**Risk**: A skill performs persistent state changes that survive the session without the user's knowledge.\n\n**Controls in place:**\n\n- File-system writes are scoped to the user's workspace (`./apps/`, workspace `Cargo.toml`/`Cargo.lock`, `target/`). The skill never writes to `~/.config/`, `~/.aomi/`, or other system locations.\n- Workspace `Cargo.toml` changes, when needed, are observable via `git diff` before commit.\n- `cargo build` and `aomi-build compile` produce output under `target/` and `plugins/`, which are normal build/plugin artifacts. No persistent change escapes the project tree.\n- `git add apps/<name>/Cargo.toml` (run by `templates/quick-scaffold.sh` only when the user opts into it) only stages, never commits. The user makes the actual commit.\n- Read-side tooling (`cargo metadata`, `git ls-files`, `git status`) is non-destructive.\n\n### AST06 — Insecure Skill Communication\n\n**Risk**: A skill exfiltrates data through a side channel (logging, telemetry, off-domain HTTP).\n\n**Controls in place:**\n\n- The skill makes no direct network calls. `permissions.network.allow: []` and `deny: \"*\"` are declarative; scanners verify no HTTP/fetch/url patterns appear in the skill body or templates.\n- `cargo build` may fetch crate dependencies from `crates.io` as part of the user's local Cargo configuration. The skill does not modify that configuration — it relies on whatever registry the user has set up. Users in air-gapped environments (vendored deps, local registry) work without modification.\n- The skill **never embeds credentials in scaffolded source files**. Auth resolution patterns (`resolve_secret_value` in `tool.rs`) read from explicit tool arguments first, then fall back to environment variables — the same pattern shown in `references/examples.md` \"Example 1\" using the `apps/binance` reference.\n- No telemetry, no logging to remote endpoints, no analytics.\n\n### AST07 — Inadequate Logging / Auditability\n\n**Risk**: A skill takes actions that cannot be reconstructed after the fact.\n\n**Controls in place:**\n\n- All file writes are observable via `git status` / `git diff` before commit. The user reviews the scaffolded code before running it.\n- All shell invocations (`cargo run`, `cargo build`, `git add`) are observable in the user's shell history.\n- The standard validation loop produces deterministic build artifacts under `target/release/lib<name>.dylib` (or platform equivalent), inspectable via `cargo` or `nm`.\n- Scaffolded source files include doc comments on every tool and arg struct (see `references/examples.md`); the model-facing schema is recoverable directly from the source.\n\n### AST08 — Skill Supply-Chain Attacks\n\n**Risk**: A dependency the skill relies on is compromised; the skill picks up the compromise transitively.\n\n**Controls in place:**\n\n- The skill itself has **no runtime dependencies** beyond the `cargo` / `git` binaries and the user's local Rust toolchain.\n- Scaffolded apps depend on `aomi-sdk = { workspace = true }`, which resolves through the user's `aomi-sdk` checkout. The host enforces an exact-match SDK version gate at plugin load (see `docs/sdk-version-compatibility.md`); a tampered SDK that bumps the version stamp would not load against the user's host without coordinated action.\n- Scaffolded apps' transitive dependencies (e.g. `reqwest`, `serde`, `schemars`) are managed by Cargo as usual; the skill itself does not pin or vendor anything.\n- `templates/quick-scaffold.sh` depends only on POSIX shell and `cargo` + `git`, both checked at startup.\n- **Open**: sigstore attestation for the skill itself is not yet wired up.\n\n### AST09 — Insufficient User Consent\n\n**Risk**: A skill performs actions the user has not explicitly authorized.\n\n**Controls in place:**\n\n- Every action requires explicit user request:\n  - Scaffolding new app — user must ask (\"build an app for X\") and provide source material.\n  - Modifying workspace `Cargo.toml` — only when scaffolding (single `exclude = [...]` line addition, observable via `git diff`).\n  - Running `cargo build` — only when the user asks to validate, or when `templates/quick-scaffold.sh --build` is invoked explicitly.\n  - Staging files via `git add` — only inside `templates/quick-scaffold.sh` (a wrapper the user opts into) for the discovery-fix purpose.\n- The skill **never auto-commits**. `git commit` is the user's action, not the skill's.\n- The skill **never embeds credentials** or auto-fills secrets the user did not paste. The pattern documented in `references/examples.md` is \"read explicit args first, fall back to env vars\" — the skill teaches this pattern and never bypasses it.\n\n### AST10 — Cross-Platform Reuse\n\n**Risk**: A skill that's safe on one host (e.g. Claude Code) becomes unsafe when loaded on a different host (Codex, Cursor, OpenClaw) due to differing tool-permission semantics.\n\n**Controls in place:**\n\n- The OWASP `permissions:` manifest is declarative metadata that all OWASP-aware scanners and registries can read regardless of host. The Claude Code-specific `allowed-tools` field coexists as a sibling.\n- An `agents/openai.yaml` is provided for Codex/OpenAI-host metadata.\n- The skill's operational scope (`cargo` + `git` + filesystem within the workspace) is uniform across hosts; there are no host-specific tool surfaces that would behave differently on Codex vs Claude Code.\n- **Open**: end-to-end install verification across Claude Code + Codex + at least one community installer is on the release checklist (#9, #20). The skill is shaped for cross-platform reuse but has not yet been load-tested on every host.\n\n## Captured scanner reports\n\nAll reports under [`.scanner-reports/aomi-build/`](../.scanner-reports/aomi-build/). Re-run any scanner with the local commands documented in [`docs/todo`](../docs/todo) and `.scanner-reports/README.md` (substituting `./aomi-build/` for the target).\n\n| Scanner | Status | Findings | Report |\n|---------|--------|----------|--------|\n| Cisco AI Defense skill-scanner | **PASS** | 0 critical / 0 high / 0 medium / 0 low | [`cisco-ai-defense.md`](../.scanner-reports/aomi-build/cisco-ai-defense.md) |\n| pors/skill-audit | **PASS** | 0 errors / 2 doc-regex warns | [`pors-skill-audit.txt`](../.scanner-reports/aomi-build/pors-skill-audit.txt) |\n| NMitchem/SkillScan | **PASS** | Risk 0.0/10, 0 findings | [`skillscan.txt`](../.scanner-reports/aomi-build/skillscan.txt) |\n| Snyk agent-scan | **Pending** | Requires `SNYK_TOKEN`; report to be captured by maintainer | — |\n\n**Notes on findings**:\n\n- The 2 pors WARN findings match documentation patterns:\n  - `prompt/(delete|remove|rm)...` matches a row in `references/examples.md` table where one of the example tools is `binance_cancel_order` mapped to a `DELETE /order` HTTP endpoint. The match is on the literal string `DELETE` inside the reference table, not on a destructive instruction — the skill itself never deletes files.\n  - `prompt/(read|access|get|ext...)` matches `references/spec-to-tools.md` describing the canonical tool naming pattern (`get_*`, `read_*`, `list_*`). Documentation about the convention, not an instruction to access sensitive data.\n- Snyk requires an API token for the SaaS-backed analysis. Token-gated runs are tracked as a maintainer task; the W-code analysis approach used for `aomi-transact` (see [`aomi-transact/SECURITY.md`](../aomi-transact/SECURITY.md) Snyk W-code table) will apply if any HIGH-class characterizations come back. For `aomi-build` specifically, the only Snyk W-codes that could plausibly apply are W007 (insecure credential handling) — already mitigated by the no-credential-embedding rule and the auth resolution pattern documented in `references/examples.md`. The skill does not move funds (no W009), does not depend on third-party content (no W011), and does not invoke `npx` or other unpinned external URLs (no W012).\n\n## Reporting issues\n\nSecurity issues should be reported privately. See the top-level [`SECURITY.md`](../SECURITY.md) in `aomi-labs/skills` for the disclosure process, or open a private security advisory on the GitHub repo.\n\nFile v0.1.1:skill-card.md\n\n## Description:\n\nAomi Build helps agents scaffold Aomi apps and plugins from API documentation, OpenAPI or Swagger specs, SDK references, or product requirements, producing Rust SDK crates with tool schemas, preambles, host interop flows, and validation steps.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ceciliaz030](https://clawhub.ai/user/ceciliaz030)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to turn API specs, SDK references, or integration requirements into Aomi SDK app or plugin scaffolds. It is intended for builder workflows that need generated Rust code, a curated agent tool surface, host handoff patterns, and compile or test guidance.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The install and build workflow can run mutable Rust code with broader access than the skill clearly scopes.\n\nMitigation: Review the skill before installing, pin the Aomi SDK to a release or commit, and inspect generated files before running Cargo builds.\n\nRisk: Generated trading or wallet tools can become high-impact code if used against production accounts or networks.\n\nMitigation: Require explicit confirmation, use limits, prefer sandbox or testnet defaults, and review generated transaction or wallet handoff logic before deployment.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ceciliaz030/skills/aomi-build)\n- [Aomi Build Homepage](https://github.com/aomi-labs/skills/tree/main/aomi-build)\n- [Aomi SDK Repository](https://github.com/aomi-labs/aomi-sdk)\n- [Aomi SDK Patterns](artifact/references/aomi-sdk-patterns.md)\n- [Spec To Tools](artifact/references/spec-to-tools.md)\n- [Host Routes](artifact/references/host-routes.md)\n- [Build Examples](artifact/references/examples.md)\n- [Troubleshooting](artifact/references/troubleshooting.md)\n- [Security Posture](artifact/SECURITY.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with Rust code, shell commands, configuration snippets, and generated source file plans]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May produce or modify Aomi app files such as lib.rs, client.rs, tool.rs, Cargo.toml, generated OpenAPI clients, and build artifacts when used by an agent with filesystem and cargo access.]\n\n## Skill Version(s):\n\n0.1.1 (source: server release evidence and SKILL.md frontmatter)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v0.1.1:agents/openai.yaml\n\ninterface:\n  display_name: \"Aomi Build\"\n  short_description: \"Build Aomi apps from APIs and specs\"\n  default_prompt: \"Use $aomi-build to turn this API, OpenAPI spec, or product brief into an Aomi SDK app/plugin with a clean tool surface, preamble, and validation plan.\"\n\nArchive v0.0.1: 10 files, 43711 bytes\n\nFiles: agents/openai.yaml (270b), references/aomi-sdk-patterns.md (7205b), references/examples.md (24291b), references/host-routes.md (9471b), references/spec-to-tools.md (7154b), references/troubleshooting.md (12404b), SECURITY.md (14690b), SKILL.md (22065b), templates/quick-scaffold.sh (5705b), _meta.json (129b)\n\nFile v0.0.1:SKILL.md\n\n---\nname: aomi-build\ndescription: >\n  Build new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, SDK docs,\n  runtime interfaces, or product requirements. aomi-build scaffolds production-ready\n  Rust SDK crates (lib.rs, client.rs, tool.rs) with tool schemas, preambles,\n  host-interop flows, and validation steps — turning a vendor's full API surface into\n  AI-agent-callable tools. Trigger when the user wants to scaffold a new Aomi app from\n  an OpenAPI/Swagger spec, wrap a REST API as agent-callable tools, port an existing\n  SDK to Aomi, generate a tool surface from product requirements, or extend an Aomi\n  runtime with new integrations. Prefers real product integrations over docs-only\n  helpers whenever a callable surface exists. Output crates support sync HTTP, async\n  tools (cancellation-safe via DynAsyncSink), proxy-unwrap (EIP-1967), and host-interop\n  flows that route quote → approval → swap as multi-step transactions. Same runtime\n  that aomi-transact drives.\ncompatibility: \"Best when a local `aomi-apps` checkout is available, often at `../aomi-apps`. Falls back to bundled references when the SDK repo is not present. Targets aomi-sdk v0.1.15+ (Rust 2024 edition).\"\nlicense: MIT\nversion: \"0.1\"\nauthor: aomi-labs\ncompatible-with: claude-code\n# Claude Code allowed-tools. The skill scaffolds Rust source files (Write/Edit),\n# inspects existing apps and SDK examples (Read/Grep), and runs cargo + git\n# (Bash). Operational scope is locked down by OWASP permissions.shell below\n# to `cargo` and `git` only — defense in depth.\nallowed-tools: \"Bash(cargo:*, git:*), Read, Write, Edit, Grep\"\nmetadata:\n  author: aomi-labs\n  version: \"0.1\"\n  # Provenance — author-declared upstream coordinates.\n  # `gh skill install` will add/overwrite `ref`, `tree_sha`, `installed_via`,\n  # and `installed_at` at install time. Do not pre-populate those fields.\n  repository: aomi-labs/skills\n  homepage: https://github.com/aomi-labs/skills/tree/main/aomi-build\n\n# OWASP AST03 (Over-Privileged Skills) permission manifest.\n# Spec: https://owasp.org/www-project-agentic-skills-top-10/ast03\n# Universal Skill Format v1.0 (March 2026).\npermissions:\n  files:\n    # The skill reads source files in the user's project (the aomi-apps\n    # checkout or wherever the user runs from) and the SDK's bundled\n    # docs/examples for pattern reference.\n    read:\n      - ./\n      - ../aomi-apps/\n    # The skill writes new Rust source files within the project's apps/ tree\n    # and may amend the workspace manifest to add the new crate to `exclude`.\n    # cargo writes to target/ as a compile artifact; git writes index entries\n    # when staging the new manifest for xtask discovery.\n    write:\n      - ./apps/\n      - ./Cargo.toml\n      - ./Cargo.lock\n      - ./target/\n      - ../aomi-apps/apps/\n      - ../aomi-apps/Cargo.toml\n      - ../aomi-apps/Cargo.lock\n      - ../aomi-apps/target/\n    # Identity files must never be modified (AST03 mitigation #3).\n    # build.rs is denied because Rust build scripts run user-supplied\n    # code at compile time; the standard Aomi app shape (lib.rs,\n    # client.rs, tool.rs) does not need one. If the user genuinely\n    # needs a build script they must opt in explicitly outside the\n    # skill's default flow.\n    deny_write:\n      - SOUL.md\n      - MEMORY.md\n      - AGENTS.md\n      - build.rs\n\n  network:\n    # The skill makes no network calls of its own. Any docs / specs / repo\n    # links the user references are fetched out-of-band by the user (or by\n    # the agent's own WebFetch capability operating outside the skill's\n    # operational scope), then pasted into the conversation.\n    allow: []\n    deny: \"*\"\n\n  # Shell access restricted to `cargo` and `git` argv prefixes (least-privilege\n  # extension of the spec's boolean form, consistent with AST03 intent).\n  # `cargo` runs xtask (new-app, build-aomi), build, and test; `git` runs\n  # ls-files (used by xtask discovery) and add (track new Cargo.toml so\n  # xtask discovery picks it up).\n  shell:\n    - cargo\n    - git\n\n  # No MCP/tool surface beyond local cargo + git + filesystem.\n  tools: []\n\n# Risk tier per spec: L0 safe, L1 low, L2 elevated, L3 destructive.\n# L1 = the skill writes source files and runs the Rust toolchain.\n# It does not move funds, sign transactions, custody secrets, or make\n# network calls of its own.\nrisk_tier: L1\n\nrequires:\n  binaries: [cargo, git]\n---\n\n# Aomi Build\n\n## Overview\n\nAomi Build scaffolds production-ready Rust SDK crates for Aomi apps and plugins from\nOpenAPI/Swagger specs, SDK docs, or product requirements. Generates `lib.rs`, `client.rs`,\n`tool.rs` with typed tool schemas, host-interop flows, and validation steps.\n\n## When to Use\n\n- Scaffold a new Aomi app from an OpenAPI spec or REST API\n- Wrap an existing SDK as agent-callable Aomi tools\n- Extend an Aomi runtime with new protocol integrations\n\nDo **not** use this skill for executing transactions — use **aomi-transact** for that.\n\n## Prerequisites\n\n- Rust toolchain (2024 edition) and `cargo` on PATH\n- `git` on PATH\n- Aomi SDK v0.1.15 or newer\n- Local `aomi-apps` checkout at `../aomi-apps` (recommended)\n\n## Quick Start\n\n```bash\ncd ../aomi-apps\ncargo run -p xtask -- new-app my-integration\ncargo run -p xtask -- build-aomi --app my-integration\n```\n\n## Instructions\n\n1. Identify the integration target and its callable surface.\n2. State the proposed toolset (3–8 intent-shaped tools) before coding.\n3. Scaffold with `cargo run -p xtask -- new-app <name>`.\n4. Implement `client.rs` (HTTP, auth, models), `tool.rs` (`DynAomiTool` impls), `lib.rs` (manifest + preamble).\n5. For execution apps, return `ToolReturn::with_routes(...)` instead of bare JSON.\n6. Build and validate: `cargo run -p xtask -- build-aomi --app <name>`.\n\n## Examples\n\n```bash\ngrep -r \"dyn_aomi_app!\" ../aomi-apps/apps/\ncargo run -p xtask -- build-aomi --app binance\ncargo test --manifest-path apps/my-integration/Cargo.toml\n```\n\n## Output\n\n- Rust crate at `apps/<name>/` with `lib.rs`, `client.rs`, `tool.rs`, `Cargo.toml`\n- Compiled `.so`/`.dylib` plugin artifact under `target/`\n- Typed tool schema embedded in the plugin manifest\n\n## Error Handling\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `build-aomi` reports zero plugins | `Cargo.toml` untracked | `git add apps/<name>/Cargo.toml` then rebuild |\n| `SDK version mismatch` | Plugin built against old SDK | Bump version in `Cargo.toml`, rebuild all |\n| `JsonSchema derive failed` | Missing derive on Args | Add `schemars` dep, `#[derive(JsonSchema)]` on Args |\n| Async tool hangs | `is_canceled()` not polled | Add cancellation check in `run_async` loop |\n\n## Safety Justification\n\n`Bash(cargo:*, git:*)` — restricted to two argv prefixes. `cargo` runs xtask and compiles\ncrates; `git` runs `ls-files` and `add` only. No other shell commands permitted;\n`permissions.shell` enforces this at the OWASP AST03 level.\n\n`Read` — reads within `./` and `../aomi-apps/` only. `Write` — writes to `./apps/`,\n`../aomi-apps/apps/`, `Cargo.toml`, `Cargo.lock`, `target/` only; identity files\n(`SOUL.md`, `MEMORY.md`, `AGENTS.md`, `build.rs`) are `deny_write`-listed.\n`Edit` — same paths as Write. `Grep` — read-only search, no writes.\n\nRisk tier: L1 (source files + Rust toolchain only; no fund movement, no network calls).\n\n---\n\nUse this skill for tasks like:\n\n- \"Build an Aomi app from this OpenAPI spec.\"\n- \"Turn these REST endpoints into an Aomi plugin.\"\n- \"Scaffold a new Aomi SDK app for this product/API.\"\n- \"Update an existing Aomi app to support these new endpoints.\"\n- \"Turn these builder docs or SDK repos into an Aomi assistant.\"\n\n## First Read\n\nIf a local `aomi-apps` checkout exists (often at `../aomi-apps`), inspect these first. The current SDK is **v0.1.15**, Rust 2024 edition, and apps live in the workspace's `exclude = [...]` list discovered via `git ls-files apps/*/Cargo.toml`.\n\n- `sdk/examples/app-template-http/src/lib.rs` — canonical HTTP-API template (sync read-only)\n- `sdk/examples/app-template-http/src/client.rs`\n- `sdk/examples/app-template-http/src/tool.rs`\n- `sdk/examples/app-template-http/Cargo.toml` — note `edition = \"2024\"` and `crate-type = [\"cdylib\"]`\n- `sdk/examples/hello-app/src/lib.rs` — async tools (`IS_ASYNC = true`, `run_async`, `DynAsyncSink`), cancellation via `sink.is_canceled()`, panic containment\n- `docs/repo-structure.md` — file roles and authoring guidelines\n- `docs/host-interop.md` — public host tools (`view_state`, `run_tx`, `stage_tx`, `simulate_batch`, `commit_tx`, `commit_eip712`) and the `ToolReturn`/`RouteStep` envelope for multi-step flows\n- `docs/sdk-version-compatibility.md` — exact-match SDK version gate enforced via `aomi_sdk_version` symbol\n- 2 or 3 relevant apps under `apps/*/src/{lib,client,tool}.rs`. Recommended:\n  - `apps/binance` — execution-oriented with auth, normalized models, `namespaces = [\"common\"]`\n  - `apps/oneinch` — execution planner with multi-step preamble (quote → approval → swap)\n  - `apps/khalani` or `apps/polymarket` — host handoff via `ToolReturn::with_routes(...)`\n\nIf the supplied docs mostly point to GitHub repositories, SDKs, or examples instead of listing public endpoints:\n\n- treat those linked repositories as the real source of truth\n- inspect their README, config examples, example commands, and RPC/API surfaces\n- check whether they expose or produce a runnable service interface such as REST, GraphQL, JSON-RPC, gRPC, webhooks, or another stable client contract\n- prefer building against that executable surface instead of wrapping the docs themselves\n- avoid inventing a public transactional API that the docs do not actually publish\n\nIf the current repo is `aomi-widget`, also inspect:\n\n- `apps/landing/content/examples/*.mdx`\n- `apps/landing/content/guides/build/**/*.mdx`\n\nIf the `aomi-apps` checkout is not available, read:\n\n- [references/aomi-sdk-patterns.md](references/aomi-sdk-patterns.md) — manifest shape, file roles, real-app conventions\n- [references/spec-to-tools.md](references/spec-to-tools.md) — converting OpenAPI / SDK docs / endpoint lists into intent-shaped tools\n- [references/host-routes.md](references/host-routes.md) — `ToolReturn` envelope and `RouteStep` builders for execution apps that hand off to the host wallet\n- [references/examples.md](references/examples.md) — five end-to-end walkthroughs anchored to real apps (`binance`, builder fallback, `polymarket` routes upgrade, async tool with cancellation, SDK version bump)\n- [references/troubleshooting.md](references/troubleshooting.md) — common build/runtime failures with concrete fixes (untracked `Cargo.toml`, SDK version mismatch, async tool hangs, route resolution issues, JsonSchema derive failures)\n\n## Default Workflow\n\n1. Identify the product surface:\n   - What external API, SDK, repo, or spec is the source of truth?\n   - What concrete callable surface exists: REST, GraphQL, JSON-RPC, gRPC, webhook, CLI contract, or something else?\n   - Is there a real target we can point the app at: hosted service, self-hosted node, local example stack, or customer-provided endpoint?\n   - Is this read-only, execution-oriented, or mixed?\n   - What auth/env vars are required?\n   - What user state must come from the host or caller?\n   - Is this actually a public end-user API, a standard client interface exposed by a runtime/example app, or only builder-facing documentation?\n2. Describe the intended user-facing toolset before implementation:\n   - list the proposed tools by name\n   - say what user intent each tool serves\n   - call out which tools are read-only, which prepare actions, and which write or submit\n   - mention any expected target URL, runtime, or host dependency\n   - if the toolset is uncertain, surface the uncertainty before coding\n   - identify the primary user workflow the app should make easy first\n   - keep the first pass to the smallest sufficient toolset for that workflow unless the user asked for broader API coverage\n3. Reduce the spec into semantically meaningful tools.\n4. Scaffold or update the Aomi app using the standard file split:\n   - `lib.rs` for manifest and preamble. Register with `dyn_aomi_app!` including the `namespaces = [...]` field — `[\"common\"]` for execution apps that depend on host tools (`stage_tx`, `simulate_batch`, `commit_tx`, `commit_eip712`), `[]` for read-only apps that don't.\n   - `client.rs` for HTTP client, auth, models, and normalization\n   - `tool.rs` for `DynAomiTool` implementations. Sync tools implement `run`; async tools set `const IS_ASYNC: bool = true` and implement `run_async` with `DynAsyncSink::emit`/`complete`/`is_canceled` (see `sdk/examples/hello-app/src/lib.rs`).\n5. Write the preamble around actual tool behavior, confirmation rules, and any host handoff. Execution apps that drive multi-step wallet flows return `ToolReturn::with_routes(...)` instead of bare JSON — see [references/host-routes.md](references/host-routes.md).\n6. Validate with the SDK build flow and add focused tests when logic is non-trivial.\n\n## Tool Design Rules\n\n- First decide what kind of app this should be:\n  - product client\n  - execution assistant\n  - builder / SDK / runtime assistant\n- Before implementing, state the proposed toolset in concrete user-facing terms. This is part of the design, not optional polish.\n- Prefer the smallest sufficient toolset that makes the primary user workflow work end to end.\n- If there are multiple plausible integration targets, briefly state which one you are choosing and why before coding.\n- Prefer tools that interact with an actual product surface over tools that merely restate documentation.\n- A hosted API is not required. A self-hosted service, local example stack, standard RPC server, or other runnable interface still counts as a real integration target.\n- If the source material is SDK- or architecture-heavy, first ask whether it produces a service that clients call. If yes, build the client for that service.\n- Only fall back to a builder-oriented or docs-oriented tool surface when no stable executable target is available.\n- Do not mirror every endpoint 1:1 unless that is actually the cleanest model-facing API or the user explicitly asked for broad coverage.\n- Prefer 3 to 8 tools with clear user intent boundaries such as `search_*`, `get_*`, `build_*`, `submit_*`, `list_*`, or `resolve_*`.\n- Prefer intent-shaped tool names over raw protocol or transport names when practical.\n- Aggregate noisy upstream endpoints behind a smaller tool surface when the model does not need the raw distinction.\n- Prefer typed arguments over raw JSON string blobs when the primary workflow can be modeled cleanly that way.\n- Separate core tools from escape hatches. A generic fallback tool such as `*_rpc` or `*_raw` is fine, but it should not replace a clean core workflow.\n- Keep args typed and documented with `JsonSchema`. Field doc comments are model-facing and matter.\n- Return stable JSON with predictable keys. Normalize upstream naming, paging, and inconsistent shapes inside `client.rs` or helper functions.\n- Convert upstream errors into short actionable messages. Do not leak raw HTML, secrets, or giant payload dumps.\n\n## File Responsibilities\n\n### `lib.rs`\n\n- Keep it easy to scan.\n- Define `PREAMBLE` or a small `build_preamble()` hook.\n- Register tools with `dyn_aomi_app!`. Always include the `namespaces` field explicitly — `namespaces = [\"common\"]` for execution apps, `namespaces = []` for read-only apps. The macro generates the C ABI exports (`aomi_create`, `aomi_manifest`, `aomi_async_tool_start`, etc.) and embeds the SDK version stamp the host uses for the exact-match compatibility check.\n- Only keep manifest-level wiring here.\n\n### `client.rs`\n\n- Own the app struct, HTTP client, auth headers, env vars, typed models, and response normalization.\n- Prefer `reqwest::blocking::Client` with explicit timeouts for sync tools, matching the current SDK examples.\n- Keep third-party API quirks here instead of spreading them across tool implementations.\n\n### `tool.rs`\n\n- Implement `DynAomiTool`. Required associated types: `App` (the app struct from `client.rs`) and `Args` (a `JsonSchema + Deserialize` struct). Required consts: `NAME`, `DESCRIPTION`. Optional const: `IS_ASYNC` (defaults to `false`).\n- Use descriptions that tell the model when to call the tool, not just what endpoint it wraps.\n- Map normalized client results into concise JSON results. Sync tools return `Result<Value, String>` from `run()`; async tools return `Result<(), String>` from `run_async()` and emit progress through the `DynAsyncSink`. Cancellation: poll `sink.is_canceled()` and return `Ok(())` early.\n- Use `DynToolCallCtx` when host state such as connected wallet, session state, or caller attributes is needed. `ctx.session_id` and `ctx.call_id` are stable identifiers for logging or routing.\n- For execution apps that hand off to the wallet, return `ToolReturn::with_routes(value, [RouteStep::on_return(...).bind_as(...).prompt(...)])` instead of a bare `Value`. The `run_with_routes()` method on `DynAomiTool` has a default impl that wraps `run()` — only override it when you need routes. See [references/host-routes.md](references/host-routes.md).\n\n## Preamble Rules\n\nWrite the preamble from the app's real contract:\n\n- Define role, capabilities, workflow, and guardrails.\n- Mention tool order for multi-step flows.\n- State explicit confirmation requirements before write actions.\n- If dates matter, include the current date or instruct the app to use exact dates.\n- If the app relies on host wallet/signing tools, say that clearly and do not imply hidden infrastructure.\n\nFor deeper patterns and examples, read [references/aomi-sdk-patterns.md](references/aomi-sdk-patterns.md).\n\n## Host Interop And Execution\n\nFor execution-oriented apps:\n\n- Follow the public host conventions from `docs/host-interop.md`. The available host tools are `view_state` (read-only `eth_call`), `run_tx` (state-changing simulation), `stage_tx` (queue for later signing), `simulate_batch` (dry-run staged txs by `pending_tx_id`), `commit_tx` (sign and broadcast one staged tx), and `commit_eip712` (sign typed data). Apps reference these by name in tool descriptions and route hints — they are public contract, not private infrastructure.\n- Do not invent private namespaces (`CommonNamespace` etc.) or internal fallback behavior.\n- When the next step belongs to the host wallet or signer, return a `ToolReturn` envelope with explicit `RouteStep` builders instead of any prose-based `SYSTEM_NEXT_ACTION` convention. The runtime's `RoutedEventBridge` resolves `OnSyncReturn` and `OnBoundEvent` triggers, splices wallet-callback artifacts (`signature`, `transaction_hash`) into hinted args, and injects the continuation prompt. The runtime never parses prose — structured fields are the contract.\n- Preserve exact transaction or signature args when a downstream host tool must execute them. For raw external tx payloads, use `stage_tx` with `data: { raw: \"0x...\" }`; for ABI-driven calls, use `data: { encode: { signature, args } }`.\n- Do not claim a write succeeded until the upstream API submit step has actually completed.\n\nFor deeper coverage of the routes pattern, including `OnSyncReturn` vs `OnBoundEvent`, `bind_as` aliases, and worked examples from `apps/khalani` and `apps/polymarket`, read [references/host-routes.md](references/host-routes.md).\n\n## Validation\n\nWhen working inside `aomi-apps`:\n\n- Scaffold with `cargo run -p xtask -- new-app <name>` if starting from scratch, or copy `sdk/examples/app-template-http`. The xtask auto-derives `StructName` from the app name, generates `lib.rs`/`client.rs`/`tool.rs`, and registers the app in the workspace `exclude = [...]` list. For a one-shot wrapper that also handles `git add` for discovery and runs an initial compile check, use [templates/quick-scaffold.sh](templates/quick-scaffold.sh) — pass the app name and optionally `--build` to also run `xtask build-aomi`.\n- Build the plugin with `cargo run -p xtask -- build-aomi --app <name>`. Optional flags: `--release`, `--target <triple>`. The build validates the manifest, codesigns on macOS, and validates the produced plugin.\n- If `build-aomi` reports zero built plugins for a brand new app, check whether the new `apps/<name>/Cargo.toml` is still untracked. The xtask prefers `git ls-files apps/*/Cargo.toml` for discovery and falls back to a directory scan only when nothing is tracked. Apps marked with `[package.metadata.aomi.skip]` are skipped intentionally.\n- For a direct compile signal on an untracked app, use `cargo build --manifest-path apps/<name>/Cargo.toml`.\n- If the app has meaningful branching or normalization logic, add unit tests with `aomi_sdk::testing::{TestCtxBuilder, run_tool, run_async_tool}`. `TestCtxBuilder::new(tool_name).build()` produces a `DynToolCallCtx`; `run_tool` returns a full `ToolReturn` with routes; `run_async_tool` returns `(updates, terminal)`.\n- The host-plugin compatibility gate is **exact-match SDK version**. After bumping `sdk/Cargo.toml` `package.version`, all apps must be rebuilt — the host rejects plugins whose `aomi_sdk_version` symbol does not match its compiled `AOMI_SDK_VERSION`. See `docs/sdk-version-compatibility.md`.\n- If a real target is available, validate the app with a short ladder:\n  - compile/build\n  - connectivity check\n  - one representative read flow\n  - one representative write or submit flow when applicable\n  - post-write verification such as status, receipt, or refreshed state\n- Prefer proving one end-to-end user scenario over checking many disconnected endpoints.\n\nWhen the task also touches docs or demos in `aomi-widget`, update the relevant examples or guides to match the new app behavior.\n\n## Output Expectations\n\nAim to leave behind:\n\n- a coherent Aomi app crate or patch\n- typed tool args and strong descriptions\n- a preamble that explains the tool contract and rules\n- stable JSON outputs for the host/model\n- an app that can point at a real product surface when one exists\n- a short validation pass or a clear note about what could not be verified\n\nFile v0.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn7axfgphsdj10cpkqw6n840nh86837c\",\n  \"slug\": \"aomi-build\",\n  \"version\": \"0.0.1\",\n  \"publishedAt\": 1778929769186\n}\n\nFile v0.0.1:references/aomi-sdk-patterns.md\n\n# Aomi SDK Patterns\n\nThese patterns come from the SDK examples (`sdk/examples/app-template-http`, `sdk/examples/hello-app`) and the inspected public apps in `aomi-apps`. Current SDK is **v0.1.15**, Rust **2024 edition**.\n\n## Canonical Layout\n\nUse this split unless there is a strong reason not to:\n\n```text\napps/my-app/\n├─ Cargo.toml\n└─ src/\n   ├─ lib.rs\n   ├─ client.rs\n   └─ tool.rs\n```\n\n- `lib.rs`: manifest, preamble, `dyn_aomi_app!`\n- `client.rs`: app struct, HTTP client, auth, models, helpers\n- `tool.rs`: `DynAomiTool` impls and user-facing tool surface\n\n`Cargo.toml` must declare `edition = \"2024\"`, `crate-type = [\"cdylib\"]`, and depend on `aomi-sdk = { workspace = true }`. The host enforces an exact-match SDK version gate: after bumping `sdk/Cargo.toml`, all apps must be rebuilt — see `docs/sdk-version-compatibility.md`.\n\n## Minimal Manifest Shape\n\n```rust\nuse aomi_sdk::*;\n\nmod client;\nmod tool;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are ...\n\"#;\n\ndyn_aomi_app!(\n    app = client::MyApp,\n    name = \"my-app\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [\n        client::SearchThing,\n        client::GetThing,\n    ],\n    namespaces = []\n);\n```\n\nKeep `lib.rs` small. The manifest should be easy to audit at a glance.\n\nThe `namespaces` field is required:\n\n- `namespaces = []` for read-only apps that don't depend on host capabilities.\n- `namespaces = [\"common\"]` for execution apps that call host tools (`view_state`, `run_tx`, `stage_tx`, `simulate_batch`, `commit_tx`, `commit_eip712`) or return `ToolReturn::with_routes(...)` envelopes that reference those tools by name.\n\nThe macro generates the C ABI exports (`aomi_create`, `aomi_manifest`, `aomi_async_tool_start`, `aomi_dyn_exec_poll`, etc.) and embeds the SDK version stamp the host uses for compatibility checks.\n\n## What The Real Apps Show\n\n### `sdk/examples/app-template-http`\n\nUse as the default baseline for read-only HTTP APIs.\n\n- Simple `reqwest::blocking` client\n- Clean typed args\n- Small tool surface\n- Straightforward JSON normalization\n\n### `apps/x`\n\nUse this pattern when the upstream API:\n\n- needs an env-backed API key\n- has a wrapper response envelope\n- benefits from normalized data models and formatting helpers\n\nNotable conventions:\n\n- auth env vars live in `client.rs`\n- logical API failures are normalized before reaching tools\n- tools return concise, model-friendly JSON\n\n### `apps/polymarket`\n\nUse this pattern when the app needs:\n\n- multiple upstream API surfaces\n- dynamic preamble context such as exact current date\n- intent resolution before execution\n- multi-step flows with explicit user confirmation\n\nNotable conventions:\n\n- preamble explains exact flow order\n- tool surface separates search, details, intent resolution, preview, and submit\n- results include next-step hints without hiding uncertainty\n\n### `apps/khalani` and `apps/polymarket`\n\nUse this pattern when execution must hand off to host wallet tools.\n\nNotable conventions:\n\n- app tools never send the wallet request directly\n- build/submit tools return `ToolReturn::with_routes(value, [...])` envelopes — never prose-based hints\n- routes use `RouteStep::on_return(\"commit_eip712\", typed_data).bind_as(\"signature\").prompt(...)` to declare what tool the host should call next, what args to pass, and what alias to bind the result under\n- subsequent routes use `RouteStep::on_bound_event(\"submit_*\", template, \"signature\")` to wait for the bound alias before continuing\n- preamble tells the model to preserve exact host args and let the runtime resolve the route — the runtime never parses prose\n\n### Executable product integrations\n\nWhen the source material is mostly SDK docs, example repos, runtime notes, or architecture docs, first check whether it exposes or produces a client-facing interface such as:\n\n- REST or GraphQL\n- JSON-RPC\n- gRPC\n- webhooks\n- a stable CLI request/response contract\n- a local example service or reference node\n\nIf such a surface exists:\n\n- build the app against that executable interface\n- treat the SDK, example repo, and docs as implementation references\n- expose user-useful operations against the real service, not just summaries of the docs\n- validate with at least one real call when possible\n\n### Builder-oriented fallbacks\n\nUse a builder-oriented shape only when the source material is mostly:\n\n- SDK documentation\n- example repositories\n- architecture notes\n- runtime / RPC references\n- config files and quickstarts\n\nIn that case:\n\n- do not pretend there is a public swap, quote, or portfolio API unless the source really documents one\n- do not hide the absence of a real integration target\n- prefer tools such as `list_*_resources`, `get_*_overview`, `get_*_quickstart`, `get_*_rpc_surface`, or `get_*_network_defaults`\n- make the preamble explicit that the app is a builder assistant, not an end-user trading agent\n- say clearly what would be needed to upgrade the app into a real client later, such as a base URL, running example service, or customer endpoint\n\n## Tool Authoring Checklist\n\nEvery tool should answer these:\n\n- What user intent does it serve?\n- What exact name should the model call?\n- What fields does the model need to provide?\n- What result shape will be easiest for the model to reason over?\n- If the upstream API is inconsistent, where will normalization happen?\n\nPrefer names like:\n\n- `search_*`\n- `get_*`\n- `list_*`\n- `resolve_*`\n- `build_*`\n- `submit_*`\n\n## Client Conventions\n\nKeep these in `client.rs` whenever possible:\n\n- base URLs\n- auth headers\n- shared request helpers\n- response envelopes\n- normalization helpers\n- typed upstream models\n\nPrefer short actionable errors such as:\n\n- `X_API_KEY environment variable not set`\n- `Gamma API error 404: ...`\n- `Failed to parse markets: ...`\n\n## Validation Commands\n\nInside `aomi-apps`, the standard loop is:\n\n```bash\ncargo run -p xtask -- new-app my-app\ncargo run -p xtask -- build-aomi --app my-app\n```\n\n`build-aomi` accepts `--release` and `--target <triple>`. It validates the manifest, codesigns the cdylib on macOS, and validates the produced plugin file.\n\nOne caveat from practice:\n\n- `xtask build-aomi` discovers apps via `git ls-files apps/*/Cargo.toml` (with directory-scan fallback when nothing is tracked).\n- A brand new untracked app can therefore compile fine but still be skipped by `build-aomi`.\n- Use `cargo build --manifest-path apps/my-app/Cargo.toml` for an immediate compile check before the new app is tracked.\n- Apps with `[package.metadata.aomi.skip]` set are excluded intentionally — useful for in-progress crates.\n\nFor focused logic tests, use the SDK test helpers:\n\n```rust\nuse aomi_sdk::testing::{TestCtxBuilder, run_tool, run_async_tool};\n\nlet ctx = TestCtxBuilder::new(\"search_thing\").build();\nlet result = run_tool::<MyTool>(&MyApp, json!({\"query\": \"eth\"}), ctx)?;\n// result is a ToolReturn — bare-value tools have empty routes; route-returning\n// tools include the structured RouteStep list under result.routes\n```\n\n`run_tool` returns the full `ToolReturn` (handy for asserting routes alongside the JSON payload). `run_async_tool` returns `(updates, terminal)` so you can assert intermediate `emit` payloads as well as the final `complete` payload.\n\nFile v0.0.1:references/examples.md\n\n# Build Examples\n\nRead this when:\n\n- You need to translate a concrete spec or doc set into a working app.\n- You want to see the SKILL.md guidance applied end-to-end.\n- You're deciding what kind of app to build and want to pattern-match against a real one in `apps/`.\n\nEach example is anchored to a real app crate in `aomi-apps`. Code excerpts come directly from those crates; the **\"What you'd type\"** blocks show how you'd brief the skill to reproduce them.\n\nThe build lifecycle is consistent across every example:\n\n> **identify surface** → **propose toolset** → **scaffold** → **wire client + tools** → **build + test** → **handoff hooks**\n\nIf you only remember one thing: **don't mirror endpoints; map user intents.** A spec with 20 endpoints is rarely 20 tools. It's usually 4-8.\n\n---\n\n## 1. CEX read + signed orders — `apps/binance` shape\n\n**Anchored to** `apps/binance/src/{lib.rs, client.rs, tool.rs, types.rs}`. The canonical \"exchange API\" shape: HMAC auth, public reads, signed writes, normalized response models.\n\n### Source material\n\nA REST API doc (Binance Spot v3) with ~30 endpoints across:\n\n- public: tickers, depth, klines, 24h stats\n- signed (HMAC-SHA256): place order, cancel order, account balances, trade history\n\n### What you'd type\n\n> \"Build an Aomi app for Binance Spot. Cover the main public reads (price, depth, klines, 24h stats) plus signed order placement and account queries. Auth is HMAC-SHA256 with `BINANCE_API_KEY` + `BINANCE_SECRET_KEY`. Trading pairs use uppercase no-separator format (BTCUSDT).\"\n\n### Tool decisions\n\nResist 1:1 mapping. The spec has 30+ endpoints; the user intent reduces to 8 tools:\n\n| Tool name | Intent | Endpoint(s) |\n|-----------|--------|-------------|\n| `binance_get_price` | \"what's the price of X?\" | `GET /ticker/price` |\n| `binance_get_depth` | \"what's the order book for X?\" | `GET /depth` |\n| `binance_get_klines` | \"give me OHLC for technical analysis\" | `GET /klines` |\n| `binance_get_24hr_stats` | \"rolling 24h stats\" | `GET /ticker/24hr` |\n| `binance_place_order` | \"submit a buy/sell order\" | `POST /order` (signed) |\n| `binance_cancel_order` | \"cancel my order\" | `DELETE /order` (signed) |\n| `binance_get_account` | \"what's my balance?\" | `GET /account` (signed) |\n| `binance_get_trades` | \"my fill history\" | `GET /myTrades` (signed) |\n\nSkip: server time, exchange info, system\n\nArchive v0.1.0: 6 files, 11618 bytes\n\nFiles: agents/openai.yaml (270b), references/aomi-sdk-patterns.md (5030b), references/spec-to-tools.md (6166b), skill-card.md (2287b), SKILL.md (9550b), _meta.json (129b)","readmeExcerpt":"Skill: Build Owner: ceciliaz030 Summary: Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl... Tags: ai-agents:0.1.0, development:0.1.0, latest:0.1.1, scaffolding:0.1.0 Version history: v0.1.1 | 2026-07-10T19:31:13.740Z | auto aomi-build v0.1.1 - Revamped documentation and manifest for clarity, precise permissio","codeSnippets":[],"executableExamples":[{"language":"bash","snippet":"cd ../aomi-sdk\ncargo run -p aomi-sdk --features cli --bin aomi-build -- init my-integration\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app my-integration\ncargo run -p aomi-sdk --features cli --bin aomi-build -- new-app geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json"},{"language":"bash","snippet":"grep -r \"dyn_aomi_app!\" ../aomi-sdk/apps/\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app binance\ncargo test --manifest-path apps/my-integration/Cargo.toml"},{"language":"text","snippet":"apps/my-app/\n├─ Cargo.toml\n└─ src/\n   ├─ lib.rs\n   ├─ client.rs\n   └─ tool.rs"},{"language":"rust","snippet":"use aomi_sdk::*;\n\nmod client;\nmod tool;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are ...\n\"#;\n\ndyn_aomi_app!(\n    app = client::MyApp,\n    name = \"my-app\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [\n        client::SearchThing,\n        client::GetThing,\n    ],\n    namespaces = [\"evm-core\"]\n);"},{"language":"bash","snippet":"cargo run -p aomi-sdk --features cli --bin aomi-build -- init my-app\ncargo run -p aomi-sdk --features cli --bin aomi-build -- compile --app my-app"},{"language":"bash","snippet":"cargo run -p aomi-sdk --features cli --bin aomi-build -- gen-specs geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json\ncargo run -p aomi-sdk --features cli --bin aomi-build -- gen-client geckoterminal --force\ncargo run -p aomi-sdk --features cli --bin aomi-build -- gen-tool geckoterminal --all --force\ncargo run -p aomi-sdk --features cli --bin aomi-build -- new-app geckoterminal --from-url https://api.geckoterminal.com/docs/v2/swagger.json"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: aomi-build\ndescription: >\n  Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK\n  references. aomi-build generates production-ready Rust SDK crates (lib.rs,\n  client.rs, tool.rs) with tool schemas, preambles, host-interop flows, and\n  validation — turning a vendor's API surface into AI-agent-callable tools. It\n  covers the current `aomi-build` OpenAPI pipeline (`gen-specs` → `gen-client`\n  → `gen-tool` → curate → compile/test) as well as greenfield apps. Use when\n  the user wants to scaffold a new Aomi app from a spec, wrap a REST API as\n  agent-callable tools, port an existing SDK, or extend an Aomi runtime with new\n  integrations. Trigger with prompts about wrapping APIs, scaffolding Rust crates\n  from specs, or adding protocol integrations that aomi-transact can drive. Output\n  crates support progenitor-generated OpenAPI clients, curated tool layers, sync\n  HTTP, async tools (DynAsyncSink), typed secrets, route plans\n  (ToolReturn/RouteStep), EVM and SVM host handoffs, and multi-step\n  quote→approval→swap flows. Same runtime that aomi-transact drives.\ntags: [crypto, web3, evm, rust, sdk-scaffolding, openapi, swagger, agent-tools, defi, builder-tools]\ncompatibility: 'Best when a local aomi-sdk checkout is available, often at ../aomi-sdk. Falls back to bundled references when the SDK repo is not present. Verified against aomi-sdk v3.0.1 (Rust 2024 edition) and the current aomi-build Rust CLI. Install the current CLI with cargo install --git https://github.com/aomi-labs/aomi-sdk --features cli aomi-sdk, or run from source with cargo run -p aomi-sdk --features cli --bin aomi-build -- <command>. Designed for claude-code; also works with Cursor, Codex CLI, Gemini, and any agent runtime that supports the Anthropic skill spec.'\nlicense: MIT\nversion: \"0.1.1\"\nauthor: 'aomi-labs <hello@aomi.dev>'\n# Claude Code allowed-tools. The skill scaffolds Rust source files (Write/Edit),\n# inspects existing apps and SDK examples (Read/Grep), and runs cargo + git\n# (Bash). Operational scope is locked down by OWASP permissions.shell below\n# to `cargo` and `git` only — defense in depth.\nallowed-tools: 'Bash(cargo:*), Bash(git:*), Read, Write, Edit, Grep'\nmetadata:\n  author: 'aomi-labs <hello@aomi.dev>'\n  version: \"0.1.1\"\n  # Provenance — author-declared upstream coordinates.\n  # `gh skill install` will add/overwrite `ref`, `tree_sha`, `installed_via`,\n  # and `installed_at` at install time. Do not pre-populate those fields.\n  repository: aomi-labs/skills\n  homepage: https://github.com/aomi-labs/skills/tree/main/aomi-build\n\n# OWASP AST03 (Over-Privileged Skills) permission manifest.\n# Spec: https://owasp.org/www-project-agentic-skills-top-10/ast03\n# Universal Skill Format v1.0 (March 2026).\npermissions:\n  files:\n    # The skill reads source files in the user's project (the aomi-sdk\n    # checkout or wherever the user runs from) and the SDK's bundled\n    # docs/examples for pattern reference.\n    read:\n      - ./\n      - ../aomi-sdk/"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7axfgphsdj10cpkqw6n840nh86837c\",\n  \"slug\": \"aomi-build\",\n  \"version\": \"0.1.1\",\n  \"publishedAt\": 1783711873740\n}"},{"path":"references/aomi-sdk-patterns.md","content":"# Aomi SDK Patterns\n\nThese patterns come from the SDK examples (`sdk/examples/app-template-http`), current SDK source, and inspected public apps in `aomi-sdk/apps`. Current SDK is **v3.0.1**, Rust **2024 edition**.\n\n## Canonical Layout\n\nUse this split unless there is a strong reason not to:\n\n```text\napps/my-app/\n├─ Cargo.toml\n└─ src/\n   ├─ lib.rs\n   ├─ client.rs\n   └─ tool.rs\n```\n\n- `lib.rs`: manifest, preamble, `dyn_aomi_app!`\n- `client.rs`: app struct, HTTP client, auth, models, helpers\n- `tool.rs`: `DynAomiTool` impls and user-facing tool surface\n\n`Cargo.toml` must declare `edition = \"2024\"`, `crate-type = [\"cdylib\"]`, and depend on `aomi-sdk = { workspace = true }`. The host enforces an exact-match SDK version gate: after bumping `sdk/Cargo.toml`, all apps must be rebuilt — see `docs/sdk-version-compatibility.md`.\n\n## Minimal Manifest Shape\n\n```rust\nuse aomi_sdk::*;\n\nmod client;\nmod tool;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are ...\n\"#;\n\ndyn_aomi_app!(\n    app = client::MyApp,\n    name = \"my-app\",\n    version = \"0.1.0\",\n    preamble = PREAMBLE,\n    tools = [\n        client::SearchThing,\n        client::GetThing,\n    ],\n    namespaces = [\"evm-core\"]\n);\n```\n\nKeep `lib.rs` small. The manifest should be easy to audit at a glance.\n\nThe `namespaces` field is required. Current canonical host namespaces are explicit strings:\n\n- `namespaces = [\"evm-core\"]` for most EVM apps and generated OpenAPI apps. This injects the current EVM host tools such as `encode_and_call`, `stage_tx`, `simulate_batch`, `commit_txs`, and `evm_commit_message`.\n- `namespaces = [\"svm-reads\", \"svm-ix-broadcast\", \"svm-tx-broadcast\"]` for Solana apps that need SVM read/stage/commit host tools.\n- `namespaces = []` only for apps that should receive no host namespace at all.\n\nDo not copy the old `\"common\"` namespace into new apps. Current SDK docs note that legacy namespace was removed in host iter-39; the loader skips unknown namespaces.\n\nThe macro generates the C ABI exports (`aomi_create`, `aomi_manifest`, `aomi_async_tool_start`, `aomi_dyn_exec_poll`, etc.) and embeds the SDK version stamp the host uses for compatibility checks.\n\n## What The Real Apps Show\n\n### `sdk/examples/app-template-http`\n\nUse as the default baseline for read-only HTTP APIs.\n\n- Simple `reqwest::blocking` client\n- Clean typed args\n- Small tool surface\n- Straightforward JSON normalization\n\n### `apps/x`\n\nUse this pattern when the upstream API:\n\n- needs an env-backed API key\n- has a wrapper response envelope\n- benefits from normalized data models and formatting helpers\n\nNotable conventions:\n\n- auth env vars live in `client.rs`\n- logical API failures are normalized before reaching tools\n- tools return concise, model-friendly JSON\n\n### `apps/polymarket`\n\nUse this pattern when the app needs:\n\n- multiple upstream API surfaces\n- dynamic preamble context such as exact current date\n- intent resolution before execution\n- multi-step flows with explicit user confirmation\n\nNotable conventions:\n\n- preamble explains exact "},{"path":"references/examples.md","content":"# Build Examples\n\nRead this when:\n\n- You need to translate a concrete spec or doc set into a working app.\n- You want to see the SKILL.md guidance applied end-to-end.\n- You're deciding what kind of app to build and want to pattern-match against a real one in `apps/`.\n\nEach example is anchored to a real app crate in `aomi-sdk/apps`. Code excerpts come directly from those crates; the **\"What you'd type\"** blocks show how you'd brief the skill to reproduce them.\n\nThe build lifecycle is consistent across every example:\n\n> **identify surface** → **propose toolset** → **scaffold** → **wire client + tools** → **build + test** → **handoff hooks**\n\nIf you only remember one thing: **don't mirror endpoints; map user intents.** A spec with 20 endpoints is rarely 20 tools. It's usually 4-8.\n\n---\n\n## 1. CEX read + signed orders — `apps/binance` shape\n\n**Anchored to** `apps/binance/src/{lib.rs, client.rs, tool.rs, types.rs}`. The canonical \"exchange API\" shape: HMAC auth, public reads, signed writes, normalized response models.\n\n### Source material\n\nA REST API doc (Binance Spot v3) with ~30 endpoints across:\n\n- public: tickers, depth, klines, 24h stats\n- signed (HMAC-SHA256): place order, cancel order, account balances, trade history\n\n### What you'd type\n\n> \"Build an Aomi app for Binance Spot. Cover the main public reads (price, depth, klines, 24h stats) plus signed order placement and account queries. Auth is HMAC-SHA256 with `BINANCE_API_KEY` + `BINANCE_SECRET_KEY`. Trading pairs use uppercase no-separator format (BTCUSDT).\"\n\n### Tool decisions\n\nResist 1:1 mapping. The spec has 30+ endpoints; the user intent reduces to 8 tools:\n\n| Tool name | Intent | Endpoint(s) |\n|-----------|--------|-------------|\n| `binance_get_price` | \"what's the price of X?\" | `GET /ticker/price` |\n| `binance_get_depth` | \"what's the order book for X?\" | `GET /depth` |\n| `binance_get_klines` | \"give me OHLC for technical analysis\" | `GET /klines` |\n| `binance_get_24hr_stats` | \"rolling 24h stats\" | `GET /ticker/24hr` |\n| `binance_place_order` | \"submit a buy/sell order\" | `POST /order` (signed) |\n| `binance_cancel_order` | \"cancel my order\" | `DELETE /order` (signed) |\n| `binance_get_account` | \"what's my balance?\" | `GET /account` (signed) |\n| `binance_get_trades` | \"my fill history\" | `GET /myTrades` (signed) |\n\nSkip: server time, exchange info, system status, sub-account endpoints, futures (a separate app), savings, staking, mining. They'd bloat the model's tool surface without serving a clear primary user intent.\n\n### Manifest (`lib.rs`)\n\n```rust\nuse aomi_sdk::*;\n\nmod client;\nmod tool;\nmod types;\n\nconst PREAMBLE: &str = r#\"## Role\nYou are an AI assistant specialized in interacting with the Binance cryptocurrency exchange...\n\n## Authentication\n- Public market data endpoints do not require authentication\n- Signed endpoints (orders, account, trades) require both api_key and secret_key\n- The signature is computed as HMAC-SHA256(secret_key, query_string_with_timestamp)\n- The timestamp p"},{"path":"references/host-routes.md","content":"# Host Routes\n\nRead this when:\n\n- The app you're building must hand off to the host wallet (sign, submit, broadcast) at some point.\n- The app needs to chain multiple tool calls where a later step depends on an artifact produced by an earlier wallet callback (`signature`, `transaction_hash`).\n- You see `ToolReturn` or `RouteStep` in an existing app and want to know what the runtime does with them.\n\n## What this replaces\n\nOlder Aomi apps returned a `SYSTEM_NEXT_ACTION` field embedded inside a JSON payload, and the runtime parsed prose hints to figure out what to call next. **That convention is gone.** The current contract is structured: tools return `ToolReturn::with_routes(value, [...])` envelopes, the runtime resolves the routes mechanically, and prose is never parsed.\n\nIf you see `SYSTEM_NEXT_ACTION` in older code or docs, treat it as outdated. Replace it with a `RouteStep` that names the next tool by its `host::*` marker.\n\n## The envelope\n\nA tool returns either a bare `Value` (read-only) or a `ToolReturn` (with routes):\n\n```rust\npub struct ToolReturn {\n    pub value: Value,                 // the tool's structured payload\n    pub routes: Vec<RouteStep>,       // ordered continuations\n}\n```\n\nTools opt into routes by overriding `run_with_routes()` instead of (or in addition to) `run()`. The default `run_with_routes()` impl wraps `run()` into `ToolReturn::value(...)` with empty routes — so non-routing tools need no changes.\n\n```rust\nimpl DynAomiTool for BuildMyOrder {\n    type App = MyApp;\n    type Args = BuildMyOrderArgs;\n    const NAME: &'static str = \"build_my_order\";\n    const DESCRIPTION: &'static str = \"Build an order and return the next signing step.\";\n\n    fn run_with_routes(\n        _app: &Self::App,\n        args: Self::Args,\n        ctx: DynToolCallCtx,\n    ) -> Result<ToolReturn, String> {\n        // ... build typed_data, prepare submit_template ...\n        Ok(ToolReturn::with_routes(\n            json!({ \"preview\": preview, \"wallet_request\": typed_data.clone() }),\n            [\n                RouteStep::on_return(\"evm_commit_message\", typed_data)\n                    .bind_as(\"clob_l1_signature\")\n                    .prompt(\"Sign the typed data to authorize the order.\"),\n                RouteStep::on_bound_event(\n                    \"submit_my_order\",\n                    submit_template,\n                    \"clob_l1_signature\",\n                )\n                .prompt(\"Wallet signed — submit the order now.\"),\n            ],\n        ))\n    }\n}\n```\n\n## RouteStep anatomy\n\n```rust\npub struct RouteStep {\n    pub tool: String,          // the next tool to call (host or app-local)\n    pub args: Value,           // hinted args; aliases get spliced in\n    pub trigger: RouteTrigger, // OnSyncReturn or OnBoundEvent { alias }\n    pub bind_as: Option<String>, // publish this step's result under an alias\n    pub prompt: Option<String>,  // override prompt text for this step\n}\n```\n\n### Triggers\n\n- **`RouteTrigger::OnSyncReturn`** (built via `RouteSte"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl... Skill: Build Owner: ceciliaz030 Summary: Scaffold new Aomi apps and plugins from API docs, OpenAPI/Swagger specs, or SDK references. aomi-build generates production-ready Rust SDK crates (lib.rs, cl... Tags: ai-agents:0.1.0, development:0.1.0, latest:0.1.1, scaffolding:0.1.0 Version history: v0.1.1 | 2026-07-10T19:31:13.740Z | auto aomi-build v0.1.1 - Revamped documentation and manifest for clarity, precise permissio","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1933,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T08:11:28.517Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T10:51:54.561Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}