
Sf Datacloud
- 1 installs
- 423 repo stars
- Updated April 27, 2026
- jaganpro/claude-code-sfskills
Orchestrates multi-phase Salesforce Data Cloud pipelines (connect, prepare, harmonize, segment, act) via the external sf data360 CLI, including data spaces and data kits.
About
Provides product-level Salesforce Data Cloud workflow guidance across the connect-to-act pipeline using the sf data360 CLI plugin. A Salesforce developer uses it for cross-phase Data Cloud setup, data spaces, data kits, and troubleshooting.
- Routes single-phase work to the matching sf-datacloud-* skill
- Runs on the external sf data360 community CLI plugin
Sf Datacloud by the numbers
- 1 all-time installs (skills.sh)
- Ranked #765 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jaganpro/claude-code-sfskills --skill sf-datacloudAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 423 |
| Last updated | April 27, 2026 |
| Repository | jaganpro/claude-code-sfskills ↗ |
What it does
Orchestrates multi-phase Salesforce Data Cloud pipelines (connect, prepare, harmonize, segment, act) via the external sf data360 CLI, including data spaces and data kits.
Files
sf-datacloud: Salesforce Data Cloud Orchestrator
Use this skill when the user needs product-level Data Cloud workflow guidance rather than a single isolated command family: pipeline setup, cross-phase troubleshooting, data spaces, data kits, or deciding whether a task belongs in Connect, Prepare, Harmonize, Segment, Act, or Retrieve.
This skill intentionally follows sf-skills house style while using the external sf data360 command surface as the runtime. The plugin is not vendored into this repo.
---
When This Skill Owns the Task
Use sf-datacloud when the work involves:
- multi-phase Data Cloud setup or remediation
- data spaces (
sf data360 data-space *) - data kits (
sf data360 data-kit *) - health checks (
sf data360 doctor) - CRM-to-unified-profile pipeline design
- deciding how to move from ingestion → harmonization → segmentation → activation
- cross-phase troubleshooting where the root cause is not yet clear
Delegate to a phase-specific skill when the user is focused on one area:
| Phase | Use this skill | Typical scope |
|---|---|---|
| Connect | sf-datacloud-connect | connections, connectors, source discovery |
| Prepare | sf-datacloud-prepare | data streams, DLOs, transforms, DocAI |
| Harmonize | sf-datacloud-harmonize | DMOs, mappings, identity resolution, data graphs |
| Segment | sf-datacloud-segment | segments, calculated insights |
| Act | sf-datacloud-act | activations, activation targets, data actions |
| Retrieve | sf-datacloud-retrieve | SQL, search indexes, vector search, async query |
Delegate outside the family when the user is:
- extracting Session Tracing / STDM telemetry → sf-ai-agentforce-observability
- writing CRM SOQL only → sf-soql
- loading CRM source data → sf-data
- creating missing CRM schema → sf-metadata
- implementing downstream Apex or Flow logic → sf-apex, sf-flow
---
Required Context to Gather First
Ask for or infer:
- target org alias
- whether the plugin is already installed and linked
- whether the user wants design guidance, read-only inspection, or live mutation
- data sources involved: CRM objects, external databases, file ingestion, knowledge, etc.
- desired outcome: unified profiles, segments, activations, vector search, analytics, or troubleshooting
- whether the user is working in the default data space or a custom one
- whether the org has already been classified with
scripts/diagnose-org.mjs - which command family is failing today, if any
If plugin availability or org readiness is uncertain, start with:
- references/plugin-setup.md
- references/feature-readiness.md
scripts/verify-plugin.shscripts/diagnose-org.mjsscripts/bootstrap-plugin.sh
---
Core Operating Rules
- Use the external
sf data360plugin runtime; do not reimplement or vendor the command layer. - Prefer the smallest phase-specific skill once the task is localized.
- Run readiness classification before mutation-heavy work. Prefer
scripts/diagnose-org.mjsover guessing from one failing command. - For
sf data360commands, suppress linked-plugin warning noise with2>/dev/nullunless the stderr output is needed for debugging. - Distinguish Data Cloud SQL from CRM SOQL.
- Do not treat
sf data360 doctoras a full-product readiness check; the current upstream command only checks the search-index surface. - Do not treat
query describeas a universal tenant probe; only use it with a known DMO/DLO table after broader readiness is confirmed. - Preserve Data Cloud-specific API-version workarounds when they matter.
- Prefer generic, reusable JSON definition files over org-specific workshop payloads.
---
Recommended Workflow
1. Verify the runtime and auth
Confirm:
sfis installed- the community Data Cloud plugin is linked
- the target org is authenticated
Recommended checks:
sf data360 man
sf org display -o <alias>
bash ~/.claude/skills/sf-datacloud/scripts/verify-plugin.sh <alias>Treat sf data360 doctor as a broad health signal, not the sole gate. On partially provisioned orgs it can fail even when read-only command families like connectors, DMOs, or segments still work.
2. Classify readiness before changing anything
Run the shared classifier first:
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o <org> --jsonOnly use a query-plane probe after you know the table name is real:
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o <org> --phase retrieve --describe-table MyDMO__dlm --jsonUse the classifier to distinguish:
- empty-but-enabled modules
- feature-gated modules
- query-plane issues
- runtime/auth failures
3. Discover existing state with read-only commands
Use targeted inspection after classification:
sf data360 doctor -o <org> 2>/dev/null
sf data360 data-space list -o <org> 2>/dev/null
sf data360 data-stream list -o <org> 2>/dev/null
sf data360 dmo list -o <org> 2>/dev/null
sf data360 identity-resolution list -o <org> 2>/dev/null
sf data360 segment list -o <org> 2>/dev/null
sf data360 activation platforms -o <org> 2>/dev/null4. Localize the phase
Route the task:
- source/connector issue → Connect
- ingestion/DLO/stream issue → Prepare
- mapping/IR/unified profile issue → Harmonize
- audience or insight issue → Segment
- downstream push issue → Act
- SQL/search/index issue → Retrieve
5. Choose deterministic artifacts when possible
Prefer JSON definition files and repeatable scripts over one-off manual steps. Generic templates live in:
assets/definitions/data-stream.template.jsonassets/definitions/dmo.template.jsonassets/definitions/mapping.template.jsonassets/definitions/relationship.template.jsonassets/definitions/identity-resolution.template.jsonassets/definitions/data-graph.template.jsonassets/definitions/calculated-insight.template.jsonassets/definitions/segment.template.jsonassets/definitions/activation-target.template.jsonassets/definitions/activation.template.jsonassets/definitions/data-action-target.template.jsonassets/definitions/data-action.template.jsonassets/definitions/search-index.template.json
6. Verify after each phase
Typical verification:
- stream/DLO exists
- DMO/mapping exists
- identity resolution run completed
- unified records or segment counts look correct
- activation/search index status is healthy
---
High-Signal Gotchas
connection listrequires--connector-type.dmo list --allis useful when you need the full catalog, but first-pagedmo listis often enough for readiness checks and much faster.- Segment creation may need
--api-version 64.0. segment membersreturns opaque IDs; use SQL joins for human-readable details.sf data360 doctorcan fail on partially provisioned orgs even when some read-only commands still work; fall back to targeted smoke checks.query describeerrors such asCouldn't find CDP tenant IDorDataModelEntity ... not foundare query-plane clues, not automatic proof that the whole product is disabled.- Many long-running jobs are asynchronous in practice even when the command returns quickly.
- Some Data Cloud operations still require UI setup outside the CLI runtime.
---
Output Format
When finishing, report in this order: 1. Task classification 2. Runtime status 3. Readiness classification 4. Phase(s) involved 5. Commands or artifacts used 6. Verification result 7. Next recommended step
Suggested shape:
Data Cloud task: <setup / inspect / troubleshoot / migrate>
Runtime: <plugin ready / missing / partially verified>
Readiness: <ready / ready_empty / partial / feature_gated / blocked>
Phases: <connect / prepare / harmonize / segment / act / retrieve>
Artifacts: <json files, commands, scripts>
Verification: <passed / partial / blocked>
Next step: <next phase, setup guidance, or cross-skill handoff>---
Cross-Skill Integration
| Need | Delegate to | Reason |
|---|---|---|
| load or clean CRM source data | sf-data | seed or fix source records before ingestion |
| create missing CRM schema | sf-metadata | Data Cloud expects existing objects/fields |
| deploy permissions or bundles | sf-deploy | environment preparation |
| write Apex against Data Cloud outputs | sf-apex | code implementation |
| Flow automation after segmentation/activation | sf-flow | declarative orchestration |
| session tracing / STDM / parquet analysis | sf-ai-agentforce-observability | different Data Cloud use case |
---
Reference Map
Start here
- README.md
- references/plugin-setup.md
- references/feature-readiness.md
- UPSTREAM.md
Phase skills
- sf-datacloud-connect
- sf-datacloud-prepare
- sf-datacloud-harmonize
- sf-datacloud-segment
- sf-datacloud-act
- sf-datacloud-retrieve
Deterministic helpers
- scripts/bootstrap-plugin.sh
- scripts/verify-plugin.sh
- scripts/diagnose-org.mjs
- assets/definitions/
{
"developerName": "CRM_Target",
"displayName": "CRM Target",
"description": "Example activation target definition for a downstream destination"
}
{
"developerName": "High_Value_Customers_Activation",
"displayName": "High Value Customers Activation",
"description": "Example activation linking a published segment to a destination target",
"segmentName": "High_Value_Customers",
"activationTargetName": "CRM_Target"
}
{
"developerName": "Lifetime_Value",
"displayName": "Lifetime Value",
"description": "Example calculated insight over an order-style DMO",
"definitionType": "CALCULATED_METRIC",
"expression": "SELECT ssot__IndividualId__c, SUM(ssot__TotalAmount__c) AS total_spend FROM ssot__SalesOrder__dlm GROUP BY ssot__IndividualId__c"
}
{
"developerName": "Webhook_Target",
"displayName": "Webhook Target",
"description": "Example data action target for downstream delivery"
}
{
"developerName": "Notify_High_Value_Customer_System",
"displayName": "Notify High Value Customer System",
"description": "Example data action definition that uses a downstream data action target"
}
{
"developerName": "Customer_360_Graph",
"displayName": "Customer 360 Graph",
"description": "Example Data Graph rooted on a unified customer entity",
"sourceObject": {
"name": "UnifiedssotIndividualMain__dlm",
"type": "derived",
"fields": [
{
"name": "ssot__Id__c"
},
{
"name": "ssot__FirstName__c"
},
{
"name": "ssot__LastName__c"
}
],
"relatedObjects": []
}
}
{
"name": "Customer_Stream",
"label": "Customer Stream",
"datasource": "SalesforceDotCom_Home",
"datastreamType": "CONNECTORSFRAMEWORK",
"connectorInfo": {
"connectorType": "SalesforceDotCom",
"connectorDetails": {
"name": "SalesforceDotCom_Home",
"sourceObject": "Custom_Object__c"
}
},
"dataLakeObjectInfo": {
"label": "Customer",
"name": "Customer__dll",
"category": "Profile",
"dataspaceInfo": [
{
"name": "Default"
}
],
"dataLakeFieldInputRepresentations": [
{
"name": "Id",
"label": "Record ID",
"dataType": "Text",
"isPrimaryKey": true
},
{
"name": "External_Id_c",
"label": "External ID",
"dataType": "Text",
"isPrimaryKey": false
}
]
},
"sourceFields": [
{
"name": "Id",
"dataType": "Text"
},
{
"name": "External_Id__c",
"dataType": "Text"
}
],
"mappings": [],
"refreshConfig": {
"isAccelerationEnabled": true,
"refreshMode": "UPSERT",
"frequency": {
"frequencyType": "HOURLY"
}
}
}
{
"developerName": "Customer_Profile__dlm",
"label": "Customer Profile",
"category": "Profile",
"fields": [
{
"name": "External_Id_c__c",
"label": "External ID",
"type": "Text"
},
{
"name": "Preferred_Tier_c__c",
"label": "Preferred Tier",
"type": "Text"
}
]
}
{
"label": "Main",
"description": "Generic identity resolution ruleset using customer identifiers and contact points",
"configurationType": "individual",
"rulesetId": "main",
"doesRunAutomatically": false,
"matchRules": [
{
"label": "Exact Email",
"criteria": [
{
"entityName": "ssot__ContactPointEmail__dlm",
"fieldName": "ssot__EmailAddress__c",
"matchMethodType": "exactnormalized",
"caseSensitiveMatch": false,
"shouldMatchOnBlank": false
}
]
}
],
"reconciliationRules": [
{
"entityName": "ssot__Individual__dlm",
"ruleType": "lastupdated",
"shouldIgnoreEmptyValue": true,
"sources": [],
"fields": []
}
]
}
{
"sourceObjectName": "Customer__dll",
"targetObjectName": "Customer_Profile__dlm",
"fieldMappings": [
{
"sourceFieldName": "External_Id_c",
"targetFieldName": "External_Id_c__c"
},
{
"sourceFieldName": "Preferred_Tier_c",
"targetFieldName": "Preferred_Tier_c__c"
}
]
}
{
"relationships": [
{
"sourceObjectName": "Order_Header__dlm",
"targetObjectName": "ssot__Individual__dlm",
"cardinality": "ManyToOne",
"sourceFieldName": "Customer_ID_c__c",
"targetFieldName": "ssot__PartyId__c",
"relationshipOwner": "DataCloud"
}
]
}
{
"developerName": "Knowledge_Index",
"displayName": "Knowledge Index",
"description": "Example semantic search index for a structured or unstructured Data Cloud source",
"sourceObject": "KnowledgeArticleVersion__dlm",
"embeddingModel": "e5_large_v2",
"chunkingStrategy": "passage_extraction",
"similarityMetric": "COSINE"
}
{
"displayName": "High Value Customers",
"developerName": "High_Value_Customers",
"description": "Example segment built from a unified DMO",
"segmentType": "Dbt",
"includeDbt": {
"models": {
"models": [
{
"name": "UnifiedCustomers",
"sql": "SELECT ui.ssot__Id__c FROM UnifiedssotIndividualMain__dlm ui JOIN ssot__SalesOrder__dlm so ON ui.ssot__Id__c = so.ssot__IndividualId__c GROUP BY ui.ssot__Id__c HAVING SUM(so.ssot__TotalAmount__c) > 1000"
}
]
}
}
}
Credits & Acknowledgments
This skill family distills publicly shared Data Cloud implementation knowledge into sf-skills house style. Attribution is intentionally explicit because contributor credit is a core sf-skills principle.
---
Primary Contributor
Gnanasekaran Thoppae
Primary contributor for the sf-datacloud family
Key contributions:
- phase-based Data Cloud skill decomposition
- community
sf data360CLI command coverage across the Data Cloud lifecycle - operational gotchas for connections, mappings, identity resolution, search, segmentation, and activation
- practical install and verification workflows for the community runtime
---
Public upstream sources distilled into this family
gthoppae/sf-data360-skills
Repository: https://github.com/gthoppae/sf-data360-skills
Key contributions:
- Connect / Prepare / Harmonize / Segment / Act / Retrieve skill structure
- orchestration patterns for cross-phase Data Cloud work
- high-signal Data Cloud gotchas and command selection guidance
gthoppae/sf-cli-plugin-data360
Repository: https://github.com/gthoppae/sf-cli-plugin-data360
Key contributions:
- deterministic
sf data360runtime surface - command taxonomy spanning connections, streams, DMOs, identity resolution, query, search index, activation, and more
- install, testing, and live-validation patterns for the community CLI
---
Official Salesforce resources
Salesforce Architects
- Data Cloud Reference Architecture: https://architect.salesforce.com/fundamentals/data-cloud
Salesforce Documentation
- Data Cloud Developer Documentation: https://developer.salesforce.com/docs/data/data-cloud-dev/guide/dc-dev-about.html
- Data Cloud Query Guide: https://developer.salesforce.com/docs/data/data-cloud-query-guide/guide/dc-sql.html
- Segment and activation documentation: https://help.salesforce.com/
Trailhead
- Data Cloud Basics
- Identity Resolution
- Calculated Insights
- Segmentation and Activation
---
sf-skills distillation policy for this family
This family intentionally:
- keeps sf-skills naming consistency (
sf-datacloud-*) - keeps the external CLI plugin outside this repo
- favors deterministic helper scripts and reusable JSON templates
- rewrites examples into generic sf-skills-style guidance rather than copying workshop-specific payloads
See UPSTREAM.md for the maintenance contract.
---
Special thanks
To the public Salesforce Data Cloud community for documenting practical patterns around data ingestion, harmonization, identity resolution, segmentation, and retrieval.
MIT License
Copyright (c) 2024-2025 Jag Valaiyapathy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
sf-datacloud
Salesforce Data Cloud skill family for sf-skills. This is the cross-phase orchestrator for community-driven Data Cloud workflows built around the external sf data360 CLI runtime.
What this skill is for
Use sf-datacloud when the task spans multiple Data Cloud phases:
- connection + ingestion + harmonization setup
- troubleshooting a Data Cloud pipeline end to end
- managing data spaces or data kits
- deciding which specialized Data Cloud skill to use next
What this skill is not
- It does not vendor or fork the external Data Cloud CLI plugin.
- It does not use MCP.
- It does not replace phase-specific skills once the problem is localized.
- It does not cover STDM/session tracing/parquet analysis; use
sf-ai-agentforce-observabilityfor that.
Data Cloud skill family
| Skill | Purpose |
|---|---|
| sf-datacloud | Orchestrator, data spaces, data kits, cross-phase workflows |
| sf-datacloud-connect | Connections, connectors, source discovery |
| sf-datacloud-prepare | Data streams, DLOs, transforms, DocAI |
| sf-datacloud-harmonize | DMOs, mappings, identity resolution, data graphs |
| sf-datacloud-segment | Segments, calculated insights |
| sf-datacloud-act | Activations, activation targets, data actions |
| sf-datacloud-retrieve | SQL, async query, vector search, search indexes |
Runtime model
This family assumes:
- Salesforce CLI (
sf) - a Data Cloud-enabled org
- the external community
sf data360plugin linked intosf
See references/plugin-setup.md.
Deterministic helpers included
| Path | Purpose |
|---|---|
| scripts/bootstrap-plugin.sh | Clone/update the community plugin, compile it, and link it into sf |
| scripts/verify-plugin.sh | Check that the runtime is available before starting Data Cloud work |
| scripts/diagnose-org.mjs | Classify org readiness by phase before mutating Data Cloud assets |
| references/feature-readiness.md | Map high-signal errors and feature gates to concrete next steps |
| assets/definitions/ | Generic JSON templates for repeatable Data Cloud definition files |
| UPSTREAM.md | Upstream mapping for future distillation and maintenance |
Generic templates
The family includes reusable starting points for:
- data streams
- DMOs
- mappings
- identity resolution rulesets
- segments
- search indexes
These are intentionally generic and should be adapted to the target org.
Quick start
The script examples below assume the skill is installed under ~/.claude/skills/ via the full Claude Code installer. If you are working from a repo checkout, run the same scripts from that checkout path.1. Verify the runtime
bash ~/.claude/skills/sf-datacloud/scripts/verify-plugin.sh
# or with an org alias
bash ~/.claude/skills/sf-datacloud/scripts/verify-plugin.sh myorgThe helper treats sf data360 doctor as advisory and falls back to additional read-only smoke checks when an org is only partially provisioned.
2. Diagnose feature readiness before mutating
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o myorg --json
# optional retrieve-plane probe, only when you know the table is real
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o myorg --phase retrieve --describe-table MyDMO__dlm --jsonUse the diagnose helper to distinguish between:
- feature-gated modules
- empty-but-enabled modules
- query-plane issues
- runtime/auth problems
3. Bootstrap the plugin if needed
python3 ~/.claude/sf-skills-install.py --with-datacloud-runtime
# or run the helper script directly
bash ~/.claude/skills/sf-datacloud/scripts/bootstrap-plugin.sh4. Start with read-only inspection
sf data360 man
sf data360 doctor -o myorg 2>/dev/null
sf data360 dmo list -o myorg 2>/dev/null
sf data360 segment list -o myorg 2>/dev/null
sf data360 activation platforms -o myorg 2>/dev/nullCommon examples
"Set up a Customer 360 proof of concept in Data Cloud"
"Troubleshoot why my unified profiles are not increasing"
"I need to figure out whether this issue is in mappings, identity resolution, or segment SQL"
"Show me how to inspect data spaces and data kits for this org"References
- SKILL.md - Orchestrator guidance
- references/plugin-setup.md - Plugin install and verification
- references/feature-readiness.md - Readiness classification and setup guidance
- UPSTREAM.md - Upstream tracking and distillation policy
- CREDITS.md - Contributor and source attribution
Primary contributor
Gnanasekaran Thoppae — primary contributor for the sf-datacloud family.
License
MIT License - See LICENSE.
Data Cloud Feature Readiness
Use this guide when a Data Cloud command fails and you need to decide whether the issue is:
- runtime / plugin setup
- org authentication
- product provisioning
- feature gating
- empty-but-enabled configuration
- a bad table name or wrong probe
The goal is to detect first, classify second, and guide third instead of blindly executing a command and surfacing a raw error.
Core principle
Do not treat one failing command as proof that all of Data Cloud is unavailable.
Different command families hit different backend surfaces. A failure in one area can coexist with successful read-only access in another.
Examples:
sf data360 doctorchecks the search-index endpoint only.sf data360 query describechecks the query plane and requires a real DMO/DLO table name.sf data360 dmo listchecks a catalog/listing endpoint.sf data360 activation platformschecks the activation destination surface.
Recommended first step
Run the shared readiness classifier before mutation-heavy work:
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o myorg --jsonFor retrieve/query work, only add a table probe when you already know the table name is real:
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o myorg --phase retrieve --describe-table MyDMO__dlm --jsonSignal matrix
| Signal | What it usually means | What to do next | What not to assume |
|---|---|---|---|
sf data360 man fails | Runtime/plugin missing | Install or relink the community runtime | Do not debug org features yet |
sf org display -o <org> fails | Org auth missing | Re-authenticate the org | Do not blame Data Cloud |
No results. | Feature is reachable but currently empty | Continue with create/setup flow | Do not say the feature is disabled |
This feature is not currently enabled for this user type or org: [CdpXxx] | Specific module is gated for the current org/user | Guide the user to setup/provisioning/permission review for that module | Do not say the entire product is disabled |
Couldn't find CDP tenant ID. Please enable CDP first. | The query plane could not resolve a CDP tenant for that request | Re-check broader readiness with data-space list, dmo list, and doctor; then retry with a known table if appropriate | Do not treat this as universal proof that all Data Cloud endpoints are off |
NOT_FOUND: DataModelEntity ... is not found | The table name is not queryable in that org | Pick a real DMO/DLO from dmo list, dlo list, or dmo get | Do not say the whole query feature is unavailable |
Request failed | Generic endpoint failure or inconclusive response | Fall back to neighboring read-only probes | Do not stop after one generic failure |
High-signal feature gates
CdpDataStreams
Typical impact:
sf data360 data-stream list- prepare / ingestion workflows
Suggested guidance:
- review Data Cloud Setup provisioning
- review source connector readiness
- confirm the user has the right Data Cloud permissions
- for Salesforce CRM ingestion, confirm the connector/integration user has object + field read access in the source org
CdpIdentityResolution
Typical impact:
sf data360 identity-resolution list- harmonize / unified profile workflows
Suggested guidance:
- review harmonization and identity-resolution entitlements
- verify Data Cloud permission sets for the acting user
- confirm upstream DLO/DMO work is already healthy before retrying IR
CdpActivationTarget
Typical impact:
sf data360 activation-target list- downstream audience delivery setup
Suggested guidance:
- review Data Cloud activation permissions
- review target/destination setup in Data Cloud Setup
- confirm the org/edition actually exposes activation-target management for that user
CdpActivationExternalPlatform
Typical impact:
sf data360 activation platforms- platform catalog for destinations such as ads or storage targets
Suggested guidance:
- review Activation Targets setup
- review destination-specific authentication/configuration
- check whether the org exposes additional toggles in Data Cloud Setup → Feature Manager
CdpDataSpace
Typical impact:
sf data360 data-space list- core multi-space administration
Suggested guidance:
- review core Data Cloud provisioning and user access
- confirm the user is intended to administer/view data spaces
What is usually not fully programmatic
Do not promise a pure CLI flow for:
- initial Data Cloud tenant provisioning
- license assignment
- every org-wide Data Cloud enablement step
These commonly require Setup UI, provisioning, licensing, or account-level activation.
What is often programmatic after provisioning
Once the org is properly provisioned, many workflows are programmatic through sf data360, including:
- data-space listing / get / create / update
- DMO catalog inspection and some DMO mutations
- segment and calculated insight lifecycle operations
- activation target and data action target operations
- search-index lifecycle operations
- hybrid search and related retrieve-plane workflows on newer runtime versions
Exact coverage still depends on org entitlements and the user's permissions.
Known partial-automation gap
Some external database connectors can be created via API while the corresponding data-stream creation flow still requires UI steps or org-specific browser automation.
Practical guidance:
- treat connection creation and stream creation as separate capability checks
- do not assume a successful external connection create call means
data-stream createwill work for that connector - if the org relies on that path, guide the user toward manual UI creation or a locally validated Playwright/browser automation helper rather than promising a generic CLI-only workflow
User-facing guidance model
When blocked, respond in this order: 1. Classify the failing surface — runtime, auth, provisioning, feature gate, empty state, or bad table 2. Show the evidence — the specific command + high-signal error text 3. Give the next setup step — Data Cloud Setup, permissions, Feature Manager, source-object permissions, destination auth, etc. 4. Avoid false claims — don't say "Data Cloud is off" unless multiple core probes support that conclusion
Setup guidance to point users toward
Depending on the failing feature, guide users to review:
- Setup → Data Cloud Setup for core provisioning / Get Started flow
- Standard Data Cloud permission sets appropriate to the task (for example Architect/Admin vs activation-focused roles)
- Data Cloud Setup → Feature Manager for org-specific toggles or beta features that are actually exposed there
- Source org permissions for CRM ingestion users when streams/connectors depend on object/field access
- Activation target and destination authentication when activation features are partially enabled but not usable yet
Skill author checklist
Before telling the user a feature is unavailable:
- [ ] Did I run a targeted probe for this phase?
- [ ] Did I check at least one neighboring read-only probe?
- [ ] Did I avoid treating
doctoras a full product readiness check? - [ ] Did I avoid treating
query describeas a universal tenant probe? - [ ] Did I tell the user exactly what setup area to review next?
Data Cloud Community Plugin Setup
The sf-datacloud family uses a community `sf data360` CLI runtime. sf-skills does not vendor or fork that plugin.
Why this setup exists
- keeps sf-skills focused on skills, prompts, docs, and templates
- lets the upstream runtime continue to evolve independently
- avoids MCP and keeps execution deterministic
Prerequisites
- Node.js 18+
- yarn
- Salesforce CLI (
sf) - git
- a Data Cloud-enabled org authenticated with
sf org login web -a <alias>
Recommended setup path
The command examples below assume the skill is installed under ~/.claude/skills/ via the full Claude Code installer. If you are working from a cloned sf-skills repo instead, run the same scripts from that checkout path.
If you use the Python installer, it can install this optional runtime for you:
python3 ~/.claude/sf-skills-install.py --with-datacloud-runtimeOr use the helper script directly:
bash ~/.claude/skills/sf-datacloud/scripts/bootstrap-plugin.shBy default it clones the plugin into ~/.sf-community-tools/datacloud/sf-cli-plugin-data360, installs dependencies, compiles it, and links it into the local Salesforce CLI.
Manual setup
git clone https://github.com/Jaganpro/sf-cli-plugin-data360.git
cd sf-cli-plugin-data360
yarn install
npx tsc
node ~/.claude/skills/sf-datacloud/scripts/generate-manifest.mjs .
sf plugins link .Verification
sf data360 man
bash ~/.claude/skills/sf-datacloud/scripts/verify-plugin.sh
bash ~/.claude/skills/sf-datacloud/scripts/verify-plugin.sh myorg
node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o myorg --jsonFor newer command families such as sf data360 query hybrid and recent pagination fixes, update the community runtime to the latest upstream commit by re-running the bootstrap helper.
sf data360 doctor is useful, but it is not the only readiness signal. On partially provisioned orgs it can fail even when other read-only Data Cloud commands still work. The helper script treats doctor as advisory and falls back to additional smoke checks.
Use diagnose-org.mjs when you need phase-specific readiness classification instead of a simple pass/fail check. It helps distinguish:
- empty-but-enabled modules
- feature-gated modules
- query-plane issues
- runtime/auth failures
Output-noise tip
When using linked community plugins, stderr can include warning noise. For normal usage, prefer:
sf data360 dmo list --all -o myorg 2>/dev/null
sf data360 segment list -o myorg 2>/dev/nullTroubleshooting
ESM auto-transpile warning
If you see Warning: @gthoppae/sf-cli-plugin-data360 is a linked ESM module and cannot be auto-transpiled, generate the oclif command manifest:
node ~/.claude/skills/sf-datacloud/scripts/generate-manifest.mjs ~/.sf-community-tools/datacloud/sf-cli-plugin-data360This tells oclif to use pre-compiled output directly instead of attempting auto-transpilation. The npx oclif manifest alternative may fail on newer Node.js versions due to @oclif/core version mismatches.
Clone error references gthoppae/sf-cli-plugin-data360
If the clone failure mentions gthoppae/sf-cli-plugin-data360, your local ~/.claude/sf-skills-install.py copy is outdated. Refresh the installer first, then retry the optional runtime install:
python3 ~/.claude/sf-skills-install.py --force-update
python3 ~/.claude/sf-skills-install.py --with-datacloud-runtimeOr rerun the latest installer directly from GitHub:
curl -sSL https://raw.githubusercontent.com/Jaganpro/sf-skills/main/tools/install.py | python3 - --with-datacloud-runtimePlugin not found after install
If sf was installed globally (with sudo), the default data directory may be root-owned. Set SF_DATA_DIR in your shell profile:
export SF_DATA_DIR="${HOME}/.local/share/sf"Then re-run sf plugins link . from the plugin directory, or re-run the bootstrap script.
What to do if the plugin is missing
1. run the bootstrap script 2. re-open your shell if sf plugin discovery is stale 3. verify with sf data360 man 4. only then start live Data Cloud work
Setup guidance reminder
The runtime can help you detect missing capability, but it cannot fully replace Setup for:
- initial Data Cloud provisioning / tenant creation
- license assignment
- every org-wide feature enablement step
When a module is gated, guide users toward the right setup area instead of promising a fully programmatic enablement flow. See feature-readiness.md.
Scope reminder
This setup is for the Data Cloud product family:
sf-datacloudsf-datacloud-connectsf-datacloud-preparesf-datacloud-harmonizesf-datacloud-segmentsf-datacloud-actsf-datacloud-retrieve
For STDM/session tracing/parquet work, use sf-ai-agentforce-observability instead of this plugin-focused family.
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_REPO="${PLUGIN_REPO:-https://github.com/Jaganpro/sf-cli-plugin-data360.git}"
BASE_DIR="${1:-${HOME}/.sf-community-tools/datacloud}"
PLUGIN_DIR="${BASE_DIR}/sf-cli-plugin-data360"
export SF_DATA_DIR="${SF_DATA_DIR:-${HOME}/.local/share/sf}"
need() {
command -v "$1" >/dev/null 2>&1 || {
echo "Missing required command: $1" >&2
exit 1
}
}
need git
need node
need yarn
need sf
need npx
mkdir -p "${BASE_DIR}"
if [[ -d "${PLUGIN_DIR}/.git" ]]; then
echo "Updating existing plugin checkout: ${PLUGIN_DIR}"
git -C "${PLUGIN_DIR}" pull --ff-only
else
echo "Cloning plugin into: ${PLUGIN_DIR}"
git clone "${PLUGIN_REPO}" "${PLUGIN_DIR}"
fi
cd "${PLUGIN_DIR}"
echo "Installing dependencies..."
yarn install
echo "Compiling plugin..."
npx tsc
echo "Generating oclif command manifest..."
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
node "${SCRIPT_DIR}/generate-manifest.mjs" "${PLUGIN_DIR}"
echo "Linking plugin into Salesforce CLI..."
mkdir -p "${SF_DATA_DIR}"
sf plugins link .
echo "Verifying runtime..."
sf data360 man >/dev/null
echo "Done. Community Data Cloud runtime is linked."
echo "Location: ${PLUGIN_DIR}"
echo "Try: sf data360 man"
#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
const HELP = `Data Cloud org readiness classifier
Usage:
node diagnose-org.mjs -o <orgAlias> [--json] [--phase <phase>] [--describe-table <table>] [--timeout <seconds>]
Options:
-o, --target-org Salesforce org alias or username (required)
--json Print machine-readable JSON
--phase Optional phase focus: all | connect | prepare | harmonize | segment | act | retrieve
--describe-table Optional DMO/DLO table name for a retrieve-plane probe
--timeout Per-command timeout in seconds (default: 45)
-h, --help Show this help text
`;
const FEATURE_GUIDANCE = {
CdpDataStreams: 'Data Streams are unavailable for this org/user. Review Data Cloud Setup provisioning, ingestion-related feature availability, and source connector permissions before using sf-datacloud-prepare.',
CdpIdentityResolution: 'Identity Resolution is unavailable for this org/user. Review Data Cloud harmonization/identity-resolution entitlements and the user\'s Data Cloud permissions before using sf-datacloud-harmonize for IR work.',
CdpActivationTarget: 'Activation Targets are unavailable for this org/user. Review activation permissions, destination setup, and any org-specific activation enablement before using sf-datacloud-act.',
CdpActivationExternalPlatform: 'Activation destination platforms are unavailable for this org/user. Review Activation Targets setup, destination auth/configuration, and any feature toggles exposed in Data Cloud Setup → Feature Manager.',
CdpDataSpace: 'Data Spaces are unavailable for this org/user. Review core Data Cloud provisioning and user permissions in Data Cloud Setup before assuming downstream phases can work.',
};
const CAVEATS = [
'sf data360 doctor is advisory only: the current upstream command checks the search-index endpoint, not every Data Cloud module.',
'Do not use query describe as a universal tenant probe. Run it only after confirming broader readiness and only against a known DMO or DLO table.',
'Core Data Cloud provisioning, tenant creation, and license assignment are not reliably exposed as a full CLI enablement flow. Use CLI detection plus Setup guidance instead of promising fully programmatic enablement.',
];
function parseArgs(argv) {
const args = { phase: 'all', timeout: 45, json: false, describeTable: null };
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
switch (arg) {
case '-o':
case '--target-org':
args.targetOrg = argv[++i];
break;
case '--phase':
args.phase = argv[++i] ?? 'all';
break;
case '--describe-table':
args.describeTable = argv[++i] ?? null;
break;
case '--timeout':
args.timeout = Number(argv[++i] ?? '45');
break;
case '--json':
args.json = true;
break;
case '-h':
case '--help':
args.help = true;
break;
default:
throw new Error(`Unknown argument: ${arg}`);
}
}
return args;
}
function trimText(text) {
return (text || '').replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, '').trim();
}
function runCommand(command, args, { json = false, timeoutSeconds = 45 } = {}) {
const finalArgs = [...args];
if (json && !finalArgs.includes('--json')) finalArgs.push('--json');
const result = spawnSync(command, finalArgs, {
encoding: 'utf8',
timeout: timeoutSeconds * 1000,
});
const stdout = result.stdout || '';
const stderr = result.stderr || '';
let parsed = null;
if (stdout.trim()) {
try {
parsed = JSON.parse(stdout);
} catch {
parsed = null;
}
}
let errorMessage = null;
if (result.error) {
if (result.error.code === 'ENOENT') {
errorMessage = `${command} is not installed or not on PATH.`;
} else if (result.error.code === 'ETIMEDOUT') {
errorMessage = `Timed out after ${timeoutSeconds}s`;
} else {
errorMessage = result.error.message;
}
}
return {
command: [command, ...finalArgs].join(' '),
exitCode: typeof result.status === 'number' ? result.status : 1,
stdout: trimText(stdout),
stderr: trimText(stderr),
parsed,
errorMessage,
};
}
function extractSuccessSample(parsed) {
const result = parsed?.result ?? {};
if (Array.isArray(result?.data)) {
return result.data[0] ?? null;
}
if (Array.isArray(result?.data?.platforms)) {
return result.data.platforms[0] ?? null;
}
if (Array.isArray(result?.semanticSearchDefinitionDetails)) {
return result.semanticSearchDefinitionDetails[0] ?? null;
}
return null;
}
function extractDetails(parsed) {
const result = parsed?.result ?? {};
if (result && typeof result === 'object' && !Array.isArray(result)) {
const details = { ...result };
delete details.data;
if (details.status || details.indexCount !== undefined || details.apiVersion || details.instanceUrl) {
return details;
}
}
return undefined;
}
function extractCount(parsed) {
const result = parsed?.result ?? {};
if (Array.isArray(result?.data)) return result.data.length;
if (typeof result?.totalSize === 'number') return result.totalSize;
if (Array.isArray(result?.data?.platforms)) return result.data.platforms.length;
if (typeof result?.indexCount === 'number') return result.indexCount;
return null;
}
function classifyError(message) {
const text = message || '';
const gateMatch = text.match(/\[([A-Za-z0-9]+)\]/);
if (text.includes("Couldn't find CDP tenant ID")) {
return {
state: 'query_service_unavailable',
code: 'QUERY_SERVICE_TENANT_LOOKUP_FAILED',
reason: 'The query service could not resolve a CDP tenant for this org context. Confirm broader readiness with data-space, DMO, or doctor probes before declaring Data Cloud fully disabled.',
};
}
if (text.includes('This feature is not currently enabled for this user type or org')) {
const featureCode = gateMatch?.[1] ?? 'UNKNOWN_FEATURE_GATE';
return {
state: 'feature_gated',
code: featureCode,
reason: FEATURE_GUIDANCE[featureCode] ?? 'This capability is gated for the current org or user. Review provisioning, permissions, and any relevant setup toggles before proceeding.',
};
}
if (text.includes('NOT_FOUND: DataModelEntity')) {
return {
state: 'table_not_found',
code: 'DATA_MODEL_ENTITY_NOT_FOUND',
reason: 'The specified DMO or DLO table name is not queryable in this org. Pick a real table from dmo list / dlo list or use dmo get first.',
};
}
if (text.includes('Request failed')) {
return {
state: 'request_failed',
code: 'REQUEST_FAILED',
reason: 'The endpoint returned a generic request failure. Treat this as inconclusive and confirm readiness with adjacent read-only probes.',
};
}
if (text.includes('is not a sf command') || text.includes('not installed or not on PATH')) {
return {
state: 'runtime_missing',
code: 'RUNTIME_MISSING',
reason: 'The sf data360 runtime is not available. Install or relink the community plugin first.',
};
}
if (text.includes('Timed out after')) {
return {
state: 'timeout',
code: 'TIMEOUT',
reason: 'The probe timed out. Retry, reduce the scope, or fall back to a smaller read-only command family.',
};
}
return {
state: 'unknown_error',
code: 'UNKNOWN_ERROR',
reason: 'The probe failed for an unclassified reason. Inspect the raw message and confirm readiness with neighboring probes.',
};
}
function normalizeProbe(name, raw, meta = {}) {
const base = {
name,
command: raw.command,
exitCode: raw.exitCode,
stderr: raw.stderr || undefined,
...meta,
};
if (raw.errorMessage) {
const error = classifyError(raw.errorMessage);
return { ...base, ...error, message: raw.errorMessage };
}
if (raw.parsed && (raw.parsed.status === 0 || raw.exitCode === 0)) {
const count = extractCount(raw.parsed);
const sample = extractSuccessSample(raw.parsed);
const details = extractDetails(raw.parsed);
let state = 'ok';
if (count !== null) {
state = count > 0 ? 'enabled_populated' : 'enabled_empty';
}
if (raw.parsed?.result?.status === 'ok') {
state = 'ok';
}
return {
...base,
state,
count,
sample: sample ?? undefined,
details,
};
}
const message = raw.parsed?.message || raw.stdout || raw.stderr || `Command failed with exit code ${raw.exitCode}`;
const error = classifyError(message);
return { ...base, ...error, message };
}
function summarizePhase(checks) {
const states = Object.values(checks).map((check) => check.state);
if (states.length === 0) return 'skipped';
if (states.every((state) => state === 'skipped')) return 'skipped';
const hasSuccess = states.some((state) => ['ok', 'enabled_empty', 'enabled_populated'].includes(state));
const hasPopulated = states.includes('enabled_populated');
const hasBlocking = states.some((state) => ['feature_gated', 'query_service_unavailable', 'request_failed', 'table_not_found', 'timeout', 'unknown_error'].includes(state));
if (hasSuccess && hasBlocking) return 'partial';
if (hasPopulated) return 'ready';
if (hasSuccess) return 'ready_empty';
if (states.every((state) => state === 'feature_gated')) return 'feature_gated';
if (states.every((state) => state === 'query_service_unavailable')) return 'query_unavailable';
if (states.every((state) => ['request_failed', 'timeout', 'unknown_error'].includes(state))) return 'blocked';
return 'unknown';
}
function guidanceFromProbe(scope, probe) {
if (!probe) return [];
if (['ok', 'enabled_empty', 'enabled_populated', 'skipped'].includes(probe.state)) return [];
if (probe.state === 'runtime_missing') {
return [
'Install or relink the community sf data360 runtime before attempting Data Cloud work.',
'Use python3 ~/.claude/sf-skills-install.py --with-datacloud-runtime or bash ~/.claude/skills/sf-datacloud/scripts/bootstrap-plugin.sh.',
];
}
if (probe.state === 'query_service_unavailable') {
return [
`${scope}: query-specific tenant lookup failed. Do not treat this as a global Data Cloud outage by itself. Re-check core probes such as data-space list, dmo list, and doctor, then use a known table name if you retry query describe.`,
];
}
if (probe.state === 'feature_gated') {
return [`${scope}: ${probe.reason}`];
}
if (probe.state === 'table_not_found') {
return [`${scope}: ${probe.reason}`];
}
if (probe.state === 'request_failed') {
return [
`${scope}: ${probe.reason}`,
'If doctor is the failing probe, remember that it only checks search indexes; use adjacent read-only probes before deciding the whole org is blocked.',
];
}
if (probe.state === 'timeout') {
return [`${scope}: ${probe.reason}`];
}
return [`${scope}: ${probe.reason}`];
}
function parseOrgDisplay(raw, targetOrg) {
const base = {
alias: targetOrg,
state: 'ok',
};
if (raw.errorMessage) {
return { ...base, state: 'org_not_authenticated', message: raw.errorMessage };
}
if (raw.parsed && raw.parsed.status === 0) {
const result = raw.parsed.result || {};
return {
...base,
alias: result.alias || targetOrg,
username: result.username,
instanceUrl: result.instanceUrl,
apiVersion: result.apiVersion,
connectedStatus: result.connectedStatus,
};
}
const message = raw.parsed?.message || raw.stdout || raw.stderr || `Failed to resolve org ${targetOrg}`;
return {
...base,
state: 'org_not_authenticated',
message,
};
}
function buildProbeMatrix(orgAlias, describeTable) {
return {
core: {
doctor: ['data360', 'doctor', '-o', orgAlias],
dataSpaces: ['data360', 'data-space', 'list', '-o', orgAlias],
dmos: ['data360', 'dmo', 'list', '-o', orgAlias],
},
phases: {
connect: {
connectorCatalog: ['data360', 'connection', 'connector-list', '-o', orgAlias],
connectionsSalesforceCRM: ['data360', 'connection', 'list', '-o', orgAlias, '--connector-type', 'SalesforceCRM'],
},
prepare: {
dataStreams: ['data360', 'data-stream', 'list', '-o', orgAlias],
dlos: ['data360', 'dlo', 'list', '-o', orgAlias],
},
harmonize: {
dmos: ['data360', 'dmo', 'list', '-o', orgAlias],
identityResolution: ['data360', 'identity-resolution', 'list', '-o', orgAlias],
},
segment: {
segments: ['data360', 'segment', 'list', '-o', orgAlias],
calculatedInsights: ['data360', 'calculated-insight', 'list', '-o', orgAlias],
},
act: {
activationPlatforms: ['data360', 'activation', 'platforms', '-o', orgAlias],
activationTargets: ['data360', 'activation-target', 'list', '-o', orgAlias],
dataActionTargets: ['data360', 'data-action-target', 'list', '-o', orgAlias],
},
retrieve: {
searchIndexes: ['data360', 'search-index', 'list', '-o', orgAlias],
queryDescribe: describeTable
? ['data360', 'query', 'describe', '-o', orgAlias, '--table', describeTable]
: null,
},
},
};
}
function printTextReport(report) {
const lines = [];
lines.push(`Data Cloud readiness for ${report.org.alias}`);
lines.push(`Runtime: ${report.runtime.state}`);
if (report.runtime.message) lines.push(` ${report.runtime.message}`);
lines.push(`Org auth: ${report.org.state}`);
if (report.org.username) lines.push(` User: ${report.org.username}`);
if (report.org.instanceUrl) lines.push(` Instance: ${report.org.instanceUrl}`);
lines.push('');
lines.push('Core probes:');
for (const [name, probe] of Object.entries(report.core)) {
lines.push(` - ${name}: ${probe.state}${probe.count !== null && probe.count !== undefined ? ` (${probe.count})` : ''}`);
if (probe.code) lines.push(` code: ${probe.code}`);
if (probe.message) lines.push(` ${probe.message}`);
}
lines.push('');
lines.push('Phase readiness:');
for (const [phase, entry] of Object.entries(report.phases)) {
lines.push(` ${phase}: ${entry.summary}`);
for (const [name, probe] of Object.entries(entry.checks)) {
lines.push(` - ${name}: ${probe.state}${probe.count !== null && probe.count !== undefined ? ` (${probe.count})` : ''}`);
if (probe.code) lines.push(` code: ${probe.code}`);
if (probe.message) lines.push(` ${probe.message}`);
}
}
lines.push('');
lines.push('Caveats:');
for (const caveat of report.caveats) {
lines.push(` - ${caveat}`);
}
lines.push('');
lines.push('Guidance:');
if (report.guidance.length === 0) {
lines.push(' - No blocking issues detected from the chosen probes. Continue with read-only inspection, then mutate intentionally.');
} else {
for (const item of report.guidance) {
lines.push(` - ${item}`);
}
}
console.log(lines.join('\n'));
}
function main() {
let args;
try {
args = parseArgs(process.argv.slice(2));
} catch (error) {
console.error(error.message);
console.error('');
console.error(HELP);
process.exit(1);
}
if (args.help) {
console.log(HELP);
process.exit(0);
}
if (!args.targetOrg) {
console.error('Missing required --target-org / -o argument.');
console.error('');
console.error(HELP);
process.exit(1);
}
const runtimeCheck = runCommand('sf', ['data360', 'man'], { json: false, timeoutSeconds: args.timeout });
const runtime = runtimeCheck.exitCode === 0
? { state: 'ok', command: runtimeCheck.command }
: { ...classifyError(runtimeCheck.stderr || runtimeCheck.stdout || runtimeCheck.errorMessage || 'sf data360 runtime is unavailable'), command: runtimeCheck.command };
const orgDisplay = parseOrgDisplay(
runCommand('sf', ['org', 'display', '-o', args.targetOrg], { json: true, timeoutSeconds: args.timeout }),
args.targetOrg
);
const matrix = buildProbeMatrix(args.targetOrg, args.describeTable);
const report = {
runtime,
org: orgDisplay,
core: {},
phases: {},
caveats: [...CAVEATS],
guidance: [],
};
if (runtime.state !== 'ok' || orgDisplay.state !== 'ok') {
report.guidance.push(...guidanceFromProbe('runtime', runtime));
if (orgDisplay.state !== 'ok') {
report.guidance.push(`Authenticate the target org first: sf org login web -a ${args.targetOrg}`);
}
if (args.json) {
console.log(JSON.stringify(report, null, 2));
} else {
printTextReport(report);
}
process.exit(runtime.state === 'ok' && orgDisplay.state === 'ok' ? 0 : 1);
}
for (const [name, commandArgs] of Object.entries(matrix.core)) {
report.core[name] = normalizeProbe(name, runCommand('sf', commandArgs, { json: true, timeoutSeconds: args.timeout }));
}
const selectedPhases = args.phase === 'all'
? Object.keys(matrix.phases)
: [args.phase];
for (const phase of selectedPhases) {
const checks = {};
const phaseMatrix = matrix.phases[phase] || {};
for (const [name, commandArgs] of Object.entries(phaseMatrix)) {
if (!commandArgs) {
checks[name] = { name, state: 'skipped', reason: 'No describe table was supplied for the query probe.' };
continue;
}
checks[name] = normalizeProbe(name, runCommand('sf', commandArgs, { json: true, timeoutSeconds: args.timeout }));
}
report.phases[phase] = {
summary: summarizePhase(checks),
checks,
};
}
const uniqueGuidance = new Set();
for (const [name, probe] of Object.entries(report.core)) {
for (const item of guidanceFromProbe(`core.${name}`, probe)) uniqueGuidance.add(item);
}
for (const [phase, entry] of Object.entries(report.phases)) {
for (const [name, probe] of Object.entries(entry.checks)) {
for (const item of guidanceFromProbe(`${phase}.${name}`, probe)) uniqueGuidance.add(item);
}
}
const coreSignals = Object.values(report.core).map((probe) => probe.state);
if (coreSignals.some((state) => ['ok', 'enabled_empty', 'enabled_populated'].includes(state))) {
uniqueGuidance.add('At least one core Data Cloud probe succeeded. Treat individual query or feature-gate failures as scoped issues, not automatic proof that the whole product is disabled.');
}
uniqueGuidance.add('For core provisioning, tenant creation, or license assignment gaps, use Setup → Data Cloud Setup and the current standard Data Cloud permission sets. Feature Manager can expose some org-specific toggles, but it is not the source of truth for every missing capability.');
uniqueGuidance.add('Reference: skills/sf-datacloud/references/feature-readiness.md');
report.guidance = [...uniqueGuidance];
if (args.json) {
console.log(JSON.stringify(report, null, 2));
} else {
printTextReport(report);
}
process.exit(0);
}
main();
#!/usr/bin/env node
/**
* Generate oclif.manifest.json for the sf-cli-plugin-data360 community plugin.
*
* Why this exists:
* `npx oclif manifest` imports every command class, which triggers the
* @oclif/core class hierarchy. When the plugin's pinned @oclif/core is
* older than what the current Node.js version expects, the import fails
* with "Cannot read properties of undefined (reading 'prototype')".
*
* This script avoids importing commands entirely. It walks the compiled
* lib/commands/ directory and extracts static metadata from source text
* via regex, producing a valid manifest that tells oclif to skip
* auto-discovery and auto-transpilation.
*
* Usage:
* node generate-manifest.mjs # uses cwd
* node generate-manifest.mjs /path/to # uses explicit plugin root
*/
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
import { join, relative, sep } from 'node:path';
const pluginRoot = process.argv[2] || process.cwd();
const pkg = JSON.parse(readFileSync(join(pluginRoot, 'package.json'), 'utf8'));
const commandsDir = join(pluginRoot, (pkg.oclif?.commands || './lib/commands').replace('./', ''));
function walk(dir) {
const files = [];
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) files.push(...walk(full));
else if (entry.endsWith('.js') && !entry.endsWith('.test.js')) files.push(full);
}
return files;
}
function parseCommandSource(source, id) {
const cmd = { id, pluginName: pkg.name, pluginType: 'link', aliases: [] };
const summaryMatch = source.match(/static\s+summary\s*=\s*['"`]([^'"`]+)/);
if (summaryMatch) cmd.summary = summaryMatch[1];
const descMatch = source.match(/static\s+description\s*=\s*['"`]([^'"`]+)/);
if (descMatch) cmd.description = descMatch[1];
if (/static\s+hidden\s*=\s*true/.test(source)) cmd.hidden = true;
if (/static\s+strict\s*=\s*false/.test(source)) cmd.strict = false;
if (/static\s+enableJsonFlag\s*=\s*true/.test(source)) cmd.enableJsonFlag = true;
return cmd;
}
const commands = {};
for (const file of walk(commandsDir)) {
const rel = relative(commandsDir, file).replace(/\.js$/, '');
const id = rel.split(sep).join(':');
try {
commands[id] = parseCommandSource(readFileSync(file, 'utf8'), id);
} catch {
commands[id] = { id, pluginName: pkg.name, pluginType: 'link', aliases: [] };
}
}
const manifest = { version: pkg.version, commands };
const outPath = join(pluginRoot, 'oclif.manifest.json');
writeFileSync(outPath, JSON.stringify(manifest, null, 2));
console.log(`Generated oclif.manifest.json: ${Object.keys(commands).length} commands (v${pkg.version})`);
#!/usr/bin/env bash
set -euo pipefail
ORG="${1:-}"
if ! command -v sf >/dev/null 2>&1; then
echo "Salesforce CLI (sf) is not installed or not on PATH." >&2
exit 1
fi
if ! sf data360 man >/dev/null 2>&1; then
echo "The community 'sf data360' runtime is not available." >&2
echo "Run bash ~/.claude/skills/sf-datacloud/scripts/bootstrap-plugin.sh first." >&2
exit 1
fi
echo "✓ sf data360 runtime detected"
if [[ -n "${ORG}" ]]; then
if sf org display -o "${ORG}" >/dev/null 2>&1; then
echo "✓ org alias '${ORG}' is authenticated"
else
echo "Org alias '${ORG}' is not authenticated." >&2
exit 1
fi
if sf data360 doctor -o "${ORG}" >/dev/null 2>&1; then
echo "✓ sf data360 doctor completed for '${ORG}'"
else
echo "! sf data360 doctor did not complete cleanly for '${ORG}'. Falling back to read-only smoke checks." >&2
smoke_passed=0
while IFS='|' read -r label command; do
[[ -z "${label}" ]] && continue
if bash -lc "${command}" >/dev/null 2>&1; then
echo "✓ ${label} smoke check passed for '${ORG}'"
smoke_passed=1
fi
done <<EOF
connectors|sf data360 connection connector-list -o "${ORG}"
dmos|sf data360 dmo list --all -o "${ORG}"
segments|sf data360 segment list -o "${ORG}"
data-action-targets|sf data360 data-action-target list -o "${ORG}"
EOF
if [[ "${smoke_passed}" -ne 1 ]]; then
echo "No fallback Data Cloud smoke checks succeeded for '${ORG}'. Check org access and Data Cloud provisioning." >&2
exit 1
fi
echo "! Doctor remains advisory for partially provisioned orgs; at least one read-only Data Cloud command succeeded." >&2
fi
fi
echo "Verification complete."
if [[ -n "${ORG}" ]]; then
echo "Next: node ~/.claude/skills/sf-datacloud/scripts/diagnose-org.mjs -o '${ORG}' --json"
fi
Upstream Distillation Map for sf-datacloud
This file exists to make downstream maintenance easy without sacrificing sf-skills consistency.
Source repositories
Skill concepts and phase model
- Repo:
gthoppae/sf-data360-skills - URL: https://github.com/gthoppae/sf-data360-skills
- Last reviewed commit for this integration:
0b446c4d245cef385c504d4677233e367fb87f36
CLI runtime and command surface
- Repo:
gthoppae/sf-cli-plugin-data360 - URL: https://github.com/gthoppae/sf-cli-plugin-data360
- Last reviewed commit for this integration:
c3507ff593da82b3de51000ccbddcf979090e900
v0.0.7 review highlights
Skills repo (sf-data360-skills)
- adds curated connector examples for SharePoint Unstructured, Snowflake, and Ingestion API
- adds Ingestion API schema-upload guidance for
connection schema-upsert - adds prepare-phase guidance for stream refresh behavior vs connection-level reruns
- adds ingestion example assets for sending records through the Ingestion API
- adds unstructured DLO guidance for UI-vs-CLI setup trade-offs
Plugin repo (sf-cli-plugin-data360)
- improves
connection schema-getoutput - improves
connection testname resolution for non-default connector types - tightens
search-index configargument handling - adds
--rawresponse output support on shared CRUD surfaces - adds connector examples and bug-fix coverage in tests
What sf-skills intentionally keeps
- the 7-part phase decomposition
- the community
sf data360command surface as the runtime - high-signal gotchas such as connector type requirements,
--allpagination, API-version quirks, SQL-vs-SOQL distinctions, and connector-specific auth shapes - deterministic JSON definition file patterns
- new upstream additions that are broadly useful, such as SharePoint Unstructured, Snowflake, Ingestion API, and unstructured-refresh guidance
What sf-skills intentionally changes
- renames the family from
sf-data360-*tosf-datacloud-* - rewrites prompts into sf-skills house style
- keeps the plugin external instead of vendoring or forking it
- adds deterministic helper scripts and generic templates suited to sf-skills users
- avoids workshop-specific org names, payloads, and repo-coupled installation assumptions
- selectively distills public-safe example payloads and gotchas instead of copying workshop flows blindly
- does not automatically vendor UI-automation helpers unless they are validated and worth the maintenance cost
Maintenance contract
When upstream changes, do not copy blindly.
Instead: 1. review new upstream commits 2. identify changed command behaviors, install patterns, or gotchas 3. update sf-skills prompts and templates in a distilled form 4. keep naming, attribution, and cross-skill boundaries consistent with sf-skills 5. update this file with the new reviewed commit SHAs
High-priority upstream areas to re-check
- installation / linking workflow for the community plugin
- command counts and topic coverage
- API-version guidance
- known issues and bug-fix notes
- live-tested command set
- any new commands affecting Connect / Prepare / Harmonize / Segment / Act / Retrieve boundaries
- connector-specific payload examples worth distilling into generic repo-safe examples
- search-index / hybrid-search guidance and any command-surface changes around hybrid scoring or prefilter behavior
- Ingestion API schema-upload flow, send-data examples, and problem-record guidance
- unstructured-source setup differences between connection-level reruns, stream refreshes, and initial UI seeding
- UI-only gaps where upstream introduces browser automation; validate before importing
Cross-skill boundary reminders
Keep Data Cloud product work in sf-datacloud-*, but do not blur into:
sf-ai-agentforce-observabilityfor STDM/session tracing/parquet workflowssf-soqlfor CRM SOQL-only taskssf-datafor CRM record seeding/cleanupsf-metadatafor CRM schema creation
Local helper files in this family
references/plugin-setup.mdscripts/bootstrap-plugin.shscripts/verify-plugin.shassets/definitions/
These are sf-skills-owned conveniences and should evolve independently from upstream when that improves user experience.