npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@llcrm/crm-cli

v0.1.1

Published

CLI binary for the LigueLead CRM. Bundles @liguelead/crm-sdk for ops/dev/migration use cases.

Readme

@liguelead/crm-cli

A first-class CLI binary for the LigueLead CRM (NestJS API + Refine frontend, Phase 61–62.2 codebase). Wraps the official @liguelead/crm-sdk for ops, dev, and migration use cases. Distributed via npm with OIDC trusted publishing + provenance attestation.

Killer use case: Bitrix migration. The CLI is the generic, productized version of the one-shot apps/api/scripts/bitrix-migration/ script that shipped Phase 62.2. See §Bitrix migration below.

Quickstart

npm install -g @liguelead/crm-cli
crm auth login                  # paste API key (ll_live_...)
crm contacts list --limit=10

That's it. From there, every command supports --help, every error has a distinct Unix exit code (see §Exit codes), and every list command auto-detects TTY-vs-pipe (table for humans, JSON for jq).

Obtain an API key

API keys are issued from the Phase 65 portal (/settings/api-keys). During the v8.0 dev period (until that ships) you can use the local helper script apps/api/scripts/create-bitrix-migration-key.ts (same approach used in Phase 62.2).

Once you have a key:

crm auth login              # interactive — masks input via @inquirer/prompts
# OR
export LIGUELEAD_API_KEY='ll_live_xxxx...'
crm contacts list           # picks up the env automatically

Auth precedence

Resolution priority: env > keychain > file (D-03, hard-coded — no override flag):

  1. LIGUELEAD_API_KEY env var — wins for CI / scripted / ephemeral
  2. OS keychain — macOS Keychain, Windows Credential Manager, Linux Secret Service (via @napi-rs/keyring)
  3. ~/.liguelead-crm/config.json — chmod 0600 fallback when keychain is unavailable (Linux without DBus, locked keychain, etc.)

