Skip to content

feat: let a one-line agent prompt set up Zerion via init + the skill - #128

Merged
evgeth merged 9 commits into
mainfrom
claude/zerion-ai-version-update-68a699
Oct 6, 2026
Merged

evgeth merged 9 commits into
mainfrom
claude/zerion-ai-version-update-68a699

Conversation

@evgeth

@evgeth evgeth commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Why

For an agent to do on-chain work with the Zerion CLI today, the prompt has to spell out a lot: how to install and log in, which version is needed, how to register a read-only wallet, and the rules for building and signing transactions safely. That makes prompts long, hard to read, and out of date as soon as the CLI changes.

This PR moves that knowledge into zerion init and the zerion skill, so a prompt can be just the request plus one fixed line:

Using the Zerion CLI, exit all my positions and sell all my tokens on Blast, then move everything to Ethereum.

Wallet: 0xYourWallet
Start by running `npx -y zerion-cli@latest init -y`.

The agent runs init, which installs or upgrades the CLI and the skill, then reads SKILL.md and works out which capability docs the request needs. Nothing here is exit-specific, and it should behave the same in Claude Code, Codex, Cursor and Gemini CLI.

What changed

zerion init (cli/commands/init.js)

  • Actually installs globally (bug fix). Under npx, the temp copy is first on PATH, so zerion --version always succeeded. init reported "already installed" and never ran npm install -g, even on a fresh machine. Run from a project's node_modules, it made the same false claim, because it only looked for _npx in the path. It now finds the global copy via `npm root -g`/zerion-cli and compares real paths: if this process is the global copy (including an npm linked checkout) it skips, otherwise it installs or upgrades.
  • Upgrades old global installs. A global copy older than the running one is upgraded to that exact version instead of being kept.
  • Detects Codex correctly. It now uses CODEX_SANDBOX / CODEX_CI / CODEX_THREAD_ID, the markers @vercel/detect-agent uses and that npx skills relies on, instead of CODEX_HOME. CODEX_HOME still relocates ~/.codex.
  • Fixes the Gemini id (bug fix). The id is now gemini-cli. skills add -a gemini was rejected as an invalid agent.
  • Installs the skill for every agent on the machine, with the running agent first. Before, it picked the first of ~/.claude, ~/.cursor, ~/.codex, so a prompt pasted into Codex on a machine that also had Claude Code installed the skill into Claude Code only.
  • Still never blocks without a TTY. This is deliberate. Waiting up to 5 minutes for browser login could hit the agent's command timeout (Claude Code's default is 2 minutes), and because auth runs before the skills step, a killed init would leave no skill installed. Instead:
    • Inside an agent, init prints zerion login --browser with instructions to run it in the background and show the user the URL.
    • The auth step's JSON gets "next": "zerion login --browser".
    • The key fallback hint now says zerion login --browser too, because plain zerion login exits with not_interactive when there's no TTY.
  • Points the agent at SKILL.md. init prints the path of the skill shipped in the package and adds skill to the JSON. Agents load skills at session start, so a skill installed mid-session may not be visible yet.

Skill

  • SKILL.md:
    • A setup check that probes first and skips finished steps: version, login, registering a read-only wallet, and a note on sandboxed agents.
    • "Rules for every on-chain task": exact on-chain balances, gas vs. proceeds (stop and ask when gas wins), swaps/bridges/transfers only through swap/bridge/send (never hand-encoded), signing-link handling, scope discipline.
    • Routing for requests that combine several capabilities.
    • Fixes the claim that every trade needs an agent token. Read-only wallets sign in the web app and need none.
  • New capabilities/defi-exit.md: find positions, pick the way out (including selling the receipt token vs. waiting out a cooldown), research the contract (ABIs from Sourcify, plus viem from the CLI install), build the protocol step, and the "after the exit" review that stops and waits.
  • capabilities/bundle.md:
    • A "Hand-built groups" section for withdraw/claim steps, with the envelope field rules. I checked them against parsePreparedGroup and decideBundleRoute; route alone decides the signing path.
    • bundle ignores --wallet and takes the signer from each envelope. The docs now say so instead of telling agents to pass it.

--quote on swap and bridge

