@victor-software-house/exa-cli 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1 +1,20 @@
1
1
  # Changelog
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Apply the documented dynamic Context token default and accept its observed cost response shape ([`83dd937`](https://github.com/victor-software-house/exa-cli/commit/83dd9375ad85618adfdd5ec8ac04a1da05773113)).
8
+
9
+ - Redesign generated help with concise command menus, focused errors, practical examples, and nested `agent` commands ([`2b9b5bf`](https://github.com/victor-software-house/exa-cli/commit/2b9b5bfa0efe04bd4b8152573b137b604f0f65c1)).
10
+
11
+ ## 0.0.1
12
+
13
+ ### Patch Changes
14
+
15
+ - Flag-driven output, per-key cache, `exa cache`, and real npm binaries ([`d0bb475`](https://github.com/victor-software-house/exa-cli/commit/d0bb4750b2874466541996a86e0d98db8e65a14d)).
16
+
17
+ - Output resolution is now flags-only: `--pretty` implies JSON and wins, `--json` or `-o` select JSON, otherwise human text prints even when piped. `-o` always writes JSON. No TTY or file-extension inference — pass `--json` where scripts used to rely on pipe detection.
18
+ - Cache keys now include a truncated SHA-256 of the API key; two accounts no longer share cached responses. Old entries silently miss — run `exa cache clear`.
19
+ - New `exa cache path|clear|prune` for cache management.
20
+ - npm now ships prebuilt per-platform binaries behind a Node launcher (`@victor-software-house/exa-cli-<platform>` optionalDependencies); the package runs anywhere Node 20+ exists, no Bun install required.
package/README.md CHANGED
@@ -1,10 +1,18 @@
1
1
  # @victor-software-house/exa-cli
2
2
 
3
- Public CLI for [Exa](https://exa.ai/) search, contents, and answer.
3
+ Agent-friendly CLI for the [Exa](https://exa.ai/) API: search, contents, answer, similar, context, and agent runs, backed by a local SQLite request cache.
4
4
 
5
5
  ## Install
6
6
 
7
- Agents and local checkouts install the GitHub Release binary through mise. Pin `0.0.0` that tag is a rolling channel, not a frozen npm version:
7
+ npm ships a launcher that pulls a prebuilt binary for your platform. Node 20+ is enough Bun is embedded in the binary:
8
+
9
+ ```bash
10
+ bun add -g @victor-software-house/exa-cli
11
+ npm install -g @victor-software-house/exa-cli
12
+ bunx @victor-software-house/exa-cli --help
13
+ ```
14
+
15
+ Agents and operator checkouts install the rolling GitHub Release binary through mise. Pin `0.0.0` — that tag is a rolling channel, not a frozen npm version:
8
16
 
9
17
  ```toml
10
18
  [tools]
@@ -16,7 +24,7 @@ mise install
16
24
  exa --help
17
25
  ```
18
26
 
19
- npm is not the install path until the package is published. Do not `npm i -g`. Source checkout:
27
+ Source checkout:
20
28
 
21
29
  ```bash
22
30
  mise install
@@ -36,21 +44,48 @@ Operator checkouts with the global fnox profile `exa` get the key from mise (`mi
36
44
  ```bash
37
45
  exa search "Exa search type auto vs neural official docs" --include-domain exa.ai
38
46
  exa contents https://exa.ai/docs/reference/search.md
39
- exa answer "What Exa search type replaces the deprecated research API?"
47
+ exa answer "when did Exa ship the context endpoint"
48
+ exa similar https://exa.ai/docs/reference/search.md
49
+ exa context "how to use React hooks for state management" --tokens-num 500
50
+ exa agent create "narrow research question"
51
+ exa agent create "narrow research question" --wait --timeout 600
52
+ exa agent get agent_run_…
53
+ exa agent wait agent_run_… --timeout 600
54
+ exa agent cancel agent_run_…
40
55
  exa doctor
56
+ exa cache path|clear|prune
41
57
  ```
42
58
 
43
- Stdout is the payload. Stderr is progress, cache hits, and errors. `--json` keeps the provider body `jq`-friendly. Cache metadata stays on stderr unless `--envelope`.
59
+ Every command also accepts `--request '<json>'` with the raw provider body (validated against the generated schema for that endpoint) instead of the flag surface.
60
+
61
+ ## Output
44
62
 
45
- ## Cache
63
+ Format is flag-driven. There is no TTY sniffing and no file-extension inference: what you pass is what you get.
46
64
 
47
- Identical requests are served from a local SQLite cache under `$XDG_CACHE_HOME/exa-cli/` or `~/.cache/exa-cli/`. Default TTL is 24 hours.
65
+ | Flags | Result |
66
+ |:--|:--|
67
+ | none | Human-readable text on stdout, even when piped |
68
+ | `--json` | Compact provider JSON |
69
+ | `--pretty` | Indented JSON (implies JSON, beats `--json`) |
70
+ | `-o FILE` | Always writes JSON to the file; `--pretty` indents it |
71
+ | `--envelope` | Wraps JSON output with `{ data, cache: { hit, ageMs } }` |
48
72
 
49
- - `--refresh` ignores a stored hit and overwrites it
50
- - `--no-cache` skips read and write
51
- - `--ttl` overrides the TTL in seconds
73
+ Writing to a file never transforms content — a `.md` output path still gets JSON. Progress, cache hits, and errors go to stderr, so `exa search … --json | jq .results` is always safe.
52
74
 
53
- Do not repeat an identical call from an agent loop. Use `--refresh` only when you need a fresh provider response.
75
+ Color applies to the text render only: on by default on a TTY, controlled by `--color` / `--no-color` and `NO_COLOR` / `FORCE_COLOR`.
76
+
77
+ ## Cache and cost
78
+
79
+ Every uncached call spends Exa credits. Identical requests within the TTL are served free from a local SQLite cache at `$XDG_CACHE_HOME/exa-cli/` or `~/.cache/exa-cli/`, announced as `cache hit age=…` on stderr.
80
+
81
+ - Cache keys include a digest of the API key — two accounts never share cached responses.
82
+ - Default TTL is 24 hours; `--ttl SECONDS` overrides per call.
83
+ - `--refresh` skips the read and overwrites the entry. Use only when staleness provably matters.
84
+ - `--no-cache` skips reads and writes.
85
+ - `exa cache prune` deletes expired entries; `exa cache clear` deletes all; `exa cache path` prints the database location.
86
+ - `agent create`, `agent get`, `agent wait`, and `agent cancel` never cache: create and cancel mutate, get and wait poll.
87
+
88
+ Do not repeat an identical call in a loop — the first response is already stored.
54
89
 
55
90
  ## Agent skill
56
91
 
@@ -62,6 +97,8 @@ npx skills add victor-software-house/exa-cli
62
97
 
63
98
  ```bash
64
99
  mise install
65
- mise run verify
66
- bun src/cli.ts search --help
100
+ mise run verify # lint + typecheck + unit tests + build
101
+ mise -E test run test:live # paid Exa calls; skips without EXA_API_KEY
67
102
  ```
103
+
104
+ Releases are CI-owned: merge a changeset, the Release workflow opens a Version Packages PR, and merging it publishes to npm (six platform packages plus the launcher umbrella), tags, and uploads versioned binaries. A new npm package name needs a one-time `mise run release:bootstrap` first (browser login; same staged platforms CI publishes).
package/bin/exa.mjs ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ // Node launcher for exa-cli: resolves the platform binary from the
3
+ // optionalDependencies-selected package and execs it. The binary embeds Bun,
4
+ // so no runtime beyond Node >=20 is required here.
5
+
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import { dirname, join } from 'node:path';
10
+
11
+ const require = createRequire(import.meta.url);
12
+
13
+ const scope = '@victor-software-house';
14
+
15
+ function platformPackages() {
16
+ const id = `${process.platform}-${process.arch}`;
17
+ if (id === 'win32-x64') {
18
+ return [`${scope}/exa-cli-windows-x64`];
19
+ }
20
+ if (id === 'linux-x64') {
21
+ // os/cpu cannot distinguish glibc from musl; npm selects one via libc,
22
+ // so probe both and use whichever package resolved.
23
+ return [`${scope}/exa-cli-linux-x64`, `${scope}/exa-cli-linux-x64-musl`];
24
+ }
25
+ if (id === 'darwin-x64' || id === 'darwin-arm64' || id === 'linux-arm64') {
26
+ return [`${scope}/exa-cli-${id}`];
27
+ }
28
+ return [];
29
+ }
30
+
31
+ function findBinary() {
32
+ for (const pkg of platformPackages()) {
33
+ try {
34
+ const manifest = require.resolve(`${pkg}/package.json`);
35
+ const binary = join(
36
+ dirname(manifest),
37
+ 'bin',
38
+ process.platform === 'win32' ? 'exa.exe' : 'exa',
39
+ );
40
+ if (existsSync(binary)) {
41
+ return binary;
42
+ }
43
+ } catch {
44
+ // platform package not installed; try the next candidate
45
+ }
46
+ }
47
+ return undefined;
48
+ }
49
+
50
+ const binary = findBinary();
51
+ if (binary === undefined) {
52
+ process.stderr.write(
53
+ `exa-cli: no binary for ${process.platform}-${process.arch}.\n` +
54
+ 'Reinstall with `bun add -g @victor-software-house/exa-cli` or `npm install -g @victor-software-house/exa-cli`.\n',
55
+ );
56
+ process.exit(1);
57
+ }
58
+
59
+ const child = spawn(binary, process.argv.slice(2), { stdio: 'inherit' });
60
+
61
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
62
+ process.on(signal, () => {
63
+ if (!child.killed) {
64
+ child.kill(signal);
65
+ }
66
+ });
67
+ }
68
+
69
+ child.on('error', (error) => {
70
+ process.stderr.write(`${error}\n`);
71
+ process.exit(1);
72
+ });
73
+
74
+ child.on('exit', (code, signal) => {
75
+ if (signal) {
76
+ process.kill(process.pid, signal);
77
+ } else {
78
+ process.exit(code ?? 1);
79
+ }
80
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@victor-software-house/exa-cli",
3
- "version": "0.0.0",
4
- "description": "Public CLI for Exa search, contents, and answer with a local SQLite cache.",
3
+ "version": "0.0.2",
4
+ "description": "Agent-friendly CLI for the Exa API with a local SQLite request cache.",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Victor Araújo",
@@ -9,38 +9,11 @@
9
9
  },
10
10
  "type": "module",
11
11
  "bin": {
12
- "exa": "./dist/cli.mjs"
13
- },
14
- "exports": {
15
- ".": {
16
- "types": "./dist/cli.d.mts",
17
- "import": "./dist/cli.mjs"
18
- },
19
- "./package.json": "./package.json"
20
- },
21
- "files": [
22
- "dist",
23
- "skills",
24
- "README.md",
25
- "CHANGELOG.md",
26
- "LICENSE"
27
- ],
28
- "scripts": {
29
- "prepublishOnly": "mise run verify"
12
+ "exa": "./bin/exa.mjs"
30
13
  },
14
+ "files": ["bin", "skills", "README.md", "CHANGELOG.md", "LICENSE"],
31
15
  "engines": {
32
- "node": ">=26"
33
- },
34
- "packageManager": "bun@1.4.0",
35
- "devEngines": {
36
- "runtime": {
37
- "name": "node",
38
- "version": ">=26"
39
- },
40
- "packageManager": {
41
- "name": "bun",
42
- "version": ">=1.4.0 <2.0.0"
43
- }
16
+ "node": ">=20"
44
17
  },
45
18
  "publishConfig": {
46
19
  "access": "public",
@@ -60,35 +33,12 @@
60
33
  "search",
61
34
  "optique"
62
35
  ],
63
- "dependencies": {
64
- "@hey-api/client-fetch": "0.13.1",
65
- "@optique/core": "1.2.4",
66
- "@optique/env": "1.2.4",
67
- "@optique/run": "1.2.4",
68
- "@optique/zod": "1.2.4",
69
- "picocolors": "1.1.1",
70
- "ts-pattern": "5.9.0",
71
- "zod": "4.5.2"
72
- },
73
- "devDependencies": {
74
- "@biomejs/biome": "2.5.10",
75
- "@changesets/cli": "2.31.1",
76
- "@changesets/get-github-info": "0.8.0",
77
- "@changesets/types": "6.1.0",
78
- "@commitlint/cli": "21.2.2",
79
- "@commitlint/config-conventional": "21.2.2",
80
- "@hey-api/openapi-ts": "0.0.0-next-20260824173136",
81
- "@limegrass/eslint-plugin-import-alias": "1.6.1",
82
- "@oxlint/plugins": "1.80.0",
83
- "@types/bun": "1.4.0",
84
- "@types/node": "26.4.0",
85
- "@victor-software-house/anti-slop": "0.0.1",
86
- "bun-release": "0.0.0",
87
- "eslint-plugin-zod": "4.10.0",
88
- "liquidjs": "10.27.2",
89
- "oxlint": "1.80.0",
90
- "oxlint-tsgolint": "7.0.2001",
91
- "tsdown": "0.22.14",
92
- "typescript": "7.0.2"
36
+ "optionalDependencies": {
37
+ "@victor-software-house/exa-cli-darwin-arm64": "0.0.2",
38
+ "@victor-software-house/exa-cli-darwin-x64": "0.0.2",
39
+ "@victor-software-house/exa-cli-linux-x64": "0.0.2",
40
+ "@victor-software-house/exa-cli-linux-arm64": "0.0.2",
41
+ "@victor-software-house/exa-cli-linux-x64-musl": "0.0.2",
42
+ "@victor-software-house/exa-cli-windows-x64": "0.0.2"
93
43
  }
94
44
  }
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: exa
3
- description: Use the Exa CLI for live web search, known-page contents, and cited answers. Use when a question needs current web evidence and GitHub source is not already a clone or submodule. Prefer this CLI over ad-hoc HTTP.
3
+ description: Use the Exa CLI for current web evidence, known-page retrieval, cited answers, coding context, or asynchronous Exa Agent research. Prefer it over ad-hoc HTTP; use GitHub tooling or an existing clone or submodule for GitHub implementation truth.
4
4
  ---
5
5
 
6
6
  # Exa CLI
7
7
 
8
- Install the GitHub Release binary with mise (`"github:victor-software-house/exa-cli" = "0.0.0"`). The CLI binary is `exa`.
8
+ Install the GitHub Release binary with mise (`"github:victor-software-house/exa-cli" = "0.0.0"`) or globally from npm (`bun add -g @victor-software-house/exa-cli`, Node 20+). The CLI binary is `exa`.
9
9
 
10
10
  ```bash
11
11
  npx skills add victor-software-house/exa-cli
@@ -13,33 +13,58 @@ npx skills add victor-software-house/exa-cli
13
13
 
14
14
  Auth is `EXA_API_KEY` or `--api-key`. Do not print the key.
15
15
 
16
- ## Cache
16
+ ## Cost discipline
17
17
 
18
- The CLI caches identical requests in SQLite. Do not repeat an identical call. Use `--refresh` only when you need a fresh provider response. `--no-cache` disables the store.
18
+ Every uncached call spends Exa credits. The local SQLite cache makes identical requests within the TTL free a hit prints `cache hit age=…` on stderr. Rules:
19
19
 
20
- Stdout is the payload. Cache hits print on stderr. `--json` is `jq`-friendly provider JSON.
20
+ - Never re-issue an identical request; the response is already cached.
21
+ - `--refresh` only when a cached answer is provably stale.
22
+ - `--no-cache` almost never.
23
+ - Agent commands (`agent create`, `agent get`, `agent wait`, `agent cancel`) are never cached.
24
+ - Housekeeping: `exa cache path`, `exa cache prune` (expired entries), `exa cache clear`.
21
25
 
22
- ## Commands
26
+ The cache key includes a digest of the API key, so switching accounts never serves another account's responses.
27
+
28
+ ## Output
29
+
30
+ Without flags the CLI prints human-readable text, even when piped. For machine consumption always pass `--json` (compact) or `--pretty` (indented; implies JSON). `-o FILE` always writes JSON regardless of the file extension. `--envelope` wraps JSON with cache metadata.
23
31
 
24
- Search (always include `contents.highlights` by default):
32
+ ## Retention workflow
33
+
34
+ Capture the complete response once, project from the file, never re-fetch to re-project:
25
35
 
26
36
  ```bash
27
- exa search "precise query with entity and source qualifier" --include-domain exa.ai --json
37
+ out_dir="${CLAUDE_JOB_DIR:-$(mktemp -d)}/exa"
38
+ mkdir -p "$out_dir"
39
+
40
+ exa search "precise query with entity and source qualifier" \
41
+ --include-domain exa.ai \
42
+ --json -o "$out_dir/search.json"
43
+
44
+ jq '.results[] | {url, title}' "$out_dir/search.json"
28
45
  ```
29
46
 
30
- Known pages:
47
+ ## Commands
31
48
 
32
49
  ```bash
50
+ exa search "precise query with entity and source qualifier" --include-domain exa.ai --json
33
51
  exa contents https://exa.ai/docs/reference/search.md --json
52
+ exa answer "narrow question" --json
53
+ exa similar https://exa.ai/docs/reference/search.md --json
54
+ exa context "how to use React hooks for state management" --tokens-num 500 --json
34
55
  ```
35
56
 
36
- Cited synthesis:
57
+ Agent runs (create is not cached; block with `--wait` or `agent wait` instead of a manual poll loop):
37
58
 
38
59
  ```bash
39
- exa answer "narrow question" --json
60
+ exa agent create "narrow research question" --json
61
+ exa agent create "narrow research question" --wait --timeout 600 --json
62
+ exa agent get agent_run_… --json
63
+ exa agent wait agent_run_… --timeout 600 --json
64
+ exa agent cancel agent_run_… --json
40
65
  ```
41
66
 
42
- Raw body is JSON for that commands generated Hey API schema. Optique parses it; do not pass a file path:
67
+ Raw body is JSON text for that command's generated Hey API schema. Optique parses it; do not pass a file path:
43
68
 
44
69
  ```bash
45
70
  exa search --request '{"query":"Exa search type auto","contents":{"highlights":true}}' --json
@@ -47,4 +72,4 @@ exa search --request '{"query":"Exa search type auto","contents":{"highlights":t
47
72
 
48
73
  Do not treat snippets, citations, or generated answers as proof. Read the cited URL before making a factual claim.
49
74
 
50
- `/context` and Agent runs are not commands on this CLI. GitHub truth stays `gh` or a pinned submodule.
75
+ `/context` is `exa context`. Agent runs are `exa agent create`, `exa agent get`, `exa agent wait`, and `exa agent cancel`. GitHub implementation truth still stays `gh` or a pinned submodule.
package/dist/cli.d.mts DELETED
@@ -1 +0,0 @@
1
- import "zod/compile";