diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 6a15af08c..22a60bb73 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -58,6 +58,7 @@ Anything after the `--` double dash (the "slop") is parsed as arguments to the c - `completion` — Print shell completion code for the specified shell - `cache` — Cache for transactions and contract specs - `version` — Print version information +- `skill` — Print an AI-agent skill guide for using the Stellar CLI - `plugin` — The subcommand for CLI plugins - `ledger` — Fetch ledger information - `message` — Sign and verify arbitrary messages using SEP-53 @@ -4860,6 +4861,14 @@ Print version information - `--only-version-major` — Print only the major version - `--only-commit` — Print only the commit sha +## `stellar skill` + +Print an AI-agent skill guide for using the Stellar CLI + +Outputs a Markdown document describing how to use the Stellar CLI idiomatically. It is meant to be read by AI coding agents (or pasted into their instructions) so they follow the CLI's conventions: using named networks, identities, and contract aliases instead of raw RPC URLs, secret keys, and hard-coded contract ids. + +**Usage:** `stellar skill` + ## `stellar plugin` The subcommand for CLI plugins diff --git a/cmd/crates/soroban-test/tests/it/main.rs b/cmd/crates/soroban-test/tests/it/main.rs index 61b62b6b6..3495e2ac2 100644 --- a/cmd/crates/soroban-test/tests/it/main.rs +++ b/cmd/crates/soroban-test/tests/it/main.rs @@ -12,6 +12,7 @@ mod log; mod message; mod plugin; mod rpc_provider; +mod skill; mod strkey; mod tx; mod util; diff --git a/cmd/crates/soroban-test/tests/it/skill.rs b/cmd/crates/soroban-test/tests/it/skill.rs new file mode 100644 index 000000000..e831986e0 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/skill.rs @@ -0,0 +1,16 @@ +use soroban_test::TestEnv; + +#[test] +fn skill_prints_agent_guide() { + let sandbox = TestEnv::default(); + sandbox + .new_assert_cmd("skill") + .assert() + .success() + .stdout(predicates::str::contains("network use")) + .stdout(predicates::str::contains("--alias")) + .stdout(predicates::str::contains("--id")) + .stdout(predicates::str::contains("contract build")) + .stdout(predicates::str::contains("--send=no")) + .stdout(predicates::str::contains("container use")); +} diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 4d82ce089..178580b81 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -20,6 +20,7 @@ pub mod ledger; pub mod message; pub mod network; pub mod plugin; +pub mod skill; pub mod snapshot; pub mod token; pub mod tx; @@ -121,6 +122,7 @@ impl Root { Cmd::Container(container) => container.run(&self.global_args).await?, Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?, Cmd::Version(version) => version.run(), + Cmd::Skill(skill) => skill.run(), Cmd::Keys(id) => id.run(&self.global_args).await?, Cmd::Token(token) => token.run(&self.global_args).await?, Cmd::Tx(tx) => tx.run(&self.global_args).await?, @@ -214,6 +216,10 @@ pub enum Cmd { /// Print version information Version(version::Cmd), + /// Print an AI-agent skill guide for using the Stellar CLI + #[command(long_about = skill::LONG_ABOUT)] + Skill(skill::Cmd), + /// The subcommand for CLI plugins #[command(subcommand)] Plugin(plugin::Cmd), diff --git a/cmd/soroban-cli/src/commands/skill/SKILL.md b/cmd/soroban-cli/src/commands/skill/SKILL.md new file mode 100644 index 000000000..d09a2687e --- /dev/null +++ b/cmd/soroban-cli/src/commands/skill/SKILL.md @@ -0,0 +1,126 @@ +# Stellar CLI skill + +A guide for AI agents driving the `stellar` CLI. Follow these conventions so commands stay short, reproducible, and free of hard-coded secrets and ids. + +## Overview + +The Stellar CLI (`stellar`) manages keys and accounts, builds and deploys smart contracts, deploys asset contracts, streams events, and encodes/decodes XDR. + +- Every command has help: `stellar --help`. +- List a contract's functions and their arguments on the fly: + + stellar contract invoke --id -- --help + + Anything after `--` is parsed against the contract's own schema. + +## Networks: prefer `stellar network use` + +Set a default network once instead of repeating `--network`, `--rpc-url`, and `--network-passphrase` on every command: + + stellar network use testnet + +After this, other commands use that network by default and you can omit the network flags entirely. + +- List configured networks: `stellar network ls` +- Add a custom network: `stellar network add --rpc-url --network-passphrase ` +- Clear the default: `stellar network unset` + +Only pass `--network ` explicitly when a single command needs to target a different network than the default. + +## Identities: prefer `stellar keys use`, never raw secret keys + +Create named identities and select a default with `stellar keys use`, just like networks. Do not paste raw `S...` secret seeds on the command line. + + stellar keys generate alice --fund # generates and funds on testnet + stellar keys use alice # sign and pay as alice by default + +After `stellar keys use`, other commands sign and pay with that identity, so you can omit the source flag entirely. + +- List identities: `stellar keys ls` +- Show an address: `stellar keys address alice` +- Clear the default: `stellar keys unset` + +Only pass `--source ` when a single command needs to override the default identity. + +## Contracts: use aliases, don't stash contract ids in env vars + +When deploying, assign an alias with `--alias` so the CLI persists the contract id for you: + + stellar contract deploy \ + --wasm target/wasm32v1-none/release/hello.wasm \ + --alias hello + +Then reference the contract by its alias with `--id` — the CLI resolves the alias to the real contract id automatically: + + stellar contract invoke --id hello -- hello --to world + +Do **not** capture the deployed contract id into a shell variable or `.env` file and thread it through later commands. Aliases are stored per-network, survive across sessions, and keep commands readable. + +- Manage aliases: `stellar contract alias ls`, `stellar contract alias add`, `stellar contract alias rm` +- Asset contracts accept `--alias` too: `stellar contract asset deploy --asset --alias ` + +## Building and deploying from source + +Scaffold, build, and deploy a contract project: + + stellar contract init my-project # scaffold a Cargo workspace + stellar contract build # compile to target/wasm32v1-none/release/.wasm + +Inside a contract project you can deploy without pointing at a `.wasm` — the CLI builds it for you: + + stellar contract deploy --alias counter -- --admin alice + +Constructor arguments go after the `--`, passed as `--arg-name value`; they are forwarded to the contract's `__constructor`. + +- Deploy a prebuilt file: `stellar contract deploy --wasm --alias ` +- Upload Wasm without instantiating a contract (e.g. for factories or upgrades): `stellar contract upload` (the older `install` is a deprecated alias). + +## Discovering a contract's interface + +Besides `stellar contract invoke --id -- --help`, you can inspect a deployed contract's functions and types without invoking it: + + stellar contract info interface --id + +`--id` accepts a contract id or an alias and works across contract commands (it's the short form of `--contract-id`) — prefer it everywhere. + +## Reading data and parsing output + +- For view/query calls, use `--send=no` to simulate without submitting a transaction or paying fees: + + stellar contract invoke --id counter --send=no -- get_count + +- A function's return value is printed to **stdout** as JSON; logs and diagnostics go to **stderr**. When capturing output for parsing, add `-q`/`--quiet` to silence logs and keep stdout clean. + +## Data lifecycle and TTL (archival) + +Contract storage is rented and each entry has a time-to-live (TTL). Behavior on expiry depends on the storage type: contract code, instance, and persistent entries are **archived** when their TTL runs out and must be restored before use, whereas **temporary** entries are deleted permanently and cannot be restored. + +- `stellar contract read` — inspect a contract's storage entries +- `stellar contract extend` — bump an entry's TTL before it expires +- `stellar contract restore` — restore an archived persistent or instance entry (temporary data can't be restored) + +## Running a local network + +For fast, offline iteration, run a self-contained network (node + RPC + faucet) in a container: + + stellar container start local + +- Container engine: defaults to Docker (or any Docker-compatible CLI such as Podman). On Apple silicon (macOS 26+) you can use Apple's `container` CLI. Set the default once with `stellar container use ` (engines: `docker`, `apple-container`); override a single command with `--engine`, or set `STELLAR_CONTAINER_ENGINE`. +- On testnet, fund an account through friendbot: `stellar keys fund alice`. + +## Inspecting configuration + +Use these to see the current state instead of guessing: + +- `stellar env` — effective environment variables and config in use +- `stellar network ls` — configured networks and the default +- `stellar keys ls` — configured identities +- `stellar contract alias ls` — stored contract aliases, grouped by network + +## Putting it together + + stellar network use testnet + stellar keys generate alice --fund + stellar keys use alice + stellar contract deploy --wasm hello.wasm --alias hello + stellar contract invoke --id hello -- hello --to world diff --git a/cmd/soroban-cli/src/commands/skill/mod.rs b/cmd/soroban-cli/src/commands/skill/mod.rs new file mode 100644 index 000000000..7639621ba --- /dev/null +++ b/cmd/soroban-cli/src/commands/skill/mod.rs @@ -0,0 +1,22 @@ +use clap::Parser; + +pub const LONG_ABOUT: &str = "\ +Print an AI-agent skill guide for using the Stellar CLI + +Outputs a Markdown document describing how to use the Stellar CLI idiomatically. +It is meant to be read by AI coding agents (or pasted into their instructions) +so they follow the CLI's conventions: using named networks, identities, and +contract aliases instead of raw RPC URLs, secret keys, and hard-coded contract +ids. +"; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd {} + +impl Cmd { + #[allow(clippy::unused_self)] + pub fn run(&self) { + print!("{}", include_str!("SKILL.md")); + } +}