A stateless, JSON-first native command-line interface for Exa Search, Contents, and Agent, plus three agent skills tailored to the CLI.
The exa executable is written in TypeScript and compiled with scriptc into native binaries. Runtime use does not require Node.js, a JavaScript engine, a daemon, MCP, a credential store, or node_modules.
| Platform | Release target |
|---|---|
| macOS Apple Silicon | darwin-arm64 |
| Linux x86_64, glibc 2.36+ | linux-x64 |
| Linux arm64, glibc 2.36+ | linux-arm64 |
Windows and macOS Intel are not supported in this release line.
Download the archive for the host from the latest release, verify it, and place exa on PATH.
Example for macOS Apple Silicon:
VERSION="$(gh release view --repo jackmazac/exa-cli --json tagName --jq '.tagName | ltrimstr("v")')"
gh release download "v${VERSION}" \
--repo jackmazac/exa-cli \
--pattern "exa-v${VERSION}-darwin-arm64.tar.gz" \
--pattern SHA256SUMS
grep "exa-v${VERSION}-darwin-arm64.tar.gz" SHA256SUMS | shasum -a 256 -c -
tar -xzf "exa-v${VERSION}-darwin-arm64.tar.gz"
install -m 0755 exa "$HOME/.local/bin/exa"
exa --versionVerify an immutable GitHub release and its downloaded asset:
gh release verify "v${VERSION}" --repo jackmazac/exa-cli
gh release verify-asset "v${VERSION}" \
"exa-v${VERSION}-darwin-arm64.tar.gz" \
--repo jackmazac/exa-cliEach release also contains jackmazac-exa-cli-<version>.tgz. It packages all three native targets and installs the matching binary:
gh release download "v${VERSION}" \
--repo jackmazac/exa-cli \
--pattern "jackmazac-exa-cli-${VERSION}.tgz"
npm install --global \
--allow-scripts=@jackmazac/exa-cli \
"./jackmazac-exa-cli-${VERSION}.tgz"
exa --versionThe explicit script approval allows the reviewed installer to select the host binary under current and future npm install-script policies. Node.js is used only by that installer; the installed exa command is native.
Create an Exa API key and set it in the environment:
export EXA_API_KEY="your_api_key_here"Never put the key in command arguments, JSON input, committed environment files, or logs. EXA_SOURCE is optional integration attribution.
exa search <query> [options]
exa search --input <file|->
exa fetch <url>... [options]
exa fetch --input <file|->
exa agent run <query> [options]
exa agent run --input <file|->
exa agent get <agent_run_id>
exa agent wait <agent_run_id> [--poll-interval-ms 4000]
exa agent cancel <agent_run_id>
Global options:
--pretty
--timeout-ms <integer>
--verbose
--help
--version
exa search "recent grid-scale battery deployments" \
--type fast \
--num-results 5Advanced request object:
exa search --input - <<'JSON'{ "query": "recent grid-scale battery deployments", "type": "deep", "numResults": 8, "contents": { "highlights": true, "maxAgeHours": 24 }}JSONexa fetch "https://exa.ai/docs/reference/search" \
--max-characters 3000Advanced Contents request:
exa fetch --input - <<'JSON'{ "urls": ["https://exa.ai/docs/reference/search"], "highlights": { "query": "request fields and defaults" }, "maxAgeHours": 24}JSONexa agent run \
"Compare five grid-storage technologies with cited evidence." \
--effort lowIf a known run is still active:
exa agent get agent_run_123
exa agent wait agent_run_123 --poll-interval-ms 4000
exa agent cancel agent_run_123Agent creation can incur paid work. Do not blindly retry after UPSTREAM_STATE_UNKNOWN; the upstream run may have been created before the connection failed.
Every invocation writes one newline-terminated JSON envelope to stdout.
Success:
{
"version": 1,
"ok": true,
"command": "search",
"data": {
"results": []
}
}Failure:
{
"version": 1,
"ok": false,
"command": "search",
"error": {
"code": "RATE_LIMITED",
"message": "Request was rate limited.",
"status": 429,
"retryAfterSeconds": 10
}
}| Exit | Meaning |
|---|---|
0 | Success, including a known Agent run that remains queued or running |
2 | Usage or input validation |
3 | Missing or rejected credentials |
4 | Rate limit or Agent concurrency limit |
5 | Other upstream failure |
6 | Network, timeout, malformed response, or unknown upstream state |
130 | SIGINT |
Stdout is reserved for the envelope. Verbose diagnostics and Agent progress go to stderr.
The repository ships:
build-with-exa: choose a supported Exa surface and integrateexaas a JSON subprocess.exa-search: Search modes, filters, result content, structured output, and response handling.exa-contents: known-URL extraction, freshness, subpages, extras, and per-URL statuses throughexa fetch.
Install all three into Codex:
python3 /path/to/install-skill-from-github.py \
--repo jackmazac/exa-cli \
--path skills/build-with-exa skills/exa-search skills/exa-contents \
--dest "$HOME/.codex/skills"Use the same command with these destinations for other agents:
~/.claude/skills
~/.pi/agent/skills
Restart or begin a new agent session after installation.
Every release publishes:
exa-v<version>-darwin-arm64.tar.gzexa-v<version>-linux-x64.tar.gzexa-v<version>-linux-arm64.tar.gzjackmazac-exa-cli-<version>.tgzrelease-manifest.jsonSHA256SUMS
release-manifest.json binds every artifact SHA-256 to the exact 40-character source commit. SHA256SUMS covers every artifact and the manifest. GitHub Actions creates Sigstore-backed artifact attestations with actions/attest.
The repository is configured for immutable GitHub Releases. A release is created as a draft with all assets attached, then published; after publication, its tag and assets cannot be changed.
The package uses semantic versions:
- Major: breaking CLI or envelope behavior.
- Minor: backwards-compatible commands, options, API coverage, or skills.
Preview or write a version bump:
npx tsx scripts/bump-version.ts --dry-run minor
npm run version:minor
npm run version:majorThe script updates package.json, package-lock.json, and src/version.ts together.
Release options:
- Run the Release native CLI workflow manually and choose
minorormajor. The workflow commits the synchronized version, creates the exact tag, runs all gates, builds and attests assets, and publishes the immutable release. - Push an exact
v<major>.<minor>.<patch>tag matchingpackage.json. The same build and publication gates run without changing the version.
Requires Node.js 24 and macOS arm64 for the complete cross-platform release build.
npm ci
npm run typecheck
npm test
npm run native:coverage
npm run native:build
npm run test:native
npm run release:artifacts
npm run release:verifynpm run ci runs typecheck, Vitest, fully-static scriptc coverage, native compilation, Node/native differential fixtures, and native dependency isolation checks.
- Credentials are accepted only through
EXA_API_KEY. - Plain HTTP API base URLs are accepted only for loopback integration tests.
- Request headers and credentials are never serialized into CLI error envelopes.
- Secret and Git-history scans are release gates.
- Report security issues privately through the repository’s GitHub security advisory page.
MIT