Skill: rjmcgirr-pl/salesforce-dx (version 1.0.0)

--- name: salesforce-dx description: Query Salesforce data and manage sales pipelines using the `sf` CLI. Use for SOQL queries (simple to complex), opportunity pipeline analysis, forecast reporting, data exports, schema exploration, and CRM data operations. Also use for executive workflows like looking up deals by name, finding contact info to email prospects, preparing pipeline reviews, and cross-referencing CRM data with other tools. Triggers on Salesforce, SOQL, pipeline, opportunity, forecast, CRM data, deal lookup, prospect email, account info, or sf CLI questions. --- # Salesforce DX — Data & Pipeline Query data and manage pipelines with the `sf` CLI. ## Prerequisites ```bash # Verify CLI and auth sf --version sf org list ``` If no orgs listed, authenticate: ```bash sf org login web --alias my-org --set-default ``` ## Schema Discovery Before querying, explore available objects and fields: ```bash # List all objects sf sobject list --target-org my-org # Describe object fields sf sobject describe --sobject Opportunity --target-org my-org # Quick field list (names only) sf sobject describe --sobject Opportunity --target-org my-org | grep -E "^name:|^type:" ``` ## SOQL Queries ### Basic Patterns ```bash # Simple query sf data query -q "SELECT Id, Name, Amount FROM Opportunity LIMIT 10" # With WHERE clause sf data query -q "SELECT Id, Name FROM Opportunity WHERE StageName = 'Closed Won'" # Date filtering sf data query -q "SELECT Id, Name FROM Opportunity WHERE CloseDate = THIS_QUARTER" # Export to CSV sf data query -q "SELECT Id, Name, Amount FROM Opportunity" --result-format csv > opps.csv ``` ### Relationships ```bash # Parent lookup (Account from Opportunity) sf data query -q "SELECT Id, Name, Account.Name, Account.Industry FROM Opportunity" # Child subquery (Opportunities from Account) sf data query -q "SELECT Id, Name, (SELECT Id, Name, Amount FROM Opportunities) FROM Account LIMIT 5" ``` ### Aggregations ```bash # COUNT sf data query -q "SELECT COUNT(Id) total FROM Opportunity WHERE IsClosed = false" # SUM and GROUP BY sf data query -q "SELECT StageName, SUM(Amount) total FROM Opportunity GROUP BY StageName" # Multiple aggregates sf data query -q "SELECT StageName, COUNT(Id) cnt, SUM(Amount) total, AVG(Amount) avg FROM Opportunity GROUP BY StageName" ``` ### Bulk Queries (Large Datasets) ```bash # Use --bulk for >2000 records sf data query -q "SELECT Id, Name, Amount FROM Opportunity" --bulk --wait 10 ``` ## Pipeline Management ### Pipeline Snapshot ```bash # Open pipeline by stage sf data query -q "SELECT StageName, COUNT(Id) cnt, SUM(Amount) total FROM Opportunity WHERE IsClosed = false GROUP BY StageName ORDER BY StageName" # Pipeline by owner sf data query -q "SELECT Owner.Name, SUM(Amount) total FROM Opportunity WHERE IsClosed = false GROUP BY Owner.Name ORDER BY SUM(Amount) DESC" # Pipeline by close month sf data query -q "SELECT CALENDAR_MONTH(CloseDate) month, SUM(Amount) total FROM Opportunity WHERE IsClosed = false AND CloseDate = THIS_YEAR GROUP BY CALENDAR_MONTH(CloseDate) ORDER BY CALENDAR_MONTH(CloseDate)" ``` ### Win/Loss Analysis ```bash # Win rate by stage sf data query -q "SELECT StageName, COUNT(Id) FROM Opportunity WHERE IsClosed = true GROUP BY StageName" # Closed won this quarter sf data query -q "SELECT Id, Name, Amount, CloseDate FROM Opportunity WHERE StageName = 'Closed Won' AND CloseDate = THIS_QUARTER ORDER BY Amount DESC" # Lost deals with reasons sf data query -q "SELECT Id, Name, Amount, StageName, Loss_Reason__c FROM Opportunity WHERE StageName = 'Closed Lost' AND CloseDate = THIS_QUARTER" ``` ### Forecast Queries ```bash # Weighted pipeline (assumes Probability field) sf data query -q "SELECT StageName, SUM(Amount) gross, SUM(ExpectedRevenue) weighted FROM Opportunity WHERE IsClosed = false GROUP BY StageName" # Deals closing this month sf data query -q "SELECT Id, Name, Amount, StageName, CloseDate FROM Opportunity WHERE CloseDate = THIS_MONTH AND IsClosed = false ORDER BY Amount DESC" # Stale deals (no activity in 30 days) sf data query -q "SELECT Id, Name, Amount, LastActivityDate FROM Opportunity WHERE IsClosed = false AND LastActivityDate < LAST_N_DAYS:30" ``` ## Data Operations ### Create Records ```bash sf data create record -s Opportunity -v "Name='New Deal' StageName='Prospecting' CloseDate=2024-12-31 Amount=50000" ``` ### Update Records ```bash # By ID sf data update record -s Opportunity -i 006xx000001234 -v "StageName='Negotiation'" # Bulk update via CSV sf data upsert bulk -s Opportunity -f updates.csv -i Id --wait 10 ``` ### Export/Import ```bash # Export with relationships sf data export tree -q "SELECT Id, Name, (SELECT Id, Subject FROM Tasks) FROM Account WHERE Industry = 'Technology'" -d ./export # Import sf data import tree -f ./export/Account.json ``` ## JSON Output for Scripting Add `--json` for structured output: ```bash sf data query -q "SELECT Id, Name, Amount FROM Opportunity WHERE IsClosed = false" --json ``` Parse with jq: ```bash sf data query -q "SELECT Id, Name FROM Opportunity LIMIT 5" --json | jq '.result.records[].Name' ``` ## Common Date Literals | Literal | Meaning | |---------|---------| | TODAY | Current day | | THIS_WEEK | Current week | | THIS_MONTH | Current month | | THIS_QUARTER | Current quarter | | THIS_YEAR | Current year | | LAST_N_DAYS:n | Past n days | | NEXT_N_DAYS:n | Next n days | | LAST_QUARTER | Previous quarter | ## Troubleshooting **"Malformed query"** — Check field API names (not labels). Use `sf sobject describe` to verify. **"QUERY_TIMEOUT"** — Add filters, use `--bulk`, or add `LIMIT`. **"INVALID_FIELD"** — Field may not exist on that object or your profile lacks access. **Large result sets** — Use `--bulk` flag for queries returning >2000 records. ## Executive Workflows ### Quick Deal Lookup Find a deal by name or account: ```bash # By opportunity name (fuzzy) sf data query -q "SELECT Id, Name, Amount, StageName, CloseDate, Owner.Name, Account.Name FROM Opportunity WHERE Name LIKE '%Acme%' ORDER BY Amount DESC" # By account name sf data query -q "SELECT Id, Name, Amount, StageName, CloseDate FROM Opportunity WHERE Account.Name LIKE '%Microsoft%' AND IsClosed = false" # Recent deals I own sf data query -q "SELECT Id, Name, Amount, StageName, CloseDate, Account.Name FROM Opportunity WHERE OwnerId = '<my-user-id>' AND IsClosed = false ORDER BY CloseDate" ``` ### Get Contact Info for Outreach Find someone to email at a company: ```bash # Contacts at an account sf data query -q "SELECT Id, Name, Email, Phone, Title FROM Contact WHERE Account.Name LIKE '%Acme%'" # Decision makers (by title) sf data query -q "SELECT Name, Email, Title, Account.Name FROM Contact WHERE Title LIKE '%CEO%' OR Title LIKE '%VP%' OR Title LIKE '%Director%'" # Contacts on a specific deal sf data query -q "SELECT Contact.Name, Contact.Email, Contact.Title, Role FROM OpportunityContactRole WHERE Opportunity.Name LIKE '%Acme%'" ``` ### Prep for Pipeline Review Get a quick executive summary: ```bash # Top 10 deals closing this quarter sf data query -q "SELECT Name, Account.Name, Amount, StageName, CloseDate, Owner.Name FROM Opportunity WHERE CloseDate = THIS_QUARTER AND IsClosed = false ORDER BY Amount DESC LIMIT 10" # Deals by rep (for 1:1s) sf data query -q "SELECT Owner.Name, COUNT(Id) deals, SUM(Amount) total FROM Opportunity WHERE IsClosed = false GROUP BY Owner.Name ORDER BY SUM(Amount) DESC" # Deals needing attention (stale) sf data query -q "SELECT Name, Amount, StageName, LastActivityDate, Owner.Name FROM Opportunity WHERE IsClosed = false AND LastActivityDate < LAST_N_DAYS:14 ORDER BY Amount DESC LIMIT 10" ``` ### Account Intelligence Before a call or meeting: ```bash # Account overview sf data query -q "SELECT Id, Name, Industry, BillingCity, Website, OwnerId FROM Account WHERE Name LIKE '%Acme%'" # All open deals with account sf data query -q "SELECT Name, Amount, StageName, CloseDate FROM Opportunity WHERE Account.Name LIKE '%Acme%' AND IsClosed = false" # Recent activities sf data query -q "SELECT Subject, Status, ActivityDate FROM Task WHERE Account.Name LIKE '%Acme%' ORDER BY ActivityDate DESC LIMIT 5" ``` ### Cross-Tool Workflows **Salesforce + Email (via gog/gmail):** 1. Find contact email: `sf data query -q "SELECT Email FROM Contact WHERE Account.Name LIKE '%Acme%'"` 2. Draft email using that address with your email tool **Salesforce + Calendar:** 1. Find deals closing soon: `sf data query -q "SELECT Name, Account.Name, CloseDate FROM Opportunity WHERE CloseDate = THIS_WEEK"` 2. Cross-reference with calendar to ensure follow-ups scheduled **Quick CRM Update After Call:** ```bash # Log a task sf data create record -s Task -v "Subject='Call with John' WhatId='<opportunity-id>' Status='Completed' ActivityDate=$(date +%Y-%m-%d)" # Update opportunity stage sf data update record -s Opportunity -i <opp-id> -v "StageName='Negotiation' NextStep='Send proposal'" ``` ### Finding Your User ID Needed for "deals I own" queries: ```bash sf data query -q "SELECT Id, Name FROM User WHERE Email = '[REDACTED_SECRET]'" ``` Store this in your local config for quick reference. ## References - **[soql-patterns.md](references/soql-patterns.md)** — Advanced SOQL patterns (polymorphic, semi-joins, formula fields) - **[pipeline-queries.md](references/pipeline-queries.md)** — Ready-to-use pipeline and forecast queries

