Agent-native command line interface for Topline OS.
os-cli is a single static binary that runs standalone: one Private
Integration Token, one location ID, no MCP server install, no second token,
no second hostname. Analytics commands query a local SQLite mirror of your
CRM — synced with topline local sync and queried with topline local sql.
The legacy hosted query family that delegated to os-mcp still works for
back-compat, but it is no longer required for any first-class workflow.
Raw API and MCP tools are useful, but sales operations questions are usually compound:
- What happened this week in a qualified pipeline?
- Which deals have activity but no stage movement?
- Who needs follow-up today?
- Which tasks are stale or lying to the pipeline?
- What is the complete brief before I touch this deal?
A good CLI should answer those with one command, compact JSON, and optional local SQLite state instead of ten remote round trips.
Using Claude Code? Follow the focused end-to-end guide:
docs/install-claude-code.md— binary install, env vars, and dropping the skill into~/.claude/skills/in one shot.
go install github.com/Topline-com/os-cli/cmd/topline@latestLocal build:
git clone https://github.com/Topline-com/os-cli.git
cd os-cli
go build ./cmd/toplineHermes-aware install (recommended for any agent that loads CLI env from
~/.hermes/.env or ~/.hermes/profiles/*/.env):
git clone https://github.com/Topline-com/os-cli.git
cd os-cli
./scripts/install-local.shThe script builds ~/.local/bin/topline-bin and writes a wrapper at
~/.local/bin/topline that loads only the TOPLINE_* env keys needed by REST
and SQL commands (TOPLINE_PIT, TOPLINE_LOCATION_ID, TOPLINE_BRAND_NAME,
TOPLINE_BASE_URL, TOPLINE_QUERY_TOKEN, TOPLINE_QUERY_BASE_URL,
TOPLINE_MCP_ACCESS_TOKEN, TOPLINE_MCP_TOKEN). It does not print or persist
secrets — only those env-var keys are written into the wrapper.
Set the same environment variables used by the MCP:
export TOPLINE_PIT="pit-..."
export TOPLINE_LOCATION_ID="your_location_id"
export TOPLINE_BRAND_NAME="Topline OS"Optional for tests/proxies:
export TOPLINE_BASE_URL="https://services.leadconnectorhq.com"SQL/query commands use the hosted os-mcp warehouse HTTP API, not the raw
LeadConnector REST API. That surface requires a connection-bound token minted by
the remote MCP, because raw PITs are intentionally rejected for read-only SQL:
export TOPLINE_QUERY_TOKEN="signed_connection_token_from_/connect"
export TOPLINE_QUERY_BASE_URL="https://os-mcp.topline.com" # optional defaultGenerate TOPLINE_QUERY_TOKEN at https://os-mcp.topline.com/connect using the
same PIT + Location ID. Keep it out of command history and commits.
topline setup-check
topline contacts search --query "Jane Doe" --limit 5
topline opportunities pipelines
topline opportunities search --pipeline-id CLUy1QapsrEeBiNrmQiL --status open --limit 100
topline conversations messages --conversation-id abc123 --limit 10Agent-safe output:
topline --agent pipeline audit \
--pipeline-id CLUy1QapsrEeBiNrmQiL \
--since this-week-et \
--status openpipeline audit now performs the expensive CRM join inside the CLI: paginated
open opportunities → recent conversations → recent messages → overdue tasks. It
fetches every opportunity page for the selected pipeline/status before building
counts, values, and stage summaries. The CLI first scans the 100 most recent
conversations and intersects them with open
pipeline contacts; if that scan is not deep enough to cover the requested window,
it falls back to per-contact conversation lookups. The JSON includes
activityJoinIncluded: true, activityJoinStats, and activeDeals summaries
with opportunity name, stage, value, message count, and per-deal activity counts,
so agents do not need a second lookup just to name the touched deals. If
activityJoinIncluded is missing or false, do not trust zero activity as a final
answer; run a fallback conversation/message join. Use --skip-activity when you
only need the open count/value/stage breakdown.
Warehouse SQL/query API:
topline --agent query doctor
topline --agent query schema
topline --agent query explain --tables opportunities,pipeline_stages,messages
topline --agent query sql --sql '
SELECT status, COUNT(*) AS n, SUM(monetary_value) AS value
FROM opportunities
GROUP BY status
ORDER BY n DESC
'query doctor is the readiness probe agents should run before deciding SQL vs
REST. It reports whether TOPLINE_QUERY_TOKEN is present, whether the token is
a (rejected) raw PIT, whether the hosted schema endpoint is reachable, and
whether the warehouse exposes the core analytics tables (contacts,
opportunities, messages, pipelines, pipeline_stages, call_events,
appointments, conversations). Missing tables are surfaced as os-mcp
coverage bugs — not as a reason to silently fall back to REST.
query delegates to the hosted Topline-com/os-mcp SQL surface
(/query/api/*): schema/catalog discovery, table explanation, and safe
SELECT / WITH ... SELECT execution. The worker enforces the same read-only
SQL gate as MCP (DDL, DML, PRAGMA, ATTACH, multi-statement SQL, and
hidden tables are rejected) and caps results at 5,000 rows. Use this for
Streamlined-style analytics questions where a relational scan beats paginated
REST fan-out.
Native (recommended) — local SQLite mirror, one token, zero MCP:
topline local sync # pulls pipelines + opportunities into ~/.topline/state.db
topline local status # row counts + last sync timestamp
topline local sql --sql 'SELECT status, COUNT(*) AS n FROM opportunities GROUP BY status'
topline local pipeline snapshot --pipeline-id PIPE # open count + value per stage (local)
topline local pipeline stale --days 14 # open opportunities untouched 14d+ (local)The local family uses only TOPLINE_PIT + TOPLINE_LOCATION_ID. No
TOPLINE_QUERY_TOKEN, no hosted MCP, no separate connect flow. The mirror
is a plain SQLite file at ~/.topline/state.db (override with --db PATH
or TOPLINE_DB). Schema lives in internal/sync/schema.go and ships with
the binary.
This is the path under active development — compound commands
(bottleneck, orphans, unanswered, health, deal brief) land on top
of the local mirror in subsequent phases.
Legacy SQLite scaffolding (kept for back-compat):
topline sync init --db topline.dbRaw escape hatch:
topline raw request GET /contacts/ --query '{"limit":1}'
topline raw request POST /contacts/ --body '{"firstName":"Jane","email":"jane@example.com"}'This repo is built around four rules:
- Local SQLite beats repeated remote calls for compound questions.
- Compound commands beat ten agent tool calls.
- Agent-shaped JSON beats raw payload dumps.
- Workflow commands should encode how sales operators actually work.
Parity command scaffolding exists for the public MCP action surface:
- contacts
- conversations/messages
- opportunities/pipelines
- calendars/appointments
- tasks
- notes
- custom fields
- custom values
- workflows
- tags
- users
- forms
- surveys
- location
- raw requests
Agent-native foundations included now:
local sync— REST → local SQLite mirror (pipelines, opportunities); idempotent, paginatedlocal sql— read-only SQL against the local mirrorlocal pipeline snapshot— open count + value per stage, locally computedlocal pipeline stale --days N— open opportunities untouched N days, locally computedlocal status— row counts + last sync timestamppipeline auditwith recent conversation scan + parallel message/task joinsquery schema|catalog|explain|sql— back-compat hosted MCP warehouse surface--agent,--mask-pii, compact JSON output
Next commands should be high-level sales workflows, not endpoint wrappers:
topline deal brief --opportunity-id opp_123
topline followup queue --pipeline-id pipe_123 --since 2026-05-01
topline hygiene --pipeline-id pipe_123
topline activity rollup --pipeline-id pipe_123 --since 2026-05-11 --group-by owner
topline sync run --since 2026-05-01go test ./...
go build ./cmd/toplineCommit identity for this repo should use Alex:
git config user.email alex@topline.com
git config user.name "Alex Skatell"MIT.