Portable Agent Skills for the AlphaFi protocols on Sui — AlphaLend (lending),
AlphaFi vaults and stSUI (liquid staking) — built over the public
@alphafi/* JavaScript SDKs. Version 1.0.0 (see
CHANGELOG.md).
Four skills in the Agent Skills format
(skills/<name>/SKILL.md + references/ + scripts/), loadable by Claude
Code, opencode and Kilo, covering 14 journeys with seventeen .mjs
scripts:
| Skill | Wraps | Journeys |
|---|---|---|
alphafi-sui-setup |
@mysten/sui (shared runtime) |
environment probe (check-env.mjs), the shared execution runtime and safety layer every other script imports, the portfolio-composition recipe |
alphafi-alphalend |
@alphafi/alphalend-sdk |
R2 markets, R1 positions, R3 health factor, W1 supply, W2 withdraw, W3 borrow, W4 repay, W5 claim rewards |
alphafi-vaults |
@alphafi/alphafi-sdk |
R4 vault pools, R1 vault positions, W6 deposit, W7 withdraw (incl. the AlphaVault two-step initiate/claim) |
alphafi-stsui |
@alphafi/stsui-sdk (called direct) |
R5 stSUI rate/APR/APY/TVL/fees, R1 stSUI balances, W8 stake, W9 instant unstake |
Every write script builds an unsigned transaction with the SDK, simulates it
over gRPC, runs guardrails and prints a dry-run summary; signing is an
explicit opt-in (see Execution modes). Every script
prints one JSON envelope with --json and uses a fixed exit-code contract
(skills/alphafi-sui-setup/references/output-contract.md).
v1 non-goals are listed at the end (Non-goals): no cross-SDK script, no swaps/looping/zaps, no MCP server, no key management.
- Node.js ≥ 22.14 (tested on 24.x) and npm. Nothing under
skills/runs without the rootnode_modules—npm install(ornpm ci) once in the repository root is mandatory on every install path. - Network access, per skill (the same list every skill's
compatibilityfield andskills/alphafi-sui-setup/SKILL.mdcarry — there is no degraded mode when a host is blocked; reads reportstatus: "unknown"and exit 4, writes fail):
| Skill | Hosts |
|---|---|
alphafi-sui-setup |
Sui mainnet gRPC fullnode (fullnode.mainnet.sui.io:443) |
alphafi-alphalend |
gRPC fullnode, Sui GraphQL (graphql.mainnet.sui.io), api.alphalend.xyz (reads + Lazer price updates for withdraw/borrow), api.alphafi.xyz, ws.stsui.com (best-effort stSUI staking APR on markets.mjs; unreachable ⇒ stakingApr 0) |
alphafi-vaults |
gRPC fullnode, Sui GraphQL, api.alphalend.xyz, api.alphafi.xyz, hermes.pyth.network (only for Lending/Navi deposits) |
alphafi-stsui |
gRPC fullnode, Sui GraphQL, api.alphafi.xyz (USD TVL only) |
- Mainnet supported, testnet best-effort:
SUI_NETWORK=testnetis accepted (with an explicitSUI_RPC_URL) and mapped into the SDKs, but v1 targets mainnet and testnet behaviour is unverified. - The RPC endpoint is gRPC-only: public Sui fullnodes no longer answer
JSON-RPC, and a JSON-RPC URL in
SUI_RPC_URLmakes every script exit 3E_RPC_NOT_GRPC.
Run node skills/alphafi-sui-setup/scripts/check-env.mjs after installing
to probe the endpoint and print the host list.
Three recipes. The first works on every harness and is the one to use whenever the scripts must run; the other two are discovery paths.
git clone https://github.com/AlphaFiTech/alphafi-skills.git
cd alphafi-skills && npm ci
# in the project that should see the skills:
for s in alphafi-sui-setup alphafi-alphalend alphafi-vaults alphafi-stsui; do
ln -s "$PWD/skills/$s" "<project>/.claude/skills/$s"
done.claude/skills/ is read by all three harnesses. Alternates:
.opencode/skills/ (opencode), .kilo/skills/ (Kilo), .agents/skills/
(opencode and Kilo), and the global ~/.claude/skills/ for every project.
The scripts locate the repository root and its node_modules from their
own real path, so a symlinked skill directory works from any working
directory. Skills surface as alphafi-<x>.
Discovery-compatible: the four skill directories are found by the Kilo
catalogue layout. In the T46 spike (npx skills add <local clone> --agent claude-code -y, skills CLI of 2026-08-30) the CLI copied each skill
directory into .claude/skills/<name> (reported (copied)) and recorded
it in skills-lock.json. A copied skill directory has no package.json
marker above it and no node_modules, so its scripts stop with
E_REPO_ROOT (exit 3) — the copied SKILL.md and references/ still
work as instructions. To run the scripts, use recipe 1 (a clone with
npm ci); should your harness receive symlinks instead, the scripts
resolve the clone through them and work once npm ci has run there.
/plugin marketplace add AlphaFiTech/alphafi-skills
/plugin install alphafi-skills@alphafi-skills
Then install the dependencies inside the installed plugin directory —
Claude Code may not run npm install for a plugin (older versions leave the
cache copy with package.json but no node_modules; 2.1.251 installed them
itself), so run it anyway — it is idempotent — and every script refuses to run
until the directory has its node_modules:
cd ~/.claude/plugins/cache/alphafi-skills/alphafi-skills/<version> # the versioned cache dir
npm installSkills surface as alphafi-skills:alphafi-<x>. Optional Claude Code-only
bootstrap: you may instead install the dependencies once into the plugin's
persistent data directory (npm install --prefix "${CLAUDE_PLUGIN_DATA}")
and symlink ${CLAUDE_PLUGIN_DATA}/node_modules into the cache directory
after each plugin update — the scripts only require that
<plugin dir>/node_modules/@mysten/sui resolves. Neither the plugin path
nor the symlink path writes into the other's files.
All scripts read the same variables (--help on any script prints this
table; the full contract is skills/alphafi-sui-setup/references/environment.md).
| Variable | Semantics |
|---|---|
SUI_NETWORK |
mainnet (default) or testnet. Any other value → exit 2 E_USAGE. testnet maps to "testnet" for AlphalendClient/AlphaFiSDK and setConf("testing") for stsui-sdk; documented as unverified/best-effort (REQ-ASK-050). |
SUI_RPC_URL |
gRPC endpoint. Default when SUI_NETWORK=mainnet: https://fullnode.mainnet.sui.io:443. No default for testnet — absence with SUI_NETWORK=testnet → exit 2 E_USAGE: SUI_RPC_URL is required when SUI_NETWORK=testnet, before any network call. The setup skill and README state that JSON-RPC URLs no longer work on public fullnodes. |
SUI_GRAPHQL_URL |
Optional. Propagated to AlphalendClient(network, graphqlUrl), AlphaFiSDK({graphqlUrl}), stsui setGraphQLUrl() (REQ-ASK-044). Not used by the script client. |
ALPHAFI_API_URL |
Optional. Propagated to AlphaFiSDK({apiBaseUrl}) only — the alphalend GraphQL URL is hard-coded in the SDK (G-A4) and references/sdk-knobs.md says so. |
SUI_ADDRESS |
Sender when --address is absent. In --execute mode the key-derived address wins and any explicit address must equal it (SPEC-ASK-003 §D3). |
SUI_PRIVATE_KEY |
bech32 suiprivkey…; read only inside keys.mjs in --execute mode; never copied into RuntimeEnv (SPEC-ASK-003 §D2). |
SUI_RPC_TOKEN |
Passed through to the SDK constructors as grpcToken (the HEAD-derived SDKs' knob); accepted but inert at the v1 pins, never printed (REQ-ASK-044; SDK-release dependency per SPEC-ASK-007 §D3). |
SUI_RPC_URL is a gRPC endpoint and governs the script client only
(build, simulate, execute, balances, the endpoint probe); at the pinned SDK
versions the SDKs keep their own transports (see
Known dependency issues). Sender resolution:
--address > SUI_ADDRESS; in --execute mode the key-derived address is
used when neither is given. Dry-run and emit-tx never read the key.
Every write script has exactly three mutually exclusive modes; passing two mode flags is a usage error (exit 2) before any network call.
| Mode | What happens | Key needed |
|---|---|---|
--dry-run (default when no mode flag is given) |
build → simulateTransaction → guardrails → dry-run summary; nothing is signed, written or executed |
no |
--emit-tx |
dry-run first; then write the unsigned transaction bytes (txBytes and txKindBytes) to --out or ./alphafi-tx-<journey>-<UTC ts>.json for an external signer, with a --gas-margin (default 20 %) budget |
no (a set key is ignored with W_KEY_IGNORED_IN_EMIT) |
--execute |
dry-run first; then the confirmation gate (--yes or a typed yes on a TTY); then a second simulation, sign in-process with SUI_PRIVATE_KEY and execute |
yes, Ed25519 only |
Recommendation: an autonomous agent acting for a human should use
--emit-tx and hand the bytes to the human's wallet or signer; --execute
with an in-process key is an opt-in for an operator at their own
terminal. Even when a key is present the default is --dry-run. A failed
simulation (exit 6) or a guardrail refusal (exit 5) stops every mode before
anything is signed, written or executed. The full sequence, the emitted
file format and the confirmation gate are in
skills/alphafi-sui-setup/references/execution-modes.md. Emitted bytes
usually pin one gas coin (gasPaymentPinned: true — observed for every
sender that owns Coin<SUI> objects; only address-balance-only senders get
an unpinned payment), so any other transaction from the sender invalidates
them: sign promptly or re-emit.
In brief — the normative rules, every guardrail default and the refusal
codes live in skills/alphafi-sui-setup/SKILL.md and its
references/safety-rules.md:
- The private key is read from
SUI_PRIVATE_KEYonly, only in--execute, only after the dry-run passed; no script accepts key material as a flag; every output is redacted; the key's address must equal the address the transaction was built for. - Guardrails run in every mode, refuse (never clamp) and report every
reason: simulation status, sender outflow bounded by the expected debits
plus gas, no debit from another address, AlphaLend projected health
factor ≥
--min-hf(default1.2) on withdraw/borrow, expected-vs-simulated deviation ≤--max-deviation-bps(10 bps stake/unstake, 100 bps vault deposit/withdraw), gas budget ≤--max-gas(default0.1SUI). - An agent must never, on its own initiative, add
--yes, move a key into arguments/files/transcripts, raise a limit flag to force a pass, or chain a dry-run into--executewithout the human confirming the exact action. - Proceeds of withdraw/borrow/claim/unstake land in the address balance
(
0x2::coin::send_funds), not as a newCoinobject — check the total balance via gRPCgetBalance.
Filled by the release-evidence procedure (docs/evidence/2026-08-30-portability.md,
Claude Code CLI 2.1.251); rows not exercised locally stay documented, not
verified.
| Harness / path | Status | Notes |
|---|---|---|
Claude Code — symlinked .claude/skills |
verified | primary recipe; symlinks followed, the four skills list as alphafi-<x> |
| Claude Code — plugin marketplace | verified | versioned cache dir …/alphafi-skills/<version>/, skills list as alphafi-skills:alphafi-<x>; 2.1.251 installed node_modules itself — run npm install there anyway (idempotent; older versions do not) |
opencode — .claude/skills symlink |
verified (opencode 1.18.25) | also .opencode/skills/ (both verified: opencode debug skill lists the four alphafi-* skills from either directory), .agents/skills/ |
Kilo — .claude/skills symlink |
documented-not-verified | also .kilo/skills/, .agents/skills/; npx skills add copies; CLI not installed on the evidence machine |
| Mysten-partner deploy agent | documented-not-verified | unknown harness; the primary recipe and host list are the stated default |
v1 pins the published @alphafi/alphalend-sdk 4.1.0, @alphafi/alphafi-sdk 2.2.0, @alphafi/stsui-sdk 2.0.3 and @mysten/sui 2.27.1 (exact). Until
the HEAD-derived SDK releases land (tracked in
docs/sdk-gaps.md, G-A7 / G-V11):
- No
grpcUrl/grpcTokenknobs in the pinned alphalend and vaults SDKs —SUI_RPC_URLgoverns only the script client; the SDKs read through their own default transports (SUI_GRAPHQL_URLis the one override that reaches all three). Functional; may be flagged by supply-chain scanners. Seeskills/alphafi-sui-setup/references/sdk-knobs.md. - Nested
@alphafi/alphalend-sdk@3.1.1inside the pinned@alphafi/alphafi-sdk2.2.0, which pulls two@mysten/sui@1.45.2copies and the deprecated@mysten/sui.jsinto the tree. Functional; may be flagged by supply-chain scanners. npm lsreports an invalid peer (the nested tree's@mysten/suipeer range). Functional; may be flagged by supply-chain scanners.
Re-pinning to the fixed releases is a version bump with a CHANGELOG.md
entry (the procedure is in the setup skill's sdk-knobs.md).
Start with node skills/alphafi-sui-setup/scripts/check-env.mjs (add
--json). Entries are keyed by the exact error codes the scripts emit;
the quoted text is the runtime message template.
E_DEPS_MISSING(exit 3) — the rootnode_modulesis missing (fresh clone, copied skill directory, or a plugin install without thenpm installstep). Message:E_DEPS_MISSING: dependencies not installed; run "npm install" (or "npm ci") in <root>— run exactly that in the named directory.E_REPO_ROOT(exit 3) — the script is not inside a clone or the installed plugin directory (typically a copied skill directory, recipe 2):E_REPO_ROOT: could not find the alphafi-skills repository root above <start>; run the scripts from a clone or the installed plugin directory.E_RPC_NOT_GRPC(exit 3) —SUI_RPC_URLpoints at a JSON-RPC or plain HTTP endpoint. Message:E_RPC_NOT_GRPC: <url> answered as JSON-RPC or HTTP; SUI_RPC_URL must be a gRPC endpoint (public fullnode JSON-RPC is deprecated) — <detail>— setSUI_RPC_URLto a gRPC endpoint (defaulthttps://fullnode.mainnet.sui.io:443).E_RPC_UNREACHABLE(exit 3) — DNS, connection or timeout failure onSUI_RPC_URL. Messages:E_RPC_UNREACHABLE: <url> could not be reached (<message>)orE_RPC_UNREACHABLE: <url> probe failed (<message>)— check the URL, your network and any proxy.E_UPSTREAM(exit 4) — an AlphaFi API, Sui GraphQL, Lazer or Hermes host gave no usable answer (blocked host, outage, or an SDK returningundefined/"0"). Messages:E_UPSTREAM: <host> gave no usable answer for <name>orE_UPSTREAM: unexpected failure — <cause>— the message names the dependency and carries the cause; retry later and never treat the affected value as zero. The blocked-host list is in Prerequisites.E_USAGE: SUI_RPC_URL is required when SUI_NETWORK=testnet(exit 2) — testnet has no default endpoint.E_CONFIRM_REQUIRED(exit 7) —--executewithout a TTY and without--yes; the human runs the printed--execute --yescommand themselves.E_GUARD_<NAME>(exit 5) /E_SIM_FAILED(exit 6) — the action is not safe as specified; read the summary, do not raise limits to pass.
v1 deliberately does not include:
- a cross-SDK "portfolio" script (compose the three
--jsonreads per the setup skill's recipe instead); - swaps, zaps, looping / leveraged strategies, LP-collateral journeys;
- an MCP server or any harness-specific runtime — the skills are files;
- key management: no keystore, no wallet integration, no key generation;
--executeis an opt-in with a key the operator already holds; - fixing the SDK gaps recorded in docs/sdk-gaps.md — they are handed to the SDK workstream, and this repository only works around them;
- testnet verification (best-effort only) and any degraded mode when an AlphaFi host is unreachable.
npm test— the offline unit suite (node:test, SDK and client stubbed; no network);npm run test:coverageadds the coverage summary and writescoverage/lcov.info;npm run lint— ESLint, Prettier andtsc --noEmitover the JSDoc-typed.mjsfiles.- Repo checks (also run by CI):
npm run validate:skills,check:frontmatter,check:skill-size,check:sdk-map,validate:plugin(theclaudeCLI when onPATH, else an offline JSON-schema check — the log says which),check:test-names(every SPEC-ASK-007 matrix row and REQ id has a named test; with--lcov coverage/lcov.infoeverylib/module is in the coverage report) andcheck:versions(the lockstep; on a pull request that touchesskills/all five version fields must change together with a newCHANGELOG.mdentry). - CI (
.github/workflows/ci.yml, every push and pull request, GitHub-hosted Ubuntu, Node 22):install,lint,unit(behind an HTTP-proxy sinkhole so the offline suite cannot reach the network),skills,plugin,versionsandsecrets(gitleaks over the whole history with.gitleaks.toml, the same rules as the pre-commit hook). CI holds no secrets, never signs and never executes a transaction. npm run test:integration— on demand, from a networked machine, with no key in the environment (the suite aborts if one is set): read-only calls andsimulateTransactiondry-runs against mainnet public endpoints for the fixed public address intest/integration/config.jsonand a funded public sender selected at run time; about 1–2 minutes; a script over 60 s warns and over 120 s fails; setINTEGRATION_LOG=<file>to save the run as JSON. If your environment routes egress through a proxy, setNODE_USE_ENV_PROXY=1so Node'sfetchhonourshttps_proxy; if egress is denied altogether, dispatch theintegrationworkflow instead (gh workflow run integration.yml) and read the run log — it is the same suite on a GitHub-hosted runner. Runs are recorded underdocs/evidence/.docs/evidence/scripts/— the read-only probe scripts behind the recorded evidence (hf-compare.mjsfor the health-factor cross-check,gas-probe.mjsfor thegasData.paymenttest); they need no key and are run from the repository root (node docs/evidence/scripts/<name>.mjs …).
MIT — see LICENSE.