path: scripts/schema-export.sh
content: #!/bin/bash
# schema-export.sh — Export object fields to a readable markdown file
# Usage: ./schema-export.sh <ObjectName> [org-alias]
#
# Examples:
#   ./schema-export.sh Opportunity
#   ./schema-export.sh Account my-sandbox

set -e

OBJECT="${1:?Usage: schema-export.sh <ObjectName> [org-alias]}"
ORG="${2:-}"

ORG_FLAG=""
if [ -n "$ORG" ]; then
  ORG_FLAG="--target-org $ORG"
fi

OUTPUT_FILE="${OBJECT}_schema.md"

echo "# $OBJECT Schema" > "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Generated: $(date)" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "## Fields" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "| API Name | Label | Type | Required |" >> "$OUTPUT_FILE"
echo "|----------|-------|------|----------|" >> "$OUTPUT_FILE"

sf sobject describe --sobject "$OBJECT" $ORG_FLAG --json 2>/dev/null | \
  jq -r '.result.fields[] | "| \(.name) | \(.label) | \(.type) | \(.nillable | not) |"' >> "$OUTPUT_FILE"

echo "" >> "$OUTPUT_FILE"
echo "## Record Types" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"

sf sobject describe --sobject "$OBJECT" $ORG_FLAG --json 2>/dev/null | \
  jq -r '.result.recordTypeInfos[] | "- \(.name) (\(.developerName))"' >> "$OUTPUT_FILE" 2>/dev/null || echo "- (none)" >> "$OUTPUT_FILE"