Windows — the file fallback skips POSIX chmod (NTFS doesn't honor it). User-home directory ACL inheritance is the implicit defence. Documented partial limitation.

Commands

| Group | Commands | | ------------ | ------------------------------------------------------------------- | | contacts | list, get, search, create, update, delete | | companies | list, get, search, create, update, delete | | deals | list, get, search, create, update, delete | | tickets | list, get, search, create, update, delete | | activities | list, get, search, create, update, delete | | auth | login, logout | | config | list, revoke, create (stub), rotate (stub) | | bulk | import, export, delete |

Use crm <group> <action> --help for full flag documentation. Examples:

crm contacts create [email protected] --firstName=Foo --lastName=Bar
crm tickets update <id> --status=closed
crm deals search "Acme"
crm activities list --entity-type=contact --entity-id=<uuid>

TTY auto-detection

Output format is auto-detected (Pitfall #5 — process.stdout.isTTY alone is unreliable on Windows + various CI runners):

| Context | Output | | ----------------------------- | ------- | | Interactive TTY | table | | Piped to another command | json | | Redirected to file | json | | Detected CI (30+ runners) | json | | --json flag | json | | --ndjson flag (lists only) | ndjson|

Status messages (Fetching..., Found N record(s).) emit on stderr when format is table; silent on pipe to keep stdout clean for | jq.

Exit codes

| Code | Meaning | Trigger | | ---: | ------------------------------- | -------------------------------------------- | | 0 | Success | Caller's responsibility | | 1 | Generic failure | Any unmapped Error or non-Error throw | | 2 | Validation / usage error | CrmValidationError, malformed --filter, missing required flag | | 3 | Auth / permission | CrmAuthError; missing API key | | 4 | Rate limit | CrmRateLimitError | | 5 | Server fault | CrmError with code starting 5 | | 6 | Not found (404) | CrmNotFoundError — id unknown to server (I-1 lock) | | 127 | Command not found | Shell-level — citty unrecognised subcommand |

Bulk import recipe (CLI-04)

# CSV must have a header row matching entity fields (D-05 — strict).
# external_id column REQUIRED for idempotent re-runs (D-06 + D-08).
echo "external_id,email,firstName,lastName" > contacts.csv
echo "cust-001,[email protected],Alice,Anderson" >> contacts.csv

crm bulk import contacts --csv=contacts.csv
# 1 ok, 0 failed
# Re-run is safe (D-08): same command produces no duplicates.

Customise the idempotency column with --idempotency-key=cpf (or any other column name). The value lands at customFields.<key> per CLAUDE.md regra 5 — never as a top-level column. Failed rows go to <input>.errors.csv with an appended _error column (D-07 continue-on-error).

Bulk export recipe (CLI-05)

crm bulk export contacts --format=csv --output=dump.csv
# Output starts with UTF-8 BOM bytes (0xEF 0xBB 0xBF) for Excel pt-BR compat.
# Streams the full tenant via SDK autoPaginate (cursor-based, never offset).

Customise columns via --columns=id,email,firstName to pick a subset. Default columns differ per entity — see apps/cli/src/lib/columns.ts.

Bulk delete recipe + filter syntax (CLI-05 + I-3)

# Filter syntax (v1, locked): <field>=<value>
# Multiple --filter flags AND together.
# Malformed (no `=`, multiple `=`, empty key/value) → exit 2.
crm bulk delete tickets \
  --filter status=closed \
  --filter pipelineId=abc-123 \
  --yes-i-know-what-im-doing \
  --confirm

Friction is intentional. The CLI:

  1. Parses each --filter strictly (rejects malformed, exit 2)
  2. Fetches matches via *ControllerSearch (max 1000; otherwise refuses)
  3. Prints a 10-record preview to stderr in table form
  4. Refuses to proceed without --yes-i-know-what-im-doing (exit 2)
  5. Refuses to proceed without --confirm after the preview (exit 2)
  6. Only THEN issues per-record DELETE calls

For complex filters (operators, JSON values, regex), use the SDK directly. v2 may extend the syntax — never break v1.

Killer use case — Bitrix migration

This CLI exists because LigueLead is migrating off Bitrix24 and the ad-hoc one-shot script in apps/api/scripts/bitrix-migration/ (shipped Phase 62.2) needed a productized, repeatable form.

The 410 Bitrix custom fields pre-triaged in .bitrix-analysis/02-triage-custom-fields.md include a "long tail" of low-frequency fields that are best handled by a generic CSV bulk importer rather than bespoke ETL code:

# 1. Export from Bitrix to CSV with `external_id` named after Bitrix's deal id.
# 2. (Optional) preserve the original Bitrix field name with --idempotency-key:
crm bulk import deals \
  --csv=bitrix-export.csv \
  --idempotency-key=external_bitrix_id

Re-running the same import is safe (D-08 — same external id resolves to UPDATE, not duplicate). External-id-based identity is canonical per CLAUDE.md regra 20 — UUIDs in this CRM are local to the environment that generated them.

The smoke test in apps/cli/src/__tests__/bitrix-smoke.spec.ts is the machine-checkable form of this recipe — if you change the README example above, please update the smoke spec accordingly.

Troubleshooting

  • Linux without DBus — keychain is unavailable; the CLI silently falls back to ~/.liguelead-crm/config.json (chmod 0600). Documented Pitfall #2.
  • Windows file fallback degraded — POSIX mode bits aren't honored on NTFS; user-home directory ACL inheritance is the implicit defence. Pitfall #4.
  • CI runners with PTY — most CI envs auto-detect via ci-info (30+ runners covered) and force JSON output. If a CI claims to be a TTY, set CI=true or pass --json. Pitfall #5.
  • Large CSVs (>100k rows) — the in-memory idempotency index size is bounded by the number of existing tenant entities, not the input CSV. When the API later supports server-side customFields filtering (probe CUSTOM_FIELDS_SEARCH_SUPPORTED=true flips at build time), per-row lookups become O(1) HTTP calls instead.
  • --filter complex queries — v1 supports only <field>=<value> with AND across flags. For OR/regex/JSON values, use the SDK directly.
  • Authentication failed — exit 3. Run crm auth login (or set LIGUELEAD_API_KEY). The CLI never echoes the key value to stderr/stdout.

See also

License

UNLICENSED — internal LigueLead tool.