{
  "markdown": "# DealMachine CLI\n\nDealMachine CLI (`dm`) -- property intelligence from the command line.\n\nA standalone Commander.js CLI that talks to the DealMachine REST API. Provides **17 command groups** covering agent guidance, authentication, property search, people lookup, enrichment, comps, list management, and developer utilities. Compiles to a single ESM bundle via `tsc`.\n\nThis package has **zero** `@dealmachine/*` dependencies -- it is a self-contained binary that communicates exclusively through the public API.\n\n---\n\n## AI agent integrations\n\nThis repository is also the public distribution package for the DealMachine MCP server and DealMachine skill.\n\n- Hosted MCP server: `https://mcp.dealmachine.com`\n- API documentation: `https://api.docs.dealmachine.com`\n- Account and API keys: `https://dealmachine.com/settings/developer`\n- Privacy policy: `https://dealmachine.com/privacy-policy`\n- Terms of service: `https://dealmachine.com/terms-of-service`\n- Support: `support@dealmachine.com`\n\nThe MCP server supports OAuth 2.1 for ChatGPT, Claude, Cursor, Codex, and other compatible clients. It can also use a DealMachine API key in developer clients that support bearer-token configuration.\n\nThe plugin package includes:\n\n- A hosted MCP connection for property, people, enrichment, comparable-sales, and account tools\n- A credit-aware skill that discovers filters and fields, counts first, and confirms large paid operations\n- A portable [Agent Plugins](https://agent-plugins.org/) package for compatible clients\n- Manifests for OpenAI, Claude, Cursor, GitHub Copilot, and Gemini\n- Official MCP Registry metadata in `server.json`\n\nThe portable package follows Agent Plugins 1.0.0:\n\n```text\ndealmachine-cli/\n├── plugin.json\n├── mcp.json\n└── skills/\n    └── dealmachine/\n        ├── SKILL.md\n        ├── REFERENCE.md\n        └── SETUP.md\n```\n\nCompatible clients discover the DealMachine skill from `skills/dealmachine/` and connect to the\nhosted Streamable HTTP MCP server declared in `mcp.json`. Client-specific manifests remain in the\nrepository for compatibility, marketplace metadata, and richer presentation.\n\nExample requests:\n\n- \"Find high-equity absentee-owned properties in Austin and estimate the credit cost first.\"\n- \"Look up the owner of this property and find available contact data.\"\n- \"Find comparable sales for this property.\"\n- \"Research people who match these criteria for a targeted prospecting list.\"\n\nDirect skill installation:\n\n```bash\nnpx skills add DealMachine/dealmachine-cli\n```\n\n---\n\n## Table of Contents\n\n- [AI agent integrations](#ai-agent-integrations)\n- [Installation](#installation)\n- [Authentication](#authentication)\n- [Configuration](#configuration)\n- [Commands](#commands)\n  - [Agents](#agents-commands) -- `agents`, `agents guide`, `agents playbook`, `agents install`, `agents permissions`\n  - [Auth](#auth-commands) -- `login`, `logout`, `whoami`\n  - [Config](#config-commands) -- `config get`, `config set`, `config path`\n  - [Account](#account-commands) -- `account`\n  - [Usage](#usage-commands) -- `usage`\n  - [Properties](#properties-commands) -- `search`, `count`, `get`, `ids`, `export`\n  - [People](#people-commands) -- `search`, `count`, `get`, `ids`, `export`\n  - [Enrich](#enrich-commands) -- `address`, `latlng`, `apn`, `email`, `phone`, `name`\n  - [Comps](#comps-commands) -- comparable property analysis\n  - [Lists](#lists-commands) -- `search`, `create`, `get`, `update`, `delete`, `build`, `import`, `items`, `add`, `remove`, `export`\n  - [Filters](#filters-commands) -- list available search filters\n  - [Fields](#fields-commands) -- list available data fields\n  - [Activity](#activity-commands) -- `search`, `get`\n  - [Addresses](#addresses-commands) -- `autocomplete`, `validate`\n  - [Dev](#dev-commands) -- `license add`, `license list`, `license remove`\n- [Global Options](#global-options)\n- [Input Methods](#input-methods)\n- [Project Structure](#project-structure)\n- [Building](#building)\n- [Adding New Commands](#adding-new-commands)\n- [Dependencies](#dependencies)\n\n---\n\n## Installation\n\n### From npm (global)\n\n```bash\nnpm install -g dealmachine\ndm login\n```\n\nThe canonical implementation package is `@dealmachine/cli`. The `dealmachine` package is the short install alias and provides the same `dm` command.\n\n### From source\n\n```bash\ncd packages/cli\nnpm run build\nnode dist/index.js whoami\n```\n\n### Link for local development\n\n```bash\ncd packages/cli\nnpm link\ndm --version\n```\n\nThe binary entry is `dist/index.js`, declared in `package.json` under `bin.dm`. Requires Node.js >= 18.\n\n---\n\n## Authentication\n\nThe CLI supports two authentication methods.\n\n### Device Auth Flow (RFC 8628)\n\nThe default `dm login` command uses the OAuth 2.0 Device Authorization Grant (RFC 8628). This is the recommended flow for interactive use:\n\n```bash\ndm login\n```\n\n1. The CLI requests a device code from `POST /v1/auth/device/code` with client ID `dealmachine-next-cli` and your machine's hostname.\n2. A verification URL and user code are displayed. The browser opens automatically (unless `--no-browser`).\n3. You authorize the device in the browser by entering the user code.\n4. The CLI polls `POST /v1/auth/device/token` at the server-specified interval.\n5. On success, the API key, key ID, and organization details are stored to `~/.dealmachine/config.json`.\n\nThe polling handles all RFC 8628 responses: `authorization_pending`, `slow_down` (backs off by 5s), `access_denied`, and `expired_token`.\n\n```bash\n# Skip auto-opening the browser\ndm login --no-browser\n\n# Target a specific environment\ndm login --env local\ndm login --env staging\n```\n\n### Direct API Key Login\n\nFor CI pipelines, scripts, or local development, pass an API key directly:\n\n```bash\ndm login --key dm_sk_live_abc123...\n```\n\nThe key is verified against `GET /v1/account` before being stored. If verification fails, the CLI exits with a non-zero code.\n\nIf you do not have an API key yet, use `dm signup`, `dm plans`, and `dm checkout` first. Public plan checkout only accepts self-serve Basic and Pro prices from the shared plan catalog and is capped at 60,000 monthly data credits.\n\n### Switching Environments\n\nIf you are already logged in, you can switch the target API environment without logging out:\n\n```bash\ndm login --env local       # Switch to http://localhost:3001/v1\ndm login --env staging     # Switch to https://api-staging.v2.dealmachine.com/v1\ndm login --env production  # Switch to https://api.v2.dealmachine.com/v1\n```\n\n### Logout\n\n```bash\ndm logout\n```\n\nRemoves the config file at `~/.dealmachine/config.json`.\n\n---\n\n## Configuration\n\nCredentials are stored at `~/.dealmachine/config.json` with file permissions `0600` (owner read/write only). The config directory `~/.dealmachine/` is created with mode `0700`.\n\n### Config File Schema\n\n```json\n{\n  \"apiKey\": \"dm_sk_live_...\",\n  \"keyId\": \"key_abc123\",\n  \"organizationId\": 42,\n  \"organizationName\": \"Acme Corp\",\n  \"organizationSlug\": \"acme-corp\",\n  \"apiEnvironment\": \"production\"\n}\n```\n\n### Environment Variables\n\nThe CLI checks these environment variables for API URL resolution (in priority order):\n\n| Variable                             | Purpose             | Example                             |\n| ------------------------------------ | ------------------- | ----------------------------------- |\n| `DM_API_URL` / `DEALMACHINE_API_URL` | Direct URL override | `http://localhost:3001/v1`          |\n| `DM_ENV` / `DEALMACHINE_ENVIRONMENT` | Environment name    | `local`, `staging`, or `production` |\n\nIf none are set, the CLI falls back to the `apiEnvironment` field in the config file, then defaults to `production`.\n\n### API Environments\n\n| Environment  | URL                                         |\n| ------------ | ------------------------------------------- |\n| `local`      | `http://localhost:3001/v1`                  |\n| `staging`    | `https://api-staging.v2.dealmachine.com/v1` |\n| `production` | `https://api.v2.dealmachine.com/v1`         |\n\n---\n\n## Commands\n\n### Agents Commands\n\n#### `dm agents`\n\nPrint concise guidance for agents using the CLI. This is the recommended first command when an agent has access to `dm` but has not loaded the DealMachine Playbook yet.\n\n```bash\ndm agents\ndm agents --json\n```\n\nThe guide tells agents to use `--json` and `--quiet`, verify auth, fetch live filters and fields before searches, count before credit-consuming work, and confirm expected credit usage before fetching records or exporting.\n\n#### `dm agents guide`\n\nPrint the same concise agent guidance explicitly.\n\n```bash\ndm agents guide\ndm agents guide --json\n```\n\n#### `dm agents playbook`\n\nPrint the bundled DealMachine Playbook Markdown. Agents should load this before translating natural language property, people, contact, enrichment, list, export, comps, or credit-usage requests into CLI commands.\n\n```bash\ndm agents playbook\ndm agents playbook --json\ndm agents skill        # alias\n```\n\nThe public CLI source keeps its bundled Playbook at `playbook/PLAYBOOK.md`. Monorepo builds can also copy `packages/playbooks/playbook/SKILL.md`. The build writes the selected source to `dist/agents/dealmachine-playbook.md`, so the command works from a published CLI package as well as a local source checkout.\n\n#### `dm agents install claude-code`\n\nInstall the Playbook as a native Claude Code skill. Personal scope is the default. Project scope\ninstalls under the current repository.\n\n```bash\ndm agents install claude-code\ndm agents install claude-code --project\n```\n\n#### `dm agents permissions`\n\nPrint the narrow Claude Code allowlist for free discovery and count commands. Paid and mutating\ncommands are not pre-approved.\n\n```bash\ndm agents permissions\ndm agents permissions --json\n```\n\n---\n\n### Auth Commands\n\n#### `dm signup`\n\nCreate a public API account and receive an API key:\n\n```bash\ndm signup developer@example.com --first-name Ada --last-name Lovelace --phone-number +15551234567\ndm signup developer@example.com --login\n```\n\n#### `dm plans`\n\nList public self-serve Basic and Pro plans:\n\n```bash\ndm plans\ndm plans --json\n```\n\n#### `dm checkout`\n\nCreate a Stripe checkout session using a price ID from `dm plans`:\n\n```bash\ndm checkout --price-id price_xxx_monthly\n```\n\n#### `dm login`\n\nAuthenticate with your DealMachine account.\n\n```bash\ndm login                            # Device auth flow (opens browser)\ndm login --no-browser               # Device auth, manual code entry\ndm login --key dm_sk_live_abc123    # Direct API key\ndm login --env local                # Target local API\n```\n\n| Option                | Description                                          |\n| --------------------- | ---------------------------------------------------- |\n| `--no-browser`        | Do not automatically open the browser                |\n| `--key <api-key>`     | Login directly with an API key (skips browser)       |\n| `--env <environment>` | API environment: `local`, `staging`, or `production` |\n\n#### `dm logout`\n\nRemove stored credentials.\n\n```bash\ndm logout\n```\n\n#### `dm whoami`\n\nShow current authentication status.\n\n```bash\ndm whoami               # Show stored credentials\ndm whoami --verify      # Verify credentials against the API\n```\n\n| Option     | Description                     |\n| ---------- | ------------------------------- |\n| `--verify` | Verify credentials with the API |\n\n---\n\n### Config Commands\n\n#### `dm config get [key]`\n\nGet a configuration value, or display all values when no key is given.\n\n```bash\ndm config get                   # Show all config values\ndm config get apiEnvironment    # Show specific value\ndm config get apiKey            # Shows truncated key (first 20 chars)\n```\n\nAvailable keys: `organizationName`, `organizationSlug`, `organizationId`, `apiEnvironment`, `keyId`, `apiKey`.\n\n#### `dm config set <key> <value>`\n\nSet a configuration value. Only `apiEnvironment` is editable.\n\n```bash\ndm config set apiEnvironment local\ndm config set apiEnvironment staging\ndm config set apiEnvironment production\n```\n\n#### `dm config path`\n\nPrint the absolute path to the config file.\n\n```bash\ndm config path\n# /Users/you/.dealmachine/config.json\n```\n\n---\n\n### Account Commands\n\n#### `dm account`\n\nDisplay account information including organization name, ID, creation date, and auth type.\n\n```bash\ndm account\n```\n\nOutput:\n\n```\nAccount\n────────────────────────────────────────\nOrganization:  Acme Corp\nOrg ID:        42\nCreated:       Jan 15, 2025\nAuth Type:     api_key\n```\n\n---\n\n### Usage Commands\n\n#### `dm usage`\n\nShow credit usage for the current billing cycle.\n\n```bash\ndm usage           # Human-readable table\ndm usage --json    # Machine-readable JSON\n```\n\nOutput:\n\n```\nCredit Usage\n──────────────────────────────────────────────────\n  Plan:         Pro\n  Cycle:        Mar 1, 2026 — Mar 31, 2026\n\n  Credits:      4,200 / 10,000 (42%)\n  Remaining:    5,800\n\n  Breakdown:\n    Properties: 3,100\n    People:     1,100\n```\n\n---\n\n### Properties Commands\n\n#### `dm properties search`\n\nSearch properties with filters and locations.\n\n```bash\n# Inline JSON body\ndm properties search --body '{\n  \"locations\": [{\"type\": \"zip_code\", \"code\": \"78704\"}],\n  \"filters\": [{\"filter_id\": \"property_type\", \"operator\": \"is_any_of\", \"value\": [\"single_family\"]}]\n}'\n\n# From a file\ndm properties search -f search.json\n\n# Pipe from stdin\ncat search.json | dm properties search\n\n# Machine-readable output\ndm properties search -f search.json --json              # Free estimate for scripts and agents\ndm properties search -f search.json --json --yes        # Run after approval\n\n# Explicit free estimate\ndm properties search -f search.json --estimate-cost\n\n# Query Builder protocol filters\ndm properties search --include-lists 123,456 --exclude-previously-exported --body '{\"locations\":[]}'\n```\n\n| Option                            | Description                                                                   |\n| --------------------------------- | ----------------------------------------------------------------------------- |\n| `--body <json>`                   | Request body as JSON string                                                   |\n| `-f, --file <path>`               | Read request body from a JSON file                                            |\n| `--include-lists <ids>`           | Comma-separated list IDs to include                                           |\n| `--exclude-lists <ids>`           | Comma-separated list IDs to exclude                                           |\n| `--exclude-previously-exported`   | Exclude records already exported by your organization                         |\n| `--bigquery-data-environment <n>` | Query Builder data environment (`1` production, `2` staging, `3` development) |\n| `--estimate-cost`                  | Preview counts and credit cost without consuming credits                     |\n| `--yes`                            | Confirm approved credit spend for non-interactive execution                  |\n| `--json`                          | Output as JSON                                                                |\n\n#### `dm properties count`\n\nCount properties matching filters without consuming credits.\n\n```bash\ndm properties count --body '{\"locations\": [{\"type\": \"state\", \"code\": \"TX\"}]}'\ndm properties count -f filters.json --json\n```\n\n#### `dm properties get <id>`\n\nGet a single property by its DealMachine ID.\n\n```bash\ndm properties get prop_12345\ndm properties get prop_12345 --contact-audience owners_and_family\ndm properties get prop_12345 --contact-audience none\ndm properties get prop_12345 --fields estimated_value,equity\ndm properties get prop_12345 --json\n```\n\n| Option                          | Description                                                  |\n| ------------------------------- | ------------------------------------------------------------ |\n| `--contact-audience <audience>` | `owners`, `owners_and_family`, `renters`, `residents`, `all`, `none` |\n| `--fields <csv>`                | Comma-separated property field IDs from `dm fields`          |\n| `--json`                        | Output as JSON                                               |\n\nProperty lookup defaults to `owners`. If you only need property data, use `--contact-audience none`. This omits contacts and avoids people credits.\n\n#### `dm properties ids [ids...]`\n\nGet multiple properties by their IDs in a single batch request.\n\n```bash\n# Positional arguments\ndm properties ids prop_111 prop_222 prop_333\n\n# Via JSON body\ndm properties ids --body '{\"ids\": [\"prop_111\", \"prop_222\"]}'\n\n# From file\ndm properties ids -f ids.json --contact-audience owners\ndm properties ids -f ids.json --contact-audience none\n```\n\n| Option                          | Description                                                                    |\n| ------------------------------- | ------------------------------------------------------------------------------ |\n| `--body <json>`                 | Request body as JSON string                                                    |\n| `-f, --file <path>`             | Read request body from a JSON file                                             |\n| `--contact-audience <audience>` | Include contacts: `owners`, `owners_and_family`, `renters`, `residents`, `all`, `none` |\n| `--json`                        | Output as JSON                                                                 |\n\n#### `dm properties export`\n\nExport properties as CSV (up to 1,000,000 records). Returns signed download URLs.\n\n```bash\ndm properties export -f search.json\ndm properties export -f search.json --require-phone --scrub-dnc\ndm properties export --body '{\"locations\": [...]}' --mobile-only --json\n```\n\n| Option              | Description                                                 |\n| ------------------- | ----------------------------------------------------------- |\n| `--body <json>`     | Request body as JSON string                                 |\n| `-f, --file <path>` | Read request body from a JSON file                          |\n| `--require-phone`   | Only include records where the contact has a phone number   |\n| `--require-email`   | Only include records where the contact has an email address |\n| `--mobile-only`     | Only include wireless phone numbers                         |\n| `--landline-only`   | Only include landline phone numbers                         |\n| `--scrub-dnc`       | Exclude contacts on the Do Not Call registry                |\n| `--json`            | Output as JSON                                              |\n\n---\n\n### People Commands\n\n#### `dm people search`\n\nSearch people with filters and locations.\n\n```bash\ndm people search --body '{\n  \"locations\": [{\"type\": \"zip_code\", \"code\": \"78704\"}],\n  \"filters\": [{\"filter_id\": \"age\", \"operator\": \"between\", \"value\": [30, 50]}]\n}'\ndm people search -f people-search.json --json\ndm people search -f people-search.json --estimate-cost\ndm people search -f people-search.json --json --yes\ndm people search --include-lists 123 --exclude-lists 456 --exclude-previously-exported --body '{\"locations\":[]}'\n```\n\nNon-interactive People Search returns a free estimate unless `--yes` is supplied. A specific person\nby name uses `dm enrich name`, not People Search.\n\n#### `dm people count`\n\nCount people matching filters without consuming credits.\n\n```bash\ndm people count -f filters.json\n```\n\n#### `dm people get <id>`\n\nGet a single person by their DealMachine ID.\n\n```bash\ndm people get per_12345\ndm people get per_12345 --include-properties --property-limit 20\ndm people get per_12345 --fields estimated_household_income,estimated_value\ndm people get per_12345 --json\n```\n\n| Option                 | Description                                                |\n| ---------------------- | ---------------------------------------------------------- |\n| `--include-properties` | Include associated properties                              |\n| `--property-limit <n>` | Maximum associated properties to return, from 1 through 100 |\n| `--fields <csv>`       | Comma-separated field IDs from `dm fields`                 |\n| `--json`               | Output as JSON                                             |\n\n#### `dm people ids [ids...]`\n\nGet multiple people by their IDs in a single batch request.\n\n```bash\ndm people ids per_111 per_222 per_333\ndm people ids --body '{\"ids\": [\"per_111\", \"per_222\"]}' --include-properties --property-limit 20\ndm people ids per_111 per_222 --fields estimated_household_income,estimated_value\n```\n\n| Option                 | Description                                                    |\n| ---------------------- | -------------------------------------------------------------- |\n| `--include-properties` | Include associated properties                                  |\n| `--property-limit <n>` | Maximum associated properties to return per person, up to 100 |\n| `--fields <csv>`       | Comma-separated field IDs from `dm fields`                     |\n\n#### `dm people export`\n\nExport people as CSV (up to 1,000,000 records). Returns signed download URLs.\n\n```bash\ndm people export -f search.json --require-email\ndm people export -f search.json --mobile-only --scrub-dnc --json\n```\n\nContact filter options are the same as `dm properties export`.\n\n---\n\n### Enrich Commands\n\nAll enrichment commands support three input modes: a positional argument for single-item lookup, `--body`/`-f` for JSON payloads, and `-f` with a `.csv` file for batch enrichment from CSV. Batches larger than 250 items are automatically chunked. Every enrichment command accepts `--fields <csv>` and sends the selected field IDs to the API. Email, phone, and name matches also include a free `property_count`; use `--include-properties` when you need the property records themselves.\n\n#### `dm enrich address [address]`\n\nLook up a property by street address.\n\n```bash\n# Single address\ndm enrich address \"123 Main St, Austin, TX 78704\"\ndm enrich address \"123 Main St, Austin, TX 78704\" --contact-audience none\ndm enrich address \"123 Main St, Austin, TX 78704\" --fields estimated_value,equity\n\n# Batch from JSON\ndm enrich address --body '{\"data\": [{\"full_address\": \"123 Main St, Austin, TX\"}]}'\n\n# Batch from CSV (auto-detected by .csv extension)\ndm enrich address -f addresses.csv --contact-audience owners\n\n# CSV columns: full_address (or street, city, state, zip)\n```\n\n| Option                          | Description                                           |\n| ------------------------------- | ----------------------------------------------------- |\n| `--body <json>`                 | Request body as JSON string                           |\n| `-f, --file <path>`             | Read from JSON or CSV file                            |\n| `--contact-audience <audience>` | `owners`, `owners_and_family`, `renters`, `residents`, `none` |\n| `--fields <csv>`                | Comma-separated field IDs from `dm fields`            |\n| `--json`                        | Output as JSON                                        |\n\nUse `--contact-audience none` whenever you only need the property. The response omits contacts and consumes zero people credits.\n\n#### `dm enrich latlng [coords]`\n\nLook up a property by latitude/longitude coordinates.\n\n```bash\ndm enrich latlng 30.25,-97.75\ndm enrich latlng -f coordinates.csv --fields estimated_value,equity --contact-audience none\n# CSV columns: latitude, longitude (or lat, lng/lon/long)\n```\n\n#### `dm enrich apn [apn]`\n\nLook up a property by Assessor's Parcel Number. Narrow results with `--state` or `--zip`.\n\n```bash\ndm enrich apn \"0123-456-789\" --state TX\ndm enrich apn -f parcels.csv --zip 78704 --fields estimated_value,equity\n# CSV columns: apn (or parcel_id, parcel_number)\n```\n\n| Option                          | Description                                           |\n| ------------------------------- | ----------------------------------------------------- |\n| `--state <code>`                | Narrow by state (e.g., TX)                            |\n| `--zip <code>`                  | Narrow by ZIP code                                    |\n| `--contact-audience <audience>` | `owners`, `owners_and_family`, `renters`, `residents`, `none` |\n| `--fields <csv>`                | Comma-separated field IDs from `dm fields`            |\n\n#### `dm enrich email [email]`\n\nLook up a person by email address.\n\n```bash\ndm enrich email jane@example.com\ndm enrich email jane@example.com --include-properties\ndm enrich email -f emails.csv --fields estimated_household_income,estimated_value --json\n# CSV columns: email (or email_address)\n```\n\n| Option                 | Description                                    |\n| ---------------------- | ---------------------------------------------- |\n| `--include-properties` | Include associated properties                  |\n| `--fields <csv>`       | Comma-separated field IDs from `dm fields`     |\n\n#### `dm enrich phone [phone]`\n\nLook up a person by phone number.\n\n```bash\ndm enrich phone 5125551234\ndm enrich phone -f phones.csv --include-properties --fields estimated_value\n# CSV columns: phone (or phone_number)\n```\n\n| Option                 | Description                                |\n| ---------------------- | ------------------------------------------ |\n| `--include-properties` | Include associated properties              |\n| `--fields <csv>`       | Comma-separated field IDs from `dm fields` |\n\n#### `dm enrich name [name]`\n\nLook up people by name. Supports \"First Last\" or just \"Last\" format.\n\n```bash\ndm enrich name \"Jane Doe\" --state TX --estimate-cost\ndm enrich name \"Jane Doe\" --state TX --json --yes\ndm enrich name \"Doe\" --state TX --page 2\ndm enrich name \"Jane Doe\" --zip 78704 --include-properties\ndm enrich name \"Jane Doe\" --fields estimated_household_income,estimated_value\n```\n\n| Option                 | Description                   |\n| ---------------------- | ----------------------------- |\n| `--state <code>`       | Narrow by state               |\n| `--zip <code>`         | Narrow by ZIP code            |\n| `--include-properties` | Include associated properties |\n| `--fields <csv>`       | Field IDs from `dm fields`     |\n| `--estimate-cost`      | Preview count and credits     |\n| `--yes`                | Confirm approved credit spend |\n| `--page <n>`           | Page number                   |\n| `--per-page <n>`       | Results per page              |\n\n---\n\n### Comps Commands\n\n#### `dm comps [property_ids...]`\n\nFind comparable properties (sales comps) for one or more properties.\n\n```bash\n# Single property with defaults\ndm comps prop_12345\n\n# Multiple properties with options\ndm comps prop_12345 prop_67890 --radius 2 --timeframe 12months --limit 50\n\n# Full control via JSON body\ndm comps --body '{\n  \"property_ids\": [\"prop_12345\"],\n  \"location\": {\"type\": \"radius\", \"radius_miles\": 1.5},\n  \"criteria\": {\"timeframe\": \"6months\", \"sort_by\": \"match\", \"limit\": 25}\n}'\n```\n\n| Option                   | Description                                                |\n| ------------------------ | ---------------------------------------------------------- |\n| `--body <json>`          | Request body as JSON string                                |\n| `-f, --file <path>`      | Read request body from a JSON file                         |\n| `--radius <miles>`       | Search radius in miles (default: 1)                        |\n| `--timeframe <period>`   | `3months`, `6months`, `12months`, `all` (default: 6months) |\n| `--limit <n>`            | Max comps per property (default: 25, max: 100)             |\n| `--sort-by <field>`      | `distance`, `price`, `date`, `match` (default: match)      |\n| `--sort-direction <dir>` | `asc`, `desc` (default: desc)                              |\n| `--include-foreclosures` | Include foreclosure sales                                  |\n| `--json`                 | Output as JSON                                             |\n\nOutput includes subject property details, value estimation with confidence interval, summary statistics (average/median price, price per sqft), and a table of comparable properties.\n\n---\n\n### Lists Commands\n\n#### `dm lists search`\n\nSearch and list all saved lists.\n\n```bash\ndm lists search\ndm lists search --search \"Austin\" --source-type properties --sort newest\ndm lists search --page 2 --per-page 50 --json\n```\n\n| Option                 | Description                         |\n| ---------------------- | ----------------------------------- |\n| `--search <term>`      | Search lists by name                |\n| `--source-type <type>` | `properties` or `people`            |\n| `--sort <order>`       | `newest`, `oldest`, `name`, `count` |\n| `-p, --page <n>`       | Page number                         |\n| `--per-page <n>`       | Results per page                    |\n\n#### `dm lists create`\n\nCreate a new list.\n\n```bash\n# Empty list\ndm lists create --name \"Austin Leads\"\n\n# Pre-populated with record IDs (max 250)\ndm lists create --name \"Hot Leads\" --source-type properties --ids 123,456,789\n\n# With search filters for a list build\ndm lists create --name \"TX SFR\" -f search-filters.json\n```\n\n| Option                 | Description                                          |\n| ---------------------- | ---------------------------------------------------- |\n| `--name <name>`        | List name (required)                                 |\n| `--source-type <type>` | `properties` or `people`                             |\n| `--ids <csv>`          | Comma-separated record IDs to pre-populate (max 250) |\n| `--body <json>`        | Request body as JSON (filters/locations)             |\n| `-f, --file <path>`    | Read request body from a JSON file                   |\n\n#### `dm lists get <id>`\n\nGet details of a specific list including status, progress, and error state.\n\n```bash\ndm lists get list_abc123\n```\n\n#### `dm lists update <id>`\n\nRename a list.\n\n```bash\ndm lists update list_abc123 --name \"New Name\"\n```\n\n#### `dm lists delete <id>`\n\nDelete a list and all its items.\n\n```bash\ndm lists delete list_abc123\n```\n\n#### `dm lists build <id>`\n\nBuild a list from search filters. This is an asynchronous operation -- poll with `dm lists get` for status.\n\n```bash\ndm lists build list_abc123 -f search-filters.json\n```\n\n#### `dm lists import <id>`\n\nImport record IDs into an existing list.\n\n```bash\ndm lists import list_abc123 --ids 111,222,333 --source-type properties\ndm lists import list_abc123 -f import-payload.json\n```\n\n#### `dm lists items <id>`\n\nList items in a list with pagination.\n\n```bash\ndm lists items list_abc123\ndm lists items list_abc123 --page 2 --per-page 100 --json\n```\n\n#### `dm lists add <id>`\n\nAdd items to a list by ID.\n\n```bash\ndm lists add list_abc123 --ids 111,222,333\ndm lists add list_abc123 --ids 111,222 --id-type internal_property_id\n```\n\n| Option             | Description                                    |\n| ------------------ | ---------------------------------------------- |\n| `--ids <csv>`      | Comma-separated list of IDs to add (required)  |\n| `--id-type <type>` | `internal_property_id` or `internal_person_id` |\n\n#### `dm lists remove <id>`\n\nRemove items from a list by ID.\n\n```bash\ndm lists remove list_abc123 --ids 111,222,333\n```\n\n#### `dm lists export <id>`\n\nExport list items. Credits are charged per record.\n\n```bash\ndm lists export list_abc123\ndm lists export list_abc123 --fields \"full_address,estimated_value,owner_name\" --anchor property\n```\n\n| Option            | Description                              |\n| ----------------- | ---------------------------------------- |\n| `--fields <csv>`  | Comma-separated list of fields to export |\n| `--anchor <type>` | `property` or `person`                   |\n\n---\n\n### Filters Commands\n\n#### `dm filters`\n\nList available search filters with their types, operators, and groupings.\n\n```bash\ndm filters\ndm filters --source-type properties --search \"bed\"\ndm filters --group-id building_information --json\n```\n\n| Option                 | Description              |\n| ---------------------- | ------------------------ |\n| `--source-type <type>` | `properties` or `people` |\n| `--group-id <id>`      | Filter by group ID       |\n| `--search <term>`      | Search filters by name   |\n| `--page <n>`           | Page number              |\n| `--per-page <n>`       | Results per page         |\n\n---\n\n### Fields Commands\n\n#### `dm fields`\n\nList available data fields with filterable/sortable flags.\n\n```bash\ndm fields\ndm fields --source-type people --search \"phone\"\ndm fields --group-id contact_info --json\n```\n\n| Option                 | Description              |\n| ---------------------- | ------------------------ |\n| `--source-type <type>` | `properties` or `people` |\n| `--group-id <id>`      | Filter by group ID       |\n| `--search <term>`      | Search fields by name    |\n| `--page <n>`           | Page number              |\n| `--per-page <n>`       | Results per page         |\n\n---\n\n### Locations Commands\n\nSearch and retrieve DealMachine locations.\n\n```bash\ndm locations search -q \"Harris\" --type county --state TX --json\ndm locations get loc_city_48106 --json\n```\n\n`dm locations autocomplete` remains available as a deprecated alias for `dm addresses autocomplete`.\n\n---\n\n### Activity Commands\n\n#### `dm activity search`\n\nSearch past API activity with type filters and free-text search.\n\n```bash\ndm activity search -t search_properties enrich_address\ndm activity search -q \"Austin\" --page 2\ndm activity search --body '{\"types\": [\"search_properties\"], \"page\": 1}'\n```\n\n| Option                   | Description                                |\n| ------------------------ | ------------------------------------------ |\n| `--body <json>`          | Request body as JSON string                |\n| `-f, --file <path>`      | Read request body from a JSON file         |\n| `-t, --types <types...>` | Filter by activity types (space-separated) |\n| `-q, --query <text>`     | Free-text search across activity           |\n| `--page <n>`             | Page number                                |\n| `--per-page <n>`         | Results per page                           |\n\n#### `dm activity get <id>`\n\nGet full details of a specific activity record, including the original request, result summary, and entity IDs (people and properties).\n\n```bash\ndm activity get act_abc123\ndm activity get act_abc123 --json\n```\n\n---\n\n### Addresses Commands\n\n#### `dm addresses autocomplete <query>`\n\nReturn free, bounded address and normalized location suggestions.\n\n```bash\ndm addresses autocomplete \"1200 Barton Springs\" --state TX\ndm addresses autocomplete \"saint louis 63101\" --scope location --limit 5 --json\n```\n\n| Option                 | Description                                      |\n| ---------------------- | ------------------------------------------------ |\n| `--scope <scope>`      | `all`, `address`, or `location`, default `all`   |\n| `--state <code>`       | Prefer a two-letter state abbreviation           |\n| `--limit <n>`          | Maximum suggestions, default 5 and max 10        |\n| `--latitude <number>`  | Latitude for nearby ranking, requires longitude  |\n| `--longitude <number>` | Longitude for nearby ranking, requires latitude  |\n| `--json`               | Output raw JSON response                          |\n\nAutocomplete does not request fields, perform enrichment, or consume data credits.\n\n#### `dm addresses validate [address]`\n\nValidate and standardize addresses via USPS.\n\n```bash\n# Single address\ndm addresses validate \"123 Main St, Austin, TX 78704\"\n\n# Batch via JSON\ndm addresses validate --body '{\"data\": [{\"full_address\": \"123 Main St, Austin TX\"}]}'\n\n# From file\ndm addresses validate -f addresses.json --json\n```\n\nOutput shows each address as valid, corrected (with corrections listed), or invalid (with reason).\n\n---\n\n### Dev Commands\n\nLocal development utilities that operate directly against the Docker MySQL container (`dealmachine-next-mysql`). These require the local database to be running (`npm run db:start` from the repo root).\n\n#### `dm dev license add <key_id>`\n\nAdd a license to an API key in the local database.\n\n```bash\ndm dev license add key_abc123 --type state --code TX\ndm dev license add key_abc123 --type zip_code --code 78704\ndm dev license add key_abc123 --type unlimited\ndm dev license add key_abc123 --type county --code 48453 --expires 2026-12-31\n```\n\n| Option             | Description                                              |\n| ------------------ | -------------------------------------------------------- |\n| `--type <type>`    | `state`, `county`, `zip_code`, or `unlimited` (required) |\n| `--code <code>`    | Location code: state abbreviation, FIPS code, or ZIP     |\n| `--expires <date>` | Expiration date in ISO format                            |\n\n#### `dm dev license list [key_id]`\n\nList all licenses, optionally filtered by key ID.\n\n```bash\ndm dev license list\ndm dev license list key_abc123\n```\n\n#### `dm dev license remove <license_id>`\n\nRemove a license by its numeric ID.\n\n```bash\ndm dev license remove 42\n```\n\n---\n\n## Global Options\n\nEvery command supports these flags:\n\n| Flag        | Description                                                |\n| ----------- | ---------------------------------------------------------- |\n| `--json`    | Output as machine-readable JSON (for scripting and piping) |\n| `--quiet`   | Suppress spinners and decorative output for agents/scripts |\n| `--help`    | Show usage information for any command                     |\n| `--version` | Show the CLI version                                       |\n\n---\n\n## Input Methods\n\nCommands that accept a request body support three input methods, checked in this order:\n\n1. **`--body <json>`** -- Inline JSON string.\n2. **`-f, --file <path>`** -- Read from a JSON file. Enrichment commands also accept `.csv` files for batch processing.\n3. **Stdin pipe** -- Read JSON from piped input (detected when stdin is not a TTY).\n\n```bash\n# Inline\ndm properties search --body '{\"locations\": [...]}'\n\n# File\ndm properties search -f query.json\n\n# Pipe\ncat query.json | dm properties search\n\n# CSV enrichment (enrich commands only)\ndm enrich address -f addresses.csv\n```\n\n### CSV Batch Enrichment\n\nThe `enrich` commands detect `.csv` files by extension and auto-parse them. Expected column names per command:\n\n| Command          | Required Columns        | Alternative Column Names             |\n| ---------------- | ----------------------- | ------------------------------------ |\n| `enrich address` | `full_address`          | or `street` + `city`, `state`, `zip` |\n| `enrich latlng`  | `latitude`, `longitude` | `lat`, `lng`/`lon`/`long`            |\n| `enrich apn`     | `apn`                   | `parcel_id`, `parcel_number`         |\n| `enrich email`   | `email`                 | `email_address`                      |\n| `enrich phone`   | `phone`                 | `phone_number`                       |\n\nBatches larger than 250 items are automatically chunked with progress spinners. If an export limit is reached mid-batch, the CLI stops and returns results collected so far.\n\n---\n\n## Project Structure\n\n```\npackages/cli/\n  scripts/\n    copy-agent-assets.mjs      # Bundles the Playbook Markdown into dist/agents\n  src/\n    index.ts                  # Program entrypoint -- registers all 17 command groups\n    lib/\n      config.ts               # Read/write ~/.dealmachine/config.json (mode 0600)\n      client.ts               # HTTP client wrapper (apiRequest, formatDate, getApiKey)\n      api.ts                  # Device auth flow client (requestDeviceCode, pollForToken, verifyCredentials)\n      output.ts               # Formatting helpers (printTable, printJson, printKeyValue, parseRequestBody)\n    commands/\n      agents.ts               # dm agents    -- agent guide and Playbook output\n      login.ts                # dm login     -- device auth + API key login\n      logout.ts               # dm logout    -- remove credentials\n      whoami.ts               # dm whoami    -- show/verify auth status\n      config.ts               # dm config    -- get, set, path\n      account.ts              # dm account   -- show account info\n      usage.ts                # dm usage     -- credit usage\n      properties.ts           # dm properties -- search, count, get, ids, export\n      people.ts               # dm people    -- search, count, get, ids, export\n      enrich.ts               # dm enrich    -- address, latlng, apn, email, phone, name\n      comps.ts                # dm comps     -- comparable properties\n      lists.ts                # dm lists     -- full CRUD + build, import, export\n      filters.ts              # dm filters   -- list available filters\n      fields.ts               # dm fields    -- list available fields\n      activity.ts             # dm activity  -- search, get\n      addresses.ts            # dm addresses -- validate\n      dev.ts                  # dm dev       -- local license management\n  dist/                       # Compiled output (ESM)\n  package.json\n  tsconfig.json\n```\n\n### Key Modules\n\n| Module          | Responsibility                                                                                                                                                                                                                                          |\n| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `lib/config.ts` | Manages `~/.dealmachine/config.json`. Enforces `0600` file permissions and `0700` directory permissions. Provides typed read/write/delete helpers.                                                                                                      |\n| `lib/client.ts` | Central HTTP client. Resolves the API base URL from env vars, config, or defaults. Attaches the `Authorization: Bearer` header and versioned `User-Agent`. Exits with a non-zero code on HTTP errors.                                                        |\n| `lib/api.ts`    | Device authorization flow implementation. Handles `POST /v1/auth/device/code` and `POST /v1/auth/device/token` with RFC 8628-compliant polling and error mapping. Also provides `verifyCredentials` for key validation.                                 |\n| `lib/output.ts` | All output formatting: `printTable` (auto-width columns), `printJson`, `printKeyValue`, `printPagination`, `printCredits`, `printTotals`, `printWarning`, `printHeader`. Also exports `parseRequestBody` which handles `--body`, `-f`, and stdin input. |\n\n---\n\n## Building\n\n```bash\nnpm run build      # Compile TypeScript and bundle agent Playbook assets to dist/\nnpm run dev        # Watch mode (tsc --watch)\nnpm run eval:cold-start:local      # Verify a clean local install and routing contract\nnpm run eval:cold-start:published  # Verify the latest public npm artifact\nnpm run eval:cold-start:deployed   # Verify deployed documentation and skill assets\n```\n\nThe published and deployed checks are release gates. They are expected to fail before a release is\npublished or the documentation deployment reaches production. The scenario catalog is stored in\n`evals/claude-code-name-lookup.json` so the same prompt variants remain visible and reviewable.\n\n### Standalone Binary\n\nThe compiled `dist/index.js` includes a `#!/usr/bin/env node` shebang and is declared in `package.json` under `bin.dm`. When installed globally via npm, it becomes available as `dm` on the PATH.\n\nFor distribution as a standalone binary without npm:\n\n```bash\n# Build\ncd packages/cli\nnpm run build\n\n# The entire dist/ directory is the distributable artifact\n# dist/index.js is the entrypoint (requires Node.js >= 18 on the target machine)\n```\n\nThe `files` array in `package.json` ensures only `dist/` is included in the published package.\n\n### TypeScript Configuration\n\n- Target: ES2022\n- Module: NodeNext (ESM)\n- Strict mode enabled\n- Outputs declarations, declaration maps, and source maps\n- No project references (standalone compilation)\n\n---\n\n## Adding New Commands\n\n### Step 1: Create the command file\n\nCreate `src/commands/mycommand.ts`:\n\n```typescript\n/**\n * MyCommand -- description of what this command does\n */\n\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { apiRequest } from '../lib/client.js';\nimport { printJson, printHeader, printKeyValue } from '../lib/output.js';\n\ninterface MyResponse {\n  data: { id: string; name: string };\n}\n\nexport async function myCommand(options: { json?: boolean }): Promise<void> {\n  const spinner = ora('Doing something...').start();\n  const data = await apiRequest<MyResponse>('/my-endpoint');\n  spinner.stop();\n\n  if (options.json) {\n    printJson(data);\n    return;\n  }\n\n  printHeader('My Command');\n  printKeyValue({\n    ID: data.data.id,\n    Name: data.data.name,\n  });\n  console.log();\n}\n```\n\n### Step 2: Register in index.ts\n\nImport and wire up the command in `src/index.ts`:\n\n```typescript\nimport { myCommand } from './commands/mycommand.js';\n\n// Top-level command\nprogram\n  .command('mycommand')\n  .description('Description shown in --help')\n  .option('--json', 'Output as JSON')\n  .action(async (options) => {\n    await myCommand(options);\n  });\n\n// Or as a subcommand group\nconst myGroup = program.command('mygroup').description('Group description');\n\nmyGroup\n  .command('sub1')\n  .description('Subcommand description')\n  .action(async (options) => {\n    await mySub1(options);\n  });\n```\n\n### Step 3: Build and test\n\n```bash\nnpm run build\nnode dist/index.js mycommand --json\n```\n\n### Conventions\n\n- One file per command group in `src/commands/`.\n- Always support `--json` for machine-readable output.\n- Use `ora` for spinners during API calls.\n- Use `chalk` for colored terminal output.\n- Use `apiRequest<T>` from `lib/client.ts` for all API calls -- it handles auth, errors, and exits.\n- Use `parseRequestBody` from `lib/output.ts` when the command accepts `--body`, `-f`, or stdin input.\n- Use `printHeader`, `printTable`, `printKeyValue`, `printCredits`, `printPagination` for consistent output formatting.\n- All imports must use the `.js` extension (ESM requirement with NodeNext resolution).\n\n---\n\n## Dependencies\n\n### Runtime\n\n| Package     | Version | Purpose                                                                |\n| ----------- | ------- | ---------------------------------------------------------------------- |\n| `commander` | ^12.1.0 | CLI framework -- command registration, option parsing, help generation |\n| `chalk`     | ^5.3.0  | Terminal string styling (colors, bold, dim)                            |\n| `ora`       | ^8.1.0  | Spinner animations for async operations                                |\n| `open`      | ^10.1.0 | Opens the browser for the device auth flow                             |\n\n### Dev\n\n| Package       | Version | Purpose                  |\n| ------------- | ------- | ------------------------ |\n| `typescript`  | ^5.6.3  | TypeScript compiler      |\n| `@types/node` | ^22.0.0 | Node.js type definitions |\n\n### Internal Package Dependencies\n\n**None.** This package is a fully standalone binary with zero `@dealmachine/*` dependencies. It communicates exclusively through the public REST API.\n\n### Used By\n\nThe **Playbook** at `packages/playbooks/playbook/` uses `dm` commands to execute property intelligence workflows. The CLI is the primary interface through which the Playbook interacts with DealMachine data. Agents can load the bundled Playbook directly with `dm agents playbook`.\n",
  "bytes": 47434,
  "sha": "16c851483691940e7904a1d84b99dfef89cd2d14950cbe9b461eb5c92a5ad940",
  "repo_slug": "dealmachine/dealmachine-cli",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_dealmachine_dealmachine_86cad5cd/readme"
}