Prints the price and stops, skipping the balance check. An exit's gas-vs-proceeds check has to price tokens that a claim or unstake hasn't delivered yet, and --prepare rejects those with insufficient_funds. In an agent test run, the agent worked around this by importing CLI internals and quoting with another holder's address.

  • With a short balance, the result is marked enoughBalance: false with a note that the price is indicative, since the API leaves out network gas and the protocol fee until the tokens are there.
  • --quote and --prepare are rejected together (conflicting_flags).
  • The insufficient_funds error now suggests --quote.
  • bridge … --quote lists every route and its price, then stops. Plain bridge lists routes only when there are several, executes a single one, and refuses a short balance. --quote always lists, never executes, and prices tokens the wallet doesn't hold yet. An agent in a test run tried bridge --quote before it existed and the flag was silently ignored. The route listing and balance check moved into tested helpers; plain bridge behaves as before.
  • SKILL.md, trading.md and defi-exit.md use --quote for pricing, and the CLI help lists it for both commands.

Signing links are one-time

SKILL.md now says a link belongs to the run that printed it. Once that run ends, the agent shouldn't re-share it: the CLI no longer receives the result, and because the transaction travels inside the link, signing it and a fresh one could execute the action twice. The agent checks the chain first, then generates a new link if nothing went through. An agent in the same test run had re-shared an expired link.

Chain ids and public RPCs in zerion chains; ABIs from Sourcify

  • zerion chains prints each EVM chain's chainId (decimal) and chainIdHex, taken from the external_id the chains API already returns. Agents need the number for ABI lookups and the hex form for a hand-built envelope's chainId. Solana gets none. The table view gains a Chain ID column.
  • zerion chains --json also prints each chain's rpcUrls: the public HTTPS endpoints from the chains API's rpc.public_servers_url, in catalog order, without WebSocket URLs. None needs a key. In a keyless test, 134 of the 177 listed HTTPS URLs answered with the right chain ID, covering 55 of 65 EVM chains. The <chain>.drpc.org pattern the docs used before reached 46, all of which the catalog also covers. SKILL.md now says to use $ETH_RPC_URL if set, otherwise try rpcUrls in order, skipping any that errors or answers with a different chain ID, and to say so if none answers.
  • defi-exit.md gets ABIs from Sourcify's keyless v2 API by chain ID, following proxyResolution to implementation contracts. Blockscout's <chain>.blockscout.com didn't exist for many chains we tried, and its API started returning Cloudflare challenges after a few requests; Sourcify found all 16 DeFi contracts tested across Ethereum and Base. Etherscan's V2 API is a fallback only when the user already has ETHERSCAN_API_KEY set, since it rejects keyless requests. With no verified ABI, agents stop rather than scrape explorer pages or guess.

Telling users which wallet signs

When sharing a signing link, SKILL.md now has the agent tell the user what to expect. app.zerion.io shows the full review first: signing address, balance changes, fee, and a security check. If no wallet is connected, it then asks them to connect one, and it must be the wallet that owns the signing address. I confirmed the page's behavior in a browser with no wallet connected.

npx zerion-cli@latest init in docs and help

A bare npx zerion-cli runs whatever global copy is installed. With zerion-cli 1.6.0 installed globally, bare npx zerion-cli --version printed 1.6.0, and @latest printed 1.9.1. Because init installs globally, returning users would otherwise never run new code. The README, the router's setup.init help, the init HELP examples and two partner docs now use @latest.

Testing

  • Unit tests: all 513 pass on Node 20 (after merging main), including 17 new init tests, 6 for --quote on swap and bridge, and 5 for chain IDs and RPC URLs:
    • agent vs. non-agent login hints
    • the skill path
    • install / upgrade / skip from npx, from a project's node_modules, and from the global copy itself, using a fake npm
    • version comparison (full semver precedence, including next prereleases)
    • agent detection and skill targets
    • Note: npm test's ** pattern needs Node 21+; on 20, list the test files explicitly. CI uses Node 24.
  • End to end: ran npm pack, then npx -y file:<tgz> init -y with a temp HOME containing ~/.claude and ~/.codex, CODEX_THREAD_ID set and no API key. Result:
    • exit 0
    • global install correctly skipped (same version)
    • the login hint and next were printed
    • the skill was installed to ~/.agents/skills/zerion ("universal: Codex") and linked into Claude Code
    • the SKILL.md path was printed

