Skip to content

Commit 45eac93

Browse files
Copilotfuxingloh
andcommitted
feat: redesign CLI per USE-45 - space subcommands, --uri/--url options, new commands
Co-authored-by: fuxingloh <4266087+fuxingloh@users.noreply.github.com>
1 parent 935b48c commit 45eac93

File tree

13 files changed

+552
-82
lines changed

13 files changed

+552
-82
lines changed

CLAUDE.md

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ All top-level commands use Turborepo (`turbo`) to orchestrate across packages.
3636
bun run --filter localhost-aixyz dev
3737

3838
# Terminal 2 — send a message via the CLI
39-
use-agently a2a http://localhost:3000 -m "Hello!"
39+
use-agently a2a send --uri http://localhost:3000 -m "Hello!"
4040
```
4141

4242
The agent exposes a free endpoint (`accepts: { scheme: "free" }`), so no wallet balance is needed. Set `OPENAI_API_KEY` in `packages/localhost-aixyz/.env.local` before starting the dev server.
@@ -108,20 +108,19 @@ Commands are grouped into four categories. New commands must be placed in one of
108108

109109
### Subcommand Pattern
110110

111-
Commands that have subcommands use a **colon separator**: `<command>:<subcommand>`.
111+
Commands that have subcommands use a **space separator**: `<command> <subcommand>`.
112112

113113
```
114-
use-agently marketplace:agents "query"
115-
use-agently a2a:card "uri"
116-
use-agently web:get "url"
114+
use-agently a2a send --uri "uri"
115+
use-agently a2a card --uri "uri"
116+
use-agently mcp tools --uri "uri"
117+
use-agently mcp call "tool" "args" --uri "uri"
118+
use-agently web get --uri "url"
117119
```
118120

119-
Shorthands (aliases) are allowed for frequently used commands:
121+
### URI / URL Interchangeability
120122

121-
```
122-
use-agently m "query" # alias for: use-agently marketplace
123-
use-agently m:agents "query" # alias for: use-agently marketplace:agents
124-
```
123+
All protocol commands accept both `--uri` and `--url` as equivalent options. Either flag resolves to the same value.
125124

126125
### Full Command Reference
127126

@@ -138,39 +137,33 @@ use-agently balance # Show on-chain wallet balance
138137
#### Discovery
139138

140139
```bash
141-
use-agently marketplace # List all agents/tools/skills on the marketplace
142-
use-agently marketplace "query" # Search the marketplace
143-
use-agently marketplace:agents "query" # Search agents specifically
144-
use-agently marketplace:tools "query" # Search tools specifically
145-
use-agently marketplace:skills "query" # Search skills specifically
146-
147-
# Shorthands
148-
use-agently m "query"
149-
use-agently m:agents "query"
150-
use-agently m:tools "query"
151-
use-agently m:skills "query"
140+
use-agently agents # List all agents on the marketplace
141+
use-agently search [query] # Search the marketplace
142+
use-agently search [query] --protocol a2a,mcp,web # Filter by protocol(s)
152143
```
153144

154145
#### Operations
155146

156147
```bash
157148
use-agently init # Initialize a wallet and config
158-
use-agently config # Show or edit current configuration
159149
use-agently update # Update the CLI to the latest version
160-
use-agently wallets # List and manage configured wallets
161150
```
162151

163152
#### Protocols
164153

165154
```bash
166-
use-agently erc-8004 "uri" # Resolve an ERC-8004 agent URI
167-
use-agently a2a "uri/url" # Send a message to an agent via A2A protocol
168-
use-agently a2a:card "uri/url" # Fetch and display the A2A agent card
169-
use-agently mcp "uri/url" # Connect to an MCP server
170-
171-
use-agently web "url" # HTTP GET (default method)
172-
use-agently web:get "url" # HTTP GET
173-
use-agently web:put "url" # HTTP PUT
155+
use-agently erc-8004 --uri "uri" # Resolve an ERC-8004 agent URI
156+
use-agently a2a send --uri "uri/url" -m "message" # Send a message via A2A protocol
157+
use-agently a2a card --uri "uri/url" # Fetch and display the A2A agent card
158+
use-agently mcp tools --uri "uri/url" # List tools on an MCP server
159+
use-agently mcp call "tool" ["args"] --uri "uri/url" # Call a tool on an MCP server
160+
161+
use-agently web get --uri "url" # HTTP GET
162+
use-agently web post --uri "url" -d '{"k":"v"}' # HTTP POST
163+
use-agently web put --uri "url" -d '{"k":"v"}' # HTTP PUT
164+
use-agently web delete --uri "url" # HTTP DELETE
165+
use-agently web head --uri "url" # HTTP HEAD
166+
use-agently web patch --uri "url" -d '{"k":"v"}' # HTTP PATCH
174167
```
175168

176169
### Design Rules for New Commands
@@ -191,7 +184,7 @@ use-agently web:put "url" # HTTP PUT
191184

192185
5. **Structured output** — use exit codes to signal success/failure. For `--output json`, emit valid JSON to stdout so agents can parse results.
193186

194-
6. **Colon subcommand convention** — use `command:subcommand` (colon separator) for protocol variants and marketplace filters. Register these as Commander.js commands named `"command:subcommand"`.
187+
6. **Space subcommand convention** — use `command subcommand` (space separator) for protocol variants. Do not use colon separators.
195188

196189
## Documentation
197190

README.md

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use-agently balance
2929
use-agently agents
3030

3131
# Send a message to an agent (use the URI from `use-agently agents`)
32-
use-agently a2a <agent-uri> -m "Hello, agent!"
32+
use-agently a2a send --uri <agent-uri> -m "Hello, agent!"
3333
```
3434