echo "✓ Exported to $OUTPUT_FILE"


path: references/pipeline-queries.md
content: # Pipeline & Forecast Queries

Ready-to-use queries for pipeline management. Copy and modify for your org.

## Pipeline Overview

### Current Open Pipeline
```sql
SELECT StageName, COUNT(Id) count, SUM(Amount) total 
FROM Opportunity 
WHERE IsClosed = false 
GROUP BY StageName 
ORDER BY StageName
```

### Pipeline by Owner
```sql
SELECT Owner.Name, COUNT(Id) deals, SUM(Amount) total 
FROM Opportunity 
WHERE IsClosed = false 
GROUP BY Owner.Name 
ORDER BY SUM(Amount) DESC
```

### Pipeline by Account
```sql
SELECT Account.Name, COUNT(Id) deals, SUM(Amount) total 
FROM Opportunity 
WHERE IsClosed = false 
GROUP BY Account.Name 
ORDER BY SUM(Amount) DESC 
LIMIT 20
```

## Forecast Views

### This Quarter Forecast
```sql
SELECT StageName, SUM(Amount) total, SUM(ExpectedRevenue) weighted 
FROM Opportunity 
WHERE CloseDate = THIS_QUARTER AND IsClosed = false 
GROUP BY StageName
```

### Monthly Forecast (This Year)
```sql
SELECT CALENDAR_MONTH(CloseDate) month, SUM(Amount) total 
FROM Opportunity 
WHERE CloseDate = THIS_YEAR AND IsClosed = false 
GROUP BY CALENDAR_MONTH(CloseDate) 
ORDER BY CALENDAR_MONTH(CloseDate)
```

