Stop Switching Tools: Let Your AI Agent Drive Hurley for You
How the hurley SKILL turns natural language into production-ready HTTP tests
Stop Switching Tools: Let Your AI Agent Drive Hurley for You
How the hurley SKILL turns natural language into production-ready HTTP tests

You have a new microservice. You want to verify its endpoints, stress-test them before release, and script a multi-step auth flow — all without leaving your editor. The old answer was three different tools: curl for quick checks, wrk or k6 for load tests, a custom script for chained calls.
The modern answer: One tool with an AI agent that already knows how to use it.
What Is hurley?
hurley is a curl-like HTTP client and performance testing tool written in Rust. It ships with a SKILL file — a structured prompt fragment that teaches an LLM agent exactly which flags to use, which mode to activate, and which pitfalls to avoid.
This article walks through every feature of hurley and shows you how to trigger each one through a conversation with your AI assistant. By the end, you’ll see how five different testing scenarios across all four modes generate correct commands on the first try — with no documentation lookup required.
Understanding the SKILL Concept
A SKILL is a Markdown file with a YAML front-matter header that an LLM agent loads before answering your request. For hurley, the SKILL lives at [.github/skills/hurley/SKILL.md](https://github.com/dursunkoc/hurley/blob/main/.github/skills/hurley/SKILL.md) inside the repository and is registered with agents like GitHub Copilot.
When you ask anything matching hurley’s capabilities — sending HTTP requests, running load tests, parameterizing datasets, or chaining workflow steps — the agent loads the SKILL automatically and uses it as its working reference.
The practical effect? An agent that behaves like a senior engineer who has memorized the hurley manual:
- It picks the right flags the first time
- It never confuses
--perfwith--workflow - It warns you before generating a command that would silently behave differently than expected
The Four Modes at a Glance
Before diving into examples, understand the complete mental model. Each mode serves a different testing scenario, and the SKILL helps the agent pick the right one:

Pro tip: The SKILL encodes this decision logic. The agent consults this table before generating every command, eliminating guesswork about which flags to use.
Mode 1: Single HTTP Requests
The simplest use case. You want to inspect an endpoint, send a payload, or debug response headers.
What you say to the agent:
“Send a GET to https://api.example.com/health and show me the response headers.”
What the agent generates:
hurley -i https://api.example.com/health
-i includes the response headers in output. The agent knows this because the SKILL lists every flag with its short form, long form, and meaning.
POST with a JSON body
“POST
{"event": "signup", "user": "alice"}to https://api.example.com/events with a content-type header and an Authorization Bearer token."
hurley -X POST https://api.example.com/events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-token>" \
-d '{"event": "signup", "user": "alice"}'
The agent knows the default method is GET and will always emit -X POST explicitly for mutation requests. It also knows -H is repeatable, so multiple headers are expressed as separate flags rather than combined into a single string.
POST body from a file
“POST the contents of
payload.jsonto https://api.example.com/orders."
hurley -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-f payload.json
-f reads the body from disk. The agent chooses this over -d when you describe the body as a file.
PUT, DELETE, PATCH
“Update item 42 — set its status to ‘archived’ using a PUT request with a Bearer token.”
hurley -X PUT https://api.example.com/items/42 \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{"status": "archived"}'
“Delete the user with ID 99.”
hurley -X DELETE https://api.example.com/users/99 \
-H "Authorization: Bearer <your-token>"
Verbose and redirect flags
“Fetch https://short.example.com/abc, follow any redirects, and show the full request details.”
hurley -v -L https://short.example.com/abc
-v prints request details before sending. -L follows HTTP 3xx responses up to 10 hops.
Mode 2: Performance Testing
Switch to performance mode by adding -n (total requests), -c (concurrent connections), or both. hurley uses the Tokio async runtime internally, so it handles thousands of concurrent connections without spawning OS threads.
Single-endpoint load test
“Load test GET /products with 50 concurrent connections and 2000 total requests.”
hurley https://api.example.com/products -c 50 -n 2000
The agent activates performance mode because -c 50 and -n 2000 both exceed 1. It does not add --perf or --workflow because you named a single endpoint.
POST load test
“Hammer my POST /orders endpoint with 20 concurrent connections and 500 requests, sending
{"source": "web"}each time."
hurley -X POST https://api.example.com/orders \
-H "Content-Type: application/json" \
-d '{"source": "web"}' \
-c 20 -n 500
Multi-endpoint dataset with --perf
When you want to spread load across multiple endpoints simultaneously, describe your scenario and the agent builds both the command and the dataset file.
“Load test my users API — mix GETs to /users and /users/1, a POST to /users, and a DELETE to /users/99–500 requests total at 20 concurrency.”
**requests.json** (agent generates this):
[
{"method": "GET", "path": "/users"},
{"method": "GET", "path": "/users/1", "headers": {"Authorization": "Bearer token"}},
{"method": "POST", "path": "/users", "body": {"name": "test"}},
{"method": "DELETE", "path": "/users/99"}
]
hurley https://api.example.com --perf requests.json -c 20 -n 500
hurley cycles through the dataset entries to fill the total request count and reports per-endpoint metrics in the breakdown section.
Machine-readable output
“Run the same load test but give me JSON output so I can pipe it into my CI pipeline.”
hurley https://api.example.com --perf requests.json -c 20 -n 500 --output json
The agent appends --output json when you explicitly ask for machine-readable or scriptable output.
What the report looks like
For a text-format run you get a structured report with p50/p95/p99 latency percentiles and an endpoint-level breakdown:
═══════════════════════════════════════════════════════════
PERFORMANCE RESULTS
═══════════════════════════════════════════════════════════
📊 Request Summary
Total Requests: 500
Successful: 498
Failed: 2
Error Rate: 0.40%
⏱️ Timing
Total Duration: 4812.34 ms
Requests/sec: 103.90
📈 Latency Distribution
Min: 12.10 ms
Max: 298.45 ms
Avg: 67.23 ms
p50 (Median): 58.90 ms
p95: 152.67 ms
p99: 241.34 ms
════════════════════════════════════════
ENDPOINT BREAKDOWN
════════════════════════════════════════
📍 GET /users
Total: 200 Success: 200 Error: 0.00%
p50: 45.12 ms p95: 110.34 ms p99: 132.56 ms
📍 POST /users
Total: 150 Success: 148 Error: 1.33%
p50: 72.45 ms p95: 180.23 ms p99: 241.34 ms
p95 and p99 matter because they reveal tail latency — the worst-case experience for real users — which average values entirely hide.
Mode 3: Parameterized Load Testing with --data-file
Real traffic is varied. If you hammer an endpoint with the same payload, you cache-warm the database and get results that do not reflect production. --data-file feeds real variability into every request.
How it works
Put {{placeholder}} tokens anywhere in the URL, headers, or body. Provide a CSV or JSON file whose columns match those names. hurley cycles through the rows sequentially, wrapping back to the start when it runs out.
The agent builds it from a description
“Run a parameterized load test against POST /users/{{user_id}} with an Authorization header using {{api_token}} and a body of
{"role": "{{role}}"}. Use my file users.csv with 10 concurrency and 1000 requests."
Assumed users.csv:
user_id,api_token,role
101,abc123token,admin
102,def456token,user
103,ghi789token,viewer
hurley -X POST https://api.example.com/users/{{user_id}} \
-H "Authorization: Bearer {{api_token}}" \
-d '{"role": "{{role}}"}' \
--data-file users.csv -c 10 -n 1000
The agent knows placeholders work in all three locations simultaneously — URL path, header values, and body — and emits them without modification. It also knows the column names must exactly match (case-sensitive) and will warn you if they look mismatched.
JSON data file instead of CSV
“Same test but my data is in users.json format, not CSV.”
[
{"user_id": "101", "api_token": "abc123token", "role": "admin"},
{"user_id": "102", "api_token": "def456token", "role": "user"},
{"user_id": "103", "api_token": "ghi789token", "role": "viewer"}
]
hurley -X POST https://api.example.com/users/{{user_id}} \
-H "Authorization: Bearer {{api_token}}" \
-d '{"role": "{{role}}"}' \
--data-file users.json -c 10 -n 1000
hurley auto-detects format from the file extension.
Standalone sequential mode
You do not always want a load test. Sometimes you want to iterate over every row exactly once — like seeding a test database or verifying one request per account.
“Send one GET request per user in my users.json — no concurrency, no load test, just one request per row.”
hurley https://api.example.com/users/{{user_id}} --data-file users.json
No -n or -c means no performance mode. hurley sends exactly one request per data row, sequentially. The SKILL explicitly teaches this distinction so the agent never accidentally omits -n from a load test, and never accidentally adds it to a per-row iteration.
Mode 4: Workflows — Conditional Multi-step Execution
Some scenarios are inherently sequential: authenticate, then fetch a resource, then act on its contents. hurley’s --workflow mode handles this natively with a JSON definition file and a condition expression language.
The concept
Each step has an id. Later steps can read the JSON body of any earlier step's response using responses.<step_id>.<dot.path>. If a condition evaluates to false, the step is skipped entirely. Steps without a condition always run.
The agent writes the workflow file for you
“Create a workflow that first GETs https://httpbin.org/json to get user info, then POSTs to https://httpbin.org/post with
{"message": "Author matched!"}only if the slideshow author in the response equals 'Yours Truly'."
**flow.json** (agent generates):
{
"steps": [
{
"id": "get_user",
"request": {
"method": "GET",
"path": "https://httpbin.org/json"
}
},
{
"id": "post_if_match",
"condition": "responses.get_user.slideshow.author == \"Yours Truly\"",
"request": {
"method": "POST",
"path": "https://httpbin.org/post",
"body": {"message": "Author matched!"}
}
}
]
}
hurley --workflow flow.json https://httpbin.org
The agent knows the responses.<id>.<path> dot-notation and correctly escapes string literals in condition expressions.
Real-world three-step auth workflow
“Build a workflow for my API: step 1 POSTs to /auth/login with
{"username": "admin", "password": "secret"}and gets a token back. Step 2 checks if step 1's response hasstatus == "ok"before proceeding. Step 3 GETs /dashboard with an Authorization header. Use https://api.example.com as the base."
**auth_workflow.json** (agent generates):
{
"steps": [
{
"id": "login",
"request": {
"method": "POST",
"path": "/auth/login",
"body": {"username": "admin", "password": "secret"},
"headers": {"Content-Type": "application/json"}
}
},
{
"id": "check_status",
"condition": "responses.login.status == \"ok\"",
"request": {
"method": "GET",
"path": "/auth/validate"
}
},
{
"id": "get_dashboard",
"request": {
"method": "GET",
"path": "/dashboard"
}
}
]
}
hurley --workflow auth_workflow.json https://api.example.com
Execution output is colored and step-by-step:
🚀 Starting Workflow Execution
➜ Step: login
➜ Step: check_status
Evaluating condition: responses.login.status == "ok" ... Matched
➜ Step: get_dashboard
✅ Workflow Completed
If the condition had evaluated to false, the check_status step would have printed Skipped (false) and execution would have continued to get_dashboard.
Condition expressions the agent understands
The SKILL documents the full expression language so the agent generates valid conditions for any scenario:

The Agent’s Quick Decision Guide
The SKILL provides a decision table the agent uses internally every time it generates a command. Understanding it helps you write more effective prompts:

Pitfalls the SKILL Prevents
Without the SKILL, these are easy mistakes. With it, the agent catches them before generating a broken command.
**--data-file alone is not performance mode.** Saying "run through my CSV" without specifying -n or -c sends one request per row sequentially. The SKILL explains this explicitly so the agent never adds concurrency flags to an iteration request and never omits them from a genuine load test.
**--perf vs --workflow are not interchangeable.** --perf is for concurrent load testing across a dataset of endpoints. --workflow is for sequential, dependent, conditional steps. The SKILL defines both precisely so the agent always picks the right one.
Placeholder names are case-sensitive. {{User_Id}} and {{user_id}} are different tokens. If a placeholder does not match a column header exactly, hurley reports every missing placeholder and aborts before sending a single request. The SKILL warns the agent to echo the exact column names from the data file in the generated command.
Workflow path needs a full URL or a valid base. If a step path is relative (e.g., /api/users), hurley prepends the base URL argument. The SKILL reminds the agent to always pass the correct base URL argument alongside --workflow.
Installation
Via Cargo
cargo install hurley
From source
git clone https://github.com/dursunkoc/hurley.git
cd hurley
cargo build --release
The binary is available as hurley after installation, or at target/release/hurley when built from source.
Putting It All Together
Here is a complete session with an AI agent that has the hurley SKILL loaded, covering all four modes in a realistic API development workflow:
You: Check if my auth service is up.
Agent: hurley https://auth.example.com/health
You: POST a login request with username "testuser" and password "pass123".
Agent: hurley -X POST https://auth.example.com/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "pass123"}'
You: Now load test that login endpoint with 25 concurrency and 1000 requests.
Agent: hurley -X POST https://auth.example.com/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "pass123"}' \
-c 25 -n 1000
You: I have 500 real user credentials in users.csv. Re-run the test cycling through them.
Agent: hurley -X POST https://auth.example.com/login \
-H "Content-Type: application/json" \
-d '{"username": "{{username}}", "password": "{{password}}"}' \
--data-file users.csv -c 25 -n 1000
You: Create a workflow: login, and if login returns success==true, fetch /profile.
Agent: [generates auth_flow.json]
hurley --workflow auth_flow.json https://auth.example.com
You: Give me the load test results as JSON for my CI dashboard.
Agent: hurley -X POST https://auth.example.com/login \
-H "Content-Type: application/json" \
-d '{"username": "{{username}}", "password": "{{password}}"}' \
--data-file users.csv -c 25 -n 1000 --output json
Five different commands across all four modes, each correct on the first try, with no documentation lookup required.
Conclusion
hurley consolidates what used to require curl, k6 or wrk, and a custom scripting layer into a single binary. The SKILL file takes that consolidation one step further: it eliminates the learning curve entirely within an LLM-powered workflow, letting you describe what you want and trust that the generated command reflects the correct mode, the right flags, and the expected behavior.
If you are using GitHub Copilot or any SKILL-aware agent, install hurley, register the SKILL, and start describing your API tests in plain language. The agent handles the rest.
Source code: github.com/dursunkoc/hurley
메타데이터
- post_id
- 99d6c4122703
- slug
- stop-switching-tools-let-your-ai-agent-drive-hurley-for-you-99d6c4122703
- url
- https://medium.com/@dursunkoc/stop-switching-tools-let-your-ai-agent-drive-hurley-for-you-99d6c4122703
- canonical_url
- https://medium.com/@dursunkoc/stop-switching-tools-let-your-ai-agent-drive-hurley-for-you-99d6c4122703
- author_url
- https://medium.com/@dursunkoc
- status
- ok
- fetched_at
- 2026-07-13 06:23:13