3535
## Commands
@@ -94,19 +94,67 @@ List available agents on Agently.
9494
use-agently agents
9595
```
9696

97+
### `search`
98+
99+
Search the Agently marketplace for agents, optionally filtering by query and/or protocol.
100+
101+
```bash
102+
use-agently search
103+
use-agently search "echo"
104+
use-agently search --protocol a2a
105+
use-agently search "assistant" --protocol "a2a,mcp"
106+
```
107+
97108
### `a2a`
98109

99-
Send a message to an agent via the A2A protocol. The `<agent-uri>` is the identifier shown by `use-agently agents` (e.g. `echo-agent`). The CLI resolves it to `https://use-agently.com/<agent-uri>/`. Payments are handled automatically via x402 when agents require them.
110+
Interact with agents via the A2A protocol. Both `--uri` and `--url` are accepted. The identifier is resolved to `https://use-agently.com/<agent-uri>/` if it is not a full URL. Payments are handled automatically via x402 when agents require them.
111+
112+
```bash
113+
# Send a message
114+
use-agently a2a send --uri <agent-uri> -m "What can you do?"
115+
116+
# Fetch the agent card
117+
use-agently a2a card --uri <agent-uri>
118+
```
119+
120+
### `mcp`
121+
122+
Connect to an MCP server to list or call tools.
123+
124+
```bash
125+
# List tools
126+
use-agently mcp tools --uri http://localhost:3000
127+
128+
# Call a tool
129+
use-agently mcp call echo '{"message":"hello"}' --uri http://localhost:3000
130+
```
131+
132+
### `erc-8004`
133+
134+
Resolve an ERC-8004 agent URI and display its details from the Agently marketplace.
135+
136+
```bash
137+
use-agently erc-8004 --uri eip155:8453/erc-8004:0x1234/1
138+
```
139+
140+
### `web`
141+
142+
Make HTTP requests. Supports all standard HTTP methods. Both `--uri` and `--url` are accepted.
100143

101144
```bash
102-
use-agently a2a <agent-uri> -m "What can you do?"
145+
use-agently web get --uri https://example.com/api
146+
use-agently web post --uri https://example.com/api -d '{"key":"value"}'
147+
use-agently web put --uri https://example.com/api -d '{"key":"value"}'
148+
use-agently web delete --uri https://example.com/api
149+
use-agently web head --uri https://example.com/api
150+
use-agently web patch --uri https://example.com/api -d '{"key":"value"}'
103151
```
104152

105153
## How It Works
106154