### Commit vs Best Case
```sql
-- Requires ForecastCategory field
SELECT ForecastCategory, SUM(Amount) total 
FROM Opportunity 
WHERE CloseDate = THIS_QUARTER AND IsClosed = false 
GROUP BY ForecastCategory
```

## Win/Loss Analysis

### Win Rate by Quarter
```sql
SELECT CALENDAR_QUARTER(CloseDate) qtr,
  SUM(CASE WHEN IsWon = true THEN 1 ELSE 0 END) wins,
  SUM(CASE WHEN IsWon = false THEN 1 ELSE 0 END) losses
FROM Opportunity 
WHERE IsClosed = true AND CloseDate = THIS_YEAR 
GROUP BY CALENDAR_QUARTER(CloseDate)
```

Note: CASE not supported in SOQL. Alternative approach:
```sql
-- Wins
SELECT CALENDAR_QUARTER(CloseDate) qtr, COUNT(Id) wins 
FROM Opportunity 
WHERE IsWon = true AND CloseDate = THIS_YEAR 
GROUP BY CALENDAR_QUARTER(CloseDate)

-- Losses
SELECT CALENDAR_QUARTER(CloseDate) qtr, COUNT(Id) losses 
FROM Opportunity 
WHERE IsWon = false AND IsClosed = true AND CloseDate = THIS_YEAR 
GROUP BY CALENDAR_QUARTER(CloseDate)
```

### Average Deal Size
```sql
SELECT StageName, AVG(Amount) avg_deal, COUNT(Id) count 
FROM Opportunity 
WHERE IsWon = true AND CloseDate = THIS_YEAR 
GROUP BY StageName
```

### Sales Cycle Length
```sql
SELECT Id, Name, Amount, CreatedDate, CloseDate 
FROM Opportunity 
WHERE IsWon = true AND CloseDate = THIS_YEAR 
ORDER BY CloseDate DESC
```
Calculate days between CreatedDate and CloseDate in your analysis tool.

