Dench CLI
Agent-facing CLI for the Dench agent workspace.
Usage
Run without installing. The npm package is @dench.com/cli, but the binary
it installs is named dench, so always invoke it as
npx -y -p @dench.com/cli dench <command>. A bare
npx -y @dench.com/cli <command> will fail because npx looks for a bin
matching the package's last path segment (cli) and there isn't one.
npx -y -p @dench.com/cli dench signin --name "AI Agent - Billing Repo"
Or install globally:
npm install -g @dench.com/cli
dench signin --name "AI Agent - Billing Repo"
--kind accepts any string. Suggested values: claude_code, codex, cursor,
hermes, openclaw, or any custom kind such as aider, goose, or
some_custom_agent. Defaults to other when omitted. Values are normalized
to lowercase snake_case before send (e.g. --kind "Claude Code" becomes
claude_code).
The default backend is https://dench.com (production). Use dench backend
to switch:
dench backend show
dench backend set production
dench backend set staging
dench backend set local # http://localhost:3000
dench backend set https://example.com # custom URL
dench backend clear # revert to production
For one-shot overrides (no config change):
dench --prod <cmd>
dench --staging <cmd>
dench --backend local <cmd>
dench --host https://example.com <cmd>
DENCH_HOST=https://example.com dench <cmd>
The smooth setup flow is:
- Run
dench signin --name "AI Agent - Billing Repo"once. - Let the CLI open the approval link. If it cannot, ask the human to open the
printed link and confirm the email/workspace. Use
--no-opento print the link without opening it. - Run
dench status --mine --jsonanddench crm entries list task --jsonto verify setup. Tasks live as CRMtaskentries — there is no separatedench taskscommand.
The CLI selects the approved session for later commands automatically. Setup is complete after approval, both verification commands work, and you know which Dench agent/session is selected.
Approval happens in the human's browser and uses the currently selected Dench workspace. The human should switch to the intended workspace before approving. The CLI opens the approval link automatically when possible. If opening fails, use the printed link manually.
If multiple sessions already exist, do not log in again to fix ambiguity and do not silently guess from the newest timestamp, agent name, or kind. List sessions and ask the human which exact session to use:
dench sessions
dench use <session-key-or-workspace-slug>
Use a human-readable DENCH_SESSION_KEY at sign-in time only for long-lived
agents or when the human asks for a stable identity:
DENCH_SESSION_KEY=billing-repo-agent dench signin --name "AI Agent - Billing Repo"
DENCH_SESSION_KEY=billing-repo-agent dench status --mine --json
Do not set DENCH_SESSION_KEY to values like auto:... or explicit:....
Those are internal scopes shown by dench sessions; select them with
dench use <session-key-or-workspace-slug> instead.
dench logout removes only the current selected session for this local agent
context. Other saved sessions can still be reused by future chats. To remove the
wrong local session, run dench logout --session <session-key>. Use
dench logout --all only when you want to clear every local Dench session.
Do not create or edit local agent memory files during setup. Use Dench memory only when the human asks or when saving a stable workspace fact.
Tasks live as CRM task entries — there is no separate dench tasks,
dench task, dench claim, or dench log command. Create one via
dench crm entries create task --data '{"Name":"…","Status":"To Do"}'
only when the human assigns durable work or coordination benefits from
a tracked entry (confirm field names with dench crm fields list task
first — unknown fields are rejected). For ad-hoc requests, work directly.
Use dench context when you need to know who you are, which workspace you
are using, connected apps, and the best
next commands. Add --json for structured output.
Past chat threads
Browse and search the user's past chat threads in this workspace (own + shared). Useful for pulling in earlier context when the user refers to something they discussed before. The shared chat harness uses these same CLI commands through its execution tool; there is no separate agent-only chat-history command family.
dench chat list [--query <substr>] [--limit N] [--include-archived] [--json]
dench chat search "<query>" [--limit N] [--json]
dench chat read <thread-id> [--limit N] [--json]
Visibility scope: only threads created by the current user, plus threads
in the same organization marked as shared.
dench chat search is a keyword full-text search across past message
text — pick concrete tokens that the prior chat would have used (file
paths, product names, error strings) rather than paraphrases.
Inside a sandbox, dench chat uses DENCH_API_KEY + DENCH_RUN_ID
automatically; locally, run dench signin first.
Billing
Fresh workspaces verify a card for abuse prevention before starter credits are issued. The card verification does not charge the human. New workspaces receive $5 in starter AI credits, with automatic overage disabled by default.
Check AI credits:
dench billing status --json
Create a top-up link for the human to pay:
dench billing topup --amount 5 --json
Do not claim the top-up succeeded until Stripe returns success or
dench billing status --json shows the new credits.
External Apps
List connected apps:
dench integrations --json
dench integrations is an alias for dench tool status with no toolkit.
Other tool commands:
dench tool status [toolkit] --json
dench tool connect <toolkit> --json
dench tool search "list recent gmail emails" --toolkit gmail
dench tool run <tool_slug> --args '{"key":"value"}' --json
dench tool connect gmail, outlook, linkedin, and calendar print Dench
sign-in links using the same application, scopes, and callback as the web
app. Sign in with dench signin or use a member-owned Dench API key, then open
the returned link to authorize your account. New mailbox and LinkedIn
connections are private by default. An organization-only key cannot own a
personal connection.
Check the result with dench tool status <toolkit> --json. calendar shows
both Google and Outlook calendars; google-calendar (googlecalendar) and
outlook-calendar (outlookcalendar, microsoft-calendar) select one provider.
These status checks use Dench's own connection state and need no gateway key.
Other toolkits connect as connected apps. --callback-url is only supported
for connected apps; Dench-connected accounts use Dench's managed callback.
In the JSON, each connection has a source of app or dench, and
nativeAvailable: false means Dench-connected accounts could not be checked.
Start with compact dench tool search output. It lists ranked tools with slug,
toolkit, connection status, and a read-only/writes hint. Add --json only when
you need full raw schemas or exact argument details.
Run clear read-only information retrieval tools through Dench directly. This
includes slugs containing FETCH, GET, LIST, SEARCH, READ, or FIND,
such as GMAIL_FETCH_EMAILS:
dench tool run GMAIL_FETCH_EMAILS --args '{"max_results":10}' --json
Anything else acts in the connected app as soon as it runs; Dench has no approval step, so confirm it with the human first unless they asked for it.
For Gmail and other email tools, summarize safely. Prefer metadata and snippets.
Do not repeat OTP codes, security codes, passwords, tokens, API keys, or secrets
from email bodies. Non-JSON tool run display redacts likely sensitive codes;
--json preserves the raw provider response for cases that need it.
Run dench tool --help or dench integrations --help for command help.
For staging:
dench signin --staging
dench signin --host https://staging.dench.com
Saved prospect lists
Find supports separate past seniority and profile/work-description filters. Past seniority can match a different job from the past employer: a former Microsoft engineer who founded a separate startup can match both. Founder includes Co-Founder; Founding Team and C-level are separate choices. Company business focus is distinct from legal company type.
dench leads find --titles "CTO,VP of Engineering,Director of Engineering" \
--headcount "51-200,201-500,501-1000" --company-keywords "SaaS,B2B,Cloud" \
--past-company-names "Apple,Microsoft,Google,Alphabet,Amazon,Nvidia,Meta,Facebook,Tesla" \
--profile-keywords "AI infrastructure,Cloud migration,DevOps" \
--locations "California,Washington,Oregon,British Columbia" \
--past-seniorities "Founder" --limit 25 --json
These fields are also available in Find's editable filters, search_people,
and the API. dench leads filters --filter past-seniorities lists choices.
Descriptions use word matching; missing evidence does not match. Candidate
qualification is bounded, so keep paging when a continuation token is returned,
including after an empty page. Company focus needs another candidate filter.
People found with dench leads find stay in draft lists, not the CRM.
Generated results, --save-as searches, and dench lists save remain unpublished
until you click Save list in chat or Find. This keeps the same artifact id,
rows and reviews, then adds the list to Find Leads and the saved-list directory.
Enriching contacts or explicitly importing people does not publish the draft.
Existing lists without draft metadata remain saved.
For new prospect discovery, the lead-finding skill reads
/workspace/ORGANISATION.md and does not create ICP.md. The current
request overrides that brief. The result is one saved list.
Ordinary web, company, or hiring research does not require lead-finding or ICP
questions, reads, or scoring. There is no mandatory per-profile ICP decision.
In Find, click Find in a Work email, Phone, or Personal email cell to
request that field. The control discloses credit use; the lookup starts directly.
Different people run concurrently; wait for a person's current lookup before
starting another field. The cell shimmers with Enriching, then shows the
returned value or Not found. Header play targets selected profiles, or the
whole saved list when none are selected, with credit confirmation inline.
Bulk progress floats temporarily and disappears when finished, including misses.
Contact results persist in the list; other columns remain under View settings.
dench leads find --titles "VP Sales" --save-as "Q3 targets" --json
dench lists save --title "10 RevOps leaders" --people-file /tmp/shortlist.json --json
# Click Save list in chat or Find before looking it up in the saved directory.
dench lists --search "Q3 targets" --limit 25 --json
dench lists rows <artifactId> --enrichment enriched --limit 100 --json
dench lists rows <artifactId> --enrichment enriched --limit 100 --cursor <continueCursor> --json
lists save takes a non-empty JSON array of person rows (name or
fullName required), consolidating selected discoveries into a new list
without searching, enriching, or importing CRM rows. It leaves source lists
unchanged. Optional --filters-json stores criteria/source evidence metadata.
In chat, explicitly present the final list; research batches stay in activity
details instead of becoming competing result sets.
Scoring is optional and only when the user asks. Saved assessments remain available
in list reads and CSV exports; the ICP fit and Why this lead columns are
temporarily hidden in Find. Scores are not conversion probabilities or CRM
statuses. To save assessments for canonical
rows returned by lists rows, create a JSON array of
{row, suggestion: {fit, score, reason}} (1–100 items per file):
dench lists assess <artifactId> --assessments-file /tmp/icp-assessments.json --json
fit is likely_fit, unlikely_fit, or unknown; score is a whole number
from 0 to 100. The CLI validates the file before writes and reports partial
failures; retrying updates existing assessments. Re-read rows to verify every
saved score and reason. Older profiles have no saved score until assessed.
Directory and row reads return page, continueCursor, and isDone.
Directory/search reads show only saved lists; row reads accept a draft's id too.
Follow cursors even through empty filtered pages. Lists survive new chats;
read fresh rows and contacts instead of an old transcript or CSV. Each row
has its original zero-based index. Enriched means a contact was returned,
not merely that a lookup completed. Never resubmit pending/uncertain jobs.
Only when the user explicitly asks to import the selected profiles:
dench artifacts import <artifactId> --to lead --rows 0,4,9 --status "Contacted" --view "Q3 outbound" --pin
This imports exactly those original indices and known contacts, reusing existing
CRM records. Inspect the actual Lead status options first. Omit --status
to keep the default New; use --enrichment enriched for contact-bearing rows.
Enrichment is optional for explicit imports. Import sets status only, even
Qualified: it never purchases contacts, sends outreach, or converts records.
See the lead-finding skill and the
sales pipeline playbook.
Outreach campaigns
dench campaign runs email + LinkedIn outreach campaigns (the Outreach →
Campaigns page) from the terminal, from a brief to a launch: search for people
with filters and exclusions, add them from a search, CSV, saved list, CRM view
or one at a time, write the steps and AI-written lines, preview the exact
messages, check readiness, launch, and follow the replies. <campaign> is an
id or an exact, case-insensitive name. Confirm dench campaign help lists
draft, search, add, steps, person and preview. It needs a workspace
member's credentials (dench signin), a paid workspace for searches, and
connected sender accounts (dench campaign accounts lists them).
dench campaign accounts --json # email + LinkedIn sender ids, warm-up, availability and capacity
dench campaign draft --name "Founder outreach" --request-id founders-q4 --timezone America/Los_Angeles --window "Mon-Fri 9-18" --daily-cap 30 --email-mailboxes <mailboxId1>,<mailboxId2> --linkedin-accounts <linkedinId>
dench campaign search --filters '{"titles":["Founder"],"locations":["United States"]}' --not-industries "Staffing and Recruiting" --limit 25 --out audience.json
dench campaign add <campaign> --people-file audience.json
dench campaign steps set <campaign> --file steps.json
dench campaign personalize set <campaign> --file lines.json
dench campaign preview <campaign> --person "Ada Lovelace"
dench campaign launch <campaign> --check
dench campaign launch <campaign> --ack-linkedin-risk
dench campaign get <campaign> # status, schedule, funnel, Now/Next, holds, people
dench campaign replies <campaign> --since 7d
dench campaign people <campaign> --status paused
dench campaign events <campaign>
dench campaign pause|resume|archive <campaign>
dench campaign stop <campaign> --person <enrollmentId>
dench campaign create --name "Founder outreach" makes the same kind of draft
as draft (which also takes --request-id, so a retry returns the same
draft). A search costs 1 cent per person returned and prints the most it can
cost before it runs; add --people-file adds those people without searching
again. Other ways to fill a campaign:
add <campaign> --csv people.csv, --list <artifactId> (a saved Find list),
--search '<filters json>' (search and add in one billed call),
--object lead --view "Q3 targets", --object people --entries <id1>,<id2>,
or --email [email protected] for one person. --not-industries,
--not-titles, --not-domains and the other --not-<filter> flags leave
values out of a search.
A campaign holds at most 300 people; for more, dench campaign duplicate <campaign> makes a new draft with the same steps, schedule and senders, and no
people, and the next page of the search goes into the copy. Fix a mistake in a
draft with dench campaign remove <campaign> --person <enrollmentId> or
dench campaign person set; dench campaign sync pulls new contact details
from the CRM records. steps set replaces every step and works on drafts only;
steps edit --file steps.json changes a step's text and waits in place, also
after launch. launch --check never launches: it lists each blocker with the
command that fixes it and exits 1 when blocked. The schedule, daily cap and
stop-on-reply stay editable with dench campaign set. Every command takes
--json. The full guide, from reading a brief to monitoring the launch,
including the steps.json shape and the --window spec, is
references/CAMPAIGNS.md (dench campaign help for the flag list).
dench email multichannel drives the same campaigns with fewer controls.
Use --email-mailboxes / --linkedin-accounts for campaign sender pools
(up to 50 accessible accounts per channel). Accepts comma-separated ids or a
JSON array; omit a flag to preserve it, or pass none / '[]' to clear it.
API fields: emailMailboxAccountIds / linkedinAccountIds. Pool campaigns
omit per-step sender ids; legacy steps remain until conversion. Launch still
checks availability, and each conversation keeps its original sender.
dench campaign accounts --json discovers both mailboxes and LinkedIn
accounts, including unused senders. Only the acting member's private and
workspace-shared connections are listed; provider credentials are never
returned. LinkedIn summaries expose linkedinAccountId, display/owner labels,
availability, remainingToday (invites/messages/profiles) and
remainingInvitesThisWeek. Capacity is an account-local usage snapshot before
queued work and campaign limits; today's invites also respect the weekly cap.
The API equivalent is GET $DENCH_API_BASE/campaigns/accounts/linkedin.
Outreach needs a workspace member's credentials (a dench signin session, a
member-bound DENCH_API_KEY, or an agent session); an organization-only key
is refused.
Workspace files and reusable setup
Use dench files for saved workspace documents and dench stage for outputs.
Harness A keeps a separate scratch directory per job. The old filesystem and
home-sync daemons are legacy compatibility infrastructure; they are not needed
to execute a command or restore a saved environment.
dench files cat /reports/example.md
dench env list --json
dench env save openai-report --path ./openai-report --json
dench env show openai-report --json
dench env run openai-report -- node report.mjs
dench secrets set OPENAI_API_KEY --stdin < /private/path/key.txt
A saved directory requires dench-env.json, SKILL.md, an explicit file allowlist
and verification commands. It builds dependencies once, publishes a ready
revision, and reuses the prepared snapshot or compatible bundle. Only the chosen
environment is loaded. Secrets are encrypted separately and injected into the
executing process; they never become dependency archives or memory facts.
New automation runs select the latest ready revision; active jobs and their children retain the resolved revision. State is isolated by job or automation unless an explicit shared state key is chosen. State writes and refreshed credential files use compare-and-set versions; conflicts do not replay business operations. Rotate credentials without reinstalling packages.
On a different local OS/runtime, explicitly prepare a separate platform cache
once using dench env run openai-report --prepare-local -- node report.mjs.
Normal ready runs never invoke a package installer. The local executor provides
separate directories, not operating-system isolation.
See the environment reference
for the manifest, cold-start costs, mutable state and recovery receipts. Use
dench memory search/save for facts and preferences, dench context set for
standing context, and dench secrets for credentials. An old memory claiming
that a service is configured cannot replace a successful storage receipt.
Sandbox / API key auth (no dench signin required)
Inside a Dench sandbox the Dench API key is set in the environment when the
sandbox starts. When DENCH_API_KEY is present, the CLI skips the
dench signin flow entirely and uses the key as a bearer for every org-scoped
command — dench crm, dench files, and other workspace operations. The
selected connection determines the backend; hosted credentials stay scoped to
the authorized workspace and job.
export DENCH_API_KEY=dench_pk_live_…
# Optional but enables nicer URLs in CLI output:
export DENCH_API_URL=https://app.dench.com
export DENCH_ORG_ID=…
export DENCH_ORG_SLUG=…
dench crm entries list people
Auth precedence:
- A locally-stored
dench signinsession for the current backend (set viadench backend,--host,--prod, or--staging;DENCH_HOST=<url>does the same for one command). DENCH_API_KEY(sandbox / automation path).
Plain terminal usage without a saved session or DENCH_API_KEY returns
login_required.
Commands that need to impersonate a specific agent (such as
dench memory) still require an agent session
(dench signin) — the unified API key is org-scoped and cannot speak
as a single agent. Those commands return a clear "agent session
required" error in API-key mode.
Focused workspace context
dench context --instructions reads explicit IDENTITY, RULES and TOOLS notes,
plus document availability/revisions. It does not load business profiles,
personal profiles or aggregate memory. Fetch only what the task needs:
dench context --sections organisation,user,memory --json
Section names are identity, organisation, user, memory, rules, and
tools. Multiple names use one query. Reading never fills profiles or writes
memory. Edit the selected context with
dench context set <identity|organisation|user|tools|rules> --stdin; older profile
aliases remain compatibility paths. Personal USER context is unavailable to shared
channel sessions. dench context --json keeps its existing workspace overview.