--- name: eximagent description: "Trade-intelligence CLI for coding agents — find real buyers, importers & distributors from customs trade data, then enrich and reach them. Buyer/importer/distributor discovery, B2B lead gen, contact enrichment, cold outreach with stage tracking, tariff + HS-code + corridor + country lookups, OFAC sanctions screening, per-company negotiation memory, company-profile extraction from any website, raw-markdown crawl, product/facility image extraction for multimodal hosts, bulk processing with auto-pick. Use whenever the user mentions eximagent/this CLI/the trade CLI, or asks: find buyers / importers / distributors, look up tariffs, what HS code, sanctions check, draft cold outreach, tell me about this company website, enrich N companies, crawl these websites, show/list/manage saved collections or corridors or templates or knowledge base, trade prospect lists, or any export/import/international-trade task. Invoke `eximagent ` for every step; auth via OAuth device flow or PAT." --- # eximagent — trade-domain CLI for coding agents ## Install If the `eximagent` binary is not already on PATH, install it first — one command, no runtime to set up. **macOS / Linux:** ```bash curl -fsSL https://cli.eximagent.ai/install | sh ``` **Windows (PowerShell):** ```powershell irm https://cli.eximagent.ai/install.ps1 | iex ``` Then authenticate with `eximagent login` (OAuth device flow) or `eximagent login --token `, and verify with `eximagent whoami`. The installer also drops this skill into the host agent's skill directories. ## Quick start 1. `eximagent whoami` — confirm auth (run `eximagent login` if it fails). 2. `eximagent profile get` — see the operator profile that grounds every later turn. 3. `eximagent hscode search --query ""` — disambiguate HS code if the user did not give one. 4. `eximagent search run --product "" --location ` (preview) → `--confirmed` to start. 5. `eximagent collection get --name ` → `eximagent enrich company --url ` for the standouts. 6. `eximagent enrich contacts --collectionId ` (filter-free first pass) → `eximagent email draft --dry-run` → confirm → `eximagent email send --confirm`. 7. Stuck? `eximagent collection analyze --collectionId --question ""` ranks with reasoning. ## Intent map — plain-language user phrase to canonical command | User says | Canonical command | Why | |-----------|-------------------|-----| | "find buyers / importers / distributors" | `search run` | buyer discovery | | "help me understand this company" | `enrich company` | per-company deep crawl + summary | | "find employee contacts" / "find decision makers" | `enrich contacts` | filter-free people search; recall-first | | "find procurement people" | `enrich contacts` then `employees filter --departments procurement` | discover wide, narrow after | | "rank these by size / fit / strategic priority" | `collection analyze --question "..."` | LLM ranking with auditable evidence per rank | | "re-check this person's email" | `enrich contact --employeeId ` | single-row re-verify | | "look up duties / tariff" | `tariff` first, `trade lookup --type ...` fallback | structured trade DB | | "what HS code matches X" | `hscode search --query X` | HS disambiguation | | "screen against sanctions" | `sanctions check --name X` | OFAC SDN | | "save this trade lane" | `corridor save` | reusable corridor | | "draft outreach to these" | `email draft --dry-run` | preview-first | | "send all" | `email send --confirm` | flush with countdown | | "show / list / manage my saved " | ` list` (server-side) | NEVER search local files | Read past this point only when the quick-start chain leaves a question unanswered. The deeper material below explains every command, doctrine, error code, stream protocol, and workflow-state primitive. You have `eximagent`, a CLI of trade-domain primitives. Use it for buyer discovery, trade research, company enrichment, contact enrichment, outreach drafting, sanctions checks, corridor management, raw-page crawls, image-rich multimodal outputs, and trade-memory tasks. You are the orchestrator. `eximagent` is a dispatcher, not an autonomous workflow engine. Each command does one thing; your job is to choose the right order, batch by default, preview expensive steps, and keep the user out of bad runs. ## What this CLI is best at - Fast first-pass buyer discovery from a vague prompt - HS code and tariff research - Building collections of trade prospects - Lightweight company-level enrichment from websites (structured + image-bearing) - Raw-markdown crawl of any website with caching - Bulk processing of large lists (companies, URLs, HS queries) in a single call - Drafting outreach once the target list is already clean ## What this CLI is not good at by itself - Fully trustworthy contact data — you must respect confidence tags - Clean send-ready outreach lists without review - Resolving exact LinkedIn company URLs from raw company names at scale (auto-resolve helps; not perfect) - Anything outside the trade-domain surface Treat `eximagent` as a strong discovery and enrichment helper. The data it returns is honest about its own confidence — read the tags and speak about each level accurately. ## Golden path Use this order unless the user explicitly wants something else: 1. Ground in auth and profile 2. Clarify product, market, and HS code if needed 3. Run buyer search (preview → confirm → stream) 4. Inspect results and shortlist 5. Enrich company data on the shortlist (`enrich company`) 6. Enrich contacts only for validated targets (`enrich contacts`) 7. Draft outreach (`email draft --dry-run` first) Skipping the shortlist step is the most common waste of budget and time. ## Decision tree — if the user asks X, do Y - **Find buyers / importers / distributors** → `profile get` to ground → clarify product + market + HS → `hscode search` if HS unknown → `--dry-run search run` preview → confirm → `search run --confirmed` (capture `runId` from the kickoff response) → `stream --run-id ` and BLOCK until the terminal `complete` event before reading rows - **Look up tariff** → if HS is known: `tariff --exporter --importer --product`; otherwise `hscode search` first - **Custom duties / NTM measures / remedies** → `trade lookup --type duties|taxes|remedies|ntm|all` - **Identify a company from a name** → `company --name "..."` (single) or `--inputs file.ndjson` (bulk) - **Profile a website** → `enrich company --url ...` (single) or `--inputs file.ndjson` (bulk). For just raw markdown without structuring: `crawl --url ...` - **Screen against sanctions** → `sanctions check --name "..."` or `--inputs names.ndjson` - **Draft outreach** → verify list quality first, then `email draft --dry-run` → user confirms → `email send --confirm` - **Show me / my / saved / existing collections|corridors|templates|kb** → these are SERVER-side, owned by the authenticated user. NEVER search the local filesystem, session state, or temp dirs for them. Call the command directly: `collection list` / `corridor list` / `template list` / `kb list`. "saved"/"my"/"existing" never means local files. - **Multimodal "what do these companies actually sell?"** → bulk `enrich company` for the list → pass the returned `keyFacts.images` URLs to your host's vision tool - **A list of N companies / URLs / HS queries** → ALWAYS use `--inputs` bulk shape, never loop - **Who actually ships / imports / exports a product** → `product shipments --hs_code ` or `shipments search --hs6 <6digit> --dest ` — real per-shipment customs records, not a model guess - **A company's real shipment history** → `company shipments --name ""` (matches exporter or importer) - **Trade flowing on a route / lane** → `route shipments --origin --dest ` - **Price / value evidence for a product** → `price shipments --hs_code [--min_weight_kg N]` - **One shipment's full record or raw provenance** → `shipments get --id ` / `evidence show --id ` ## Trade-shipment records `shipments`, `company/product/route/price shipments`, and `evidence show` query actual per-shipment customs + bill-of-lading records — hard evidence of who shipped what, where, when, and (where reported) at what value. Use them when the user wants real trade activity, not website inference. Reference companies by name (`company shipments`) or products by HS code / 6-digit prefix; filter routes by ISO-2 countries. Every shipment response carries a `coverage` envelope: `{coveredCountries[], periodStart, periodEnd, status, confidenceLevel, completenessRatio, usageGuidance, blindSpot}`. Read it before drawing conclusions — `status: covered` is strong evidence; `partial`/`limited` is directional only; `unavailable` means there is no usable data for that query or period, so do not assert absence of trade. Surface the `coverage` honestly to the user instead of overstating completeness. ## Trade-intelligence signals (use these for any number, not raw rows) For any market / buyer / price / volume / concentration / recurrence QUESTION, call a signal verb — never page raw `shipments search` rows and aggregate them yourself. `shipments search` caps at a 1000-row page (browsing only); a metric built from that page is wrong. The signal verbs aggregate server-side over the FULL matching corpus and return a small business-ready result: - `shipments market-signals --hs6 --dest ` — is this market attractive? shipment count, unique buyers/sellers, avg+median price/kg, top5 buyer share (concentration), month-over-month volume + price direction. - `shipments buyer-recurrence --hs6 --dest ` — which buyers are durable accounts? active months, recurring/new/returning counts, retention, repeat-shipment ratio, per-buyer scale. This ranks the outreach shortlist. - `shipments price-trend --hs6 --dest ` — where are prices heading? monthly avg/median/p25/p75/stddev price/kg + MoM change. Sets negotiating posture. - `shipments route-signals --hs6 --dest ` — which origin→destination lanes lead the market, ranked by traded value. All four scope with `--hs6` OR `--dest` (same arg names as `shipments search`) plus optional `--origin`/`--source`/`--from`/`--to` (months `YYYY-MM`). Each response's `nextActions` chain the sibling verbs with valid flags — follow them. Lead the user with the commercial read (concentrated vs broadening, rising vs falling, who recurs), not the data source. Analyze the ENTIRE database — two paths, in this order: 1. **Push the computation down (default).** `analytics query` (structured groupBy × measures × filters) and `analytics sql` (guarded read-only SELECT) aggregate server-side over the FULL corpus and return a small result — you read the answer, not the rows. This is almost always what "analyze the whole db" needs. Read `analytics catalog` first for the schema. 2. **Pull raw rows only when you truly need row-level data.** Both `analytics query` and `analytics sql` page the entire result set: each full page returns a `nextActions` next-page command (cursor) you follow until empty. For a large dump, use `--out [--format ndjson|csv]` — the CLI auto-pages the whole result to disk and prints only a summary; then analyze the file programmatically (`duckdb`, `jq`, pandas) and read only your computed aggregates into context. NEVER read a multi-thousand-row dump into your context — query the file with code. Drill-down on a specific shipment: `shipments get --id --view detail|logistics|financials|parties|evidence` projects richer fields; `evidence show --id --normalized` returns the English-normalized semantic fields alongside the raw source record. The 24-field `summary` view stays the default — request a wider view only when the user needs that depth. Golden trade-intelligence chain: `market-signals` (is it attractive?) → `buyer-recurrence` (who are the durable accounts?) → `price-trend` (what posture?) → `route-signals` (which lane?) → `shipments get --view`/`evidence show --normalized` (verify one account) → `enrich` the shortlist for outreach only once the trade-intelligence picture is clear. Every command is `eximagent [] [--flag value ...]`. The `exim` prefix is optional: ```bash eximagent profile get # preferred, agent-natural eximagent exim profile get # also works (explicit) ``` Args are camelCase across the surface (`exporterCountry`, `hsCode`, `businessType`). Kebab-case (`--hs-code`) is accepted as an alias. Exit codes: `0` success / `1` recoverable (retry with backoff) / `2` fatal (surface to user) / `64` usage error (fix the call). Network: every command makes an HTTPS call to the EximAgent server, so the CLI needs outbound network on port 443. If a call fails with a connection/socket-blocked error (e.g. Windows `connectex … forbidden by its access permissions`), it's THIS environment blocking egress, not the server. Enable network for the run — under codex/a sandbox network is usually off by default, so re-run with it enabled (e.g. `codex --dangerously-bypass-approvals-and-sandbox`); behind a corporate proxy set `HTTPS_PROXY`; or allow the EximAgent host through firewall/antivirus. ## Universal flags - `--inputs ` — bulk input: NDJSON list, one entity per line. Server processes the batch with bounded concurrency. Output: one streamed NDJSON document covering the whole batch. - `--dry-run` — preview only; sets `confirmed=false` + `dryRun=true` server-side. Safe wrapper for any billable / irreversible command. - `--profile ` — switch saved account. Same as `EXIMAGENT_PROFILE=`. - `--strict` — single-input only: opt back into the blocking-candidates flow on ambiguous input. Default is auto-pick. - `--output yaml|table|json` — output format (default: JSON / NDJSON). - `--stream` — NDJSON stream events for long-running commands. - `--out [--format ndjson|csv]` — bulk export for `analytics query` / `analytics sql`: auto-pages the ENTIRE result set to a file (10000 rows/page), printing only a small summary (rowCount, pages, columns, 3-row sample, byte size) to your context. Never streams the whole result into your context. **Shell-safe list args**: comma-list flags (`--titles`, `--departments`) whose values contain shell-sensitive characters (`R&D`, `A&B`, spaces) break an UNQUOTED command — `&` is a shell control operator, so `--titles procurement,R&D,formulation` runs `D,formulation` as a separate command. Two safe forms: (a) **repeat the flag** — `--titles procurement --titles "R&D" --titles formulation` (the CLI merges repeated flags into one comma-list), or (b) **quote the whole value** — `--titles "procurement,R&D,formulation"`. Never emit an unquoted value containing `&`, spaces, `|`, `;`, `$`, or `*`. ```bash # Preview a search without burning credits: eximagent --dry-run search run --product "" --location DE --hsCode 090111 # Bulk: one tool call enriches all rows from an NDJSON file eximagent enrich company --inputs companies.ndjson # Multi-account: eximagent --profile client-a whoami ``` ## Clarification-first — for single-shot human prompts The user often speaks in vague human-like prompts ("find me some buyers", "send outreach", "what's the tariff"). Your job: 1. **Ask back before guessing.** If a required arg is missing or ambiguous, ask ONE concrete question, then wait. Never invent product names, target countries, HS codes, or recipient titles. 2. **Run `eximagent profile get` first** to ground in user defaults (product, targets, signature, incoterm, timezone). 3. **Preview billable / irreversible calls** with `--dry-run` and present the plan + expected cost before confirming. 4. **Never blind-retry `INVALID_ARG`.** That is your bug; read `error.details.expected` and `did_you_mean`, fix the call shape. ### Worked example — vague prompt → clarify → execute User: *"find me some buyers"* ```bash eximagent profile get ``` If profile is empty (`{exists:false, profile:null}`): You (to user): "Quick — what product are you exporting, which country market are you targeting, and the HS code if you know it?" User: *" to "* ```bash eximagent hscode search --query "" ``` Pick the right HS code with the user. Then preview: ```bash eximagent --dry-run search run --product "" --location --hsCode --direction buyers ``` After explicit user "go" — kick off the run, then BLOCK on its terminal event before reading rows: ```bash RID=$(eximagent search run --product "" --location --hsCode --direction buyers --confirmed --output json | jq -r '.data.items[0].runId') eximagent stream --run-id "$RID" # blocks until terminal {kind:"complete"} ``` ## Cold start (every fresh session) ```bash eximagent whoami # verify auth eximagent profile get # ground in user defaults ``` If profile is empty, gather business basics from the user and `profile extract --from text --utterance "..."` before any large workflow. ## Token economy — never loop tool calls over a list Every per-entity tool accepts `--inputs ` with one entity per NDJSON line. The server fans out with bounded concurrency and streams a single result back. One agent tool call → one streamed batch result → one context fill on your side. **The rule:** for any list of more than ~5 items, use `--inputs`. Never loop a per-entity command over a list. Looping burns the host's token budget on N copies of the same prompt + N copies of the same tool-result framing. ```bash # WRONG — burns token budget per row: # for url in $(cat list.txt); do eximagent enrich company --url "$url"; done # RIGHT — one bulk call, one streamed result: jq -R '{url: .}' list.txt | eximagent enrich company --inputs - ``` Bulk output is NDJSON: one `{kind:"row", index, input, output, status, autoResolved?, alternatives?, costCents, durationMs}` event per row, terminal `{kind:"complete", rows, ok, autoResolved, failed, totalCostCents, totalDurationMs}`. The terminal event is the summary the user wants — surface it. ## Scale and batching rules | collection size | safe pattern | notes | |---|---|---| | 1-10 | single calls or bulk; either is fine | tiny enough that loops do not hurt much, but bulk is still preferred for consistency | | 10-50 | bulk only; shortlist before contact enrichment | company-level enrichment ok across the whole set | | 50-200 | bulk; ALWAYS shortlist before contact enrichment | contact enrichment on a noisy 100+ list wastes budget | | 200+ | bulk only; shortlist aggressively; consider `--limit` / `--priority` on enrich-contacts | review the shortlist before billable enrichment | `collection items list` supports cursor pagination — fetch the whole collection without size limits. `enrich contacts` supports `--priority high|medium|low`, `--limit N`, `--row-ids `, `--only-with-website`, `--max-cost-cents N` to scope the run inside a large collection without creating a shortlist collection. **Bulk hard caps**: `--inputs` rows max **1000 per call**. Above that → `INVALID_ARG`. Split into multiple sequential bulk calls if the user has >1000 rows. Server-side concurrency caps at **25 workers**; passing `--concurrency 100` gets silently clamped (response `started` event surfaces `concurrencyClamped:true`). **Long-running ops** (`enrich contacts` on 200+ rows, `search run --confirmed=true`): these can run for several minutes. The CLI prints a stderr heartbeat `[eximagent] still working (Ns)` every 30s so you know the call is alive, and retries transient failures with backoff. The per-call timeout defaults to 180s — raise it for long runs with `EXIMAGENT_TIMEOUT_MS`. DO NOT kill the call before the response lands. **`run` disambiguation**: `eximagent exim run status ` and `eximagent exim run summary ` are top-level commands for any search run. `eximagent exim search run` is the buyer-discovery kickoff. Different verbs, same word — read the path carefully. **Preview→confirm binding**: `exim search run --confirmed=false` returns `previewToken: "pt_..."`. Pass that same token back as `--previewToken` on the `--confirmed=true` call. If any other arg drifts between the two calls (typo fix, HS code added, location reword), server rejects with `INVALID_ARG: previewToken mismatch` — re-run preview to get a fresh token. **Search-run lifecycle — the search MUST run, then BLOCK on its terminal event**: `search run` is read-only discovery, not a billable send — it MUST execute (`--confirmed=true`, or via the preview→confirm pair). "Preview only" / "dry run" in the user's ask refers to the EMAIL draft (`email draft --dry-run`), never the search. Results land only at completion, so `collection get` / `run status` report `running` + `totalCompanies: 0` until the run finishes — this is NORMAL, not a stall, not an empty result. You MUST `eximagent stream --run-id ` (or poll `run status ` every ~10-20s) and BLOCK until the terminal `{kind:"complete"}` event before `enrich`/`draft`/`collection items list`. NEVER re-run `search run` for the same intent (it starts and bills a separate run) and NEVER read company rows off the kickoff response. ## Auto-pick on ambiguity (bulk default) When an input is ambiguous (company name matches multiple plausible websites, HS prefix matches multiple chapters, country alias has alternates), the server picks the top-1 candidate by confidence and emits the row with `autoResolved: true` and `alternatives: [{candidate, score, snippet}, ...]` so you and the user can audit later. - Bulk runs NEVER block on candidates. They never emit a `status:"candidates"` event. - Single runs auto-pick by default. Pass `--strict` to opt back into the blocking-candidates flow when the user explicitly wants interactive disambiguation. - If a pick is wrong, the user asks the agent to redo just that row — overall productivity is far better than confirming every row up front. ## Forward momentum — never stop after a single step Every tool response carries `nextActions: NextAction[]` with the most-natural next steps for the result state. Each entry: `{command, cost, label, rationale}` — `command` is the ready-to-run CLI line, `cost` is the spend tier of that step (`free`/`low`/`medium`/`high`), `label` is a one-line action name, `rationale` is why it follows. Your job: 1. After ANY tool returns successfully, read `nextActions[]` BEFORE replying to the user. 2. If exactly one action is `free`/`low` cost AND the user's original ask covers this step, auto-execute its `command` WITHOUT confirming. 3. Otherwise surface `nextActions[]` to the user as a numbered pick list — one line each, using `label` + `cost`. 4. NEVER say "I'm done" if `nextActions` is non-empty — explicitly state the open steps + ask the user to pick or say "skip remaining". Example: search.run completes. The terminal response carries `nextActions: [{command:"eximagent enrich company ...", cost:"medium", label:"Enrich the shortlist", rationale:"..."}, {command:"eximagent email draft --dry-run ...", cost:"free", label:"Draft outreach", rationale:"..."}]`. User said "find buyers for ready for outreach" — that scope covers enrichment + drafts. Run each `command` in order. ## Reporting discipline — driver must always know what you are doing For ANY multi-step workflow (a chain of tool calls toward one user goal), you MUST emit five narration moments. These are non-negotiable — silent multi-step runs violate the platform contract. 1. **Plan card** before the first billable step. Format: numbered step list with ETA + cost forecast per step, total ETA, total cost forecast, captured scope. 2. **Per-step ENTRY** before each tool call: `[2/4] enriching 30 companies on shortlist...` 3. **Per-step EXIT** after each tool returns: `[2/4] done · 30 enriched · 28 with valid website · 2 unreachable · 78s · $0.61 cumulative` 4. **Decision rationale** when picking a non-obvious branch: `"Picking enrich-company before enrich-contacts because contacts need company data"` 5. **Final English summary** as a colleague would report — not raw JSON. 2-3 sentences. What was done, key numbers, what user should know. Skip-transparency: when you intentionally skip a step (`[3/4 SKIPPED] enrich contacts (scope: shortlist-only)`) — say so. Retry-narration: when a step partially fails and retries — say so. The driver should never have to guess. ## Workflow state on collections `collection get` / `collection list` / `collection items list` / `run summary` all carry `enrichmentStatus: {company, contacts, drafts}` (`not-started` / `partial` / `complete`). Use this to know whether enrich/draft has already happened on a collection — never re-derive, never re-ask the user. ## Output protocol - **stdout** = the result AND every error. NDJSON (one object per line for streams) or a single object one-shot on success — AND on ANY failure (validation, upstream, timeout, network) the structured `{"error":{code,message,retryable,details?},"nextActions"?}` envelope lands on stdout too. ALWAYS parse stdout for both; the exit code (`0` ok / non-zero fail) is the signal, the stdout envelope is the reason. - **stderr** = progress breadcrumbs ONLY — the `[eximagent] still working (Ns)` heartbeat + `{kind:"stage"}` lines. It is NEVER the terminal error and NEVER data. A heartbeat means the call is ALIVE, not failed — do NOT capture stderr (or a wrapper's own generic "Command failed") as the failure reason; the reason is always the stdout envelope with its typed `code`. - Long-running commands emit stage-level events (`{kind:"stage", stage, completed, total, etaMs}`) plus heartbeats so a quiet in-flight run is visibly healthy, then a terminal `{kind:"complete"|"failed"}`. - Use `eximagent run status ` for an on-demand snapshot of any run and `eximagent run summary ` for the per-stage counts + total cost when it finishes. `run status` carries live progress mid-run: `companiesProcessed` (agrees with `collection get`), `expectedCompanies`, `percentComplete`, `stage`, and `lastHeartbeatAt` — use these to track progress and to tell a live run (recent heartbeat) from a stale one, instead of re-running the search. ## Confidence model — read every field's tag, speak accordingly Every contact and company field returned by an enrichment tool carries `{value, source, confidence}`. Levels: - **verified** — upstream provider confirmed the contact. Present as fact: *"Hans Schmidt, procurement manager at ."* - **extracted** — pulled from crawled markdown via regex or model inference. Candidate data: *"An email matching `name@.example` was found on their site."* Never call it verified. - **heuristic** — derived from secondary signals (LinkedIn company-page activity, page-context inference). Suggestion: *"Likely a procurement role based on LinkedIn signals."* - **inferred** — model guess from context. Hypothesis: *"Probably an importer of based on their about-page description."* Always frame as opinion, not fact. When summarizing a row to the user, lead with verified facts and qualify everything below verified. ## Multimodal — image URLs in the output Tool output carries images: - `keyFacts.images: [{url, alt, hint}]` — company-level images (products, facility, team, other) extracted from the crawled site, filtered for legitimate product/facility imagery. - `sellingProducts[i].imageUrls` / `buyingProducts[i].imageUrls` — per-product photos when the page associates them. If your host model is multimodal, **route the image URLs directly to its vision tool** before describing in text. This is the highest-bandwidth signal for "does this company actually do what their text claims". ``` # Example agent flow: # 1. eximagent enrich company --inputs shortlist.ndjson → rows with keyFacts.images[] # 2. for each row, host's vision tool reads the image URLs # 3. agent answers "yes this looks like a specialty manufacturer" with image-grounded evidence ``` Anti-pattern: do NOT paste image URLs to the user as text-only links and ask them to open. Feed images to vision. Phase 1 stores image URLs only (no byte storage); plan around source-page rot by re-enriching when needed. ## State and references — bare name or id Pass any collection, corridor, template, company, knowledge, product, or bookmark by its bare name or its id directly to the matching arg. The server resolves a UUID-shaped value as an Id, otherwise as a name. No sigil, no prefix. ```bash eximagent collection get --collectionId "my-buyers-q2" eximagent corridor remove --name "my-lane" eximagent email draft --collectionId "my-buyers-q2" --templateName "cold-intro" ``` If a reference does not resolve, you get `NOT_FOUND`. Call the matching list command to discover real names, then retry. The current user profile is a singleton — use `profile get`. ## Discovering existing state There is no global "list everything" command. Use the per-kind list: ```bash eximagent collection list eximagent corridor list eximagent template list eximagent kb list eximagent products list eximagent reminder list eximagent monitor list ``` ## Error recovery (typed — never blind-retry) | code | retry? | action | |------|--------|--------| | `INVALID_ARG` | NO | Your bug. Read `error.details.expected` + `did_you_mean` (and `error.details.enum` for enum args — those are the allowed values). Args are camelCase. | | `NOT_FOUND` | NO | Reference does not resolve. Call the matching list command first, then retry. | | `UPSTREAM_ERROR` | YES | Upstream provider 5xx/4xx. Exponential backoff, retry once. If still failing, surface `traceId`. Fall back to authoritative web search when the upstream is the only source. | | `RATE_LIMITED` | YES | Wait per `error.details.retryAfterMs`, retry up to 3x. | | `BUSY` | YES | Another action in-flight on same context. Wait + retry. | | `FORBIDDEN` | NO | Auth failed. Surface "run `eximagent login`" to the user. | | `INTERNAL_ERROR` | NO | Unexpected server fault. Surface `traceId`. | | `CLIENT_TIMEOUT` | YES | The CLI's OWN client-side timeout (default 180s) fired before the server replied — the job may still be running server-side, this is NOT a server failure. `error.details.timeoutMs` shows the limit; the envelope's `nextActions` gives the exact `--stream` re-run. Raise `EXIMAGENT_TIMEOUT_MS` or re-run with `--stream` to watch live progress. Never treat a slow command as a failed one. | | `NETWORK_ERROR` | YES | Could not reach the server from THIS environment (egress blocked / sandbox / proxy), not a server fault. Enable network for the run (under codex: `--dangerously-bypass-approvals-and-sandbox`); behind a proxy set `HTTPS_PROXY`. | ## When to stop and ask the user Pause and ask explicitly when: - The user's product, target market, or HS code is ambiguous and `profile get` does not resolve it. - A billable run >$1 estimated cost is about to fire and the user has not confirmed. - Multiple plausible company identities resolve and the user wants the right one before paying for enrichment (use `--strict` on single calls; in bulk, auto-pick proceeds and the user can audit `autoResolved` rows after). - `email send` is about to flush — never send without explicit user "yes" / "send" / "confirm". - The user's framing of "buyers" vs "sellers" is unclear in context. In every other case, decide and proceed. ## Anti-patterns - Looping a per-entity command over a list. Use `--inputs` bulk shape. - Enriching contacts on a 300+ raw collection before shortlisting. Always shortlist by priority or score first. - Treating `extracted` emails / phones as verified. Read the confidence tag. - Presenting image URLs to the user as text links. Feed images to your host's vision tool. - Sending email without explicit user confirmation. - Blind-retrying `INVALID_ARG`. - Asking the user "which one?" on every ambiguous bulk row. Auto-pick + audit after. - Inventing commands not in the surface below. If a command is not listed, it does not exist. - Using snake_case args (`hs_code`). Use camelCase (`hsCode`); kebab-case alias also accepted. - Invoking `_admin/*` commands. Operator-tier, hidden. ## Recovery playbook - **Collection appears empty mid-run** (`totalCompanies: 0` while `status: running`) → NORMAL, not failure. Results land only at the terminal `complete` event. `stream --run-id ` or poll `run status ` and BLOCK on terminal before reading rows. Do NOT declare failure, do NOT re-run `search run`. - **`BUSY` on the user context** → wait `retryAfterMs`, retry. Surface a one-line "in-flight elsewhere, retrying in Ns" breadcrumb. - **Preview text contradicts intent** → `--direction buyers|sellers` makes the intent explicit. Re-issue the preview before paying. - **Bulk row count smaller than input** → check `failed` count + per-row `status`. Re-run only the failed indices. - **`enrich contacts` returns 0 verified** → fall back to company-level signals + manual review. Do not double down on the same call. - **Source page rot on an image** → re-enrich the company (crawl cache is 100d; image URLs refresh). - **A wrong `autoResolved` pick** → re-run that one row with `--strict` (single) and let the user pick from `alternatives`. ## Minimum viable output per workflow A "good" agent response includes these for each common workflow: - **Buyer discovery** → collection name, total companies, top 5–10 by score, data-quality caveat (drawing on confidence tags), recommended next step (shortlist + enrich). - **Tariff lookup** → corridor (exporter → importer), HS code, duty rate(s), source attribution, notes on remedies if any. - **Company enrichment (bulk)** → row count, ok / autoResolved / failed counts, total cost, 2–3 worth-mentioning highlights (specialty signals, scale signals, image-grounded confirmations), recommended next step. - **Outreach draft** → recipient count, subject + preview of one draft, "ready to send?" confirmation prompt. - **Sanctions check** → hit / no-hit, program (OFAC SDN / SDGT / etc.), alias matched, advisory caveat. ## Tooling expectations eximagent is not a complete workflow tool. Reach outside the CLI for: - **Spreadsheets** for human review of shortlists and exports. CSV-to-NDJSON: `jq -R '{url: .}' < list.txt | eximagent ... --inputs -`. - **Web validation** when an `autoResolved` pick looks suspicious — open the alternative URLs in your host's browser tool. - **Manual review** for contact-extraction noise before any outreach run on extracted contacts. - **Vision tool** for image URLs from any enrich / crawl output (multimodal hosts only). ## Reliability notes - Buyer discovery surfaces some non-buyer pages — confidence tags + shortlist step are the filter. - Extracted emails / phones may be noisy — describe them as candidate data until verified. - Contact enrichment may return 0 verified contacts even on legitimate companies. Fall back to company-level signals. - Long batch operations may have failed rows even when the batch completes — read `failed` count + per-row `status` and re-run the failures with the same `--inputs`. ## Recommended workflow patterns ### Buyer discovery ```bash eximagent profile get eximagent hscode search --query "" eximagent --dry-run search run --product "" --location --hsCode --direction buyers RID=$(eximagent search run --product "" --location --hsCode --direction buyers --confirmed --output json | jq -r '.data.items[0].runId') eximagent stream --run-id "$RID" # blocks until terminal {kind:"complete"}; only then read rows ``` ### Shortlist before enrichment ```bash eximagent collection items list --collectionId "" --priority high --limit 25 ``` Or scope enrich-contacts directly inside the large collection: ```bash eximagent --stream enrich contacts --collectionId "" --priority high --limit 25 --titles "Procurement,Buyer" ``` ### Bulk company enrichment from a CSV/spreadsheet ```bash jq -R '{url: .}' company-urls.txt | eximagent enrich company --inputs - # one bulk call, one streamed result, image URLs included per row ``` ### Raw markdown of a list of websites ```bash jq -R '{url: .}' urls.txt | eximagent crawl --inputs - ``` ### Outreach last ```bash eximagent email draft --collectionId "" --brief "" --dry-run eximagent email send --collectionId "" --confirm ``` ## Common chains ### Full outreach campaign (vague → confirmed) ```bash eximagent profile get RID=$(eximagent search run --product "" --location --hsCode --direction buyers --confirmed --output json | jq -r '.data.items[0].runId') eximagent stream --run-id "$RID" # blocks until terminal {kind:"complete"} eximagent collection items list --collectionId "" --priority high --limit 25 eximagent --stream enrich contacts --collectionId "" --priority high --titles "Procurement,Buying,Purchasing,Sourcing" eximagent email draft --collectionId "" --brief "" --dry-run eximagent email send --collectionId "" --confirm ``` ### Trade Q&A inline ```bash eximagent tariff --exporter VN --importer DE --product "" eximagent hscode search --query "" eximagent company --name "" ``` ### Refine search ```bash eximagent search refine --collectionId "" --addLocation "Austria,Switzerland" RID=$(eximagent search run --product "" --location "DE,AT,CH" --hsCode --direction buyers --confirmed --output json | jq -r '.data.items[0].runId') eximagent stream --run-id "$RID" # blocks until terminal {kind:"complete"} ``` ## Realistic worked examples - **Buyers in a target market for a product** → `profile get` → `hscode search --query ""` → `--dry-run search run --product "" --location --hsCode --direction buyers` → confirm → `search run ... --confirmed` (capture `runId`) → `stream --run-id ` until terminal `complete` → shortlist `--priority high --limit 25` → `enrich company --inputs shortlist.ndjson` → review → `email draft --dry-run`. - **Importers for a different product** → same pattern, `--product ""`, `--hsCode `, `--direction buyers --location `. - **Tariff exposure for a product moving between two countries** → `hscode search --query ""` → `tariff --exporter --importer --product ""` → optionally `trade lookup --exporter --importer --hsCode --type all`. - **Multimodal triage of a bulk-enriched list** → `enrich company --inputs companies.ndjson` → for each `row.output.keyFacts.images`, route URLs to host vision tool → answer "which look like specialty roasters" image-grounded. ## Known limitations - `linkedin lookup` works best with canonical LinkedIn company URLs. Auto-resolve from company name or website URL is supported but not always correct — provide a canonical URL when you have one. - `enrich contacts` is collection-scoped; subset is via the row-subset flags inside that collection, not arbitrary cross-collection selection. - Image extraction is URL-only in Phase 1; if the source page rots, re-enrich. - `--strict` is single-input only. - The crawl cache is 100d; for fresher data, the cache key is the canonical URL. - Long bulk runs respect upstream rate limits; concurrency defaults to 10 and reduces under upstream pressure. ## What not to do - Do not guess product, market, HS code, titles, or send timing. - Do not blind-retry `INVALID_ARG`. - Do not parse stderr as data. - Do not send email without explicit user confirmation. - Do not assume collection rows equal verified contacts — read confidence tags. - Do not assume extracted emails or phones are clean. - Do not run huge enrichments before creating a shortlist. - Do not loop tool calls over a list; use `--inputs`. - Do not invent commands not listed below. - Do not use snake_case args; use camelCase such as `hsCode`. - Do not call hidden `_admin/*` commands. ## Command surface (auto-generated from REGISTRY) Each entry lists the command, what it does, its flags, and one example. Required and schema-bearing flags carry their full description inline; optional flags show name (+ enum) only — the example shows real usage, and sane defaults apply when omitted. For the complete per-flag detail and every example of any command, run `eximagent manifest` (machine-readable JSON of the full tree). ### `eximagent analytics anomalies` Surface month-over-month momentum anomalies (spikes/drops) in a corridor before you ask — scope by --dest, --origin, and/or --hs6; flags months whose measure swings past the threshold. The analyst that works while you sleep. - `--dest` - `--hs6` - `--measure` - `--origin` - example: `eximagent analytics anomalies --dest CO --hs6 090111` ### `eximagent analytics catalog` Get the trade-analytics semantic catalog — the dimensions, measures, filter operators, time windows, and example queries you compose an `analytics query` from. Always read this before composing a query; never guess column names. (no args) - example: `eximagent analytics catalog` ### `eximagent analytics query` Answer ANY analytic question over the full trade corpus — compose a structured AnalyticsQuerySpec (groupBy × measures × filters × orderBy × limit) from `analytics catalog`. Aggregates are dedup-correct over the entire matching corpus, never a row sample. - `--spec (required) — AnalyticsQuerySpec JSON (limit up to 10000/page; add offset + orderBy to page through the ENTIRE corpus — nextActions returns the next-page command): {"groupBy":["hs6"],"measures":["value_usd","shipment_count"],"filters":[{"dim":"dest","op":"=","value":"CO"}],"orderBy":"value_usd","descending":true,"limit":1000,"offset":0}` - example: `eximagent analytics query --spec '{"groupBy":["hs6"],"measures":["value_usd"],"orderBy":"value_usd","descending":true,"limit":10}'` ### `eximagent analytics sql` Escape hatch for analytic questions the structured query cannot express: run a read-only SELECT over the trade corpus. SELECT-only, table-allow-listed, no table functions, auto-LIMITed + time-bounded. Prefer `analytics query`; use this only for the long tail. - `--limit` - `--offset` - `--querySql (required) — A single read-only SELECT over the trade tables (tradeData + signal tables)` - example: `eximagent analytics sql --query_sql "SELECT hs_6, count() FROM tradeData WHERE destination_country='CO' GROUP BY hs_6 ORDER BY 2 DESC"` ### `eximagent collection analyze` Rank every company in a collection against a plain-language analytical question. Each rank carries a 0-100 score plus one-sentence reasoning grounded in auditable evidence (description, match reasons, trade-potential score). Composes evidence per company from the persisted collection rows, calls an LLM with structured output, returns ranked items. - `--collectionId (required) — UUID of the collection to rank` - `--question (required) — Plain-language analytical question to rank against` - `--rankingCriteria` - example: `collection analyze --collectionId js7abc --question "Which of these companies look largest?"` ### `eximagent collection clone` Deep-clone a collection (copies all items + custom columns; status reset to draft). - `--name (required) — Name for cloned collection` - `--sourceId (required) — Source collection ID` - example: `collection clone --sourceId --name 'germany-q3-clone'` ### `eximagent collection columns add` Add an AI column to a collection. Per-row cells are populated by a follow-up streaming pass. - `--collectionId (required) — Target collection ID` - `--label (required) — Column label (display)` - `--outputType enum: text|classification|score` - `--prompt (required) — Per-row prompt; agent fills cells using company context` - example: `collection add-column --collectionId --label 'Reply likelihood' --prompt 'Score 0-100 based on …' --outputType score` ### `eximagent collection columns run` Populate per-row AI cells for an existing column. Sequential call per company; idempotent. - `--collectionId (required) — Target collection ID` - `--columnId (required) — AI column ID (returned by columns add)` - example: `collection columns run --collection-id --column-id reply-likelihood-l8r2` ### `eximagent collection create` Create an empty collection (manual outreach list, no pipeline run). Use this when the user wants to start a list to add companies/contacts to manually, OR bind a default email template, WITHOUT running the search pipeline. For autonomous buyer discovery use `search run` instead. - `--businessType enum: export|import` - `--description` - `--name (required) — Collection name or id` - `--templateName` - example: `collection create --name q2-buyers` ### `eximagent collection get` Get a collection by ID — METADATA ONLY (name, totals, priority counts, status). Does NOT return the company rows; for company rows use `exim collection items list`. - `--collectionId (required) — Collection ID` - example: `collection get --collectionId ` ### `eximagent collection items add` Add a company to a collection. Accepts either an existing companyId OR a companyUrl (auto-upserts into companies on the fly). Use companyName to give the upserted company a display name. Resolve collection by id or name. - `--collectionId` - `--collectionName` - `--companyId` - `--companyName` - `--companyUrl` - `--priority enum: high|medium|low` - example: `collection items add --collectionId --companyUrl https:// --companyName ""` ### `eximagent collection items filter` Filter rows in a collection: drop rows below minScore / not matching priority / without enriched mails. Idempotent — applies to current rows only. - `--minScore` - `--name (required) — Collection name` - `--priority enum: high|medium|low` - `--requireMail` - example: `collection items filter --name germany-q2 --priority high` ### `eximagent collection items list` List the company rows inside a collection — name, score, priority, country, enriched mails, outreach stage. Use this to actually see WHO is in the collection after a search run completes. `collection get` returns only metadata (totals + priority counts); this returns the rows. Sorted by score descending. Target the collection with --collectionId (the id from search output, or the collection name) — same arg as every other collection/email/enrich command — or --name; defaults to your most-recent collection if neither given. Cursor-paginated (nextCursor), no size cap. - `--collectionId` - `--cursor` - `--limit` - `--name` - `--priority enum: high|low|medium` - `--profile` - example: `collection items list` ### `eximagent collection items merge` Merge rows from one collection into another. De-duplicates by companyId. Optionally soft-deletes the source. - `--from (required) — Source collection name (rows copied FROM here)` - `--into (required) — Destination collection name (rows merged INTO here)` - `--removeSource` - example: `collection items merge --from --into ` ### `eximagent collection items remove` Remove a single row from a collection. Either pass --itemId directly OR pass --collectionId/--collectionName + --companyUrl to look up and remove the row for that company. Use `collection items list` to discover names + URLs. - `--collectionId` - `--collectionName` - `--companyUrl` - `--itemId` - example: `collection items remove --itemId ` ### `eximagent collection list` List user collections (most recent first). Tombstoned (soft-deleted) collections are excluded by default — pass --includeDeleted true to see them. - `--includeDeleted` - example: `collection list` ### `eximagent collection remove` Soft-delete a collection (tombstoned; mention chips render as deleted). - `--collectionId (required) — Collection ID to soft-delete` - example: `collection remove --collectionId ` ### `eximagent collection rename` Rename a collection. - `--collectionId (required) — Collection ID to rename` - `--name (required) — New name` - example: `collection rename --collectionId --name 'germany-q3'` ### `eximagent collection similar` Find user's existing collections similar to a free-text query. Token-overlap scoring on (name, industry, targetCountry, query). - `--limit` - `--query (required) — Free-text criteria description (industry, region, product, etc.)` - example: `collection similar --query " importers in "` ### `eximagent collection stats` Aggregate stats for a collection: totals, priority counts, conversion rate, runtime. - `--name (required) — Collection name or id` - example: `collection stats --name germany-q2` ### `eximagent collection update` Update collection metadata (description, businessType, industry, targetCountry). For rename use `collection rename`. For items use `collection items add/remove`. - `--businessType enum: export|import` - `--collectionId (required) — Collection ID to update` - `--description` - `--industry` - `--targetCountry` - example: `collection update --collectionId --description "Q2 push, German roasters"` ### `eximagent companies competitors` Competitive set: companies importing the same HS6 into the same reporting country, with shared-HS6 overlap and their volume vs this company - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies customers` Customers (importers this company sells to): each canonical id, shipments, value, shared HS basket, first/last seen, share of exports — ranked by value - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies graph` Local counterparty neighborhood: top suppliers + customers, competitor count, shared-supplier peers, supplier concentration (single-source risk) - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies list` Unique resolved companies per reporting country × HS6, each with its trade fingerprint - `--country` - `--from` - `--hs6` - `--limit` - `--name` - `--origin` - `--role enum: exporter|importer` - `--to` ### `eximagent companies profile` Full trade fingerprint for one company: HS basket, corridors, counterparties, recurrence - `--country` - `--name` - `--taxId` ### `eximagent companies suppliers` Suppliers (exporters this company buys from): each canonical id, shipments, value, shared HS basket, first/last seen, share of imports — ranked by value - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies trajectory` One company's value/shipment time-series by --bucket, with trend + peak months - `--bucket` - `--country` - `--name` - `--taxId` ### `eximagent company` Company URL + profile + summary. - `--address` - `--country` - `--name (required) — Company name` - `--product` - example: `company --name ""` ### `eximagent company-memory get` Pull the latest outbound + inbound email exchange between the user and a company (PG email + sg_* events). Use with a company name or id to surface negotiation context (last quote subject, last reply snippet). - `--companyId (required) — company UUID of the counterparty` - example: `company-memory get --companyId ` ### `eximagent company resolve` Knowledge-first company resolution: one cheap LLM call that returns the website it already knows (with confidence 0-2 + reason) and the entity splits — BEFORE any paid web-search/crawl. Call this first; escalate to `enrich company` only when escalate=true (confidence<2 or a split). - `--country` - `--name (required) — Company name (may be a packed multi-entity cell)` - `--products` - example: `eximagent company resolve --name "Avianca" --country CO` ### `eximagent company shipments` Shipments where a company is exporter or importer - `--limit` - `--name (required) — company name` ### `eximagent company split` Decompose a packed trade-data company-name cell ('A // B', 'C/O', newline-joined) into its distinct legal entities — DBA/trade names stay as one entity. Use before company lookup / sanctions check / enrich when a name string may hold multiple companies. - `--name (required) — The raw company-name cell to split` - example: `eximagent company split --name "L & S SHRINK SYSTEMS INC. // OXYGEN DEVELOPMENT"` ### `eximagent company verify` Resolve a raw company name to its canonical website/domain — the canonical company identity that collapses name variants. Triage-first (cheap); escalates to a website-verification agent (crawl + reasoning) only when triage is weak. Cached, so repeat verification is free. - `--name (required) — Raw company name to verify` - example: `eximagent company verify --name "Avianca"` ### `eximagent contacts add` Manually add a contact (employee) tied to a company. Accepts either an existing companyId OR a companyUrl (auto-upserts into companies on the fly). Source tagged as 'manual'. - `--companyId` - `--companyName` - `--companyUrl` - `--department` - `--linkedinUrl` - `--location` - `--mail (required) — Email address` - `--name` - `--phone` - `--title` - example: `contacts add --companyUrl https:// --companyName '' --mail name@ --name '' --title 'Procurement Manager'` ### `eximagent corridor get` Get a saved trade corridor by name. Returns the full corridor record (exporter, importer, HS code, defaults, tariff snapshot if any). Run `corridor list` first to find the name. - `--name (required) — Corridor name or id (e.g. fr-de-widgets). Discover via `corridor list`.` - example: `corridor get --name fr-de-widgets` ### `eximagent corridor list` List saved trade corridors (most recent first). (no args) - example: `corridor list` ### `eximagent corridor remove` Remove a saved corridor by name. - `--name (required) — Corridor name to remove` - example: `corridor remove --name ` ### `eximagent corridor save` Save a trade lane (exporter × importer × HS code) as a reusable corridor, referenced later by its name or id and (when starred) surfaced as an empty-chat starter chip. - `--defaultCurrency` - `--defaultIncoterm` - `--exporterCountry (required) — Exporter country — accepts country name, ISO2, ISO3` - `--hsCode` - `--importerCountry (required) — Importer country — accepts country name, ISO2, ISO3` - `--name (required) — Corridor name or id (kebab-case, e.g. fr-de-widgets)` - `--star` - example: `corridor save --name fr-de-widgets --exporter-country France --importer-country Germany --hs-code 854430 --default-incoterm FOB --default-currency EUR --star true` ### `eximagent corridor yield` Deliverable-contact yield SLO per corridor class (market × goods): deliverable contacts per enriched company, flagging every class below target so low-yield lanes surface as a measured class, not an anecdote. (no args) - example: `corridor yield` ### `eximagent country resolve` Resolve a country by name/ISO2/ISO3 - `--query (required) — Country identifier: name, ISO2, or ISO3` - example: `country resolve USA` ### `eximagent crawl run` Fetch raw markdown content for a website. Returns the homepage plus a small set of structurally-relevant internal pages. Reuses the same crawl cache the search pipeline uses, so repeat asks within 100 days are free. For batch crawls use the bulk shape: `eximagent --inputs urls.ndjson exim crawl run` where each row is `{"url": "https://..."}` — one tool call, N parallel crawls, one streamed result. - `--url (required) — Website URL to crawl (https://...). Returns the homepage plus a small set of internal pages (about/contact/products if discovered). Cache-aware: 100-day TTL keyed by canonical URL.` - example: `crawl run --url https://example.com` ### `eximagent duty exposure` Trade-remedy and tariff profile for an HS/origin: effective rate, remedies, protection level - `--dest` - `--from` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent duty fta` FTA utilization for a market: preference used vs full-duty paid, un-utilized FTA by agreement - `--dest` - `--from` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent email cancel` Cancel an in-flight email send for a collection. Pipeline checks the cancel flag between drafts; partial sends already flushed cannot be unsent. - `--collectionId (required) — Collection whose in-flight send should be cancelled` - example: `email cancel --collection-id ` ### `eximagent email draft` Draft personalized outbound emails per recipient WITHOUT sending (preview-only). Renders ConfirmSendCard with countdown; on user confirm the agent flushes the same drafts via `email send --confirm` with the same collectionId + brief + sender args. - `--brief (required) — Outreach brief — what to mention, tone, ask` - `--ccMe` - `--collectionId (required) — UUID of the collection to draft for` - `--profile` - `--senderEmail (required) — From email (rendered into drafts)` - `--senderName (required) — Sender display name` - `--trackingEnabled` - example: `email draft --collectionId --brief 'angle for outreach; soft ask' --senderEmail you@x.com --senderName 'Your Name'` ### `eximagent email followup` Schedule a non-responder follow-up. Wraps monitor.create with signal='non-responder-followup'; on fire, agent assembles drafts + ConfirmSendCard before flushing. - `--after (required) — Wait window before firing (cron or 'every Xd' / 'every Xh')` - `--brief (required) — Follow-up brief (what to add in the second touch)` - `--campaignId (required) — Source campaign / collection id` - `--name (required) — Follow-up name or id` - example: `email followup --campaign-id js7abc --name dach-q2-followup --after 'every 5d' --brief 'gentle nudge with case-study link'` ### `eximagent email history` List sent emails with delivery + engagement counts (delivered / opened / clicked / bounced). - `--collectionId` - `--limit` - example: `email history` ### `eximagent email send` Draft personalized outbound emails per recipient (profile-aware) and send. PREVIEW BY DEFAULT — without --confirm it only drafts and returns a preview, never sends. Pass --confirm to flush; the pre-send sanity guard (signature/sender-email/OFAC) runs first and refuses on any failed check. Writes the sent record on send. - `--brief (required) — Outreach brief — what to mention, tone, ask` - `--ccMe` - `--collectionId (required) — UUID of the collection to email` - `--confirm` - `--dryRun` - `--includeLowConfidence` - `--profile` - `--regenerate` - `--senderEmail (required) — From email (must be operator-owned domain)` - `--senderName (required) — Sender display name` - `--trackingEnabled` - example: `email send --collectionId --brief 'angle; soft ask' --senderEmail you@x.com --senderName 'Your Name' # preview` ### `eximagent employees filter` Filter persisted contacts of a collection by title / department / verification confidence. Pure in-memory filter over the collection rows that enrich contacts already persisted — never re-discovers, never billable. Recall-first doctrine: discover unfiltered, narrow after. - `--collectionId (required) — UUID of the target collection` - `--confidence enum: extracted|verified` - `--departments` - `--titles` - example: `employees filter --collectionId js7abc... --departments procurement` ### `eximagent employees rank` Rank persisted contacts of a collection by one of three criteria: verified-first (deliverability confidence), seniority (title words like director/VP/head), or engagement-likelihood (upstream likely-to-engage flag). Pure in-memory ranking over already-persisted rows; never re-discovers, never billable. - `--by (required) enum: engagement-likelihood|seniority|verified-first — Ranking criterion: by deliverability confidence, by seniority words in title, or by likely-to-engage flag.` - `--collectionId (required) — UUID of the target collection` - `--limit` - example: `employees rank --collectionId js7abc... --by seniority` ### `eximagent enrich company` Structured company profile from the website itself. Crawls homepage + a few internal pages and runs a fast model on raw content to extract: description, industries, selling products (with HS codes), buying products (with HS codes), certificates, expos attended, international trade markers, nearest port, branches, trade potential 0-100. Also regex-extracts contact mails + phones from the crawled markdown (with noise filtering). Two input modes: (a) --url when you have the canonical site; (b) --company when you only have the name — we search the web and either run on the single plausible site OR return a candidates list. Cache-aware (100d). - `--compact` - `--company` - `--strict` - `--url` - exactly one of: `--company` / `--url` - example: `enrich company --url https://` ### `eximagent enrich contact` Re-verify a single employee email via the contact verification provider; fills mail when found. - `--employeeId (required) — Employee ID whose email to re-verify` - example: `enrich contact --employeeId ` ### `eximagent enrich contacts` Enrich a collection (or a row subset of one) with decision-maker contacts. Serves already-resolved named contacts from the trade corpus first (free), then supplements with upstream people-search + verified-email enrichment. Subset filters: --priority (high/medium/low only), --limit N (top N after filter), --row-ids (explicit collectionItem ids), --only-with-website (skip companies without canonical site), --max-cost-cents (hard budget cap; loop stops at threshold). Confidence model on emitted contacts: verified-source → confidence=verified (deliverable email); probable-source → confidence=extracted (probable email shape). Agents should state verified emails as fact; extracted as candidate data. - `--collectionId (required) — UUID of the target collection` - `--departments` - `--limit` - `--maxCostCents` - `--onlyWithWebsite` - `--priority enum: high|low|medium` - `--rowIds` - `--seniorities` - `--titles` - example: `enrich contacts --collectionId js7abc... --titles 'procurement manager,buyer'` ### `eximagent evidence show` Show raw evidence for one shipment (source provenance) - `--id (required) — shipment id` - `--normalized` ### `eximagent hscode search` Search HS codes; cascades lexical -> hybrid (embedding) -> llm-expanded as result quality demands. - `--level enum: section|chapter|heading|subheading` - `--limit` - `--query (required) — HS code (digits) or product keyword` - example: `hscode search --query "frozen shrimp"` ### `eximagent ideal-profile delete` Delete a saved ideal customer profile. - `--name (required) — Profile name` ### `eximagent ideal-profile get` Get a saved ideal customer profile. - `--name (required) — Profile name` ### `eximagent ideal-profile list` List saved ideal customer profiles. (no args) ### `eximagent ideal-profile save` Save or update an ideal customer profile with optional custom weights. - `--name (required) — Profile name` - `--profile` - `--weights` ### `eximagent kb add` Register a knowledge-base entry. Accepts either (a) uploaded file via --fileId (from kb upload-url flow), OR (b) text content via --content (no upload needed). Content gets injected into email-gen + profile-extract context. - `--businessType enum: export|import` - `--content` - `--fileId` - `--fileSize` - `--fileType` - `--filename (required) — Display filename` - `--tags` - example: `kb add --fileId --filename pricing.pdf --tags 'pricing,sales'` ### `eximagent kb get` Get a knowledge-base entry by filename. Returns the full record including tags + metadata + (truncated) content. For a quick excerpt only, use `kb preview`. - `--filename (required) — Filename of the KB entry. Discover via `kb list`.` - example: `kb get --filename brochure.pdf` ### `eximagent kb list` List user knowledge-base entries. (no args) - example: `kb list` ### `eximagent kb preview` Preview a knowledge-base file: returns a content excerpt + classification (B/L / COO / invoice / etc.) + tags so user can verify content without downloading. - `--kbId (required) — KB entry id` - example: `kb preview --kb-id ` ### `eximagent kb remove` Delete a knowledge-base entry by ID. - `--id (required) — KB entry ID` - example: `kb remove --id ` ### `eximagent landed cost` Landed-cost breakdown for an HS/market: FOB, freight, insurance, duties, per-kg and over-FOB - `--dest` - `--from` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent linkedin lookup` LinkedIn company page batch lookup. Accepts canonical LinkedIn URLs via --urls AND/OR company names or website URLs via --queries — names/URLs auto-resolve to canonical LinkedIn pages via web search before scrape. Cached 100 days per resolved URL. Returns structured profile data keyed by resolved URL plus a resolutions report so the agent can audit which queries mapped to which URL. - `--queries` - `--urls` - example: `linkedin lookup --urls "https://linkedin.com/company/,https://linkedin.com/company/"` ### `eximagent market anomalies` Trend-relative anomalies for an HS6 market: price-shock and volume-spike periods vs the market's own baseline - `--bucket enum: month|quarter` - `--dest (required) — destination country (ISO 2) — the market whose movement to read (required)` - `--from` - `--hs6 (required) — HS code (6-digit) for the traded market (required)` - `--origin` - `--source` - `--to` ### `eximagent market churned` Lapsed importers: active before --since, gone after - `--country` - `--hs6` - `--limit` - `--since` ### `eximagent market compare` Compare one HS6 across destinations side by side: value, price/kg, growth, top buyer — the cross-country arbitrage and sizing view - `--dests (required) — comma-separated destination ISO2 list to compare the HS across` - `--hs6 (required) — HS code (6-digit) for the traded market (required)` ### `eximagent market entrants` New importers in a market (country×HS6), no history before --since - `--country` - `--hs6` - `--limit` - `--since` ### `eximagent market growth` Growing importers: recent value over prior by --min_growth - `--country` - `--hs6` - `--limit` - `--minGrowth` - `--since` ### `eximagent market momentum` Rank a destination's HS6 markets by recent-vs-prior growth — the emerging and declining surfaces - `--dest (required) — destination country (ISO 2) to scan for hot/cooling HS6 markets (required)` - `--hs2` - `--limit` - `--source` ### `eximagent market trend` Market time-series for an HS6 into a destination: value, buyers, price, MoM/YoY growth, momentum and a rising/falling/flat verdict - `--bucket enum: month|quarter` - `--dest (required) — destination country (ISO 2) — the market whose movement to read (required)` - `--from` - `--hs6 (required) — HS code (6-digit) for the traded market (required)` - `--origin` - `--source` - `--to` ### `eximagent monitor cancel` Cancel (disable) a monitor by name. - `--name (required) — Monitor name to cancel` - example: `monitor cancel --name germany-replies` ### `eximagent monitor create` Create a recurring SIGNAL-BASED monitor that fires on trade-domain events (reply arrived, non-responder follow-up, new company matching criteria, tariff change, dead domain). NOT for date-based reminders — use `reminder create` for those. - `--description (required) — What this monitor watches` - `--enrollSequenceId` - `--name (required) — Monitor name or id` - `--schedule (required) — Schedule: hourly|daily|weekly or 'every m|h|d|w' (e.g. 'every 6h')` - `--signal (required) enum: reply-arrived|non-responder-followup|new-company-matching-criteria|tariff-change|dead-domain — Signal kind to watch` - `--target (required) — Target identifier (collectionId, criteria JSON, etc.)` - example: `monitor create --name reply-watch --signal reply-arrived --target campaign-123 --schedule 'every 1h' --description 'Watch for inbound reply'` ### `eximagent monitor get` Get a saved monitor by name. Returns the full record (signal, target, schedule, enabled). Run `monitor list` first if you need to discover the name. - `--name (required) — Monitor name. Discover via `monitor list`.` - example: `monitor get --name ` ### `eximagent monitor list` List user SIGNAL-BASED monitors (enabled by default). Monitors are recurring, fire on trade events (reply-arrived, tariff-change, etc.). NOT the same as `reminder list` (one-shot date-based). When user asks broadly "what watches/alerts do I have", call this AND `reminder list` to surface both. - `--includeDisabled` - example: `monitor list` ### `eximagent pipeline stop` Cancel a running search and clear busy state. With --runId, cancels that specific run; without it, cancels the user's in-flight run. Marks the run as cancelled. Idempotent — no-op when nothing is running. - `--runId` - example: `pipeline stop` ### `eximagent policy get` Show the current outreach policy. (no args) ### `eximagent policy history` List every saved outreach-policy version (audit trail). (no args) ### `eximagent policy set` Set the outreach policy that grounds reply drafting: approved facts, rules (e.g. price_floor), and guardrails (blocked_terms, require_nda_before_coa). Each save bumps the version. - `--facts` - `--guardrails — JSON guardrails, e.g. {"blockedTerms":["guarantee"],"unavailableCerts":["HALAL"],"tone":"warm, concise","requireNdaBeforeCoa":true,"requireSignature":true,"autoSendSafe":false}` - `--rules — JSON rules, e.g. {"priceFloor":10,"signature":"— Phuc, EximAgent"}` - example: `eximagent policy set --rules '{"priceFloor":10}'` ### `eximagent price shipments` Shipments for an HS code above a weight threshold - `--hsCode (required) — HS code (4/6/8/10 digit prefix)` - `--limit` - `--minWeightKg` ### `eximagent product shipments` Shipments matching an HS code prefix - `--hsCode (required) — HS code (4/6/8/10 digit prefix)` - `--limit` ### `eximagent products add` Save or update a product in the user profile. Image uploaded via products upload-url flow. - `--businessType enum: export|import` - `--category` - `--description` - `--hsCode` - `--imageId` - `--moq` - `--name (required) — Product name or id` - example: `products add --name 'premium widget A' --moq '5 tons' --description 'industrial grade, custom spec'` ### `eximagent products get` Get a saved user product by name. Returns the full product record (description, HS code, MOQ, category, image). Run `products list` first to discover the name. - `--name (required) — Product name or id. Discover via `products list`.` - example: `products get --name premium-widget-a` ### `eximagent products list` List user products. - `--businessType enum: export|import` - example: `products list` ### `eximagent products remove` Remove a product from the user profile. - `--name (required) — Product name to remove` - example: `products remove --name premium-widget-a` ### `eximagent profile-match analyze` Extract an ideal-customer profile from one or more reference companies (url/name/text, positive or negative) and save it for ranking. - `--apply` - `--collectionId` - `--name` - `--references — JSON array of references: [{"type":"url|text","value":"...","polarity":"positive|negative"}]` - `--url` - example: `eximagent profile-match analyze --url https://acme-importer.com --name specialty-coffee` ### `eximagent profile-match lookalike` Find companies that trade like a seed company — same products and source markets derived from real shipment behavior in the corpus, ranked by trade-axis fit. - `--limit` - `--name (required) — Seed company name to find trade-axis lookalikes for` - `--role` - example: `eximagent profile-match lookalike --name "Acme Coffee Importers" --role importer` ### `eximagent profile-match rank` Rank the companies in a collection by fit against a saved ideal profile, with explained scores. - `--collectionId (required) — Collection of candidate companies to rank` - `--name (required) — Saved ideal profile name` - example: `eximagent profile-match rank --collection_id --name specialty-coffee` ### `eximagent profile-match score` Fit-rank ANY list of companies against a saved ideal profile — the general fit primitive: pipe in search results, an enrichment batch, or any hand-built list; each gets an explained fit score + band + matched/missing attributes. Fit is a lens for every surface. - `--companies (required) — Companies to score: JSON [{"name":"...","website":"..."}]` - `--name` - `--references` - example: `eximagent profile-match score --name specialty-coffee --companies '[{"name":"Acme Coffee","website":"acme.com"}]'` ### `eximagent profile-match search` Search the company corpus and rank hits by fit to a saved ideal profile or inline references — fit-ranking without a pre-built collection. - `--country` - `--market` - `--name` - `--product` - `--query` - `--references` - example: `eximagent profile-match search --name specialty-coffee --product coffee` ### `eximagent profile-match watch` Turn a saved ideal profile into a self-growing watchlist — a scheduled agent that keeps finding new strong-fit companies and accumulates them in a 'watch:' collection. - `--name (required) — Saved ideal profile name to watch` - `--schedule` - example: `eximagent profile-match watch --name specialty-coffee --schedule daily` ### `eximagent profile extract` Extract user-profile fields (company, products, markets, role, language, contacts) from text utterance OR URL crawl OR file content. Default behavior: persists extracted fields to the profile (apply=true). Pass --apply false to preview the extraction without writing. - `--apply` - `--content` - `--filename` - `--from (required) enum: text|url|file — Source kind: 'text' = extract from utterance; 'url' = crawl website then extract; 'file' = use provided text content` - `--url` - `--utterance` - example: `profile extract --from text --utterance "I export industrial fans from France"` ### `eximagent profile get` Read the user's business profile. (no args) - example: `profile get` ### `eximagent profile reset` Reset the current user profile to empty. Irreversible. Use when the user explicitly asks to "start fresh" / "clear my profile" / "reset". Other artifacts (collections, corridors, templates, etc.) are not touched — remove those with their own `remove` verbs. (no args) - example: `profile reset` ### `eximagent profile update` Update fields on the user's business profile. Omitted fields are preserved. - `--company` - `--companyDescription` - `--companyLinkedin` - `--description` - `--industries` - `--job` - `--note` - `--preferredLanguage` - `--role` - `--signature` - `--sources` - `--targets` - `--userLinkedin` - `--websites` - example: `profile update --signature 'Best, / '` ### `eximagent prospects find` Compound lead query: growing importers of an HS6 in a market who buy from a non- supplier, have a deliverable email, and are not already in a collection - `--excludeCollection` - `--growing` - `--hasEmail` - `--hs6 (required) — HS code (6-digit) to scope the market` - `--limit` - `--market` - `--minGrowth` - `--since` - `--supplierNot` - example: `eximagent prospects find --hs6 090111 --market us --growing --supplier_not br --has_email` ### `eximagent reminder cancel` Cancel a pending reminder by name. - `--name (required) — Reminder name to cancel` - example: `reminder cancel --name ` ### `eximagent reminder create` Create a one-shot DATE-BASED reminder that fires at a specific ISO timestamp (e.g. "remind me Monday 9am to review the German campaign"). NOT for recurring or signal-based watches — for those use `monitor create`. Use `reminder list` to see saved reminders. - `--description (required) — What to remember` - `--name (required) — Reminder name or id` - `--tz` - `--whenIso (required) — When to fire (ISO 8601, e.g. 2026-05-15T10:00:00Z)` - example: `reminder create --name --whenIso 2026-05-15T10:00:00Z --description 'Check reply received'` ### `eximagent reminder get` Get a saved reminder by name. Returns the full record (description, fireAt, fired). Run `reminder list` first to discover the name. - `--name (required) — Reminder name. Discover via `reminder list`.` - example: `reminder get --name ` ### `eximagent reminder list` List user DATE-BASED reminders (pending by default). Reminders are one-shot, fire at a specific ISO timestamp. NOT the same as `monitor list` (recurring trade-signal watches — replies, tariff changes, etc.). When user asks "what reminders do I have", call this AND `monitor list` to surface both. - `--includeFired` - example: `reminder list` ### `eximagent reply approve` Send an AI-drafted reply after review. Preview by default; --confirm sends. A suppression check runs first and refuses on a suppressed recipient. - `--confirm` - `--reviewId (required) — Reply review UUID` - example: `eximagent reply approve --reviewId --confirm` ### `eximagent reply edit` Edit an AI-drafted reply's subject/body before approving; keeps it pending. - `--body` - `--reviewId (required) — Reply review UUID` - `--subject` ### `eximagent reply list` List AI-drafted replies awaiting human review. Replies are grounded in the outreach policy and validated against its guardrails; any violation or handoff signal routes to review. (no args) ### `eximagent reply reject` Reject an AI-drafted reply; nothing is sent. - `--reviewId (required) — Reply review UUID` ### `eximagent reply request-rewrite` Ask the AI to redraft a reply, optionally with reviewer guidance; result stays pending. - `--guidance` - `--reviewId (required) — Reply review UUID` ### `eximagent reply show` Surface the most-recent inbound reply for the user (or scoped to a collection). Returns {fromEmail, subject, snippet, summary, receivedAt}. Populated by the inbound-mail webhook. - `--collectionId` - example: `reply show` ### `eximagent route shipments` Shipments on an origin→destination country route - `--dest (required) — Destination country (ISO 2)` - `--limit` - `--origin (required) — Origin country (ISO 2)` ### `eximagent run cancel` Cancel an in-flight async run, keyed by --runId OR --collectionId, and release its idempotency slot so a fresh run can start. Marks the run terminal (status=cancelled) and refunds the reserved estimate so cancelled work is never billed. No-op-safe: cancelling a run that is already terminal returns NOT_FOUND. After cancel use `run retry` to re-run cleanly. - `--collectionId` - `--runId` - exactly one of: `--runId` / `--collectionId` - example: `run cancel --collectionId 1f2...` ### `eximagent run retry` Re-run a collection's discovery with a fresh idempotency slot after a previous run finished, failed, stalled, or was cancelled. Cancels any still-running run first (refunding its reserve), then kicks off a new run over the same saved criteria. Pass --newIdempotencyKey to label the retry; the server always allocates a fresh run id so paid work is never duplicated. - `--collectionId (required) — Collection UUID whose discovery to re-run.` - `--newIdempotencyKey` - example: `run retry --collectionId 1f2... --newIdempotencyKey retry-1` ### `eximagent run status` Current snapshot of an async run, keyed by --runId OR --collectionId. Works on in-flight runs (status=running with partial counts), finished runs (status=completed/cancelled/failed), and stalled runs (status=stalled when the heartbeat has gone silent past the stall threshold). Returns the parent collection name, start/end timestamps, companies processed/expected, percentComplete, reserved/charged/refunded cents, canRetry + retryAfterSeconds, and the priority breakdown when available. This is the same envelope a duplicate Idempotency-Key returns and the /status route emits. - `--collectionId` - `--runId` - exactly one of: `--runId` / `--collectionId` - example: `run status --runId 9b3...` ### `eximagent run summary` Per-run summary card after a search pipeline finishes (or while it is still running). Returns criteria + query + start/finish timestamps + duration + total companies + priority breakdown + model used + status. For the live snapshot of an in-flight run use `exim run status`; this command is the post-mortem view that prints the full criteria back so the agent can re-run with the same shape. - `--runId (required) — Run UUID; finished runs preferred.` - example: `run summary --runId 9b3...` ### `eximagent sanctions check` Screen a name against the OFAC SDN sanctions list (Specially Designated Nationals). Returns matches with program (e.g. SDGT/IRAN/CUBA) and aliases. Substring + alias match. Always advisory — final clearance lives with the founder/legal. - `--name (required) — Company or person name to screen against OFAC SDN list` - example: `sanctions check --name ""` ### `eximagent search refine` Preview an additive/subtractive refinement of an existing collection's criteria. Returns a diff card (kind: 'refine-preview') showing which filters add/remove + expected company-count delta. After user confirms, caller should invoke `exim search run --confirmed=true` with merged criteria. - `--addExcludeBusinessType` - `--addExcludeLocation` - `--addIndustry` - `--addLocation` - `--collectionId (required) — collection UUID to refine` - `--removeExcludeBusinessType` - `--removeExcludeLocation` - `--removeIndustry` - `--removeLocation` - example: `search refine --collection-id --add-location "Austria,Switzerland"` ### `eximagent search run` Autonomous buyer/importer discovery. Finds candidate companies, reviews and scores each, ranks top-K, and saves them to a Collection. Preview is mandatory — first call (confirmed=false) returns a criteria preview; user confirms → second call (confirmed=true) starts the non-blocking run. Emits progress events to the run. - `--category enum: export|import` - `--confirmed` - `--direction enum: buyers|sellers` - `--excludeBusinessType` - `--excludeLocation` - `--hsCode` - `--industry` - `--location` - `--name` - `--previewToken` - `--product (required) — Product to find buyers/sellers for` - `--targetBusinessDescription` - example: `search run --product "" --location Germany --category import` ### `eximagent sequence create` Create a multi-step email sequence (cadenced follow-ups). Steps is a JSON array of {waitDays,subject,body}; templates support {first_name},{company},{sender_name},{signature}. - `--name (required) — Sequence name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature} in templates` - `--steps (required) — JSON array of steps {waitDays,subject,body,variants?:[{subject,body}]}; variants A/B auto-converge to the best by click-rate` - example: `eximagent sequence create --name X --senderEmail you@x.com --steps '[{"waitDays":0,"subject":"Hi"}]'` ### `eximagent sequence from-brief` Draft a multi-step sequence from a natural-language brief (AI), saved as draft. - `--brief (required) — Natural-language outreach brief` - `--name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature}` ### `eximagent sequence from-template` Create a new draft sequence from a saved template. - `--name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature}` - `--templateName (required) — Template to instantiate` ### `eximagent sequence list` List your email sequences and their status. (no args) ### `eximagent sequence metrics` Show a sequence's enrollment counts by status (enrolled/active/replied/completed/suppressed). - `--sequenceId (required) — Sequence UUID` ### `eximagent sequence pause` Pause an active sequence; no further steps send until resumed. - `--sequenceId (required) — Sequence UUID` ### `eximagent sequence start` Activate a sequence and enroll a collection's recipients. Preview by default; --confirm sends real emails on schedule. A reply from a recipient auto-stops their remaining follow-ups. - `--collectionId (required) — Collection UUID to enroll` - `--confirm` - `--sequenceId (required) — Sequence UUID` - example: `eximagent sequence start --sequenceId --collectionId --confirm` ### `eximagent sequence template-from-trade` Generate a sequence template from a seed company's real customs trade pattern (AI), saved as a reusable trade-seeded template — outreach copy tuned to how companies like the seed actually trade. - `--name (required) — Template name` - `--seedCompany (required) — Seed company whose corpus trade pattern seeds the template` ### `eximagent sequence template-save` Save a reusable sequence template (named set of steps) for fast sequence creation. - `--name (required) — Template name` - `--steps (required) — JSON array of steps {waitDays,subject,body,variants?:[{subject,body}]}; variants A/B auto-converge to the best by click-rate` ### `eximagent sequence templates` List your saved sequence templates. (no args) ### `eximagent sequence validate` Check a sequence is launch-ready (sender set, steps present, policy configured). - `--sequenceId (required) — Sequence UUID` ### `eximagent shipments buyer-recurrence` Buyer recurrence cohort: repeat buyers, retention and shipment scale - `--buyer` - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` - example: `eximagent shipments buyer-recurrence --hs6 090111 --dest DE` ### `eximagent shipments get` Get one shipment by deterministic id - `--id (required) — shipment id` - `--view enum: summary|detail|logistics|financials|parties|evidence` ### `eximagent shipments market-signals` Market-level demand, concentration and momentum signals for an HS or destination - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` - example: `eximagent shipments market-signals --hs6 090111 --dest DE` ### `eximagent shipments price-trend` Monthly price-per-kg trend, dispersion and concentration shift - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` - example: `eximagent shipments price-trend --hs6 090111 --dest DE` ### `eximagent shipments route-signals` Origin to destination route signals ranked by traded value - `--dest` - `--from` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` - example: `eximagent shipments route-signals --hs6 090111 --dest DE` ### `eximagent shipments search` Search shipments across all dimensions - `--dest` - `--hs6` - `--hsCode` - `--limit` - `--name` - `--origin` - example: `eximagent shipments search --hs6 090111 --dest DE --limit 50` ### `eximagent stats show` Conversational analytics on sent emails: reply-rate / open-rate / click-rate / bounce-rate computed from PG email + sg_* event tables. Optionally scope to a single collection. - `--collectionId` - `--limit` - example: `stats show` ### `eximagent suppression add` Manually suppress an email from all outreach. - `--email (required) — Email address to suppress` ### `eximagent suppression list` List manually suppressed emails. (no args) ### `eximagent suppression remove` Remove a manual suppression. - `--email (required) — Email address to un-suppress` ### `eximagent tariff` Trade/tariff/duty for any corridor. --product optional (omit for corridor overview). - `--exporter (required) — Sending country — accepts country name, ISO2, ISO3 (e.g. "Viet Nam", "VN", "VNM")` - `--importer (required) — Receiving country — accepts country name, ISO2, ISO3 (e.g. "Germany", "DE", "DEU")` - `--product` - example: `tariff --exporter --importer --product ` ### `eximagent template add` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` - example: `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent template create` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` - example: `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent template delete` Delete a saved email template by name. Irreversible. NOT for cancelling a draft batch — for that the draft layer has its own flow. - `--name (required) — Template name to delete. Discover via `template list`.` - example: `template remove --name ` ### `eximagent template edit` Update a saved template. Pass only fields to change; others stay. - `--category` - `--content — New body (replaces if provided; supports {{variables}} + [FILE_ID:...] markers)` - `--name (required) — Template name or id` - `--subject — New subject (replaces if provided; supports {{variables}})` - example: `template edit --name cold-intro --content 'Hi {{name}}, ...'` ### `eximagent template generate` Generate an email template (subject + body with {{variables}}) from a free-text prompt. Caller saves via `template save`. - `--prompt (required) — Prompt: tone + intent + product + audience (e.g. "casual cold outreach for B2B importers")` - example: `template generate --prompt 'casual cold-outreach for first contact, B2B importers'` ### `eximagent template get` Read a saved template by name (full content + variables + attachments). - `--name (required) — Template name or id` - example: `template recall --name cold-intro` ### `eximagent template list` List the user saved email templates. (no args) - example: `template list` ### `eximagent template recall` Read a saved template by name (full content + variables + attachments). - `--name (required) — Template name or id` - example: `template recall --name cold-intro` ### `eximagent template remove` Delete a saved email template by name. Irreversible. NOT for cancelling a draft batch — for that the draft layer has its own flow. - `--name (required) — Template name to delete. Discover via `template list`.` - example: `template remove --name ` ### `eximagent template save` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` - example: `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent trade lookup` Search trade data: tariffs, duties, remedies, NTM measures by importer/exporter/product. - `--exporter (required) — Sending country — accepts country name, ISO2, ISO3 (e.g. "Viet Nam", "VN", "VNM")` - `--hsCode` - `--importer (required) — Receiving country — accepts country name, ISO2, ISO3 (e.g. "Germany", "DE", "DEU")` - `--limit` - `--product` - `--type enum: all|duties|taxes|remedies|ntm` - exactly one of: `--hsCode` / `--product` - example: `trade lookup --exporter US --importer VN --hs-code 730441` ### `eximagent usage` Show today's metered spend for the calling account: net charged cents (after refunds), the portion settled on real LLM/vendor usage, estimated, refunded, against the daily cap. (no args) - example: `eximagent usage` ### `eximagent watch company` Register a proactive watch on a company (by tax id + country, or name); alerts when it imports again or its supplier set changes. - `--country` - `--event enum: imports|supplier-change` - `--name` - `--taxId` ### `eximagent watch list` List the caller's active proactive watches with their ids. (no args) ### `eximagent watch market` Register a proactive watch on a market (HS6 × destination); alerts on new-entrant, price-shock, volume-spike, or momentum events as the corpus updates. - `--dest` - `--event enum: new-entrant|price-shock|volume-spike|momentum` - `--hs6` ### `eximagent watch remove` Remove (disable) one of the caller's watches by id. - `--id (required) — watch id to remove`