## Pipeline Health

### Stale Opportunities (No Activity 30+ Days)
```sql
SELECT Id, Name, StageName, Amount, LastActivityDate, Owner.Name 
FROM Opportunity 
WHERE IsClosed = false AND LastActivityDate < LAST_N_DAYS:30 
ORDER BY Amount DESC
```

### Overdue Close Dates
```sql
SELECT Id, Name, StageName, Amount, CloseDate, Owner.Name 
FROM Opportunity 
WHERE IsClosed = false AND CloseDate < TODAY 
ORDER BY CloseDate
```

### Deals Without Next Steps
```sql
SELECT Id, Name, Amount, StageName 
FROM Opportunity 
WHERE IsClosed = false AND NextStep = null 
ORDER BY Amount DESC
```

### Large Deals at Risk
```sql
SELECT Id, Name, Amount, StageName, LastActivityDate 
FROM Opportunity 
WHERE IsClosed = false 
  AND Amount > 100000 
  AND LastActivityDate < LAST_N_DAYS:14 
ORDER BY Amount DESC
```

## Conversion Tracking

### Lead Source Performance
```sql
SELECT LeadSource, COUNT(Id) total, SUM(Amount) pipeline 
FROM Opportunity 
WHERE IsClosed = false 
GROUP BY LeadSource 
ORDER BY SUM(Amount) DESC
```

### Campaign ROI
```sql
SELECT Campaign.Name, COUNT(Id) opps, SUM(Amount) pipeline 
FROM Opportunity 
WHERE CampaignId != null 
GROUP BY Campaign.Name 
ORDER BY SUM(Amount) DESC
```

## Historical Comparisons

### QoQ Closed Won
```sql
-- This quarter
SELECT SUM(Amount) FROM Opportunity WHERE IsWon = true AND CloseDate = THIS_QUARTER

-- Last quarter  
SELECT SUM(Amount) FROM Opportunity WHERE IsWon = true AND CloseDate = LAST_QUARTER
```

### YoY Growth
```sql
-- This year
SELECT SUM(Amount) FROM Opportunity WHERE IsWon = true AND CloseDate = THIS_YEAR

-- Last year
SELECT SUM(Amount) FROM Opportunity WHERE IsWon = true AND CloseDate = LAST_YEAR
```

## Team Performance

### Leaderboard (Closed Won This Quarter)
```sql
SELECT Owner.Name, COUNT(Id) deals, SUM(Amount) total 
FROM Opportunity 
WHERE IsWon = true AND CloseDate = THIS_QUARTER 
GROUP BY Owner.Name 
ORDER BY SUM(Amount) DESC
```

### Activity by Rep
```sql
SELECT Owner.Name, COUNT(Id) tasks 
FROM Task 
WHERE CreatedDate = THIS_MONTH 
GROUP BY Owner.Name 
ORDER BY COUNT(Id) DESC
```

## Export Templates

### Full Pipeline Export
```sql
SELECT Id, Name, Account.Name, StageName, Amount, ExpectedRevenue, 
  CloseDate, Probability, LeadSource, Owner.Name, CreatedDate, 
  LastActivityDate, NextStep, Description 
FROM Opportunity 
WHERE IsClosed = false 
ORDER BY Amount DESC
```

### Closed Won Export
```sql
SELECT Id, Name, Account.Name, Amount, CloseDate, LeadSource, 
  Owner.Name, CreatedDate 
FROM Opportunity 
WHERE IsWon = true AND CloseDate = THIS_YEAR 
ORDER BY CloseDate DESC
```


path: references/soql-patterns.md
content: # Advanced SOQL Patterns

## Semi-Joins and Anti-Joins