Reviewer notes

  • Merge this, then release right away. npx skills add installs the skill from main, but the CLI comes from npm. The skill now requires CLI 1.11.0 (the release this lands in) before using --quote. 1.10.0 silently ignores --quote and runs the swap or bridge: with a read-only wallet that produces a signing link, and with an agent token it would execute. Until 1.11.0 is on npm, agents following the new skill stop at the version check, which is safe but blocks them, so merge the release PR soon after this one.

  • JSON output change: the skills step's JSON now reports agents: [...] instead of agent.

  • Not in this PR: three CLI follow-ups that would remove more rules from the docs:

    • bundle choosing the web-app route on its own for read-only wallets
    • a --no-wait flag that prints the signing link and exits
    • bundle refusing hand-built swap/approve/transfer calls

🤖 Generated with Claude Code

Agent prompts that use the CLI currently have to spell out setup and the
rules for on-chain work. This moves that knowledge into the CLI and the
skill, so a prompt can be the user's request plus
`npx -y zerion-cli@latest init -y`, and work the same in Claude Code,
Codex, Cursor and Gemini CLI.

init, when run by an agent:
- Installs the CLI globally from npx. Under npx the temp copy is first on
  PATH, so the old `zerion --version` check always passed and init never
  installed anything. It now reads the global copy via `npm root -g`.
- Upgrades a global copy older than the running one, instead of skipping.
- Detects Codex by CODEX_SANDBOX / CODEX_CI / CODEX_THREAD_ID (as
  @vercel/detect-agent does), not CODEX_HOME, and uses `gemini-cli`, the
  id `skills add -a` accepts.
- Installs the skill for every agent on the machine, running agent first.
- Still never blocks on browser login without a TTY. Inside an agent it
  names `zerion login --browser` (also as `next` in the JSON) for the
  agent to run in the background; the key fallback hint now says
  `--browser` too, since plain `zerion login` needs a TTY.
- Prints the packaged SKILL.md path (and adds `skill` to the JSON), for
  agents that only load skills at session start.

Skill:
- SKILL.md: a setup check that skips finished steps, rules for every
  on-chain task (exact balances, gas vs. proceeds, swaps only via
  swap/bridge/send, signing-link handling, scope), routing for compound
  requests. Fixes the claim that every trade needs an agent token.
- New capabilities/defi-exit.md for withdraw/unstake/redeem/claim/repay.
- bundle.md: rules for hand-built groups (protocol legs only).

Docs and help use `npx zerion-cli@latest init`: a bare name runs whatever
global copy is installed, so returning users would never get new code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@evgeth
evgeth force-pushed the claude/zerion-ai-version-update-68a699 branch from f161d79 to 5bef2f2 Compare October 3, 2026 08:56
evgeth and others added 8 commits October 4, 2026 17:13
…emver

- defi-exit.md, SKILL.md: the user's request sets the scope, and CLI
  reads only confirm what exists inside it. `positions --defi --chain`
  returns every protocol on the chain, so treating the request as a hint
  could exit positions the user never asked about. Those now only come
  up as suggestions after the exit.
- A swap that spends tokens an earlier leg brings in can't share its
  bundle: `swap --prepare` rejects an insufficient balance, and `bundle`
  checks current balances, not future inflows. The protocol leg goes
  alone unless the wallet already holds enough to cover the swap.
- init: isOlderVersion follows semver precedence instead of dropping
  prerelease tags, so a `next` build upgrades to its release and to newer
  `next` builds. A release is still never replaced by its own prerelease.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found by running an agent against the new skill end to end.

- `zerion swap … --quote` prints the price and stops, skipping the balance
  check. An exit's gas-vs-proceeds check needs to price tokens a claim or
  unstake hasn't delivered yet, and `--prepare` rejects those, so agents
  improvised by importing CLI internals and quoting with another holder's
  address. When the balance is short the price is marked indicative
  (`enoughBalance: false`, no network gas or protocol fee), and the
  insufficient-funds error now points to `--quote`. SKILL.md, trading.md
  and defi-exit.md use it for pricing.