107155
1. **Wallet**`init` generates an EVM private key stored locally. This wallet signs x402 payment headers when agents charge for their services.
108-
2. **Discovery**`agents` fetches the agent directory from Agently, showing names, descriptions, supported protocols, and URIs.
109-
3. **Communication**`a2a` takes an agent URI (e.g. `echo-agent`), constructs the URL as `https://use-agently.com/<agent-uri>/`, resolves the A2A card, opens a JSON-RPC or REST transport, and sends your message. If the agent returns a 402 Payment Required, the x402 fetch wrapper automatically signs and retries the request.
156+
2. **Discovery**`agents` and `search` fetch the agent directory from Agently, showing names, descriptions, supported protocols, and URIs.
157+
3. **Communication**`a2a send` takes an agent URI (e.g. `echo-agent`), constructs the URL as `https://use-agently.com/<agent-uri>/`, resolves the A2A card, opens a JSON-RPC or REST transport, and sends your message. If the agent returns a 402 Payment Required, the x402 fetch wrapper automatically signs and retries the request.
110158

111159
## Development
112160

packages/use-agently/src/cli.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import { initCommand } from "./commands/init";
33
import { whoamiCommand } from "./commands/whoami";
44
import { balanceCommand } from "./commands/balance";
55
import { agentsCommand } from "./commands/agents";
6-
import { a2aCommand, a2aCardCommand } from "./commands/a2a";
6+
import { searchCommand } from "./commands/search";
7+
import { a2aCommand } from "./commands/a2a";
78
import { mcpCommand } from "./commands/mcp";
9+
import { erc8004Command } from "./commands/erc8004";
10+
import { webCommand } from "./commands/web";
811
import { doctorCommand } from "./commands/doctor";
912
import { updateCommand } from "./commands/update";
1013

@@ -30,11 +33,13 @@ cli.addCommand(balanceCommand.helpGroup("Diagnostics"));
3033

3134
// Discovery
3235
cli.addCommand(agentsCommand.helpGroup("Discovery"));
36+
cli.addCommand(searchCommand.helpGroup("Discovery"));
3337

3438
// Protocols
3539
cli.addCommand(a2aCommand.helpGroup("Protocols"));
36-
cli.addCommand(a2aCardCommand.helpGroup("Protocols"));
3740
cli.addCommand(mcpCommand.helpGroup("Protocols"));
41+
cli.addCommand(erc8004Command.helpGroup("Protocols"));
42+
cli.addCommand(webCommand.helpGroup("Protocols"));
3843

3944
// Lifecycle
4045
cli.addCommand(initCommand.helpGroup("Lifecycle"));

packages/use-agently/src/commands/a2a.test.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,33 +51,65 @@ describe("a2a command", () => {
5151
const out = captureOutput();
5252

5353
test("text output", async () => {
54-
await cli.parseAsync(["test", "use-agently", "a2a", agent.getAgentUrl() + "/free-echo/", "-m", "hello world"]);
54+
await cli.parseAsync([
55+
"test",
56+
"use-agently",
57+
"a2a",
58+
"send",
59+
"--uri",
60+
agent.getAgentUrl() + "/free-echo/",
61+
"-m",
62+
"hello world",
63+
]);
5564
expect(out.stdout).toBe("hello world");
5665
});
5766

5867
test("streams text output 10 times", async () => {
59-
await cli.parseAsync(["test", "use-agently", "a2a", agent.getAgentUrl() + "/free-echo-10/", "-m", "hi"]);
68+
await cli.parseAsync([
69+
"test",
70+
"use-agently",
71+
"a2a",
72+
"send",
73+
"--uri",
74+
agent.getAgentUrl() + "/free-echo-10/",
75+
"-m",
76+
"hi",
77+
]);
6078
// free-echo-10 streams the message back 10 times with 200ms delays between each chunk
6179
const expected = "hi\nhi\nhi\nhi\nhi\nhi\nhi\nhi\nhi\nhi";
6280
expect(out.stdout).toBe(expected);
6381
}, 15000);
82+
83+
test("--url alias works", async () => {
84+
await cli.parseAsync([
85+
"test",
86+
"use-agently",
87+
"a2a",
88+
"send",
89+
"--url",
90+
agent.getAgentUrl() + "/free-echo/",
91+
"-m",
92+
"hello url",
93+
]);
94+
expect(out.stdout).toBe("hello url");
95+
});
6496
});
6597
});
6698