```sql
-- Accounts WITH opportunities (semi-join)
SELECT Id, Name FROM Account 
WHERE Id IN (SELECT AccountId FROM Opportunity WHERE Amount > 100000)

-- Accounts WITHOUT opportunities (anti-join)
SELECT Id, Name FROM Account 
WHERE Id NOT IN (SELECT AccountId FROM Opportunity)

-- Contacts with open cases
SELECT Id, Name FROM Contact 
WHERE Id IN (SELECT ContactId FROM Case WHERE IsClosed = false)
```

## Polymorphic Relationships (Who/What)

```sql
-- Tasks with polymorphic WhatId
SELECT Id, Subject, What.Type, What.Name 
FROM Task 
WHERE What.Type IN ('Account', 'Opportunity')

-- Using TYPEOF (complex polymorphism)
SELECT Id, Subject,
  TYPEOF What
    WHEN Account THEN Name, Industry
    WHEN Opportunity THEN Name, Amount
  END
FROM Task
```

## Multi-Level Relationships

```sql
-- 5 levels up
SELECT Id, Name, Account.Owner.Manager.Name FROM Contact

-- Parent + child in same query
SELECT Id, Name, Account.Name,
  (SELECT Id, Subject FROM Tasks WHERE Status != 'Completed')
FROM Opportunity
```

## Dynamic Date Ranges

```sql
-- Rolling 90 days
SELECT Id, Name FROM Opportunity 
WHERE CreatedDate = LAST_N_DAYS:90

-- Specific fiscal period
SELECT Id, Name FROM Opportunity 
WHERE CloseDate = THIS_FISCAL_QUARTER

-- Between two dates
SELECT Id, Name FROM Opportunity 
WHERE CloseDate >= 2024-01-01 AND CloseDate <= 2024-03-31
```

## Aggregate Edge Cases

```sql
-- GROUP BY with HAVING (filter aggregates)
SELECT StageName, SUM(Amount) total 
FROM Opportunity 
GROUP BY StageName 
HAVING SUM(Amount) > 100000

-- COUNT_DISTINCT
SELECT COUNT_DISTINCT(AccountId) unique_accounts FROM Opportunity

-- GROUP BY ROLLUP (subtotals)
SELECT LeadSource, StageName, SUM(Amount) 
FROM Opportunity 
GROUP BY ROLLUP(LeadSource, StageName)
```

## Filtering on Null

```sql
-- Null checks
SELECT Id, Name FROM Opportunity WHERE Amount = null
SELECT Id, Name FROM Opportunity WHERE Amount != null

-- Empty picklists
SELECT Id, Name FROM Opportunity WHERE LeadSource = ''
```

## Text Search

```sql
-- LIKE wildcards
SELECT Id, Name FROM Account WHERE Name LIKE 'Acme%'
SELECT Id, Name FROM Account WHERE Name LIKE '%Corp%'

-- Multiple values
SELECT Id, Name FROM Opportunity 
WHERE StageName IN ('Closed Won', 'Closed Lost')
```

## Record Types

```sql
-- Filter by record type
SELECT Id, Name FROM Opportunity 
WHERE RecordType.DeveloperName = 'Enterprise_Deal'

-- Include record type in results
SELECT Id, Name, RecordType.Name FROM Opportunity
```

## Order and Limit

```sql
-- Top N with NULLS LAST
SELECT Id, Name, Amount FROM Opportunity 
ORDER BY Amount DESC NULLS LAST 
LIMIT 10

-- Offset for pagination
SELECT Id, Name FROM Account 
ORDER BY Name 
LIMIT 100 OFFSET 200
```

## Formula and Calculated Fields

Formula fields are queryable like regular fields:
```sql
SELECT Id, Name, Calculated_ARR__c FROM Opportunity
```

Note: You cannot use formula fields in WHERE for aggregate queries in some contexts.

## Querying Metadata

Use `--use-tooling-api` for metadata:
```bash
sf data query -q "SELECT Id, Name FROM ApexClass WHERE Status = 'Active'" --use-tooling-api
sf data query -q "SELECT Id, DeveloperName FROM CustomObject" --use-tooling-api
sf data query -q "SELECT Id, TableEnumOrId, DeveloperName FROM CustomField" --use-tooling-api
```


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)