- SKILL.md: a signing link belongs to the run that printed it. Once that
  run ends, don't re-share it: the CLI no longer gets the result, and the
  transaction rides in the link, so signing it and a fresh one could
  execute twice. Check the chain, then generate a new link if needed.
- init: decide "already installed globally" by comparing this package's
  real path with the global install, not by looking for `_npx` in argv.
  Run from a project's node_modules, init used to claim a global install
  that didn't exist; it now installs or upgrades one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In an agent test run, the agent wrote its viem script to a temp file:
`require('viem')` failed there (resolution follows the script's location,
not the cwd), and copying the script into the CLI's own folder failed too
(the package is ESM, so `.js` files there can't `require`). Point agents
at `NODE_PATH="$(npm root -g)/zerion-cli/node_modules" node script.js`
instead of writing files into the install.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `zerion bridge … --quote` lists every route and its price, then stops.
  Plain `bridge` lists routes only when there are several and executes a
  single one, and it refuses a short balance; `--quote` always lists,
  never executes, and prices tokens the wallet doesn't hold yet (marked
  `enoughBalance: false`, indicative). An agent in a test run reached for
  `bridge --quote` and the flag was silently ignored. The insufficient-
  funds error suggests it, and `--quote --prepare` is rejected. The
  listing and the balance check move into tested helpers; plain `bridge`
  behaves as before.
- SKILL.md: when sharing a signing link, tell the user what to expect —
  app.zerion.io shows the full review first, then asks them to connect a
  wallet if none is connected, and it must be the wallet that owns the
  signing address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts were doc text where both sides edited the same lines:
- SKILL.md: kept this branch's setup check and took main's new Node
  minimum (22.12, or 20.19 on Node 20). Added one setup-check line for
  main's new `unsupported_node` error: tell the user to upgrade instead of
  patching dependencies.
- capabilities/bundle.md: took main's removal of the per-file Node
  minimum (stated only in SKILL.md now) and kept this branch's corrected
  auth sentence (read-only wallets need no agent token).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skill is installed from main while the CLI comes from npm releases,
so an agent can read these docs on a CLI that predates `--quote`.
1.10.0 silently ignores the flag and runs the swap or bridge instead of
pricing it: a read-only wallet gets a signing link, and a wallet with an
agent token would execute immediately. Raise the setup check's minimum
from 1.7.0 to 1.11.0, the release this branch lands in, so an older CLI
is upgraded through `init` first, and note the requirement next to
`--quote` in trading.md and defi-exit.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ckscout

- `zerion chains` prints each EVM chain's `chainId` (decimal) and
  `chainIdHex`, from the `external_id` the chains API already returns.
  Agents need the number for ABI lookups and the hex form for a hand-built
  envelope's `chainId`. Solana gets none (its external id isn't an EVM
  chain id). The table view gains a Chain ID column.
- defi-exit.md: fetch ABIs from Sourcify's keyless v2 API by chain id,
  following `proxyResolution` to implementations. The
  `<chain>.blockscout.com` pattern didn't exist for many chains and its
  API started returning Cloudflare challenges under light use; Sourcify
  found all 16 DeFi contracts tested across Ethereum and Base. Etherscan's
  V2 API is a fallback only when the user has ETHERSCAN_API_KEY set, since
  it rejects keyless requests. No verified ABI means stop, not scrape or
  guess.
- bundle.md and SKILL.md point at the new chain id fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`zerion chains --json` now prints each chain's `rpcUrls`: the public HTTPS
endpoints listed in the chains API's `rpc.public_servers_url`, in catalog
order, WebSocket URLs dropped. None needs a key. In a keyless test, 134 of
the 177 listed HTTPS URLs answered with the right chain id, covering 55 of
65 EVM chains, a superset of the 46 that the `<chain>.drpc.org` naming
pattern reached.

SKILL.md: for exact balances and test calls, use $ETH_RPC_URL if set,
otherwise try `rpcUrls` in order, skipping any that errors or answers with
a different chain id; if none answers, say so instead of picking an RPC.
This replaces the hard-coded dRPC pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@evgeth
evgeth merged commit 488cdd0 into main Oct 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant