agentsclimarketplace

Salesforce reports

Skill eprouveze/claude-skills/skills/salesforce-reports

Create, clone, read, run, and delete Salesforce Reports from the command line via the Analytics REST API, driven by the Salesforce CLI (`sf`). Org-agnostic — works against any org you've authed with `sf`. Includes a `--setup` flow (installs the sf CLI if missing, saves a default org/API version, and an optional Global Company filter for GAM roles). Use when the user says "create a Salesforce report", "clone a report", "generate a report via sf CLI", "list/run/delete reports", "report API", or "Analytics REST reports".From its SKILL.md

Install
npx -y skills add eprouveze/claude-skills --skill salesforce-reports

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

8.7 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it

Salesforce Reports

Manage Salesforce Reports (the analytics object, not deploy/package "report" status commands) from the CLI. There is no native sf report create command — report CRUD lives in the Analytics REST API (/services/data/vXX.0/analytics/reports). This skill wraps that API through sf api request rest, which is the only path that authenticates correctly with the CLI's (masked) access token.

Everything routes through one script: scripts/sfreport.sh.

Quick start

S=~/.claude/skills/salesforce-reports/scripts/sfreport.sh

"$S" setup --org org62        # installs sf CLI if missing, saves config, checks auth
"$S" list                     # all reports you can see
"$S" types                    # report types (the 'type' you pass to create)
"$S" get  <id> --describe     # a report's metadata (filters, columns, format)
"$S" run  <id>                # run synchronously, return rows
"$S" clone --from <id> --name "Copy of X"   # most reliable way to create a report
"$S" create --type <apiName> --name "New" \
     --column <api> --filter '<col> <op> <value>' --group <api>   # report with columns/filters
"$S" delete <id> --yes        # delete (HTTP 204 on success)
"$S" --help                   # full reference

The script picks its target org from: --org flag → saved config (setup) → the sf CLI's configured target-org.

Setup

sfreport.sh setup is the front door:

  1. sf CLI presence. If sf is missing, setup offers to install it (npm → Homebrew → yarn, whichever is present). Non-interactively it installs without prompting.
  2. Default org + API version. Saved to ~/.config/claude-skills/salesforce-reports.env so you don't repeat --org every call. API version defaults to 62.0 (Analytics REST is stable there).
  3. Auth check. Confirms the Analytics REST endpoint is reachable; on failure it prints the sf org login web command to run.
  4. Global Company filter (GAM roles only — optional). See below.

Non-interactive form for scripts/CI:

"$S" setup --org myorg --api 62.0 --check-only      # check, don't write config
"$S" setup --org myorg --global-company "NTT, Inc." --gc-column Account.Global_Company__c

Global Company filter (GAM roles)

A Global Account Manager scopes reports to a single global parent account. This skill can re-point that filter on any clone so you don't hand-edit metadata:

"$S" clone --from <id> --name "NTT Pipeline" \
      --gc "NTT, Inc." --gc-column Account.Global_Company__c

Configure a default once at setup (GLOBAL_COMPANY + GC_COLUMN_DEFAULT) and every clone inherits it; override per-call with --gc / --gc-column. Resolution order for the value: --gc arg → $GLOBAL_COMPANY (env or saved config).

This is GAM-specific. Any other role can ignore it entirely — leave both blank at setup and the skill never touches report filters. To discover the right column on a report:

"$S" get <id> --describe | python3 -c \
  'import sys,json;[print(f["column"]) for f in json.load(sys.stdin)["reportMetadata"]["reportFilters"]]'

How creation actually works

Two create paths, in order of reliability:

  • Clone (recommended). POST /analytics/reports?cloneId=<id> with the source report's full reportMetadata (renamed). The skill fetches /describe, renames, optionally re-points the Global Company filter, and POSTs. This always yields a working report with columns and groupings already in place.

  • Create with metadata. POST /analytics/reports with a reportMetadata naming a reportType. Use types to find a valid reportType api name, and get <existing> --describe to discover that type's valid column / filter / grouping api names. The skill accepts:

    • --column <api> (repeatable) → detailColumns
    • --filter '<col> <operator> <value>' (repeatable) → reportFilters; the value is the remainder of the string, so spaces/commas are fine (--filter 'StageName equals Closed Won'). Operators: equals, notEqual, lessThan, greaterThan, contains, startsWith, etc.
    • --group <api> (repeatable) → groupingsDown (required for SUMMARY/MATRIX)
    • --boolean-filter '<expr>'reportBooleanFilter (e.g. '1 AND (2 OR 3)')
    • --gc <value> --gc-column <api> → adds the GAM Global Company equals-filter With no --column, create still produces an empty stub. Clone remains the easiest path when you want to start from an existing report's full layout.
    "$S" create --type Opportunity --name "My Q3 Pipeline" --format SUMMARY \
          --column StageName --column Amount --column CloseDate \
          --filter 'StageName equals Closed Won' --filter 'Amount greaterThan 50000' \
          --group StageName
    

Known gotchas

  • Clone still needs a metadata body. POST ...?cloneId=<id> with a bare {} returns BAD_REQUEST: "there is no metadata". You must POST the full reportMetadata. The skill handles this by fetching /describe first.
  • sf api request rest DELETE is finicky. A plain -X DELETE errors No 'mode' found in 'body' entry; -b '' and --body '{...}' don't help; an object-shaped header errors keyValPair.map is not a function. The working form (used internally) is -f <envelope.json> with header as an array of "k:v" strings and body: {"mode":"raw","raw":""}.
  • Don't curl with the CLI token. sf org display [--verbose] returns a masked 54-char access token; curl with it gives INVALID_AUTH_HEADER. Always go through sf api request rest, which authenticates internally.
  • API version. Defaults to 62.0. The CLI's default data API may be higher (e.g. 67.0) but Analytics REST report CRUD is verified on 62.0; bump with --api only if you've tested.

Anti-patterns

  • sf data delete record --sobject Report — fails with INSUFFICIENT_ACCESS_OR_READONLY even when the Analytics DELETE succeeds; the SObject path enforces different rights. Delete reports via the Analytics endpoint (sfreport.sh delete).
  • Hardcoding an org alias. The skill is org-agnostic; pass --org or save one via setup. Don't bake org62 into callers.
  • Treating sf commands | grep report hits as report CRUD. Those are package/deploy status commands (package version report, project deploy report). Unrelated.
  • Bare-create then expecting data. A create without columns is empty by design — clone when you want a usable report.

Validated patterns

  • Clone → verify returned 18-char Id → use/delete. Verified on org62: create HTTP 200, delete HTTP 204, post-delete getNOT_FOUND.
  • setup --check-only as a CI/pre-flight gate before any report automation.
  • GAM scoping by re-pointing one reportFilters entry on clone, rather than rebuilding the report — preserves columns, groupings, and report type.

Self-improvement

This skill ships with a lightweight feedback loop (learnings.md). Adopt or ignore — the skill works without it.

Trigger a review when:

  • The user corrects a created/cloned report's filter, name, or scope (strongest signal — log immediately; 2–3 corrections on the same theme → promote to the body).
  • The sf CLI changes its body/auth handling (e.g. the DELETE envelope bug gets fixed — the case-delete-envelope.sh golden case is the canary).
  • A new Analytics REST behavior or error code surfaces.
  • learnings.md crosses ~100 bullets (consolidation time).
  • The skill mis-triggers or fails to trigger.

Consolidation pass (5–10 min, weekly or threshold-driven): each learnings.md entry gets one fate — apply (merge into Known gotchas / Anti-patterns / Validated patterns), capture (leave in the log), or dismiss (delete). Bump last-consolidated: in frontmatter. Golden cases that previously passed and now fail are the loudest signal to consolidate early.

Golden cases live in golden/ — run ORG=<sandbox> golden/run-all.sh before changing the script.

What ships with it: 8 files

26.3 KB alongside SKILL.md, 6 of them executable

scripts/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.