67-
describe("a2a:card command", () => {
99+
describe("a2a card command", () => {
68100
describe("cli", () => {
69101
const out = captureOutput();
70102

71103
test("text output returns agent card fields", async () => {
72-
await cli.parseAsync(["test", "use-agently", "a2a:card", agent.getAgentUrl()]);
104+
await cli.parseAsync(["test", "use-agently", "a2a", "card", "--uri", agent.getAgentUrl()]);
73105
const card = out.yaml as Record<string, unknown>;
74106
expect(card).toHaveProperty("name");
75107
expect(card).toHaveProperty("description");
76108
expect(card).toHaveProperty("url");
77109
});
78110

79111
test("json output returns agent card as JSON", async () => {
80-
await cli.parseAsync(["test", "use-agently", "-o", "json", "a2a:card", agent.getAgentUrl()]);
112+
await cli.parseAsync(["test", "use-agently", "-o", "json", "a2a", "card", "--uri", agent.getAgentUrl()]);
81113
const card = out.json as Record<string, unknown>;
82114
expect(card).toHaveProperty("name");
83115
expect(card).toHaveProperty("description");

packages/use-agently/src/commands/a2a.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,37 @@ function extractStreamEventText(event: any): string {
5858
return "";
5959
}
6060

61+
function resolveUriOption(options: { uri?: string; url?: string }, commandName: string): string {
62+
const value = options.uri || options.url;
63+
if (!value) {
64+
throw new Error(
65+
`Missing required option --uri for '${commandName}'.\nExpected a URL or agent URI, e.g. --uri https://example.com/agent or --uri echo-agent`,
66+
);
67+
}
68+
return value;
69+
}
70+
6171
export const a2aCommand = new Command("a2a")
72+
.description("Interact with agents via the A2A protocol")
73+
.action(function () {
74+
(this as Command).outputHelp();
75+
});
76+
77+
const a2aSendCommand = new Command("send")
6278
.description("Send a message to an agent via A2A protocol")
63-
.argument("<agent>", "Agent URI")
79+
.option("--uri <value>", "Agent URI or URL (e.g. https://example.com/agent or echo-agent)")
80+
.option("--url <value>", "Agent URI or URL (alias for --uri)")
6481
.requiredOption("-m, --message <text>", "Message to send")
65-
.action(async (agentUri: string, options: { message: string }) => {
82+
.addHelpText(
83+
"after",
84+
'\nExamples:\n use-agently a2a send --uri https://example.com/agent -m "Hello!"\n use-agently a2a send --uri echo-agent -m "Hello!"',
85+
)
86+
.action(async (options: { uri?: string; url?: string; message: string }) => {
87+
const agentInput = resolveUriOption(options, "a2a send");
6688
const config = await getConfigOrThrow();
6789
const wallet = loadWallet(config.wallet);
6890
const paymentFetch = createPaymentFetch(wallet);
69-
const agentUrl = resolveAgentUrl(agentUri);
91+
const agentUrl = resolveAgentUrl(agentInput);
7092
const client = await createA2AClient(agentUrl, paymentFetch as typeof fetch);
7193

7294
const stream = client.sendMessageStream({
@@ -96,16 +118,21 @@ export const a2aCommand = new Command("a2a")
96118
}
97119
});
98120

99-
export const a2aCardCommand = new Command("a2a:card")
121+
const a2aCardSubCommand = new Command("card")
100122
.description("Fetch and display the A2A agent card")
101-
.argument("<agent>", "Agent URL or URI (e.g. https://example.com/agent or my-agent)")
123+
.option("--uri <value>", "Agent URI or URL (e.g. https://example.com/agent or echo-agent)")
124+
.option("--url <value>", "Agent URI or URL (alias for --uri)")
102125
.addHelpText(
103126
"after",
104-
"\nExamples:\n use-agently a2a:card https://example.com/agent\n use-agently a2a:card my-agent",
127+
"\nExamples:\n use-agently a2a card --uri https://example.com/agent\n use-agently a2a card --uri echo-agent",
105128
)
106-
.action(async (agentInput: string, _options: Record<string, never>, command: Command) => {
129+
.action(async (options: { uri?: string; url?: string }, command: Command) => {
130+
const agentInput = resolveUriOption(options, "a2a card");
107131
const agentUrl = resolveAgentUrl(agentInput);
108132
const resolver = new DefaultAgentCardResolver();
109133
const card = await resolver.resolve(agentUrl);
110134
output(command, card);
111135
});
136+
137+
a2aCommand.addCommand(a2aSendCommand);
138+
a2aCommand.addCommand(a2aCardSubCommand);

0 commit comments

Comments
 (0)